@@ -1989,6 +1989,36 @@ static word32 BuildKexInitPayload(WOLFSSH* ssh, const char* kexList,
19891989 return idx ;
19901990}
19911991
1992+ #if !defined(WOLFSSH_NO_AES_CBC ) && !defined(WOLFSSH_NO_AES_CTR ) \
1993+ && !defined(WOLFSSH_NO_HMAC_SHA1 ) && !defined(WOLFSSH_NO_HMAC_SHA2_256 )
1994+ /* Like BuildKexInitPayload but with explicit per-direction cipher/MAC lists. */
1995+ static word32 BuildKexInitPayloadFull (const char * kexList ,
1996+ const char * keyList , const char * encC2S , const char * encS2C ,
1997+ const char * macC2S , const char * macS2C ,
1998+ byte firstPacketFollows , byte * out , word32 outSz )
1999+ {
2000+ word32 idx = 0 ;
2001+
2002+ AssertTrue (idx + COOKIE_SZ <= outSz );
2003+ WMEMSET (out + idx , 0 , COOKIE_SZ );
2004+ idx += COOKIE_SZ ;
2005+ idx = AppendString (out , outSz , idx , kexList );
2006+ idx = AppendString (out , outSz , idx , keyList );
2007+ idx = AppendString (out , outSz , idx , encC2S );
2008+ idx = AppendString (out , outSz , idx , encS2C );
2009+ idx = AppendString (out , outSz , idx , macC2S );
2010+ idx = AppendString (out , outSz , idx , macS2C );
2011+ idx = AppendString (out , outSz , idx , "none" );
2012+ idx = AppendString (out , outSz , idx , "none" );
2013+ idx = AppendString (out , outSz , idx , "" );
2014+ idx = AppendString (out , outSz , idx , "" );
2015+ idx = AppendByte (out , outSz , idx , firstPacketFollows );
2016+ idx = AppendUint32 (out , outSz , idx , 0 ); /* reserved */
2017+
2018+ return idx ;
2019+ }
2020+ #endif /* AES_CBC + AES_CTR + HMAC guards (BuildKexInitPayloadFull) */
2021+
19922022typedef struct {
19932023 const char * description ;
19942024 const char * kexList ;
@@ -2107,6 +2137,79 @@ static void TestFirstPacketFollows(void)
21072137 TestFirstPacketFollowsSkipped ();
21082138}
21092139
2140+ #if !defined(WOLFSSH_NO_AES_CBC ) && !defined(WOLFSSH_NO_AES_CTR ) \
2141+ && !defined(WOLFSSH_NO_HMAC_SHA1 ) && !defined(WOLFSSH_NO_HMAC_SHA2_256 )
2142+ static void TestIndependentAlgoNegotiation (void )
2143+ {
2144+ WOLFSSH_CTX * ctx ;
2145+ WOLFSSH * ssh ;
2146+ byte payload [512 ];
2147+ word32 payloadSz ;
2148+ word32 idx ;
2149+
2150+ ctx = wolfSSH_CTX_new (WOLFSSH_ENDPOINT_SERVER , NULL );
2151+ AssertNotNull (ctx );
2152+
2153+ /* Sub-test A: different non-AEAD cipher and MAC per direction */
2154+ ssh = wolfSSH_new (ctx );
2155+ AssertNotNull (ssh );
2156+ AssertIntEQ (wolfSSH_SetAlgoListKex (ssh , FPF_KEX_GOOD ), WS_SUCCESS );
2157+ AssertIntEQ (wolfSSH_SetAlgoListKey (ssh , FPF_KEY_GOOD ), WS_SUCCESS );
2158+ AssertIntEQ (wolfSSH_SetAlgoListCipher (ssh ,
2159+ "aes128-cbc,aes256-ctr" ), WS_SUCCESS );
2160+ AssertIntEQ (wolfSSH_SetAlgoListMac (ssh ,
2161+ "hmac-sha1,hmac-sha2-256" ), WS_SUCCESS );
2162+ idx = 0 ;
2163+ payloadSz = BuildKexInitPayloadFull (
2164+ FPF_KEX_GOOD , FPF_KEY_GOOD ,
2165+ "aes128-cbc" , /* C2S enc */
2166+ "aes256-ctr" , /* S2C enc */
2167+ "hmac-sha1" , /* C2S MAC */
2168+ "hmac-sha2-256" , /* S2C MAC */
2169+ 0 , payload , (word32 )sizeof (payload ));
2170+ (void )wolfSSH_TestDoKexInit (ssh , payload , payloadSz , & idx );
2171+ AssertNotNull (ssh -> handshake );
2172+ AssertIntEQ (ssh -> handshake -> peerEncryptId , ID_AES128_CBC );
2173+ AssertIntEQ (ssh -> handshake -> encryptId , ID_AES256_CTR );
2174+ AssertIntEQ (ssh -> handshake -> peerMacId , ID_HMAC_SHA1 );
2175+ AssertIntEQ (ssh -> handshake -> macId , ID_HMAC_SHA2_256 );
2176+ AssertIntEQ (ssh -> handshake -> peerAeadMode , 0 );
2177+ AssertIntEQ (ssh -> handshake -> aeadMode , 0 );
2178+ wolfSSH_free (ssh );
2179+
2180+ #ifndef WOLFSSH_NO_AES_GCM
2181+ /* Sub-test B: AEAD S2C, non-AEAD C2S — MAC only negotiated for C2S */
2182+ ssh = wolfSSH_new (ctx );
2183+ AssertNotNull (ssh );
2184+ AssertIntEQ (wolfSSH_SetAlgoListKex (ssh , FPF_KEX_GOOD ), WS_SUCCESS );
2185+ AssertIntEQ (wolfSSH_SetAlgoListKey (ssh , FPF_KEY_GOOD ), WS_SUCCESS );
2186+ AssertIntEQ (wolfSSH_SetAlgoListCipher (ssh ,
2187+ "aes128-cbc,aes256-gcm@openssh.com" ), WS_SUCCESS );
2188+ AssertIntEQ (wolfSSH_SetAlgoListMac (ssh ,
2189+ "hmac-sha1,hmac-sha2-256" ), WS_SUCCESS );
2190+ idx = 0 ;
2191+ payloadSz = BuildKexInitPayloadFull (
2192+ FPF_KEX_GOOD , FPF_KEY_GOOD ,
2193+ "aes128-cbc" , /* C2S enc: non-AEAD */
2194+ "aes256-gcm@openssh.com" , /* S2C enc: AEAD */
2195+ "hmac-sha1" , /* C2S MAC: negotiated */
2196+ "hmac-sha2-256" , /* S2C MAC: skipped (aeadMode) */
2197+ 0 , payload , (word32 )sizeof (payload ));
2198+ (void )wolfSSH_TestDoKexInit (ssh , payload , payloadSz , & idx );
2199+ AssertNotNull (ssh -> handshake );
2200+ AssertIntEQ (ssh -> handshake -> peerEncryptId , ID_AES128_CBC );
2201+ AssertIntEQ (ssh -> handshake -> encryptId , ID_AES256_GCM );
2202+ AssertIntEQ (ssh -> handshake -> peerAeadMode , 0 );
2203+ AssertIntEQ (ssh -> handshake -> aeadMode , 1 );
2204+ AssertIntEQ (ssh -> handshake -> peerMacId , ID_HMAC_SHA1 );
2205+ AssertIntEQ (ssh -> handshake -> macId , ID_NONE );
2206+ wolfSSH_free (ssh );
2207+ #endif /* !WOLFSSH_NO_AES_GCM */
2208+
2209+ wolfSSH_CTX_free (ctx );
2210+ }
2211+ #endif /* AES_CBC + AES_CTR + HMAC guards */
2212+
21102213#endif /* first_packet_follows coverage guard */
21112214
21122215
@@ -2155,6 +2258,13 @@ int main(int argc, char** argv)
21552258 && !defined(WOLFSSH_NO_CURVE25519_SHA256 ) \
21562259 && !defined(WOLFSSH_NO_RSA_SHA2_256 )
21572260 TestFirstPacketFollows ();
2261+ #endif
2262+ #if !defined(WOLFSSH_NO_ECDH_SHA2_NISTP256 ) && !defined(WOLFSSH_NO_RSA ) \
2263+ && !defined(WOLFSSH_NO_CURVE25519_SHA256 ) \
2264+ && !defined(WOLFSSH_NO_RSA_SHA2_256 ) \
2265+ && !defined(WOLFSSH_NO_AES_CBC ) && !defined(WOLFSSH_NO_AES_CTR ) \
2266+ && !defined(WOLFSSH_NO_HMAC_SHA1 ) && !defined(WOLFSSH_NO_HMAC_SHA2_256 )
2267+ TestIndependentAlgoNegotiation ();
21582268#endif
21592269 TestDisconnectSetsDisconnectError ();
21602270 TestClientBuffersIdempotent ();
0 commit comments