Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ML-DSA/Dilithium: obtain security level from DER when decoding #8177

Merged
merged 10 commits into from
Nov 19, 2024
6 changes: 4 additions & 2 deletions tests/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -34843,10 +34843,12 @@ static int test_wc_dilithium_der(void)
1), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(wc_Dilithium_PrivateKeyToDer(key, der, DILITHIUM_MAX_DER_SIZE),
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
/* When security level is not set, we attempt to parse it from DER. Since
* the supplied DER is invalid, this should fail with ASN parsing error */
ExpectIntEQ(wc_Dilithium_PublicKeyDecode(der, &idx, key, pubDerLen),
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
WC_NO_ERR_TRACE(ASN_PARSE_E));
ExpectIntEQ(wc_Dilithium_PrivateKeyDecode(der, &idx, key, privDerLen),
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
WC_NO_ERR_TRACE(ASN_PARSE_E));

#ifndef WOLFSSL_NO_ML_DSA_44
ExpectIntEQ(wc_dilithium_set_level(key, WC_ML_DSA_44), 0);
Expand Down
92 changes: 73 additions & 19 deletions wolfcrypt/src/asn.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
const byte** privKey, word32* privKeyLen,
const byte** pubKey, word32* pubKeyLen, int keyType)
const byte** pubKey, word32* pubKeyLen, int* inOutKeyType)
SparkiDev marked this conversation as resolved.
Show resolved Hide resolved
{
#ifndef WOLFSSL_ASN_TEMPLATE
word32 oid;
Expand Down Expand Up @@ -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) {
SparkiDev marked this conversation as resolved.
Show resolved Hide resolved
/* 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;
Expand Down Expand Up @@ -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);
Expand All @@ -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. */
Expand Down Expand Up @@ -35450,7 +35478,7 @@ int DecodeAsymKey(const byte* input, word32* inOutIdx, word32 inSz,

if (ret == 0) {
ret = DecodeAsymKey_Assign(input, inOutIdx, inSz, &privKeyPtr,
&privKeyPtrLen, &pubKeyPtr, &pubKeyPtrLen, keyType);
&privKeyPtrLen, &pubKeyPtr, &pubKeyPtrLen, &keyType);
}
if ((ret == 0) && (privKeyPtrLen > *privKeyLen)) {
ret = BUFFER_E;
Expand All @@ -35473,7 +35501,7 @@ int DecodeAsymKey(const byte* input, word32* inOutIdx, word32 inSz,
}

int DecodeAsymKeyPublic_Assign(const byte* input, word32* inOutIdx, word32 inSz,
const byte** pubKey, word32* pubKeyLen, int keyType)
const byte** pubKey, word32* pubKeyLen, int *inOutKeyType)
{
int ret = 0;
#ifndef WOLFSSL_ASN_TEMPLATE
Expand All @@ -35498,8 +35526,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);
Expand All @@ -35519,19 +35557,34 @@ 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)
ret = ASN_PARSE_E;
/* 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) &&
Expand All @@ -35546,6 +35599,7 @@ int DecodeAsymKeyPublic_Assign(const byte* input, word32* inOutIdx, word32 inSz,
FREE_ASNGETDATA(dataASN, NULL);
#endif /* WOLFSSL_ASN_TEMPLATE */
return ret;

}

int DecodeAsymKeyPublic(const byte* input, word32* inOutIdx, word32 inSz,
Expand All @@ -35561,7 +35615,7 @@ int DecodeAsymKeyPublic(const byte* input, word32* inOutIdx, word32 inSz,

if (ret == 0) {
ret = DecodeAsymKeyPublic_Assign(input, inOutIdx, inSz, &pubKeyPtr,
&pubKeyPtrLen, keyType);
&pubKeyPtrLen, &keyType);
}
if ((ret == 0) && (pubKeyPtrLen > *pubKeyLen)) {
ret = BUFFER_E;
Expand Down
86 changes: 74 additions & 12 deletions wolfcrypt/src/dilithium.c
Original file line number Diff line number Diff line change
Expand Up @@ -9501,18 +9501,48 @@ int wc_dilithium_export_key(dilithium_key* key, byte* priv, word32 *privSz,

#ifndef WOLFSSL_DILITHIUM_NO_ASN1

/* Maps ASN.1 OID to wolfCrypt security level macros */
static int mapOidToSecLevel(word32 oid)
dgarske marked this conversation as resolved.
Show resolved Hide resolved
{
switch (oid) {
case ML_DSA_LEVEL2k:
return WC_ML_DSA_44;
case ML_DSA_LEVEL3k:
return WC_ML_DSA_65;
case ML_DSA_LEVEL5k:
return WC_ML_DSA_87;
#ifdef WOLFSSL_DILITHIUM_FIPS204_DRAFT
case DILITHIUM_LEVEL2k:
return WC_ML_DSA_44_DRAFT;
case DILITHIUM_LEVEL3k:
return WC_ML_DSA_65_DRAFT;
case DILITHIUM_LEVEL5k:
return WC_ML_DSA_87_DRAFT;
#endif
default:
return ASN_UNKNOWN_OID_E;
}
}

#if defined(WOLFSSL_DILITHIUM_PRIVATE_KEY)

/* Decode the DER encoded Dilithium key.
*
* @param [in] input Array holding DER encoded data.
* @param [in, out] inOutIdx On in, index into array of start of DER encoding.
* On out, index into array after DER encoding.
* @param [in, out] key Dilithium key to store key.
* @param [in] inSz Total size of data in array.
* @param [in, out] key Dilithium key structure to hold the decoded key.
* If the security level is set in the key structure on
* input, the DER key will be decoded as such and will
* fail if there is a mismatch. If the level and
* parameters are not set in the key structure on
* input, the level will be detected from the DER
* file based on the algorithm OID, appropriately
* decoded, then updated in the key structure on
* output.
* @param [in] inSz Total size of the input DER buffer array.
* @return 0 on success.
* @return BAD_FUNC_ARG when input, inOutIdx or key is NULL or inSz is 0.
* @return BAD_FUNC_ARG when level not set.
* @return Other negative on parse error.
*/
int wc_Dilithium_PrivateKeyDecode(const byte* input, word32* inOutIdx,
Expand Down Expand Up @@ -9557,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(input, inOutIdx, inSz,
&privKey, &privKeyLen,
&pubKey, &pubKeyLen, &keytype);
if (ret == 0
#ifdef WOLFSSL_WC_DILITHIUM
&& key->params == NULL
dgarske marked this conversation as resolved.
Show resolved Hide resolved
#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. */
Expand Down Expand Up @@ -9756,7 +9798,15 @@ static int dilithium_check_type(const byte* input, word32* inOutIdx, byte type,
* @param [in] input Array holding DER encoded data.
* @param [in, out] inOutIdx On in, index into array of start of DER encoding.
* On out, index into array after DER encoding.
* @param [in, out] key Dilithium key to store key.
* @param [in, out] key Dilithium key structure to hold the decoded key.
* If the security level is set in the key structure
* on input, the DER key will be decoded as such
* and will fail if there is a mismatch. If the level
* and parameters are not set in the key structure on
* input, the level will be detected from the DER file
* based on the algorithm OID, appropriately decoded,
* then updated in the key structure on output.
* updated in the key structure on output.
* @param [in] inSz Total size of data in array.
* @return 0 on success.
* @return BAD_FUNC_ARG when input, inOutIdx or key is NULL or inSz is 0.
Expand Down Expand Up @@ -9818,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(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. */
Expand Down
Loading
Loading