Add some EVP APIs for low-level EC point and scalar formats I haven't yet done ECPrivateKey. That one is a bit hairy since it is two formats in one. Bug: 42290408 Change-Id: Ie579392f5df34c221f79879fa468b930a692e196 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/98730 Reviewed-by: Lily Chen <chlily@google.com> Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/crypto/evp/evp_test.cc b/crypto/evp/evp_test.cc index ec2271f..cb23f77 100644 --- a/crypto/evp/evp_test.cc +++ b/crypto/evp/evp_test.cc
@@ -112,9 +112,7 @@ }; const std::map<std::string, AlgorithmInfo> kAllAlgorithms = { - {"RSA", - {EVP_pkey_rsa(), - /*kem=*/nullptr, EVP_PKEY_RSA, true}}, + {"RSA", {EVP_pkey_rsa(), /*kem=*/nullptr, EVP_PKEY_RSA, true}}, {"RSA-PSS-SHA-256", {EVP_pkey_rsa_pss_sha256(), /*kem=*/nullptr, EVP_PKEY_RSA_PSS, false}}, @@ -307,7 +305,8 @@ EVP_PKEY *(*parse_func)(const EVP_PKEY_ALG *, const uint8_t *, size_t)) { SCOPED_TRACE(attr); - if (!t->HasAttribute(attr)) { + if (!t->HasAttribute(attr) || + t->HasAttribute("SkipImport" + std::string(attr))) { return true; // Nothing to import. } std::vector<uint8_t> input; @@ -521,6 +520,12 @@ alg_info, &EVP_PKEY_from_raw_private_key) || !ImportWithAlgCommon(t, &key_set, KeyRole::kPrivate, "PrivateSeed", alg_info, &EVP_PKEY_from_private_seed) || + !ImportWithAlgCommon(t, &key_set, KeyRole::kPublic, "ECUncompressedPoint", + alg_info, &EVP_PKEY_from_ec_uncompressed_point) || + !ImportWithAlgCommon(t, &key_set, KeyRole::kPublic, "ECCompressedPoint", + alg_info, &EVP_PKEY_from_ec_compressed_point) || + !ImportWithAlgCommon(t, &key_set, KeyRole::kPrivate, "ECPrivateScalar", + alg_info, &EVP_PKEY_from_ec_private_scalar) || !ImportRSAPublicParams(t, &key_set, alg_info) || !ImportRSAPrivateParams(t, &key_set, alg_info)) { return false; @@ -605,7 +610,13 @@ !CheckRawKey(t, "RawPublic", KeyRoleSelect::kAny, key, EVP_PKEY_get_raw_public_key) || !CheckRawKey(t, "PrivateSeed", KeyRoleSelect::kPrivate, key, - EVP_PKEY_get_private_seed)) { + EVP_PKEY_get_private_seed) || + !CheckMarshalCBB(t, "ECUncompressedPoint", KeyRoleSelect::kAny, key, + EVP_PKEY_marshal_ec_uncompressed_point) || + !CheckMarshalCBB(t, "ECCompressedPoint", KeyRoleSelect::kAny, key, + EVP_PKEY_marshal_ec_compressed_point) || + !CheckMarshalCBB(t, "ECPrivateScalar", KeyRoleSelect::kPrivate, key, + EVP_PKEY_marshal_ec_private_scalar)) { return false; } }
diff --git a/crypto/evp/p_ec.cc b/crypto/evp/p_ec.cc index 06ef932..3e72432 100644 --- a/crypto/evp/p_ec.cc +++ b/crypto/evp/p_ec.cc
@@ -575,6 +575,127 @@ return ec_key; } +static const EC_GROUP *alg_to_group(const EVP_PKEY_ALG *alg) { + if (alg->method->pkey_id != EVP_PKEY_EC) { + OPENSSL_PUT_ERROR(EVP, EVP_R_UNSUPPORTED_ALGORITHM); + return nullptr; + } + auto *alg_ec = static_cast<const EVP_PKEY_ALG_EC *>(alg); + if (alg_ec->ec_group == nullptr) { + OPENSSL_PUT_ERROR(EVP, EVP_R_UNSUPPORTED_ALGORITHM); + return nullptr; + } + return alg_ec->ec_group(); +} + +static EVP_PKEY *pkey_from_ec_point(const EC_GROUP *group, const uint8_t *in, + size_t len) { + UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new()); + UniquePtr<EC_KEY> ec_key(EC_KEY_new()); + if (pkey == nullptr || ec_key == nullptr || + !EC_KEY_set_group(ec_key.get(), group) || + !EC_KEY_oct2key(ec_key.get(), in, len, nullptr)) { + return nullptr; + } + EVP_PKEY_assign_EC_KEY(pkey.get(), ec_key.release()); + return pkey.release(); +} + +EVP_PKEY *EVP_PKEY_from_ec_uncompressed_point( + const EVP_PKEY_ALG *alg, const uint8_t *in, size_t len) { + const EC_GROUP *group = alg_to_group(alg); + if (group == nullptr) { + return nullptr; + } + // The `EC_KEY` APIs are not separated by uncompressed and compressed. + if (len == 0 || in[0] != 0x04) { + OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); + return nullptr; + } + return pkey_from_ec_point(group, in, len); +} + +EVP_PKEY *EVP_PKEY_from_ec_compressed_point(const EVP_PKEY_ALG *alg, + const uint8_t *in, size_t len) { + const EC_GROUP *group = alg_to_group(alg); + if (group == nullptr) { + return nullptr; + } + // The `EC_KEY` APIs are not separated by uncompressed and compressed. + if (len == 0 || in[0] == 0x04) { + OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); + return nullptr; + } + return pkey_from_ec_point(group, in, len); +} + +static int marshal_ec_point(CBB *cbb, const EVP_PKEY *key, + point_conversion_form_t form) { + const EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(key); + if (ec_key == nullptr) { + return 0; + } + const EC_GROUP *group = EC_KEY_get0_group(ec_key); + const EC_POINT *pub = EC_KEY_get0_public_key(ec_key); + if (group == nullptr || pub == nullptr) { + OPENSSL_PUT_ERROR(EVP, EVP_R_MISSING_PUBLIC_KEY); + return 0; + } + return EC_POINT_point2cbb(cbb, group, pub, form, nullptr); +} + +int EVP_PKEY_marshal_ec_uncompressed_point(CBB *cbb, const EVP_PKEY *key) { + return marshal_ec_point(cbb, key, POINT_CONVERSION_UNCOMPRESSED); +} + +int EVP_PKEY_marshal_ec_compressed_point(CBB *cbb, const EVP_PKEY *key) { + return marshal_ec_point(cbb, key, POINT_CONVERSION_COMPRESSED); +} + +EVP_PKEY *EVP_PKEY_from_ec_private_scalar(const EVP_PKEY_ALG *alg, + const uint8_t *in, size_t len) { + const EC_GROUP *group = alg_to_group(alg); + if (group == nullptr) { + return nullptr; + } + UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new()); + UniquePtr<EC_KEY> ec_key(EC_KEY_new()); + if (pkey == nullptr || ec_key == nullptr || + !EC_KEY_set_group(ec_key.get(), group) || + !EC_KEY_oct2priv(ec_key.get(), in, len)) { + return nullptr; + } + // TODO(crbug.com/42290404): `EC_KEY_oct2priv` should fill in the public key + // internally. + UniquePtr<EC_POINT> pub(EC_POINT_new(group)); + if (pub == nullptr || + !EC_POINT_mul(group, pub.get(), EC_KEY_get0_private_key(ec_key.get()), + nullptr, nullptr, nullptr) || + !EC_KEY_set_public_key(ec_key.get(), pub.get())) { + return 0; + } + + EVP_PKEY_assign_EC_KEY(pkey.get(), ec_key.release()); + return pkey.release(); +} + +int EVP_PKEY_marshal_ec_private_scalar(CBB *cbb, const EVP_PKEY *key) { + const EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(key); + if (ec_key == nullptr) { + return 0; + } + size_t len = EC_KEY_priv2oct(ec_key, nullptr, 0); + if (len == 0) { + return 0; + } + uint8_t *out; + if (!CBB_reserve(cbb, &out, len)) { + return 0; + } + len = EC_KEY_priv2oct(ec_key, out, len); + return len != 0 && CBB_did_write(cbb, len); +} + int EVP_PKEY_get_ec_curve_nid(const EVP_PKEY *pkey) { const EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(pkey); if (ec_key == nullptr) {
diff --git a/crypto/evp/test/ec_tests.txt b/crypto/evp/test/ec_tests.txt index 33ccff3..9810033 100644 --- a/crypto/evp/test/ec_tests.txt +++ b/crypto/evp/test/ec_tests.txt
@@ -5,6 +5,9 @@ Algorithm = EC-P-256 Bits = 256 ECCurve = prime256v1 +ECUncompressedPoint = 042c150f429ce70f216c252cf5e062ce1f639cd5d165c7f89424072c27197d78b33b920e95cdb664e990dcf0cfea0d94e2a8e6af9d0e58056e653104925b9fe6c9 +ECCompressedPoint = 032c150f429ce70f216c252cf5e062ce1f639cd5d165c7f89424072c27197d78b3 +ECPrivateScalar = 8a872fb62893c4d1ffc5b9f0f91758069f8352e08fa05a49f8db926cb5728725 PKCS8 = 308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b02010104208a872fb62893c4d1ffc5b9f0f91758069f8352e08fa05a49f8db926cb5728725a144034200042c150f429ce70f216c252cf5e062ce1f639cd5d165c7f89424072c27197d78b33b920e95cdb664e990dcf0cfea0d94e2a8e6af9d0e58056e653104925b9fe6c9 SPKI = 3059301306072a8648ce3d020106082a8648ce3d030107034200042c150f429ce70f216c252cf5e062ce1f639cd5d165c7f89424072c27197d78b33b920e95cdb664e990dcf0cfea0d94e2a8e6af9d0e58056e653104925b9fe6c9 # The same key as above with redundant parameters. @@ -15,17 +18,22 @@ PKCS8NonCanonical = 308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b02010104208a872fb62893c4d1ffc5b9f0f91758069f8352e08fa05a49f8db926cb5728725a144034200042c150f429ce70f216c252cf5e062ce1f639cd5d165c7f89424072c27197d78b33b920e95cdb664e990dcf0cfea0d94e2a8e6af9d0e58056e653104925b9fe6c9 # The same key as above with the optional public key omitted. -# -# TODO(crbug.com/503025567): Currently, we remember whether the public key was -# omitted and roundtrip it back out, so this is a seprate key set. Pick a -# canonical one instead? Key = P-256-MissingPublic Algorithm = EC-P-256 Bits = 256 ECCurve = prime256v1 +ECUncompressedPoint = 042c150f429ce70f216c252cf5e062ce1f639cd5d165c7f89424072c27197d78b33b920e95cdb664e990dcf0cfea0d94e2a8e6af9d0e58056e653104925b9fe6c9 +ECCompressedPoint = 032c150f429ce70f216c252cf5e062ce1f639cd5d165c7f89424072c27197d78b3 +ECPrivateScalar = 8a872fb62893c4d1ffc5b9f0f91758069f8352e08fa05a49f8db926cb5728725 PKCS8 = 3041020100301306072a8648ce3d020106082a8648ce3d0301070427302502010104208a872fb62893c4d1ffc5b9f0f91758069f8352e08fa05a49f8db926cb5728725 # Even with the public key omitted, we compute the public key. SPKI = 3059301306072a8648ce3d020106082a8648ce3d030107034200042c150f429ce70f216c252cf5e062ce1f639cd5d165c7f89424072c27197d78b33b920e95cdb664e990dcf0cfea0d94e2a8e6af9d0e58056e653104925b9fe6c9 +# TODO(crbug.com/503025567): Currently, we remember whether the public key was +# omitted and roundtrip it back out, so this is a seprate key set. Pick a +# canonical one instead? Remembering it also means that importing +# ECPrivateScalar produces a slightly different object from importing PKCS8. To +# avoid breaking the cross-format tests, skip importing ECPrivateScalar. +SkipImportECPrivateScalar # The same key, but with the redundant parameters in the ECPrivateKey mismatched. Key = P-256-BadInnerParameters @@ -68,6 +76,9 @@ Algorithm = EC-P-224 Bits = 224 ECCurve = secp224r1 +ECUncompressedPoint = 041a95d8c8d08635ad7fa0facd3a9fe56f8f42451b58ea8a29f7d28b0fb214690b1149a69f016f644cdb3320b78a381027835d6091903f0513 +ECCompressedPoint = 031a95d8c8d08635ad7fa0facd3a9fe56f8f42451b58ea8a29f7d28b0f +ECPrivateScalar = aa17cf0a0c8064bf6b509c212e48293d61e0d97acc85f8dea5c99166 PKCS8 = 3078020100301006072a8648ce3d020106052b810400210461305f020101041caa17cf0a0c8064bf6b509c212e48293d61e0d97acc85f8dea5c99166a13c033a00041a95d8c8d08635ad7fa0facd3a9fe56f8f42451b58ea8a29f7d28b0fb214690b1149a69f016f644cdb3320b78a381027835d6091903f0513 SPKI = 304e301006072a8648ce3d020106052b81040021033a00041a95d8c8d08635ad7fa0facd3a9fe56f8f42451b58ea8a29f7d28b0fb214690b1149a69f016f644cdb3320b78a381027835d6091903f0513 @@ -77,6 +88,9 @@ Algorithm = EC-P-384 Bits = 384 ECCurve = secp384r1 +ECUncompressedPoint = 04826b3df548ad2e0b96436cb13508e88745a33b4b06cf485ad8350824b4dfe01ee66a5e1d1aaebfebcaa6337a1f33c338afc0d59b7ce7e389f73f66c9c4a44bbfcf570aec5cc52e7b6608c9061ab4d72de933448c39dd9238177917d398c22c5e +ECCompressedPoint = 02826b3df548ad2e0b96436cb13508e88745a33b4b06cf485ad8350824b4dfe01ee66a5e1d1aaebfebcaa6337a1f33c338 +ECPrivateScalar = a29991b86091401328e62ec8caadd0482f887ff0936910e42a56c19f48cbe87331037a4e2b36f1091dd4a26ee2d2b01f PKCS8 = 3081b6020100301006072a8648ce3d020106052b8104002204819e30819b0201010430a29991b86091401328e62ec8caadd0482f887ff0936910e42a56c19f48cbe87331037a4e2b36f1091dd4a26ee2d2b01fa16403620004826b3df548ad2e0b96436cb13508e88745a33b4b06cf485ad8350824b4dfe01ee66a5e1d1aaebfebcaa6337a1f33c338afc0d59b7ce7e389f73f66c9c4a44bbfcf570aec5cc52e7b6608c9061ab4d72de933448c39dd9238177917d398c22c5e SPKI = 3076301006072a8648ce3d020106052b8104002203620004826b3df548ad2e0b96436cb13508e88745a33b4b06cf485ad8350824b4dfe01ee66a5e1d1aaebfebcaa6337a1f33c338afc0d59b7ce7e389f73f66c9c4a44bbfcf570aec5cc52e7b6608c9061ab4d72de933448c39dd9238177917d398c22c5e @@ -86,9 +100,79 @@ Algorithm = EC-P-521 Bits = 521 ECCurve = secp521r1 +ECUncompressedPoint = 0400f58adcbe5c07f6b500fadd3209487e38f9567f97c2204435a4eb140739905c201407e2530a6667216aad01fb849bcbefa3862b2f187f13c9a87923d378a0a184df017b4bb93f4766531785878458da6aaa525724e10dfb1f35cfbe55c56fc705714295ea74b6c3e714152ad78e929f5415683aed9bc7c68f0329934177829d715f03f2 +ECCompressedPoint = 0200f58adcbe5c07f6b500fadd3209487e38f9567f97c2204435a4eb140739905c201407e2530a6667216aad01fb849bcbefa3862b2f187f13c9a87923d378a0a184df +ECPrivateScalar = 002eeee9c16b9b4d2dee606f443cf3b41d2899a4734ff7555b54c735afb34a6912f81c68a89ea9b427c69a1026d98ef1d7f9c683aec5c5103d9a4c21e403c638412f PKCS8 = 3081ee020100301006072a8648ce3d020106052b810400230481d63081d30201010442002eeee9c16b9b4d2dee606f443cf3b41d2899a4734ff7555b54c735afb34a6912f81c68a89ea9b427c69a1026d98ef1d7f9c683aec5c5103d9a4c21e403c638412fa18189038186000400f58adcbe5c07f6b500fadd3209487e38f9567f97c2204435a4eb140739905c201407e2530a6667216aad01fb849bcbefa3862b2f187f13c9a87923d378a0a184df017b4bb93f4766531785878458da6aaa525724e10dfb1f35cfbe55c56fc705714295ea74b6c3e714152ad78e929f5415683aed9bc7c68f0329934177829d715f03f2 SPKI = 30819b301006072a8648ce3d020106052b81040023038186000400f58adcbe5c07f6b500fadd3209487e38f9567f97c2204435a4eb140739905c201407e2530a6667216aad01fb849bcbefa3862b2f187f13c9a87923d378a0a184df017b4bb93f4766531785878458da6aaa525724e10dfb1f35cfbe55c56fc705714295ea74b6c3e714152ad78e929f5415683aed9bc7c68f0329934177829d715f03f2 +# Compressed and uncompressed forms swapped +Key = P-256-WrongForm-1 +Algorithm = EC-P-256 +ECUncompressedPoint = 032c150f429ce70f216c252cf5e062ce1f639cd5d165c7f89424072c27197d78b3 +Error = DECODE_ERROR + +Key = P-256-WrongForm-2 +Algorithm = EC-P-256 +ECCompressedPoint = 042c150f429ce70f216c252cf5e062ce1f639cd5d165c7f89424072c27197d78b33b920e95cdb664e990dcf0cfea0d94e2a8e6af9d0e58056e653104925b9fe6c9 +Error = DECODE_ERROR + +# Wrong length +Key = P-256-Scalar-Too-Short +Algorithm = EC-P-256 +ECPrivateScalar = 8a872fb62893c4d1ffc5b9f0f91758069f8352e08fa05a49f8db926cb57287 +Error = DECODE_ERROR + +Key = P-256-Scalar-Too-Long +Algorithm = EC-P-256 +ECPrivateScalar = 008a872fb62893c4d1ffc5b9f0f91758069f8352e08fa05a49f8db926cb5728725 +Error = DECODE_ERROR + +# Scalar not reduced. +Key = P-256-Scalar-NotReduced-1 +Algorithm = EC-P-256 +ECPrivateScalar = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551 +Error = INVALID_SCALAR + +Key = P-256-Scalar-NotReduced-2 +Algorithm = EC-P-256 +ECPrivateScalar = ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552 +Error = INVALID_SCALAR + +# Truncated points +Key = P-256-Truncated-Uncompressed +Algorithm = EC-P-256 +ECUncompressedPoint = 042c150f429ce70f216c252cf5e062ce1f639cd5d165c7f89424072c27197d78b33b920e95cdb664e990dcf0cfea0d94e2a8e6af9d0e58056e653104925b9f +Error = INVALID_ENCODING + +Key = P-256-Truncated-Compressed +Algorithm = EC-P-256 +ECCompressedPoint = 032c150f429ce70f216c252cf5e062ce1f639cd5d165c7f89424072c27197d78 +Error = INVALID_ENCODING + +# First byte is wrong +Key = P-256-Bad-First-Byte-Uncompressed +Algorithm = EC-P-256 +ECUncompressedPoint = 052c150f429ce70f216c252cf5e062ce1f639cd5d165c7f89424072c27197d78b33b920e95cdb664e990dcf0cfea0d94e2a8e6af9d0e58056e653104925b9fe6c9 +Error = DECODE_ERROR + +Key = P-256-Bad-First-Byte-Compressed +Algorithm = EC-P-256 +ECCompressedPoint = 052c150f429ce70f216c252cf5e062ce1f639cd5d165c7f89424072c27197d78b3 +Error = INVALID_ENCODING + +# Uncompressed point not on the curve +Key = P-256-Not-On-Curve +Algorithm = EC-P-256 +ECUncompressedPoint = 042c150f429ce70f216c252cf5e062ce1f639cd5d165c7f89424072c27197d78b33b920e95cdb664e990dcf0cfea0d94e2a8e6af9d0e58056e653104925b9fe6c8 +Error = POINT_IS_NOT_ON_CURVE + +# Compressed point has no square root +Key = P-256-No-Square-Root-2 +Algorithm = EC-P-256 +ECCompressedPoint = 03ffffffff00000001000000000000000000000000fffffffffffffffffffffffe +Error = INVALID_COMPRESSED_POINT + # ECDSA tests @@ -152,13 +236,18 @@ Algorithm = EC-P-256 Bits = 256 ECCurve = prime256v1 -PKCS8 = 3041020100301306072a8648ce3d020106082a8648ce3d0301070427302502010104207d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534 +ECUncompressedPoint = 04ead218590119e8876b29146ff89ca61770c4edbbf97d38ce385ed281d8a6b23028af61281fd35e2fa7002523acc85a429cb06ee6648325389f59edfce1405141 +ECCompressedPoint = 03ead218590119e8876b29146ff89ca61770c4edbbf97d38ce385ed281d8a6b230 +ECPrivateScalar = 7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534 +PKCS8 = 308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b02010104207d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534a14403420004ead218590119e8876b29146ff89ca61770c4edbbf97d38ce385ed281d8a6b23028af61281fd35e2fa7002523acc85a429cb06ee6648325389f59edfce1405141 SPKI = 3059301306072a8648ce3d020106082a8648ce3d03010703420004ead218590119e8876b29146ff89ca61770c4edbbf97d38ce385ed281d8a6b23028af61281fd35e2fa7002523acc85a429cb06ee6648325389f59edfce1405141 Key = ECDH-P256-Peer Algorithm = EC-P-256 Bits = 256 ECCurve = prime256v1 +ECUncompressedPoint = 04700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac +ECCompressedPoint = 02700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287 SPKI = 3059301306072a8648ce3d020106082a8648ce3d03010703420004700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac Derive = ECDH-P256-Ours/Private
diff --git a/include/openssl/evp.h b/include/openssl/evp.h index 8f523ac..ee5cdae 100644 --- a/include/openssl/evp.h +++ b/include/openssl/evp.h
@@ -1119,6 +1119,50 @@ // EC specific control functions. +// The following functions decode `len` bytes from `in` as an EC public key in +// compressed or uncompressed X9.62 format, respectively. The EC curve is +// determined by `alg`, which must be an EC algorithm (i.e. one of +// `EVP_pkey_ec_*`). +// +// The functions return a newly-allocated `EVP_PKEY` on success or NULL on +// error. +// +// To support both uncompressed and compressed forms, call +// `EVP_PKEY_from_ec_uncompressed_point` when the input begins with 0x04 and +// `EVP_PKEY_from_ec_compressed_point` otherwise. Uncompressed form is more +// common. +OPENSSL_EXPORT EVP_PKEY *EVP_PKEY_from_ec_uncompressed_point( + const EVP_PKEY_ALG *alg, const uint8_t *in, size_t len); +OPENSSL_EXPORT EVP_PKEY *EVP_PKEY_from_ec_compressed_point( + const EVP_PKEY_ALG *alg, const uint8_t *in, size_t len); + +// The following functions marshal `key`'s EC public key in compressed or +// uncompressed X9.62 format, respectively, and write the result to `cbb`. They +// return one on success and zero on error. It is an error if `key` is not an EC +// key. +OPENSSL_EXPORT int EVP_PKEY_marshal_ec_uncompressed_point(CBB *cbb, + const EVP_PKEY *key); +OPENSSL_EXPORT int EVP_PKEY_marshal_ec_compressed_point(CBB *cbb, + const EVP_PKEY *key); + +// EVP_PKEY_from_ec_private_scalar decodes `len` bytes from `in` as an EC +// private key, encoded as a big-endian, zero-padded integer. The EC curve is +// determined by `alg`, which must be an EC algorithm (i.e. one of +// `EVP_pkey_ec_*`). The input must be fully reduced and padded to the size of +// the group order (e.g. 32 bytes for P-256) or the import will fail. +// +// This function returns a newly-allocated `EVP_PKEY` on success or NULL on +// error. +OPENSSL_EXPORT EVP_PKEY *EVP_PKEY_from_ec_private_scalar( + const EVP_PKEY_ALG *alg, const uint8_t *in, size_t len); + +// EVP_PKEY_marshal_ec_private_scalar marshals `key`'s private key as a +// big-endian, zero-padded integer EC scalar and writes the result to `cbb`. It +// returns one on success or zero on error. It is an error if `key` is not an EC +// key. +OPENSSL_EXPORT int EVP_PKEY_marshal_ec_private_scalar(CBB *cbb, + const EVP_PKEY *key); + // EVP_PKEY_get_ec_curve_nid returns `pkey`'s curve as a NID constant, such as // `NID_X9_62_prime256v1`, or `NID_undef` if `pkey` is not an EC key. OPENSSL_EXPORT int EVP_PKEY_get_ec_curve_nid(const EVP_PKEY *pkey);
diff --git a/include/openssl/prefix_symbols.h b/include/openssl/prefix_symbols.h index a2fec15..4cf8e44 100644 --- a/include/openssl/prefix_symbols.h +++ b/include/openssl/prefix_symbols.h
@@ -1168,6 +1168,9 @@ #pragma redefine_extname EVP_PKEY_encrypt_init BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_PKEY_encrypt_init) #pragma redefine_extname EVP_PKEY_eq BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_PKEY_eq) #pragma redefine_extname EVP_PKEY_free BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_PKEY_free) +#pragma redefine_extname EVP_PKEY_from_ec_compressed_point BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_PKEY_from_ec_compressed_point) +#pragma redefine_extname EVP_PKEY_from_ec_private_scalar BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_PKEY_from_ec_private_scalar) +#pragma redefine_extname EVP_PKEY_from_ec_uncompressed_point BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_PKEY_from_ec_uncompressed_point) #pragma redefine_extname EVP_PKEY_from_private_key_info BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_PKEY_from_private_key_info) #pragma redefine_extname EVP_PKEY_from_private_seed BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_PKEY_from_private_seed) #pragma redefine_extname EVP_PKEY_from_raw_private_key BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_PKEY_from_raw_private_key) @@ -1195,6 +1198,9 @@ #pragma redefine_extname EVP_PKEY_is_opaque BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_PKEY_is_opaque) #pragma redefine_extname EVP_PKEY_keygen BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_PKEY_keygen) #pragma redefine_extname EVP_PKEY_keygen_init BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_PKEY_keygen_init) +#pragma redefine_extname EVP_PKEY_marshal_ec_compressed_point BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_PKEY_marshal_ec_compressed_point) +#pragma redefine_extname EVP_PKEY_marshal_ec_private_scalar BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_PKEY_marshal_ec_private_scalar) +#pragma redefine_extname EVP_PKEY_marshal_ec_uncompressed_point BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_PKEY_marshal_ec_uncompressed_point) #pragma redefine_extname EVP_PKEY_missing_parameters BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_PKEY_missing_parameters) #pragma redefine_extname EVP_PKEY_new BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_PKEY_new) #pragma redefine_extname EVP_PKEY_new_raw_private_key BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_PKEY_new_raw_private_key) @@ -4291,6 +4297,9 @@ #define EVP_PKEY_encrypt_init BORINGSSL_ADD_PREFIX(EVP_PKEY_encrypt_init) #define EVP_PKEY_eq BORINGSSL_ADD_PREFIX(EVP_PKEY_eq) #define EVP_PKEY_free BORINGSSL_ADD_PREFIX(EVP_PKEY_free) +#define EVP_PKEY_from_ec_compressed_point BORINGSSL_ADD_PREFIX(EVP_PKEY_from_ec_compressed_point) +#define EVP_PKEY_from_ec_private_scalar BORINGSSL_ADD_PREFIX(EVP_PKEY_from_ec_private_scalar) +#define EVP_PKEY_from_ec_uncompressed_point BORINGSSL_ADD_PREFIX(EVP_PKEY_from_ec_uncompressed_point) #define EVP_PKEY_from_private_key_info BORINGSSL_ADD_PREFIX(EVP_PKEY_from_private_key_info) #define EVP_PKEY_from_private_seed BORINGSSL_ADD_PREFIX(EVP_PKEY_from_private_seed) #define EVP_PKEY_from_raw_private_key BORINGSSL_ADD_PREFIX(EVP_PKEY_from_raw_private_key) @@ -4318,6 +4327,9 @@ #define EVP_PKEY_is_opaque BORINGSSL_ADD_PREFIX(EVP_PKEY_is_opaque) #define EVP_PKEY_keygen BORINGSSL_ADD_PREFIX(EVP_PKEY_keygen) #define EVP_PKEY_keygen_init BORINGSSL_ADD_PREFIX(EVP_PKEY_keygen_init) +#define EVP_PKEY_marshal_ec_compressed_point BORINGSSL_ADD_PREFIX(EVP_PKEY_marshal_ec_compressed_point) +#define EVP_PKEY_marshal_ec_private_scalar BORINGSSL_ADD_PREFIX(EVP_PKEY_marshal_ec_private_scalar) +#define EVP_PKEY_marshal_ec_uncompressed_point BORINGSSL_ADD_PREFIX(EVP_PKEY_marshal_ec_uncompressed_point) #define EVP_PKEY_missing_parameters BORINGSSL_ADD_PREFIX(EVP_PKEY_missing_parameters) #define EVP_PKEY_new BORINGSSL_ADD_PREFIX(EVP_PKEY_new) #define EVP_PKEY_new_raw_private_key BORINGSSL_ADD_PREFIX(EVP_PKEY_new_raw_private_key)
diff --git a/rust/bssl-crypto/src/ec.rs b/rust/bssl-crypto/src/ec.rs index 1c3fa1e..134993c 100644 --- a/rust/bssl-crypto/src/ec.rs +++ b/rust/bssl-crypto/src/ec.rs
@@ -357,6 +357,9 @@ // BoringSSL allows an `EC_KEY` to have a private scalar without a // public point, but `Key` is never exposed in that state. + // + // TODO(crbug.com/42290404): `EC_KEY_oct2priv` should fill in the public + // key. // Safety: `key.0` is valid by construction. The returned value is // still owned the `EC_KEY`.