CTR mode may now be used with arbitrary long data chunks.
[libgcrypt.git] / cipher / cipher.c
index 2c33ee9..90fdb17 100644 (file)
@@ -1,6 +1,6 @@
 /* cipher.c  - cipher dispatcher
  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003
- *               2005, 2007, 2008 Free Software Foundation, Inc.
+ *               2005, 2007, 2008, 2009 Free Software Foundation, Inc.
  *
  * This file is part of Libgcrypt.
  *
 #define CTX_MAGIC_NORMAL 0x24091964
 #define CTX_MAGIC_SECURE 0x46919042
 
+/* Try to use 16 byte aligned cipher context for better performance.
+   We use the aligned attribute, thus it is only possible to implement
+   this with gcc.  */
 #undef NEED_16BYTE_ALIGNED_CONTEXT
-#if defined (__i386__) && SIZEOF_UNSIGNED_LONG == 4 && defined (__GNUC__)
-#define NEED_16BYTE_ALIGNED_CONTEXT 1
+#if defined (__GNUC__)
+# define NEED_16BYTE_ALIGNED_CONTEXT 1
 #endif
 
 /* A dummy extraspec so that we do not need to tests the extraspec
@@ -58,39 +61,39 @@ static struct cipher_table_entry
       &dummy_extra_spec,                  GCRY_CIPHER_BLOWFISH },
 #endif
 #if USE_DES
-    { &_gcry_cipher_spec_des,       
+    { &_gcry_cipher_spec_des,
       &dummy_extra_spec,                  GCRY_CIPHER_DES },
     { &_gcry_cipher_spec_tripledes,
       &_gcry_cipher_extraspec_tripledes,  GCRY_CIPHER_3DES, 1 },
 #endif
 #if USE_ARCFOUR
-    { &_gcry_cipher_spec_arcfour,    
+    { &_gcry_cipher_spec_arcfour,
       &dummy_extra_spec,                  GCRY_CIPHER_ARCFOUR },
 #endif
 #if USE_CAST5
-    { &_gcry_cipher_spec_cast5,      
+    { &_gcry_cipher_spec_cast5,
       &dummy_extra_spec,                  GCRY_CIPHER_CAST5 },
 #endif
 #if USE_AES
-    { &_gcry_cipher_spec_aes,        
+    { &_gcry_cipher_spec_aes,
       &_gcry_cipher_extraspec_aes,        GCRY_CIPHER_AES,    1 },
-    { &_gcry_cipher_spec_aes192,     
+    { &_gcry_cipher_spec_aes192,
       &_gcry_cipher_extraspec_aes192,     GCRY_CIPHER_AES192, 1 },
-    { &_gcry_cipher_spec_aes256,     
+    { &_gcry_cipher_spec_aes256,
       &_gcry_cipher_extraspec_aes256,     GCRY_CIPHER_AES256, 1 },
 #endif
 #if USE_TWOFISH
     { &_gcry_cipher_spec_twofish,
       &dummy_extra_spec,                  GCRY_CIPHER_TWOFISH },
-    { &_gcry_cipher_spec_twofish128,     
+    { &_gcry_cipher_spec_twofish128,
       &dummy_extra_spec,                  GCRY_CIPHER_TWOFISH128 },
 #endif
 #if USE_SERPENT
-    { &_gcry_cipher_spec_serpent128, 
+    { &_gcry_cipher_spec_serpent128,
       &dummy_extra_spec,                  GCRY_CIPHER_SERPENT128 },
     { &_gcry_cipher_spec_serpent192,
       &dummy_extra_spec,                  GCRY_CIPHER_SERPENT192 },
-    { &_gcry_cipher_spec_serpent256, 
+    { &_gcry_cipher_spec_serpent256,
       &dummy_extra_spec,                  GCRY_CIPHER_SERPENT256 },
 #endif
 #if USE_RFC2268
@@ -98,13 +101,13 @@ static struct cipher_table_entry
       &dummy_extra_spec,                  GCRY_CIPHER_RFC2268_40 },
 #endif
 #if USE_SEED
-    { &_gcry_cipher_spec_seed, 
+    { &_gcry_cipher_spec_seed,
       &dummy_extra_spec,                  GCRY_CIPHER_SEED },
 #endif
 #if USE_CAMELLIA
     { &_gcry_cipher_spec_camellia128,
       &dummy_extra_spec,                  GCRY_CIPHER_CAMELLIA128 },
-    { &_gcry_cipher_spec_camellia192, 
+    { &_gcry_cipher_spec_camellia192,
       &dummy_extra_spec,                  GCRY_CIPHER_CAMELLIA192 },
     { &_gcry_cipher_spec_camellia256,
       &dummy_extra_spec,                  GCRY_CIPHER_CAMELLIA256 },
@@ -118,7 +121,7 @@ static gcry_module_t ciphers_registered;
 /* This is the lock protecting CIPHERS_REGISTERED.  */
 static ath_mutex_t ciphers_registered_lock = ATH_MUTEX_INITIALIZER;
 
-/* Flag to check wether the default ciphers have already been
+/* Flag to check whether the default ciphers have already been
    registered.  */
 static int default_ciphers_registered;
 
@@ -137,19 +140,20 @@ static int default_ciphers_registered;
   while (0)
 
 
-/* A VIA processor with the Padlock engine requires an alignment of
-   most data on a 16 byte boundary.  Because we trick out the compiler
-   while allocating the context, the align attribute as used in
-   rijndael.c does not work on its own.  Thus we need to make sure
-   that the entire context structure is a aligned on that boundary.
-   We achieve this by defining a new type and use that instead of our
-   usual alignment type.  */
-typedef union 
+/* A VIA processor with the Padlock engine as well as the Intel AES_NI
+   instructions require an alignment of most data on a 16 byte
+   boundary.  Because we trick out the compiler while allocating the
+   context, the align attribute as used in rijndael.c does not work on
+   its own.  Thus we need to make sure that the entire context
+   structure is a aligned on that boundary.  We achieve this by
+   defining a new type and use that instead of our usual alignment
+   type.  */
+typedef union
 {
   PROPERLY_ALIGNED_TYPE foo;
 #ifdef NEED_16BYTE_ALIGNED_CONTEXT
   char bar[16] __attribute__ ((aligned (16)));
-#endif  
+#endif
   char c[1];
 } cipher_context_alignment_t;
 
@@ -166,7 +170,7 @@ struct gcry_cipher_handle
 
   /* The algorithm id.  This is a hack required because the module
      interface does not easily allow to retrieve this value. */
-  int algo;  
+  int algo;
 
   /* A structure with function pointers for bulk operations.  Due to
      limitations of the module system (we don't want to change the
@@ -174,16 +178,19 @@ struct gcry_cipher_handle
      open function intializes them and the actual encryption routines
      use them if they are not NULL.  */
   struct {
-    void (*cfb_enc)(void *context, unsigned char *iv, 
+    void (*cfb_enc)(void *context, unsigned char *iv,
                     void *outbuf_arg, const void *inbuf_arg,
                     unsigned int nblocks);
-    void (*cfb_dec)(void *context, unsigned char *iv, 
+    void (*cfb_dec)(void *context, unsigned char *iv,
                     void *outbuf_arg, const void *inbuf_arg,
                     unsigned int nblocks);
-    void (*cbc_enc)(void *context, unsigned char *iv, 
+    void (*cbc_enc)(void *context, unsigned char *iv,
                     void *outbuf_arg, const void *inbuf_arg,
                     unsigned int nblocks, int cbc_mac);
-    void (*cbc_dec)(void *context, unsigned char *iv, 
+    void (*cbc_dec)(void *context, unsigned char *iv,
+                    void *outbuf_arg, const void *inbuf_arg,
+                    unsigned int nblocks);
+    void (*ctr_enc)(void *context, unsigned char *iv,
                     void *outbuf_arg, const void *inbuf_arg,
                     unsigned int nblocks);
   } bulk;
@@ -192,19 +199,29 @@ struct gcry_cipher_handle
   int mode;
   unsigned int flags;
 
-  /* The initialization vector.  To help code optimization we make
-     sure that it is aligned on an unsigned long and u32 boundary.  */
+  struct {
+    unsigned int key:1; /* Set to 1 if a key has been set.  */
+    unsigned int iv:1;  /* Set to 1 if a IV has been set.  */
+  } marks;
+
+  /* The initialization vector.  For best performance we make sure
+     that it is properly aligned.  In particular some implementations
+     of bulk operations expect an 16 byte aligned IV.  */
   union {
-    unsigned long dummy_iv;         
-    u32 dummy_u32_iv;
-    unsigned char iv[MAX_BLOCKSIZE];   
+    cipher_context_alignment_t iv_align;
+    unsigned char iv[MAX_BLOCKSIZE];
   } u_iv;
 
-  unsigned char lastiv[MAX_BLOCKSIZE];
-  int unused;  /* Number of unused bytes in the IV. */
-
-  unsigned char ctr[MAX_BLOCKSIZE];     /* For Counter (CTR) mode. */
+  /* The counter for CTR mode.  This field is also used by AESWRAP and
+     thus we can't use the U_IV union.  */
+  union {
+    cipher_context_alignment_t iv_align;
+    unsigned char ctr[MAX_BLOCKSIZE];
+  } u_ctr;
 
+  /* Space to save an IV or CTR for chaining operations.  */
+  unsigned char lastiv[MAX_BLOCKSIZE];
+  int unused;  /* Number of unused bytes in LASTIV. */
 
   /* What follows are two contexts of the cipher in use.  The first
      one needs to be aligned well enough for the cipher operation
@@ -222,7 +239,7 @@ struct gcry_cipher_handle
 static gcry_err_code_t
 dummy_setkey (void *c, const unsigned char *key, unsigned int keylen)
 {
-  (void)c; 
+  (void)c;
   (void)key;
   (void)keylen;
   return GPG_ERR_NO_ERROR;
@@ -281,7 +298,7 @@ cipher_register_default (void)
 {
   gcry_err_code_t err = GPG_ERR_NO_ERROR;
   int i;
-  
+
   for (i = 0; !err && cipher_table[i].cipher; i++)
     {
       if (! cipher_table[i].cipher->setkey)
@@ -384,8 +401,8 @@ _gcry_cipher_register (gcry_cipher_spec_t *cipher,
 
   ath_mutex_lock (&ciphers_registered_lock);
   err = _gcry_module_add (&ciphers_registered, 0,
-                         (void *)cipher, 
-                         (void *)(extraspec? extraspec : &dummy_extra_spec), 
+                         (void *)cipher,
+                         (void *)(extraspec? extraspec : &dummy_extra_spec),
                           &mod);
   ath_mutex_unlock (&ciphers_registered_lock);
 
@@ -415,7 +432,7 @@ gcry_cipher_unregister (gcry_module_t module)
    ispassed as NULL.  A pointer to the specification of the module
    implementing this algorithm is return in OID_SPEC unless passed as
    NULL.*/
-static int 
+static int
 search_oid (const char *oid, int *algorithm, gcry_cipher_oid_spec_t *oid_spec)
 {
   gcry_module_t module;
@@ -479,7 +496,7 @@ gcry_cipher_map_name (const char *string)
     }
 
   ath_mutex_unlock (&ciphers_registered_lock);
-  
+
   return algorithm;
 }
 
@@ -588,7 +605,7 @@ check_cipher_algo (int algorithm)
   else
     err = GPG_ERR_CIPHER_ALGO;
   ath_mutex_unlock (&ciphers_registered_lock);
-  
+
   return err;
 }
 
@@ -678,10 +695,10 @@ gcry_cipher_open (gcry_cipher_hd_t *handle,
   /* If the application missed to call the random poll function, we do
      it here to ensure that it is used once in a while. */
   _gcry_fast_random_poll ();
-  
+
   REGISTER_DEFAULT_CIPHERS;
 
-  /* Fetch the according module and check wether the cipher is marked
+  /* Fetch the according module and check whether the cipher is marked
      available for use.  */
   ath_mutex_lock (&ciphers_registered_lock);
   module = _gcry_module_lookup_id (ciphers_registered, algo);
@@ -693,7 +710,6 @@ gcry_cipher_open (gcry_cipher_hd_t *handle,
        {
          /* Not available for use.  */
          err = GPG_ERR_CIPHER_ALGO;
-         _gcry_module_release (module);
        }
       else
         {
@@ -707,7 +723,7 @@ gcry_cipher_open (gcry_cipher_hd_t *handle,
 
   /* check flags */
   if ((! err)
-      && ((flags & ~(0 
+      && ((flags & ~(0
                     | GCRY_CIPHER_SECURE
                     | GCRY_CIPHER_ENABLE_SYNC
                     | GCRY_CIPHER_CBC_CTS
@@ -724,6 +740,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;
@@ -805,9 +822,10 @@ gcry_cipher_open (gcry_cipher_hd_t *handle,
               h->bulk.cfb_dec = _gcry_aes_cfb_dec;
               h->bulk.cbc_enc = _gcry_aes_cbc_enc;
               h->bulk.cbc_dec = _gcry_aes_cbc_dec;
+              h->bulk.ctr_enc = _gcry_aes_ctr_enc;
               break;
 #endif /*USE_AES*/
-              
+
             default:
               break;
             }
@@ -882,7 +900,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);
 }
@@ -894,7 +915,7 @@ static void
 cipher_setiv( gcry_cipher_hd_t c, const byte *iv, unsigned ivlen )
 {
   memset (c->u_iv.iv, 0, c->cipher->blocksize);
-  if (iv) 
+  if (iv)
     {
       if (ivlen != c->cipher->blocksize)
         {
@@ -905,7 +926,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,61 +942,92 @@ 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);
+  memset (c->u_ctr.ctr, 0, c->cipher->blocksize);
 }
 
 
-static void
-do_ecb_encrypt( gcry_cipher_hd_t c, byte *outbuf, const byte *inbuf,
-                unsigned int nblocks )
+\f
+static gcry_err_code_t
+do_ecb_encrypt (gcry_cipher_hd_t c,
+                unsigned char *outbuf, unsigned int outbuflen,
+                const unsigned char *inbuf, unsigned int inbuflen)
 {
-  unsigned int n;
-  
+  unsigned int blocksize = c->cipher->blocksize;
+  unsigned int n, nblocks;
+
+  if (outbuflen < inbuflen)
+    return GPG_ERR_BUFFER_TOO_SHORT;
+  if ((inbuflen % blocksize))
+    return GPG_ERR_INV_LENGTH;
+
+  nblocks = inbuflen / c->cipher->blocksize;
+
   for (n=0; n < nblocks; n++ )
     {
-      c->cipher->encrypt ( &c->context.c, outbuf, (byte*)/*arggg*/inbuf );
-      inbuf  += c->cipher->blocksize;
-      outbuf += c->cipher->blocksize;
+      c->cipher->encrypt (&c->context.c, outbuf, (byte*)/*arggg*/inbuf);
+      inbuf  += blocksize;
+      outbuf += blocksize;
     }
+  return 0;
 }
 
-static void
-do_ecb_decrypt( gcry_cipher_hd_t c, byte *outbuf, const byte *inbuf,
-                unsigned int nblocks )
+static gcry_err_code_t
+do_ecb_decrypt (gcry_cipher_hd_t c,
+                unsigned char *outbuf, unsigned int outbuflen,
+                const unsigned char *inbuf, unsigned int inbuflen)
 {
-  unsigned int n;
+  unsigned int blocksize = c->cipher->blocksize;
+  unsigned int n, nblocks;
 
-  for (n=0; n < nblocks; n++ ) 
+  if (outbuflen < inbuflen)
+    return GPG_ERR_BUFFER_TOO_SHORT;
+  if ((inbuflen % blocksize))
+    return GPG_ERR_INV_LENGTH;
+  nblocks = inbuflen / c->cipher->blocksize;
+
+  for (n=0; n < nblocks; n++ )
     {
-      c->cipher->decrypt ( &c->context.c, outbuf, (byte*)/*arggg*/inbuf );
-      inbuf  += c->cipher->blocksize;
-      outbuf += c->cipher->blocksize;
+      c->cipher->decrypt (&c->context.c, outbuf, (byte*)/*arggg*/inbuf );
+      inbuf  += blocksize;
+      outbuf += blocksize;
     }
+
+  return 0;
 }
 
 
-static void
-do_cbc_encrypt (gcry_cipher_hd_t c, unsigned char *outbuf, 
-                const unsigned char *inbuf, unsigned int nbytes )
+static gcry_err_code_t
+do_cbc_encrypt (gcry_cipher_hd_t c,
+                unsigned char *outbuf, unsigned int outbuflen,
+                const unsigned char *inbuf, unsigned int inbuflen)
 {
   unsigned int n;
   unsigned char *ivp;
   int i;
   size_t blocksize = c->cipher->blocksize;
-  unsigned nblocks = nbytes / blocksize;
+  unsigned nblocks = inbuflen / blocksize;
+
+  if (outbuflen < ((c->flags & GCRY_CIPHER_CBC_MAC)? blocksize : inbuflen))
+    return GPG_ERR_BUFFER_TOO_SHORT;
+
+  if ((inbuflen % c->cipher->blocksize)
+      && !(inbuflen > c->cipher->blocksize
+           && (c->flags & GCRY_CIPHER_CBC_CTS)))
+    return GPG_ERR_INV_LENGTH;
 
-  if ((c->flags & GCRY_CIPHER_CBC_CTS) && nbytes > blocksize) 
+  if ((c->flags & GCRY_CIPHER_CBC_CTS) && inbuflen > blocksize)
     {
-      if ((nbytes % blocksize) == 0)
+      if ((inbuflen % blocksize) == 0)
        nblocks--;
     }
 
   if (c->bulk.cbc_enc)
     {
       c->bulk.cbc_enc (&c->context.c, c->u_iv.iv, outbuf, inbuf, nblocks,
-                       (c->flags & GCRY_CIPHER_CBC_MAC)); 
+                       (c->flags & GCRY_CIPHER_CBC_MAC));
       inbuf  += nblocks * blocksize;
       if (!(c->flags & GCRY_CIPHER_CBC_MAC))
         outbuf += nblocks * blocksize;
@@ -991,17 +1046,17 @@ do_cbc_encrypt (gcry_cipher_hd_t c, unsigned char *outbuf,
         }
     }
 
-  if ((c->flags & GCRY_CIPHER_CBC_CTS) && nbytes > blocksize)
+  if ((c->flags & GCRY_CIPHER_CBC_CTS) && inbuflen > blocksize)
     {
       /* We have to be careful here, since outbuf might be equal to
          inbuf.  */
       int restbytes;
       unsigned char b;
 
-      if ((nbytes % blocksize) == 0)
+      if ((inbuflen % blocksize) == 0)
         restbytes = blocksize;
       else
-        restbytes = nbytes % blocksize;
+        restbytes = inbuflen % blocksize;
 
       outbuf -= blocksize;
       for (ivp = c->u_iv.iv, i = 0; i < restbytes; i++)
@@ -1012,40 +1067,51 @@ do_cbc_encrypt (gcry_cipher_hd_t c, unsigned char *outbuf,
         }
       for (; i < blocksize; i++)
         outbuf[i] = 0 ^ *ivp++;
-      
+
       c->cipher->encrypt (&c->context.c, outbuf, outbuf);
       memcpy (c->u_iv.iv, outbuf, blocksize);
     }
+
+  return 0;
 }
 
 
-static void
-do_cbc_decrypt (gcry_cipher_hd_t c, unsigned char *outbuf, 
-                const unsigned char *inbuf, unsigned int nbytes)
+static gcry_err_code_t
+do_cbc_decrypt (gcry_cipher_hd_t c,
+                unsigned char *outbuf, unsigned int outbuflen,
+                const unsigned char *inbuf, unsigned int inbuflen)
 {
   unsigned int n;
   unsigned char *ivp;
   int i;
   size_t blocksize = c->cipher->blocksize;
-  unsigned int nblocks = nbytes / blocksize;
+  unsigned int nblocks = inbuflen / blocksize;
+
+  if (outbuflen < inbuflen)
+    return GPG_ERR_BUFFER_TOO_SHORT;
+
+  if ((inbuflen % c->cipher->blocksize)
+      && !(inbuflen > c->cipher->blocksize
+           && (c->flags & GCRY_CIPHER_CBC_CTS)))
+    return GPG_ERR_INV_LENGTH;
 
-  if ((c->flags & GCRY_CIPHER_CBC_CTS) && nbytes > blocksize)
+  if ((c->flags & GCRY_CIPHER_CBC_CTS) && inbuflen > blocksize)
     {
       nblocks--;
-      if ((nbytes % blocksize) == 0)
+      if ((inbuflen % blocksize) == 0)
        nblocks--;
       memcpy (c->lastiv, c->u_iv.iv, blocksize);
     }
 
   if (c->bulk.cbc_dec)
     {
-      c->bulk.cbc_dec (&c->context.c, c->u_iv.iv, outbuf, inbuf, nblocks); 
+      c->bulk.cbc_dec (&c->context.c, c->u_iv.iv, outbuf, inbuf, nblocks);
       inbuf  += nblocks * blocksize;
       outbuf += nblocks * blocksize;
     }
   else
     {
-      for (n=0; n < nblocks; n++ ) 
+      for (n=0; n < nblocks; n++ )
         {
           /* Because outbuf and inbuf might be the same, we have to
            * save the original ciphertext block.  We use LASTIV for
@@ -1060,22 +1126,22 @@ do_cbc_decrypt (gcry_cipher_hd_t c, unsigned char *outbuf,
         }
     }
 
-  if ((c->flags & GCRY_CIPHER_CBC_CTS) && nbytes > blocksize) 
+  if ((c->flags & GCRY_CIPHER_CBC_CTS) && inbuflen > blocksize)
     {
       int restbytes;
-      
-      if ((nbytes % blocksize) == 0)
+
+      if ((inbuflen % blocksize) == 0)
         restbytes = blocksize;
       else
-        restbytes = nbytes % blocksize;
-      
+        restbytes = inbuflen % blocksize;
+
       memcpy (c->lastiv, c->u_iv.iv, blocksize );         /* Save Cn-2. */
       memcpy (c->u_iv.iv, inbuf + blocksize, restbytes ); /* Save Cn. */
 
       c->cipher->decrypt ( &c->context.c, outbuf, inbuf );
       for (ivp=c->u_iv.iv,i=0; i < restbytes; i++ )
         outbuf[i] ^= *ivp++;
-      
+
       memcpy(outbuf + blocksize, outbuf, restbytes);
       for(i=restbytes; i < blocksize; i++)
         c->u_iv.iv[i] = outbuf[i];
@@ -1084,32 +1150,38 @@ do_cbc_decrypt (gcry_cipher_hd_t c, unsigned char *outbuf,
         outbuf[i] ^= *ivp++;
       /* c->lastiv is now really lastlastiv, does this matter? */
     }
+
+  return 0;
 }
 
 
-static void
-do_cfb_encrypt( gcry_cipher_hd_t c, unsigned char *outbuf, 
-                const unsigned char *inbuf, unsigned int nbytes )
+static gcry_err_code_t
+do_cfb_encrypt (gcry_cipher_hd_t c,
+                unsigned char *outbuf, unsigned int outbuflen,
+                const unsigned char *inbuf, unsigned int inbuflen)
 {
   unsigned char *ivp;
   size_t blocksize = c->cipher->blocksize;
   size_t blocksize_x_2 = blocksize + blocksize;
-  
-  if ( nbytes <= c->unused )
+
+  if (outbuflen < inbuflen)
+    return GPG_ERR_BUFFER_TOO_SHORT;
+
+  if ( inbuflen <= c->unused )
     {
       /* Short enough to be encoded by the remaining XOR mask. */
       /* XOR the input with the IV and store input into IV. */
       for (ivp=c->u_iv.iv+c->cipher->blocksize - c->unused;
-           nbytes;
-           nbytes--, c->unused-- )
+           inbuflen;
+           inbuflen--, c->unused-- )
         *outbuf++ = (*ivp++ ^= *inbuf++);
-      return;
+      return 0;
     }
 
   if ( c->unused )
     {
       /* XOR the input with the IV and store input into IV */
-      nbytes -= c->unused;
+      inbuflen -= c->unused;
       for(ivp=c->u_iv.iv+blocksize - c->unused; c->unused; c->unused-- )
         *outbuf++ = (*ivp++ ^= *inbuf++);
     }
@@ -1117,17 +1189,17 @@ do_cfb_encrypt( gcry_cipher_hd_t c, unsigned char *outbuf,
   /* Now we can process complete blocks.  We use a loop as long as we
      have at least 2 blocks and use conditions for the rest.  This
      also allows to use a bulk encryption function if available.  */
-  if (nbytes >= blocksize_x_2 && c->bulk.cfb_enc)
+  if (inbuflen >= blocksize_x_2 && c->bulk.cfb_enc)
     {
-      unsigned int nblocks = nbytes / blocksize;
-      c->bulk.cfb_enc (&c->context.c, c->u_iv.iv, outbuf, inbuf, nblocks); 
+      unsigned int nblocks = inbuflen / blocksize;
+      c->bulk.cfb_enc (&c->context.c, c->u_iv.iv, outbuf, inbuf, nblocks);
       outbuf += nblocks * blocksize;
       inbuf  += nblocks * blocksize;
-      nbytes -= nblocks * blocksize;
+      inbuflen -= nblocks * blocksize;
     }
   else
     {
-      while ( nbytes >= blocksize_x_2 )
+      while ( inbuflen >= blocksize_x_2 )
         {
           int i;
           /* Encrypt the IV. */
@@ -1135,11 +1207,11 @@ do_cfb_encrypt( gcry_cipher_hd_t c, unsigned char *outbuf,
           /* XOR the input with the IV and store input into IV.  */
           for(ivp=c->u_iv.iv,i=0; i < blocksize; i++ )
             *outbuf++ = (*ivp++ ^= *inbuf++);
-          nbytes -= blocksize;
+          inbuflen -= blocksize;
         }
     }
 
-  if ( nbytes >= blocksize )
+  if ( inbuflen >= blocksize )
     {
       int i;
       /* Save the current IV and then encrypt the IV. */
@@ -1148,51 +1220,56 @@ do_cfb_encrypt( gcry_cipher_hd_t c, unsigned char *outbuf,
       /* XOR the input with the IV and store input into IV */
       for(ivp=c->u_iv.iv,i=0; i < blocksize; i++ )
         *outbuf++ = (*ivp++ ^= *inbuf++);
-      nbytes -= blocksize;
+      inbuflen -= blocksize;
     }
-  if ( nbytes ) 
+  if ( inbuflen )
     {
       /* Save the current IV and then encrypt the IV. */
       memcpy( c->lastiv, c->u_iv.iv, blocksize );
       c->cipher->encrypt ( &c->context.c, c->u_iv.iv, c->u_iv.iv );
       c->unused = blocksize;
       /* Apply the XOR. */
-      c->unused -= nbytes;
-      for(ivp=c->u_iv.iv; nbytes; nbytes-- )
+      c->unused -= inbuflen;
+      for(ivp=c->u_iv.iv; inbuflen; inbuflen-- )
         *outbuf++ = (*ivp++ ^= *inbuf++);
     }
+  return 0;
 }
 
 
-static void
-do_cfb_decrypt( gcry_cipher_hd_t c, unsigned char *outbuf, 
-                const unsigned char *inbuf, unsigned int nbytes )
+static gcry_err_code_t
+do_cfb_decrypt (gcry_cipher_hd_t c,
+                unsigned char *outbuf, unsigned int outbuflen,
+                const unsigned char *inbuf, unsigned int inbuflen)
 {
   unsigned char *ivp;
   unsigned long temp;
   int i;
   size_t blocksize = c->cipher->blocksize;
   size_t blocksize_x_2 = blocksize + blocksize;
-  
-  if (nbytes <= c->unused)
+
+  if (outbuflen < inbuflen)
+    return GPG_ERR_BUFFER_TOO_SHORT;
+
+  if (inbuflen <= c->unused)
     {
       /* Short enough to be encoded by the remaining XOR mask. */
       /* XOR the input with the IV and store input into IV. */
       for (ivp=c->u_iv.iv+blocksize - c->unused;
-           nbytes; 
-           nbytes--, c->unused--)
+           inbuflen;
+           inbuflen--, c->unused--)
         {
           temp = *inbuf++;
           *outbuf++ = *ivp ^ temp;
           *ivp++ = temp;
         }
-      return;
+      return 0;
     }
-  
+
   if (c->unused)
     {
       /* XOR the input with the IV and store input into IV. */
-      nbytes -= c->unused;
+      inbuflen -= c->unused;
       for (ivp=c->u_iv.iv+blocksize - c->unused; c->unused; c->unused-- )
         {
           temp = *inbuf++;
@@ -1200,21 +1277,21 @@ do_cfb_decrypt( gcry_cipher_hd_t c, unsigned char *outbuf,
           *ivp++ = temp;
         }
     }
-  
+
   /* Now we can process complete blocks.  We use a loop as long as we
      have at least 2 blocks and use conditions for the rest.  This
      also allows to use a bulk encryption function if available.  */
-  if (nbytes >= blocksize_x_2 && c->bulk.cfb_dec)
+  if (inbuflen >= blocksize_x_2 && c->bulk.cfb_dec)
     {
-      unsigned int nblocks = nbytes / blocksize;
-      c->bulk.cfb_dec (&c->context.c, c->u_iv.iv, outbuf, inbuf, nblocks); 
+      unsigned int nblocks = inbuflen / blocksize;
+      c->bulk.cfb_dec (&c->context.c, c->u_iv.iv, outbuf, inbuf, nblocks);
       outbuf += nblocks * blocksize;
       inbuf  += nblocks * blocksize;
-      nbytes -= nblocks * blocksize;
+      inbuflen -= nblocks * blocksize;
     }
   else
     {
-      while (nbytes >= blocksize_x_2 )
+      while (inbuflen >= blocksize_x_2 )
         {
           /* Encrypt the IV. */
           c->cipher->encrypt ( &c->context.c, c->u_iv.iv, c->u_iv.iv );
@@ -1225,11 +1302,11 @@ do_cfb_decrypt( gcry_cipher_hd_t c, unsigned char *outbuf,
               *outbuf++ = *ivp ^ temp;
               *ivp++ = temp;
             }
-          nbytes -= blocksize;
+          inbuflen -= blocksize;
         }
     }
 
-  if (nbytes >= blocksize )
+  if (inbuflen >= blocksize )
     {
       /* Save the current IV and then encrypt the IV. */
       memcpy ( c->lastiv, c->u_iv.iv, blocksize);
@@ -1241,99 +1318,109 @@ do_cfb_decrypt( gcry_cipher_hd_t c, unsigned char *outbuf,
           *outbuf++ = *ivp ^ temp;
           *ivp++ = temp;
         }
-      nbytes -= blocksize;
+      inbuflen -= blocksize;
     }
 
-  if (nbytes)
-    { 
+  if (inbuflen)
+    {
       /* Save the current IV and then encrypt the IV. */
       memcpy ( c->lastiv, c->u_iv.iv, blocksize );
       c->cipher->encrypt ( &c->context.c, c->u_iv.iv, c->u_iv.iv );
       c->unused = blocksize;
       /* Apply the XOR. */
-      c->unused -= nbytes;
-      for (ivp=c->u_iv.iv; nbytes; nbytes-- )
+      c->unused -= inbuflen;
+      for (ivp=c->u_iv.iv; inbuflen; inbuflen-- )
         {
           temp = *inbuf++;
           *outbuf++ = *ivp ^ temp;
           *ivp++ = temp;
         }
     }
+  return 0;
 }
 
 
-static void
-do_ofb_encrypt( gcry_cipher_hd_t c,
-                byte *outbuf, const byte *inbuf, unsigned nbytes )
+static gcry_err_code_t
+do_ofb_encrypt (gcry_cipher_hd_t c,
+                unsigned char *outbuf, unsigned int outbuflen,
+                const unsigned char *inbuf, unsigned int inbuflen)
 {
-  byte *ivp;
+  unsigned char *ivp;
   size_t blocksize = c->cipher->blocksize;
 
-  if ( nbytes <= c->unused )
+  if (outbuflen < inbuflen)
+    return GPG_ERR_BUFFER_TOO_SHORT;
+
+  if ( inbuflen <= c->unused )
     {
       /* Short enough to be encoded by the remaining XOR mask. */
       /* XOR the input with the IV */
       for (ivp=c->u_iv.iv+c->cipher->blocksize - c->unused;
-           nbytes;
-           nbytes--, c->unused-- )
+           inbuflen;
+           inbuflen--, c->unused-- )
         *outbuf++ = (*ivp++ ^ *inbuf++);
-      return;
+      return 0;
     }
 
   if( c->unused )
     {
-      nbytes -= c->unused;
+      inbuflen -= c->unused;
       for(ivp=c->u_iv.iv+blocksize - c->unused; c->unused; c->unused-- )
         *outbuf++ = (*ivp++ ^ *inbuf++);
     }
 
   /* Now we can process complete blocks. */
-  while ( nbytes >= blocksize )
+  while ( inbuflen >= blocksize )
     {
       int i;
       /* Encrypt the IV (and save the current one). */
       memcpy( c->lastiv, c->u_iv.iv, blocksize );
       c->cipher->encrypt ( &c->context.c, c->u_iv.iv, c->u_iv.iv );
-      
+
       for (ivp=c->u_iv.iv,i=0; i < blocksize; i++ )
         *outbuf++ = (*ivp++ ^ *inbuf++);
-      nbytes -= blocksize;
+      inbuflen -= blocksize;
     }
-  if ( nbytes )
+  if ( inbuflen )
     { /* process the remaining bytes */
       memcpy( c->lastiv, c->u_iv.iv, blocksize );
       c->cipher->encrypt ( &c->context.c, c->u_iv.iv, c->u_iv.iv );
       c->unused = blocksize;
-      c->unused -= nbytes;
-      for(ivp=c->u_iv.iv; nbytes; nbytes-- )
+      c->unused -= inbuflen;
+      for(ivp=c->u_iv.iv; inbuflen; inbuflen-- )
         *outbuf++ = (*ivp++ ^ *inbuf++);
     }
+  return 0;
 }
 
-static void
-do_ofb_decrypt( gcry_cipher_hd_t c,
-                byte *outbuf, const byte *inbuf, unsigned int nbytes )
+static gcry_err_code_t
+do_ofb_decrypt (gcry_cipher_hd_t c,
+                unsigned char *outbuf, unsigned int outbuflen,
+                const unsigned char *inbuf, unsigned int inbuflen)
 {
-  byte *ivp;
+  unsigned char *ivp;
   size_t blocksize = c->cipher->blocksize;
-  
-  if( nbytes <= c->unused )
+
+  if (outbuflen < inbuflen)
+    return GPG_ERR_BUFFER_TOO_SHORT;
+
+  if( inbuflen <= c->unused )
     {
       /* Short enough to be encoded by the remaining XOR mask. */
-      for (ivp=c->u_iv.iv+blocksize - c->unused; nbytes; nbytes--,c->unused--)
+      for (ivp=c->u_iv.iv+blocksize - c->unused; inbuflen; inbuflen--,c->unused--)
         *outbuf++ = *ivp++ ^ *inbuf++;
-      return;
+      return 0;
     }
 
   if ( c->unused )
     {
-      nbytes -= c->unused;
+      inbuflen -= c->unused;
       for (ivp=c->u_iv.iv+blocksize - c->unused; c->unused; c->unused-- )
         *outbuf++ = *ivp++ ^ *inbuf++;
     }
 
   /* Now we can process complete blocks. */
-  while ( nbytes >= blocksize )
+  while ( inbuflen >= blocksize )
     {
       int i;
       /* Encrypt the IV (and save the current one). */
@@ -1341,113 +1428,335 @@ do_ofb_decrypt( gcry_cipher_hd_t c,
       c->cipher->encrypt ( &c->context.c, c->u_iv.iv, c->u_iv.iv );
       for (ivp=c->u_iv.iv,i=0; i < blocksize; i++ )
         *outbuf++ = *ivp++ ^ *inbuf++;
-      nbytes -= blocksize;
+      inbuflen -= blocksize;
     }
-  if ( nbytes ) 
+  if ( inbuflen )
     { /* Process the remaining bytes. */
       /* Encrypt the IV (and save the current one). */
       memcpy( c->lastiv, c->u_iv.iv, blocksize );
       c->cipher->encrypt ( &c->context.c, c->u_iv.iv, c->u_iv.iv );
       c->unused = blocksize;
-      c->unused -= nbytes;
-      for (ivp=c->u_iv.iv; nbytes; nbytes-- )
+      c->unused -= inbuflen;
+      for (ivp=c->u_iv.iv; inbuflen; inbuflen-- )
         *outbuf++ = *ivp++ ^ *inbuf++;
     }
+  return 0;
 }
 
 
-static void
-do_ctr_encrypt( gcry_cipher_hd_t c, byte *outbuf, const byte *inbuf,
-                unsigned int nbytes )
+static gcry_err_code_t
+do_ctr_encrypt (gcry_cipher_hd_t c,
+                unsigned char *outbuf, unsigned int outbuflen,
+                const unsigned char *inbuf, unsigned int inbuflen)
 {
   unsigned int n;
-  byte tmp[MAX_BLOCKSIZE];
   int i;
+  unsigned int blocksize = c->cipher->blocksize;
+  unsigned int nblocks;
+
+  if (outbuflen < inbuflen)
+    return GPG_ERR_BUFFER_TOO_SHORT;
 
-  for(n=0; n < nbytes; n++)
+  /* First process a left over encrypted counter.  */
+  if (c->unused)
     {
-      if ((n % c->cipher->blocksize) == 0)
-       {
-         c->cipher->encrypt (&c->context.c, tmp, c->ctr);
+      gcry_assert (c->unused < blocksize);
+      i = blocksize - c->unused;
+      for (n=0; c->unused && n < inbuflen; c->unused--, n++, i++)
+        {
+          /* XOR input with encrypted counter and store in output.  */
+          outbuf[n] = inbuf[n] ^ c->lastiv[i];
+        }
+      inbuf  += n;
+      outbuf += n;
+      inbuflen -= n;
+    }
+
+
+  /* Use a bulk method if available.  */
+  nblocks = inbuflen / blocksize;
+  if (nblocks && c->bulk.ctr_enc)
+    {
+      c->bulk.ctr_enc (&c->context.c, c->u_ctr.ctr, outbuf, inbuf, nblocks);
+      inbuf  += nblocks * blocksize;
+      outbuf += nblocks * blocksize;
+      inbuflen -= nblocks * blocksize;
+    }
 
-         for (i = c->cipher->blocksize; i > 0; i--)
+  /* If we don't have a bulk method use the standard method.  We also
+     use this method for the a remaining partial block.  */
+  if (inbuflen)
+    {
+      unsigned char tmp[MAX_BLOCKSIZE];
+
+      for (n=0; n < inbuflen; n++)
+        {
+          if ((n % blocksize) == 0)
+            {
+              c->cipher->encrypt (&c->context.c, tmp, c->u_ctr.ctr);
+
+              for (i = blocksize; i > 0; i--)
+                {
+                  c->u_ctr.ctr[i-1]++;
+                  if (c->u_ctr.ctr[i-1] != 0)
+                    break;
+                }
+            }
+
+          /* XOR input with encrypted counter and store in output.  */
+          outbuf[n] = inbuf[n] ^ tmp[n % blocksize];
+        }
+
+      /* Save the unused bytes of the counter.  */
+      n %= blocksize;
+      c->unused = (blocksize - n) % blocksize;
+      if (c->unused)
+        memcpy (c->lastiv+n, tmp+n, c->unused);
+
+      wipememory (tmp, sizeof tmp);
+    }
+
+  return 0;
+}
+
+static gcry_err_code_t
+do_ctr_decrypt (gcry_cipher_hd_t c,
+                unsigned char *outbuf, unsigned int outbuflen,
+                const unsigned char *inbuf, unsigned int inbuflen)
+{
+  return do_ctr_encrypt (c, outbuf, outbuflen, inbuf, inbuflen);
+}
+
+
+/* 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->u_ctr.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--)
            {
-             c->ctr[i-1]++;
-             if (c->ctr[i-1] != 0)
+             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);
+        }
+   }
 
-      /* XOR input with encrypted counter and store in output. */
-      outbuf[n] = inbuf[n] ^ tmp[n % c->cipher->blocksize];
-    }
+  return 0;
 }
 
-static void
-do_ctr_decrypt( gcry_cipher_hd_t c, byte *outbuf, const byte *inbuf,
-                unsigned int nbytes )
+/* 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)
 {
-  do_ctr_encrypt (c, outbuf, inbuf, nbytes);
+  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->u_ctr.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 contraints 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;
 
-    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:
+      rc = do_ecb_encrypt (c, outbuf, outbuflen, inbuf, inbuflen);
+      break;
+
+    case GCRY_CIPHER_MODE_CBC:
+      rc = do_cbc_encrypt (c, outbuf, outbuflen, inbuf, inbuflen);
+      break;
+
+    case GCRY_CIPHER_MODE_CFB:
+      rc = do_cfb_encrypt (c, outbuf, outbuflen, inbuf, inbuflen);
+      break;
+
+    case GCRY_CIPHER_MODE_OFB:
+      rc = do_ofb_encrypt (c, outbuf, outbuflen, inbuf, inbuflen);
+      break;
+
+    case GCRY_CIPHER_MODE_CTR:
+      rc = do_ctr_encrypt (c, outbuf, outbuflen, 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);
+      rc = 0;
+      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);
+          rc = 0;
+        }
+      break;
+
+    default:
+      log_fatal ("cipher_encrypt: invalid mode %d\n", c->mode );
+      rc = GPG_ERR_INV_CIPHER_MODE;
+      break;
     }
-    return rc;
+
+  return rc;
 }
 
 
@@ -1461,29 +1770,15 @@ gcry_cipher_encrypt (gcry_cipher_hd_t h, void *out, size_t outsize,
 {
   gcry_err_code_t err;
 
-  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);
-    }
-  else if (outsize < ((h->flags & GCRY_CIPHER_CBC_MAC) ?
-                      h->cipher->blocksize : 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)
-                      && (inlen > h->cipher->blocksize)))))
-          && (inlen % h->cipher->blocksize))
-    err = GPG_ERR_INV_ARG;
+  if (!in)  /* Caller requested in-place encryption.  */
+    err = cipher_encrypt (h, out, outsize, out, outsize);
   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);
 }
@@ -1493,60 +1788,67 @@ gcry_cipher_encrypt (gcry_cipher_hd_t h, void *out, size_t outsize,
 /****************
  * Decrypt INBUF to OUTBUF with the mode selected at open.
  * inbuf and outbuf may overlap or be the same.
- * Depending on the mode some some contraints apply to NBYTES.
+ * Depending on the mode some some contraints apply to INBUFLEN.
  */
 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;
 
-    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:
+      rc = do_ecb_decrypt (c, outbuf, outbuflen, inbuf, inbuflen);
+      break;
+
+    case GCRY_CIPHER_MODE_CBC:
+      rc = do_cbc_decrypt (c, outbuf, outbuflen, inbuf, inbuflen);
+      break;
+
+    case GCRY_CIPHER_MODE_CFB:
+      rc = do_cfb_decrypt (c, outbuf, outbuflen, inbuf, inbuflen);
+      break;
+
+    case GCRY_CIPHER_MODE_OFB:
+      rc = do_ofb_decrypt (c, outbuf, outbuflen, inbuf, inbuflen);
+      break;
+
+    case GCRY_CIPHER_MODE_CTR:
+      rc = do_ctr_decrypt (c, outbuf, outbuflen, 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);
+      rc = 0;
+      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);
+          rc = 0;
+        }
+      break;
+
+    default:
+      log_fatal ("cipher_decrypt: invalid mode %d\n", c->mode );
+      rc = GPG_ERR_INV_CIPHER_MODE;
+      break;
     }
-    return rc;
+
+  return rc;
 }
 
 
@@ -1554,25 +1856,12 @@ gcry_error_t
 gcry_cipher_decrypt (gcry_cipher_hd_t h, void *out, size_t outsize,
                     const void *in, size_t inlen)
 {
-  gcry_err_code_t err = 0;
+  gcry_err_code_t err;
 
-  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);
-    }
-  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)
-                      && (inlen > h->cipher->blocksize)))))
-          && (inlen % h->cipher->blocksize) != 0)
-    err = GPG_ERR_INV_ARG;
+  if (!in) /* Caller requested in-place encryption. */
+    err = cipher_decrypt (h, out, outsize, out, outsize);
   else
-    err = cipher_decrypt (h, out, in, inlen);
+    err = cipher_decrypt (h, out, outsize, in, inlen);
 
   return gcry_error (err);
 }
@@ -1618,9 +1907,15 @@ gpg_error_t
 _gcry_cipher_setctr (gcry_cipher_hd_t hd, const void *ctr, size_t ctrlen)
 {
   if (ctr && ctrlen == hd->cipher->blocksize)
-    memcpy (hd->ctr, ctr, hd->cipher->blocksize);
+    {
+      memcpy (hd->u_ctr.ctr, ctr, hd->cipher->blocksize);
+      hd->unused = 0;
+    }
   else if (!ctr || !ctrlen)
-    memset (hd->ctr, 0, hd->cipher->blocksize);
+    {
+      memset (hd->u_ctr.ctr, 0, hd->cipher->blocksize);
+      hd->unused = 0;
+    }
   else
     return gpg_error (GPG_ERR_INV_ARG);
   return 0;
@@ -1679,17 +1974,12 @@ gcry_cipher_ctl( gcry_cipher_hd_t h, int cmd, void *buffer, size_t buflen)
       break;
 
     case GCRYCTL_SET_CTR: /* Deprecated; use gcry_cipher_setctr.  */
-      if (buffer && buflen == h->cipher->blocksize)
-       memcpy (h->ctr, buffer, h->cipher->blocksize);
-      else if (buffer == NULL || buflen == 0)
-       memset (h->ctr, 0, h->cipher->blocksize);
-      else
-       rc = GPG_ERR_INV_ARG;
+      rc = gpg_err_code (_gcry_cipher_setctr (h, buffer, buflen));
       break;
 
     case 61:  /* Disable weak key detection (private).  */
       if (h->extraspec->set_extra_info)
-        rc = h->extraspec->set_extra_info 
+        rc = h->extraspec->set_extra_info
           (&h->context.c, CIPHER_INFO_NO_WEAK_KEY, NULL, 0);
       else
         rc = GPG_ERR_NOT_SUPPORTED;
@@ -1697,7 +1987,7 @@ gcry_cipher_ctl( gcry_cipher_hd_t h, int cmd, void *buffer, size_t buflen)
 
     case 62: /* Return current input vector (private).  */
       /* This is the input block as used in CFB and OFB mode which has
-         initially been set as IV.  The returned format is: 
+         initially been set as IV.  The returned format is:
            1 byte  Actual length of the block in bytes.
            n byte  The block.
          If the provided buffer is too short, an error is returned. */
@@ -1708,7 +1998,7 @@ gcry_cipher_ctl( gcry_cipher_hd_t h, int cmd, void *buffer, size_t buflen)
           unsigned char *ivp;
           unsigned char *dst = buffer;
           int n = h->unused;
-          
+
           if (!n)
             n = h->cipher->blocksize;
           gcry_assert (n <= h->cipher->blocksize);
@@ -1730,10 +2020,10 @@ gcry_cipher_ctl( gcry_cipher_hd_t h, int cmd, void *buffer, size_t buflen)
 /* Return information about the cipher handle H.  CMD is the kind of
    information requested.  BUFFER and NBYTES are reserved for now.
 
-   There are no values for CMD yet defined.  
+   There are no values for CMD yet defined.
+
+   The function always returns GPG_ERR_INV_OP.
 
-   The fucntion always returns GPG_ERR_INV_OP.
-   
  */
 gcry_error_t
 gcry_cipher_info (gcry_cipher_hd_t h, int cmd, void *buffer, size_t *nbytes)
@@ -1770,11 +2060,11 @@ gcry_cipher_info (gcry_cipher_hd_t h, int cmd, void *buffer, size_t *nbytes)
     GCRYCTL_TEST_ALGO:
        Returns 0 if the specified algorithm ALGO is available for use.
        BUFFER and NBYTES must be zero.
-  
+
    Note: Because this function is in most cases used to return an
    integer value, we can make it easier for the caller to just look at
    the return value.  The caller will in all cases consult the value
-   and thereby detecting whether a error occured or not (i.e. while
+   and thereby detecting whether a error occurred or not (i.e. while
    checking the block size)
  */
 gcry_error_t
@@ -1839,7 +2129,7 @@ gcry_cipher_algo_info (int algo, int what, void *buffer, size_t *nbytes)
    gcry_cipher_algo_info because it allows for proper type
    checking.  */
 size_t
-gcry_cipher_get_algo_keylen (int algo) 
+gcry_cipher_get_algo_keylen (int algo)
 {
   size_t n;
 
@@ -1855,7 +2145,7 @@ gcry_cipher_get_algo_keylen (int algo)
    gcry_cipher_algo_info because it allows for proper type
    checking.  */
 size_t
-gcry_cipher_get_algo_blklen (int algo) 
+gcry_cipher_get_algo_blklen (int algo)
 {
   size_t n;
 
@@ -1916,7 +2206,7 @@ _gcry_cipher_selftest (int algo, int extended, selftest_report_func_t report)
     {
       ec = GPG_ERR_CIPHER_ALGO;
       if (report)
-        report ("cipher", algo, "module", 
+        report ("cipher", algo, "module",
                 module && !(module->flags & FLAG_MODULE_DISABLED)?
                 "no selftest available" :
                 module? "algorithm disabled" : "algorithm not found");