static int dh_init(DH *dh);
static int dh_finish(DH *dh);
-static int compute_key(unsigned char *key, const BIGNUM *pub_key, DH *dh)
+/*
+ * See SP800-56Ar3 Section 5.7.1.1
+ * Finite Field Cryptography Diffie-Hellman (FFC DH) Primitive
+ */
+int ossl_dh_compute_key(unsigned char *key, const BIGNUM *pub_key, DH *dh)
{
BN_CTX *ctx = NULL;
BN_MONT_CTX *mont = NULL;
- BIGNUM *tmp;
+ BIGNUM *z = NULL, *pminus1;
int ret = -1;
-#ifndef FIPS_MODULE
- int check_result;
-#endif
if (BN_num_bits(dh->params.p) > OPENSSL_DH_MAX_MODULUS_BITS) {
ERR_raise(ERR_LIB_DH, DH_R_MODULUS_TOO_LARGE);
if (ctx == NULL)
goto err;
BN_CTX_start(ctx);
- tmp = BN_CTX_get(ctx);
- if (tmp == NULL)
+ pminus1 = BN_CTX_get(ctx);
+ z = BN_CTX_get(ctx);
+ if (z == NULL)
goto err;
if (dh->priv_key == NULL) {
if (!mont)
goto err;
}
-/* TODO(3.0) : Solve in a PR related to Key validation for DH */
-#ifndef FIPS_MODULE
- if (!DH_check_pub_key(dh, pub_key, &check_result) || check_result) {
- ERR_raise(ERR_LIB_DH, DH_R_INVALID_PUBKEY);
- goto err;
- }
-#endif
- if (!dh->meth->bn_mod_exp(dh, tmp, pub_key, dh->priv_key, dh->params.p, ctx,
+
+ /* (Step 1) Z = pub_key^priv_key mod p */
+ if (!dh->meth->bn_mod_exp(dh, z, pub_key, dh->priv_key, dh->params.p, ctx,
mont)) {
ERR_raise(ERR_LIB_DH, ERR_R_BN_LIB);
goto err;
}
+ /* (Step 2) Error if z <= 1 or z = p - 1 */
+ if (BN_copy(pminus1, dh->params.p) == NULL
+ || !BN_sub_word(pminus1, 1)
+ || BN_cmp(z, BN_value_one()) <= 0
+ || BN_cmp(z, pminus1) == 0) {
+ ERR_raise(ERR_LIB_DH, DH_R_INVALID_SECRET);
+ goto err;
+ }
+
/* return the padded key, i.e. same number of bytes as the modulus */
- ret = BN_bn2binpad(tmp, key, BN_num_bytes(dh->params.p));
+ ret = BN_bn2binpad(z, key, BN_num_bytes(dh->params.p));
err:
+ BN_clear(z); /* (Step 2) destroy intermediate values */
BN_CTX_end(ctx);
BN_CTX_free(ctx);
return ret;
/* compute the key; ret is constant unless compute_key is external */
#ifdef FIPS_MODULE
- ret = compute_key(key, pub_key, dh);
+ ret = ossl_dh_compute_key(key, pub_key, dh);
#else
ret = dh->meth->compute_key(key, pub_key, dh);
#endif
/* rv is constant unless compute_key is external */
#ifdef FIPS_MODULE
- rv = compute_key(key, pub_key, dh);
+ rv = ossl_dh_compute_key(key, pub_key, dh);
#else
rv = dh->meth->compute_key(key, pub_key, dh);
#endif
static DH_METHOD dh_ossl = {
"OpenSSL DH Method",
generate_key,
- compute_key,
+ ossl_dh_compute_key,
dh_bn_mod_exp,
dh_init,
dh_finish,
/*
- * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
#ifndef OPENSSL_NO_DH
# include <openssl/dh.h>
+# include "crypto/bn_dh.h"
+# include "crypto/dh.h"
static int cb(int p, int n, BN_GENCB *arg);
return 1;
}
+static int dh_computekey_range_test(void)
+{
+ int ret = 0, sz;
+ DH *dh = NULL;
+ BIGNUM *p = NULL, *q = NULL, *g = NULL, *pub = NULL, *priv = NULL;
+ unsigned char *buf = NULL;
+
+ if (!TEST_ptr(p = BN_dup(&ossl_bignum_ffdhe2048_p))
+ || !TEST_ptr(q = BN_dup(&ossl_bignum_ffdhe2048_q))
+ || !TEST_ptr(g = BN_dup(&ossl_bignum_const_2))
+ || !TEST_ptr(dh = DH_new())
+ || !TEST_true(DH_set0_pqg(dh, p, q, g)))
+ goto err;
+ p = q = g = NULL;
+ sz = DH_size(dh);
+
+ if (!TEST_ptr(buf = OPENSSL_malloc(sz))
+ || !TEST_ptr(pub = BN_new())
+ || !TEST_ptr(priv = BN_new()))
+ goto err;
+
+ if (!TEST_true(BN_set_word(priv, 1))
+ || !TEST_true(DH_set0_key(dh, NULL, priv))
+ || !TEST_true(BN_set_word(pub, 1)))
+ goto err;
+
+ /* Given z = pub ^ priv mod p */
+
+ /* Test that z == 1 fails */
+ if (!TEST_int_le(ossl_dh_compute_key(buf, pub, dh), 0))
+ goto err;
+ /* Test that z == 0 fails */
+ if (!TEST_ptr(BN_copy(pub, DH_get0_p(dh)))
+ || !TEST_int_le(ossl_dh_compute_key(buf, pub, dh), 0))
+ goto err;
+ /* Test that z == p - 1 fails */
+ if (!TEST_true(BN_sub_word(pub, 1))
+ || !TEST_int_le(ossl_dh_compute_key(buf, pub, dh), 0))
+ goto err;
+ /* Test that z == p - 2 passes */
+ if (!TEST_true(BN_sub_word(pub, 1))
+ || !TEST_int_eq(ossl_dh_compute_key(buf, pub, dh), sz))
+ goto err;
+
+ ret = 1;
+err:
+ OPENSSL_free(buf);
+ BN_free(pub);
+ BN_free(g);
+ BN_free(q);
+ BN_free(p);
+ DH_free(dh);
+ return ret;
+}
+
/* Test data from RFC 5114 */
static const unsigned char dhtest_1024_160_xA[] = {
0xC2, 0x6C, 0x5D, 0x7C
};
-static const unsigned char dhtest_rfc5114_2048_224_bad_y[] = {
- 0x45, 0x32, 0x5F, 0x51, 0x07, 0xE5, 0xDF, 0x1C, 0xD6, 0x02, 0x82, 0xB3,
- 0x32, 0x8F, 0xA4, 0x0F, 0x87, 0xB8, 0x41, 0xFE, 0xB9, 0x35, 0xDE, 0xAD,
- 0xC6, 0x26, 0x85, 0xB4, 0xFF, 0x94, 0x8C, 0x12, 0x4C, 0xBF, 0x5B, 0x20,
- 0xC4, 0x46, 0xA3, 0x26, 0xEB, 0xA4, 0x25, 0xB7, 0x68, 0x8E, 0xCC, 0x67,
- 0xBA, 0xEA, 0x58, 0xD0, 0xF2, 0xE9, 0xD2, 0x24, 0x72, 0x60, 0xDA, 0x88,
- 0x18, 0x9C, 0xE0, 0x31, 0x6A, 0xAD, 0x50, 0x6D, 0x94, 0x35, 0x8B, 0x83,
- 0x4A, 0x6E, 0xFA, 0x48, 0x73, 0x0F, 0x83, 0x87, 0xFF, 0x6B, 0x66, 0x1F,
- 0xA8, 0x82, 0xC6, 0x01, 0xE5, 0x80, 0xB5, 0xB0, 0x52, 0xD0, 0xE9, 0xD8,
- 0x72, 0xF9, 0x7D, 0x5B, 0x8B, 0xA5, 0x4C, 0xA5, 0x25, 0x95, 0x74, 0xE2,
- 0x7A, 0x61, 0x4E, 0xA7, 0x8F, 0x12, 0xE2, 0xD2, 0x9D, 0x8C, 0x02, 0x70,
- 0x34, 0x44, 0x32, 0xC7, 0xB2, 0xF3, 0xB9, 0xFE, 0x17, 0x2B, 0xD6, 0x1F,
- 0x8B, 0x7E, 0x4A, 0xFA, 0xA3, 0xB5, 0x3E, 0x7A, 0x81, 0x9A, 0x33, 0x66,
- 0x62, 0xA4, 0x50, 0x18, 0x3E, 0xA2, 0x5F, 0x00, 0x07, 0xD8, 0x9B, 0x22,
- 0xE4, 0xEC, 0x84, 0xD5, 0xEB, 0x5A, 0xF3, 0x2A, 0x31, 0x23, 0xD8, 0x44,
- 0x22, 0x2A, 0x8B, 0x37, 0x44, 0xCC, 0xC6, 0x87, 0x4B, 0xBE, 0x50, 0x9D,
- 0x4A, 0xC4, 0x8E, 0x45, 0xCF, 0x72, 0x4D, 0xC0, 0x89, 0xB3, 0x72, 0xED,
- 0x33, 0x2C, 0xBC, 0x7F, 0x16, 0x39, 0x3B, 0xEB, 0xD2, 0xDD, 0xA8, 0x01,
- 0x73, 0x84, 0x62, 0xB9, 0x29, 0xD2, 0xC9, 0x51, 0x32, 0x9E, 0x7A, 0x6A,
- 0xCF, 0xC1, 0x0A, 0xDB, 0x0E, 0xE0, 0x62, 0x77, 0x6F, 0x59, 0x62, 0x72,
- 0x5A, 0x69, 0xA6, 0x5B, 0x70, 0xCA, 0x65, 0xC4, 0x95, 0x6F, 0x9A, 0xC2,
- 0xDF, 0x72, 0x6D, 0xB1, 0x1E, 0x54, 0x7B, 0x51, 0xB4, 0xEF, 0x7F, 0x89,
- 0x93, 0x74, 0x89, 0x59
-};
-
typedef struct {
DH *(*get_param) (void);
const unsigned char *xA;
unsigned char *Z1 = NULL;
unsigned char *Z2 = NULL;
const rfc5114_td *td = NULL;
- BIGNUM *bady = NULL, *priv_key = NULL, *pub_key = NULL;
+ BIGNUM *priv_key = NULL, *pub_key = NULL;
const BIGNUM *pub_key_tmp;
for (i = 0; i < (int)OSSL_NELEM(rfctd); i++) {
OPENSSL_free(Z2);
Z2 = NULL;
}
-
- /* Now i == OSSL_NELEM(rfctd) */
- /* RFC5114 uses unsafe primes, so now test an invalid y value */
- if (!TEST_ptr(dhA = DH_get_2048_224())
- || !TEST_ptr(Z1 = OPENSSL_malloc(DH_size(dhA))))
- goto bad_err;
-
- if (!TEST_ptr(bady = BN_bin2bn(dhtest_rfc5114_2048_224_bad_y,
- sizeof(dhtest_rfc5114_2048_224_bad_y),
- NULL)))
- goto bad_err;
-
- if (!DH_generate_key(dhA))
- goto bad_err;
-
- if (DH_compute_key(Z1, bady, dhA) != -1) {
- /*
- * DH_compute_key should fail with -1. If we get here we unexpectedly
- * allowed an invalid y value
- */
- goto err;
- }
- /* We'll have a stale error on the queue from the above test so clear it */
- ERR_clear_error();
- BN_free(bady);
- DH_free(dhA);
- OPENSSL_free(Z1);
return 1;
bad_err:
- BN_free(bady);
DH_free(dhA);
DH_free(dhB);
BN_free(pub_key);
return 0;
err:
- BN_free(bady);
DH_free(dhA);
DH_free(dhB);
OPENSSL_free(Z1);
TEST_note("No DH support");
#else
ADD_TEST(dh_test);
+ ADD_TEST(dh_computekey_range_test);
ADD_TEST(rfc5114_test);
ADD_TEST(rfc7919_test);
ADD_ALL_TESTS(dh_test_prime_groups, OSSL_NELEM(prime_groups));