Rearrange ECDSA implementation.
The extra computation in the "setup" half is a remnant of ECDSA_sign_setup, which was designed to amortize the message-independent portion of the signature. We don't care about this amortization, but we do want some control over k for testing. Instead, have ecdsa_sign_impl take k and do the rest. In doing so, this lets us compute m and kinv slightly later, which means fewer temporaries are needed. Bug: 391 Change-Id: I398004a5388ce5b7e839db6c36edf8464643d16f Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/45866 Commit-Queue: David Benjamin <davidben@google.com> Reviewed-by: Adam Langley <agl@google.com>
This commit is contained in:
committed by
CQ bot account: commit-bot@chromium.org
parent
e7b5675015
commit
fd68d7552b
@@ -198,65 +198,66 @@ int ECDSA_do_verify(const uint8_t *digest, size_t digest_len,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ecdsa_sign_setup(const EC_KEY *eckey, EC_SCALAR *out_kinv_mont,
|
||||
EC_SCALAR *out_r, const uint8_t *digest,
|
||||
size_t digest_len, const EC_SCALAR *priv_key) {
|
||||
static ECDSA_SIG *ecdsa_sign_impl(const EC_GROUP *group, int *out_retry,
|
||||
const EC_SCALAR *priv_key, const EC_SCALAR *k,
|
||||
const uint8_t *digest, size_t digest_len) {
|
||||
*out_retry = 0;
|
||||
|
||||
// Check that the size of the group order is FIPS compliant (FIPS 186-4
|
||||
// B.5.2).
|
||||
const EC_GROUP *group = EC_KEY_get0_group(eckey);
|
||||
const BIGNUM *order = EC_GROUP_get0_order(group);
|
||||
if (BN_num_bits(order) < 160) {
|
||||
OPENSSL_PUT_ERROR(ECDSA, EC_R_INVALID_GROUP_ORDER);
|
||||
return 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int ret = 0;
|
||||
EC_SCALAR k;
|
||||
// Compute r, the x-coordinate of k * generator.
|
||||
EC_RAW_POINT tmp_point;
|
||||
do {
|
||||
// Include the private key and message digest in the k generation.
|
||||
if (eckey->fixed_k != NULL) {
|
||||
if (!ec_bignum_to_scalar(group, &k, eckey->fixed_k)) {
|
||||
goto err;
|
||||
}
|
||||
if (ec_scalar_is_zero(group, &k)) {
|
||||
OPENSSL_PUT_ERROR(ECDSA, ERR_R_INTERNAL_ERROR);
|
||||
goto err;
|
||||
}
|
||||
} else {
|
||||
// Pass a SHA512 hash of the private key and digest as additional data
|
||||
// into the RBG. This is a hardening measure against entropy failure.
|
||||
OPENSSL_STATIC_ASSERT(SHA512_DIGEST_LENGTH >= 32,
|
||||
"additional_data is too large for SHA-512");
|
||||
SHA512_CTX sha;
|
||||
uint8_t additional_data[SHA512_DIGEST_LENGTH];
|
||||
SHA512_Init(&sha);
|
||||
SHA512_Update(&sha, priv_key->words, order->width * sizeof(BN_ULONG));
|
||||
SHA512_Update(&sha, digest, digest_len);
|
||||
SHA512_Final(additional_data, &sha);
|
||||
if (!ec_random_nonzero_scalar(group, &k, additional_data)) {
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
EC_SCALAR r;
|
||||
if (!ec_point_mul_scalar_base(group, &tmp_point, k) ||
|
||||
!ec_get_x_coordinate_as_scalar(group, &r, &tmp_point)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Compute k^-1 in the Montgomery domain. This is |ec_scalar_to_montgomery|
|
||||
// followed by |ec_scalar_inv0_montgomery|, but |ec_scalar_inv0_montgomery|
|
||||
// followed by |ec_scalar_from_montgomery| is equivalent and slightly more
|
||||
// efficient. Note k is non-zero, so the inverse must exist.
|
||||
ec_scalar_inv0_montgomery(group, out_kinv_mont, &k);
|
||||
ec_scalar_from_montgomery(group, out_kinv_mont, out_kinv_mont);
|
||||
if (ec_scalar_is_zero(group, &r)) {
|
||||
*out_retry = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Compute r, the x-coordinate of generator * k.
|
||||
if (!ec_point_mul_scalar_base(group, &tmp_point, &k) ||
|
||||
!ec_get_x_coordinate_as_scalar(group, out_r, &tmp_point)) {
|
||||
goto err;
|
||||
}
|
||||
} while (ec_scalar_is_zero(group, out_r));
|
||||
// s = priv_key * r. Note if only one parameter is in the Montgomery domain,
|
||||
// |ec_scalar_mod_mul_montgomery| will compute the answer in the normal
|
||||
// domain.
|
||||
EC_SCALAR s;
|
||||
ec_scalar_to_montgomery(group, &s, &r);
|
||||
ec_scalar_mul_montgomery(group, &s, priv_key, &s);
|
||||
|
||||
ret = 1;
|
||||
// s = m + priv_key * r.
|
||||
EC_SCALAR tmp;
|
||||
digest_to_scalar(group, &tmp, digest, digest_len);
|
||||
ec_scalar_add(group, &s, &s, &tmp);
|
||||
|
||||
err:
|
||||
OPENSSL_cleanse(&k, sizeof(k));
|
||||
// s = k^-1 * (m + priv_key * r). First, we compute k^-1 in the Montgomery
|
||||
// domain. This is |ec_scalar_to_montgomery| followed by
|
||||
// |ec_scalar_inv0_montgomery|, but |ec_scalar_inv0_montgomery| followed by
|
||||
// |ec_scalar_from_montgomery| is equivalent and slightly more efficient.
|
||||
// Then, as above, only one parameter is in the Montgomery domain, so the
|
||||
// result is in the normal domain. Finally, note k is non-zero (or computing r
|
||||
// would fail), so the inverse must exist.
|
||||
ec_scalar_inv0_montgomery(group, &tmp, k); // tmp = k^-1 R^2
|
||||
ec_scalar_from_montgomery(group, &tmp, &tmp); // tmp = k^-1 R
|
||||
ec_scalar_mul_montgomery(group, &s, &s, &tmp);
|
||||
if (ec_scalar_is_zero(group, &s)) {
|
||||
*out_retry = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ECDSA_SIG *ret = ECDSA_SIG_new();
|
||||
if (ret == NULL || //
|
||||
!bn_set_words(ret->r, r.words, order->width) ||
|
||||
!bn_set_words(ret->s, s.words, order->width)) {
|
||||
ECDSA_SIG_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -275,54 +276,37 @@ ECDSA_SIG *ECDSA_do_sign(const uint8_t *digest, size_t digest_len,
|
||||
const BIGNUM *order = EC_GROUP_get0_order(group);
|
||||
const EC_SCALAR *priv_key = &eckey->priv_key->scalar;
|
||||
|
||||
int ok = 0;
|
||||
ECDSA_SIG *ret = ECDSA_SIG_new();
|
||||
EC_SCALAR kinv_mont, r_mont, s, m, tmp;
|
||||
if (ret == NULL) {
|
||||
OPENSSL_PUT_ERROR(ECDSA, ERR_R_MALLOC_FAILURE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
digest_to_scalar(group, &m, digest, digest_len);
|
||||
for (;;) {
|
||||
if (!ecdsa_sign_setup(eckey, &kinv_mont, &r_mont, digest, digest_len,
|
||||
priv_key) ||
|
||||
!bn_set_words(ret->r, r_mont.words, order->width)) {
|
||||
goto err;
|
||||
EC_SCALAR k;
|
||||
if (eckey->fixed_k != NULL) {
|
||||
if (!ec_bignum_to_scalar(group, &k, eckey->fixed_k)) {
|
||||
return NULL;
|
||||
}
|
||||
if (ec_scalar_is_zero(group, &k)) {
|
||||
OPENSSL_PUT_ERROR(ECDSA, ERR_R_INTERNAL_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
// Pass a SHA512 hash of the private key and digest as additional data
|
||||
// into the RBG. This is a hardening measure against entropy failure.
|
||||
OPENSSL_STATIC_ASSERT(SHA512_DIGEST_LENGTH >= 32,
|
||||
"additional_data is too large for SHA-512");
|
||||
SHA512_CTX sha;
|
||||
uint8_t additional_data[SHA512_DIGEST_LENGTH];
|
||||
SHA512_Init(&sha);
|
||||
SHA512_Update(&sha, priv_key->words, order->width * sizeof(BN_ULONG));
|
||||
SHA512_Update(&sha, digest, digest_len);
|
||||
SHA512_Final(additional_data, &sha);
|
||||
if (!ec_random_nonzero_scalar(group, &k, additional_data)) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Compute priv_key * r (mod order). Note if only one parameter is in the
|
||||
// Montgomery domain, |ec_scalar_mod_mul_montgomery| will compute the answer
|
||||
// in the normal domain.
|
||||
ec_scalar_to_montgomery(group, &r_mont, &r_mont);
|
||||
ec_scalar_mul_montgomery(group, &s, priv_key, &r_mont);
|
||||
|
||||
// Compute tmp = m + priv_key * r.
|
||||
ec_scalar_add(group, &tmp, &m, &s);
|
||||
|
||||
// Finally, multiply s by k^-1. That was retained in Montgomery form, so the
|
||||
// same technique as the previous multiplication works.
|
||||
ec_scalar_mul_montgomery(group, &s, &tmp, &kinv_mont);
|
||||
if (!bn_set_words(ret->s, s.words, order->width)) {
|
||||
goto err;
|
||||
}
|
||||
if (!BN_is_zero(ret->s)) {
|
||||
// s != 0 => we have a valid signature
|
||||
break;
|
||||
int retry;
|
||||
ECDSA_SIG *sig =
|
||||
ecdsa_sign_impl(group, &retry, priv_key, &k, digest, digest_len);
|
||||
if (sig != NULL || !retry) {
|
||||
return sig;
|
||||
}
|
||||
}
|
||||
|
||||
ok = 1;
|
||||
|
||||
err:
|
||||
if (!ok) {
|
||||
ECDSA_SIG_free(ret);
|
||||
ret = NULL;
|
||||
}
|
||||
OPENSSL_cleanse(&kinv_mont, sizeof(kinv_mont));
|
||||
OPENSSL_cleanse(&r_mont, sizeof(r_mont));
|
||||
OPENSSL_cleanse(&s, sizeof(s));
|
||||
OPENSSL_cleanse(&tmp, sizeof(tmp));
|
||||
OPENSSL_cleanse(&m, sizeof(m));
|
||||
return ret;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user