Add functions for "raw" EVP_PKEY serializations.

cryptography.io expects this API for Ed25519. Replace the now redundant
EVP_PKEY_new_ed25519_* functions. Ours were more static-linker-friendly,
but we weren't taking advantage of it and EVP_PKEY needs an overhaul to
be static-linker-friendly anyway.

Update-Note: EVP_PKEY_new_ed25519_public and
EVP_PKEY_new_ed25519_private are removed in favor of
EVP_PKEY_new_raw_{public,private}_key. I do not believe anyone was using
those functions.

Change-Id: Ibd12b4e9e37cd8dd4dbc7ebc4346ba52655d523a
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/35806
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/evp/evp.c b/crypto/evp/evp.c
index ed7cc85..de4db7e 100644
--- a/crypto/evp/evp.c
+++ b/crypto/evp/evp.c
@@ -330,7 +330,73 @@
   return 1;
 }
 
+EVP_PKEY *EVP_PKEY_new_raw_private_key(int type, ENGINE *unused,
+                                       const uint8_t *in, size_t len) {
+  EVP_PKEY *ret = EVP_PKEY_new();
+  if (ret == NULL ||
+      !EVP_PKEY_set_type(ret, type)) {
+    goto err;
+  }
 
+  if (ret->ameth->set_priv_raw == NULL) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    goto err;
+  }
+
+  if (!ret->ameth->set_priv_raw(ret, in, len)) {
+    goto err;
+  }
+
+  return ret;
+
+err:
+  EVP_PKEY_free(ret);
+  return NULL;
+}
+
+EVP_PKEY *EVP_PKEY_new_raw_public_key(int type, ENGINE *unused,
+                                      const uint8_t *in, size_t len) {
+  EVP_PKEY *ret = EVP_PKEY_new();
+  if (ret == NULL ||
+      !EVP_PKEY_set_type(ret, type)) {
+    goto err;
+  }
+
+  if (ret->ameth->set_pub_raw == NULL) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    goto err;
+  }
+
+  if (!ret->ameth->set_pub_raw(ret, in, len)) {
+    goto err;
+  }
+
+  return ret;
+
+err:
+  EVP_PKEY_free(ret);
+  return NULL;
+}
+
+int EVP_PKEY_get_raw_private_key(const EVP_PKEY *pkey, uint8_t *out,
+                                 size_t *out_len) {
+  if (pkey->ameth->get_priv_raw == NULL) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    return 0;
+  }
+
+  return pkey->ameth->get_priv_raw(pkey, out, out_len);
+}
+
+int EVP_PKEY_get_raw_public_key(const EVP_PKEY *pkey, uint8_t *out,
+                                size_t *out_len) {
+  if (pkey->ameth->get_pub_raw == NULL) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    return 0;
+  }
+
+  return pkey->ameth->get_pub_raw(pkey, out, out_len);
+}
 
 int EVP_PKEY_cmp_parameters(const EVP_PKEY *a, const EVP_PKEY *b) {
   if (a->type != b->type) {
diff --git a/crypto/evp/evp_extra_test.cc b/crypto/evp/evp_extra_test.cc
index 423661d..d033343 100644
--- a/crypto/evp/evp_extra_test.cc
+++ b/crypto/evp/evp_extra_test.cc
@@ -555,13 +555,10 @@
       0xa6, 0x23, 0x25, 0xaf, 0x02, 0x1a, 0x68, 0xf7, 0x07, 0x51, 0x1a,
   };
 
-  static const uint8_t kPrivateKey[64] = {
+  static const uint8_t kPrivateKeySeed[32] = {
       0x9d, 0x61, 0xb1, 0x9d, 0xef, 0xfd, 0x5a, 0x60, 0xba, 0x84, 0x4a,
       0xf4, 0x92, 0xec, 0x2c, 0xc4, 0x44, 0x49, 0xc5, 0x69, 0x7b, 0x32,
-      0x69, 0x19, 0x70, 0x3b, 0xac, 0x03, 0x1c, 0xae, 0x7f, 0x60, 0xd7,
-      0x5a, 0x98, 0x01, 0x82, 0xb1, 0x0a, 0xb7, 0xd5, 0x4b, 0xfe, 0xd3,
-      0xc9, 0x64, 0x07, 0x3a, 0x0e, 0xe1, 0x72, 0xf3, 0xda, 0xa6, 0x23,
-      0x25, 0xaf, 0x02, 0x1a, 0x68, 0xf7, 0x07, 0x51, 0x1a,
+      0x69, 0x19, 0x70, 0x3b, 0xac, 0x03, 0x1c, 0xae, 0x7f, 0x60,
   };
 
   static const uint8_t kPrivateKeyPKCS8[] = {
@@ -572,10 +569,38 @@
   };
 
   // Create a public key.
-  bssl::UniquePtr<EVP_PKEY> pubkey(EVP_PKEY_new_ed25519_public(kPublicKey));
+  bssl::UniquePtr<EVP_PKEY> pubkey(EVP_PKEY_new_raw_public_key(
+      EVP_PKEY_ED25519, nullptr, kPublicKey, sizeof(kPublicKey)));
   ASSERT_TRUE(pubkey);
   EXPECT_EQ(EVP_PKEY_ED25519, EVP_PKEY_id(pubkey.get()));
 
+  // The public key must be extractable.
+  uint8_t buf[32];
+  size_t len;
+  ASSERT_TRUE(EVP_PKEY_get_raw_public_key(pubkey.get(), nullptr, &len));
+  EXPECT_EQ(len, 32u);
+  ASSERT_TRUE(EVP_PKEY_get_raw_public_key(pubkey.get(), buf, &len));
+  EXPECT_EQ(Bytes(buf, len), Bytes(kPublicKey));
+  // Passing too large of a buffer is okay. The function will still only read
+  // 32 bytes.
+  len = 64;
+  ASSERT_TRUE(EVP_PKEY_get_raw_public_key(pubkey.get(), buf, &len));
+  EXPECT_EQ(Bytes(buf, len), Bytes(kPublicKey));
+  // Passing too small of a buffer is noticed.
+  len = 31;
+  EXPECT_FALSE(EVP_PKEY_get_raw_public_key(pubkey.get(), buf, &len));
+  uint32_t err = ERR_get_error();
+  EXPECT_EQ(ERR_LIB_EVP, ERR_GET_LIB(err));
+  EXPECT_EQ(EVP_R_BUFFER_TOO_SMALL, ERR_GET_REASON(err));
+  ERR_clear_error();
+
+  // There is no private key.
+  EXPECT_FALSE(EVP_PKEY_get_raw_private_key(pubkey.get(), nullptr, &len));
+  err = ERR_get_error();
+  EXPECT_EQ(ERR_LIB_EVP, ERR_GET_LIB(err));
+  EXPECT_EQ(EVP_R_NOT_A_PRIVATE_KEY, ERR_GET_REASON(err));
+  ERR_clear_error();
+
   // The public key must encode properly.
   bssl::ScopedCBB cbb;
   uint8_t *der;
@@ -589,16 +614,40 @@
   // The public key must gracefully fail to encode as a private key.
   ASSERT_TRUE(CBB_init(cbb.get(), 0));
   EXPECT_FALSE(EVP_marshal_private_key(cbb.get(), pubkey.get()));
-  uint32_t err = ERR_get_error();
+  err = ERR_get_error();
   EXPECT_EQ(ERR_LIB_EVP, ERR_GET_LIB(err));
   EXPECT_EQ(EVP_R_NOT_A_PRIVATE_KEY, ERR_GET_REASON(err));
+  ERR_clear_error();
   cbb.Reset();
 
   // Create a private key.
-  bssl::UniquePtr<EVP_PKEY> privkey(EVP_PKEY_new_ed25519_private(kPrivateKey));
+  bssl::UniquePtr<EVP_PKEY> privkey(EVP_PKEY_new_raw_private_key(
+      EVP_PKEY_ED25519, NULL, kPrivateKeySeed, sizeof(kPrivateKeySeed)));
   ASSERT_TRUE(privkey);
   EXPECT_EQ(EVP_PKEY_ED25519, EVP_PKEY_id(privkey.get()));
 
+  // The private key must be extractable.
+  ASSERT_TRUE(EVP_PKEY_get_raw_private_key(privkey.get(), nullptr, &len));
+  EXPECT_EQ(len, 32u);
+  ASSERT_TRUE(EVP_PKEY_get_raw_private_key(privkey.get(), buf, &len));
+  EXPECT_EQ(Bytes(buf, len), Bytes(kPrivateKeySeed));
+  // Passing too large of a buffer is okay. The function will still only read
+  // 32 bytes.
+  len = 64;
+  ASSERT_TRUE(EVP_PKEY_get_raw_private_key(privkey.get(), buf, &len));
+  EXPECT_EQ(Bytes(buf, len), Bytes(kPrivateKeySeed));
+  // Passing too small of a buffer is noticed.
+  len = 31;
+  EXPECT_FALSE(EVP_PKEY_get_raw_private_key(privkey.get(), buf, &len));
+  err = ERR_get_error();
+  EXPECT_EQ(ERR_LIB_EVP, ERR_GET_LIB(err));
+  EXPECT_EQ(EVP_R_BUFFER_TOO_SMALL, ERR_GET_REASON(err));
+  ERR_clear_error();
+  // The public key must be extractable.
+  len = 32;
+  ASSERT_TRUE(EVP_PKEY_get_raw_public_key(privkey.get(), buf, &len));
+  EXPECT_EQ(Bytes(buf, len), Bytes(kPublicKey));
+
   // The public key must encode from the private key.
   ASSERT_TRUE(CBB_init(cbb.get(), 0));
   ASSERT_TRUE(EVP_marshal_public_key(cbb.get(), privkey.get()));
@@ -617,7 +666,8 @@
   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_ed25519_public(kZeros));
+  bssl::UniquePtr<EVP_PKEY> pubkey2(EVP_PKEY_new_raw_public_key(
+      EVP_PKEY_ED25519, nullptr, 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()));
@@ -627,7 +677,6 @@
   ASSERT_TRUE(
       EVP_DigestSignInit(ctx.get(), nullptr, nullptr, nullptr, privkey.get()));
   EXPECT_FALSE(EVP_DigestSignUpdate(ctx.get(), nullptr, 0));
-  size_t len;
   EXPECT_FALSE(EVP_DigestSignFinal(ctx.get(), nullptr, &len));
   ERR_clear_error();
 
@@ -643,12 +692,12 @@
   ctx.Reset();
   ASSERT_TRUE(
       EVP_DigestSignInit(ctx.get(), nullptr, nullptr, nullptr, privkey.get()));
-  uint8_t buf[16];
-  len = sizeof(buf);
+  len = 31;
   EXPECT_FALSE(EVP_DigestSign(ctx.get(), buf, &len, nullptr /* msg */, 0));
   err = ERR_get_error();
   EXPECT_EQ(ERR_LIB_EVP, ERR_GET_LIB(err));
   EXPECT_EQ(EVP_R_BUFFER_TOO_SMALL, ERR_GET_REASON(err));
+  ERR_clear_error();
 }
 
 static void ExpectECGroupOnly(const EVP_PKEY *pkey, int nid) {
diff --git a/crypto/evp/evp_test.cc b/crypto/evp/evp_test.cc
index 4d74292..9df5493 100644
--- a/crypto/evp/evp_test.cc
+++ b/crypto/evp/evp_test.cc
@@ -179,7 +179,62 @@
       !t->GetBytes(&output, "Output")) {
     return false;
   }
