Tighten EC_KEY's association with its group.
This is to simplify
https://boringssl-review.googlesource.com/c/boringssl/+/24445/.
Setting or changing an EC_KEY's group after the public or private keys
have been configured is quite awkward w.r.t. consistency checks. It
becomes additionally messy if we mean to store private keys as
EC_SCALARs (and avoid the BIGNUM timing leak), whose size is
curve-dependent.
Instead, require that callers configure the group before setting either
half of the keypair. Additionally, reject EC_KEY_set_group calls that
change the group. This will simplify clearing one more BIGNUM timing
leak.
Update-Note: This will break code which sets the group and key in a
weird order. I checked calls of EC_KEY_new and confirmed they all
set the group first. If I missed any, let me know.
Change-Id: Ie89f90a318b31b6b98f71138e5ff3de5323bc9a6
Reviewed-on: https://boringssl-review.googlesource.com/24425
Commit-Queue: Adam Langley <agl@google.com>
Reviewed-by: Adam Langley <agl@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/crypto/fipsmodule/ec/ec_key.c b/crypto/fipsmodule/ec/ec_key.c
index f64cb21..4e0bcb2 100644
--- a/crypto/fipsmodule/ec/ec_key.c
+++ b/crypto/fipsmodule/ec/ec_key.c
@@ -233,20 +233,21 @@
const EC_GROUP *EC_KEY_get0_group(const EC_KEY *key) { return key->group; }
int EC_KEY_set_group(EC_KEY *key, const EC_GROUP *group) {
+ // If |key| already has a group, it is an error to switch to another one.
+ if (key->group != NULL) {
+ if (EC_GROUP_cmp(key->group, group, NULL) != 0) {
+ OPENSSL_PUT_ERROR(EC, EC_R_GROUP_MISMATCH);
+ return 0;
+ }
+ return 1;
+ }
+
+ assert(key->priv_key == NULL);
+ assert(key->pub_key == NULL);
+
EC_GROUP_free(key->group);
- // TODO(fork): duplicating the group seems wasteful but see
- // |EC_KEY_set_conv_form|.
key->group = EC_GROUP_dup(group);
- if (key->group == NULL) {
- return 0;
- }
- // XXX: |BN_cmp| is not constant time.
- if (key->priv_key != NULL &&
- (BN_is_negative(key->priv_key) ||
- BN_cmp(key->priv_key, EC_GROUP_get0_order(group)) >= 0)) {
- return 0;
- }
- return 1;
+ return key->group != NULL;
}
const BIGNUM *EC_KEY_get0_private_key(const EC_KEY *key) {
@@ -254,10 +255,14 @@
}
int EC_KEY_set_private_key(EC_KEY *key, const BIGNUM *priv_key) {
+ if (key->group == NULL) {
+ OPENSSL_PUT_ERROR(EC, EC_R_MISSING_PARAMETERS);
+ return 0;
+ }
+
// XXX: |BN_cmp| is not constant time.
- if (key->group != NULL &&
- (BN_is_negative(priv_key) ||
- BN_cmp(priv_key, EC_GROUP_get0_order(key->group)) >= 0)) {
+ if (BN_is_negative(priv_key) ||
+ BN_cmp(priv_key, EC_GROUP_get0_order(key->group)) >= 0) {
OPENSSL_PUT_ERROR(EC, EC_R_WRONG_ORDER);
return 0;
}
@@ -271,6 +276,16 @@
}
int EC_KEY_set_public_key(EC_KEY *key, const EC_POINT *pub_key) {
+ if (key->group == NULL) {
+ OPENSSL_PUT_ERROR(EC, EC_R_MISSING_PARAMETERS);
+ return 0;
+ }
+
+ if (EC_GROUP_cmp(key->group, pub_key->group, NULL) != 0) {
+ OPENSSL_PUT_ERROR(EC, EC_R_GROUP_MISMATCH);
+ return 0;
+ }
+
EC_POINT_free(key->pub_key);
key->pub_key = EC_POINT_dup(pub_key, key->group);
return (key->pub_key == NULL) ? 0 : 1;
diff --git a/crypto/fipsmodule/ec/ec_test.cc b/crypto/fipsmodule/ec/ec_test.cc
index 8e7a81d..d2cd2af 100644
--- a/crypto/fipsmodule/ec/ec_test.cc
+++ b/crypto/fipsmodule/ec/ec_test.cc
@@ -305,6 +305,36 @@
EXPECT_NE(0, EC_GROUP_cmp(group.get(), group3.get(), NULL));
}
+TEST(ECTest, SetKeyWithoutGroup) {
+ bssl::UniquePtr<EC_KEY> key(EC_KEY_new());
+ ASSERT_TRUE(key);
+
+ // Private keys may not be configured without a group.
+ EXPECT_FALSE(EC_KEY_set_private_key(key.get(), BN_value_one()));
+
+ // Public keys may not be configured without a group.
+ bssl::UniquePtr<EC_GROUP> group(
+ EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
+ ASSERT_TRUE(group);
+ EXPECT_FALSE(
+ EC_KEY_set_public_key(key.get(), EC_GROUP_get0_generator(group.get())));
+}
+
+TEST(ECTest, GroupMismatch) {
+ bssl::UniquePtr<EC_KEY> key(EC_KEY_new_by_curve_name(NID_secp384r1));
+ ASSERT_TRUE(key);
+ bssl::UniquePtr<EC_GROUP> p256(
+ EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
+ ASSERT_TRUE(p256);
+
+ // Changing a key's group is invalid.
+ EXPECT_FALSE(EC_KEY_set_group(key.get(), p256.get()));
+
+ // Configuring a public key with the wrong group is invalid.
+ EXPECT_FALSE(
+ EC_KEY_set_public_key(key.get(), EC_GROUP_get0_generator(p256.get())));
+}
+
class ECCurveTest : public testing::TestWithParam<EC_builtin_curve> {};
TEST_P(ECCurveTest, SetAffine) {
diff --git a/include/openssl/ec_key.h b/include/openssl/ec_key.h
index 7ef1b14..5fe0318 100644
--- a/include/openssl/ec_key.h
+++ b/include/openssl/ec_key.h
@@ -116,14 +116,16 @@
OPENSSL_EXPORT const EC_GROUP *EC_KEY_get0_group(const EC_KEY *key);
// EC_KEY_set_group sets the |EC_GROUP| object that |key| will use to |group|.
-// It returns one on success and zero otherwise.
+// It returns one on success and zero otherwise. If |key| already has a group,
+// it is an error to change to a different one.
OPENSSL_EXPORT int EC_KEY_set_group(EC_KEY *key, const EC_GROUP *group);
// EC_KEY_get0_private_key returns a pointer to the private key inside |key|.
OPENSSL_EXPORT const BIGNUM *EC_KEY_get0_private_key(const EC_KEY *key);
// EC_KEY_set_private_key sets the private key of |key| to |priv|. It returns
-// one on success and zero otherwise.
+// one on success and zero otherwise. |key| must already have had a group
+// configured (see |EC_KEY_set_group| and |EC_KEY_new_by_curve_name|).
OPENSSL_EXPORT int EC_KEY_set_private_key(EC_KEY *key, const BIGNUM *prv);
// EC_KEY_get0_public_key returns a pointer to the public key point inside
@@ -131,7 +133,9 @@
OPENSSL_EXPORT const EC_POINT *EC_KEY_get0_public_key(const EC_KEY *key);
// EC_KEY_set_public_key sets the public key of |key| to |pub|, by copying it.
-// It returns one on success and zero otherwise.
+// It returns one on success and zero otherwise. |key| must already have had a
+// group configured (see |EC_KEY_set_group| and |EC_KEY_new_by_curve_name|), and
+// |pub| must also belong to that group.
OPENSSL_EXPORT int EC_KEY_set_public_key(EC_KEY *key, const EC_POINT *pub);
#define EC_PKEY_NO_PARAMETERS 0x001