Remove LCM dependency from RSA_check_key.
Instead of checking d * e = 1 (mod lcm(p-1, q-1)), we can separately
check d * e = 1 (mod p-1) and d * e = 1 (mod q-1). This drops an LCM
dependency from key import and is 2x faster.
(Our constant-time LCM implementation can probably be faster if we
tried, but now it's only used in RSA keygen, so it doesn't matter much.
It's also using the unoptimized constant-time division, which is
probably the next target if we decide we care about speeding this up.)
Before:
Did 6768 RSA 2048 checking operations in 3015824us (2244.2 ops/sec)
Did 5610 RSA 2048 signing operations in 3033396us (1849.4 ops/sec)
Did 1953 RSA 4096 checking operations in 3060828us (638.1 ops/sec)
Did 817 RSA 4096 signing operations in 3021092us (270.4 ops/sec)
After:
Did 13175 RSA 2048 checking operations in 3090576us (4263.0 ops/sec)
Did 5610 RSA 2048 signing operations in 3032966us (1849.7 ops/sec)
Did 3720 RSA 4096 checking operations in 3085971us (1205.5 ops/sec)
Did 820 RSA 4096 signing operations in 3027312us (270.9 ops/sec)
Bug: 316
Change-Id: Ie29554c02d31f586dd0f8bdee03a227f1d5dc916
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/40146
Commit-Queue: Adam Langley <agl@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/fipsmodule/rsa/rsa.c b/crypto/fipsmodule/rsa/rsa.c
index a7fb7ae..2929673 100644
--- a/crypto/fipsmodule/rsa/rsa.c
+++ b/crypto/fipsmodule/rsa/rsa.c
@@ -655,7 +655,12 @@
}
static int check_mod_inverse(int *out_ok, const BIGNUM *a, const BIGNUM *ainv,
- const BIGNUM *m, int check_reduced, BN_CTX *ctx) {
+ const BIGNUM *m, BN_CTX *ctx) {
+ if (BN_is_negative(ainv) || BN_cmp(ainv, m) >= 0) {
+ *out_ok = 0;
+ return 1;
+ }
+
BN_CTX_start(ctx);
BIGNUM *tmp = BN_CTX_get(ctx);
int ret = tmp != NULL &&
@@ -663,19 +668,12 @@
bn_div_consttime(NULL, tmp, tmp, m, ctx);
if (ret) {
*out_ok = BN_is_one(tmp);
- if (check_reduced && (BN_is_negative(ainv) || BN_cmp(ainv, m) >= 0)) {
- *out_ok = 0;
- }
}
BN_CTX_end(ctx);
return ret;
}
int RSA_check_key(const RSA *key) {
- BIGNUM n, pm1, qm1, lcm, dmp1, dmq1, iqmp_times_q;
- BN_CTX *ctx;
- int ok = 0, has_crt_values;
-
if (RSA_is_opaque(key)) {
// Opaque keys can't be checked.
return 1;
@@ -697,50 +695,53 @@
return 1;
}
- ctx = BN_CTX_new();
+ BN_CTX *ctx = BN_CTX_new();
if (ctx == NULL) {
OPENSSL_PUT_ERROR(RSA, ERR_R_MALLOC_FAILURE);
return 0;
}
- BN_init(&n);
+ BIGNUM tmp, de, pm1, qm1, dmp1, dmq1;
+ int ok = 0;
+ BN_init(&tmp);
+ BN_init(&de);
BN_init(&pm1);
BN_init(&qm1);
- BN_init(&lcm);
BN_init(&dmp1);
BN_init(&dmq1);
- BN_init(&iqmp_times_q);
-
- int d_ok;
- if (!bn_mul_consttime(&n, key->p, key->q, ctx) ||
- // lcm = lcm(p, q)
- !bn_usub_consttime(&pm1, key->p, BN_value_one()) ||
- !bn_usub_consttime(&qm1, key->q, BN_value_one()) ||
- !bn_lcm_consttime(&lcm, &pm1, &qm1, ctx) ||
- // Other implementations use the Euler totient rather than the Carmichael
- // totient, so allow unreduced |key->d|.
- !check_mod_inverse(&d_ok, key->e, key->d, &lcm,
- 0 /* don't require reduced */, ctx)) {
+ if (!bn_mul_consttime(&tmp, key->p, key->q, ctx)) {
OPENSSL_PUT_ERROR(RSA, ERR_LIB_BN);
goto out;
}
- if (BN_cmp(&n, key->n) != 0) {
+ if (BN_cmp(&tmp, key->n) != 0) {
OPENSSL_PUT_ERROR(RSA, RSA_R_N_NOT_EQUAL_P_Q);
goto out;
}
- if (!d_ok) {
- OPENSSL_PUT_ERROR(RSA, RSA_R_D_E_NOT_CONGRUENT_TO_1);
- goto out;
- }
-
if (BN_is_negative(key->d) || BN_cmp(key->d, key->n) >= 0) {
OPENSSL_PUT_ERROR(RSA, RSA_R_D_OUT_OF_RANGE);
goto out;
}
- has_crt_values = key->dmp1 != NULL;
+ // d must be an inverse of e mod the Carmichael totient, lcm(p-1, q-1), but it
+ // may be unreduced because other implementations use the Euler totient. We
+ // simply check that d * e is one mod p-1 and mod q-1.
+ if (!bn_usub_consttime(&pm1, key->p, BN_value_one()) ||
+ !bn_usub_consttime(&qm1, key->q, BN_value_one()) ||
+ !bn_mul_consttime(&de, key->d, key->e, ctx) ||
+ !bn_div_consttime(NULL, &tmp, &de, &pm1, ctx) ||
+ !bn_div_consttime(NULL, &de, &de, &qm1, ctx)) {
+ OPENSSL_PUT_ERROR(RSA, ERR_LIB_BN);
+ goto out;
+ }
+
+ if (!BN_is_one(&tmp) || !BN_is_one(&de)) {
+ OPENSSL_PUT_ERROR(RSA, RSA_R_D_E_NOT_CONGRUENT_TO_1);
+ goto out;
+ }
+
+ int has_crt_values = key->dmp1 != NULL;
if (has_crt_values != (key->dmq1 != NULL) ||
has_crt_values != (key->iqmp != NULL)) {
OPENSSL_PUT_ERROR(RSA, RSA_R_INCONSISTENT_SET_OF_CRT_VALUES);
@@ -749,12 +750,9 @@
if (has_crt_values) {
int dmp1_ok, dmq1_ok, iqmp_ok;
- if (!check_mod_inverse(&dmp1_ok, key->e, key->dmp1, &pm1,
- 1 /* check reduced */, ctx) ||
- !check_mod_inverse(&dmq1_ok, key->e, key->dmq1, &qm1,
- 1 /* check reduced */, ctx) ||
- !check_mod_inverse(&iqmp_ok, key->q, key->iqmp, key->p,
- 1 /* check reduced */, ctx)) {
+ if (!check_mod_inverse(&dmp1_ok, key->e, key->dmp1, &pm1, ctx) ||
+ !check_mod_inverse(&dmq1_ok, key->e, key->dmq1, &qm1, ctx) ||
+ !check_mod_inverse(&iqmp_ok, key->q, key->iqmp, key->p, ctx)) {
OPENSSL_PUT_ERROR(RSA, ERR_LIB_BN);
goto out;
}
@@ -768,13 +766,12 @@
ok = 1;
out:
- BN_free(&n);
+ BN_free(&tmp);
+ BN_free(&de);
BN_free(&pm1);
BN_free(&qm1);
- BN_free(&lcm);
BN_free(&dmp1);
BN_free(&dmq1);
- BN_free(&iqmp_times_q);
BN_CTX_free(ctx);
return ok;