CTR mode may now be used with arbitrary long data chunks.
[libgcrypt.git] / cipher / cipher.c
index 42c2b1d..90fdb17 100644 (file)
@@ -1,5 +1,6 @@
 /* cipher.c  - cipher dispatcher
- * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003
+ *               2005, 2007, 2008, 2009 Free Software Foundation, Inc.
  *
  * This file is part of Libgcrypt.
  *
@@ -14,8 +15,7 @@
  * 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 <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include <assert.h>
 
 #include "g10lib.h"
 #include "cipher.h"
-#include "des.h"
-#include "blowfish.h"
-#include "cast5.h"
-#include "arcfour.h"
-#include "dynload.h"
+#include "ath.h"
 
 #define MAX_BLOCKSIZE 16
 #define TABLE_SIZE 14
 #define CTX_MAGIC_NORMAL 0x24091964
 #define CTX_MAGIC_SECURE 0x46919042
 
-#define digitp(p)   (*(p) >= 0 && *(p) <= '9')
+/* 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;
+  cipher_extra_spec_t *extraspec;
+  unsigned int algorithm;
+  int fips_allowed;
+} cipher_table[] =
+  {
+#if USE_BLOWFISH
+    { &_gcry_cipher_spec_blowfish,
+      &dummy_extra_spec,                  GCRY_CIPHER_BLOWFISH },
+#endif
+#if USE_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,
+      &dummy_extra_spec,                  GCRY_CIPHER_ARCFOUR },
+#endif
+#if USE_CAST5
+    { &_gcry_cipher_spec_cast5,
+      &dummy_extra_spec,                  GCRY_CIPHER_CAST5 },
+#endif
+#if USE_AES
+    { &_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
+    { &_gcry_cipher_spec_twofish,
+      &dummy_extra_spec,                  GCRY_CIPHER_TWOFISH },
+    { &_gcry_cipher_spec_twofish128,
+      &dummy_extra_spec,                  GCRY_CIPHER_TWOFISH128 },
+#endif
+#if USE_SERPENT
+    { &_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
+#if USE_RFC2268
+    { &_gcry_cipher_spec_rfc2268_40,
+      &dummy_extra_spec,                  GCRY_CIPHER_RFC2268_40 },
+#endif
+#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                    }
+  };
+
+/* List of registered ciphers.  */
+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 whether the default ciphers have already been
+   registered.  */
+static int default_ciphers_registered;
+
+/* Convenient macro for registering the default ciphers.  */
+#define REGISTER_DEFAULT_CIPHERS                   \
+  do                                               \
+    {                                              \
+      ath_mutex_lock (&ciphers_registered_lock);   \
+      if (! default_ciphers_registered)            \
+        {                                          \
+          cipher_register_default ();              \
+          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;
 
-static struct {
-  const char *oidstring;
+
+/* 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;
+  cipher_extra_spec_t *extraspec;
+  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;
-  int mode;
-} oid_table[] = {
-  { "1.2.840.113549.3.7",      GCRY_CIPHER_3DES,   GCRY_CIPHER_MODE_CBC },
-
-  /* OIDs from NIST. See http://csrc.nist.gov.csor/ */
-  { "2.16.840.1.101.3.4.1.1",  GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_ECB },
-  { "2.16.840.1.101.3.4.1.2",  GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC },
-  { "2.16.840.1.101.3.4.1.3",  GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_OFB },
-  { "2.16.840.1.101.3.4.1.4",  GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CFB },
-  { "2.16.840.1.101.3.4.1.21", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_ECB },
-  { "2.16.840.1.101.3.4.1.22", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CBC },
-  { "2.16.840.1.101.3.4.1.23", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_OFB },
-  { "2.16.840.1.101.3.4.1.24", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CFB },
-  { "2.16.840.1.101.3.4.1.41", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_ECB },
-  { "2.16.840.1.101.3.4.1.42", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC },
-  { "2.16.840.1.101.3.4.1.43", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_OFB },
-  { "2.16.840.1.101.3.4.1.44", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CFB },
-
-  /* Teletrust specific OID for 3DES. */
-  { "1.3.36.3.1.3.2.1",        GCRY_CIPHER_3DES,   GCRY_CIPHER_MODE_CBC },
-
-  {NULL}
-};
 
+  /* 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;
 
-struct cipher_table_s {
-    const char *name;
-    int algo;
-    size_t blocksize;
-    size_t keylen;
-    size_t contextsize; /* allocate this amount of context */
-    int  (*setkey)( void *c, byte *key, unsigned keylen );
-    void (*encrypt)( void *c, byte *outbuf, byte *inbuf );
-    void (*decrypt)( void *c, byte *outbuf, byte *inbuf );
-    void (*stencrypt)( void *c, byte *outbuf, byte *inbuf, unsigned int n );
-    void (*stdecrypt)( void *c, byte *outbuf, byte *inbuf, unsigned int n );
-};
 
-static struct cipher_table_s cipher_table[TABLE_SIZE];
-static int disabled_algos[TABLE_SIZE];
-
-struct gcry_cipher_handle {
-    int magic;
-    int  algo;
-    int  mode;
-    unsigned int flags;
-    size_t blocksize;
-    byte iv[MAX_BLOCKSIZE];    /* (this should be ulong aligned) */
-    byte lastiv[MAX_BLOCKSIZE];
-    int  unused;  /* in IV */
-    int  (*setkey)( void *c, byte *key, unsigned keylen );
-    void (*encrypt)( void *c, byte *outbuf, byte *inbuf );
-    void (*decrypt)( void *c, byte *outbuf, byte *inbuf );
-    void (*stencrypt)( void *c, byte *outbuf, byte *inbuf, unsigned int n );
-    void (*stdecrypt)( void *c, byte *outbuf, byte *inbuf, unsigned int n );
-    PROPERLY_ALIGNED_TYPE context;
+  int mode;
+  unsigned int flags;
+
+  struct {
+    unsigned int key:1; /* Set to 1 if a key has been set.  */
+    unsigned int iv:1;  /* Set to 1 if 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];
+  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;
 };
 
 
-static int
-dummy_setkey( void *c, byte *key, unsigned keylen ) { return 0; }
-static void
-dummy_encrypt_block( void *c, byte *outbuf, byte *inbuf ) { BUG(); }
-static void
-dummy_decrypt_block( void *c, byte *outbuf, byte *inbuf ) { BUG(); }
+\f
+/* These dummy functions are used in case a cipher implementation
+   refuses to provide it's own functions.  */
+
+static gcry_err_code_t
+dummy_setkey (void *c, const unsigned char *key, unsigned int keylen)
+{
+  (void)c;
+  (void)key;
+  (void)keylen;
+  return GPG_ERR_NO_ERROR;
+}
+
 static void
-dummy_encrypt_stream( void *c, byte *outbuf, byte *inbuf, unsigned int n )
-{ BUG(); }
+dummy_encrypt_block (void *c,
+                    unsigned char *outbuf, const unsigned char *inbuf)
+{
+  (void)c;
+  (void)outbuf;
+  (void)inbuf;
+  BUG();
+}
+
 static void
-dummy_decrypt_stream( void *c, byte *outbuf, byte *inbuf, unsigned int n )
-{ BUG(); }
+dummy_decrypt_block (void *c,
+                    unsigned char *outbuf, const unsigned char *inbuf)
+{
+  (void)c;
+  (void)outbuf;
+  (void)inbuf;
+  BUG();
+}
 
+static void
+dummy_encrypt_stream (void *c,
+                     unsigned char *outbuf, const unsigned char *inbuf,
+                     unsigned int n)
+{
+  (void)c;
+  (void)outbuf;
+  (void)inbuf;
+  (void)n;
+  BUG();
+}
 
+static void
+dummy_decrypt_stream (void *c,
+                     unsigned char *outbuf, const unsigned char *inbuf,
+                     unsigned int n)
+{
+  (void)c;
+  (void)outbuf;
+  (void)inbuf;
+  (void)n;
+  BUG();
+}
 
-/****************
- * Put the static entries into the table.
- */
+\f
+/* Internal function.  Register all the ciphers included in
+   CIPHER_TABLE.  Note, that this function gets only used by the macro
+   REGISTER_DEFAULT_CIPHERS which protects it using a mutex. */
 static void
-setup_cipher_table(void)
+cipher_register_default (void)
 {
-    int i;
+  gcry_err_code_t err = GPG_ERR_NO_ERROR;
+  int i;
 
-    for (i=0; i < TABLE_SIZE; i++ ) {
-        cipher_table[i].encrypt = dummy_encrypt_block;
-        cipher_table[i].decrypt = dummy_decrypt_block;
-        cipher_table[i].stencrypt = dummy_encrypt_stream;
-        cipher_table[i].stdecrypt = dummy_decrypt_stream;
+  for (i = 0; !err && cipher_table[i].cipher; i++)
+    {
+      if (! cipher_table[i].cipher->setkey)
+       cipher_table[i].cipher->setkey = dummy_setkey;
+      if (! cipher_table[i].cipher->encrypt)
+       cipher_table[i].cipher->encrypt = dummy_encrypt_block;
+      if (! cipher_table[i].cipher->decrypt)
+       cipher_table[i].cipher->decrypt = dummy_decrypt_block;
+      if (! cipher_table[i].cipher->stencrypt)
+       cipher_table[i].cipher->stencrypt = dummy_encrypt_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,
+                             (void *) cipher_table[i].extraspec,
+                             NULL);
     }
-    
-    i = 0;
-    cipher_table[i].algo = GCRY_CIPHER_RIJNDAEL;
-    cipher_table[i].name = _gcry_rijndael_get_info( cipher_table[i].algo,
-                                        &cipher_table[i].keylen,
-                                        &cipher_table[i].blocksize,
-                                        &cipher_table[i].contextsize,
-                                        &cipher_table[i].setkey,
-                                        &cipher_table[i].encrypt,
-                                        &cipher_table[i].decrypt     );
-    if( !cipher_table[i].name )
-       BUG();
-    i++;
-    cipher_table[i].algo = GCRY_CIPHER_RIJNDAEL192;
-    cipher_table[i].name = _gcry_rijndael_get_info( cipher_table[i].algo,
-                                        &cipher_table[i].keylen,
-                                        &cipher_table[i].blocksize,
-                                        &cipher_table[i].contextsize,
-                                        &cipher_table[i].setkey,
-                                        &cipher_table[i].encrypt,
-                                        &cipher_table[i].decrypt     );
-    if( !cipher_table[i].name )
-       BUG();
-    i++;
-    cipher_table[i].algo = GCRY_CIPHER_RIJNDAEL256;
-    cipher_table[i].name = _gcry_rijndael_get_info( cipher_table[i].algo,
-                                        &cipher_table[i].keylen,
-                                        &cipher_table[i].blocksize,
-                                        &cipher_table[i].contextsize,
-                                        &cipher_table[i].setkey,
-                                        &cipher_table[i].encrypt,
-                                        &cipher_table[i].decrypt     );
-    if( !cipher_table[i].name )
-       BUG();
-    i++;
-    cipher_table[i].algo = GCRY_CIPHER_TWOFISH;
-    cipher_table[i].name = _gcry_twofish_get_info( cipher_table[i].algo,
-                                        &cipher_table[i].keylen,
-                                        &cipher_table[i].blocksize,
-                                        &cipher_table[i].contextsize,
-                                        &cipher_table[i].setkey,
-                                        &cipher_table[i].encrypt,
-                                        &cipher_table[i].decrypt     );
-    if( !cipher_table[i].name )
-       BUG();
-    i++;
-    cipher_table[i].algo = GCRY_CIPHER_BLOWFISH;
-    cipher_table[i].name = _gcry_blowfish_get_info( cipher_table[i].algo,
-                                        &cipher_table[i].keylen,
-                                        &cipher_table[i].blocksize,
-                                        &cipher_table[i].contextsize,
-                                        &cipher_table[i].setkey,
-                                        &cipher_table[i].encrypt,
-                                        &cipher_table[i].decrypt     );
-    if( !cipher_table[i].name )
-       BUG();
-    i++;
-    cipher_table[i].algo = GCRY_CIPHER_CAST5;
-    cipher_table[i].name = _gcry_cast5_get_info( cipher_table[i].algo,
-                                        &cipher_table[i].keylen,
-                                        &cipher_table[i].blocksize,
-                                        &cipher_table[i].contextsize,
-                                        &cipher_table[i].setkey,
-                                        &cipher_table[i].encrypt,
-                                        &cipher_table[i].decrypt     );
-    if( !cipher_table[i].name )
-       BUG();
-    i++;
-    cipher_table[i].algo = GCRY_CIPHER_3DES;
-    cipher_table[i].name = _gcry_des_get_info( cipher_table[i].algo,
-                                        &cipher_table[i].keylen,
-                                        &cipher_table[i].blocksize,
-                                        &cipher_table[i].contextsize,
-                                        &cipher_table[i].setkey,
-                                        &cipher_table[i].encrypt,
-                                        &cipher_table[i].decrypt     );
-    if( !cipher_table[i].name )
-       BUG();
-    i++;
-    cipher_table[i].algo = GCRY_CIPHER_ARCFOUR;
-    cipher_table[i].name = _gcry_arcfour_get_info( cipher_table[i].algo,
-                                        &cipher_table[i].keylen,
-                                        &cipher_table[i].blocksize,
-                                        &cipher_table[i].contextsize,
-                                        &cipher_table[i].setkey,
-                                        &cipher_table[i].stencrypt,
-                                        &cipher_table[i].stdecrypt   );
-    if( !cipher_table[i].name )
-       BUG();
-    i++;
-    cipher_table[i].algo = GCRY_CIPHER_DES;
-    cipher_table[i].name = _gcry_des_get_info( cipher_table[i].algo,
-                                        &cipher_table[i].keylen,
-                                        &cipher_table[i].blocksize,
-                                        &cipher_table[i].contextsize,
-                                        &cipher_table[i].setkey,
-                                        &cipher_table[i].encrypt,
-                                        &cipher_table[i].decrypt     );
-    if( !cipher_table[i].name )
-       BUG();
-    i++;
-
-    cipher_table[i].algo = CIPHER_ALGO_DUMMY;
-    cipher_table[i].name = "DUMMY";
-    cipher_table[i].blocksize = 8;
-    cipher_table[i].keylen = 128;
-    cipher_table[i].contextsize = 0;
-    cipher_table[i].setkey = dummy_setkey;
-    i++;
-
-    for( ; i < TABLE_SIZE; i++ )
-       cipher_table[i].name = NULL;
+
+  if (err)
+    BUG ();
 }
 
+/* Internal callback function.  Used via _gcry_module_lookup.  */
+static int
+gcry_cipher_lookup_func_name (void *spec, void *data)
+{
+  gcry_cipher_spec_t *cipher = (gcry_cipher_spec_t *) spec;
+  char *name = (char *) data;
+  const char **aliases = cipher->aliases;
+  int i, ret = ! stricmp (name, cipher->name);
+
+  if (aliases)
+    for (i = 0; aliases[i] && (! ret); i++)
+      ret = ! stricmp (name, aliases[i]);
 
-/****************
- * Try to load all modules and return true if new modules are available
- */
+  return ret;
+}
+
+/* Internal callback function.  Used via _gcry_module_lookup.  */
 static int
-load_cipher_modules(void)
+gcry_cipher_lookup_func_oid (void *spec, void *data)
 {
-    static int done = 0;
-    static int initialized = 0;
-    void *context = NULL;
-    struct cipher_table_s *ct;
-    int ct_idx;
-    int i;
-    const char *name;
-    int any = 0;
-
-    if( !initialized ) {
-       _gcry_cipher_modules_constructor();
-       setup_cipher_table(); /* load static modules on the first call */
-       initialized = 1;
-       return 1;
-    }
+  gcry_cipher_spec_t *cipher = (gcry_cipher_spec_t *) spec;
+  char *oid = (char *) data;
+  gcry_cipher_oid_spec_t *oid_specs = cipher->oids;
+  int ret = 0, i;
 
-    if( done )
-       return 0;
-    done = 1;
+  if (oid_specs)
+    for (i = 0; oid_specs[i].oid && (! ret); i++)
+      if (! stricmp (oid, oid_specs[i].oid))
+       ret = 1;
 
-    for(ct_idx=0, ct = cipher_table; ct_idx < TABLE_SIZE; ct_idx++,ct++ ) {
-       if( !ct->name )
-           break;
-    }
-    if( ct_idx >= TABLE_SIZE-1 )
-       BUG(); /* table already full */
-    /* now load all extensions */
-    while( (name = _gcry_enum_gnupgext_ciphers( &context, &ct->algo,
-                               &ct->keylen, &ct->blocksize, &ct->contextsize,
-                               &ct->setkey, &ct->encrypt, &ct->decrypt)) ) {
-       if( ct->blocksize != 8 && ct->blocksize != 16 ) {
-           log_info("skipping cipher %d: unsupported blocksize\n", ct->algo);
-           continue;
-       }
-       for(i=0; cipher_table[i].name; i++ )
-           if( cipher_table[i].algo == ct->algo )
-               break;
-       if( cipher_table[i].name ) {
-           log_info("skipping cipher %d: already loaded\n", ct->algo );
-           continue;
-       }
-       /* put it into the table */
-       if( _gcry_log_verbosity( 2 ) )
-           log_info("loaded cipher %d (%s)\n", ct->algo, name);
-       ct->name = name;
-       ct_idx++;
-       ct++;
-       any = 1;
-       /* check whether there are more available table slots */
-       if( ct_idx >= TABLE_SIZE-1 ) {
-           log_info("cipher table full; ignoring other extensions\n");
-           break;
-       }
-    }
-    _gcry_enum_gnupgext_ciphers( &context, NULL, NULL, NULL, NULL,
-                                          NULL, NULL, NULL );
-    return any;
+  return ret;
 }
 
-/* locate the OID in the oid table and return the index or -1 when not
-   found */
-static int 
-search_oid (const char *string)
+/* Internal function.  Lookup a cipher entry by it's name.  */
+static gcry_module_t
+gcry_cipher_lookup_name (const char *name)
 {
-  int i;
-  const char *s;
+  gcry_module_t cipher;
+
+  cipher = _gcry_module_lookup (ciphers_registered, (void *) name,
+                               gcry_cipher_lookup_func_name);
+
+  return cipher;
+}
+
+/* Internal function.  Lookup a cipher entry by it's oid.  */
+static gcry_module_t
+gcry_cipher_lookup_oid (const char *oid)
+{
+  gcry_module_t cipher;
+
+  cipher = _gcry_module_lookup (ciphers_registered, (void *) oid,
+                               gcry_cipher_lookup_func_oid);
+
+  return cipher;
+}
+
+/* Register a new cipher module whose specification can be found in
+   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,
+                       cipher_extra_spec_t *extraspec,
+                       int *algorithm_id,
+                       gcry_module_t *module)
+{
+  gcry_err_code_t err = 0;
+  gcry_module_t mod;
 
-  if (string && (digitp (string)
-                 || !strncmp (string, "oid.", 4) 
-                 || !strncmp (string, "OID.", 4) ))
+  /* 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,
+                         (void *)cipher,
+                         (void *)(extraspec? extraspec : &dummy_extra_spec),
+                          &mod);
+  ath_mutex_unlock (&ciphers_registered_lock);
+
+  if (! err)
     {
-      s =  digitp(string)? string : (string+4);
+      *module = mod;
+      *algorithm_id = mod->mod_id;
+    }
 
-      for (i=0; oid_table[i].oidstring; i++)
-        {
-          if (!strcmp (s, oid_table[i].oidstring))
-            return i;
-        }
+  return gcry_error (err);
+}
+
+/* Unregister the cipher identified by MODULE, which must have been
+   registered with gcry_cipher_register.  */
+void
+gcry_cipher_unregister (gcry_module_t module)
+{
+  ath_mutex_lock (&ciphers_registered_lock);
+  _gcry_module_release (module);
+  ath_mutex_unlock (&ciphers_registered_lock);
+}
+
+/* Locate the OID in the oid table and return the index or -1 when not
+   found.  An opitonal "oid." or "OID." prefix in OID is ignored, the
+   OID is expected to be in standard IETF dotted notation.  The
+   internal algorithm number is returned in ALGORITHM unless it
+   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
+search_oid (const char *oid, int *algorithm, gcry_cipher_oid_spec_t *oid_spec)
+{
+  gcry_module_t module;
+  int ret = 0;
+
+  if (oid && ((! strncmp (oid, "oid.", 4))
+             || (! strncmp (oid, "OID.", 4))))
+    oid += 4;
+
+  module = gcry_cipher_lookup_oid (oid);
+  if (module)
+    {
+      gcry_cipher_spec_t *cipher = module->spec;
+      int i;
+
+      for (i = 0; cipher->oids[i].oid && !ret; i++)
+       if (! stricmp (oid, cipher->oids[i].oid))
+         {
+           if (algorithm)
+             *algorithm = module->mod_id;
+           if (oid_spec)
+             *oid_spec = cipher->oids[i];
+           ret = 1;
+         }
+      _gcry_module_release (module);
     }
-  return -1;
+
+  return ret;
 }
 
-/****************
- * Map a string to the cipher algo.
- * Returns: The algo ID of the cipher for the gioven name or
- *         0 if the name is not known.
- */
+/* Map STRING to the cipher algorithm identifier.  Returns the
+   algorithm ID of the cipher for the given name or 0 if the name is
+   not known.  It is valid to pass NULL for STRING which results in a
+   return value of 0. */
 int
-gcry_cipher_map_name( const char *string )
+gcry_cipher_map_name (const char *string)
 {
-  int i;
-  const char *s;
-  
-  if (!string)
+  gcry_module_t cipher;
+  int ret, algorithm = 0;
+
+  if (! string)
     return 0;
 
-  /* kludge to alias RIJNDAEL to AES */
-  if ( *string == 'R' || *string == 'r')
-    {
-      if (!strcasecmp (string, "RIJNDAEL"))
-        string = "AES";
-      else if (!strcasecmp (string, "RIJNDAEL192"))
-        string = "AES192";
-      else if (!strcasecmp (string, "RIJNDAEL256"))
-        string = "AES256";
-    }
+  REGISTER_DEFAULT_CIPHERS;
 
   /* If the string starts with a digit (optionally prefixed with
      either "OID." or "oid."), we first look into our table of ASN.1
      object identifiers to figure out the algorithm */
-  i = search_oid (string);
-  if (i != -1)
-    return oid_table[i].algo;
-  
-  do 
+
+  ath_mutex_lock (&ciphers_registered_lock);
+
+  ret = search_oid (string, &algorithm, NULL);
+  if (! ret)
     {
-      for (i=0; (s=cipher_table[i].name); i++ )
-        if ( !stricmp( s, string ) )
-          return cipher_table[i].algo;
-    } 
-  while ( load_cipher_modules() );
-  return 0;
+      cipher = gcry_cipher_lookup_name (string);
+      if (cipher)
+       {
+         algorithm = cipher->mod_id;
+         _gcry_module_release (cipher);
+       }
+    }
+
+  ath_mutex_unlock (&ciphers_registered_lock);
+
+  return algorithm;
 }
 
+
+/* Given a STRING with an OID in dotted decimal notation, this
+   function returns the cipher mode (GCRY_CIPHER_MODE_*) associated
+   with that OID or 0 if no mode is known.  Passing NULL for string
+   yields a return value of 0. */
 int
 gcry_cipher_mode_from_oid (const char *string)
 {
-  int i;
+  gcry_cipher_oid_spec_t oid_spec;
+  int ret = 0, mode = 0;
+
+  if (!string)
+    return 0;
 
-  i = search_oid (string);
-  return i == -1? 0 : oid_table[i].mode;
+  ath_mutex_lock (&ciphers_registered_lock);
+  ret = search_oid (string, NULL, &oid_spec);
+  if (ret)
+    mode = oid_spec.mode;
+  ath_mutex_unlock (&ciphers_registered_lock);
+
+  return mode;
 }
 
 
-/****************
- * Map a cipher algo to a string
- */
+/* Map the cipher algorithm whose ID is contained in ALGORITHM to a
+   string representation of the algorithm name.  For unknown algorithm
  IDs this function returns "?".  */
 static const char *
-cipher_algo_to_string( int algo )
+cipher_algo_to_string (int algorithm)
 {
-    int i;
-
-    do {
-       for(i=0; cipher_table[i].name; i++ )
-           if( cipher_table[i].algo == algo )
-               return cipher_table[i].name;
-    } while( load_cipher_modules() );
-    return NULL;
+  gcry_module_t cipher;
+  const char *name;
+
+  REGISTER_DEFAULT_CIPHERS;
+
+  ath_mutex_lock (&ciphers_registered_lock);
+  cipher = _gcry_module_lookup_id (ciphers_registered, algorithm);
+  if (cipher)
+    {
+      name = ((gcry_cipher_spec_t *) cipher->spec)->name;
+      _gcry_module_release (cipher);
+    }
+  else
+    name = "?";
+  ath_mutex_unlock (&ciphers_registered_lock);
+
+  return name;
 }
 
-/****************
- * This function simply returns the name of the algorithm or some constant
- * string when there is no algo.  It will never return NULL.
- */
+/* Map the cipher algorithm identifier ALGORITHM to a string
+   representing this algorithm.  This string is the default name as
+   used by Libgcrypt.  An pointer to an empty string is returned for
  an unknown algorithm.  NULL is never returned. */
 const char *
-gcry_cipher_algo_name( int algo )
+gcry_cipher_algo_name (int algorithm)
 {
-    const char *s = cipher_algo_to_string( algo );
-    return s? s: "";
+  return cipher_algo_to_string (algorithm);
 }
 
 
-
+/* Flag the cipher algorithm with the identifier ALGORITHM as
+   disabled.  There is no error return, the function does nothing for
+   unknown algorithms.  Disabled algorithms are vitually not available
+   in Libgcrypt. */
 static void
-disable_cipher_algo( int algo )
+disable_cipher_algo (int algorithm)
 {
-    int i;
+  gcry_module_t cipher;
 
-    for(i=0; i < DIM(disabled_algos); i++ ) {
-       if( !disabled_algos[i] || disabled_algos[i] == algo ) {
-           disabled_algos[i] = algo;
-           return;
-       }
+  REGISTER_DEFAULT_CIPHERS;
+
+  ath_mutex_lock (&ciphers_registered_lock);
+  cipher = _gcry_module_lookup_id (ciphers_registered, algorithm);
+  if (cipher)
+    {
+      if (! (cipher->flags & FLAG_MODULE_DISABLED))
+       cipher->flags |= FLAG_MODULE_DISABLED;
+      _gcry_module_release (cipher);
     }
-    /* fixme: we should use a linked list */
-    log_fatal("can't disable cipher algo %d: table full\n", algo );
+  ath_mutex_unlock (&ciphers_registered_lock);
 }
 
-/****************
- * Return 0 if the cipher algo is available
- */
-static int
-check_cipher_algo( int algo )
+
+/* 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)
 {
-    int i;
-
-    do {
-       for(i=0; cipher_table[i].name; i++ )
-          if( cipher_table[i].algo == algo ) {
-               for(i=0; i < DIM(disabled_algos); i++ ) {
-                  if( disabled_algos[i] == algo )
-                      return GCRYERR_INV_CIPHER_ALGO;
-               }
-               return 0; /* okay */
-          }
-    } while( load_cipher_modules() );
-    return GCRYERR_INV_CIPHER_ALGO;
-}
+  gcry_err_code_t err = GPG_ERR_NO_ERROR;
+  gcry_module_t cipher;
 
