Skip to content

Commit b74500b

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 353a379 commit b74500b

9 files changed

Lines changed: 3406 additions & 23 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: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,21 @@
55
EXTRA_DIST += \
66
certs/mldsa/mldsa44_seed-only.der \
77
certs/mldsa/mldsa44_priv-only.der \
8+
certs/mldsa/mldsa44_pub-spki.der \
89
certs/mldsa/mldsa44_seed-priv.der \
910
certs/mldsa/mldsa44_oqskeypair.der \
1011
certs/mldsa/mldsa44_bare-seed.der \
1112
certs/mldsa/mldsa44_bare-priv.der \
1213
certs/mldsa/mldsa65_seed-only.der \
1314
certs/mldsa/mldsa65_priv-only.der \
15+
certs/mldsa/mldsa65_pub-spki.der \
1416
certs/mldsa/mldsa65_seed-priv.der \
1517
certs/mldsa/mldsa65_oqskeypair.der \
1618
certs/mldsa/mldsa65_bare-seed.der \
1719
certs/mldsa/mldsa65_bare-priv.der \
1820
certs/mldsa/mldsa87_seed-only.der \
1921
certs/mldsa/mldsa87_priv-only.der \
22+
certs/mldsa/mldsa87_pub-spki.der \
2023
certs/mldsa/mldsa87_seed-priv.der \
2124
certs/mldsa/mldsa87_oqskeypair.der \
2225
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
@@ -18645,6 +18645,70 @@ defined(OPENSSL_EXTRA) && defined(WOLFSSL_DH_EXTRA)
1864518645
#endif /* !HAVE_FIPS || HAVE_FIPS_VERSION > 2 */
1864618646
#endif /* USE_CERT_BUFFERS_2048 && !NO_DH && && OPENSSL_EXTRA */
1864718647

18648+
#if defined(HAVE_DILITHIUM) && defined(WOLFSSL_WC_DILITHIUM) && \
18649+
!defined(WOLFSSL_DILITHIUM_NO_VERIFY)
18650+
18651+
#if !defined(WOLFSSL_NO_ML_DSA_44)
18652+
/* ML-DSA-44 PUBKEY test (raw key bytes) */
18653+
ExpectIntGT(BIO_write(bio, bench_dilithium_level2_pubkey,
18654+
sizeof_bench_dilithium_level2_pubkey), 0);
18655+
ExpectNotNull(pkey = d2i_PUBKEY_bio(bio, NULL));
18656+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
18657+
EVP_PKEY_free(pkey);
18658+
pkey = NULL;
18659+
18660+
/* ML-DSA-44 PUBKEY test (LAMPS SubjectPublicKeyInfo DER) */
18661+
ExpectIntGT(BIO_write(bio, mldsa44_pub_spki, sizeof_mldsa44_pub_spki), 0);
18662+
ExpectNotNull(pkey = d2i_PUBKEY_bio(bio, NULL));
18663+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
18664+
EVP_PKEY_free(pkey);
18665+
pkey = NULL;
18666+
#endif
18667+
18668+
#if !defined(WOLFSSL_NO_ML_DSA_65)
18669+
/* ML-DSA-65 PUBKEY test (raw key bytes) */
18670+
ExpectIntGT(BIO_write(bio, bench_dilithium_level3_pubkey,
18671+
sizeof_bench_dilithium_level3_pubkey), 0);
18672+
ExpectNotNull(pkey = d2i_PUBKEY_bio(bio, NULL));
18673+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
18674+
EVP_PKEY_free(pkey);
18675+
pkey = NULL;
18676+
18677+
/* ML-DSA-65 PUBKEY test (LAMPS SubjectPublicKeyInfo DER) */
18678+
ExpectIntGT(BIO_write(bio, mldsa65_pub_spki, sizeof_mldsa65_pub_spki), 0);
18679+
ExpectNotNull(pkey = d2i_PUBKEY_bio(bio, NULL));
18680+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
18681+
EVP_PKEY_free(pkey);
18682+
pkey = NULL;
18683+
#endif
18684+
18685+
#if !defined(WOLFSSL_NO_ML_DSA_87)
18686+
/* ML-DSA-87 PUBKEY test (raw key bytes) */
18687+
ExpectIntGT(BIO_write(bio, bench_dilithium_level5_pubkey,
18688+
sizeof_bench_dilithium_level5_pubkey), 0);
18689+
ExpectNotNull(pkey = d2i_PUBKEY_bio(bio, NULL));
18690+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
18691+
EVP_PKEY_free(pkey);
18692+
pkey = NULL;
18693+
18694+
/* ML-DSA-87 PUBKEY test (LAMPS SubjectPublicKeyInfo DER) */
18695+
ExpectIntGT(BIO_write(bio, mldsa87_pub_spki, sizeof_mldsa87_pub_spki), 0);
18696+
ExpectNotNull(pkey = d2i_PUBKEY_bio(bio, NULL));
18697+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
18698+
EVP_PKEY_free(pkey);
18699+
pkey = NULL;
18700+
#endif
18701+
18702+
#endif /* HAVE_DILITHIUM && WOLFSSL_WC_DILITHIUM && !NO_VERIFY */
18703+
18704+
/* Negative test, invalid input must return NULL */
18705+
{
18706+
unsigned char garbage[64];
18707+
XMEMSET(garbage, 0xA5, sizeof(garbage));
18708+
ExpectIntGT(BIO_write(bio, garbage, (int)sizeof(garbage)), 0);
18709+
ExpectNull(d2i_PUBKEY_bio(bio, NULL));
18710+
}
18711+
1864818712
BIO_free(bio);
1864918713

