Skip to content

Commit 3f7d785

Browse files
committed
Add ML-DSA SPKI/PKCS#8 DER support to d2i_PUBKEY and d2i_PrivateKey, fix d2iTryAltDhKey returning 0 on non-DH input.
1 parent 1c9555c commit 3f7d785

9 files changed

Lines changed: 3407 additions & 30 deletions

File tree

certs/mldsa/README.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
ML-DSA (FIPS 204) test key material for wolfSSL tests.
2+
3+
File variants, per level N in {44, 65, 87}:
4+
mldsa<N>_bare-seed.der raw 32-byte seed
5+
mldsa<N>_seed-only.der PKCS#8 with seed-only private key
6+
mldsa<N>_bare-priv.der raw expanded private key
7+
mldsa<N>_priv-only.der PKCS#8 with expanded-only private key
8+
mldsa<N>_seed-priv.der PKCS#8 with seed-and-expanded private key
9+
mldsa<N>_oqskeypair.der liboqs concatenated (priv || pub) format
10+
mldsa<N>_pub-spki.der SubjectPublicKeyInfo wrapping the public key
11+
12+
The *_pub-spki.der files were derived from the matching *_priv-only.der files
13+
using OpenSSL 3.5+:
14+
15+
openssl pkey -inform DER -in mldsa<N>_priv-only.der \
16+
-pubout -outform DER -out mldsa<N>_pub-spki.der
17+
18+
Regenerating the private-key variants requires producing each of the
19+
PKCS#8 shape options explicitly; OpenSSL's default output is the
20+
seed-and-expanded form.

certs/mldsa/include.am

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,24 @@
33
#
44

55
EXTRA_DIST += \
6+
certs/mldsa/README.txt \
67
certs/mldsa/mldsa44_seed-only.der \
78
certs/mldsa/mldsa44_priv-only.der \
9+
certs/mldsa/mldsa44_pub-spki.der \
810
certs/mldsa/mldsa44_seed-priv.der \
911
certs/mldsa/mldsa44_oqskeypair.der \
1012
certs/mldsa/mldsa44_bare-seed.der \
1113
certs/mldsa/mldsa44_bare-priv.der \
1214
certs/mldsa/mldsa65_seed-only.der \
1315
certs/mldsa/mldsa65_priv-only.der \
16+
certs/mldsa/mldsa65_pub-spki.der \
1417
certs/mldsa/mldsa65_seed-priv.der \
1518
certs/mldsa/mldsa65_oqskeypair.der \
1619
certs/mldsa/mldsa65_bare-seed.der \
1720
certs/mldsa/mldsa65_bare-priv.der \
1821
certs/mldsa/mldsa87_seed-only.der \
1922
certs/mldsa/mldsa87_priv-only.der \
23+
certs/mldsa/mldsa87_pub-spki.der \
2024
certs/mldsa/mldsa87_seed-priv.der \
2125
certs/mldsa/mldsa87_oqskeypair.der \
2226
certs/mldsa/mldsa87_bare-seed.der \

certs/mldsa/mldsa44_pub-spki.der

1.3 KB
Binary file not shown.

certs/mldsa/mldsa65_pub-spki.der

1.93 KB
Binary file not shown.

certs/mldsa/mldsa87_pub-spki.der

2.55 KB
Binary file not shown.

gencertbuf.pl

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2106,6 +2106,51 @@
21062106
21072107
";
21082108

