Преглед на файлове

Merge pull request #8177 from bigbrett/dilithium-get-algo-from-der

ML-DSA/Dilithium: obtain security level from DER when decoding
David Garske преди 2 дни
родител
ревизия
18f52b2573
променени са 6 файла, в които са добавени 408 реда и са изтрити 37 реда
  1. 4 2
      tests/api.c
  2. 69 19
      wolfcrypt/src/asn.c
  3. 74 12
      wolfcrypt/src/dilithium.c
  4. 216 1
      wolfcrypt/test/test.c
  5. 4 2
      wolfssl/wolfcrypt/asn.h
  6. 41 1
      wolfssl/wolfcrypt/dilithium.h

+ 4 - 2
tests/api.c

@@ -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);

+ 69 - 19
wolfcrypt/src/asn.c

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

+ 74 - 12
wolfcrypt/src/dilithium.c

@@ -9501,6 +9501,29 @@ 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.
@@ -9508,11 +9531,18 @@ int wc_dilithium_export_key(dilithium_key* key, byte* priv, word32 *privSz,
  * @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,
@@ -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. */
@@ -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.
@@ -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. */

+ 216 - 1
wolfcrypt/test/test.c

@@ -45858,6 +45858,211 @@ out:
 }
 #endif
 
+
+#if defined(WOLFSSL_DILITHIUM_PRIVATE_KEY) || \
+    defined(WOLFSSL_DILITHIUM_PUBLIC_KEY)
+/* 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         isPublicOnlyKey)
+{
+    int           ret;
+    dilithium_key key;
+    word32        idx;
+    byte*         der;
+    word32        derSz;
+
+    /* Size the buffer to accommodate the largest encoded key size */
+    const word32 maxDerSz = DILITHIUM_MAX_PRV_KEY_DER_SIZE;
+
+    /* Allocate DER buffer */
+    der = (byte*)XMALLOC(maxDerSz, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
+    if (der == NULL) {
+        return MEMORY_E;
+    }
+
+    /* Initialize key */
+    ret = wc_dilithium_init(&key);
+
+    /* Import raw key, setting the security level */
+    if (ret == 0) {
+        ret = wc_dilithium_set_level(&key, expectedLevel);
+    }
+
+    if (ret == 0) {
+#ifdef WOLFSSL_DILITHIUM_PUBLIC_KEY
+        if (isPublicOnlyKey) {
+            ret = wc_dilithium_import_public(rawKey, rawKeySz, &key);
+        }
+#endif
+#ifdef WOLFSSL_DILITHIUM_PRIVATE_KEY
+        if (!isPublicOnlyKey) {
+            ret = wc_dilithium_import_private(rawKey, rawKeySz, &key);
+        }
+#endif
+    }
+
+    /* Export raw key as DER */
+    if (ret == 0) {
+#ifdef WOLFSSL_DILITHIUM_PUBLIC_KEY
+        if (isPublicOnlyKey) {
+            ret = wc_Dilithium_PublicKeyToDer(&key, der, maxDerSz, 1);
+        }
+#endif
+#ifdef WOLFSSL_DILITHIUM_PRIVATE_KEY
+        if (!isPublicOnlyKey) {
+            ret = wc_Dilithium_PrivateKeyToDer(&key, der, maxDerSz);
+        }
+#endif
+        if (ret >= 0) {
+            derSz = ret;
+            ret   = 0;
+        }
+    }
+
+    /* Free and reinit key to test fresh decode */
+    if (ret == 0) {
+        wc_dilithium_free(&key);
+        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;
+#ifdef WOLFSSL_DILITHIUM_PUBLIC_KEY
+        if (isPublicOnlyKey) {
+            ret = wc_Dilithium_PublicKeyDecode(der, &idx, &key, derSz);
+        }
+#endif
+#ifdef WOLFSSL_DILITHIUM_PRIVATE_KEY
+        if (!isPublicOnlyKey) {
+            ret = wc_Dilithium_PrivateKeyDecode(der, &idx, &key, derSz);
+        }
+#endif
+    }
+
+    /* 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;
+#ifdef WOLFSSL_DILITHIUM_PUBLIC_KEY
+        if (isPublicOnlyKey) {
+            ret = wc_Dilithium_PublicKeyDecode(der, &idx, &key, derSz);
+        }
+#endif
+#ifdef WOLFSSL_DILITHIUM_PRIVATE_KEY
+        if (!isPublicOnlyKey) {
+            ret = wc_Dilithium_PrivateKeyDecode(der, &idx, &key, derSz);
+        }
+#endif
+    }
+
+    /* 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",
+               expectedLevel, key.level);
+        ret = WC_TEST_RET_ENC_NC;
+    }
+
+    /* Cleanup */
+    XFREE(der, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
+    wc_dilithium_free(&key);
+    return ret;
+}
+
+/* Test Dilithium key decoding and security level detection */
+static wc_test_ret_t dilithium_decode_test(void)
+{
+    wc_test_ret_t ret;
+    const byte*   key;
+    word32        keySz;
+
+#ifdef WOLFSSL_DILITHIUM_PRIVATE_KEY
+    const int isPrvKey = 0;
+#endif
+#ifdef WOLFSSL_DILITHIUM_PUBLIC_KEY
+    const int isPubKey = 1;
+#endif
+
+#ifndef WOLFSSL_NO_ML_DSA_44
+#ifdef WOLFSSL_DILITHIUM_PRIVATE_KEY
+    /* Test 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;
+    }
+#endif /* WOLFSSL_DILITHIUM_PRIVATE_KEY */
+
+#ifdef WOLFSSL_DILITHIUM_PUBLIC_KEY
+    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;
+    }
+#endif /* WOLFSSL_DILITHIUM_PUBLIC_KEY */
+#endif /* WOLFSSL_NO_ML_DSA_44 */
+
+#ifndef WOLFSSL_NO_ML_DSA_65
+#ifdef WOLFSSL_DILITHIUM_PRIVATE_KEY
+    /* Test 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;
+    }
+#endif /* WOLFSSL_DILITHIUM_PRIVATE_KEY */
+
+#ifdef WOLFSSL_DILITHIUM_PUBLIC_KEY
+    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;
+    }
+#endif /* WOLFSSL_DILITHIUM_PUBLIC_KEY */
+#endif /* WOLFSSL_NO_ML_DSA_65 */
+
+#ifndef WOLFSSL_NO_ML_DSA_87
+#ifdef WOLFSSL_DILITHIUM_PRIVATE_KEY
+    /* Test 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;
+    }
+#endif /* WOLFSSL_DILITHIUM_PRIVATE_KEY */
+
+#ifdef WOLFSSL_DILITHIUM_PUBLIC_KEY
+    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;
+    }
+#endif /* WOLFSSL_DILITHIUM_PUBLIC_KEY */
+#endif /* WOLFSSL_NO_ML_DSA_87 */
+
+    return ret;
+}
+#endif /* WOLFSSL_DILITHIUM_PUBLIC_KEY || WOLFSSL_DILITHIUM_PRIVATE_KEY */
+
+
 WOLFSSL_TEST_SUBROUTINE wc_test_ret_t dilithium_test(void)
 {
     wc_test_ret_t ret;
@@ -45916,8 +46121,18 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t dilithium_test(void)
 #endif
 #endif
 
+#if defined(WOLFSSL_DILITHIUM_PRIVATE_KEY) || \
+    defined(WOLFSSL_DILITHIUM_PUBLIC_KEY)
+    ret = dilithium_decode_test();
+    if (ret != 0) {
+        ERROR_OUT(WC_TEST_RET_ENC_EC(ret), out);
+    }
+#endif /* WOLFSSL_DILITHIUM_PRIVATE_KEY || WOLFSSL_DILITHIUM_PUBLIC_KEY */
+
 #if !defined(WOLFSSL_DILITHIUM_NO_MAKE_KEY) || \
-    !defined(WOLFSSL_DILITHIUM_NO_VERIFY)
+    !defined(WOLFSSL_DILITHIUM_NO_VERIFY) || \
+    defined(WOLFSSL_DILITHIUM_PRIVATE_KEY) || \
+    defined(WOLFSSL_DILITHIUM_PUBLIC_KEY)
 out:
 #endif
     wc_FreeRng(&rng);

+ 4 - 2
wolfssl/wolfcrypt/asn.h

@@ -2546,7 +2546,8 @@ WOLFSSL_LOCAL int SetAsymKeyDerPublic(const byte* pubKey, word32 pubKeyLen,
     byte* output, word32 outLen, int keyType, int withHeader);
 WOLFSSL_LOCAL int DecodeAsymKeyPublic_Assign(const byte* input,
     word32* inOutIdx, word32 inSz, const byte** pubKey, word32* pubKeyLen,
-    int keyType);
+    int* keyType);
+
 WOLFSSL_LOCAL int DecodeAsymKeyPublic(const byte* input, word32* inOutIdx,
     word32 inSz, byte* pubKey, word32* pubKeyLen, int keyType);
 
@@ -2901,7 +2902,8 @@ WOLFSSL_LOCAL int  VerifyX509Acert(const byte* cert, word32 certSz,
     || 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 keyType);
+    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);

+ 41 - 1
wolfssl/wolfcrypt/dilithium.h

@@ -114,37 +114,55 @@
 #define DILITHIUM_LEVEL2_PUB_KEY_SIZE   1312
 #define DILITHIUM_LEVEL2_PRV_KEY_SIZE   \
     (DILITHIUM_LEVEL2_PUB_KEY_SIZE + DILITHIUM_LEVEL2_KEY_SIZE)
+/* Buffer sizes large enough to store exported DER encoded keys */
+#define DILITHIUM_LEVEL2_PUB_KEY_DER_SIZE 1334
+#define DILITHIUM_LEVEL2_PRV_KEY_DER_SIZE 2588
 
 #define DILITHIUM_LEVEL3_KEY_SIZE       4032
 #define DILITHIUM_LEVEL3_SIG_SIZE       3309
 #define DILITHIUM_LEVEL3_PUB_KEY_SIZE   1952
 #define DILITHIUM_LEVEL3_PRV_KEY_SIZE   \
     (DILITHIUM_LEVEL3_PUB_KEY_SIZE + DILITHIUM_LEVEL3_KEY_SIZE)
+/* Buffer sizes large enough to store exported DER encoded keys */
+#define DILITHIUM_LEVEL3_PUB_KEY_DER_SIZE 1974
+#define DILITHIUM_LEVEL3_PRV_KEY_DER_SIZE 4060
+
 
 #define DILITHIUM_LEVEL5_KEY_SIZE       4896
 #define DILITHIUM_LEVEL5_SIG_SIZE       4627
 #define DILITHIUM_LEVEL5_PUB_KEY_SIZE   2592
 #define DILITHIUM_LEVEL5_PRV_KEY_SIZE   \
     (DILITHIUM_LEVEL5_PUB_KEY_SIZE + DILITHIUM_LEVEL5_KEY_SIZE)
-
+/* Buffer sizes large enough to store exported DER encoded keys */
+#define DILITHIUM_LEVEL5_PUB_KEY_DER_SIZE 2614
+#define DILITHIUM_LEVEL5_PRV_KEY_DER_SIZE 4924
 
 #define ML_DSA_LEVEL2_KEY_SIZE          2560
 #define ML_DSA_LEVEL2_SIG_SIZE          2420
 #define ML_DSA_LEVEL2_PUB_KEY_SIZE      1312
 #define ML_DSA_LEVEL2_PRV_KEY_SIZE   \
     (ML_DSA_LEVEL2_PUB_KEY_SIZE + ML_DSA_LEVEL2_KEY_SIZE)
