See ChangeLog: Sat Sep 18 12:16:08 CEST 1999 Werner Koch
authorWerner Koch <wk@gnupg.org>
Sat, 18 Sep 1999 10:17:18 +0000 (10:17 +0000)
committerWerner Koch <wk@gnupg.org>
Sat, 18 Sep 1999 10:17:18 +0000 (10:17 +0000)
cipher/Makefile.am
cipher/cipher.c
cipher/rndw32.c [new file with mode: 0644]
src/gcrypt.h
src/mdapi.c
src/symapi.c

index 3d9a5cb..e68ee19 100644 (file)
@@ -99,4 +99,9 @@ rndlinux: $(srcdir)/rndlinux.c
 rndegd: $(srcdir)/rndegd.c
        $(COMPILE) $(DYNLINK_MOD_CFLAGS) -o rndegd $(srcdir)/rndegd.c
 
+# We need to have a better system for selection which modules
+# to compile.  For now this works.
+rndw32: $(srcdir)/rndw32.c
+       echo "#!/bin/sh" >rndw32
+       echo "Not usable as a module" >>rndw32
 
index 990671f..7224ab6 100644 (file)
@@ -362,6 +362,7 @@ cipher_open( int algo, int mode, int secure )
     if( algo == CIPHER_ALGO_DUMMY )
        hd->mode = CIPHER_MODE_DUMMY;
     else if( mode == CIPHER_MODE_AUTO_CFB ) {
+       #warning Remove this code and the AUTO:CFB macro.
        if( algo >= 100 )
            hd->mode = CIPHER_MODE_CFB;
        else
diff --git a/cipher/rndw32.c b/cipher/rndw32.c
new file mode 100644 (file)
index 0000000..032d666
--- /dev/null
@@ -0,0 +1,265 @@
+/* rndw32.c  - interface to the Winseed DLL
+ *     Copyright (C) 1999 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * 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.
+ *
+ * GnuPG 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.
+ *
+ * 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
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+
+#include <windows.h>
+
+#include "types.h"
+#include "util.h"
+#include "dynload.h"
+
+
+#ifdef IS_MODULE
+  #define _(a) (a)
+#else
+  #include "i18n.h"
+#endif
+
+
+#define WIN32_SLOW_SEEDER      0
+#define WIN32_FAST_SEEDER      1
+
+#define PCP_SUCCESS            0
+#define PCP_NULL_POINTER       1
+#define PCP_SEEDER_FAILED      2
+#define PCP_SEEDER_NO_MEM      3
+#define PCP_SEEDER_TOO_SMALL   4
+#define PCP_DLL_LOAD_FAILED    5
+#define PCP_UNKNOWN_PLATFORM   6
+#define PCP_ERROR_VERSION      7
+#define PCP_DLL_FUNC           8
+#define PCP_UNKNOWN_SEEDER_TYPE 9
+
+typedef void *WIN32_SEEDER;
+
+static WIN32_SEEDER (WINAPI *create_instance)( byte type, unsigned int *reason);
+static void        (WINAPI *delete_instance)( WIN32_SEEDER that );
+static unsigned int (WINAPI *get_internal_seed_size)( WIN32_SEEDER that );
+static void        (WINAPI *set_internal_seed_size)( WIN32_SEEDER that,
+                                                     unsigned int new_size);
+static unsigned int (WINAPI *get_expected_seed_size)( WIN32_SEEDER that);
+static unsigned int (WINAPI *get_seed)( WIN32_SEEDER that, byte *buffer,
+                                       unsigned int *desired_length);
+
+static WIN32_SEEDER slow_seeder, fast_seeder;
+static byte *entropy_buffer;
+static size_t entropy_buffer_size;
+
+static char *entropy_dll;
+
+/****************
+ * Load and initialize the winseed DLL
+ * NOTE: winseed is not part of the GnuPG distribution.  It should be available
+ * at the GNU crypto FTP server site.
+ * We do not load the DLL on demand to have a better control over the
+ * location of the library.
+ */
+static void
+load_and_init_winseed( void )
+{
+    int hInstance;
+    void *addr;
+    unsigned int reason = 0;
+    unsigned int n1, n2;
+    const char *dllname = entropy_dll? entropy_dll : "c:/gnupg/entropy.dll";
+
+    hInstance = LoadLibrary( dllname );
+    if( !hInstance )
+       goto failure;
+    if( !(addr = GetProcAddress( hInstance, "WS_create_instance" )) )
+       goto failure;
+    create_instance = addr;
+    if( !(addr = GetProcAddress( hInstance, "WS_delete_instance" )) )
+       goto failure;
+    delete_instance = addr;
+    if( !(addr = GetProcAddress( hInstance, "WS_get_internal_seed_size" )) )
+       goto failure;
+    get_internal_seed_size = addr;
+    if( !(addr = GetProcAddress( hInstance, "WS_set_internal_seed_size" )) )
+       goto failure;
+    set_internal_seed_size = addr;
+    if( !(addr = GetProcAddress( hInstance, "WS_get_expected_seed_size" )) )
+       goto failure;
+    get_expected_seed_size = addr;
+    if( !(addr = GetProcAddress( hInstance, "WS_get_seed" )) )
+       goto failure;
+    get_seed = addr;
+
+    /* we have all the functions - init the system */
+    slow_seeder = create_instance( WIN32_SLOW_SEEDER, &reason);
+    if( !slow_seeder ) {
+       g10_log_fatal("error creating winseed slow seeder: rc=%u\n", reason );
+       goto failure;
+    }
+    fast_seeder = create_instance( WIN32_FAST_SEEDER, &reason);
+    if( !fast_seeder ) {
+       g10_log_fatal("error creating winseed fast seeder: rc=%u\n", reason );
+       goto failure;
+    }
+    g10_log_info("slow and fast seeders created.\n");
+    n1 = get_internal_seed_size( slow_seeder );
+    g10_log_info("slow buffer size=%u\n", n1);
+    n2 = get_internal_seed_size( fast_seeder );
+    g10_log_info("fast buffer size=%u\n", n2);
+
+    entropy_buffer_size =  n1 > n2? n1: n2;
+    entropy_buffer = m_alloc( entropy_buffer_size );
+    g10_log_info("using a buffer of size=%u\n", entropy_buffer_size );
+
+    return;
+
+  failure:
+    g10_log_fatal("error loading winseed DLL `%s'\n", dllname );
+}
+
+
+
+
+
+/* Note: we always use the highest level.
+ * TO boost the performance we may want to add some
+ * additional code for level 1
+ */
+static int
+gather_random( void (*add)(const void*, size_t, int), int requester,
+                                         size_t length, int level )
+{
+    unsigned int result;
+    unsigned int nbytes;
+
+    if( !slow_seeder )
+       load_and_init_winseed();
+
+    /* Our estimation on how much entropy we should use is very vague.
+     * Winseed delivers some amount of entropy on each slow poll and
+     * we add it to our random pool.  Depending on the required quality
+     * level we adjust the requested length so that for higer quality
+     * we make sure to add more entropy to our pool.  However, as we don't
+     * like to waste any entropy collected by winseed, we always add
+     * at least everything we got from winseed.
+     */
+    if( level > 1 )
+       length *= 100;
+    else if( level > 0 )
+       length *= 10;
+
+    for(;;) {
+       nbytes = entropy_buffer_size;
+       result = get_seed( slow_seeder, entropy_buffer, &nbytes);
+       if( result ) {
+           g10_log_fatal("rndw32: get_seed(slow) failed: rc=%u\n", result);
+           return -1; /* actually never reached */
+       }
+       g10_log_info("rndw32: slow poll level %d, need %u, got %u\n",
+                     level, (unsigned int)length, (unsigned int)nbytes );
+       (*add)( entropy_buffer, nbytes, requester );
+       if( length <= nbytes )
+           return 0; /* okay */
+       length -= nbytes;
+       g10_log_info("rndw32: need more\n");
+    }
+}
+
+static int
+gather_random_fast( void (*add)(const void*, size_t, int), int requester )
+{
+    unsigned int result;
+    unsigned int nbytes;
+
+    if( !fast_seeder )
+       load_and_init_winseed();
+
+    /* winseed delivers a constant ammount of entropy for a fast
+     * poll.  We can simply use this and add it to the pool; no need
+     * a loop like it is used in the slow poll */
+    nbytes = entropy_buffer_size;
+    result = get_seed( fast_seeder, entropy_buffer, &nbytes);
+    if( result ) {
+       g10_log_fatal("rndw32: get_seed(fast) failed: rc=%u\n", result);
+       return -1; /* actually never reached */
+    }
+    /*g10_log_info("rndw32: fast poll got %u\n", (unsigned int)nbytes );*/
+    (*add)( entropy_buffer, nbytes, requester );
+    return 0;
+}
+
+
+
+#ifndef IS_MODULE
+static
+#endif
+const char * const gnupgext_version = "RNDW32 ($Revision$)";
+
+static struct {
+    int class;
+    int version;
+    void *func;
+} func_table[] = {
+    { 40, 1, gather_random },
+    { 41, 1, gather_random_fast },
+};
+
+
+#ifndef IS_MODULE
+static
+#endif
+void *
+gnupgext_enum_func( int what, int *sequence, int *class, int *vers )
+{
+    void *ret;
+    int i = *sequence;
+
+    do {
+       if ( i >= DIM(func_table) || i < 0 ) {
+           return NULL;
+       }
+       *class = func_table[i].class;
+       *vers  = func_table[i].version;
+       ret = func_table[i].func;
+       i++;
+    } while ( what && what != *class );
+
+    *sequence = i;
+    return ret;
+}
+
+#ifdef USE_STATIC_RNDW32
+void
+rndw32_set_dll_name( const char *name )
+{
+    entropy_dll = m_strdup( name );
+}
+#endif
+
+#ifndef IS_MODULE
+void
+rndw32_constructor(void)
+{
+    register_internal_cipher_extension( gnupgext_version,
+                                       gnupgext_enum_func );
+}
+#endif
+
index 5172be6..cb4cb43 100644 (file)
@@ -61,6 +61,9 @@ enum gcry_ctl_cmds {
     GCRYCTL_CFB_SYNC = 3,
     GCRYCTL_RESET    = 4,   /* e.g. for MDs */
     GCRYCTL_FINALIZE = 5,
+    GCRYCTL_GET_KEYLEN = 6,
+    GCRYCTL_GET_BLKLEN = 7,
+    GCRYCTL_TEST_ALGO = 8,
 };
 
 int gcry_control( enum gcry_ctl_cmds, ... );
@@ -181,20 +184,20 @@ enum gcry_cipher_flags {
 
 #if 0 /* not yet done */
 int gcry_string_to_cipher_algo( const char *string );
-const char * gcry_cipher_algo_to_string( int algo );
 int gcry_check_cipher_algo( int algo );
-unsigned gcry_cipher_get_keylen( int algo );
-unsigned gcry_cipher_get_blocksize( int algo );
 #endif
 
-int gcry_cipher_open( GCRY_CIPHER_HD *rhd, int algo, int mode, unsigned flags);
+GCRY_CIPHER_HD r gcry_cipher_open( int algo, int mode, unsigned flags);
 void gcry_cipher_close( GCRY_CIPHER_HD h );
 int  gcry_cipher_ctl( GCRY_CIPHER_HD h, int cmd, byte *buffer, size_t buflen);
+int gcry_cipher_info( GCRY_CIPHER_HD h, int what, void *buffer, size_t *nbytes);
+int gcry_cipher_algo_info( int algo, int what, void *buffer, size_t *nbytes);
+const char *gcry_cipher_algo_name( int algo );
 
 int gcry_cipher_encrypt( GCRY_CIPHER_HD h, byte *out, size_t outsize,
-                                           byte *in, size_t inlen );
+                                     const byte *in, size_t inlen );
 int gcry_cipher_decrypt( GCRY_CIPHER_HD h, byte *out, size_t outsize,
-                                           byte *in, size_t inlen );
+                                     const byte *in, size_t inlen );
 
 
 /* some handy macros */
