mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-08-14 13:11:27 +02:00
PKCS#8 algId check and DER-only typed d2i for ML-DSA
This commit is contained in:
@@ -30,6 +30,9 @@
|
||||
|
||||
#include <wolfssl/openssl/evp.h>
|
||||
#include <wolfssl/openssl/kdf.h>
|
||||
#ifdef WOLFSSL_HAVE_MLDSA
|
||||
#include <wolfssl/wolfcrypt/wc_mldsa.h>
|
||||
#endif
|
||||
#include <tests/api/api.h>
|
||||
#include <tests/api/test_evp_pkey.h>
|
||||
|
||||
@@ -3127,6 +3130,75 @@ int test_wolfSSL_d2i_PUBKEY_mldsa_reuse(void)
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
/* Typed d2i entry points for ML-DSA: a PKCS#8 key of another algorithm
|
||||
* and raw (non-DER) bytes must both be rejected; a matching PKCS#8
|
||||
* ML-DSA key must decode. */
|
||||
int test_wolfSSL_d2i_PrivateKey_mldsa(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(OPENSSL_EXTRA) && defined(WOLFSSL_HAVE_MLDSA) && \
|
||||
defined(WOLFSSL_MLDSA_PRIVATE_KEY) && !defined(WOLFSSL_MLDSA_NO_ASN1) && \
|
||||
!defined(WOLFSSL_NO_ML_DSA_44) && !defined(NO_RSA) && \
|
||||
!defined(NO_FILESYSTEM)
|
||||
WOLFSSL_EVP_PKEY* pkey = NULL;
|
||||
const unsigned char* p;
|
||||
unsigned char mldsaDer[4096];
|
||||
unsigned char rsaDer[2048];
|
||||
unsigned char rawBlob[2560]; /* ML-DSA-44 raw private key size */
|
||||
int mldsaSz = 0;
|
||||
int rsaSz = 0;
|
||||
XFILE f = XBADFILE;
|
||||
|
||||
ExpectTrue((f = XFOPEN("./certs/mldsa/mldsa44-key.der", "rb"))
|
||||
!= XBADFILE);
|
||||
ExpectIntGT(mldsaSz = (int)XFREAD(mldsaDer, 1, sizeof(mldsaDer), f), 0);
|
||||
if (f != XBADFILE) {
|
||||
XFCLOSE(f);
|
||||
f = XBADFILE;
|
||||
}
|
||||
ExpectTrue((f = XFOPEN("./certs/server-keyPkcs8.der", "rb")) != XBADFILE);
|
||||
ExpectIntGT(rsaSz = (int)XFREAD(rsaDer, 1, sizeof(rsaDer), f), 0);
|
||||
if (f != XBADFILE) {
|
||||
XFCLOSE(f);
|
||||
}
|
||||
|
||||
/* PKCS#8 ML-DSA private key decodes with the matching type. */
|
||||
p = mldsaDer;
|
||||
ExpectNotNull(pkey = wolfSSL_d2i_PrivateKey(WC_EVP_PKEY_DILITHIUM, NULL,
|
||||
&p, (long)mldsaSz));
|
||||
ExpectIntEQ(wolfSSL_EVP_PKEY_id(pkey), WC_EVP_PKEY_DILITHIUM);
|
||||
wolfSSL_EVP_PKEY_free(pkey);
|
||||
pkey = NULL;
|
||||
|
||||
/* PKCS#8 RSA key requested as ML-DSA is rejected by the algId check. */
|
||||
p = rsaDer;
|
||||
ExpectNull(wolfSSL_d2i_PrivateKey(WC_EVP_PKEY_DILITHIUM, NULL, &p,
|
||||
(long)rsaSz));
|
||||
|
||||
/* Raw (non-DER) private key bytes are rejected: d2i is a DER API, the
|
||||
* size-keyed raw import is for the auto-detect path only. Use genuine
|
||||
* raw bytes so the rejection is due to the format, not the contents. */
|
||||
{
|
||||
MlDsaKey mldsa;
|
||||
word32 idx = 0;
|
||||
word32 rawSz = (word32)sizeof(rawBlob);
|
||||
|
||||
ExpectIntEQ(wc_MlDsaKey_Init(&mldsa, NULL, INVALID_DEVID), 0);
|
||||
PRIVATE_KEY_UNLOCK();
|
||||
ExpectIntEQ(wc_MlDsaKey_PrivateKeyDecode(&mldsa, mldsaDer,
|
||||
(word32)mldsaSz, &idx), 0);
|
||||
ExpectIntEQ(wc_MlDsaKey_ExportPrivRaw(&mldsa, rawBlob, &rawSz), 0);
|
||||
PRIVATE_KEY_LOCK();
|
||||
wc_MlDsaKey_Free(&mldsa);
|
||||
|
||||
p = rawBlob;
|
||||
ExpectNull(wolfSSL_d2i_PrivateKey(WC_EVP_PKEY_DILITHIUM, NULL, &p,
|
||||
(long)rawSz));
|
||||
}
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
int test_wolfSSL_EVP_PKEY_x25519(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
|
||||
@@ -72,6 +72,7 @@ int test_wolfSSL_EVP_PKEY_ed25519(void);
|
||||
int test_wolfSSL_CTX_use_PrivateKey_ed25519(void);
|
||||
int test_wolfSSL_EVP_PKEY_ed448(void);
|
||||
int test_wolfSSL_d2i_PUBKEY_mldsa_reuse(void);
|
||||
int test_wolfSSL_d2i_PrivateKey_mldsa(void);
|
||||
int test_wolfSSL_EVP_PKEY_x25519(void);
|
||||
int test_wolfSSL_EVP_PKEY_x448(void);
|
||||
int test_wolfSSL_EVP_PKEY_encoded_public_key(void);
|
||||
@@ -127,6 +128,7 @@ int test_wolfSSL_d2i_PrivateKey_reuse_resets_state(void);
|
||||
TEST_DECL_GROUP("evp_pkey", test_wolfSSL_CTX_use_PrivateKey_ed25519), \
|
||||
TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_ed448), \
|
||||
TEST_DECL_GROUP("evp_pkey", test_wolfSSL_d2i_PUBKEY_mldsa_reuse), \
|
||||
TEST_DECL_GROUP("evp_pkey", test_wolfSSL_d2i_PrivateKey_mldsa), \
|
||||
TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_x25519), \
|
||||
TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_x448), \
|
||||
TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_encoded_public_key), \
|
||||
|
||||
+19
-5
@@ -1032,13 +1032,15 @@ static int d2iTryFalconKey(WOLFSSL_EVP_PKEY** out, const unsigned char* mem,
|
||||
* @param [in] priv 1 means private key, 0 means public key.
|
||||
* @param [in] prePopulated 1 means *out already holds the input bytes
|
||||
* so the d2i_make_pkey allocate/copy is skipped.
|
||||
* @param [in] allowRaw 1 means size-keyed raw key bytes are accepted
|
||||
* in addition to DER (auto-detect path only).
|
||||
* @return 1 on success.
|
||||
* @return 0 when input was recognized as this key type but
|
||||
* object creation/import failed.
|
||||
* @return WOLFSSL_FATAL_ERROR when input is not this key type.
|
||||
*/
|
||||
static int d2iTryMlDsaKey(WOLFSSL_EVP_PKEY** out, const unsigned char* mem,
|
||||
long memSz, int priv, int prePopulated)
|
||||
long memSz, int priv, int prePopulated, int allowRaw)
|
||||
{
|
||||
static const byte levels[] = { WC_ML_DSA_44, WC_ML_DSA_65, WC_ML_DSA_87 };
|
||||
word32 inSz = (word32)memSz;
|
||||
@@ -1063,8 +1065,9 @@ static int d2iTryMlDsaKey(WOLFSSL_EVP_PKEY** out, const unsigned char* mem,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Raw key bytes are size-keyed, try each level */
|
||||
numLevels = (int)(sizeof(levels) / sizeof(levels[0]));
|
||||
/* Raw key bytes are size-keyed, try each level. Only the auto-detect
|
||||
* path accepts raw bytes; the typed d2i entry points are DER APIs. */
|
||||
numLevels = allowRaw ? (int)(sizeof(levels) / sizeof(levels[0])) : 0;
|
||||
for (i = 0; i < numLevels && !isMlDsa; i++) {
|
||||
if (wc_MlDsaKey_SetParams(mldsa, levels[i]) != 0) {
|
||||
continue;
|
||||
@@ -1226,7 +1229,7 @@ static WOLFSSL_EVP_PKEY* d2i_evp_pkey_try(WOLFSSL_EVP_PKEY** out,
|
||||
else
|
||||
#endif /* HAVE_FALCON */
|
||||
#ifdef WOLFSSL_HAVE_MLDSA
|
||||
if (d2iTryMlDsaKey(&pkey, *in, inSz, priv, 0) >= 0) {
|
||||
if (d2iTryMlDsaKey(&pkey, *in, inSz, priv, 0, 1) >= 0) {
|
||||
found = 1;
|
||||
}
|
||||
else
|
||||
@@ -1463,6 +1466,17 @@ static WOLFSSL_EVP_PKEY* d2i_evp_pkey(int type, WOLFSSL_EVP_PKEY** out,
|
||||
#endif
|
||||
#ifdef HAVE_ED448
|
||||
|| (type == WC_EVP_PKEY_ED448 && algId != ED448k)
|
||||
#endif
|
||||
#ifdef WOLFSSL_HAVE_MLDSA
|
||||
|| (type == WC_EVP_PKEY_DILITHIUM &&
|
||||
algId != ML_DSA_44k && algId != ML_DSA_65k &&
|
||||
algId != ML_DSA_87k
|
||||
#ifdef WOLFSSL_MLDSA_FIPS204_DRAFT
|
||||
&& algId != DILITHIUM_LEVEL2k
|
||||
&& algId != DILITHIUM_LEVEL3k
|
||||
&& algId != DILITHIUM_LEVEL5k
|
||||
#endif
|
||||
)
|
||||
#endif
|
||||
) {
|
||||
WOLFSSL_MSG("PKCS8 does not match EVP key type");
|
||||
@@ -1592,7 +1606,7 @@ static WOLFSSL_EVP_PKEY* d2i_evp_pkey(int type, WOLFSSL_EVP_PKEY** out,
|
||||
#if defined(WOLFSSL_HAVE_MLDSA)
|
||||
case WC_EVP_PKEY_DILITHIUM:
|
||||
/* local already holds the input bytes: prePopulated=1. */
|
||||
if (d2iTryMlDsaKey(&local, p, local->pkey_sz, priv, 1) != 1) {
|
||||
if (d2iTryMlDsaKey(&local, p, local->pkey_sz, priv, 1, 0) != 1) {
|
||||
wolfSSL_EVP_PKEY_free(local);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user