Skip to content

Commit c71c2ba

Browse files
committed
OpenSSL compat with ML-DSA
1 parent 4a7695e commit c71c2ba

6 files changed

Lines changed: 351 additions & 7 deletions

File tree

src/pk.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6557,6 +6557,18 @@ WOLFSSL_EVP_PKEY* wolfSSL_PEM_read_bio_PrivateKey(WOLFSSL_BIO* bio,
65576557
case ED448k:
65586558
type = WC_EVP_PKEY_ED448;
65596559
break;
6560+
#endif
6561+
#ifdef WOLFSSL_HAVE_MLDSA
6562+
case ML_DSA_44k:
6563+
case ML_DSA_65k:
6564+
case ML_DSA_87k:
6565+
#ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
6566+
case DILITHIUM_LEVEL2k:
6567+
case DILITHIUM_LEVEL3k:
6568+
case DILITHIUM_LEVEL5k:
6569+
#endif
6570+
type = WC_EVP_PKEY_DILITHIUM;
6571+
break;
65606572
#endif
65616573
default:
65626574
type = WOLFSSL_FATAL_ERROR;
@@ -6714,6 +6726,18 @@ WOLFSSL_EVP_PKEY* wolfSSL_PEM_read_PrivateKey(XFILE fp, WOLFSSL_EVP_PKEY **key,
67146726
case ED448k:
67156727
type = WC_EVP_PKEY_ED448;
67166728
break;
6729+
#endif
6730+
#ifdef WOLFSSL_HAVE_MLDSA
6731+
case ML_DSA_44k:
6732+
case ML_DSA_65k:
6733+
case ML_DSA_87k:
6734+
#ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
6735+
case DILITHIUM_LEVEL2k:
6736+
case DILITHIUM_LEVEL3k:
6737+
case DILITHIUM_LEVEL5k:
6738+
#endif
6739+
type = WC_EVP_PKEY_DILITHIUM;
6740+
break;
67176741
#endif
67186742
default:
67196743
type = WOLFSSL_FATAL_ERROR;

src/x509.c

Lines changed: 197 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12271,6 +12271,35 @@ static int CertFromX509(Cert* cert, WOLFSSL_X509* x509)
1227112271
return WOLFSSL_FAILURE;
1227212272
}
1227312273
}
12274+
#ifdef WOLFSSL_HAVE_MLDSA
12275+
else if (pkey->type == WC_EVP_PKEY_DILITHIUM) {
12276+
/* ML-DSA does not use a separate hash; ignore md. */
12277+
switch (WOLFSSL_ATOMIC_LOAD(pkey->mldsaOID)) {
12278+
case ML_DSA_44k:
12279+
sigType = CTC_ML_DSA_LEVEL2;
12280+
break;
12281+
case ML_DSA_65k:
12282+
sigType = CTC_ML_DSA_LEVEL3;
12283+
break;
12284+
case ML_DSA_87k:
12285+
sigType = CTC_ML_DSA_LEVEL5;
12286+
break;
12287+
#ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
12288+
case DILITHIUM_LEVEL2k:
12289+
sigType = CTC_DILITHIUM_LEVEL2;
12290+
break;
12291+
case DILITHIUM_LEVEL3k:
12292+
sigType = CTC_DILITHIUM_LEVEL3;
12293+
break;
12294+
case DILITHIUM_LEVEL5k:
12295+
sigType = CTC_DILITHIUM_LEVEL5;
12296+
break;
12297+
#endif
12298+
default:
12299+
return WOLFSSL_FAILURE;
12300+
}
12301+
}
12302+
#endif /* WOLFSSL_HAVE_MLDSA */
1227412303
else
1227512304
return WOLFSSL_FAILURE;
1227612305
return sigType;
@@ -12752,6 +12781,10 @@ static int CertFromX509(Cert* cert, WOLFSSL_X509* x509)
1275212781
int type = -1;
1275312782
int sigType;
1275412783
WC_RNG rng;
12784+
#if defined(WOLFSSL_HAVE_MLDSA) && defined(WOLFSSL_MLDSA_PRIVATE_KEY) && \
12785+
!defined(WOLFSSL_MLDSA_NO_ASN1) && !defined(WOLFSSL_MLDSA_NO_SIGN)
12786+
MlDsaKey* mldsa = NULL;
12787+
#endif
1275512788

