CTR mode may now be used with arbitrary long data chunks.
[libgcrypt.git] / cipher / cipher.c
index d36db66..90fdb17 100644 (file)
@@ -1,21 +1,21 @@
 /* cipher.c  - cipher dispatcher
- *     Copyright (C) 1998 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 GnuPG.
+ * This file is part of Libgcrypt.
  *
- * GnuPG is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser general Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
  *
- * GnuPG is distributed in the hope that it will be useful,
+ * Libgcrypt is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
+ * GNU Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU 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
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 "dynload.h"
+#include "ath.h"
 
 #define MAX_BLOCKSIZE 16
-#define TABLE_SIZE 12
+#define TABLE_SIZE 14
 #define CTX_MAGIC_NORMAL 0x24091964
 #define CTX_MAGIC_SECURE 0x46919042
 
-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 );
-};
+/* 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 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 );
-    PROPERLY_ALIGNED_TYPE context;
+
+/* 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;
+
+  /* A structure with function pointers for bulk operations.  Due to
+     limitations of the module system (we don't want to change the
+     API) we need to keep these function pointers here.  The cipher
+     open function intializes them and the actual encryption routines
+     use them if they are not NULL.  */
+  struct {
+    void (*cfb_enc)(void *context, unsigned char *iv,
+                    void *outbuf_arg, const void *inbuf_arg,
+                    unsigned int nblocks);
+    void (*cfb_dec)(void *context, unsigned char *iv,
+                    void *outbuf_arg, const void *inbuf_arg,
+                    unsigned int nblocks);
+    void (*cbc_enc)(void *context, unsigned char *iv,
+                    void *outbuf_arg, const void *inbuf_arg,
+                    unsigned int nblocks, int cbc_mac);
+    void (*cbc_dec)(void *context, unsigned char *iv,
+                    void *outbuf_arg, const void *inbuf_arg,
+                    unsigned int nblocks);
+    void (*ctr_enc)(void *context, unsigned char *iv,
+                    void *outbuf_arg, const void *inbuf_arg,
+                    unsigned int nblocks);
+  } bulk;
+
+
+  int mode;
+  unsigned int flags;
+
+  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; }
+\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_block( void *c, byte *outbuf, byte *inbuf ) { BUG(); }
+dummy_encrypt_block (void *c,
+                    unsigned char *outbuf, const unsigned char *inbuf)
+{
+  (void)c;
+  (void)outbuf;
+  (void)inbuf;
+  BUG();
+}
+
 static void
-dummy_decrypt_block( void *c, byte *outbuf, byte *inbuf ) { 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)
-{
-    int i;
-
-    i = 0;
-    cipher_table[i].algo = GCRY_CIPHER_RIJNDAEL;
-    cipher_table[i].name = 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 = 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 = 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 = 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 = 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 = 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 = 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;
-    cipher_table[i].encrypt = dummy_encrypt_block;
-    cipher_table[i].decrypt = dummy_decrypt_block;
-    i++;
-
-    for( ; i < TABLE_SIZE; i++ )
-       cipher_table[i].name = NULL;
+cipher_register_default (void)
+{
+  gcry_err_code_t err = GPG_ERR_NO_ERROR;
+  int i;
+
+  for (i = 0; !err && cipher_table[i].cipher; i++)
+    {
+      if (! cipher_table[i].cipher->setkey)
+       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);
+    }
+
+  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);
 
-/****************
- * Try to load all modules and return true if new modules are available
- */
+  if (aliases)
+    for (i = 0; aliases[i] && (! ret); i++)
+      ret = ! stricmp (name, aliases[i]);
+
+  return ret;
+}
+
+/* Internal callback function.  Used via _gcry_module_lookup.  */
 static int
