Add a very roundabout EC keygen API.
OpenSSL's EVP-level EC API involves a separate "paramgen" operation,
which is ultimately just a roundabout way to go from a NID to an
EC_GROUP. But Node uses this, and it's the pattern used within OpenSSL
these days, so this appears to be the official upstream recommendation.
Also add a #define for OPENSSL_EC_EXPLICIT_CURVE, because Node uses it,
but fail attempts to use it. Explicit curve encodings are forbidden by
RFC 5480 and generally a bad idea. (Parsing such keys back into OpenSSL
will cause it to lose the optimized path.)
Change-Id: I5e97080e77cf90fc149f6cf6f2cc4900f573fc64
Reviewed-on: https://boringssl-review.googlesource.com/c/34565
Commit-Queue: David Benjamin <davidben@google.com>
Commit-Queue: Adam Langley <agl@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/evp/evp_ctx.c b/crypto/evp/evp_ctx.c
index 3599f77..daa1954 100644
--- a/crypto/evp/evp_ctx.c
+++ b/crypto/evp/evp_ctx.c
@@ -415,7 +415,7 @@
return 1;
}
-int EVP_PKEY_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY **ppkey) {
+int EVP_PKEY_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY **out_pkey) {
if (!ctx || !ctx->pmeth || !ctx->pmeth->keygen) {
OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
return 0;
@@ -425,21 +425,60 @@
return 0;
}
- if (!ppkey) {
+ if (!out_pkey) {
return 0;
}
- if (!*ppkey) {
- *ppkey = EVP_PKEY_new();
- if (!*ppkey) {
+ if (!*out_pkey) {
+ *out_pkey = EVP_PKEY_new();
+ if (!*out_pkey) {
OPENSSL_PUT_ERROR(EVP, ERR_LIB_EVP);
return 0;
}
}
- if (!ctx->pmeth->keygen(ctx, *ppkey)) {
- EVP_PKEY_free(*ppkey);
- *ppkey = NULL;
+ if (!ctx->pmeth->keygen(ctx, *out_pkey)) {
+ EVP_PKEY_free(*out_pkey);
+ *out_pkey = NULL;
+ return 0;
+ }
+ return 1;
+}
+
+int EVP_PKEY_paramgen_init(EVP_PKEY_CTX *ctx) {
+ if (!ctx || !ctx->pmeth || !ctx->pmeth->paramgen) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+ return 0;
+ }
+ ctx->operation = EVP_PKEY_OP_PARAMGEN;
+ return 1;
+}
+
+int EVP_PKEY_paramgen(EVP_PKEY_CTX *ctx, EVP_PKEY **out_pkey) {
+ if (!ctx || !ctx->pmeth || !ctx->pmeth->paramgen) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+ return 0;
+ }
+ if (ctx->operation != EVP_PKEY_OP_PARAMGEN) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATON_NOT_INITIALIZED);
+ return 0;
+ }
+
+ if (!out_pkey) {
+ return 0;
+ }
+
+ if (!*out_pkey) {
+ *out_pkey = EVP_PKEY_new();
+ if (!*out_pkey) {
+ OPENSSL_PUT_ERROR(EVP, ERR_LIB_EVP);
+ return 0;
+ }
+ }
+
+ if (!ctx->pmeth->paramgen(ctx, *out_pkey)) {
+ EVP_PKEY_free(*out_pkey);
+ *out_pkey = NULL;
return 0;
}
return 1;
diff --git a/crypto/evp/evp_extra_test.cc b/crypto/evp/evp_extra_test.cc
index f12650e..1f6a61d 100644
--- a/crypto/evp/evp_extra_test.cc
+++ b/crypto/evp/evp_extra_test.cc
@@ -638,3 +638,60 @@
EXPECT_FALSE(EVP_DigestVerifyFinal(ctx.get(), nullptr, 0));
ERR_clear_error();
}
+
+static void ExpectECGroupOnly(const EVP_PKEY *pkey, int nid) {
+ EC_KEY *ec = EVP_PKEY_get0_EC_KEY(pkey);
+ ASSERT_TRUE(ec);
+ const EC_GROUP *group = EC_KEY_get0_group(ec);
+ ASSERT_TRUE(group);
+ EXPECT_EQ(nid, EC_GROUP_get_curve_name(group));
+ EXPECT_FALSE(EC_KEY_get0_public_key(ec));
+ EXPECT_FALSE(EC_KEY_get0_private_key(ec));
+}
+
+static void ExpectECGroupAndKey(const EVP_PKEY *pkey, int nid) {
+ EC_KEY *ec = EVP_PKEY_get0_EC_KEY(pkey);
+ ASSERT_TRUE(ec);
+ const EC_GROUP *group = EC_KEY_get0_group(ec);
+ ASSERT_TRUE(group);
+ EXPECT_EQ(nid, EC_GROUP_get_curve_name(group));
+ EXPECT_TRUE(EC_KEY_get0_public_key(ec));
+ EXPECT_TRUE(EC_KEY_get0_private_key(ec));
+}
+
+TEST(EVPExtraTest, ECKeygen) {
+ // |EVP_PKEY_paramgen| may be used as an extremely roundabout way to get an
+ // |EC_GROUP|.
+ bssl::UniquePtr<EVP_PKEY_CTX> ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr));
+ ASSERT_TRUE(ctx);
+ ASSERT_TRUE(EVP_PKEY_paramgen_init(ctx.get()));
+ ASSERT_TRUE(
+ EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx.get(), NID_X9_62_prime256v1));
+ EVP_PKEY *raw = nullptr;
+ ASSERT_TRUE(EVP_PKEY_paramgen(ctx.get(), &raw));
+ bssl::UniquePtr<EVP_PKEY> pkey(raw);
+ raw = nullptr;
+ ExpectECGroupOnly(pkey.get(), NID_X9_62_prime256v1);
+
+ // That resulting |EVP_PKEY| may be used as a template for key generation.
+ ctx.reset(EVP_PKEY_CTX_new(pkey.get(), nullptr));
+ ASSERT_TRUE(ctx);
+ ASSERT_TRUE(EVP_PKEY_keygen_init(ctx.get()));
+ raw = nullptr;
+ ASSERT_TRUE(EVP_PKEY_keygen(ctx.get(), &raw));
+ pkey.reset(raw);
+ raw = nullptr;
+ ExpectECGroupAndKey(pkey.get(), NID_X9_62_prime256v1);
+
+ // |EVP_PKEY_paramgen| may also be skipped.
+ ctx.reset(EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr));
+ ASSERT_TRUE(ctx);
+ ASSERT_TRUE(EVP_PKEY_keygen_init(ctx.get()));
+ ASSERT_TRUE(
+ EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx.get(), NID_X9_62_prime256v1));
+ raw = nullptr;
+ ASSERT_TRUE(EVP_PKEY_keygen(ctx.get(), &raw));
+ pkey.reset(raw);
+ raw = nullptr;
+ ExpectECGroupAndKey(pkey.get(), NID_X9_62_prime256v1);
+}
diff --git a/crypto/evp/internal.h b/crypto/evp/internal.h
index 4aefa35..43847ea 100644
--- a/crypto/evp/internal.h
+++ b/crypto/evp/internal.h
@@ -119,6 +119,7 @@
#define EVP_PKEY_OP_ENCRYPT (1 << 6)
#define EVP_PKEY_OP_DECRYPT (1 << 7)
#define EVP_PKEY_OP_DERIVE (1 << 8)
+#define EVP_PKEY_OP_PARAMGEN (1 << 9)
#define EVP_PKEY_OP_TYPE_SIG \
(EVP_PKEY_OP_SIGN | EVP_PKEY_OP_VERIFY | EVP_PKEY_OP_VERIFYRECOVER)
@@ -128,7 +129,7 @@
#define EVP_PKEY_OP_TYPE_NOGEN \
(EVP_PKEY_OP_SIG | EVP_PKEY_OP_CRYPT | EVP_PKEY_OP_DERIVE)
-#define EVP_PKEY_OP_TYPE_GEN EVP_PKEY_OP_KEYGEN
+#define EVP_PKEY_OP_TYPE_GEN (EVP_PKEY_OP_KEYGEN | EVP_PKEY_OP_PARAMGEN)
// EVP_PKEY_CTX_ctrl performs |cmd| on |ctx|. The |keytype| and |optype|
// arguments can be -1 to specify that any type and operation are acceptable,
@@ -171,6 +172,7 @@
#define EVP_PKEY_CTRL_GET_RSA_MGF1_MD (EVP_PKEY_ALG_CTRL + 10)
#define EVP_PKEY_CTRL_RSA_OAEP_LABEL (EVP_PKEY_ALG_CTRL + 11)
#define EVP_PKEY_CTRL_GET_RSA_OAEP_LABEL (EVP_PKEY_ALG_CTRL + 12)
+#define EVP_PKEY_CTRL_EC_PARAMGEN_CURVE_NID (EVP_PKEY_ALG_CTRL + 13)
struct evp_pkey_ctx_st {
// Method associated with this operation
@@ -219,6 +221,8 @@
int (*derive)(EVP_PKEY_CTX *ctx, uint8_t *key, size_t *keylen);
+ int (*paramgen)(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey);
+
int (*ctrl)(EVP_PKEY_CTX *ctx, int type, int p1, void *p2);
} /* EVP_PKEY_METHOD */;
diff --git a/crypto/evp/p_ec.c b/crypto/evp/p_ec.c
index d311d22..9c325ae 100644
--- a/crypto/evp/p_ec.c
+++ b/crypto/evp/p_ec.c
@@ -76,6 +76,7 @@
typedef struct {
// message digest
const EVP_MD *md;
+ EC_GROUP *gen_group;
} EC_PKEY_CTX;
@@ -111,6 +112,7 @@
return;
}
+ EC_GROUP_free(dctx->gen_group);
OPENSSL_free(dctx);
}
@@ -199,6 +201,16 @@
// Default behaviour is OK
return 1;
+ case EVP_PKEY_CTRL_EC_PARAMGEN_CURVE_NID: {
+ EC_GROUP *group = EC_GROUP_new_by_curve_name(p1);
+ if (group == NULL) {
+ return 0;
+ }
+ EC_GROUP_free(dctx->gen_group);
+ dctx->gen_group = group;
+ return 1;
+ }
+
default:
OPENSSL_PUT_ERROR(EVP, EVP_R_COMMAND_NOT_SUPPORTED);
return 0;
@@ -206,14 +218,35 @@
}
static int pkey_ec_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) {
- if (ctx->pkey == NULL) {
+ EC_PKEY_CTX *dctx = ctx->data;
+ const EC_GROUP *group = dctx->gen_group;
+ if (group == NULL) {
+ if (ctx->pkey == NULL) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_NO_PARAMETERS_SET);
+ return 0;
+ }
+ group = EC_KEY_get0_group(ctx->pkey->pkey.ec);
+ }
+ EC_KEY *ec = EC_KEY_new();
+ if (ec == NULL ||
+ !EC_KEY_set_group(ec, group) ||
+ !EC_KEY_generate_key(ec)) {
+ EC_KEY_free(ec);
+ return 0;
+ }
+ EVP_PKEY_assign_EC_KEY(pkey, ec);
+ return 1;
+}
+
+static int pkey_ec_paramgen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) {
+ EC_PKEY_CTX *dctx = ctx->data;
+ if (dctx->gen_group == NULL) {
OPENSSL_PUT_ERROR(EVP, EVP_R_NO_PARAMETERS_SET);
return 0;
}
EC_KEY *ec = EC_KEY_new();
if (ec == NULL ||
- !EC_KEY_set_group(ec, EC_KEY_get0_group(ctx->pkey->pkey.ec)) ||
- !EC_KEY_generate_key(ec)) {
+ !EC_KEY_set_group(ec, dctx->gen_group)) {
EC_KEY_free(ec);
return 0;
}
@@ -235,5 +268,20 @@
NULL /* encrypt */,
NULL /* decrypt */,
pkey_ec_derive,
+ pkey_ec_paramgen,
pkey_ec_ctrl,
};
+
+int EVP_PKEY_CTX_set_ec_paramgen_curve_nid(EVP_PKEY_CTX *ctx, int nid) {
+ return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC, EVP_PKEY_OP_TYPE_GEN,
+ EVP_PKEY_CTRL_EC_PARAMGEN_CURVE_NID, nid, NULL);
+}
+
+int EVP_PKEY_CTX_set_ec_param_enc(EVP_PKEY_CTX *ctx, int encoding) {
+ // BoringSSL only supports named curve syntax.
+ if (encoding != OPENSSL_EC_NAMED_CURVE) {
+ OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PARAMETERS);
+ return 0;
+ }
+ return 1;
+}
diff --git a/crypto/evp/p_ed25519.c b/crypto/evp/p_ed25519.c
index 554a379..062ea45 100644
--- a/crypto/evp/p_ed25519.c
+++ b/crypto/evp/p_ed25519.c
@@ -67,5 +67,6 @@
NULL /* encrypt */,
NULL /* decrypt */,
NULL /* derive */,
+ NULL /* paramgen */,
NULL /* ctrl */,
};
diff --git a/crypto/evp/p_rsa.c b/crypto/evp/p_rsa.c
index cfc6bea..eb59901 100644
--- a/crypto/evp/p_rsa.c
+++ b/crypto/evp/p_rsa.c
@@ -553,7 +553,8 @@
pkey_rsa_verify_recover,
pkey_rsa_encrypt,
pkey_rsa_decrypt,
- 0 /* derive */,
+ NULL /* derive */,
+ NULL /* paramgen */,
pkey_rsa_ctrl,
};
diff --git a/include/openssl/ec.h b/include/openssl/ec.h
index 16dadf2..fcecb62 100644
--- a/include/openssl/ec.h
+++ b/include/openssl/ec.h
@@ -325,6 +325,7 @@
OPENSSL_EXPORT void EC_GROUP_set_asn1_flag(EC_GROUP *group, int flag);
#define OPENSSL_EC_NAMED_CURVE 0
+#define OPENSSL_EC_EXPLICIT_CURVE 1
typedef struct ec_method_st EC_METHOD;
diff --git a/include/openssl/evp.h b/include/openssl/evp.h
index cbe93d8..c446984 100644
--- a/include/openssl/evp.h
+++ b/include/openssl/evp.h
@@ -646,9 +646,23 @@
OPENSSL_EXPORT int EVP_PKEY_keygen_init(EVP_PKEY_CTX *ctx);
// EVP_PKEY_keygen performs a key generation operation using the values from
-// |ctx| and sets |*ppkey| to a fresh |EVP_PKEY| containing the resulting key.
+// |ctx|. If |*out_pkey| is non-NULL, it overwrites |*out_pkey| with the
+// resulting key. Otherwise, it sets |*out_pkey| to a newly-allocated |EVP_PKEY|
+// containing the result. It returns one on success or zero on error.
+OPENSSL_EXPORT int EVP_PKEY_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY **out_pkey);
+
+// EVP_PKEY_paramgen_init initialises an |EVP_PKEY_CTX| for a parameter
+// generation operation. It should be called before |EVP_PKEY_paramgen|.
+//
// It returns one on success or zero on error.
-OPENSSL_EXPORT int EVP_PKEY_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY **ppkey);
+OPENSSL_EXPORT int EVP_PKEY_paramgen_init(EVP_PKEY_CTX *ctx);
+
+// EVP_PKEY_paramgen performs a parameter generation using the values from
+// |ctx|. If |*out_pkey| is non-NULL, it overwrites |*out_pkey| with the
+// resulting parameters, but no key. Otherwise, it sets |*out_pkey| to a
+// newly-allocated |EVP_PKEY| containing the result. It returns one on success
+// or zero on error.
+OPENSSL_EXPORT int EVP_PKEY_paramgen(EVP_PKEY_CTX *ctx, EVP_PKEY **out_pkey);
// Generic control functions.
@@ -746,6 +760,15 @@
const uint8_t **out_label);
+// EC specific control functions.
+
+// EVP_PKEY_CTX_set_ec_paramgen_curve_nid sets the curve used for
+// |EVP_PKEY_keygen| or |EVP_PKEY_paramgen| operations to |nid|. It returns one
+// on success and zero on error.
+OPENSSL_EXPORT int EVP_PKEY_CTX_set_ec_paramgen_curve_nid(EVP_PKEY_CTX *ctx,
+ int nid);
+
+
// Deprecated functions.
// EVP_PKEY_DH is defined for compatibility, but it is impossible to create an
@@ -846,6 +869,11 @@
// EVP_PKEY_get1_DH returns NULL.
OPENSSL_EXPORT DH *EVP_PKEY_get1_DH(const EVP_PKEY *pkey);
+// EVP_PKEY_CTX_set_ec_param_enc returns one if |encoding| is
+// |OPENSSL_EC_NAMED_CURVE| or zero with an error otherwise.
+OPENSSL_EXPORT int EVP_PKEY_CTX_set_ec_param_enc(EVP_PKEY_CTX *ctx,
+ int encoding);
+
// Preprocessor compatibility section (hidden).
//