@@ -4018,3 +4018,63 @@ int test_wc_mlkem_decap_fo_reject(void)
40184018 return EXPECT_RESULT ();
40194019} /* END test_wc_mlkem_decap_fo_reject */
40204020
4021+ /*
4022+ * Exercise the public-key-hash integrity check in wc_MlKemKey_DecodePrivateKey.
4023+ * Encode a valid private key, flip a byte inside the embedded H(pk) field,
4024+ * then re-decode and assert MLKEM_PUB_HASH_E is returned.
4025+ *
4026+ * Private key layout per FIPS 203: dk_PKE || ek || H(ek) || z
4027+ * The 32-byte hash H(ek) sits at offset (privLen - 2 * WC_ML_KEM_SYM_SZ).
4028+ */
4029+ int test_wc_mlkem_decode_privkey_bad_pubhash (void )
4030+ {
4031+ EXPECT_DECLS ;
4032+ #if defined(WOLFSSL_HAVE_MLKEM ) && defined(WOLFSSL_WC_MLKEM ) && \
4033+ !defined(WOLFSSL_NO_ML_KEM ) && !defined(WOLFSSL_MLKEM_NO_MAKE_KEY )
4034+ MlKemKey * key = NULL ;
4035+ WC_RNG rng ;
4036+ byte priv [WC_ML_KEM_MAX_PRIVATE_KEY_SIZE ];
4037+ word32 privLen = 0 ;
4038+ #ifndef WOLFSSL_NO_ML_KEM_768
4039+ const int mlkemType = WC_ML_KEM_768 ;
4040+ #elif !defined(WOLFSSL_NO_ML_KEM_512 )
4041+ const int mlkemType = WC_ML_KEM_512 ;
4042+ #else
4043+ const int mlkemType = WC_ML_KEM_1024 ;
4044+ #endif
4045+
4046+ XMEMSET (& rng , 0 , sizeof (rng ));
4047+ XMEMSET (priv , 0 , sizeof (priv ));
4048+
4049+ key = (MlKemKey * )XMALLOC (sizeof (* key ), NULL , DYNAMIC_TYPE_TMP_BUFFER );
4050+ ExpectNotNull (key );
4051+ ExpectIntEQ (wc_InitRng (& rng ), 0 );
4052+
4053+ ExpectIntEQ (wc_MlKemKey_Init (key , mlkemType , NULL , INVALID_DEVID ), 0 );
4054+ ExpectIntEQ (wc_MlKemKey_MakeKey (key , & rng ), 0 );
4055+ ExpectIntEQ (wc_MlKemKey_PrivateKeySize (key , & privLen ), 0 );
4056+ ExpectTrue (privLen > (word32 )(2 * WC_ML_KEM_SYM_SZ ));
4057+ ExpectIntEQ (wc_MlKemKey_EncodePrivateKey (key , priv , privLen ), 0 );
4058+
4059+ /* Baseline: untampered blob decodes cleanly. */
4060+ wc_MlKemKey_Free (key );
4061+ ExpectIntEQ (wc_MlKemKey_Init (key , mlkemType , NULL , INVALID_DEVID ), 0 );
4062+ ExpectIntEQ (wc_MlKemKey_DecodePrivateKey (key , priv , privLen ), 0 );
4063+ wc_MlKemKey_Free (key );
4064+
4065+ /* Flip one byte inside H(ek) - the 32 bytes preceding z. */
4066+ if (privLen > (word32 )(2 * WC_ML_KEM_SYM_SZ )) {
4067+ priv [privLen - 2 * WC_ML_KEM_SYM_SZ ] ^= 0x01 ;
4068+ }
4069+
4070+ ExpectIntEQ (wc_MlKemKey_Init (key , mlkemType , NULL , INVALID_DEVID ), 0 );
4071+ ExpectIntEQ (wc_MlKemKey_DecodePrivateKey (key , priv , privLen ),
4072+ WC_NO_ERR_TRACE (MLKEM_PUB_HASH_E ));
4073+ wc_MlKemKey_Free (key );
4074+
4075+ DoExpectIntEQ (wc_FreeRng (& rng ), 0 );
4076+ XFREE (key , NULL , DYNAMIC_TYPE_TMP_BUFFER );
4077+ #endif
4078+ return EXPECT_RESULT ();
4079+ } /* END test_wc_mlkem_decode_privkey_bad_pubhash */
4080+
0 commit comments