[PATCH 2/3] tests/basic: check 32-bit and 64-bit overflow for CTR and ChaCha20

Jussi Kivilinna jussi.kivilinna at iki.fi
Wed Dec 2 20:08:37 CET 2020


* tests/basic.c (check_one_cipher_ctr_reset)
(check_one_cipher_ctr_overflow): New.
(check_one_cipher): Add counter overflow tests for ChaCha20 and CTR
mode.
--

Patch adds counter overflow tests to check for correct counter handling
in bulk processing implementations.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna at iki.fi>
---
 tests/basic.c | 232 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 232 insertions(+)

diff --git a/tests/basic.c b/tests/basic.c
index 1d12c4a2..4beeeed9 100644
--- a/tests/basic.c
+++ b/tests/basic.c
@@ -9415,6 +9415,210 @@ err_out_free:
 
 
 
+static int
+check_one_cipher_ctr_reset (gcry_cipher_hd_t hd, int algo, int mode,
+			    u32 ctr_high_bits, int be_ctr,
+			    int pass)
+{
+  unsigned char iv[16] = { 0 };
+  unsigned char swap;
+  unsigned int ivlen;
+  u32 ctr_low_bits;
+  int err;
+  int i;
+
+  /* This should be largest parallel block processing count in any
+   * implementation negated. Currently for CTR this is 32 and, for
+   * ChaCha20, count is 8. */
+  ctr_low_bits = (mode == GCRY_CIPHER_MODE_CTR) ? -32 : -8;
+
+  gcry_cipher_reset (hd);
+
+  if (mode == GCRY_CIPHER_MODE_CTR)
+    ivlen = get_algo_mode_blklen(algo, GCRY_CIPHER_MODE_ECB);
+  else
+    ivlen = 16;
+
+  /* Little-endian fill. */
+  for (i = 0; i < 4; i++)
+    iv[i + 0] = (ctr_low_bits >> (i * 8)) & 0xff;
+  for (i = 0; i < 4; i++)
+    iv[i + 4] = (ctr_high_bits >> (i * 8)) & 0xff;
+
+  if (be_ctr)
+    {
+      /* Swap to big-endian. */
+      for (i = 0; i < ivlen / 2; i++)
+	{
+	  swap = iv[i];
+	  iv[i] = iv[ivlen - (i + 1)];
+	  iv[ivlen - (i + 1)] = swap;
+	}
+    }
+
+  clutter_vector_registers();
+  if (mode == GCRY_CIPHER_MODE_CTR)
+    err = gcry_cipher_setctr (hd, iv, ivlen);
+  else
+    err = gcry_cipher_setiv (hd, iv, ivlen);
+
+  if (err)
+    {
+      fail ("pass %d, algo %d, mode %d, gcry_cipher_setiv failed: %s\n",
+	    pass, algo, mode, gpg_strerror (err));
+      gcry_cipher_close (hd);
+      return -1;
+    }
+
+  return 0;
+}
+
+static int
+check_one_cipher_ctr_overflow (int algo, int mode, int flags,
+			       const char *key, size_t nkey,
+			       const unsigned char *plain, size_t nplain,
+			       unsigned long ctr_high_bits, int be_ctr,
+			       int pass)
+{
+  gcry_cipher_hd_t hd;
+  unsigned char *out;
+  unsigned char *enc_result;
+  int keylen;
+  gcry_error_t err = 0;
+  unsigned int firstlen;
+  unsigned int leftlen;
+  unsigned int blklen;
+  unsigned int pos;
+  unsigned int i;
+
+  out = malloc (nplain);
+  enc_result = malloc (nplain);
+  if (!out || !enc_result)
+    {
+      fail ("pass %d, algo %d, mode %d, malloc failed\n",
+	    pass, algo, mode);
+      goto err_out_free;
+    }
+
+  assert (nkey == 64);
+  assert (nplain > 0);
+  assert ((nplain % 16) == 0);
+
+  keylen = gcry_cipher_get_algo_keylen (algo);
+  if (!keylen)
+    {
+      fail ("pass %d, algo %d, mode %d, gcry_cipher_get_algo_keylen failed\n",
+	    pass, algo, mode);
+      goto err_out_free;
+    }
+
+  if (keylen < 40 / 8 || keylen > 32)
+    {
+      fail ("pass %d, algo %d, mode %d, keylength problem (%d)\n",
+	    pass, algo, mode, keylen);
+      goto err_out_free;
+    }
+
+  err = gcry_cipher_open (&hd, algo, mode, flags);
+  if (err)
+    {
+      fail ("pass %d, algo %d, mode %d, gcry_cipher_open failed: %s\n",
+	    pass, algo, mode, gpg_strerror (err));
+      goto err_out_free;
+    }
+
+  clutter_vector_registers();
+  err = gcry_cipher_setkey (hd, key, keylen);
+  if (err)
+    {
+      fail ("pass %d, algo %d, mode %d, gcry_cipher_setkey failed: %s\n",
+	    pass, algo, mode, gpg_strerror (err));
+      gcry_cipher_close (hd);
+      goto err_out_free;
+    }
+
+  if (check_one_cipher_ctr_reset (hd, algo, mode, ctr_high_bits, be_ctr,
+				  pass) < 0)
+    goto err_out_free;
+
+  /* Non-bulk processing. */
+  for (i = 0; i < nplain; i += 16)
+    {
+      clutter_vector_registers();
+      err = gcry_cipher_encrypt (hd, out + i, 16, plain + i, 16);
+      if (err)
+	{
+	  fail ("pass %d, algo %d, mode %d, gcry_cipher_encrypt failed: %s\n",
+		pass, algo, mode, gpg_strerror (err));
+	  gcry_cipher_close (hd);
+	  goto err_out_free;
+	}
+    }
+
+  memcpy (enc_result, out, nplain);
+
+  /* Test with different bulk processing sizes. */
+  for (blklen = 2 * 16; blklen <= 32 * 16; blklen *= 2)
+    {
+      /* Move bulk processing start offset, test at different spots to
+       * test bulk counter calculation throughly. */
+      for (firstlen = 16; firstlen < 8 * 64; firstlen += 16)
+	{
+	  if (check_one_cipher_ctr_reset (hd, algo, mode, ctr_high_bits, be_ctr,
+					  pass) < 0)
+	    goto err_out_free;
+
+	  clutter_vector_registers();
+	  err = gcry_cipher_encrypt (hd, out, firstlen, plain, firstlen);
+	  if (err)
+	    {
+	      fail ("pass %d, algo %d, mode %d, gcry_cipher_encrypt "
+		    "failed: %s\n", pass, algo, mode, gpg_strerror (err));
+	      gcry_cipher_close (hd);
+	      goto err_out_free;
+	    }
+
+	  leftlen = nplain - firstlen;
+	  pos = firstlen;
+	  while (leftlen)
+	    {
+	      unsigned int currlen = leftlen > blklen ? blklen : leftlen;
+
+	      clutter_vector_registers();
+	      err = gcry_cipher_encrypt (hd, out + pos, currlen, plain + pos,
+					 currlen);
+	      if (err)
+		{
+		  fail ("pass %d, algo %d, mode %d, block len %d, first len %d,"
+			"gcry_cipher_encrypt failed: %s\n", pass, algo, mode,
+			blklen, firstlen, gpg_strerror (err));
+		  gcry_cipher_close (hd);
+		  goto err_out_free;
+		}
+
+	      pos += currlen;
+	      leftlen -= currlen;
+	    }
+
+	  if (memcmp (enc_result, out, nplain))
+	    fail ("pass %d, algo %d, mode %d, block len %d, first len %d, "
+	          "encrypt mismatch\n", pass, algo, mode, blklen, firstlen);
+	}
+    }
+
+  gcry_cipher_close (hd);
+
+  free (enc_result);
+  free (out);
+  return 0;
+
+err_out_free:
+  free (enc_result);
+  free (out);
+  return -1;
+}
+
+
 static void
 check_one_cipher (int algo, int mode, int flags)
 {
@@ -9491,6 +9695,34 @@ check_one_cipher (int algo, int mode, int flags)
 			     50))
     goto out;
 
