Use a minimal totient when generating RSA keys.
FIPS 186-4 wants d = e^-1 (mod lcm(p-1, q-1)), not (p-1)*(q-1).
Note this means the size of d might reveal information about p-1 and
q-1. However, we do operations with Chinese Remainder Theorem, so we
only use d (mod p-1) and d (mod q-1) as exponents. Using a minimal
totient does not affect those two values.
This removes RSA_recover_crt_params. Using a minimal d breaks (or rather
reveals an existing bug in) the function.
While I'm here, rename those ridiculous variable names.
Change-Id: Iaf623271d49cd664ba0eca24aa25a393f5666fac
Reviewed-on: https://boringssl-review.googlesource.com/15944
Commit-Queue: David Benjamin <davidben@google.com>
Commit-Queue: Adam Langley <agl@google.com>
Reviewed-by: Adam Langley <agl@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/crypto/rsa/rsa.c b/crypto/rsa/rsa.c
index afc1432..7ad78a1 100644
--- a/crypto/rsa/rsa.c
+++ b/crypto/rsa/rsa.c
@@ -564,11 +564,6 @@
return ret;
}
-static void bn_free_and_null(BIGNUM **bn) {
- BN_free(*bn);
- *bn = NULL;
-}
-
int RSA_check_key(const RSA *key) {
BIGNUM n, pm1, qm1, lcm, gcd, de, dmp1, dmq1, iqmp_times_q;
BN_CTX *ctx;
@@ -612,7 +607,7 @@
BN_init(&iqmp_times_q);
if (!BN_mul(&n, key->p, key->q, ctx) ||
- /* lcm = lcm(prime-1, for all primes) */
+ /* lcm = lcm(p, q) */
!BN_sub(&pm1, key->p, BN_value_one()) ||
!BN_sub(&qm1, key->q, BN_value_one()) ||
!BN_mul(&lcm, &pm1, &qm1, ctx) ||
@@ -623,7 +618,7 @@
if (!BN_div(&lcm, NULL, &lcm, &gcd, ctx) ||
!BN_gcd(&gcd, &pm1, &qm1, ctx) ||
- /* de = d*e mod lcm(prime-1, for all primes). */
+ /* de = d*e mod lcm(p, q). */
!BN_mod_mul(&de, key->d, key->e, &lcm, ctx)) {
OPENSSL_PUT_ERROR(RSA, ERR_LIB_BN);
goto out;
@@ -767,134 +762,6 @@
return ret;
}
-int RSA_recover_crt_params(RSA *rsa) {
- BN_CTX *ctx;
- BIGNUM *totient, *rem, *multiple, *p_plus_q, *p_minus_q;
- int ok = 0;
-
- if (rsa->n == NULL || rsa->e == NULL || rsa->d == NULL) {
- OPENSSL_PUT_ERROR(RSA, RSA_R_EMPTY_PUBLIC_KEY);
- return 0;
- }
-
- if (rsa->p || rsa->q || rsa->dmp1 || rsa->dmq1 || rsa->iqmp) {
- OPENSSL_PUT_ERROR(RSA, RSA_R_CRT_PARAMS_ALREADY_GIVEN);
- return 0;
- }
-
- /* This uses the algorithm from section 9B of the RSA paper:
- * http://people.csail.mit.edu/rivest/Rsapaper.pdf */
-
- ctx = BN_CTX_new();
- if (ctx == NULL) {
- OPENSSL_PUT_ERROR(RSA, ERR_R_MALLOC_FAILURE);
- return 0;
- }
-
- BN_CTX_start(ctx);
- totient = BN_CTX_get(ctx);
- rem = BN_CTX_get(ctx);
- multiple = BN_CTX_get(ctx);
- p_plus_q = BN_CTX_get(ctx);
- p_minus_q = BN_CTX_get(ctx);
-
- if (totient == NULL || rem == NULL || multiple == NULL || p_plus_q == NULL ||
- p_minus_q == NULL) {
- OPENSSL_PUT_ERROR(RSA, ERR_R_MALLOC_FAILURE);
- goto err;
- }
-
- /* ed-1 is a small multiple of φ(n). */
- if (!BN_mul(totient, rsa->e, rsa->d, ctx) ||
- !BN_sub_word(totient, 1) ||
- /* φ(n) =
- * pq - p - q + 1 =
- * n - (p + q) + 1
- *
- * Thus n is a reasonable estimate for φ(n). So, (ed-1)/n will be very
- * close. But, when we calculate the quotient, we'll be truncating it
- * because we discard the remainder. Thus (ed-1)/multiple will be >= n,
- * which the totient cannot be. So we add one to the estimate.
- *
- * Consider ed-1 as:
- *
- * multiple * (n - (p+q) + 1) =
- * multiple*n - multiple*(p+q) + multiple
- *
- * When we divide by n, the first term becomes multiple and, since
- * multiple and p+q is tiny compared to n, the second and third terms can
- * be ignored. Thus I claim that subtracting one from the estimate is
- * sufficient. */
- !BN_div(multiple, NULL, totient, rsa->n, ctx) ||
- !BN_add_word(multiple, 1) ||
- !BN_div(totient, rem, totient, multiple, ctx)) {
- OPENSSL_PUT_ERROR(RSA, ERR_R_BN_LIB);
- goto err;
- }
-
- if (!BN_is_zero(rem)) {
- OPENSSL_PUT_ERROR(RSA, RSA_R_BAD_RSA_PARAMETERS);
- goto err;
- }
-
- rsa->p = BN_new();
- rsa->q = BN_new();
- rsa->dmp1 = BN_new();
- rsa->dmq1 = BN_new();
- rsa->iqmp = BN_new();
- if (rsa->p == NULL || rsa->q == NULL || rsa->dmp1 == NULL || rsa->dmq1 ==
- NULL || rsa->iqmp == NULL) {
- OPENSSL_PUT_ERROR(RSA, ERR_R_MALLOC_FAILURE);
- goto err;
- }
-
- /* φ(n) = n - (p + q) + 1 =>
- * n - totient + 1 = p + q */
- if (!BN_sub(p_plus_q, rsa->n, totient) ||
- !BN_add_word(p_plus_q, 1) ||
- /* p - q = sqrt((p+q)^2 - 4n) */
- !BN_sqr(rem, p_plus_q, ctx) ||
- !BN_lshift(multiple, rsa->n, 2) ||
- !BN_sub(rem, rem, multiple) ||
- !BN_sqrt(p_minus_q, rem, ctx) ||
- /* q is 1/2 (p+q)-(p-q) */
- !BN_sub(rsa->q, p_plus_q, p_minus_q) ||
- !BN_rshift1(rsa->q, rsa->q) ||
- !BN_div(rsa->p, NULL, rsa->n, rsa->q, ctx) ||
- !BN_mul(multiple, rsa->p, rsa->q, ctx)) {
- OPENSSL_PUT_ERROR(RSA, ERR_R_BN_LIB);
- goto err;
- }
-
- if (BN_cmp(multiple, rsa->n) != 0) {
- OPENSSL_PUT_ERROR(RSA, RSA_R_INTERNAL_ERROR);
- goto err;
- }
-
- if (!BN_sub(rem, rsa->p, BN_value_one()) ||
- !BN_mod(rsa->dmp1, rsa->d, rem, ctx) ||
- !BN_sub(rem, rsa->q, BN_value_one()) ||
- !BN_mod(rsa->dmq1, rsa->d, rem, ctx) ||
- !BN_mod_inverse(rsa->iqmp, rsa->q, rsa->p, ctx)) {
- OPENSSL_PUT_ERROR(RSA, ERR_R_BN_LIB);
- goto err;
- }
-
- ok = 1;
-
-err:
- BN_CTX_end(ctx);
- BN_CTX_free(ctx);
- if (!ok) {
- bn_free_and_null(&rsa->p);
- bn_free_and_null(&rsa->q);
- bn_free_and_null(&rsa->dmp1);
- bn_free_and_null(&rsa->dmq1);
- bn_free_and_null(&rsa->iqmp);
- }
- return ok;
-}
-
int RSA_private_transform(RSA *rsa, uint8_t *out, const uint8_t *in,
size_t len) {
if (rsa->meth->private_transform) {
diff --git a/crypto/rsa/rsa_impl.c b/crypto/rsa/rsa_impl.c
index 012a984..5f5eb5e 100644
--- a/crypto/rsa/rsa_impl.c
+++ b/crypto/rsa/rsa_impl.c
@@ -928,11 +928,11 @@
goto bn_err;
}
BN_CTX_start(ctx);
- BIGNUM *r0 = BN_CTX_get(ctx);
- BIGNUM *r1 = BN_CTX_get(ctx);
- BIGNUM *r2 = BN_CTX_get(ctx);
- BIGNUM *r3 = BN_CTX_get(ctx);
- if (r0 == NULL || r1 == NULL || r2 == NULL || r3 == NULL) {
+ BIGNUM *totient = BN_CTX_get(ctx);
+ BIGNUM *pm1 = BN_CTX_get(ctx);
+ BIGNUM *qm1 = BN_CTX_get(ctx);
+ BIGNUM *gcd = BN_CTX_get(ctx);
+ if (totient == NULL || pm1 == NULL || qm1 == NULL || gcd == NULL) {
goto bn_err;
}
@@ -969,11 +969,19 @@
rsa->q = tmp;
}
- /* Calculate d. */
- if (!BN_sub(r1 /* p-1 */, rsa->p, BN_value_one()) ||
- !BN_sub(r2 /* q-1 */, rsa->q, BN_value_one()) ||
- !BN_mul(r0 /* (p-1)(q-1) */, r1, r2, ctx) ||
- !BN_mod_inverse(rsa->d, rsa->e, r0, ctx)) {
+ /* Calculate d = e^(-1) (mod lcm(p-1, q-1)), per FIPS 186-4. This differs
+ * from typical RSA implementations which use (p-1)*(q-1).
+ *
+ * Note this means the size of d might reveal information about p-1 and
+ * q-1. However, we do operations with Chinese Remainder Theorem, so we only
+ * use d (mod p-1) and d (mod q-1) as exponents. Using a minimal totient
+ * does not affect those two values. */
+ if (!BN_sub(pm1, rsa->p, BN_value_one()) ||
+ !BN_sub(qm1, rsa->q, BN_value_one()) ||
+ !BN_mul(totient, pm1, qm1, ctx) ||
+ !BN_gcd(gcd, pm1, qm1, ctx) ||
+ !BN_div(totient, NULL, totient, gcd, ctx) ||
+ !BN_mod_inverse(rsa->d, rsa->e, totient, ctx)) {
goto bn_err;
}
@@ -984,9 +992,9 @@
if (/* Calculate n. */
!BN_mul(rsa->n, rsa->p, rsa->q, ctx) ||
/* Calculate d mod (p-1). */
- !BN_mod(rsa->dmp1, rsa->d, r1, ctx) ||
+ !BN_mod(rsa->dmp1, rsa->d, pm1, ctx) ||
/* Calculate d mod (q-1) */
- !BN_mod(rsa->dmq1, rsa->d, r2, ctx)) {
+ !BN_mod(rsa->dmq1, rsa->d, qm1, ctx)) {
goto bn_err;
}
diff --git a/crypto/rsa/rsa_test.cc b/crypto/rsa/rsa_test.cc
index 415f6b8..be69abe 100644
--- a/crypto/rsa/rsa_test.cc
+++ b/crypto/rsa/rsa_test.cc
@@ -555,43 +555,6 @@
buf_len, key.get()));
}
-TEST(RSATest, RecoverCRTParams) {
- bssl::UniquePtr<BIGNUM> e(BN_new());
- ASSERT_TRUE(e);
- ASSERT_TRUE(BN_set_word(e.get(), RSA_F4));
-
- bssl::UniquePtr<RSA> key1(RSA_new());
- ASSERT_TRUE(key1);
- ASSERT_TRUE(RSA_generate_key_ex(key1.get(), 512, e.get(), nullptr));
-
- EXPECT_TRUE(RSA_check_key(key1.get()));
-
- // Create a copy of the key without CRT parameters.
- bssl::UniquePtr<RSA> key2(RSA_new());
- ASSERT_TRUE(key2);
- key2->n = BN_dup(key1->n);
- key2->e = BN_dup(key1->e);
- key2->d = BN_dup(key1->d);
- ASSERT_TRUE(key2->n);
- ASSERT_TRUE(key2->e);
- ASSERT_TRUE(key2->d);
-
- ASSERT_TRUE(RSA_recover_crt_params(key2.get()));
-
- // The recovered RSA parameters should work.
- EXPECT_TRUE(RSA_check_key(key2.get()));
-
- uint8_t buf[128];
- unsigned buf_len = sizeof(buf);
- ASSERT_LE(RSA_size(key2.get()), buf_len);
-
- const uint8_t kDummyHash[32] = {0};
- EXPECT_TRUE(RSA_sign(NID_sha256, kDummyHash, sizeof(kDummyHash), buf,
- &buf_len, key2.get()));
- EXPECT_TRUE(RSA_verify(NID_sha256, kDummyHash, sizeof(kDummyHash), buf,
- buf_len, key2.get()));
-}
-
TEST(RSATest, ASN1) {
// Test that private keys may be decoded.
bssl::UniquePtr<RSA> rsa(
diff --git a/include/openssl/rsa.h b/include/openssl/rsa.h
index b697a7d..6ce75f6 100644
--- a/include/openssl/rsa.h
+++ b/include/openssl/rsa.h
@@ -336,13 +336,6 @@
* if they pass and zero otherwise. Opaque keys always fail. */
OPENSSL_EXPORT int RSA_check_fips(RSA *key);
-/* RSA_recover_crt_params uses |rsa->n|, |rsa->d| and |rsa->e| in order to
- * calculate the two primes used and thus the precomputed, CRT values. These
- * values are set in the |p|, |q|, |dmp1|, |dmq1| and |iqmp| members of |rsa|,
- * which must be |NULL| on entry. It returns one on success and zero
- * otherwise. */
-OPENSSL_EXPORT int RSA_recover_crt_params(RSA *rsa);
-
/* RSA_verify_PKCS1_PSS_mgf1 verifies that |EM| is a correct PSS padding of
* |mHash|, where |mHash| is a digest produced by |Hash|. |EM| must point to
* exactly |RSA_size(rsa)| bytes of data. The |mgf1Hash| argument specifies the