CTR mode may now be used with arbitrary long data chunks.
[libgcrypt.git] / cipher / cipher.c
index 43576fa..90fdb17 100644 (file)
@@ -1,6 +1,6 @@
 /* cipher.c  - cipher dispatcher
  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003
 /* cipher.c  - cipher dispatcher
  * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003
- *               2005, Free Software Foundation, Inc.
+ *               2005, 2007, 2008, 2009 Free Software Foundation, Inc.
  *
  * This file is part of Libgcrypt.
  *
  *
  * This file is part of Libgcrypt.
  *
@@ -15,8 +15,7 @@
  * GNU Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
  * 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
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
  */
 
 #include <config.h>
 #define CTX_MAGIC_NORMAL 0x24091964
 #define CTX_MAGIC_SECURE 0x46919042
 
 #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 (__GNUC__)
+# define NEED_16BYTE_ALIGNED_CONTEXT 1
+#endif
+
+/* A dummy extraspec so that we do not need to tests the extraspec
+   field from the module specification against NULL and instead
+   directly test the respective fields of extraspecs.  */
+static cipher_extra_spec_t dummy_extra_spec;
+
 /* This is the list of the default ciphers, which are included in
    libgcrypt.  */
 static struct cipher_table_entry
 {
   gcry_cipher_spec_t *cipher;
 /* This is the list of the default ciphers, which are included in
    libgcrypt.  */
 static struct cipher_table_entry
 {
   gcry_cipher_spec_t *cipher;
+  cipher_extra_spec_t *extraspec;
   unsigned int algorithm;
   unsigned int algorithm;
+  int fips_allowed;
 } cipher_table[] =
   {
 #if USE_BLOWFISH
 } cipher_table[] =
   {
 #if USE_BLOWFISH
-    { &_gcry_cipher_spec_blowfish,   GCRY_CIPHER_BLOWFISH },
+    { &_gcry_cipher_spec_blowfish,
+      &dummy_extra_spec,                  GCRY_CIPHER_BLOWFISH },
 #endif
 #if USE_DES
 #endif
 #if USE_DES
-    { &_gcry_cipher_spec_des,        GCRY_CIPHER_DES },
-    { &_gcry_cipher_spec_tripledes,  GCRY_CIPHER_3DES },
+    { &_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
 #endif
 #if USE_ARCFOUR
-    { &_gcry_cipher_spec_arcfour,    GCRY_CIPHER_ARCFOUR },
+    { &_gcry_cipher_spec_arcfour,
+      &dummy_extra_spec,                  GCRY_CIPHER_ARCFOUR },
 #endif
 #if USE_CAST5
 #endif
 #if USE_CAST5
-    { &_gcry_cipher_spec_cast5,      GCRY_CIPHER_CAST5 },
+    { &_gcry_cipher_spec_cast5,
+      &dummy_extra_spec,                  GCRY_CIPHER_CAST5 },
 #endif
 #if USE_AES
 #endif
 #if USE_AES
-    { &_gcry_cipher_spec_aes,        GCRY_CIPHER_AES },
-    { &_gcry_cipher_spec_aes192,     GCRY_CIPHER_AES192 },
-    { &_gcry_cipher_spec_aes256,     GCRY_CIPHER_AES256 },
+    { &_gcry_cipher_spec_aes,
+      &_gcry_cipher_extraspec_aes,        GCRY_CIPHER_AES,    1 },
+    { &_gcry_cipher_spec_aes192,
+      &_gcry_cipher_extraspec_aes192,     GCRY_CIPHER_AES192, 1 },
+    { &_gcry_cipher_spec_aes256,
+      &_gcry_cipher_extraspec_aes256,     GCRY_CIPHER_AES256, 1 },
 #endif
 #if USE_TWOFISH
 #endif
 #if USE_TWOFISH
-    { &_gcry_cipher_spec_twofish,    GCRY_CIPHER_TWOFISH },
-    { &_gcry_cipher_spec_twofish128, GCRY_CIPHER_TWOFISH128 },
+    { &_gcry_cipher_spec_twofish,
+      &dummy_extra_spec,                  GCRY_CIPHER_TWOFISH },
+    { &_gcry_cipher_spec_twofish128,
+      &dummy_extra_spec,                  GCRY_CIPHER_TWOFISH128 },
 #endif
 #if USE_SERPENT
 #endif
 #if USE_SERPENT
-    { &_gcry_cipher_spec_serpent128, GCRY_CIPHER_SERPENT128 },
-    { &_gcry_cipher_spec_serpent192, GCRY_CIPHER_SERPENT192 },
-    { &_gcry_cipher_spec_serpent256, GCRY_CIPHER_SERPENT256 },
+    { &_gcry_cipher_spec_serpent128,
+      &dummy_extra_spec,                  GCRY_CIPHER_SERPENT128 },
+    { &_gcry_cipher_spec_serpent192,
+      &dummy_extra_spec,                  GCRY_CIPHER_SERPENT192 },
+    { &_gcry_cipher_spec_serpent256,
+      &dummy_extra_spec,                  GCRY_CIPHER_SERPENT256 },
 #endif
 #endif
-#ifdef USE_RFC2268
-    { &_gcry_cipher_spec_rfc2268_40, GCRY_CIPHER_RFC2268_40 },
+#if USE_RFC2268
+    { &_gcry_cipher_spec_rfc2268_40,
+      &dummy_extra_spec,                  GCRY_CIPHER_RFC2268_40 },
 #endif
 #endif
-#ifdef USE_SEED
-    { &_gcry_cipher_spec_seed,       GCRY_CIPHER_SEED },
+#if USE_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,
+      &dummy_extra_spec,                  GCRY_CIPHER_CAMELLIA192 },
+    { &_gcry_cipher_spec_camellia256,
+      &dummy_extra_spec,                  GCRY_CIPHER_CAMELLIA256 },
 #endif
     { NULL                    }
   };
 #endif
     { NULL                    }
   };
@@ -84,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;
 
 /* 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;
 
    registered.  */
 static int default_ciphers_registered;
 
@@ -95,29 +132,106 @@ static int default_ciphers_registered;
       ath_mutex_lock (&ciphers_registered_lock);   \
       if (! default_ciphers_registered)            \
         {                                          \
       ath_mutex_lock (&ciphers_registered_lock);   \
       if (! default_ciphers_registered)            \
         {                                          \
-          gcry_cipher_register_default ();         \
+          cipher_register_default ();              \
           default_ciphers_registered = 1;          \
         }                                          \
       ath_mutex_unlock (&ciphers_registered_lock); \
     }                                              \
   while (0)
 
           default_ciphers_registered = 1;          \
         }                                          \
       ath_mutex_unlock (&ciphers_registered_lock); \
     }                                              \
   while (0)
 
