[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 ();