2109+
# ML-DSA test key material encoded per the IETF LAMPS WG profile:
2110+
# SubjectPublicKeyInfo for public keys, PKCS#8 PrivateKeyInfo for
2111+
# private keys, using the NIST id-ml-dsa-N OIDs.
2112+
print OUT_FILE "#if defined(HAVE_DILITHIUM)\n\n";
2113+
2114+
for my $L ( [44,"WOLFSSL_NO_ML_DSA_44"],
2115+
[65,"WOLFSSL_NO_ML_DSA_65"],
2116+
[87,"WOLFSSL_NO_ML_DSA_87"] ) {
2117+
my ($n, $noLevel) = @$L;
2118+
2119+
print OUT_FILE "#if !defined($noLevel)\n\n";
2120+
2121+
print OUT_FILE "#ifndef WOLFSSL_DILITHIUM_NO_VERIFY\n";
2122+
print OUT_FILE "/* ./certs/mldsa/mldsa${n}_pub-spki.der */\n";
2123+
print OUT_FILE "static const unsigned char mldsa${n}_pub_spki[] =\n{\n";
2124+
file_to_hex("./certs/mldsa/mldsa${n}_pub-spki.der");
2125+
print OUT_FILE "};\n";
2126+
print OUT_FILE "#define sizeof_mldsa${n}_pub_spki (sizeof(mldsa${n}_pub_spki))\n";
2127+
print OUT_FILE "#endif /* !WOLFSSL_DILITHIUM_NO_VERIFY */\n\n";
2128+
2129+
print OUT_FILE "#ifndef WOLFSSL_DILITHIUM_NO_SIGN\n";
2130+
print OUT_FILE "/* ./certs/mldsa/mldsa${n}_priv-only.der */\n";
2131+
print OUT_FILE "static const unsigned char mldsa${n}_priv_only[] =\n{\n";
2132+
file_to_hex("./certs/mldsa/mldsa${n}_priv-only.der");
2133+
print OUT_FILE "};\n";
2134+
print OUT_FILE "#define sizeof_mldsa${n}_priv_only (sizeof(mldsa${n}_priv_only))\n";
2135+
2136+
print OUT_FILE "/* ./certs/mldsa/mldsa${n}_seed-priv.der */\n";
2137+
print OUT_FILE "static const unsigned char mldsa${n}_seed_priv[] =\n{\n";
2138+
file_to_hex("./certs/mldsa/mldsa${n}_seed-priv.der");
2139+
print OUT_FILE "};\n";
2140+
print OUT_FILE "#define sizeof_mldsa${n}_seed_priv (sizeof(mldsa${n}_seed_priv))\n";
2141+
2142+
print OUT_FILE "/* ./certs/mldsa/mldsa${n}_seed-only.der */\n";
2143+
print OUT_FILE "static const unsigned char mldsa${n}_seed_only[] =\n{\n";
2144+
file_to_hex("./certs/mldsa/mldsa${n}_seed-only.der");
2145+
print OUT_FILE "};\n";
2146+
print OUT_FILE "#define sizeof_mldsa${n}_seed_only (sizeof(mldsa${n}_seed_only))\n";
2147+
print OUT_FILE "#endif /* !WOLFSSL_DILITHIUM_NO_SIGN */\n\n";
2148+
2149+
print OUT_FILE "#endif /* !$noLevel */\n\n";
2150+
}
2151+
2152+
print OUT_FILE "#endif /* HAVE_DILITHIUM */\n\n";
2153+
21092154
# convert and print sphincs keys
21102155
print OUT_FILE "#if defined(HAVE_SPHINCS)\n\n";
21112156
for (my $i = 0; $i < $num_sphincs; $i++) {

tests/api.c

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18820,6 +18820,70 @@ defined(OPENSSL_EXTRA) && defined(WOLFSSL_DH_EXTRA)
1882018820
#endif /* !HAVE_FIPS || HAVE_FIPS_VERSION > 2 */
1882118821
#endif /* USE_CERT_BUFFERS_2048 && !NO_DH && && OPENSSL_EXTRA */
1882218822

18823+
#if defined(HAVE_DILITHIUM) && defined(WOLFSSL_WC_DILITHIUM) && \
18824+
!defined(WOLFSSL_DILITHIUM_NO_VERIFY)
18825+
18826+
#if !defined(WOLFSSL_NO_ML_DSA_44)
18827+
/* ML-DSA-44 PUBKEY test (raw key bytes) */
18828+
ExpectIntGT(BIO_write(bio, bench_dilithium_level2_pubkey,
18829+
sizeof_bench_dilithium_level2_pubkey), 0);
18830+
ExpectNotNull(pkey = d2i_PUBKEY_bio(bio, NULL));
18831+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
18832+
EVP_PKEY_free(pkey);
18833+
pkey = NULL;
18834+
18835+
/* ML-DSA-44 PUBKEY test (LAMPS SubjectPublicKeyInfo DER) */
18836+
ExpectIntGT(BIO_write(bio, mldsa44_pub_spki, sizeof_mldsa44_pub_spki), 0);
18837+
ExpectNotNull(pkey = d2i_PUBKEY_bio(bio, NULL));
18838+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
18839+
EVP_PKEY_free(pkey);
18840+
pkey = NULL;
18841+
#endif
18842+
18843+
#if !defined(WOLFSSL_NO_ML_DSA_65)
18844+
/* ML-DSA-65 PUBKEY test (raw key bytes) */
18845+
ExpectIntGT(BIO_write(bio, bench_dilithium_level3_pubkey,
18846+
sizeof_bench_dilithium_level3_pubkey), 0);
18847+
ExpectNotNull(pkey = d2i_PUBKEY_bio(bio, NULL));
18848+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
18849+
EVP_PKEY_free(pkey);
18850+
pkey = NULL;
18851+
18852+
/* ML-DSA-65 PUBKEY test (LAMPS SubjectPublicKeyInfo DER) */
18853+
ExpectIntGT(BIO_write(bio, mldsa65_pub_spki, sizeof_mldsa65_pub_spki), 0);
18854+
ExpectNotNull(pkey = d2i_PUBKEY_bio(bio, NULL));
18855+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
18856+
EVP_PKEY_free(pkey);
18857+
pkey = NULL;
18858+
#endif
18859+
18860+
#if !defined(WOLFSSL_NO_ML_DSA_87)
18861+
/* ML-DSA-87 PUBKEY test (raw key bytes) */
18862+
ExpectIntGT(BIO_write(bio, bench_dilithium_level5_pubkey,
18863+
sizeof_bench_dilithium_level5_pubkey), 0);
18864+
ExpectNotNull(pkey = d2i_PUBKEY_bio(bio, NULL));
18865+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
18866+
EVP_PKEY_free(pkey);
18867+
pkey = NULL;
18868+
18869+
/* ML-DSA-87 PUBKEY test (LAMPS SubjectPublicKeyInfo DER) */
18870+
ExpectIntGT(BIO_write(bio, mldsa87_pub_spki, sizeof_mldsa87_pub_spki), 0);
18871+
ExpectNotNull(pkey = d2i_PUBKEY_bio(bio, NULL));
18872+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
18873+
EVP_PKEY_free(pkey);
18874+
pkey = NULL;
18875+
#endif
18876+
18877+
#endif /* HAVE_DILITHIUM && WOLFSSL_WC_DILITHIUM && !NO_VERIFY */
18878+
18879+
/* Negative test, invalid input must return NULL */
18880+
{
18881+
unsigned char garbage[64];
18882+
XMEMSET(garbage, 0xA5, sizeof(garbage));
18883+
ExpectIntGT(BIO_write(bio, garbage, (int)sizeof(garbage)), 0);
18884+
ExpectNull(d2i_PUBKEY_bio(bio, NULL));
18885+
}
18886+
1882318887
BIO_free(bio);
1882418888

1882518889
(void)pkey;
@@ -18906,6 +18970,156 @@ static int test_wolfSSL_d2i_PrivateKeys_bio(void)
1890618970
}
1890718971
#endif
1890818972