+
+/* 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
+  char c[1];
+} cipher_context_alignment_t;
+
+
 /* The handle structure.  */
 struct gcry_cipher_handle
 {
   int magic;
   size_t actual_handle_size;     /* Allocated size of this handle. */
 /* The handle structure.  */
 struct gcry_cipher_handle
 {
   int magic;
   size_t actual_handle_size;     /* Allocated size of this handle. */
+  size_t handle_offset;          /* Offset to the malloced block.  */
   gcry_cipher_spec_t *cipher;
   gcry_cipher_spec_t *cipher;
+  cipher_extra_spec_t *extraspec;
   gcry_module_t module;
   gcry_module_t module;
+
+  /* The algorithm id.  This is a hack required because the module
+     interface does not easily allow to retrieve this value. */
+  int algo;
+
+  /* A structure with function pointers for bulk operations.  Due to
+     limitations of the module system (we don't want to change the
+     API) we need to keep these function pointers here.  The cipher
+     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 *outbuf_arg, const void *inbuf_arg,
+                    unsigned int nblocks);
+    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 *outbuf_arg, const void *inbuf_arg,
+                    unsigned int nblocks, int cbc_mac);
+    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;
+
+
   int mode;
   unsigned int flags;
   int mode;
   unsigned int flags;
-  unsigned char iv[MAX_BLOCKSIZE];     /* (this should be ulong aligned) */
+
+  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 {
+    cipher_context_alignment_t iv_align;
+    unsigned char iv[MAX_BLOCKSIZE];
+  } u_iv;
+
+  /* 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];
   unsigned char lastiv[MAX_BLOCKSIZE];
-  int unused;  /* in IV */
-  unsigned char ctr[MAX_BLOCKSIZE];     /* For Counter (CTR) mode. */
-  PROPERLY_ALIGNED_TYPE context;
+  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
+     whereas the second one is a copy created by cipher_setkey and
+     used by cipher_reset.  That second copy has no need for proper
+     aligment because it is only accessed by memcpy.  */
+  cipher_context_alignment_t context;
 };
 
 };
 
+
 \f
 /* These dummy functions are used in case a cipher implementation
    refuses to provide it's own functions.  */
 \f
 /* These dummy functions are used in case a cipher implementation
    refuses to provide it's own functions.  */
@@ -125,7 +239,7 @@ struct gcry_cipher_handle
 static gcry_err_code_t
 dummy_setkey (void *c, const unsigned char *key, unsigned int keylen)
 {
 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;
   (void)key;
   (void)keylen;
   return GPG_ERR_NO_ERROR;
@@ -180,11 +294,11 @@ dummy_decrypt_stream (void *c,
    CIPHER_TABLE.  Note, that this function gets only used by the macro
    REGISTER_DEFAULT_CIPHERS which protects it using a mutex. */
 static void
    CIPHER_TABLE.  Note, that this function gets only used by the macro
    REGISTER_DEFAULT_CIPHERS which protects it using a mutex. */
 static void
-gcry_cipher_register_default (void)
+cipher_register_default (void)
 {
   gcry_err_code_t err = GPG_ERR_NO_ERROR;
   int i;
 {
   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)
   for (i = 0; !err && cipher_table[i].cipher; i++)
     {
       if (! cipher_table[i].cipher->setkey)
@@ -198,9 +312,13 @@ gcry_cipher_register_default (void)
       if (! cipher_table[i].cipher->stdecrypt)
        cipher_table[i].cipher->stdecrypt = dummy_decrypt_stream;
 
       if (! cipher_table[i].cipher->stdecrypt)
        cipher_table[i].cipher->stdecrypt = dummy_decrypt_stream;
 
+      if ( fips_mode () && !cipher_table[i].fips_allowed )
+        continue;
+
       err = _gcry_module_add (&ciphers_registered,
                              cipher_table[i].algorithm,
                              (void *) cipher_table[i].cipher,
       err = _gcry_module_add (&ciphers_registered,
                              cipher_table[i].algorithm,
                              (void *) cipher_table[i].cipher,
+                             (void *) cipher_table[i].extraspec,
                              NULL);
     }
 
                              NULL);
     }
 
@@ -269,16 +387,23 @@ gcry_cipher_lookup_oid (const char *oid)
    CIPHER.  On success, a new algorithm ID is stored in ALGORITHM_ID
    and a pointer representhing this module is stored in MODULE.  */
 gcry_error_t
    CIPHER.  On success, a new algorithm ID is stored in ALGORITHM_ID
    and a pointer representhing this module is stored in MODULE.  */
 gcry_error_t
-gcry_cipher_register (gcry_cipher_spec_t *cipher,
-                     int *algorithm_id,
-                     gcry_module_t *module)
+_gcry_cipher_register (gcry_cipher_spec_t *cipher,
+                       cipher_extra_spec_t *extraspec,
+                       int *algorithm_id,
+                       gcry_module_t *module)
 {
   gcry_err_code_t err = 0;
   gcry_module_t mod;
 
 {
   gcry_err_code_t err = 0;
   gcry_module_t mod;
 
+  /* We do not support module loading in fips mode.  */
+  if (fips_mode ())
+    return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
   ath_mutex_lock (&ciphers_registered_lock);
   err = _gcry_module_add (&ciphers_registered, 0,
   ath_mutex_lock (&ciphers_registered_lock);
   err = _gcry_module_add (&ciphers_registered, 0,
-                         (void *) cipher, &mod);
+                         (void *)cipher,
+                         (void *)(extraspec? extraspec : &dummy_extra_spec),
+                          &mod);
   ath_mutex_unlock (&ciphers_registered_lock);
 
   if (! err)
   ath_mutex_unlock (&ciphers_registered_lock);
 
   if (! err)
@@ -307,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.*/
    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;
 search_oid (const char *oid, int *algorithm, gcry_cipher_oid_spec_t *oid_spec)
 {
   gcry_module_t module;
@@ -371,7 +496,7 @@ gcry_cipher_map_name (const char *string)
     }
 
   ath_mutex_unlock (&ciphers_registered_lock);
     }
 
   ath_mutex_unlock (&ciphers_registered_lock);
-  
+
   return algorithm;
 }
 
   return algorithm;
 }
 
@@ -458,8 +583,9 @@ disable_cipher_algo (int algorithm)
 }
 
 
 }
 
 
-/* Return 0 if the cipher algorithm with indentifier ALGORITHM is
-   available. Returns a basic error code value if it is not available.  */
+/* Return 0 if the cipher algorithm with identifier ALGORITHM is
+   available. Returns a basic error code value if it is not
+   available.  */
 static gcry_err_code_t
 check_cipher_algo (int algorithm)
 {
 static gcry_err_code_t
 check_cipher_algo (int algorithm)
 {
@@ -479,7 +605,7 @@ check_cipher_algo (int algorithm)
   else
     err = GPG_ERR_CIPHER_ALGO;
   ath_mutex_unlock (&ciphers_registered_lock);
   else
     err = GPG_ERR_CIPHER_ALGO;
   ath_mutex_unlock (&ciphers_registered_lock);
-  
+
   return err;
 }
 
   return err;
 }
 
@@ -501,7 +627,7 @@ cipher_get_keylen (int algorithm)
   if (cipher)
     {
       len = ((gcry_cipher_spec_t *) cipher->spec)->keylen;
   if (cipher)
     {
       len = ((gcry_cipher_spec_t *) cipher->spec)->keylen;
-      if (! len)
+      if (!len)
        log_bug ("cipher %d w/o key length\n", algorithm);
       _gcry_module_release (cipher);
     }
        log_bug ("cipher %d w/o key length\n", algorithm);
       _gcry_module_release (cipher);
     }
