chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

View File

@@ -0,0 +1,416 @@
// Copyright (c) 1998-2005 The OpenSSL Project. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
#include <openssl/ecdsa.h>
#include <assert.h>
#include <string.h>
#include <openssl/bn.h>
#include <openssl/bytestring.h>
#include <openssl/err.h>
#include <openssl/mem.h>
#include <openssl/sha.h>
#include <openssl/type_check.h>
#include "../../internal.h"
#include "../bn/internal.h"
#include "../ec/internal.h"
#include "internal.h"
// digest_to_scalar interprets |digest_len| bytes from |digest| as a scalar for
// ECDSA.
static void digest_to_scalar(const EC_GROUP *group, EC_SCALAR *out,
const uint8_t *digest, size_t digest_len) {
const BIGNUM *order = EC_GROUP_get0_order(group);
size_t num_bits = BN_num_bits(order);
// Need to truncate digest if it is too long: first truncate whole bytes.
size_t num_bytes = (num_bits + 7) / 8;
if (digest_len > num_bytes) {
digest_len = num_bytes;
}
bn_big_endian_to_words(out->words, order->width, digest, digest_len);
// If it is still too long, truncate remaining bits with a shift.
if (8 * digest_len > num_bits) {
bn_rshift_words(out->words, out->words, 8 - (num_bits & 0x7), order->width);
}
// |out| now has the same bit width as |order|, but this only bounds by
// 2*|order|. Subtract the order if out of range.
//
// Montgomery multiplication accepts the looser bounds, so this isn't strictly
// necessary, but it is a cleaner abstraction and has no performance impact.
BN_ULONG tmp[EC_MAX_WORDS];
bn_reduce_once_in_place(out->words, 0 /* no carry */, order->d, tmp,
order->width);
}
ECDSA_SIG *ECDSA_SIG_new(void) {
ECDSA_SIG *sig = OPENSSL_malloc(sizeof(ECDSA_SIG));
if (sig == NULL) {
return NULL;
}
sig->r = BN_new();
sig->s = BN_new();
if (sig->r == NULL || sig->s == NULL) {
ECDSA_SIG_free(sig);
return NULL;
}
return sig;
}
void ECDSA_SIG_free(ECDSA_SIG *sig) {
if (sig == NULL) {
return;
}
BN_free(sig->r);
BN_free(sig->s);
OPENSSL_free(sig);
}
const BIGNUM *ECDSA_SIG_get0_r(const ECDSA_SIG *sig) {
return sig->r;
}
const BIGNUM *ECDSA_SIG_get0_s(const ECDSA_SIG *sig) {
return sig->s;
}
void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **out_r,
const BIGNUM **out_s) {
if (out_r != NULL) {
*out_r = sig->r;
}
if (out_s != NULL) {
*out_s = sig->s;
}
}
int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s) {
if (r == NULL || s == NULL) {
return 0;
}
BN_free(sig->r);
BN_free(sig->s);
sig->r = r;
sig->s = s;
return 1;
}
int ecdsa_do_verify_no_self_test(const uint8_t *digest, size_t digest_len,
const ECDSA_SIG *sig, const EC_KEY *eckey) {
const EC_GROUP *group = EC_KEY_get0_group(eckey);
const EC_POINT *pub_key = EC_KEY_get0_public_key(eckey);
if (group == NULL || pub_key == NULL || sig == NULL) {
OPENSSL_PUT_ERROR(ECDSA, ECDSA_R_MISSING_PARAMETERS);
return 0;
}
EC_SCALAR r, s, u1, u2, s_inv_mont, m;
if (BN_is_zero(sig->r) ||
!ec_bignum_to_scalar(group, &r, sig->r) ||
BN_is_zero(sig->s) ||
!ec_bignum_to_scalar(group, &s, sig->s)) {
OPENSSL_PUT_ERROR(ECDSA, ECDSA_R_BAD_SIGNATURE);
return 0;
}
// s_inv_mont = s^-1 in the Montgomery domain.
if (!ec_scalar_to_montgomery_inv_vartime(group, &s_inv_mont, &s)) {
OPENSSL_PUT_ERROR(ECDSA, ERR_R_INTERNAL_ERROR);
return 0;
}
// u1 = m * s^-1 mod order
// u2 = r * s^-1 mod order
//
// |s_inv_mont| is in Montgomery form while |m| and |r| are not, so |u1| and
// |u2| will be taken out of Montgomery form, as desired.
digest_to_scalar(group, &m, digest, digest_len);
ec_scalar_mul_montgomery(group, &u1, &m, &s_inv_mont);
ec_scalar_mul_montgomery(group, &u2, &r, &s_inv_mont);
EC_JACOBIAN point;
if (!ec_point_mul_scalar_public(group, &point, &u1, &pub_key->raw, &u2)) {
OPENSSL_PUT_ERROR(ECDSA, ERR_R_EC_LIB);
return 0;
}
if (!ec_cmp_x_coordinate(group, &point, &r)) {
OPENSSL_PUT_ERROR(ECDSA, ECDSA_R_MISMATCHED_SIGNATURE);
return 0;
}
return 1;
}
int ECDSA_do_verify(const uint8_t *digest, size_t digest_len,
const ECDSA_SIG *sig, const EC_KEY *eckey) {
boringssl_ensure_ecc_self_test();
return ecdsa_do_verify_no_self_test(digest, digest_len, sig, eckey);
}
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 BIGNUM *order = EC_GROUP_get0_order(group);
if (BN_num_bits(order) < 160) {
OPENSSL_PUT_ERROR(ECDSA, EC_R_INVALID_GROUP_ORDER);
return NULL;
}
// Compute r, the x-coordinate of k * generator.
EC_JACOBIAN tmp_point;
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;
}
if (constant_time_declassify_int(ec_scalar_is_zero(group, &r))) {
*out_retry = 1;
return NULL;
}
// 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);
// s = m + priv_key * r.
EC_SCALAR tmp;
digest_to_scalar(group, &tmp, digest, digest_len);
ec_scalar_add(group, &s, &s, &tmp);
// 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 (constant_time_declassify_int(ec_scalar_is_zero(group, &s))) {
*out_retry = 1;
return NULL;
}
CONSTTIME_DECLASSIFY(r.words, sizeof(r.words));
CONSTTIME_DECLASSIFY(s.words, sizeof(r.words));
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;
}
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->eckey_method && eckey->eckey_method->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) {
boringssl_ensure_ecc_self_test();
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) {
boringssl_ensure_ecc_self_test();
if (eckey->eckey_method && eckey->eckey_method->sign_sig) {
return eckey->eckey_method->sign_sig(digest, (int)digest_len, NULL, NULL,
(EC_KEY *)eckey);
}
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;
}
// We have to avoid the underlying |SHA512_Final| services updating the
// indicator state, so we lock the state here.
FIPS_service_indicator_lock_state();
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);
FIPS_service_indicator_unlock_state();
// Cap iterations so callers who supply invalid values as custom groups do not
// infinite loop. This does not impact valid parameters (e.g. those covered by
// FIPS) because the probability of requiring even one retry is negligible,
// let alone 32.
static const int kMaxIterations = 32;
int iters = 0;
for (;;) {
EC_SCALAR k;
if (!ec_random_nonzero_scalar(group, &k, additional_data)) {
OPENSSL_cleanse(&k, sizeof(EC_SCALAR));
return NULL;
}
// TODO(davidben): Move this inside |ec_random_nonzero_scalar| or lower, so
// that all scalars we generate are, by default, secret.
CONSTTIME_SECRET(k.words, sizeof(k.words));
int retry;
ECDSA_SIG *sig =
ecdsa_sign_impl(group, &retry, priv_key, &k, digest, digest_len);
if (sig != NULL || !retry) {
OPENSSL_cleanse(&k, sizeof(EC_SCALAR));
return sig;
}
iters++;
if (iters > kMaxIterations) {
OPENSSL_cleanse(&k, sizeof(EC_SCALAR));
OPENSSL_PUT_ERROR(ECDSA, ECDSA_R_TOO_MANY_ITERATIONS);
return NULL;
}
}
}
// |ECDSA_sign| uses ASN1/CBB functionality, so it was previously placed in
// crypto/ecdsa_extra/ecdsa_asn1.c. It's now moved within the FIPS boundary for
// FIPS compliance.
int ECDSA_sign(int type, const uint8_t *digest, size_t digest_len, uint8_t *sig,
unsigned int *sig_len, const EC_KEY *eckey) {
if (eckey->eckey_method && eckey->eckey_method->sign) {
return eckey->eckey_method->sign(type, digest, (int)digest_len, sig, sig_len,
NULL, NULL,
(EC_KEY*) eckey /* cast away const */);
}
int ret = 0;
ECDSA_SIG *s = ECDSA_do_sign(digest, digest_len, eckey);
if (s == NULL) {
*sig_len = 0;
goto err;
}
CBB cbb;
CBB_init_fixed(&cbb, sig, ECDSA_size(eckey));
size_t len;
if (!ECDSA_SIG_marshal(&cbb, s) ||
!CBB_finish(&cbb, NULL, &len)) {
OPENSSL_PUT_ERROR(ECDSA, ECDSA_R_ENCODE_ERROR);
*sig_len = 0;
goto err;
}
*sig_len = (unsigned)len;
ret = 1;
err:
ECDSA_SIG_free(s);
return ret;
}
// |ECDSA_verify| uses ASN1/CBB functionality, so it was previously placed in
// crypto/evp_extra/ecdsa_asn1.c. It's now moved within the FIPS boundary for
// FIPS compliance.
int ECDSA_verify(int type, const uint8_t *digest, size_t digest_len,
const uint8_t *sig, size_t sig_len, const EC_KEY *eckey) {
ECDSA_SIG *s;
int ret = 0;
uint8_t *der = NULL;
// Decode the ECDSA signature.
s = ECDSA_SIG_from_bytes(sig, sig_len);
if (s == NULL) {
goto err;
}
// Defend against potential laxness in the DER parser.
size_t der_len;
if (!ECDSA_SIG_to_bytes(&der, &der_len, s) ||
der_len != sig_len || OPENSSL_memcmp(sig, der, sig_len) != 0) {
// This should never happen. crypto/bytestring is strictly DER.
OPENSSL_PUT_ERROR(ECDSA, ERR_R_INTERNAL_ERROR);
goto err;
}
ret = ECDSA_do_verify(digest, digest_len, s, eckey);
err:
OPENSSL_free(der);
ECDSA_SIG_free(s);
return ret;
}
ECDSA_SIG *ecdsa_digestsign_no_self_test(const EVP_MD *md, const uint8_t *input,
size_t in_len, const EC_KEY *eckey,
const uint8_t *nonce,
size_t nonce_len) {
uint8_t digest[EVP_MAX_MD_SIZE];
unsigned int digest_len = EVP_MAX_MD_SIZE;
if (!EVP_Digest(input, in_len, digest, &digest_len, md, NULL)) {
return 0;
}
return ecdsa_sign_with_nonce_for_known_answer_test(digest, digest_len, eckey,
nonce, nonce_len);
}
int ecdsa_digestverify_no_self_test(const EVP_MD *md, const uint8_t *input,
size_t in_len, const ECDSA_SIG *sig,
const EC_KEY *eckey){
uint8_t digest[EVP_MAX_MD_SIZE];
unsigned int digest_len = EVP_MAX_MD_SIZE;
if (!EVP_Digest(input, in_len, digest, &digest_len, md, NULL)) {
return 0;
}
return ecdsa_do_verify_no_self_test(digest, digest_len, sig, eckey);
}