+  /* Pass 6: Counter overflow tests for ChaCha20 and CTR mode. */
+  if (mode == GCRY_CIPHER_MODE_STREAM && algo == GCRY_CIPHER_CHACHA20)
+    {
+      /* 32bit overflow test (little-endian counter) */
+      if (check_one_cipher_ctr_overflow (algo, mode, flags, key, 64, plain,
+					  medium_buffer_size, 0UL,
+					  0, 60))
+	goto out;
+      /* 64bit overflow test (little-endian counter) */
+      if (check_one_cipher_ctr_overflow (algo, mode, flags, key, 64, plain,
+					  medium_buffer_size, 0xffffffffUL,
+					  0, 61))
+	goto out;
+    }
+   else if (mode == GCRY_CIPHER_MODE_CTR)
+    {
+      /* 32bit overflow test (big-endian counter) */
+      if (check_one_cipher_ctr_overflow (algo, mode, flags, key, 64, plain,
+					  medium_buffer_size, 0UL,
+					  1, 62))
+	goto out;
+      /* 64bit overflow test (big-endian counter) */
+      if (check_one_cipher_ctr_overflow (algo, mode, flags, key, 64, plain,
+					  medium_buffer_size, 0xffffffffUL,
+					  1, 63))
+	goto out;
+    }
+
 out:
   free (plain);
 }
-- 
2.27.0




More information about the Gcrypt-devel mailing list