[PATCH] CBC-MAC support

Simon Josefsson jas@extundo.com
Sat, 22 Mar 2003 00:17:44 +0100


The last typos included here too, as the patches would conflict
otherwise.

PS. By accident one attempt at key derivation support was deleted
after a "cvsco" a few weeks ago, but I'll try again soon.

Index: AUTHORS
===================================================================
RCS file: /cvs/gnupg/libgcrypt/AUTHORS,v
retrieving revision 1.39
diff -u -p -r1.39 AUTHORS
--- AUTHORS	21 Jan 2003 09:06:54 -0000	1.39
+++ AUTHORS	21 Mar 2003 23:09:52 -0000
@@ -50,7 +50,7 @@ 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 flag,
+Assigns past and future changes to FSF (cipher/{md4,crc}.c, CTS/MAC flags,
 self test improvements)
 simon@josefsson.org
 
@@ -72,7 +72,7 @@ files from Cryptlib.  Copyright Peter Gu
 Wedgwood 1996-1999.
 
 
- Copyright 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+ Copyright 1998, 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
 
  This file is free software; as a special exception the author gives
  unlimited permission to copy and/or distribute it, with or without
Index: NEWS
===================================================================
RCS file: /cvs/gnupg/libgcrypt/NEWS,v
retrieving revision 1.30
diff -u -p -r1.30 NEWS
--- NEWS	19 Mar 2003 11:55:35 -0000	1.30
+++ NEWS	21 Mar 2003 23:09:52 -0000
@@ -11,6 +11,9 @@ Noteworthy changes in version 1.1.13 (un
  * The random module won't print the "not enough random bytes
    available" anymore.  A new progress status is issued instead.
 
+ * CBC-MAC for block ciphers is now supported, by using a
+   GCRY_CIPHER_CBC_MAC cipher flag.
+
  * Interface changes relative to the 1.1.12 release:
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 gcry_pk_decrypt         ENHANCED: Allows flag to return complete S-expression.
Index: cipher/ChangeLog
===================================================================
RCS file: /cvs/gnupg/libgcrypt/cipher/ChangeLog,v
retrieving revision 1.152
diff -u -p -r1.152 ChangeLog
--- cipher/ChangeLog	19 Mar 2003 11:57:38 -0000	1.152
+++ cipher/ChangeLog	21 Mar 2003 23:09:53 -0000
@@ -1,3 +1,9 @@
+2003-03-22  Simon Josefsson  <jas@extundo.com>
+
+	* cipher.c (gcry_cipher_open, do_cbc_encrypt)
+	(gcry_cipher_encrypt): Support GCRY_CIPHER_CBC_MAC.
+	(gcry_cipher_ctl): Support GCRYCTL_SET_CBC_MAC.
+
 2003-03-19  Werner Koch  <wk@gnupg.org>
 
 	* primegen.c (gen_prime): New args EXTRA_CHECK and EXTRA_CHECK_ARG
Index: cipher/cipher.c
===================================================================
RCS file: /cvs/gnupg/libgcrypt/cipher/cipher.c,v
retrieving revision 1.51
diff -u -p -r1.51 cipher.c
--- cipher/cipher.c	4 Mar 2003 18:07:42 -0000	1.51
+++ cipher/cipher.c	21 Mar 2003 23:09:53 -0000
@@ -516,7 +516,9 @@ gcry_cipher_open( int algo, int mode, un
     /* check flags */
     if( (flags & ~(GCRY_CIPHER_SECURE|
 		   GCRY_CIPHER_ENABLE_SYNC|
-		   GCRY_CIPHER_CBC_CTS)) ) {
+		   GCRY_CIPHER_CBC_CTS|
+		   GCRY_CIPHER_CBC_MAC)) ||
+	(flags & GCRY_CIPHER_CBC_CTS & GCRY_CIPHER_CBC_MAC)) {
 	set_lasterr( GCRYERR_INV_CIPHER_ALGO );
 	return NULL;
     }
