--- pgp/src/idea.c 2018/04/24 16:39:32 1.1.1.3 +++ pgp/src/idea.c 2018/04/24 16:41:15 1.1.1.4 @@ -1,631 +1,644 @@ -/* idea.c - C source code for IDEA block cipher. - * IDEA (International Data Encryption Algorithm), formerly known as - * IPES (Improved Proposed Encryption Standard). - * Algorithm developed by Xuejia Lai and James L. Massey, of ETH Zurich. - * This implementation modified and derived from original C code - * developed by Xuejia Lai. - * Zero-based indexing added, names changed from IPES to IDEA. - * CFB functions added. Random number routines added. - * - * Optimized for speed 21 Oct 92 by Colin Plumb. - * Very minor speedup on 23 Feb 93 by Colin Plumb. - * idearand() given a separate expanded key on 25 Feb 93, Colin Plumb. - * - * There are two adjustments that can be made to this code to - * speed it up. Defaults may be used for PCs. Only the -DIDEA32 - * pays off significantly if selectively set or not set. - * Experiment to see what works better for you. - * - * Multiplication: default is inline, -DAVOID_JUMPS uses a - * different version that does not do any conditional - * jumps (a few percent worse on a SPARC), while - * -DSMALL_CACHE takes it out of line to stay - * within a small on-chip code cache. - * Variables: normally, 16-bit variables are used, but some - * machines (notably RISCs) do not have 16-bit registers, - * so they do a great deal of masking. -DIDEA32 uses "int" - * register variables and masks explicitly only where - * necessary. On a SPARC, for example, this boosts - * performace by 30%. - * - * The IDEA(tm) block cipher is covered by a patent held by ETH and a - * Swiss company called Ascom-Tech AG. The Swiss patent number is - * PCT/CH91/00117. International patents are pending. IDEA(tm) is a - * trademark of Ascom-Tech AG. There is no license fee required for - * noncommercial use. Commercial users may obtain licensing details - * from Dieter Profos, Ascom Tech AG, Solothurn Lab, Postfach 151, 4502 - * Solothurn, Switzerland, Tel +41 65 242885, Fax +41 65 235761. - * - * The IDEA block cipher uses a 64-bit block size, and a 128-bit key - * size. It breaks the 64-bit cipher block into four 16-bit words - * because all of the primitive inner operations are done with 16-bit - * arithmetic. It likewise breaks the 128-bit cipher key into eight - * 16-bit words. - * - * For further information on the IDEA cipher, see these papers: - * 1) Xuejia Lai, "Detailed Description and a Software Implementation of - * the IPES Cipher", Institute for Signal and Information - * Processing, ETH-Zentrum, Zurich, Switzerland, 1991 - * 2) Xuejia Lai, James L. Massey, Sean Murphy, "Markov Ciphers and - * Differential Cryptanalysis", Advances in Cryptology- EUROCRYPT'91 - * - * This code assumes that each pair of 8-bit bytes comprising a 16-bit - * word in the key and in the cipher block are externally represented - * with the Most Significant Byte (MSB) first, regardless of the - * internal native byte order of the target CPU. - */ - -#include "idea.h" - -#ifdef TEST -#include -#include -#endif - -#define ROUNDS 8 /* Don't change this value, should be 8 */ -#define KEYLEN (6*ROUNDS+4) /* length of key schedule */ - -typedef word16 IDEAkey[KEYLEN]; - -#ifdef IDEA32 /* Use >16-bit temporaries */ -#define low16(x) ((x) & 0xFFFF) -typedef unsigned int uint16; /* at LEAST 16 bits, maybe more */ -#else -#define low16(x) (x) /* this is only ever applied to uint16's */ -typedef word16 uint16; -#endif - -#ifdef _GNUC_ -/* __const__ simply means there are no side effects for this function, - * which is useful info for the gcc optimizer */ -#define CONST __const__ -#else -#define CONST -#endif - -static void en_key_idea(word16 *userkey, word16 *Z); -static void de_key_idea(IDEAkey Z, IDEAkey DK); -static void cipher_idea(word16 in[4], word16 out[4], CONST IDEAkey Z); - -/* - * Multiplication, modulo (2**16)+1 - * Note that this code is structured like this on the assumption that - * untaken branches are cheaper than taken branches, and the compiler - * doesn't schedule branches. - */ -#ifdef SMALL_CACHE -CONST static uint16 mul(register uint16 a, register uint16 b) -{ - register word32 p; - - if (a) - { if (b) - { p = (word32)a * b; - b = low16(p); - a = p>>16; - return b - a + (b < a); - } - else - { return 1-a; - } - } - else - { return 1-b; - } -} /* mul */ -#endif /* SMALL_CACHE */ - -/* - * Compute multiplicative inverse of x, modulo (2**16)+1, - * using Euclid's GCD algorithm. It is unrolled twice to - * avoid swapping the meaning of the registers each iteration, - * and some subtracts of t have been changed to adds. - */ -CONST static uint16 inv(uint16 x) -{ - uint16 t0, t1; - uint16 q, y; - - if (x <= 1) - return x; /* 0 and 1 are self-inverse */ - t1 = 0x10001L / x; /* Since x >= 2, this fits into 16 bits */ - y = 0x10001L % x; - if (y == 1) - return low16(1-t1); - t0 = 1; - do - { q = x / y; - x = x % y; - t0 += q * t1; - if (x == 1) - return t0; - q = y / x; - y = y % x; - t1 += q * t0; - } while (y != 1); - return low16(1-t1); -} /* inv */ - -/* Compute IDEA encryption subkeys Z */ -static void en_key_idea(word16 *userkey, word16 *Z) -{ - int i,j; - - /* - * shifts - */ - for (j=0; j<8; j++) - Z[j] = *userkey++; - - for (i=0; j> 7; - Z += i & 8; - i &= 7; - } -} /* en_key_idea */ - -/* Compute IDEA decryption subkeys DK from encryption subkeys Z */ -/* Note: these buffers *may* overlap! */ -static void de_key_idea(IDEAkey Z, IDEAkey DK) -{ - int j; - uint16 t1, t2, t3; - IDEAkey T; - word16 *p = T + KEYLEN; - - t1 = inv(*Z++); - t2 = -*Z++; - t3 = -*Z++; - *--p = inv(*Z++); - *--p = t3; - *--p = t2; - *--p = t1; - - for (j = 1; j < ROUNDS; j++) - { - t1 = *Z++; - *--p = *Z++; - *--p = t1; - - t1 = inv(*Z++); - t2 = -*Z++; - t3 = -*Z++; - *--p = inv(*Z++); - *--p = t2; - *--p = t3; - *--p = t1; - } - t1 = *Z++; - *--p = *Z++; - *--p = t1; - - t1 = inv(*Z++); - t2 = -*Z++; - t3 = -*Z++; - *--p = inv(*Z++); - *--p = t3; - *--p = t2; - *--p = t1; -/* Copy and destroy temp copy */ - for (j = 0, p = T; j < KEYLEN; j++) - { - *DK++ = *p; - *p++ = 0; - } -} /* de_key_idea */ - -/* - * MUL(x,y) computes x = x*y, modulo 0x10001. Requires two temps, - * t16 and t32. x must me a side-effect-free lvalue. y may be - * anything, but unlike x, must be strictly 16 bits even if low16() - * is #defined. - * All of these are equivalent - see which is faster on your machine - */ -#ifdef SMALL_CACHE -#define MUL(x,y) (x = mul(low16(x),y)) -#else -#ifdef AVOID_JUMPS -#define MUL(x,y) (x = low16(x-1), t16 = low16((y)-1), \ - t32 = (word32)x*t16+x+t16+1, x = low16(t32), \ - t16 = t32>>16, x = x-t16+(x>16, \ - x = x-t16+(x 0 */ -{ - if (count) - do - *buf++ ^= *mask++; - while (--count); -} /* xorbuf */ - - -/* - * cfbshift - shift bytes into IV for CFB input - * Used only for Cipher Feedback (CFB) mode of encryption. - * Can be applied for any block encryption algorithm with any - * block size, such as the DES or the IDEA cipher. - */ -static void cfbshift(register byteptr iv, register byteptr buf, - register int count, int blocksize) -/* iv is the initialization vector. - * buf is the buffer pointer. - * count is the number of bytes to shift in...must be > 0. - * blocksize is 8 bytes for DES or IDEA ciphers. - */ -{ - int retained; - if (count) - { - retained = blocksize-count; /* number bytes in iv to retain */ - /* left-shift retained bytes of IV over by count bytes to make room */ - while (retained--) - { - *iv = *(iv+count); - iv++; - } - /* now copy count bytes from buf to shifted tail of IV */ - do *iv++ = *buf++; - while (--count); - } -} /* cfbshift */ - - - -/* Key schedules for IDEA encryption and decryption */ -static IDEAkey Z; -static word16 *iv_idea; /* pointer to IV for CFB or CBC */ -static boolean cfb_dc_idea; /* TRUE iff CFB decrypting */ - - -/* initkey_idea initializes IDEA for ECB mode operations */ -static void initkey_idea(byte key[16], boolean decryp) -{ - word16 userkey[8]; /* IDEA key is 16 bytes long */ - int i; - /* Assume each pair of bytes comprising a word is ordered MSB-first. */ - for (i=0; i<8; i++) - { - userkey[i] = (key[0]<<8) + key[1]; - key++; key++; - } - en_key_idea(userkey,Z); - if (decryp) - { - de_key_idea(Z,Z); /* compute inverse key schedule DK */ - } - for (i=0; i<8; i++) /* Erase dangerous traces */ - userkey[i] = 0; -} /* initkey_idea */ - - -/* Run a 64-bit block thru IDEA in ECB (Electronic Code Book) mode, - using the currently selected key schedule. -*/ -static void idea_ecb(word16 *inbuf, word16 *outbuf) -{ - /* Assume each pair of bytes comprising a word is ordered MSB-first. */ -#ifndef HIGHFIRST /* If this is a least-significant-byte-first CPU */ - word16 x; - - /* Invert the byte order for each 16-bit word for internal use. */ - x = inbuf[0]; outbuf[0] = x >> 8 | x << 8; - x = inbuf[1]; outbuf[1] = x >> 8 | x << 8; - x = inbuf[2]; outbuf[2] = x >> 8 | x << 8; - x = inbuf[3]; outbuf[3] = x >> 8 | x << 8; - cipher_idea(outbuf, outbuf, Z); - x = outbuf[0]; outbuf[0] = x >> 8 | x << 8; - x = outbuf[1]; outbuf[1] = x >> 8 | x << 8; - x = outbuf[2]; outbuf[2] = x >> 8 | x << 8; - x = outbuf[3]; outbuf[3] = x >> 8 | x << 8; -#else /* HIGHFIRST */ - /* Byte order for internal and external representations is the same. */ - cipher_idea(inbuf, outbuf, Z); -#endif /* HIGHFIRST */ -} /* idea_ecb */ - - -/* - * initcfb - Initializes the IDEA key schedule tables via key, - * and initializes the Cipher Feedback mode IV. - * References context variables cfb_dc_idea and iv_idea. - */ -void initcfb_idea(word16 iv0[4], byte key[16], boolean decryp) -/* iv0 is copied to global iv_idea, buffer will be destroyed by ideacfb. - key is pointer to key buffer. - decryp is TRUE if decrypting, FALSE if encrypting. -*/ -{ - iv_idea = iv0; - cfb_dc_idea = decryp; - initkey_idea(key,FALSE); -} /* initcfb_idea */ - - -/* - * ideacfb - encipher a buffer with IDEA enciphering algorithm, - * using Cipher Feedback (CFB) mode. - * - * Assumes initcfb_idea has already been called. - * References context variables cfb_dc_idea and iv_idea. - */ -void ideacfb(byteptr buf, int count) -/* buf is input, output buffer, may be more than 1 block. - * count is byte count of buffer. May be > IDEABLOCKSIZE. - */ -{ - int chunksize; /* smaller of count, IDEABLOCKSIZE */ - word16 temp[IDEABLOCKSIZE/2]; - - while ((chunksize = min(count,IDEABLOCKSIZE)) > 0) - { - idea_ecb(iv_idea,temp); /* encrypt iv_idea, making temp. */ - - if (cfb_dc_idea) /* buf is ciphertext */ - /* shift in ciphertext to IV... */ - cfbshift((byte *)iv_idea,buf,chunksize,IDEABLOCKSIZE); - - /* convert buf via xor */ - xorbuf(buf,(byte *)temp,chunksize); /* buf now has enciphered output */ - - if (!cfb_dc_idea) /* buf was plaintext, is now ciphertext */ - /* shift in ciphertext to IV... */ - cfbshift((byte *)iv_idea,buf,chunksize,IDEABLOCKSIZE); - - count -= chunksize; - buf += chunksize; - } -} /* ideacfb */ - - -/* - close_idea function erases all the key schedule information when - we are all done with a set of operations for a particular IDEA key - context. This is to prevent any sensitive data from being left - around in memory. -*/ -void close_idea(void) /* erase current key schedule tables */ -{ - short i; - for (i = 0; i < KEYLEN; i++) - Z[i] = 0; -} /* close_idea() */ - -/********************************************************************/ - -/* - * These buffers are used by init_idearand, idearand, and close_idearand. - */ -static word16 dtbuf_idea[4] = {0}; /* buffer for enciphered timestamp */ -static word16 randseed_idea[4] = {0}; /* seed for IDEA random # generator */ -static word16 randbuf_idea[4] = {0}; /* buffer for IDEA random # generator */ -static byte randbuf_idea_counter = 0; /* # of random bytes left in randbuf_idea */ -static IDEAkey randkey_idea; /* Expanded key for IDEA random # generator */ - -/* - * init_idearand - initialize idearand, IDEA random number generator. - * Used for generating cryptographically strong random numbers. - * Much of the design comes from Appendix C of ANSI X9.17. - * key is pointer to IDEA key buffer. - * seed is pointer to random number seed buffer. - * tstamp is a 32-bit timestamp - */ -void init_idearand(byte key[16], byte seed[8], word32 tstamp) -{ - int i; - - en_key_idea((word16 *)key, randkey_idea); - - for (i=0; i<4; i++) /* capture timestamp material */ - { dtbuf_idea[i] = tstamp; /* get bottom word */ - tstamp = tstamp >> 16; /* drop bottom word */ - /* tstamp has only 4 bytes-- last 4 bytes will always be 0 */ - } - /* Start with enciphered timestamp: */ - cipher_idea(dtbuf_idea, dtbuf_idea, randkey_idea); - - /* initialize seed material */ - for (i=0; i<8; i++) - ((byte *)randseed_idea)[i] = seed[i]; - - randbuf_idea_counter = 0; /* # of random bytes left in randbuf_idea */ - -} /* init_idearand */ - - -/* - * idearand - IDEA pseudo-random number generator - * Used for generating cryptographically strong random numbers. - * Much of the design comes from Appendix C of ANSI X9.17. - */ -byte idearand(void) -{ - int i; - if (randbuf_idea_counter==0) /* if random buffer is spent...*/ - { /* Combine enciphered timestamp with seed material: */ - for (i=0; i<4; i++) - randseed_idea[i] ^= dtbuf_idea[i]; - cipher_idea(randseed_idea,randbuf_idea,randkey_idea); /* fill new block */ - - /* Compute new seed vector: */ - for (i=0; i<4; i++) - randseed_idea[i] = randbuf_idea[i] ^ dtbuf_idea[i]; - cipher_idea(randseed_idea,randseed_idea,randkey_idea); /* fill new seed */ - - randbuf_idea_counter = 8; /* reset counter for full buffer */ - } - /* Take a byte from randbuf_idea: */ - return(((byte *)randbuf_idea)[--randbuf_idea_counter]); -} /* idearand */ - - -void close_idearand(void) -{ /* Erase random IDEA buffers and wipe out IDEA key info */ - int i; - for (i=0; i<4; i++) - { randbuf_idea[i] = 0; - randseed_idea[i] = 0; - dtbuf_idea[i] = 0; - } - for (i = 0; i16-bit temporaries */ +#define low16(x) ((x) & 0xFFFF) +typedef unsigned int uint16; /* at LEAST 16 bits, maybe more */ +#else +#define low16(x) (x) /* this is only ever applied to uint16's */ +typedef word16 uint16; +#endif + +#ifdef _GNUC_ +/* __const__ simply means there are no side effects for this function, + * which is useful info for the gcc optimizer + */ +#define CONST __const__ +#else +#define CONST +#endif + +/* + * Multiplication, modulo (2**16)+1 + * Note that this code is structured on the assumption that + * untaken branches are cheaper than taken branches, and the + * compiler doesn't schedule branches. + */ +#ifdef SMALL_CACHE +CONST static uint16 +mul(register uint16 a, register uint16 b) +{ + register word32 p; + + p = (word32)a * b; + if (p) { + b = low16(p); + a = p>>16; + return (b - a) + (b < a); + } else if (a) { + return 1-b; + } else { + return 1-a; + } +} /* mul */ +#endif /* SMALL_CACHE */ + +/* + * Compute the multiplicative inverse of x, modulo 65537, using Euclid's + * algorithm. It is unrolled twice to avoid swapping the registers each + * iteration, and some subtracts of t have been changed to adds. + */ +CONST static uint16 +mulInv(uint16 x) +{ + uint16 t0, t1; + uint16 q, y; + + if (x <= 1) + return x; /* 0 and 1 are self-inverse */ + t1 = 0x10001L / x; /* Since x >= 2, this fits into 16 bits */ + y = 0x10001L % x; + if (y == 1) + return low16(1-t1); + t0 = 1; + do { + q = x / y; + x = x % y; + t0 += q * t1; + if (x == 1) + return t0; + q = y / x; + y = y % x; + t1 += q * t0; + } while (y != 1); + return low16(1-t1); +} /* mukInv */ + +/* + * Expand a 128-bit user key to a working encryption key EK + */ +static void +ideaExpandKey(byte const *userkey, word16 *EK) +{ + int i,j; + + for (j=0; j<8; j++) { + EK[j] = (userkey[0]<<8) + userkey[1]; + userkey += 2; + } + for (i=0; j < IDEAKEYLEN; j++) { + i++; + EK[i+7] = EK[i & 7] << 9 | EK[i+1 & 7] >> 7; + EK += i & 8; + i &= 7; + } +} /* ideaExpandKey */ + +/* + * Compute IDEA decryption key DK from an expanded IDEA encryption key EK + * Note that the input and output may be the same. Thus, the key is + * inverted into an internal buffer, and then copied to the output. + */ +static void +ideaInvertKey(word16 const *EK, word16 DK[IDEAKEYLEN]) +{ + int i; + uint16 t1, t2, t3; + word16 temp[IDEAKEYLEN]; + word16 *p = temp + IDEAKEYLEN; + + t1 = mulInv(*EK++); + t2 = -*EK++; + t3 = -*EK++; + *--p = mulInv(*EK++); + *--p = t3; + *--p = t2; + *--p = t1; + + for (i = 0; i < IDEAROUNDS-1; i++) { + t1 = *EK++; + *--p = *EK++; + *--p = t1; + + t1 = mulInv(*EK++); + t2 = -*EK++; + t3 = -*EK++; + *--p = mulInv(*EK++); + *--p = t2; + *--p = t3; + *--p = t1; + } + t1 = *EK++; + *--p = *EK++; + *--p = t1; + + t1 = mulInv(*EK++); + t2 = -*EK++; + t3 = -*EK++; + *--p = mulInv(*EK++); + *--p = t3; + *--p = t2; + *--p = t1; +/* Copy and destroy temp copy */ + memcpy(DK, temp, sizeof(temp)); + burn(temp); +} /* ideaInvertKey */ + +/* + * MUL(x,y) computes x = x*y, modulo 0x10001. Requires two temps, + * t16 and t32. x is modified, and must me a side-effect-free lvalue. + * y may be anything, but unlike x, must be strictly 16 bits even if + * low16() is #defined. + * All of these are equivalent - see which is faster on your machine + */ +#ifdef SMALL_CACHE +#define MUL(x,y) (x = mul(low16(x),y)) +#else /* !SMALL_CACHE */ +#ifdef AVOID_JUMPS +#define MUL(x,y) (x = low16(x-1), t16 = low16((y)-1), \ + t32 = (word32)x*t16 + x + t16 + 1, x = low16(t32), \ + t16 = t32>>16, x = (x-t16) + (x>16, \ + x = (x-t16)+(x>8) | (x1<<8); + x2 = (x2>>8) | (x2<<8); + x3 = (x3>>8) | (x3<<8); + x4 = (x4>>8) | (x4<<8); +#endif + do { + MUL(x1,*key++); + x2 += *key++; + x3 += *key++; + MUL(x4, *key++); + + s3 = x3; + x3 ^= x1; + MUL(x3, *key++); + s2 = x2; + x2 ^= x4; + x2 += x3; + MUL(x2, *key++); + x3 += x2; + + x1 ^= x2; x4 ^= x3; + + x2 ^= s3; x3 ^= s2; + } while (--r); + MUL(x1, *key++); + x3 += *key++; + x2 += *key++; + MUL(x4, *key); + + out = (word16 *)outbuf; +#ifdef HIGHFIRST + *out++ = x1; + *out++ = x3; + *out++ = x2; + *out = x4; +#else /* !HIGHFIRST */ + x1 = low16(x1); + x2 = low16(x2); + x3 = low16(x3); + x4 = low16(x4); + *out++ = (x1>>8) | (x1<<8); + *out++ = (x3>>8) | (x3<<8); + *out++ = (x2>>8) | (x2<<8); + *out = (x4>>8) | (x4<<8); +#endif +} /* ideaCipher */ + +/*-------------------------------------------------------------*/ + +#ifdef TEST + +#include +#include +/* + * This is the number of Kbytes of test data to encrypt. + * It defaults to 1 MByte. + */ +#ifndef BLOCKS +#ifndef KBYTES +#define KBYTES 1024 +#endif +#define BLOCKS (64*KBYTES) +#endif + +int +main(void) +{ /* Test driver for IDEA cipher */ + int i, j, k; + byte userkey[16]; + word16 EK[IDEAKEYLEN], DK[IDEAKEYLEN]; + byte XX[8], YY[8], ZZ[8]; + clock_t start, end; + long l; + + /* Make a sample user key for testing... */ + for(i=0; i<16; i++) + userkey[i] = i+1; + + /* Compute encryption subkeys from user key... */ + ideaExpandKey(userkey, EK); + printf("\nEncryption key subblocks: "); + for (j=0; jiv, iv, 8); + else + fill0(context->iv, 8); + context->bufleft = 0; +} + +void +ideaCfbInit(struct IdeaCfbContext *context, byte const (key[16])) +{ + ideaExpandKey(key, context->key); + ideaCfbReinit(context,0); +} + +void +ideaCfbDestroy(struct IdeaCfbContext *context) +{ + burn(*context); +} + +/* + * Okay, explanation time: + * Phil invented a unique way of doing CFB that's sensitive to semantic + * boundaries within the data being encrypted. One way to phrase + * CFB en/decryption is to say that you XOR the current 8 bytes with + * IDEA(previous 8 bytes of ciphertext). Normally, you repeat this + * at 8-byte intervals, but Phil decided to resync things on the + * boundaries between elements in the stream being encrypted. + * + * That is, the last 4 bytes of a 12-byte field are en/decrypted using + * the first 4 bytes of IDEA(previous 8 bytes of ciphertext), but then + * the last 4 bytes of that IDEA computation are thrown away, and the + * first 8 bytes of the next field are en/decrypted using + * IDEA(last 8 bytes of ciphertext). This is equivalent to using a + * shorter feedback length (if you're familiar with the general CFB + * technique) briefly, and doesn't weaken the cipher any (using shorter + * CFB lengths makes it stronger, actually), it just makes it a bit unusual. + * + * Anyway, to accomodate this behaviour, every time we do an IDEA + * encrpytion of 8 bytes of ciphertext to get 8 bytes of XOR mask, + * we remember the ciphertext. Then if we have to resync things + * after having processed, say, 2 bytes, we refill the iv buffer + * with the last 6 bytes of the old ciphertext followed by the + * 2 bytes of new ciphertext stored in the front of the iv buffer. + */ +void +ideaCfbSync(struct IdeaCfbContext *context) +{ + int bufleft = context->bufleft; + + if (bufleft) { + memcpy(context->iv+bufleft, context->iv, 8-bufleft); + memcpy(context->iv, context->oldcipher+8-bufleft, bufleft); + context->bufleft = 0; + } +} + +/* + * Encrypt a buffer of data, using IDEA in CFB mode. + * There are more compact ways of writing this, but this is + * written for speed. + */ +void +ideaCfbEncrypt(struct IdeaCfbContext *context, byte const *src, + byte *dest, int count) +{ + int bufleft = context->bufleft; + byte *bufptr = context->iv + 8-bufleft; + + /* If there are no more bytes to encrypt that there are bytes + * in the buffer, XOR them in and return. + */ + if (count <= bufleft) { + context->bufleft = bufleft - count; + while (count--) { + *dest++ = *bufptr++ ^= *src++; + } + return; + } + count -= bufleft; + /* Encrypt the first bufleft (0 to 7) bytes of the input by XOR + * with the last bufleft bytes in the iv buffer. + */ + while (bufleft--) { + *dest++ = (*bufptr++ ^= *src++); + } + /* Encrypt middle blocks of the input by cranking the cipher, + * XORing 8-byte blocks, and repeating until the count + * is 8 or less. + */ + while (count > 8) { + bufptr = context->iv; + memcpy(context->oldcipher, bufptr, 8); + ideaCipher(bufptr, bufptr, context->key); + bufleft = 8; + count -= 8; + do { + *dest++ = (*bufptr++ ^= *src++); + } while (--bufleft); + } + /* Do the last 1 to 8 bytes */ + bufptr = context->iv; + memcpy(context->oldcipher, bufptr, 8); + ideaCipher(bufptr, bufptr, context->key); + context->bufleft = 8-count; + do { + *dest++ = (*bufptr++ ^= *src++); + } while (--count); +} + + +/* + * Decrypt a buffer of data, using IDEA in CFB mode. + * There are more compact ways of writing this, but this is + * written for speed. + */ +void +ideaCfbDecrypt(struct IdeaCfbContext *context, byte const *src, + byte *dest, int count) +{ + int bufleft = context->bufleft; + static byte *bufptr; + byte t; + + bufptr = context->iv + (8-bufleft); + if (count <= bufleft) { + context->bufleft = bufleft - count; + while (count--) { + t = *bufptr; + *dest++ = t ^ (*bufptr++ = *src++); + } + return; + } + count -= bufleft; + while (bufleft--) { + t = *bufptr; + *dest++ = t ^ (*bufptr++ = *src++); + } + while (count > 8) { + bufptr = context->iv; + memcpy(context->oldcipher, bufptr, 8); + ideaCipher(bufptr, bufptr, context->key); + bufleft = 8; + count -= 8; + do { + t = *bufptr; + *dest++ = t ^ (*bufptr++ = *src++); + } while (--bufleft); + } + bufptr = context->iv; + memcpy(context->oldcipher, bufptr, 8); + ideaCipher(bufptr, bufptr, context->key); + context->bufleft = 8-count; + do { + t = *bufptr; + *dest++ = t ^ (*bufptr++ = *src++); + } while (--count); +} + +/********************************************************************/ + +/* + * Cryptographically strong pseudo-random-number generator. + * The design is from Appendix C of ANSI X9.17, "Financial + * Institution Key Management (Wholesale)", with IDEA + * substituted for the DES. + */ + +/* + * Initialize a cryptographic random-number generator. + * key and seed should be arbitrary. + */ +void +ideaRandInit(struct IdeaRandContext *context, byte const (key[16]), + byte const (seed[8])) +{ + int i; + + ideaExpandKey(key, context->key); + context->bufleft = 0; + memcpy(context->internalbuf, seed, 8); +} + + +/* + * Read out the RNG's state. + */ +void +ideaRandState(struct IdeaRandContext *context, byte key[16], byte seed[8]) +{ + int i; + + memcpy(seed, context->internalbuf, 8); + for (i = 0; i < 8; i++) { + key[2*i] = context->key[i] >> 8; + key[2*i+1] = context->key[i]; + } + +} + +/* + * Encrypt the RNG's state with the given CFB encryptor. + */ +void +ideaRandWash(struct IdeaRandContext *context, struct IdeaCfbContext *cfb) +{ + byte keyseed[16+8]; + int i; + + ideaRandState(context, keyseed, keyseed+16); + ideaCfbEncrypt(cfb, keyseed, keyseed, 16+8); + ideaRandInit(context, keyseed, keyseed+16); + + memset(keyseed, 0, 16+8); +} + +/* + * Cryptographic pseudo-random-number generator, used for generating + * session keys. + */ +byte +ideaRandByte(struct IdeaRandContext *c) +{ + int i; + + if (!c->bufleft) { + byte timestamp[8]; + + /* Get some true-random noise to help */ + randPoolGetBytes(timestamp, sizeof(timestamp)); + + /* Compute next 8 bytes of output */ + for (i=0; i<8; i++) + c->outbuf[i] = c->internalbuf[i] ^ timestamp[i]; + ideaCipher(c->outbuf, c->outbuf, c->key); + /* Compute new seed vector */ + for (i=0; i<8; i++) + c->internalbuf[i] = c->outbuf[i] ^ timestamp[i]; + ideaCipher(c->internalbuf, c->internalbuf, c->key); + burn(timestamp); + c->bufleft = 8; + } + return c->outbuf[--c->bufleft]; +} + +/* end of idea.c */ +