Make EVP_PKEY_keygen work for Ed25519.

For cryptography.io.

Change-Id: I90d0a7526cd1283126400568a4596444457136ca
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/36105
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_extra_test.cc b/crypto/evp/evp_extra_test.cc
index d033343..f520598 100644
--- a/crypto/evp/evp_extra_test.cc
+++ b/crypto/evp/evp_extra_test.cc
@@ -756,3 +756,29 @@
   raw = nullptr;
   ExpectECGroupAndKey(pkey.get(), NID_X9_62_prime256v1);
 }
+
+// Test that |EVP_PKEY_keygen| works for Ed25519.
+TEST(EVPExtraTest, Ed25519Keygen) {
+  bssl::UniquePtr<EVP_PKEY_CTX> pctx(
+      EVP_PKEY_CTX_new_id(EVP_PKEY_ED25519, nullptr));
+  ASSERT_TRUE(pctx);
+  ASSERT_TRUE(EVP_PKEY_keygen_init(pctx.get()));
+  EVP_PKEY *raw = nullptr;
+  ASSERT_TRUE(EVP_PKEY_keygen(pctx.get(), &raw));
+  bssl::UniquePtr<EVP_PKEY> pkey(raw);
+
+  // Round-trip a signature to sanity-check the key is good.
+  bssl::ScopedEVP_MD_CTX ctx;
+  ASSERT_TRUE(
+      EVP_DigestSignInit(ctx.get(), nullptr, nullptr, nullptr, pkey.get()));
+  uint8_t sig[64];
+  size_t len = sizeof(sig);
+  ASSERT_TRUE(EVP_DigestSign(ctx.get(), sig, &len,
+                             reinterpret_cast<const uint8_t *>("hello"), 5));
+
+  ctx.Reset();
+  ASSERT_TRUE(
+      EVP_DigestVerifyInit(ctx.get(), nullptr, nullptr, nullptr, pkey.get()));
+  ASSERT_TRUE(EVP_DigestVerify(ctx.get(), sig, len,
+                               reinterpret_cast<const uint8_t *>("hello"), 5));
+}
diff --git a/crypto/evp/p_ed25519.c b/crypto/evp/p_ed25519.c
index db89389..9149afb 100644
--- a/crypto/evp/p_ed25519.c
+++ b/crypto/evp/p_ed25519.c
@@ -16,6 +16,7 @@
 
 #include <openssl/curve25519.h>
 #include <openssl/err.h>
+#include <openssl/mem.h>
 
 #include "internal.h"
 
@@ -23,6 +24,27 @@
 // Ed25519 has no parameters to copy.
 static int pkey_ed25519_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src) { return 1; }
 
+static int pkey_ed25519_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) {
+  ED25519_KEY *key = OPENSSL_malloc(sizeof(ED25519_KEY));
+  if (key == NULL) {
+    OPENSSL_PUT_ERROR(EVP, ERR_R_MALLOC_FAILURE);
+    return 0;
+  }
+
+  if (!EVP_PKEY_set_type(pkey, EVP_PKEY_ED25519)) {
+    OPENSSL_free(key);
+    return 0;
+  }
+
+  uint8_t pubkey_unused[32];
+  ED25519_keypair(pubkey_unused, key->key.priv);
+  key->has_private = 1;
+
+  OPENSSL_free(pkey->pkey.ptr);
+  pkey->pkey.ptr = key;
+  return 1;
+}
+
 static int pkey_ed25519_sign_message(EVP_PKEY_CTX *ctx, uint8_t *sig,
                                      size_t *siglen, const uint8_t *tbs,
                                      size_t tbslen) {
@@ -68,7 +90,7 @@
     NULL /* init */,
     pkey_ed25519_copy,
     NULL /* cleanup */,
-    NULL /* keygen */,
+    pkey_ed25519_keygen,
     NULL /* sign */,
     pkey_ed25519_sign_message,
     NULL /* verify */,