Add PowerPC extra CFLAGS also for chacha20-ppc and crc-ppc
[libgcrypt.git] / cipher / idea.c
index 3d1b01a..abfe675 100644 (file)
@@ -1,38 +1,68 @@
 /* idea.c  -  IDEA function
- *     Copyright (c) 1997 by Werner Koch (dd9jn)
+ * Copyright 1997, 1998, 1999, 2001 Werner Koch (dd9jn)
+ * Copyright 2013 g10 Code GmbH
  *
- * ATTENTION: This code patented and needs a license for any commercial use.
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
  *
- * The code herin is take from:
- *   Bruce Schneier: Applied Cryptography. John Wiley & Sons, 1996.
- *   ISBN 0-471-11709-9. .
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
  *
- * This file is part of G10.
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * WERNER KOCH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  *
- * G10 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.
+ * Except as contained in this notice, the name of Werner Koch shall not be
+ * used in advertising or otherwise to promote the sale, use or other dealings
+ * in this Software without prior written authorization from Werner Koch.
  *
- * G10 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.
+ * Patents on IDEA have expired:
+ *   Europe: EP0482154 on 2011-05-16,
+ *   Japan:  JP3225440 on 2011-05-16,
+ *   U.S.:   5,214,703 on 2012-01-07.
+ */
+
+/*
+ * Please see http://www.noepatents.org/ to learn why software patents
+ * are bad for society and what you can do to fight them.
  *
- * 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
+ * The code herein is based on the one from:
+ *   Bruce Schneier: Applied Cryptography. John Wiley & Sons, 1996.
+ *   ISBN 0-471-11709-9.
  */
 
+
 #include <config.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include "util.h"
-#include "types.h"
-#include "idea.h"
+#include <assert.h>
+
+#include "types.h"  /* for byte and u32 typedefs */
+#include "g10lib.h"
+#include "cipher.h"
 
 
+#define IDEA_KEYSIZE 16
+#define IDEA_BLOCKSIZE 8
+#define IDEA_ROUNDS 8
+#define IDEA_KEYLEN (6*IDEA_ROUNDS+4)
+
+typedef struct {
+    u16 ek[IDEA_KEYLEN];
+    u16 dk[IDEA_KEYLEN];
+    int have_dk;
+} IDEA_context;
+
+static const char *selftest(void);
+
 
 static u16
 mul_inv( u16 x )
@@ -42,8 +72,8 @@ mul_inv( u16 x )
 
     if( x < 2 )
        return x;
-    t1 = 0x10001L / x;
-    y =  0x10001L % x;
+    t1 = 0x10001UL / x;
+    y =  0x10001UL % x;
     if( y == 1 )
        return (1-t1) & 0xffff;
 
@@ -64,7 +94,7 @@ mul_inv( u16 x )
 
 
 static void
-expand_key( byte *userkey, u16 *ek )
+expand_key( const byte *userkey, u16 *ek )
 {
     int i,j;
 
@@ -106,8 +136,8 @@ invert_key( u16 *ek, u16 dk[IDEA_KEYLEN] )
        t2 = -*ek++;
        t3 = -*ek++;
        *--p = mul_inv( *ek++ );
-       *--p = t3;
        *--p = t2;
+       *--p = t3;
        *--p = t1;
     }
     t1 = *ek++;
@@ -122,17 +152,21 @@ invert_key( u16 *ek, u16 dk[IDEA_KEYLEN] )
     *--p = t2;
     *--p = t1;
     memcpy(dk, temp, sizeof(temp) );
-    memset(temp, 0, sizeof(temp) );  /* burn temp */
+    wipememory(temp, sizeof(temp));
 }
 
 
 static void
