mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-08-04 03:14:13 +02:00
Merge pull request #10958 from Frauschi/fenrir
wolfCrypt security hardening, portability fixes, and negative test coverage
This commit is contained in:
@@ -33,6 +33,7 @@ int wc_Chacha_SetIV(ChaCha* ctx, const byte* inIv, word32 counter);
|
||||
|
||||
\return 0 入力の暗号化または復号に成功した場合に返されます
|
||||
\return BAD_FUNC_ARG ctx入力引数の処理中にエラーが発生した場合に返されます
|
||||
\return MISSING_KEY 処理前にwc_Chacha_SetKeyでキーが設定されていない場合に返されます
|
||||
|
||||
\param ctx ivを設定するChaCha構造体へのポインタ
|
||||
\param output 出力暗号文または復号された平文を格納するバッファへのポインタ
|
||||
|
||||
@@ -232,6 +232,7 @@ void wc_ecc_key_free(ecc_key* key);
|
||||
\return MP_MULMOD_E 共有鍵の計算中にエラーがある場合に返される可能性があります
|
||||
\return MP_TO_E 共有鍵の計算中にエラーがある場合に返される可能性があります
|
||||
\return MP_MEM 共有鍵の計算中にエラーがある場合に返される可能性があります
|
||||
\return ECC_INF_E 計算された共有秘密鍵が無限遠点である場合に返されます
|
||||
|
||||
\param private_key ローカル秘密鍵を含むecc_key構造体へのポインタ
|
||||
\param public_key 受信した公開鍵を含むecc_key構造体へのポインタ
|
||||
|
||||
@@ -41,6 +41,8 @@ int wc_Chacha_SetIV(ChaCha* ctx, const byte* inIv, word32 counter);
|
||||
\return 0 Returned upon successfully encrypting or decrypting the input
|
||||
\return BAD_FUNC_ARG returned if there is an error processing the ctx
|
||||
input argument
|
||||
\return MISSING_KEY returned if wc_Chacha_SetKey was not called to set a
|
||||
key before processing
|
||||
|
||||
\param ctx pointer to the ChaCha structure on which to set the iv
|
||||
\param output pointer to a buffer in which to store the output ciphertext
|
||||
|
||||
@@ -1050,6 +1050,14 @@ int wc_DhSetCheckKey(DhKey* key, const byte* p, word32 pSz,
|
||||
|
||||
/*!
|
||||
\ingroup Diffie-Hellman
|
||||
|
||||
\note Parameters that are not one of the built-in FFDHE groups are
|
||||
untrusted, so the modulus is tested for primality with random
|
||||
Miller-Rabin witnesses. This function takes no RNG, so it instantiates a
|
||||
temporary one, which allocates memory and consumes entropy on every such
|
||||
call. When neither is available the test falls back to fixed witness
|
||||
bases, which a purpose-built composite can defeat. Call
|
||||
wc_DhSetCheckKey() with an initialized WC_RNG to supply your own.
|
||||
*/
|
||||
int wc_DhSetKey_ex(DhKey* key, const byte* p, word32 pSz,
|
||||
const byte* g, word32 gSz, const byte* q, word32 qSz);
|
||||
|
||||
@@ -316,6 +316,8 @@ void wc_ecc_key_free(ecc_key* key);
|
||||
shared key
|
||||
\return MP_MEM may be returned if there is an error while computing the
|
||||
shared key
|
||||
\return ECC_INF_E returned when the computed shared secret is the point at
|
||||
infinity
|
||||
|
||||
\param private_key pointer to the ecc_key structure containing the local
|
||||
private key
|
||||
|
||||
@@ -703,3 +703,28 @@ int test_wc_Chacha_XChachaSetKey(void)
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
} /* END test_wc_Chacha_XChachaSetKey */
|
||||
|
||||
/*
|
||||
* A zero-initialized context that only receives a nonce, with the key setup
|
||||
* omitted, must not silently encrypt with an all-zero key. wc_Chacha_Process
|
||||
* has to reject the missing key instead of returning success.
|
||||
*/
|
||||
int test_wc_Chacha_MissingKey(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#ifdef HAVE_CHACHA
|
||||
ChaCha ctx;
|
||||
const byte iv[CHACHA_IV_BYTES] = { 0 };
|
||||
const byte input[32] = { 0 };
|
||||
byte cipher[32];
|
||||
|
||||
XMEMSET(&ctx, 0, sizeof(ctx));
|
||||
XMEMSET(cipher, 0, sizeof(cipher));
|
||||
|
||||
/* Nonce set, but wc_Chacha_SetKey() deliberately skipped. */
|
||||
ExpectIntEQ(wc_Chacha_SetIV(&ctx, iv, 0), 0);
|
||||
ExpectIntEQ(wc_Chacha_Process(&ctx, cipher, input, sizeof(input)),
|
||||
WC_NO_ERR_TRACE(MISSING_KEY));
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
} /* END test_wc_Chacha_MissingKey */
|
||||
|
||||
@@ -32,6 +32,7 @@ int test_wc_Chacha_CounterOverflow(void);
|
||||
int test_wc_Chacha_InPlace(void);
|
||||
int test_wc_Chacha_UnalignedBuffers(void);
|
||||
int test_wc_Chacha_XChachaSetKey(void);
|
||||
int test_wc_Chacha_MissingKey(void);
|
||||
|
||||
#define TEST_CHACHA_DECLS \
|
||||
TEST_DECL_GROUP("chacha", test_wc_Chacha_SetKey), \
|
||||
@@ -41,6 +42,7 @@ int test_wc_Chacha_XChachaSetKey(void);
|
||||
TEST_DECL_GROUP("chacha", test_wc_Chacha_CounterOverflow), \
|
||||
TEST_DECL_GROUP("chacha", test_wc_Chacha_InPlace), \
|
||||
TEST_DECL_GROUP("chacha", test_wc_Chacha_UnalignedBuffers), \
|
||||
TEST_DECL_GROUP("chacha", test_wc_Chacha_XChachaSetKey)
|
||||
TEST_DECL_GROUP("chacha", test_wc_Chacha_XChachaSetKey), \
|
||||
TEST_DECL_GROUP("chacha", test_wc_Chacha_MissingKey)
|
||||
|
||||
#endif /* WOLFCRYPT_TEST_CHACHA_H */
|
||||
|
||||
@@ -316,6 +316,41 @@ int test_wc_DhSetKey(void)
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
/*
|
||||
* wc_DhSetKey_ex validates untrusted parameters, so it must reject a composite
|
||||
* modulus even when that composite is a strong pseudoprime to the fixed
|
||||
* small-prime Miller-Rabin bases. n = 341550071728321 = 10670053 * 32010157 is
|
||||
* a strong pseudoprime to bases 2, 3, 5, 7, 11, 13, 17 and 19, which the
|
||||
* deterministic fixed-base test wrongly accepts as prime. Random-witness
|
||||
* testing rejects it. As with the library's own primality check, rejection is
|
||||
* probabilistic: eight random Miller-Rabin rounds leave a negligible
|
||||
* (well under 1e-4) chance of accepting the composite, so a one-off failure
|
||||
* here is statistical, not a regression.
|
||||
*/
|
||||
int test_wc_DhSetKey_ex_pseudoprime(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
/* wc_DhSetKey_ex takes no RNG, so it has to create one to get random
|
||||
* witnesses. Builds that cannot allocate it fall back to the fixed-base
|
||||
* test by design, and that test accepts this composite. */
|
||||
#if !defined(NO_DH) && !defined(HAVE_SELFTEST) && !defined(HAVE_FIPS) && \
|
||||
!defined(WC_NO_RNG) && !defined(WOLFSSL_NO_MALLOC) && \
|
||||
!defined(WOLFSSL_STATIC_MEMORY)
|
||||
DhKey key;
|
||||
/* n = 341550071728321, big-endian. */
|
||||
byte p[] = { 0x01, 0x36, 0xA3, 0x52, 0xB2, 0xC8, 0xC1 };
|
||||
byte g[] = { 0x02 };
|
||||
|
||||
XMEMSET(&key, 0, sizeof(key));
|
||||
|
||||
ExpectIntEQ(wc_InitDhKey(&key), 0);
|
||||
ExpectIntEQ(wc_DhSetKey_ex(&key, p, sizeof(p), g, sizeof(g), NULL, 0),
|
||||
WC_NO_ERR_TRACE(DH_CHECK_PUB_E));
|
||||
wc_FreeDhKey(&key);
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
/*
|
||||
* Testing wc_DhSetNamedKey(), wc_DhGetNamedKeyParamSize(),
|
||||
* wc_DhCopyNamedKey() and wc_DhCmpNamedKey().
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
int test_wc_DhPublicKeyDecode(void);
|
||||
int test_wc_DhAgree_subgroup_check(void);
|
||||
int test_wc_DhSetKey(void);
|
||||
int test_wc_DhSetKey_ex_pseudoprime(void);
|
||||
int test_wc_DhSetNamedKey_and_helpers(void);
|
||||
int test_wc_DhGenerateKeyPair_bad_args(void);
|
||||
int test_wc_DhGenerateKeyPair_and_Agree(void);
|
||||
@@ -42,6 +43,7 @@ int test_wc_DhGenerateKeyPair_CheckDhLN(void);
|
||||
TEST_DECL_GROUP("dh", test_wc_DhPublicKeyDecode), \
|
||||
TEST_DECL_GROUP("dh", test_wc_DhAgree_subgroup_check), \
|
||||
TEST_DECL_GROUP("dh", test_wc_DhSetKey), \
|
||||
TEST_DECL_GROUP("dh", test_wc_DhSetKey_ex_pseudoprime), \
|
||||
TEST_DECL_GROUP("dh", test_wc_DhSetNamedKey_and_helpers), \
|
||||
TEST_DECL_GROUP("dh", test_wc_DhGenerateKeyPair_bad_args), \
|
||||
TEST_DECL_GROUP("dh", test_wc_DhGenerateKeyPair_and_Agree), \
|
||||
|
||||
@@ -295,6 +295,67 @@ int test_wc_ecc_check_key(void)
|
||||
return EXPECT_RESULT();
|
||||
} /* END test_wc_ecc_check_key */
|
||||
|
||||
/*
|
||||
* Negative coverage for the public-key checks in wc_ecc_check_key. A point off
|
||||
* the curve must be rejected with IS_POINT_E, and a coordinate outside
|
||||
* [0, p-1] with ECC_OUT_OF_RANGE_E. Uses secp224r1, which is not single
|
||||
* precision accelerated, so the software validation path runs even in SP
|
||||
* builds that offload P-256.
|
||||
*/
|
||||
int test_wc_ecc_check_key_invalid_pubkey(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
/* Older FIPS-certified modules ship a frozen source tree where these
|
||||
* imports and checks behave differently, so restrict to non-FIPS or
|
||||
* FIPS v7 and later, and skip the CAVP selftest build. */
|
||||
#if (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) && \
|
||||
!defined(HAVE_SELFTEST) && \
|
||||
defined(HAVE_ECC) && defined(HAVE_ECC_KEY_IMPORT) && \
|
||||
!defined(NO_ECC_CHECK_PUBKEY_ORDER) && \
|
||||
!defined(WOLF_CRYPTO_CB_ONLY_ECC) && \
|
||||
(defined(HAVE_ECC224) || defined(HAVE_ALL_CURVES)) && \
|
||||
(ECC_MIN_KEY_SZ <= 224) && \
|
||||
!defined(WOLFSSL_VALIDATE_ECC_IMPORT) && !defined(WOLFSSL_SP_MATH) && \
|
||||
!defined(WOLFSSL_ATECC508A) && !defined(WOLFSSL_ATECC608A) && \
|
||||
!defined(WOLFSSL_MICROCHIP_TA100) && !defined(WOLFSSL_CRYPTOCELL) && \
|
||||
!defined(WOLFSSL_SILABS_SE_ACCEL) && !defined(WOLFSSL_SE050) && \
|
||||
!defined(WOLFSSL_STM32_PKA) && !defined(WOLFSSL_KCAPI_ECC)
|
||||
ecc_key key;
|
||||
const char* qx =
|
||||
"b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21";
|
||||
const char* qy =
|
||||
"bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34";
|
||||
/* Qy with its low bit flipped: still less than p, but not on the curve. */
|
||||
const char* qyOffCurve =
|
||||
"bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e35";
|
||||
/* p, the SECP224R1 field prime, used as an out-of-range coordinate. */
|
||||
const char* pModulus =
|
||||
"ffffffffffffffffffffffffffffffff000000000000000000000001";
|
||||
|
||||
/* Point not on the curve: rejected by the on-curve check. */
|
||||
XMEMSET(&key, 0, sizeof(key));
|
||||
ExpectIntEQ(wc_ecc_init(&key), 0);
|
||||
ExpectIntEQ(wc_ecc_import_raw(&key, qx, qyOffCurve, NULL, "SECP224R1"), 0);
|
||||
ExpectIntEQ(wc_ecc_check_key(&key), WC_NO_ERR_TRACE(IS_POINT_E));
|
||||
wc_ecc_free(&key);
|
||||
|
||||
/* Qx == p: rejected by the coordinate-range check. */
|
||||
XMEMSET(&key, 0, sizeof(key));
|
||||
ExpectIntEQ(wc_ecc_init(&key), 0);
|
||||
ExpectIntEQ(wc_ecc_import_raw(&key, pModulus, qy, NULL, "SECP224R1"), 0);
|
||||
ExpectIntEQ(wc_ecc_check_key(&key), WC_NO_ERR_TRACE(ECC_OUT_OF_RANGE_E));
|
||||
wc_ecc_free(&key);
|
||||
|
||||
/* Qy == p: rejected by the coordinate-range check. */
|
||||
XMEMSET(&key, 0, sizeof(key));
|
||||
ExpectIntEQ(wc_ecc_init(&key), 0);
|
||||
ExpectIntEQ(wc_ecc_import_raw(&key, qx, pModulus, NULL, "SECP224R1"), 0);
|
||||
ExpectIntEQ(wc_ecc_check_key(&key), WC_NO_ERR_TRACE(ECC_OUT_OF_RANGE_E));
|
||||
wc_ecc_free(&key);
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
} /* END test_wc_ecc_check_key_invalid_pubkey */
|
||||
|
||||
/*
|
||||
* Testing wc_ecc_get_generator()
|
||||
*/
|
||||
@@ -578,6 +639,108 @@ int test_wc_ecc_shared_secret(void)
|
||||
return EXPECT_RESULT();
|
||||
} /* END tests_wc_ecc_shared_secret */
|
||||
|
||||
/* ECC_INF_E rejection is not present in the frozen ecc.c of older
|
||||
* FIPS-certified modules, so restrict to non-FIPS or FIPS v7 and later, and
|
||||
* skip the CAVP selftest build. The hardware ports listed here either do not
|
||||
* compile wc_ecc_shared_secret_gen_sync at all or offload agreement to a
|
||||
* secure element, so the software rejection never runs. */
|
||||
#if (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) && \
|
||||
!defined(HAVE_SELFTEST) && \
|
||||
defined(HAVE_ECC) && defined(HAVE_ECC_DHE) && \
|
||||
defined(HAVE_ECC_KEY_IMPORT) && !defined(WC_NO_RNG) && \
|
||||
!defined(WOLFSSL_VALIDATE_ECC_IMPORT) && \
|
||||
!defined(WOLFSSL_ATECC508A) && !defined(WOLFSSL_ATECC608A) && \
|
||||
!defined(WOLFSSL_MICROCHIP_TA100) && !defined(WOLFSSL_CRYPTOCELL) && \
|
||||
!defined(WOLFSSL_SILABS_SE_ACCEL) && !defined(WOLFSSL_SE050) && \
|
||||
!defined(WOLFSSL_KCAPI_ECC) && !defined(WOLF_CRYPTO_CB_ONLY_ECC)
|
||||
#define TEST_ECC_SHARED_SECRET_AT_INFINITY
|
||||
|
||||
/* Agree with a private scalar equal to the curve order n. Every point on these
|
||||
* curves has order n, so n times the peer point is the identity for any peer
|
||||
* point, and wc_ecc_shared_secret() must fail instead of handing back an
|
||||
* all-zero secret. */
|
||||
static int ecc_shared_secret_inf_case(const char* qx, const char* qy,
|
||||
const char* order, const char* curveName)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
ecc_key key;
|
||||
ecc_key pubKey;
|
||||
WC_RNG rng;
|
||||
byte out[MAX_ECC_BYTES];
|
||||
word32 outlen = (word32)sizeof(out);
|
||||
|
||||
XMEMSET(&key, 0, sizeof(key));
|
||||
XMEMSET(&pubKey, 0, sizeof(pubKey));
|
||||
XMEMSET(&rng, 0, sizeof(rng));
|
||||
|
||||
PRIVATE_KEY_UNLOCK();
|
||||
|
||||
ExpectIntEQ(wc_ecc_init(&key), 0);
|
||||
ExpectIntEQ(wc_ecc_init(&pubKey), 0);
|
||||
ExpectIntEQ(wc_InitRng(&rng), 0);
|
||||
|
||||
ExpectIntEQ(wc_ecc_import_raw(&key, qx, qy, order, curveName), 0);
|
||||
ExpectIntEQ(wc_ecc_import_raw(&pubKey, qx, qy, NULL, curveName), 0);
|
||||
|
||||
#if defined(ECC_TIMING_RESISTANT) && (!defined(HAVE_FIPS) || \
|
||||
(!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION != 2))) && \
|
||||
!defined(HAVE_SELFTEST)
|
||||
ExpectIntEQ(wc_ecc_set_rng(&key, &rng), 0);
|
||||
#endif
|
||||
|
||||
ExpectIntEQ(wc_ecc_shared_secret(&key, &pubKey, out, &outlen),
|
||||
WC_NO_ERR_TRACE(ECC_INF_E));
|
||||
|
||||
DoExpectIntEQ(wc_FreeRng(&rng), 0);
|
||||
wc_ecc_free(&pubKey);
|
||||
wc_ecc_free(&key);
|
||||
#ifdef FP_ECC
|
||||
wc_ecc_fp_free();
|
||||
#endif
|
||||
PRIVATE_KEY_LOCK();
|
||||
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* A shared secret that computes to the point at infinity must be rejected
|
||||
* (SP 800-56Ar3 5.7.1.2), not returned as an all-zero secret. Both math
|
||||
* backends are covered because they detect the identity differently: the
|
||||
* software path checks the mapped point, while the single precision
|
||||
* generators report success and serialize the identity as an all-zero
|
||||
* x-coordinate, so it has to be caught from the output.
|
||||
*/
|
||||
int test_wc_ecc_shared_secret_at_infinity(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#ifdef TEST_ECC_SHARED_SECRET_AT_INFINITY
|
||||
#if (defined(HAVE_ECC224) || defined(HAVE_ALL_CURVES)) && \
|
||||
(ECC_MIN_KEY_SZ <= 224) && !defined(WOLFSSL_SP_MATH)
|
||||
/* secp224r1 is not single precision accelerated, so this drives the
|
||||
* software ECDH path even in SP builds that offload P-256. */
|
||||
ExpectIntEQ(ecc_shared_secret_inf_case(
|
||||
"b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21",
|
||||
"bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34",
|
||||
"ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d",
|
||||
"SECP224R1"), 1);
|
||||
#endif
|
||||
#if (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) && \
|
||||
(ECC_MIN_KEY_SZ <= 256) && \
|
||||
(!defined(WOLFSSL_SP_MATH) || \
|
||||
(defined(WOLFSSL_HAVE_SP_ECC) && !defined(WOLFSSL_SP_NO_256)))
|
||||
/* secp256r1 covers the single precision path in --enable-sp builds, the
|
||||
* common TLS configuration. Uses the curve generator as the peer point. */
|
||||
ExpectIntEQ(ecc_shared_secret_inf_case(
|
||||
"6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
|
||||
"4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5",
|
||||
"ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551",
|
||||
"SECP256R1"), 1);
|
||||
#endif
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
} /* END test_wc_ecc_shared_secret_at_infinity */
|
||||
|
||||
#if defined(HAVE_ECC) && defined(HAVE_ECC_DHE) && !defined(WC_NO_RNG) && \
|
||||
(defined(HAVE_ECC384) || defined(HAVE_ECC521) || \
|
||||
defined(HAVE_ALL_CURVES)) && \
|
||||
|
||||
@@ -31,11 +31,13 @@ int test_wc_ecc_get_curve_id_from_dp_params(void);
|
||||
int test_wc_ecc_make_key(void);
|
||||
int test_wc_ecc_init(void);
|
||||
int test_wc_ecc_check_key(void);
|
||||
int test_wc_ecc_check_key_invalid_pubkey(void);
|
||||
int test_wc_ecc_get_generator(void);
|
||||
int test_wc_ecc_size(void);
|
||||
int test_wc_ecc_params(void);
|
||||
int test_wc_ecc_signVerify_hash(void);
|
||||
int test_wc_ecc_shared_secret(void);
|
||||
int test_wc_ecc_shared_secret_at_infinity(void);
|
||||
int test_wc_ecc_shared_secret_size_bounds(void);
|
||||
int test_wc_ecc_export_x963(void);
|
||||
int test_wc_ecc_export_x963_ex(void);
|
||||
@@ -78,11 +80,13 @@ int test_wc_EccDecisionCoverage4(void);
|
||||
TEST_DECL_GROUP("ecc", test_wc_ecc_make_key), \
|
||||
TEST_DECL_GROUP("ecc", test_wc_ecc_init), \
|
||||
TEST_DECL_GROUP("ecc", test_wc_ecc_check_key), \
|
||||
TEST_DECL_GROUP("ecc", test_wc_ecc_check_key_invalid_pubkey), \
|
||||
TEST_DECL_GROUP("ecc", test_wc_ecc_get_generator), \
|
||||
TEST_DECL_GROUP("ecc", test_wc_ecc_size), \
|
||||
TEST_DECL_GROUP("ecc", test_wc_ecc_params), \
|
||||
TEST_DECL_GROUP("ecc", test_wc_ecc_signVerify_hash), \
|
||||
TEST_DECL_GROUP("ecc", test_wc_ecc_shared_secret), \
|
||||
TEST_DECL_GROUP("ecc", test_wc_ecc_shared_secret_at_infinity), \
|
||||
TEST_DECL_GROUP("ecc", test_wc_ecc_shared_secret_size_bounds), \
|
||||
TEST_DECL_GROUP("ecc", test_wc_ecc_export_x963), \
|
||||
TEST_DECL_GROUP("ecc", test_wc_ecc_export_x963_ex), \
|
||||
|
||||
@@ -177,6 +177,115 @@ int test_wc_ed448_sign_msg(void)
|
||||
return EXPECT_RESULT();
|
||||
} /* END test_wc_ed448_sign_msg */
|
||||
|
||||
/*
|
||||
* RFC 8032 requires the Ed448 signature scalar S to be canonical (S < L).
|
||||
* Because L times the base point is the identity, a malleated signature with
|
||||
* S' = S + L recomputes the same R, so the S-range check is the only guard
|
||||
* against it. Confirm a signature with S >= L (including the malleability case
|
||||
* S + L) is rejected with BAD_FUNC_ARG, while an in-range but wrong S fails
|
||||
* verification with SIG_VERIFY_E.
|
||||
*/
|
||||
int test_wc_ed448_verify_sig_S_range(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
/* The S-range rejection may be absent in the frozen ed448.c of older
|
||||
* FIPS-certified modules, so restrict to non-FIPS or FIPS v7 and later. */
|
||||
#if (!defined(HAVE_FIPS) || FIPS_VERSION3_GE(7,0,0)) && \
|
||||
defined(HAVE_ED448) && defined(HAVE_ED448_SIGN) && \
|
||||
defined(HAVE_ED448_VERIFY)
|
||||
ed448_key key;
|
||||
WC_RNG rng;
|
||||
byte msg[] = "Everybody gets Friday off.\n";
|
||||
byte sig[ED448_SIG_SIZE];
|
||||
byte badSig[ED448_SIG_SIZE];
|
||||
word32 msglen = sizeof(msg);
|
||||
word32 siglen = sizeof(sig);
|
||||
int verify_ok = 0;
|
||||
int i;
|
||||
int carry;
|
||||
int sum;
|
||||
/* Ed448 group order L, little-endian, 57 bytes. */
|
||||
static const byte order[] = {
|
||||
0xf3, 0x44, 0x58, 0xab, 0x92, 0xc2, 0x78, 0x23,
|
||||
0x55, 0x8f, 0xc5, 0x8d, 0x72, 0xc2, 0x6c, 0x21,
|
||||
0x90, 0x36, 0xd6, 0xae, 0x49, 0xdb, 0x4e, 0xc4,
|
||||
0xe9, 0x23, 0xca, 0x7c, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f,
|
||||
0x00
|
||||
};
|
||||
|
||||
XMEMSET(&key, 0, sizeof(key));
|
||||
XMEMSET(&rng, 0, sizeof(rng));
|
||||
XMEMSET(sig, 0, sizeof(sig));
|
||||
|
||||
ExpectIntEQ(wc_ed448_init(&key), 0);
|
||||
ExpectIntEQ(wc_InitRng(&rng), 0);
|
||||
ExpectIntEQ(wc_ed448_make_key(&rng, ED448_KEY_SIZE, &key), 0);
|
||||
|
||||
/* Produce a valid signature and confirm it verifies. */
|
||||
ExpectIntEQ(wc_ed448_sign_msg(msg, msglen, sig, &siglen, &key, NULL, 0), 0);
|
||||
ExpectIntEQ(siglen, ED448_SIG_SIZE);
|
||||
ExpectIntEQ(wc_ed448_verify_msg(sig, siglen, msg, msglen, &verify_ok, &key,
|
||||
NULL, 0), 0);
|
||||
ExpectIntEQ(verify_ok, 1);
|
||||
|
||||
/* Malleability: S' = S + L. The same R is recomputed, so only the S-range
|
||||
* check can reject it. */
|
||||
XMEMCPY(badSig, sig, ED448_SIG_SIZE);
|
||||
carry = 0;
|
||||
for (i = 0; i < (int)sizeof(order); i++) {
|
||||
sum = (int)badSig[ED448_SIG_SIZE / 2 + i] + (int)order[i] + carry;
|
||||
badSig[ED448_SIG_SIZE / 2 + i] = (byte)(sum & 0xff);
|
||||
carry = sum >> 8;
|
||||
}
|
||||
verify_ok = 1;
|
||||
ExpectIntEQ(wc_ed448_verify_msg(badSig, ED448_SIG_SIZE, msg, msglen,
|
||||
&verify_ok, &key, NULL, 0), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
ExpectIntEQ(verify_ok, 0);
|
||||
|
||||
/* S exactly equal to L. */
|
||||
XMEMCPY(badSig, sig, ED448_SIG_SIZE);
|
||||
XMEMCPY(badSig + ED448_SIG_SIZE / 2, order, sizeof(order));
|
||||
verify_ok = 1;
|
||||
ExpectIntEQ(wc_ed448_verify_msg(badSig, ED448_SIG_SIZE, msg, msglen,
|
||||
&verify_ok, &key, NULL, 0), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
ExpectIntEQ(verify_ok, 0);
|
||||
|
||||
/* S greater than L in a high byte. */
|
||||
XMEMCPY(badSig, sig, ED448_SIG_SIZE);
|
||||
XMEMCPY(badSig + ED448_SIG_SIZE / 2, order, sizeof(order));
|
||||
badSig[ED448_SIG_SIZE / 2 + 55] = 0x40;
|
||||
verify_ok = 1;
|
||||
ExpectIntEQ(wc_ed448_verify_msg(badSig, ED448_SIG_SIZE, msg, msglen,
|
||||
&verify_ok, &key, NULL, 0), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
ExpectIntEQ(verify_ok, 0);
|
||||
|
||||
/* S greater than L in a low byte. */
|
||||
XMEMCPY(badSig, sig, ED448_SIG_SIZE);
|
||||
XMEMCPY(badSig + ED448_SIG_SIZE / 2, order, sizeof(order));
|
||||
badSig[ED448_SIG_SIZE / 2 + 0] = 0xf4;
|
||||
verify_ok = 1;
|
||||
ExpectIntEQ(wc_ed448_verify_msg(badSig, ED448_SIG_SIZE, msg, msglen,
|
||||
&verify_ok, &key, NULL, 0), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
||||
ExpectIntEQ(verify_ok, 0);
|
||||
|
||||
/* S below L: passes the range check, fails verification instead. */
|
||||
XMEMCPY(badSig, sig, ED448_SIG_SIZE);
|
||||
XMEMCPY(badSig + ED448_SIG_SIZE / 2, order, sizeof(order));
|
||||
badSig[ED448_SIG_SIZE / 2 + 0] = 0xf2;
|
||||
verify_ok = 1;
|
||||
ExpectIntEQ(wc_ed448_verify_msg(badSig, ED448_SIG_SIZE, msg, msglen,
|
||||
&verify_ok, &key, NULL, 0), WC_NO_ERR_TRACE(SIG_VERIFY_E));
|
||||
ExpectIntEQ(verify_ok, 0);
|
||||
|
||||
DoExpectIntEQ(wc_FreeRng(&rng), 0);
|
||||
wc_ed448_free(&key);
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
} /* END test_wc_ed448_verify_sig_S_range */
|
||||
|
||||
/*
|
||||
* Test that wc_ed448_sign_msg() rejects a public-key-only key object.
|
||||
* A key with pubKeySet=1 but privKeySet=0 must not silently sign.
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
int test_wc_ed448_make_key(void);
|
||||
int test_wc_ed448_init(void);
|
||||
int test_wc_ed448_sign_msg(void);
|
||||
int test_wc_ed448_verify_sig_S_range(void);
|
||||
int test_wc_ed448_sign_msg_pubonly_fails(void);
|
||||
int test_wc_ed448_import_public(void);
|
||||
int test_wc_ed448_import_private_key(void);
|
||||
@@ -47,6 +48,7 @@ int test_wc_ed448_check_key_decisions(void);
|
||||
TEST_DECL_GROUP("ed448", test_wc_ed448_make_key), \
|
||||
TEST_DECL_GROUP("ed448", test_wc_ed448_init), \
|
||||
TEST_DECL_GROUP("ed448", test_wc_ed448_sign_msg), \
|
||||
TEST_DECL_GROUP("ed448", test_wc_ed448_verify_sig_S_range), \
|
||||
TEST_DECL_GROUP("ed448", test_wc_ed448_sign_msg_pubonly_fails), \
|
||||
TEST_DECL_GROUP("ed448", test_wc_ed448_import_public), \
|
||||
TEST_DECL_GROUP("ed448", test_wc_ed448_import_private_key), \
|
||||
|
||||
@@ -257,6 +257,7 @@ int wc_Chacha_SetKey(ChaCha* ctx, const byte* key, word32 keySz)
|
||||
#endif
|
||||
|
||||
ctx->left = 0; /* resets state */
|
||||
ctx->keySet = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -370,6 +371,9 @@ int wc_Chacha_Process(ChaCha* ctx, byte* output, const byte* input,
|
||||
if (ctx == NULL || input == NULL || output == NULL)
|
||||
return BAD_FUNC_ARG;
|
||||
|
||||
if (!ctx->keySet)
|
||||
return MISSING_KEY;
|
||||
|
||||
#ifdef USE_INTEL_CHACHA_SPEEDUP
|
||||
/* handle left overs */
|
||||
if (msglen > 0 && ctx->left > 0) {
|
||||
|
||||
+30
-3
@@ -2670,10 +2670,37 @@ static int _DhSetKey(DhKey* key, const byte* p, word32 pSz, const byte* g,
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if (rng != NULL)
|
||||
ret = mp_prime_is_prime_ex(keyP, 8, &isPrime, rng);
|
||||
else
|
||||
#ifndef WC_NO_RNG
|
||||
WC_RNG* checkRng = rng;
|
||||
WC_RNG* tmpRng = NULL;
|
||||
|
||||
/* A fixed-base Miller-Rabin test can be fooled by a crafted
|
||||
* composite, so use random witnesses when an RNG is available.
|
||||
* Create a temporary RNG when the caller did not supply one. */
|
||||
if (checkRng == NULL) {
|
||||
if (wc_rng_new_ex(&tmpRng, NULL, 0, key->heap,
|
||||
INVALID_DEVID) == 0) {
|
||||
checkRng = tmpRng;
|
||||
}
|
||||
}
|
||||
|
||||
if (checkRng != NULL) {
|
||||
ret = mp_prime_is_prime_ex(keyP, 8, &isPrime, checkRng);
|
||||
}
|
||||
else {
|
||||
/* Fall back to the deterministic test rather than failing the
|
||||
* parameter load. This is the weaker check: a composite
|
||||
* crafted against the fixed bases is accepted as prime. */
|
||||
WOLFSSL_MSG("DH: no RNG, primality test uses fixed bases");
|
||||
ret = mp_prime_is_prime(keyP, 8, &isPrime);
|
||||
}
|
||||
|
||||
/* Safe on NULL and zeroizes the RNG state before freeing. */
|
||||
wc_rng_free(tmpRng);
|
||||
#else
|
||||
(void)rng;
|
||||
ret = mp_prime_is_prime(keyP, 8, &isPrime);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (ret == 0 && isPrime == 0)
|
||||
|
||||
@@ -4869,6 +4869,14 @@ int wc_ecc_shared_secret_gen_sync(ecc_key* private_key, ecc_point* point,
|
||||
{
|
||||
int err = MP_OKAY;
|
||||
mp_int* k = ecc_get_k(private_key);
|
||||
#ifdef WOLFSSL_SE050
|
||||
/* A key-id handle holds no software private scalar - ECDH for such a key
|
||||
* runs on the SE050 - so the identity this function computes from the
|
||||
* all-zero scalar is not a shared secret and must not be rejected here. */
|
||||
int checkInf = !private_key->keyIdSet;
|
||||
#else
|
||||
int checkInf = 1;
|
||||
#endif
|
||||
#ifdef HAVE_ECC_CDH
|
||||
WC_DECLARE_VAR(k_lcl, mp_int, 1, 0);
|
||||
#endif
|
||||
@@ -4997,6 +5005,7 @@ int wc_ecc_shared_secret_gen_sync(ecc_key* private_key, ecc_point* point,
|
||||
#endif
|
||||
#if defined(WOLFSSL_SP_MATH)
|
||||
{
|
||||
(void)checkInf;
|
||||
err = WC_KEY_SIZE_E;
|
||||
goto errout;
|
||||
}
|
||||
@@ -5064,6 +5073,14 @@ int wc_ecc_shared_secret_gen_sync(ecc_key* private_key, ecc_point* point,
|
||||
/* Use constant time map if compiled in */
|
||||
err = ecc_map_ex(result, curve->prime, mp, 1);
|
||||
}
|
||||
if (err == MP_OKAY) {
|
||||
/* SP 800-56Ar3 5.7.1.2: the shared secret must not be the point at
|
||||
* infinity. ecc_map_ex reports that case as x, y of zero with a
|
||||
* success code, so check the point explicitly. */
|
||||
if (checkInf && wc_ecc_point_is_at_infinity(result)) {
|
||||
err = ECC_INF_E;
|
||||
}
|
||||
}
|
||||
if (err == MP_OKAY) {
|
||||
x = mp_unsigned_bin_size(curve->prime);
|
||||
if (*outlen < (word32)x || x < mp_unsigned_bin_size(result->x)) {
|
||||
@@ -5087,6 +5104,25 @@ int wc_ecc_shared_secret_gen_sync(ecc_key* private_key, ecc_point* point,
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WOLFSSL_HAVE_SP_ECC
|
||||
if ((err == MP_OKAY) && checkInf) {
|
||||
/* The single precision implementations above serialize the point at
|
||||
* infinity as an all-zero x-coordinate and report success, so the
|
||||
* identity has to be recognized from the output. SP 800-56Ar3 5.7.1.2
|
||||
* requires an error and stop in that case. Accumulate over the whole
|
||||
* buffer so the scan does not branch on the secret. */
|
||||
word32 i;
|
||||
byte acc = 0;
|
||||
|
||||
for (i = 0; i < *outlen; i++) {
|
||||
acc |= out[i];
|
||||
}
|
||||
if (acc == 0) {
|
||||
err = ECC_INF_E;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
errout:
|
||||
|
||||
#ifdef HAVE_ECC_CDH
|
||||
|
||||
@@ -567,12 +567,10 @@ static int esp_mp_hw_unlock(void)
|
||||
* This releases the RSA Accelerator from reset.*/
|
||||
portENTER_CRITICAL_SAFE(&wc_rsa_reg_lock);
|
||||
{
|
||||
DPORT_REG_CLR_BIT(
|
||||
(volatile void *)(DR_REG_RSA_BASE + SYSTEM_CRYPTO_RSA_CLK_EN),
|
||||
SYSTEM_PERIP_CLK_EN1_REG);
|
||||
DPORT_REG_SET_BIT(
|
||||
(volatile void *)(DR_REG_RSA_BASE + SYSTEM_RSA_MEM_PD),
|
||||
SYSTEM_RSA_PD_CTRL_REG);
|
||||
DPORT_REG_CLR_BIT((volatile void *)(SYSTEM_PERIP_CLK_EN1_REG),
|
||||
SYSTEM_CRYPTO_RSA_CLK_EN);
|
||||
DPORT_REG_SET_BIT((volatile void *)(SYSTEM_RSA_PD_CTRL_REG),
|
||||
SYSTEM_RSA_MEM_PD);
|
||||
}
|
||||
portEXIT_CRITICAL_SAFE(&wc_rsa_reg_lock);
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32C6)
|
||||
|
||||
@@ -2093,7 +2093,11 @@ int psoc6_ecc_verify_hash_ex(MATH_INT_T* r, MATH_INT_T* s, const byte* hash,
|
||||
uint8_t k[MAX_ECC_KEYSIZE] = { 0 };
|
||||
|
||||
if (!key || !verif_res || !r || !s || !hash)
|
||||
return -BAD_FUNC_ARG;
|
||||
return BAD_FUNC_ARG;
|
||||
|
||||
/* Fail closed on both channels: every early return below leaves the
|
||||
* caller's result flag in the failed state. */
|
||||
*verif_res = 0;
|
||||
|
||||
/* Enable CRYPTO block if not enabled */
|
||||
if (!Cy_Crypto_Core_IsEnabled(crypto_base)) {
|
||||
@@ -2105,7 +2109,12 @@ int psoc6_ecc_verify_hash_ex(MATH_INT_T* r, MATH_INT_T* s, const byte* hash,
|
||||
sSz = mp_unsigned_bin_size(s);
|
||||
|
||||
if (keySz > MAX_ECC_KEYSIZE)
|
||||
return -BAD_FUNC_ARG;
|
||||
return BAD_FUNC_ARG;
|
||||
|
||||
/* Reject r or s values that would overflow their keySz slot in
|
||||
* signature_buf when serialized. */
|
||||
if (rSz > keySz || sSz > keySz)
|
||||
return BAD_FUNC_ARG;
|
||||
|
||||
/* Prepare ECC key */
|
||||
ecc_key.type = PK_PUBLIC;
|
||||
|
||||
@@ -1085,10 +1085,13 @@ static int IntelQaSymCipher(IntelQaDev* dev, byte* out, const byte* in,
|
||||
status = cpaCySymPerformOp(dev->handle, dev, opData,
|
||||
bufferList, bufferList, &verifyResult);
|
||||
|
||||
if (symOperation == CPA_CY_SYM_OP_ALGORITHM_CHAINING &&
|
||||
cipherAlgorithm == CPA_CY_SYM_CIPHER_AES_GCM &&
|
||||
cipherDirection == CPA_CY_SYM_CIPHER_DIRECTION_DECRYPT &&
|
||||
hashAlgorithm == CPA_CY_SYM_HASH_AES_GCM) {
|
||||
if (status != CPA_STATUS_SUCCESS) {
|
||||
ret = ASYNC_OP_E;
|
||||
}
|
||||
else if (symOperation == CPA_CY_SYM_OP_ALGORITHM_CHAINING &&
|
||||
cipherAlgorithm == CPA_CY_SYM_CIPHER_AES_GCM &&
|
||||
cipherDirection == CPA_CY_SYM_CIPHER_DIRECTION_DECRYPT &&
|
||||
hashAlgorithm == CPA_CY_SYM_HASH_AES_GCM) {
|
||||
if (verifyResult == CPA_FALSE) {
|
||||
ret = AES_GCM_AUTH_E;
|
||||
}
|
||||
|
||||
+4
-4
@@ -387,8 +387,8 @@ int wc_InitRsaKey_Id(RsaKey* key, unsigned char* id, int len, void* heap,
|
||||
{
|
||||
int ret = 0;
|
||||
#if defined(WOLFSSL_SE050) && !defined(WOLFSSL_SE050_NO_RSA)
|
||||
/* SE050 TLS users store a word32 at id, need to cast back */
|
||||
word32* keyPtr = NULL;
|
||||
/* SE050 TLS users store a word32 at id, need to read it back */
|
||||
word32 keyId = 0;
|
||||
#endif
|
||||
|
||||
if (key == NULL)
|
||||
@@ -403,8 +403,8 @@ int wc_InitRsaKey_Id(RsaKey* key, unsigned char* id, int len, void* heap,
|
||||
#if defined(WOLFSSL_SE050) && !defined(WOLFSSL_SE050_NO_RSA)
|
||||
/* Set SE050 ID from word32, populate RsaKey with public from SE050 */
|
||||
if (len == (int)sizeof(word32)) {
|
||||
keyPtr = (word32*)key->id;
|
||||
ret = wc_RsaUseKeyId(key, *keyPtr, 0);
|
||||
keyId = readUnalignedWord32(key->id);
|
||||
ret = wc_RsaUseKeyId(key, keyId, 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -4436,12 +4436,14 @@ static void mlkem_cbd_eta3(sword16* p, const byte* r)
|
||||
#else
|
||||
/* Calculate eight integer coefficients at a time. */
|
||||
for (i = 0; i < MLKEM_N; i += 16) {
|
||||
const word32* r32 = (const word32*)r;
|
||||
word32 r0 = readUnalignedWord32(r);
|
||||
word32 r1 = readUnalignedWord32(r + 4);
|
||||
word32 r2 = readUnalignedWord32(r + 8);
|
||||
/* Take the next 12 bytes, little endian, as 24 bit values. */
|
||||
word32 t0 = r32[0] & 0xffffff;
|
||||
word32 t1 = ((r32[0] >> 24) | (r32[1] << 8)) & 0xffffff;
|
||||
word32 t2 = ((r32[1] >> 16) | (r32[2] << 16)) & 0xffffff;
|
||||
word32 t3 = r32[2] >> 8 ;
|
||||
word32 t0 = r0 & 0xffffff;
|
||||
word32 t1 = ((r0 >> 24) | (r1 << 8)) & 0xffffff;
|
||||
word32 t2 = ((r1 >> 16) | (r2 << 16)) & 0xffffff;
|
||||
word32 t3 = r2 >> 8 ;
|
||||
word32 d0;
|
||||
word32 d1;
|
||||
word32 d2;
|
||||
@@ -5862,18 +5864,22 @@ static void mlkem_vec_compress_10_c(byte* r, sword16* v, unsigned int k)
|
||||
sword16 t14 = TO_COMP_WORD_10(v, i, j, 14);
|
||||
sword16 t15 = TO_COMP_WORD_10(v, i, j, 15);
|
||||
|
||||
word32* r32 = (word32*)r;
|
||||
/* Pack sixteen 10-bit values into byte array. */
|
||||
r32[0] = (word32)t0 | ((word32)t1 << 10) |
|
||||
((word32)t2 << 20) | ((word32)t3 << 30);
|
||||
r32[1] = ((word32)t3 >> 2) | ((word32)t4 << 8) |
|
||||
((word32)t5 << 18) | ((word32)t6 << 28);
|
||||
r32[2] = ((word32)t6 >> 4) | ((word32)t7 << 6) |
|
||||
((word32)t8 << 16) | ((word32)t9 << 26);
|
||||
r32[3] = ((word32)t9 >> 6) | ((word32)t10 << 4) |
|
||||
((word32)t11 << 14) | ((word32)t12 << 24);
|
||||
r32[4] = ((word32)t12 >> 8) | ((word32)t13 << 2) |
|
||||
((word32)t14 << 12) | ((word32)t15 << 22);
|
||||
writeUnalignedWord32(r + 0,
|
||||
(word32)t0 | ((word32)t1 << 10) |
|
||||
((word32)t2 << 20) | ((word32)t3 << 30));
|
||||
writeUnalignedWord32(r + 4,
|
||||
((word32)t3 >> 2) | ((word32)t4 << 8) |
|
||||
((word32)t5 << 18) | ((word32)t6 << 28));
|
||||
writeUnalignedWord32(r + 8,
|
||||
((word32)t6 >> 4) | ((word32)t7 << 6) |
|
||||
((word32)t8 << 16) | ((word32)t9 << 26));
|
||||
writeUnalignedWord32(r + 12,
|
||||
((word32)t9 >> 6) | ((word32)t10 << 4) |
|
||||
((word32)t11 << 14) | ((word32)t12 << 24));
|
||||
writeUnalignedWord32(r + 16,
|
||||
((word32)t12 >> 8) | ((word32)t13 << 2) |
|
||||
((word32)t14 << 12) | ((word32)t15 << 22));
|
||||
|
||||
/* Move over set bytes. */
|
||||
r += 20;
|
||||
|
||||
@@ -95,6 +95,7 @@ typedef struct ChaCha {
|
||||
#elif defined(USE_RISCV_CHACHA_SPEEDUP)
|
||||
ALIGN8 word32 over[CHACHA_CHUNK_WORDS];
|
||||
#endif
|
||||
WC_BITFIELD keySet:1; /* set to 1 once a key is set */
|
||||
} ChaCha;
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user