Implement EVP interfaces for Ed25519 and X25519.
authorjsing <jsing@openbsd.org>
Thu, 10 Nov 2022 16:37:51 +0000 (16:37 +0000)
committerjsing <jsing@openbsd.org>
Thu, 10 Nov 2022 16:37:51 +0000 (16:37 +0000)
ok beck@ tb@

lib/libcrypto/Makefile
lib/libcrypto/asn1/ameth_lib.c
lib/libcrypto/ec/ec.h
lib/libcrypto/ec/ec_err.c
lib/libcrypto/ec/ecx_methods.c [new file with mode: 0644]
lib/libcrypto/evp/evp.h
lib/libcrypto/evp/evp_locl.h
lib/libcrypto/evp/pmeth_lib.c

index b25ea5f..084d6c1 100644 (file)
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.85 2022/11/10 16:00:34 joshua Exp $
+# $OpenBSD: Makefile,v 1.86 2022/11/10 16:37:51 jsing Exp $
 
 LIB=   crypto
 LIBREBUILD=y
@@ -33,6 +33,7 @@ CFLAGS+= -I${LCRYPTO_SRC}/asn1
 CFLAGS+= -I${LCRYPTO_SRC}/bio
 CFLAGS+= -I${LCRYPTO_SRC}/bn
 CFLAGS+= -I${LCRYPTO_SRC}/bytestring
+CFLAGS+= -I${LCRYPTO_SRC}/curve25519
 CFLAGS+= -I${LCRYPTO_SRC}/dh
 CFLAGS+= -I${LCRYPTO_SRC}/dsa
 CFLAGS+= -I${LCRYPTO_SRC}/ec
@@ -361,6 +362,7 @@ SRCS+= ecp_nistp521.c
 SRCS+= ecp_nistputil.c
 SRCS+= ecp_oct.c
 SRCS+= ecp_smpl.c
+SRCS+= ecx_methods.c
 
 # ecdh/
 SRCS+= ecdh_kdf.c
index cca1dcc..143c010 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: ameth_lib.c,v 1.28 2022/11/09 18:17:16 jsing Exp $ */
+/* $OpenBSD: ameth_lib.c,v 1.29 2022/11/10 16:37:51 jsing Exp $ */
 /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
  * project 2006.
  */
@@ -75,11 +75,13 @@ extern const EVP_PKEY_ASN1_METHOD cmac_asn1_meth;
 extern const EVP_PKEY_ASN1_METHOD dh_asn1_meth;
 extern const EVP_PKEY_ASN1_METHOD dsa_asn1_meths[];
 extern const EVP_PKEY_ASN1_METHOD eckey_asn1_meth;
+extern const EVP_PKEY_ASN1_METHOD ed25519_asn1_meth;
 extern const EVP_PKEY_ASN1_METHOD gostimit_asn1_meth;
 extern const EVP_PKEY_ASN1_METHOD gostr01_asn1_meths[];
 extern const EVP_PKEY_ASN1_METHOD hmac_asn1_meth;
 extern const EVP_PKEY_ASN1_METHOD rsa_asn1_meths[];
 extern const EVP_PKEY_ASN1_METHOD rsa_pss_asn1_meth;