+  REGISTER_DEFAULT_CIPHERS;
 
-static unsigned
-cipher_get_keylen( int algo )
-{
-    int i;
-    unsigned len = 0;
-
-    do {
-       for(i=0; cipher_table[i].name; i++ ) {
-           if( cipher_table[i].algo == algo ) {
-               len = cipher_table[i].keylen;
-               if( !len )
-                   log_bug("cipher %d w/o key length\n", algo );
-               return len;
-           }
-       }
-    } while( load_cipher_modules() );
-    log_bug("cipher %d not found\n", algo );
-    return 0;
+  ath_mutex_lock (&ciphers_registered_lock);
+  cipher = _gcry_module_lookup_id (ciphers_registered, algorithm);
+  if (cipher)
+    {
+      if (cipher->flags & FLAG_MODULE_DISABLED)
+       err = GPG_ERR_CIPHER_ALGO;
+      _gcry_module_release (cipher);
+    }
+  else
+    err = GPG_ERR_CIPHER_ALGO;
+  ath_mutex_unlock (&ciphers_registered_lock);
+
+  return err;
 }
 
-static unsigned
-cipher_get_blocksize( int algo )
+
+/* Return the standard length of the key for the cipher algorithm with
+   the identifier ALGORITHM.  This function expects a valid algorithm
+   and will abort if the algorithm is not available or the length of
+   the key is not known. */
+static unsigned int
+cipher_get_keylen (int algorithm)
 {
-    int i;
-    unsigned len = 0;
-
-    do {
-       for(i=0; cipher_table[i].name; i++ ) {
-           if( cipher_table[i].algo == algo ) {
-               len = cipher_table[i].blocksize;
-               if( !len )
-                   log_bug("cipher %d w/o blocksize\n", algo );
-               return len;
-           }
-       }
-    } while( load_cipher_modules() );
-    log_bug("cipher %d not found\n", algo );
-    return 0;
-}
+  gcry_module_t cipher;
+  unsigned len = 0;
 
+  REGISTER_DEFAULT_CIPHERS;
 
-/****************
- * Open a cipher handle for use with algorithm ALGO, in mode MODE
- * and return the handle.  Return NULL and set the internal error variable
- * if something goes wrong.
- */
+  ath_mutex_lock (&ciphers_registered_lock);
+  cipher = _gcry_module_lookup_id (ciphers_registered, algorithm);
+  if (cipher)
+    {
+      len = ((gcry_cipher_spec_t *) cipher->spec)->keylen;
+      if (!len)
+       log_bug ("cipher %d w/o key length\n", algorithm);
+      _gcry_module_release (cipher);
+    }
+  else
+    log_bug ("cipher %d not found\n", algorithm);
+  ath_mutex_unlock (&ciphers_registered_lock);
+
+  return len;
+}
 
-GCRY_CIPHER_HD
-gcry_cipher_open( int algo, int mode, unsigned int flags )
+/* Return the block length of the cipher algorithm with the identifier
+   ALGORITHM.  This function expects a valid algorithm and will abort
+   if the algorithm is not available or the length of the key is not
+   known. */
+static unsigned int
+cipher_get_blocksize (int algorithm)
 {
-    GCRY_CIPHER_HD h;
-    int idx;
-    int secure = (flags & GCRY_CIPHER_SECURE);
+  gcry_module_t cipher;
+  unsigned len = 0;
 
-    fast_random_poll();
+  REGISTER_DEFAULT_CIPHERS;
 
-    /* check whether the algo is available */
-    if( check_cipher_algo( algo ) ) {
-       set_lasterr( GCRYERR_INV_CIPHER_ALGO );
-       return NULL;
+  ath_mutex_lock (&ciphers_registered_lock);
+  cipher = _gcry_module_lookup_id (ciphers_registered, algorithm);
+  if (cipher)
+    {
+      len = ((gcry_cipher_spec_t *) cipher->spec)->blocksize;
+      if (! len)
+         log_bug ("cipher %d w/o blocksize\n", algorithm);
+      _gcry_module_release (cipher);
     }
+  else
+    log_bug ("cipher %d not found\n", algorithm);
+  ath_mutex_unlock (&ciphers_registered_lock);
 
-    /* check flags */
-    if( (flags & ~(GCRY_CIPHER_SECURE|
-                  GCRY_CIPHER_ENABLE_SYNC|
-                  GCRY_CIPHER_CBC_CTS)) ) {
-       set_lasterr( GCRYERR_INV_CIPHER_ALGO );
-       return NULL;
-    }
+  return len;
+}
 
-    /* get the table index of the algo */
-    for(idx=0; cipher_table[idx].name; idx++ )
-       if( cipher_table[idx].algo == algo )
-           break;
-    if( !cipher_table[idx].name )
-       BUG(); /* check_cipher_algo() should have loaded the algo */
 
-    if( algo == CIPHER_ALGO_DUMMY )
-       mode = GCRY_CIPHER_MODE_NONE;  /* force this mode for dummy algo */
+/*
+   Open a cipher handle for use with cipher algorithm ALGORITHM, using
+   the cipher mode MODE (one of the GCRY_CIPHER_MODE_*) and return a
+   handle in HANDLE.  Put NULL into HANDLE and return an error code if
+   something goes wrong.  FLAGS may be used to modify the
+   operation.  The defined flags are:
+
+   GCRY_CIPHER_SECURE:  allocate all internal buffers in secure memory.
+   GCRY_CIPHER_ENABLE_SYNC:  Enable the sync operation as used in OpenPGP.
+   GCRY_CIPHER_CBC_CTS:  Enable CTS mode.
+   GCRY_CIPHER_CBC_MAC:  Enable MAC mode.
+
+   Values for these flags may be combined using OR.
+ */
+gcry_error_t
+gcry_cipher_open (gcry_cipher_hd_t *handle,
+                 int algo, int mode, unsigned int flags)
+{
+  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;
+
+  /* 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 whether the cipher is marked
+     available for use.  */
+  ath_mutex_lock (&ciphers_registered_lock);
+  module = _gcry_module_lookup_id (ciphers_registered, algo);
+  if (module)
+    {
+      /* Found module.  */
 
-    /* check that a valid mode has been requested */
-    switch( mode ) {
+      if (module->flags & FLAG_MODULE_DISABLED)
+       {
+         /* Not available for use.  */
+         err = GPG_ERR_CIPHER_ALGO;
+       }
+      else
+        {
+          cipher = (gcry_cipher_spec_t *) module->spec;
+          extraspec = module->extraspec;
+        }
+    }
+  else
+    err = GPG_ERR_CIPHER_ALGO;
+  ath_mutex_unlock (&ciphers_registered_lock);
+
+  /* check flags */
+  if ((! err)
+      && ((flags & ~(0
+                    | GCRY_CIPHER_SECURE
+                    | GCRY_CIPHER_ENABLE_SYNC
+                    | GCRY_CIPHER_CBC_CTS
+                    | GCRY_CIPHER_CBC_MAC))
+         || (flags & GCRY_CIPHER_CBC_CTS & GCRY_CIPHER_CBC_MAC)))
+    err = GPG_ERR_CIPHER_ALGO;
+
+  /* check that a valid mode has been requested */
+  if (! err)
+    switch (mode)
+      {
       case GCRY_CIPHER_MODE_ECB:
       case GCRY_CIPHER_MODE_CBC:
       case GCRY_CIPHER_MODE_CFB:
-        if ( cipher_table[idx].encrypt == dummy_encrypt_block
-             || cipher_table[idx].decrypt == dummy_decrypt_block ) {
-            set_lasterr( GCRYERR_INV_CIPHER_MODE );
-            return NULL;
-        }
-        break;
+      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;
+       break;
+
       case GCRY_CIPHER_MODE_STREAM:
-        if ( cipher_table[idx].stencrypt == dummy_encrypt_stream
-             || cipher_table[idx].stdecrypt == dummy_decrypt_stream ) {
-            set_lasterr( GCRYERR_INV_CIPHER_MODE );
-            return NULL;
-        }
+       if ((cipher->stencrypt == dummy_encrypt_stream)
+           || (cipher->stdecrypt == dummy_decrypt_stream))
+         err = GPG_ERR_INV_CIPHER_MODE;
        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:
-       set_lasterr( GCRYERR_INV_CIPHER_MODE );
-       return NULL;
+       err = GPG_ERR_INV_CIPHER_MODE;
+      }
+
+  /* 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
+                     - 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);
+      else
+       h = gcry_calloc (1, size);
+
+      if (! h)
+       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->actual_handle_size = size - off;
+          h->handle_offset = off;
+         h->cipher = cipher;
+         h->extraspec = extraspec;
+         h->module = module;
+          h->algo = algo;
+         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;
+            }
+       }
     }
 
-    /* ? perform selftest here and mark this with a flag in cipher_table ? */
+  /* Done.  */
 
-    h = secure ? gcry_calloc_secure( 1, sizeof *h
-                                      + cipher_table[idx].contextsize
-                                      - sizeof(PROPERLY_ALIGNED_TYPE) )
-              : gcry_calloc( 1, sizeof *h + cipher_table[idx].contextsize
-                                       - sizeof(PROPERLY_ALIGNED_TYPE)  );
-    if( !h ) {
-       set_lasterr( GCRYERR_NO_MEM );
-       return NULL;
+  if (err)
+    {
+      if (module)
+       {
+         /* Release module.  */
+         ath_mutex_lock (&ciphers_registered_lock);
+         _gcry_module_release (module);
+         ath_mutex_unlock (&ciphers_registered_lock);
+       }
     }
-    h->magic = secure ? CTX_MAGIC_SECURE : CTX_MAGIC_NORMAL;
-    h->algo = algo;
-    h->mode = mode;
-    h->flags = flags;
-    h->blocksize = cipher_table[idx].blocksize;
-    h->setkey  = cipher_table[idx].setkey;
-    h->encrypt = cipher_table[idx].encrypt;
-    h->decrypt = cipher_table[idx].decrypt;
-    h->stencrypt = cipher_table[idx].stencrypt;
-    h->stdecrypt = cipher_table[idx].stdecrypt;
-
-    return h;
+
+  *handle = err ? NULL : h;
+
+  return gcry_error (err);
 }
 
 
+/* Release all resources associated with the cipher handle H. H may be
+   NULL in which case this is a no-operation. */
 void
-gcry_cipher_close( GCRY_CIPHER_HD h )
+gcry_cipher_close (gcry_cipher_hd_t h)
 {
-    if( !h )
-       return;
-    if( h->magic != CTX_MAGIC_SECURE && h->magic != CTX_MAGIC_NORMAL )
-       _gcry_fatal_error(GCRYERR_INTERNAL,
-                       "gcry_cipher_close: already closed/invalid handle");
+  size_t off;
+
+  if (!h)
+    return;
+
+  if ((h->magic != CTX_MAGIC_SECURE)
+      && (h->magic != CTX_MAGIC_NORMAL))
+    _gcry_fatal_error(GPG_ERR_INTERNAL,
+                     "gcry_cipher_close: already closed/invalid handle");
+  else
     h->magic = 0;
-    gcry_free(h);
+
+  /* Release module.  */
+  ath_mutex_lock (&ciphers_registered_lock);
+  _gcry_module_release (h->module);
+  ath_mutex_unlock (&ciphers_registered_lock);
+
+  /* We always want to wipe out the memory even when the context has
+     been allocated in secure memory.  The user might have disabled
+     secure memory or is using his own implementation which does not
+     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);
+
+  gcry_free ((char*)h - off);
 }
 
 
-static int
-cipher_setkey( GCRY_CIPHER_HD c, byte *key, unsigned keylen )
+/* 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 int keylen)
 {
-    return (*c->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);
+      c->marks.key = 1;
+    }
+  else
+    c->marks.key = 0;
+
+  return gcry_error (ret);
 }
 
 
+/* Set the IV to be used for the encryption context C to IV with
+   length IVLEN.  The length should match the required length. */
 static void
-cipher_setiv( GCRY_CIPHER_HD c, const byte *iv, unsigned ivlen )
+cipher_setiv( gcry_cipher_hd_t c, const byte *iv, unsigned ivlen )
 {
-    memset( c->iv, 0, c->blocksize );
-    if( iv ) {
-       if( ivlen != c->blocksize )
-           log_info("WARNING: cipher_setiv: ivlen=%u blklen=%u\n",
-                                            ivlen, (unsigned)c->blocksize );
-       if( ivlen > c->blocksize )
-           ivlen = c->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 context.  This is basically
+   the same as an release followed by a new. */
 static void
-do_ecb_encrypt( GCRY_CIPHER_HD c, byte *outbuf, const byte *inbuf, unsigned nblocks )
+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->u_ctr.ctr, 0, c->cipher->blocksize);
+}
+
+
+\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 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;
 
-    for(n=0; n < nblocks; n++ ) {
-       (*c->encrypt)( &c->context.c, outbuf, (byte*)/*arggg*/inbuf );
-       inbuf  += c->blocksize;
-       outbuf += c->blocksize;
+  nblocks = inbuflen / 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 c, byte *outbuf, const byte *inbuf, unsigned 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;
 
-    for(n=0; n < nblocks; n++ ) {
-       (*c->decrypt)( &c->context.c, outbuf, (byte*)/*arggg*/inbuf );
-       inbuf  += c->blocksize;
-       outbuf += c->blocksize;
+  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  += blocksize;
+      outbuf += blocksize;
     }
+
+  return 0;
 }
 
-static void
-do_cbc_encrypt( GCRY_CIPHER_HD c, byte *outbuf, const byte *inbuf, unsigned 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->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--;
     }
 
-    for(n=0; n < nblocks; n++ ) {
-       /* fixme: the xor should works on words and not on
-        * bytes.  Maybe it is a good idea to enhance the cipher backend
-        * API to allow for CBC handling in the backend */
-       for(ivp=c->iv,i=0; i < blocksize; i++ )
-           outbuf[i] = inbuf[i] ^ *ivp++;
-       (*c->encrypt)( &c->context.c, outbuf, outbuf );
-       memcpy(c->iv, outbuf, blocksize );
-       inbuf  += c->blocksize;
-       outbuf += c->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)
-      {
-       int restbytes;
+  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)
-         restbytes = blocksize;
-       else
-         restbytes = nbytes % blocksize;
+      if ((inbuflen % blocksize) == 0)
+        restbytes = blocksize;
+      else
+        restbytes = inbuflen % blocksize;
 
-       memcpy(outbuf, outbuf - c->blocksize, restbytes);
-       outbuf -= c->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++;
 
-       for(ivp=c->iv,i=0; i < restbytes; i++ )
-           outbuf[i] = inbuf[i] ^ *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->encrypt)( &c->context.c, outbuf, outbuf );
-       memcpy(c->iv, outbuf, blocksize );
-      }
+  return 0;
 }
 
-static void
-do_cbc_decrypt( GCRY_CIPHER_HD c, byte *outbuf, const byte *inbuf, unsigned 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->blocksize;
-    unsigned 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 ((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->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->decrypt)( &c->context.c, outbuf, (char*)/*argggg*/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++;
-       memcpy(c->iv, c->lastiv, blocksize );
-       inbuf  += c->blocksize;
-       outbuf += c->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->decrypt)( &c->context.c, outbuf, (char*)/*argggg*/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->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 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->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->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( 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++);
-    }
+  if (outbuflen < inbuflen)
+    return GPG_ERR_BUFFER_TOO_SHORT;
 
-    /* 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->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( nbytes ) { /* process the remaining bytes */
-       /* encrypt the IV (and save the current one) */
-       memcpy( c->lastiv, c->iv, blocksize );
-       (*c->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 <= 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;
     }
-}
 
-static void
-do_cfb_decrypt( GCRY_CIPHER_HD c,
-                byte *outbuf, const byte *inbuf, unsigned nbytes )
-{
-    byte *ivp;
-    ulong temp;
-    size_t blocksize = c->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;
+  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-- ) {
-           temp = *inbuf++;
-           *outbuf++ = *ivp ^ temp;
-           *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 (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->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;
+  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->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;
-       }
+  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 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 (outbuflen < inbuflen)
+    return GPG_ERR_BUFFER_TOO_SHORT;
 
-/****************
- * 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.
- */
-static int
-cipher_encrypt( GCRY_CIPHER_HD c, byte *outbuf,
-                                 const byte *inbuf, unsigned int nbytes )
+  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. */
+      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.  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;
+    }
+  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 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)
 {
-    int rc = 0;
+  unsigned char *ivp;
+  size_t blocksize = c->cipher->blocksize;
 
-    switch( c->mode ) {
-      case GCRY_CIPHER_MODE_ECB:
-       if (!(nbytes%c->blocksize))
-            do_ecb_encrypt(c, outbuf, inbuf, nbytes/c->blocksize );
-        else 
-            rc = GCRYERR_INV_ARG;
-       break;
-      case GCRY_CIPHER_MODE_CBC:
-       if (!(nbytes%c->blocksize) || (nbytes > c->blocksize && 
-                                      (c->flags & GCRY_CIPHER_CBC_CTS)))
-            do_cbc_encrypt(c, outbuf, inbuf, nbytes );
-        else 
-            rc = GCRYERR_INV_ARG;
-       break;
-      case GCRY_CIPHER_MODE_CFB:
-       do_cfb_encrypt(c, outbuf, inbuf, nbytes );
-       break;
-      case GCRY_CIPHER_MODE_STREAM:
-        (*c->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 = GCRYERR_INV_CIPHER_MODE;
-        break;
+  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;
+           inbuflen;
+           inbuflen--, c->unused-- )
+        *outbuf++ = (*ivp++ ^ *inbuf++);
+      return 0;
+    }
+
+  if( 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 ( 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++);
+      inbuflen -= blocksize;
+    }
+  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 -= inbuflen;
+      for(ivp=c->u_iv.iv; inbuflen; inbuflen-- )
+        *outbuf++ = (*ivp++ ^ *inbuf++);
+    }
+  return 0;
+}
+
+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)
+{
+  unsigned char *ivp;
+  size_t blocksize = c->cipher->blocksize;
+
+  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; inbuflen; inbuflen--,c->unused--)
+        *outbuf++ = *ivp++ ^ *inbuf++;
+      return 0;
+    }
+
+  if ( 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 ( 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++;
+      inbuflen -= blocksize;
+    }
+  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 -= inbuflen;
+      for (ivp=c->u_iv.iv; inbuflen; inbuflen-- )
+        *outbuf++ = *ivp++ ^ *inbuf++;
+    }
+  return 0;
+}
+
+
+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;
+  int i;
+  unsigned int blocksize = c->cipher->blocksize;
+  unsigned int nblocks;
+
+  if (outbuflen < inbuflen)
+    return GPG_ERR_BUFFER_TOO_SHORT;
+
+  /* First process a left over encrypted counter.  */
+  if (c->unused)
+    {
+      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);
+}
+
+
+/* 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--)
+           {
+             t[x]++;
+             if (t[x])
+               break;
+           }
+          /* A := MSB_64(B) ^ t */
+          for (x=0; x < 8; x++)
+            a[x] = b[x] ^ t[x];
+          /* R[i] := LSB_64(B) */
+          memcpy (r+i*8, b+8, 8);
+        }
+   }
+
+  return 0;
+}
+
+/* Perform the AES-Unwrap algorithm as specified by RFC3394.  We
+   implement this as a mode usable with any cipher algorithm of
+   blocksize 128.  */
+static gcry_err_code_t
+do_aeswrap_decrypt (gcry_cipher_hd_t c, byte *outbuf, unsigned int outbuflen,
+                    const byte *inbuf, unsigned int inbuflen)
+{
+  int j, x;
+  unsigned int n, i;
+  unsigned char *r, *a, *b;
+  unsigned char t[8];
+
+#if MAX_BLOCKSIZE < 8
+#error Invalid block size
+#endif
+  /* We require a cipher with a 128 bit block length.  */
+  if (c->cipher->blocksize != 16)
+    return GPG_ERR_INV_LENGTH;
+
+  /* The output buffer must be able to hold the input data minus one
+     additional block.  Fixme: The caller has more restrictive checks
+     - we may want to fix them for this mode.  */
+  if (outbuflen + 8  < inbuflen)
+    return GPG_ERR_BUFFER_TOO_SHORT;
+  /* Input data must be multiple of 64 bits.  */
+  if (inbuflen % 8)
+    return GPG_ERR_INV_ARG;
+
+  n = inbuflen / 8;
+
+  /* We need at least three 64 bit blocks.  */
+  if (n < 3)
+    return GPG_ERR_INV_ARG;
+
+  r = outbuf;
+  a = c->lastiv;  /* We use c->LASTIV as buffer for A.  */
+  b = c->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 rc;
+  return j? GPG_ERR_CHECKSUM : 0;
 }
 
 
 /****************
- * Encrypt IN and write it to OUT.  If IN is NULL, in-place encryption has
- * been requested,
+ * Encrypt INBUF to OUTBUF with the mode selected at open.
+ * inbuf and outbuf may overlap or be the same.
+ * Depending on the mode some constraints apply to INBUFLEN.
  */