@@ -561,6 +687,7 @@ gcry_cipher_open (gcry_cipher_hd_t *handle,
 {
   int secure = (flags & GCRY_CIPHER_SECURE);
   gcry_cipher_spec_t *cipher = NULL;
 {
   int secure = (flags & GCRY_CIPHER_SECURE);
   gcry_cipher_spec_t *cipher = NULL;
+  cipher_extra_spec_t *extraspec = NULL;
   gcry_module_t module = NULL;
   gcry_cipher_hd_t h = NULL;
   gcry_err_code_t err = 0;
   gcry_module_t module = NULL;
   gcry_cipher_hd_t h = NULL;
   gcry_err_code_t err = 0;
@@ -568,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 ();
   /* 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;
 
   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);
      available for use.  */
   ath_mutex_lock (&ciphers_registered_lock);
   module = _gcry_module_lookup_id (ciphers_registered, algo);
@@ -583,10 +710,12 @@ gcry_cipher_open (gcry_cipher_hd_t *handle,
        {
          /* Not available for use.  */
          err = GPG_ERR_CIPHER_ALGO;
        {
          /* Not available for use.  */
          err = GPG_ERR_CIPHER_ALGO;
-         _gcry_module_release (module);
        }
       else
        }
       else
-       cipher = (gcry_cipher_spec_t *) module->spec;
+        {
+          cipher = (gcry_cipher_spec_t *) module->spec;
+          extraspec = module->extraspec;
+        }
     }
   else
     err = GPG_ERR_CIPHER_ALGO;
     }
   else
     err = GPG_ERR_CIPHER_ALGO;
@@ -594,7 +723,7 @@ gcry_cipher_open (gcry_cipher_hd_t *handle,
 
   /* check flags */
   if ((! err)
 
   /* check flags */
   if ((! err)
-      && ((flags & ~(0 
+      && ((flags & ~(0
                     | GCRY_CIPHER_SECURE
                     | GCRY_CIPHER_ENABLE_SYNC
                     | GCRY_CIPHER_CBC_CTS
                     | GCRY_CIPHER_SECURE
                     | GCRY_CIPHER_ENABLE_SYNC
                     | GCRY_CIPHER_CBC_CTS
@@ -611,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_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;
        if ((cipher->encrypt == dummy_encrypt_block)
            || (cipher->decrypt == dummy_decrypt_block))
          err = GPG_ERR_INV_CIPHER_MODE;
@@ -623,21 +753,32 @@ gcry_cipher_open (gcry_cipher_hd_t *handle,
        break;
 
       case GCRY_CIPHER_MODE_NONE:
        break;
 
       case GCRY_CIPHER_MODE_NONE:
-       /* FIXME: issue a warning when this mode is used */
+        /* This mode may be used for debugging.  It copies the main
+           text verbatim to the ciphertext.  We do not allow this in
+           fips mode or if no debug flag has been set.  */
+       if (fips_mode () || !_gcry_get_debug_flag (0))
+          err = GPG_ERR_INV_CIPHER_MODE;
        break;
 
       default:
        err = GPG_ERR_INV_CIPHER_MODE;
       }
 
        break;
 
       default:
        err = GPG_ERR_INV_CIPHER_MODE;
       }
 
-  /* ? FIXME: perform selftest here and mark this with a flag in
-     cipher_table ? */
+  /* Perform selftest here and mark this with a flag in cipher_table?
+     No, we should not do this as it takes too long.  Further it does
+     not make sense to exclude algorithms with failing selftests at
+     runtime: If a selftest fails there is something seriously wrong
+     with the system and thus we better die immediately. */
 
   if (! err)
     {
       size_t size = (sizeof (*h)
                      + 2 * cipher->contextsize
 
   if (! err)
     {
       size_t size = (sizeof (*h)
                      + 2 * cipher->contextsize
-                     - sizeof (PROPERLY_ALIGNED_TYPE));
+                     - sizeof (cipher_context_alignment_t)
+#ifdef NEED_16BYTE_ALIGNED_CONTEXT
+                     + 15  /* Space for leading alignment gap.  */
+#endif /*NEED_16BYTE_ALIGNED_CONTEXT*/
+                     );
 
       if (secure)
        h = gcry_calloc_secure (1, size);
 
       if (secure)
        h = gcry_calloc_secure (1, size);
@@ -648,12 +789,46 @@ gcry_cipher_open (gcry_cipher_hd_t *handle,
        err = gpg_err_code_from_errno (errno);
       else
        {
        err = gpg_err_code_from_errno (errno);
       else
        {
+          size_t off = 0;
+
+#ifdef NEED_16BYTE_ALIGNED_CONTEXT
+          if ( ((unsigned long)h & 0x0f) )
+            {
+              /* The malloced block is not aligned on a 16 byte
+                 boundary.  Correct for this.  */
+              off = 16 - ((unsigned long)h & 0x0f);
+              h = (void*)((char*)h + off);
+            }
+#endif /*NEED_16BYTE_ALIGNED_CONTEXT*/
+
          h->magic = secure ? CTX_MAGIC_SECURE : CTX_MAGIC_NORMAL;
          h->magic = secure ? CTX_MAGIC_SECURE : CTX_MAGIC_NORMAL;
-          h->actual_handle_size = size;
+          h->actual_handle_size = size - off;
+          h->handle_offset = off;
          h->cipher = cipher;
          h->cipher = cipher;
+         h->extraspec = extraspec;
          h->module = module;
          h->module = module;
+          h->algo = algo;
          h->mode = mode;
          h->flags = flags;
          h->mode = mode;
          h->flags = flags;
+
+          /* Setup bulk encryption routines.  */
+          switch (algo)
+            {
+#ifdef USE_AES
+            case GCRY_CIPHER_AES128:
+            case GCRY_CIPHER_AES192:
+            case GCRY_CIPHER_AES256:
+              h->bulk.cfb_enc = _gcry_aes_cfb_enc;
+              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;
+            }
        }
     }
 
        }
     }
 
@@ -681,7 +856,9 @@ gcry_cipher_open (gcry_cipher_hd_t *handle,
 void
 gcry_cipher_close (gcry_cipher_hd_t h)
 {
 void
 gcry_cipher_close (gcry_cipher_hd_t h)
 {
-  if (! h)
+  size_t off;
+
+  if (!h)
     return;
 
   if ((h->magic != CTX_MAGIC_SECURE)
     return;
 
   if ((h->magic != CTX_MAGIC_SECURE)
@@ -702,25 +879,31 @@ gcry_cipher_close (gcry_cipher_hd_t h)
      do the wiping.  To accomplish this we need to keep track of the
      actual size of this structure because we have no way to known
      how large the allocated area was when using a standard malloc. */
      do the wiping.  To accomplish this we need to keep track of the
      actual size of this structure because we have no way to known
      how large the allocated area was when using a standard malloc. */
+  off = h->handle_offset;
   wipememory (h, h->actual_handle_size);
 
   wipememory (h, h->actual_handle_size);
 
-  gcry_free (h);
+  gcry_free ((char*)h - off);
 }
 
 
 /* Set the key to be used for the encryption context C to KEY with
    length KEYLEN.  The length should match the required length. */
 static gcry_error_t
 }
 
 
 /* Set the key to be used for the encryption context C to KEY with
    length KEYLEN.  The length should match the required length. */
 static gcry_error_t
-cipher_setkey (gcry_cipher_hd_t c, byte *key, unsigned keylen)
+cipher_setkey (gcry_cipher_hd_t c, byte *key, unsigned int keylen)
 {
   gcry_err_code_t ret;
 
   ret = (*c->cipher->setkey) (&c->context.c, key, keylen);
 {
   gcry_err_code_t ret;
 
   ret = (*c->cipher->setkey) (&c->context.c, key, keylen);
-  if (! ret)
-    /* Duplicate initial context.  */
-    memcpy ((void *) ((char *) &c->context.c + c->cipher->contextsize),
-           (void *) &c->context.c,
-           c->cipher->contextsize);
+  if (!ret)
+    {
+      /* Duplicate initial context.  */
+      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);
 }
 
   return gcry_error (ret);
 }
@@ -731,20 +914,27 @@ cipher_setkey (gcry_cipher_hd_t c, byte *key, unsigned keylen)
 static void
 cipher_setiv( gcry_cipher_hd_t c, const byte *iv, unsigned ivlen )
 {
 static void
 cipher_setiv( gcry_cipher_hd_t c, const byte *iv, unsigned ivlen )
 {
-    memset( c->iv, 0, c->cipher->blocksize );
-    if( iv ) {
-       if( ivlen != c->cipher->blocksize )
-           log_info("WARNING: cipher_setiv: ivlen=%u blklen=%u\n",
-                    ivlen, (unsigned) c->cipher->blocksize );
-       if (ivlen > c->cipher->blocksize)
-         ivlen = c->cipher->blocksize;
-       memcpy( c->iv, iv, ivlen );
+  memset (c->u_iv.iv, 0, c->cipher->blocksize);
+  if (iv)
+    {
+      if (ivlen != c->cipher->blocksize)
+        {
+          log_info ("WARNING: cipher_setiv: ivlen=%u blklen=%u\n",
+                    ivlen, (unsigned int)c->cipher->blocksize);
+          fips_signal_error ("IV length does not match blocklength");
+        }
+      if (ivlen > c->cipher->blocksize)
+        ivlen = c->cipher->blocksize;
+      memcpy (c->u_iv.iv, iv, ivlen);
+      c->marks.iv = 1;
     }
     }
-    c->unused = 0;
+  else
+      c->marks.iv = 0;
+  c->unused = 0;
 }
 
 
 }
 
 
-/* Reset the cipher context to the initial contex.  This is basically
+/* Reset the cipher context to the initial context.  This is basically
    the same as an release followed by a new. */
 static void
 cipher_reset (gcry_cipher_hd_t c)
    the same as an release followed by a new. */
 static void
 cipher_reset (gcry_cipher_hd_t c)
@@ -752,434 +942,821 @@ cipher_reset (gcry_cipher_hd_t c)
   memcpy (&c->context.c,
          (char *) &c->context.c + c->cipher->contextsize,
          c->cipher->contextsize);
   memcpy (&c->context.c,
          (char *) &c->context.c + c->cipher->contextsize,
          c->cipher->contextsize);
-  memset (c->iv, 0, c->cipher->blocksize);
+  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->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;
+  for (n=0; n < nblocks; n++ )
+    {
+      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 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->decrypt ( &c->context.c, outbuf, (byte*)/*arggg*/inbuf );
-       inbuf  += c->cipher->blocksize;
-       outbuf += c->cipher->blocksize;
+  for (n=0; n < nblocks; n++ )
+    {
+      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, byte *outbuf, const byte *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;
-    byte *ivp;
-    int i;
-    size_t blocksize = c->cipher->blocksize;
-    unsigned nblocks = nbytes / blocksize;
-
-    if ((c->flags & GCRY_CIPHER_CBC_CTS) && nbytes > blocksize) {
-      if ((nbytes % blocksize) == 0)
+  unsigned int n;
+  unsigned char *ivp;
+  int i;
+  size_t blocksize = c->cipher->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) && inbuflen > blocksize)
+    {
+      if ((inbuflen % blocksize) == 0)
        nblocks--;
     }
 
        nblocks--;
     }
 
-    for(n=0; n < nblocks; n++ ) {
-       /* fixme: the xor should work on words and not on
-        * bytes.  Maybe it is a good idea to enhance the cipher backend
-        * API to allow for CBC handling direct in the backend */
-       for(ivp=c->iv,i=0; i < blocksize; i++ )
-           outbuf[i] = inbuf[i] ^ *ivp++;
-       c->cipher->encrypt ( &c->context.c, outbuf, outbuf );
-       memcpy(c->iv, outbuf, blocksize );
-       inbuf  += blocksize;
-       if (!(c->flags & GCRY_CIPHER_CBC_MAC))
-         outbuf += blocksize;
+  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));
+      inbuf  += nblocks * blocksize;
+      if (!(c->flags & GCRY_CIPHER_CBC_MAC))
+        outbuf += nblocks * blocksize;
+    }
+  else
+    {
+      for (n=0; n < nblocks; n++ )
+        {
+          for (ivp=c->u_iv.iv,i=0; i < blocksize; i++ )
+            outbuf[i] = inbuf[i] ^ *ivp++;
+          c->cipher->encrypt ( &c->context.c, outbuf, outbuf );
+          memcpy (c->u_iv.iv, outbuf, blocksize );
+          inbuf  += blocksize;
+          if (!(c->flags & GCRY_CIPHER_CBC_MAC))
+            outbuf += blocksize;
+        }
     }
 
     }
 
-    if ((c->flags & GCRY_CIPHER_CBC_CTS) && nbytes > blocksize)
-      {
-       /* We have to be careful here, since outbuf might be equal to
-          inbuf.  */
-
-       int restbytes;
-       byte b;
-
-       if ((nbytes % blocksize) == 0)
-         restbytes = blocksize;
-       else
-         restbytes = 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;
 
 
-       outbuf -= blocksize;
-       for (ivp = c->iv, i = 0; i < restbytes; i++)
-         {
-           b = inbuf[i];
-           outbuf[blocksize + i] = outbuf[i];
-           outbuf[i] = b ^ *ivp++;
-         }
-       for (; i < blocksize; i++)
-         outbuf[i] = 0 ^ *ivp++;
+      if ((inbuflen % blocksize) == 0)
+        restbytes = blocksize;
+      else
+        restbytes = inbuflen % blocksize;
+
+      outbuf -= blocksize;
+      for (ivp = c->u_iv.iv, i = 0; i < restbytes; i++)
+        {
+          b = inbuf[i];
+          outbuf[blocksize + i] = outbuf[i];
+          outbuf[i] = b ^ *ivp++;
+        }
+      for (; i < blocksize; i++)
+        outbuf[i] = 0 ^ *ivp++;
+
+      c->cipher->encrypt (&c->context.c, outbuf, outbuf);
+      memcpy (c->u_iv.iv, outbuf, blocksize);
+    }
 
 
-       c->cipher->encrypt (&c->context.c, outbuf, outbuf);
-       memcpy (c->iv, outbuf, blocksize);
-      }
+  return 0;
 }
 
 }
 
-static void
-do_cbc_decrypt( gcry_cipher_hd_t c, byte *outbuf, const byte *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;
-    byte *ivp;
-    int i;
-    size_t blocksize = c->cipher->blocksize;
-    unsigned int nblocks = nbytes / blocksize;
+  unsigned int n;
+  unsigned char *ivp;
+  int i;
+  size_t blocksize = c->cipher->blocksize;
+  unsigned int nblocks = inbuflen / blocksize;
+
+  if (outbuflen < inbuflen)
+    return GPG_ERR_BUFFER_TOO_SHORT;
 
 
-    if ((c->flags & GCRY_CIPHER_CBC_CTS) && nbytes > blocksize) {
+  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) && inbuflen > blocksize)
+    {
       nblocks--;
       nblocks--;
-      if ((nbytes % blocksize) == 0)
+      if ((inbuflen % blocksize) == 0)
        nblocks--;
        nblocks--;
-      memcpy(c->lastiv, c->iv, blocksize );
+      memcpy (c->lastiv, c->u_iv.iv, blocksize);
     }
 
     }
 
-    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 this here because it is not used otherwise. */
-       memcpy(c->lastiv, inbuf, blocksize );
-       c->cipher->decrypt ( &c->context.c, outbuf, inbuf );
-       for(ivp=c->iv,i=0; i < blocksize; i++ )
+  if (c->bulk.cbc_dec)
+    {
+      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++ )
+        {
+          /* Because outbuf and inbuf might be the same, we have to
+           * save the original ciphertext block.  We use LASTIV for
+           * this here because it is not used otherwise. */
+          memcpy (c->lastiv, inbuf, blocksize);
+          c->cipher->decrypt ( &c->context.c, outbuf, inbuf );
+          for (ivp=c->u_iv.iv,i=0; i < blocksize; i++ )
            outbuf[i] ^= *ivp++;
            outbuf[i] ^= *ivp++;
-       memcpy(c->iv, c->lastiv, blocksize );
-       inbuf  += c->cipher->blocksize;
-       outbuf += c->cipher->blocksize;
+          memcpy(c->u_iv.iv, c->lastiv, blocksize );
+          inbuf  += c->cipher->blocksize;
+          outbuf += c->cipher->blocksize;
+        }
     }
 
     }
 
-    if ((c->flags & GCRY_CIPHER_CBC_CTS) && nbytes > blocksize) {
-       int restbytes;
-
-       if ((nbytes % blocksize) == 0)
-         restbytes = blocksize;
-       else
-         restbytes = nbytes % blocksize;
-
-       memcpy(c->lastiv, c->iv, blocksize ); /* save Cn-2 */
-       memcpy(c->iv, inbuf + blocksize, restbytes ); /* save Cn */
-
-       c->cipher->decrypt ( &c->context.c, outbuf, inbuf );
-       for(ivp=c->iv,i=0; i < restbytes; i++ )
-           outbuf[i] ^= *ivp++;
+  if ((c->flags & GCRY_CIPHER_CBC_CTS) && inbuflen > blocksize)
+    {
+      int restbytes;
 
 
-       memcpy(outbuf + blocksize, outbuf, restbytes);
-       for(i=restbytes; i < blocksize; i++)
-         c->iv[i] = outbuf[i];
-       c->cipher->decrypt ( &c->context.c, outbuf, c->iv );
-       for(ivp=c->lastiv,i=0; i < blocksize; i++ )
-           outbuf[i] ^= *ivp++;
-       /* c->lastiv is now really lastlastiv, does this matter? */
+      if ((inbuflen % blocksize) == 0)
+        restbytes = blocksize;
+      else
+        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];
+      c->cipher->decrypt (&c->context.c, outbuf, c->u_iv.iv);
+      for(ivp=c->lastiv,i=0; i < blocksize; i++ )
+        outbuf[i] ^= *ivp++;
+      /* c->lastiv is now really lastlastiv, does this matter? */
     }
     }
+
+  return 0;
 }
 
 
 }
 
 
-static void
-do_cfb_encrypt( gcry_cipher_hd_t c,
-                byte *outbuf, const byte *inbuf, unsigned 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)
 {
 {
-    byte *ivp;
-    size_t blocksize = c->cipher->blocksize;
-
-    if( nbytes <= 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->iv+c->cipher->blocksize - c->unused;
-             nbytes;
-             nbytes--, c->unused-- )
-          *outbuf++ = (*ivp++ ^= *inbuf++);
-       return;
+  unsigned char *ivp;
+  size_t blocksize = c->cipher->blocksize;
+  size_t blocksize_x_2 = blocksize + blocksize;
+
+  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;
+           inbuflen;
+           inbuflen--, c->unused-- )
+        *outbuf++ = (*ivp++ ^= *inbuf++);
+      return 0;
+    }
+
+  if ( c->unused )
+    {
+      /* XOR the input with the IV and store input into IV */
+      inbuflen -= c->unused;
+      for(ivp=c->u_iv.iv+blocksize - c->unused; c->unused; c->unused-- )
+        *outbuf++ = (*ivp++ ^= *inbuf++);
     }
 
     }
 
-    if( c->unused ) {
-       /* XOR the input with the IV and store input into IV */
-       nbytes -= c->unused;
-       for(ivp=c->iv+blocksize - c->unused; c->unused; c->unused-- )
-           *outbuf++ = (*ivp++ ^= *inbuf++);
+  /* 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 (inbuflen >= blocksize_x_2 && c->bulk.cfb_enc)
+    {
+      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;
+      inbuflen -= nblocks * blocksize;
+    }
+  else
+    {
+      while ( inbuflen >= blocksize_x_2 )
+        {
+          int i;
+          /* Encrypt the IV. */
+          c->cipher->encrypt ( &c->context.c, c->u_iv.iv, c->u_iv.iv );
+          /* 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++);
+          inbuflen -= blocksize;
+        }
     }
 
     }
 
-    /* Now we can process complete blocks. */
-    while( nbytes >= blocksize ) {
-       int i;
-       /* Encrypt the IV (and save the current one). */
-       memcpy( c->lastiv, c->iv, blocksize );
-       c->cipher->encrypt ( &c->context.c, c->iv, c->iv );
-       /* XOR the input with the IV and store input into IV */
-       for(ivp=c->iv,i=0; i < blocksize; i++ )
-           *outbuf++ = (*ivp++ ^= *inbuf++);
-       nbytes -= blocksize;
+  if ( inbuflen >= blocksize )
+    {
+      int i;
+      /* 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 );
+      /* 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++);
+      inbuflen -= blocksize;
     }
     }
-    if( nbytes ) { /* process the remaining bytes */
-       /* encrypt the IV (and save the current one) */
-       memcpy( c->lastiv, c->iv, blocksize );
-       c->cipher->encrypt ( &c->context.c, c->iv, c->iv );
-       c->unused = blocksize;
-       /* and apply the xor */
-       c->unused -= nbytes;
-       for(ivp=c->iv; nbytes; nbytes-- )
-           *outbuf++ = (*ivp++ ^= *inbuf++);
+  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 -= inbuflen;
+      for(ivp=c->u_iv.iv; inbuflen; inbuflen-- )
+        *outbuf++ = (*ivp++ ^= *inbuf++);
     }
     }
+  return 0;
 }
 
 }
 