@@ -205,6 +208,13 @@ int gcry_cipher_decrypt( GCRY_CIPHER_HD h, byte *out, size_t outsize,
 #define gcry_cipher_sync(h)  gcry_cipher_ctl( (h), GCRYCTL_CFB_SYNC, \
                                                                   NULL, 0 )
 
+#define gcry_cipher_get_algo_keylen(a) \
+           gcry_cipher_algo_info( (a), GCRYCTL_GET_KEYLEN, NULL, NULL );
+#define gcry_cipher_get_algo_blklen(a) \
+           gcry_cipher_algo_info( (a), GCRYCTL_GET_BLKLEN, NULL, NULL );
+#define gcry_cipher_test_algo(a) \
+           gcry_cipher_algo_info( (a), GCRYCTL_TEST_ALGO, NULL, NULL );
+
 
 /*********************************************
  *******  asymmetric cipher functions  *******
@@ -236,16 +246,16 @@ enum gcry_md_flags {
 };
 
 
-int gcry_md_open( GCRY_MD_HD *ret_hd, int algo, unsigned flags );
+GCRY_MD_HD gcry_md_open( int algo, unsigned flags );
 void gcry_md_close( GCRY_MD_HD hd );
 int gcry_md_enable( GCRY_MD_HD hd, int algo );
 GCRY_MD_HD gcry_md_copy( GCRY_MD_HD hd );
 int gcry_md_ctl( GCRY_MD_HD hd, int cmd, byte *buffer, size_t buflen);
 void gcry_md_write( GCRY_MD_HD hd, const byte *buffer, size_t length);
 byte *gcry_md_read( GCRY_MD_HD hd, int algo );
-int gcry_md_algo( GCRY_MD_HD hd );
-size_t gcry_md_dlen( int algo );
-int gcry_md_get( GCRY_MD_HD hd, int algo, byte *buffer, int buflen );
+int gcry_md_get_algo( GCRY_MD_HD hd );
+int gcry_md_get_dlen( int algo );
+/*??int gcry_md_get( GCRY_MD_HD hd, int algo, byte *buffer, int buflen );*/
 
 
 /*****************************************
index a0bbc64..5fd2eb6 100644 (file)
 #include "memory.h"
 
 
-int
-gcry_md_open( GCRY_MD_HD *ret_hd, int algo, unsigned flags )
+GCRY_MD_HD
+gcry_md_open( GCRY_MD_HD *ret_hd, int algo, unsigned int flags )
 {
-    GCRY_MD_HD hd;
-
     /* fixme: check that algo is available and that only valid
      * flag values are used */
     hd = md_open( algo, (flags & GCRY_MD_FLAG_SECURE) );
