[patch 1/2] use chacha20 from openssl (1.1.0+) when possible

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

 



On some cpu's optimized chacha implementation in openssl (1.1.0+) is
notably faster (and on others it is just faster) than generic C
implementation in openssh.

Sadly, openssl's chacha20-poly1305 (EVP_chacha20_poly1305) uses
different scheme (with padding/etc - see rfc8439) and it looks it is not
possible to use in openssh.

OpenSSL 1.1.1+ also exports "raw" poly1305 primitive, but I
have not tried it yet (it was not in 1.1.0).

Trivial benchmark:
time ssh -c chacha20-poly1305@xxxxxxxxxxx -S none -o Compression=no \
localhost 'dd if=/dev/zero bs=100000 count=10000' >/dev/null
(comparing "user time" only)

openssh: 7.9p1, self-compiled, based on upstream package from debian/unstable, hostkey - ecdsa/p256, pubkey auth key - ecdh/p256

Machine: pretty old amd k8 (w/ SSE2, but no SSSE3/AVX/AESNI)
OS: linux/debian/stretch, openssl 1.1.0j-1deb9u1
i386: speed: +8%
amd64: speed: +10%

Machine: raspberry pi 3b+ (BCM2837B0, 4-core Cortex-A53 @1.4GHz)
OS: raspbian/stretch

baseline: armhf/raspbian: unpatched ssh-7.9p1: 30.8s

with openssl 1.1.0j-1deb9u1 from raspbian (compiled for armv6 without neon):

armhf/raspbian: 24.7 seconds, speed: +25%

with openssl 1.1.0j-1deb9u1 from debian/stretch/armhf (compiled for
armv7 with neon autodetection):
armhf: 22.2 seconds, speed: +39%

Patches against 7.9p1 (tested) and git master (untested, only resolved configure.ac conflict) attached.
> From c5836f4630034ea3adb9e6e9071b4e6e9deafcb1 Mon Sep 17 00:00:00 2001
From: "Yuriy M. Kaminskiy" <yumkam@xxxxxxxxx>
Date: Wed, 16 Jan 2019 01:21:08 +0300
Subject: [PATCH] use chacha20 from openssl when possible

---
 cipher-chachapoly.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++---
 cipher-chachapoly.h | 11 ++++++-
 cipher.c            |  4 +--
 configure.ac        |  4 +++
 4 files changed, 95 insertions(+), 7 deletions(-)

diff --git a/cipher-chachapoly.c b/cipher-chachapoly.c
index 0899c5ad..c495a87f 100644
--- a/cipher-chachapoly.c
+++ b/cipher-chachapoly.c
@@ -30,13 +30,39 @@
 
 int
 chachapoly_init(struct chachapoly_ctx *ctx,
-    const u_char *key, u_int keylen)
+    const u_char *key, u_int keylen, int do_encrypt)
 {
+	int ret = 0;
+
 	if (keylen != (32 + 32)) /* 2 x 256 bit keys */
 		return SSH_ERR_INVALID_ARGUMENT;
+#if defined(WITH_OPENSSL) && defined(HAVE_EVP_CHACHA20)
+	if ((ctx->main_evp = EVP_CIPHER_CTX_new()) == NULL) {
+		ret = SSH_ERR_ALLOC_FAIL;
+		goto out;
+	}
+	if ((ctx->header_evp = EVP_CIPHER_CTX_new()) == NULL) {
+		ret = SSH_ERR_ALLOC_FAIL;
+		goto out;
+	}
+	if (!EVP_CipherInit(ctx->main_evp, EVP_chacha20(), key, NULL,
+				do_encrypt)) {
+		ret = SSH_ERR_LIBCRYPTO_ERROR;
+		goto out;
+	}
+	if (!EVP_CipherInit(ctx->header_evp, EVP_chacha20(), key + 32, NULL,
+				do_encrypt)) {
+		ret = SSH_ERR_LIBCRYPTO_ERROR;
+		goto out;
+	}
+#else
 	chacha_keysetup(&ctx->main_ctx, key, 256);
 	chacha_keysetup(&ctx->header_ctx, key + 32, 256);
-	return 0;
+#endif
+out:
+	if (ret != 0)
+		chachapoly_done(ctx);
+	return ret;
 }
 
 /*
@@ -52,20 +78,34 @@ int
 chachapoly_crypt(struct chachapoly_ctx *ctx, u_int seqnr, u_char *dest,
     const u_char *src, u_int len, u_int aadlen, u_int authlen, int do_encrypt)
 {
+#if defined(WITH_OPENSSL) && defined(HAVE_EVP_CHACHA20)
+	u_char seqbuf[16];
+	int r = SSH_ERR_LIBCRYPTO_ERROR;
+#else
 	u_char seqbuf[8];
+	int r = SSH_ERR_INTERNAL_ERROR;
 	const u_char one[8] = { 1, 0, 0, 0, 0, 0, 0, 0 }; /* NB little-endian */