+/* Buffer sizes large enough to store exported DER encoded keys */
+#define ML_DSA_LEVEL2_PUB_KEY_DER_SIZE DILITHIUM_LEVEL2_PUB_KEY_DER_SIZE
+#define ML_DSA_LEVEL2_PRV_KEY_DER_SIZE DILITHIUM_LEVEL2_PRV_KEY_DER_SIZE
 
 #define ML_DSA_LEVEL3_KEY_SIZE          4032
 #define ML_DSA_LEVEL3_SIG_SIZE          3309
 #define ML_DSA_LEVEL3_PUB_KEY_SIZE      1952
 #define ML_DSA_LEVEL3_PRV_KEY_SIZE   \
     (ML_DSA_LEVEL3_PUB_KEY_SIZE + ML_DSA_LEVEL3_KEY_SIZE)
+/* Buffer sizes large enough to store exported DER encoded keys */
+#define ML_DSA_LEVEL3_PUB_KEY_DER_SIZE DILITHIUM_LEVEL3_PUB_KEY_DER_SIZE
+#define ML_DSA_LEVEL3_PRV_KEY_DER_SIZE DILITHIUM_LEVEL3_PRV_KEY_DER_SIZE
 
 #define ML_DSA_LEVEL5_KEY_SIZE          4896
 #define ML_DSA_LEVEL5_SIG_SIZE          4627
 #define ML_DSA_LEVEL5_PUB_KEY_SIZE      2592
 #define ML_DSA_LEVEL5_PRV_KEY_SIZE   \
     (ML_DSA_LEVEL5_PUB_KEY_SIZE + ML_DSA_LEVEL5_KEY_SIZE)
+/* Buffer sizes large enough to store exported DER encoded keys */
+#define ML_DSA_LEVEL5_PUB_KEY_DER_SIZE DILITHIUM_LEVEL5_PUB_KEY_DER_SIZE
+#define ML_DSA_LEVEL5_PRV_KEY_DER_SIZE DILITHIUM_LEVEL5_PRV_KEY_DER_SIZE
 
 
 
@@ -503,18 +521,27 @@
 #define DILITHIUM_LEVEL2_PUB_KEY_SIZE OQS_SIG_ml_dsa_44_ipd_length_public_key
 #define DILITHIUM_LEVEL2_PRV_KEY_SIZE \
     (DILITHIUM_LEVEL2_PUB_KEY_SIZE+DILITHIUM_LEVEL2_KEY_SIZE)
+/* Buffer sizes large enough to store exported DER encoded keys */
+#define DILITHIUM_LEVEL2_PUB_KEY_DER_SIZE 1334
+#define DILITHIUM_LEVEL2_PRV_KEY_DER_SIZE 2588
 
 #define DILITHIUM_LEVEL3_KEY_SIZE     OQS_SIG_ml_dsa_65_ipd_length_secret_key
 #define DILITHIUM_LEVEL3_SIG_SIZE     OQS_SIG_ml_dsa_65_ipd_length_signature
 #define DILITHIUM_LEVEL3_PUB_KEY_SIZE OQS_SIG_ml_dsa_65_ipd_length_public_key
 #define DILITHIUM_LEVEL3_PRV_KEY_SIZE \
     (DILITHIUM_LEVEL3_PUB_KEY_SIZE+DILITHIUM_LEVEL3_KEY_SIZE)
+/* Buffer sizes large enough to store exported DER encoded keys */
+#define DILITHIUM_LEVEL3_PUB_KEY_DER_SIZE 1974
+#define DILITHIUM_LEVEL3_PRV_KEY_DER_SIZE 4060
 
 #define DILITHIUM_LEVEL5_KEY_SIZE     OQS_SIG_ml_dsa_87_ipd_length_secret_key
 #define DILITHIUM_LEVEL5_SIG_SIZE     OQS_SIG_ml_dsa_87_ipd_length_signature
 #define DILITHIUM_LEVEL5_PUB_KEY_SIZE OQS_SIG_ml_dsa_87_ipd_length_public_key
 #define DILITHIUM_LEVEL5_PRV_KEY_SIZE \
     (DILITHIUM_LEVEL5_PUB_KEY_SIZE+DILITHIUM_LEVEL5_KEY_SIZE)