-  EXPECT_EQ(Bytes(output), Bytes(der, der_len)) << "Re-encoding the key did not match.";
+  EXPECT_EQ(Bytes(output), Bytes(der, der_len))
+      << "Re-encoding the key did not match.";
+
+  if (t->HasAttribute("ExpectNoRawPrivate")) {
+    size_t len;
+    EXPECT_FALSE(EVP_PKEY_get_raw_private_key(pkey.get(), nullptr, &len));
+  } else if (t->HasAttribute("ExpectRawPrivate")) {
+    std::vector<uint8_t> expected;
+    if (!t->GetBytes(&expected, "ExpectRawPrivate")) {
+      return false;
+    }
+
+    std::vector<uint8_t> raw;
+    size_t len;
+    if (!EVP_PKEY_get_raw_private_key(pkey.get(), nullptr, &len)) {
+      return false;
+    }
+    raw.resize(len);
+    if (!EVP_PKEY_get_raw_private_key(pkey.get(), raw.data(), &len)) {
+      return false;
+    }
+    raw.resize(len);
+    EXPECT_EQ(Bytes(raw), Bytes(expected));
+
+    // Short buffers should be rejected.
+    raw.resize(len - 1);
+    len = raw.size();
+    EXPECT_FALSE(EVP_PKEY_get_raw_private_key(pkey.get(), raw.data(), &len));
+  }
+
+  if (t->HasAttribute("ExpectNoRawPublic")) {
+    size_t len;
+    EXPECT_FALSE(EVP_PKEY_get_raw_public_key(pkey.get(), nullptr, &len));
+  } else if (t->HasAttribute("ExpectRawPublic")) {
+    std::vector<uint8_t> expected;
+    if (!t->GetBytes(&expected, "ExpectRawPublic")) {
+      return false;
+    }
+
+    std::vector<uint8_t> raw;
+    size_t len;
+    if (!EVP_PKEY_get_raw_public_key(pkey.get(), nullptr, &len)) {
+      return false;
+    }
+    raw.resize(len);
+    if (!EVP_PKEY_get_raw_public_key(pkey.get(), raw.data(), &len)) {
+      return false;
+    }
+    raw.resize(len);
+    EXPECT_EQ(Bytes(raw), Bytes(expected));
+
+    // Short buffers should be rejected.
+    raw.resize(len - 1);
+    len = raw.size();
+    EXPECT_FALSE(EVP_PKEY_get_raw_public_key(pkey.get(), raw.data(), &len));
+  }
 
   // Save the key for future tests.
   const std::string &key_name = t->GetParameter();
