Skip to content

Commit e7f48e1

Browse files
committed
evp: support ML-DSA in wolfSSL_EVP_PKCS82PKEY() and wolfSSL_X509_check_private_key()
1 parent d796a17 commit e7f48e1

4 files changed

Lines changed: 143 additions & 7 deletions

File tree

tests/api/test_asn.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1360,7 +1360,12 @@ int test_wc_DecodeObjectId(void)
13601360
#if defined(HAVE_PKCS8) && !defined(NO_ASN) && \
13611361
(defined(WOLFSSL_TEST_CERT) || defined(OPENSSL_EXTRA) || \
13621362
defined(OPENSSL_EXTRA_X509_SMALL) || defined(WOLFSSL_PUBLIC_ASN)) && \
1363-
(defined(HAVE_ED25519) || defined(HAVE_ED448) || defined(HAVE_DILITHIUM))
1363+
(defined(HAVE_ED25519) || \
1364+
(defined(HAVE_ED448) && defined(HAVE_ED448_KEY_EXPORT) && \
1365+
defined(WOLFSSL_KEY_GEN)) || \
1366+
(defined(HAVE_DILITHIUM) && \
1367+
!defined(WOLFSSL_DILITHIUM_NO_MAKE_KEY) && \
1368+
!defined(WOLFSSL_DILITHIUM_NO_ASN1)))
13641369
/* Run ToTraditional_ex() on a copy of der and assert the algId, returned
13651370
* length, and the inner OCTET STRING tag/length at the start of the
13661371
* (in-place rewritten) buffer. */
@@ -1673,7 +1678,7 @@ int test_ToTraditional_ex_negative(void)
16731678
((size_t)derSz + 2 + ED25519_PUB_KEY_SIZE <= sizeof(copy))) {
16741679
word32 trailerSz = 2 + ED25519_PUB_KEY_SIZE;
16751680
XMEMCPY(copy, der, (size_t)derSz);
1676-
ExpectTrue(copy[1] + trailerSz < 0x80);
1681+
ExpectTrue(copy[1] < (byte)(0x80 - trailerSz));
16771682
copy[1] = (byte)(copy[1] + trailerSz);
16781683
copy[derSz] = ASN_CONTEXT_SPECIFIC | ASN_ASYMKEY_PUBKEY;
16791684
copy[derSz + 1] = ED25519_PUB_KEY_SIZE;
@@ -1735,7 +1740,7 @@ int test_ToTraditional_ex_mldsa_bad_params(void)
17351740
der[sz++] = ASN_SEQUENCE | ASN_CONSTRUCTED;
17361741
der[sz++] = (byte)(sizeof(mldsaOid) + 2 + 2);
17371742
der[sz++] = ASN_OBJECT_ID;
1738-
der[sz++] = sizeof(mldsaOid);
1743+
der[sz++] = (byte)sizeof(mldsaOid);
17391744
XMEMCPY(der + sz, mldsaOid, sizeof(mldsaOid)); sz += sizeof(mldsaOid);
17401745
/* Disallowed, NULL parameter after the ML-DSA OID. */
17411746
der[sz++] = ASN_TAG_NULL;
@@ -1762,11 +1767,11 @@ int test_ToTraditional_ex_mldsa_bad_params(void)
17621767
der[sz++] = ASN_SEQUENCE | ASN_CONSTRUCTED;
17631768
der[sz++] = (byte)(sizeof(mldsaOid) + 2 + sizeof(extraOid) + 2);
17641769
der[sz++] = ASN_OBJECT_ID;
1765-
der[sz++] = sizeof(mldsaOid);
1770+
der[sz++] = (byte)sizeof(mldsaOid);
17661771
XMEMCPY(der + sz, mldsaOid, sizeof(mldsaOid)); sz += sizeof(mldsaOid);
17671772
/* Disallowed, OBJECT_ID parameter after the ML-DSA OID. */
17681773
der[sz++] = ASN_OBJECT_ID;
1769-
der[sz++] = sizeof(extraOid);
1774+
der[sz++] = (byte)sizeof(extraOid);
17701775
XMEMCPY(der + sz, extraOid, sizeof(extraOid)); sz += sizeof(extraOid);
17711776
der[sz++] = ASN_OCTET_STRING;
17721777
der[sz++] = (byte)(privKeySz + 2);

tests/api/test_ossl_x509_crypto.c

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,108 @@ int test_wolfSSL_X509_check_private_key(void)
7171
return EXPECT_RESULT();
7272
}
7373

