@@ -1423,23 +1423,13 @@ static void test_pubkey_auth_wrong_key(void)
14231423#if !defined(WOLFSSH_NO_MLDSA ) && !defined(WOLFSSH_NO_MLDSA44 ) && \
14241424 defined(WOLFSSL_MLDSA_PRIVATE_KEY ) && !defined(WOLFSSL_MLDSA_NO_ASN1 ) && \
14251425 !defined(WOLFSSL_MLDSA_NO_MAKE_KEY )
1426- /* Covers the private-only ML-DSA host key through the real load path
1427- * (wolfSSH_CTX_UsePrivateKey_buffer), not just IdentifyAsn1Key called
1428- * directly as in the unit test. */
1429- static void test_pubkey_load_mldsa_privonly_hostkey (void )
1426+ /* Generate a private-only ML-DSA-44 host key in PKCS#8 DER. Caller frees. */
1427+ static byte * mldsa_privonly_hostkey_der (int * derSz )
14301428{
1431- WOLFSSH_CTX * ctx ;
14321429 MlDsaKey mlKey ;
14331430 WC_RNG mlRng ;
14341431 byte * mlDer ;
14351432 int mlDerSz ;
1436- #ifdef WOLFSSH_HAVE_MLDSA_DERIVE_PUB
1437- const int expect = WS_SUCCESS ;
1438- #else
1439- const int expect = WS_CRYPTO_FAILED ;
1440- #endif
1441-
1442- printf ("Testing ML-DSA private-only host key load\n" );
14431433
14441434 WMEMSET (& mlKey , 0 , sizeof (mlKey ));
14451435 AssertIntEQ (wc_MlDsaKey_Init (& mlKey , NULL , INVALID_DEVID ), 0 );
@@ -1455,16 +1445,167 @@ static void test_pubkey_load_mldsa_privonly_hostkey(void)
14551445 wc_MlDsaKey_Free (& mlKey );
14561446 AssertIntGT (mlDerSz , 0 );
14571447
1448+ * derSz = mlDerSz ;
1449+ return mlDer ;
1450+ }
1451+
1452+ /* Covers the private-only ML-DSA host key through the real load path
1453+ * (wolfSSH_CTX_UsePrivateKey_buffer), not just IdentifyAsn1Key called
1454+ * directly as in the unit test. */
1455+ static void test_pubkey_load_mldsa_privonly_hostkey (void )
1456+ {
1457+ WOLFSSH_CTX * ctx ;
1458+ byte * mlDer ;
1459+ int mlDerSz ;
1460+ #ifdef WOLFSSH_HAVE_MLDSA_DERIVE_PUB
1461+ const int expect = WS_SUCCESS ;
1462+ #else
1463+ const int expect = WS_CRYPTO_FAILED ;
1464+ #endif
1465+
1466+ printf ("Testing ML-DSA private-only host key load\n" );
1467+
1468+ mlDer = mldsa_privonly_hostkey_der (& mlDerSz );
1469+
14581470 ctx = wolfSSH_CTX_new (WOLFSSH_ENDPOINT_SERVER , NULL );
14591471 AssertNotNull (ctx );
14601472 AssertIntEQ (wolfSSH_CTX_UsePrivateKey_buffer (ctx , mlDer , (word32 )mlDerSz ,
14611473 WOLFSSH_FORMAT_ASN1 ), expect );
14621474 wolfSSH_CTX_free (ctx );
14631475
1476+ WMEMSET (mlDer , 0 , mlDerSz );
14641477 WFREE (mlDer , NULL , 0 );
14651478}
1466- #endif /* !WOLFSSH_NO_MLDSA && !WOLFSSH_NO_MLDSA44 && WOLFSSL_MLDSA_PRIVATE_KEY
1467- * && !WOLFSSL_MLDSA_NO_ASN1 && !WOLFSSL_MLDSA_NO_MAKE_KEY */
1479+
1480+ #if defined(WOLFSSH_HAVE_MLDSA_DERIVE_PUB ) && !defined(WOLFSSH_NO_ECDSA )
1481+ /* ML-DSA counterpart to test_pubkey_auth_ed25519_privonly_hostkey: a real
1482+ * handshake with a private-only host key, exercising the derived public
1483+ * key all the way through SendKexGetSigningKey. */
1484+ static void test_pubkey_auth_mldsa_privonly_hostkey (void )
1485+ {
1486+ PubkeyServerCtx sCtx = {0 };
1487+ PubkeyClientCtx cCtx ;
1488+ byte pubKeyBuf [512 ];
1489+ byte * p = pubKeyBuf ;
1490+ word32 pubKeySz = sizeof (pubKeyBuf );
1491+ const byte * pubKeyType = NULL ;
1492+ word32 pubKeyTypeSz = 0 ;
1493+ byte privKeyBuf [1300 ];
1494+ byte * privKeyPtr = privKeyBuf ;
1495+ word32 privKeySz = sizeof (privKeyBuf );
1496+ const byte * privKeyType = NULL ;
1497+ word32 privKeyTypeSz = 0 ;
1498+ byte * hostKeyDer ;
1499+ int hostKeyDerSz ;
1500+
1501+ printf ("Testing ML-DSA private-only host key at KEX (derived pubkey)\n" );
1502+
1503+ hostKeyDer = mldsa_privonly_hostkey_der (& hostKeyDerSz );
1504+
1505+ AssertIntEQ (wolfSSH_ReadKey_buffer ((const byte * )hanselPublicEcc ,
1506+ (word32 )WSTRLEN (hanselPublicEcc ), WOLFSSH_FORMAT_SSH ,
1507+ & p , & pubKeySz , & pubKeyType , & pubKeyTypeSz , NULL ), WS_SUCCESS );
1508+
1509+ AssertIntEQ (wc_Sha256Hash (pubKeyBuf , pubKeySz , sCtx .hash ), 0 );
1510+
1511+ AssertIntEQ (wolfSSH_ReadKey_buffer (hanselPrivateEcc , hanselPrivateEccSz ,
1512+ WOLFSSH_FORMAT_ASN1 ,
1513+ & privKeyPtr , & privKeySz , & privKeyType , & privKeyTypeSz , NULL ),
1514+ WS_SUCCESS );
1515+
1516+ cCtx .publicKeyType = pubKeyType ;
1517+ cCtx .publicKeyTypeSz = pubKeyTypeSz ;
1518+ cCtx .publicKey = pubKeyBuf ;
1519+ cCtx .publicKeySz = pubKeySz ;
1520+ cCtx .privateKey = privKeyBuf ;
1521+ cCtx .privateKeySz = privKeySz ;
1522+
1523+ run_pubkey_test_ex (& sCtx , & cCtx , WS_SUCCESS , hostKeyDer ,
1524+ (word32 )hostKeyDerSz );
1525+
1526+ WMEMSET (privKeyBuf , 0 , sizeof (privKeyBuf ));
1527+ WMEMSET (hostKeyDer , 0 , hostKeyDerSz );
1528+ WFREE (hostKeyDer , NULL , 0 );
1529+ }
1530+ #endif /* WOLFSSH_HAVE_MLDSA_DERIVE_PUB && !WOLFSSH_NO_ECDSA */
1531+
1532+ #if defined(WOLFSSH_HAVE_MLDSA_DERIVE_PUB ) && \
1533+ defined(WOLFSSL_MLDSA_PUBLIC_KEY ) && \
1534+ (!defined(WOLFSSH_NO_ECDSA ) || !defined(WOLFSSH_NO_RSA ))
1535+ /* User auth with a private-only ML-DSA-44 client key, read the way an
1536+ * application would, through wolfSSH_ReadKey_buffer(). */
1537+ static void test_pubkey_auth_mldsa_privonly_userkey (void )
1538+ {
1539+ PubkeyServerCtx sCtx = {0 };
1540+ PubkeyClientCtx cCtx ;
1541+ MlDsaKey mlKey ;
1542+ WC_RNG rng ;
1543+ byte * pubDer ;
1544+ int pubDerSz ;
1545+ byte * privDer ;
1546+ int privDerSz ;
1547+ byte * pubBlob = NULL ;
1548+ word32 pubBlobSz = 0 ;
1549+ const byte * pubType = NULL ;
1550+ word32 pubTypeSz = 0 ;
1551+ byte * privKey = NULL ;
1552+ word32 privKeySz = 0 ;
1553+ const byte * privType = NULL ;
1554+ word32 privTypeSz = 0 ;
1555+
1556+ printf ("Testing ML-DSA private-only client key for user auth\n" );
1557+
1558+ WMEMSET (& mlKey , 0 , sizeof (mlKey ));
1559+ AssertIntEQ (wc_MlDsaKey_Init (& mlKey , NULL , INVALID_DEVID ), 0 );
1560+ AssertIntEQ (wc_MlDsaKey_SetParams (& mlKey , WC_ML_DSA_44 ), 0 );
1561+ AssertIntEQ (wc_InitRng (& rng ), 0 );
1562+ AssertIntEQ (wc_MlDsaKey_MakeKey (& mlKey , & rng ), 0 );
1563+ wc_FreeRng (& rng );
1564+
1565+ pubDer = (byte * )WMALLOC (WC_MLDSA_44_PUB_KEY_DER_SIZE , NULL , 0 );
1566+ AssertNotNull (pubDer );
1567+ pubDerSz = wc_MlDsaKey_PublicKeyToDer (& mlKey , pubDer ,
1568+ WC_MLDSA_44_PUB_KEY_DER_SIZE , 1 );
1569+ AssertIntGT (pubDerSz , 0 );
1570+ privDer = (byte * )WMALLOC (WC_MLDSA_44_PRV_KEY_DER_SIZE , NULL , 0 );
1571+ AssertNotNull (privDer );
1572+ privDerSz = wc_MlDsaKey_PrivateKeyToDer (& mlKey , privDer ,
1573+ WC_MLDSA_44_PRV_KEY_DER_SIZE );
1574+ wc_MlDsaKey_Free (& mlKey );
1575+ AssertIntGT (privDerSz , 0 );
1576+
1577+ AssertIntEQ (wolfSSH_ReadKey_buffer_ex (pubDer , (word32 )pubDerSz ,
1578+ WOLFSSH_FORMAT_ASN1 , & pubBlob , & pubBlobSz , & pubType , & pubTypeSz ,
1579+ 0 , NULL ), WS_SUCCESS );
1580+ AssertIntEQ (wolfSSH_ReadKey_buffer (privDer , (word32 )privDerSz ,
1581+ WOLFSSH_FORMAT_ASN1 , & privKey , & privKeySz , & privType ,
1582+ & privTypeSz , NULL ), WS_SUCCESS );
1583+ AssertIntEQ (privTypeSz , pubTypeSz );
1584+ AssertIntEQ (WMEMCMP (privType , pubType , pubTypeSz ), 0 );
1585+
1586+ AssertIntEQ (wc_Sha256Hash (pubBlob , pubBlobSz , sCtx .hash ), 0 );
1587+
1588+ cCtx .publicKeyType = pubType ;
1589+ cCtx .publicKeyTypeSz = pubTypeSz ;
1590+ cCtx .publicKey = pubBlob ;
1591+ cCtx .publicKeySz = pubBlobSz ;
1592+ cCtx .privateKey = privKey ;
1593+ cCtx .privateKeySz = privKeySz ;
1594+
1595+ run_pubkey_test_ex (& sCtx , & cCtx , WS_SUCCESS , NULL , 0 );
1596+
1597+ WMEMSET (privKey , 0 , privKeySz );
1598+ WFREE (privKey , NULL , DYNTYPE_PRIVKEY );
1599+ WFREE (pubBlob , NULL , DYNTYPE_PRIVKEY );
1600+ WMEMSET (privDer , 0 , privDerSz );
1601+ WFREE (privDer , NULL , 0 );
1602+ WFREE (pubDer , NULL , 0 );
1603+ }
1604+ #endif /* WOLFSSH_HAVE_MLDSA_DERIVE_PUB && WOLFSSL_MLDSA_PUBLIC_KEY &&
1605+ * (!WOLFSSH_NO_ECDSA || !WOLFSSH_NO_RSA) */
1606+ #endif /* !WOLFSSH_NO_MLDSA && !WOLFSSH_NO_MLDSA44
1607+ * && WOLFSSL_MLDSA_PRIVATE_KEY && !WOLFSSL_MLDSA_NO_ASN1
1608+ * && !WOLFSSL_MLDSA_NO_MAKE_KEY */
14681609
14691610#if !defined(WOLFSSH_NO_ED25519 ) && defined(HAVE_ED25519 ) && \
14701611 defined(HAVE_ED25519_MAKE_KEY ) && defined(HAVE_ED25519_KEY_EXPORT )
@@ -1599,6 +1740,8 @@ static void test_pubkey_auth_ecdsa_privonly_hostkey(void)
15991740 run_pubkey_test_ex (& sCtx , & cCtx , WS_SUCCESS , hostKeyDer ,
16001741 (word32 )hostKeyDerSz );
16011742
1743+ WMEMSET (privKeyBuf , 0 , sizeof (privKeyBuf ));
1744+ WMEMSET (hostKeyDer , 0 , hostKeyDerSz );
16021745 WFREE (hostKeyDer , NULL , 0 );
16031746}
16041747#endif /* !WOLFSSH_NO_ECDSA && !WOLFSSH_NO_RSA */
@@ -2514,6 +2657,14 @@ int wolfSSH_AuthTest(int argc, char** argv)
25142657 defined(WOLFSSL_MLDSA_PRIVATE_KEY ) && !defined(WOLFSSL_MLDSA_NO_ASN1 ) && \
25152658 !defined(WOLFSSL_MLDSA_NO_MAKE_KEY )
25162659 test_pubkey_load_mldsa_privonly_hostkey ();
2660+ #if defined(WOLFSSH_HAVE_MLDSA_DERIVE_PUB ) && !defined(WOLFSSH_NO_ECDSA )
2661+ test_pubkey_auth_mldsa_privonly_hostkey ();
2662+ #endif
2663+ #if defined(WOLFSSH_HAVE_MLDSA_DERIVE_PUB ) && \
2664+ defined(WOLFSSL_MLDSA_PUBLIC_KEY ) && \
2665+ (!defined(WOLFSSH_NO_ECDSA ) || !defined(WOLFSSH_NO_RSA ))
2666+ test_pubkey_auth_mldsa_privonly_userkey ();
2667+ #endif
25172668#endif
25182669#endif /* !NO_SHA256 */
25192670
0 commit comments