Skip to content

Commit 49a6805

Browse files
committed
add constant time DH key agreement APIs:
* adds wc_DhAgree_ct(). * adds wolfSSL_DH_compute_key_padded(), using wc_DhAgree_ct() if available, with fallback fixup code. * adds unit test coverage in api.c:test_wolfSSL_DH() for expected-success calls to wolfSSL_DH_compute_key() and wolfSSL_DH_compute_key_padded().
1 parent dbfebea commit 49a6805

6 files changed

Lines changed: 178 additions & 28 deletions

File tree

src/pk.c

Lines changed: 74 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8672,20 +8672,8 @@ int wolfSSL_DH_generate_key(WOLFSSL_DH* dh)
86728672
}
86738673

86748674

8675-
/* Compute the shared key from the private key and peer's public key.
8676-
*
8677-
* Return code compliant with OpenSSL.
8678-
* OpenSSL returns 0 when number of bits in p are smaller than minimum
8679-
* supported.
8680-
*
8681-
* @param [out] key Buffer to place shared key.
8682-
* @param [in] otherPub Peer's public key.
8683-
* @param [in] dh DH key containing private key.
8684-
* @return -1 on error.
8685-
* @return Size of shared secret in bytes on success.
8686-
*/
8687-
int wolfSSL_DH_compute_key(unsigned char* key, const WOLFSSL_BIGNUM* otherPub,
8688-
WOLFSSL_DH* dh)
8675+
static int _DH_compute_key(unsigned char* key, const WOLFSSL_BIGNUM* otherPub,
8676+
WOLFSSL_DH* dh, int ct)
86898677
{
86908678
int ret = 0;
86918679
word32 keySz = 0;
@@ -8773,10 +8761,39 @@ int wolfSSL_DH_compute_key(unsigned char* key, const WOLFSSL_BIGNUM* otherPub,
87738761

87748762
PRIVATE_KEY_UNLOCK();
87758763
/* Calculate shared secret from private and public keys. */
8776-
if ((ret == 0) && (wc_DhAgree((DhKey*)dh->internal, key, &keySz, priv,
8777-
(word32)privSz, pub, (word32)pubSz) < 0)) {
8778-
WOLFSSL_ERROR_MSG("wc_DhAgree failed");
8779-
ret = WOLFSSL_FATAL_ERROR;
8764+
if (ret == 0) {
8765+
word32 padded_keySz = keySz;
8766+
#if (!defined(HAVE_FIPS) || FIPS_VERSION_GE(7,0)) && !defined(HAVE_SELFTEST)
8767+
if (ct) {
8768+
if (wc_DhAgree_ct((DhKey*)dh->internal, key, &keySz, priv,
8769+
(word32)privSz, pub, (word32)pubSz) < 0) {
8770+
WOLFSSL_ERROR_MSG("wc_DhAgree_ct failed");
8771+
ret = WOLFSSL_FATAL_ERROR;
8772+
}
8773+
}
8774+
else
8775+
#endif /* (!HAVE_FIPS || FIPS_VERSION_GE(7,0)) && !HAVE_SELFTEST */
8776+
{
8777+
if (wc_DhAgree((DhKey*)dh->internal, key, &keySz, priv,
8778+
(word32)privSz, pub, (word32)pubSz) < 0) {
8779+
WOLFSSL_ERROR_MSG("wc_DhAgree failed");
8780+
ret = WOLFSSL_FATAL_ERROR;
8781+
}
8782+
}
8783+
8784+
if ((ret == 0) && ct) {
8785+
/* Arrange for correct fixed-length, right-justified key, even if
8786+
* the crypto back end doesn't support it. With some crypto back
8787+
* ends this forgoes formal constant-timeness on the key agreement,
8788+
* but assured that wolfSSL_DH_compute_key_padded() functions
8789+
* correctly.
8790+
*/
8791+
if (keySz < padded_keySz) {
8792+
XMEMMOVE(key, key + (padded_keySz - keySz),
8793+
padded_keySz - keySz);
8794+
XMEMSET(key, 0, padded_keySz - keySz);
8795+
}
8796+
}
87808797
}
87818798
if (ret == 0) {
87828799
/* Return actual length. */
@@ -8800,6 +8817,45 @@ int wolfSSL_DH_compute_key(unsigned char* key, const WOLFSSL_BIGNUM* otherPub,
88008817

88018818
return ret;
88028819
}
8820+
8821+
/* Compute the shared key from the private key and peer's public key.
8822+
*
8823+
* Return code compliant with OpenSSL.
8824+
* OpenSSL returns 0 when number of bits in p are smaller than minimum
8825+
* supported.
8826+
*
8827+
* @param [out] key Buffer to place shared key.
8828+
* @param [in] otherPub Peer's public key.
8829+
* @param [in] dh DH key containing private key.
8830+
* @return -1 on error.
8831+
* @return Size of shared secret in bytes on success.
8832+
*/
8833+
int wolfSSL_DH_compute_key(unsigned char* key, const WOLFSSL_BIGNUM* otherPub,
8834+
WOLFSSL_DH* dh)
8835+
{
8836+
return _DH_compute_key(key, otherPub, dh, 0);
8837+
}
8838+
8839+
/* Compute the shared key from the private key and peer's public key as in
8840+
* wolfSSL_DH_compute_key, but using constant time processing, with an output
8841+
* key length fixed at the nominal DH key size. Leading zeros are retained.
8842+
*
8843+
* Return code compliant with OpenSSL.
8844+
* OpenSSL returns 0 when number of bits in p are smaller than minimum
8845+
* supported.
8846+
*
8847+
* @param [out] key Buffer to place shared key.
8848+
* @param [in] otherPub Peer's public key.
8849+
* @param [in] dh DH key containing private key.
8850+
* @return -1 on error.
8851+
* @return Size of shared secret in bytes on success.
8852+
*/
8853+
int wolfSSL_DH_compute_key_padded(unsigned char* key,
8854+
const WOLFSSL_BIGNUM* otherPub, WOLFSSL_DH* dh)
8855+
{
8856+
return _DH_compute_key(key, otherPub, dh, 1);
8857+
}
8858+
88038859
#endif /* !HAVE_FIPS || (HAVE_FIPS && !WOLFSSL_DH_EXTRA) ||
88048860
* HAVE_FIPS_VERSION > 2 */
88058861

tests/api.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82334,6 +82334,30 @@ static int test_wolfSSL_DH(void)
8233482334
ExpectNotNull(dh->g);
8233582335
ExpectTrue(pt == buf);
8233682336
ExpectIntEQ(DH_generate_key(dh), 1);
82337+
82338+
/* first, test for expected successful key agreement. */
82339+
if (EXPECT_SUCCESS()) {
82340+
DH *dh2 = NULL;
82341+
unsigned char buf2[268];
82342+
int sz1 = 0, sz2 = 0;
82343+
82344+
ExpectNotNull(dh2 = d2i_DHparams(NULL, &pt, len));
82345+
ExpectIntEQ(DH_generate_key(dh2), 1);
82346+
82347+
ExpectIntGT(sz1=DH_compute_key(buf, dh2->pub_key, dh), 0);
82348+
ExpectIntGT(sz2=DH_compute_key(buf2, dh->pub_key, dh2), 0);
82349+
ExpectIntEQ(sz1, sz2);
82350+
ExpectIntEQ(XMEMCMP(buf, buf2, (size_t)sz1), 0);
82351+
82352+
ExpectIntNE(sz1 = DH_size(dh), 0);
82353+
ExpectIntEQ(DH_compute_key_padded(buf, dh2->pub_key, dh), sz1);
82354+
ExpectIntEQ(DH_compute_key_padded(buf2, dh->pub_key, dh2), sz1);
82355+
ExpectIntEQ(XMEMCMP(buf, buf2, (size_t)sz1), 0);
82356+
82357+
if (dh2 != NULL)
82358+
DH_free(dh2);
82359+
}
82360+
8233782361
ExpectIntEQ(DH_generate_key(dh), 1);
8233882362
ExpectIntEQ(DH_compute_key(NULL, NULL, NULL), -1);
8233982363
ExpectNotNull(pub = BN_new());

wolfcrypt/src/dh.c

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1981,7 +1981,7 @@ int wc_DhGenerateKeyPair(DhKey* key, WC_RNG* rng,
19811981

19821982
#ifndef WOLFSSL_KCAPI_DH
19831983
static int wc_DhAgree_Sync(DhKey* key, byte* agree, word32* agreeSz,
1984-
const byte* priv, word32 privSz, const byte* otherPub, word32 pubSz)
1984+
const byte* priv, word32 privSz, const byte* otherPub, word32 pubSz, int ct)
19851985
{
19861986
int ret = 0;
19871987
#if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC)
@@ -2159,8 +2159,17 @@ static int wc_DhAgree_Sync(DhKey* key, byte* agree, word32* agreeSz,
21592159
if (ret == 0 && mp_read_unsigned_bin(y, otherPub, pubSz) != MP_OKAY)
21602160
ret = MP_READ_E;
21612161

2162-
if (ret == 0 && mp_exptmod(y, x, &key->p, z) != MP_OKAY)
2163-
ret = MP_EXPTMOD_E;
2162+
if (ret == 0) {
2163+
if (ct)
2164+
ret = mp_exptmod_ex(y, x,
2165+
((int)*agreeSz + DIGIT_BIT - 1) / DIGIT_BIT,
2166+
&key->p, z);
2167+
else
2168+
ret = mp_exptmod(y, x, &key->p, z);
2169+
if (ret != MP_OKAY)
2170+
ret = MP_EXPTMOD_E;
2171+
}
2172+
21642173
#ifdef WOLFSSL_CHECK_MEM_ZERO
21652174
if (ret == 0)
21662175
mp_memzero_add("wc_DhAgree_Sync z", z);
@@ -2170,11 +2179,18 @@ static int wc_DhAgree_Sync(DhKey* key, byte* agree, word32* agreeSz,
21702179
if (ret == 0 && (mp_cmp_d(z, 1) == MP_EQ))
21712180
ret = MP_VAL;
21722181

2173-
if (ret == 0 && mp_to_unsigned_bin(z, agree) != MP_OKAY)
2174-
ret = MP_TO_E;
2175-
2176-
if (ret == 0)
2177-
*agreeSz = (word32)mp_unsigned_bin_size(z);
2182+
if (ret == 0) {
2183+
if (ct) {
2184+
if (mp_to_unsigned_bin_len_ct(z, agree, (int)*agreeSz) != MP_OKAY)
2185+
ret = MP_TO_E;
2186+
}
2187+
else {
2188+
if (mp_to_unsigned_bin(z, agree) != MP_OKAY)
2189+
ret = MP_TO_E;
2190+
if (ret == 0)
2191+
*agreeSz = (word32)mp_unsigned_bin_size(z);
2192+
}
2193+
}
21782194

21792195
mp_forcezero(z);
21802196
mp_clear(y);
@@ -2183,6 +2199,7 @@ static int wc_DhAgree_Sync(DhKey* key, byte* agree, word32* agreeSz,
21832199
RESTORE_VECTOR_REGISTERS();
21842200

21852201
#else
2202+
(void)ct;
21862203
ret = WC_KEY_SIZE_E;
21872204
#endif
21882205

@@ -2238,7 +2255,8 @@ static int wc_DhAgree_Async(DhKey* key, byte* agree, word32* agreeSz,
22382255
#endif
22392256

22402257
/* otherwise use software DH */
2241-
ret = wc_DhAgree_Sync(key, agree, agreeSz, priv, privSz, otherPub, pubSz);
2258+
ret = wc_DhAgree_Sync(key, agree, agreeSz, priv, privSz, otherPub, pubSz,
2259+
0);
22422260

22432261
return ret;
22442262
}
@@ -2267,13 +2285,26 @@ int wc_DhAgree(DhKey* key, byte* agree, word32* agreeSz, const byte* priv,
22672285
else
22682286
#endif
22692287
{
2270-
ret = wc_DhAgree_Sync(key, agree, agreeSz, priv, privSz, otherPub, pubSz);
2288+
ret = wc_DhAgree_Sync(key, agree, agreeSz, priv, privSz, otherPub,
2289+
pubSz, 0);
22712290
}
22722291
#endif /* WOLFSSL_KCAPI_DH */
22732292

22742293
return ret;
22752294
}
22762295

2296+
int wc_DhAgree_ct(DhKey* key, byte* agree, word32 *agreeSz, const byte* priv,
2297+
word32 privSz, const byte* otherPub, word32 pubSz)
2298+
{
2299+
if (key == NULL || agree == NULL || agreeSz == NULL || priv == NULL ||
2300+
otherPub == NULL) {
2301+
return BAD_FUNC_ARG;
2302+
}
2303+
2304+
return wc_DhAgree_Sync(key, agree, agreeSz, priv, privSz, otherPub, pubSz,
2305+
1);
2306+
}
2307+
22772308
#ifdef WOLFSSL_DH_EXTRA
22782309
WOLFSSL_LOCAL int wc_DhKeyCopy(DhKey* src, DhKey* dst)
22792310
{

wolfcrypt/test/test.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22785,6 +22785,38 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t dh_test(void)
2278522785
if (agreeSz != agreeSz2 || XMEMCMP(agree, agree2, agreeSz)) {
2278622786
ERROR_OUT(WC_TEST_RET_ENC_NC, done);
2278722787
}
22788+
22789+
#if (!defined(HAVE_FIPS) || FIPS_VERSION_GE(7,0)) && \
22790+
!defined(HAVE_SELFTEST)
22791+
agreeSz = DH_TEST_BUF_SIZE;
22792+
agreeSz2 = DH_TEST_BUF_SIZE;
22793+
22794+
ret = wc_DhAgree_ct(key, agree, &agreeSz, priv, privSz, pub2, pubSz2);
22795+
if (ret != 0)
22796+
ERROR_OUT(WC_TEST_RET_ENC_EC(ret), done);
22797+
22798+
ret = wc_DhAgree_ct(key2, agree2, &agreeSz2, priv2, privSz2, pub, pubSz);
22799+
if (ret != 0)
22800+
ERROR_OUT(WC_TEST_RET_ENC_EC(ret), done);
22801+
22802+
#ifdef WOLFSSL_PUBLIC_MP
22803+
if (agreeSz != (word32)mp_unsigned_bin_size(&key->p))
22804+
{
22805+
ERROR_OUT(WC_TEST_RET_ENC_NC, done);
22806+
}
22807+
#endif
22808+
22809+
if (agreeSz != agreeSz2)
22810+
{
22811+
ERROR_OUT(WC_TEST_RET_ENC_NC, done);
22812+
}
22813+
22814+
if (XMEMCMP(agree, agree2, agreeSz) != 0)
22815+
{
22816+
ERROR_OUT(WC_TEST_RET_ENC_NC, done);
22817+
}
22818+
#endif /* (!HAVE_FIPS || FIPS_VERSION_GE(7,0)) && !HAVE_SELFTEST */
22819+
2278822820
#endif /* !WC_NO_RNG */
2278922821

2279022822
#if defined(WOLFSSL_KEY_GEN) && !defined(HAVE_FIPS) && !defined(HAVE_SELFTEST)

wolfssl/openssl/dh.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ WOLFSSL_API int wolfSSL_DH_size(WOLFSSL_DH* dh);
6767
WOLFSSL_API int wolfSSL_DH_generate_key(WOLFSSL_DH* dh);
6868
WOLFSSL_API int wolfSSL_DH_compute_key(unsigned char* key, const WOLFSSL_BIGNUM* pub,
6969
WOLFSSL_DH* dh);
70+
WOLFSSL_API int wolfSSL_DH_compute_key_padded(unsigned char* key,
71+
const WOLFSSL_BIGNUM* otherPub, WOLFSSL_DH* dh);
72+
7073
WOLFSSL_API int wolfSSL_DH_LoadDer(WOLFSSL_DH* dh, const unsigned char* derBuf,
7174
int derSz);
7275
WOLFSSL_API int wolfSSL_DH_set_length(WOLFSSL_DH* dh, long len);
@@ -91,6 +94,7 @@ typedef WOLFSSL_DH DH;
9194
#define DH_size wolfSSL_DH_size
9295
#define DH_generate_key wolfSSL_DH_generate_key
9396
#define DH_compute_key wolfSSL_DH_compute_key
97+
#define DH_compute_key_padded wolfSSL_DH_compute_key_padded
9498
#define DH_set_length wolfSSL_DH_set_length
9599
#define DH_set0_pqg wolfSSL_DH_set0_pqg
96100
#define DH_get0_pqg wolfSSL_DH_get0_pqg

wolfssl/wolfcrypt/dh.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,9 @@ WOLFSSL_API int wc_DhGenerateKeyPair(DhKey* key, WC_RNG* rng, byte* priv,
151151
WOLFSSL_API int wc_DhAgree(DhKey* key, byte* agree, word32* agreeSz,
152152
const byte* priv, word32 privSz, const byte* otherPub,
153153
word32 pubSz);
154+
WOLFSSL_API int wc_DhAgree_ct(DhKey* key, byte* agree, word32* agreeSz,
155+
const byte* priv, word32 privSz, const byte* otherPub,
156+
word32 pubSz);
154157

155158
WOLFSSL_API int wc_DhKeyDecode(const byte* input, word32* inOutIdx, DhKey* key,
156159
word32 inSz); /* wc_DhKeyDecode is in asn.c */

0 commit comments

Comments
 (0)