Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions src/pk.c
Original file line number Diff line number Diff line change
Expand Up @@ -6557,6 +6557,18 @@ WOLFSSL_EVP_PKEY* wolfSSL_PEM_read_bio_PrivateKey(WOLFSSL_BIO* bio,
case ED448k:
type = WC_EVP_PKEY_ED448;
break;
#endif
#ifdef WOLFSSL_HAVE_MLDSA
case ML_DSA_44k:
case ML_DSA_65k:
case ML_DSA_87k:
#ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
case DILITHIUM_LEVEL2k:
case DILITHIUM_LEVEL3k:
case DILITHIUM_LEVEL5k:
#endif
type = WC_EVP_PKEY_DILITHIUM;
break;
#endif
default:
type = WOLFSSL_FATAL_ERROR;
Expand Down Expand Up @@ -6714,6 +6726,18 @@ WOLFSSL_EVP_PKEY* wolfSSL_PEM_read_PrivateKey(XFILE fp, WOLFSSL_EVP_PKEY **key,
case ED448k:
type = WC_EVP_PKEY_ED448;
break;
#endif
#ifdef WOLFSSL_HAVE_MLDSA
case ML_DSA_44k:
case ML_DSA_65k:
case ML_DSA_87k:
#ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
case DILITHIUM_LEVEL2k:
case DILITHIUM_LEVEL3k:
case DILITHIUM_LEVEL5k:
#endif
type = WC_EVP_PKEY_DILITHIUM;
break;
#endif
default:
type = WOLFSSL_FATAL_ERROR;
Expand Down
202 changes: 197 additions & 5 deletions src/x509.c
Original file line number Diff line number Diff line change
Expand Up @@ -12271,6 +12271,35 @@ static int CertFromX509(Cert* cert, WOLFSSL_X509* x509)
return WOLFSSL_FAILURE;
}
}
#ifdef WOLFSSL_HAVE_MLDSA
else if (pkey->type == WC_EVP_PKEY_DILITHIUM) {
/* ML-DSA does not use a separate hash; ignore md. */
switch (WOLFSSL_ATOMIC_LOAD(pkey->mldsaOID)) {
case ML_DSA_44k:
sigType = CTC_ML_DSA_LEVEL2;
break;
case ML_DSA_65k:
sigType = CTC_ML_DSA_LEVEL3;
break;
case ML_DSA_87k:
sigType = CTC_ML_DSA_LEVEL5;
break;
#ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
case DILITHIUM_LEVEL2k:
sigType = CTC_DILITHIUM_LEVEL2;
break;
case DILITHIUM_LEVEL3k:
sigType = CTC_DILITHIUM_LEVEL3;
break;
case DILITHIUM_LEVEL5k:
sigType = CTC_DILITHIUM_LEVEL5;
break;
#endif
default:
return WOLFSSL_FAILURE;
}
}
#endif /* WOLFSSL_HAVE_MLDSA */
else
return WOLFSSL_FAILURE;
return sigType;
Expand Down Expand Up @@ -12752,6 +12781,10 @@ static int CertFromX509(Cert* cert, WOLFSSL_X509* x509)
int type = -1;
int sigType;
WC_RNG rng;
#if defined(WOLFSSL_HAVE_MLDSA) && defined(WOLFSSL_MLDSA_PRIVATE_KEY) && \
!defined(WOLFSSL_MLDSA_NO_ASN1) && !defined(WOLFSSL_MLDSA_NO_SIGN)
MlDsaKey* mldsa = NULL;
#endif