-cipher( byte *outbuf, byte *inbuf, u16 *key )
+cipher( byte *outbuf, const byte *inbuf, u16 *key )
 {
-    u16 x1, x2, x3,x4, s2, s3;
-    u16 *in, *out;
+    u16 s2, s3;
+    u16 in[4];
     int r = IDEA_ROUNDS;
-  #define MUL(x,y) \
+#define x1 (in[0])
+#define x2 (in[1])
+#define x3 (in[2])
+#define x4 (in[3])
+#define MUL(x,y) \
        do {u16 _t16; u32 _t32;                     \
            if( (_t16 = (y)) ) {                    \
                if( (x = (x)&0xffff) ) {            \
@@ -150,17 +184,13 @@ cipher( byte *outbuf, byte *inbuf, u16 *key )
            }                                       \
        } while(0)
 
-    in = (u16*)inbuf;
-    x1 = *in++;
-    x2 = *in++;
-    x3 = *in++;
-    x4 = *in;
-  #ifdef HAVE_LITTLE_ENDIAN
+    memcpy (in, inbuf, sizeof in);
+#ifndef WORDS_BIGENDIAN
     x1 = (x1>>8) | (x1<<8);
     x2 = (x2>>8) | (x2<<8);
     x3 = (x3>>8) | (x3<<8);
     x4 = (x4>>8) | (x4<<8);
-  #endif
+#endif
     do {
        MUL(x1, *key++);
        x2 += *key++;
@@ -187,166 +217,165 @@ cipher( byte *outbuf, byte *inbuf, u16 *key )
     x2 += *key++;
     MUL(x4, *key);
 
-    out = (u16*)outbuf;
-  #ifdef HAVE_LITTLE_ENDIAN
-    *out++ = (x1>>8) | (x1<<8);
-    *out++ = (x3>>8) | (x3<<8);
-    *out++ = (x2>>8) | (x2<<8);
-    *out   = (x4>>8) | (x4<<8);
-  #else
-    *out++ = x1;
-    *out++ = x3;
-    *out++ = x2;
-    *out   = x4;
-  #endif
-  #undef MUL
+#ifndef WORDS_BIGENDIAN
+    x1 = (x1>>8) | (x1<<8);
+    x2 = (x2>>8) | (x2<<8);
+    x3 = (x3>>8) | (x3<<8);
+    x4 = (x4>>8) | (x4<<8);
+#endif
+    memcpy (outbuf+0, &x1, 2);
+    memcpy (outbuf+2, &x3, 2);
+    memcpy (outbuf+4, &x2, 2);
+    memcpy (outbuf+6, &x4, 2);
+#undef MUL
+#undef x1
+#undef x2
+#undef x3
+#undef x4
 }
 
 
-void
-idea_setkey( IDEA_context *c, byte *key )
+static int
+do_setkey( IDEA_context *c, const byte *key, unsigned int keylen )
 {
+    static int initialized = 0;
+    static const char *selftest_failed = 0;
+
+    if( !initialized ) {
+       initialized = 1;
+       selftest_failed = selftest();
+       if( selftest_failed )
+           log_error( "%s\n", selftest_failed );
+    }
+    if( selftest_failed )
+       return GPG_ERR_SELFTEST_FAILED;
+
+    assert(keylen == 16);
+    c->have_dk = 0;
     expand_key( key, c->ek );
     invert_key( c->ek, c->dk );
+    return 0;
 }
 
-void
-idea_setiv( IDEA_context *c, byte *iv )
+static gcry_err_code_t
+idea_setkey (void *context, const byte *key, unsigned int keylen,
+             gcry_cipher_hd_t hd)
 {
-    if( iv )
-       memcpy( c->iv, iv, IDEA_BLOCKSIZE );
-    else
-       memset( c->iv, 0, IDEA_BLOCKSIZE );
-    c->nleft = 0;
+    IDEA_context *ctx = context;
+    int rc = do_setkey (ctx, key, keylen);
+    (void)hd;
+    _gcry_burn_stack (23+6*sizeof(void*));
+    return rc;
 }
 
-
-void
-idea_encode( IDEA_context *c, byte *outbuf, byte *inbuf, unsigned nblocks )
+static void
+encrypt_block( IDEA_context *c, byte *outbuf, const byte *inbuf )
 {
-    unsigned n;
-
-    for(n=0; n < nblocks; n++ ) {
-       cipher( outbuf, inbuf, c->ek );
-       inbuf  += 8;
-       outbuf += 8;
-    }
+    cipher( outbuf, inbuf, c->ek );
 }
 
-
-void
-idea_decode( IDEA_context *c, byte *outbuf, byte *inbuf, unsigned nblocks )
+static unsigned int
+idea_encrypt (void *context, byte *out, const byte *in)
 {
-    unsigned n;
-
-    for(n=0; n < nblocks; n++ ) {
-       cipher( outbuf, inbuf, c->dk );
-       inbuf  += 8;
-       outbuf += 8;
-    }
+    IDEA_context *ctx = context;
+    encrypt_block (ctx, out, in);
+    return /*burn_stack*/ (24+3*sizeof (void*));
 }
 
-
 static void
-cfbshift( byte *iv, byte *buf, unsigned count)
+decrypt_block( IDEA_context *c, byte *outbuf, const byte *inbuf )
 {
-    unsigned n;
-
-    if( count ) {
-       for( n = IDEA_BLOCKSIZE - count; n; n-- )
-           *iv++ = iv[count];
-       for( ; count; count-- )
-           *iv++ = *buf++;
+    if( !c->have_dk ) {
+       c->have_dk = 1;
+       invert_key( c->ek, c->dk );
     }
+    cipher( outbuf, inbuf, c->dk );
 }
 
-
-/****************
- * FIXME: Make use of bigger chunks
- */
-static void
-xorblock( byte *out, byte *a, byte *b, unsigned count )
+static unsigned int
+idea_decrypt (void *context, byte *out, const byte *in)
 {
-    for( ; count ; count--, a++, b++ )
-       *out++ = *a ^ *b ;
-}
-
-
-void
-idea_encode_cfb( IDEA_context *c, byte *outbuf, byte *inbuf, unsigned nbytes)
-{
-    byte temp[IDEA_BLOCKSIZE];
-
-    while( nbytes >= IDEA_BLOCKSIZE ) {
-       cipher( temp, c->iv, c->ek );
-       xorblock( outbuf, inbuf, temp, IDEA_BLOCKSIZE);
-       cfbshift( c->iv, outbuf, IDEA_BLOCKSIZE );
-       nbytes -= IDEA_BLOCKSIZE;
-       inbuf += IDEA_BLOCKSIZE;
-       outbuf += IDEA_BLOCKSIZE;
-    }
-    if( nbytes ) {
-       cipher( temp, c->iv, c->ek );
-       xorblock( outbuf, inbuf, temp, nbytes );
-       cfbshift( c->iv, outbuf, nbytes );
-    }
+    IDEA_context *ctx = context;
+    decrypt_block (ctx, out, in);
+    return /*burn_stack*/ (24+3*sizeof (void*));
 }
 
 
-void
-idea_decode_cfb( IDEA_context *c, byte *outbuf, byte *inbuf, unsigned nbytes)
+static const char *
+selftest( void )
 {
-    byte t, *ivptr;
-
-    ivptr = c->iv + IDEA_BLOCKSIZE - c->nleft;
-    if( nbytes <= c->nleft ) {
-       c->nleft -= nbytes;
-       for( ; nbytes ; nbytes--, ivptr++, inbuf++ ) {
-           t = *ivptr;
-           *outbuf++ = t ^ (*ivptr = *inbuf) ;
-       }
-       return;
-    }
+static struct {
+    byte key[16];
+    byte plain[8];
+    byte cipher[8];
+} test_vectors[] = {
+    { { 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04,
+       0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08 },
+      { 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03 },
+      { 0x11, 0xFB, 0xED, 0x2B, 0x01, 0x98, 0x6D, 0xE5 } },
+    { { 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04,
+       0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08 },
+      { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 },
+      { 0x54, 0x0E, 0x5F, 0xEA, 0x18, 0xC2, 0xF8, 0xB1 } },
+    { { 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04,
+       0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08 },
+      { 0x00, 0x19, 0x32, 0x4B, 0x64, 0x7D, 0x96, 0xAF },
+      { 0x9F, 0x0A, 0x0A, 0xB6, 0xE1, 0x0C, 0xED, 0x78 } },
+    { { 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04,
+       0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08 },
+      { 0xF5, 0x20, 0x2D, 0x5B, 0x9C, 0x67, 0x1B, 0x08 },
+      { 0xCF, 0x18, 0xFD, 0x73, 0x55, 0xE2, 0xC5, 0xC5 } },
+    { { 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04,
+       0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08 },
+      { 0xFA, 0xE6, 0xD2, 0xBE, 0xAA, 0x96, 0x82, 0x6E },
+      { 0x85, 0xDF, 0x52, 0x00, 0x56, 0x08, 0x19, 0x3D } },
+    { { 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04,
+       0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08 },
+      { 0x0A, 0x14, 0x1E, 0x28, 0x32, 0x3C, 0x46, 0x50 },
+      { 0x2F, 0x7D, 0xE7, 0x50, 0x21, 0x2F, 0xB7, 0x34 } },
+    { { 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04,
+       0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08 },
+      { 0x05, 0x0A, 0x0F, 0x14, 0x19, 0x1E, 0x23, 0x28 },
+      { 0x7B, 0x73, 0x14, 0x92, 0x5D, 0xE5, 0x9C, 0x09 } },
+    { { 0x00, 0x05, 0x00, 0x0A, 0x00, 0x0F, 0x00, 0x14,
+       0x00, 0x19, 0x00, 0x1E, 0x00, 0x23, 0x00, 0x28 },
+      { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 },
+      { 0x3E, 0xC0, 0x47, 0x80, 0xBE, 0xFF, 0x6E, 0x20 } },
+    { { 0x3A, 0x98, 0x4E, 0x20, 0x00, 0x19, 0x5D, 0xB3,
+       0x2E, 0xE5, 0x01, 0xC8, 0xC4, 0x7C, 0xEA, 0x60 },
+      { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 },
+      { 0x97, 0xBC, 0xD8, 0x20, 0x07, 0x80, 0xDA, 0x86 } },
+    { { 0x00, 0x64, 0x00, 0xC8, 0x01, 0x2C, 0x01, 0x90,
+       0x01, 0xF4, 0x02, 0x58, 0x02, 0xBC, 0x03, 0x20 },
+      { 0x05, 0x32, 0x0A, 0x64, 0x14, 0xC8, 0x19, 0xFA },
+      { 0x65, 0xBE, 0x87, 0xE7, 0xA2, 0x53, 0x8A, 0xED } },
+    { { 0x9D, 0x40, 0x75, 0xC1, 0x03, 0xBC, 0x32, 0x2A,
+       0xFB, 0x03, 0xE7, 0xBE, 0x6A, 0xB3, 0x00, 0x06 },
+      { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 },
+      { 0xF5, 0xDB, 0x1A, 0xC4, 0x5E, 0x5E, 0xF9, 0xF9 } }
+};
+    IDEA_context c;
+    byte buffer[8];
+    int i;
 
-    nbytes -= c->nleft;
-    for( ; c->nleft ; c->nleft--, ivptr++, inbuf++ ) {
-       t = *ivptr;
-       *outbuf++ = t ^ (*ivptr = *inbuf) ;
+    for(i=0; i < DIM(test_vectors); i++ ) {
+       do_setkey( &c, test_vectors[i].key, 16 );
+       encrypt_block( &c, buffer, test_vectors[i].plain );
+       if( memcmp( buffer, test_vectors[i].cipher, 8 ) )
+           return "IDEA test encryption failed.";
+       decrypt_block( &c, buffer, test_vectors[i].cipher );
+       if( memcmp( buffer, test_vectors[i].plain, 8 ) )
+           return "IDEA test decryption failed.";
     }
 
-    while( nbytes >= IDEA_BLOCKSIZE ) {
-       memcpy(c->lastcipher, c->iv, IDEA_BLOCKSIZE);
-       cipher( c->iv, c->iv, c->ek );
-       c->nleft = IDEA_BLOCKSIZE;
-       nbytes -= IDEA_BLOCKSIZE;
-       ivptr = c->iv;
-       for( ; c->nleft; c->nleft--, ivptr++, inbuf++ ) {
-           t = *ivptr;
-           *outbuf++ = t ^ (*ivptr = *inbuf) ;
-       }
-    }
-    memcpy(c->lastcipher, c->iv, IDEA_BLOCKSIZE);
-    cipher( c->iv, c->iv, c->ek );
-    c->nleft = IDEA_BLOCKSIZE - nbytes;
-    ivptr = c->iv;
-    for( ; nbytes; nbytes--, ivptr++, inbuf++ ) {
-       t = *ivptr;
-       *outbuf++ = t ^ (*ivptr = *inbuf) ;
-    }
-}
-
-
-/****************
- * This is used for the special way IDEA CFB is used in PGP
- */
-void
-idea_sync_cfb( IDEA_context *c )
-{
-    if( c->nleft ) {
-       memmove(c->iv + c->nleft, c->iv, IDEA_BLOCKSIZE - c->nleft );
-       memcpy(c->iv, c->lastcipher + IDEA_BLOCKSIZE - c->nleft, c->nleft);
-       c->nleft = 0;
-    }
+    return NULL;
 }
 
 
+gcry_cipher_spec_t _gcry_cipher_spec_idea =
+  {
+    GCRY_CIPHER_IDEA, {0, 0},
+    "IDEA", NULL, NULL, IDEA_BLOCKSIZE, 128,
+    sizeof (IDEA_context),
+    idea_setkey, idea_encrypt, idea_decrypt
+  };