+#endif
 	u_char expected_tag[POLY1305_TAGLEN], poly_key[POLY1305_KEYLEN];
-	int r = SSH_ERR_INTERNAL_ERROR;
 
 	/*
 	 * Run ChaCha20 once to generate the Poly1305 key. The IV is the
 	 * packet sequence number.
 	 */
 	memset(poly_key, 0, sizeof(poly_key));
+#if defined(WITH_OPENSSL) && defined(HAVE_EVP_CHACHA20)
+	memset(seqbuf + 0, 0, 8);
+	POKE_U64(seqbuf + 8, seqnr);
+	if (!EVP_CipherInit(ctx->main_evp, NULL, NULL, seqbuf, do_encrypt))
+		goto out;
+	if (EVP_Cipher(ctx->main_evp, poly_key, (u_char *)poly_key, sizeof(poly_key)) < 0)
+		goto out;
+#else
 	POKE_U64(seqbuf, seqnr);
 	chacha_ivsetup(&ctx->main_ctx, seqbuf, NULL);
 	chacha_encrypt_bytes(&ctx->main_ctx,
 	    poly_key, poly_key, sizeof(poly_key));
+#endif
 
 	/* If decrypting, check tag before anything else */
 	if (!do_encrypt) {
@@ -80,14 +120,29 @@ chachapoly_crypt(struct chachapoly_ctx *ctx, u_int seqnr, u_char *dest,
 
 	/* Crypt additional data */
 	if (aadlen) {
+#if defined(WITH_OPENSSL) && defined(HAVE_EVP_CHACHA20)
+		if (!EVP_CipherInit(ctx->header_evp, NULL, NULL, seqbuf, do_encrypt))
+			goto out;
+		if (EVP_Cipher(ctx->header_evp, dest, src, aadlen) < 0)
+			goto out;
+#else
 		chacha_ivsetup(&ctx->header_ctx, seqbuf, NULL);
 		chacha_encrypt_bytes(&ctx->header_ctx, src, dest, aadlen);
+#endif
 	}
 
 	/* Set Chacha's block counter to 1 */
+#if defined(WITH_OPENSSL) && defined(HAVE_EVP_CHACHA20)
+	seqbuf[0] = 1;
+	if (!EVP_CipherInit(ctx->main_evp, NULL, NULL, seqbuf, do_encrypt))
+		goto out;
+	if (EVP_Cipher(ctx->main_evp, dest + aadlen, src + aadlen, len) < 0)
+		goto out;
+#else
 	chacha_ivsetup(&ctx->main_ctx, seqbuf, one);
 	chacha_encrypt_bytes(&ctx->main_ctx, src + aadlen,
 	    dest + aadlen, len);
+#endif
 
 	/* If encrypting, calculate and append tag */
 	if (do_encrypt) {
@@ -107,13 +162,33 @@ int
 chachapoly_get_length(struct chachapoly_ctx *ctx,
     u_int *plenp, u_int seqnr, const u_char *cp, u_int len)
 {
-	u_char buf[4], seqbuf[8];
+	u_char buf[4], seqbuf[16];
 
 	if (len < 4)
 		return SSH_ERR_MESSAGE_INCOMPLETE;
+#if defined(WITH_OPENSSL) && defined(HAVE_EVP_CHACHA20)
+	memset(seqbuf + 0, 0, 8);
+	POKE_U64(seqbuf + 8, seqnr);
+	if (!EVP_CipherInit(ctx->header_evp, NULL, NULL, seqbuf, 0))
+		return SSH_ERR_LIBCRYPTO_ERROR;
+	if (EVP_Cipher(ctx->header_evp, buf, (u_char *)cp, 4) < 0)
+		return SSH_ERR_LIBCRYPTO_ERROR;
+#else
 	POKE_U64(seqbuf, seqnr);
 	chacha_ivsetup(&ctx->header_ctx, seqbuf, NULL);
 	chacha_encrypt_bytes(&ctx->header_ctx, cp, buf, 4);
+#endif
 	*plenp = PEEK_U32(buf);
 	return 0;
 }
+
+int	chachapoly_done(struct chachapoly_ctx *cpctx) {
+#if defined(WITH_OPENSSL) && defined(HAVE_EVP_CHACHA20)
+	EVP_CIPHER_CTX_free(cpctx->main_evp);
+	EVP_CIPHER_CTX_free(cpctx->header_evp);
+	cpctx->main_evp = cpctx->header_evp = NULL;
+#else
+	explicit_bzero(cpctx, sizeof(*cpctx));
+#endif
+	return 0;
+}
diff --git a/cipher-chachapoly.h b/cipher-chachapoly.h
index b7072be7..f01180bb 100644
--- a/cipher-chachapoly.h
+++ b/cipher-chachapoly.h
@@ -19,17 +19,25 @@
 #define CHACHA_POLY_AEAD_H
 
 #include <sys/types.h>
+#if defined(WITH_OPENSSL) && defined(HAVE_EVP_CHACHA20)
+#include <openssl/evp.h>
+#else
 #include "chacha.h"
+#endif
 #include "poly1305.h"
 
 #define CHACHA_KEYLEN	32 /* Only 256 bit keys used here */
 
 struct chachapoly_ctx {
+#if defined(WITH_OPENSSL) && defined(HAVE_EVP_CHACHA20)
+	EVP_CIPHER_CTX *main_evp, *header_evp;
+#else
 	struct chacha_ctx main_ctx, header_ctx;
+#endif
 };
 
 int	chachapoly_init(struct chachapoly_ctx *cpctx,
-    const u_char *key, u_int keylen)
+    const u_char *key, u_int keylen, int do_encrypt)
     __attribute__((__bounded__(__buffer__, 2, 3)));
 int	chachapoly_crypt(struct chachapoly_ctx *cpctx, u_int seqnr,
     u_char *dest, const u_char *src, u_int len, u_int aadlen, u_int authlen,
@@ -37,5 +45,6 @@ int	chachapoly_crypt(struct chachapoly_ctx *cpctx, u_int seqnr,
 int	chachapoly_get_length(struct chachapoly_ctx *cpctx,
     u_int *plenp, u_int seqnr, const u_char *cp, u_int len)
     __attribute__((__bounded__(__buffer__, 4, 5)));
+int	chachapoly_done(struct chachapoly_ctx *cpctx);
 
 #endif /* CHACHA_POLY_AEAD_H */
diff --git a/cipher.c b/cipher.c
index 12c59888..f011edb7 100644
--- a/cipher.c
+++ b/cipher.c
@@ -259,7 +259,7 @@ cipher_init(struct sshcipher_ctx **ccp, const struct sshcipher *cipher,
 
 	cc->cipher = cipher;
 	if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) {
-		ret = chachapoly_init(&cc->cp_ctx, key, keylen);
+		ret = chachapoly_init(&cc->cp_ctx, key, keylen, do_encrypt);
 		goto out;
 	}
 	if ((cc->cipher->flags & CFLAG_NONE) != 0) {
@@ -413,7 +413,7 @@ cipher_free(struct sshcipher_ctx *cc)
 	if (cc == NULL)
 		return;
 	if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0)
-		explicit_bzero(&cc->cp_ctx, sizeof(cc->cp_ctx));
+		chachapoly_done(&cc->cp_ctx);
 	else if ((cc->cipher->flags & CFLAG_AESCTR) != 0)
 		explicit_bzero(&cc->ac_ctx, sizeof(cc->ac_ctx));
 #ifdef WITH_OPENSSL
diff --git a/configure.ac b/configure.ac
index 7379ab35..144a6bf0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2796,6 +2796,10 @@ if test "x$openssl" = "xyes" ; then
 		[AC_DEFINE([HAVE_EVP_CIPHER_CTX_CTRL], [1],
 		    [Define if libcrypto has EVP_CIPHER_CTX_ctrl])])
 
+	AC_SEARCH_LIBS([EVP_chacha20], [crypto],
+		[AC_DEFINE([HAVE_EVP_CHACHA20], [1],
+		    [Define if libcrypto has EVP_chacha20])])
+
 	# LibreSSL/OpenSSL 1.1x API
 	AC_SEARCH_LIBS([DH_get0_key], [crypto],
 		[AC_DEFINE([HAVE_DH_GET0_KEY], [1],
-- 
2.11.0


> From 819a6dac6c55df33925a9d139addbc72d299d062 Mon Sep 17 00:00:00 2001
From: "Yuriy M. Kaminskiy" <yumkam@xxxxxxxxx>
Date: Wed, 16 Jan 2019 01:21:08 +0300
Subject: [PATCH] use chacha20 from openssl when possible

---
 cipher-chachapoly.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++---
 cipher-chachapoly.h | 11 ++++++-
 cipher.c            |  4 +--
 configure.ac        |  1 +
 4 files changed, 92 insertions(+), 7 deletions(-)

diff --git a/cipher-chachapoly.c b/cipher-chachapoly.c
index 0899c5ad..c495a87f 100644
--- a/cipher-chachapoly.c
+++ b/cipher-chachapoly.c
@@ -30,13 +30,39 @@
 
 int
 chachapoly_init(struct chachapoly_ctx *ctx,
-    const u_char *key, u_int keylen)
+    const u_char *key, u_int keylen, int do_encrypt)
 {
+	int ret = 0;
+
 	if (keylen != (32 + 32)) /* 2 x 256 bit keys */
 		return SSH_ERR_INVALID_ARGUMENT;
+#if defined(WITH_OPENSSL) && defined(HAVE_EVP_CHACHA20)
+	if ((ctx->main_evp = EVP_CIPHER_CTX_new()) == NULL) {
+		ret = SSH_ERR_ALLOC_FAIL;
+		goto out;
+	}
+	if ((ctx->header_evp = EVP_CIPHER_CTX_new()) == NULL) {
+		ret = SSH_ERR_ALLOC_FAIL;
+		goto out;
+	}
+	if (!EVP_CipherInit(ctx->main_evp, EVP_chacha20(), key, NULL,
+				do_encrypt)) {
+		ret = SSH_ERR_LIBCRYPTO_ERROR;
+		goto out;
+	}
+	if (!EVP_CipherInit(ctx->header_evp, EVP_chacha20(), key + 32, NULL,
+				do_encrypt)) {
+		ret = SSH_ERR_LIBCRYPTO_ERROR;
+		goto out;
+	}
+#else
 	chacha_keysetup(&ctx->main_ctx, key, 256);
 	chacha_keysetup(&ctx->header_ctx, key + 32, 256);
-	return 0;
+#endif
+out:
+	if (ret != 0)
+		chachapoly_done(ctx);
+	return ret;
 }
 
 /*
@@ -52,20 +78,34 @@ int
 chachapoly_crypt(struct chachapoly_ctx *ctx, u_int seqnr, u_char *dest,
     const u_char *src, u_int len, u_int aadlen, u_int authlen, int do_encrypt)
 {
+#if defined(WITH_OPENSSL) && defined(HAVE_EVP_CHACHA20)
+	u_char seqbuf[16];
+	int r = SSH_ERR_LIBCRYPTO_ERROR;
+#else
 	u_char seqbuf[8];
+	int r = SSH_ERR_INTERNAL_ERROR;
 	const u_char one[8] = { 1, 0, 0, 0, 0, 0, 0, 0 }; /* NB little-endian */
+#endif
 	u_char expected_tag[POLY1305_TAGLEN], poly_key[POLY1305_KEYLEN];
-	int r = SSH_ERR_INTERNAL_ERROR;
 
 	/*
 	 * Run ChaCha20 once to generate the Poly1305 key. The IV is the
 	 * packet sequence number.
 	 */
 	memset(poly_key, 0, sizeof(poly_key));
+#if defined(WITH_OPENSSL) && defined(HAVE_EVP_CHACHA20)
+	memset(seqbuf + 0, 0, 8);
+	POKE_U64(seqbuf + 8, seqnr);
+	if (!EVP_CipherInit(ctx->main_evp, NULL, NULL, seqbuf, do_encrypt))
+		goto out;
+	if (EVP_Cipher(ctx->main_evp, poly_key, (u_char *)poly_key, sizeof(poly_key)) < 0)
+		goto out;
+#else
 	POKE_U64(seqbuf, seqnr);
 	chacha_ivsetup(&ctx->main_ctx, seqbuf, NULL);
 	chacha_encrypt_bytes(&ctx->main_ctx,
 	    poly_key, poly_key, sizeof(poly_key));
+#endif
 
 	/* If decrypting, check tag before anything else */
 	if (!do_encrypt) {
@@ -80,14 +120,29 @@ chachapoly_crypt(struct chachapoly_ctx *ctx, u_int seqnr, u_char *dest,
 
 	/* Crypt additional data */
 	if (aadlen) {
+#if defined(WITH_OPENSSL) && defined(HAVE_EVP_CHACHA20)
+		if (!EVP_CipherInit(ctx->header_evp, NULL, NULL, seqbuf, do_encrypt))
+			goto out;
+		if (EVP_Cipher(ctx->header_evp, dest, src, aadlen) < 0)
+			goto out;
+#else
 		chacha_ivsetup(&ctx->header_ctx, seqbuf, NULL);
 		chacha_encrypt_bytes(&ctx->header_ctx, src, dest, aadlen);
+#endif
 	}
 
 	/* Set Chacha's block counter to 1 */
+#if defined(WITH_OPENSSL) && defined(HAVE_EVP_CHACHA20)
+	seqbuf[0] = 1;
+	if (!EVP_CipherInit(ctx->main_evp, NULL, NULL, seqbuf, do_encrypt))
+		goto out;
+	if (EVP_Cipher(ctx->main_evp, dest + aadlen, src + aadlen, len) < 0)
+		goto out;
+#else
 	chacha_ivsetup(&ctx->main_ctx, seqbuf, one);
 	chacha_encrypt_bytes(&ctx->main_ctx, src + aadlen,
 	    dest + aadlen, len);
+#endif
 
 	/* If encrypting, calculate and append tag */
 	if (do_encrypt) {
@@ -107,13 +162,33 @@ int
 chachapoly_get_length(struct chachapoly_ctx *ctx,
     u_int *plenp, u_int seqnr, const u_char *cp, u_int len)
 {
-	u_char buf[4], seqbuf[8];
+	u_char buf[4], seqbuf[16];
 
 	if (len < 4)
 		return SSH_ERR_MESSAGE_INCOMPLETE;
+#if defined(WITH_OPENSSL) && defined(HAVE_EVP_CHACHA20)
+	memset(seqbuf + 0, 0, 8);
+	POKE_U64(seqbuf + 8, seqnr);
+	if (!EVP_CipherInit(ctx->header_evp, NULL, NULL, seqbuf, 0))
+		return SSH_ERR_LIBCRYPTO_ERROR;
+	if (EVP_Cipher(ctx->header_evp, buf, (u_char *)cp, 4) < 0)
+		return SSH_ERR_LIBCRYPTO_ERROR;
+#else
 	POKE_U64(seqbuf, seqnr);
 	chacha_ivsetup(&ctx->header_ctx, seqbuf, NULL);
 	chacha_encrypt_bytes(&ctx->header_ctx, cp, buf, 4);
+#endif
 	*plenp = PEEK_U32(buf);
 	return 0;
 }
+
+int	chachapoly_done(struct chachapoly_ctx *cpctx) {
+#if defined(WITH_OPENSSL) && defined(HAVE_EVP_CHACHA20)
+	EVP_CIPHER_CTX_free(cpctx->main_evp);
+	EVP_CIPHER_CTX_free(cpctx->header_evp);
+	cpctx->main_evp = cpctx->header_evp = NULL;
+#else
+	explicit_bzero(cpctx, sizeof(*cpctx));
+#endif
+	return 0;
+}
diff --git a/cipher-chachapoly.h b/cipher-chachapoly.h
index b7072be7..f01180bb 100644
--- a/cipher-chachapoly.h
+++ b/cipher-chachapoly.h
@@ -19,17 +19,25 @@
 #define CHACHA_POLY_AEAD_H
 
 #include <sys/types.h>
+#if defined(WITH_OPENSSL) && defined(HAVE_EVP_CHACHA20)
+#include <openssl/evp.h>
+#else
 #include "chacha.h"
+#endif
 #include "poly1305.h"
 
 #define CHACHA_KEYLEN	32 /* Only 256 bit keys used here */
 
 struct chachapoly_ctx {
+#if defined(WITH_OPENSSL) && defined(HAVE_EVP_CHACHA20)
+	EVP_CIPHER_CTX *main_evp, *header_evp;
+#else
 	struct chacha_ctx main_ctx, header_ctx;
+#endif
 };
 
 int	chachapoly_init(struct chachapoly_ctx *cpctx,
-    const u_char *key, u_int keylen)
+    const u_char *key, u_int keylen, int do_encrypt)
     __attribute__((__bounded__(__buffer__, 2, 3)));
 int	chachapoly_crypt(struct chachapoly_ctx *cpctx, u_int seqnr,
     u_char *dest, const u_char *src, u_int len, u_int aadlen, u_int authlen,
@@ -37,5 +45,6 @@ int	chachapoly_crypt(struct chachapoly_ctx *cpctx, u_int seqnr,
 int	chachapoly_get_length(struct chachapoly_ctx *cpctx,
     u_int *plenp, u_int seqnr, const u_char *cp, u_int len)
     __attribute__((__bounded__(__buffer__, 4, 5)));
+int	chachapoly_done(struct chachapoly_ctx *cpctx);
 
 #endif /* CHACHA_POLY_AEAD_H */
diff --git a/cipher.c b/cipher.c
index 12c59888..f011edb7 100644
--- a/cipher.c
+++ b/cipher.c
@@ -259,7 +259,7 @@ cipher_init(struct sshcipher_ctx **ccp, const struct sshcipher *cipher,
 
 	cc->cipher = cipher;
 	if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) {
-		ret = chachapoly_init(&cc->cp_ctx, key, keylen);
+		ret = chachapoly_init(&cc->cp_ctx, key, keylen, do_encrypt);
 		goto out;
 	}
 	if ((cc->cipher->flags & CFLAG_NONE) != 0) {
@@ -413,7 +413,7 @@ cipher_free(struct sshcipher_ctx *cc)
 	if (cc == NULL)
 		return;
 	if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0)
-		explicit_bzero(&cc->cp_ctx, sizeof(cc->cp_ctx));
+		chachapoly_done(&cc->cp_ctx);
 	else if ((cc->cipher->flags & CFLAG_AESCTR) != 0)
 		explicit_bzero(&cc->ac_ctx, sizeof(cc->ac_ctx));
 #ifdef WITH_OPENSSL
diff --git a/configure.ac b/configure.ac
index c1427247..dcb9da2f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2754,6 +2754,7 @@ if test "x$openssl" = "xyes" ; then
 		EVP_PKEY_get0_RSA \
 		EVP_MD_CTX_new \
 		EVP_MD_CTX_free \
+		EVP_chacha20 \
 	])
 
 	if test "x$openssl_engine" = "xyes" ; then
-- 
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