(void)req;
WOLFSSL_ENTER("wolfSSL_X509_resign_cert");
Expand All @@ -12776,14 +12809,83 @@ static int CertFromX509(Cert* cert, WOLFSSL_X509* x509)
key = pkey->ecc->internal;
}
#endif
#if defined(WOLFSSL_HAVE_MLDSA) && defined(WOLFSSL_MLDSA_PRIVATE_KEY) && \
!defined(WOLFSSL_MLDSA_NO_ASN1) && !defined(WOLFSSL_MLDSA_NO_SIGN)
if (pkey->type == WC_EVP_PKEY_DILITHIUM) {
/* Decode the ML-DSA private key held as DER in pkey.ptr. */
word32 idx = 0;
byte level = 0;

mldsa = (MlDsaKey*)XMALLOC(sizeof(MlDsaKey), NULL,
DYNAMIC_TYPE_MLDSA);
if (mldsa == NULL)
return WOLFSSL_FATAL_ERROR;
if (wc_MlDsaKey_Init(mldsa, NULL, INVALID_DEVID) != 0) {
XFREE(mldsa, NULL, DYNAMIC_TYPE_MLDSA);
return WOLFSSL_FATAL_ERROR;
}
if (wc_MlDsaKey_PrivateKeyDecode(mldsa,
(const byte*)pkey->pkey.ptr, (word32)pkey->pkey_sz,
&idx) != 0 ||
wc_MlDsaKey_GetParams(mldsa, &level) != 0) {
wc_MlDsaKey_Free(mldsa);
XFREE(mldsa, NULL, DYNAMIC_TYPE_MLDSA);
return WOLFSSL_FATAL_ERROR;
}
switch (level) {
case WC_ML_DSA_44:
type = ML_DSA_LEVEL2_TYPE;
break;
case WC_ML_DSA_65:
type = ML_DSA_LEVEL3_TYPE;
break;
case WC_ML_DSA_87:
type = ML_DSA_LEVEL5_TYPE;
break;
#ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
case WC_ML_DSA_44_DRAFT:
type = DILITHIUM_LEVEL2_TYPE;
break;
case WC_ML_DSA_65_DRAFT:
type = DILITHIUM_LEVEL3_TYPE;
break;
case WC_ML_DSA_87_DRAFT:
type = DILITHIUM_LEVEL5_TYPE;
break;
#endif
default:
wc_MlDsaKey_Free(mldsa);
XFREE(mldsa, NULL, DYNAMIC_TYPE_MLDSA);
return WOLFSSL_FATAL_ERROR;
}
key = mldsa;
}
#endif /* WOLFSSL_HAVE_MLDSA && WOLFSSL_MLDSA_PRIVATE_KEY &&
* !WOLFSSL_MLDSA_NO_ASN1 && !WOLFSSL_MLDSA_NO_SIGN */