View File

@@ -0,0 +1,433 @@
// Copyright (c) 1998-2005 The OpenSSL Project. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
#include <openssl/ecdsa.h>
#include <vector>
#include <gtest/gtest.h>
#include <openssl/bn.h>
#include <openssl/crypto.h>
#include <openssl/ec.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/mem.h>
#include <openssl/nid.h>
#include <openssl/rand.h>
#include "../ec/internal.h"
#include "../../test/file_test.h"
#include "../../test/test_util.h"
// Though we do not support secp160r1, it is reachable from the deprecated
// custom curve APIs and has some unique properties (n is larger than p with the
// difference crossing a word boundary on 32-bit), so test it explicitly.
static bssl::UniquePtr<EC_GROUP> NewSecp160r1Group() {
static const char kP[] = "ffffffffffffffffffffffffffffffff7fffffff";
static const char kA[] = "ffffffffffffffffffffffffffffffff7ffffffc";
static const char kB[] = "1c97befc54bd7a8b65acf89f81d4d4adc565fa45";
static const char kX[] = "4a96b5688ef573284664698968c38bb913cbfc82";
static const char kY[] = "23a628553168947d59dcc912042351377ac5fb32";
static const char kN[] = "0100000000000000000001f4c8f927aed3ca752257";
bssl::UniquePtr<BIGNUM> p = HexToBIGNUM(kP), a = HexToBIGNUM(kA),
b = HexToBIGNUM(kB), x = HexToBIGNUM(kX),
y = HexToBIGNUM(kY), n = HexToBIGNUM(kN);
if (!p || !a || !b || !x || !y || !n) {
return nullptr;
}
bssl::UniquePtr<EC_GROUP> group(
EC_GROUP_new_curve_GFp(p.get(), a.get(), b.get(), nullptr));
if (!group) {
return nullptr;
}
bssl::UniquePtr<EC_POINT> g(EC_POINT_new(group.get()));
if (!g ||
!EC_POINT_set_affine_coordinates_GFp(group.get(), g.get(), x.get(),
y.get(), nullptr) ||
!EC_GROUP_set_generator(group.get(), g.get(), n.get(), BN_value_one())) {
return nullptr;
}
return group;
}
enum API {
kEncodedAPI,
kRawAPI,
};
// VerifyECDSASig checks that verifying |ecdsa_sig| gives |expected_result|.
static void VerifyECDSASig(API api, const uint8_t *digest, size_t digest_len,
const ECDSA_SIG *ecdsa_sig, EC_KEY *eckey,
int expected_result) {
switch (api) {
case kEncodedAPI: {
uint8_t *der;
size_t der_len;
ASSERT_TRUE(ECDSA_SIG_to_bytes(&der, &der_len, ecdsa_sig));
bssl::UniquePtr<uint8_t> delete_der(der);
EXPECT_EQ(expected_result,
ECDSA_verify(0, digest, digest_len, der, der_len, eckey));
break;
}
case kRawAPI:
EXPECT_EQ(expected_result,
ECDSA_do_verify(digest, digest_len, ecdsa_sig, eckey));
break;
default:
FAIL() << "Unknown API type.";
}
}
// TestTamperedSig verifies that signature verification fails when a valid
// signature is tampered with. |ecdsa_sig| must be a valid signature, which will
// be modified.
static void TestTamperedSig(API api, const uint8_t *digest,
size_t digest_len, ECDSA_SIG *ecdsa_sig,
EC_KEY *eckey, const BIGNUM *order) {
SCOPED_TRACE(api);
// Modify a single byte of the signature: to ensure we don't
// garble the ASN1 structure, we read the raw signature and
// modify a byte in one of the bignums directly.
// Store the two BIGNUMs in raw_buf.
size_t r_len = BN_num_bytes(ecdsa_sig->r);
size_t s_len = BN_num_bytes(ecdsa_sig->s);
size_t bn_len = BN_num_bytes(order);
ASSERT_LE(r_len, bn_len);
ASSERT_LE(s_len, bn_len);
size_t buf_len = 2 * bn_len;
std::vector<uint8_t> raw_buf(buf_len);
// Pad the bignums with leading zeroes.
ASSERT_TRUE(BN_bn2bin_padded(raw_buf.data(), bn_len, ecdsa_sig->r));
ASSERT_TRUE(BN_bn2bin_padded(raw_buf.data() + bn_len, bn_len, ecdsa_sig->s));
// Modify a single byte in the buffer.
size_t offset = raw_buf[10] % buf_len;
uint8_t dirt = raw_buf[11] ? raw_buf[11] : 1;
raw_buf[offset] ^= dirt;
// Now read the BIGNUMs back in from raw_buf.
ASSERT_TRUE(BN_bin2bn(raw_buf.data(), bn_len, ecdsa_sig->r));
ASSERT_TRUE(BN_bin2bn(raw_buf.data() + bn_len, bn_len, ecdsa_sig->s));
VerifyECDSASig(api, digest, digest_len, ecdsa_sig, eckey, 0);
// Sanity check: Undo the modification and verify signature.
raw_buf[offset] ^= dirt;
ASSERT_TRUE(BN_bin2bn(raw_buf.data(), bn_len, ecdsa_sig->r));
ASSERT_TRUE(BN_bin2bn(raw_buf.data() + bn_len, bn_len, ecdsa_sig->s));
VerifyECDSASig(api, digest, digest_len, ecdsa_sig, eckey, 1);
}
TEST(ECDSATest, BuiltinCurves) {
// Fill digest values with some random data.
uint8_t digest[20], wrong_digest[20];
ASSERT_TRUE(RAND_bytes(digest, 20));
CONSTTIME_DECLASSIFY(digest, 20);
ASSERT_TRUE(RAND_bytes(wrong_digest, 20));
CONSTTIME_DECLASSIFY(wrong_digest, 20);
static const struct {
int nid;
const char *name;
} kCurves[] = {
{ NID_secp224r1, "secp224r1" },
{ NID_X9_62_prime256v1, "secp256r1" },
{ NID_secp384r1, "secp384r1" },
{ NID_secp521r1, "secp521r1" },
{ NID_secp160r1, "secp160r1" },
{ NID_secp256k1, "secp256k1" },
};
for (const auto &curve : kCurves) {
SCOPED_TRACE(curve.name);
bssl::UniquePtr<EC_GROUP> group;
if (curve.nid == NID_secp160r1) {
group = NewSecp160r1Group();
} else {
group.reset(EC_GROUP_new_by_curve_name(curve.nid));
}
ASSERT_TRUE(group);
const BIGNUM *order = EC_GROUP_get0_order(group.get());
// Create a new ECDSA key.
bssl::UniquePtr<EC_KEY> eckey(EC_KEY_new());
ASSERT_TRUE(eckey);
ASSERT_TRUE(EC_KEY_set_group(eckey.get(), group.get()));
ASSERT_TRUE(EC_KEY_generate_key(eckey.get()));
// Create a second key.
bssl::UniquePtr<EC_KEY> wrong_eckey(EC_KEY_new());
ASSERT_TRUE(wrong_eckey);
ASSERT_TRUE(EC_KEY_set_group(wrong_eckey.get(), group.get()));
ASSERT_TRUE(EC_KEY_generate_key(wrong_eckey.get()));
// Check the key.
EXPECT_TRUE(EC_KEY_check_key(eckey.get()));
bssl::UniquePtr<EVP_PKEY> ec_pkey(EVP_PKEY_new());
ASSERT_TRUE(ec_pkey);
ASSERT_TRUE(EVP_PKEY_set1_EC_KEY(ec_pkey.get(), eckey.get()));
bssl::UniquePtr<EVP_PKEY_CTX> ec_key_ctx(
EVP_PKEY_CTX_new(ec_pkey.get(), NULL));
ASSERT_TRUE(ec_key_ctx);
EXPECT_TRUE(EVP_PKEY_check(ec_key_ctx.get()));
EXPECT_TRUE(EVP_PKEY_public_check((ec_key_ctx.get())));
// Test ASN.1-encoded signatures.
// Create a signature.
std::vector<uint8_t> signature(ECDSA_size(eckey.get()));
unsigned sig_len;
ASSERT_TRUE(
ECDSA_sign(0, digest, 20, signature.data(), &sig_len, eckey.get()));
signature.resize(sig_len);
// ECDSA signing should be non-deterministic. This does not verify k is
// generated securely but at least checks it was randomized at all.
std::vector<uint8_t> signature2(ECDSA_size(eckey.get()));
ASSERT_TRUE(
ECDSA_sign(0, digest, 20, signature2.data(), &sig_len, eckey.get()));
signature2.resize(sig_len);
EXPECT_NE(Bytes(signature), Bytes(signature2));
// Verify the signature.
EXPECT_TRUE(ECDSA_verify(0, digest, 20, signature.data(), signature.size(),
eckey.get()));
// Verify the signature with the wrong key.
EXPECT_FALSE(ECDSA_verify(0, digest, 20, signature.data(), signature.size(),
wrong_eckey.get()));
ERR_clear_error();
// Verify the signature using the wrong digest.
EXPECT_FALSE(ECDSA_verify(0, wrong_digest, 20, signature.data(),
signature.size(), eckey.get()));
ERR_clear_error();
// Verify a truncated signature.
EXPECT_FALSE(ECDSA_verify(0, digest, 20, signature.data(),
signature.size() - 1, eckey.get()));
ERR_clear_error();
// Verify a tampered signature.
bssl::UniquePtr<ECDSA_SIG> ecdsa_sig(
ECDSA_SIG_from_bytes(signature.data(), signature.size()));
ASSERT_TRUE(ecdsa_sig);
TestTamperedSig(kEncodedAPI, digest, 20, ecdsa_sig.get(), eckey.get(),
order);
// Test ECDSA_SIG signing and verification.
// Create a signature.
ecdsa_sig.reset(ECDSA_do_sign(digest, 20, eckey.get()));
ASSERT_TRUE(ecdsa_sig);
// Verify the signature using the correct key.
EXPECT_TRUE(ECDSA_do_verify(digest, 20, ecdsa_sig.get(), eckey.get()));
// Verify the signature with the wrong key.
EXPECT_FALSE(
ECDSA_do_verify(digest, 20, ecdsa_sig.get(), wrong_eckey.get()));
ERR_clear_error();
// Verify the signature using the wrong digest.
EXPECT_FALSE(
ECDSA_do_verify(wrong_digest, 20, ecdsa_sig.get(), eckey.get()));
ERR_clear_error();
// Verify a tampered signature.
TestTamperedSig(kRawAPI, digest, 20, ecdsa_sig.get(), eckey.get(), order);
}
}
static size_t BitsToBytes(size_t bits) {
return (bits / 8) + (7 + (bits % 8)) / 8;
}
TEST(ECDSATest, MaxSigLen) {
static const size_t kBits[] = {224, 256, 384, 521, 10000};
for (size_t bits : kBits) {
SCOPED_TRACE(bits);
size_t order_len = BitsToBytes(bits);
// Create the largest possible |ECDSA_SIG| of the given constraints.
bssl::UniquePtr<ECDSA_SIG> sig(ECDSA_SIG_new());
ASSERT_TRUE(sig);
std::vector<uint8_t> bytes(order_len, 0xff);
ASSERT_TRUE(BN_bin2bn(bytes.data(), bytes.size(), sig->r));
ASSERT_TRUE(BN_bin2bn(bytes.data(), bytes.size(), sig->s));
// Serialize it.
uint8_t *der;
size_t der_len;
ASSERT_TRUE(ECDSA_SIG_to_bytes(&der, &der_len, sig.get()));
OPENSSL_free(der);
EXPECT_EQ(der_len, ECDSA_SIG_max_len(order_len));
}
}
static bssl::UniquePtr<EC_GROUP> GetCurve(FileTest *t, const char *key) {
std::string curve_name;
if (!t->GetAttribute(&curve_name, key)) {
return nullptr;
}
if (curve_name == "P-224") {
return bssl::UniquePtr<EC_GROUP>(const_cast<EC_GROUP *>(EC_group_p224()));
}
if (curve_name == "P-256") {
return bssl::UniquePtr<EC_GROUP>(const_cast<EC_GROUP *>(EC_group_p256()));
}
if (curve_name == "P-384") {
return bssl::UniquePtr<EC_GROUP>(const_cast<EC_GROUP *>(EC_group_p384()));
}
if (curve_name == "P-521") {
return bssl::UniquePtr<EC_GROUP>(const_cast<EC_GROUP *>(EC_group_p521()));
}
if (curve_name == "secp256k1") {
return bssl::UniquePtr<EC_GROUP>(EC_GROUP_new_by_curve_name(NID_secp256k1));
}
if (curve_name == "secp160r1") {
return NewSecp160r1Group();
}
ADD_FAILURE() << "Unknown curve: " << curve_name;
return nullptr;
}
static bssl::UniquePtr<EC_GROUP> MakeCustomClone(const EC_GROUP *group) {
bssl::UniquePtr<BN_CTX> ctx(BN_CTX_new());
bssl::UniquePtr<BIGNUM> p(BN_new()), a(BN_new()), b(BN_new()), x(BN_new()),
y(BN_new());
if (!ctx || !p || !a || !b || !x || !y ||
!EC_GROUP_get_curve_GFp(group, p.get(), a.get(), b.get(), ctx.get()) ||
!EC_POINT_get_affine_coordinates_GFp(
group, EC_GROUP_get0_generator(group), x.get(), y.get(), ctx.get())) {
return nullptr;
}
bssl::UniquePtr<EC_GROUP> ret(
EC_GROUP_new_curve_GFp(p.get(), a.get(), b.get(), ctx.get()));
if (!ret) {
return nullptr;
}
bssl::UniquePtr<EC_POINT> g(EC_POINT_new(ret.get()));
if (!g ||
!EC_POINT_set_affine_coordinates_GFp(ret.get(), g.get(), x.get(), y.get(),
ctx.get()) ||
!EC_GROUP_set_generator(ret.get(), g.get(), EC_GROUP_get0_order(group),
BN_value_one())) {
return nullptr;
}
return ret;
}
static bssl::UniquePtr<BIGNUM> GetBIGNUM(FileTest *t, const char *key) {
std::vector<uint8_t> bytes;
if (!t->GetBytes(&bytes, key)) {
return nullptr;
}
return bssl::UniquePtr<BIGNUM>(BN_bin2bn(bytes.data(), bytes.size(), nullptr));
}
TEST(ECDSATest, VerifyTestVectors) {
FileTestGTest("crypto/fipsmodule/ecdsa/ecdsa_verify_tests.txt",
[](FileTest *t) {
for (bool custom_group : {false, true}) {
SCOPED_TRACE(custom_group);
bssl::UniquePtr<EC_GROUP> group = GetCurve(t, "Curve");
ASSERT_TRUE(group);
if (custom_group) {
group = MakeCustomClone(group.get());
ASSERT_TRUE(group);
}
bssl::UniquePtr<BIGNUM> x = GetBIGNUM(t, "X");
ASSERT_TRUE(x);
bssl::UniquePtr<BIGNUM> y = GetBIGNUM(t, "Y");
ASSERT_TRUE(y);
bssl::UniquePtr<BIGNUM> r = GetBIGNUM(t, "R");
ASSERT_TRUE(r);
bssl::UniquePtr<BIGNUM> s = GetBIGNUM(t, "S");
ASSERT_TRUE(s);
std::vector<uint8_t> digest;
ASSERT_TRUE(t->GetBytes(&digest, "Digest"));
bssl::UniquePtr<EC_KEY> key(EC_KEY_new());
ASSERT_TRUE(key);
bssl::UniquePtr<EC_POINT> pub_key(EC_POINT_new(group.get()));
ASSERT_TRUE(pub_key);
bssl::UniquePtr<ECDSA_SIG> sig(ECDSA_SIG_new());
ASSERT_TRUE(sig);
ASSERT_TRUE(EC_KEY_set_group(key.get(), group.get()));
ASSERT_TRUE(EC_POINT_set_affine_coordinates_GFp(
group.get(), pub_key.get(), x.get(), y.get(), nullptr));
ASSERT_TRUE(EC_KEY_set_public_key(key.get(), pub_key.get()));
ASSERT_TRUE(BN_copy(sig->r, r.get()));
ASSERT_TRUE(BN_copy(sig->s, s.get()));
EXPECT_EQ(
t->HasAttribute("Invalid") ? 0 : 1,
ECDSA_do_verify(digest.data(), digest.size(), sig.get(), key.get()));
}
});
}
TEST(ECDSATest, SignTestVectors) {
FileTestGTest("crypto/fipsmodule/ecdsa/ecdsa_sign_tests.txt",
[](FileTest *t) {
for (bool custom_group : {false, true}) {
SCOPED_TRACE(custom_group);
bssl::UniquePtr<EC_GROUP> group = GetCurve(t, "Curve");
ASSERT_TRUE(group);
if (custom_group) {
group = MakeCustomClone(group.get());
ASSERT_TRUE(group);
}
bssl::UniquePtr<BIGNUM> priv_key = GetBIGNUM(t, "Private");
ASSERT_TRUE(priv_key);
bssl::UniquePtr<BIGNUM> x = GetBIGNUM(t, "X");
ASSERT_TRUE(x);
bssl::UniquePtr<BIGNUM> y = GetBIGNUM(t, "Y");
ASSERT_TRUE(y);
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");
ASSERT_TRUE(s);
std::vector<uint8_t> digest;
ASSERT_TRUE(t->GetBytes(&digest, "Digest"));
bssl::UniquePtr<EC_KEY> key(EC_KEY_new());
ASSERT_TRUE(key);
bssl::UniquePtr<EC_POINT> pub_key(EC_POINT_new(group.get()));
ASSERT_TRUE(pub_key);
ASSERT_TRUE(EC_KEY_set_group(key.get(), group.get()));
ASSERT_TRUE(EC_KEY_set_private_key(key.get(), priv_key.get()));
ASSERT_TRUE(EC_POINT_set_affine_coordinates_GFp(
group.get(), pub_key.get(), x.get(), y.get(), nullptr));
ASSERT_TRUE(EC_KEY_set_public_key(key.get(), pub_key.get()));
ASSERT_TRUE(EC_KEY_check_key(key.get()));
bssl::UniquePtr<EVP_PKEY> ec_pkey(EVP_PKEY_new());
ASSERT_TRUE(ec_pkey);
ASSERT_TRUE(EVP_PKEY_set1_EC_KEY(ec_pkey.get(), key.get()));
bssl::UniquePtr<EVP_PKEY_CTX> ec_key_ctx(
EVP_PKEY_CTX_new(ec_pkey.get(), NULL));
ASSERT_TRUE(ec_key_ctx);
EXPECT_TRUE(EVP_PKEY_check(ec_key_ctx.get()));
EXPECT_TRUE(EVP_PKEY_public_check((ec_key_ctx.get())));
bssl::UniquePtr<ECDSA_SIG> sig(
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));
EXPECT_EQ(0, BN_cmp(s.get(), sig->s));
}
});
}

