On 17.02.2019 15:46, Yuriy M. Kaminskiy wrote: > See attached: > > (1) patch against 7.9p1, tested with openssl 1.1.0j and openssl > 1.1.1a on linux/i386; passes regression test and connects to > unpatched sshd without problems; As ed25519-from-openssl patch came out a bit less convoluted, I've tried to do same with ecdh/x25519. So, here are V2: (1) use openssl-1.1.1a api, (2) [optional] emulate openssl-1.1.1a api for openssl-1.1.0. Unfortunately, it was a bit slower (as it needs to (de)serialize private key): > I hacked a bit regress/unittests/kex, and benchmarked > do_kex_with_key("curve25519-sha256@xxxxxxxxxx", KEY_ED25519, 256); > Before: > 0.3295s per call > After:> 0.2183s per call openssl/1.1.0j, curve25519 + ecdsa-sha256 (openssh's builtin eddsa is too slow, so difference between V1 and V2 is lost in noise, so I replaced ed25519 with ecdsa/p256 for this test) ecdh/25519 V1: 0.0185s per call ecdh/25519 V2: 0.0205s per call openssl/1.1.1a, curve25519 + ed25519 (with ed25519 patch) ecdh/25519 V1: 0.0115s per call ecdh/25519 V2: 0.0131s per call (worse by 14%) > That is, 50% speedup; assuming ed25519 (added to openssl in 1.1.1) > takes about same time as ecdh/x25519, there are potential for total > 200% speedup in KEX. > > (2) rebased patch against git master; passes regression test; > > I relied on presence of NID_X25519 for autodetection; probably it > makes sense to check if is actually working it autoconf; then again, > maybe not (it won't work when cross-compiling anyway). > > P.S. given amount of feedback I received so far, it seems everyone > follows motto "it cannot be secure if it is not slow".
>From 0dd2a94627daedae3597fa4b8678847b91b3b1ed Mon Sep 17 00:00:00 2001 From: "Yuriy M. Kaminskiy" <yumkam@xxxxxxxxx> Date: Mon, 18 Feb 2019 02:43:19 +0300 Subject: [PATCH 1/2] use curve25519/ecdh from openssl (1.1.1a) when possible --- configure.ac | 4 +++ kex.h | 2 +- kexc25519.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++-- kexsntrup4591761x25519.c | 6 ++-- 4 files changed, 96 insertions(+), 6 deletions(-) diff --git a/configure.ac b/configure.ac index 30be6c18..183f5cc5 100644 --- a/configure.ac +++ b/configure.ac @@ -2759,6 +2759,10 @@ if test "x$openssl" = "xyes" ; then EVP_PKEY_get0_RSA \ EVP_MD_CTX_new \ EVP_MD_CTX_free \ + EVP_PKEY_new_raw_public_key \ + EVP_PKEY_new_raw_private_key \ + EVP_PKEY_get_raw_public_key \ + EVP_PKEY_get_raw_private_key \ ]) if test "x$openssl_engine" = "xyes" ; then diff --git a/kex.h b/kex.h index 6d446d1c..fb1f9bcf 100644 --- a/kex.h +++ b/kex.h @@ -235,7 +235,7 @@ int kexgex_hash(int, const struct sshbuf *, const struct sshbuf *, const BIGNUM *, const u_char *, size_t, u_char *, size_t *); -void kexc25519_keygen(u_char key[CURVE25519_SIZE], u_char pub[CURVE25519_SIZE]) +int kexc25519_keygen(u_char key[CURVE25519_SIZE], u_char pub[CURVE25519_SIZE]) __attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE))) __attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE))); int kexc25519_shared_key(const u_char key[CURVE25519_SIZE], diff --git a/kexc25519.c b/kexc25519.c index f13d766d..b10c964e 100644 --- a/kexc25519.c +++ b/kexc25519.c @@ -40,19 +40,67 @@ #include "ssherr.h" #include "ssh2.h" +#ifdef WITH_OPENSSL +# if defined(HAVE_EVP_PKEY_GET_RAW_PUBLIC_KEY) && defined(HAVE_EVP_PKEY_GET_RAW_PRIVATE_KEY) && defined(HAVE_EVP_PKEY_NEW_RAW_PUBLIC_KEY) && defined(HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY) +# include <openssl/evp.h> +# include "openbsd-compat/openssl-compat.h" +# if defined(EVP_PKEY_X25519) +# define WITH_OPENSSL_X25519 +# endif +#endif /* HAVE_* */ +#endif /* WITH_OPENSSL */ + +#ifndef WITH_OPENSSL_X25519 extern int crypto_scalarmult_curve25519(u_char a[CURVE25519_SIZE], const u_char b[CURVE25519_SIZE], const u_char c[CURVE25519_SIZE]) __attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE))) __attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE))) __attribute__((__bounded__(__minbytes__, 3, CURVE25519_SIZE))); +#endif -void +int kexc25519_keygen(u_char key[CURVE25519_SIZE], u_char pub[CURVE25519_SIZE]) { +#ifdef WITH_OPENSSL_X25519 + int r = SSH_ERR_LIBCRYPTO_ERROR; + size_t pklen; + EVP_PKEY_CTX *pctx = NULL; + EVP_PKEY *pkey = NULL; + + if ((pctx = EVP_PKEY_CTX_new_id(NID_X25519, NULL)) == NULL) + goto out; + if (EVP_PKEY_keygen_init(pctx) <= 0) + goto out; + if (EVP_PKEY_keygen(pctx, &pkey) <= 0) + goto out; + if (EVP_PKEY_get_raw_public_key(pkey, NULL, &pklen) <= 0) + goto out; + if (pklen != CURVE25519_SIZE) + goto out; + if (EVP_PKEY_get_raw_public_key(pkey, pub, &pklen) <= 0) + goto out; + if (pklen != CURVE25519_SIZE) + goto out; + if (EVP_PKEY_get_raw_private_key(pkey, NULL, &pklen) <= 0) + goto out; + if (pklen != CURVE25519_SIZE) + goto out; + if (EVP_PKEY_get_raw_private_key(pkey, key, &pklen) <= 0) + goto out; + if (pklen != CURVE25519_SIZE) + goto out; + r = 0; +out: + EVP_PKEY_CTX_free(pctx); + EVP_PKEY_free(pkey); + return r; +#else static const u_char basepoint[CURVE25519_SIZE] = {9}; arc4random_buf(key, CURVE25519_SIZE); crypto_scalarmult_curve25519(pub, key, basepoint); + return 0; +#endif } int @@ -62,8 +110,42 @@ kexc25519_shared_key_ext(const u_char key[CURVE25519_SIZE], u_char shared_key[CURVE25519_SIZE]; u_char zero[CURVE25519_SIZE]; int r; +#ifdef WITH_OPENSSL_X25519 + EVP_PKEY_CTX *pctx = NULL; + EVP_PKEY *pkey = NULL; + EVP_PKEY *pubpk = NULL; + size_t pklen; + r = SSH_ERR_LIBCRYPTO_ERROR; + pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_X25519, NULL, key, CURVE25519_SIZE); + if (pkey == NULL) + goto out; + pctx = EVP_PKEY_CTX_new(pkey, NULL); + if (pctx == NULL) + goto out; + pubpk = EVP_PKEY_new_raw_public_key(EVP_PKEY_X25519, NULL, pub, CURVE25519_SIZE); + if (pubpk == NULL) + goto out; + if (EVP_PKEY_derive_init(pctx) <= 0) + goto out; + if (EVP_PKEY_derive_set_peer(pctx, pubpk) <= 0) + goto out; + if (EVP_PKEY_derive(pctx, NULL, &pklen) <= 0) + goto out; + if (pklen != CURVE25519_SIZE) + goto out; + if (EVP_PKEY_derive(pctx, shared_key, &pklen) <= 0) + goto out; + r = 0; +out: + EVP_PKEY_free(pubpk); + EVP_PKEY_free(pkey); + EVP_PKEY_CTX_free(pctx); + if (r) + return r; +#else crypto_scalarmult_curve25519(shared_key, key, pub); +#endif /* Check for all-zero shared secret */ explicit_bzero(zero, CURVE25519_SIZE); @@ -99,7 +181,8 @@ kex_c25519_keypair(struct kex *kex) return SSH_ERR_ALLOC_FAIL; if ((r = sshbuf_reserve(buf, CURVE25519_SIZE, &cp)) != 0) goto out; - kexc25519_keygen(kex->c25519_client_key, cp); + if ((r = kexc25519_keygen(kex->c25519_client_key, cp)) != 0) + goto out; #ifdef DEBUG_KEXECDH dump_digest("client public key c25519:", cp, CURVE25519_SIZE); #endif @@ -139,7 +222,8 @@ kex_c25519_enc(struct kex *kex, const struct sshbuf *client_blob, } if ((r = sshbuf_reserve(server_blob, CURVE25519_SIZE, &server_pub)) != 0) goto out; - kexc25519_keygen(server_key, server_pub); + if ((r = kexc25519_keygen(server_key, server_pub)) != 0) + goto out; /* allocate shared secret */ if ((buf = sshbuf_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; diff --git a/kexsntrup4591761x25519.c b/kexsntrup4591761x25519.c index 3b9b664f..6bff013e 100644 --- a/kexsntrup4591761x25519.c +++ b/kexsntrup4591761x25519.c @@ -56,7 +56,8 @@ kex_kem_sntrup4591761x25519_keypair(struct kex *kex) crypto_kem_sntrup4591761_PUBLICKEYBYTES); #endif cp += crypto_kem_sntrup4591761_PUBLICKEYBYTES; - kexc25519_keygen(kex->c25519_client_key, cp); + if ((r = kexc25519_keygen(kex->c25519_client_key, cp)) != 0) + goto out; #ifdef DEBUG_KEXECDH dump_digest("client public key c25519:", cp, CURVE25519_SIZE); #endif @@ -119,7 +120,8 @@ kex_kem_sntrup4591761x25519_enc(struct kex *kex, crypto_kem_sntrup4591761_enc(ciphertext, kem_key, client_pub); /* generate ECDH key pair, store server pubkey after ciphertext */ server_pub = ciphertext + crypto_kem_sntrup4591761_CIPHERTEXTBYTES; - kexc25519_keygen(server_key, server_pub); + if ((r = kexc25519_keygen(server_key, server_pub)) != 0) + goto out; /* append ECDH shared key */ client_pub += crypto_kem_sntrup4591761_PUBLICKEYBYTES; if ((r = kexc25519_shared_key_ext(server_key, client_pub, buf, 1)) < 0) -- 2.11.0
>From 8fbd0083cec464c5c3a03f7b0c797bcb692d2a6a Mon Sep 17 00:00:00 2001 From: "Yuriy M. Kaminskiy" <yumkam@xxxxxxxxx> Date: Mon, 18 Feb 2019 14:04:09 +0300 Subject: [PATCH 2/2] curve25519/ecdh: emulate openssl-1.1.1 API on openssl-1.1.0 Dumbed down to support only EVP_PKEY_X25519. --- kexc25519.c | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 115 insertions(+), 4 deletions(-) diff --git a/kexc25519.c b/kexc25519.c index b10c964e..2d4a087f 100644 --- a/kexc25519.c +++ b/kexc25519.c @@ -41,13 +41,124 @@ #include "ssh2.h" #ifdef WITH_OPENSSL -# if defined(HAVE_EVP_PKEY_GET_RAW_PUBLIC_KEY) && defined(HAVE_EVP_PKEY_GET_RAW_PRIVATE_KEY) && defined(HAVE_EVP_PKEY_NEW_RAW_PUBLIC_KEY) && defined(HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY) # include <openssl/evp.h> # include "openbsd-compat/openssl-compat.h" -# if defined(EVP_PKEY_X25519) + +# if defined(NID_X25519) # define WITH_OPENSSL_X25519 -# endif -#endif /* HAVE_* */ +/* dumbed-down specialized openssl-1.1.1a API emulation */ +# if !defined(EVP_PKEY_X25519) +# define EVP_PKEY_X25519 NID_X25519 +# endif +# if !defined(HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY) || !defined(HAVE_EVP_PKEY_NEW_RAW_PUBLIC_KEY) || !defined(HAVE_EVP_PKEY_NEW_RAW_PUBLIC_KEY) || !defined(HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY) +# include <string.h> +# include <openssl/x509.h> +# endif +# if !defined(HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY) +# define EVP_PKEY_new_raw_private_key ssh_EVP_PKEY_new_raw_private_key +static +EVP_PKEY *EVP_PKEY_new_raw_private_key(int type, ENGINE *e, + const unsigned char *key, size_t keylen) +{ + unsigned char buf[16 + CURVE25519_SIZE] = { + 0x30, 0x2e, /* SEQUENCE */ + 0x02, 0x01, 0x00, /* INTEGER :0 */ + 0x30, 0x05, /* SEQUENCE */ + 0x06, 0x03, 0x2b, 0x65, 0x6e, /* OBJECT :X25519 */ + 0x04, 0x22, /* OCTET STRING */ + 0x04, 0x20, /* OCTET STRING */ + /* raw X25519 private key */ + }; + EVP_PKEY *pkey = NULL; + const unsigned char *p = buf; + if (type != NID_X25519) + return NULL; + if (keylen != CURVE25519_SIZE) + return NULL; + memcpy (buf + sizeof(buf) - keylen, key, keylen); + pkey = d2i_PrivateKey(NID_X25519, NULL, &p, sizeof(buf)); + /* assert(pkey == NULL || p == buf + sizeof(buf)); */ + /* assert(pkey == NULL || EVP_PKEY_id(pkey) == NID_X25519); */ + return pkey; +} +# endif /* !HAVE_EVP_PKEY_NEW_RAW_PRIVATE_KEY */ +# if !defined(HAVE_EVP_PKEY_NEW_RAW_PUBLIC_KEY) +# define EVP_PKEY_new_raw_public_key ssh_EVP_PKEY_new_raw_public_key +static +EVP_PKEY *EVP_PKEY_new_raw_public_key(int type, ENGINE *e, + const unsigned char *key, size_t keylen) +{ + unsigned char buf[12 + CURVE25519_SIZE] = { + 0x30, 0x2a, /* SEQUENCE */ + 0x30, 0x05, /* SEQUENCE */ + 0x06, 0x03, 0x2b, 0x65, 0x6e, /* OBJECT :X25519 */ + 0x03, 0x21, /* BITSTRING */ + 0x00, /* raw X25519 public key */ + }; + EVP_PKEY *pkey = NULL; + const unsigned char *p = buf; + if (type != NID_X25519) + return NULL; + if (keylen != CURVE25519_SIZE) + return NULL; + memcpy (buf + sizeof(buf) - keylen, key, keylen); + pkey = d2i_PUBKEY(NULL, &p, sizeof(buf)); + /* assert(pkey == NULL || p == buf + sizeof(buf)); */ + /* assert(pkey == NULL || EVP_PKEY_id(pkey) == NID_X25519); */ + return pkey; +} +# endif /* !HAVE_EVP_PKEY_NEW_RAW_PUBLIC_KEY */ +# if !defined(HAVE_EVP_PKEY_GET_RAW_PRIVATE_KEY) +# define EVP_PKEY_get_raw_private_key ssh_EVP_PKEY_get_raw_private_key +static +int EVP_PKEY_get_raw_private_key(const EVP_PKEY *pkey, unsigned char *priv, size_t *len) +{ + int r = 0, l; + unsigned char *p = NULL; + if (EVP_PKEY_id(pkey) != NID_X25519) + return 0; + if (priv == NULL) { + *len = CURVE25519_SIZE; + return 1; + } + l = i2d_PrivateKey((EVP_PKEY *)pkey, &p); + /* DER X25519 private key representation ends with raw private key */ + if (l < CURVE25519_SIZE) + goto out; + *len = CURVE25519_SIZE; + memcpy(priv, p + l - CURVE25519_SIZE, CURVE25519_SIZE); + r = 1; +out: + OPENSSL_clear_free(p, l); + return r; +} +# endif /* !HAVE_EVP_PKEY_GET_RAW_PRIVATE_KEY */ +# if !defined(HAVE_EVP_PKEY_GET_RAW_PUBLIC_KEY) +# define EVP_PKEY_get_raw_public_key ssh_EVP_PKEY_get_raw_public_key +static +int EVP_PKEY_get_raw_public_key(const EVP_PKEY *pkey, unsigned char *pub, size_t *len) +{ + int r = 0, l; + unsigned char *p = NULL; + if (EVP_PKEY_id(pkey) != NID_X25519) + return 0; + if (pub == NULL) { + *len = CURVE25519_SIZE; + return 1; + } + l = i2d_PUBKEY((EVP_PKEY *)pkey, &p); + /* DER X25519 public key representation ends with raw public key */ + if (l < CURVE25519_SIZE) + goto out; + *len = CURVE25519_SIZE; + memcpy(pub, p + l - CURVE25519_SIZE, CURVE25519_SIZE); + r = 1; +out: + OPENSSL_free(p); + return r; +} +# endif /* !HAVE_EVP_PKEY_GET_RAW_PUBLIC_KEY */ +# endif /* NID_X25519 */ #endif /* WITH_OPENSSL */ #ifndef WITH_OPENSSL_X25519 -- 2.11.0
_______________________________________________ openssh-unix-dev mailing list openssh-unix-dev@xxxxxxxxxxx https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev