From: jsing Date: Thu, 10 Nov 2022 16:37:51 +0000 (+0000) Subject: Implement EVP interfaces for Ed25519 and X25519. X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=c93606bba17b5c5dfedfc6be81baf5a3c520d6c0;p=openbsd Implement EVP interfaces for Ed25519 and X25519. ok beck@ tb@ --- 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 + * + * 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 "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\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\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, +}; 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 =