From 10ce4feedec9df53636aa64e41e43e01d260f42c Mon Sep 17 00:00:00 2001
From: Nikos Mavrogiannopoulos <nmav@redhat.com>
Date: Wed, 31 Jan 2018 13:22:20 +0100
Subject: [PATCH] gcm: use ctr_crypt16() for improved performance

That also enhances ctr_crypt16() to be complete and handle
the full input.
---
 Makefile.in    |  1 +
 ctr-internal.h | 39 +++++++++++++++++++++++++++++++++++++++
 ctr.c          | 44 ++++++++++++++++++++++++++++++--------------
 gcm.c          | 50 ++++++++++++++++++--------------------------------
 4 files changed, 88 insertions(+), 46 deletions(-)
 create mode 100644 ctr-internal.h

diff --git a/Makefile.in b/Makefile.in
index 6a0c13ec..58980341 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -228,6 +228,7 @@ DISTFILES = $(SOURCES) $(HEADERS) getopt.h getopt_int.h \
 	aes-internal.h camellia-internal.h serpent-internal.h \
 	cast128_sboxes.h desinfo.h desCode.h \
 	memxor-internal.h nettle-internal.h nettle-write.h \
+	ctr-internal.h \
 	gmp-glue.h ecc-internal.h fat-setup.h \
 	mini-gmp.h asm.m4 \
 	nettle.texinfo nettle.info nettle.html nettle.pdf sha-example.c
diff --git a/ctr-internal.h b/ctr-internal.h
new file mode 100644
index 00000000..246afaba
--- /dev/null
+++ b/ctr-internal.h
@@ -0,0 +1,39 @@
+/* ctr.c
+
+   Cipher counter mode.
+
+   Copyright (C) 2005-2018 Niels Möller
+
+   This file is part of GNU Nettle.
+
+   GNU Nettle is free software: you can redistribute it and/or
+   modify it under the terms of either:
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at your
+       option) any later version.
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at your
+       option) any later version.
+
+   or both in parallel, as here.
+
+   GNU Nettle is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see http://www.gnu.org/licenses/.
+*/
+
+void
+ctr_crypt16(const void *ctx, nettle_cipher_func *f,
+	    void (*fillfunc)(uint8_t *ctr, size_t blocks, uint64_t *buffer),
+	    uint8_t *ctr,
+	    size_t length, uint8_t *dst,
+	    const uint8_t *src);
diff --git a/ctr.c b/ctr.c
index 8295e1af..2c7ddf3d 100644
--- a/ctr.c
+++ b/ctr.c
@@ -40,6 +40,7 @@
 #include <string.h>
 
 #include "ctr.h"
+#include "ctr-internal.h"
 
 #include "macros.h"
 #include "memxor.h"
@@ -106,20 +107,37 @@ ctr_fill16(uint8_t *ctr, size_t blocks, uint64_t *buffer)
 # endif
 #endif /* !WORDS_BIGENDIAN */
 