-static void
-do_cfb_decrypt( gcry_cipher_hd_t c,
-                byte *outbuf, const byte *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)
 {
 {
-    byte *ivp;
-    ulong temp;
-    size_t blocksize = c->cipher->blocksize;
-
-    if( nbytes <= 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->iv+blocksize - c->unused; nbytes; nbytes--,c->unused--) {
-           temp = *inbuf++;
-           *outbuf++ = *ivp ^ temp;
-           *ivp++ = temp;
-       }
-       return;
+  unsigned char *ivp;
+  unsigned long temp;
+  int i;
+  size_t blocksize = c->cipher->blocksize;
+  size_t blocksize_x_2 = blocksize + blocksize;
+
+  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;
+           inbuflen;
+           inbuflen--, c->unused--)
+        {
+          temp = *inbuf++;
+          *outbuf++ = *ivp ^ temp;
+          *ivp++ = temp;
+        }
+      return 0;
     }
 
     }
 
-    if( c->unused ) {
-       /* XOR the input with the IV and store input into IV. */
-       nbytes -= c->unused;
-       for(ivp=c->iv+blocksize - c->unused; c->unused; c->unused-- ) {
-           temp = *inbuf++;
-           *outbuf++ = *ivp ^ temp;
-           *ivp++ = temp;
-       }
+  if (c->unused)
+    {
+      /* XOR the input with the IV and store input into IV. */
+      inbuflen -= c->unused;
+      for (ivp=c->u_iv.iv+blocksize - c->unused; c->unused; c->unused-- )
+        {
+          temp = *inbuf++;
+          *outbuf++ = *ivp ^ temp;
+          *ivp++ = temp;
+        }
     }
 
     }
 
