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