-load_cipher_modules(void)
-{
-    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 ) {
-       cipher_modules_constructor();
-       setup_cipher_table(); /* load static modules on the first call */
-       initialized = 1;
-       return 1;
-    }
-
-    if( done )
-       return 0;
-    done = 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 = 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( g10_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;
-       }
-    }
-    enum_gnupgext_ciphers( &context, NULL, NULL, NULL, NULL,
-                                          NULL, NULL, NULL );
-    return any;
+gcry_cipher_lookup_func_oid (void *spec, void *data)
+{
+  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 (oid_specs)
+    for (i = 0; oid_specs[i].oid && (! ret); i++)
+      if (! stricmp (oid, oid_specs[i].oid))
+       ret = 1;
+
+  return ret;
 }
 
+/* Internal function.  Lookup a cipher entry by it's name.  */
+static gcry_module_t
+gcry_cipher_lookup_name (const char *name)
+{
+  gcry_module_t cipher;
 
-/****************
- * 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.
- */
-int
-gcry_cipher_map_name( const char *string )
+  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)
 {
-    int i;
-    const char *s;
+  gcry_module_t cipher;
 
-    do {
-       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_module_lookup (ciphers_registered, (void *) oid,
+                               gcry_cipher_lookup_func_oid);
+
+  return cipher;
 }
 
-/****************
- * Map a cipher algo to a string
- */
-static const char *
-cipher_algo_to_string( int algo )
+/* 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)
 {
-    int i;
+  gcry_err_code_t err = 0;
+  gcry_module_t mod;
+
+  /* We do not support module loading in fips mode.  */
+  if (fips_mode ())
+    return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
+  ath_mutex_lock (&ciphers_registered_lock);
+  err = _gcry_module_add (&ciphers_registered, 0,
+                         (void *)cipher,
+                         (void *)(extraspec? extraspec : &dummy_extra_spec),
+                          &mod);
+  ath_mutex_unlock (&ciphers_registered_lock);
+
+  if (! err)
+    {
+      *module = mod;
+      *algorithm_id = mod->mod_id;
+    }
 
-    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;
+  return gcry_error (err);
 }
 
-/****************
- * This function simply returns the name of the algorithm or some constant
- * string when there is no algo.  It will never return NULL.
- */
-const char *
-gcry_cipher_algo_name( int algo )
+/* Unregister the cipher identified by MODULE, which must have been
+   registered with gcry_cipher_register.  */
+void
+gcry_cipher_unregister (gcry_module_t module)
 {
-    const char *s = cipher_algo_to_string( algo );
-    return s? s: "";
+  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 ret;
+}
 
-static void
-disable_cipher_algo( int algo )
+/* 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)
 {
-    int i;
+  gcry_module_t cipher;
+  int ret, algorithm = 0;
+
+  if (! string)
+    return 0;
+
+  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 */
 
