[PATCH v2 1-2/2] use ecdh/X25519 from openssl when possible (openssl-1.1.1+)

[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

 



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

[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux