Skip to content

Commit 1739412

Browse files
author
Emma Stensland
committed
tests: cover the ML-DSA host public key cache
1 parent 68f870c commit 1739412

3 files changed

Lines changed: 334 additions & 35 deletions

File tree

‎tests/auth.c‎

Lines changed: 177 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@
3939
#if (!defined(WOLFSSH_NO_ED25519) && defined(HAVE_ED25519) && \
4040
defined(HAVE_ED25519_MAKE_KEY) && defined(HAVE_ED25519_KEY_EXPORT)) || \
4141
(!defined(WOLFSSH_NO_ECDSA) && !defined(WOLFSSH_NO_RSA) && \
42-
defined(HAVE_ECC_KEY_EXPORT))
42+
defined(HAVE_ECC_KEY_EXPORT)) || \
43+
(!defined(WOLFSSH_NO_MLDSA) && defined(WOLFSSH_CERTS) && \
44+
defined(WOLFSSL_CERT_GEN))
4345
#include <wolfssl/wolfcrypt/asn_public.h>
4446
#endif
4547
#ifdef NO_FILESYSTEM
@@ -596,6 +598,15 @@ static THREAD_RETURN WOLFSSH_THREAD pubkey_server_thread(void* args)
596598
}
597599
}
598600

601+
#ifdef WOLFSSH_CERTS
602+
if (serverArgs->hostCertBuf != NULL &&
603+
wolfSSH_CTX_UseCert_buffer(ctx, serverArgs->hostCertBuf,
604+
serverArgs->hostCertBufSz, WOLFSSH_FORMAT_ASN1) < 0) {
605+
serverArgs->return_code = WS_BAD_FILE_E;
606+
goto cleanup;
607+
}
608+
#endif /* WOLFSSH_CERTS */
609+
599610
clientFd = accept(listenFd, (struct sockaddr*)&clientAddr, &clientAddrSz);
600611
if (clientFd == WOLFSSH_SOCKET_INVALID) {
601612
serverArgs->return_code = WS_SOCKET_ERROR_E;
@@ -638,10 +649,15 @@ static int AcceptAnyServerHostKey(const byte* pubKey, word32 pubKeySz,
638649
* WS_FATAL_ERROR for a reject test
639650
* hostKeyBuf - server host key DER; NULL uses the default fixture key
640651
* via load_key() (what every existing caller wants)
641-
* hostKeyBufSz - size of hostKeyBuf; ignored when hostKeyBuf is NULL */
642-
static int run_pubkey_test_ex(PubkeyServerCtx* sCtx, PubkeyClientCtx* cCtx,
652+
* hostKeyBufSz - size of hostKeyBuf; ignored when hostKeyBuf is NULL
653+
* hostCertBuf - server host cert DER; NULL = none
654+
* rootCertBuf - client root CA for hostCertBuf
655+
* hostKeyAlgo - client host key algo list; NULL keeps the default */
656+
static int run_pubkey_test_host(PubkeyServerCtx* sCtx, PubkeyClientCtx* cCtx,
643657
int expect, const byte* hostKeyBuf,
644-
word32 hostKeyBufSz)
658+
word32 hostKeyBufSz, const byte* hostCertBuf,
659+
word32 hostCertBufSz, const byte* rootCertBuf,
660+
word32 rootCertBufSz, const char* hostKeyAlgo)
645661
{
646662
thread_args serverArgs;
647663
tcp_ready ready;
@@ -662,6 +678,8 @@ static int run_pubkey_test_ex(PubkeyServerCtx* sCtx, PubkeyClientCtx* cCtx,
662678
serverArgs.caCertSz = sCtx->caCertSz;
663679
serverArgs.hostKeyBuf = hostKeyBuf;
664680
serverArgs.hostKeyBufSz = hostKeyBufSz;
681+
serverArgs.hostCertBuf = hostCertBuf;
682+
serverArgs.hostCertBufSz = hostCertBufSz;
665683
InitTcpReady(serverArgs.signal);
666684

667685
ThreadStart(pubkey_server_thread, (void*)&serverArgs, &serThread);
@@ -671,6 +689,17 @@ static int run_pubkey_test_ex(PubkeyServerCtx* sCtx, PubkeyClientCtx* cCtx,
671689
AssertNotNull(clientCtx);
672690
wolfSSH_CTX_SetPublicKeyCheck(clientCtx, AcceptAnyServerHostKey);
673691
wolfSSH_SetUserAuth(clientCtx, clientPubkeyUserAuth);
692+
#ifdef WOLFSSH_CERTS
693+
if (rootCertBuf != NULL)
694+
AssertIntEQ(wolfSSH_CTX_AddRootCert_buffer(clientCtx, rootCertBuf,
695+
rootCertBufSz, WOLFSSH_FORMAT_ASN1), WS_SUCCESS);
696+
#else
697+
(void)rootCertBuf;
698+
(void)rootCertBufSz;
699+
#endif
700+
if (hostKeyAlgo != NULL)
701+
AssertIntEQ(wolfSSH_CTX_SetAlgoListKey(clientCtx, hostKeyAlgo),
702+
WS_SUCCESS);
674703

675704
clientSsh = wolfSSH_new(clientCtx);
676705
AssertNotNull(clientSsh);
@@ -708,6 +737,14 @@ static int run_pubkey_test_ex(PubkeyServerCtx* sCtx, PubkeyClientCtx* cCtx,
708737
return WS_SUCCESS;
709738
}
710739

740+
static int run_pubkey_test_ex(PubkeyServerCtx* sCtx, PubkeyClientCtx* cCtx,
741+
int expect, const byte* hostKeyBuf,
742+
word32 hostKeyBufSz)
743+
{
744+
return run_pubkey_test_host(sCtx, cCtx, expect, hostKeyBuf, hostKeyBufSz,
745+
NULL, 0, NULL, 0, NULL);
746+
}
747+
711748
/* Existing callers all want the default fixture host key, and every one of
712749
* them is an RSA or ECDSA test. */
713750
#if !defined(WOLFSSH_NO_RSA) || !defined(WOLFSSH_NO_ECDSA)
@@ -1470,6 +1507,37 @@ static void test_pubkey_load_mldsa_privonly_hostkey(void)
14701507
}
14711508

14721509
#if defined(WOLFSSH_HAVE_MLDSA_DERIVE_PUB) && !defined(WOLFSSH_NO_ECDSA)
1510+
/* Authorize hansel's ECC key on the server and have the client present
1511+
* it. cCtx points into pubBuf and privBuf. */
1512+
static void load_hansel_ecc_client(PubkeyServerCtx* sCtx,
1513+
PubkeyClientCtx* cCtx, byte* pubBuf, word32 pubBufSz,
1514+
byte* privBuf, word32 privBufSz)
1515+
{
1516+
const byte* pubKeyType = NULL;
1517+
word32 pubKeyTypeSz = 0;
1518+
const byte* privKeyType = NULL;
1519+
word32 privKeyTypeSz = 0;
1520+
1521+
AssertIntEQ(wolfSSH_ReadKey_buffer((const byte*)hanselPublicEcc,
1522+
(word32)WSTRLEN(hanselPublicEcc), WOLFSSH_FORMAT_SSH,
1523+
&pubBuf, &pubBufSz, &pubKeyType, &pubKeyTypeSz, NULL),
1524+
WS_SUCCESS);
1525+
1526+
AssertIntEQ(wc_Sha256Hash(pubBuf, pubBufSz, sCtx->hash), 0);
1527+
1528+
AssertIntEQ(wolfSSH_ReadKey_buffer(hanselPrivateEcc, hanselPrivateEccSz,
1529+
WOLFSSH_FORMAT_ASN1,
1530+
&privBuf, &privBufSz, &privKeyType, &privKeyTypeSz, NULL),
1531+
WS_SUCCESS);
1532+
1533+
cCtx->publicKeyType = pubKeyType;
1534+
cCtx->publicKeyTypeSz = pubKeyTypeSz;
1535+
cCtx->publicKey = pubBuf;
1536+
cCtx->publicKeySz = pubBufSz;
1537+
cCtx->privateKey = privBuf;
1538+
cCtx->privateKeySz = privBufSz;
1539+
}
1540+
14731541
/* ML-DSA counterpart to test_pubkey_auth_ed25519_privonly_hostkey: a real
14741542
* handshake with a private-only host key, exercising the derived public
14751543
* key all the way through SendKexGetSigningKey. */
@@ -1478,39 +1546,16 @@ static void test_pubkey_auth_mldsa_privonly_hostkey(void)
14781546
PubkeyServerCtx sCtx = {0};
14791547
PubkeyClientCtx cCtx;
14801548
byte pubKeyBuf[512];
1481-
byte* p = pubKeyBuf;
1482-
word32 pubKeySz = sizeof(pubKeyBuf);
1483-
const byte* pubKeyType = NULL;
1484-
word32 pubKeyTypeSz = 0;
14851549
byte privKeyBuf[1300];
1486-
byte* privKeyPtr = privKeyBuf;
1487-
word32 privKeySz = sizeof(privKeyBuf);
1488-
const byte* privKeyType = NULL;
1489-
word32 privKeyTypeSz = 0;
14901550
byte* hostKeyDer;
14911551
int hostKeyDerSz;
14921552

14931553
printf("Testing ML-DSA private-only host key at KEX (derived pubkey)\n");
14941554

14951555
hostKeyDer = mldsa_privonly_hostkey_der(&hostKeyDerSz);
14961556

1497-
AssertIntEQ(wolfSSH_ReadKey_buffer((const byte*)hanselPublicEcc,
1498-
(word32)WSTRLEN(hanselPublicEcc), WOLFSSH_FORMAT_SSH,
1499-
&p, &pubKeySz, &pubKeyType, &pubKeyTypeSz, NULL), WS_SUCCESS);
1500-
1501-
AssertIntEQ(wc_Sha256Hash(pubKeyBuf, pubKeySz, sCtx.hash), 0);
1502-
1503-
AssertIntEQ(wolfSSH_ReadKey_buffer(hanselPrivateEcc, hanselPrivateEccSz,
1504-
WOLFSSH_FORMAT_ASN1,
1505-
&privKeyPtr, &privKeySz, &privKeyType, &privKeyTypeSz, NULL),
1506-
WS_SUCCESS);
1507-
1508-
cCtx.publicKeyType = pubKeyType;
1509-
cCtx.publicKeyTypeSz = pubKeyTypeSz;
1510-
cCtx.publicKey = pubKeyBuf;
1511-
cCtx.publicKeySz = pubKeySz;
1512-
cCtx.privateKey = privKeyBuf;
1513-
cCtx.privateKeySz = privKeySz;
1557+
load_hansel_ecc_client(&sCtx, &cCtx, pubKeyBuf, sizeof(pubKeyBuf),
1558+
privKeyBuf, sizeof(privKeyBuf));
15141559

15151560
run_pubkey_test_ex(&sCtx, &cCtx, WS_SUCCESS, hostKeyDer,
15161561
(word32)hostKeyDerSz);
@@ -1521,6 +1566,104 @@ static void test_pubkey_auth_mldsa_privonly_hostkey(void)
15211566
}
15221567
#endif /* WOLFSSH_HAVE_MLDSA_DERIVE_PUB && !WOLFSSH_NO_ECDSA */
15231568

1569+
/* A generated cert can't meet the FPKI profile the client enforces. */
1570+
#if defined(WOLFSSH_HAVE_MLDSA_DERIVE_PUB) && !defined(WOLFSSH_NO_ECDSA) && \
1571+
defined(WOLFSSH_CERTS) && defined(WOLFSSL_CERT_GEN) && \
1572+
defined(WOLFSSH_NO_FPKI)
1573+
/* Make an ML-DSA-44 cert for key, signed by signer (self-signed when
1574+
* issuerDer is NULL). Caller frees. */
1575+
static byte* mldsa44_cert_der(MlDsaKey* key, MlDsaKey* signer,
1576+
const byte* issuerDer, int issuerDerSz, WC_RNG* rng, int* derSz)
1577+
{
1578+
Cert cert;
1579+
byte* der;
1580+
int sz;
1581+
1582+
der = (byte*)WMALLOC(16384, NULL, 0);
1583+
AssertNotNull(der);
1584+
wc_InitCert(&cert);
1585+
WSTRNCPY(cert.subject.commonName, issuerDer == NULL ?
1586+
"wolfSSH-mldsa-ca" : "wolfSSH-mldsa-host", CTC_NAME_SIZE - 1);
1587+
WSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE - 1);
1588+
cert.daysValid = 365;
1589+
cert.sigType = CTC_ML_DSA_44;
1590+
if (issuerDer == NULL) {
1591+
cert.selfSigned = 1;
1592+
cert.isCA = 1;
1593+
}
1594+
else {
1595+
AssertIntEQ(wc_SetIssuerBuffer(&cert, issuerDer, issuerDerSz), 0);
1596+
}
1597+
sz = wc_MakeCert_ex(&cert, der, 16384, ML_DSA_44_TYPE, key, rng);
1598+
AssertIntGT(sz, 0);
1599+
sz = wc_SignCert_ex(sz, CTC_ML_DSA_44, der, 16384, ML_DSA_44_TYPE,
1600+
signer, rng);
1601+
AssertIntGT(sz, 0);
1602+
1603+
*derSz = sz;
1604+
return der;
1605+
}
1606+
1607+
/* x509v3-ssh-mldsa-44 handshake with a private-only host key: the cert
1608+
* path signs without the cached public key and sends the certificate. */
1609+
static void test_pubkey_auth_mldsa_privonly_hostcert(void)
1610+
{
1611+
PubkeyServerCtx sCtx = {0};
1612+
PubkeyClientCtx cCtx;
1613+
byte pubKeyBuf[512];
1614+
byte privKeyBuf[1300];
1615+
MlDsaKey caKey;
1616+
MlDsaKey hostKey;
1617+
WC_RNG rng;
1618+
byte* caDer;
1619+
int caDerSz;
1620+
byte* certDer;
1621+
int certDerSz;
1622+
byte* hostKeyDer;
1623+
int hostKeyDerSz;
1624+
1625+
printf("Testing ML-DSA private-only host key with x509v3 cert\n");
1626+
1627+
AssertIntEQ(wc_InitRng(&rng), 0);
1628+
WMEMSET(&caKey, 0, sizeof(caKey));
1629+
WMEMSET(&hostKey, 0, sizeof(hostKey));
1630+
AssertIntEQ(wc_MlDsaKey_Init(&caKey, NULL, INVALID_DEVID), 0);
1631+
AssertIntEQ(wc_MlDsaKey_SetParams(&caKey, WC_ML_DSA_44), 0);
1632+
AssertIntEQ(wc_MlDsaKey_MakeKey(&caKey, &rng), 0);
1633+
AssertIntEQ(wc_MlDsaKey_Init(&hostKey, NULL, INVALID_DEVID), 0);
1634+
AssertIntEQ(wc_MlDsaKey_SetParams(&hostKey, WC_ML_DSA_44), 0);
1635+
AssertIntEQ(wc_MlDsaKey_MakeKey(&hostKey, &rng), 0);
1636+
1637+
/* The client refuses a CA as the leaf, so issue the host cert. */
1638+
caDer = mldsa44_cert_der(&caKey, &caKey, NULL, 0, &rng, &caDerSz);
1639+
certDer = mldsa44_cert_der(&hostKey, &caKey, caDer, caDerSz, &rng,
1640+
&certDerSz);
1641+
wc_MlDsaKey_Free(&caKey);
1642+
wc_FreeRng(&rng);
1643+
1644+
hostKeyDer = (byte*)WMALLOC(WC_MLDSA_44_PRV_KEY_DER_SIZE, NULL, 0);
1645+
AssertNotNull(hostKeyDer);
1646+
hostKeyDerSz = wc_MlDsaKey_PrivateKeyToDer(&hostKey, hostKeyDer,
1647+
WC_MLDSA_44_PRV_KEY_DER_SIZE);
1648+
wc_MlDsaKey_Free(&hostKey);
1649+
AssertIntGT(hostKeyDerSz, 0);
1650+
1651+
load_hansel_ecc_client(&sCtx, &cCtx, pubKeyBuf, sizeof(pubKeyBuf),
1652+
privKeyBuf, sizeof(privKeyBuf));
1653+
1654+
run_pubkey_test_host(&sCtx, &cCtx, WS_SUCCESS, hostKeyDer,
1655+
(word32)hostKeyDerSz, certDer, (word32)certDerSz,
1656+
caDer, (word32)caDerSz, "x509v3-ssh-mldsa-44");
1657+
1658+
WMEMSET(privKeyBuf, 0, sizeof(privKeyBuf));
1659+
WMEMSET(hostKeyDer, 0, hostKeyDerSz);
1660+
WFREE(hostKeyDer, NULL, 0);
1661+
WFREE(certDer, NULL, 0);
1662+
WFREE(caDer, NULL, 0);
1663+
}
1664+
#endif /* WOLFSSH_HAVE_MLDSA_DERIVE_PUB && !WOLFSSH_NO_ECDSA &&
1665+
* WOLFSSH_CERTS && WOLFSSL_CERT_GEN && WOLFSSH_NO_FPKI */
1666+
15241667
#if defined(WOLFSSH_HAVE_MLDSA_DERIVE_PUB) && \
15251668
defined(WOLFSSL_MLDSA_PUBLIC_KEY) && \
15261669
(!defined(WOLFSSH_NO_ECDSA) || !defined(WOLFSSH_NO_RSA))
@@ -2650,6 +2793,11 @@ int wolfSSH_AuthTest(int argc, char** argv)
26502793
#if defined(WOLFSSH_HAVE_MLDSA_DERIVE_PUB) && !defined(WOLFSSH_NO_ECDSA)
26512794
test_pubkey_auth_mldsa_privonly_hostkey();
26522795
#endif
2796+
#if defined(WOLFSSH_HAVE_MLDSA_DERIVE_PUB) && \
2797+
!defined(WOLFSSH_NO_ECDSA) && defined(WOLFSSH_CERTS) && \
2798+
defined(WOLFSSL_CERT_GEN) && defined(WOLFSSH_NO_FPKI)
2799+
test_pubkey_auth_mldsa_privonly_hostcert();
2800+
#endif
26532801
#if defined(WOLFSSH_HAVE_MLDSA_DERIVE_PUB) && \
26542802
defined(WOLFSSL_MLDSA_PUBLIC_KEY) && \
26552803
(!defined(WOLFSSH_NO_ECDSA) || !defined(WOLFSSH_NO_RSA))

‎tests/auth.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ typedef struct thread_args {
3434
word32 caCertSz;
3535
const byte* hostKeyBuf; /* server host key; NULL = use load_key() */
3636
word32 hostKeyBufSz;
37+
const byte* hostCertBuf; /* server host cert DER; NULL = none */
38+
word32 hostCertBufSz;
3739
} thread_args;
3840

3941
#endif /* _WOLFSSH_TESTS_AUTH_H_ */

0 commit comments

Comments
 (0)