-    for(i=0; i < DIM(disabled_algos); i++ ) {
-       if( !disabled_algos[i] || disabled_algos[i] == algo ) {
-           disabled_algos[i] = algo;
-           return;
+  ath_mutex_lock (&ciphers_registered_lock);
+
+  ret = search_oid (string, &algorithm, NULL);
+  if (! ret)
+    {
+      cipher = gcry_cipher_lookup_name (string);
+      if (cipher)
+       {
+         algorithm = cipher->mod_id;
+         _gcry_module_release (cipher);
        }
     }
-    /* fixme: we should use a linked list */
-    log_fatal("can't disable cipher algo %d: table full\n", algo );
-}
 
-/****************
- * Return 0 if the cipher algo is available
- */
-static int
-check_cipher_algo( int algo )
-{
-    int i;
+  ath_mutex_unlock (&ciphers_registered_lock);
 
-    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;
+  return algorithm;
 }
 
 
-static unsigned
-cipher_get_keylen( int algo )
+/* 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;
-    unsigned len = 0;
+  gcry_cipher_oid_spec_t oid_spec;
+  int ret = 0, mode = 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 );
+  if (!string)
     return 0;
+
+  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;
 }
 
-static unsigned
-cipher_get_blocksize( int algo )
+
+/* 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 algorithm)
 {
-    int i;
-    unsigned len = 0;
+  gcry_module_t cipher;
+  const char *name;
 
-    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;
+  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;
 }
 
+/* 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 algorithm)
+{
+  return cipher_algo_to_string (algorithm);
+}
 
-/****************
- * 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.
- */
 
-GCRY_CIPHER_HD
-gcry_cipher_open( int algo, int mode, unsigned int flags )
+/* 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 algorithm)
 {
-    GCRY_CIPHER_HD h;
-    int idx;
-    int secure = (flags & GCRY_CIPHER_SECURE);
+  gcry_module_t cipher;
 
-    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)
+    {
+      if (! (cipher->flags & FLAG_MODULE_DISABLED))
+       cipher->flags |= FLAG_MODULE_DISABLED;
+      _gcry_module_release (cipher);
     }
+  ath_mutex_unlock (&ciphers_registered_lock);
+}
 
-    /* check flags */
-    if( (flags & ~(GCRY_CIPHER_SECURE|GCRY_CIPHER_ENABLE_SYNC)) ) {
-       set_lasterr( GCRYERR_INV_CIPHER_ALGO );
-       return NULL;
+
+/* 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)
+{
+  gcry_err_code_t err = GPG_ERR_NO_ERROR;
+  gcry_module_t cipher;
+
+  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)
+       err = GPG_ERR_CIPHER_ALGO;
+      _gcry_module_release (cipher);
     }
+  else
+    err = GPG_ERR_CIPHER_ALGO;
+  ath_mutex_unlock (&ciphers_registered_lock);
 
-    /* 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 */
+  return err;
+}
 
-    if( algo == CIPHER_ALGO_DUMMY )
-       mode = GCRY_CIPHER_MODE_NONE;  /* force this mode for dummy algo */
 
-    /* check that a valid mode has been requested */
-    switch( mode ) {
+/* 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)
+{
+  gcry_module_t cipher;
+  unsigned len = 0;
+
+  REGISTER_DEFAULT_CIPHERS;
+
+  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;
+}
+
+/* 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_module_t cipher;
+  unsigned len = 0;
+
+  REGISTER_DEFAULT_CIPHERS;
+
+  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);
+
+  return len;
+}
+
+
+/*
+   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.  */
+
+      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:
+      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->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_ALGO );
-       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 ? g10_calloc_secure( 1, sizeof *h
-                                      + cipher_table[idx].contextsize
-                                      - sizeof(PROPERLY_ALIGNED_TYPE) )
-              : g10_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;
 
-    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 )
-       g10_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;
-    g10_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)
 {
-    unsigned n;
+  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 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;
-
-    for(n=0; n < nblocks; n++ ) {
-       (*c->decrypt)( &c->context.c, outbuf, (byte*)/*arggg*/inbuf );
-       inbuf  += c->blocksize;
-       outbuf += c->blocksize;
+  unsigned int blocksize = c->cipher->blocksize;
+  unsigned int n, nblocks;
+
+  if (outbuflen < inbuflen)
+    return GPG_ERR_BUFFER_TOO_SHORT;
+  if ((inbuflen % blocksize))
+    return GPG_ERR_INV_LENGTH;
+  nblocks = inbuflen / c->cipher->blocksize;
+
+  for (n=0; n < nblocks; n++ )
+    {
+      c->cipher->decrypt (&c->context.c, outbuf, (byte*)/*arggg*/inbuf );
+      inbuf  += blocksize;
+      outbuf += blocksize;
     }
+
+  return 0;
 }
 
-static void
-do_cbc_encrypt( GCRY_CIPHER_HD c, byte *outbuf, const byte *inbuf, unsigned nblocks )
+
+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 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--;
+    }
+
+  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;
+        }
+    }
 
