summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Sing <jsing@cvs.openbsd.org>2022-11-10 16:37:53 +0000
committerJoel Sing <jsing@cvs.openbsd.org>2022-11-10 16:37:53 +0000
commite392082edded0a81c75383b9ddc67d67b8395205 (patch)
tree80803c1ab0fc96c528f7c24a6abf5036731611a7
parent58165d78cfdbed99dd775baa4c3f1e07dd73aa16 (diff)
Implement EVP interfaces for Ed25519 and X25519.
ok beck@ tb@
-rw-r--r--lib/libcrypto/Makefile4
-rw-r--r--lib/libcrypto/asn1/ameth_lib.c6
-rw-r--r--lib/libcrypto/ec/ec.h4
-rw-r--r--lib/libcrypto/ec/ec_err.c4
-rw-r--r--lib/libcrypto/ec/ecx_methods.c862
-rw-r--r--lib/libcrypto/evp/evp.h11
-rw-r--r--lib/libcrypto/evp/evp_locl.h12
-rw-r--r--lib/libcrypto/evp/pmeth_lib.c6
8 files changed, 902 insertions, 7 deletions
diff --git a/lib/libcrypto/Makefile b/lib/libcrypto/Makefile
index b25ea5f948c..084d6c1786d 100644
--- a/lib/libcrypto/Makefile
+++ b/lib/libcrypto/Makefile
@@ -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
diff --git a/lib/libcrypto/asn1/ameth_lib.c b/lib/libcrypto/asn1/ameth_lib.c
index cca1dcce7d6..143c010dfa2 100644
--- a/lib/libcrypto/asn1/ameth_lib.c
+++ b/lib/libcrypto/asn1/ameth_lib.c
@@ -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 =
diff --git a/lib/libcrypto/ec/ec.h b/lib/libcrypto/ec/ec.h
index 52c8f2f3294..9a7c5de8a52 100644
--- a/lib/libcrypto/ec/ec.h
+++ b/lib/libcrypto/ec/ec.h
@@ -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
diff --git a/lib/libcrypto/ec/ec_err.c b/lib/libcrypto/ec/ec_err.c
index d8ead9636cc..2ff8e9974ee 100644
--- a/lib/libcrypto/ec/ec_err.c
+++ b/lib/libcrypto/ec/ec_err.c
@@ -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
index 00000000000..741cb2de6b0
--- /dev/null
+++ b/lib/libcrypto/ec/ecx_methods.c
@@ -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,
+};
diff --git a/lib/libcrypto/evp/evp.h b/lib/libcrypto/evp/evp.h
index f4702ab4337..f8bab26b3f9 100644
--- a/lib/libcrypto/evp/evp.h
+++ b/lib/libcrypto/evp/evp.h
@@ -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.
*
@@ -115,6 +115,10 @@
#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];
diff --git a/lib/libcrypto/evp/evp_locl.h b/lib/libcrypto/evp/evp_locl.h
index dd7d2522e61..37fc55eb9c6 100644
--- a/lib/libcrypto/evp/evp_locl.h
+++ b/lib/libcrypto/evp/evp_locl.h
@@ -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 */
diff --git a/lib/libcrypto/evp/pmeth_lib.c b/lib/libcrypto/evp/pmeth_lib.c
index 0a0451fd92a..19610dfbad8 100644
--- a/lib/libcrypto/evp/pmeth_lib.c
+++ b/lib/libcrypto/evp/pmeth_lib.c
@@ -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 =