1275612789
(void)req;
1275712790
WOLFSSL_ENTER("wolfSSL_X509_resign_cert");
@@ -12776,14 +12809,83 @@ static int CertFromX509(Cert* cert, WOLFSSL_X509* x509)
1277612809
key = pkey->ecc->internal;
1277712810
}
1277812811
#endif
12812+
#if defined(WOLFSSL_HAVE_MLDSA) && defined(WOLFSSL_MLDSA_PRIVATE_KEY) && \
12813+
!defined(WOLFSSL_MLDSA_NO_ASN1) && !defined(WOLFSSL_MLDSA_NO_SIGN)
12814+
if (pkey->type == WC_EVP_PKEY_DILITHIUM) {
12815+
/* Decode the ML-DSA private key held as DER in pkey.ptr. */
12816+
word32 idx = 0;
12817+
byte level = 0;
12818+
12819+
mldsa = (MlDsaKey*)XMALLOC(sizeof(MlDsaKey), NULL,
12820+
DYNAMIC_TYPE_MLDSA);
12821+
if (mldsa == NULL)
12822+
return WOLFSSL_FATAL_ERROR;
12823+
if (wc_MlDsaKey_Init(mldsa, NULL, INVALID_DEVID) != 0) {
12824+
XFREE(mldsa, NULL, DYNAMIC_TYPE_MLDSA);
12825+
return WOLFSSL_FATAL_ERROR;
12826+
}
12827+
if (wc_MlDsaKey_PrivateKeyDecode(mldsa,
12828+
(const byte*)pkey->pkey.ptr, (word32)pkey->pkey_sz,
12829+
&idx) != 0 ||
12830+
wc_MlDsaKey_GetParams(mldsa, &level) != 0) {
12831+
wc_MlDsaKey_Free(mldsa);
12832+
XFREE(mldsa, NULL, DYNAMIC_TYPE_MLDSA);
12833+
return WOLFSSL_FATAL_ERROR;
12834+
}
12835+
switch (level) {
12836+
case WC_ML_DSA_44:
12837+
type = ML_DSA_LEVEL2_TYPE;
12838+
break;
12839+
case WC_ML_DSA_65:
12840+
type = ML_DSA_LEVEL3_TYPE;
12841+
break;
12842+
case WC_ML_DSA_87:
12843+
type = ML_DSA_LEVEL5_TYPE;
12844+
break;
12845+
#ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
12846+
case WC_ML_DSA_44_DRAFT:
12847+
type = DILITHIUM_LEVEL2_TYPE;
12848+
break;
12849+
case WC_ML_DSA_65_DRAFT:
12850+
type = DILITHIUM_LEVEL3_TYPE;
12851+
break;
12852+
case WC_ML_DSA_87_DRAFT:
12853+
type = DILITHIUM_LEVEL5_TYPE;
12854+
break;
12855+
#endif
12856+
default:
12857+
wc_MlDsaKey_Free(mldsa);
12858+
XFREE(mldsa, NULL, DYNAMIC_TYPE_MLDSA);
12859+
return WOLFSSL_FATAL_ERROR;
12860+
}
12861+
key = mldsa;
12862+
}
12863+
#endif /* WOLFSSL_HAVE_MLDSA && WOLFSSL_MLDSA_PRIVATE_KEY &&
12864+
* !WOLFSSL_MLDSA_NO_ASN1 && !WOLFSSL_MLDSA_NO_SIGN */
1277912865