74+
/* EVP_PKCS82PKEY() must populate pkey.ptr/pkey_sz for ML-DSA so
75+
* X509_check_private_key() (wc_CheckPrivateKey) can redecode the DER, and
76+
* d2i_PKCS8_PKEY() must keep the full PKCS#8 wrapper for ML-DSA level recovery
77+
* from the AlgorithmIdentifier. */
78+
int test_wolfSSL_X509_check_private_key_mldsa(void)
79+
{
80+
EXPECT_DECLS;
81+
#if defined(OPENSSL_EXTRA) && !defined(NO_FILESYSTEM) && \
82+
!defined(NO_BIO) && !defined(NO_CHECK_PRIVATE_KEY) && \
83+
defined(HAVE_DILITHIUM) && !defined(WOLFSSL_DILITHIUM_NO_SIGN) && \
84+
!defined(WOLFSSL_DILITHIUM_NO_VERIFY) && \
85+
(defined(OPENSSL_ALL) || defined(WOLFSSL_WPAS_SMALL)) && \
86+
(!defined(WOLFSSL_NO_ML_DSA_44) || !defined(WOLFSSL_NO_ML_DSA_65) || \
87+
!defined(WOLFSSL_NO_ML_DSA_87))
88+
static const struct {
89+
const char* keyPath;
90+
const char* certPath;
91+
const char* mismatchCertPath; /* NULL if no other level available */
92+
} cases[] = {
93+
#if !defined(WOLFSSL_NO_ML_DSA_44)
94+
{ "./certs/mldsa/mldsa44-key.pem",
95+
"./certs/mldsa/mldsa44-cert.der",
96+
#if !defined(WOLFSSL_NO_ML_DSA_65)
97+
"./certs/mldsa/mldsa65-cert.der"
98+
#elif !defined(WOLFSSL_NO_ML_DSA_87)
99+
"./certs/mldsa/mldsa87-cert.der"
100+
#else
101+
NULL
102+
#endif
103+
},
104+
#endif
105+
#if !defined(WOLFSSL_NO_ML_DSA_65)
106+
{ "./certs/mldsa/mldsa65-key.pem",
107+
"./certs/mldsa/mldsa65-cert.der",
108+
#if !defined(WOLFSSL_NO_ML_DSA_87)
109+
"./certs/mldsa/mldsa87-cert.der"
110+
#elif !defined(WOLFSSL_NO_ML_DSA_44)
111+
"./certs/mldsa/mldsa44-cert.der"
112+
#else
113+
NULL
114+
#endif
115+
},
116+
#endif
117+
#if !defined(WOLFSSL_NO_ML_DSA_87)
118+
{ "./certs/mldsa/mldsa87-key.pem",
119+
"./certs/mldsa/mldsa87-cert.der",
120+
#if !defined(WOLFSSL_NO_ML_DSA_44)
121+
"./certs/mldsa/mldsa44-cert.der"
122+
#elif !defined(WOLFSSL_NO_ML_DSA_65)
123+
"./certs/mldsa/mldsa65-cert.der"
124+
#else
125+
NULL
126+
#endif
127+
},
128+
#endif
129+
};
130+
size_t i;
131+
132+
for (i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
133+
PKCS8_PRIV_KEY_INFO* pt = NULL;
134+
EVP_PKEY* pkey = NULL;
135+
X509* x509 = NULL;
136+
X509* mismatchX509 = NULL;
137+
BIO* bio = NULL;
138+
byte* buf = NULL;
139+
size_t sz = 0;
140+
141+
ExpectIntEQ(load_file(cases[i].keyPath, &buf, &sz), 0);
142+
143+
ExpectNotNull(bio = BIO_new_mem_buf((void*)buf, (int)sz));
144+
ExpectNotNull(pt = d2i_PKCS8_PRIV_KEY_INFO_bio(bio, NULL));
145+
146+
ExpectNotNull(pkey = EVP_PKCS82PKEY(pt));
147+
if (pkey != NULL) {
148+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
149+
/* pkey.ptr must hold the DER so that X509_check_private_key() to
150+
* wc_CheckPrivateKey() can re-decode it. */
151+
ExpectNotNull(pkey->pkey.ptr);
152+
ExpectIntGT(pkey->pkey_sz, 0);
153+
}
154+
155+
ExpectNotNull(x509 = X509_load_certificate_file(
156+
cases[i].certPath, SSL_FILETYPE_ASN1));
157+
ExpectIntEQ(X509_check_private_key(x509, pkey), 1);
158+
159+
if (cases[i].mismatchCertPath != NULL) {
160+
ExpectNotNull(mismatchX509 = X509_load_certificate_file(
161+
cases[i].mismatchCertPath, SSL_FILETYPE_ASN1));
162+
ExpectIntEQ(X509_check_private_key(mismatchX509, pkey), 0);
163+
}
164+
165+
X509_free(mismatchX509);
166+
X509_free(x509);
167+
EVP_PKEY_free(pkey);
168+
PKCS8_PRIV_KEY_INFO_free(pt);
169+
BIO_free(bio);
170+
XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
171+
}
172+
#endif
173+
return EXPECT_RESULT();
174+
}
175+
74176
int test_wolfSSL_X509_verify(void)
75177
{
76178
EXPECT_DECLS;

tests/api/test_ossl_x509_crypto.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,16 @@
2525
#include <tests/api/api_decl.h>
2626

2727
int test_wolfSSL_X509_check_private_key(void);
28+
int test_wolfSSL_X509_check_private_key_mldsa(void);
2829
int test_wolfSSL_X509_verify(void);
2930
int test_wolfSSL_X509_sign(void);
3031
int test_wolfSSL_X509_sign2(void);
3132
int test_wolfSSL_make_cert(void);
3233

3334
#define TEST_OSSL_X509_CRYPTO_DECLS \
3435
TEST_DECL_GROUP("ossl_x509_crypto", test_wolfSSL_X509_check_private_key), \
36+
TEST_DECL_GROUP("ossl_x509_crypto", \
37+
test_wolfSSL_X509_check_private_key_mldsa), \
3538
TEST_DECL_GROUP("ossl_x509_crypto", test_wolfSSL_X509_verify), \
3639
TEST_DECL_GROUP("ossl_x509_crypto", test_wolfSSL_X509_sign), \
3740
TEST_DECL_GROUP("ossl_x509_crypto", test_wolfSSL_X509_sign2), \

wolfcrypt/src/evp_pk.c

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -942,7 +942,8 @@ static int d2iTryDilithiumKey(WOLFSSL_EVP_PKEY** out, const unsigned char* mem,
942942
return WOLFSSL_FATAL_ERROR;
943943
}
944944

945-
return d2i_make_pkey(out, NULL, 0, priv, WC_EVP_PKEY_DILITHIUM);
945+
/* Copy the consumed DER into pkey->pkey.ptr when the input was DER */
946+
return d2i_make_pkey(out, mem, keyIdx, priv, WC_EVP_PKEY_DILITHIUM);
946947
}
947948
#endif /* HAVE_DILITHIUM */
948949