-int
-gcry_cipher_encrypt( GCRY_CIPHER_HD h, byte *out, size_t outsize,
-                                      const byte  *in, size_t inlen )
+static gcry_err_code_t
+cipher_encrypt (gcry_cipher_hd_t c, byte *outbuf, unsigned int outbuflen,
+               const byte *inbuf, unsigned int inbuflen)
 {
-    int rc;
+  gcry_err_code_t rc;
 
-    if ( !in ) {
-       /* caller requested in-place encryption */
-       /* actullay cipher_encrypt() does not need to know about it, but
-        * we may chnage this to get better performace */
-       rc = cipher_encrypt ( h, out, out, outsize );
-    }
-    else {
-       if ( outsize < inlen )
-           return set_lasterr ( GCRYERR_TOO_SHORT );
-        if ( ( h->mode == GCRY_CIPHER_MODE_ECB ||
-               (h->mode == GCRY_CIPHER_MODE_CBC && 
-               !((h->flags & GCRY_CIPHER_CBC_CTS) &&
-                 (inlen > h->blocksize)))) &&
-            (inlen % h->blocksize) != 0 )
-         return set_lasterr( GCRYERR_INV_ARG );
-
-       rc = cipher_encrypt ( h, out, in, inlen );
+  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? set_lasterr (rc):0;
+  return rc;
+}
+
+
+/****************
+ * Encrypt IN and write it to OUT.  If IN is NULL, in-place encryption has
+ * been requested.
+ */
+gcry_error_t
+gcry_cipher_encrypt (gcry_cipher_hd_t h, void *out, size_t outsize,
+                     const void *in, size_t inlen)
+{
+  gcry_err_code_t err;
+
+  if (!in)  /* Caller requested in-place encryption.  */
+    err = cipher_encrypt (h, out, outsize, out, outsize);
+  else
+    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);
+
+  return gcry_error (err);
 }
 
 
