Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crypto/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,7 @@ add_library(
ecdh_extra/ecdh_extra.c
ecdsa_extra/ecdsa_asn1.c
ec_extra/ec_asn1.c
ec_extra/ec_brainpool.c
ec_extra/ec_derive.c
ec_extra/hash_to_curve.c
err/err.c
Expand Down
7 changes: 5 additions & 2 deletions crypto/ec_extra/ec_asn1.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,11 @@ static const CBS_ASN1_TAG kPublicKeyTag =
// acceptable groups, so parsers don't have to pull in all four.
typedef const EC_GROUP *(*ec_group_func)(void);
static const ec_group_func kAllGroups[] = {
&EC_group_p224, &EC_group_p256, &EC_group_p384,
&EC_group_p521, &EC_group_secp256k1,
&EC_group_p224, &EC_group_p256,
&EC_group_p384, &EC_group_p521,
&EC_group_secp256k1, &EC_group_brainpoolP224r1,
&EC_group_brainpoolP256r1, &EC_group_brainpoolP320r1,
&EC_group_brainpoolP384r1, &EC_group_brainpoolP512r1,
};

EC_KEY *EC_KEY_parse_private_key(CBS *cbs, const EC_GROUP *group) {
Expand Down
254 changes: 254 additions & 0 deletions crypto/ec_extra/ec_brainpool.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC

// Brainpool elliptic curve groups from RFC 5639.
// These curves are not FIPS-approved and are therefore defined outside
// the FIPS module boundary (crypto/fipsmodule/).

#include <openssl/ec.h>

#include <string.h>

#include <openssl/mem.h>
#include <openssl/nid.h>

#include "../fipsmodule/bn/internal.h"
#include "../fipsmodule/ec/internal.h"
#include "../internal.h"
#include "internal.h"

#include "../fipsmodule/ec/builtin_curves.h"

// DEFINE_METHOD_FUNCTION (from delocate.h) cannot be used outside the FIPS
// module because it relies on delocated BSS storage within bcm.o. Since
// Brainpool curves are not FIPS-approved, we use an equivalent lazy-init
// pattern directly.
#define DEFINE_CURVE_DATA(type, name) \
static type name##_storage; \
static CRYPTO_once_t name##_once = CRYPTO_ONCE_INIT; \
static void name##_do_init(type *out); \
static void name##_init(void) { name##_do_init(&name##_storage); } \
const type *name(void) { \
CRYPTO_once(&name##_once, name##_init); \
return (const type *)&name##_storage; \
} \
static void name##_do_init(type *out)


// Duplicated from crypto/fipsmodule/ec/ec.c to avoid exposing
// FIPS-internal helpers across the module boundary.
static void ec_group_init_static_mont(BN_MONT_CTX *mont, size_t num_words,
const BN_ULONG *modulus,
const BN_ULONG *rr, uint64_t n0) {
bn_set_static_words(&mont->N, modulus, num_words);
bn_set_static_words(&mont->RR, rr, num_words);
#if defined(OPENSSL_64_BIT)
mont->n0[0] = n0;
#elif defined(OPENSSL_32_BIT)
mont->n0[0] = (uint32_t)n0;
mont->n0[1] = (uint32_t)(n0 >> 32);
#else
#error "unknown word length"
#endif
}

// Brainpool curves have arbitrary a coefficients (not -3 like NIST curves,
// not 0 like secp256k1), so a is provided in precomputed Montgomery form.
static void ec_group_set_a_mont(EC_GROUP *group, const BN_ULONG *mont_a,
size_t num_bytes) {
group->a_is_minus3 = 0;
OPENSSL_memset(group->a.words, 0, sizeof(EC_FELEM));
OPENSSL_memcpy(group->a.words, mont_a, num_bytes);
}

DEFINE_CURVE_DATA(EC_GROUP, EC_group_brainpoolP224r1) {
out->curve_name = NID_brainpoolP224r1;
out->comment = "brainpoolP224r1";
// OID 1.3.36.3.3.2.8.1.1.5 — RFC 5639, Section 4.1
static const uint8_t kOID[] = {0x2b, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01,
0x01, 0x05};
OPENSSL_memcpy(out->oid, kOID, sizeof(kOID));
out->oid_len = sizeof(kOID);

ec_group_init_static_mont(
&out->field, OPENSSL_ARRAY_SIZE(kbrainpoolP224r1Field),
kbrainpoolP224r1Field, kbrainpoolP224r1FieldRR, kbrainpoolP224r1FieldN0);
ec_group_init_static_mont(
&out->order, OPENSSL_ARRAY_SIZE(kbrainpoolP224r1Order),
kbrainpoolP224r1Order, kbrainpoolP224r1OrderRR, kbrainpoolP224r1OrderN0);

out->meth = EC_GFp_mont_method();
out->generator.group = out;
OPENSSL_memcpy(out->generator.raw.X.words, kbrainpoolP224r1MontGX,
sizeof(kbrainpoolP224r1MontGX));
OPENSSL_memcpy(out->generator.raw.Y.words, kbrainpoolP224r1MontGY,
sizeof(kbrainpoolP224r1MontGY));
OPENSSL_memcpy(out->generator.raw.Z.words, kbrainpoolP224r1FieldR,
sizeof(kbrainpoolP224r1FieldR));
OPENSSL_memcpy(out->b.words, kbrainpoolP224r1MontB,
sizeof(kbrainpoolP224r1MontB));

ec_group_set_a_mont(out, kbrainpoolP224r1MontA,
sizeof(kbrainpoolP224r1MontA));
out->has_order = 1;
out->field_greater_than_order = 1;
out->conv_form = POINT_CONVERSION_UNCOMPRESSED;
out->mutable_ec_group = 0;
}

DEFINE_CURVE_DATA(EC_GROUP, EC_group_brainpoolP256r1) {
out->curve_name = NID_brainpoolP256r1;
out->comment = "brainpoolP256r1";
// OID 1.3.36.3.3.2.8.1.1.7 — RFC 5639, Section 4.1
static const uint8_t kOID[] = {0x2b, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01,
0x01, 0x07};
OPENSSL_memcpy(out->oid, kOID, sizeof(kOID));
out->oid_len = sizeof(kOID);

ec_group_init_static_mont(
&out->field, OPENSSL_ARRAY_SIZE(kbrainpoolP256r1Field),
kbrainpoolP256r1Field, kbrainpoolP256r1FieldRR, kbrainpoolP256r1FieldN0);
ec_group_init_static_mont(
&out->order, OPENSSL_ARRAY_SIZE(kbrainpoolP256r1Order),
kbrainpoolP256r1Order, kbrainpoolP256r1OrderRR, kbrainpoolP256r1OrderN0);

out->meth = EC_GFp_mont_method();
out->generator.group = out;
OPENSSL_memcpy(out->generator.raw.X.words, kbrainpoolP256r1MontGX,
sizeof(kbrainpoolP256r1MontGX));
OPENSSL_memcpy(out->generator.raw.Y.words, kbrainpoolP256r1MontGY,
sizeof(kbrainpoolP256r1MontGY));
OPENSSL_memcpy(out->generator.raw.Z.words, kbrainpoolP256r1FieldR,
sizeof(kbrainpoolP256r1FieldR));
OPENSSL_memcpy(out->b.words, kbrainpoolP256r1MontB,
sizeof(kbrainpoolP256r1MontB));

ec_group_set_a_mont(out, kbrainpoolP256r1MontA,
sizeof(kbrainpoolP256r1MontA));
out->has_order = 1;
out->field_greater_than_order = 1;
out->conv_form = POINT_CONVERSION_UNCOMPRESSED;
out->mutable_ec_group = 0;
}

DEFINE_CURVE_DATA(EC_GROUP, EC_group_brainpoolP320r1) {
out->curve_name = NID_brainpoolP320r1;
out->comment = "brainpoolP320r1";
// OID 1.3.36.3.3.2.8.1.1.9 — RFC 5639, Section 4.1
static const uint8_t kOID[] = {0x2b, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01,
0x01, 0x09};
OPENSSL_memcpy(out->oid, kOID, sizeof(kOID));
out->oid_len = sizeof(kOID);

ec_group_init_static_mont(
&out->field, OPENSSL_ARRAY_SIZE(kbrainpoolP320r1Field),
kbrainpoolP320r1Field, kbrainpoolP320r1FieldRR, kbrainpoolP320r1FieldN0);
ec_group_init_static_mont(
&out->order, OPENSSL_ARRAY_SIZE(kbrainpoolP320r1Order),
kbrainpoolP320r1Order, kbrainpoolP320r1OrderRR, kbrainpoolP320r1OrderN0);

out->meth = EC_GFp_mont_method();
out->generator.group = out;
OPENSSL_memcpy(out->generator.raw.X.words, kbrainpoolP320r1MontGX,
sizeof(kbrainpoolP320r1MontGX));
OPENSSL_memcpy(out->generator.raw.Y.words, kbrainpoolP320r1MontGY,
sizeof(kbrainpoolP320r1MontGY));
OPENSSL_memcpy(out->generator.raw.Z.words, kbrainpoolP320r1FieldR,
sizeof(kbrainpoolP320r1FieldR));
OPENSSL_memcpy(out->b.words, kbrainpoolP320r1MontB,
sizeof(kbrainpoolP320r1MontB));

ec_group_set_a_mont(out, kbrainpoolP320r1MontA,
sizeof(kbrainpoolP320r1MontA));
out->has_order = 1;
out->field_greater_than_order = 1;
out->conv_form = POINT_CONVERSION_UNCOMPRESSED;
out->mutable_ec_group = 0;
}

DEFINE_CURVE_DATA(EC_GROUP, EC_group_brainpoolP384r1) {
out->curve_name = NID_brainpoolP384r1;
out->comment = "brainpoolP384r1";
// OID 1.3.36.3.3.2.8.1.1.11 — RFC 5639, Section 4.1
static const uint8_t kOID[] = {0x2b, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01,
0x01, 0x0b};
OPENSSL_memcpy(out->oid, kOID, sizeof(kOID));
out->oid_len = sizeof(kOID);

ec_group_init_static_mont(
&out->field, OPENSSL_ARRAY_SIZE(kbrainpoolP384r1Field),
kbrainpoolP384r1Field, kbrainpoolP384r1FieldRR, kbrainpoolP384r1FieldN0);
ec_group_init_static_mont(
&out->order, OPENSSL_ARRAY_SIZE(kbrainpoolP384r1Order),
kbrainpoolP384r1Order, kbrainpoolP384r1OrderRR, kbrainpoolP384r1OrderN0);

out->meth = EC_GFp_mont_method();
out->generator.group = out;
OPENSSL_memcpy(out->generator.raw.X.words, kbrainpoolP384r1MontGX,
sizeof(kbrainpoolP384r1MontGX));
OPENSSL_memcpy(out->generator.raw.Y.words, kbrainpoolP384r1MontGY,
sizeof(kbrainpoolP384r1MontGY));
OPENSSL_memcpy(out->generator.raw.Z.words, kbrainpoolP384r1FieldR,
sizeof(kbrainpoolP384r1FieldR));
OPENSSL_memcpy(out->b.words, kbrainpoolP384r1MontB,
sizeof(kbrainpoolP384r1MontB));

ec_group_set_a_mont(out, kbrainpoolP384r1MontA,
sizeof(kbrainpoolP384r1MontA));
out->has_order = 1;
out->field_greater_than_order = 1;
out->conv_form = POINT_CONVERSION_UNCOMPRESSED;
out->mutable_ec_group = 0;
}

DEFINE_CURVE_DATA(EC_GROUP, EC_group_brainpoolP512r1) {
out->curve_name = NID_brainpoolP512r1;
out->comment = "brainpoolP512r1";
// OID 1.3.36.3.3.2.8.1.1.13 — RFC 5639, Section 4.1
static const uint8_t kOID[] = {0x2b, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01,
0x01, 0x0d};
OPENSSL_memcpy(out->oid, kOID, sizeof(kOID));
out->oid_len = sizeof(kOID);

ec_group_init_static_mont(
&out->field, OPENSSL_ARRAY_SIZE(kbrainpoolP512r1Field),
kbrainpoolP512r1Field, kbrainpoolP512r1FieldRR, kbrainpoolP512r1FieldN0);
ec_group_init_static_mont(
&out->order, OPENSSL_ARRAY_SIZE(kbrainpoolP512r1Order),
kbrainpoolP512r1Order, kbrainpoolP512r1OrderRR, kbrainpoolP512r1OrderN0);

out->meth = EC_GFp_mont_method();
out->generator.group = out;
OPENSSL_memcpy(out->generator.raw.X.words, kbrainpoolP512r1MontGX,
sizeof(kbrainpoolP512r1MontGX));
OPENSSL_memcpy(out->generator.raw.Y.words, kbrainpoolP512r1MontGY,
sizeof(kbrainpoolP512r1MontGY));
OPENSSL_memcpy(out->generator.raw.Z.words, kbrainpoolP512r1FieldR,
sizeof(kbrainpoolP512r1FieldR));
OPENSSL_memcpy(out->b.words, kbrainpoolP512r1MontB,
sizeof(kbrainpoolP512r1MontB));

ec_group_set_a_mont(out, kbrainpoolP512r1MontA,
sizeof(kbrainpoolP512r1MontA));
out->has_order = 1;
out->field_greater_than_order = 1;
out->conv_form = POINT_CONVERSION_UNCOMPRESSED;
out->mutable_ec_group = 0;
}

const EC_GROUP *ec_group_new_by_curve_name_nonfips(int nid) {
switch (nid) {
case NID_brainpoolP224r1:
return EC_group_brainpoolP224r1();
case NID_brainpoolP256r1:
return EC_group_brainpoolP256r1();
case NID_brainpoolP320r1:
return EC_group_brainpoolP320r1();
case NID_brainpoolP384r1:
return EC_group_brainpoolP384r1();
case NID_brainpoolP512r1:
return EC_group_brainpoolP512r1();
default:
return NULL;
}
}
6 changes: 6 additions & 0 deletions crypto/ec_extra/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ OPENSSL_EXPORT int ec_hash_to_scalar_p384_xmd_sha512_draft07(
const EC_GROUP *group, EC_SCALAR *out, const uint8_t *dst, size_t dst_len,
const uint8_t *msg, size_t msg_len);

// ec_group_new_by_curve_name_nonfips returns the EC_GROUP for non-FIPS curves,
// or NULL if |nid| is not a supported non-FIPS curve. Called from the default
// fallthrough in EC_GROUP_new_by_curve_name to keep per-curve dispatch logic
// outside the FIPS module boundary (bcm.o).
const EC_GROUP *ec_group_new_by_curve_name_nonfips(int nid);

enum ECParametersType {
UNKNOWN_EC_PARAMETERS = 0,
NAMED_CURVE_EC_PARAMETERS = 1,
Expand Down
94 changes: 93 additions & 1 deletion crypto/evp_extra/evp_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -931,6 +931,25 @@ TEST(EVPTest, WycheproofECDSAsecp256k1) {
"third_party/vectors/converted/wycheproof/testvectors_v1/ecdsa_secp256k1_sha512_test.txt");
}

//= third_party/vectors/vectors_spec.md#wycheproof
//# AWS-LC MUST test against `testvectors_v1/ecdsa_brainpoolP224r1_sha224_test.txt`.
//# AWS-LC MUST test against `testvectors_v1/ecdsa_brainpoolP256r1_sha256_test.txt`.
//# AWS-LC MUST test against `testvectors_v1/ecdsa_brainpoolP320r1_sha384_test.txt`.
//# AWS-LC MUST test against `testvectors_v1/ecdsa_brainpoolP384r1_sha384_test.txt`.
//# AWS-LC MUST test against `testvectors_v1/ecdsa_brainpoolP512r1_sha512_test.txt`.
TEST(EVPTest, WycheproofECDSABrainpool) {
RunWycheproofVerifyTest(
"third_party/vectors/converted/wycheproof/testvectors_v1/ecdsa_brainpoolP224r1_sha224_test.txt");
RunWycheproofVerifyTest(
"third_party/vectors/converted/wycheproof/testvectors_v1/ecdsa_brainpoolP256r1_sha256_test.txt");
RunWycheproofVerifyTest(
"third_party/vectors/converted/wycheproof/testvectors_v1/ecdsa_brainpoolP320r1_sha384_test.txt");
RunWycheproofVerifyTest(
"third_party/vectors/converted/wycheproof/testvectors_v1/ecdsa_brainpoolP384r1_sha384_test.txt");
RunWycheproofVerifyTest(
"third_party/vectors/converted/wycheproof/testvectors_v1/ecdsa_brainpoolP512r1_sha512_test.txt");
}

//= third_party/vectors/vectors_spec.md#wycheproof
//# AWS-LC MUST test against `testvectors_v1/ed25519_test.txt`.
TEST(EVPTest, WycheproofEdDSA) {
Expand Down Expand Up @@ -1503,8 +1522,81 @@ TEST(EVPTest, ECTLSEncodedPoint) {
NID_secp521r1 // curve_nid
};

// secp256k1 test vector, taken from Wycheproof ecdh_secp256k1_test.json
// (tcId 1, "normal case")
static const uint8_t kSecp256k1PublicKey[] = {
/* uncompressed */
0x04,
/* x-coordinate */
0xd8, 0x09, 0x6a, 0xf8, 0xa1, 0x1e, 0x0b, 0x80, 0x03, 0x7e, 0x1e, 0xe6,
0x82, 0x46, 0xb5, 0xdc, 0xbb, 0x0a, 0xeb, 0x1c, 0xf1, 0x24, 0x4f, 0xd7,
0x67, 0xdb, 0x80, 0xf3, 0xfa, 0x27, 0xda, 0x2b,
/* y-coordinate */
0x39, 0x68, 0x12, 0xea, 0x16, 0x86, 0xe7, 0x47, 0x2e, 0x96, 0x92, 0xea,
0xf3, 0xe9, 0x58, 0xe5, 0x0e, 0x95, 0x00, 0xd3, 0xb4, 0xc7, 0x72, 0x43,
0xdb, 0x1f, 0x2a, 0xcd, 0x67, 0xba, 0x9c, 0xc4
};
static const uint8_t kSecp256k1PrivateKey[] = {
0xf4, 0xb7, 0xff, 0x7c, 0xcc, 0xc9, 0x88, 0x13, 0xa6, 0x9f, 0xae, 0x3d,
0xf2, 0x22, 0xbf, 0xe3, 0xf4, 0xe2, 0x8f, 0x76, 0x4b, 0xf9, 0x1b, 0x4a,
0x10, 0xd8, 0x09, 0x6c, 0xe4, 0x46, 0xb2, 0x54
};
static const uint8_t kSecp256k1ExpectedSharedSecret[] = {
0x54, 0x4d, 0xfa, 0xe2, 0x2a, 0xf6, 0xaf, 0x93, 0x90, 0x42, 0xb1, 0xd8,
0x5b, 0x71, 0xa1, 0xe4, 0x9e, 0x9a, 0x56, 0x14, 0x12, 0x3c, 0x4d, 0x6a,
0xd0, 0xc8, 0xaf, 0x65, 0xba, 0xf8, 0x7d, 0x65
};

struct ectlsencodedpoint_test_data secp256k1_test_data = {
kSecp256k1PublicKey, // public_key
(1 + 32 + 32), // public_key_size
kSecp256k1PrivateKey, // private_key
32, // private_key_size
kSecp256k1ExpectedSharedSecret, // expected_shared_secret
32, // expected_shared_secret_size
EVP_PKEY_EC, // key_type
NID_secp256k1 // curve_nid
};

// brainpoolP256r1 test vector, taken from RFC 7027 Section A.1
// (Alice's private key dA with Bob's public key Q_B)
static const uint8_t kBrainpoolP256r1PublicKey[] = {
/* uncompressed */
0x04,
/* x-coordinate (x_qB) */
0x8d, 0x2d, 0x68, 0x8c, 0x6c, 0xf9, 0x3e, 0x11, 0x60, 0xad, 0x04, 0xcc,
0x44, 0x29, 0x11, 0x7d, 0xc2, 0xc4, 0x18, 0x25, 0xe1, 0xe9, 0xfc, 0xa0,
0xad, 0xdd, 0x34, 0xe6, 0xf1, 0xb3, 0x9f, 0x7b,
/* y-coordinate (y_qB) */
0x99, 0x0c, 0x57, 0x52, 0x08, 0x12, 0xbe, 0x51, 0x26, 0x41, 0xe4, 0x70,
0x34, 0x83, 0x21, 0x06, 0xbc, 0x7d, 0x3e, 0x8d, 0xd0, 0xe4, 0xc7, 0xf1,
0x13, 0x6d, 0x70, 0x06, 0x54, 0x7c, 0xec, 0x6a
};
static const uint8_t kBrainpoolP256r1PrivateKey[] = {
0x81, 0xdb, 0x1e, 0xe1, 0x00, 0x15, 0x0f, 0xf2, 0xea, 0x33, 0x8d, 0x70,
0x82, 0x71, 0xbe, 0x38, 0x30, 0x0c, 0xb5, 0x42, 0x41, 0xd7, 0x99, 0x50,
0xf7, 0x7b, 0x06, 0x30, 0x39, 0x80, 0x4f, 0x1d
};
static const uint8_t kBrainpoolP256r1ExpectedSharedSecret[] = {
0x89, 0xaf, 0xc3, 0x9d, 0x41, 0xd3, 0xb3, 0x27, 0x81, 0x4b, 0x80, 0x94,
0x0b, 0x04, 0x25, 0x90, 0xf9, 0x65, 0x56, 0xec, 0x91, 0xe6, 0xae, 0x79,
0x39, 0xbc, 0xe3, 0x1f, 0x3a, 0x18, 0xbf, 0x2b
};

struct ectlsencodedpoint_test_data brainpool_p256r1_test_data = {
kBrainpoolP256r1PublicKey, // public_key
(1 + 32 + 32), // public_key_size
kBrainpoolP256r1PrivateKey, // private_key
32, // private_key_size
kBrainpoolP256r1ExpectedSharedSecret, // expected_shared_secret
32, // expected_shared_secret_size
EVP_PKEY_EC, // key_type
NID_brainpoolP256r1 // curve_nid
};

ectlsencodedpoint_test_data test_data_all[] = {x25519_test_data,
p224_test_data, p256_test_data, p384_test_data, p521_test_data};
p224_test_data, p256_test_data, p384_test_data, p521_test_data,
secp256k1_test_data, brainpool_p256r1_test_data};

uint8_t *output = nullptr;
uint8_t *shared_secret = nullptr;
Expand Down
Loading
Loading