/* Sign the certificate (request) body. */
ret = wc_InitRng(&rng);
if (ret != 0)
if (ret != 0) {
#if defined(WOLFSSL_HAVE_MLDSA) && \
defined(WOLFSSL_MLDSA_PRIVATE_KEY) && \
!defined(WOLFSSL_MLDSA_NO_ASN1) && !defined(WOLFSSL_MLDSA_NO_SIGN)
if (mldsa != NULL) {
wc_MlDsaKey_Free(mldsa);
XFREE(mldsa, NULL, DYNAMIC_TYPE_MLDSA);
}
#endif
return ret;
}
ret = wc_SignCert_ex(certBodySz, sigType, der, (word32)derSz, type, key,
&rng);
wc_FreeRng(&rng);
#if defined(WOLFSSL_HAVE_MLDSA) && defined(WOLFSSL_MLDSA_PRIVATE_KEY) && \
!defined(WOLFSSL_MLDSA_NO_ASN1) && !defined(WOLFSSL_MLDSA_NO_SIGN)
if (mldsa != NULL) {
wc_MlDsaKey_Free(mldsa);
XFREE(mldsa, NULL, DYNAMIC_TYPE_MLDSA);
}
#endif
if (ret < 0) {
WOLFSSL_LEAVE("wolfSSL_X509_resign_cert", ret);
return ret;
Expand Down Expand Up @@ -12850,7 +12952,13 @@ static int CertFromX509(Cert* cert, WOLFSSL_X509* x509)

#ifndef WC_MAX_X509_GEN
/* able to override max size until dynamic buffer created */
#define WC_MAX_X509_GEN 4096
#ifdef WOLFSSL_HAVE_MLDSA
/* ML-DSA public keys and signatures are large (ML-DSA-87:
* 2592 byte public key, 4627 byte signature). */
#define WC_MAX_X509_GEN 20480
#else
#define WC_MAX_X509_GEN 4096
#endif
#endif

/* returns the size of signature on success */
Expand Down Expand Up @@ -16378,6 +16486,90 @@ int wolfSSL_X509_set_pubkey(WOLFSSL_X509 *cert, WOLFSSL_EVP_PKEY *pkey)
}
break;
#endif
#if defined(WOLFSSL_HAVE_MLDSA) && defined(WOLFSSL_MLDSA_PUBLIC_KEY) && \
!defined(WOLFSSL_MLDSA_NO_ASN1) && defined(WC_ENABLE_ASYM_KEY_EXPORT)
case WC_EVP_PKEY_DILITHIUM:
{
/* Decode key DER (private or public) and export public part. */
MlDsaKey* mldsa;
word32 idx = 0;
byte level = 0;
int decodeOk = 0;

mldsa = (MlDsaKey*)XMALLOC(sizeof(MlDsaKey), cert->heap,
DYNAMIC_TYPE_MLDSA);
if (mldsa == NULL)
return WOLFSSL_FAILURE;
if (wc_MlDsaKey_Init(mldsa, NULL, INVALID_DEVID) != 0) {
XFREE(mldsa, cert->heap, DYNAMIC_TYPE_MLDSA);
return WOLFSSL_FAILURE;
}
#ifdef WOLFSSL_MLDSA_PRIVATE_KEY
if (wc_MlDsaKey_PrivateKeyDecode(mldsa,
(const byte*)pkey->pkey.ptr, (word32)pkey->pkey_sz,
&idx) == 0) {
decodeOk = 1;
}
#endif
if (!decodeOk) {
idx = 0;
if (wc_MlDsaKey_PublicKeyDecode(mldsa,
(const byte*)pkey->pkey.ptr, (word32)pkey->pkey_sz,
&idx) != 0) {
wc_MlDsaKey_Free(mldsa);
XFREE(mldsa, cert->heap, DYNAMIC_TYPE_MLDSA);
return WOLFSSL_FAILURE;
}
}
if (wc_MlDsaKey_GetParams(mldsa, &level) != 0) {
wc_MlDsaKey_Free(mldsa);
XFREE(mldsa, cert->heap, DYNAMIC_TYPE_MLDSA);
return WOLFSSL_FAILURE;
}

derSz = MLDSA_MAX_PUB_KEY_SIZE + MAX_ALGO_SZ + MAX_SEQ_SZ * 2;
p = (byte*)XMALLOC(derSz, cert->heap, DYNAMIC_TYPE_PUBLIC_KEY);
if (p == NULL) {
wc_MlDsaKey_Free(mldsa);
XFREE(mldsa, cert->heap, DYNAMIC_TYPE_MLDSA);
return WOLFSSL_FAILURE;
}
derSz = wc_MlDsaKey_PublicKeyToDer(mldsa, p, (word32)derSz, 1);
wc_MlDsaKey_Free(mldsa);
XFREE(mldsa, cert->heap, DYNAMIC_TYPE_MLDSA);
if (derSz <= 0) {
XFREE(p, cert->heap, DYNAMIC_TYPE_PUBLIC_KEY);
return WOLFSSL_FAILURE;
}
switch (level) {
case WC_ML_DSA_44:
cert->pubKeyOID = ML_DSA_44k;
break;
case WC_ML_DSA_65:
cert->pubKeyOID = ML_DSA_65k;
break;
case WC_ML_DSA_87:
cert->pubKeyOID = ML_DSA_87k;
break;
#ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
case WC_ML_DSA_44_DRAFT:
cert->pubKeyOID = DILITHIUM_LEVEL2k;
break;
case WC_ML_DSA_65_DRAFT:
cert->pubKeyOID = DILITHIUM_LEVEL3k;
break;
case WC_ML_DSA_87_DRAFT:
cert->pubKeyOID = DILITHIUM_LEVEL5k;
break;
#endif
default:
XFREE(p, cert->heap, DYNAMIC_TYPE_PUBLIC_KEY);
return WOLFSSL_FAILURE;
}
}
break;
#endif /* WOLFSSL_HAVE_MLDSA && WOLFSSL_MLDSA_PUBLIC_KEY &&
* !WOLFSSL_MLDSA_NO_ASN1 && WC_ENABLE_ASYM_KEY_EXPORT */
default:
return WOLFSSL_FAILURE;
}
Expand Down Expand Up @@ -16727,8 +16919,8 @@ int wolfSSL_X509_REQ_sign(WOLFSSL_X509 *req, WOLFSSL_EVP_PKEY *pkey,
const WOLFSSL_EVP_MD *md)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 [Medium] wolfSSL_X509_REQ_sign changes lack ML-DSA test coverage
💡 SUGGEST test

The PR modifies wolfSSL_X509_REQ_sign (buffer sizing) and the wolfSSL_X509_resign_cert ML-DSA signing path that it shares with wolfSSL_X509_sign. The new tests exercise wolfSSL_X509_sign (in test_wolfSSL_X509_set_pubkey) and PEM/d2i loading, but there is no test that signs a certificate REQUEST with an ML-DSA key. A grep of tests/api/test_x509.c (which holds the REQ_sign tests) shows no ML-DSA/dilithium references. The REQ path has its own buffer-declaration mechanism (WC_DECLARE_VAR stack vs. XMALLOC heap) and argument ordering, so it is worth covering separately.

Suggestion: Add a test that builds an X509_REQ, calls wolfSSL_X509_set_pubkey/X509_REQ_sign with an ML-DSA key, and verifies the resulting request.