+extern const EVP_PKEY_ASN1_METHOD x25519_asn1_meth;
 
 static const EVP_PKEY_ASN1_METHOD *asn1_methods[] = {
        &cmac_asn1_meth,
@@ -90,6 +92,7 @@ static const EVP_PKEY_ASN1_METHOD *asn1_methods[] = {
        &dsa_asn1_meths[3],
        &dsa_asn1_meths[4],
        &eckey_asn1_meth,
+       &ed25519_asn1_meth,
        &gostimit_asn1_meth,
        &gostr01_asn1_meths[0],
        &gostr01_asn1_meths[1],
@@ -98,6 +101,7 @@ static const EVP_PKEY_ASN1_METHOD *asn1_methods[] = {
        &rsa_asn1_meths[0],
        &rsa_asn1_meths[1],
        &rsa_pss_asn1_meth,
+       &x25519_asn1_meth,
 };
 
 static const size_t asn1_methods_count =
index 52c8f2f..9a7c5de 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: ec.h,v 1.28 2022/07/12 14:42:49 kn Exp $ */
+/* $OpenBSD: ec.h,v 1.29 2022/11/10 16:37:51 jsing Exp $ */
 /*
  * Originally written by Bodo Moeller for the OpenSSL project.
  */
@@ -1289,6 +1289,8 @@ void ERR_load_EC_strings(void);
 #define EC_R_INVALID_FIELD                              103
 #define EC_R_INVALID_FORM                               104
 #define EC_R_INVALID_GROUP_ORDER                        122
+#define EC_R_INVALID_KEY                                165
+#define EC_R_INVALID_PEER_KEY                           152
 #define EC_R_INVALID_PENTANOMIAL_BASIS                  132
 #define EC_R_INVALID_PRIVATE_KEY                        123
 #define EC_R_INVALID_TRINOMIAL_BASIS                    137
index d8ead96..2ff8e99 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: ec_err.c,v 1.13 2022/07/12 14:42:49 kn Exp $ */
+/* $OpenBSD: ec_err.c,v 1.14 2022/11/10 16:37:52 jsing Exp $ */
 /* ====================================================================
  * Copyright (c) 1999-2011 The OpenSSL Project.  All rights reserved.
  *
@@ -96,6 +96,8 @@ static ERR_STRING_DATA EC_str_reasons[] =
        {ERR_REASON(EC_R_INVALID_FIELD), "invalid field"},
        {ERR_REASON(EC_R_INVALID_FORM), "invalid form"},
        {ERR_REASON(EC_R_INVALID_GROUP_ORDER), "invalid group order"},
+       {ERR_REASON(EC_R_INVALID_KEY), "invalid key"},
+       {ERR_REASON(EC_R_INVALID_PEER_KEY), "invalid peer key"},
        {ERR_REASON(EC_R_INVALID_PENTANOMIAL_BASIS), "invalid pentanomial basis"},
        {ERR_REASON(EC_R_INVALID_PRIVATE_KEY), "invalid private key"},
        {ERR_REASON(EC_R_INVALID_TRINOMIAL_BASIS), "invalid trinomial basis"},
diff --git a/lib/libcrypto/ec/ecx_methods.c b/lib/libcrypto/ec/ecx_methods.c
new file mode 100644 (file)
index 0000000..741cb2d
--- /dev/null
@@ -0,0 +1,862 @@
+/*     $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, &param, &param_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, &param, &param_len, &algor, p8pki))
+               goto err;
+       if ((aos = d2i_ASN1_OCTET_STRING(NULL, &param, 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, &param_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,
+};
index f4702ab..f8bab26 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: evp.h,v 1.110 2022/11/10 15:17:30 jsing Exp $ */
+/* $OpenBSD: evp.h,v 1.111 2022/11/10 16:37:52 jsing Exp $ */
 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
  * All rights reserved.
  *
 #define EVP_PKEY_HKDF          NID_hkdf
 #define EVP_PKEY_GOSTR12_256   NID_id_tc26_gost3410_2012_256
 #define EVP_PKEY_GOSTR12_512   NID_id_tc26_gost3410_2012_512
+#if defined(LIBRESSL_NEXT_API) || defined(LIBRESSL_INTERNAL)
+#define EVP_PKEY_ED25519       NID_ED25519
+#define EVP_PKEY_X25519                NID_X25519
+#endif
 
 #ifdef __cplusplus
 extern "C" {
@@ -297,6 +301,11 @@ extern "C" {
 /* Length of tag for TLS */
 #define EVP_CHACHAPOLY_TLS_TAG_LEN                     16
 
+#if defined(LIBRESSL_NEXT_API) || defined(LIBRESSL_INTERNAL)
+#define ED25519_KEYLEN                                 32
+#define X25519_KEYLEN                                  32
+#endif
+
 typedef struct evp_cipher_info_st {
        const EVP_CIPHER *cipher;
        unsigned char iv[EVP_MAX_IV_LENGTH];
index dd7d252..37fc55e 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: evp_locl.h,v 1.29 2022/11/10 15:17:30 jsing Exp $ */
+/* $OpenBSD: evp_locl.h,v 1.30 2022/11/10 16:37:52 jsing Exp $ */
 /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
  * project 2000.
  */
@@ -74,6 +74,15 @@ typedef int evp_verify_method(int type, const unsigned char *m,
     unsigned int m_length, const unsigned char *sigbuf, unsigned int siglen,
     void *key);
 
+struct ecx_key_st {
+       int nid;
+       int key_len;
+       uint8_t *priv_key;
+       size_t priv_key_len;
+       uint8_t *pub_key;
+       size_t pub_key_len;
+};
+
 /* Type needs to be a bit field
  * Sub-type needs to be for variations on the method, as in, can it do
  * arbitrary encryption.... */
@@ -96,6 +105,7 @@ struct evp_pkey_st {
 #endif
 #ifndef OPENSSL_NO_EC
                struct ec_key_st *ec;   /* ECC */
+               struct ecx_key_st *ecx; /* ECX */
 #endif
 #ifndef OPENSSL_NO_GOST
                struct gost_key_st *gost; /* GOST */
index 0a0451f..19610df 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: pmeth_lib.c,v 1.24 2022/11/09 18:25:36 jsing Exp $ */
+/* $OpenBSD: pmeth_lib.c,v 1.25 2022/11/10 16:37:52 jsing Exp $ */
 /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
  * project 2006.
  */
@@ -82,24 +82,28 @@ extern const EVP_PKEY_METHOD cmac_pkey_meth;
 extern const EVP_PKEY_METHOD dh_pkey_meth;
 extern const EVP_PKEY_METHOD dsa_pkey_meth;
 extern const EVP_PKEY_METHOD ec_pkey_meth;
+extern const EVP_PKEY_METHOD ed25519_pkey_meth;
 extern const EVP_PKEY_METHOD gostimit_pkey_meth;
 extern const EVP_PKEY_METHOD gostr01_pkey_meth;
 extern const EVP_PKEY_METHOD hkdf_pkey_meth;
 extern const EVP_PKEY_METHOD hmac_pkey_meth;
 extern const EVP_PKEY_METHOD rsa_pkey_meth;
 extern const EVP_PKEY_METHOD rsa_pss_pkey_meth;
+extern const EVP_PKEY_METHOD x25519_pkey_meth;
 
 static const EVP_PKEY_METHOD *pkey_methods[] = {
        &cmac_pkey_meth,
        &dh_pkey_meth,
        &dsa_pkey_meth,
        &ec_pkey_meth,
+       &ed25519_pkey_meth,
        &gostimit_pkey_meth,
        &gostr01_pkey_meth,
        &hkdf_pkey_meth,
        &hmac_pkey_meth,
        &rsa_pkey_meth,
        &rsa_pss_pkey_meth,
+       &x25519_pkey_meth,
 };
 
 static const size_t pkey_methods_count =