Skip to content

Commit f2bb22a

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

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
@@ -38,7 +38,9 @@
3838
#endif
3939
#if (!defined(WOLFSSH_NO_ED25519) && defined(HAVE_ED25519) && \
4040
defined(HAVE_ED25519_MAKE_KEY) && defined(HAVE_ED25519_KEY_EXPORT)) || \
41-
(!defined(WOLFSSH_NO_ECDSA) && defined(HAVE_ECC_KEY_EXPORT))
41+
(!defined(WOLFSSH_NO_ECDSA) && defined(HAVE_ECC_KEY_EXPORT)) || \
42+
(!defined(WOLFSSH_NO_MLDSA) && defined(WOLFSSH_CERTS) && \
43+
defined(WOLFSSL_CERT_GEN))
4244
#include <wolfssl/wolfcrypt/asn_public.h>
4345
#endif
4446
#ifdef NO_FILESYSTEM
@@ -595,6 +597,15 @@ static THREAD_RETURN WOLFSSH_THREAD pubkey_server_thread(void* args)
595597
}
596598
}
597599

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

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

674703
clientSsh = wolfSSH_new(clientCtx);
675704
AssertNotNull(clientSsh);
@@ -707,6 +736,14 @@ static int run_pubkey_test_ex(PubkeyServerCtx* sCtx, PubkeyClientCtx* cCtx,
707736
return WS_SUCCESS;
708737
}
709738

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

14801517
#if defined(WOLFSSH_HAVE_MLDSA_DERIVE_PUB) && !defined(WOLFSSH_NO_ECDSA)
1518+
/* Authorize hansel's ECC key on the server and have the client present
1519+
* it. cCtx points into pubBuf and privBuf. */
1520+
static void load_hansel_ecc_client(PubkeyServerCtx* sCtx,
1521+
PubkeyClientCtx* cCtx, byte* pubBuf, word32 pubBufSz,
1522+
byte* privBuf, word32 privBufSz)
1523+
{
1524+
const byte* pubKeyType = NULL;
1525+
word32 pubKeyTypeSz = 0;
1526+
const byte* privKeyType = NULL;
1527+
word32 privKeyTypeSz = 0;
1528+
1529+
AssertIntEQ(wolfSSH_ReadKey_buffer((const byte*)hanselPublicEcc,
1530+
(word32)WSTRLEN(hanselPublicEcc), WOLFSSH_FORMAT_SSH,
1531+
&pubBuf, &pubBufSz, &pubKeyType, &pubKeyTypeSz, NULL),
1532+
WS_SUCCESS);
1533+
1534+
AssertIntEQ(wc_Sha256Hash(pubBuf, pubBufSz, sCtx->hash), 0);
1535+
1536+
AssertIntEQ(wolfSSH_ReadKey_buffer(hanselPrivateEcc, hanselPrivateEccSz,
1537+
WOLFSSH_FORMAT_ASN1,
1538+
&privBuf, &privBufSz, &privKeyType, &privKeyTypeSz, NULL),
1539+
WS_SUCCESS);
1540+
1541+
cCtx->publicKeyType = pubKeyType;
1542+
cCtx->publicKeyTypeSz = pubKeyTypeSz;
1543+
cCtx->publicKey = pubBuf;
1544+
cCtx->publicKeySz = pubBufSz;
1545+
cCtx->privateKey = privBuf;
1546+
cCtx->privateKeySz = privBufSz;
1547+
}
1548+
14811549
/* ML-DSA counterpart to test_pubkey_auth_ed25519_privonly_hostkey: a real
14821550
* handshake with a private-only host key, exercising the derived public
14831551
* key all the way through SendKexGetSigningKey. */
@@ -1486,39 +1554,16 @@ static void test_pubkey_auth_mldsa_privonly_hostkey(void)
14861554
PubkeyServerCtx sCtx = {0};
14871555
PubkeyClientCtx cCtx;
14881556
byte pubKeyBuf[512];
1489-
byte* p = pubKeyBuf;
1490-
word32 pubKeySz = sizeof(pubKeyBuf);
1491-
const byte* pubKeyType = NULL;
1492-
word32 pubKeyTypeSz = 0;
14931557
byte privKeyBuf[1300];
1494-
byte* privKeyPtr = privKeyBuf;
1495-
word32 privKeySz = sizeof(privKeyBuf);
1496-
const byte* privKeyType = NULL;
1497-
word32 privKeyTypeSz = 0;
14981558
byte* hostKeyDer;
14991559
int hostKeyDerSz;
15001560

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

15031563
hostKeyDer = mldsa_privonly_hostkey_der(&hostKeyDerSz);
15041564

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;
1565+
load_hansel_ecc_client(&sCtx, &cCtx, pubKeyBuf, sizeof(pubKeyBuf),
1566+
privKeyBuf, sizeof(privKeyBuf));
15221567

15231568
run_pubkey_test_ex(&sCtx, &cCtx, WS_SUCCESS, hostKeyDer,
15241569
(word32)hostKeyDerSz);
@@ -1529,6 +1574,104 @@ static void test_pubkey_auth_mldsa_privonly_hostkey(void)
15291574
}
15301575
#endif /* WOLFSSH_HAVE_MLDSA_DERIVE_PUB && !WOLFSSH_NO_ECDSA */
15311576

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