-    *ret_hd = hd;
-    return 0;
+    return hd;
 }
 
 void
@@ -68,8 +65,6 @@ gcry_md_ctl( GCRY_MD_HD hd, int cmd, byte *buffer, size_t buflen)
 {
     if( cmd == GCRYCTL_FINALIZE )
        md_final( hd );
-    else if( cmd == GCRYCTL_FINALIZE )
-       md_final( hd );
     else
        return GCRYERR_INV_OP;
     return 0;
@@ -93,16 +88,17 @@ gcry_md_read( GCRY_MD_HD hd, int algo )
 }
 
 int
-gcry_md_algo( GCRY_MD_HD hd )
+gcry_md_get_algo( GCRY_MD_HD hd )
 {
-    return md_get_algo( hd );
+    return md_get_algo( hd ); /* fixme: we need error handling */
 }
 
 /****************
  * Return the length of the digest in bytes.
+ * This function will return 0 in case of errors.
  */
-size_t
-gcry_md_dlen( int algo )
+unsigned int
+gcry_md_get_algo_dlen( int algo )
 {
     /* we do some very quick checks here */
     switch( algo )
@@ -110,7 +106,10 @@ gcry_md_dlen( int algo )
       case GCRY_MD_MD5: return 16;
       case GCRY_MD_SHA1:
       case GCRY_MD_RMD160: return 20;
-      default: return 0; /* fixme: pass it to a lookup function */
+      default:
+       /* fixme: pass it to a lookup function */
+       set_lasterr( GCRYERR_INV_ALGO );
+       return -1;
     }
 }
 