diff --git a/crypto/evp/evp_tests.txt b/crypto/evp/evp_tests.txt
index 9dbe1cb..6090f5f 100644
--- a/crypto/evp/evp_tests.txt
+++ b/crypto/evp/evp_tests.txt
@@ -6,11 +6,15 @@
 PrivateKey = RSA-2048
 Type = RSA
 Input = 308204bc020100300d06092a864886f70d0101010500048204a6308204a20201000282010100cd0081ea7b2ae1ea06d59f7c73d9ffb94a09615c2e4ba7c636cef08dd3533ec3185525b015c769b99a77d6725bf9c3532a9b6e5f6627d5fb85160768d3dda9cbd35974511717dc3d309d2fc47ee41f97e32adb7f9dd864a1c4767a666ecd71bc1aacf5e7517f4b38594fea9b05e42d5ada9912008013e45316a4d9bb8ed086b88d28758bacaf922d46a868b485d239c9baeb0e2b64592710f42b2d1ea0a4b4802c0becab328f8a68b0073bdb546feea9809d2849912b390c1532bc7e29c7658f8175fae46f34332ff87bcab3e40649b98577869da0ea718353f0722754886913648760d122be676e0fc483dd20ffc31bda96a31966c9aa2e75ad03de47e1c44f02030100010282010060297ac7991b167a06d6b24758b8cbe208beb9b2d9ec9738bd80f90a2e35005dd7ce292d9e29ba885bd316fef1f20913bc0ac90d6b0808b2414d82104441d8624a33ce0233c8f780a48b375aff02d76712228a702484db3f9ebecccfbbee1709dba182800d949e9e4216e0bff3558388f8bd90da373a1d82743ec3fbdd1427fd16825a657a316912e8695365117ca2f845c909405fcac55f895fc15d20386c26ee78c9e99075029a178a6c1e4cf0c200e8a9cfb27e9d156f86e6c2adc22b1a84a1cd5ca5b2790875d79407c84b352395cb81cc3fed5bb043b69ede0c07204550025cee8c5f440170b6120bb48e0f747bcd8f522110850df043c428dfd187053102818100f6f961b47cbc035d3aedebc7de850a956b65ecdb9cf60764063f15aa48553c58d972fe6675056e35ddfdc37bf3b9f2f622ee271337256849c9bef2176fe8f7c3f8bb91ba374dd53baf3dec814d2bdec10c1fdc88cdd16876f26b1edfa3f094197edf4d42ff1fb2971103b898ca859c427287086a842ab410bb69cf2d35af6be302818100d47e724a7ff41048b270c2524a4101878b73159bb73d3dbc187b220e635b3534f96e243a184d93f860b6bfbb6b71c1ed9a1e1f458583023c301e96a692c1a08b53d0ec9ca910100d80451e3b7dc6a01bac4aecef8df798846bc235a08cbba2cf4c06804cc11219e95608c714e3f1430d491fadbba32a5751a04f97745834c9a502818021f2452bb9b95dfd028c914bf799f1ca77e89a95d50d3c16d384f8455f8bd7af9eb3dfa3d591d9842def235f7630a8e48c088ff6642e101794535a933e1e976fa8509fc728b2da0c4a1a08d7fcf37abaae1ff3001aca1dc1bbb05d9dffbaa1a09f7fb1eef38237d9ebccc722b9338436dde7119112798c26809c1a8dec4320610281801f7510aa62c2d8de4a3c53282781f41e02d0e8b402ae78432e449c48110161a11403f02d01880a8dcc938152d79721a4711a607ac4471ebf964810f95be47a45e60499e29f4c9773c83773404f606637728c2d0351bb03c326c8bb73a721e7fa5440ea2172bba1465fcc30dcb0d9f89930e815aa1f7f9729a857e00e0338dd590281804d1f0d756fe77e01099a652f50a88b7b685dc5bf00981d5d2376fd0c6fe29cd5b638734479305a73ad3c1599d39eae3bae035fbd6fed07c28de705933879a06e48e6a603686ed8e2560a5f6af1f2c24faf4aa960e382186f15eedce9a2491ae730680dd4cf778b70faa86826ab3223477cc91377b19a6d5a2eaea219760beed5
