/* $OpenBSD: ecx_methods.c,v 1.13 2024/04/02 04:04:07 tb Exp $ */ /* * Copyright (c) 2022 Joel Sing * * 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 #include #include #include #include #include #include #include "asn1_local.h" #include "bytestring.h" #include "curve25519_internal.h" #include "evp_local.h" #include "x509_local.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; } /* Reimplementation of ASN1_buf_print() that adds a secondary indent of 4. */ static int ecx_buf_print(BIO *bio, const uint8_t *buf, size_t buf_len, int indent) { uint8_t u8; size_t octets = 0; const char *sep = ":", *nl = ""; CBS cbs; if (indent > 60) indent = 60; indent += 4; if (indent < 0) indent = 0; CBS_init(&cbs, buf, buf_len); while (CBS_len(&cbs) > 0) { if (!CBS_get_u8(&cbs, &u8)) return 0; if (octets++ % 15 == 0) { if (BIO_printf(bio, "%s%*s", nl, indent, "") < 0) return 0; nl = "\n"; } if (CBS_len(&cbs) == 0) sep = ""; if (BIO_printf(bio, "%02x%s", u8, sep) <= 0) return 0; } if (BIO_printf(bio, "\n") <= 0) return 0; return 1; } 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\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 (!ecx_buf_print(bio, ecx_key->pub_key, ecx_key->pub_key_len, indent)) 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\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 (!ecx_buf_print(bio, ecx_key->priv_key, ecx_key->priv_key_len, indent)) return 0; if (BIO_printf(bio, "%*spub:\n", indent, "") <= 0) return 0; if (!ecx_buf_print(bio, ecx_key->pub_key, ecx_key->pub_key_len, indent)) 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; ecx_key_free(ecx_key); } static int ecx_ctrl(EVP_PKEY *pkey, int op, long arg1, void *arg2) { /* Not supported. */ return -2; } #ifndef OPENSSL_NO_CMS static int ecx_cms_sign_or_verify(EVP_PKEY *pkey, long verify, CMS_SignerInfo *si) { X509_ALGOR *digestAlgorithm, *signatureAlgorithm; if (verify != 0 && verify != 1) return -1; /* Check that we have an Ed25519 public key. */ if (EVP_PKEY_id(pkey) != NID_ED25519) return -1; CMS_SignerInfo_get0_algs(si, NULL, NULL, &digestAlgorithm, &signatureAlgorithm); /* RFC 8419, section 2.3: digestAlgorithm MUST be SHA-512. */ if (digestAlgorithm == NULL) return -1; if (OBJ_obj2nid(digestAlgorithm->algorithm) != NID_sha512) return -1; /* * RFC 8419, section 2.4: signatureAlgorithm MUST be Ed25519, and the * parameters MUST be absent. For verification check that this is the * case, for signing set the signatureAlgorithm accordingly. */ if (verify) { const ASN1_OBJECT *obj; int param_type; if (signatureAlgorithm == NULL) return -1; X509_ALGOR_get0(&obj, ¶m_type, NULL, signatureAlgorithm); if (OBJ_obj2nid(obj) != NID_ED25519) return -1; if (param_type != V_ASN1_UNDEF) return -1; return 1; } if (!X509_ALGOR_set0_by_nid(signatureAlgorithm, NID_ED25519, V_ASN1_UNDEF, NULL)) return -1; return 1; } #endif static int ecx_sign_ctrl(EVP_PKEY *pkey, int op, long arg1, void *arg2) { switch (op) { #ifndef OPENSSL_NO_CMS case ASN1_PKEY_CTRL_CMS_SIGN: return ecx_cms_sign_or_verify(pkey, arg1, arg2); #endif 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 = NULL; int ret = 0; if (priv == NULL || 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_priv(ecx_key, priv, 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_set_pub_key(EVP_PKEY *pkey, const uint8_t *pub, size_t len) { struct ecx_key_st *ecx_key = NULL; int ret = 0; if (pub == NULL || 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, pub, 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_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 -1; } if (!EVP_DigestVerifyInit(md_ctx, NULL, NULL, NULL, pkey)) return -1; 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) { if (!X509_ALGOR_set0_by_nid(algor1, NID_ED25519, V_ASN1_UNDEF, NULL)) return 0; if (algor2 != NULL) { if (!X509_ALGOR_set0_by_nid(algor2, NID_ED25519, 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; *out_sig_len = ecx_sig_size(pkey_ctx->pkey); 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 -1; if (sig_len != ecx_sig_size(pkey_ctx->pkey)) return -1; 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; #ifndef OPENSSL_NO_CMS case EVP_PKEY_CTRL_CMS_SIGN: #endif case EVP_PKEY_CTRL_DIGESTINIT: return 1; } return -2; } const EVP_PKEY_ASN1_METHOD x25519_asn1_meth = { .base_method = &x25519_asn1_meth, .pkey_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 = { .base_method = &ed25519_asn1_meth, .pkey_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, };