1865018714
(void)pkey;
@@ -18731,6 +18795,156 @@ static int test_wolfSSL_d2i_PrivateKeys_bio(void)
1873118795
}
1873218796
#endif
1873318797

18798+
#if defined(HAVE_DILITHIUM) && defined(WOLFSSL_WC_DILITHIUM) && \
18799+
!defined(WOLFSSL_DILITHIUM_NO_SIGN)
18800+
#if !defined(WOLFSSL_NO_ML_DSA_44)
18801+
/* ML-DSA-44 PrivateKey test (raw bytes) */
18802+
ExpectNotNull(bio = BIO_new(BIO_s_mem()));
18803+
ExpectIntGT(BIO_write(bio, bench_dilithium_level2_key,
18804+
sizeof_bench_dilithium_level2_key), 0);
18805+
ExpectNotNull(pkey = d2i_PrivateKey_bio(bio, NULL));
18806+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
18807+
EVP_PKEY_free(pkey);
18808+
pkey = NULL;
18809+
BIO_free(bio);
18810+
bio = NULL;
18811+
18812+
/* ML-DSA-44 PrivateKey test (LAMPS PKCS#8 priv-only DER) */
18813+
ExpectNotNull(bio = BIO_new(BIO_s_mem()));
18814+
ExpectIntGT(BIO_write(bio, mldsa44_priv_only,
18815+
sizeof_mldsa44_priv_only), 0);
18816+
ExpectNotNull(pkey = d2i_PrivateKey_bio(bio, NULL));
18817+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
18818+
EVP_PKEY_free(pkey);
18819+
pkey = NULL;
18820+
BIO_free(bio);
18821+
bio = NULL;
18822+
18823+
/* ML-DSA-44 PrivateKey test (LAMPS PKCS#8 seed-priv DER) */
18824+
ExpectNotNull(bio = BIO_new(BIO_s_mem()));
18825+
ExpectIntGT(BIO_write(bio, mldsa44_seed_priv,
18826+
sizeof_mldsa44_seed_priv), 0);
18827+
ExpectNotNull(pkey = d2i_PrivateKey_bio(bio, NULL));
18828+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
18829+
EVP_PKEY_free(pkey);
18830+
pkey = NULL;
18831+
BIO_free(bio);
18832+
bio = NULL;
18833+
18834+
#ifndef WOLFSSL_DILITHIUM_NO_MAKE_KEY
18835+
/* ML-DSA-44 PrivateKey test (LAMPS PKCS#8 seed-only DER) --
18836+
* requires wc_dilithium_make_key_from_seed to expand the seed. */
18837+
ExpectNotNull(bio = BIO_new(BIO_s_mem()));
18838+
ExpectIntGT(BIO_write(bio, mldsa44_seed_only,
18839+
sizeof_mldsa44_seed_only), 0);
18840+
ExpectNotNull(pkey = d2i_PrivateKey_bio(bio, NULL));
18841+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
18842+
EVP_PKEY_free(pkey);
18843+
pkey = NULL;
18844+
BIO_free(bio);
18845+
bio = NULL;
18846+
#endif
18847+
#endif
18848+
18849+
#if !defined(WOLFSSL_NO_ML_DSA_65)
18850+
/* ML-DSA-65 PrivateKey test (raw bytes) */
18851+
ExpectNotNull(bio = BIO_new(BIO_s_mem()));
18852+
ExpectIntGT(BIO_write(bio, bench_dilithium_level3_key,
18853+
sizeof_bench_dilithium_level3_key), 0);
18854+
ExpectNotNull(pkey = d2i_PrivateKey_bio(bio, NULL));
18855+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
18856+
EVP_PKEY_free(pkey);
18857+
pkey = NULL;
18858+
BIO_free(bio);
18859+
bio = NULL;
18860+
18861+
/* ML-DSA-65 PrivateKey test (LAMPS PKCS#8 priv-only DER) */
18862+
ExpectNotNull(bio = BIO_new(BIO_s_mem()));
18863+
ExpectIntGT(BIO_write(bio, mldsa65_priv_only,
18864+
sizeof_mldsa65_priv_only), 0);
18865+
ExpectNotNull(pkey = d2i_PrivateKey_bio(bio, NULL));
18866+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
18867+
EVP_PKEY_free(pkey);
18868+
pkey = NULL;
18869+
BIO_free(bio);
18870+
bio = NULL;
18871+
18872+
/* ML-DSA-65 PrivateKey test (LAMPS PKCS#8 seed-priv DER) */
18873+
ExpectNotNull(bio = BIO_new(BIO_s_mem()));
18874+
ExpectIntGT(BIO_write(bio, mldsa65_seed_priv,
18875+
sizeof_mldsa65_seed_priv), 0);
18876+
ExpectNotNull(pkey = d2i_PrivateKey_bio(bio, NULL));
18877+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
18878+
EVP_PKEY_free(pkey);
18879+
pkey = NULL;
18880+
BIO_free(bio);
18881+
bio = NULL;
18882+
18883+
#ifndef WOLFSSL_DILITHIUM_NO_MAKE_KEY
18884+
/* ML-DSA-65 PrivateKey test (LAMPS PKCS#8 seed-only DER) --
18885+
* requires wc_dilithium_make_key_from_seed to expand the seed. */
18886+
ExpectNotNull(bio = BIO_new(BIO_s_mem()));
18887+
ExpectIntGT(BIO_write(bio, mldsa65_seed_only,
18888+
sizeof_mldsa65_seed_only), 0);
18889+
ExpectNotNull(pkey = d2i_PrivateKey_bio(bio, NULL));
18890+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
18891+
EVP_PKEY_free(pkey);
18892+
pkey = NULL;
18893+
BIO_free(bio);
18894+
bio = NULL;
18895+
#endif
18896+
#endif
18897+
18898+
#if !defined(WOLFSSL_NO_ML_DSA_87)
18899+
/* ML-DSA-87 PrivateKey test (raw bytes) */
18900+
ExpectNotNull(bio = BIO_new(BIO_s_mem()));
18901+
ExpectIntGT(BIO_write(bio, bench_dilithium_level5_key,
18902+
sizeof_bench_dilithium_level5_key), 0);
18903+
ExpectNotNull(pkey = d2i_PrivateKey_bio(bio, NULL));
18904+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
18905+
EVP_PKEY_free(pkey);
18906+
pkey = NULL;
18907+
BIO_free(bio);
18908+
bio = NULL;
18909+
18910+
/* ML-DSA-87 PrivateKey test (LAMPS PKCS#8 priv-only DER) */
18911+
ExpectNotNull(bio = BIO_new(BIO_s_mem()));
18912+
ExpectIntGT(BIO_write(bio, mldsa87_priv_only,
18913+
sizeof_mldsa87_priv_only), 0);
18914+
ExpectNotNull(pkey = d2i_PrivateKey_bio(bio, NULL));
18915+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
18916+
EVP_PKEY_free(pkey);
18917+
pkey = NULL;
18918+
BIO_free(bio);
18919+
bio = NULL;
18920+
18921+
/* ML-DSA-87 PrivateKey test (LAMPS PKCS#8 seed-priv DER) */
18922+
ExpectNotNull(bio = BIO_new(BIO_s_mem()));
18923+
ExpectIntGT(BIO_write(bio, mldsa87_seed_priv,
18924+
sizeof_mldsa87_seed_priv), 0);
18925+
ExpectNotNull(pkey = d2i_PrivateKey_bio(bio, NULL));
18926+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
18927+
EVP_PKEY_free(pkey);
18928+
pkey = NULL;
18929+
BIO_free(bio);
18930+
bio = NULL;
18931+
18932+
#ifndef WOLFSSL_DILITHIUM_NO_MAKE_KEY
18933+
/* ML-DSA-87 PrivateKey test (LAMPS PKCS#8 seed-only DER) --
18934+
* requires wc_dilithium_make_key_from_seed to expand the seed. */
18935+
ExpectNotNull(bio = BIO_new(BIO_s_mem()));
18936+
ExpectIntGT(BIO_write(bio, mldsa87_seed_only,
18937+
sizeof_mldsa87_seed_only), 0);
18938+
ExpectNotNull(pkey = d2i_PrivateKey_bio(bio, NULL));
18939+
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_DILITHIUM);
18940+
EVP_PKEY_free(pkey);
18941+
pkey = NULL;
18942+
BIO_free(bio);
18943+
bio = NULL;
18944+
#endif
18945+
#endif
18946+
#endif /* HAVE_DILITHIUM && WOLFSSL_WC_DILITHIUM && !NO_SIGN */
18947+
1873418948
ExpectNotNull(bio = BIO_new(BIO_s_mem()));
1873518949
#ifndef NO_WOLFSSL_SERVER
1873618950
ExpectNotNull(ctx = SSL_CTX_new(wolfSSLv23_server_method()));
@@ -18800,6 +19014,18 @@ static int test_wolfSSL_d2i_PrivateKeys_bio(void)
1880019014
RSA_free(rsa);
1880119015
}
1880219016
#endif /* WOLFSSL_KEY_GEN && !NO_RSA */
19017+
19018+
/* Negative test, invalid input must return NULL */
19019+
{
19020+
BIO* nbio = NULL;
19021+
unsigned char garbage[64];
19022+
XMEMSET(garbage, 0xA5, sizeof(garbage));
19023+
ExpectNotNull(nbio = BIO_new(BIO_s_mem()));
19024+
ExpectIntGT(BIO_write(nbio, garbage, (int)sizeof(garbage)), 0);
19025+
ExpectNull(d2i_PrivateKey_bio(nbio, NULL));
19026+
BIO_free(nbio);
19027+
}
19028+
1880319029
SSL_CTX_free(ctx);
1880419030
ctx = NULL;
1880519031
BIO_free(bio);

0 commit comments

Comments
 (0)