+ExpectNoRawPrivate
+ExpectNoRawPublic
 
 # The public half of the same key encoded as a SubjectPublicKeyInfo.
 PublicKey = RSA-2048-SPKI
 Type = RSA
 Input = 30820122300d06092a864886f70d01010105000382010f003082010a0282010100cd0081ea7b2ae1ea06d59f7c73d9ffb94a09615c2e4ba7c636cef08dd3533ec3185525b015c769b99a77d6725bf9c3532a9b6e5f6627d5fb85160768d3dda9cbd35974511717dc3d309d2fc47ee41f97e32adb7f9dd864a1c4767a666ecd71bc1aacf5e7517f4b38594fea9b05e42d5ada9912008013e45316a4d9bb8ed086b88d28758bacaf922d46a868b485d239c9baeb0e2b64592710f42b2d1ea0a4b4802c0becab328f8a68b0073bdb546feea9809d2849912b390c1532bc7e29c7658f8175fae46f34332ff87bcab3e40649b98577869da0ea718353f0722754886913648760d122be676e0fc483dd20ffc31bda96a31966c9aa2e75ad03de47e1c44f0203010001
+ExpectNoRawPrivate
+ExpectNoRawPublic
 
 # The same key but with a negative RSA modulus.
 PublicKey = RSA-2048-SPKI-Negative
@@ -31,21 +35,29 @@
 PrivateKey = RSA-512
 Type = RSA
 Input = 30820154020100300d06092a864886f70d01010105000482013e3082013a020100024100dd20403d976a38c9d79152d87b5c8e9f05033eadd7b7de709bf5b0c4a5182a97d18483526b02362b992e154a9f37faa396ca2685cdab8fec09877ebe705f4dd70203010001024055bebcca655d7e39de8a6eaa9d636db682161907064039544755c53eeb99ec618c03a210dbc61471eaba10c5c365c9726d6b7a96f54d455f7d168d49367270e1022100f21a05d9fd6817301ce49ce10448f9bdd44f5ef5b7557cd7d83155db46382ae7022100e9d1f7157783db2feab1936954ddc4e83aa365695868144cda1be6813b61d791022100d6001eb0040920860ce41fafdf23ca6dfbdf74e6e9f98cf3164cf5c16f9e727d02206f6f73f4b52b10517be6f9bc5f87fa0a3bb817e2e711636b651f9af1c85d4f21022063eff2e57f5b4ca20342cfe793e25526624e3692f192461f9e1ce7f13f2d72c8
+ExpectNoRawPrivate
+ExpectNoRawPublic
 
 # RSA 515 bit key.
 PrivateKey = RSA-515
 Type = RSA
 Input = 30820157020100300d06092a864886f70d0101010500048201413082013d0201000241054fa166e205e658bbe8a2dc35311c0c2b75b7e4569fd9642c8bae809279271fc824f26baa1166ea46298ca63379ea76adbada2b61e5066820a35beaec1aca227f020301000102410266c972be0d30e53ac2acb1aa13b4bd0401cccf212452a66b4615f7e943831f67b4ca48560582d0ca886044aaaaf87945252a848c1947944186e6eb83969bf91102210309e631761842cc8a2ccfd372c20a9cba21de1a199c30ab440bc6b51079f4e825022101bf715c1db432627ca7c29a293b9210f2eff1e92d12f306ebaa5334f8ee03dcd30221018ac58a765f2b8f37d434081fe5ff92b81735ead2f263f4968ccf63d61fbe3d0d0221015b247a1159a2d5a25d0db049593c6405f77f3a278c521d066e290c2a2d8fb59d0221026224aa31fd95c14d24fd03b8a195bba4cc88df7c37f5370a5ab19f882f1404d6