-    /* now we can process complete blocks */
-    while( nbytes >= blocksize ) {
-       int i;
-       /* encrypt the IV (and save the current one) */
-       memcpy( c->lastiv, c->iv, blocksize );
-       c->cipher->encrypt ( &c->context.c, c->iv, c->iv );
-       /* XOR the input with the IV and store input into IV */
-       for(ivp=c->iv,i=0; i < blocksize; i++ ) {
-           temp = *inbuf++;
-           *outbuf++ = *ivp ^ temp;
-           *ivp++ = temp;
-       }
-       nbytes -= blocksize;
+  /* 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 (inbuflen >= blocksize_x_2 && c->bulk.cfb_dec)
+    {
+      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;
+      inbuflen -= nblocks * blocksize;
     }
     }
-    if( nbytes ) { /* process the remaining bytes */
-       /* encrypt the IV (and save the current one) */
-       memcpy( c->lastiv, c->iv, blocksize );
-       c->cipher->encrypt ( &c->context.c, c->iv, c->iv );
-       c->unused = blocksize;
-       /* and apply the xor */
-       c->unused -= nbytes;
-       for(ivp=c->iv; nbytes; nbytes-- ) {
-           temp = *inbuf++;
-           *outbuf++ = *ivp ^ temp;
-           *ivp++ = temp;
-       }
+  else
+    {
+      while (inbuflen >= blocksize_x_2 )
+        {
+          /* Encrypt the IV. */
+          c->cipher->encrypt ( &c->context.c, c->u_iv.iv, c->u_iv.iv );
+          /* XOR the input with the IV and store input into IV. */
+          for (ivp=c->u_iv.iv,i=0; i < blocksize; i++ )
+            {
+              temp = *inbuf++;
+              *outbuf++ = *ivp ^ temp;
+              *ivp++ = temp;
+            }
+          inbuflen -= blocksize;
+        }
+    }
+
+  if (inbuflen >= blocksize )
+    {
+      /* 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 );
+      /* XOR the input with the IV and store input into IV */
+      for (ivp=c->u_iv.iv,i=0; i < blocksize; i++ )
+        {
+          temp = *inbuf++;
+          *outbuf++ = *ivp ^ temp;
+          *ivp++ = temp;
+        }
+      inbuflen -= blocksize;
+    }
+
+  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 -= 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;
 
   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 */
     {
       /* Short enough to be encoded by the remaining XOR mask. */
       /* XOR the input with the IV */
-      for (ivp=c->iv+c->cipher->blocksize - c->unused;
-           nbytes;
-           nbytes--, c->unused-- )
+      for (ivp=c->u_iv.iv+c->cipher->blocksize - c->unused;
+           inbuflen;
+           inbuflen--, c->unused-- )
         *outbuf++ = (*ivp++ ^ *inbuf++);
         *outbuf++ = (*ivp++ ^ *inbuf++);
-      return;
+      return 0;
     }
 
   if( c->unused )
     {
     }
 
   if( c->unused )
     {
-      nbytes -= c->unused;
-      for(ivp=c->iv+blocksize - c->unused; c->unused; 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. */
         *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). */
     {
       int i;
       /* Encrypt the IV (and save the current one). */
-      memcpy( c->lastiv, c->iv, blocksize );
-      c->cipher->encrypt ( &c->context.c, c->iv, c->iv );
-      
-      for (ivp=c->iv,i=0; i < blocksize; i++ )
+      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++);
         *outbuf++ = (*ivp++ ^ *inbuf++);
-      nbytes -= blocksize;
+      inbuflen -= blocksize;
     }
     }
