[PATCH] Updated: EVP_MAC interface for Poly1305

Chris Rapier rapier at psc.edu
Tue Nov 1 03:16:30 AEDT 2022


I've updated the patch to allow for the use of the EVP_MAC interface 
under OpenSSL 3.0. The test is now in configure and explicitly tests to 
see if the poly1305 algorithm can be loaded via EVP_MAC_fetch. I've also 
move the EVP calls to poly1305.c. To do this I had to extend the 
poly1305_auth interface to include another parameter. If the poly1305 
MAC is available it will pass in the mac context. Otherwise it will pass 
a NULL. Throughput performance, in my test bed is, a bit more than 
double in comparison to baseline. Obviously results will vary but there 
is a significantly reduction in CPU utilization.

I was told that my mailer (thunderbird) was breaking patches so I've 
include the patch both inline and attached.
diff --git a/cipher-chachapoly-libcrypto.c b/cipher-chachapoly-libcrypto.c
index 719f9c843..7dbbe7ee1 100644
--- a/cipher-chachapoly-libcrypto.c
+++ b/cipher-chachapoly-libcrypto.c
@@ -35,8 +35,18 @@
  #include "ssherr.h"
  #include "cipher-chachapoly.h"

+
+/* using the EVP_MAC interface for poly1305 is significantly
+ * faster than the version bundled with OpenSSH. However,
+ * this interface is only available in OpenSSL 3.0+
+ * -cjr 10/21/2022 */
  struct chachapoly_ctx {
  	EVP_CIPHER_CTX *main_evp, *header_evp;
+#ifdef OPENSSL_HAVE_POLY_EVP
+	EVP_MAC_CTX    *poly_ctx;
+#else
+	char           *poly_ctx;
+#endif
  };

  struct chachapoly_ctx *
@@ -57,6 +67,15 @@ chachapoly_new(const u_char *key, u_int keylen)
  		goto fail;
  	if (EVP_CIPHER_CTX_iv_length(ctx->header_evp) != 16)
  		goto fail;
+#ifdef OPENSSL_HAVE_POLY_EVP
+	EVP_MAC *mac = NULL;
+	if ((mac = EVP_MAC_fetch(NULL, "POLY1305", NULL)) == NULL)
+		goto fail;
+	if ((ctx->poly_ctx = EVP_MAC_CTX_new(mac)) == NULL)
+		goto fail;
+#else
+	ctx->poly_ctx = NULL;
+#endif
  	return ctx;
   fail:
  	chachapoly_free(ctx);
@@ -70,6 +89,9 @@ chachapoly_free(struct chachapoly_ctx *cpctx)
  		return;
  	EVP_CIPHER_CTX_free(cpctx->main_evp);
  	EVP_CIPHER_CTX_free(cpctx->header_evp);
+#ifdef OPENSSL_HAVE_POLY_EVP
+	EVP_MAC_CTX_free(cpctx->poly_ctx);
+#endif
  	freezero(cpctx, sizeof(*cpctx));
  }

