From a82d1a6b12f4e40e07c95a11f559e380cd05ffc1 Mon Sep 17 00:00:00 2001 From: Koji Takeda Date: Mon, 14 Jul 2025 10:35:31 +0900 Subject: [PATCH] Support importing seed of ML-DSA key --- tests/api/test_mldsa.c | 253 ++++++++++++++++++++++++++++++++------ tests/api/test_mldsa.h | 32 ++--- wolfcrypt/src/asn.c | 134 ++++++++++++-------- wolfcrypt/src/dilithium.c | 112 ++++++++--------- wolfssl/wolfcrypt/asn.h | 10 +- 5 files changed, 376 insertions(+), 165 deletions(-) diff --git a/tests/api/test_mldsa.c b/tests/api/test_mldsa.c index 873a085c9..132b797c9 100644 --- a/tests/api/test_mldsa.c +++ b/tests/api/test_mldsa.c @@ -16658,7 +16658,219 @@ int test_wc_dilithium_verify_kats(void) return EXPECT_RESULT(); } -int test_mldsa_pkcs8(void) +#if !defined(NO_ASN) && defined(HAVE_PKCS8) && \ + defined(HAVE_DILITHIUM) && defined(WOLFSSL_WC_DILITHIUM) && \ + !defined(WOLFSSL_DILITHIUM_NO_ASN1) && defined(WOLFSSL_ASN_TEMPLATE) +static struct { + const char* fileName; + byte level; + /* 0: Unsupported, 1: Supported*/ + int p8_lv; /* Support PKCS8 format with specifying level */ + int p8_nolv; /* Support PKCS8 format without specifying level */ + int trad_lv; /* Support traditional format with specifying level */ + int trad_nolv; /* Support traditional format without specifying level */ +} ossl_form[] = { + /* + * Generated test files with the following commands: + * openssl genpkey -outform DER -algorithm ${ALGO} \ + * -provparam ml-dsa.output_formats=${OUT_FORM} -out ${OUT_FILE} + */ + + /* ALGO=ML-DSA-44, OUT_FORM=seed-only, OUT_FILE=mldsa44_seed-only.der */ + {"certs/mldsa/mldsa44_seed-only.der", WC_ML_DSA_44, 1, 1, 1, 0}, + /* ALGO=ML-DSA-44, OUT_FORM=priv-only, OUT_FILE=mldsa44_priv-only.der */ + {"certs/mldsa/mldsa44_priv-only.der", WC_ML_DSA_44, 1, 1, 1, 0}, + /* ALGO=ML-DSA-44, OUT_FORM=seed-priv, OUT_FILE=mldsa44_seed-priv.der */ + {"certs/mldsa/mldsa44_seed-priv.der", WC_ML_DSA_44, 1, 1, 1, 0}, + /* ALGO=ML-DSA-44, OUT_FORM=oqskeypair, OUT_FILE=mldsa44_oqskeypair.der */ + {"certs/mldsa/mldsa44_oqskeypair.der", WC_ML_DSA_44, 1, 1, 1, 0}, + /* ALGO=ML-DSA-44, OUT_FORM=bare-seed, OUT_FILE=mldsa44_bare-seed.der */ + {"certs/mldsa/mldsa44_bare-seed.der", WC_ML_DSA_44, 0, 0, 0, 0}, + /* ALGO=ML-DSA-44, OUT_FORM=bare-priv, OUT_FILE=mldsa44_bare-priv.der */ + {"certs/mldsa/mldsa44_bare-priv.der", WC_ML_DSA_44, 0, 0, 0, 0}, + /* ALGO=ML-DSA-65, OUT_FORM=seed-only, OUT_FILE=mldsa65_seed-only.der */ + {"certs/mldsa/mldsa65_seed-only.der", WC_ML_DSA_65, 1, 1, 1, 0}, + /* ALGO=ML-DSA-65, OUT_FORM=priv-only, OUT_FILE=mldsa65_priv-only.der */ + {"certs/mldsa/mldsa65_priv-only.der", WC_ML_DSA_65, 1, 1, 1, 0}, + /* ALGO=ML-DSA-65, OUT_FORM=seed-priv, OUT_FILE=mldsa65_seed-priv.der */ + {"certs/mldsa/mldsa65_seed-priv.der", WC_ML_DSA_65, 1, 1, 1, 0}, + /* ALGO=ML-DSA-65, OUT_FORM=oqskeypair, OUT_FILE=mldsa65_oqskeypair.der */ + {"certs/mldsa/mldsa65_oqskeypair.der", WC_ML_DSA_65, 1, 1, 1, 0}, + /* ALGO=ML-DSA-65, OUT_FORM=bare-seed, OUT_FILE=mldsa65_bare-seed.der */ + {"certs/mldsa/mldsa65_bare-seed.der", WC_ML_DSA_65, 0, 0, 0, 0}, + /* ALGO=ML-DSA-65, OUT_FORM=bare-priv, OUT_FILE=mldsa65_bare-priv.der */ + {"certs/mldsa/mldsa65_bare-priv.der", WC_ML_DSA_65, 0, 0, 0, 0}, + /* ALGO=ML-DSA-87, OUT_FORM=seed-only, OUT_FILE=mldsa87_seed-only.der */ + {"certs/mldsa/mldsa87_seed-only.der", WC_ML_DSA_87, 1, 1, 1, 0}, + /* ALGO=ML-DSA-87, OUT_FORM=priv-only, OUT_FILE=mldsa87_priv-only.der */ + {"certs/mldsa/mldsa87_priv-only.der", WC_ML_DSA_87, 1, 1, 1, 0}, + /* ALGO=ML-DSA-87, OUT_FORM=seed-priv, OUT_FILE=mldsa87_seed-priv.der */ + {"certs/mldsa/mldsa87_seed-priv.der", WC_ML_DSA_87, 1, 1, 1, 0}, + /* ALGO=ML-DSA-87, OUT_FORM=oqskeypair, OUT_FILE=mldsa87_oqskeypair.der */ + {"certs/mldsa/mldsa87_oqskeypair.der", WC_ML_DSA_87, 1, 1, 1, 0}, + /* ALGO=ML-DSA-87, OUT_FORM=bare-seed, OUT_FILE=mldsa87_bare-seed.der */ + {"certs/mldsa/mldsa87_bare-seed.der", WC_ML_DSA_87, 0, 0, 0, 0}, + /* ALGO=ML-DSA-87, OUT_FORM=bare-priv, OUT_FILE=mldsa87_bare-priv.der */ + {"certs/mldsa/mldsa87_bare-priv.der", WC_ML_DSA_87, 0, 0, 0, 0} +}; +#endif + +int test_wc_Dilithium_PrivateKeyDecode_OpenSSL_form(void) +{ + EXPECT_DECLS; + +#if !defined(NO_ASN) && defined(HAVE_PKCS8) && \ + defined(HAVE_DILITHIUM) && defined(WOLFSSL_WC_DILITHIUM) && \ + !defined(WOLFSSL_DILITHIUM_NO_ASN1) && defined(WOLFSSL_ASN_TEMPLATE) + + byte* der = NULL; + size_t derMaxSz = ML_DSA_LEVEL5_BOTH_KEY_DER_SIZE; + size_t derSz = 0; + FILE* fp = NULL; + word32 inOutIdx = 0; + word32 inOutIdx2 = 0; + dilithium_key key; + int expect = 0; + int pkeySz = 0; + byte level = 0; + + ExpectNotNull(der = (byte*) XMALLOC(derMaxSz, NULL, + DYNAMIC_TYPE_TMP_BUFFER)); + + for (size_t i = 0; i < sizeof(ossl_form) / sizeof(ossl_form[0]); ++i) { + ExpectNotNull(fp = XFOPEN(ossl_form[i].fileName, "rb")); + ExpectIntGT(derSz = XFREAD(der, 1, derMaxSz, fp), 0); + ExpectIntEQ(XFCLOSE(fp), 0); + + /* Specify a level with PKCS8 format */ + XMEMSET(&key, 0, sizeof(key)); + ExpectIntEQ(wc_dilithium_init(&key), 0); + ExpectIntEQ(wc_dilithium_set_level(&key, ossl_form[i].level), 0); + inOutIdx = 0; + expect = ossl_form[i].p8_lv ? 0 : ASN_PARSE_E; + ExpectIntEQ(wc_Dilithium_PrivateKeyDecode(der, &inOutIdx, &key, + (word32)derSz), expect); + if (expect == 0) { + ExpectIntEQ(wc_dilithium_get_level(&key, &level), 0); + ExpectIntEQ(level, ossl_form[i].level); + } + wc_dilithium_free(&key); + + /* Not specify a level with PKCS8 format */ + XMEMSET(&key, 0, sizeof(key)); + ExpectIntEQ(wc_dilithium_init(&key), 0); + inOutIdx = 0; + expect = ossl_form[i].p8_nolv ? 0 : ASN_PARSE_E; + ExpectIntEQ(wc_Dilithium_PrivateKeyDecode(der, &inOutIdx, &key, + (word32)derSz), expect); + if (expect == 0) { + ExpectIntEQ(wc_dilithium_get_level(&key, &level), 0); + ExpectIntEQ(level, ossl_form[i].level); + } + wc_dilithium_free(&key); + + /* Specify a level with traditional format */ + XMEMSET(&key, 0, sizeof(key)); + ExpectIntEQ(wc_dilithium_init(&key), 0); + ExpectIntEQ(wc_dilithium_set_level(&key, ossl_form[i].level), 0); + inOutIdx = 0; + expect = ossl_form[i].trad_lv ? 0 : ASN_PARSE_E; + ExpectIntGT(pkeySz = wc_GetPkcs8TraditionalOffset(der, &inOutIdx, + (word32)derSz), 0); + inOutIdx2 = 0; + ExpectIntEQ(wc_Dilithium_PrivateKeyDecode(der + inOutIdx, &inOutIdx2, + &key, (word32)pkeySz), expect); + if (expect == 0) { + ExpectIntEQ(wc_dilithium_get_level(&key, &level), 0); + ExpectIntEQ(level, ossl_form[i].level); + } + wc_dilithium_free(&key); + + /* Not specify a level with traditional format */ + XMEMSET(&key, 0, sizeof(key)); + ExpectIntEQ(wc_dilithium_init(&key), 0); + inOutIdx = 0; + expect = ossl_form[i].trad_nolv ? 0 : ASN_PARSE_E; + ExpectIntGT(pkeySz = wc_GetPkcs8TraditionalOffset(der, &inOutIdx, + (word32)derSz), 0); + inOutIdx2 = 0; + ExpectIntEQ(wc_Dilithium_PrivateKeyDecode(der + inOutIdx, &inOutIdx2, + &key, (word32)pkeySz), expect); + if (expect == 0) { + ExpectIntEQ(wc_dilithium_get_level(&key, &level), 0); + ExpectIntEQ(level, ossl_form[i].level); + } + wc_dilithium_free(&key); + } + + XFREE(der, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#endif + return EXPECT_RESULT(); +} + +int test_mldsa_pkcs8_import_OpenSSL_form(void) +{ + EXPECT_DECLS; +#if !defined(NO_ASN) && defined(HAVE_PKCS8) && \ + defined(HAVE_DILITHIUM) && defined(WOLFSSL_WC_DILITHIUM) && \ + !defined(WOLFSSL_DILITHIUM_NO_ASN1) && defined(WOLFSSL_ASN_TEMPLATE) && \ + !defined(NO_TLS) && \ + (!defined(NO_WOLFSSL_CLIENT) || !defined(NO_WOLFSSL_SERVER)) + + byte* der = NULL; + size_t derMaxSz = ML_DSA_LEVEL5_BOTH_KEY_DER_SIZE; + size_t derSz = 0; + WOLFSSL_CTX* ctx = NULL; + FILE* fp = NULL; +#ifdef WOLFSSL_DER_TO_PEM + byte* pem = NULL; + size_t pemMaxSz = ML_DSA_LEVEL5_BOTH_KEY_PEM_SIZE; + size_t pemSz = 0; +#endif /* WOLFSSL_DER_TO_PEM */ + int expect = 0; + + ExpectNotNull(der = (byte*) XMALLOC(derMaxSz, NULL, + DYNAMIC_TYPE_TMP_BUFFER)); +#ifdef WOLFSSL_DER_TO_PEM + ExpectNotNull(pem = (byte*) XMALLOC(pemMaxSz, NULL, + DYNAMIC_TYPE_TMP_BUFFER)); +#endif /* WOLFSSL_DER_TO_PEM */ + +#ifndef NO_WOLFSSL_SERVER + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_server_method())); +#else + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method())); +#endif /* NO_WOLFSSL_SERVER */ + + for (size_t i = 0; i < sizeof(ossl_form) / sizeof(ossl_form[0]); ++i) { + ExpectNotNull(fp = XFOPEN(ossl_form[i].fileName, "rb")); + ExpectIntGT(derSz = XFREAD(der, 1, derMaxSz, fp), 0); + ExpectIntEQ(XFCLOSE(fp), 0); + + /* DER */ + expect = ossl_form[i].p8_nolv ? WOLFSSL_SUCCESS : WOLFSSL_BAD_FILE; + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_buffer(ctx, der, derSz, + WOLFSSL_FILETYPE_ASN1), expect); + +#ifdef WOLFSSL_DER_TO_PEM + /* PEM */ + ExpectIntGT(pemSz = wc_DerToPem(der, (word32)derSz, pem, + (word32)pemMaxSz, PKCS8_PRIVATEKEY_TYPE), 0); + expect = ossl_form[i].p8_nolv ? WOLFSSL_SUCCESS : ASN_PARSE_E; + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_buffer(ctx, pem, pemSz, + WOLFSSL_FILETYPE_PEM), expect); +#endif /* WOLFSSL_DER_TO_PEM */ + } + + XFREE(der, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#ifdef WOLFSSL_DER_TO_PEM + XFREE(pem, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#endif /* WOLFSSL_DER_TO_PEM */ +#endif + return EXPECT_RESULT(); +} + +int test_mldsa_pkcs8_export_import_wolfSSL_form(void) { EXPECT_DECLS; #if !defined(NO_ASN) && defined(HAVE_PKCS8) && \ @@ -16676,10 +16888,8 @@ int test_mldsa_pkcs8(void) byte* temp = NULL; /* Store PEM or intermediate key */ word32 derSz = 0; word32 pemSz = 0; - word32 keySz = 0; dilithium_key mldsa_key; WC_RNG rng; - word32 size; int ret; struct { @@ -16746,43 +16956,6 @@ int test_mldsa_pkcs8(void) ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_buffer(ctx, der, derSz, WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS); -#ifdef WOLFSSL_DER_TO_PEM - ExpectIntGT(pemSz = wc_DerToPem(der, derSz, temp, tempMaxSz, - PKCS8_PRIVATEKEY_TYPE), 0); - ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_buffer(ctx, temp, pemSz, - WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS); -#endif /* WOLFSSL_DER_TO_PEM */ - } - - /* Test private + public key (integrated format) */ - for (i = 0; i < sizeof(test_variant) / sizeof(test_variant[0]); ++i) { - ExpectIntEQ(wc_dilithium_set_level(&mldsa_key, test_variant[i].wcId), - 0); - ExpectIntEQ(wc_dilithium_make_key(&mldsa_key, &rng), 0); - - if (EXPECT_FAIL()) - break; - - keySz = 0; - temp[0] = 0x04; /* ASN.1 OCTET STRING */ - temp[1] = 0x82; /* 2 bytes length field */ - temp[2] = (test_variant[i].keySz >> 8) & 0xff; /* MSB of the length */ - temp[3] = test_variant[i].keySz & 0xff; /* LSB of the length */ - keySz += 4; - size = tempMaxSz - keySz; - ExpectIntEQ(wc_dilithium_export_private(&mldsa_key, temp + keySz, - &size), 0); - keySz += size; - size = tempMaxSz - keySz; - ExpectIntEQ(wc_dilithium_export_public(&mldsa_key, temp + keySz, &size), - 0); - keySz += size; - derSz = derMaxSz; - ExpectIntGT(wc_CreatePKCS8Key(der, &derSz, temp, keySz, - test_variant[i].oidSum, NULL, 0), 0); - ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_buffer(ctx, der, derSz, - WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS); - #ifdef WOLFSSL_DER_TO_PEM ExpectIntGT(pemSz = wc_DerToPem(der, derSz, temp, tempMaxSz, PKCS8_PRIVATEKEY_TYPE), 0); diff --git a/tests/api/test_mldsa.h b/tests/api/test_mldsa.h index d1322e571..488c3a2b3 100644 --- a/tests/api/test_mldsa.h +++ b/tests/api/test_mldsa.h @@ -35,22 +35,26 @@ int test_wc_dilithium_der(void); int test_wc_dilithium_make_key_from_seed(void); int test_wc_dilithium_sig_kats(void); int test_wc_dilithium_verify_kats(void); -int test_mldsa_pkcs8(void); +int test_wc_Dilithium_PrivateKeyDecode_OpenSSL_form(void); +int test_mldsa_pkcs8_import_OpenSSL_form(void); +int test_mldsa_pkcs8_export_import_wolfSSL_form(void); int test_mldsa_pkcs12(void); -#define TEST_MLDSA_DECLS \ - TEST_DECL_GROUP("mldsa", test_wc_dilithium), \ - TEST_DECL_GROUP("mldsa", test_wc_dilithium_make_key), \ - TEST_DECL_GROUP("mldsa", test_wc_dilithium_sign), \ - TEST_DECL_GROUP("mldsa", test_wc_dilithium_verify), \ - TEST_DECL_GROUP("mldsa", test_wc_dilithium_sign_vfy), \ - TEST_DECL_GROUP("mldsa", test_wc_dilithium_check_key), \ - TEST_DECL_GROUP("mldsa", test_wc_dilithium_public_der_decode), \ - TEST_DECL_GROUP("mldsa", test_wc_dilithium_der), \ - TEST_DECL_GROUP("mldsa", test_wc_dilithium_make_key_from_seed), \ - TEST_DECL_GROUP("mldsa", test_wc_dilithium_sig_kats), \ - TEST_DECL_GROUP("mldsa", test_wc_dilithium_verify_kats), \ - TEST_DECL_GROUP("mldsa", test_mldsa_pkcs8), \ +#define TEST_MLDSA_DECLS \ + TEST_DECL_GROUP("mldsa", test_wc_dilithium), \ + TEST_DECL_GROUP("mldsa", test_wc_dilithium_make_key), \ + TEST_DECL_GROUP("mldsa", test_wc_dilithium_sign), \ + TEST_DECL_GROUP("mldsa", test_wc_dilithium_verify), \ + TEST_DECL_GROUP("mldsa", test_wc_dilithium_sign_vfy), \ + TEST_DECL_GROUP("mldsa", test_wc_dilithium_check_key), \ + TEST_DECL_GROUP("mldsa", test_wc_dilithium_public_der_decode), \ + TEST_DECL_GROUP("mldsa", test_wc_dilithium_der), \ + TEST_DECL_GROUP("mldsa", test_wc_dilithium_make_key_from_seed), \ + TEST_DECL_GROUP("mldsa", test_wc_dilithium_sig_kats), \ + TEST_DECL_GROUP("mldsa", test_wc_dilithium_verify_kats), \ + TEST_DECL_GROUP("mldsa", test_wc_Dilithium_PrivateKeyDecode_OpenSSL_form), \ + TEST_DECL_GROUP("mldsa", test_mldsa_pkcs8_import_OpenSSL_form), \ + TEST_DECL_GROUP("mldsa", test_mldsa_pkcs8_export_import_wolfSSL_form), \ TEST_DECL_GROUP("mldsa", test_mldsa_pkcs12) #endif /* WOLFCRYPT_TEST_MLDSA_H */ diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 83159ad65..bd448c355 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -36991,6 +36991,7 @@ int wc_EccKeyToPKCS8(ecc_key* key, byte* output, /* ASN.1 template for a general asymmetric private key: Ed25519, Ed448, * falcon, dilithium, etc. * RFC 8410, 7 - Private Key Format (but public value is EXPLICIT OCTET_STRING) + * Check draft-ietf-lamps-dilithium-certificates of draft RFC also. */ static const ASNItem privateKeyASN[] = { /* SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 }, @@ -37001,9 +37002,13 @@ static const ASNItem privateKeyASN[] = { /* PKEYALGO_OID */ { 2, ASN_OBJECT_ID, 0, 0, 1 }, /* privateKey */ /* PKEY */ { 1, ASN_OCTET_STRING, 0, 1, 0 }, - /* CurvePrivateKey */ + /* CurvePrivateKey */ /* PKEY_CURVEPKEY */ { 2, ASN_OCTET_STRING, 0, 0, 2 }, -/* PKEY_MLDSASEQ */ { 2, ASN_SEQUENCE, 1, 0, 2 }, +/* PKEY_SEED_ONLY */ { 2, ASN_CONTEXT_SPECIFIC | ASN_PKEY_SEED, + 0, 0, 2 }, +/* PKEY_BOTH_SEQ */ { 2, ASN_SEQUENCE, 1, 1, 2 }, +/* PKEY_BOTH_SEED */ { 3, ASN_OCTET_STRING, 0, 0, 0 }, +/* PKEY_BOTH_KEY */ { 3, ASN_OCTET_STRING, 0, 0, 0 }, /* attributes */ /* ATTRS */ { 1, ASN_CONTEXT_SPECIFIC | ASN_ASYMKEY_ATTRS, 1, 1, 1 }, /* publicKey */ @@ -37016,7 +37021,10 @@ enum { PRIVKEYASN_IDX_PKEYALGO_OID, PRIVKEYASN_IDX_PKEY, PRIVKEYASN_IDX_PKEY_CURVEPKEY, - PRIVKEYASN_IDX_PKEY_MLDSASEQ, + PRIVKEYASN_IDX_PKEY_SEED_ONLY, + PRIVKEYASN_IDX_PKEY_BOTH_SEQ, + PRIVKEYASN_IDX_PKEY_BOTH_SEED, + PRIVKEYASN_IDX_PKEY_BOTH_KEY, PRIVKEYASN_IDX_ATTRS, PRIVKEYASN_IDX_PUBKEY }; @@ -37033,9 +37041,11 @@ enum { int DecodeAsymKey_Assign(const byte* input, word32* inOutIdx, word32 inSz, + const byte** seed, word32* seedLen, const byte** privKey, word32* privKeyLen, const byte** pubKey, word32* pubKeyLen, int* inOutKeyType) { + int allowSeed = 0; #ifndef WOLFSSL_ASN_TEMPLATE word32 oid; int version, length, endKeyIdx, privSz, pubSz; @@ -37048,14 +37058,27 @@ int DecodeAsymKey_Assign(const byte* input, word32* inOutIdx, word32 inSz, #endif if (input == NULL || inOutIdx == NULL || inSz == 0 || - privKey == NULL || privKeyLen == NULL || inOutKeyType == NULL) { + privKey == NULL || privKeyLen == NULL || + pubKey == NULL || pubKeyLen == NULL || + inOutKeyType == NULL) { #ifdef WOLFSSL_ASN_TEMPLATE FREE_ASNGETDATA(dataASN, NULL); #endif return BAD_FUNC_ARG; } + if ((seed == NULL && seedLen != NULL) || + (seed != NULL && seedLen == NULL)) { + return BAD_FUNC_ARG; + } + + allowSeed = (seed != NULL && seedLen != NULL); #ifndef WOLFSSL_ASN_TEMPLATE + /* The seed can't be parsed without WOLF_ASN_TEMPLATE */ + if (allowSeed) { + return ASN_PARSE_E; + } + if (GetSequence(input, inOutIdx, &length, inSz) >= 0) { endKeyIdx = (int)*inOutIdx + length; @@ -37083,13 +37106,7 @@ int DecodeAsymKey_Assign(const byte* input, word32* inOutIdx, word32 inSz, return ASN_PARSE_E; if (GetOctetString(input, inOutIdx, &privSz, inSz) < 0) { - if (oid != ML_DSA_LEVEL2k && oid != ML_DSA_LEVEL3k && - oid != ML_DSA_LEVEL5k) { - return ASN_PARSE_E; - } - if (GetSequence(input, inOutIdx, &privSz, inSz) < 0) { - return ASN_PARSE_E; - } + return ASN_PARSE_E; } priv = input + *inOutIdx; @@ -37150,53 +37167,69 @@ int DecodeAsymKey_Assign(const byte* input, word32* inOutIdx, word32 inSz, } /* Parse full private key. */ - ret = GetASN_Items(privateKeyASN, dataASN, privateKeyASN_Length, 1, input, - inOutIdx, inSz); - if (ret != 0) { - /* Parse just the OCTET_STRING. */ + ret = GetASN_Items(privateKeyASN, dataASN, privateKeyASN_Length, 1, + input, inOutIdx, inSz); + if (ret == 0) { + /* Store detected OID if requested */ + if (ret == 0 && *inOutKeyType == ANONk) { + *inOutKeyType = + (int)dataASN[PRIVKEYASN_IDX_PKEYALGO_OID].data.oid.sum; + } + } + /* Parse traditional format (a part of full private key). */ + else if (ret != 0) { ret = GetASN_Items(&privateKeyASN[PRIVKEYASN_IDX_PKEY_CURVEPKEY], - &dataASN[PRIVKEYASN_IDX_PKEY_CURVEPKEY], 1, 0, input, - inOutIdx, inSz); + &dataASN[PRIVKEYASN_IDX_PKEY_CURVEPKEY], + PRIVKEYASN_IDX_ATTRS - PRIVKEYASN_IDX_PKEY_CURVEPKEY, 0, + input, inOutIdx, inSz); if (ret != 0) { ret = ASN_PARSE_E; } } - - /* Store detected OID if requested */ - if (ret == 0 && *inOutKeyType == ANONk) { - *inOutKeyType = - (int)dataASN[PRIVKEYASN_IDX_PKEYALGO_OID].data.oid.sum; + } + if (ret == 0) { + /* priv-only */ + if (dataASN[PRIVKEYASN_IDX_PKEY_CURVEPKEY].data.ref.length != 0) { + if (allowSeed) { + *seedLen = 0; + *seed = NULL; + } + *privKeyLen + = dataASN[PRIVKEYASN_IDX_PKEY_CURVEPKEY].data.ref.length; + *privKey = dataASN[PRIVKEYASN_IDX_PKEY_CURVEPKEY].data.ref.data; } - } - if (ret == 0 && dataASN[PRIVKEYASN_IDX_PKEY_CURVEPKEY].data.ref.length != 0) { - /* Import private value. */ - *privKeyLen = dataASN[PRIVKEYASN_IDX_PKEY_CURVEPKEY].data.ref.length; - *privKey = dataASN[PRIVKEYASN_IDX_PKEY_CURVEPKEY].data.ref.data; - } - else if (ret == 0 && - dataASN[PRIVKEYASN_IDX_PKEY_MLDSASEQ].data.ref.length != 0) { - if (*inOutKeyType != ML_DSA_LEVEL2k && - *inOutKeyType != ML_DSA_LEVEL3k && - *inOutKeyType != ML_DSA_LEVEL5k) { - ret = ASN_PARSE_E; + /* seed-only */ + else if (allowSeed && + dataASN[PRIVKEYASN_IDX_PKEY_SEED_ONLY].data.ref.length != 0) { + *seedLen = dataASN[PRIVKEYASN_IDX_PKEY_SEED_ONLY].data.ref.length; + *seed = dataASN[PRIVKEYASN_IDX_PKEY_SEED_ONLY].data.ref.data; + *privKeyLen = 0; + *privKey = NULL; + } + /* seed-priv */ + else if (allowSeed && + dataASN[PRIVKEYASN_IDX_PKEY_BOTH_SEQ].data.ref.length != 0) { + *seedLen = dataASN[PRIVKEYASN_IDX_PKEY_BOTH_SEED].data.ref.length; + *seed = dataASN[PRIVKEYASN_IDX_PKEY_BOTH_SEED].data.ref.data; + *privKeyLen = dataASN[PRIVKEYASN_IDX_PKEY_BOTH_KEY].data.ref.length; + *privKey = dataASN[PRIVKEYASN_IDX_PKEY_BOTH_KEY].data.ref.data; } else { - /* Import private value. */ - *privKeyLen = dataASN[PRIVKEYASN_IDX_PKEY_MLDSASEQ].data.ref.length; - *privKey = dataASN[PRIVKEYASN_IDX_PKEY_MLDSASEQ].data.ref.data; + ret = ASN_PARSE_E; } } - if ((ret == 0) && dataASN[PRIVKEYASN_IDX_PUBKEY].tag == 0) { - /* Set public length to 0 as not seen. */ - if (pubKeyLen != NULL) - *pubKeyLen = 0; - } - else if (ret == 0) { - /* Import public value. */ - if (pubKeyLen != NULL) + + if (ret == 0) { + if (dataASN[PRIVKEYASN_IDX_PUBKEY].data.ref.length != 0) { + /* Import public value. */ *pubKeyLen = dataASN[PRIVKEYASN_IDX_PUBKEY].data.ref.length; - if (pubKey != NULL && pubKeyLen != NULL) *pubKey = dataASN[PRIVKEYASN_IDX_PUBKEY].data.ref.data; + } + else { + /* Set public length to 0 as not seen. */ + *pubKeyLen = 0; + *pubKey = NULL; + } } FREE_ASNGETDATA(dataASN, NULL); @@ -37219,8 +37252,8 @@ int DecodeAsymKey(const byte* input, word32* inOutIdx, word32 inSz, } if (ret == 0) { - ret = DecodeAsymKey_Assign(input, inOutIdx, inSz, &privKeyPtr, - &privKeyPtrLen, &pubKeyPtr, &pubKeyPtrLen, &keyType); + ret = DecodeAsymKey_Assign(input, inOutIdx, inSz, NULL, NULL, + &privKeyPtr, &privKeyPtrLen, &pubKeyPtr, &pubKeyPtrLen, &keyType); } if ((ret == 0) && (privKeyPtrLen > *privKeyLen)) { ret = BUFFER_E; @@ -37606,10 +37639,11 @@ int SetAsymKeyDer(const byte* privKey, word32 privKeyLen, oidKeyType); /* Leave space for private key. */ SetASN_Buffer(&dataASN[PRIVKEYASN_IDX_PKEY_CURVEPKEY], NULL, privKeyLen); + /* Don't write ML-DSA specific things. */ + SetASNItem_NoOut(dataASN, PRIVKEYASN_IDX_PKEY_SEED_ONLY, + PRIVKEYASN_IDX_ATTRS); /* Don't write out attributes. */ dataASN[PRIVKEYASN_IDX_ATTRS].noOut = 1; - /* Don't write sequence. */ - dataASN[PRIVKEYASN_IDX_PKEY_MLDSASEQ].noOut = 1; if (pubKey) { /* Leave space for public key. */ SetASN_Buffer(&dataASN[PRIVKEYASN_IDX_PUBKEY], NULL, pubKeyLen); diff --git a/wolfcrypt/src/dilithium.c b/wolfcrypt/src/dilithium.c index ac8e5d810..ea0219c48 100644 --- a/wolfcrypt/src/dilithium.c +++ b/wolfcrypt/src/dilithium.c @@ -9659,31 +9659,6 @@ int dilithium_get_oid_sum(dilithium_key* key, int* keyFormat) { #if defined(WOLFSSL_DILITHIUM_PRIVATE_KEY) -/* OCT OCT */ -#define ALT_PRIV_DER_PREFIX (2 + 32 + 4) -/* SEQ [ OCT OCT ] */ -#define ALT_PRIV_DER_PREFIX_SEQ (4 + 2 + 32 + 4) - -/* Get the private only key size for the ML-DSA level/parameter id. - * - * @param [in] level Level of the ML-DSA key. - * @return Private key only encoding size for key level on success. - * @return 0 on failure. - */ -static word32 dilithium_get_priv_size(int level) -{ - switch (level) { - case WC_ML_DSA_44: - return ML_DSA_LEVEL2_KEY_SIZE; - case WC_ML_DSA_65: - return ML_DSA_LEVEL3_KEY_SIZE; - case WC_ML_DSA_87: - return ML_DSA_LEVEL5_KEY_SIZE; - default: - return 0; - } -} - /* Decode the DER encoded Dilithium key. * * @param [in] input Array holding DER encoded data. @@ -9708,11 +9683,14 @@ int wc_Dilithium_PrivateKeyDecode(const byte* input, word32* inOutIdx, dilithium_key* key, word32 inSz) { int ret = 0; + const byte* seed = NULL; const byte* privKey = NULL; const byte* pubKey = NULL; + word32 seedLen = 0; word32 privKeyLen = 0; word32 pubKeyLen = 0; int keyType = 0; + int autoKeyType = ANONk; /* Validate parameters. */ if ((input == NULL) || (inOutIdx == NULL) || (key == NULL) || (inSz == 0)) { @@ -9756,34 +9734,45 @@ int wc_Dilithium_PrivateKeyDecode(const byte* input, word32* inOutIdx, if (ret == 0) { /* Decode the asymmetric key and get out private and public key data. */ +#ifndef WOLFSSL_ASN_TEMPLATE ret = DecodeAsymKey_Assign(input, inOutIdx, inSz, + NULL, NULL, &privKey, &privKeyLen, - &pubKey, &pubKeyLen, &keyType); - if (ret == 0 -#ifdef WOLFSSL_WC_DILITHIUM - && key->params == NULL -#endif - ) { + &pubKey, &pubKeyLen, &autoKeyType); +#else + ret = DecodeAsymKey_Assign(input, inOutIdx, inSz, + &seed, &seedLen, + &privKey, &privKeyLen, + &pubKey, &pubKeyLen, &autoKeyType); +#endif /* WOLFSSL_ASN_TEMPLATE */ + } + + if (ret == 0) { + if (keyType == ANONk && autoKeyType != ANONk) { /* Set the security level based on the decoded key. */ - ret = mapOidToSecLevel(keyType); + ret = mapOidToSecLevel(autoKeyType); if (ret > 0) { ret = wc_dilithium_set_level(key, (byte)ret); } } - /* If it failed to decode try alternative DER encoding. */ - else if (ret != 0) { - word32 levelSize = dilithium_get_priv_size(key->level); - privKey = input + *inOutIdx; - privKeyLen = inSz - *inOutIdx; - - /* Check for an alternative DER encoding. */ - if (privKeyLen == ALT_PRIV_DER_PREFIX_SEQ + levelSize) { - privKey += ALT_PRIV_DER_PREFIX_SEQ; - privKeyLen -= ALT_PRIV_DER_PREFIX_SEQ; + else if (keyType != ANONk && autoKeyType != ANONk) { + if (keyType == autoKeyType) ret = 0; - } + else + ret = ASN_PARSE_E; + } + else if (keyType != ANONk && autoKeyType == ANONk) { + ret = 0; + } + else { /* keyType == ANONk && autoKeyType == ANONk */ + /* + * When decoding traditional format with not specifying a level will + * cause this error. + */ + ret = ASN_PARSE_E; } } + if ((ret == 0) && (pubKey == NULL) && (pubKeyLen == 0)) { /* Check if the public key is included in the private key. */ #if defined(WOLFSSL_DILITHIUM_FIPS204_DRAFT) @@ -9828,32 +9817,39 @@ int wc_Dilithium_PrivateKeyDecode(const byte* input, word32* inOutIdx, pubKeyLen = ML_DSA_LEVEL5_PUB_KEY_SIZE; privKeyLen -= ML_DSA_LEVEL5_PUB_KEY_SIZE; } - else { - word32 levelSize = dilithium_get_priv_size(key->level); - - if (privKeyLen == ALT_PRIV_DER_PREFIX + levelSize) { - privKey += ALT_PRIV_DER_PREFIX; - privKeyLen -= ALT_PRIV_DER_PREFIX; - } - } } if (ret == 0) { - /* Check whether public key data was found. */ -#if defined(WOLFSSL_DILITHIUM_PUBLIC_KEY) - if (pubKeyLen == 0) + /* Generate a key pair if seed exists and decoded key pair is ignored */ + if (seedLen != 0) { +#if defined(WOLFSSL_WC_DILITHIUM) + if (seedLen == DILITHIUM_SEED_SZ) { + ret = wc_dilithium_make_key_from_seed(key, seed); + } + else { + ret = ASN_PARSE_E; + } +#else + ret = NOT_COMPILED_IN; #endif - { - /* No public key data, only import private key data. */ - ret = wc_dilithium_import_private(privKey, privKeyLen, key); } #if defined(WOLFSSL_DILITHIUM_PUBLIC_KEY) - else { + /* Check whether public key data was found. */ + else if (pubKeyLen != 0 && privKeyLen != 0) { /* Import private and public key data. */ ret = wc_dilithium_import_key(privKey, privKeyLen, pubKey, pubKeyLen, key); } #endif + else if (pubKeyLen == 0 && privKeyLen != 0) + { + /* No public key data, only import private key data. */ + ret = wc_dilithium_import_private(privKey, privKeyLen, key); + } + else { + /* Not a problem of ASN.1 structure, but the contents is invalid */ + ret = ASN_PARSE_E; + } } (void)pubKey; diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index 136735876..cffd587e3 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -204,7 +204,10 @@ enum ASN_Tags { /* OneAsymmetricKey Fields */ ASN_ASYMKEY_ATTRS = 0x00, - ASN_ASYMKEY_PUBKEY = 0x01 + ASN_ASYMKEY_PUBKEY = 0x01, + + /* PKEY Fields */ + ASN_PKEY_SEED = 0x00 }; /* NOTE: If ASN_UTC_TIME_SIZE or ASN_GENERALIZED_TIME_SIZE are ever modified @@ -2727,8 +2730,9 @@ WOLFSSL_LOCAL int VerifyX509Acert(const byte* cert, word32 certSz, || (defined(HAVE_CURVE448) && defined(HAVE_CURVE448_KEY_IMPORT)) \ || defined(HAVE_FALCON) || defined(HAVE_DILITHIUM) || defined(HAVE_SPHINCS)) WOLFSSL_LOCAL int DecodeAsymKey_Assign(const byte* input, word32* inOutIdx, - word32 inSz, const byte** privKey, word32* privKeyLen, const byte** pubKey, - word32* pubKeyLen, int* inOutKeyType); + word32 inSz, const byte** seed, word32* seedLen, const byte** privKey, + word32* privKeyLen, const byte** pubKey, word32* pubKeyLen, + int* inOutKeyType); WOLFSSL_LOCAL int DecodeAsymKey(const byte* input, word32* inOutIdx, word32 inSz, byte* privKey, word32* privKeyLen, byte* pubKey,