*** empty log message ***
[libgcrypt.git] / cipher / cipher.c
index 6ac4682..95c4b70 100644 (file)
 #include "util.h"
 #include "errors.h"
 #include "cipher.h"
+#include "des.h"
 #include "blowfish.h"
 #include "cast5.h"
-#include "des.h"
 #include "dynload.h"
 
 
-#define STD_BLOCKSIZE 8
-#define TABLE_SIZE 20
+#define MAX_BLOCKSIZE 16
+#define TABLE_SIZE 10
 
 struct cipher_table_s {
     const char *name;
     int algo;
+    size_t blocksize;
     size_t keylen;
     size_t contextsize; /* allocate this amount of context */
-    void (*setkey)( void *c, byte *key, unsigned keylen );
+    int  (*setkey)( void *c, byte *key, unsigned keylen );
     void (*encrypt)( void *c, byte *outbuf, byte *inbuf );
     void (*decrypt)( void *c, byte *outbuf, byte *inbuf );
 };
@@ -54,19 +55,19 @@ static struct cipher_table_s cipher_table[TABLE_SIZE];
 struct cipher_handle_s {
     int  algo;
     int  mode;
-    byte iv[STD_BLOCKSIZE];    /* (this should be ulong aligned) */
-    byte lastiv[STD_BLOCKSIZE];
+    size_t blocksize;
+    byte iv[MAX_BLOCKSIZE];    /* (this should be ulong aligned) */
+    byte lastiv[MAX_BLOCKSIZE];
     int  unused;  /* in IV */
-    void (*setkey)( void *c, byte *key, unsigned keylen );
+    int  (*setkey)( void *c, byte *key, unsigned keylen );
     void (*encrypt)( void *c, byte *outbuf, byte *inbuf );
     void (*decrypt)( void *c, byte *outbuf, byte *inbuf );
     byte context[1];
 };
 
 
-
-static void
-dummy_setkey( void *c, byte *key, unsigned keylen ) { }
+static int
+dummy_setkey( void *c, byte *key, unsigned keylen ) { return 0; }
 static void
 dummy_encrypt_block( void *c, byte *outbuf, byte *inbuf ) { BUG(); }
 static void
@@ -81,49 +82,56 @@ static void
 setup_cipher_table()
 {
 
-    static int initialized = 0;
     int i;
-    size_t blocksize;
-
-    if( initialized )
-       return;
 
     i = 0;
     cipher_table[i].algo = CIPHER_ALGO_BLOWFISH;
     cipher_table[i].name = blowfish_get_info( cipher_table[i].algo,
                                         &cipher_table[i].keylen,
-                                        &blocksize,
+                                        &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 || blocksize != STD_BLOCKSIZE )
+    if( !cipher_table[i].name )
        BUG();
     i++;
     cipher_table[i].algo = CIPHER_ALGO_CAST5;
     cipher_table[i].name = cast5_get_info( cipher_table[i].algo,
                                         &cipher_table[i].keylen,
-                                        &blocksize,
+                                        &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_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 || blocksize != STD_BLOCKSIZE )
+    if( !cipher_table[i].name )
        BUG();
     i++;
     cipher_table[i].algo = CIPHER_ALGO_BLOWFISH160;
     cipher_table[i].name = blowfish_get_info( cipher_table[i].algo,
                                         &cipher_table[i].keylen,
-                                        &blocksize,
+                                        &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 || blocksize != STD_BLOCKSIZE )
+    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;
@@ -133,7 +141,6 @@ setup_cipher_table()
 
     for( ; i < TABLE_SIZE; i++ )
        cipher_table[i].name = NULL;
-    initialized = 1;
 }
 
 
