Add ECDSA nonce-testing functions.
There are a few places where it is useful to run ECDSA with a specified nonce: - An ECDSA KAT in the module self-check - Unit tests for particular test vectors - Fuzzing the implementation (requested by the cryptofuzz project) This replaces the fixed_k machinery with a separate function. Although they are effectively the same, I've used two different functions. One is internal and only used in the module self-check. The other is exported for unit tests and cryptofuzz but marked with a for_testing. (Chromium's presubmits flag uses of "for_testing" functions outside of unit tests. The KAT version isn't in a test per se, so it's a separate function.) Bug: 391 Change-Id: I0f764d89bf0ac2081307e1079623d508fb0f2df7 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/45867 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
fd68d7552b
commit
409ea2837d
@@ -171,7 +171,6 @@ void EC_KEY_free(EC_KEY *r) {
|
||||
EC_GROUP_free(r->group);
|
||||
EC_POINT_free(r->pub_key);
|
||||
ec_wrapped_scalar_free(r->priv_key);
|
||||
BN_free(r->fixed_k);
|
||||
|
||||
CRYPTO_free_ex_data(g_ec_ex_data_class_bss_get(), r, &r->ex_data);
|
||||
|
||||
|
||||
@@ -729,10 +729,6 @@ struct ec_key_st {
|
||||
EC_POINT *pub_key;
|
||||
EC_WRAPPED_SCALAR *priv_key;
|
||||
|
||||
// fixed_k may contain a specific value of 'k', to be used in ECDSA signing.
|
||||
// This is only for the FIPS power-on tests.
|
||||
BIGNUM *fixed_k;
|
||||
|
||||
unsigned int enc_flag;
|
||||
point_conversion_form_t conv_form;
|
||||
|
||||
|
||||
@@ -61,9 +61,10 @@
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/type_check.h>
|
||||
|
||||
#include "../../internal.h"
|
||||
#include "../bn/internal.h"
|
||||
#include "../ec/internal.h"
|
||||
#include "../../internal.h"
|
||||
#include "internal.h"
|
||||
|
||||
|
||||
// digest_to_scalar interprets |digest_len| bytes from |digest| as a scalar for
|
||||
@@ -261,6 +262,41 @@ static ECDSA_SIG *ecdsa_sign_impl(const EC_GROUP *group, int *out_retry,
|
||||
return ret;
|
||||
}
|
||||
|
||||
ECDSA_SIG *ecdsa_sign_with_nonce_for_known_answer_test(const uint8_t *digest,
|
||||
size_t digest_len,
|
||||
const EC_KEY *eckey,
|
||||
const uint8_t *nonce,
|
||||
size_t nonce_len) {
|
||||
if (eckey->ecdsa_meth && eckey->ecdsa_meth->sign) {
|
||||
OPENSSL_PUT_ERROR(ECDSA, ECDSA_R_NOT_IMPLEMENTED);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const EC_GROUP *group = EC_KEY_get0_group(eckey);
|
||||
if (group == NULL || eckey->priv_key == NULL) {
|
||||
OPENSSL_PUT_ERROR(ECDSA, ERR_R_PASSED_NULL_PARAMETER);
|
||||
return NULL;
|
||||
}
|
||||
const EC_SCALAR *priv_key = &eckey->priv_key->scalar;
|
||||
|
||||
EC_SCALAR k;
|
||||
if (!ec_scalar_from_bytes(group, &k, nonce, nonce_len)) {
|
||||
return NULL;
|
||||
}
|
||||
int retry_ignored;
|
||||
return ecdsa_sign_impl(group, &retry_ignored, priv_key, &k, digest,
|
||||
digest_len);
|
||||
}
|
||||
|
||||
// This function is only exported for testing and is not called in production
|
||||
// code.
|
||||
ECDSA_SIG *ECDSA_sign_with_nonce_and_leak_private_key_for_testing(
|
||||
const uint8_t *digest, size_t digest_len, const EC_KEY *eckey,
|
||||
const uint8_t *nonce, size_t nonce_len) {
|
||||
return ecdsa_sign_with_nonce_for_known_answer_test(digest, digest_len, eckey,
|
||||
nonce, nonce_len);
|
||||
}
|
||||
|
||||
ECDSA_SIG *ECDSA_do_sign(const uint8_t *digest, size_t digest_len,
|
||||
const EC_KEY *eckey) {
|
||||
if (eckey->ecdsa_meth && eckey->ecdsa_meth->sign) {
|
||||
@@ -276,30 +312,21 @@ 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;
|
||||
|
||||
// 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);
|
||||
|
||||
for (;;) {
|
||||
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;
|
||||
}
|
||||
if (!ec_random_nonzero_scalar(group, &k, additional_data)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int retry;
|
||||
|
||||
@@ -434,8 +434,8 @@ TEST(ECDSATest, SignTestVectors) {
|
||||
ASSERT_TRUE(x);
|
||||
bssl::UniquePtr<BIGNUM> y = GetBIGNUM(t, "Y");
|
||||
ASSERT_TRUE(y);
|
||||
bssl::UniquePtr<BIGNUM> k = GetBIGNUM(t, "K");
|
||||
ASSERT_TRUE(k);
|
||||
std::vector<uint8_t> k;
|
||||
ASSERT_TRUE(t->GetBytes(&k, "K"));
|
||||
bssl::UniquePtr<BIGNUM> r = GetBIGNUM(t, "R");
|
||||
ASSERT_TRUE(r);
|
||||
bssl::UniquePtr<BIGNUM> s = GetBIGNUM(t, "S");
|
||||
@@ -454,10 +454,9 @@ TEST(ECDSATest, SignTestVectors) {
|
||||
ASSERT_TRUE(EC_KEY_set_public_key(key.get(), pub_key.get()));
|
||||
ASSERT_TRUE(EC_KEY_check_key(key.get()));
|
||||
|
||||
// Set the fixed k for testing purposes.
|
||||
key->fixed_k = k.release();
|
||||
bssl::UniquePtr<ECDSA_SIG> sig(
|
||||
ECDSA_do_sign(digest.data(), digest.size(), key.get()));
|
||||
ECDSA_sign_with_nonce_and_leak_private_key_for_testing(
|
||||
digest.data(), digest.size(), key.get(), k.data(), k.size()));
|
||||
ASSERT_TRUE(sig);
|
||||
|
||||
EXPECT_EQ(0, BN_cmp(r.get(), sig->r));
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/* Copyright (c) 2021, Google Inc.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
|
||||
|
||||
#ifndef OPENSSL_HEADER_CRYPTO_FIPSMODULE_ECDSA_INTERNAL_H
|
||||
#define OPENSSL_HEADER_CRYPTO_FIPSMODULE_ECDSA_INTERNAL_H
|
||||
|
||||
#include <openssl/base.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
// ecdsa_sign_with_nonce_for_known_answer_test behaves like |ECDSA_do_sign| but
|
||||
// takes a fixed nonce. This function is used as part of known-answer tests in
|
||||
// the FIPS module.
|
||||
ECDSA_SIG *ecdsa_sign_with_nonce_for_known_answer_test(const uint8_t *digest,
|
||||
size_t digest_len,
|
||||
const EC_KEY *eckey,
|
||||
const uint8_t *nonce,
|
||||
size_t nonce_len);
|
||||
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // OPENSSL_HEADER_CRYPTO_FIPSMODULE_ECDSA_INTERNAL_H
|
||||
@@ -32,6 +32,7 @@
|
||||
|
||||
#include "../../internal.h"
|
||||
#include "../ec/internal.h"
|
||||
#include "../ecdsa/internal.h"
|
||||
#include "../rand/internal.h"
|
||||
#include "../tls/internal.h"
|
||||
|
||||
@@ -727,14 +728,12 @@ int boringssl_fips_self_test(
|
||||
// ECDSA Sign/Verify KAT
|
||||
|
||||
// The 'k' value for ECDSA is fixed to avoid an entropy draw.
|
||||
ec_key->fixed_k = BN_new();
|
||||
if (ec_key->fixed_k == NULL ||
|
||||
!BN_set_word(ec_key->fixed_k, 42)) {
|
||||
fprintf(stderr, "Out of memory\n");
|
||||
goto err;
|
||||
}
|
||||
uint8_t ecdsa_k[32] = {0};
|
||||
ecdsa_k[31] = 42;
|
||||
|
||||
sig = ECDSA_do_sign(kPlaintextSHA256, sizeof(kPlaintextSHA256), ec_key);
|
||||
sig = ecdsa_sign_with_nonce_for_known_answer_test(
|
||||
kPlaintextSHA256, sizeof(kPlaintextSHA256), ec_key, ecdsa_k,
|
||||
sizeof(ecdsa_k));
|
||||
|
||||
uint8_t ecdsa_r_bytes[sizeof(kECDSASigR)];
|
||||
uint8_t ecdsa_s_bytes[sizeof(kECDSASigS)];
|
||||
|
||||
@@ -162,6 +162,25 @@ OPENSSL_EXPORT int ECDSA_SIG_to_bytes(uint8_t **out_bytes, size_t *out_len,
|
||||
OPENSSL_EXPORT size_t ECDSA_SIG_max_len(size_t order_len);
|
||||
|
||||
|
||||
// Testing-only functions.
|
||||
|
||||
// ECDSA_sign_with_nonce_and_leak_private_key_for_testing behaves like
|
||||
// |ECDSA_do_sign| but uses |nonce| for the ECDSA nonce 'k', instead of a random
|
||||
// value. |nonce| is interpreted as a big-endian integer. It must be reduced
|
||||
// modulo the group order and padded with zeros up to |BN_num_bytes(order)|
|
||||
// bytes.
|
||||
//
|
||||
// WARNING: This function is only exported for testing purposes, when using test
|
||||
// vectors or fuzzing strategies. It must not be used outside tests and may leak
|
||||
// any private keys it is used with.
|
||||
OPENSSL_EXPORT ECDSA_SIG *
|
||||
ECDSA_sign_with_nonce_and_leak_private_key_for_testing(const uint8_t *digest,
|
||||
size_t digest_len,
|
||||
const EC_KEY *eckey,
|
||||
const uint8_t *nonce,
|
||||
size_t nonce_len);
|
||||
|
||||
|
||||
// Deprecated functions.
|
||||
|
||||
// d2i_ECDSA_SIG parses an ASN.1, DER-encoded, signature from |len| bytes at
|
||||
|
||||
Reference in New Issue
Block a user