index e2aca4e..5cd2177 100644 (file)
@@ -49,48 +49,60 @@ struct gcry_cipher_context {
 };
 
 
-int
-gcry_cipher_open( GCRY_CIPHER_HD *ret_hd, int algo, int mode, unsigned flags )
+GCRY_CIPHER_HD
+gcry_cipher_open( int algo, int mode, unsigned flags )
 {
     GCRY_CIPHER_HD h;
 
     /* check whether the algo is available */
-    if( check_cipher_algo( algo ) )
-       return set_lasterr( GCRYERR_INV_ALGO );
+    if( check_cipher_algo( algo ) ) {
+       set_lasterr( GCRYERR_INV_ALGO );
+       return NULL;
+    }
 
     /* check flags */
-    if( (flags & ~(GCRY_CIPHER_SECURE|GCRY_CIPHER_ENABLE_SYNC)) )
-       return set_lasterr( GCRYERR_INV_ARG );
+    if( (flags & ~(GCRY_CIPHER_SECURE|GCRY_CIPHER_ENABLE_SYNC)) ) {
+       set_lasterr( GCRYERR_INV_ARG );
+       return NULL;
+    }
 
     /* map mode to internal mode */
     switch( mode ) {
-      case GCRY_CIPHER_MODE_NONE: mode = CIPHER_MODE_DUMMY; break;
-      case GCRY_CIPHER_MODE_ECB: mode = CIPHER_MODE_ECB; break;
+      case GCRY_CIPHER_MODE_NONE:
+       mode = CIPHER_MODE_DUMMY;
+       break;
+      case GCRY_CIPHER_MODE_ECB:
+       mode = CIPHER_MODE_ECB;
+       break;
       case GCRY_CIPHER_MODE_CFB:
        mode = (flags & GCRY_CIPHER_ENABLE_SYNC) ? CIPHER_MODE_PHILS_CFB
                                                 : CIPHER_MODE_CFB;
        break;
-      case GCRY_CIPHER_MODE_CBC: mode = CIPHER_MODE_CBC; break;
+      case GCRY_CIPHER_MODE_CBC: mode = CIPHER_MODE_CBC;
+       break;
       default:
-       return set_lasterr( GCRYERR_INV_ALGO );
+       set_lasterr( GCRYERR_INV_ALGO );
+       return NULL;
     }
 
  /*    FIXME: issue a warning when CIPHER_MODE_NONE is used */
 
     /* allocate the handle */
     h = m_lib_alloc_clear( sizeof *h );
-    if( !h )
-       return set_lasterr( GCRYERR_NOMEM );
+    if( !h ) {
+       set_lasterr( GCRYERR_NOMEM );
+       return NULL;
+    }
     h->magic = CONTEXT_MAGIC;
     h->mode = mode;
     h->hd = cipher_open( algo, mode, (flags & GCRY_CIPHER_SECURE) );
     if( !h ) {
        m_lib_free( h );
-       return set_lasterr( GCRYERR_INTERNAL );
+       set_lasterr( GCRYERR_INTERNAL );
+       return NULL;
     }
 
-    *ret_hd = h;
-    return 0;
+    return h;
 }
 
 
