Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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 @@ -403,6 +403,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.
Comment thread
manastasova marked this conversation as resolved.
Outdated
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) \
Comment thread
manastasova marked this conversation as resolved.
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