-  if ( nbytes )
+  if ( inbuflen )
     { /* process the remaining bytes */
     { /* process the remaining bytes */
-      memcpy( c->lastiv, c->iv, blocksize );
-      c->cipher->encrypt ( &c->context.c, c->iv, c->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;
       c->unused = blocksize;
-      c->unused -= nbytes;
-      for(ivp=c->iv; nbytes; nbytes-- )
+      c->unused -= inbuflen;
+      for(ivp=c->u_iv.iv; inbuflen; inbuflen-- )
         *outbuf++ = (*ivp++ ^ *inbuf++);
     }
         *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;
   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. */
     {
       /* Short enough to be encoded by the remaining XOR mask. */
-      for (ivp=c->iv+blocksize - c->unused; nbytes; nbytes--,c->unused--)
+      for (ivp=c->u_iv.iv+blocksize - c->unused; inbuflen; inbuflen--,c->unused--)
         *outbuf++ = *ivp++ ^ *inbuf++;
         *outbuf++ = *ivp++ ^ *inbuf++;
-      return;
+      return 0;
     }
 
   if ( c->unused )
     {
     }
 
   if ( c->unused )
     {
-      nbytes -= c->unused;
-      for (ivp=c->iv+blocksize - c->unused; c->unused; 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. */
         *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). */
     {
       int i;
       /* Encrypt the IV (and save the current one). */
-      memcpy( c->lastiv, c->iv, blocksize );
-      c->cipher->encrypt ( &c->context.c, c->iv, c->iv );
-      for (ivp=c->iv,i=0; i < blocksize; i++ )
+      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++;
         *outbuf++ = *ivp++ ^ *inbuf++;
-      nbytes -= blocksize;
+      inbuflen -= blocksize;
     }
     }
-  if ( nbytes ) 
+  if ( inbuflen )
     { /* Process the remaining bytes. */
       /* Encrypt the IV (and save the current one). */
     { /* Process the remaining bytes. */
       /* Encrypt the IV (and save the current one). */
-      memcpy( c->lastiv, c->iv, blocksize );
-      c->cipher->encrypt ( &c->context.c, c->iv, c->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;
       c->unused = blocksize;
-      c->unused -= nbytes;
-      for (ivp=c->iv; nbytes; nbytes-- )
+      c->unused -= inbuflen;
+      for (ivp=c->u_iv.iv; inbuflen; inbuflen-- )
         *outbuf++ = *ivp++ ^ *inbuf++;
     }
         *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;
 {
   unsigned int n;
-  byte tmp[MAX_BLOCKSIZE];
   int i;
   int i;
+  unsigned int blocksize = c->cipher->blocksize;
+  unsigned int nblocks;
 
 
-  for(n=0; n < nbytes; n++)
+  if (outbuflen < inbuflen)
+    return GPG_ERR_BUFFER_TOO_SHORT;
+
+  /* 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;
+    }
+
+  /* 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);
+}
 
 
-         for (i = c->cipher->blocksize; i > 0; i--)
+
+/* 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;
            }
                break;
            }
-       }
-
-      /* XOR input with encrypted counter and store in output. */
-      outbuf[n] = inbuf[n] ^ tmp[n % c->cipher->blocksize];
-    }
+          /* 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;
 }
 
 }
 
-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.
 }
 
 
 /****************
  * 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
  */
 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( 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;
 }
 
 
 }
 
 
@@ -1193,27 +1770,15 @@ gcry_cipher_encrypt (gcry_cipher_hd_t h, void *out, size_t outsize,
 {
   gcry_err_code_t err;
 
 {
   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
   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)
   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);
 }
 
   return gcry_error (err);
 }
@@ -1223,52 +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.
 /****************
  * 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
  */
 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( 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;
 }
 
 
 }
 
 
@@ -1276,23 +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_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
   else
-    err = cipher_decrypt (h, out, in, inlen);
+    err = cipher_decrypt (h, out, outsize, in, inlen);
 
   return gcry_error (err);
 }
 
   return gcry_error (err);
 }