View File

@@ -0,0 +1,51 @@
// Copyright (c) 2021, Google Inc.
// SPDX-License-Identifier: ISC
#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);
// ecdsa_do_verify_no_self_test does the same as |ECDSA_do_verify|, but doesn't
// try to run the self-test first. This is for use in the self tests themselves,
// to prevent an infinite loop.
int ecdsa_do_verify_no_self_test(const uint8_t *digest, size_t digest_len,
const ECDSA_SIG *sig, const EC_KEY *eckey);
// ecdsa_digestsign_no_self_test calculates the digest and calls
// |ecdsa_sign_with_nonce_for_known_answer_test|, which doesn't try to run the
// self-test first. This is for use in the self tests themselves, to prevent
// an infinite loop.
ECDSA_SIG *ecdsa_digestsign_no_self_test(const EVP_MD *md, const uint8_t *input,
size_t in_len, const EC_KEY *eckey,
const uint8_t *nonce,
size_t nonce_len);
// ecdsa_digestverify_no_self_test calculates the digest and calls
// |ecdsa_do_verify_no_self_test|, which doesn't try to run the self-test
// first. This is for use in the self tests themselves, to prevent an infinite
// loop.
int ecdsa_digestverify_no_self_test(const EVP_MD *md, const uint8_t *input,
size_t in_len, const ECDSA_SIG *sig,
const EC_KEY *eckey);
#if defined(__cplusplus)
}
#endif
#endif // OPENSSL_HEADER_CRYPTO_FIPSMODULE_ECDSA_INTERNAL_H