+ExpectNoRawPrivate
+ExpectNoRawPublic
 
 # EC P-256 key
 PrivateKey = P-256
 Type = EC
 Input = 308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b02010104208a872fb62893c4d1ffc5b9f0f91758069f8352e08fa05a49f8db926cb5728725a144034200042c150f429ce70f216c252cf5e062ce1f639cd5d165c7f89424072c27197d78b33b920e95cdb664e990dcf0cfea0d94e2a8e6af9d0e58056e653104925b9fe6c9
+ExpectNoRawPrivate
+ExpectNoRawPublic
 
 # The same key as above with the optional public key omitted.
 PrivateKey = P-256-MissingPublic
 Type = EC
 Input = 3041020100301306072a8648ce3d020106082a8648ce3d0301070427302502010104208a872fb62893c4d1ffc5b9f0f91758069f8352e08fa05a49f8db926cb5728725
+ExpectNoRawPrivate
+ExpectNoRawPublic
 
 # The same key as above with redundant parameters.
 PrivateKey = P-256-ExtraParameters
@@ -53,6 +65,8 @@
 Input = 308193020100301306072a8648ce3d020106082a8648ce3d0301070479307702010104208a872fb62893c4d1ffc5b9f0f91758069f8352e08fa05a49f8db926cb5728725a00a06082a8648ce3d030107a144034200042c150f429ce70f216c252cf5e062ce1f639cd5d165c7f89424072c27197d78b33b920e95cdb664e990dcf0cfea0d94e2a8e6af9d0e58056e653104925b9fe6c9
 # The key re-encodes with the parameters removed.
 Output = 308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b02010104208a872fb62893c4d1ffc5b9f0f91758069f8352e08fa05a49f8db926cb5728725a144034200042c150f429ce70f216c252cf5e062ce1f639cd5d165c7f89424072c27197d78b33b920e95cdb664e990dcf0cfea0d94e2a8e6af9d0e58056e653104925b9fe6c9
+ExpectNoRawPrivate
+ExpectNoRawPublic
 
 # The same key, but with the redundant parameters in the ECPrivateKey mismatched.
 PrivateKey = P-256-BadInnerParameters
@@ -63,6 +77,8 @@
 PublicKey = P-256-SPKI
 Type = EC
 Input = 3059301306072a8648ce3d020106082a8648ce3d030107034200042c150f429ce70f216c252cf5e062ce1f639cd5d165c7f89424072c27197d78b33b920e95cdb664e990dcf0cfea0d94e2a8e6af9d0e58056e653104925b9fe6c9
+ExpectNoRawPrivate
+ExpectNoRawPublic
 
 # The same as above, but with the curve explicitly spelled out.
 PublicKey = P-256-SPKI
@@ -78,58 +94,84 @@
 PrivateKey = DSA-1024
 Type = DSA
 Input = 308202650201003082023906072a8648ce3804013082022c02820101009e12fab3de12213501dd82aa10ca2d101d2d4ebfef4d2a3f8daa0fe0cedad8d6af85616aa2f3252c0a2b5a6db09e6f14900e0ddb8311876dd8f9669525f99ed65949e184d5064793271169a228680b95ec12f59a8e20b21f2b58eb2a2012d35bde2ee351822fe8f32d0a330565dcce5c672b7259c14b2433d0b5b2ca2b2db0ab626e8f13f47fe0345d904e7294bb038e9ce21a9e580b83356278706cfe768436c69de149ccff98b4aab8cb4f6385c9f102ce59346eaeef27e0ad222d53d6e89cc8cde5776dd00057b03f2d88ab3cedbafd7b585f0b7f7835e17a3728bbf25ea62572f245dc111f3ce39cb6ffacc31b0a2790e7bde90224ea9b09315362af3d2b022100f381dcf53ebf724f8b2e5ca82c010fb4b5eda9358d0fd88ed278589488b54fc3028201000c402a725dcc3a62e02bf4cf43cd17f4a493591220223669cf4193edab423ad08dfb552e308a6a57a5ffbc7cd0fb2087f81f8df0cb08ab2133287d2b6968714a94f633c940845a48a3e16708dde761cc6a8eab2d84db21b6ea5b07681493cc9c31fbc368b243f6ddf8c932a8b4038f44e7b15ca876344a147859f2b43b39458668ad5e0a1a9a669546dd2812e3b3617a0aef99d58e3bb4cc87fd94225e01d2dcc469a77268146c51918f18e8b4d70aa1f0c7623bcc52cf3731d38641b2d2830b7eecb2f09552ff137d046e494e7f33c3590002b16d1b97d936fda28f90c3ed3ca35338168ac16f77c3c57adc2e8f7c6c2256e41a5f65450590dbb5bcf06d66610423022100b0c768702743bc51242993a971a52889795444f7c6452203d0ce84fe6117d46e
+ExpectNoRawPrivate
+ExpectNoRawPublic
 
 # A DSA public key.
 PublicKey = DSA-1024-SPKI
 Type = DSA
 Input = 308201b73082012c06072a8648ce3804013082011f02818100b3429b8b128c9079f9b72e86857e98d265e5d91661ed8b5f4cc56e5eed1e571da30186983a9dd76297eab73ee13a1db841f8800d04a7cab478af6cde2ea4a2868531af169a24858c6268efa39ceb7ed0d4227eb5bbb01124a2a5a26038c7bcfb8cc827f68f5202345166e4718596799b65c9def82828ce44e62e38e41a0d24b1021500c5a56c81ddd87f47e676546c56d05706421624cf0281810094de40d27314fe929e47ff9b1ac65cfc73ef38c4d381c890be6217b15039ae18190e6b421af8c0bda35a5cfd050f58ae2644adce83e68c8e5ba11729df56bbb21e227a60b816cc033fa799a38fe1ba5b4aa1801b6f841ce3df99feb3b4fb96950c960af13fa2ce920aabc12dd24ad2044a35063ea0e25f67f560f4cfbdc5598303818400028180258c30ebbb7f34fdc873ce679f6cea373c7886d75d4421b90920db034daedd292c64d8edd8cdbdd7f3ad23d74cfa2135247d0cef6ecf2e14f99e19d22a8c1266bd8fb8719c0e5667c716c45c7adbdabe548085bdad2dfee636f8d52fd6adb2193df6c4f0520fbd171b91882e0e4f321f8250ffecf4dbea00e114427d3ef96c1a