@@ -1304,13 +1873,52 @@ gcry_cipher_decrypt (gcry_cipher_hd_t h, void *out, size_t outsize,
  * the corresponding flag is set.
  */
 static void
  * the corresponding flag is set.
  */
 static void
-cipher_sync( gcry_cipher_hd_t c )
+cipher_sync (gcry_cipher_hd_t c)
+{
+  if ((c->flags & GCRY_CIPHER_ENABLE_SYNC) && c->unused)
+    {
+      memmove (c->u_iv.iv + c->unused,
+               c->u_iv.iv, c->cipher->blocksize - c->unused);
+      memcpy (c->u_iv.iv,
+              c->lastiv + c->cipher->blocksize - c->unused, c->unused);
+      c->unused = 0;
+    }
+}
+
+
+gcry_error_t
+_gcry_cipher_setkey (gcry_cipher_hd_t hd, const void *key, size_t keylen)
+{
+  return cipher_setkey (hd, (void*)key, keylen);
+}
+
+
+gcry_error_t
+_gcry_cipher_setiv (gcry_cipher_hd_t hd, const void *iv, size_t ivlen)
+{
+  cipher_setiv (hd, iv, ivlen);
+  return 0;
+}
+
+/* Set counter for CTR mode.  (CTR,CTRLEN) must denote a buffer of
+   block size length, or (NULL,0) to set the CTR to the all-zero
+   block. */
+gpg_error_t
+_gcry_cipher_setctr (gcry_cipher_hd_t hd, const void *ctr, size_t ctrlen)
 {
 {
-    if( (c->flags & GCRY_CIPHER_ENABLE_SYNC) && c->unused ) {
-       memmove(c->iv + c->unused, c->iv, c->cipher->blocksize - c->unused );
-       memcpy(c->iv, c->lastiv + c->cipher->blocksize - c->unused, c->unused);
-       c->unused = 0;
+  if (ctr && ctrlen == hd->cipher->blocksize)
+    {
+      memcpy (hd->u_ctr.ctr, ctr, hd->cipher->blocksize);
+      hd->unused = 0;
     }
     }
+  else if (!ctr || !ctrlen)
+    {
+      memset (hd->u_ctr.ctr, 0, hd->cipher->blocksize);
+      hd->unused = 0;
+    }
+  else
+    return gpg_error (GPG_ERR_INV_ARG);
+  return 0;
 }
 
 
 }
 
 
@@ -1321,18 +1929,22 @@ gcry_cipher_ctl( gcry_cipher_hd_t h, int cmd, void *buffer, size_t buflen)
 
   switch (cmd)
     {
 
   switch (cmd)
     {
-    case GCRYCTL_SET_KEY:
+    case GCRYCTL_SET_KEY:  /* Deprecated; use gcry_cipher_setkey.  */
       rc = cipher_setkey( h, buffer, buflen );
       break;
       rc = cipher_setkey( h, buffer, buflen );
       break;
-    case GCRYCTL_SET_IV:
+
+    case GCRYCTL_SET_IV:   /* Deprecated; use gcry_cipher_setiv.  */
       cipher_setiv( h, buffer, buflen );
       break;
       cipher_setiv( h, buffer, buflen );
       break;
+
     case GCRYCTL_RESET:
       cipher_reset (h);
       break;
     case GCRYCTL_RESET:
       cipher_reset (h);
       break;
+
     case GCRYCTL_CFB_SYNC:
       cipher_sync( h );
       break;
     case GCRYCTL_CFB_SYNC:
       cipher_sync( h );
       break;
+
     case GCRYCTL_SET_CBC_CTS:
       if (buflen)
        if (h->flags & GCRY_CIPHER_CBC_MAC)
     case GCRYCTL_SET_CBC_CTS:
       if (buflen)
        if (h->flags & GCRY_CIPHER_CBC_MAC)
@@ -1342,6 +1954,7 @@ gcry_cipher_ctl( gcry_cipher_hd_t h, int cmd, void *buffer, size_t buflen)
       else
        h->flags &= ~GCRY_CIPHER_CBC_CTS;
       break;
       else
        h->flags &= ~GCRY_CIPHER_CBC_CTS;
       break;
+
     case GCRYCTL_SET_CBC_MAC:
       if (buflen)
        if (h->flags & GCRY_CIPHER_CBC_CTS)
     case GCRYCTL_SET_CBC_MAC:
       if (buflen)
        if (h->flags & GCRY_CIPHER_CBC_CTS)
@@ -1351,21 +1964,49 @@ gcry_cipher_ctl( gcry_cipher_hd_t h, int cmd, void *buffer, size_t buflen)
       else
        h->flags &= ~GCRY_CIPHER_CBC_MAC;
       break;
       else
        h->flags &= ~GCRY_CIPHER_CBC_MAC;
       break;
+
     case GCRYCTL_DISABLE_ALGO:
     case GCRYCTL_DISABLE_ALGO:
-      /* this one expects a NULL handle and buffer pointing to an
-       * integer with the algo number.
-       */
+      /* This command expects NULL for H and BUFFER to point to an
+         integer with the algo number.  */
       if( h || !buffer || buflen != sizeof(int) )
        return gcry_error (GPG_ERR_CIPHER_ALGO);
       disable_cipher_algo( *(int*)buffer );
       break;
       if( h || !buffer || buflen != sizeof(int) )
        return gcry_error (GPG_ERR_CIPHER_ALGO);
       disable_cipher_algo( *(int*)buffer );
       break;
-    case GCRYCTL_SET_CTR:
-      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);
+
+    case GCRYCTL_SET_CTR: /* Deprecated; use gcry_cipher_setctr.  */
+      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
+          (&h->context.c, CIPHER_INFO_NO_WEAK_KEY, NULL, 0);
+      else
+        rc = GPG_ERR_NOT_SUPPORTED;
+      break;
+
+    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:
+           1 byte  Actual length of the block in bytes.
+           n byte  The block.
+         If the provided buffer is too short, an error is returned. */
+      if (buflen < (1 + h->cipher->blocksize))
+        rc = GPG_ERR_TOO_SHORT;
       else
       else
-       rc = GPG_ERR_INV_ARG;
+        {
+          unsigned char *ivp;
+          unsigned char *dst = buffer;
+          int n = h->unused;
+
+          if (!n)
+            n = h->cipher->blocksize;
+          gcry_assert (n <= h->cipher->blocksize);
+          *dst++ = n;
+          ivp = h->u_iv.iv + h->cipher->blocksize - n;
+          while (n--)
+            *dst++ = *ivp++;
+        }
       break;
 
     default:
       break;
 
     default:
@@ -1376,11 +2017,16 @@ gcry_cipher_ctl( gcry_cipher_hd_t h, int cmd, void *buffer, size_t buflen)
 }
 
 
 }
 
 