-#if USE_CTR_CRYPT16
-static size_t
+void
 ctr_crypt16(const void *ctx, nettle_cipher_func *f,
+	    void (*fill)(uint8_t *ctr, size_t blocks, uint64_t *buffer),
 	    uint8_t *ctr,
 	    size_t length, uint8_t *dst,
 	    const uint8_t *src)
 {
   if (dst != src && !((uintptr_t) dst % sizeof(uint64_t)))
     {
+      size_t done;
       size_t blocks = length / 16u;
-      ctr_fill16 (ctr, blocks, (uint64_t *) dst);
-      f(ctx, blocks * 16, dst, dst);
-      memxor (dst, src, blocks * 16);
-      return blocks * 16;
+      fill (ctr, blocks, (uint64_t *) dst);
+
+      done = blocks * 16;
+      f(ctx, done, dst, dst);
+      memxor (dst, src, done);
+
+      dst += done;
+      src += done;
+      length -= done;
+
+      /* remainder < 1 block */
+      if (length > 0)
+        {
+	  uint8_t block[16];
+
+	  f(ctx, 16, block, ctr);
+	  fill (ctr, 1, (uint64_t *) ctr);
+	  memxor3(dst, src, block, length);
+	}
+      return;
     }
   else
     {
@@ -133,7 +151,7 @@ ctr_crypt16(const void *ctx, nettle_cipher_func *f,
       for (i = 0; blocks >= CTR_BUFFER_LIMIT / 16;
 	   i += CTR_BUFFER_LIMIT, blocks -= CTR_BUFFER_LIMIT / 16)
 	{
-	  ctr_fill16 (ctr, CTR_BUFFER_LIMIT / 16, buffer->u64);
+	  fill (ctr, CTR_BUFFER_LIMIT / 16, buffer->u64);
 	  f(ctx, CTR_BUFFER_LIMIT, buffer->b, buffer->b);
 	  if (length - i < CTR_BUFFER_LIMIT)
 	    goto done;
@@ -143,15 +161,14 @@ ctr_crypt16(const void *ctx, nettle_cipher_func *f,
       if (blocks > 0)
 	{
 	  assert (length - i < CTR_BUFFER_LIMIT);
-	  ctr_fill16 (ctr, blocks, buffer->u64);
+	  fill (ctr, blocks, buffer->u64);
 	  f(ctx, blocks * 16, buffer->b, buffer->b);
 	done:
 	  memxor3 (dst + i, src + i, buffer->b, length - i);
       }
-      return length;
+      return;
     }
 }
-#endif /* USE_CTR_CRYPT16 */
 
 void
 ctr_crypt(const void *ctx, nettle_cipher_func *f,
@@ -162,10 +179,9 @@ ctr_crypt(const void *ctx, nettle_cipher_func *f,
 #if USE_CTR_CRYPT16
   if (block_size == 16)
     {
-      size_t done = ctr_crypt16(ctx, f, ctr, length, dst, src);
-      length -= done;
-      src += done;
-      dst += done;
+      ctr_crypt16(ctx, f, ctr_fill16, ctr, length, dst, src);
+
+      return;
     }
 #endif
 
diff --git a/gcm.c b/gcm.c
index 0a2102f1..062410e9 100644
--- a/gcm.c
+++ b/gcm.c
@@ -51,6 +51,7 @@
 #include "memxor.h"
 #include "nettle-internal.h"
 #include "macros.h"
+#include "ctr.h"
 
 #define GHASH_POLYNOMIAL 0xE1UL
 
@@ -435,40 +436,25 @@ gcm_update(struct gcm_ctx *ctx, const struct gcm_key *key,
 }
 
 static void
-gcm_crypt(struct gcm_ctx *ctx, const void *cipher, nettle_cipher_func *f,
-	  size_t length, uint8_t *dst, const uint8_t *src)
+gcm_fill(uint8_t *ctr, size_t blocks, uint64_t *_buffer)
 {
-  uint8_t buffer[GCM_BLOCK_SIZE];
+  uint8_t *buffer = _buffer;
+  uint32_t c;
 
-  if (src != dst)
-    {
-      for (; length >= GCM_BLOCK_SIZE;
-           (length -= GCM_BLOCK_SIZE,
-	    src += GCM_BLOCK_SIZE, dst += GCM_BLOCK_SIZE))
-        {
-          f (cipher, GCM_BLOCK_SIZE, dst, ctx->ctr.b);
-          memxor (dst, src, GCM_BLOCK_SIZE);
-          INC32 (ctx->ctr);
-        }
-    }
-  else
-    {
-      for (; length >= GCM_BLOCK_SIZE;
-           (length -= GCM_BLOCK_SIZE,
-	    src += GCM_BLOCK_SIZE, dst += GCM_BLOCK_SIZE))
-        {
-          f (cipher, GCM_BLOCK_SIZE, buffer, ctx->ctr.b);
-          memxor (dst, buffer, GCM_BLOCK_SIZE);
-          INC32 (ctx->ctr);
-        }
-    }
-  if (length > 0)
+  c = READ_UINT32(ctr + GCM_BLOCK_SIZE - 4);
+
+  while (blocks-- > 0)
     {
-      /* A final partial block */
-      f (cipher, GCM_BLOCK_SIZE, buffer, ctx->ctr.b);
-      memxor3 (dst, src, buffer, length);
-      INC32 (ctx->ctr);
+      memcpy(buffer, ctr, GCM_BLOCK_SIZE - 4);
+      buffer += GCM_BLOCK_SIZE - 4;
+
+      WRITE_UINT32(buffer, c);
+      buffer += 4;
+
+      c++;
     }
+
+  WRITE_UINT32(ctr + GCM_BLOCK_SIZE - 4, c);
 }
 
 void
@@ -478,7 +464,7 @@ gcm_encrypt (struct gcm_ctx *ctx, const struct gcm_key *key,
 {
   assert(ctx->data_size % GCM_BLOCK_SIZE == 0);
 
-  gcm_crypt(ctx, cipher, f, length, dst, src);
+  ctr_crypt16(cipher, f, gcm_fill, ctx->ctr.b, length, dst, src);
   gcm_hash(key, &ctx->x, length, dst);
 
   ctx->data_size += length;
@@ -492,7 +478,7 @@ gcm_decrypt(struct gcm_ctx *ctx, const struct gcm_key *key,
   assert(ctx->data_size % GCM_BLOCK_SIZE == 0);
 
   gcm_hash(key, &ctx->x, length, src);
-  gcm_crypt(ctx, cipher, f, length, dst, src);
+  ctr_crypt16(cipher, f, gcm_fill, ctx->ctr.b, length, dst, src);
 
   ctx->data_size += length;
 }
-- 
2.14.3