1278012866
/* Sign the certificate (request) body. */
1278112867
ret = wc_InitRng(&rng);
12782-
if (ret != 0)
12868+
if (ret != 0) {
12869+
#if defined(WOLFSSL_HAVE_MLDSA) && \
12870+
defined(WOLFSSL_MLDSA_PRIVATE_KEY) && \
12871+
!defined(WOLFSSL_MLDSA_NO_ASN1) && !defined(WOLFSSL_MLDSA_NO_SIGN)
12872+
if (mldsa != NULL) {
12873+
wc_MlDsaKey_Free(mldsa);
12874+
XFREE(mldsa, NULL, DYNAMIC_TYPE_MLDSA);
12875+
}
12876+
#endif
1278312877
return ret;
12878+
}
1278412879
ret = wc_SignCert_ex(certBodySz, sigType, der, (word32)derSz, type, key,
1278512880
&rng);
1278612881
wc_FreeRng(&rng);
12882+
#if defined(WOLFSSL_HAVE_MLDSA) && defined(WOLFSSL_MLDSA_PRIVATE_KEY) && \
12883+
!defined(WOLFSSL_MLDSA_NO_ASN1) && !defined(WOLFSSL_MLDSA_NO_SIGN)
12884+
if (mldsa != NULL) {
12885+
wc_MlDsaKey_Free(mldsa);
12886+
XFREE(mldsa, NULL, DYNAMIC_TYPE_MLDSA);
12887+
}
12888+
#endif
1278712889
if (ret < 0) {
1278812890
WOLFSSL_LEAVE("wolfSSL_X509_resign_cert", ret);
1278912891
return ret;
@@ -12850,7 +12952,13 @@ static int CertFromX509(Cert* cert, WOLFSSL_X509* x509)
1285012952

1285112953
#ifndef WC_MAX_X509_GEN
1285212954
/* able to override max size until dynamic buffer created */
12853-
#define WC_MAX_X509_GEN 4096
12955+
#ifdef WOLFSSL_HAVE_MLDSA
12956+
/* ML-DSA public keys and signatures are large (ML-DSA-87:
12957+
* 2592 byte public key, 4627 byte signature). */
12958+
#define WC_MAX_X509_GEN 20480
12959+
#else
12960+
#define WC_MAX_X509_GEN 4096
12961+
#endif
1285412962
#endif
1285512963

1285612964
/* returns the size of signature on success */
@@ -16378,6 +16486,90 @@ int wolfSSL_X509_set_pubkey(WOLFSSL_X509 *cert, WOLFSSL_EVP_PKEY *pkey)
1637816486
}
1637916487
break;
1638016488
#endif
16489+
#if defined(WOLFSSL_HAVE_MLDSA) && defined(WOLFSSL_MLDSA_PUBLIC_KEY) && \
16490+
!defined(WOLFSSL_MLDSA_NO_ASN1) && defined(WC_ENABLE_ASYM_KEY_EXPORT)
16491+
case WC_EVP_PKEY_DILITHIUM:
16492+
{
16493+
/* Decode key DER (private or public) and export public part. */
16494+
MlDsaKey* mldsa;
16495+
word32 idx = 0;
16496+
byte level = 0;
16497+
int decodeOk = 0;
16498+
16499+
mldsa = (MlDsaKey*)XMALLOC(sizeof(MlDsaKey), cert->heap,
16500+
DYNAMIC_TYPE_MLDSA);
16501+
if (mldsa == NULL)
16502+
return WOLFSSL_FAILURE;
16503+
if (wc_MlDsaKey_Init(mldsa, NULL, INVALID_DEVID) != 0) {
16504+
XFREE(mldsa, cert->heap, DYNAMIC_TYPE_MLDSA);
16505+
return WOLFSSL_FAILURE;
16506+
}
16507+
#ifdef WOLFSSL_MLDSA_PRIVATE_KEY
16508+
if (wc_MlDsaKey_PrivateKeyDecode(mldsa,
16509+
(const byte*)pkey->pkey.ptr, (word32)pkey->pkey_sz,
16510+
&idx) == 0) {
16511+
decodeOk = 1;
16512+
}
16513+
#endif
16514+
if (!decodeOk) {
16515+
idx = 0;
16516+
if (wc_MlDsaKey_PublicKeyDecode(mldsa,
16517+
(const byte*)pkey->pkey.ptr, (word32)pkey->pkey_sz,
16518+
&idx) != 0) {
16519+
wc_MlDsaKey_Free(mldsa);
16520+
XFREE(mldsa, cert->heap, DYNAMIC_TYPE_MLDSA);
16521+
return WOLFSSL_FAILURE;
16522+
}
16523+
}
16524+
if (wc_MlDsaKey_GetParams(mldsa, &level) != 0) {
16525+
wc_MlDsaKey_Free(mldsa);
16526+
XFREE(mldsa, cert->heap, DYNAMIC_TYPE_MLDSA);
16527+
return WOLFSSL_FAILURE;
16528+
}
16529+
16530+
derSz = MLDSA_MAX_PUB_KEY_SIZE + MAX_ALGO_SZ + MAX_SEQ_SZ * 2;
16531+
p = (byte*)XMALLOC(derSz, cert->heap, DYNAMIC_TYPE_PUBLIC_KEY);
16532+
if (p == NULL) {
16533+
wc_MlDsaKey_Free(mldsa);
16534+
XFREE(mldsa, cert->heap, DYNAMIC_TYPE_MLDSA);
16535+
return WOLFSSL_FAILURE;
16536+
}
16537+
derSz = wc_MlDsaKey_PublicKeyToDer(mldsa, p, (word32)derSz, 1);
16538+
wc_MlDsaKey_Free(mldsa);
16539+
XFREE(mldsa, cert->heap, DYNAMIC_TYPE_MLDSA);
16540+
if (derSz <= 0) {
16541+
XFREE(p, cert->heap, DYNAMIC_TYPE_PUBLIC_KEY);
16542+
return WOLFSSL_FAILURE;
16543+
}
16544+
switch (level) {
16545+
case WC_ML_DSA_44:
16546+
cert->pubKeyOID = ML_DSA_44k;
16547+
break;
16548+
case WC_ML_DSA_65:
16549+
cert->pubKeyOID = ML_DSA_65k;
16550+
break;
16551+
case WC_ML_DSA_87:
16552+
cert->pubKeyOID = ML_DSA_87k;
16553+
break;
16554+
#ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
16555+
case WC_ML_DSA_44_DRAFT:
16556+
cert->pubKeyOID = DILITHIUM_LEVEL2k;
16557+
break;
16558+
case WC_ML_DSA_65_DRAFT:
16559+
cert->pubKeyOID = DILITHIUM_LEVEL3k;
16560+
break;
16561+
case WC_ML_DSA_87_DRAFT:
16562+
cert->pubKeyOID = DILITHIUM_LEVEL5k;
16563+
break;
16564+
#endif
16565+
default:
16566+
XFREE(p, cert->heap, DYNAMIC_TYPE_PUBLIC_KEY);
16567+
return WOLFSSL_FAILURE;
16568+
}
16569+
}
16570+
break;
16571+
#endif /* WOLFSSL_HAVE_MLDSA && WOLFSSL_MLDSA_PUBLIC_KEY &&
16572+
* !WOLFSSL_MLDSA_NO_ASN1 && WC_ENABLE_ASYM_KEY_EXPORT */
1638116573
default:
1638216574
return WOLFSSL_FAILURE;
1638316575
}
@@ -16727,8 +16919,8 @@ int wolfSSL_X509_REQ_sign(WOLFSSL_X509 *req, WOLFSSL_EVP_PKEY *pkey,
1672716919
const WOLFSSL_EVP_MD *md)
1672816920
{
1672916921
int ret;
16730-
WC_DECLARE_VAR(der, byte, 2048, 0);
16731-
int derSz = 2048;
16922+
WC_DECLARE_VAR(der, byte, WC_MAX_X509_GEN, 0);
16923+
int derSz = WC_MAX_X509_GEN;
1673216924

1673316925
if (req == NULL || pkey == NULL || md == NULL) {
1673416926
WOLFSSL_LEAVE("wolfSSL_X509_REQ_sign", BAD_FUNC_ARG);
@@ -16748,7 +16940,7 @@ int wolfSSL_X509_REQ_sign(WOLFSSL_X509 *req, WOLFSSL_EVP_PKEY *pkey,
1674816940
return WOLFSSL_FAILURE;
1674916941
}
1675016942

16751-
if (wolfSSL_X509_resign_cert(req, 1, der, 2048, derSz,
16943+
if (wolfSSL_X509_resign_cert(req, 1, der, WC_MAX_X509_GEN, derSz,
1675216944
(WOLFSSL_EVP_MD*)md, pkey) <= 0) {
1675316945
WC_FREE_VAR_EX(der, NULL, DYNAMIC_TYPE_TMP_BUFFER);
1675416946
return WOLFSSL_FAILURE;

tests/api/test_ossl_pem.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,58 @@ int test_wolfSSL_PEM_PrivateKey_dh(void)
603603
return EXPECT_RESULT();
604604
}
605605

606+
/* test loading ML-DSA keys with PEM_read_bio_PrivateKey and
607+
* PEM_read_PrivateKey */
608+
int test_wolfSSL_PEM_PrivateKey_mldsa(void)
609+
{
610+
EXPECT_DECLS;
611+
#if defined(OPENSSL_EXTRA) && !defined(NO_CERTS) && \
612+
defined(WOLFSSL_HAVE_MLDSA) && defined(WOLFSSL_MLDSA_PRIVATE_KEY) && \
613+
!defined(WOLFSSL_MLDSA_NO_ASN1) && !defined(NO_FILESYSTEM) && \
614+
!defined(NO_BIO)
615+
const char* fnames[] = {
616+
#ifndef WOLFSSL_NO_ML_DSA_44
617+
"./certs/mldsa/mldsa44-key.pem",
618+
#endif
619+
#ifndef WOLFSSL_NO_ML_DSA_65
620+
"./certs/mldsa/mldsa65-key.pem",
621+
#endif
622+
#ifndef WOLFSSL_NO_ML_DSA_87
623+
"./certs/mldsa/mldsa87-key.pem",
624+
#endif
625+
};
626+
BIO* bio = NULL;
627+
EVP_PKEY* pkey = NULL;
628+
XFILE file = XBADFILE;
629+
word32 i;
630+
631+
for (i = 0; i < (word32)(sizeof(fnames) / sizeof(*fnames)); i++) {
632+
/* BIO variant */
633+
ExpectNotNull(bio = BIO_new_file(fnames[i], "rb"));
634+
ExpectNotNull(pkey = wolfSSL_PEM_read_bio_PrivateKey(bio, NULL, NULL,
635+
NULL));
636+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
637+
BIO_free(bio);
638+
bio = NULL;
639+
EVP_PKEY_free(pkey);
640+
pkey = NULL;
641+
642+
/* XFILE variant */
643+
ExpectTrue((file = XFOPEN(fnames[i], "rb")) != XBADFILE);
644+
ExpectNotNull(pkey = wolfSSL_PEM_read_PrivateKey(file, NULL, NULL,
645+
NULL));
646+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
647+
if (file != XBADFILE) {
648+
XFCLOSE(file);
649+
file = XBADFILE;
650+
}
651+
EVP_PKEY_free(pkey);
652+
pkey = NULL;
653+
}
654+
#endif
655+
return EXPECT_RESULT();
656+
}
657+
606658
int test_wolfSSL_PEM_PrivateKey(void)
607659
{
608660
EXPECT_DECLS;

tests/api/test_ossl_pem.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ int test_wolfSSL_PEM_PrivateKey_rsa(void);
3131
int test_wolfSSL_PEM_PrivateKey_ecc(void);
3232
int test_wolfSSL_PEM_PrivateKey_dsa(void);
3333
int test_wolfSSL_PEM_PrivateKey_dh(void);
34+
int test_wolfSSL_PEM_PrivateKey_mldsa(void);
3435
int test_wolfSSL_PEM_PrivateKey(void);
3536
int test_wolfSSL_PEM_write_PrivateKey(void);
3637
int test_wolfSSL_PEM_write_PUBKEY(void);
@@ -53,6 +54,7 @@ int test_wolfSSL_PEM_PUBKEY(void);
5354
TEST_DECL_GROUP("ossl_pem", test_wolfSSL_PEM_PrivateKey_ecc), \
5455
TEST_DECL_GROUP("ossl_pem", test_wolfSSL_PEM_PrivateKey_dsa), \
5556
TEST_DECL_GROUP("ossl_pem", test_wolfSSL_PEM_PrivateKey_dh), \
57+
TEST_DECL_GROUP("ossl_pem", test_wolfSSL_PEM_PrivateKey_mldsa), \
5658
TEST_DECL_GROUP("ossl_pem", test_wolfSSL_PEM_PrivateKey), \
5759
TEST_DECL_GROUP("ossl_pem", test_wolfSSL_PEM_write_PrivateKey), \
5860
TEST_DECL_GROUP("ossl_pem", test_wolfSSL_PEM_write_PUBKEY), \

0 commit comments

Comments
 (0)