Add EVP_PKEY_ALG-based raw public/private key importers

Keeping it to X25519/Ed25519 for now, but arguably we could pick "raw"
options for most of our keys. The problem is just when there's a couple
different options. (ECPrivateKey or just the serialized scalar for EC
keys, compressed vs uncompressed points.)

Bug: 42290364
Change-Id: I1e1a0a3fa971f27a962946301091ebe11bac2725
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/81667
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
This commit is contained in:
David Benjamin
2025-08-26 17:56:24 -04:00
committed by Boringssl LUCI CQ
parent 74c3b4b7fd
commit 208361a22e
8 changed files with 84 additions and 59 deletions

View File

@@ -216,56 +216,60 @@ int EVP_PKEY_set_type(EVP_PKEY *pkey, int type) {
return 1;
}
EVP_PKEY *EVP_PKEY_from_raw_private_key(const EVP_PKEY_ALG *alg,
const uint8_t *in, size_t len) {
if (alg->method->set_priv_raw == nullptr) {
OPENSSL_PUT_ERROR(EVP, EVP_R_UNSUPPORTED_ALGORITHM);
return nullptr;
}
bssl::UniquePtr<EVP_PKEY> ret(EVP_PKEY_new());
if (ret == nullptr || !alg->method->set_priv_raw(ret.get(), in, len)) {
return nullptr;
}
return ret.release();
}
EVP_PKEY *EVP_PKEY_from_raw_public_key(const EVP_PKEY_ALG *alg,
const uint8_t *in, size_t len) {
if (alg->method->set_pub_raw == nullptr) {
OPENSSL_PUT_ERROR(EVP, EVP_R_UNSUPPORTED_ALGORITHM);
return nullptr;
}
bssl::UniquePtr<EVP_PKEY> ret(EVP_PKEY_new());
if (ret == nullptr || !alg->method->set_pub_raw(ret.get(), in, len)) {
return nullptr;
}
return ret.release();
}
EVP_PKEY *EVP_PKEY_new_raw_private_key(int type, ENGINE *unused,
const uint8_t *in, size_t len) {
// To avoid pulling in all key types, look for specifically the key types that
// support |set_priv_raw|.
const EVP_PKEY_ASN1_METHOD *method;
switch (type) {
case EVP_PKEY_X25519:
method = &x25519_asn1_meth;
break;
return EVP_PKEY_from_raw_private_key(EVP_pkey_x25519(), in, len);
case EVP_PKEY_ED25519:
method = &ed25519_asn1_meth;
break;
return EVP_PKEY_from_raw_private_key(EVP_pkey_ed25519(), in, len);
default:
OPENSSL_PUT_ERROR(EVP, EVP_R_UNSUPPORTED_ALGORITHM);
return nullptr;
}
bssl::UniquePtr<EVP_PKEY> ret(EVP_PKEY_new());
if (ret == nullptr || //
!method->set_priv_raw(ret.get(), in, len)) {
return nullptr;
}
return ret.release();
}
EVP_PKEY *EVP_PKEY_new_raw_public_key(int type, ENGINE *unused,
const uint8_t *in, size_t len) {
// To avoid pulling in all key types, look for specifically the key types that
// support |set_pub_raw|.
const EVP_PKEY_ASN1_METHOD *method;
switch (type) {
case EVP_PKEY_X25519:
method = &x25519_asn1_meth;
break;
return EVP_PKEY_from_raw_public_key(EVP_pkey_x25519(), in, len);
case EVP_PKEY_ED25519:
method = &ed25519_asn1_meth;
break;
return EVP_PKEY_from_raw_public_key(EVP_pkey_ed25519(), in, len);
default:
OPENSSL_PUT_ERROR(EVP, EVP_R_UNSUPPORTED_ALGORITHM);
return nullptr;
}
bssl::UniquePtr<EVP_PKEY> ret(EVP_PKEY_new());
if (ret == nullptr || //
!method->set_pub_raw(ret.get(), in, len)) {
return nullptr;
}
return ret.release();
}
int EVP_PKEY_get_raw_private_key(const EVP_PKEY *pkey, uint8_t *out,

View File

@@ -704,8 +704,8 @@ TEST(EVPExtraTest, Ed25519) {
};
// Create a public key.
bssl::UniquePtr<EVP_PKEY> pubkey(EVP_PKEY_new_raw_public_key(
EVP_PKEY_ED25519, nullptr, kPublicKey, sizeof(kPublicKey)));
bssl::UniquePtr<EVP_PKEY> pubkey(EVP_PKEY_from_raw_public_key(
EVP_pkey_ed25519(), kPublicKey, sizeof(kPublicKey)));
ASSERT_TRUE(pubkey);
EXPECT_EQ(EVP_PKEY_ED25519, EVP_PKEY_id(pubkey.get()));
@@ -753,8 +753,8 @@ TEST(EVPExtraTest, Ed25519) {
cbb.Reset();
// Create a private key.
bssl::UniquePtr<EVP_PKEY> privkey(EVP_PKEY_new_raw_private_key(
EVP_PKEY_ED25519, NULL, kPrivateKeySeed, sizeof(kPrivateKeySeed)));
bssl::UniquePtr<EVP_PKEY> privkey(EVP_PKEY_from_raw_private_key(
EVP_pkey_ed25519(), kPrivateKeySeed, sizeof(kPrivateKeySeed)));
ASSERT_TRUE(privkey);
EXPECT_EQ(EVP_PKEY_ED25519, EVP_PKEY_id(privkey.get()));
@@ -797,8 +797,8 @@ TEST(EVPExtraTest, Ed25519) {
EXPECT_EQ(1, EVP_PKEY_cmp(pubkey.get(), privkey.get()));
static const uint8_t kZeros[32] = {0};
bssl::UniquePtr<EVP_PKEY> pubkey2(EVP_PKEY_new_raw_public_key(
EVP_PKEY_ED25519, nullptr, kZeros, sizeof(kZeros)));
bssl::UniquePtr<EVP_PKEY> pubkey2(
EVP_PKEY_from_raw_public_key(EVP_pkey_ed25519(), kZeros, sizeof(kZeros)));
ASSERT_TRUE(pubkey2);
EXPECT_EQ(0, EVP_PKEY_cmp(pubkey.get(), pubkey2.get()));
EXPECT_EQ(0, EVP_PKEY_cmp(privkey.get(), pubkey2.get()));
@@ -1133,6 +1133,10 @@ TEST(EVPExtraTest, RawKeyUnsupported) {
EVP_PKEY_new_raw_public_key(EVP_PKEY_RSA, nullptr, kKey, sizeof(kKey)));
EXPECT_FALSE(
EVP_PKEY_new_raw_private_key(EVP_PKEY_RSA, nullptr, kKey, sizeof(kKey)));
EXPECT_FALSE(
EVP_PKEY_from_raw_public_key(EVP_pkey_rsa(), kKey, sizeof(kKey)));
EXPECT_FALSE(
EVP_PKEY_from_raw_private_key(EVP_pkey_rsa(), kKey, sizeof(kKey)));
}
// The default salt length for PSS should be |RSA_PSS_SALTLEN_DIGEST|.

View File

@@ -215,8 +215,8 @@ static bool ImportKey(FileTest *t, KeyMap *key_map, KeyRole key_role) {
if (!t->GetBytes(&raw, "RawPublic")) {
return false;
}
new_key.reset(EVP_PKEY_new_raw_public_key(alg_info.pkey_id, nullptr,
raw.data(), raw.size()));
new_key.reset(
EVP_PKEY_from_raw_public_key(alg_info.alg, raw.data(), raw.size()));
if (new_key == nullptr) {
return false;
}
@@ -227,8 +227,8 @@ static bool ImportKey(FileTest *t, KeyMap *key_map, KeyRole key_role) {
if (!t->GetBytes(&raw, "RawPrivate")) {
return false;
}
new_key.reset(EVP_PKEY_new_raw_private_key(alg_info.pkey_id, nullptr,
raw.data(), raw.size()));
new_key.reset(
EVP_PKEY_from_raw_private_key(alg_info.alg, raw.data(), raw.size()));
if (new_key == nullptr) {
return false;
}

View File

@@ -649,11 +649,11 @@ class TrustTokenProtocolTestBase : public ::testing::Test {
uint8_t public_key[32], private_key[64];
ED25519_keypair(public_key, private_key);
bssl::UniquePtr<EVP_PKEY> priv(EVP_PKEY_new_raw_private_key(
EVP_PKEY_ED25519, nullptr, private_key, 32));
bssl::UniquePtr<EVP_PKEY> priv(
EVP_PKEY_from_raw_private_key(EVP_pkey_ed25519(), private_key, 32));
ASSERT_TRUE(priv);
bssl::UniquePtr<EVP_PKEY> pub(
EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, nullptr, public_key, 32));
EVP_PKEY_from_raw_public_key(EVP_pkey_ed25519(), public_key, 32));
ASSERT_TRUE(pub);
TRUST_TOKEN_CLIENT_set_srr_key(client.get(), pub.get());
@@ -899,10 +899,10 @@ TEST_P(TrustTokenProtocolTest, IssuedWithBadKeyID) {
uint8_t public_key[32], private_key[64];
ED25519_keypair(public_key, private_key);
bssl::UniquePtr<EVP_PKEY> priv(
EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, nullptr, private_key, 32));
EVP_PKEY_from_raw_private_key(EVP_pkey_ed25519(), private_key, 32));
ASSERT_TRUE(priv);
bssl::UniquePtr<EVP_PKEY> pub(
EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, nullptr, public_key, 32));
EVP_PKEY_from_raw_public_key(EVP_pkey_ed25519(), public_key, 32));
ASSERT_TRUE(pub);
TRUST_TOKEN_CLIENT_set_srr_key(client.get(), pub.get());

