--- /dev/null
+/* $OpenBSD: ecx_methods.c,v 1.1 2022/11/10 16:37:52 jsing Exp $ */
+/*
+ * Copyright (c) 2022 Joel Sing <jsing@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <string.h>
+
+#include <openssl/curve25519.h>
+#include <openssl/ec.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+
+#include "asn1_locl.h"
+#include "bytestring.h"
+#include "curve25519_internal.h"
+#include "evp_locl.h"
+
+/*
+ * EVP PKEY and PKEY ASN.1 methods Ed25519 and X25519.
+ *
+ * RFC 7748 - Elliptic Curves for Security.
+ * RFC 8032 - Edwards-Curve Digital Signature Algorithm (EdDSA).
+ */
+
+#define ED25519_BITS 253
+#define ED25519_SECURITY_BITS 128
+#define ED25519_SIG_SIZE 64
+
+#define X25519_BITS 253
+#define X25519_SECURITY_BITS 128
+
+static int
+ecx_key_len(int nid)
+{
+ switch (nid) {
+ case NID_ED25519:
+ return ED25519_KEYLEN;
+ case NID_X25519:
+ return X25519_KEYLEN;
+ }
+
+ return 0;
+}
+
+static struct ecx_key_st *
+ecx_key_new(int nid)
+{
+ struct ecx_key_st *ecx_key;
+ int key_len;
+
+ if ((key_len = ecx_key_len(nid)) == 0)
+ return NULL;
+
+ if ((ecx_key = calloc(1, sizeof(*ecx_key))) == NULL)
+ return NULL;
+
+ ecx_key->nid = nid;
+ ecx_key->key_len = key_len;
+
+ return ecx_key;
+}
+
+static void
+ecx_key_clear(struct ecx_key_st *ecx_key)
+{
+ freezero(ecx_key->priv_key, ecx_key->priv_key_len);
+ ecx_key->priv_key = NULL;
+ ecx_key->priv_key_len = 0;
+
+ freezero(ecx_key->pub_key, ecx_key->pub_key_len);
+ ecx_key->pub_key = NULL;
+ ecx_key->pub_key_len = 0;
+}
+
+static void
+ecx_key_free(struct ecx_key_st *ecx_key)
+{
+ if (ecx_key == NULL)
+ return;
+
+ ecx_key_clear(ecx_key);
+
+ freezero(ecx_key, sizeof(*ecx_key));
+}
+
+static int
+ecx_key_generate(struct ecx_key_st *ecx_key)
+{
+ uint8_t *pub_key = NULL, *priv_key = NULL;
+ int ret = 0;
+
+ ecx_key_clear(ecx_key);
+
+ if ((pub_key = calloc(1, ecx_key->key_len)) == NULL)
+ goto err;
+ if ((priv_key = calloc(1, ecx_key->key_len)) == NULL)
+ goto err;
+
+ switch (ecx_key->nid) {
+ case NID_ED25519:
+ ED25519_keypair(pub_key, priv_key);
+ break;
+ case NID_X25519:
+ X25519_keypair(pub_key, priv_key);
+ break;
+ default:
+ goto err;
+ }
+
+ ecx_key->priv_key = priv_key;
+ ecx_key->priv_key_len = ecx_key->key_len;
+ priv_key = NULL;
+
+ ecx_key->pub_key = pub_key;
+ ecx_key->pub_key_len = ecx_key->key_len;
+ pub_key = NULL;
+
+ ret = 1;
+
+ err:
+ freezero(pub_key, ecx_key->key_len);
+ freezero(priv_key, ecx_key->key_len);
+
+ return ret;
+}
+
+static int
+ecx_key_set_priv(struct ecx_key_st *ecx_key, const uint8_t *priv_key,
+ size_t priv_key_len)
+{
+ uint8_t *pub_key = NULL;
+ CBS cbs;
+
+ ecx_key_clear(ecx_key);
+
+ if (priv_key_len != ecx_key->key_len)
+ goto err;
+
+ if ((pub_key = calloc(1, ecx_key->key_len)) == NULL)
+ goto err;
+
+ switch (ecx_key->nid) {
+ case NID_ED25519:
+ ED25519_public_from_private(pub_key, priv_key);
+ break;
+ case NID_X25519:
+ X25519_public_from_private(pub_key, priv_key);
+ break;
+ default:
+ goto err;
+ }
+
+ CBS_init(&cbs, priv_key, priv_key_len);
+ if (!CBS_stow(&cbs, &ecx_key->priv_key, &ecx_key->priv_key_len))
+ goto err;
+
+ ecx_key->pub_key = pub_key;
+ ecx_key->pub_key_len = ecx_key->key_len;
+ pub_key = NULL;
+
+ err:
+ freezero(pub_key, ecx_key->key_len);
+
+ return 1;
+}
+
+static int
+ecx_key_set_pub(struct ecx_key_st *ecx_key, const uint8_t *pub_key,
+ size_t pub_key_len)
+{
+ CBS cbs;
+
+ ecx_key_clear(ecx_key);
+
+ if (pub_key_len != ecx_key->key_len)
+ return 0;
+
+ CBS_init(&cbs, pub_key, pub_key_len);
+ if (!CBS_stow(&cbs, &ecx_key->pub_key, &ecx_key->pub_key_len))
+ return 0;
+
+ return 1;
+}
+
+static int
+ecx_pub_decode(EVP_PKEY *pkey, X509_PUBKEY *xpubkey)
+{
+ struct ecx_key_st *ecx_key = NULL;
+ X509_ALGOR *algor;
+ int algor_type;
+ const uint8_t *param;
+ int param_len;
+ int ret = 0;
+
+ if (!X509_PUBKEY_get0_param(NULL, ¶m, ¶m_len, &algor, xpubkey))
+ goto err;
+
+ /* Ensure that parameters have not been specified in the encoding. */
+ if (algor != NULL) {
+ X509_ALGOR_get0(NULL, &algor_type, NULL, algor);
+ if (algor_type != V_ASN1_UNDEF) {
+ ECerror(EC_R_INVALID_ENCODING);
+ goto err;
+ }
+ }
+
+ if (param == NULL || param_len != ecx_key_len(pkey->ameth->pkey_id)) {
+ ECerror(EC_R_INVALID_ENCODING);
+ goto err;
+ }
+
+ if ((ecx_key = ecx_key_new(pkey->ameth->pkey_id)) == NULL)
+ goto err;
+ if (!ecx_key_set_pub(ecx_key, param, param_len))
+ goto err;
+ if (!EVP_PKEY_assign(pkey, pkey->ameth->pkey_id, ecx_key))
+ goto err;
+ ecx_key = NULL;
+
+ ret = 1;
+
+ err:
+ ecx_key_free(ecx_key);
+
+ return ret;
+}
+
+static int
+ecx_pub_encode(X509_PUBKEY *xpubkey, const EVP_PKEY *pkey)
+{
+ const struct ecx_key_st *ecx_key = pkey->pkey.ecx;
+ uint8_t *pub_key = NULL;
+ size_t pub_key_len = 0;
+ ASN1_OBJECT *aobj;
+ CBS cbs;
+ int ret = 0;
+
+ if (ecx_key == NULL) {
+ ECerror(EC_R_INVALID_KEY);
+ goto err;
+ }
+
+ if (ecx_key->pub_key_len != ecx_key->key_len)
+ goto err;
+
+ if ((aobj = OBJ_nid2obj(pkey->ameth->pkey_id)) == NULL)
+ goto err;
+
+ CBS_init(&cbs, ecx_key->pub_key, ecx_key->pub_key_len);
+ if (!CBS_stow(&cbs, &pub_key, &pub_key_len))
+ goto err;
+
+ if (!X509_PUBKEY_set0_param(xpubkey, aobj, V_ASN1_UNDEF, NULL,
+ pub_key, pub_key_len))
+ goto err;
+
+ pub_key = NULL;
+ pub_key_len = 0;
+
+ ret = 1;
+
+ err:
+ free(pub_key);
+
+ return ret;
+}
+
+static int
+ecx_pub_cmp(const EVP_PKEY *pkey1, const EVP_PKEY *pkey2)
+{
+ if (pkey1->pkey.ecx == NULL || pkey1->pkey.ecx->pub_key == NULL)
+ return -2;
+ if (pkey2->pkey.ecx == NULL || pkey2->pkey.ecx->pub_key == NULL)
+ return -2;
+ if (pkey1->pkey.ecx->pub_key_len != pkey2->pkey.ecx->pub_key_len)
+ return -2;
+
+ return timingsafe_memcmp(pkey1->pkey.ecx->pub_key, pkey2->pkey.ecx->pub_key,
+ pkey1->pkey.ecx->pub_key_len) == 0;
+}
+
+static int
+ecx_pub_print(BIO *bio, const EVP_PKEY *pkey, int indent, ASN1_PCTX *ctx)
+{
+ struct ecx_key_st *ecx_key = pkey->pkey.ecx;
+ const char *name;
+
+ if ((name = OBJ_nid2ln(pkey->ameth->pkey_id)) == NULL)
+ return 0;
+
+ if (ecx_key == NULL || ecx_key->pub_key == NULL)
+ return BIO_printf(bio, "%*s<INVALID PUBLIC KEY>\n",
+ indent, "") > 0;
+
+ if (BIO_printf(bio, "%*s%s Public-Key:\n", indent, "", name) <= 0)
+ return 0;
+ if (BIO_printf(bio, "%*spub:\n", indent, "") <= 0)
+ return 0;
+ if (ASN1_buf_print(bio, ecx_key->pub_key, ecx_key->pub_key_len,
+ indent + 4) == 0)
+ return 0;
+
+ return 1;
+}
+
+static int
+ecx_priv_decode(EVP_PKEY *pkey, const PKCS8_PRIV_KEY_INFO *p8pki)
+{
+ struct ecx_key_st *ecx_key = NULL;
+ ASN1_OCTET_STRING *aos = NULL;
+ const X509_ALGOR *algor;
+ int algor_type;
+ const uint8_t *param;
+ int param_len;
+ int ret = 0;
+
+ if (!PKCS8_pkey_get0(NULL, ¶m, ¶m_len, &algor, p8pki))
+ goto err;
+ if ((aos = d2i_ASN1_OCTET_STRING(NULL, ¶m, param_len)) == NULL)
+ goto err;
+
+ /* Ensure that parameters have not been specified in the encoding. */
+ if (algor != NULL) {
+ X509_ALGOR_get0(NULL, &algor_type, NULL, algor);
+ if (algor_type != V_ASN1_UNDEF) {
+ ECerror(EC_R_INVALID_ENCODING);
+ goto err;
+ }
+ }
+
+ if (ASN1_STRING_get0_data(aos) == NULL ||
+ ASN1_STRING_length(aos) != ecx_key_len(pkey->ameth->pkey_id)) {
+ ECerror(EC_R_INVALID_ENCODING);
+ goto err;
+ }
+
+ if ((ecx_key = ecx_key_new(pkey->ameth->pkey_id)) == NULL)
+ goto err;
+ if (!ecx_key_set_priv(ecx_key, ASN1_STRING_get0_data(aos),
+ ASN1_STRING_length(aos)))
+ goto err;
+ if (!EVP_PKEY_assign(pkey, pkey->ameth->pkey_id, ecx_key))
+ goto err;
+ ecx_key = NULL;
+
+ ret = 1;
+
+ err:
+ ASN1_OCTET_STRING_free(aos);
+ ecx_key_free(ecx_key);
+
+ return ret;
+}
+
+static int
+ecx_priv_encode(PKCS8_PRIV_KEY_INFO *p8pki, const EVP_PKEY *pkey)
+{
+ struct ecx_key_st *ecx_key = pkey->pkey.ecx;
+ ASN1_OCTET_STRING *aos = NULL;
+ ASN1_OBJECT *aobj;
+ uint8_t *der = NULL;
+ int der_len = 0;
+ int ret = 0;
+
+ if (ecx_key == NULL || ecx_key->priv_key == NULL) {
+ ECerror(EC_R_INVALID_PRIVATE_KEY);
+ goto err;
+ }
+
+ if ((aobj = OBJ_nid2obj(pkey->ameth->pkey_id)) == NULL)
+ goto err;
+
+ if ((aos = ASN1_OCTET_STRING_new()) == NULL)
+ goto err;
+ if (!ASN1_OCTET_STRING_set(aos, ecx_key->priv_key,
+ ecx_key->priv_key_len))
+ goto err;
+ if ((der_len = i2d_ASN1_OCTET_STRING(aos, &der)) < 0)
+ goto err;
+ if (!PKCS8_pkey_set0(p8pki, aobj, 0, V_ASN1_UNDEF, NULL, der, der_len))
+ goto err;
+
+ der = NULL;
+ der_len = 0;
+
+ ret = 1;
+
+ err:
+ freezero(der, der_len);
+ ASN1_OCTET_STRING_free(aos);
+
+ return ret;
+}
+
+static int
+ecx_priv_print(BIO *bio, const EVP_PKEY *pkey, int indent, ASN1_PCTX *ctx)
+{
+ struct ecx_key_st *ecx_key = pkey->pkey.ecx;
+ const char *name;
+
+ if ((name = OBJ_nid2ln(pkey->ameth->pkey_id)) == NULL)
+ return 0;
+
+ if (ecx_key == NULL || ecx_key->priv_key == NULL)
+ return BIO_printf(bio, "%*s<INVALID PRIVATE KEY>\n",
+ indent, "") > 0;
+
+ if (BIO_printf(bio, "%*s%s Private-Key:\n", indent, "", name) <= 0)
+ return 0;
+ if (BIO_printf(bio, "%*spriv:\n", indent, "") <= 0)
+ return 0;
+ if (ASN1_buf_print(bio, ecx_key->priv_key, ecx_key->priv_key_len,
+ indent + 4) == 0)
+ return 0;
+ if (BIO_printf(bio, "%*spub:\n", indent, "") <= 0)
+ return 0;
+ if (ASN1_buf_print(bio, ecx_key->pub_key, ecx_key->pub_key_len,
+ indent + 4) == 0)
+ return 0;
+
+ return 1;
+}
+
+static int
+ecx_size(const EVP_PKEY *pkey)
+{
+ return ecx_key_len(pkey->ameth->pkey_id);
+}
+
+static int
+ecx_sig_size(const EVP_PKEY *pkey)
+{
+ switch (pkey->ameth->pkey_id) {
+ case EVP_PKEY_ED25519:
+ return ED25519_SIG_SIZE;
+ }
+ return 0;
+}
+
+static int
+ecx_bits(const EVP_PKEY *pkey)
+{
+ switch (pkey->ameth->pkey_id) {
+ case EVP_PKEY_ED25519:
+ return ED25519_BITS;
+ case EVP_PKEY_X25519:
+ return X25519_BITS;
+ }
+ return 0;
+}
+
+static int
+ecx_security_bits(const EVP_PKEY *pkey)
+{
+ switch (pkey->ameth->pkey_id) {
+ case EVP_PKEY_ED25519:
+ return ED25519_SECURITY_BITS;
+ case EVP_PKEY_X25519:
+ return X25519_SECURITY_BITS;
+ }
+ return 0;
+}
+
+static int
+ecx_param_cmp(const EVP_PKEY *pkey1, const EVP_PKEY *pkey2)
+{
+ /* No parameters, so always equivalent. */
+ return 1;
+}
+
+static void
+ecx_free(EVP_PKEY *pkey)
+{
+ struct ecx_key_st *ecx_key = pkey->pkey.ecx;
+
+ return ecx_key_free(ecx_key);
+}
+
+static int
+ecx_ctrl(EVP_PKEY *pkey, int op, long arg1, void *arg2)
+{
+ /* Not supported. */
+ return -2;
+}
+
+static int
+ecx_sign_ctrl(EVP_PKEY *pkey, int op, long arg1, void *arg2)
+{
+ switch (op) {
+ case ASN1_PKEY_CTRL_DEFAULT_MD_NID:
+ /* PureEdDSA does its own hashing. */
+ *(int *)arg2 = NID_undef;
+ return 2;
+ }
+ return -2;
+}
+
+static int
+ecx_set_priv_key(EVP_PKEY *pkey, const uint8_t *priv, size_t len)
+{
+ struct ecx_key_st *ecx_key;
+ int ret = 0;
+
+ if (priv == NULL || len != ecx_key_len(pkey->ameth->pkey_id)) {
+ ECerror(EC_R_INVALID_ENCODING);
+ return 0;
+ }
+
+ if ((ecx_key = ecx_key_new(pkey->ameth->pkey_id)) == NULL)
+ return 0;
+ if (!ecx_key_set_priv(ecx_key, priv, len))
+ return 0;
+ if (!EVP_PKEY_assign(pkey, pkey->ameth->pkey_id, ecx_key))
+ goto err;
+ ecx_key = NULL;
+
+ ret = 1;
+
+ err:
+ ecx_key_free(ecx_key);
+
+ return ret;
+}
+
+static int
+ecx_set_pub_key(EVP_PKEY *pkey, const uint8_t *pub, size_t len)
+{
+ struct ecx_key_st *ecx_key;
+ int ret = 0;
+
+ if (pub == NULL || len != ecx_key_len(pkey->ameth->pkey_id)) {
+ ECerror(EC_R_INVALID_ENCODING);
+ return 0;
+ }
+
+ if ((ecx_key = ecx_key_new(pkey->ameth->pkey_id)) == NULL)
+ return 0;
+ if (!ecx_key_set_pub(ecx_key, pub, len))
+ return 0;
+ if (!EVP_PKEY_assign(pkey, pkey->ameth->pkey_id, ecx_key))
+ goto err;
+ ecx_key = NULL;
+
+ ret = 1;
+
+ err:
+ ecx_key_free(ecx_key);
+
+ return ret;
+}
+
+static int
+ecx_get_priv_key(const EVP_PKEY *pkey, unsigned char *out_priv, size_t *out_len)
+{
+ struct ecx_key_st *ecx_key = pkey->pkey.ecx;
+ CBS cbs;
+
+ if (out_priv == NULL) {
+ *out_len = ecx_key_len(pkey->ameth->pkey_id);
+ return 1;
+ }
+
+ if (ecx_key == NULL || ecx_key->priv_key == NULL)
+ return 0;
+
+ CBS_init(&cbs, ecx_key->priv_key, ecx_key->priv_key_len);
+ if (!CBS_write_bytes(&cbs, out_priv, *out_len, out_len))
+ return 0;
+
+ return 1;
+}
+
+static int
+ecx_get_pub_key(const EVP_PKEY *pkey, unsigned char *out_pub, size_t *out_len)
+{
+ struct ecx_key_st *ecx_key = pkey->pkey.ecx;
+ CBS cbs;
+
+ if (out_pub == NULL) {
+ *out_len = ecx_key_len(pkey->ameth->pkey_id);
+ return 1;
+ }
+
+ if (ecx_key == NULL || ecx_key->pub_key == NULL)
+ return 0;
+
+ CBS_init(&cbs, ecx_key->pub_key, ecx_key->pub_key_len);
+ if (!CBS_write_bytes(&cbs, out_pub, *out_len, out_len))
+ return 0;
+
+ return 1;
+}
+
+static int
+pkey_ecx_keygen(EVP_PKEY_CTX *pkey_ctx, EVP_PKEY *pkey)
+{
+ struct ecx_key_st *ecx_key = NULL;
+ int ret = 0;
+
+ if ((ecx_key = ecx_key_new(pkey_ctx->pmeth->pkey_id)) == NULL)
+ goto err;
+ if (!ecx_key_generate(ecx_key))
+ goto err;
+ if (!EVP_PKEY_assign(pkey, pkey_ctx->pmeth->pkey_id, ecx_key))
+ goto err;
+ ecx_key = NULL;
+
+ ret = 1;
+
+ err:
+ ecx_key_free(ecx_key);
+
+ return ret;
+}
+
+static int
+pkey_ecx_derive(EVP_PKEY_CTX *pkey_ctx, unsigned char *out_key,
+ size_t *out_key_len)
+{
+ struct ecx_key_st *ecx_key, *ecx_peer_key;
+
+ if (pkey_ctx->pkey == NULL || pkey_ctx->peerkey == NULL) {
+ ECerror(EC_R_KEYS_NOT_SET);
+ return 0;
+ }
+
+ if ((ecx_key = pkey_ctx->pkey->pkey.ecx) == NULL) {
+ ECerror(EC_R_INVALID_PRIVATE_KEY);
+ return 0;
+ }
+ if (ecx_key->priv_key == NULL) {
+ ECerror(EC_R_INVALID_PRIVATE_KEY);
+ return 0;
+ }
+
+ if ((ecx_peer_key = pkey_ctx->peerkey->pkey.ecx) == NULL) {
+ ECerror(EC_R_INVALID_PEER_KEY);
+ return 0;
+ }
+
+ if (out_key != NULL) {
+ if (!X25519(out_key, ecx_key->priv_key, ecx_peer_key->pub_key))
+ return 0;
+ }
+
+ *out_key_len = X25519_KEYLEN;
+
+ return 1;
+}
+
+static int
+pkey_ecx_ctrl(EVP_PKEY_CTX *pkey_ctx, int op, int arg1, void *arg2)
+{
+ if (op == EVP_PKEY_CTRL_PEER_KEY)
+ return 1;
+
+ return -2;
+}
+
+static int
+ecx_item_verify(EVP_MD_CTX *md_ctx, const ASN1_ITEM *it, void *asn,
+ X509_ALGOR *algor, ASN1_BIT_STRING *abs, EVP_PKEY *pkey)
+{
+ const ASN1_OBJECT *aobj;
+ int nid, param_type;
+
+ X509_ALGOR_get0(&aobj, ¶m_type, NULL, algor);
+
+ nid = OBJ_obj2nid(aobj);
+
+ if (nid != NID_ED25519 || param_type != V_ASN1_UNDEF) {
+ ECerror(EC_R_INVALID_ENCODING);
+ return 0;
+ }
+
+ if (!EVP_DigestVerifyInit(md_ctx, NULL, NULL, NULL, pkey))
+ return 0;
+
+ return 2;
+}
+
+static int
+ecx_item_sign(EVP_MD_CTX *md_ctx, const ASN1_ITEM *it, void *asn,
+ X509_ALGOR *algor1, X509_ALGOR *algor2, ASN1_BIT_STRING *abs)
+{
+ ASN1_OBJECT *aobj;
+
+ if ((aobj = OBJ_nid2obj(NID_ED25519)) == NULL)
+ return 0;
+
+ if (!X509_ALGOR_set0(algor1, aobj, V_ASN1_UNDEF, NULL))
+ return 0;
+
+ if (algor2 != NULL) {
+ if (!X509_ALGOR_set0(algor2, aobj, V_ASN1_UNDEF, NULL))
+ return 0;
+ }
+
+ /* Tell ASN1_item_sign_ctx() that identifiers are set and it needs to sign. */
+ return 3;
+}
+
+static int
+pkey_ecx_digestsign(EVP_MD_CTX *md_ctx, unsigned char *out_sig,
+ size_t *out_sig_len, const unsigned char *message, size_t message_len)
+{
+ struct ecx_key_st *ecx_key;
+ EVP_PKEY_CTX *pkey_ctx;
+
+ pkey_ctx = EVP_MD_CTX_pkey_ctx(md_ctx);
+ ecx_key = pkey_ctx->pkey->pkey.ecx;
+
+ if (out_sig == NULL) {
+ *out_sig_len = ecx_sig_size(pkey_ctx->pkey);
+ return 1;
+ }
+ if (*out_sig_len < ecx_sig_size(pkey_ctx->pkey)) {
+ ECerror(EC_R_BUFFER_TOO_SMALL);
+ return 0;
+ }
+
+ if (ecx_key == NULL)
+ return 0;
+ if (ecx_key->priv_key == NULL || ecx_key->pub_key == NULL)
+ return 0;
+
+ if (!ED25519_sign(out_sig, message, message_len, ecx_key->pub_key,
+ ecx_key->priv_key))
+ return 0;
+
+ return 1;
+}
+
+static int
+pkey_ecx_digestverify(EVP_MD_CTX *md_ctx, const unsigned char *sig,
+ size_t sig_len, const unsigned char *message, size_t message_len)
+{
+ struct ecx_key_st *ecx_key;
+ EVP_PKEY_CTX *pkey_ctx;
+
+ pkey_ctx = EVP_MD_CTX_pkey_ctx(md_ctx);
+ ecx_key = pkey_ctx->pkey->pkey.ecx;
+
+ if (ecx_key == NULL || ecx_key->pub_key == NULL)
+ return 0;
+ if (sig_len != ecx_sig_size(pkey_ctx->pkey))
+ return 0;
+
+ return ED25519_verify(message, message_len, sig, ecx_key->pub_key);
+}
+
+static int
+pkey_ecx_ed_ctrl(EVP_PKEY_CTX *pkey_ctx, int op, int arg1, void *arg2)
+{
+ switch (op) {
+ case EVP_PKEY_CTRL_MD:
+ /* PureEdDSA does its own hashing. */
+ if (arg2 != NULL && (const EVP_MD *)arg2 != EVP_md_null()) {
+ ECerror(EC_R_INVALID_DIGEST_TYPE);
+ return 0;
+ }
+ return 1;
+
+ case EVP_PKEY_CTRL_DIGESTINIT:
+ return 1;
+ }
+ return -2;
+}
+
+const EVP_PKEY_ASN1_METHOD x25519_asn1_meth = {
+ .pkey_id = EVP_PKEY_X25519,
+ .pkey_base_id = EVP_PKEY_X25519,
+ .pkey_flags = 0,
+ .pem_str = "X25519",
+ .info = "OpenSSL X25519 algorithm",
+
+ .pub_decode = ecx_pub_decode,
+ .pub_encode = ecx_pub_encode,
+ .pub_cmp = ecx_pub_cmp,
+ .pub_print = ecx_pub_print,
+
+ .priv_decode = ecx_priv_decode,
+ .priv_encode = ecx_priv_encode,
+ .priv_print = ecx_priv_print,
+
+ .pkey_size = ecx_size,
+ .pkey_bits = ecx_bits,
+ .pkey_security_bits = ecx_security_bits,
+
+ .param_cmp = ecx_param_cmp,
+
+ .pkey_free = ecx_free,
+ .pkey_ctrl = ecx_ctrl,
+
+ .set_priv_key = ecx_set_priv_key,
+ .set_pub_key = ecx_set_pub_key,
+ .get_priv_key = ecx_get_priv_key,
+ .get_pub_key = ecx_get_pub_key,
+};
+
+const EVP_PKEY_METHOD x25519_pkey_meth = {
+ .pkey_id = EVP_PKEY_X25519,
+ .keygen = pkey_ecx_keygen,
+ .derive = pkey_ecx_derive,
+ .ctrl = pkey_ecx_ctrl,
+};
+
+const EVP_PKEY_ASN1_METHOD ed25519_asn1_meth = {
+ .pkey_id = EVP_PKEY_ED25519,
+ .pkey_base_id = EVP_PKEY_ED25519,
+ .pkey_flags = 0,
+ .pem_str = "ED25519",
+ .info = "OpenSSL ED25519 algorithm",
+
+ .pub_decode = ecx_pub_decode,
+ .pub_encode = ecx_pub_encode,
+ .pub_cmp = ecx_pub_cmp,
+ .pub_print = ecx_pub_print,
+
+ .priv_decode = ecx_priv_decode,
+ .priv_encode = ecx_priv_encode,
+ .priv_print = ecx_priv_print,
+
+ .pkey_size = ecx_sig_size,
+ .pkey_bits = ecx_bits,
+ .pkey_security_bits = ecx_security_bits,
+
+ .param_cmp = ecx_param_cmp,
+
+ .pkey_free = ecx_free,
+ .pkey_ctrl = ecx_sign_ctrl,
+
+ .item_verify = ecx_item_verify,
+ .item_sign = ecx_item_sign,
+
+ .set_priv_key = ecx_set_priv_key,
+ .set_pub_key = ecx_set_pub_key,
+ .get_priv_key = ecx_get_priv_key,
+ .get_pub_key = ecx_get_pub_key,
+};
+
+const EVP_PKEY_METHOD ed25519_pkey_meth = {
+ .pkey_id = EVP_PKEY_ED25519,
+ .flags = EVP_PKEY_FLAG_SIGCTX_CUSTOM,
+ .keygen = pkey_ecx_keygen,
+ .ctrl = pkey_ecx_ed_ctrl,
+ .digestsign = pkey_ecx_digestsign,
+ .digestverify = pkey_ecx_digestverify,
+};