From 70b3dbe447ee38e0f48615c36b57c41a12c1612d Mon Sep 17 00:00:00 2001 From: Daniel Pouzzner Date: Sun, 28 Jul 2024 22:20:57 -0500 Subject: [PATCH] add constant time DH key agreement APIs and related logic: * wc_DhAgree_ct() * wolfSSL_DH_compute_key_padded() * sp_read_unsigned_bin_ct() * wolfcrypt/src/integer.c:mp_read_unsigned_bin_ct() * fp_read_unsigned_bin_ct() --- src/pk.c | 83 +++++++++++++++++++++------- wolfcrypt/src/dh.c | 104 ++++++++++++++++++++++++++++++------ wolfcrypt/src/integer.c | 5 ++ wolfcrypt/src/sp_int.c | 45 +++++++++++----- wolfcrypt/src/tfm.c | 23 +++++++- wolfcrypt/test/test.c | 31 +++++++++++ wolfssl/openssl/dh.h | 4 ++ wolfssl/wolfcrypt/dh.h | 3 ++ wolfssl/wolfcrypt/integer.h | 1 + wolfssl/wolfcrypt/sp_int.h | 2 + wolfssl/wolfcrypt/tfm.h | 2 + 11 files changed, 254 insertions(+), 49 deletions(-) diff --git a/src/pk.c b/src/pk.c index 2c06eca20e..bf2de00bfc 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(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; } +#if defined(HAVE_FIPS) || defined(HAVE_SELFTEST) + 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) { +#if !defined(HAVE_FIPS) && !defined(HAVE_SELFTEST) + 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(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(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..91a275bcf9 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,18 @@ 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 +2294,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 +2324,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..a4a69a59bb 100644 --- a/wolfcrypt/src/integer.c +++ b/wolfcrypt/src/integer.c @@ -777,6 +777,11 @@ int mp_read_unsigned_bin (mp_int * a, const unsigned char *b, int c) return MP_OKAY; } +int mp_read_unsigned_bin_ct(mp_int * a, const unsigned char *b, int c) +{ + return mp_read_unsigned_bin(a, b, c); +} + /* 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..3326cf7e29 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(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(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(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..0d642253f0 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(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(a, b, c, 0); +} + +int fp_read_unsigned_bin_ct(fp_int *a, const unsigned char *b, int c) +{ + return _read_unsigned_bin(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..f3d81376d6 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); } + +#if !defined(HAVE_FIPS) && !defined(HAVE_SELFTEST) + 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 && !HAVE_SELFTEST */ + #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