18973+
#if defined(HAVE_DILITHIUM) && defined(WOLFSSL_WC_DILITHIUM) && \
18974+
!defined(WOLFSSL_DILITHIUM_NO_SIGN)
18975+
#if !defined(WOLFSSL_NO_ML_DSA_44)
18976+
/* ML-DSA-44 PrivateKey test (raw bytes) */
18977+
ExpectNotNull(bio = BIO_new(BIO_s_mem()));
18978+
ExpectIntGT(BIO_write(bio, bench_dilithium_level2_key,
18979+
sizeof_bench_dilithium_level2_key), 0);
18980+
ExpectNotNull(pkey = d2i_PrivateKey_bio(bio, NULL));
18981+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
18982+
EVP_PKEY_free(pkey);
18983+
pkey = NULL;
18984+
BIO_free(bio);
18985+
bio = NULL;
18986+
18987+
/* ML-DSA-44 PrivateKey test (LAMPS PKCS#8 priv-only DER) */
18988+
ExpectNotNull(bio = BIO_new(BIO_s_mem()));
18989+
ExpectIntGT(BIO_write(bio, mldsa44_priv_only,
18990+
sizeof_mldsa44_priv_only), 0);
18991+
ExpectNotNull(pkey = d2i_PrivateKey_bio(bio, NULL));
18992+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
18993+
EVP_PKEY_free(pkey);
18994+
pkey = NULL;
18995+
BIO_free(bio);
18996+
bio = NULL;
18997+
18998+
/* ML-DSA-44 PrivateKey test (LAMPS PKCS#8 seed-priv DER) */
18999+
ExpectNotNull(bio = BIO_new(BIO_s_mem()));
19000+
ExpectIntGT(BIO_write(bio, mldsa44_seed_priv,
19001+
sizeof_mldsa44_seed_priv), 0);
19002+
ExpectNotNull(pkey = d2i_PrivateKey_bio(bio, NULL));
19003+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
19004+
EVP_PKEY_free(pkey);
19005+
pkey = NULL;
19006+
BIO_free(bio);
19007+
bio = NULL;
19008+
19009+
#ifndef WOLFSSL_DILITHIUM_NO_MAKE_KEY
19010+
/* ML-DSA-44 PrivateKey test (LAMPS PKCS#8 seed-only DER) --
19011+
* requires wc_dilithium_make_key_from_seed to expand the seed. */
19012+
ExpectNotNull(bio = BIO_new(BIO_s_mem()));
19013+
ExpectIntGT(BIO_write(bio, mldsa44_seed_only,
19014+
sizeof_mldsa44_seed_only), 0);
19015+
ExpectNotNull(pkey = d2i_PrivateKey_bio(bio, NULL));
19016+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
19017+
EVP_PKEY_free(pkey);
19018+
pkey = NULL;
19019+
BIO_free(bio);
19020+
bio = NULL;
19021+
#endif
19022+
#endif
19023+
19024+
#if !defined(WOLFSSL_NO_ML_DSA_65)
19025+
/* ML-DSA-65 PrivateKey test (raw bytes) */
19026+
ExpectNotNull(bio = BIO_new(BIO_s_mem()));
19027+
ExpectIntGT(BIO_write(bio, bench_dilithium_level3_key,
19028+
sizeof_bench_dilithium_level3_key), 0);
19029+
ExpectNotNull(pkey = d2i_PrivateKey_bio(bio, NULL));
19030+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
19031+
EVP_PKEY_free(pkey);
19032+
pkey = NULL;
19033+
BIO_free(bio);
19034+
bio = NULL;
19035+
19036+
/* ML-DSA-65 PrivateKey test (LAMPS PKCS#8 priv-only DER) */
19037+
ExpectNotNull(bio = BIO_new(BIO_s_mem()));
19038+
ExpectIntGT(BIO_write(bio, mldsa65_priv_only,
19039+
sizeof_mldsa65_priv_only), 0);
19040+
ExpectNotNull(pkey = d2i_PrivateKey_bio(bio, NULL));
19041+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
19042+
EVP_PKEY_free(pkey);
19043+
pkey = NULL;
19044+
BIO_free(bio);
19045+
bio = NULL;
19046+
19047+
/* ML-DSA-65 PrivateKey test (LAMPS PKCS#8 seed-priv DER) */
19048+
ExpectNotNull(bio = BIO_new(BIO_s_mem()));
19049+
ExpectIntGT(BIO_write(bio, mldsa65_seed_priv,
19050+
sizeof_mldsa65_seed_priv), 0);
19051+
ExpectNotNull(pkey = d2i_PrivateKey_bio(bio, NULL));
19052+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
19053+
EVP_PKEY_free(pkey);
19054+
pkey = NULL;
19055+
BIO_free(bio);
19056+
bio = NULL;
19057+
19058+
#ifndef WOLFSSL_DILITHIUM_NO_MAKE_KEY
19059+
/* ML-DSA-65 PrivateKey test (LAMPS PKCS#8 seed-only DER) --
19060+
* requires wc_dilithium_make_key_from_seed to expand the seed. */
19061+
ExpectNotNull(bio = BIO_new(BIO_s_mem()));
19062+
ExpectIntGT(BIO_write(bio, mldsa65_seed_only,
19063+
sizeof_mldsa65_seed_only), 0);
19064+
ExpectNotNull(pkey = d2i_PrivateKey_bio(bio, NULL));
19065+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
19066+
EVP_PKEY_free(pkey);
19067+
pkey = NULL;
19068+
BIO_free(bio);
19069+
bio = NULL;
19070+
#endif
19071+
#endif
19072+
19073+
#if !defined(WOLFSSL_NO_ML_DSA_87)
19074+
/* ML-DSA-87 PrivateKey test (raw bytes) */
19075+
ExpectNotNull(bio = BIO_new(BIO_s_mem()));
19076+
ExpectIntGT(BIO_write(bio, bench_dilithium_level5_key,
19077+
sizeof_bench_dilithium_level5_key), 0);
19078+
ExpectNotNull(pkey = d2i_PrivateKey_bio(bio, NULL));
19079+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
19080+
EVP_PKEY_free(pkey);
19081+
pkey = NULL;
19082+
BIO_free(bio);
19083+
bio = NULL;
19084+
19085+
/* ML-DSA-87 PrivateKey test (LAMPS PKCS#8 priv-only DER) */
19086+
ExpectNotNull(bio = BIO_new(BIO_s_mem()));
19087+
ExpectIntGT(BIO_write(bio, mldsa87_priv_only,
19088+
sizeof_mldsa87_priv_only), 0);
19089+
ExpectNotNull(pkey = d2i_PrivateKey_bio(bio, NULL));
19090+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
19091+
EVP_PKEY_free(pkey);
19092+
pkey = NULL;
19093+
BIO_free(bio);
19094+
bio = NULL;
19095+
19096+
/* ML-DSA-87 PrivateKey test (LAMPS PKCS#8 seed-priv DER) */
19097+
ExpectNotNull(bio = BIO_new(BIO_s_mem()));
19098+
ExpectIntGT(BIO_write(bio, mldsa87_seed_priv,
19099+
sizeof_mldsa87_seed_priv), 0);
19100+
ExpectNotNull(pkey = d2i_PrivateKey_bio(bio, NULL));
19101+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
19102+
EVP_PKEY_free(pkey);
19103+
pkey = NULL;
19104+
BIO_free(bio);
19105+
bio = NULL;
19106+
19107+
#ifndef WOLFSSL_DILITHIUM_NO_MAKE_KEY
19108+
/* ML-DSA-87 PrivateKey test (LAMPS PKCS#8 seed-only DER) --
19109+
* requires wc_dilithium_make_key_from_seed to expand the seed. */
19110+
ExpectNotNull(bio = BIO_new(BIO_s_mem()));
19111+
ExpectIntGT(BIO_write(bio, mldsa87_seed_only,
19112+
sizeof_mldsa87_seed_only), 0);
19113+
ExpectNotNull(pkey = d2i_PrivateKey_bio(bio, NULL));
19114+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
19115+
EVP_PKEY_free(pkey);
19116+
pkey = NULL;
19117+
BIO_free(bio);
19118+
bio = NULL;
19119+
#endif
19120+
#endif
19121+
#endif /* HAVE_DILITHIUM && WOLFSSL_WC_DILITHIUM && !NO_SIGN */
19122+
1890919123
ExpectNotNull(bio = BIO_new(BIO_s_mem()));
1891019124
#ifndef NO_WOLFSSL_SERVER
1891119125
ExpectNotNull(ctx = SSL_CTX_new(wolfSSLv23_server_method()));
@@ -18975,6 +19189,18 @@ static int test_wolfSSL_d2i_PrivateKeys_bio(void)
1897519189
RSA_free(rsa);
1897619190
}
1897719191
#endif /* WOLFSSL_KEY_GEN && !NO_RSA */
19192+
19193+
/* Negative test, invalid input must return NULL */
19194+
{
19195+
BIO* nbio = NULL;
19196+
unsigned char garbage[64];
19197+
XMEMSET(garbage, 0xA5, sizeof(garbage));
19198+
ExpectNotNull(nbio = BIO_new(BIO_s_mem()));
19199+
ExpectIntGT(BIO_write(nbio, garbage, (int)sizeof(garbage)), 0);
19200+
ExpectNull(d2i_PrivateKey_bio(nbio, NULL));
19201+
BIO_free(nbio);
19202+
}
19203+
1897819204
SSL_CTX_free(ctx);
1897919205
ctx = NULL;
1898019206
BIO_free(bio);

0 commit comments

Comments
 (0)