+ExpectNoRawPrivate
+ExpectNoRawPublic
 
 # The same key as above, but without the parameters.
 PublicKey = DSA-1024-SPKI-No-Params
 Type = DSA
 Input = 308192300906072a8648ce38040103818400028180258c30ebbb7f34fdc873ce679f6cea373c7886d75d4421b90920db034daedd292c64d8edd8cdbdd7f3ad23d74cfa2135247d0cef6ecf2e14f99e19d22a8c1266bd8fb8719c0e5667c716c45c7adbdabe548085bdad2dfee636f8d52fd6adb2193df6c4f0520fbd171b91882e0e4f321f8250ffecf4dbea00e114427d3ef96c1a
+ExpectNoRawPrivate
+ExpectNoRawPublic
 
 # Private keys from RFC 8032.
 PrivateKey = Ed25519
 Type = Ed25519
 Input = 302e020100300506032b6570042204209d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60
+ExpectRawPrivate = 9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60
+ExpectRawPublic = d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a
 
 PrivateKey = Ed25519-2
 Type = Ed25519
 Input = 302e020100300506032b6570042204204ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb
+ExpectRawPrivate = 4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb
+ExpectRawPublic = 3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c
 
 PrivateKey = Ed25519-3
 Type = Ed25519
 Input = 302e020100300506032b657004220420c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7
+ExpectRawPrivate = c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7
+ExpectRawPublic = fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025
 
 PrivateKey = Ed25519-4
 Type = Ed25519
 Input = 302e020100300506032b657004220420f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5
+ExpectRawPrivate = f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5
+ExpectRawPublic = 278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e
 
 PrivateKey = Ed25519-5
 Type = Ed25519
 Input = 302e020100300506032b657004220420833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42
+ExpectRawPrivate = 833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42
+ExpectRawPublic = ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf
 
 # Public keys from RFC 8032.
 PublicKey = Ed25519-SPKI
 Type = Ed25519
 Input = 302a300506032b6570032100d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a
+ExpectNoRawPrivate
+ExpectRawPublic = d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a
 
 PublicKey = Ed25519-SPKI-2
 Type = Ed25519
 Input = 302a300506032b65700321003d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c
+ExpectNoRawPrivate
+ExpectRawPublic = 3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c
 
 PublicKey = Ed25519-SPKI-3
 Type = Ed25519
 Input = 302a300506032b6570032100fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025
+ExpectNoRawPrivate
+ExpectRawPublic = fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025
 
 PublicKey = Ed25519-SPKI-4
 Type = Ed25519
 Input = 302a300506032b6570032100278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e
+ExpectNoRawPrivate
+ExpectRawPublic = 278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e
 
 PublicKey = Ed25519-SPKI-5
 Type = Ed25519
 Input = 302a300506032b6570032100ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf
+ExpectNoRawPrivate
+ExpectRawPublic = ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf
 
 # The first key, private and public, with invalid NULL parameters.
 PrivateKey = Ed25519-NULL
diff --git a/crypto/evp/internal.h b/crypto/evp/internal.h
index 43847ea..a599706 100644
--- a/crypto/evp/internal.h
+++ b/crypto/evp/internal.h
@@ -96,6 +96,11 @@
   // |out|. It returns one on success and zero on error.
   int (*priv_encode)(CBB *out, const EVP_PKEY *key);
 
+  int (*set_priv_raw)(EVP_PKEY *pkey, const uint8_t *in, size_t len);
+  int (*set_pub_raw)(EVP_PKEY *pkey, const uint8_t *in, size_t len);
+  int (*get_priv_raw)(const EVP_PKEY *pkey, uint8_t *out, size_t *out_len);
+  int (*get_pub_raw)(const EVP_PKEY *pkey, uint8_t *out, size_t *out_len);
+
   // pkey_opaque returns 1 if the |pk| is opaque. Opaque keys are backed by
   // custom implementations which do not expose key material and parameters.
   int (*pkey_opaque)(const EVP_PKEY *pk);
diff --git a/crypto/evp/p_dsa_asn1.c b/crypto/evp/p_dsa_asn1.c
index 34b2e70..d50e0fc 100644
--- a/crypto/evp/p_dsa_asn1.c
+++ b/crypto/evp/p_dsa_asn1.c
@@ -255,6 +255,11 @@
   dsa_priv_decode,
   dsa_priv_encode,
 
+  NULL /* set_priv_raw */,
+  NULL /* set_pub_raw */,
+  NULL /* get_priv_raw */,
+  NULL /* get_pub_raw */,
+
   NULL /* pkey_opaque */,
 
   int_dsa_size,