@@ -126,26 +138,143 @@ int gcry_cipher_ctl( GCRY_CIPHER_HD h, int cmd, byte *buffer, size_t buflen)
 }
 
 
+/****************
+ * 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, byte *buffer, size_t *nbytes)
+{
+    switch( cmd ) {
+      default:
+       set_lasterr( GCRYERR_INV_OP );
+       return -1;
+    }
+    return 0;
+}
+
+/****************
+ * 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.
+ *  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)
+ */
+int
+gcry_cipher_algo_info( int algo, int what, void *buffer, size_t *nbytes)
+{
+    switch( what ) {
+      case GCRYCTL_GET_KEYLEN:
+       if( buffer || nbytes ) {
+           set_lasterr( GCRYERR_INV_ARG );
+           return -1;
+       }
+       BUG(); /* FIXME: implement this */
+       break;
+
+      case GCRYCTL_GET_BLKLEN:
+       if( buffer || nbytes ) {
+           set_lasterr( GCRYERR_INV_ARG );
+           return -1;
+       }
+       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_ALGO );
+       return -1;
+
+      case GCRYCTL_TEST_ALGO:
+       if( buffer || nbytes ) {
+           set_lasterr( GCRYERR_INV_ARG );
+           return -1;
+       }
+       if( check_cipher_algo( algo ) ) {
+           set_lasterr( GCRYERR_INV_ALGO );
+           return -1;
+       }
+       return 0;
+
+
+      default:
+       set_lasterr( GCRYERR_INV_OP );
+       return -1;
+    }
+    return 0;
+}
+
+
+/****************
+ * This function simply returns the name of the algorithm or soem constant
+ * string when there is no algo.  It will never return NULL.
+ */
+const char *
+gcry_cipher_algo_name( int algo )
+{
+    return cipher_algo_to_string( algo );
+}
+
+
+/****************
+ * Encrypt IN and write it to OUT.  If IN is NULL, in-place encryption has
+ * been requested,
+ */
 int
 gcry_cipher_encrypt( GCRY_CIPHER_HD h, byte *out, size_t outsize,
-                                      byte  *in, size_t inlen )
+                                      const byte  *in, size_t inlen )
 {
-    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->hd, out, in, 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->hd, 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->hd, out, in, inlen );
+    }
     return 0;
 }
 
 int
 gcry_cipher_decrypt( GCRY_CIPHER_HD h, byte *out, size_t outsize,
-                                      byte  *in, size_t inlen )
+                                const byte  *in, size_t inlen )
 {
-    if( outsize < inlen )
-       return set_lasterr( GCRYERR_TOO_SHORT );
-    cipher_decrypt( h->hd, out, in, 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->hd, 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->hd, out, in, inlen );
+    }
     return 0;
 }