@@ -936,73 +1788,82 @@ gcry_cipher_encrypt( GCRY_CIPHER_HD h, byte *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 int
-cipher_decrypt( GCRY_CIPHER_HD c, byte *outbuf, const byte *inbuf,
-                                                       unsigned nbytes )
+static gcry_err_code_t
+cipher_decrypt (gcry_cipher_hd_t c, byte *outbuf, unsigned int outbuflen,
+                const byte *inbuf, unsigned int inbuflen)
 {
-    int rc = 0;
+  gcry_err_code_t rc;
 
-    switch( c->mode ) {
-      case GCRY_CIPHER_MODE_ECB:
-       if (!(nbytes%c->blocksize))
-            do_ecb_decrypt(c, outbuf, inbuf, nbytes/c->blocksize );
-        else 
-            rc = GCRYERR_INV_ARG;
-       break;
-      case GCRY_CIPHER_MODE_CBC:
-       if (!(nbytes%c->blocksize) || (nbytes > c->blocksize && 
-                                      (c->flags & GCRY_CIPHER_CBC_CTS)))
-            do_cbc_decrypt(c, outbuf, inbuf, nbytes );
-        else 
-            rc = GCRYERR_INV_ARG;
-       break;
-      case GCRY_CIPHER_MODE_CFB:
-       do_cfb_decrypt(c, outbuf, inbuf, nbytes );
-       break;
-      case GCRY_CIPHER_MODE_STREAM:
-        (*c->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 = GCRYERR_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;
 }
 
 
-int
-gcry_cipher_decrypt( GCRY_CIPHER_HD h, byte *out, size_t outsize,
-                                const byte  *in, size_t inlen )
+gcry_error_t
+gcry_cipher_decrypt (gcry_cipher_hd_t h, void *out, size_t outsize,
+                    const void *in, size_t inlen)
 {
-    int rc;
+  gcry_err_code_t err;
 
-    if( !in ) {
-       /* caller requested in-place encryption */
-       /* actullay cipher_encrypt() does not need to know about it, but
-        * we may chnage this to get better performace */
-       rc = cipher_decrypt( h, out, out, outsize );
-    }
-    else {
-       if( outsize < inlen )
-           return set_lasterr( GCRYERR_TOO_SHORT );
-        if ( ( h->mode == GCRY_CIPHER_MODE_ECB ||
-               (h->mode == GCRY_CIPHER_MODE_CBC && 
-               !((h->flags & GCRY_CIPHER_CBC_CTS) &&
-                 (inlen > h->blocksize)))) &&
-            (inlen % h->blocksize) != 0 )
-            return set_lasterr( GCRYERR_INV_ARG );
-
-       rc = cipher_decrypt( h, out, in, inlen );
-    }
-    return rc? set_lasterr (rc):0;
+  if (!in) /* Caller requested in-place encryption. */
+    err = cipher_decrypt (h, out, outsize, out, outsize);
+  else
+    err = cipher_decrypt (h, out, outsize, in, inlen);
+
+  return gcry_error (err);
 }
 
 
@@ -1012,141 +1873,350 @@ gcry_cipher_decrypt( GCRY_CIPHER_HD h, byte *out, size_t outsize,
  * the corresponding flag is set.
  */
 static void
-cipher_sync( GCRY_CIPHER_HD c )
+cipher_sync (gcry_cipher_hd_t c)
 {
-    if( (c->flags & GCRY_CIPHER_ENABLE_SYNC) && c->unused ) {
-       memmove(c->iv + c->unused, c->iv, c->blocksize - c->unused );
-       memcpy(c->iv, c->lastiv + c->blocksize - c->unused, c->unused);
-       c->unused = 0;
+  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;
     }
 }
 
 
-int
-gcry_cipher_ctl( GCRY_CIPHER_HD h, int cmd, void *buffer, size_t buflen)
+gcry_error_t
+_gcry_cipher_setkey (gcry_cipher_hd_t hd, const void *key, size_t keylen)
 {
-  int rc = 0;
+  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 (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;
+}
+
+
+gcry_error_t
+gcry_cipher_ctl( gcry_cipher_hd_t h, int cmd, void *buffer, size_t buflen)
+{
+  gcry_err_code_t rc = GPG_ERR_NO_ERROR;
 
   switch (cmd)
     {
-    case GCRYCTL_SET_KEY:
+    case GCRYCTL_SET_KEY:  /* Deprecated; use gcry_cipher_setkey.  */
       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;
+
+    case GCRYCTL_RESET:
+      cipher_reset (h);
+      break;
+
     case GCRYCTL_CFB_SYNC:
       cipher_sync( h );
       break;
+
     case GCRYCTL_SET_CBC_CTS:
       if (buflen)
-       h->flags |= GCRY_CIPHER_CBC_CTS;
+       if (h->flags & GCRY_CIPHER_CBC_MAC)
+         rc = GPG_ERR_INV_FLAG;
+       else
+         h->flags |= GCRY_CIPHER_CBC_CTS;
       else
        h->flags &= ~GCRY_CIPHER_CBC_CTS;
       break;
 
+    case GCRYCTL_SET_CBC_MAC:
+      if (buflen)
+       if (h->flags & GCRY_CIPHER_CBC_CTS)
+         rc = GPG_ERR_INV_FLAG;
+       else
+         h->flags |= GCRY_CIPHER_CBC_MAC;
+      else
+       h->flags &= ~GCRY_CIPHER_CBC_MAC;
+      break;
+
     case GCRYCTL_DISABLE_ALGO:
-      /* this one expects a NULL handle and buffer pointing to an
-       * integer with the algo number.
-       */
+      /* This command expects NULL for H and BUFFER to point to an
+         integer with the algo number.  */
       if( h || !buffer || buflen != sizeof(int) )
-        return set_lasterr( GCRYERR_INV_CIPHER_ALGO );
+       return gcry_error (GPG_ERR_CIPHER_ALGO);
       disable_cipher_algo( *(int*)buffer );
       break;
 
+    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
+        {
+          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:
-      rc = GCRYERR_INV_OP;
+      rc = GPG_ERR_INV_OP;
     }
-  return set_lasterr (rc);
+
+  return gcry_error (rc);
 }
 
 
-/****************
- * Return information about the cipher handle.
- * -1 is returned on error and gcry_errno() may be used to get more information
- * about the error.
+/* 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.
+
  */
-int
-gcry_cipher_info( GCRY_CIPHER_HD h, int cmd, void *buffer, size_t *nbytes)
+gcry_error_t
+gcry_cipher_info (gcry_cipher_hd_t h, int cmd, void *buffer, size_t *nbytes)
 {
-    switch( cmd ) {
-      default:
-       set_lasterr( GCRYERR_INV_OP );
-       return -1;
+  gcry_err_code_t err = GPG_ERR_NO_ERROR;
+
+  (void)h;
+  (void)buffer;
+  (void)nbytes;
+
+  switch (cmd)
+    {
+    default:
+      err = GPG_ERR_INV_OP;
     }
-    return 0;
+
+  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.
- *
- * On error the value -1 is returned and the error reason may be
- * retrieved by gcry_errno().
* 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)
  */
-int
-gcry_cipher_algo_infoint algo, int what, void *buffer, size_t *nbytes)
+gcry_error_t
+gcry_cipher_algo_info (int algo, int what, void *buffer, size_t *nbytes)
 {
-    unsigned int ui;
+  gcry_err_code_t err = GPG_ERR_NO_ERROR;
+  unsigned int ui;
 
-    switch( what ) {
-      case GCRYCTL_GET_KEYLEN:
-       if( buffer || nbytes ) {
-           set_lasterr( GCRYERR_INV_CIPHER_ALGO );
-           break;
+  switch (what)
+    {
+    case GCRYCTL_GET_KEYLEN:
+      if (buffer || (! nbytes))
+       err = GPG_ERR_CIPHER_ALGO;
+      else
+       {
+         ui = cipher_get_keylen (algo);
+         if ((ui > 0) && (ui <= 512))
+           *nbytes = (size_t) ui / 8;
+         else
+           /* The only reason is an invalid algo or a strange
+              blocksize.  */
+           err = GPG_ERR_CIPHER_ALGO;
        }
-       ui = cipher_get_keylen( algo );
-       if( ui > 0 && ui <= 512 )
-           return (int)ui/8;
-       /* the only reason is an invalid algo or a strange blocksize */
-       set_lasterr( GCRYERR_INV_CIPHER_ALGO );
-       break;
+      break;
 
-      case GCRYCTL_GET_BLKLEN:
-       if( buffer || nbytes ) {
-           set_lasterr( GCRYERR_INV_CIPHER_ALGO );
-           break;
+    case GCRYCTL_GET_BLKLEN:
+      if (buffer || (! nbytes))
+       err = GPG_ERR_CIPHER_ALGO;
+      else
+       {
+         ui = cipher_get_blocksize (algo);
+         if ((ui > 0) && (ui < 10000))
+           *nbytes = ui;
+         else
+           /* The only reason is an invalid algo or a strange
+              blocksize.  */
+           err = GPG_ERR_CIPHER_ALGO;
        }
-       ui = cipher_get_blocksize( algo );
-       if( ui > 0 && ui < 10000 )
-           return (int)ui;
-       /* the only reason is an invalid algo or a strange blocksize */
-       set_lasterr( GCRYERR_INV_CIPHER_ALGO );
-       break;
+      break;
 
-      case GCRYCTL_TEST_ALGO:
-       if( buffer || nbytes ) {
-           set_lasterr( GCRYERR_INV_ARG );
-           break;
-       }
-       if( check_cipher_algo( algo ) ) {
-           set_lasterr( GCRYERR_INV_CIPHER_ALGO );
-           break;
-       }
-       return 0;
+    case GCRYCTL_TEST_ALGO:
+      if (buffer || nbytes)
+       err = GPG_ERR_INV_ARG;
+      else
+       err = check_cipher_algo (algo);
+      break;
 
       default:
-       set_lasterr( GCRYERR_INV_OP );
+       err = GPG_ERR_INV_OP;
     }
-    return -1;
+
+  return gcry_error (err);
+}
+
+
+/* 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
+gcry_cipher_get_algo_keylen (int algo)
+{
+  size_t n;
+
+  if (gcry_cipher_algo_info (algo, GCRYCTL_GET_KEYLEN, NULL, &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
+gcry_cipher_get_algo_blklen (int algo)
+{
+  size_t n;
+
+  if (gcry_cipher_algo_info( algo, GCRYCTL_GET_BLKLEN, NULL, &n))
+    n = 0;
+  return n;
+}
+
+/* Explicitly initialize this module.  */
+gcry_err_code_t
+_gcry_cipher_init (void)
+{
+  gcry_err_code_t err = GPG_ERR_NO_ERROR;
+
+  REGISTER_DEFAULT_CIPHERS;
+
+  return err;
+}
+
+/* Get a list consisting of the IDs of the loaded cipher modules.  If
+   LIST is zero, write the number of loaded cipher modules to
+   LIST_LENGTH and return.  If LIST is non-zero, the first
+   *LIST_LENGTH algorithm IDs are stored in LIST, which must be of
+   according size.  In case there are less cipher modules than
+   *LIST_LENGTH, *LIST_LENGTH is updated to the correct number.  */
+gcry_error_t
+gcry_cipher_list (int *list, int *list_length)
+{
+  gcry_err_code_t err = GPG_ERR_NO_ERROR;
+
+  ath_mutex_lock (&ciphers_registered_lock);
+  err = _gcry_module_list (ciphers_registered, list, list_length);
+  ath_mutex_unlock (&ciphers_registered_lock);
+
+  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);
+}