📄 pgpidea.c
字号:
/*
* pgpIDEA.c - C source code for IDEA block cipher.
* Algorithm developed by Xuejia Lai and James L. Massey, of ETH Zurich.
*
* $Id: pgpIDEA.c,v 1.16 1997/10/14 01:48:18 heller Exp $
*
* 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 best for your machine.
*
* Multiplication: default is inline, -DAVOID_JUMPS uses a different
* version that does not do any conditional jumps (a few percent
* worse on a SPARC, better on other machines), while
* -DSMALL_CACHE takes it out of line to stay within a small
* on-chip code cache. (Not really applicable with current L1
* cache sizes.)
* Variables: normally, 16-bit variables are used, but some machines do
* not have 16-bit registers, so they do a great deal of masking.
* -DUSE_IDEA32 uses "int" register variables and masks explicitly
* only where necessary. On a SPARC, for example, this boosts
* performance 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 runs on arrays of bytes by taking pairs in big-endian order
* to make the 16-bit words that IDEA uses internally. This produces the
* same result regardless of the byte order of the native CPU.
*/
#include "pgpSDKBuildFlags.h"
#ifndef PGP_IDEA
#error you must define PGP_IDEA one way or the other
#endif
#if PGP_IDEA /* [ */
#include <string.h>
#include "pgpConfig.h"
#include "pgpSymmetricCipherPriv.h"
#include "pgpIDEA.h"
#include "pgpMem.h"
#include "pgpUsuals.h"
/* If IDEA32 isn't predefined as 1 or 0, make a guess. */
#ifndef USE_IDEA32
#if UINT_MAX > 0xffff
#define USE_IDEA32 1
#endif
#endif
#if USE_IDEA32 /* Use >16-bit temporaries */
#define low16(x) ((x) & 0xFFFF)
typedef unsigned int uint16; /* at LEAST 16 bits, maybe more */
#else
#define low16(x) (uint16)(x)
typedef PGPUInt16 uint16;
#endif
/* A few handy definitions */
#define IDEA_ROUNDS 8
#define IDEA_KEYLEN (6*IDEA_ROUNDS+4)
#define IDEA_KEYBYTES (sizeof(PGPUInt16) * IDEA_KEYLEN)
/*
* Flags in priv array to record whether key schedule is in encrypt
* or decrypt mode
*/
#define IDEA_ENCRYPTION_MODE 0x11
#define IDEA_DECRYPTION_MODE 0x22
/* Private functions */
/* Expand a 128-bit user key to a working encryption key EK */
static void
ideaExpandKey(PGPByte const *userkey, PGPUInt16 *EK)
{
int i, j;
for (j=0; j<8; j++) {
EK[j] = (userkey[0]<<8) + userkey[1];
userkey += 2;
}
for (i=0; j < IDEA_KEYLEN; j++) {
i++;
EK[i+7] = EK[i & 7] << 9 | EK[(i+1) & 7] >> 7;
EK += i & 8;
i &= 7;
}
} /* ideaExpandKey */
/*
* 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.
*/
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);
} /* mulInv */
/*
* 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(PGPUInt16 const EK[IDEA_KEYLEN], PGPUInt16 DK[IDEA_KEYLEN])
{
int i;
uint16 t1, t2, t3;
PGPUInt16 temp[IDEA_KEYLEN];
PGPUInt16 *p = temp + IDEA_KEYLEN;
t1 = mulInv(*EK++);
t2 = -*EK++;
t3 = -*EK++;
*--p = mulInv(*EK++);
*--p = t3;
*--p = t2;
*--p = t1;
for (i = 0; i < IDEA_ROUNDS-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));
pgpClearMemory( temp, sizeof(temp));
} /* ideaInvertKey */
/*
* MUL(x,y) computes x *= y, modulo 0x10001. Requires two temps,
* t16 and t32. x is modified, and must be a side-effect-free lvalue.
* y may be anything, and is guaranteed to be evaluated exactly once,
* 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.
*/
#if SMALL_CACHE
#define MUL(x,y) (x = mul(low16(x),y))
static uint16
mul(register uint16 a, register uint16 b)
{
register PGPUInt32 p;
p = (PGPUInt32)a * b;
if (p) {
b = low16(p);
a = p>>16;
return (b - a) + (b < a);
} else {
return 1-a-b;
}
} /* mul */
#elif AVOID_JUMPS
#define MUL(x,y) ( x = low16(x-1), t16 = low16((y)-1), \
t32 = (PGPUInt32)x*t16 + x + t16, \
x = low16(t32), t16 = t32>>16, \
x = (x-t16) + (x<t16) + 1 )
#else /* default */
#define MUL(x,y) (x = \
( ( t32 = (PGPUInt32)(x=low16(x))*(t16=(y)) ) != 0 ) ? \
x = low16(t32), \
t16 = (uint16)(t32>>16), \
(x-t16)+(x<t16) \
: \
(uint16)(1-x-t16) \
)
#endif
/*
* IDEA encryption/decryption algorithm. In and out may be the same buffer.
* key is a pointer to IDEAKEYLEN words. (Borland C 3.1 gives an error if
* you declare it as a "PGPUInt16 const key[IDEAKEYLEN]" array.)
*/
static void
ideaCipher(PGPByte const inbuf[8], PGPByte outbuf[8], PGPUInt16 const *key)
{
register uint16 x1, x2, x3, x4, s2, s3;
#if !SMALL_CACHE
register uint16 t16; /* Temporaries needed by MUL macro */
register PGPUInt32 t32;
#endif
int r = IDEA_ROUNDS;
x1 = (uint16)inbuf[0] << 8 | inbuf[1];
x2 = (uint16)inbuf[2] << 8 | inbuf[3];
x3 = (uint16)inbuf[4] << 8 | inbuf[5];
x4 = (uint16)inbuf[6] << 8 | inbuf[7];
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);
outbuf[0] = (PGPByte)(x1>>8);
outbuf[1] = (PGPByte)x1;
outbuf[2] = (PGPByte)(x3>>8);
outbuf[3] = (PGPByte)x3;
outbuf[4] = (PGPByte)(x2>>8);
outbuf[5] = (PGPByte)x2;
outbuf[6] = (PGPByte)(x4>>8);
outbuf[7] = (PGPByte)x4;
} /* ideaCipher */
/*
* Exported functions
*/
static void
ideaKey(void *priv, void const *key)
{
/* Do key schedule for encryption, can be converted later */
ideaExpandKey((const PGPByte *) key, (PGPUInt16 *)priv);
*((PGPByte *)priv + IDEA_KEYBYTES) = IDEA_ENCRYPTION_MODE;
}
static void
ideaEncrypt(void *priv, void const *in, void *out)
{
/* Make sure key schedule is in the right mode */
if (*((PGPByte *)priv + IDEA_KEYBYTES) != IDEA_ENCRYPTION_MODE) {
ideaInvertKey ((PGPUInt16 *)priv, (PGPUInt16 *)priv);
*((PGPByte *)priv + IDEA_KEYBYTES) = IDEA_ENCRYPTION_MODE;
}
/* Do the operation */
ideaCipher((const PGPByte *) in, (PGPByte *) out, (PGPUInt16 *)priv);
}
static void
ideaDecrypt(void *priv, void const *in, void *out)
{
/* Make sure key schedule is in the right mode */
if (*((PGPByte *)priv + IDEA_KEYBYTES) != IDEA_DECRYPTION_MODE) {
ideaInvertKey ((PGPUInt16 *)priv, (PGPUInt16 *)priv);
*((PGPByte *)priv + IDEA_KEYBYTES) = IDEA_DECRYPTION_MODE;
}
/* Do the operation */
ideaCipher((const PGPByte *) in, (PGPByte *) out, (PGPUInt16 *)priv);
}
/*
* Do one 64-bit step of a Tandem Davies-Meyer hash computation.
* The hash buffer is 32 bytes long and contains H (0..7), then G (8..15),
* then 16 bytes of scratch space. The buf is 8 bytes long.
* xkey is a temporary key schedule buffer.
* This and the extra data in the hash buffer are allocated by the
* caller to reduce the amount of buffer-wiping we have to do.
* (It's only called from ideaWash, so the interface can be a bit
* specialized.)
*/
static void
ideaStepTandemDM(PGPByte *hash, PGPByte const *buf, PGPUInt16 *xkey)
{
int i;
/* key1 = G << 64 + M, remembering that IDEA is big-endian */
memcpy(hash+16, buf, 8);
ideaExpandKey(hash+8, xkey);
/* W = E_key1(H), key2 = M << 64 + W */
ideaCipher(hash, hash+24, xkey);
ideaExpandKey(hash+16, xkey);
/* V = E_key2(G) */
ideaCipher(hash+8, hash+16, xkey);
/* H ^= W, G ^= V */
for (i = 0; i < 8; i++) {
hash[i] ^= hash[i+24];
hash[i+8] ^= hash[i+16];
}
}
/*
* Munge the key of the CipherContext based on the supplied bytes.
* This is for random-number generation, so the exact technique is
* unimportant, but it happens to use the current key as the
* IV for computing a tandem Davies-Meyer hash of the bytes,
* and uses the output as the new key.
*/
static void
ideaWash(void *priv, void const *bufIn, PGPSize len)
{
unsigned i;
PGPByte hash[32];
PGPUInt16 *xkey = (PGPUInt16 *)priv;
PGPByte *buf = (PGPByte *) bufIn;
/* Read out the key in canonical byte order for the IV */
for (i = 0; i < 8; i++) {
hash[2*i] = (PGPByte)(xkey[i]>>8);
hash[2*i+1] = (PGPByte)xkey[i];
}
/* Do the initial blocks of the hash */
i = len;
while (i >= 8) {
ideaStepTandemDM(hash, buf, xkey);
buf += 8;
i -= 8;
}
/*
* At the end, we do Damgard-Merkle strengthening, just like
* MD5 or SHA. Pad with 0x80 then 0 bytes to 6 mod 8, then
* add the length. We use a 16-bit length in bytes instead
* of a 64-bit length in bits, but that is cryptographically
* irrelevant.
*/
/* Do the first partial block - i <= 7 */
memcpy(hash+24, buf, i);
hash[24 + i++] = 0x80;
if (i > 6) {
pgpClearMemory(hash+24+i, 8-i);
ideaStepTandemDM(hash, hash+24, xkey);
i = 0;
}
pgpClearMemory(hash+24+i, 6-i);
hash[30] = (PGPByte)(len >> 8);
hash[31] = (PGPByte)len;
ideaStepTandemDM(hash, hash+24, xkey);
/* Re-schedule the key */
ideaExpandKey(hash, xkey);
pgpClearMemory( hash, sizeof(hash));
}
/*
* Define a Cipher for the generic cipher. This is the only
* real exported thing -- everything else can be static, since everything
* is referenced through function pointers!
*/
PGPCipherVTBL const cipherIDEA = {
"IDEA",
kPGPCipherAlgorithm_IDEA,
8, /* Blocksize */
16, /* Keysize */
IDEA_KEYBYTES + 1, /* Last one remembers encrypt vs decrypt */
alignof(PGPUInt16),
ideaKey,
ideaEncrypt,
ideaDecrypt,
ideaWash
};
#if UNITTEST /* Currently unused; left in in case of future need */
/* Test driver proper starts here */
#include <stdio.h>
#include <time.h>
/*
* 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;
PGPByte userkey[16];
PGPByte priv[IDEA_KEYBYTES+1];
PGPByte 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... */
ideaKey(priv, userkey);
#if 0
ideaExpandKey(userkey, EK);
printf("\nEncryption key subblocks: ");
for (j=0; j<IDEA_ROUNDS+1; j++) {
printf("\nround %d: ", j+1);
if (j < IDEA_ROUNDS)
for(i=0; i<6; i++)
printf(" %6u", EK[j*6+i]);
else
for(i=0; i<4; i++)
printf(" %6u", EK[j*6+i]);
}
/* Compute decryption subkeys from encryption subkeys... */
ideaInvertKey(EK, DK);
printf("\nDecryption key subblocks: ");
for (j=0; j<IDEA_ROUNDS+1; j++) {
printf("\nround %d: ", j+1);
if (j < IDEA_ROUNDS)
for(i=0; i<6; i++)
printf(" %6u", DK[j*6+i]);
else
for(i=0; i<4; i++)
printf(" %6u", DK[j*6+i]);
}
#endif
/* Make a sample plaintext pattern for testing... */
for (k=0; k<8; k++)
XX[k] = k;
printf("\n Encrypting %d bytes (%ld blocks)...", BLOCKS*16, BLOCKS);
fflush(stdout);
start = clock();
memcpy(YY, XX, 8);
for (l = 0; l < BLOCKS; l++)
ideaEncrypt(priv, YY, YY); /* repeated encryption */
memcpy(ZZ, YY, 8);
for (l = 0; l < BLOCKS; l++)
ideaDecrypt(priv, ZZ, ZZ); /* repeated decryption */
end = clock() - start;
l = end * 1000 / CLOCKS_PER_SEC + 1;
i = l/1000;
j = l%1000;
l = BLOCKS * 16 * CLOCKS_PER_SEC / end;
printf("%d.%03d seconds = %ld bytes per second\n", i, j, l);
printf("\nX %3u %3u %3u %3u %3u %3u %3u \n",
XX[0], XX[1], XX[2], XX[3], XX[4], XX[5], XX[6], XX[7]);
printf("\nY %3u %3u %3u %3u %3u %3u %3u \n",
YY[0], YY[1], YY[2], YY[3], YY[4], YY[5], YY[6], YY[7]);
printf("\nZ %3u %3u %3u %3u %3u %3u %3u \n",
ZZ[0], ZZ[1], ZZ[2], ZZ[3], ZZ[4], ZZ[5], ZZ[6], ZZ[7]);
/* Now decrypted ZZ should be same as original XX */
for (k=0; k<8; k++)
if (XX[k] != ZZ[k]) {
printf("\n\07Error! Noninvertable encryption.\n");
exit(-1); /* error exit */
}
printf("\nNormal exit.\n");
return 0; /* normal exit */
} /* main */
#endif /* 0 */
#endif /* ] PGP_IDEA */
/*__Editor_settings____
Local Variables:
tab-width: 4
End:
vi: ts=4 sw=4
vim: si
_____________________*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -