@@ -795,6 +795,79 @@ static int test_mlkem_decapsulate(void)
795795 TEST_CTX_FREE ();
796796 TEST_PASS ();
797797}
798+
799+ /* Reconnect on a reused context switching key-exchange method must free the
800+ * prior ephemeral key while ctx->kexType still names its union member (no
801+ * type-confused free). Run under valgrind in CI to catch a mismatched free. */
802+ static int test_kex_reconnect_method_switch (void )
803+ {
804+ byte ek [WOLFSPDM_MLKEM768_EK_SIZE ];
805+ byte rsp [72 ];
806+ word32 ekSz = sizeof (ek );
807+ word32 len ;
808+ TEST_CTX_SETUP ();
809+
810+ printf ("test_kex_reconnect_method_switch...\n" );
811+ ctx -> spdmVersion = SPDM_VERSION_14 ;
812+
813+ /* Round 1 leaves a live ML-KEM ephemeral key. */
814+ ctx -> kemAlgSel = SPDM_KEM_ALGO_ML_KEM_768 ;
815+ ASSERT_SUCCESS (wolfSPDM_GenerateMlKemKey (ctx , ek , & ekSz ));
816+ ASSERT_EQ (ctx -> flags .ephemeralKeyInit , 1 , "ML-KEM key live" );
817+ ASSERT_EQ (ctx -> kexType , WOLFSPDM_KEX_MLKEM , "kexType MLKEM" );
818+
819+ /* Reconnect negotiates ECDHE: ParseAlgorithms frees the live ML-KEM key
820+ * (still matching kexType) before flipping to ECDHE. */
821+ len = build_algorithms_14_kem (rsp , SPDM_DHE_ALGO_SECP384R1 , 0 );
822+ ASSERT_SUCCESS (wolfSPDM_ParseAlgorithms (ctx , rsp , len ));
823+ ASSERT_EQ (ctx -> kexType , WOLFSPDM_KEX_ECDHE , "switched to ECDHE" );
824+ ASSERT_EQ (ctx -> flags .ephemeralKeyInit , 0 , "stale ML-KEM key freed" );
825+
826+ /* And the reverse: a live ECDHE key, then a reconnect negotiating ML-KEM. */
827+ ASSERT_SUCCESS (wolfSPDM_GenerateEphemeralKey (ctx ));
828+ ASSERT_EQ (ctx -> flags .ephemeralKeyInit , 1 , "ECDHE key live" );
829+ len = build_algorithms_14_kem (rsp , 0 , SPDM_KEM_ALGO_ML_KEM_768 );
830+ ASSERT_SUCCESS (wolfSPDM_ParseAlgorithms (ctx , rsp , len ));
831+ ASSERT_EQ (ctx -> kexType , WOLFSPDM_KEX_MLKEM , "switched to ML-KEM" );
832+ ASSERT_EQ (ctx -> flags .ephemeralKeyInit , 0 , "stale ECDHE key freed" );
833+
834+ TEST_CTX_FREE ();
835+ TEST_PASS ();
836+ }
837+
838+ /* KEY_EXCHANGE with ML-KEM places the encapsulation key ek as ExchangeData at
839+ * offset 40; confirm it decodes as a valid ML-KEM-768 public key. */
840+ static int test_build_key_exchange_mlkem (void )
841+ {
842+ byte buf [WOLFSPDM_KEX_REQ_BUF ];
843+ word32 bufSz = sizeof (buf );
844+ MlKemKey check ;
845+ int checkInit = 0 ;
846+ TEST_CTX_SETUP ();
847+
848+ printf ("test_build_key_exchange_mlkem...\n" );
849+ ctx -> spdmVersion = SPDM_VERSION_14 ;
850+ ctx -> kexType = WOLFSPDM_KEX_MLKEM ;
851+ ctx -> kemAlgSel = SPDM_KEM_ALGO_ML_KEM_768 ;
852+
853+ ASSERT_SUCCESS (wolfSPDM_BuildKeyExchange (ctx , buf , & bufSz ));
854+ ASSERT_EQ (buf [1 ], SPDM_KEY_EXCHANGE , "KEY_EXCHANGE code" );
855+ /* offset 40 = 4 hdr + 2 sessionId + 2 policy/rsvd + 32 random. */
856+ ASSERT_EQ (wc_MlKemKey_Init (& check , WC_ML_KEM_768 , NULL , INVALID_DEVID ), 0 ,
857+ "MlKemKey_Init" );
858+ checkInit = 1 ;
859+ ASSERT_EQ (wc_MlKemKey_DecodePublicKey (& check , & buf [40 ],
860+ WOLFSPDM_MLKEM768_EK_SIZE ), 0 , "ek decodes at offset 40" );
861+ /* 40 fixed + 1184 ek + 22 OpaqueData block. */
862+ ASSERT_EQ (bufSz , 40u + WOLFSPDM_MLKEM768_EK_SIZE + 22u ,
863+ "ML-KEM KEY_EXCHANGE total size" );
864+
865+ if (checkInit ) {
866+ wc_MlKemKey_Free (& check );
867+ }
868+ TEST_CTX_FREE ();
869+ TEST_PASS ();
870+ }
798871#endif /* WOLFSPDM_HAVE_MLKEM */
799872
800873#ifdef WOLFSPDM_HAVE_CHUNK
@@ -2824,6 +2897,8 @@ int main(void)
28242897 test_negotiate_algorithms_kem_build ();
28252898 test_parse_algorithms_kem_select ();
28262899 test_mlkem_decapsulate ();
2900+ test_kex_reconnect_method_switch ();
2901+ test_build_key_exchange_mlkem ();
28272902#endif
28282903#ifdef WOLFSPDM_HAVE_CHUNK
28292904 test_chunk_reassemble ();
0 commit comments