diff --git a/crypto/evp/p_ec_asn1.c b/crypto/evp/p_ec_asn1.c
index 0ad8f38..dedc5e0 100644
--- a/crypto/evp/p_ec_asn1.c
+++ b/crypto/evp/p_ec_asn1.c
@@ -237,6 +237,11 @@
   eckey_priv_decode,
   eckey_priv_encode,
 
+  NULL /* set_priv_raw */,
+  NULL /* set_pub_raw */,
+  NULL /* get_priv_raw */,
+  NULL /* get_pub_raw */,
+
   eckey_opaque,
 
   int_ec_size,
diff --git a/crypto/evp/p_ed25519_asn1.c b/crypto/evp/p_ed25519_asn1.c
index ba08c6d..bd44b3f 100644
--- a/crypto/evp/p_ed25519_asn1.c
+++ b/crypto/evp/p_ed25519_asn1.c
@@ -28,45 +28,101 @@
   pkey->pkey.ptr = NULL;
 }
 
-static int set_pubkey(EVP_PKEY *pkey, const uint8_t pubkey[32]) {
+static int ed25519_set_priv_raw(EVP_PKEY *pkey, const uint8_t *in, size_t len) {
+  if (len != 32) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
+    return 0;
+  }
+
   ED25519_KEY *key = OPENSSL_malloc(sizeof(ED25519_KEY));
   if (key == NULL) {
     OPENSSL_PUT_ERROR(EVP, ERR_R_MALLOC_FAILURE);
     return 0;
   }
-  key->has_private = 0;
-  OPENSSL_memcpy(key->key.pub.value, pubkey, 32);
+
+  // The RFC 8032 encoding stores only the 32-byte seed, so we must recover the
+  // full representation which we use from it.
+  uint8_t pubkey_unused[32];
+  ED25519_keypair_from_seed(pubkey_unused, key->key.priv, in);
+  key->has_private = 1;
 
   ed25519_free(pkey);
   pkey->pkey.ptr = key;
   return 1;
 }
 
-static int set_privkey(EVP_PKEY *pkey, const uint8_t privkey[64]) {
+static int ed25519_set_pub_raw(EVP_PKEY *pkey, const uint8_t *in, size_t len) {
+  if (len != 32) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
+    return 0;
+  }
+
   ED25519_KEY *key = OPENSSL_malloc(sizeof(ED25519_KEY));
   if (key == NULL) {
     OPENSSL_PUT_ERROR(EVP, ERR_R_MALLOC_FAILURE);
     return 0;
   }
-  key->has_private = 1;
-  OPENSSL_memcpy(key->key.priv, privkey, 64);
+
+  OPENSSL_memcpy(key->key.pub.value, in, 32);
+  key->has_private = 0;
 
   ed25519_free(pkey);
   pkey->pkey.ptr = key;
   return 1;
 }
 
+static int ed25519_get_priv_raw(const EVP_PKEY *pkey, uint8_t *out,
+                                size_t *out_len) {
+  const ED25519_KEY *key = pkey->pkey.ptr;
+  if (!key->has_private) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_NOT_A_PRIVATE_KEY);
+    return 0;
+  }
+
+  if (out == NULL) {
+    *out_len = 32;
+    return 1;
+  }
+
+  if (*out_len < 32) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_BUFFER_TOO_SMALL);
+    return 0;
+  }
+
+  // The raw private key format is the first 32 bytes of the private key.
+  OPENSSL_memcpy(out, key->key.priv, 32);
+  *out_len = 32;
+  return 1;
+}
+
+static int ed25519_get_pub_raw(const EVP_PKEY *pkey, uint8_t *out,
+                               size_t *out_len) {
+  const ED25519_KEY *key = pkey->pkey.ptr;
+  if (out == NULL) {
+    *out_len = 32;
+    return 1;
+  }
+
+  if (*out_len < 32) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_BUFFER_TOO_SMALL);
+    return 0;
+  }
+
+  OPENSSL_memcpy(out, key->key.pub.value, 32);
+  *out_len = 32;
+  return 1;
+}
+
 static int ed25519_pub_decode(EVP_PKEY *out, CBS *params, CBS *key) {
   // See RFC 8410, section 4.
 
   // The parameters must be omitted. Public keys have length 32.
-  if (CBS_len(params) != 0 ||
-      CBS_len(key) != 32) {
+  if (CBS_len(params) != 0) {
     OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
     return 0;
   }
 
-  return set_pubkey(out, CBS_data(key));
+  return ed25519_set_pub_raw(out, CBS_data(key), CBS_len(key));
 }
 
 static int ed25519_pub_encode(CBB *out, const EVP_PKEY *pkey) {
@@ -103,17 +159,12 @@
   CBS inner;
   if (CBS_len(params) != 0 ||
       !CBS_get_asn1(key, &inner, CBS_ASN1_OCTETSTRING) ||
-      CBS_len(key) != 0 ||
-      CBS_len(&inner) != 32) {
+      CBS_len(key) != 0) {
     OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
     return 0;
   }
 
-  // The PKCS#8 encoding stores only the 32-byte seed, so we must recover the
-  // full representation which we use from it.
-  uint8_t pubkey[32], privkey[64];
-  ED25519_keypair_from_seed(pubkey, privkey, CBS_data(&inner));
-  return set_privkey(out, privkey);
+  return ed25519_set_priv_raw(out, CBS_data(&inner), CBS_len(&inner));
 }
 
 static int ed25519_priv_encode(CBB *out, const EVP_PKEY *pkey) {
@@ -156,6 +207,10 @@
     ed25519_pub_cmp,
     ed25519_priv_decode,
     ed25519_priv_encode,
+    ed25519_set_priv_raw,
+    ed25519_set_pub_raw,
+    ed25519_get_priv_raw,
+    ed25519_get_pub_raw,
     NULL /* pkey_opaque */,
     ed25519_size,
     ed25519_bits,
@@ -164,27 +219,3 @@
     NULL /* param_cmp */,
     ed25519_free,
 };
-
-EVP_PKEY *EVP_PKEY_new_ed25519_public(const uint8_t public_key[32]) {
-  EVP_PKEY *ret = EVP_PKEY_new();
-  if (ret == NULL ||
-      !EVP_PKEY_set_type(ret, EVP_PKEY_ED25519) ||
-      !set_pubkey(ret, public_key)) {
-    EVP_PKEY_free(ret);
-    return NULL;
-  }
-
-  return ret;
-}
-
-EVP_PKEY *EVP_PKEY_new_ed25519_private(const uint8_t private_key[64]) {
-  EVP_PKEY *ret = EVP_PKEY_new();
-  if (ret == NULL ||
-      !EVP_PKEY_set_type(ret, EVP_PKEY_ED25519) ||
-      !set_privkey(ret, private_key)) {
-    EVP_PKEY_free(ret);
-    return NULL;
-  }
-
-  return ret;
-}
diff --git a/crypto/evp/p_rsa_asn1.c b/crypto/evp/p_rsa_asn1.c
index 85f6fc8..c097103 100644
--- a/crypto/evp/p_rsa_asn1.c
+++ b/crypto/evp/p_rsa_asn1.c
@@ -178,6 +178,11 @@
   rsa_priv_decode,
   rsa_priv_encode,
 
+  NULL /* set_priv_raw */,
+  NULL /* set_pub_raw */,
+  NULL /* get_priv_raw */,
+  NULL /* get_pub_raw */,
+
   rsa_opaque,
 
   int_rsa_size,
diff --git a/crypto/x509/x509_test.cc b/crypto/x509/x509_test.cc
index c0ac170..1f664b9 100644
--- a/crypto/x509/x509_test.cc
+++ b/crypto/x509/x509_test.cc
@@ -1327,9 +1327,11 @@
   uint8_t pub_bytes[32], priv_bytes[64];
   ED25519_keypair(pub_bytes, priv_bytes);
 
-  bssl::UniquePtr<EVP_PKEY> pub(EVP_PKEY_new_ed25519_public(pub_bytes));
+  bssl::UniquePtr<EVP_PKEY> pub(
+      EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, nullptr, pub_bytes, 32));
   ASSERT_TRUE(pub);