@@ -690,7 +692,8 @@ do_cbc_encrypt( GCRY_CIPHER_HD c, byte *
 	(*c->encrypt)( &c->context.c, outbuf, outbuf );
 	memcpy(c->iv, outbuf, blocksize );
 	inbuf  += c->blocksize;
-	outbuf += c->blocksize;
+	if (!(c->flags & GCRY_CIPHER_CBC_MAC))
+	  outbuf += c->blocksize;
     }
 
     if ((c->flags & GCRY_CIPHER_CBC_CTS) && nbytes > blocksize)
@@ -940,7 +943,7 @@ gcry_cipher_encrypt (GcryCipherHd h, byt
     }
   else
     {
-      if ( outsize < inlen )
+      if ( outsize < ((h->flags & GCRY_CIPHER_CBC_MAC) ? h->blocksize : inlen))
         rc = GCRYERR_TOO_SHORT;
       else if ((h->mode == GCRY_CIPHER_MODE_ECB
                 || (h->mode == GCRY_CIPHER_MODE_CBC
@@ -1074,11 +1077,22 @@ gcry_cipher_ctl( GCRY_CIPHER_HD h, int c
       break;
     case GCRYCTL_SET_CBC_CTS:
       if (buflen)
-	h->flags |= GCRY_CIPHER_CBC_CTS;
+	if (h->flags & GCRY_CIPHER_CBC_MAC)
+	  rc = GCRYERR_INV_FLAG;
+	else
+	  h->flags |= GCRY_CIPHER_CBC_CTS;
       else
 	h->flags &= ~GCRY_CIPHER_CBC_CTS;
       break;
-
+    case GCRYCTL_SET_CBC_MAC:
+      if (buflen)
+	if (h->flags & GCRY_CIPHER_CBC_CTS)
+	  rc = GCRYERR_INV_FLAG;
+	else
+	  h->flags |= GCRY_CIPHER_CBC_MAC;
+      else
+	h->flags &= ~GCRY_CIPHER_CBC_MAC;
+      break;
     case GCRYCTL_DISABLE_ALGO:
       /* this one expects a NULL handle and buffer pointing to an
        * integer with the algo number.
Index: doc/ChangeLog
===================================================================
RCS file: /cvs/gnupg/libgcrypt/doc/ChangeLog,v
retrieving revision 1.10
diff -u -p -r1.10 ChangeLog
--- doc/ChangeLog	4 Mar 2003 18:17:05 -0000	1.10
+++ doc/ChangeLog	21 Mar 2003 23:09:53 -0000
@@ -1,3 +1,7 @@
+2003-03-22  Simon Josefsson  <jas@extundo.com>
+
+	* gcrypt.texi: Add CBC-MAC.
+
 2003-03-04  Moritz Schulte  <moritz@g10code.com>
 
 	* gcrypt.texi (Cipher Functions): Added gcry_cipher_reset.
Index: doc/gcrypt.texi
===================================================================
RCS file: /cvs/gnupg/libgcrypt/doc/gcrypt.texi,v
retrieving revision 1.10
diff -u -p -r1.10 gcrypt.texi
--- doc/gcrypt.texi	19 Mar 2003 11:59:28 -0000	1.10
+++ doc/gcrypt.texi	21 Mar 2003 23:09:53 -0000
@@ -72,7 +72,7 @@ entitled ``GNU Free Documentation Licens
 @top Main Menu
 This is Edition @value{EDITION}, last updated @value{UPDATED}, of
 @cite{The `Libgcrypt' Reference Manual}, for Version
-@value{VERSION} of the @acronym{GPGME} library.
+@value{VERSION} of the @acronym{Libgcrypt} library.
 @end ifnottex
 
 @menu
@@ -396,7 +396,12 @@ This flag enables the CFB sync mode, whi
 Libgcrypt's CFB mode implementation to allow for OpenPGP's CFB variant. 
 See @code{gcry_cipher_sync}.
 @item GCRY_CIPHER_CBC_CTS
-Enable cipher text stealing for the CBS mode.
+Enable cipher text stealing (CTS) for the CBC mode.  Cannot be used
+simultaneous as GCRY_CIPHER_CBC_MAC.
+@item GCRY_CIPHER_CBC_MAC
+Compute CBC-MAC keyed checksums.  This is the same as CBC mode, but
+only output the last block.  Cannot be used simultaneous as
+GCRY_CIPHER_CBC_CTS.
 @end table
 
 @end deftypefun 
@@ -690,6 +695,8 @@ this is the hashed data is highly confid
 @item GCRY_MD_FLAG_HMAC
 Turn the algorithm into a HMAC message authentication algorithm.  Note
 that the function @code{gcry_md_setkey} must be used set the MAC key.
+If you want CBC message authenentication codes based on a cipher, see
+@xref{Cipher Functions}.
 
 @end table
 @c begin table of hash flags
Index: src/ChangeLog
===================================================================
RCS file: /cvs/gnupg/libgcrypt/src/ChangeLog,v
retrieving revision 1.78
diff -u -p -r1.78 ChangeLog
--- src/ChangeLog	19 Mar 2003 11:59:45 -0000	1.78
+++ src/ChangeLog	21 Mar 2003 23:09:53 -0000
@@ -1,3 +1,7 @@
+2003-03-22  Simon Josefsson  <jas@extundo.com>
+
+	* gcrypt.h: Add GCRYCTL_SET_CBC_MAC and GCRY_CIPHER_CBC_MAC.
+
 2003-03-19  Werner Koch  <wk@gnupg.org>
 
 	* g10lib.h: Adjusted primegen.c prototypes.
Index: src/gcrypt.h
===================================================================
RCS file: /cvs/gnupg/libgcrypt/src/gcrypt.h,v
retrieving revision 1.74
diff -u -p -r1.74 gcrypt.h
--- src/gcrypt.h	4 Mar 2003 18:09:33 -0000	1.74
+++ src/gcrypt.h	21 Mar 2003 23:09:53 -0000
@@ -169,6 +169,7 @@ enum gcry_control_cmds
     GCRYCTL_RESET,
     GCRYCTL_CFB_SYNC,
     GCRYCTL_SET_CBC_CTS,
+    GCRYCTL_SET_CBC_MAC,
     GCRYCTL_ENABLE_ALGO,	/* Not implemented.  */
     GCRYCTL_DISABLE_ALGO,
     GCRYCTL_FINALIZE,
@@ -545,7 +546,8 @@ enum gcry_cipher_flags
   {
     GCRY_CIPHER_SECURE	    = 1,  /* Allocate in secure memory. */
     GCRY_CIPHER_ENABLE_SYNC = 2,  /* Enable CFB sync mode. */
-    GCRY_CIPHER_CBC_CTS = 4       /* Enable CBC cipher text stealing (CTS). */
+    GCRY_CIPHER_CBC_CTS	    = 4,  /* Enable CBC cipher text stealing (CTS). */
+    GCRY_CIPHER_CBC_MAC	    = 8   /* Enable CBC message auth. code (MAC). */
   };
 
 
Index: tests/ChangeLog
===================================================================
RCS file: /cvs/gnupg/libgcrypt/tests/ChangeLog,v
retrieving revision 1.16
diff -u -p -r1.16 ChangeLog
--- tests/ChangeLog	19 Mar 2003 11:57:55 -0000	1.16
+++ tests/ChangeLog	21 Mar 2003 23:09:53 -0000
@@ -1,3 +1,8 @@
+2003-03-22  Simon Josefsson  <jas@extundo.com>
+
+	* basic.c (check_cbc_mac_cipher): New.
+	(main): Use it.
+
 2003-03-19  Werner Koch  <wk@gnupg.org>
 
 	* keygen.c (check_rsa_keys): Don't expect an exponent when asking
Index: tests/basic.c
===================================================================
RCS file: /cvs/gnupg/libgcrypt/tests/basic.c,v
retrieving revision 1.12
diff -u -p -r1.12 basic.c
--- tests/basic.c	19 Mar 2003 11:57:55 -0000	1.12
+++ tests/basic.c	21 Mar 2003 23:09:53 -0000
@@ -1,5 +1,5 @@
 /* basic.c  -  basic regression tests
- *	Copyright (C) 2001, 2002 Free Software Foundation, Inc.
+ *	Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
  *
  * This file is part of Libgcrypt.
  *
@@ -88,6 +88,86 @@ die ( const char *format, ... )
 #define MAX_DATA_LEN 100
 
 static void
+check_cbc_mac_cipher (void)
+{
+  struct tv {
+    int algo;
+    char key[MAX_DATA_LEN];
+    char plaintext[MAX_DATA_LEN];
+    size_t plaintextlen;
+    char mac[MAX_DATA_LEN];
+  } tv[] = {
+    { GCRY_CIPHER_AES,
+      "chicken teriyaki",
+      "This is a sample plaintext for CBC MAC of sixtyfour bytes.......", 0,
+      "\x23\x8f\x6d\xc7\x53\x6a\x62\x97\x11\xc4\xa5\x16\x43\xea\xb0\xb6" },
+    { GCRY_CIPHER_3DES,
+      "abcdefghABCDEFGH01234567",
+      "This is a sample plaintext for CBC MAC of sixtyfour bytes.......", 0,
+      "\x5c\x11\xf0\x01\x47\xbd\x3d\x3a" },
+    { GCRY_CIPHER_DES,
+      "abcdefgh",
+      "This is a sample plaintext for CBC MAC of sixtyfour bytes.......", 0,
+      "\xfa\x4b\xdf\x9d\xfa\xab\x01\x70" }
+  };
+  GCRY_CIPHER_HD hd;
+  char out[MAX_DATA_LEN];
+  int i;
+
+  for (i = 0; i < sizeof(tv) / sizeof(tv[0]); i++)
+    {
+      hd = gcry_cipher_open (tv[i].algo,
+			     GCRY_CIPHER_MODE_CBC,
+			     GCRY_CIPHER_CBC_MAC);
+      if (!hd) {
+	fail ("cbc-mac algo %d, grcy_open_cipher failed: %s\n",
+	      tv[i].algo, gcry_strerror (-1) );
+	return;
+      }
+
+      if (gcry_cipher_setkey (hd, tv[i].key,
+			      gcry_cipher_get_algo_keylen (tv[i].algo))) {
+	fail ("cbc-mac algo %d, gcry_cipher_setkey failed: %s\n",
+	      tv[i].algo, gcry_strerror (-1) );
+	gcry_cipher_close (hd);
+	return;
+      }
+
+      if (gcry_cipher_setiv (hd, NULL, 0)) {
+	fail ("cbc-mac algo %d, gcry_cipher_setiv failed: %s\n",
+	      tv[i].algo, gcry_strerror (-1) );
+	gcry_cipher_close (hd);
+	return;
+      }
+
+      if ( gcry_cipher_encrypt (hd,
+				out, gcry_cipher_get_algo_blklen(tv[i].algo),
+				tv[i].plaintext,
+				tv[i].plaintextlen ?
+				tv[i].plaintextlen :
+				strlen(tv[i].plaintext))) {
+	fail ("cbc-mac algo %d, gcry_cipher_encrypt failed: %s\n",
+	      tv[i].algo, gcry_strerror (-1) );
+	gcry_cipher_close (hd);
+	return;
+      }
+
+#if 0
+      { int j;
+	for (j=0; j < gcry_cipher_get_algo_blklen(tv[i].algo); j++)
+	  printf("\\x%02x", out[j] & 0xFF);
+	printf("\n");
+      }
+#endif
+
+      if ( memcmp (tv[i].mac, out, gcry_cipher_get_algo_blklen(tv[i].algo)) )
+	fail ("cbc-mac algo %d, encrypt mismatch entry %d\n", tv[i].algo, i);
+
+      gcry_cipher_close (hd);
+    }
+}
+
+static void
 check_aes128_cbc_cts_cipher ()
 {
   char key[128/8] = "chicken teriyaki";
@@ -600,6 +680,7 @@ main (int argc, char **argv)
     gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u , 0);
   check_ciphers ();
   check_aes128_cbc_cts_cipher ();
+  check_cbc_mac_cipher ();
   check_digests ();
   check_pubkey ();