Ver código fonte

Add function to recover RSA CRT params.

Some RSA private keys are specified with only n, e and d. Although we
can use these keys directly, it's nice to have a uniform representation
that includes the precomputed CRT values. This change adds a function
that can recover the primes from a minimal private key of that form.
kris/onging/CECPQ3_patch15
Adam Langley 10 anos atrás
pai
commit
409766d218
8 arquivos alterados com 348 adições e 2 exclusões
  1. +8
    -0
      crypto/bn/bn.h
  2. +2
    -0
      crypto/bn/bn_error.c
  3. +51
    -1
      crypto/bn/bn_test.c
  4. +75
    -0
      crypto/bn/sqrt.c
  5. +138
    -0
      crypto/rsa/rsa.c
  6. +11
    -0
      crypto/rsa/rsa.h
  7. +4
    -0
      crypto/rsa/rsa_error.c
  8. +59
    -1
      crypto/rsa/rsa_test.c

+ 8
- 0
crypto/bn/bn.h Ver arquivo

@@ -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 */

+ 2
- 0
crypto/bn/bn_error.c Ver arquivo

@@ -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"},


+ 51
- 1
crypto/bn/bn_test.c Ver arquivo

@@ -69,9 +69,10 @@

#include <stdio.h>

#include <openssl/bio.h>
#include <openssl/bn.h>
#include <openssl/err.h>
#include <openssl/bio.h>
#include <openssl/mem.h>

#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;
}

+ 75
- 0
crypto/bn/sqrt.c Ver arquivo

@@ -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;
}

+ 138
- 0
crypto/rsa/rsa.c Ver arquivo

@@ -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;
}

+ 11
- 0
crypto/rsa/rsa.h Ver arquivo

@@ -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 */

+ 4
- 0
crypto/rsa/rsa_error.c Ver arquivo

@@ -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"},


+ 59
- 1
crypto/rsa/rsa_test.c Ver arquivo

@@ -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;
}



Carregando…
Cancelar
Salvar