diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index bf9d5886b7..efdb541031 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -35307,9 +35307,10 @@ enum { || (defined(HAVE_CURVE448) && defined(HAVE_CURVE448_KEY_IMPORT)) \ || defined(HAVE_FALCON) || defined(HAVE_DILITHIUM) || defined(HAVE_SPHINCS)) -int DecodeAsymKey_Assign(const byte* input, word32* inOutIdx, word32 inSz, + +int DecodeAsymKey_Assign_ex(const byte* input, word32* inOutIdx, word32 inSz, const byte** privKey, word32* privKeyLen, - const byte** pubKey, word32* pubKeyLen, int keyType) + const byte** pubKey, word32* pubKeyLen, int* inOutKeyType) { #ifndef WOLFSSL_ASN_TEMPLATE word32 oid; @@ -35337,14 +35338,24 @@ int DecodeAsymKey_Assign(const byte* input, word32* inOutIdx, word32 inSz, if (GetMyVersion(input, inOutIdx, &version, inSz) < 0) return ASN_PARSE_E; if (version != 0) { - WOLFSSL_MSG("Unrecognized version of ED25519 private key"); + WOLFSSL_MSG("Unrecognized version of private key"); return ASN_PARSE_E; } if (GetAlgoId(input, inOutIdx, &oid, oidKeyType, inSz) < 0) return ASN_PARSE_E; - if (oid != (word32)keyType) - return ASN_PARSE_E; + + if (inOutKeyType != NULL) { + /* If user supplies ANONk (0) key type, we want to auto-detect from + * DER and copy it back to user */ + if (*inOutKeyType == ANONk) { + *inOutKeyType = oid; + } + /* Otherwise strictly validate against the expected type */ + else if (oid != (word32)*inOutKeyType) { + return ASN_PARSE_E; + } + } if (GetOctetString(input, inOutIdx, &length, inSz) < 0) return ASN_PARSE_E; @@ -35394,10 +35405,21 @@ int DecodeAsymKey_Assign(const byte* input, word32* inOutIdx, word32 inSz, return 0; #else if (ret == 0) { - /* Require OID. */ - word32 oidSz; - const byte* oid = OidFromId((word32)keyType, oidKeyType, &oidSz); - GetASN_ExpBuffer(&dataASN[EDKEYASN_IDX_PKEYALGO_OID], oid, oidSz); + /* If user supplies an expected keyType (algorithm OID sum), attempt to + * process DER accordingly */ + if (inOutKeyType != NULL && *inOutKeyType != 0) { + word32 oidSz; + /* Explicit OID check - use expected type */ + const byte* oidDerBytes = OidFromId((word32)*inOutKeyType, + oidKeyType, &oidSz); + GetASN_ExpBuffer(&dataASN[EDKEYASN_IDX_PKEYALGO_OID], oidDerBytes, + oidSz); + } + else { + /* Auto-detect OID using template */ + GetASN_OID(&dataASN[EDKEYASN_IDX_PKEYALGO_OID], oidKeyType); + } + /* Parse full private key. */ ret = GetASN_Items(edKeyASN, dataASN, edKeyASN_Length, 1, input, inOutIdx, inSz); @@ -35410,6 +35432,12 @@ int DecodeAsymKey_Assign(const byte* input, word32* inOutIdx, word32 inSz, ret = ASN_PARSE_E; } } + + /* Store detected OID if requested */ + if (ret == 0 && inOutKeyType != NULL && *inOutKeyType == ANONk) { + *inOutKeyType = + (int)dataASN[EDKEYASN_IDX_PKEYALGO_OID].data.oid.sum; + } } if (ret == 0) { /* Import private value. */ @@ -35434,6 +35462,15 @@ int DecodeAsymKey_Assign(const byte* input, word32* inOutIdx, word32 inSz, #endif /* WOLFSSL_ASN_TEMPLATE */ } + +int DecodeAsymKey_Assign(const byte* input, word32* inOutIdx, word32 inSz, + const byte** privKey, word32* privKeyLen, + const byte** pubKey, word32* pubKeyLen, int keyType) +{ + return DecodeAsymKey_Assign_ex(input, inOutIdx, inSz, privKey, privKeyLen, + pubKey, pubKeyLen, &keyType); +} + int DecodeAsymKey(const byte* input, word32* inOutIdx, word32 inSz, byte* privKey, word32* privKeyLen, byte* pubKey, word32* pubKeyLen, int keyType) @@ -35472,8 +35509,8 @@ int DecodeAsymKey(const byte* input, word32* inOutIdx, word32 inSz, return ret; } -int DecodeAsymKeyPublic_Assign(const byte* input, word32* inOutIdx, word32 inSz, - const byte** pubKey, word32* pubKeyLen, int keyType) +int DecodeAsymKeyPublic_Assign_ex(const byte* input, word32* inOutIdx, word32 inSz, + const byte** pubKey, word32* pubKeyLen, int *inOutKeyType) { int ret = 0; #ifndef WOLFSSL_ASN_TEMPLATE @@ -35498,8 +35535,18 @@ int DecodeAsymKeyPublic_Assign(const byte* input, word32* inOutIdx, word32 inSz, if (GetObjectId(input, inOutIdx, &oid, oidKeyType, inSz) < 0) return ASN_PARSE_E; - if (oid != (word32)keyType) - return ASN_PARSE_E; + + if (inOutKeyType != NULL) { + /* If user supplies ANONk (0) key type, we want to auto-detect from + * DER and copy it back to user */ + if (*inOutKeyType == ANONk) { + *inOutKeyType = oid; + } + /* Otherwise strictly validate against the expected type */ + else if (oid != (word32)*inOutKeyType) { + return ASN_PARSE_E; + } + } /* key header */ ret = CheckBitString(input, inOutIdx, &length, inSz, 1, NULL); @@ -35519,12 +35566,21 @@ int DecodeAsymKeyPublic_Assign(const byte* input, word32* inOutIdx, word32 inSz, CALLOC_ASNGETDATA(dataASN, publicKeyASN_Length, ret, NULL); if (ret == 0) { - /* Require OID. */ - word32 oidSz; - const byte* oid = OidFromId((word32)keyType, oidKeyType, &oidSz); - - GetASN_ExpBuffer(&dataASN[PUBKEYASN_IDX_ALGOID_OID], oid, oidSz); - /* Decode Ed25519 private key. */ + /* If user supplies an expected keyType (algorithm OID sum), attempt to + * process DER accordingly */ + if (inOutKeyType != NULL && *inOutKeyType != ANONk) { + word32 oidSz; + /* Explicit OID check - use expected type */ + const byte* oidDerBytes = OidFromId((word32)*inOutKeyType, + oidKeyType, &oidSz); + GetASN_ExpBuffer(&dataASN[PUBKEYASN_IDX_ALGOID_OID], oidDerBytes, + oidSz); + } + else { + /* Auto-detect OID using template */ + GetASN_OID(&dataASN[PUBKEYASN_IDX_ALGOID_OID], oidKeyType); + } + /* Decode public key. */ ret = GetASN_Items(publicKeyASN, dataASN, publicKeyASN_Length, 1, input, inOutIdx, inSz); if (ret != 0) @@ -35532,6 +35588,12 @@ int DecodeAsymKeyPublic_Assign(const byte* input, word32* inOutIdx, word32 inSz, /* check that input buffer is exhausted */ if (*inOutIdx != inSz) ret = ASN_PARSE_E; + + /* Store detected OID if requested */ + if (ret == 0 && inOutKeyType != NULL && *inOutKeyType == ANONk) { + *inOutKeyType = + (int)dataASN[PUBKEYASN_IDX_ALGOID_OID].data.oid.sum; + } } /* Check that the all the buffer was used. */ if ((ret == 0) && @@ -35546,6 +35608,14 @@ int DecodeAsymKeyPublic_Assign(const byte* input, word32* inOutIdx, word32 inSz, FREE_ASNGETDATA(dataASN, NULL); #endif /* WOLFSSL_ASN_TEMPLATE */ return ret; + +} + +int DecodeAsymKeyPublic_Assign(const byte* input, word32* inOutIdx, word32 inSz, + const byte** pubKey, word32* pubKeyLen, int keyType) +{ + return DecodeAsymKeyPublic_Assign_ex(input, inOutIdx, inSz, pubKey, + pubKeyLen, &keyType); } int DecodeAsymKeyPublic(const byte* input, word32* inOutIdx, word32 inSz, diff --git a/wolfcrypt/src/dilithium.c b/wolfcrypt/src/dilithium.c index c74ca493ad..b5bd40f43a 100644 --- a/wolfcrypt/src/dilithium.c +++ b/wolfcrypt/src/dilithium.c @@ -9524,59 +9524,6 @@ static int mapOidToSecLevel(word32 oid) } } -/* Get security level from DER encoded key. Returns a positive security level - * (e.g. WC_ML_DSA_44, etc.) on success, or a negative value on error. - * - * Expected ASN.1 Structure: - * - * SEQUENCE { -- Outer wrapper - * version INTEGER, -- Version number (usually 0) - * algorithm SEQUENCE { -- AlgorithmIdentifier - * algorithm OBJECT IDENTIFIER -- OID identifying Dilithium variant - * } - * -- Note: Remaining key data after algorithm is ignored for this function - * } - */ -static int getSecLevelFromDer(const byte* der, word32 derSz) -{ - word32 idx = 0; - int seqSz, algSeqSz; - int ret; - word32 oid = 0; - int version; - - if (der == NULL || derSz == 0) { - return BAD_FUNC_ARG; - } - - /* Parse outer SEQUENCE wrapper and get its size - * Advances idx past the SEQUENCE header */ - if ((ret = GetSequence(der, &idx, &seqSz, derSz)) < 0) { - return ret; - } - - /* Parse and skip over version INTEGER - * Advances idx past the version field */ - if ((ret = GetMyVersion(der, &idx, &version, derSz)) < 0) { - return ret; - } - - /* Parse AlgorithmIdentifier SEQUENCE and get its size - * Advances idx past the SEQUENCE header */ - if ((ret = GetSequence(der, &idx, &algSeqSz, derSz)) < 0) { - return ret; - } - - /* Parse OID value from AlgorithmIdentifier - * Advances idx past the OID, stores numeric OID value - * oidSigType indicates this is a signature algorithm OID */ - if ((ret = GetObjectId(der, &idx, &oid, oidSigType, derSz - idx)) < 0) { - return ret; - } - - return mapOidToSecLevel(oid); -} - #if defined(WOLFSSL_DILITHIUM_PRIVATE_KEY) /* Decode the DER encoded Dilithium key. @@ -9613,23 +9560,6 @@ int wc_Dilithium_PrivateKeyDecode(const byte* input, word32* inOutIdx, ret = BAD_FUNC_ARG; } - /* If expected security level not set in key, detect it from DER */ - if (key->level == 0 -#ifdef WOLFSSL_WC_DILITHIUM - || key->params == NULL -#endif - ) { - int level; - level = getSecLevelFromDer(input + *inOutIdx, inSz - *inOutIdx); - if (level < 0) { - ret = level; - } - else { - /* Set params based on level parsed from DER*/ - ret = wc_dilithium_set_level(key, level); - } - } - if (ret == 0) { /* Get OID sum for level. */ #if defined(WOLFSSL_DILITHIUM_FIPS204_DRAFT) @@ -9657,15 +9587,27 @@ int wc_Dilithium_PrivateKeyDecode(const byte* input, word32* inOutIdx, keytype = ML_DSA_LEVEL5k; } else { - /* Level not set. */ - ret = BAD_FUNC_ARG; + /* Level not set by caller, decode from DER */ + keytype = ANONk; /* 0, not a valid key type in this situation*/ } } if (ret == 0) { /* Decode the asymmetric key and get out private and public key data. */ - ret = DecodeAsymKey_Assign(input, inOutIdx, inSz, &privKey, &privKeyLen, - &pubKey, &pubKeyLen, keytype); + ret = DecodeAsymKey_Assign_ex(input, inOutIdx, inSz, + &privKey, &privKeyLen, + &pubKey, &pubKeyLen, &keytype); + if (ret == 0 +#ifdef WOLFSSL_WC_DILITHIUM + && key->params == NULL +#endif + ) { + /* Set the security level based on the decoded key. */ + ret = mapOidToSecLevel(keytype); + if (ret > 0) { + ret = wc_dilithium_set_level(key, ret); + } + } } if ((ret == 0) && (pubKey == NULL) && (pubKeyLen == 0)) { /* Check if the public key is included in the private key. */ @@ -9883,25 +9825,6 @@ int wc_Dilithium_PublicKeyDecode(const byte* input, word32* inOutIdx, ret = BAD_FUNC_ARG; } -#if !defined(WOLFSSL_DILITHIUM_NO_ASN1) - /* If expected security level not set in key, detect it from DER */ - if (key->level == 0 -#ifdef WOLFSSL_WC_DILITHIUM - || key->params == NULL -#endif - ) { - int level; - level = getSecLevelFromDer(input + *inOutIdx, inSz - *inOutIdx); - if (level < 0) { - ret = level; - } - else { - /* Set params based on level parsed from DER*/ - ret = wc_dilithium_set_level(key, level); - } - } -#endif /* !WOLFSSL_DILITHIUM_NO_ASN1 */ - if (ret == 0) { /* Try to import the key directly. */ ret = wc_dilithium_import_public(input, inSz, key); @@ -9945,13 +9868,25 @@ int wc_Dilithium_PublicKeyDecode(const byte* input, word32* inOutIdx, keytype = ML_DSA_LEVEL5k; } else { - /* Level not set. */ - ret = BAD_FUNC_ARG; + /* Level not set by caller, decode from DER */ + keytype = ANONk; /* 0, not a valid key type in this situation*/ } if (ret == 0) { /* Decode the asymmetric key and get out public key data. */ - ret = DecodeAsymKeyPublic_Assign(input, inOutIdx, inSz, &pubKey, - &pubKeyLen, keytype); + ret = DecodeAsymKeyPublic_Assign_ex(input, inOutIdx, inSz, + &pubKey, &pubKeyLen, + &keytype); + if (ret == 0 +#ifdef WOLFSSL_WC_DILITHIUM + && key->params == NULL +#endif + ) { + /* Set the security level based on the decoded key. */ + ret = mapOidToSecLevel(keytype); + if (ret > 0) { + ret = wc_dilithium_set_level(key, ret); + } + } } #else /* Get OID sum for level. */ diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index f105641ea3..cf70d0d64a 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -45672,14 +45672,14 @@ static wc_test_ret_t dilithium_param_test(int param, WC_RNG* rng) /* Tests decoding a key from DER without the security level specified */ static wc_test_ret_t test_dilithium_decode_level(const byte* rawKey, word32 rawKeySz, - int expectedLevel) + int expectedLevel, + int isPublicOnlyKey) { int ret; dilithium_key key; word32 idx; byte* der; word32 derSz; - /* DER encoding adds ~256 bytes of overhead to raw key */ const word32 estimatedDerSz = rawKeySz + 256; @@ -45692,17 +45692,27 @@ static wc_test_ret_t test_dilithium_decode_level(const byte* rawKey, /* Initialize key */ ret = wc_dilithium_init(&key); - /* Import raw key, setting the security level */ + /* Import raw private key, setting the security level */ if (ret == 0) { ret = wc_dilithium_set_level(&key, expectedLevel); } if (ret == 0) { - ret = wc_dilithium_import_private(rawKey, rawKeySz, &key); + if (isPublicOnlyKey) { + ret = wc_dilithium_import_public(rawKey, rawKeySz, &key); + } + else { + ret = wc_dilithium_import_private(rawKey, rawKeySz, &key); + } } /* Export raw key as DER */ if (ret == 0) { - ret = wc_Dilithium_PrivateKeyToDer(&key, der, estimatedDerSz); + if (isPublicOnlyKey) { + ret = wc_Dilithium_PublicKeyToDer(&key, der, estimatedDerSz, 1); + } + else { + ret = wc_Dilithium_PrivateKeyToDer(&key, der, estimatedDerSz); + } if (ret >= 0) { derSz = ret; ret = 0; @@ -45715,13 +45725,38 @@ static wc_test_ret_t test_dilithium_decode_level(const byte* rawKey, ret = wc_dilithium_init(&key); } + /* First test decoding when security level is set externally */ + if (ret == 0) { + ret = wc_dilithium_set_level(&key, expectedLevel); + } + if (ret == 0) { + idx = 0; + if (isPublicOnlyKey) { + ret = wc_Dilithium_PublicKeyDecode(der, &idx, &key, derSz); + } + else { + ret = wc_Dilithium_PrivateKeyDecode(der, &idx, &key, derSz); + } + } + + /* Free and reinit key to test fresh decode */ + if (ret == 0) { + wc_dilithium_free(&key); + ret = wc_dilithium_init(&key); + } + /* Test decoding without setting security level - should auto-detect */ if (ret == 0) { idx = 0; - ret = wc_Dilithium_PrivateKeyDecode(der, &idx, &key, derSz); + if (isPublicOnlyKey) { + ret = wc_Dilithium_PublicKeyDecode(der, &idx, &key, derSz); + } + else { + ret = wc_Dilithium_PrivateKeyDecode(der, &idx, &key, derSz); + } } - /* Verify detected security level */ + /* Verify auto-detected security level */ if (ret == 0 && key.level != expectedLevel) { printf("Dilithium key decode failed to detect level.\n" "\tExpected level=%d\n\tGot level=%d\n", @@ -45739,14 +45774,24 @@ static wc_test_ret_t test_dilithium_decode_level(const byte* rawKey, static wc_test_ret_t dilithium_decode_test(void) { wc_test_ret_t ret; - const byte* privKey; - word32 privKeySz; + const byte* key; + word32 keySz; + + const int isPrvKey = 0; + const int isPubKey = 1; #ifndef WOLFSSL_NO_ML_DSA_44 /* Test ML-DSA-44 */ - privKey = bench_dilithium_level2_key; - privKeySz = sizeof_bench_dilithium_level2_key; - ret = test_dilithium_decode_level(privKey, privKeySz, WC_ML_DSA_44); + key = bench_dilithium_level2_key; + keySz = sizeof_bench_dilithium_level2_key; + ret = test_dilithium_decode_level(key, keySz, WC_ML_DSA_44, isPrvKey); + if (ret != 0) { + return ret; + } + + key = bench_dilithium_level2_pubkey; + keySz = sizeof_bench_dilithium_level2_pubkey; + ret = test_dilithium_decode_level(key, keySz, WC_ML_DSA_44, isPubKey); if (ret != 0) { return ret; } @@ -45754,9 +45799,16 @@ static wc_test_ret_t dilithium_decode_test(void) #ifndef WOLFSSL_NO_ML_DSA_65 /* Test ML-DSA-65 */ - privKey = bench_dilithium_level3_key; - privKeySz = sizeof_bench_dilithium_level3_key; - ret = test_dilithium_decode_level(privKey, privKeySz, WC_ML_DSA_65); + key = bench_dilithium_level3_key; + keySz = sizeof_bench_dilithium_level3_key; + ret = test_dilithium_decode_level(key, keySz, WC_ML_DSA_65, isPrvKey); + if (ret != 0) { + return ret; + } + + key = bench_dilithium_level3_pubkey; + keySz = sizeof_bench_dilithium_level3_pubkey; + ret = test_dilithium_decode_level(key, keySz, WC_ML_DSA_65, isPubKey); if (ret != 0) { return ret; } @@ -45764,9 +45816,16 @@ static wc_test_ret_t dilithium_decode_test(void) #ifndef WOLFSSL_NO_ML_DSA_87 /* Test ML-DSA-87 */ - privKey = bench_dilithium_level5_key; - privKeySz = sizeof_bench_dilithium_level5_key; - ret = test_dilithium_decode_level(privKey, privKeySz, WC_ML_DSA_87); + key = bench_dilithium_level5_key; + keySz = sizeof_bench_dilithium_level5_key; + ret = test_dilithium_decode_level(key, keySz, WC_ML_DSA_87, isPrvKey); + if (ret != 0) { + return ret; + } + + key = bench_dilithium_level5_pubkey; + keySz = sizeof_bench_dilithium_level5_pubkey; + ret = test_dilithium_decode_level(key, keySz, WC_ML_DSA_87, isPubKey); if (ret != 0) { return ret; } diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index f9d1eb79cf..e37f3af4e3 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -2542,6 +2542,10 @@ WOLFSSL_LOCAL int SetAsymKeyDerPublic(const byte* pubKey, word32 pubKeyLen, WOLFSSL_LOCAL int DecodeAsymKeyPublic_Assign(const byte* input, word32* inOutIdx, word32 inSz, const byte** pubKey, word32* pubKeyLen, int keyType); +WOLFSSL_LOCAL int DecodeAsymKeyPublic_Assign_ex(const byte* input, + word32* inOutIdx, word32 inSz, const byte** pubKey, word32* pubKeyLen, + int* keyType); + WOLFSSL_LOCAL int DecodeAsymKeyPublic(const byte* input, word32* inOutIdx, word32 inSz, byte* pubKey, word32* pubKeyLen, int keyType); @@ -2897,6 +2901,10 @@ WOLFSSL_LOCAL int VerifyX509Acert(const byte* cert, word32 certSz, WOLFSSL_LOCAL int DecodeAsymKey_Assign(const byte* input, word32* inOutIdx, word32 inSz, const byte** privKey, word32* privKeyLen, const byte** pubKey, word32* pubKeyLen, int keyType); +WOLFSSL_LOCAL int DecodeAsymKey_Assign_ex(const byte* input, word32* inOutIdx, + word32 inSz, 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, word32* pubKeyLen, int keyType);