Implemented the AES-Wrap algorithm
authorWerner Koch <wk@gnupg.org>
Wed, 9 Dec 2009 11:21:17 +0000 (11:21 +0000)
committerWerner Koch <wk@gnupg.org>
Wed, 9 Dec 2009 11:21:17 +0000 (11:21 +0000)
NEWS
cipher/ChangeLog
cipher/cipher.c
doc/gcrypt.texi
src/ChangeLog
src/gcrypt.h.in
tests/ChangeLog
tests/Makefile.am
tests/aeswrap.c [new file with mode: 0644]
tests/benchmark.c

diff --git a/NEWS b/NEWS
index a981224..ba2c3ab 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,12 @@
 Noteworthy changes in version 1.5.x (unreleased)
 ------------------------------------------------
 
+ * New cipher algorithm mode for AES-WRAP.
+
+ * Interface changes relative to the 1.4.2 release:
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ GCRY_CIPHER_MODE_AESWRAP   NEW.
+
 
 Noteworthy changes in version 1.4.4 (2009-01-22)
 ------------------------------------------------
index f07a60f..2ddd1d9 100644 (file)
@@ -1,3 +1,16 @@
+2009-12-09  Werner Koch  <wk@g10code.com>
+
+       * cipher.c (gcry_cipher_open): Allow for GCRY_CIPHER_MODE_AESWRAP.
+       (cipher_encrypt, cipher_decrypt): Ditto.
+       (do_aeswrap_encrypt, do_aeswrap_decrypt): New.
+       (struct gcry_cipher_handle): Add field marks.
+       (cipher_setkey, cipher_setiv): Update marks flags.
+       (cipher_reset): Reset marks.
+       (cipher_encrypt, cipher_decrypt): Add new arg OUTBUFLEN.
+       (gcry_cipher_encrypt, gcry_cipher_decrypt): Pass outbuflen to
+       cipher_encrypt.  Replace GPG_ERR_TOO_SHORT by
+       GPG_ERR_BUFFER_TOO_SHORT.
+
 2009-08-21  Werner Koch  <wk@g10code.com>
 
        * dsa.c (dsa_generate_ext): Release retfactors array before
index 6d97c19..355ba68 100644 (file)
@@ -192,6 +192,11 @@ struct gcry_cipher_handle
   int mode;
   unsigned int flags;
 
+  struct {
+    unsigned int key:1; /* Set to 1 if a key has been set.  */
+    unsigned int iv:1;  /* Set to 1 if ae IV has been set.  */
+  } marks;
+
   /* The initialization vector.  To help code optimization we make
      sure that it is aligned on an unsigned long and u32 boundary.  */
   union {
@@ -724,6 +729,7 @@ gcry_cipher_open (gcry_cipher_hd_t *handle,
       case GCRY_CIPHER_MODE_CFB:
       case GCRY_CIPHER_MODE_OFB:
       case GCRY_CIPHER_MODE_CTR:
+      case GCRY_CIPHER_MODE_AESWRAP:
        if ((cipher->encrypt == dummy_encrypt_block)
            || (cipher->decrypt == dummy_decrypt_block))
          err = GPG_ERR_INV_CIPHER_MODE;
@@ -882,7 +888,10 @@ cipher_setkey (gcry_cipher_hd_t c, byte *key, unsigned int keylen)
       memcpy ((void *) ((char *) &c->context.c + c->cipher->contextsize),
               (void *) &c->context.c,
               c->cipher->contextsize);
+      c->marks.key = 1;
     }
+  else
+    c->marks.key = 0;
 
   return gcry_error (ret);
 }
@@ -905,7 +914,10 @@ cipher_setiv( gcry_cipher_hd_t c, const byte *iv, unsigned ivlen )
       if (ivlen > c->cipher->blocksize)
         ivlen = c->cipher->blocksize;
       memcpy (c->u_iv.iv, iv, ivlen);
+      c->marks.iv = 1;
     }
+  else
+      c->marks.iv = 0;
   c->unused = 0;
 }
 
@@ -918,6 +930,7 @@ cipher_reset (gcry_cipher_hd_t c)
   memcpy (&c->context.c,
          (char *) &c->context.c + c->cipher->contextsize,
          c->cipher->contextsize);
+  memset (&c->marks, 0, sizeof c->marks);
   memset (c->u_iv.iv, 0, c->cipher->blocksize);
   memset (c->lastiv, 0, c->cipher->blocksize);
   memset (c->ctr, 0, c->cipher->blocksize);
