[PATCH] Support for Counter (CTR) mode

Simon Josefsson jas@extundo.com
Sun, 30 Mar 2003 20:48:56 +0200


Self tests passed on a few platforms, but no extensive testing.

Btw, is the coding style for libgcrypt documented somewhere?
(Preferably as parameters to "indent" or emacs configuration.)
I have (tried to) use the GNU coding style here, but mixing coding
style in the same files is probably not a good idea.

Index: AUTHORS
===================================================================
RCS file: /cvs/gnupg/libgcrypt/AUTHORS,v
retrieving revision 1.40
diff -u -p -r1.40 AUTHORS
--- AUTHORS	24 Mar 2003 19:32:29 -0000	1.40
+++ AUTHORS	30 Mar 2003 18:41:57 -0000
@@ -50,8 +50,8 @@ Assigns past and future changes.
 twoaday@freakmail.de
 
 LIBGCRYPT Simon Josefsson       2002-10-25
-Assigns past and future changes to FSF (cipher/{md4,crc}.c, CTS/MAC flags,
-self test improvements)
+Assigns past and future changes to FSF (cipher/{md4,crc}.c, CTR mode,
+CTS/MAC flags, self test improvements)
 simon@josefsson.org
 
 
Index: NEWS
===================================================================
RCS file: /cvs/gnupg/libgcrypt/NEWS,v
retrieving revision 1.32
diff -u -p -r1.32 NEWS
--- NEWS	26 Mar 2003 11:01:43 -0000	1.32
+++ NEWS	30 Mar 2003 18:41:57 -0000
@@ -14,6 +14,8 @@ Noteworthy changes in version 1.1.13 (un
  * CBC-MAC for block ciphers is now supported, by using a
    GCRY_CIPHER_CBC_MAC cipher flag.
 
+ * CTR mode for block ciphers is now supported.
+
  * RSA blinding is now supported and is used automatically for RSA
    decryption.  It can be explicitely disabled by using the `no-blinding'
    symbol in the `flags' s-exp.
Index: cipher/ChangeLog
===================================================================
RCS file: /cvs/gnupg/libgcrypt/cipher/ChangeLog,v
retrieving revision 1.155
diff -u -p -r1.155 ChangeLog
--- cipher/ChangeLog	30 Mar 2003 13:26:27 -0000	1.155
+++ cipher/ChangeLog	30 Mar 2003 18:41:57 -0000
@@ -1,3 +1,12 @@
+2003-03-30  Simon Josefsson  <jas@extundo.com>
+
+	* cipher.c (struct gcry_cipher_handle): Add counter field.
+	(gcry_cipher_open): Add CTR.
+	(cipher_reset): Clear counter field.
+	(do_ctr_encrypt, do_ctr_decrypt): New functions.
+	(cipher_encrypt, cipher_decrypt): Call CTR functions.
+	(gcry_cipher_ctl): Add SET_CTR to set counter.
+
 2003-03-30  Moritz Schulte  <moritz@g10code.com>
 
 	* rsa.c (_gcry_rsa_blind): New function.
Index: cipher/cipher.c
===================================================================
RCS file: /cvs/gnupg/libgcrypt/cipher/cipher.c,v
retrieving revision 1.52
diff -u -p -r1.52 cipher.c
--- cipher/cipher.c	24 Mar 2003 19:28:11 -0000	1.52
+++ cipher/cipher.c	30 Mar 2003 18:41:57 -0000
@@ -92,6 +92,7 @@ struct gcry_cipher_handle {
     byte iv[MAX_BLOCKSIZE];	/* (this should be ulong aligned) */
     byte lastiv[MAX_BLOCKSIZE];
     int  unused;  /* in IV */
+    byte ctr[MAX_BLOCKSIZE];    /* for Counter (CTR) mode */
     int  (*setkey)( void *c, byte *key, unsigned keylen );
     void (*encrypt)( void *c, byte *outbuf, byte *inbuf );
     void (*decrypt)( void *c, byte *outbuf, byte *inbuf );
@@ -538,6 +539,7 @@ gcry_cipher_open( int algo, int mode, un
       case GCRY_CIPHER_MODE_ECB:
       case GCRY_CIPHER_MODE_CBC:
       case GCRY_CIPHER_MODE_CFB:
+      case GCRY_CIPHER_MODE_CTR:
         if ( cipher_table[idx].encrypt == dummy_encrypt_block
              || cipher_table[idx].decrypt == dummy_decrypt_block ) {
             set_lasterr( GCRYERR_INV_CIPHER_MODE );
@@ -642,6 +644,7 @@ cipher_reset (GCRY_CIPHER_HD c)
 	  cipher_table[c->algo_index].contextsize);
   memset (c->iv, 0, c->blocksize);
   memset (c->lastiv, 0, c->blocksize);
+  memset (c->ctr, 0, c->blocksize);
 }
 
 
@@ -877,6 +880,59 @@ do_cfb_decrypt( GCRY_CIPHER_HD c,
 }
 
 
+static void
+do_ctr_encrypt( GCRY_CIPHER_HD c, byte *outbuf, const byte *inbuf, unsigned nbytes )
+{
+  unsigned int n;
+  byte tmp[MAX_BLOCKSIZE];
+
+  for(n=0; n < nbytes; n++)
+    {
+      if ((n % c->blocksize) == 0)
+	{
+	  int i;
+
+	  (*c->encrypt) (&c->context.c, tmp, c->ctr);
+
+	  for (i = c->blocksize; i > 0; i--)
+	    {
+	      c->ctr[i-1]++;
+	      if (c->ctr[i-1] != 0)
+		break;
+	    }
+	}
+
+      /* XOR input with encrypted counter and store in output */
+      outbuf[n] = inbuf[n] ^ tmp[n % c->blocksize];
+    }
+}
+
+static void
+do_ctr_decrypt( GCRY_CIPHER_HD c, byte *outbuf, const byte *inbuf, unsigned nbytes )
+{
+  unsigned int n;
+  byte tmp[MAX_BLOCKSIZE];
+
+  for(n=0; n < nbytes; n++ )
+    {
+      if ((n % c->blocksize) == 0)
+	{
+	  int i;
+
+	  (*c->encrypt) (&c->context.c, tmp, c->ctr);
+
+	  for (i = c->blocksize; i > 0; i--)
+	    {
+	      c->ctr[i-1]++;
+	      if (c->ctr[i-1] != 0)
+		break;
+	    }
+	}
+
+      /* XOR input with encrypted counter and store in output */
+      outbuf[n] = inbuf[n] ^ tmp[n % c->blocksize];
+    }
+}
 
 
 /****************
@@ -907,6 +963,9 @@ cipher_encrypt( GCRY_CIPHER_HD c, byte *
       case GCRY_CIPHER_MODE_CFB:
 	do_cfb_encrypt(c, outbuf, inbuf, nbytes );
 	break;
+      case GCRY_CIPHER_MODE_CTR:
+	do_ctr_encrypt(c, outbuf, inbuf, nbytes );
+	break;
       case GCRY_CIPHER_MODE_STREAM:
         (*c->stencrypt)( &c->context.c,
                          outbuf, (byte*)/*arggg*/inbuf, nbytes );
@@ -995,6 +1054,9 @@ cipher_decrypt( GCRY_CIPHER_HD c, byte *
       case GCRY_CIPHER_MODE_CFB:
 	do_cfb_decrypt(c, outbuf, inbuf, nbytes );
 	break;
+      case GCRY_CIPHER_MODE_CTR:
+	do_ctr_decrypt(c, outbuf, inbuf, nbytes );
+	break;
       case GCRY_CIPHER_MODE_STREAM:
         (*c->stdecrypt)( &c->context.c,
                          outbuf, (byte*)/*arggg*/inbuf, nbytes );
@@ -1100,6 +1162,14 @@ gcry_cipher_ctl( GCRY_CIPHER_HD h, int c
       if( h || !buffer || buflen != sizeof(int) )
         return set_lasterr( GCRYERR_INV_CIPHER_ALGO );
       disable_cipher_algo( *(int*)buffer );
+      break;
+    case GCRYCTL_SET_CTR:
+      if (buffer && buflen == h->blocksize)
+	memcpy (h->ctr, buffer, h->blocksize);
+      else if (buffer == NULL || buflen == 0)
+	memset (h->ctr, 0, h->blocksize);
+      else
+	rc = GCRYERR_INV_ARG;
       break;
 
     default:
Index: doc/ChangeLog
===================================================================
RCS file: /cvs/gnupg/libgcrypt/doc/ChangeLog,v
retrieving revision 1.11
diff -u -p -r1.11 ChangeLog
--- doc/ChangeLog	24 Mar 2003 19:31:42 -0000	1.11
+++ doc/ChangeLog	30 Mar 2003 18:41:57 -0000
@@ -1,3 +1,7 @@
+2003-03-30  Simon Josefsson  <jas@extundo.com>
+
+	* gcrypt.texi: Add CTR.
+
 2003-03-22  Simon Josefsson  <jas@extundo.com>
 
 	* gcrypt.texi: Add CBC-MAC.
Index: doc/gcrypt.texi
===================================================================
RCS file: /cvs/gnupg/libgcrypt/doc/gcrypt.texi,v
retrieving revision 1.11
diff -u -p -r1.11 gcrypt.texi
--- doc/gcrypt.texi	24 Mar 2003 19:31:42 -0000	1.11
+++ doc/gcrypt.texi	30 Mar 2003 18:41:58 -0000
@@ -381,6 +381,9 @@ Stream mode, only to be used with stream
 @item GCRY_CIPHER_MODE_OFB
 Outer Feedback mode.
 
+@item  GCRY_CIPHER_MODE_CTR
+Counter mode.
+
 @end table
 @c end cipher modes constants
 
@@ -431,9 +434,10 @@ Note, this is currently implemented as a
 macro but may be changed to a function in the future.
 @end deftypefun
 
-Most crypto modes requires an initialization vector (IV), which usually
-is a non-secret random string acting as a kind of salt value.  To set
-this IV, use the function:
+Most crypto modes requires an initialization vector (IV), which
+usually is a non-secret random string acting as a kind of salt value.
+The CTR mode requires an counter, which is also similar to a salt
+value.  To set the IV or CTR, use these functions:
 
 @deftypefun int gcry_cipher_setiv (GCRY_CIPHER_HD @var{h}, void *@var{k}, size_t @var{l})
 
@@ -442,6 +446,16 @@ vector is passed as the buffer @var{K} o
 internal data structures.  The function checks that the IV matches the
 requirement of the selected algorithm and mode.  Note, that this is
 implemented as a macro.
+@end deftypefun
+
+@deftypefun int gcry_cipher_setctr (GCRY_CIPHER_HD @var{h}, void *@var{c}, size_t @var{l})
+
+Set the counter vector used for encryption or decryption. The counter
+is passed as the buffer @var{c} of length @var{l} and copied to
+internal data structures.  The function checks that the counter
+matches the requirement of the selected algorithm (i.e., it must be
+the same size as the block size).  Note, that this is implemented as a
+macro.
 @end deftypefun
 
 @deftypefun int gcry_cipher_reset (GCRY_CIPHER_HD @var{h})
Index: src/ChangeLog
===================================================================
RCS file: /cvs/gnupg/libgcrypt/src/ChangeLog,v
retrieving revision 1.80
diff -u -p -r1.80 ChangeLog
--- src/ChangeLog	26 Mar 2003 10:58:24 -0000	1.80
+++ src/ChangeLog	30 Mar 2003 18:41:58 -0000
@@ -1,3 +1,9 @@
+2003-03-30  Simon Josefsson  <jas@extundo.com>
+
+	* gcrypt.h (enum gcry_control_cmds): Add GCRY_SET_CTR.
+	(enum gcry_cipher_modes): Add GCRY_CIPHER_MODE_CTR.
+	(gcry_cipher_setctr): New macro to set counter.
+
 2003-03-19  Moritz Schulte  <moritz@g10code.com>
 
 	* cipher.h (PUBKEY_FLAG_NO_BLINDING): New symbol.
Index: src/gcrypt.h
===================================================================
RCS file: /cvs/gnupg/libgcrypt/src/gcrypt.h,v
retrieving revision 1.75
diff -u -p -r1.75 gcrypt.h
--- src/gcrypt.h	24 Mar 2003 19:30:30 -0000	1.75
+++ src/gcrypt.h	30 Mar 2003 18:41:58 -0000
@@ -175,6 +175,7 @@ enum gcry_control_cmds
     GCRYCTL_FINALIZE,
     GCRYCTL_START_DUMP,
     GCRYCTL_STOP_DUMP,
+    GCRYCTL_SET_CTR
   };
 
 /* Codes for use with gcry_cipher_info and gcry_md_info.  */
@@ -538,7 +539,8 @@ enum gcry_cipher_modes 
     GCRY_CIPHER_MODE_CFB    = 2,  /* Cipher feedback. */
     GCRY_CIPHER_MODE_CBC    = 3,  /* Cipher block chaining. */
     GCRY_CIPHER_MODE_STREAM = 4,  /* Used with stream ciphers. */
-    GCRY_CIPHER_MODE_OFB    = 5   /* Outer feedback. */
+    GCRY_CIPHER_MODE_OFB    = 5,  /* Outer feedback. */
+    GCRY_CIPHER_MODE_CTR    = 6   /* Counter. */
   };
 
 /* Flags used with the open function. */ 
@@ -615,6 +617,11 @@ int gcry_cipher_decrypt (GcryCipherHd h,
 /* Enable or disable CTS in future calls to gcry_encrypt(). CBC mode only. */
 #define gcry_cipher_cts(h,on)  gcry_cipher_ctl( (h), GCRYCTL_SET_CBC_CTS, \
 								   NULL, on )
+
+/* Set counter for CTR mode.  (K,L) must denote a buffer of block size
+   length, or (NULL,0) to set the CTR to the all-zero block. */
+#define gcry_cipher_setctr(h,k,l)  gcry_cipher_ctl( (h), GCRYCTL_SET_CTR, \
+						    (char*)(k), (l) )
 
 /* Retrieved the key length used with algorithm A. */
 #define gcry_cipher_get_algo_keylen(a) \
