diff --git a/crypto/bn/bn.h b/crypto/bn/bn.h index 23fca81e..40ae790c 100644 --- a/crypto/bn/bn.h +++ b/crypto/bn/bn.h @@ -367,6 +367,12 @@ int BN_div(BIGNUM *quotient, BIGNUM *rem, const BIGNUM *numerator, * remainder or (BN_ULONG)-1 on error. */ BN_ULONG BN_div_word(BIGNUM *numerator, BN_ULONG divisor); +/* BN_sqrt sets |*out_sqrt| (which may be the same |BIGNUM| as |in|) to the + * square root of |in|, using |ctx|. It returns one on success or zero on + * error. Negative numbers and non-square numbers will result in an error with + * appropriate errors on the error queue. */ +int BN_sqrt(BIGNUM *out_sqrt, const BIGNUM *in, BN_CTX *ctx); + /* Comparison functions */ @@ -781,6 +787,7 @@ unsigned BN_num_bits_word(BN_ULONG l); #define BN_F_BN_mod_inverse_no_branch 121 #define BN_F_BN_generate_dsa_nonce 122 #define BN_F_BN_generate_prime_ex 123 +#define BN_F_BN_sqrt 124 #define BN_R_NOT_A_SQUARE 100 #define BN_R_TOO_MANY_ITERATIONS 101 #define BN_R_INPUT_NOT_REDUCED 102 @@ -797,5 +804,6 @@ unsigned BN_num_bits_word(BN_ULONG l); #define BN_R_BIGNUM_TOO_LONG 113 #define BN_R_PRIVATE_KEY_TOO_LARGE 114 #define BN_R_BITS_TOO_SMALL 115 +#define BN_R_NEGATIVE_NUMBER 116 #endif /* OPENSSL_HEADER_BN_H */ diff --git a/crypto/bn/bn_error.c b/crypto/bn/bn_error.c index 679033b7..e0404d95 100644 --- a/crypto/bn/bn_error.c +++ b/crypto/bn/bn_error.c @@ -38,6 +38,7 @@ const ERR_STRING_DATA BN_error_string_data[] = { {ERR_PACK(ERR_LIB_BN, BN_F_BN_new, 0), "BN_new"}, {ERR_PACK(ERR_LIB_BN, BN_F_BN_rand, 0), "BN_rand"}, {ERR_PACK(ERR_LIB_BN, BN_F_BN_rand_range, 0), "BN_rand_range"}, + {ERR_PACK(ERR_LIB_BN, BN_F_BN_sqrt, 0), "BN_sqrt"}, {ERR_PACK(ERR_LIB_BN, BN_F_BN_usub, 0), "BN_usub"}, {ERR_PACK(ERR_LIB_BN, BN_F_bn_wexpand, 0), "bn_wexpand"}, {ERR_PACK(ERR_LIB_BN, BN_F_mod_exp_recp, 0), "mod_exp_recp"}, @@ -50,6 +51,7 @@ const ERR_STRING_DATA BN_error_string_data[] = { {ERR_PACK(ERR_LIB_BN, 0, BN_R_EXPAND_ON_STATIC_BIGNUM_DATA), "EXPAND_ON_STATIC_BIGNUM_DATA"}, {ERR_PACK(ERR_LIB_BN, 0, BN_R_INPUT_NOT_REDUCED), "INPUT_NOT_REDUCED"}, {ERR_PACK(ERR_LIB_BN, 0, BN_R_INVALID_RANGE), "INVALID_RANGE"}, + {ERR_PACK(ERR_LIB_BN, 0, BN_R_NEGATIVE_NUMBER), "NEGATIVE_NUMBER"}, {ERR_PACK(ERR_LIB_BN, 0, BN_R_NOT_A_SQUARE), "NOT_A_SQUARE"}, {ERR_PACK(ERR_LIB_BN, 0, BN_R_NOT_INITIALIZED), "NOT_INITIALIZED"}, {ERR_PACK(ERR_LIB_BN, 0, BN_R_NO_INVERSE), "NO_INVERSE"}, diff --git a/crypto/bn/bn_test.c b/crypto/bn/bn_test.c index aa9cecc4..45303fce 100644 --- a/crypto/bn/bn_test.c +++ b/crypto/bn/bn_test.c @@ -69,9 +69,10 @@ #include +#include #include #include -#include +#include #include "internal.h" @@ -101,6 +102,7 @@ int test_mod_sqrt(BIO *bp, BN_CTX *ctx); static int test_exp_mod_zero(); int test_small_prime(BIO *bp,BN_CTX *ctx); int test_mod_exp_mont5(BIO *bp, BN_CTX *ctx); +int test_sqrt(BIO *bp, BN_CTX *ctx); #if 0 int test_gf2m_add(BIO *bp); int test_gf2m_mod(BIO *bp); @@ -269,6 +271,11 @@ int main(int argc, char *argv[]) { goto err; (void)BIO_flush(out); + message(out, "BN_sqrt"); + if (!test_sqrt(out, ctx)) + goto err; + (void)BIO_flush(out); + BN_CTX_free(ctx); BIO_free(out); @@ -1289,3 +1296,46 @@ err: BN_free(&r); return ret; } + +int test_sqrt(BIO *bp, BN_CTX *ctx) { + BIGNUM *n = BN_new(), *nn = BN_new(), *sqrt = BN_new(); + unsigned i; + + /* Test some random squares. */ + for (i = 0; i < 100; i++) { + if (!BN_rand(n, 1024 /* bit length */, -1 /* no modification of top bits */, + 0 /* don't modify bottom bit */) || + !BN_mul(nn, n, n, ctx) || + !BN_sqrt(sqrt, nn, ctx)) { + BIO_print_errors_fp(stderr); + return 0; + } + if (BN_cmp(n, sqrt) != 0) { + fprintf(stderr, "Bad result from BN_sqrt.\n"); + return 0; + } + } + + /* Test some non-squares */ + for (i = 0; i < 100; i++) { + if (!BN_rand(n, 1024 /* bit length */, -1 /* no modification of top bits */, + 0 /* don't modify bottom bit */) || + !BN_mul(nn, n, n, ctx) || + !BN_add(nn, nn, BN_value_one())) { + BIO_print_errors_fp(stderr); + return 0; + } + + if (BN_sqrt(sqrt, nn, ctx)) { + char *nn_str = BN_bn2dec(nn); + fprintf(stderr, "BIO_sqrt didn't fail on a non-square: %s\n", nn_str); + OPENSSL_free(nn_str); + } + } + + BN_free(n); + BN_free(sqrt); + BN_free(nn); + + return 1; +} diff --git a/crypto/bn/sqrt.c b/crypto/bn/sqrt.c index 3ec763b3..07041f9c 100644 --- a/crypto/bn/sqrt.c +++ b/crypto/bn/sqrt.c @@ -428,3 +428,78 @@ end: BN_CTX_end(ctx); return ret; } + +int BN_sqrt(BIGNUM *out_sqrt, const BIGNUM *in, BN_CTX *ctx) { + BIGNUM *estimate, *tmp, *delta, *last_delta, *tmp2; + int ok = 0, last_delta_valid = 0; + + if (in->neg) { + OPENSSL_PUT_ERROR(BN, BN_sqrt, BN_R_NEGATIVE_NUMBER); + return 0; + } + if (BN_is_zero(in)) { + BN_zero(out_sqrt); + return 1; + } + + BN_CTX_start(ctx); + if (out_sqrt == in) { + estimate = BN_CTX_get(ctx); + } else { + estimate = out_sqrt; + } + tmp = BN_CTX_get(ctx); + last_delta = BN_CTX_get(ctx); + delta = BN_CTX_get(ctx); + if (estimate == NULL || tmp == NULL || last_delta == NULL || delta == NULL) { + OPENSSL_PUT_ERROR(BN, BN_sqrt, ERR_R_MALLOC_FAILURE); + goto err; + } + + /* We estimate that the square root of an n-bit number is 2^{n/2}. */ + BN_lshift(estimate, BN_value_one(), BN_num_bits(in)/2); + + /* This is Newton's method for finding a root of the equation |estimate|^2 - + * |in| = 0. */ + for (;;) { + /* |estimate| = 1/2 * (|estimate| + |in|/|estimate|) */ + if (!BN_div(tmp, NULL, in, estimate, ctx) || + !BN_add(tmp, tmp, estimate) || + !BN_rshift1(estimate, tmp) || + /* |tmp| = |estimate|^2 */ + !BN_sqr(tmp, estimate, ctx) || + /* |delta| = |in| - |tmp| */ + !BN_sub(delta, in, tmp)) { + OPENSSL_PUT_ERROR(BN, BN_sqrt, ERR_R_BN_LIB); + goto err; + } + + delta->neg = 0; + /* The difference between |in| and |estimate| squared is required to always + * decrease. This ensures that the loop always terminates, but I don't have + * a proof that it always finds the square root for a given square. */ + if (last_delta_valid && BN_cmp(delta, last_delta) >= 0) { + break; + } + + last_delta_valid = 1; + + tmp2 = last_delta; + last_delta = delta; + delta = tmp2; + } + + if (BN_cmp(tmp, in) != 0) { + OPENSSL_PUT_ERROR(BN, BN_sqrt, BN_R_NOT_A_SQUARE); + goto err; + } + + ok = 1; + +err: + if (ok && out_sqrt == in) { + BN_copy(out_sqrt, estimate); + } + BN_CTX_end(ctx); + return ok; +} diff --git a/crypto/rsa/rsa.c b/crypto/rsa/rsa.c index 061bc49c..583e6675 100644 --- a/crypto/rsa/rsa.c +++ b/crypto/rsa/rsa.c @@ -486,3 +486,141 @@ out: } return ret; } + +static void bn_free_and_null(BIGNUM **bn) { + if (*bn == NULL) { + return; + } + + BN_free(*bn); + *bn = NULL; +} + +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_recover_crt_params, RSA_R_EMPTY_PUBLIC_KEY); + return 0; + } + + if (rsa->p || rsa->q || rsa->dmp1 || rsa->dmq1 || rsa->iqmp) { + OPENSSL_PUT_ERROR(RSA, RSA_recover_crt_params, + 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, RSA_recover_crt_params, 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, RSA_recover_crt_params, 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, RSA_recover_crt_params, ERR_R_BN_LIB); + goto err; + } + + if (!BN_is_zero(rem)) { + OPENSSL_PUT_ERROR(RSA, RSA_recover_crt_params, 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, RSA_recover_crt_params, 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, RSA_recover_crt_params, ERR_R_BN_LIB); + goto err; + } + + if (BN_cmp(multiple, rsa->n) != 0) { + OPENSSL_PUT_ERROR(RSA, RSA_recover_crt_params, 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, RSA_recover_crt_params, 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; +} diff --git a/crypto/rsa/rsa.h b/crypto/rsa/rsa.h index b60a59c8..403e833e 100644 --- a/crypto/rsa/rsa.h +++ b/crypto/rsa/rsa.h @@ -244,6 +244,13 @@ RSA *RSAPublicKey_dup(const RSA *rsa); * |rsa| into it. It returns the fresh |RSA| object, or NULL on error. */ RSA *RSAPrivateKey_dup(const RSA *rsa); +/* 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. */ +int RSA_recover_crt_params(RSA *rsa); + /* ASN.1 functions. */ @@ -421,6 +428,7 @@ struct rsa_st { #define RSA_F_BN_BLINDING_create_param 124 #define RSA_F_decrypt 125 #define RSA_F_RSA_padding_check_PKCS1_type_2 126 +#define RSA_F_RSA_recover_crt_params 127 #define RSA_R_INVALID_MESSAGE_LENGTH 100 #define RSA_R_DATA_GREATER_THAN_MOD_LEN 101 #define RSA_R_NO_PUBLIC_EXPONENT 102 @@ -456,5 +464,8 @@ struct rsa_st { #define RSA_R_BAD_SIGNATURE 132 #define RSA_R_BN_NOT_INITIALIZED 133 #define RSA_R_PKCS_DECODING_ERROR 134 +#define RSA_R_BAD_RSA_PARAMETERS 135 +#define RSA_R_INTERNAL_ERROR 136 +#define RSA_R_CRT_PARAMS_ALREADY_GIVEN 137 #endif /* OPENSSL_HEADER_RSA_H */ diff --git a/crypto/rsa/rsa_error.c b/crypto/rsa/rsa_error.c index a165e165..6bcf850b 100644 --- a/crypto/rsa/rsa_error.c +++ b/crypto/rsa/rsa_error.c @@ -34,6 +34,7 @@ const ERR_STRING_DATA RSA_error_string_data[] = { {ERR_PACK(ERR_LIB_RSA, RSA_F_RSA_padding_check_PKCS1_type_2, 0), "RSA_padding_check_PKCS1_type_2"}, {ERR_PACK(ERR_LIB_RSA, RSA_F_RSA_padding_check_SSLv23, 0), "RSA_padding_check_SSLv23"}, {ERR_PACK(ERR_LIB_RSA, RSA_F_RSA_padding_check_none, 0), "RSA_padding_check_none"}, + {ERR_PACK(ERR_LIB_RSA, RSA_F_RSA_recover_crt_params, 0), "RSA_recover_crt_params"}, {ERR_PACK(ERR_LIB_RSA, RSA_F_RSA_sign, 0), "RSA_sign"}, {ERR_PACK(ERR_LIB_RSA, RSA_F_RSA_verify, 0), "RSA_verify"}, {ERR_PACK(ERR_LIB_RSA, RSA_F_RSA_verify_PKCS1_PSS_mgf1, 0), "RSA_verify_PKCS1_PSS_mgf1"}, @@ -47,10 +48,12 @@ const ERR_STRING_DATA RSA_error_string_data[] = { {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_BAD_E_VALUE), "BAD_E_VALUE"}, {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_BAD_FIXED_HEADER_DECRYPT), "BAD_FIXED_HEADER_DECRYPT"}, {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_BAD_PAD_BYTE_COUNT), "BAD_PAD_BYTE_COUNT"}, + {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_BAD_RSA_PARAMETERS), "BAD_RSA_PARAMETERS"}, {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_BAD_SIGNATURE), "BAD_SIGNATURE"}, {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_BLOCK_TYPE_IS_NOT_01), "BLOCK_TYPE_IS_NOT_01"}, {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_BLOCK_TYPE_IS_NOT_02), "BLOCK_TYPE_IS_NOT_02"}, {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_BN_NOT_INITIALIZED), "BN_NOT_INITIALIZED"}, + {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_CRT_PARAMS_ALREADY_GIVEN), "CRT_PARAMS_ALREADY_GIVEN"}, {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_DATA_GREATER_THAN_MOD_LEN), "DATA_GREATER_THAN_MOD_LEN"}, {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_DATA_TOO_LARGE), "DATA_TOO_LARGE"}, {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE), "DATA_TOO_LARGE_FOR_KEY_SIZE"}, @@ -60,6 +63,7 @@ const ERR_STRING_DATA RSA_error_string_data[] = { {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_DIGEST_TOO_BIG_FOR_RSA_KEY), "DIGEST_TOO_BIG_FOR_RSA_KEY"}, {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_EMPTY_PUBLIC_KEY), "EMPTY_PUBLIC_KEY"}, {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_FIRST_OCTET_INVALID), "FIRST_OCTET_INVALID"}, + {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_INTERNAL_ERROR), "INTERNAL_ERROR"}, {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_INVALID_MESSAGE_LENGTH), "INVALID_MESSAGE_LENGTH"}, {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_KEY_SIZE_TOO_SMALL), "KEY_SIZE_TOO_SMALL"}, {ERR_PACK(ERR_LIB_RSA, 0, RSA_R_LAST_OCTET_INVALID), "LAST_OCTET_INVALID"}, diff --git a/crypto/rsa/rsa_test.c b/crypto/rsa/rsa_test.c index e8d6d209..bd8e492e 100644 --- a/crypto/rsa/rsa_test.c +++ b/crypto/rsa/rsa_test.c @@ -276,6 +276,63 @@ err: return ret; } +static int test_recover_crt_params() { + RSA *key1, *key2; + BIGNUM *e = BN_new(); + uint8_t buf[128]; + unsigned buf_len = sizeof(buf); + const uint8_t kDummyHash[16] = {0}; + unsigned i; + + BN_set_word(e, RSA_F4); + + ERR_clear_error(); + + for (i = 0; i < 1; i++) { + key1 = RSA_new(); + if (!RSA_generate_key_ex(key1, 512, e, NULL)) { + fprintf(stderr, "RSA_generate_key_ex failed.\n"); + BIO_print_errors_fp(stderr); + return 0; + } + + key2 = RSA_new(); + key2->n = BN_dup(key1->n); + key2->e = BN_dup(key1->e); + key2->d = BN_dup(key1->d); + RSA_free(key1); + + if (!RSA_recover_crt_params(key2)) { + fprintf(stderr, "RSA_recover_crt_params failed.\n"); + BIO_print_errors_fp(stderr); + return 0; + } + + if (RSA_size(key2) > buf_len) { + return 0; + } + + if (!RSA_sign(NID_md5, kDummyHash, sizeof(kDummyHash), buf, &buf_len, + key2)) { + fprintf(stderr, "RSA_sign failed with recovered key.\n"); + BIO_print_errors_fp(stderr); + return 0; + } + + if (!RSA_verify(NID_md5, kDummyHash, sizeof(kDummyHash), buf, buf_len, + key2)) { + fprintf(stderr, "RSA_verify failed with recovered key.\n"); + BIO_print_errors_fp(stderr); + return 0; + } + + RSA_free(key2); + } + + BN_free(e); + return 1; +} + int main(int argc, char *argv[]) { int err = 0; int v; @@ -380,7 +437,8 @@ int main(int argc, char *argv[]) { } if (err != 0 || - !test_only_d_given()) { + !test_only_d_given() || + !test_recover_crt_params()) { err = 1; }