+/* Buffer sizes large enough to store exported DER encoded keys */
+#define DILITHIUM_LEVEL5_PUB_KEY_DER_SIZE 2614
+#define DILITHIUM_LEVEL5_PRV_KEY_DER_SIZE 4924
 
 
 #define ML_DSA_LEVEL2_KEY_SIZE        OQS_SIG_ml_dsa_44_ipd_length_secret_key
@@ -522,18 +549,27 @@
 #define ML_DSA_LEVEL2_PUB_KEY_SIZE    OQS_SIG_ml_dsa_44_ipd_length_public_key
 #define ML_DSA_LEVEL2_PRV_KEY_SIZE    \
     (ML_DSA_LEVEL2_PUB_KEY_SIZE+ML_DSA_LEVEL2_KEY_SIZE)
+/* Buffer sizes large enough to store exported DER encoded keys */
+#define ML_DSA_LEVEL2_PUB_KEY_DER_SIZE DILITHIUM_LEVEL2_PUB_KEY_DER_SIZE
+#define ML_DSA_LEVEL2_PRV_KEY_DER_SIZE DILITHIUM_LEVEL2_PRV_KEY_DER_SIZE
 
 #define ML_DSA_LEVEL3_KEY_SIZE        OQS_SIG_ml_dsa_65_ipd_length_secret_key
 #define ML_DSA_LEVEL3_SIG_SIZE        OQS_SIG_ml_dsa_65_ipd_length_signature
 #define ML_DSA_LEVEL3_PUB_KEY_SIZE    OQS_SIG_ml_dsa_65_ipd_length_public_key
 #define ML_DSA_LEVEL3_PRV_KEY_SIZE    \
     (ML_DSA_LEVEL3_PUB_KEY_SIZE+ML_DSA_LEVEL3_KEY_SIZE)
+/* Buffer sizes large enough to store exported DER encoded keys */
+#define ML_DSA_LEVEL3_PUB_KEY_DER_SIZE DILITHIUM_LEVEL3_PUB_KEY_DER_SIZE
+#define ML_DSA_LEVEL3_PRV_KEY_DER_SIZE DILITHIUM_LEVEL3_PRV_KEY_DER_SIZE
 
 #define ML_DSA_LEVEL5_KEY_SIZE        OQS_SIG_ml_dsa_87_ipd_length_secret_key
 #define ML_DSA_LEVEL5_SIG_SIZE        OQS_SIG_ml_dsa_87_ipd_length_signature
 #define ML_DSA_LEVEL5_PUB_KEY_SIZE    OQS_SIG_ml_dsa_87_ipd_length_public_key
 #define ML_DSA_LEVEL5_PRV_KEY_SIZE    \
     (ML_DSA_LEVEL5_PUB_KEY_SIZE+ML_DSA_LEVEL5_KEY_SIZE)
+/* Buffer sizes large enough to store exported DER encoded keys */
+#define ML_DSA_LEVEL5_PUB_KEY_DER_SIZE DILITHIUM_LEVEL5_PUB_KEY_DER_SIZE
+#define ML_DSA_LEVEL5_PRV_KEY_DER_SIZE DILITHIUM_LEVEL5_PRV_KEY_DER_SIZE
 
 #endif
 
@@ -541,6 +577,10 @@
 #define DILITHIUM_MAX_SIG_SIZE     DILITHIUM_LEVEL5_SIG_SIZE
 #define DILITHIUM_MAX_PUB_KEY_SIZE DILITHIUM_LEVEL5_PUB_KEY_SIZE
 #define DILITHIUM_MAX_PRV_KEY_SIZE DILITHIUM_LEVEL5_PRV_KEY_SIZE
+/* Buffer sizes large enough to store exported DER encoded keys */
+#define DILITHIUM_MAX_PUB_KEY_DER_SIZE DILITHIUM_LEVEL5_PUB_KEY_DER_SIZE
+#define DILITHIUM_MAX_PRV_KEY_DER_SIZE DILITHIUM_LEVEL5_PRV_KEY_DER_SIZE
+
 
 #ifdef WOLF_PRIVATE_KEY_ID
 #define DILITHIUM_MAX_ID_LEN    32