{
int ret;
WC_DECLARE_VAR(der, byte, 2048, 0);
int derSz = 2048;
WC_DECLARE_VAR(der, byte, WC_MAX_X509_GEN, 0);
int derSz = WC_MAX_X509_GEN;

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

if (wolfSSL_X509_resign_cert(req, 1, der, 2048, derSz,
if (wolfSSL_X509_resign_cert(req, 1, der, WC_MAX_X509_GEN, derSz,
(WOLFSSL_EVP_MD*)md, pkey) <= 0) {
WC_FREE_VAR_EX(der, NULL, DYNAMIC_TYPE_TMP_BUFFER);
return WOLFSSL_FAILURE;
Expand Down
52 changes: 52 additions & 0 deletions tests/api/test_ossl_pem.c
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,58 @@ int test_wolfSSL_PEM_PrivateKey_dh(void)
return EXPECT_RESULT();
}

/* test loading ML-DSA keys with PEM_read_bio_PrivateKey and
* PEM_read_PrivateKey */
int test_wolfSSL_PEM_PrivateKey_mldsa(void)
{
EXPECT_DECLS;
#if defined(OPENSSL_EXTRA) && !defined(NO_CERTS) && \
defined(WOLFSSL_HAVE_MLDSA) && defined(WOLFSSL_MLDSA_PRIVATE_KEY) && \
!defined(WOLFSSL_MLDSA_NO_ASN1) && !defined(NO_FILESYSTEM) && \
!defined(NO_BIO)
const char* fnames[] = {
#ifndef WOLFSSL_NO_ML_DSA_44
"./certs/mldsa/mldsa44-key.pem",
#endif
#ifndef WOLFSSL_NO_ML_DSA_65
"./certs/mldsa/mldsa65-key.pem",
#endif
#ifndef WOLFSSL_NO_ML_DSA_87
"./certs/mldsa/mldsa87-key.pem",
#endif
};
BIO* bio = NULL;
EVP_PKEY* pkey = NULL;
XFILE file = XBADFILE;
word32 i;

for (i = 0; i < (word32)(sizeof(fnames) / sizeof(*fnames)); i++) {
/* BIO variant */
ExpectNotNull(bio = BIO_new_file(fnames[i], "rb"));
ExpectNotNull(pkey = wolfSSL_PEM_read_bio_PrivateKey(bio, NULL, NULL,
NULL));
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
BIO_free(bio);
bio = NULL;
EVP_PKEY_free(pkey);
pkey = NULL;

/* XFILE variant */
ExpectTrue((file = XFOPEN(fnames[i], "rb")) != XBADFILE);
ExpectNotNull(pkey = wolfSSL_PEM_read_PrivateKey(file, NULL, NULL,
NULL));
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
if (file != XBADFILE) {
XFCLOSE(file);
file = XBADFILE;
}
EVP_PKEY_free(pkey);
pkey = NULL;
}
#endif
return EXPECT_RESULT();
}

int test_wolfSSL_PEM_PrivateKey(void)
{
EXPECT_DECLS;
Expand Down
2 changes: 2 additions & 0 deletions tests/api/test_ossl_pem.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ int test_wolfSSL_PEM_PrivateKey_rsa(void);
int test_wolfSSL_PEM_PrivateKey_ecc(void);
int test_wolfSSL_PEM_PrivateKey_dsa(void);
int test_wolfSSL_PEM_PrivateKey_dh(void);
int test_wolfSSL_PEM_PrivateKey_mldsa(void);
int test_wolfSSL_PEM_PrivateKey(void);
int test_wolfSSL_PEM_write_PrivateKey(void);
int test_wolfSSL_PEM_write_PUBKEY(void);
Expand All @@ -53,6 +54,7 @@ int test_wolfSSL_PEM_PUBKEY(void);
TEST_DECL_GROUP("ossl_pem", test_wolfSSL_PEM_PrivateKey_ecc), \
TEST_DECL_GROUP("ossl_pem", test_wolfSSL_PEM_PrivateKey_dsa), \
TEST_DECL_GROUP("ossl_pem", test_wolfSSL_PEM_PrivateKey_dh), \
TEST_DECL_GROUP("ossl_pem", test_wolfSSL_PEM_PrivateKey_mldsa), \
TEST_DECL_GROUP("ossl_pem", test_wolfSSL_PEM_PrivateKey), \
TEST_DECL_GROUP("ossl_pem", test_wolfSSL_PEM_write_PrivateKey), \
TEST_DECL_GROUP("ossl_pem", test_wolfSSL_PEM_write_PUBKEY), \
Expand Down
Loading
Loading