diff --git a/src/pk.c b/src/pk.c index 2c06eca20e..1f3d03ea8f 100644 --- a/src/pk.c +++ b/src/pk.c @@ -8645,20 +8645,8 @@ int wolfSSL_DH_generate_key(WOLFSSL_DH* dh) } -/* Compute the shared key from the private key and peer's public key. - * - * Return code compliant with OpenSSL. - * OpenSSL returns 0 when number of bits in p are smaller than minimum - * supported. - * - * @param [out] key Buffer to place shared key. - * @param [in] otherPub Peer's public key. - * @param [in] dh DH key containing private key. - * @return -1 on error. - * @return Size of shared secret in bytes on success. - */ -int wolfSSL_DH_compute_key(unsigned char* key, const WOLFSSL_BIGNUM* otherPub, - WOLFSSL_DH* dh) +static int _DH_compute_key_1(unsigned char* key, const WOLFSSL_BIGNUM* otherPub, + WOLFSSL_DH* dh, int ct) { int ret = 0; word32 keySz = 0; @@ -8680,6 +8668,12 @@ int wolfSSL_DH_compute_key(unsigned char* key, const WOLFSSL_BIGNUM* otherPub, WOLFSSL_ERROR_MSG("Bad function arguments"); ret = -1; } +#ifdef HAVE_FIPS + if (ct) { + ret = -1; + } +#endif + /* Get the maximum size of computed DH key. */ if ((ret == 0) && ((keySz = (word32)DH_size(dh)) == 0)) { WOLFSSL_ERROR_MSG("Bad DH_size"); @@ -8746,10 +8740,24 @@ int wolfSSL_DH_compute_key(unsigned char* key, const WOLFSSL_BIGNUM* otherPub, PRIVATE_KEY_UNLOCK(); /* Calculate shared secret from private and public keys. */ - if ((ret == 0) && (wc_DhAgree((DhKey*)dh->internal, key, &keySz, priv, - (word32)privSz, pub, (word32)pubSz) < 0)) { - WOLFSSL_ERROR_MSG("wc_DhAgree failed"); - ret = -1; + if (ret == 0) { +#ifndef HAVE_FIPS + if (ct) { + if (wc_DhAgree_ct((DhKey*)dh->internal, key, &keySz, priv, + (word32)privSz, pub, (word32)pubSz) < 0) { + WOLFSSL_ERROR_MSG("wc_DhAgree_ct failed"); + ret = -1; + } + } + else +#endif /* !HAVE_FIPS */ + { + if (wc_DhAgree((DhKey*)dh->internal, key, &keySz, priv, + (word32)privSz, pub, (word32)pubSz) < 0) { + WOLFSSL_ERROR_MSG("wc_DhAgree failed"); + ret = -1; + } + } } if (ret == 0) { /* Return actual length. */ @@ -8773,6 +8781,45 @@ int wolfSSL_DH_compute_key(unsigned char* key, const WOLFSSL_BIGNUM* otherPub, return ret; } + +/* Compute the shared key from the private key and peer's public key. + * + * Return code compliant with OpenSSL. + * OpenSSL returns 0 when number of bits in p are smaller than minimum + * supported. + * + * @param [out] key Buffer to place shared key. + * @param [in] otherPub Peer's public key. + * @param [in] dh DH key containing private key. + * @return -1 on error. + * @return Size of shared secret in bytes on success. + */ +int wolfSSL_DH_compute_key(unsigned char* key, const WOLFSSL_BIGNUM* otherPub, + WOLFSSL_DH* dh) +{ + return _DH_compute_key_1(key, otherPub, dh, 0); +} + +/* Compute the shared key from the private key and peer's public key as in + * wolfSSL_DH_compute_key, but using constant time processing, with an output + * key length fixed at the nominal DH key size. Leading zeros are retained. + * + * Return code compliant with OpenSSL. + * OpenSSL returns 0 when number of bits in p are smaller than minimum + * supported. + * + * @param [out] key Buffer to place shared key. + * @param [in] otherPub Peer's public key. + * @param [in] dh DH key containing private key. + * @return -1 on error. + * @return Size of shared secret in bytes on success. + */ +int wolfSSL_DH_compute_key_padded(unsigned char* key, + const WOLFSSL_BIGNUM* otherPub, WOLFSSL_DH* dh) +{ + return _DH_compute_key_1(key, otherPub, dh, 1); +} + #endif /* !HAVE_FIPS || (HAVE_FIPS && !WOLFSSL_DH_EXTRA) || * HAVE_FIPS_VERSION > 2 */ diff --git a/wolfcrypt/src/dh.c b/wolfcrypt/src/dh.c index 1a76ca6dec..f33902b7ba 100644 --- a/wolfcrypt/src/dh.c +++ b/wolfcrypt/src/dh.c @@ -1981,7 +1981,7 @@ int wc_DhGenerateKeyPair(DhKey* key, WC_RNG* rng, #ifndef WOLFSSL_KCAPI_DH static int wc_DhAgree_Sync(DhKey* key, byte* agree, word32* agreeSz, - const byte* priv, word32 privSz, const byte* otherPub, word32 pubSz) + const byte* priv, word32 privSz, const byte* otherPub, word32 pubSz, int ct) { int ret = 0; #if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) @@ -2040,8 +2040,18 @@ static int wc_DhAgree_Sync(DhKey* key, byte* agree, word32* agreeSz, SAVE_VECTOR_REGISTERS(ret = _svr_ret;); - if (ret == 0 && mp_read_unsigned_bin(y, otherPub, pubSz) != MP_OKAY) - ret = MP_READ_E; + if (ret == 0) { + if (ct) + ret = mp_read_unsigned_bin_ct(y, otherPub, pubSz); + else + ret = mp_read_unsigned_bin(y, otherPub, pubSz); + if (ret != MP_OKAY) + ret = MP_READ_E; + #if MP_OKAY != 0 + else + ret = 0; + #endif + } if (ret == 0) ret = sp_DhExp_2048(y, priv, privSz, &key->p, agree, agreeSz); @@ -2074,8 +2084,18 @@ static int wc_DhAgree_Sync(DhKey* key, byte* agree, word32* agreeSz, SAVE_VECTOR_REGISTERS(ret = _svr_ret;); - if (ret == 0 && mp_read_unsigned_bin(y, otherPub, pubSz) != MP_OKAY) - ret = MP_READ_E; + if (ret == 0) { + if (ct) + ret = mp_read_unsigned_bin_ct(y, otherPub, pubSz); + else + ret = mp_read_unsigned_bin(y, otherPub, pubSz); + if (ret != MP_OKAY) + ret = MP_READ_E; + #if MP_OKAY != 0 + else + ret = 0; + #endif + } if (ret == 0) ret = sp_DhExp_3072(y, priv, privSz, &key->p, agree, agreeSz); @@ -2108,8 +2128,18 @@ static int wc_DhAgree_Sync(DhKey* key, byte* agree, word32* agreeSz, SAVE_VECTOR_REGISTERS(ret = _svr_ret;); - if (ret == 0 && mp_read_unsigned_bin(y, otherPub, pubSz) != MP_OKAY) - ret = MP_READ_E; + if (ret == 0) { + if (ct) + ret = mp_read_unsigned_bin_ct(y, otherPub, pubSz); + else + ret = mp_read_unsigned_bin(y, otherPub, pubSz); + if (ret != MP_OKAY) + ret = MP_READ_E; + #if MP_OKAY != 0 + else + ret = 0; + #endif + } if (ret == 0) ret = sp_DhExp_4096(y, priv, privSz, &key->p, agree, agreeSz); @@ -2149,15 +2179,34 @@ static int wc_DhAgree_Sync(DhKey* key, byte* agree, word32* agreeSz, SAVE_VECTOR_REGISTERS(ret = _svr_ret;); - if (mp_read_unsigned_bin(x, priv, privSz) != MP_OKAY) + if (ct) + ret = mp_read_unsigned_bin_ct(x, priv, privSz); + else + ret = mp_read_unsigned_bin(x, priv, privSz); + if (ret != MP_OKAY) ret = MP_READ_E; + #if MP_OKAY != 0 + else + ret = 0; + #endif + #ifdef WOLFSSL_CHECK_MEM_ZERO if (ret == 0) mp_memzero_add("wc_DhAgree_Sync x", x); #endif - if (ret == 0 && mp_read_unsigned_bin(y, otherPub, pubSz) != MP_OKAY) - ret = MP_READ_E; + if (ret == 0) { + if (ct) + ret = mp_read_unsigned_bin_ct(y, otherPub, pubSz); + else + ret = mp_read_unsigned_bin(y, otherPub, pubSz); + if (ret != MP_OKAY) + ret = MP_READ_E; + #if MP_OKAY != 0 + else + ret = 0; + #endif + } if (ret == 0 && mp_exptmod(y, x, &key->p, z) != MP_OKAY) ret = MP_EXPTMOD_E; @@ -2170,11 +2219,17 @@ static int wc_DhAgree_Sync(DhKey* key, byte* agree, word32* agreeSz, if (ret == 0 && (mp_cmp_d(z, 1) == MP_EQ)) ret = MP_VAL; - if (ret == 0 && mp_to_unsigned_bin(z, agree) != MP_OKAY) - ret = MP_TO_E; - - if (ret == 0) - *agreeSz = (word32)mp_unsigned_bin_size(z); + if (ret == 0) { + if (ct) { + if (mp_to_unsigned_bin_len_ct(z, agree, (int)*agreeSz) != MP_OKAY) + ret = MP_TO_E; + } else { + if (mp_to_unsigned_bin(z, agree) != MP_OKAY) + ret = MP_TO_E; + if (ret == 0) + *agreeSz = (word32)mp_unsigned_bin_size(z); + } + } mp_forcezero(z); mp_clear(y); @@ -2238,7 +2293,8 @@ static int wc_DhAgree_Async(DhKey* key, byte* agree, word32* agreeSz, #endif /* otherwise use software DH */ - ret = wc_DhAgree_Sync(key, agree, agreeSz, priv, privSz, otherPub, pubSz); + ret = wc_DhAgree_Sync(key, agree, agreeSz, priv, privSz, otherPub, pubSz, + 0); return ret; } @@ -2267,13 +2323,26 @@ int wc_DhAgree(DhKey* key, byte* agree, word32* agreeSz, const byte* priv, else #endif { - ret = wc_DhAgree_Sync(key, agree, agreeSz, priv, privSz, otherPub, pubSz); + ret = wc_DhAgree_Sync(key, agree, agreeSz, priv, privSz, otherPub, + pubSz, 0); } #endif /* WOLFSSL_KCAPI_DH */ return ret; } +int wc_DhAgree_ct(DhKey* key, byte* agree, word32 *agreeSz, const byte* priv, + word32 privSz, const byte* otherPub, word32 pubSz) +{ + if (key == NULL || agree == NULL || agreeSz == NULL || priv == NULL || + otherPub == NULL) { + return BAD_FUNC_ARG; + } + + return wc_DhAgree_Sync(key, agree, agreeSz, priv, privSz, otherPub, pubSz, + 1); +} + #ifdef WOLFSSL_DH_EXTRA WOLFSSL_LOCAL int wc_DhKeyCopy(DhKey* src, DhKey* dst) { diff --git a/wolfcrypt/src/integer.c b/wolfcrypt/src/integer.c index 3deeaeb82e..b3576862c4 100644 --- a/wolfcrypt/src/integer.c +++ b/wolfcrypt/src/integer.c @@ -734,7 +734,8 @@ int mp_mod_2d (mp_int * a, int b, mp_int * c) /* reads a unsigned char array, assumes the msb is stored first [big endian] */ -int mp_read_unsigned_bin (mp_int * a, const unsigned char *b, int c) +static int _read_unsigned_bin_1(mp_int * a, const unsigned char *b, int c, + int ct) { int res; int digits_needed; @@ -773,10 +774,20 @@ int mp_read_unsigned_bin (mp_int * a, const unsigned char *b, int c) a->used = 2; #endif } - mp_clamp (a); + if (! ct) + mp_clamp (a); return MP_OKAY; } +int mp_read_unsigned_bin(mp_int * a, const unsigned char *b, int c) +{ + return _read_unsigned_bin_1(a, b, c, 0); +} + +int mp_read_unsigned_bin_ct(mp_int * a, const unsigned char *b, int c) +{ + return _read_unsigned_bin_1(a, b, c, 1); +} /* shift left by a certain bit count */ int mp_mul_2d (mp_int * a, int b, mp_int * c) diff --git a/wolfcrypt/src/sp_int.c b/wolfcrypt/src/sp_int.c index b1d18d5549..6be5819379 100644 --- a/wolfcrypt/src/sp_int.c +++ b/wolfcrypt/src/sp_int.c @@ -17794,17 +17794,7 @@ int sp_unsigned_bin_size(const sp_int* a) return cnt; } -/* Convert a number as an array of bytes in big-endian format to a - * multi-precision number. - * - * @param [out] a SP integer. - * @param [in] in Array of bytes. - * @param [in] inSz Number of data bytes in array. - * - * @return MP_OKAY on success. - * @return MP_VAL when the number is too big to fit in an SP. - */ -int sp_read_unsigned_bin(sp_int* a, const byte* in, word32 inSz) +static int _read_unsigned_bin_1(sp_int* a, const byte* in, word32 inSz, int ct) { int err = MP_OKAY; @@ -17888,12 +17878,43 @@ int sp_read_unsigned_bin(sp_int* a, const byte* in, word32 inSz) #endif /* LITTLE_ENDIAN_ORDER */ } #endif - sp_clamp_ct(a); + if (! ct) + sp_clamp_ct(a); } return err; } +/* Convert a number as an array of bytes in big-endian format to a + * multi-precision number. + * + * @param [out] a SP integer. + * @param [in] in Array of bytes. + * @param [in] inSz Number of data bytes in array. + * + * @return MP_OKAY on success. + * @return MP_VAL when the number is too big to fit in an SP. + */ +int sp_read_unsigned_bin(sp_int* a, const byte* in, word32 inSz) +{ + return _read_unsigned_bin_1(a, in, inSz, 0); +} + +/* Convert a number as an array of bytes in big-endian format to a + * multi-precision number, retaining leading zeros (if any). + * + * @param [out] a SP integer. + * @param [in] in Array of bytes. + * @param [in] inSz Number of data bytes in array. + * + * @return MP_OKAY on success. + * @return MP_VAL when the number is too big to fit in an SP. + */ +int sp_read_unsigned_bin_ct(sp_int* a, const byte* in, word32 inSz) +{ + return _read_unsigned_bin_1(a, in, inSz, 1); +} + /* Convert the multi-precision number to an array of bytes in big-endian format. * * The array must be large enough for encoded number - use mp_unsigned_bin_size diff --git a/wolfcrypt/src/tfm.c b/wolfcrypt/src/tfm.c index ccf15a5f43..3966ff8f1e 100644 --- a/wolfcrypt/src/tfm.c +++ b/wolfcrypt/src/tfm.c @@ -4040,7 +4040,8 @@ int fp_montgomery_reduce(fp_int *a, fp_int *m, fp_digit mp) return fp_montgomery_reduce_ex(a, m, mp, 1); } -int fp_read_unsigned_bin(fp_int *a, const unsigned char *b, int c) +static int _read_unsigned_bin_1(fp_int *a, const unsigned char *b, int c, + int ct) { #if defined(ALT_ECC_SIZE) || defined(HAVE_WOLF_BIGINT) const word32 maxC = (a->size * sizeof(fp_digit)); @@ -4146,11 +4147,22 @@ int fp_read_unsigned_bin(fp_int *a, const unsigned char *b, int c) } } #endif - fp_clamp (a); + if (! ct) + fp_clamp (a); return FP_OKAY; } +int fp_read_unsigned_bin(fp_int *a, const unsigned char *b, int c) +{ + return _read_unsigned_bin_1(a, b, c, 0); +} + +int fp_read_unsigned_bin_ct(fp_int *a, const unsigned char *b, int c) +{ + return _read_unsigned_bin_1(a, b, c, 1); +} + int fp_to_unsigned_bin_at_pos(int x, fp_int *t, unsigned char *b) { #if DIGIT_BIT == 64 || DIGIT_BIT == 32 @@ -4836,6 +4848,13 @@ int mp_read_unsigned_bin (mp_int * a, const unsigned char *b, int c) return fp_read_unsigned_bin(a, b, c); } +/* reads a unsigned char array, assumes the msb is stored first [big endian], + * and omits clamping (retains leading zeros). + */ +int mp_read_unsigned_bin_ct (mp_int * a, const unsigned char *b, int c) +{ + return fp_read_unsigned_bin_ct(a, b, c); +} int mp_sub_d(fp_int *a, fp_digit b, fp_int *c) { diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index c9e10ae5d2..39516e8070 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -22766,6 +22766,37 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t dh_test(void) if (agreeSz != agreeSz2 || XMEMCMP(agree, agree2, agreeSz)) { ERROR_OUT(WC_TEST_RET_ENC_NC, done); } + +#ifndef HAVE_FIPS + agreeSz = DH_TEST_BUF_SIZE; + agreeSz2 = DH_TEST_BUF_SIZE; + + ret = wc_DhAgree_ct(key, agree, &agreeSz, priv, privSz, pub2, pubSz2); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), done); + + ret = wc_DhAgree_ct(key2, agree2, &agreeSz2, priv2, privSz2, pub, pubSz); + if (ret != 0) + ERROR_OUT(WC_TEST_RET_ENC_EC(ret), done); + +#ifdef WOLFSSL_PUBLIC_MP + if (agreeSz != (word32)mp_unsigned_bin_size(&key->p)) + { + ERROR_OUT(WC_TEST_RET_ENC_NC, done); + } +#endif + + if (agreeSz != agreeSz2) + { + ERROR_OUT(WC_TEST_RET_ENC_NC, done); + } + + if (XMEMCMP(agree, agree2, agreeSz) != 0) + { + ERROR_OUT(WC_TEST_RET_ENC_NC, done); + } +#endif /* !HAVE_FIPS */ + #endif /* !WC_NO_RNG */ #if defined(WOLFSSL_KEY_GEN) && !defined(HAVE_FIPS) && !defined(HAVE_SELFTEST) diff --git a/wolfssl/openssl/dh.h b/wolfssl/openssl/dh.h index 49f209ce8b..ae0f02683a 100644 --- a/wolfssl/openssl/dh.h +++ b/wolfssl/openssl/dh.h @@ -67,6 +67,9 @@ WOLFSSL_API int wolfSSL_DH_size(WOLFSSL_DH* dh); WOLFSSL_API int wolfSSL_DH_generate_key(WOLFSSL_DH* dh); WOLFSSL_API int wolfSSL_DH_compute_key(unsigned char* key, const WOLFSSL_BIGNUM* pub, WOLFSSL_DH* dh); +WOLFSSL_API int wolfSSL_DH_compute_key_padded(unsigned char* key, + const WOLFSSL_BIGNUM* otherPub, WOLFSSL_DH* dh); + WOLFSSL_API int wolfSSL_DH_LoadDer(WOLFSSL_DH* dh, const unsigned char* derBuf, int derSz); WOLFSSL_API int wolfSSL_DH_set_length(WOLFSSL_DH* dh, long len); @@ -91,6 +94,7 @@ typedef WOLFSSL_DH DH; #define DH_size wolfSSL_DH_size #define DH_generate_key wolfSSL_DH_generate_key #define DH_compute_key wolfSSL_DH_compute_key +#define DH_compute_key_padded wolfSSL_DH_compute_key_padded #define DH_set_length wolfSSL_DH_set_length #define DH_set0_pqg wolfSSL_DH_set0_pqg #define DH_get0_pqg wolfSSL_DH_get0_pqg diff --git a/wolfssl/wolfcrypt/dh.h b/wolfssl/wolfcrypt/dh.h index 948c44a63a..865baa3ebe 100644 --- a/wolfssl/wolfcrypt/dh.h +++ b/wolfssl/wolfcrypt/dh.h @@ -151,6 +151,9 @@ WOLFSSL_API int wc_DhGenerateKeyPair(DhKey* key, WC_RNG* rng, byte* priv, WOLFSSL_API int wc_DhAgree(DhKey* key, byte* agree, word32* agreeSz, const byte* priv, word32 privSz, const byte* otherPub, word32 pubSz); +WOLFSSL_API int wc_DhAgree_ct(DhKey* key, byte* agree, word32* agreeSz, + const byte* priv, word32 privSz, const byte* otherPub, + word32 pubSz); WOLFSSL_API int wc_DhKeyDecode(const byte* input, word32* inOutIdx, DhKey* key, word32 inSz); /* wc_DhKeyDecode is in asn.c */ diff --git a/wolfssl/wolfcrypt/integer.h b/wolfssl/wolfcrypt/integer.h index 6efb4d8e23..c7b2b234ec 100644 --- a/wolfssl/wolfcrypt/integer.h +++ b/wolfssl/wolfcrypt/integer.h @@ -311,6 +311,7 @@ MP_API void mp_free (mp_int * a); MP_API void mp_forcezero(mp_int * a); MP_API int mp_unsigned_bin_size(const mp_int * a); MP_API int mp_read_unsigned_bin (mp_int * a, const unsigned char *b, int c); +MP_API int mp_read_unsigned_bin_ct (mp_int * a, const unsigned char *b, int c); MP_API int mp_to_unsigned_bin_at_pos(int x, mp_int *t, unsigned char *b); MP_API int mp_to_unsigned_bin (mp_int * a, unsigned char *b); #define mp_to_unsigned_bin_len_ct mp_to_unsigned_bin_len diff --git a/wolfssl/wolfcrypt/sp_int.h b/wolfssl/wolfcrypt/sp_int.h index 26978acfe0..5ade8f3d4c 100644 --- a/wolfssl/wolfcrypt/sp_int.h +++ b/wolfssl/wolfcrypt/sp_int.h @@ -1052,6 +1052,7 @@ MP_API int sp_mont_norm(sp_int* norm, const sp_int* m); MP_API int sp_unsigned_bin_size(const sp_int* a); MP_API int sp_read_unsigned_bin(sp_int* a, const byte* in, word32 inSz); +MP_API int sp_read_unsigned_bin_ct(sp_int* a, const byte* in, word32 inSz); MP_API int sp_to_unsigned_bin(const sp_int* a, byte* out); MP_API int sp_to_unsigned_bin_len(const sp_int* a, byte* out, int outSz); MP_API int sp_to_unsigned_bin_len_ct(const sp_int* a, byte* out, int outSz); @@ -1169,6 +1170,7 @@ WOLFSSL_LOCAL void sp_memzero_check(sp_int* sp); #define mp_unsigned_bin_size sp_unsigned_bin_size #define mp_read_unsigned_bin sp_read_unsigned_bin +#define mp_read_unsigned_bin_ct sp_read_unsigned_bin_ct #define mp_to_unsigned_bin sp_to_unsigned_bin #define mp_to_unsigned_bin_len sp_to_unsigned_bin_len #define mp_to_unsigned_bin_len_ct sp_to_unsigned_bin_len_ct diff --git a/wolfssl/wolfcrypt/tfm.h b/wolfssl/wolfcrypt/tfm.h index 0483f26818..dff040a7ac 100644 --- a/wolfssl/wolfcrypt/tfm.h +++ b/wolfssl/wolfcrypt/tfm.h @@ -720,6 +720,7 @@ int fp_leading_bit(fp_int *a); int fp_unsigned_bin_size(const fp_int *a); int fp_read_unsigned_bin(fp_int *a, const unsigned char *b, int c); +int fp_read_unsigned_bin_ct(fp_int *a, const unsigned char *b, int c); int fp_to_unsigned_bin(fp_int *a, unsigned char *b); int fp_to_unsigned_bin_len(fp_int *a, unsigned char *b, int c); int fp_to_unsigned_bin_at_pos(int x, fp_int *t, unsigned char *b); @@ -843,6 +844,7 @@ MP_API int mp_cmp_d(mp_int *a, mp_digit b); MP_API int mp_unsigned_bin_size(const mp_int * a); MP_API int mp_read_unsigned_bin (mp_int * a, const unsigned char *b, int c); +MP_API int mp_read_unsigned_bin_ct (mp_int * a, const unsigned char *b, int c); MP_API int mp_to_unsigned_bin_at_pos(int x, mp_int *t, unsigned char *b); MP_API int mp_to_unsigned_bin (mp_int * a, unsigned char *b); #define mp_to_unsigned_bin_len_ct mp_to_unsigned_bin_len