@@ -1696,6 +1697,10 @@ WOLFSSL_PKCS8_PRIV_KEY_INFO* wolfSSL_d2i_PKCS8_PKEY(
16961697
DerBuffer rawDer;
16971698
EncryptedInfo info;
16981699
int advanceLen = 0;
1700+
#ifdef HAVE_DILITHIUM
1701+
word32 outerIdx = 0;
1702+
int outerLen = 0;
1703+
#endif
16991704

17001705
/* Clear the encryption information and DER buffer. */
17011706
XMEMSET(&info, 0, sizeof(info));
@@ -1737,10 +1742,31 @@ WOLFSSL_PKCS8_PRIV_KEY_INFO* wolfSSL_d2i_PKCS8_PKEY(
17371742
}
17381743
if (algId == DHk) {
17391744
/* Special case for DH as we expect the DER buffer to be always
1740-
* be in PKCS8 format */
1745+
* in PKCS8 format */
17411746
rawDer.buffer = pkcs8Der->buffer;
17421747
rawDer.length = inOutIdx + (word32)ret;
17431748
}
1749+
#ifdef HAVE_DILITHIUM
1750+
else if (
1751+
#ifdef WOLFSSL_DILITHIUM_FIPS204_DRAFT
1752+
(algId == DILITHIUM_LEVEL2k) ||
1753+
(algId == DILITHIUM_LEVEL3k) ||
1754+
(algId == DILITHIUM_LEVEL5k) ||
1755+
#endif
1756+
(algId == ML_DSA_LEVEL2k) ||
1757+
(algId == ML_DSA_LEVEL3k) ||
1758+
(algId == ML_DSA_LEVEL5k)) {
1759+
1760+
/* Keep full PKCS#8 wrapper for level recovery from
1761+
* AlgorithmIdentifier parameters */
1762+
rawDer.buffer = pkcs8Der->buffer;
1763+
rawDer.length = inOutIdx + (word32)ret;
1764+
if (GetSequence(pkcs8Der->buffer, &outerIdx, &outerLen,
1765+
pkcs8Der->length) >= 0) {
1766+
rawDer.length = outerIdx + (word32)outerLen;
1767+
}
1768+
}
1769+
#endif
17441770
else {
17451771
rawDer.buffer = pkcs8Der->buffer + inOutIdx;
17461772
rawDer.length = (word32)ret;

0 commit comments

Comments
 (0)