@@ -1391,63 +1404,241 @@ do_ctr_decrypt( gcry_cipher_hd_t c, byte *outbuf, const byte *inbuf,
 }
 
 
+/* Perform the AES-Wrap algorithm as specified by RFC3394.  We
+   implement this as a mode usable with any cipher algorithm of
+   blocksize 128.  */
+static gcry_err_code_t
+do_aeswrap_encrypt (gcry_cipher_hd_t c, byte *outbuf, unsigned int outbuflen,
+                    const byte *inbuf, unsigned int inbuflen )
+{
+  int j, x;
+  unsigned int n, i;
+  unsigned char *r, *a, *b;
+  unsigned char t[8];
+
+#if MAX_BLOCKSIZE < 8
+#error Invalid block size
+#endif
+  /* We require a cipher with a 128 bit block length.  */
+  if (c->cipher->blocksize != 16)
+    return GPG_ERR_INV_LENGTH;  
+  
+  /* The output buffer must be able to hold the input data plus one
+     additional block.  */
+  if (outbuflen < inbuflen + 8)
+    return GPG_ERR_BUFFER_TOO_SHORT;
+  /* Input data must be multiple of 64 bits.  */
+  if (inbuflen % 8)
+    return GPG_ERR_INV_ARG;   
+
+  n = inbuflen / 8;
+
+  /* We need at least two 64 bit blocks.  */
+  if (n < 2)
+    return GPG_ERR_INV_ARG;
+
+  r = outbuf; 
+  a = outbuf;  /* We store A directly in OUTBUF.  */
+  b = c->ctr;  /* B is also used to concatenate stuff.  */
+
+  /* If an IV has been set we use that IV as the Alternative Initial
+     Value; if it has not been set we use the standard value.  */
+  if (c->marks.iv)
+    memcpy (a, c->u_iv.iv, 8);
+  else
+    memset (a, 0xa6, 8);
+
+  /* Copy the inbuf to the outbuf. */
+  memmove (r+8, inbuf, inbuflen);
+
+  memset (t, 0, sizeof t); /* t := 0.  */
+
+  for (j = 0; j <= 5; j++)
+    {
+      for (i = 1; i <= n; i++)
+        {
+          /* B := AES_k( A | R[i] ) */
+          memcpy (b, a, 8);
+          memcpy (b+8, r+i*8, 8);
+          c->cipher->encrypt (&c->context.c, b, b);
+          /* t := t + 1  */
+         for (x = 7; x >= 0; x--)
+           {
+             t[x]++;
+             if (t[x])
+               break;
+           }
+          /* A := MSB_64(B) ^ t */
+          for (x=0; x < 8; x++)
+            a[x] = b[x] ^ t[x];
+          /* R[i] := LSB_64(B) */
+          memcpy (r+i*8, b+8, 8);
+        }
+   }
+  
+  return 0;
+}
+
+/* Perform the AES-Unwrap algorithm as specified by RFC3394.  We
+   implement this as a mode usable with any cipher algorithm of
+   blocksize 128.  */
+static gcry_err_code_t
+do_aeswrap_decrypt (gcry_cipher_hd_t c, byte *outbuf, unsigned int outbuflen,
+                    const byte *inbuf, unsigned int inbuflen)
+{
+  int j, x;
+  unsigned int n, i;
+  unsigned char *r, *a, *b;
+  unsigned char t[8];
+
+#if MAX_BLOCKSIZE < 8
+#error Invalid block size
+#endif
+  /* We require a cipher with a 128 bit block length.  */
+  if (c->cipher->blocksize != 16)
+    return GPG_ERR_INV_LENGTH;  
+  
+  /* The output buffer must be able to hold the input data minus one
+     additional block.  Fixme: The caller has more restrictive checks
+     - we may want to fix them for this mode.  */
+  if (outbuflen + 8  < inbuflen)
+    return GPG_ERR_BUFFER_TOO_SHORT;
+  /* Input data must be multiple of 64 bits.  */
+  if (inbuflen % 8)
+    return GPG_ERR_INV_ARG;   
+
+  n = inbuflen / 8;
+
+  /* We need at least three 64 bit blocks.  */
+  if (n < 3)
+    return GPG_ERR_INV_ARG;
+
+  r = outbuf; 
+  a = c->lastiv;  /* We use c->LASTIV as buffer for A.  */
+  b = c->ctr;     /* B is also used to concatenate stuff.  */
+
+  /* Copy the inbuf to the outbuf and save A. */
+  memcpy (a, inbuf, 8);
+  memmove (r, inbuf+8, inbuflen-8);
+  n--; /* Reduce to actual number of data blocks.  */
+
+  /* t := 6 * n  */
+  i = n * 6;  /* The range is valid because: n = inbuflen / 8 - 1.  */
+  for (x=0; x < 8 && x < sizeof (i); x++)
+    t[7-x] = i >> (8*x);
+  for (; x < 8; x++)
+    t[7-x] = 0;
+
+  for (j = 5; j >= 0; j--)
+    {
+      for (i = n; i >= 1; i--)
+        {
+          /* B := AES_k^1( (A ^ t)| R[i] ) */
+          for (x = 0; x < 8; x++)
+            b[x] = a[x] ^ t[x];
+          memcpy (b+8, r+(i-1)*8, 8);
+          c->cipher->decrypt (&c->context.c, b, b);
+          /* t := t - 1  */
+         for (x = 7; x >= 0; x--)
+           {
+             t[x]--;
+             if (t[x] != 0xff)
+               break;
+           }
+          /* A := MSB_64(B) */
+          memcpy (a, b, 8);
+          /* R[i] := LSB_64(B) */
+          memcpy (r+(i-1)*8, b+8, 8);
+        }
+   }
+
+  /* If an IV has been set we compare against this Alternative Initial
+     Value; if it has not been set we compare against the standard IV.  */
+  if (c->marks.iv)
+    j = memcmp (a, c->u_iv.iv, 8);
+  else
+    {
+      for (j=0, x=0; x < 8; x++)
+        if (a[x] != 0xa6)
+          {
+            j=1;
+            break;
+          }
+    }
+  return j? GPG_ERR_CHECKSUM : 0;
+}
+
+
 /****************
  * Encrypt INBUF to OUTBUF with the mode selected at open.
  * inbuf and outbuf may overlap or be the same.
- * Depending on the mode some constraints apply to NBYTES.
+ * Depending on the mode some constraints apply to INBUFLEN.
  */
 static gcry_err_code_t