@@ -107,8 +129,7 @@ chachapoly_crypt(struct chachapoly_ctx *ctx, u_int 
seqnr, u_char *dest,
  	/* If decrypting, check tag before anything else */
  	if (!do_encrypt) {
  		const u_char *tag = src + aadlen + len;
-
-		poly1305_auth(expected_tag, src, aadlen + len, poly_key);
+		poly1305_auth(ctx->poly_ctx, expected_tag, src, aadlen + len, poly_key);
  		if (timingsafe_bcmp(expected_tag, tag, POLY1305_TAGLEN) != 0) {
  			r = SSH_ERR_MAC_INVALID;
  			goto out;
@@ -134,8 +155,8 @@ chachapoly_crypt(struct chachapoly_ctx *ctx, u_int 
seqnr, u_char *dest,

  	/* If encrypting, calculate and append tag */
  	if (do_encrypt) {
-		poly1305_auth(dest + aadlen + len, dest, aadlen + len,
-		    poly_key);
+	  poly1305_auth(ctx->poly_ctx, dest + aadlen + len, dest, aadlen + len,
+			poly_key);
  	}
  	r = 0;
   out:
diff --git a/cipher-chachapoly.c b/cipher-chachapoly.c
index 716f8d426..136d2d502 100644
--- a/cipher-chachapoly.c
+++ b/cipher-chachapoly.c
@@ -89,7 +89,7 @@ chachapoly_crypt(struct chachapoly_ctx *ctx, u_int 
seqnr, u_char *dest,
  	if (!do_encrypt) {
  		const u_char *tag = src + aadlen + len;

-		poly1305_auth(expected_tag, src, aadlen + len, poly_key);
+		poly1305_auth(NULL, expected_tag, src, aadlen + len, poly_key);
  		if (timingsafe_bcmp(expected_tag, tag, POLY1305_TAGLEN) != 0) {
  			r = SSH_ERR_MAC_INVALID;
  			goto out;
@@ -109,7 +109,7 @@ chachapoly_crypt(struct chachapoly_ctx *ctx, u_int 
seqnr, u_char *dest,

  	/* If encrypting, calculate and append tag */
  	if (do_encrypt) {
-		poly1305_auth(dest + aadlen + len, dest, aadlen + len,
+		poly1305_auth(NULL, dest + aadlen + len, dest, aadlen + len,
  		    poly_key);
  	}
  	r = 0;
diff --git a/configure.ac b/configure.ac
index 8a18f8381..5fe3ae74c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2932,6 +2933,30 @@ if test "x$openssl" = "xyes" ; then
  		EVP_chacha20 \
  	])

+	# OpenSSL 3.0 API
+	# Does OpenSSL support the EVP_MAC functions for Poly1305?
+	AC_MSG_CHECKING([whether OpenSSL supports Poly1305 MAC EVP])
+	AC_RUN_IFELSE(
+		[AC_LANG_PROGRAM([[
+	#include <stdlib.h>
+	#include <stdio.h>
+	#include <openssl/evp.h>
+		]], [[
+		EVP_MAC *mac = EVP_MAC_fetch(NULL, "poly1305", NULL);
+		if (mac == NULL)
+		   exit(1);
+		]])],
+		[
+			AC_MSG_RESULT([yes])
+			AC_DEFINE([OPENSSL_HAVE_POLY_EVP], [1],
+			    [Libcrypto supports Poly1305 MAC EVP])
+		],
+		[
+			AC_MSG_RESULT([no])
+		]
+	)
+
+
  	if test "x$openssl_engine" = "xyes" ; then
  		AC_MSG_CHECKING([for OpenSSL ENGINE support])
  		AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
diff --git a/poly1305.c b/poly1305.c
index 6fd1fc8cd..53ebff884 100644
--- a/poly1305.c
+++ b/poly1305.c
@@ -13,6 +13,15 @@
  #endif

  #include "poly1305.h"
+#ifdef OPENSSL_HAVE_POLY_EVP
+void
+poly1305_auth(EVP_MAC_CTX *poly_ctx, unsigned char 
out[POLY1305_TAGLEN], const unsigned char *m, size_t inlen, const 
unsigned char key[POLY1305_KEYLEN]) {
+	size_t poly_out_len;
+	EVP_MAC_init(poly_ctx, (const u_char *)key, POLY1305_KEYLEN, NULL);
+	EVP_MAC_update(poly_ctx, m, inlen);
+	EVP_MAC_final(poly_ctx, out, &poly_out_len, (size_t)POLY1305_TAGLEN);
+}
+#else

  #define mul32x32_64(a,b) ((uint64_t)(a) * (b))

@@ -31,7 +42,7 @@
  	} while (0)

  void
-poly1305_auth(unsigned char out[POLY1305_TAGLEN], const unsigned char 
*m, size_t inlen, const unsigned char key[POLY1305_KEYLEN]) {
+poly1305_auth(char *unused, unsigned char out[POLY1305_TAGLEN], const 
unsigned char *m, size_t inlen, const unsigned char key[POLY1305_KEYLEN]) {
  	uint32_t t0,t1,t2,t3;
  	uint32_t h0,h1,h2,h3,h4;
  	uint32_t r0,r1,r2,r3,r4;
@@ -158,3 +169,4 @@ poly1305_donna_finish:
  	U32TO8_LE(&out[ 8], f2); f3 += (f2 >> 32);
  	U32TO8_LE(&out[12], f3);
  }
+#endif /* OPENSSL_HAVE_POLY_EVP */
diff --git a/poly1305.h b/poly1305.h
index f7db5f8d7..db2524b24 100644
--- a/poly1305.h
+++ b/poly1305.h
@@ -13,8 +13,15 @@
  #define POLY1305_KEYLEN		32
  #define POLY1305_TAGLEN		16

-void poly1305_auth(u_char out[POLY1305_TAGLEN], const u_char *m, size_t 
inlen,
+#ifdef OPENSSL_HAVE_POLY_EVP
+#include <openssl/evp.h>
+
+void poly1305_auth(EVP_MAC_CTX *poly_key, u_char out[POLY1305_TAGLEN], 
const u_char *m, size_t inlen,
+    const u_char key[POLY1305_KEYLEN])
+#else
+void poly1305_auth(char *unused, u_char out[POLY1305_TAGLEN], const 
u_char *m, size_t inlen,
      const u_char key[POLY1305_KEYLEN])
+#endif
      __attribute__((__bounded__(__minbytes__, 1, POLY1305_TAGLEN)))
      __attribute__((__bounded__(__buffer__, 2, 3)))
      __attribute__((__bounded__(__minbytes__, 4, POLY1305_KEYLEN)));
-------------- next part --------------
diff --git a/cipher-chachapoly-libcrypto.c b/cipher-chachapoly-libcrypto.c
index 719f9c843..7dbbe7ee1 100644
--- a/cipher-chachapoly-libcrypto.c
+++ b/cipher-chachapoly-libcrypto.c
@@ -35,8 +35,18 @@
 #include "ssherr.h"
 #include "cipher-chachapoly.h"
 
+
+/* using the EVP_MAC interface for poly1305 is significantly
+ * faster than the version bundled with OpenSSH. However,
+ * this interface is only available in OpenSSL 3.0+
+ * -cjr 10/21/2022 */
 struct chachapoly_ctx {
 	EVP_CIPHER_CTX *main_evp, *header_evp;
+#ifdef OPENSSL_HAVE_POLY_EVP
+	EVP_MAC_CTX    *poly_ctx;
+#else
+	char           *poly_ctx;
+#endif
 };
 
 struct chachapoly_ctx *
@@ -57,6 +67,15 @@ chachapoly_new(const u_char *key, u_int keylen)
 		goto fail;
 	if (EVP_CIPHER_CTX_iv_length(ctx->header_evp) != 16)
 		goto fail;
+#ifdef OPENSSL_HAVE_POLY_EVP
+	EVP_MAC *mac = NULL;
+	if ((mac = EVP_MAC_fetch(NULL, "POLY1305", NULL)) == NULL)
+		goto fail;
+	if ((ctx->poly_ctx = EVP_MAC_CTX_new(mac)) == NULL)
+		goto fail;
+#else
+	ctx->poly_ctx = NULL;
+#endif
 	return ctx;
  fail:
 	chachapoly_free(ctx);
@@ -70,6 +89,9 @@ chachapoly_free(struct chachapoly_ctx *cpctx)
 		return;
 	EVP_CIPHER_CTX_free(cpctx->main_evp);
 	EVP_CIPHER_CTX_free(cpctx->header_evp);
+#ifdef OPENSSL_HAVE_POLY_EVP
+	EVP_MAC_CTX_free(cpctx->poly_ctx);
+#endif
 	freezero(cpctx, sizeof(*cpctx));
 }
 
@@ -107,8 +129,7 @@ chachapoly_crypt(struct chachapoly_ctx *ctx, u_int seqnr, u_char *dest,
 	/* If decrypting, check tag before anything else */
 	if (!do_encrypt) {
 		const u_char *tag = src + aadlen + len;
-
-		poly1305_auth(expected_tag, src, aadlen + len, poly_key);
+		poly1305_auth(ctx->poly_ctx, expected_tag, src, aadlen + len, poly_key);
 		if (timingsafe_bcmp(expected_tag, tag, POLY1305_TAGLEN) != 0) {
 			r = SSH_ERR_MAC_INVALID;
 			goto out;
@@ -134,8 +155,8 @@ chachapoly_crypt(struct chachapoly_ctx *ctx, u_int seqnr, u_char *dest,
 
 	/* If encrypting, calculate and append tag */
 	if (do_encrypt) {
-		poly1305_auth(dest + aadlen + len, dest, aadlen + len,
-		    poly_key);
+	  poly1305_auth(ctx->poly_ctx, dest + aadlen + len, dest, aadlen + len,
+			poly_key);
 	}
 	r = 0;
  out:
diff --git a/cipher-chachapoly.c b/cipher-chachapoly.c
index 716f8d426..136d2d502 100644
--- a/cipher-chachapoly.c
+++ b/cipher-chachapoly.c
@@ -89,7 +89,7 @@ chachapoly_crypt(struct chachapoly_ctx *ctx, u_int seqnr, u_char *dest,
 	if (!do_encrypt) {
 		const u_char *tag = src + aadlen + len;
 
-		poly1305_auth(expected_tag, src, aadlen + len, poly_key);
+		poly1305_auth(NULL, expected_tag, src, aadlen + len, poly_key);
 		if (timingsafe_bcmp(expected_tag, tag, POLY1305_TAGLEN) != 0) {
 			r = SSH_ERR_MAC_INVALID;
 			goto out;
@@ -109,7 +109,7 @@ chachapoly_crypt(struct chachapoly_ctx *ctx, u_int seqnr, u_char *dest,
 
 	/* If encrypting, calculate and append tag */
 	if (do_encrypt) {
-		poly1305_auth(dest + aadlen + len, dest, aadlen + len,
+		poly1305_auth(NULL, dest + aadlen + len, dest, aadlen + len,
 		    poly_key);
 	}
 	r = 0;
diff --git a/configure.ac b/configure.ac
index 8a18f8381..5fe3ae74c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2932,6 +2933,30 @@ if test "x$openssl" = "xyes" ; then
 		EVP_chacha20 \
 	])
 
+	# OpenSSL 3.0 API
+	# Does OpenSSL support the EVP_MAC functions for Poly1305?
+	AC_MSG_CHECKING([whether OpenSSL supports Poly1305 MAC EVP])
+	AC_RUN_IFELSE(
+		[AC_LANG_PROGRAM([[
+	#include <stdlib.h>
+	#include <stdio.h>
+	#include <openssl/evp.h>
+		]], [[
+		EVP_MAC *mac = EVP_MAC_fetch(NULL, "poly1305", NULL);
+		if (mac == NULL)
+		   exit(1);
+		]])],
+		[
+			AC_MSG_RESULT([yes])
+			AC_DEFINE([OPENSSL_HAVE_POLY_EVP], [1],
+			    [Libcrypto supports Poly1305 MAC EVP])
+		],
+		[
+			AC_MSG_RESULT([no])
+		]
+	)
+
+
 	if test "x$openssl_engine" = "xyes" ; then
 		AC_MSG_CHECKING([for OpenSSL ENGINE support])
 		AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
diff --git a/poly1305.c b/poly1305.c
index 6fd1fc8cd..53ebff884 100644
--- a/poly1305.c
+++ b/poly1305.c
@@ -13,6 +13,15 @@
 #endif
 
 #include "poly1305.h"
+#ifdef OPENSSL_HAVE_POLY_EVP
+void
+poly1305_auth(EVP_MAC_CTX *poly_ctx, unsigned char out[POLY1305_TAGLEN], const unsigned char *m, size_t inlen, const unsigned char key[POLY1305_KEYLEN]) {
+	size_t poly_out_len;
+	EVP_MAC_init(poly_ctx, (const u_char *)key, POLY1305_KEYLEN, NULL);
+	EVP_MAC_update(poly_ctx, m, inlen);
+	EVP_MAC_final(poly_ctx, out, &poly_out_len, (size_t)POLY1305_TAGLEN);
+}
+#else
 
 #define mul32x32_64(a,b) ((uint64_t)(a) * (b))
 
@@ -31,7 +42,7 @@
 	} while (0)
 
 void
-poly1305_auth(unsigned char out[POLY1305_TAGLEN], const unsigned char *m, size_t inlen, const unsigned char key[POLY1305_KEYLEN]) {
+poly1305_auth(char *unused, unsigned char out[POLY1305_TAGLEN], const unsigned char *m, size_t inlen, const unsigned char key[POLY1305_KEYLEN]) {
 	uint32_t t0,t1,t2,t3;
 	uint32_t h0,h1,h2,h3,h4;
 	uint32_t r0,r1,r2,r3,r4;
@@ -158,3 +169,4 @@ poly1305_donna_finish:
 	U32TO8_LE(&out[ 8], f2); f3 += (f2 >> 32);
 	U32TO8_LE(&out[12], f3);
 }
+#endif /* OPENSSL_HAVE_POLY_EVP */
diff --git a/poly1305.h b/poly1305.h
index f7db5f8d7..db2524b24 100644
--- a/poly1305.h
+++ b/poly1305.h
@@ -13,8 +13,15 @@
 #define POLY1305_KEYLEN		32
 #define POLY1305_TAGLEN		16
 
-void poly1305_auth(u_char out[POLY1305_TAGLEN], const u_char *m, size_t inlen,
+#ifdef OPENSSL_HAVE_POLY_EVP
+#include <openssl/evp.h>
+
+void poly1305_auth(EVP_MAC_CTX *poly_key, u_char out[POLY1305_TAGLEN], const u_char *m, size_t inlen,
+    const u_char key[POLY1305_KEYLEN])
+#else
+void poly1305_auth(char *unused, u_char out[POLY1305_TAGLEN], const u_char *m, size_t inlen,
     const u_char key[POLY1305_KEYLEN])
+#endif
     __attribute__((__bounded__(__minbytes__, 1, POLY1305_TAGLEN)))
     __attribute__((__bounded__(__buffer__, 2, 3)))
     __attribute__((__bounded__(__minbytes__, 4, POLY1305_KEYLEN)));


More information about the openssh-unix-dev mailing list