-    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->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 ((inbuflen % blocksize) == 0)
+        restbytes = blocksize;
+      else
+        restbytes = inbuflen % blocksize;
+
+      outbuf -= blocksize;
+      for (ivp = c->u_iv.iv, i = 0; i < restbytes; i++)
+        {
+          b = inbuf[i];
+          outbuf[blocksize + i] = outbuf[i];
+          outbuf[i] = b ^ *ivp++;
+        }
+      for (; i < blocksize; i++)
+        outbuf[i] = 0 ^ *ivp++;
+
+      c->cipher->encrypt (&c->context.c, outbuf, outbuf);
+      memcpy (c->u_iv.iv, outbuf, blocksize);
     }
+
+  return 0;
 }
 
-static void
-do_cbc_decrypt( GCRY_CIPHER_HD c, byte *outbuf, const byte *inbuf, unsigned nblocks )
-{
-    unsigned int n;
-    byte *ivp;
-    int i;
-    size_t blocksize = c->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++ )
+
+static gcry_err_code_t
+do_cbc_decrypt (gcry_cipher_hd_t c,
+                unsigned char *outbuf, unsigned int outbuflen,
+                const unsigned char *inbuf, unsigned int inbuflen)
+{
+  unsigned int n;
+  unsigned char *ivp;
+  int i;
+  size_t blocksize = c->cipher->blocksize;
+  unsigned int nblocks = 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) && inbuflen > blocksize)
+    {
+      nblocks--;
+      if ((inbuflen % blocksize) == 0)
+       nblocks--;
+      memcpy (c->lastiv, c->u_iv.iv, blocksize);
+    }
+
+  if (c->bulk.cbc_dec)
+    {
+      c->bulk.cbc_dec (&c->context.c, c->u_iv.iv, outbuf, inbuf, nblocks);
+      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) && inbuflen > blocksize)
+    {
+      int restbytes;
+
+      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;
+  unsigned char *ivp;
+  size_t blocksize = c->cipher->blocksize;
+  size_t blocksize_x_2 = blocksize + blocksize;
+
+  if (outbuflen < inbuflen)
+    return GPG_ERR_BUFFER_TOO_SHORT;
+
+  if ( inbuflen <= c->unused )
+    {
+      /* Short enough to be encoded by the remaining XOR mask. */
+      /* XOR the input with the IV and store input into IV. */
+      for (ivp=c->u_iv.iv+c->cipher->blocksize - c->unused;
+           inbuflen;
+           inbuflen--, c->unused-- )
+        *outbuf++ = (*ivp++ ^= *inbuf++);
+      return 0;
+    }
 
-    if( 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;
+  if ( c->unused )
+    {
+      /* XOR the input with the IV and store input into IV */
+      inbuflen -= c->unused;
+      for(ivp=c->u_iv.iv+blocksize - c->unused; c->unused; c->unused-- )
+        *outbuf++ = (*ivp++ ^= *inbuf++);
     }
 
-    if( c->unused ) {
-       /* XOR the input with the IV and store input into IV */
-       nbytes -= c->unused;
-       for(ivp=c->iv+blocksize - c->unused; c->unused; c->unused-- )
-           *outbuf++ = (*ivp++ ^= *inbuf++);
+  /* Now we can process complete blocks.  We use a loop as long as we
+     have at least 2 blocks and use conditions for the rest.  This
+     also allows to use a bulk encryption function if available.  */
+  if (inbuflen >= blocksize_x_2 && c->bulk.cfb_enc)
+    {
+      unsigned int nblocks = inbuflen / blocksize;
+      c->bulk.cfb_enc (&c->context.c, c->u_iv.iv, outbuf, inbuf, nblocks);
+      outbuf += nblocks * blocksize;
+      inbuf  += nblocks * blocksize;
+      inbuflen -= nblocks * blocksize;
+    }
+  else
+    {
+      while ( inbuflen >= blocksize_x_2 )
+        {
+          int i;
+          /* Encrypt the IV. */
+          c->cipher->encrypt ( &c->context.c, c->u_iv.iv, c->u_iv.iv );
+          /* XOR the input with the IV and store input into IV.  */
+          for(ivp=c->u_iv.iv,i=0; i < blocksize; i++ )
+            *outbuf++ = (*ivp++ ^= *inbuf++);
+          inbuflen -= blocksize;
+        }
     }
 
-    /* now we can process complete blocks */
-    while( nbytes >= blocksize ) {
-       int i;
-       /* encrypt the IV (and save the current one) */
-       memcpy( c->lastiv, c->iv, blocksize );
-       (*c->encrypt)( &c->context.c, c->iv, c->iv );
-       /* XOR the input with the IV and store input into IV */
-       for(ivp=c->iv,i=0; i < blocksize; i++ )
-           *outbuf++ = (*ivp++ ^= *inbuf++);
-       nbytes -= blocksize;
+  if ( inbuflen >= blocksize )
+    {
+      int i;
+      /* Save the current IV and then encrypt the IV. */
+      memcpy( c->lastiv, c->u_iv.iv, blocksize );
+      c->cipher->encrypt ( &c->context.c, c->u_iv.iv, c->u_iv.iv );
+      /* XOR the input with the IV and store input into IV */
+      for(ivp=c->u_iv.iv,i=0; i < blocksize; i++ )
+        *outbuf++ = (*ivp++ ^= *inbuf++);
+      inbuflen -= blocksize;
     }
-    if( nbytes ) { /* process the remaining bytes */
-       /* encrypt the IV (and save the current one) */
-       memcpy( c->lastiv, c->iv, blocksize );
-       (*c->encrypt)( &c->context.c, c->iv, c->iv );
-       c->unused = blocksize;
-       /* and apply the xor */
-       c->unused -= nbytes;
-       for(ivp=c->iv; nbytes; nbytes-- )
-           *outbuf++ = (*ivp++ ^= *inbuf++);
+  if ( inbuflen )
+    {
+      /* Save the current IV and then encrypt the IV. */
+      memcpy( c->lastiv, c->u_iv.iv, blocksize );
+      c->cipher->encrypt ( &c->context.c, c->u_iv.iv, c->u_iv.iv );
+      c->unused = blocksize;
+      /* Apply the XOR. */
+      c->unused -= inbuflen;
+      for(ivp=c->u_iv.iv; inbuflen; inbuflen-- )
+        *outbuf++ = (*ivp++ ^= *inbuf++);
     }
+  return 0;
 }
 
-static void
-do_cfb_decrypt( GCRY_CIPHER_HD 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;
+
+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;
+
+  if (inbuflen <= c->unused)
+    {
+      /* Short enough to be encoded by the remaining XOR mask. */
+      /* XOR the input with the IV and store input into IV. */
+      for (ivp=c->u_iv.iv+blocksize - c->unused;
+           inbuflen;
+           inbuflen--, c->unused--)
+        {
+          temp = *inbuf++;
+          *outbuf++ = *ivp ^ temp;
+          *ivp++ = temp;
+        }
+      return 0;
     }
 
-    if( c->unused ) {
-       /* XOR the input with the IV and store input into IV */
-       nbytes -= c->unused;
-       for(ivp=c->iv+blocksize - c->unused; c->unused; c->unused-- ) {
-           temp = *inbuf++;
-           *outbuf++ = *ivp ^ temp;
-           *ivp++ = temp;
-       }
+  if (c->unused)
+    {
+      /* XOR the input with the IV and store input into IV. */
+      inbuflen -= c->unused;
+      for (ivp=c->u_iv.iv+blocksize - c->unused; c->unused; c->unused-- )
+        {
+          temp = *inbuf++;
+          *outbuf++ = *ivp ^ temp;
+          *ivp++ = temp;
+        }
     }
 
-    /* now we can process complete blocks */
-    while( nbytes >= blocksize ) {
-       int i;
-       /* encrypt the IV (and save the current one) */
-       memcpy( c->lastiv, c->iv, blocksize );
-       (*c->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( 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;
-       }
+  /* 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)
+{
+  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. */
+      /* 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 j? GPG_ERR_CHECKSUM : 0;
 }
 
 
 /****************
  * Encrypt INBUF to OUTBUF with the mode selected at open.
  * inbuf and outbuf may overlap or be the same.
- * Depending on the mode some some contraints apply to NBYTES.
+ * Depending on the mode some constraints apply to INBUFLEN.
  */
-static void
-cipher_encrypt( GCRY_CIPHER_HD c, byte *outbuf,
-                                 const byte *inbuf, unsigned nbytes )
+static gcry_err_code_t
+cipher_encrypt (gcry_cipher_hd_t c, byte *outbuf, unsigned int outbuflen,
+               const byte *inbuf, unsigned int inbuflen)
 {
-    switch( c->mode ) {
-      case GCRY_CIPHER_MODE_ECB:
-       assert(!(nbytes%8));
-       do_ecb_encrypt(c, outbuf, inbuf, nbytes/8 );
-       break;
-      case GCRY_CIPHER_MODE_CBC:
-       assert(!(nbytes%8));  /* fixme: should be blocksize */
-       do_cbc_encrypt(c, outbuf, inbuf, nbytes/8 );
-       break;
-      case GCRY_CIPHER_MODE_CFB:
-       do_cfb_encrypt(c, outbuf, 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 );
+  gcry_err_code_t rc;
+
+  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;
 }
 
 
 /****************
  * Encrypt IN and write it to OUT.  If IN is NULL, in-place encryption has
- * been requested,
+ * been requested.
  */
-int
-gcry_cipher_encrypt( GCRY_CIPHER_HD h, byte *out, size_t outsize,
-                                      const byte  *in, size_t inlen )
-{
-    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 */
-       cipher_encrypt( h, out, out, outsize );
-    }
-    else {
-       if( outsize < inlen )
-           return set_lasterr( GCRYERR_TOO_SHORT );
-       /* fixme: check that the inlength is a multipe of the blocksize
-        * if a blockoriented mode is used, or modify cipher_encrypt to
-        * return an error in this case */
-       cipher_encrypt( h, out, in, inlen );
-    }
-    return 0;
+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);
 }
 
 
@@ -711,52 +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 void
-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)
 {
-    switch( c->mode ) {
-      case GCRY_CIPHER_MODE_ECB:
-       assert(!(nbytes%8));
-       do_ecb_decrypt(c, outbuf, inbuf, nbytes/8 );
-       break;
-      case GCRY_CIPHER_MODE_CBC:
-       assert(!(nbytes%8));    /* fixme: should assert on blocksize */
-       do_cbc_decrypt(c, outbuf, inbuf, nbytes/8 );
-       break;
-      case GCRY_CIPHER_MODE_CFB:
-       do_cfb_decrypt(c, outbuf, 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 );
+  gcry_err_code_t rc;
+
+  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;
 }
 
 
-int
-gcry_cipher_decrypt( GCRY_CIPHER_HD h, byte *out, size_t outsize,
-                                const byte  *in, size_t inlen )
-{
-    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 */
-       cipher_decrypt( h, out, out, outsize );
-    }
-    else {
-       if( outsize < inlen )
-           return set_lasterr( GCRYERR_TOO_SHORT );
-       /* fixme: check that the inlength is a multipe of the blocksize
-        * if a blockoriented mode is used, or modify cipher_encrypt to
-        * return an error in this case */
-       cipher_decrypt( h, out, in, inlen );
-    }
-    return 0;
+gcry_error_t
+gcry_cipher_decrypt (gcry_cipher_hd_t h, void *out, size_t outsize,
+                    const void *in, size_t inlen)
+{
+  gcry_err_code_t err;
+
+  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);
 }
 
 
@@ -766,132 +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)
 {
-    switch( cmd ) {
-      case GCRYCTL_SET_KEY:
-       cipher_setkey( h, buffer, buflen );
-       break;
-      case GCRYCTL_SET_IV:
-       cipher_setiv( h, buffer, buflen );
-       break;
-      case GCRYCTL_CFB_SYNC:
-       cipher_sync( h );
-       break;
+  return cipher_setkey (hd, (void*)key, keylen);
+}
 
-      case GCRYCTL_DISABLE_ALGO:
-       /* this one expects a NULL handle and buffer pointing to an
-        * integer with the algo number.
-        */
-       if( h || !buffer || buflen != sizeof(int) )
-           return set_lasterr( GCRYERR_INV_CIPHER_ALGO );
-       disable_cipher_algo( *(int*)buffer );
-       break;
 
-      default:
-       return set_lasterr( GCRYERR_INV_OP );
+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;
     }
-    return 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;
 }
 
 
-/****************
- * Return information about the cipher handle.
- * -1 is returned on error and gcry_errno() may be used to get more information
- * about the error.
- */
-int
-gcry_cipher_info( GCRY_CIPHER_HD h, int cmd, void *buffer, size_t *nbytes)
+gcry_error_t
+gcry_cipher_ctl( gcry_cipher_hd_t h, int cmd, void *buffer, size_t buflen)
 {
-    switch( cmd ) {
-      default:
-       set_lasterr( GCRYERR_INV_OP );
-       return -1;
+  gcry_err_code_t rc = GPG_ERR_NO_ERROR;
+
+  switch (cmd)
+    {
+    case GCRYCTL_SET_KEY:  /* Deprecated; use gcry_cipher_setkey.  */
+      rc = cipher_setkey( h, buffer, buflen );
+      break;
+
+    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)
+       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 command expects NULL for H and BUFFER to point to an
+         integer with the algo number.  */
+      if( h || !buffer || buflen != sizeof(int) )
+       return gcry_error (GPG_ERR_CIPHER_ALGO);
+      disable_cipher_algo( *(int*)buffer );
+      break;
+
+    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 = GPG_ERR_INV_OP;
     }
-    return 0;
+
+  return gcry_error (rc);
 }
 
-/****************
- * 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 caes 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 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_algo_info( int algo, int what, void *buffer, size_t *nbytes)
+gcry_error_t
+gcry_cipher_info (gcry_cipher_hd_t h, int cmd, void *buffer, size_t *nbytes)
 {
-    unsigned int ui;
+  gcry_err_code_t err = GPG_ERR_NO_ERROR;
 
-    switch( what ) {
-      case GCRYCTL_GET_KEYLEN:
-       if( buffer || nbytes ) {
-           set_lasterr( GCRYERR_INV_CIPHER_ALGO );
-           break;
-       }
-       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;
+  (void)h;
+  (void)buffer;
+  (void)nbytes;
 
-      case GCRYCTL_GET_BLKLEN:
-       if( buffer || nbytes ) {
-           set_lasterr( GCRYERR_INV_CIPHER_ALGO );
-           break;
-       }
-       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;
+  switch (cmd)
+    {
+    default:
+      err = GPG_ERR_INV_OP;
+    }
+
+  return gcry_error (err);
+}
+
+/* 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.
 
-      case GCRYCTL_TEST_ALGO:
-       if( buffer || nbytes ) {
-           set_lasterr( GCRYERR_INV_ARG );
-           break;
+   Note: Because this function is in most cases used to return an
+   integer value, we can make it easier for the caller to just look at
+   the return value.  The caller will in all cases consult the value
+   and thereby detecting whether a error occurred or not (i.e. while
+   checking the block size)
+ */
+gcry_error_t
+gcry_cipher_algo_info (int algo, int what, void *buffer, size_t *nbytes)
+{
+  gcry_err_code_t err = GPG_ERR_NO_ERROR;
+  unsigned int ui;
+
+  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;
        }
-       if( check_cipher_algo( algo ) ) {
-           set_lasterr( GCRYERR_INV_CIPHER_ALGO );
-           break;
+      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;
        }
-       return 0;
+      break;
+
+    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);
+}