Skip to content

Commit

Permalink
Merge pull request #8177 from bigbrett/dilithium-get-algo-from-der
Browse files Browse the repository at this point in the history
ML-DSA/Dilithium: obtain security level from DER when decoding
  • Loading branch information
dgarske authored Nov 19, 2024
2 parents 261ddc1 + 26d3b00 commit 18f52b2
Show file tree
Hide file tree
Showing 6 changed files with 408 additions and 37 deletions.
6 changes: 4 additions & 2 deletions tests/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -34999,10 +34999,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
88 changes: 69 additions & 19 deletions wolfcrypt/src/asn.c
Original file line number Diff line number Diff line change
Expand Up @@ -35319,9 +35319,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)
{
#ifndef WOLFSSL_ASN_TEMPLATE
word32 oid;
Expand All @@ -35335,7 +35336,7 @@ int DecodeAsymKey_Assign(const byte* input, word32* inOutIdx, word32 inSz,
#endif

if (input == NULL || inOutIdx == NULL || inSz == 0 ||
privKey == NULL || privKeyLen == NULL) {
privKey == NULL || privKeyLen == NULL || inOutKeyType == NULL) {
#ifdef WOLFSSL_ASN_TEMPLATE
FREE_ASNGETDATA(dataASN, NULL);
#endif
Expand All @@ -35349,14 +35350,22 @@ 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)

/* 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 @@ -35406,10 +35415,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 != ANONk) {
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 @@ -35422,6 +35442,12 @@ int DecodeAsymKey_Assign(const byte* input, word32* inOutIdx, word32 inSz,
ret = ASN_PARSE_E;
}
}

/* Store detected OID if requested */
if (ret == 0 && *inOutKeyType == ANONk) {
*inOutKeyType =
(int)dataASN[EDKEYASN_IDX_PKEYALGO_OID].data.oid.sum;
}
}
if (ret == 0) {
/* Import private value. */
Expand Down Expand Up @@ -35462,7 +35488,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 @@ -35485,7 +35511,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 @@ -35497,7 +35523,7 @@ int DecodeAsymKeyPublic_Assign(const byte* input, word32* inOutIdx, word32 inSz,
#endif

if (input == NULL || inSz == 0 || inOutIdx == NULL ||
pubKey == NULL || pubKeyLen == NULL) {
pubKey == NULL || pubKeyLen == NULL || inOutKeyType == NULL) {
return BAD_FUNC_ARG;
}

Expand All @@ -35510,8 +35536,16 @@ 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)

/* 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 @@ -35531,19 +35565,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 != 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 == ANONk) {
*inOutKeyType =
(int)dataASN[PUBKEYASN_IDX_ALGOID_OID].data.oid.sum;
}
}
/* Check that the all the buffer was used. */
if ((ret == 0) &&
Expand All @@ -35558,6 +35607,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 @@ -35573,7 +35623,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)
{
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
#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

0 comments on commit 18f52b2

Please sign in to comment.