-  bssl::UniquePtr<EVP_PKEY> priv(EVP_PKEY_new_ed25519_private(priv_bytes));
+  bssl::UniquePtr<EVP_PKEY> priv(
+      EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, nullptr, priv_bytes, 32));
   ASSERT_TRUE(priv);
 
   bssl::ScopedEVP_MD_CTX md_ctx;
diff --git a/include/openssl/evp.h b/include/openssl/evp.h
index c446984..39c418b 100644
--- a/include/openssl/evp.h
+++ b/include/openssl/evp.h
@@ -170,16 +170,6 @@
 OPENSSL_EXPORT EC_KEY *EVP_PKEY_get0_EC_KEY(const EVP_PKEY *pkey);
 OPENSSL_EXPORT EC_KEY *EVP_PKEY_get1_EC_KEY(const EVP_PKEY *pkey);
 
-// EVP_PKEY_new_ed25519_public returns a newly allocated |EVP_PKEY| wrapping an
-// Ed25519 public key, or NULL on allocation error.
-OPENSSL_EXPORT EVP_PKEY *EVP_PKEY_new_ed25519_public(
-    const uint8_t public_key[32]);
-
-// EVP_PKEY_new_ed25519_private returns a newly allocated |EVP_PKEY| wrapping an
-// Ed25519 private key, or NULL on allocation error.
-OPENSSL_EXPORT EVP_PKEY *EVP_PKEY_new_ed25519_private(
-    const uint8_t private_key[64]);
-
 #define EVP_PKEY_NONE NID_undef
 #define EVP_PKEY_RSA NID_rsaEncryption
 #define EVP_PKEY_RSA_PSS NID_rsassaPss
@@ -241,6 +231,48 @@
 OPENSSL_EXPORT int EVP_marshal_private_key(CBB *cbb, const EVP_PKEY *key);
 
 
+// Raw keys
+//
+// Some keys types support a "raw" serialization. Currently the only supported
+// raw format is Ed25519, where the public key and private key formats are those
+// specified in RFC 8032. 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,
+                                                      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
+// the number of bytes written.
+//
+// It returns one on success and zero if |pkey| has no private key, the key
+// type does not support a raw format, or the buffer is too small.
+OPENSSL_EXPORT int EVP_PKEY_get_raw_private_key(const EVP_PKEY *pkey,
+                                                uint8_t *out, size_t *out_len);
+
+// EVP_PKEY_get_raw_public_key outputs the public key for |pkey| in raw form.
+// If |out| is NULL, it sets |*out_len| to the size of the raw public key.
+// Otherwise, it writes at most |*out_len| bytes to |out| and sets |*out_len| to
+// the number of bytes written.
+//
+// It returns one on success and zero if |pkey| has no public key, the key
+// type does not support a raw format, or the buffer is too small.
+OPENSSL_EXPORT int EVP_PKEY_get_raw_public_key(const EVP_PKEY *pkey,
+                                               uint8_t *out, size_t *out_len);
+
+
 // Signing
 
 // EVP_DigestSignInit sets up |ctx| for a signing operation with |type| and