Index: tests/ChangeLog
===================================================================
RCS file: /cvs/gnupg/libgcrypt/tests/ChangeLog,v
retrieving revision 1.18
diff -u -p -r1.18 ChangeLog
--- tests/ChangeLog	26 Mar 2003 10:21:34 -0000	1.18
+++ tests/ChangeLog	30 Mar 2003 18:41:58 -0000
@@ -1,3 +1,9 @@
+2003-03-30  Simon Josefsson  <jas@extundo.com>
+
+	* basic.c (check_one_cipher): New.  Test CTR.
+	(main): Call it.
+	(check_ciphers): Check CTR mode.
+
 2003-03-26  Moritz Schulte  <moritz@g10code.com>
 
 	* Makefile.am (TESTS): Added pubkey.
Index: tests/basic.c
===================================================================
RCS file: /cvs/gnupg/libgcrypt/tests/basic.c,v
retrieving revision 1.13
diff -u -p -r1.13 basic.c
--- tests/basic.c	24 Mar 2003 19:29:28 -0000	1.13
+++ tests/basic.c	30 Mar 2003 18:41:58 -0000
@@ -252,6 +252,149 @@ check_aes128_cbc_cts_cipher ()
 }
 
 static void
+check_ctr_cipher (void)
+{
+  struct tv {
+    int algo;
+    char key[MAX_DATA_LEN];
+    char ctr[MAX_DATA_LEN];
+    struct data {
+      char plaintext[MAX_DATA_LEN];
+      int inlen;
+      char out[MAX_DATA_LEN];
+    }  data[MAX_DATA_LEN];
+  } tv[] = {
+    /* http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf */
+    { GCRY_CIPHER_AES,
+      "\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c",
+      "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
+      {{ "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a",
+	 16,
+	 "\x87\x4d\x61\x91\xb6\x20\xe3\x26\x1b\xef\x68\x64\x99\x0d\xb6\xce" },
+       { "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51",
+	 16,
+	 "\x98\x06\xf6\x6b\x79\x70\xfd\xff\x86\x17\x18\x7b\xb9\xff\xfd\xff" },
+       { "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11\xe5\xfb\xc1\x19\x1a\x0a\x52\xef",
+	 16,
+	 "\x5a\xe4\xdf\x3e\xdb\xd5\xd3\x5e\x5b\x4f\x09\x02\x0d\xb0\x3e\xab" },
+       { "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
+	 16,
+	 "\x1e\x03\x1d\xda\x2f\xbe\x03\xd1\x79\x21\x70\xa0\xf3\x00\x9c\xee" },
+       {}}
+    },
+    { GCRY_CIPHER_AES192,
+      "\x8e\x73\xb0\xf7\xda\x0e\x64\x52\xc8\x10\xf3\x2b"
+      "\x80\x90\x79\xe5\x62\xf8\xea\xd2\x52\x2c\x6b\x7b",
+      "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
+      {{ "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a",
+	 16,
+	 "\x1a\xbc\x93\x24\x17\x52\x1c\xa2\x4f\x2b\x04\x59\xfe\x7e\x6e\x0b" },
+       { "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51",
+	 16,
+	 "\x09\x03\x39\xec\x0a\xa6\xfa\xef\xd5\xcc\xc2\xc6\xf4\xce\x8e\x94" },
+       { "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11\xe5\xfb\xc1\x19\x1a\x0a\x52\xef",
+	 16,
+	 "\x1e\x36\xb2\x6b\xd1\xeb\xc6\x70\xd1\xbd\x1d\x66\x56\x20\xab\xf7" },
+       { "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
+	 16,
+	 "\x4f\x78\xa7\xf6\xd2\x98\x09\x58\x5a\x97\xda\xec\x58\xc6\xb0\x50" },
+       {}}
+    },
+    { GCRY_CIPHER_AES256,
+      "\x60\x3d\xeb\x10\x15\xca\x71\xbe\x2b\x73\xae\xf0\x85\x7d\x77\x81"
+      "\x1f\x35\x2c\x07\x3b\x61\x08\xd7\x2d\x98\x10\xa3\x09\x14\xdf\xf4",
+      "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
+      {{ "\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a",
+	 16,
+	 "\x60\x1e\xc3\x13\x77\x57\x89\xa5\xb7\xa7\xf5\x04\xbb\xf3\xd2\x28" },
+       { "\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51",
+	 16,
+	 "\xf4\x43\xe3\xca\x4d\x62\xb5\x9a\xca\x84\xe9\x90\xca\xca\xf5\xc5" },
+       { "\x30\xc8\x1c\x46\xa3\x5c\xe4\x11\xe5\xfb\xc1\x19\x1a\x0a\x52\xef",
+	 16,
+	 "\x2b\x09\x30\xda\xa2\x3d\xe9\x4c\xe8\x70\x17\xba\x2d\x84\x98\x8d" },
+       { "\xf6\x9f\x24\x45\xdf\x4f\x9b\x17\xad\x2b\x41\x7b\xe6\x6c\x37\x10",
+	 16,
+	 "\xdf\xc9\xc5\x8d\xb6\x7a\xad\xa6\x13\xc2\xdd\x08\x45\x79\x41\xa6" },
+       {}}
+    }
+  };
+  GCRY_CIPHER_HD hde, hdd;
+  char out[MAX_DATA_LEN];
+  int i,j;
+
+  for (i = 0; i < sizeof(tv) / sizeof(tv[0]); i++)
+    {
+      hde = gcry_cipher_open (tv[i].algo, GCRY_CIPHER_MODE_CTR, 0);
+      hdd = gcry_cipher_open (tv[i].algo, GCRY_CIPHER_MODE_CTR, 0);
+      if (!hde || !hdd)
+	{
+	  fail ("aes-ctr, grcy_open_cipher failed: %s\n",
+		gcry_strerror (-1));
+	  return;
+	}
+
+      if (gcry_cipher_setkey (hde, tv[i].key,
+			      gcry_cipher_get_algo_keylen(tv[i].algo)) ||
+	  gcry_cipher_setkey (hdd, tv[i].key,
+			      gcry_cipher_get_algo_keylen(tv[i].algo)))
+	{
+	  fail ("aes-ctr, gcry_cipher_setkey failed: %s\n",
+		gcry_strerror (-1));
+	  gcry_cipher_close (hde);
+	  gcry_cipher_close (hdd);
+	  return;
+	}
+
+      if (gcry_cipher_setctr (hde, tv[i].ctr,
+			      gcry_cipher_get_algo_blklen(tv[i].algo)) ||
+	  gcry_cipher_setctr (hdd, tv[i].ctr,
+			      gcry_cipher_get_algo_blklen(tv[i].algo)))
+	{
+	  fail ("aes-ctr, gcry_cipher_setctr failed: %s\n",
+		gcry_strerror (-1));
+	  gcry_cipher_close (hde);
+	  gcry_cipher_close (hdd);
+	  return;
+	}
+
+      for (j = 0; tv[i].data[j].inlen; j++)
+	{
+	  if (gcry_cipher_encrypt (hde, out, MAX_DATA_LEN,
+				   tv[i].data[j].plaintext,
+				   tv[i].data[j].inlen == -1 ?
+				   strlen(tv[i].data[j].plaintext) :
+				   tv[i].data[j].inlen))
+	    {
+	      fail ("aes-ctr, gcry_cipher_encrypt (%d, %d) failed: %s\n",
+		    i, j, gcry_strerror (-1));
+	      gcry_cipher_close (hde);
+	      gcry_cipher_close (hdd);
+	      return;
+	    }
+
+	  if (memcmp (tv[i].data[j].out, out, tv[i].data[j].inlen))
+	    fail ("aes-ctr, encrypt mismatch entry %d:%d\n", i, j);
+
+	  if (gcry_cipher_decrypt (hdd, out, tv[i].data[j].inlen, NULL, 0))
+	    {
+	      fail ("aes-ctr, gcry_cipher_decrypt (%d, %d) failed: %s\n",
+		    i, j, gcry_strerror (-1));
+	      gcry_cipher_close (hde);
+	      gcry_cipher_close (hdd);
+	      return;
+	    }
+
+	  if (memcmp (tv[i].data[j].plaintext, out, tv[i].data[j].inlen))
+	    fail ("aes-ctr, decrypt mismatch entry %d:%d\n", i, j);
+	}
+
+      gcry_cipher_close (hde);
+      gcry_cipher_close (hdd);
+    }
+}
+
+static void
 check_one_cipher (int algo, int mode, int flags)
 {
     GCRY_CIPHER_HD hd;
@@ -335,6 +478,7 @@ check_ciphers (void)
       check_one_cipher (algos[i], GCRY_CIPHER_MODE_CFB, 0);
       check_one_cipher (algos[i], GCRY_CIPHER_MODE_CBC, 0);
       check_one_cipher (algos[i], GCRY_CIPHER_MODE_CBC, GCRY_CIPHER_CBC_CTS);
+      check_one_cipher (algos[i], GCRY_CIPHER_MODE_CTR, 0);
     }
 
   for (i=0; algos2[i]; i++ ) 
@@ -681,6 +825,7 @@ main (int argc, char **argv)
   check_ciphers ();
   check_aes128_cbc_cts_cipher ();
   check_cbc_mac_cipher ();
+  check_ctr_cipher ();
   check_digests ();
   check_pubkey ();