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>
diff --git a/crypto/fipsmodule/ec/ec_key.c b/crypto/fipsmodule/ec/ec_key.c index bc09e0e..7a6daab 100644 --- a/crypto/fipsmodule/ec/ec_key.c +++ b/crypto/fipsmodule/ec/ec_key.c
@@ -171,7 +171,6 @@ 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);
diff --git a/crypto/fipsmodule/ec/internal.h b/crypto/fipsmodule/ec/internal.h index 18aabb0..289c3aa 100644 --- a/crypto/fipsmodule/ec/internal.h +++ b/crypto/fipsmodule/ec/internal.h
@@ -729,10 +729,6 @@ 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;
diff --git a/crypto/fipsmodule/ecdsa/ecdsa.c b/crypto/fipsmodule/ecdsa/ecdsa.c index e519a69..591f1bc 100644 --- a/crypto/fipsmodule/ecdsa/ecdsa.c +++ b/crypto/fipsmodule/ecdsa/ecdsa.c
@@ -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 @@ 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 @@ 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;
diff --git a/crypto/fipsmodule/ecdsa/ecdsa_test.cc b/crypto/fipsmodule/ecdsa/ecdsa_test.cc index 95e26cf..18fdb83 100644 --- a/crypto/fipsmodule/ecdsa/ecdsa_test.cc +++ b/crypto/fipsmodule/ecdsa/ecdsa_test.cc
@@ -434,8 +434,8 @@ 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 @@ 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));
diff --git a/crypto/fipsmodule/ecdsa/internal.h b/crypto/fipsmodule/ecdsa/internal.h new file mode 100644 index 0000000..5115dfa --- /dev/null +++ b/crypto/fipsmodule/ecdsa/internal.h
@@ -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
diff --git a/crypto/fipsmodule/self_check/self_check.c b/crypto/fipsmodule/self_check/self_check.c index 638500b..39570bd 100644 --- a/crypto/fipsmodule/self_check/self_check.c +++ b/crypto/fipsmodule/self_check/self_check.c
@@ -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 @@ // 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)];
diff --git a/include/openssl/ecdsa.h b/include/openssl/ecdsa.h index 9cd19a5..08950e3 100644 --- a/include/openssl/ecdsa.h +++ b/include/openssl/ecdsa.h
@@ -162,6 +162,25 @@ 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