-cipher_encrypt (gcry_cipher_hd_t c, byte *outbuf,
-               const byte *inbuf, unsigned int nbytes)
+cipher_encrypt (gcry_cipher_hd_t c, byte *outbuf, unsigned int outbuflen,
+               const byte *inbuf, unsigned int inbuflen)
 {
-    gcry_err_code_t rc = GPG_ERR_NO_ERROR;
+  gcry_err_code_t rc = 0;
 
-    switch( c->mode ) {
-      case GCRY_CIPHER_MODE_ECB:
-       if (!(nbytes%c->cipher->blocksize))
-            do_ecb_encrypt(c, outbuf, inbuf, nbytes/c->cipher->blocksize );
-        else 
-            rc = GPG_ERR_INV_ARG;
-       break;
-      case GCRY_CIPHER_MODE_CBC:
-       if (!(nbytes%c->cipher->blocksize)
-            || (nbytes > c->cipher->blocksize
-                && (c->flags & GCRY_CIPHER_CBC_CTS)))
-            do_cbc_encrypt(c, outbuf, inbuf, nbytes );
-        else 
-            rc = GPG_ERR_INV_ARG;
-       break;
-      case GCRY_CIPHER_MODE_CFB:
-       do_cfb_encrypt(c, outbuf, inbuf, nbytes );
-       break;
-      case GCRY_CIPHER_MODE_OFB:
-       do_ofb_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->cipher->stencrypt ( &c->context.c,
-                               outbuf, (byte*)/*arggg*/inbuf, nbytes );
-        break;
-      case GCRY_CIPHER_MODE_NONE:
-               if (fips_mode () || !_gcry_get_debug_flag (0))
-          {
-            fips_signal_error ("cipher mode NONE used");
-            rc = GPG_ERR_INV_CIPHER_MODE;
-          }
-        else
-          {
-            if ( inbuf != outbuf )
-              memmove (outbuf, inbuf, nbytes);
-          }
-       break;
-      default:
-        log_fatal("cipher_encrypt: invalid mode %d\n", c->mode );
-        rc = GPG_ERR_INV_CIPHER_MODE;
-        break;
+  switch (c->mode)
+    {
+    case GCRY_CIPHER_MODE_ECB:
+      if (!(inbuflen % c->cipher->blocksize))
+        do_ecb_encrypt(c, outbuf, inbuf, inbuflen/c->cipher->blocksize );
+      else 
+        rc = GPG_ERR_INV_ARG;
+      break;
+
+    case GCRY_CIPHER_MODE_CBC:
+      if (!(inbuflen % c->cipher->blocksize)
+          || (inbuflen > c->cipher->blocksize
+              && (c->flags & GCRY_CIPHER_CBC_CTS)))
+        do_cbc_encrypt(c, outbuf, inbuf, inbuflen );
+      else 
+        rc = GPG_ERR_INV_ARG;
+      break;
+      
+    case GCRY_CIPHER_MODE_CFB:
+      do_cfb_encrypt(c, outbuf, inbuf, inbuflen );
+      break;
+
+    case GCRY_CIPHER_MODE_OFB:
+      do_ofb_encrypt(c, outbuf, inbuf, inbuflen );
+      break;
+
+    case GCRY_CIPHER_MODE_CTR:
+      do_ctr_encrypt(c, outbuf, inbuf, inbuflen );
+      break;
+
+    case GCRY_CIPHER_MODE_AESWRAP:
+      rc = do_aeswrap_encrypt (c, outbuf, outbuflen, inbuf, inbuflen );
+      break;
+
+    case GCRY_CIPHER_MODE_STREAM:
+      c->cipher->stencrypt (&c->context.c,
+                            outbuf, (byte*)/*arggg*/inbuf, inbuflen );
+      break;
+
+    case GCRY_CIPHER_MODE_NONE:
+      if (fips_mode () || !_gcry_get_debug_flag (0))
+        {
+          fips_signal_error ("cipher mode NONE used");
+          rc = GPG_ERR_INV_CIPHER_MODE;
+        }
+      else
+        {
+          if ( inbuf != outbuf )
+            memmove (outbuf, inbuf, inbuflen);
+        }
+      break;
+      
+    default:
+      log_fatal ("cipher_encrypt: invalid mode %d\n", c->mode );
+      rc = GPG_ERR_INV_CIPHER_MODE;
+      break;
     }
-    return rc;
+
+  return rc;
 }
 
 
@@ -1463,14 +1654,12 @@ gcry_cipher_encrypt (gcry_cipher_hd_t h, void *out, size_t outsize,
 
   if (!in)
     {
-      /* Caller requested in-place encryption. */
-      /* Actually cipher_encrypt() does not need to know about it, but
-       * we may change it in the future to get better performance.  */
-      err = cipher_encrypt (h, out, out, outsize);
+      /* Caller requested in-place encryption.  */
+      err = cipher_encrypt (h, out, outsize, out, outsize);
     }
   else if (outsize < ((h->flags & GCRY_CIPHER_CBC_MAC) ?
                       h->cipher->blocksize : inlen))
-    err = GPG_ERR_TOO_SHORT;
+    err = GPG_ERR_BUFFER_TOO_SHORT;
   else if ((h->mode == GCRY_CIPHER_MODE_ECB
            || (h->mode == GCRY_CIPHER_MODE_CBC
                && (! ((h->flags & GCRY_CIPHER_CBC_CTS)
@@ -1478,12 +1667,12 @@ gcry_cipher_encrypt (gcry_cipher_hd_t h, void *out, size_t outsize,
           && (inlen % h->cipher->blocksize))
     err = GPG_ERR_INV_ARG;
   else
-    err = cipher_encrypt (h, out, in, inlen);
+    err = cipher_encrypt (h, out, outsize, in, inlen);
 
+  /* Failsafe: Make sure that the plaintext will never make it into
+     OUT if the encryption returned an error.  */
   if (err && out)
-    memset (out, 0x42, outsize); /* Failsafe: Make sure that the
-                                    plaintext will never make it into
-                                    OUT. */
+    memset (out, 0x42, outsize); 
 
   return gcry_error (err);
 }
@@ -1496,57 +1685,70 @@ gcry_cipher_encrypt (gcry_cipher_hd_t h, void *out, size_t outsize,
  * Depending on the mode some some contraints apply to NBYTES.
  */
 static gcry_err_code_t
-cipher_decrypt (gcry_cipher_hd_t c, byte *outbuf, const byte *inbuf,
-               unsigned int nbytes)
+cipher_decrypt (gcry_cipher_hd_t c, byte *outbuf, unsigned int outbuflen,
+                const byte *inbuf, unsigned int inbuflen)
 {
-    gcry_err_code_t rc = GPG_ERR_NO_ERROR;
+  gcry_err_code_t rc = 0;
 
-    switch( c->mode ) {
-      case GCRY_CIPHER_MODE_ECB:
-       if (!(nbytes%c->cipher->blocksize))
-            do_ecb_decrypt(c, outbuf, inbuf, nbytes/c->cipher->blocksize );
-        else 
-            rc = GPG_ERR_INV_ARG;
-       break;
-      case GCRY_CIPHER_MODE_CBC:
-       if (!(nbytes%c->cipher->blocksize)
-            || (nbytes > c->cipher->blocksize
-                && (c->flags & GCRY_CIPHER_CBC_CTS)))
-            do_cbc_decrypt(c, outbuf, inbuf, nbytes );
-        else 
-            rc = GPG_ERR_INV_ARG;
-       break;
-      case GCRY_CIPHER_MODE_CFB:
-       do_cfb_decrypt(c, outbuf, inbuf, nbytes );
-       break;
-      case GCRY_CIPHER_MODE_OFB:
-       do_ofb_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->cipher->stdecrypt ( &c->context.c,
-                               outbuf, (byte*)/*arggg*/inbuf, nbytes );
-        break;
-      case GCRY_CIPHER_MODE_NONE:
-               if (fips_mode () || !_gcry_get_debug_flag (0))
-          {
-            fips_signal_error ("cipher mode NONE used");
-            rc = GPG_ERR_INV_CIPHER_MODE;
-          }
-        else
-          {
-            if (inbuf != outbuf)
-              memmove (outbuf, inbuf, nbytes);
-          }
-       break;
-      default:
-        log_fatal ("cipher_decrypt: invalid mode %d\n", c->mode );
-        rc = GPG_ERR_INV_CIPHER_MODE;
-        break;
+  switch (c->mode)
+    {
+    case GCRY_CIPHER_MODE_ECB:
+      if (!(inbuflen % c->cipher->blocksize))
+        do_ecb_decrypt (c, outbuf, inbuf, inbuflen/c->cipher->blocksize );
+      else 
+        rc = GPG_ERR_INV_ARG;
+      break;
+
+    case GCRY_CIPHER_MODE_CBC:
+      if (!(inbuflen % c->cipher->blocksize)
+          || (inbuflen > c->cipher->blocksize
+              && (c->flags & GCRY_CIPHER_CBC_CTS)))
+        do_cbc_decrypt (c, outbuf, inbuf, inbuflen );
+      else 
+        rc = GPG_ERR_INV_ARG;
+      break;
+
+    case GCRY_CIPHER_MODE_CFB:
+      do_cfb_decrypt (c, outbuf, inbuf, inbuflen );
+
+      break;
+    case GCRY_CIPHER_MODE_OFB:
+      do_ofb_decrypt (c, outbuf, inbuf, inbuflen );
+      break;
+
+    case GCRY_CIPHER_MODE_CTR:
+      do_ctr_decrypt (c, outbuf, inbuf, inbuflen );
+      break;
+
+    case GCRY_CIPHER_MODE_AESWRAP:
+      rc = do_aeswrap_decrypt (c, outbuf, outbuflen, inbuf, inbuflen );
+      break;
+
+    case GCRY_CIPHER_MODE_STREAM:
+      c->cipher->stdecrypt (&c->context.c,
+                            outbuf, (byte*)/*arggg*/inbuf, inbuflen );
+      break;
+
+    case GCRY_CIPHER_MODE_NONE:
+      if (fips_mode () || !_gcry_get_debug_flag (0))
+        {
+          fips_signal_error ("cipher mode NONE used");
+          rc = GPG_ERR_INV_CIPHER_MODE;
+        }
+      else
+        {
+          if (inbuf != outbuf)
+            memmove (outbuf, inbuf, inbuflen);
+        }
+      break;
+      
+    default:
+      log_fatal ("cipher_decrypt: invalid mode %d\n", c->mode );
+      rc = GPG_ERR_INV_CIPHER_MODE;
+      break;
     }
-    return rc;
+
+  return rc;
 }
 
 
@@ -1559,12 +1761,15 @@ gcry_cipher_decrypt (gcry_cipher_hd_t h, void *out, size_t outsize,
   if (!in)
     {
       /* Caller requested in-place encryption. */
-      /* Actually cipher_encrypt() does not need to know about it, but
-       * we may change it in the future to get better performance.  */
-      err = cipher_decrypt (h, out, out, outsize);
+      err = cipher_decrypt (h, out, outsize, out, outsize);
+    }
+  else if (outsize < inlen && h->mode != GCRY_CIPHER_MODE_AESWRAP)
+    {
+      /* Note that do_aeswrap_decrypt does its own length checking.
+         Fixme: we should move all buffer length checkings to teh
+         actual decryption functions.  */
+      err = GPG_ERR_BUFFER_TOO_SHORT;
     }
-  else if (outsize < inlen)
-    err = GPG_ERR_TOO_SHORT;
   else if (((h->mode == GCRY_CIPHER_MODE_ECB)
            || ((h->mode == GCRY_CIPHER_MODE_CBC)
                && (! ((h->flags & GCRY_CIPHER_CBC_CTS)
@@ -1572,7 +1777,7 @@ gcry_cipher_decrypt (gcry_cipher_hd_t h, void *out, size_t outsize,
           && (inlen % h->cipher->blocksize) != 0)
     err = GPG_ERR_INV_ARG;
   else
-    err = cipher_decrypt (h, out, in, inlen);
+    err = cipher_decrypt (h, out, outsize, in, inlen);
 
   return gcry_error (err);
 }
index 6cb4bdd..5e73624 100644 (file)
@@ -1644,6 +1644,20 @@ Output Feedback mode.
 @cindex CTR, Counter mode
 Counter mode.
 
+@item  GCRY_CIPHER_MODE_AESWRAP
+@cindex AES-Wrap mode
+This mode is used to implement the AES-Wrap algorithm according to
+RFC-3394.  It may be used with any 128 bit block length algorithm,
+however the specs require one of the 3 AES algorithms.  These special
+conditions apply: If @code{gcry_cipher_setiv} has not been used the
+standard IV is used; if it has been used the lower 64 bit of the IV
+are used as the Alternative Initial Value.  On encryption the provided
+output buffer must be 64 bit (8 byte) larger than the input buffer;
+in-place encryption is still allowed.  On decryption the output buffer
+may be specified 64 bit (8 byte) shorter than then input buffer.  As
+per specs the input length must be at least 128 bits and the length
+must be a multiple of 64 bits.
+
 @end table
 
 @node Working with cipher handles
index c883d05..bfd616d 100644 (file)
@@ -1,3 +1,7 @@
+2009-12-08  Werner Koch  <wk@g10code.com>
+
+       * gcrypt.h.in (GCRY_CIPHER_MODE_AESWRAP): New.
+
 2009-12-08  Marcus Brinkmann  <marcus@g10code.de>
 
        * Makefile.am (LTRCCOMPILE): Refactor with ...
index 37390b2..33dc145 100644 (file)
@@ -849,7 +849,8 @@ enum gcry_cipher_modes
     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_CTR    = 6   /* Counter. */
+    GCRY_CIPHER_MODE_CTR    = 6,  /* Counter. */
+    GCRY_CIPHER_MODE_AESWRAP= 7   /* AES-WRAP algorithm.  */
   };
 
 /* Flags used with the open function. */ 
index c3376dd..c8332a6 100644 (file)
@@ -1,3 +1,7 @@
+2009-12-09  Werner Koch  <wk@g10code.com>
+
+       * aeswrap.c: New.
+
 2009-07-09  Werner Koch  <wk@g10code.com>
 
        * benchmark.c (progress_cb): New.
index 1288aad..b2c1239 100644 (file)
@@ -19,7 +19,7 @@
 ## Process this file with automake to produce Makefile.in
 
 TESTS = version t-mpi-bit prime register ac ac-schemes ac-data basic \
-        mpitests tsexp keygen pubkey hmac keygrip fips186-dsa
+        mpitests tsexp keygen pubkey hmac keygrip fips186-dsa aeswrap
 
 
 # random.c uses fork() thus a test for W32 does not make any sense.
diff --git a/tests/aeswrap.c b/tests/aeswrap.c
new file mode 100644 (file)
index 0000000..a8f5a5b
--- /dev/null
@@ -0,0 +1,259 @@
+/* aeswrap.c -  AESWRAP mode regression tests
+ *     Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "../src/gcrypt.h"
+
+static int verbose;
+static int error_count;
+
+static void
+fail (const char *format, ...)
+{
+  va_list arg_ptr;
+
+  va_start (arg_ptr, format);
+  vfprintf (stderr, format, arg_ptr);
+  va_end (arg_ptr);
+  error_count++;
+}
+
+static void
+die (const char *format, ...)
+{
+  va_list arg_ptr;
+
+  va_start (arg_ptr, format);
+  vfprintf (stderr, format, arg_ptr);
+  va_end (arg_ptr);
+  exit (1);
+}
+
+
+
+static void
+check (int algo,
+       const void *kek, size_t keklen,
+       const void *data, size_t datalen,
+       const void *expected, size_t expectedlen)
+{
+  gcry_error_t err;
+  gcry_cipher_hd_t hd;
+  unsigned char outbuf[32+8];
+  size_t outbuflen;
+
+  err = gcry_cipher_open (&hd, algo, GCRY_CIPHER_MODE_AESWRAP, 0);
+  if (err)
+    {
+      fail ("gcrypt_cipher_open failed: %s\n", gpg_strerror (err));
+      return;
+    }
+
+  err = gcry_cipher_setkey (hd, kek, keklen);
+  if (err)
+    {
+      fail ("grcy_cipher_setkey failed: %s\n", gpg_strerror (err));
+      return;
+    }
+
+  outbuflen = datalen + 8;
+  if (outbuflen > sizeof outbuf)
+    err = gpg_error (GPG_ERR_INTERNAL);
+  else
+    err = gcry_cipher_encrypt (hd, outbuf, outbuflen, data, datalen);
+  if (err)
+    {
+      fail ("grcy_cipher_encrypt failed: %s\n", gpg_strerror (err));
+      return;
+    }
+
+  if (outbuflen != expectedlen || memcmp (outbuf, expected, expectedlen))
+    {
+      const unsigned char *s;
+      int i;
+
+      fail ("mismatch at encryption!\n");
+      fprintf (stderr, "computed: ");
+      for (i = 0; i < outbuflen; i++)
+       fprintf (stderr, "%02x ", outbuf[i]);
+      fprintf (stderr, "\nexpected: ");
+      for (s = expected, i = 0; i < expectedlen; s++, i++)
+        fprintf (stderr, "%02x ", *s);
+      putc ('\n', stderr);
+    }
+
+
+  outbuflen = expectedlen - 8;
+  if (outbuflen > sizeof outbuf)
+    err = gpg_error (GPG_ERR_INTERNAL);
+  else
+    err = gcry_cipher_decrypt (hd, outbuf, outbuflen, expected, expectedlen);
+  if (err)
+    {
+      fail ("grcy_cipher_decrypt failed: %s\n", gpg_strerror (err));
+      return;
+    }
+
+  if (outbuflen != datalen || memcmp (outbuf, data, datalen))
+    {
+      const unsigned char *s;
+      int i;
+
+      fail ("mismatch at decryption!\n");
+      fprintf (stderr, "computed: ");
+      for (i = 0; i < outbuflen; i++)
+       fprintf (stderr, "%02x ", outbuf[i]);
+      fprintf (stderr, "\nexpected: ");
+      for (s = data, i = 0; i < datalen; s++, i++)
+        fprintf (stderr, "%02x ", *s);
+      putc ('\n', stderr);
+    }
+
+  /* Now the last step again with a key reset. */
+  gcry_cipher_reset (hd);
+
+  outbuflen = expectedlen - 8;
+  if (outbuflen > sizeof outbuf)
+    err = gpg_error (GPG_ERR_INTERNAL);
+  else
+    err = gcry_cipher_decrypt (hd, outbuf, outbuflen, expected, expectedlen);
+  if (err)
+    {
+      fail ("grcy_cipher_decrypt(2) failed: %s\n", gpg_strerror (err));
+      return;
+    }
+
+  if (outbuflen != datalen || memcmp (outbuf, data, datalen))
+    fail ("mismatch at decryption(2)!\n");
+
+  /* And once ore without a key reset. */
+  outbuflen = expectedlen - 8;
+  if (outbuflen > sizeof outbuf)
+    err = gpg_error (GPG_ERR_INTERNAL);
+  else
+    err = gcry_cipher_decrypt (hd, outbuf, outbuflen, expected, expectedlen);
+  if (err)
+    {
+      fail ("grcy_cipher_decrypt(3) failed: %s\n", gpg_strerror (err));
+      return;
+    }
+
+  if (outbuflen != datalen || memcmp (outbuf, data, datalen))
+    fail ("mismatch at decryption(3)!\n");
+
+  gcry_cipher_close (hd);
+}
+
+
+static void
+check_all (void)
+{
+  if (verbose)
+    fprintf (stderr, "4.1 Wrap 128 bits of Key Data with a 128-bit KEK\n");
+  check 
+    (GCRY_CIPHER_AES128,
+     "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", 16,
+     "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF", 16,
+     "\x1F\xA6\x8B\x0A\x81\x12\xB4\x47\xAE\xF3\x4B\xD8\xFB\x5A\x7B\x82"
+     "\x9D\x3E\x86\x23\x71\xD2\xCF\xE5", 24);
+
+  if (verbose)
+    fprintf (stderr, "4.2 Wrap 128 bits of Key Data with a 192-bit KEK\n");
+  check 
+    (GCRY_CIPHER_AES192,
+     "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
+     "\x10\x11\x12\x13\x14\x15\x16\x17", 24,
+     "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF", 16,
+     "\x96\x77\x8B\x25\xAE\x6C\xA4\x35\xF9\x2B\x5B\x97\xC0\x50\xAE\xD2"
+     "\x46\x8A\xB8\xA1\x7A\xD8\x4E\x5D", 24);
+
+  if (verbose)
+    fprintf (stderr, "4.3 Wrap 128 bits of Key Data with a 256-bit KEK\n");
+  check 
+    (GCRY_CIPHER_AES256,
+     "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
+     "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F", 32,
+     "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF", 16,
+     "\x64\xE8\xC3\xF9\xCE\x0F\x5B\xA2\x63\xE9\x77\x79\x05\x81\x8A\x2A"
+     "\x93\xC8\x19\x1E\x7D\x6E\x8A\xE7", 24);
+
+  if (verbose)
+    fprintf (stderr, "4.4 Wrap 192 bits of Key Data with a 192-bit KEK\n");
+  check 
+    (GCRY_CIPHER_AES192,
+     "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
+     "\x10\x11\x12\x13\x14\x15\x16\x17", 24,
+     "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF"
+     "\x00\x01\x02\x03\x04\x05\x06\x07", 24,
+     "\x03\x1D\x33\x26\x4E\x15\xD3\x32\x68\xF2\x4E\xC2\x60\x74\x3E\xDC"
+     "\xE1\xC6\xC7\xDD\xEE\x72\x5A\x93\x6B\xA8\x14\x91\x5C\x67\x62\xD2", 32);
+     
+  if (verbose)
+    fprintf (stderr, "4.5 Wrap 192 bits of Key Data with a 256-bit KEK\n");
+  check 
+    (GCRY_CIPHER_AES256,
+     "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
+     "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F", 32,
+     "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF"
+     "\x00\x01\x02\x03\x04\x05\x06\x07", 24,
+     "\xA8\xF9\xBC\x16\x12\xC6\x8B\x3F\xF6\xE6\xF4\xFB\xE3\x0E\x71\xE4"
+     "\x76\x9C\x8B\x80\xA3\x2C\xB8\x95\x8C\xD5\xD1\x7D\x6B\x25\x4D\xA1", 32);
+
+  if (verbose)
+    fprintf (stderr, "4.6 Wrap 256 bits of Key Data with a 256-bit KEK\n");
+  check 
+    (GCRY_CIPHER_AES,
+     "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
+     "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F", 32,
+     "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF"
+     "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", 32,
+     "\x28\xC9\xF4\x04\xC4\xB8\x10\xF4\xCB\xCC\xB3\x5C\xFB\x87\xF8\x26"
+     "\x3F\x57\x86\xE2\xD8\x0E\xD3\x26\xCB\xC7\xF0\xE7\x1A\x99\xF4\x3B"
+     "\xFB\x98\x8B\x9B\x7A\x02\xDD\x21", 40);
+}
+
+int
+main (int argc, char **argv)
+{
+  int debug = 0;
+
+  if (argc > 1 && !strcmp (argv[1], "--verbose"))
+    verbose = 1;
+  else if (argc > 1 && !strcmp (argv[1], "--debug"))
+    verbose = debug = 1;
+
+  if (!gcry_check_version (GCRYPT_VERSION))
+    die ("version mismatch\n");
+
+  gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
+  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+  if (debug)
+    gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1u, 0);
+  check_all ();
+
+  return error_count ? 1 : 0;
+}
index 17199e5..83ddf37 100644 (file)
@@ -1092,7 +1092,8 @@ main( int argc, char **argv )
   if (use_random_daemon)
     gcry_control (GCRYCTL_USE_RANDOM_DAEMON, 1);
 
-  gcry_set_progress_handler (progress_cb, NULL);
+  if (with_progress)
+    gcry_set_progress_handler (progress_cb, NULL);
 
   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);