-/****************
- * Return information about the cipher handle.
+/* 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.
+
+   The function always returns GPG_ERR_INV_OP.
+
  */
 gcry_error_t
  */
 gcry_error_t
-gcry_cipher_infogcry_cipher_hd_t h, int cmd, void *buffer, size_t *nbytes)
+gcry_cipher_info (gcry_cipher_hd_t h, int cmd, void *buffer, size_t *nbytes)
 {
   gcry_err_code_t err = GPG_ERR_NO_ERROR;
 
 {
   gcry_err_code_t err = GPG_ERR_NO_ERROR;
 
@@ -1397,27 +2043,29 @@ gcry_cipher_info( gcry_cipher_hd_t h, int cmd, void *buffer, size_t *nbytes)
   return gcry_error (err);
 }
 
   return gcry_error (err);
 }
 
-/****************
- * Return information about the given cipher algorithm
- * WHAT select the kind of information returned:
- *  GCRYCTL_GET_KEYLEN:
- *     Return the length of the key, if the algorithm
- *     supports multiple key length, the maximum supported value
- *     is returnd.  The length is return as number of octets.
- *     buffer and nbytes must be zero.
- *     The keylength is returned in _bytes_.
- *  GCRYCTL_GET_BLKLEN:
- *     Return the blocklength of the algorithm counted in octets.
- *     buffer and nbytes must be zero.
- *  GCRYCTL_TEST_ALGO:
- *     Returns 0 when the specified algorithm 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 checking
- * the block size)
+/* Return information about the given cipher algorithm ALGO.
+
+   WHAT select the kind of information returned:
+
+    GCRYCTL_GET_KEYLEN:
+       Return the length of the key.  If the algorithm ALGO
+       supports multiple key lengths, the maximum supported key length
+       is returned.  The key length is returned as number of octets.
+       BUFFER and NBYTES must be zero.
+
+    GCRYCTL_GET_BLKLEN:
+       Return the blocklength of the algorithm ALGO counted in octets.
+       BUFFER and NBYTES must be zero.
+
+    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 occurred or not (i.e. while
+   checking the block size)
  */
 gcry_error_t
 gcry_cipher_algo_info (int algo, int what, void *buffer, size_t *nbytes)
  */
 gcry_error_t
 gcry_cipher_algo_info (int algo, int what, void *buffer, size_t *nbytes)
@@ -1472,19 +2120,32 @@ gcry_cipher_algo_info (int algo, int what, void *buffer, size_t *nbytes)
 }
 
 
 }
 
 
+/* This function returns length of the key for algorithm ALGO.  If the
+   algorithm supports multiple key lengths, the maximum supported key
+   length is returned.  On error 0 is returned.  The key length is
+   returned as number of octets.
+
+   This is a convenience functions which should be preferred over
+   gcry_cipher_algo_info because it allows for proper type
+   checking.  */
 size_t
 size_t
-gcry_cipher_get_algo_keylen (int algo) 
+gcry_cipher_get_algo_keylen (int algo)
 {
   size_t n;
 
 {
   size_t n;
 
-  if (gcry_cipher_algo_infoalgo, GCRYCTL_GET_KEYLEN, NULL, &n))
+  if (gcry_cipher_algo_info (algo, GCRYCTL_GET_KEYLEN, NULL, &n))
     n = 0;
   return n;
 }
 
     n = 0;
   return n;
 }
 
+/* This functions returns the blocklength of the algorithm ALGO
+   counted in octets.  On error 0 is returned.
 
 
+   This is a convenience functions which should be preferred over
+   gcry_cipher_algo_info because it allows for proper type
+   checking.  */
 size_t
 size_t
-gcry_cipher_get_algo_blklen (int algo) 
+gcry_cipher_get_algo_blklen (int algo)
 {
   size_t n;
 
 {
   size_t n;
 
@@ -1493,7 +2154,7 @@ gcry_cipher_get_algo_blklen (int algo)
   return n;
 }
 
   return n;
 }
 
-
+/* Explicitly initialize this module.  */
 gcry_err_code_t
 _gcry_cipher_init (void)
 {
 gcry_err_code_t
 _gcry_cipher_init (void)
 {
@@ -1521,3 +2182,41 @@ gcry_cipher_list (int *list, int *list_length)
 
   return err;
 }
 
   return err;
 }
+
+
+/* Run the selftests for cipher algorithm ALGO with optional reporting
+   function REPORT.  */
+gpg_error_t
+_gcry_cipher_selftest (int algo, int extended, selftest_report_func_t report)
+{
+  gcry_module_t module = NULL;
+  cipher_extra_spec_t *extraspec = NULL;
+  gcry_err_code_t ec = 0;
+
+  REGISTER_DEFAULT_CIPHERS;
+
+  ath_mutex_lock (&ciphers_registered_lock);
+  module = _gcry_module_lookup_id (ciphers_registered, algo);
+  if (module && !(module->flags & FLAG_MODULE_DISABLED))
+    extraspec = module->extraspec;
+  ath_mutex_unlock (&ciphers_registered_lock);
+  if (extraspec && extraspec->selftest)
+    ec = extraspec->selftest (algo, extended, report);
+  else
+    {
+      ec = GPG_ERR_CIPHER_ALGO;
+      if (report)
+        report ("cipher", algo, "module",
+                module && !(module->flags & FLAG_MODULE_DISABLED)?
+                "no selftest available" :
+                module? "algorithm disabled" : "algorithm not found");
+    }
+
+  if (module)
+    {
+      ath_mutex_lock (&ciphers_registered_lock);
+      _gcry_module_release (module);
+      ath_mutex_unlock (&ciphers_registered_lock);
+    }
+  return gpg_error (ec);
+}