View File

@@ -2552,10 +2552,10 @@ TEST(X509Test, Ed25519Sign) {
ED25519_keypair(pub_bytes, priv_bytes);
bssl::UniquePtr<EVP_PKEY> pub(
EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, nullptr, pub_bytes, 32));
EVP_PKEY_from_raw_public_key(EVP_pkey_ed25519(), pub_bytes, 32));
ASSERT_TRUE(pub);
bssl::UniquePtr<EVP_PKEY> priv(
EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, nullptr, priv_bytes, 32));
EVP_PKEY_from_raw_private_key(EVP_pkey_ed25519(), priv_bytes, 32));
ASSERT_TRUE(priv);
bssl::ScopedEVP_MD_CTX md_ctx;

View File

@@ -344,20 +344,18 @@ OPENSSL_EXPORT int EVP_marshal_private_key(CBB *cbb, const EVP_PKEY *key);
// RFC 7748 and RFC 8032, respectively. Note the RFC 8032 private key format is
// the 32-byte prefix of |ED25519_sign|'s 64-byte private key.
// EVP_PKEY_new_raw_private_key returns a newly allocated |EVP_PKEY| wrapping a
// private key of the specified type. It returns one on success and zero on
// error.
OPENSSL_EXPORT EVP_PKEY *EVP_PKEY_new_raw_private_key(int type, ENGINE *unused,
// EVP_PKEY_from_raw_private_key interprets |in| as a raw private key of type
// |alg| and returns a newly-allocated |EVP_PKEY|, or nullptr on error.
OPENSSL_EXPORT EVP_PKEY *EVP_PKEY_from_raw_private_key(const EVP_PKEY_ALG *alg,
const uint8_t *in,
size_t len);
// EVP_PKEY_from_raw_public_key interprets |in| as a raw public key of type
// |alg| and returns a newly-allocated |EVP_PKEY|, or nullptr on error.
OPENSSL_EXPORT EVP_PKEY *EVP_PKEY_from_raw_public_key(const EVP_PKEY_ALG *alg,
const uint8_t *in,
size_t len);
// EVP_PKEY_new_raw_public_key returns a newly allocated |EVP_PKEY| wrapping a
// public key of the specified type. It returns one on success and zero on
// error.
OPENSSL_EXPORT EVP_PKEY *EVP_PKEY_new_raw_public_key(int type, ENGINE *unused,
const uint8_t *in,
size_t len);
// EVP_PKEY_get_raw_private_key outputs the private key for |pkey| in raw form.
// If |out| is NULL, it sets |*out_len| to the size of the raw private key.
// Otherwise, it writes at most |*out_len| bytes to |out| and sets |*out_len| to
@@ -1203,6 +1201,26 @@ OPENSSL_EXPORT int EVP_PKEY_assign(EVP_PKEY *pkey, int type, void *key);
// EVP_PKEY_type returns |nid|.
OPENSSL_EXPORT int EVP_PKEY_type(int nid);
// EVP_PKEY_new_raw_private_key interprets |in| as a raw private key of type
// |type|, which must be an |EVP_PKEY_*| constant, such as |EVP_PKEY_X25519|,
// and returns a newly-allocated |EVP_PKEY|, or nullptr on error.
//
// Prefer |EVP_PKEY_from_raw_private_key|, which allows dead code elimination to
// discard algorithms that aren't reachable from the caller.
OPENSSL_EXPORT EVP_PKEY *EVP_PKEY_new_raw_private_key(int type, ENGINE *unused,
const uint8_t *in,
size_t len);
// EVP_PKEY_new_raw_public_key interprets |in| as a raw public key of type
// |type|, which must be an |EVP_PKEY_*| constant, such as |EVP_PKEY_X25519|,
// and returns a newly-allocated |EVP_PKEY|, or nullptr on error.
//
// Prefer |EVP_PKEY_from_raw_private_key|, which allows dead code elimination to
// discard algorithms that aren't reachable from the caller.
OPENSSL_EXPORT EVP_PKEY *EVP_PKEY_new_raw_public_key(int type, ENGINE *unused,
const uint8_t *in,
size_t len);
// Preprocessor compatibility section (hidden).
//

View File

@@ -176,9 +176,8 @@ impl PublicKey {
pub fn to_der_subject_public_key_info(&self) -> Buffer {
// Safety: this only copies from the `self.0` buffer.
let mut pkey = scoped::EvpPkey::from_ptr(unsafe {
bssl_sys::EVP_PKEY_new_raw_public_key(
bssl_sys::EVP_PKEY_ED25519,
/*unused=*/ core::ptr::null_mut(),
bssl_sys::EVP_PKEY_from_raw_public_key(
bssl_sys::EVP_pkey_ed25519(),
self.0.as_ffi_ptr(),
PUBLIC_KEY_LEN,
)

View File

@@ -1480,9 +1480,9 @@ static bool SpeedTrustToken(std::string name, const TRUST_TOKEN_METHOD *method,
uint8_t public_key[32], private_key[64];
ED25519_keypair(public_key, private_key);
bssl::UniquePtr<EVP_PKEY> priv(
EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, nullptr, private_key, 32));
EVP_PKEY_from_raw_private_key(EVP_pkey_ed25519(), private_key, 32));
bssl::UniquePtr<EVP_PKEY> pub(
EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, nullptr, public_key, 32));
EVP_PKEY_from_raw_public_key(EVP_pkey_ed25519(), public_key, 32));
if (!priv || !pub) {
fprintf(stderr, "failed to generate trust token SRR key.\n");
return false;