@@ -144,14 +151,20 @@ static int
 load_cipher_modules()
 {
     static int done = 0;
+    static int initialized = 0;
     void *context = NULL;
     struct cipher_table_s *ct;
     int ct_idx;
-    size_t blocksize;
     int i;
     const char *name;
     int any = 0;
 
+    if( !initialized ) {
+       setup_cipher_table(); /* load static modules on the first call */
+       initialized = 1;
+       return 1;
+    }
+
     if( done )
        return 0;
     done = 1;
@@ -164,9 +177,9 @@ load_cipher_modules()
        BUG(); /* table already full */
     /* now load all extensions */
     while( (name = enum_gnupgext_ciphers( &context, &ct->algo,
-                               &ct->keylen, &blocksize, &ct->contextsize,
+                               &ct->keylen, &ct->blocksize, &ct->contextsize,
                                &ct->setkey, &ct->encrypt, &ct->decrypt)) ) {
-       if( blocksize != STD_BLOCKSIZE ) {
+       if( ct->blocksize != 8 && ct->blocksize != 16 ) {
            log_info("skipping cipher %d: unsupported blocksize\n", ct->algo);
            continue;
        }
@@ -178,7 +191,8 @@ load_cipher_modules()
            continue;
        }
        /* put it into the table */
-       log_info("loaded cipher %d (%s)\n", ct->algo, name);
+       if( g10_opt_verbose > 1 )
+           log_info("loaded cipher %d (%s)\n", ct->algo, name);
        ct->name = name;
        ct_idx++;
        ct++;
@@ -209,7 +223,6 @@ string_to_cipher_algo( const char *string )
     int i;
     const char *s;
 
-    setup_cipher_table();
     do {
        for(i=0; (s=cipher_table[i].name); i++ )
            if( !stricmp( s, string ) )
@@ -226,7 +239,6 @@ cipher_algo_to_string( int algo )
 {
     int i;
 
-    setup_cipher_table();
     do {
        for(i=0; cipher_table[i].name; i++ )
            if( cipher_table[i].algo == algo )
@@ -243,7 +255,6 @@ check_cipher_algo( int algo )
 {
     int i;
 
-    setup_cipher_table();
     do {
        for(i=0; cipher_table[i].name; i++ )
           if( cipher_table[i].algo == algo )
@@ -259,7 +270,6 @@ cipher_get_keylen( int algo )
     int i;
     unsigned len = 0;
 
-    setup_cipher_table();
     do {
        for(i=0; cipher_table[i].name; i++ ) {
            if( cipher_table[i].algo == algo ) {
@@ -274,6 +284,26 @@ cipher_get_keylen( int algo )
     return 0;
 }
 
+unsigned
+cipher_get_blocksize( int algo )
+{
+    int i;
+    unsigned len = 0;
+
+    do {
+       for(i=0; cipher_table[i].name; i++ ) {
+           if( cipher_table[i].algo == algo ) {
+               len = cipher_table[i].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;
+}
+
 
 /****************
  * Open a cipher handle for use with algorithm ALGO, in mode MODE
@@ -285,7 +315,6 @@ cipher_open( int algo, int mode, int secure )
     CIPHER_HANDLE hd;
     int i;
 
-    setup_cipher_table();
     fast_random_poll();
     do {
        for(i=0; cipher_table[i].name; i++ )
@@ -303,16 +332,17 @@ cipher_open( int algo, int mode, int secure )
                                        + cipher_table[i].contextsize )
                : m_alloc_clear( sizeof *hd + cipher_table[i].contextsize );
     hd->algo = algo;
+    hd->blocksize = cipher_table[i].blocksize;
     hd->setkey = cipher_table[i].setkey;
     hd->encrypt = cipher_table[i].encrypt;
     hd->decrypt = cipher_table[i].decrypt;
     if( algo == CIPHER_ALGO_DUMMY )
        hd->mode = CIPHER_MODE_DUMMY;
     else if( mode == CIPHER_MODE_AUTO_CFB ) {
-       if( algo != CIPHER_ALGO_BLOWFISH160 )
-           hd->mode = CIPHER_MODE_PHILS_CFB;
-       else
+       if( algo == CIPHER_ALGO_BLOWFISH160 || algo >= 100 )
            hd->mode = CIPHER_MODE_CFB;
+       else
+           hd->mode = CIPHER_MODE_PHILS_CFB;
     }
     else
        hd->mode = mode;
@@ -328,10 +358,10 @@ cipher_close( CIPHER_HANDLE c )
 }
 
 
-void
+int
 cipher_setkey( CIPHER_HANDLE c, byte *key, unsigned keylen )
 {
-    (*c->setkey)( &c->context, key, keylen );
+    return (*c->setkey)( &c->context, key, keylen );
 }
 
 
@@ -340,9 +370,9 @@ void
 cipher_setiv( CIPHER_HANDLE c, const byte *iv )
 {
     if( iv )
-       memcpy( c->iv, iv, STD_BLOCKSIZE );
+       memcpy( c->iv, iv, c->blocksize );
     else
-       memset( c->iv, 0, STD_BLOCKSIZE );
+       memset( c->iv, 0, c->blocksize );
     c->unused = 0;
 }
 
@@ -355,8 +385,8 @@ do_ecb_encrypt( CIPHER_HANDLE c, byte *outbuf, byte *inbuf, unsigned nblocks )
 
     for(n=0; n < nblocks; n++ ) {
        (*c->encrypt)( &c->context, outbuf, inbuf );
-       inbuf  += STD_BLOCKSIZE;;
-       outbuf += STD_BLOCKSIZE;
+       inbuf  += c->blocksize;
+       outbuf += c->blocksize;
     }
 }
 
@@ -367,8 +397,8 @@ do_ecb_decrypt( CIPHER_HANDLE c, byte *outbuf, byte *inbuf, unsigned nblocks )
 
     for(n=0; n < nblocks; n++ ) {
        (*c->decrypt)( &c->context, outbuf, inbuf );
-       inbuf  += STD_BLOCKSIZE;;
-       outbuf += STD_BLOCKSIZE;
+       inbuf  += c->blocksize;
+       outbuf += c->blocksize;
     }
 }
 
@@ -377,11 +407,12 @@ static void
 do_cfb_encrypt( CIPHER_HANDLE c, byte *outbuf, byte *inbuf, unsigned nbytes )
 {
     byte *ivp;
+    size_t blocksize = c->blocksize;
 
     if( nbytes <= c->unused ) {
        /* short enough to be encoded by the remaining XOR mask */
        /* XOR the input with the IV and store input into IV */
-       for(ivp=c->iv+STD_BLOCKSIZE - c->unused; nbytes; nbytes--, c->unused-- )
+       for(ivp=c->iv+c->blocksize - c->unused; nbytes; nbytes--, c->unused-- )
            *outbuf++ = (*ivp++ ^= *inbuf++);
        return;
     }
@@ -389,26 +420,26 @@ do_cfb_encrypt( CIPHER_HANDLE c, byte *outbuf, byte *inbuf, unsigned nbytes )
     if( c->unused ) {
        /* XOR the input with the IV and store input into IV */
        nbytes -= c->unused;
-       for(ivp=c->iv+STD_BLOCKSIZE - c->unused; c->unused; c->unused-- )
+       for(ivp=c->iv+blocksize - c->unused; c->unused; c->unused-- )
            *outbuf++ = (*ivp++ ^= *inbuf++);
     }
 
     /* now we can process complete blocks */
-    while( nbytes >= STD_BLOCKSIZE ) {
+    while( nbytes >= blocksize ) {
        int i;
        /* encrypt the IV (and save the current one) */
-       memcpy( c->lastiv, c->iv, STD_BLOCKSIZE );
+       memcpy( c->lastiv, c->iv, blocksize );
        (*c->encrypt)( &c->context, c->iv, c->iv );
        /* XOR the input with the IV and store input into IV */
-       for(ivp=c->iv,i=0; i < STD_BLOCKSIZE; i++ )
+       for(ivp=c->iv,i=0; i < blocksize; i++ )
            *outbuf++ = (*ivp++ ^= *inbuf++);
-       nbytes -= STD_BLOCKSIZE;
+       nbytes -= blocksize;
     }
     if( nbytes ) { /* process the remaining bytes */
        /* encrypt the IV (and save the current one) */
-       memcpy( c->lastiv, c->iv, STD_BLOCKSIZE );
+       memcpy( c->lastiv, c->iv, blocksize );
        (*c->encrypt)( &c->context, c->iv, c->iv );
-       c->unused = STD_BLOCKSIZE;
+       c->unused = blocksize;
        /* and apply the xor */
        c->unused -= nbytes;
        for(ivp=c->iv; nbytes; nbytes-- )
@@ -421,11 +452,12 @@ do_cfb_decrypt( CIPHER_HANDLE c, byte *outbuf, 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+STD_BLOCKSIZE - c->unused; nbytes; nbytes--,c->unused--){
+       for(ivp=c->iv+blocksize - c->unused; nbytes; nbytes--,c->unused--){
            temp = *inbuf++;
            *outbuf++ = *ivp ^ temp;
            *ivp++ = temp;
@@ -436,7 +468,7 @@ do_cfb_decrypt( CIPHER_HANDLE c, byte *outbuf, byte *inbuf, unsigned nbytes )
     if( c->unused ) {
        /* XOR the input with the IV and store input into IV */
        nbytes -= c->unused;
-       for(ivp=c->iv+STD_BLOCKSIZE - c->unused; c->unused; c->unused-- ) {
+       for(ivp=c->iv+blocksize - c->unused; c->unused; c->unused-- ) {
            temp = *inbuf++;
            *outbuf++ = *ivp ^ temp;
            *ivp++ = temp;
@@ -444,70 +476,24 @@ do_cfb_decrypt( CIPHER_HANDLE c, byte *outbuf, byte *inbuf, unsigned nbytes )
     }
 
     /* now we can process complete blocks */
-  #ifdef BIG_ENDIAN_HOST
-    /* This does only make sense for big endian hosts, due to ... ivp = temp*/
-    if( !((ulong)inbuf % SIZEOF_UNSIGNED_LONG) ) {
-       while( nbytes >= STD_BLOCKSIZE ) {
-           /* encrypt the IV (and save the current one) */
-           memcpy( c->lastiv, c->iv, STD_BLOCKSIZE );
-           (*c->encrypt)( &c->context, c->iv, c->iv );
-           ivp = c->iv;
-           /* XOR the input with the IV and store input into IV */
-         #if SIZEOF_UNSIGNED_LONG == STD_BLOCKSIZE
-           temp = *(ulong*)inbuf;
-           *(ulong*)outbuf = *(ulong*)c->iv ^ temp;
-           *(ulong*)ivp    = temp;
-         #elif (2*SIZEOF_UNSIGNED_LONG) == STD_BLOCKSIZE
-           temp = ((ulong*)inbuf)[0];
-           ((ulong*)outbuf)[0] = ((ulong*)c->iv)[0] ^ temp;
-           ((ulong*)ivp)[0] = temp;
-           temp = ((ulong*)inbuf)[1];
-           ((ulong*)outbuf)[1] = ((ulong*)c->iv)[1] ^ temp;
-           ((ulong*)ivp)[1] = temp;
-         #elif (4*SIZEOF_UNSIGNED_LONG) == STD_BLOCKSIZE
-           temp = ((ulong*)inbuf)[0];
-           ((ulong*)outbuf)[0] = ((ulong*)c->iv)[0] ^ temp;
-           ((ulong*)ivp)[0] = temp;
-           temp = ((ulong*)inbuf)[1];
-           ((ulong*)outbuf)[1] = ((ulong*)c->iv)[1] ^ temp;
-           ((ulong*)ivp)[1] = temp;
-           temp = ((ulong*)inbuf)[2];
-           ((ulong*)outbuf)[2] = ((ulong*)c->iv)[2] ^ temp;
-           ((ulong*)ivp)[2] = temp;
-           temp = ((ulong*)inbuf)[3];
-           ((ulong*)outbuf)[3] = ((ulong*)c->iv)[3] ^ temp;
-           ((ulong*)ivp)[3] = temp;
-         #else
-           #error Please disable the align test.
-         #endif
-           nbytes -= STD_BLOCKSIZE;
-           inbuf  += STD_BLOCKSIZE;
-           outbuf += STD_BLOCKSIZE;
-       }
-    }
-    else { /* non aligned version */
-  #endif /* BIG_ENDIAN_HOST */
-       while( nbytes >= STD_BLOCKSIZE ) {
-           int i;
-           /* encrypt the IV (and save the current one) */
-           memcpy( c->lastiv, c->iv, STD_BLOCKSIZE );
-           (*c->encrypt)( &c->context, c->iv, c->iv );
-           /* XOR the input with the IV and store input into IV */
-           for(ivp=c->iv,i=0; i < STD_BLOCKSIZE; i++ ) {
-               temp = *inbuf++;
-               *outbuf++ = *ivp ^ temp;
-               *ivp++ = temp;
-           }
-           nbytes -= STD_BLOCKSIZE;
+    while( nbytes >= blocksize ) {
+       int i;
+       /* encrypt the IV (and save the current one) */
+       memcpy( c->lastiv, c->iv, blocksize );
+       (*c->encrypt)( &c->context, 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;
        }
-   #ifdef BIG_ENDIAN_HOST
+       nbytes -= blocksize;
     }
-   #endif
     if( nbytes ) { /* process the remaining bytes */
        /* encrypt the IV (and save the current one) */
-       memcpy( c->lastiv, c->iv, STD_BLOCKSIZE );
+       memcpy( c->lastiv, c->iv, blocksize );
        (*c->encrypt)( &c->context, c->iv, c->iv );
-       c->unused = STD_BLOCKSIZE;
+       c->unused = blocksize;
        /* and apply the xor */
        c->unused -= nbytes;
        for(ivp=c->iv; nbytes; nbytes-- ) {
@@ -580,8 +566,8 @@ void
 cipher_sync( CIPHER_HANDLE c )
 {
     if( c->mode == CIPHER_MODE_PHILS_CFB && c->unused ) {
-       memmove(c->iv + c->unused, c->iv, STD_BLOCKSIZE - c->unused );
-       memcpy(c->iv, c->lastiv + STD_BLOCKSIZE - c->unused, 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;
     }
 }