diff --git a/doc/dox_comments/header_files/asn_public.h b/doc/dox_comments/header_files/asn_public.h index 5f9d06aafcf..2592e5cd005 100644 --- a/doc/dox_comments/header_files/asn_public.h +++ b/doc/dox_comments/header_files/asn_public.h @@ -2830,10 +2830,48 @@ int wc_Curve25519KeyToDer(curve25519_key* key, byte* output, word32 outLen, \endcode \sa wc_Ed25519PrivateKeyToDer + \sa wc_Ed25519PrivateKeyDecode_ex */ int wc_Ed25519PrivateKeyDecode(const byte* input, word32* inOutIdx, ed25519_key* key, word32 inSz); +/*! + \ingroup Ed25519 + \brief Decodes Ed25519 private key from DER format with control over + validation of a bundled public key. When the DER contains a public key + and trusted is 0, the public key is validated with + wc_ed25519_check_key(), including a check that it matches the private + key. When trusted is 1 all validation of the bundled public key is + skipped, so trusted should only be set with known-good key material. + wc_Ed25519PrivateKeyDecode() behaves as trusted set to 0. + + \return 0 on success + \return negative on error + + \param input DER encoded Ed25519 private key buffer + \param inOutIdx Pointer to index in buffer + \param key Ed25519 key structure to store key + \param inSz Size of input buffer + \param trusted Indicates whether the bundled public key data is trusted. + When 0, the public key is validated with wc_ed25519_check_key(). + When 1, the public key is imported without any validation. + + _Example_ + \code + ed25519_key key; + word32 idx = 0; + // key material comes from this application's own storage + int ret = wc_Ed25519PrivateKeyDecode_ex(derBuf, &idx, &key, + derSz, 1); + \endcode + + \sa wc_Ed25519PrivateKeyDecode + \sa wc_Ed25519PrivateKeyToDer + \sa wc_ed25519_import_private_key_ex +*/ +int wc_Ed25519PrivateKeyDecode_ex(const byte* input, word32* inOutIdx, + ed25519_key* key, word32 inSz, int trusted); + /*! \ingroup Ed25519 \brief Decodes Ed25519 public key from DER format. @@ -2952,10 +2990,48 @@ int wc_Ed25519PublicKeyToDer(const ed25519_key* key, byte* output, \endcode \sa wc_Ed448PrivateKeyToDer + \sa wc_Ed448PrivateKeyDecode_ex */ int wc_Ed448PrivateKeyDecode(const byte* input, word32* inOutIdx, ed448_key* key, word32 inSz); +/*! + \ingroup Ed448 + \brief Decodes Ed448 private key from DER format with control over + validation of a bundled public key. When the DER contains a public key + and trusted is 0, the public key is validated with wc_ed448_check_key(), + including a check that it matches the private key. When trusted is 1 + all validation of the bundled public key is skipped, so trusted should + only be set with known-good key material. wc_Ed448PrivateKeyDecode() + behaves as trusted set to 0. + + \return 0 on success + \return negative on error + + \param input DER encoded Ed448 private key buffer + \param inOutIdx Pointer to index in buffer + \param key Ed448 key structure to store key + \param inSz Size of input buffer + \param trusted Indicates whether the bundled public key data is trusted. + When 0, the public key is validated with wc_ed448_check_key(). + When 1, the public key is imported without any validation. + + _Example_ + \code + ed448_key key; + word32 idx = 0; + // key material comes from this application's own storage + int ret = wc_Ed448PrivateKeyDecode_ex(derBuf, &idx, &key, + derSz, 1); + \endcode + + \sa wc_Ed448PrivateKeyDecode + \sa wc_Ed448PrivateKeyToDer + \sa wc_ed448_import_private_key_ex +*/ +int wc_Ed448PrivateKeyDecode_ex(const byte* input, word32* inOutIdx, + ed448_key* key, word32 inSz, int trusted); + /*! \ingroup Ed448 \brief Decodes Ed448 public key from DER format. diff --git a/tests/api/test_ed25519.c b/tests/api/test_ed25519.c index d9ff72b18e5..9e84df51015 100644 --- a/tests/api/test_ed25519.c +++ b/tests/api/test_ed25519.c @@ -731,6 +731,68 @@ int test_wc_Ed25519KeyToDer_oneasymkey_version(void) return EXPECT_RESULT(); } +/* The trusted flag controls whether a public key bundled in the DER is + * checked against the private key during decode. */ +int test_wc_Ed25519PrivateKeyDecode_ex(void) +{ + EXPECT_DECLS; +#if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT) && \ + defined(HAVE_ED25519_KEY_IMPORT) && defined(HAVE_ED25519_MAKE_KEY) + ed25519_key key; + ed25519_key key2; + WC_RNG rng; + byte der[256]; + int derSz = 0; + word32 idx; + + XMEMSET(&key, 0, sizeof(key)); + XMEMSET(&key2, 0, sizeof(key2)); + XMEMSET(&rng, 0, sizeof(rng)); + + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectIntEQ(wc_ed25519_init(&key), 0); + ExpectIntEQ(wc_ed25519_init(&key2), 0); + ExpectIntEQ(wc_ed25519_make_key(&rng, ED25519_KEY_SIZE, &key), 0); + + /* Bundled private+public DER; public key is the trailing + * ED25519_PUB_KEY_SIZE bytes. */ + ExpectIntGT(derSz = wc_Ed25519KeyToDer(&key, der, (word32)sizeof(der)), + 0); + + /* Intact DER decodes with either trust setting. */ + idx = 0; + ExpectIntEQ(wc_Ed25519PrivateKeyDecode_ex(der, &idx, &key2, + (word32)derSz, 0), 0); + idx = 0; + ExpectIntEQ(wc_Ed25519PrivateKeyDecode_ex(der, &idx, &key2, + (word32)derSz, 1), 0); + + /* Corrupt the bundled public key. */ + if (derSz > 0) { + der[derSz - 1] ^= 0x01; + } + + /* Untrusted decode checks the public key and must reject it, as must + * the non-ex decode. */ + idx = 0; + ExpectIntEQ(wc_Ed25519PrivateKeyDecode_ex(der, &idx, &key2, + (word32)derSz, 0), WC_NO_ERR_TRACE(PUBLIC_KEY_E)); + idx = 0; + ExpectIntEQ(wc_Ed25519PrivateKeyDecode(der, &idx, &key2, (word32)derSz), + WC_NO_ERR_TRACE(PUBLIC_KEY_E)); + + /* Trusted decode skips the check. */ + idx = 0; + ExpectIntEQ(wc_Ed25519PrivateKeyDecode_ex(der, &idx, &key2, + (word32)derSz, 1), 0); + + wc_ed25519_free(&key); + wc_ed25519_free(&key2); + wc_FreeRng(&rng); +#endif + return EXPECT_RESULT(); +} + /* Ed25519 identity and small-order public keys must be rejected. When * the public key is the identity point (or any small-order point), any * signature of the form (R = [S]B, S) verifies for arbitrary messages diff --git a/tests/api/test_ed25519.h b/tests/api/test_ed25519.h index e7ac8fe7427..ecb3e05fcf0 100644 --- a/tests/api/test_ed25519.h +++ b/tests/api/test_ed25519.h @@ -37,6 +37,7 @@ int test_wc_Ed25519PublicKeyToDer(void); int test_wc_Ed25519KeyToDer(void); int test_wc_Ed25519PrivateKeyToDer(void); int test_wc_Ed25519KeyToDer_oneasymkey_version(void); +int test_wc_Ed25519PrivateKeyDecode_ex(void); int test_wc_ed25519_reject_small_order_keys(void); int test_wc_ed25519_sign_verify_ctx_ph(void); int test_wc_ed25519_verify_streaming(void); @@ -63,6 +64,7 @@ int test_wc_ed25519_make_public_argchecks(void); TEST_DECL_GROUP("ed25519", test_wc_ed25519_verify_streaming), \ TEST_DECL_GROUP("ed25519", test_wc_ed25519_check_key_edgecases), \ TEST_DECL_GROUP("ed25519", test_wc_ed25519_import_variants), \ - TEST_DECL_GROUP("ed25519", test_wc_ed25519_make_public_argchecks) + TEST_DECL_GROUP("ed25519", test_wc_ed25519_make_public_argchecks), \ + TEST_DECL_GROUP("ed25519", test_wc_Ed25519PrivateKeyDecode_ex) #endif /* WOLFCRYPT_TEST_ED25519_H */ diff --git a/tests/api/test_ed448.c b/tests/api/test_ed448.c index 10ce68643a4..78832b153d0 100644 --- a/tests/api/test_ed448.c +++ b/tests/api/test_ed448.c @@ -663,6 +663,67 @@ int test_wc_Ed448KeyToDer_oneasymkey_version(void) return EXPECT_RESULT(); } +/* The trusted flag controls whether a public key bundled in the DER is + * checked against the private key during decode. */ +int test_wc_Ed448PrivateKeyDecode_ex(void) +{ + EXPECT_DECLS; +#if defined(HAVE_ED448) && defined(HAVE_ED448_KEY_EXPORT) && \ + defined(HAVE_ED448_KEY_IMPORT) + ed448_key key; + ed448_key key2; + WC_RNG rng; + byte der[512]; + int derSz = 0; + word32 idx; + + XMEMSET(&key, 0, sizeof(key)); + XMEMSET(&key2, 0, sizeof(key2)); + XMEMSET(&rng, 0, sizeof(rng)); + + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectIntEQ(wc_ed448_init(&key), 0); + ExpectIntEQ(wc_ed448_init(&key2), 0); + ExpectIntEQ(wc_ed448_make_key(&rng, ED448_KEY_SIZE, &key), 0); + + /* Bundled private+public DER; public key is the trailing + * ED448_PUB_KEY_SIZE bytes. */ + ExpectIntGT(derSz = wc_Ed448KeyToDer(&key, der, (word32)sizeof(der)), 0); + + /* Intact DER decodes with either trust setting. */ + idx = 0; + ExpectIntEQ(wc_Ed448PrivateKeyDecode_ex(der, &idx, &key2, + (word32)derSz, 0), 0); + idx = 0; + ExpectIntEQ(wc_Ed448PrivateKeyDecode_ex(der, &idx, &key2, + (word32)derSz, 1), 0); + + /* Corrupt the bundled public key. */ + if (derSz > 0) { + der[derSz - 1] ^= 0x01; + } + + /* Untrusted decode checks the public key and must reject it, as must + * the non-ex decode. */ + idx = 0; + ExpectIntEQ(wc_Ed448PrivateKeyDecode_ex(der, &idx, &key2, + (word32)derSz, 0), WC_NO_ERR_TRACE(PUBLIC_KEY_E)); + idx = 0; + ExpectIntEQ(wc_Ed448PrivateKeyDecode(der, &idx, &key2, (word32)derSz), + WC_NO_ERR_TRACE(PUBLIC_KEY_E)); + + /* Trusted decode skips the check. */ + idx = 0; + ExpectIntEQ(wc_Ed448PrivateKeyDecode_ex(der, &idx, &key2, + (word32)derSz, 1), 0); + + wc_ed448_free(&key); + wc_ed448_free(&key2); + wc_FreeRng(&rng); +#endif + return EXPECT_RESULT(); +} + /* Ed448 identity and small-order public keys must be rejected. * Edwards448 has cofactor 4, so the small-order subgroup contains the * identity, an order-2 point, and two order-4 points. With any of these diff --git a/tests/api/test_ed448.h b/tests/api/test_ed448.h index a40edd17bb3..91201313fa6 100644 --- a/tests/api/test_ed448.h +++ b/tests/api/test_ed448.h @@ -37,6 +37,7 @@ int test_wc_Ed448PublicKeyToDer(void); int test_wc_Ed448KeyToDer(void); int test_wc_Ed448PrivateKeyToDer(void); int test_wc_Ed448KeyToDer_oneasymkey_version(void); +int test_wc_Ed448PrivateKeyDecode_ex(void); int test_wc_ed448_reject_small_order_keys(void); #define TEST_ED448_DECLS \ @@ -53,6 +54,7 @@ int test_wc_ed448_reject_small_order_keys(void); TEST_DECL_GROUP("ed448", test_wc_Ed448KeyToDer), \ TEST_DECL_GROUP("ed448", test_wc_Ed448PrivateKeyToDer), \ TEST_DECL_GROUP("ed448", test_wc_Ed448KeyToDer_oneasymkey_version), \ + TEST_DECL_GROUP("ed448", test_wc_Ed448PrivateKeyDecode_ex), \ TEST_DECL_GROUP("ed448", test_wc_ed448_reject_small_order_keys) #endif /* WOLFCRYPT_TEST_ED448_H */ diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 8b870e3ce0b..6a9ebd2e89d 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -33691,8 +33691,8 @@ int DecodeAsymKeyPublic(const byte* input, word32* inOutIdx, word32 inSz, #endif /* WC_ENABLE_ASYM_KEY_IMPORT */ #if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_IMPORT) -int wc_Ed25519PrivateKeyDecode(const byte* input, word32* inOutIdx, - ed25519_key* key, word32 inSz) +int wc_Ed25519PrivateKeyDecode_ex(const byte* input, word32* inOutIdx, + ed25519_key* key, word32 inSz, int trusted) { int ret; byte privKey[ED25519_KEY_SIZE], pubKey[2*ED25519_PUB_KEY_SIZE+1]; @@ -33710,13 +33710,19 @@ int wc_Ed25519PrivateKeyDecode(const byte* input, word32* inOutIdx, ret = wc_ed25519_import_private_only(privKey, privKeyLen, key); } else { - ret = wc_ed25519_import_private_key(privKey, privKeyLen, - pubKey, pubKeyLen, key); + ret = wc_ed25519_import_private_key_ex(privKey, privKeyLen, + pubKey, pubKeyLen, key, trusted); } } return ret; } +int wc_Ed25519PrivateKeyDecode(const byte* input, word32* inOutIdx, + ed25519_key* key, word32 inSz) +{ + return wc_Ed25519PrivateKeyDecode_ex(input, inOutIdx, key, inSz, 0); +} + int wc_Ed25519PublicKeyDecode(const byte* input, word32* inOutIdx, ed25519_key* key, word32 inSz) { @@ -34114,8 +34120,8 @@ int wc_Curve25519KeyToDer(curve25519_key* key, byte* output, word32 outLen, #endif /* HAVE_CURVE25519 && HAVE_CURVE25519_KEY_EXPORT */ #if defined(HAVE_ED448) && defined(HAVE_ED448_KEY_IMPORT) -int wc_Ed448PrivateKeyDecode(const byte* input, word32* inOutIdx, - ed448_key* key, word32 inSz) +int wc_Ed448PrivateKeyDecode_ex(const byte* input, word32* inOutIdx, + ed448_key* key, word32 inSz, int trusted) { int ret; byte privKey[ED448_KEY_SIZE], pubKey[ED448_PUB_KEY_SIZE]; @@ -34133,13 +34139,19 @@ int wc_Ed448PrivateKeyDecode(const byte* input, word32* inOutIdx, ret = wc_ed448_import_private_only(privKey, privKeyLen, key); } else { - ret = wc_ed448_import_private_key(privKey, privKeyLen, - pubKey, pubKeyLen, key); + ret = wc_ed448_import_private_key_ex(privKey, privKeyLen, + pubKey, pubKeyLen, key, trusted); } } return ret; } +int wc_Ed448PrivateKeyDecode(const byte* input, word32* inOutIdx, + ed448_key* key, word32 inSz) +{ + return wc_Ed448PrivateKeyDecode_ex(input, inOutIdx, key, inSz, 0); +} + int wc_Ed448PublicKeyDecode(const byte* input, word32* inOutIdx, ed448_key* key, word32 inSz) { diff --git a/wolfssl/wolfcrypt/asn_public.h b/wolfssl/wolfcrypt/asn_public.h index d557ac94cd0..aec62717a86 100644 --- a/wolfssl/wolfcrypt/asn_public.h +++ b/wolfssl/wolfcrypt/asn_public.h @@ -850,6 +850,9 @@ WOLFSSL_API int wc_DhPrivKeyToDer(DhKey* key, byte* out, word32* outSz); #ifdef HAVE_ED25519_KEY_IMPORT WOLFSSL_API int wc_Ed25519PrivateKeyDecode(const byte* input, word32* inOutIdx, ed25519_key* key, word32 inSz); +WOLFSSL_API int wc_Ed25519PrivateKeyDecode_ex(const byte* input, + word32* inOutIdx, ed25519_key* key, word32 inSz, + int trusted); WOLFSSL_API int wc_Ed25519PublicKeyDecode(const byte* input, word32* inOutIdx, ed25519_key* key, word32 inSz); #endif @@ -886,6 +889,9 @@ WOLFSSL_API int wc_Curve25519KeyToDer(curve25519_key* key, byte* output, #ifdef HAVE_ED448_KEY_IMPORT WOLFSSL_API int wc_Ed448PrivateKeyDecode( const byte* input, word32* inOutIdx, ed448_key* key, word32 inSz); +WOLFSSL_API int wc_Ed448PrivateKeyDecode_ex( + const byte* input, word32* inOutIdx, ed448_key* key, word32 inSz, + int trusted); WOLFSSL_API int wc_Ed448PublicKeyDecode( const byte* input, word32* inOutIdx, ed448_key* key, word32 inSz); #endif