[2839] in cryptography@c2.net mail archive
RE: "damn the bitmaps..."
daemon@ATHENA.MIT.EDU (Colin Plumb)
Wed Jun 24 18:30:03 1998
Date: Wed, 24 Jun 1998 16:11:15 -0600 (MDT)
From: Colin Plumb <colin@nyx.net>
To: cryptography@c2.net, jya@pipeline.com
This one appears to work. The output matches, modulo the rotations.
Note the ugly hack I did to the key vector (duplicating the last two
entries to make each round's subkey always consecutive).
Uncommented and in need of cleanup, of course.
the simplicity of the key schedule is interesting. Civilian work is
tending towards more sophisticated key scheduling, to defend the independent
round subkeys assumptions made in various strength proofs.
I also wonder about the F[] table. Things like 26->60, 27->68 (one bit in,
one bit out) seem odd.
Anyway, I'm sure it'll be a fruitful source of discussion.
#include <stdio.h>
#include <string.h>
typedef unsigned char uint8;
typedef unsigned short uint16;
static uint8 const F[0x100] = {
0xa3, 0xd7, 0x09, 0x83, 0xf8, 0x48, 0xf6, 0xf4, 0xb3, 0x21, 0x15, 0x78, 0x99, 0xb1, 0xaf, 0xf9,
0xe7, 0x2d, 0x4d, 0x8a, 0xce, 0x4c, 0xca, 0x2e, 0x52, 0x95, 0xd9, 0x1e, 0x4e, 0x38, 0x44, 0x28,
0x0a, 0xdf, 0x02, 0xa0, 0x17, 0xf1, 0x60, 0x68, 0x12, 0xb7, 0x7a, 0xc3, 0xe9, 0xfa, 0x3d, 0x53,
0x96, 0x84, 0x6b, 0xba, 0xf2, 0x63, 0x9a, 0x19, 0x7c, 0xae, 0xe5, 0xf5, 0xf7, 0x16, 0x6a, 0xa2,
0x39, 0xb6, 0x7b, 0x0f, 0xc1, 0x93, 0x81, 0x1b, 0xee, 0xb4, 0x1a, 0xea, 0xd0, 0x91, 0x2f, 0xb8,
0x55, 0xb9, 0xda, 0x85, 0x3f, 0x41, 0xbf, 0xe0, 0x5a, 0x58, 0x80, 0x5f, 0x66, 0x0b, 0xd8, 0x90,
0x35, 0xd5, 0xc0, 0xa7, 0x33, 0x06, 0x65, 0x69, 0x45, 0x00, 0x94, 0x56, 0x6d, 0x98, 0x9b, 0x76,
0x97, 0xfc, 0xb2, 0xc2, 0xb0, 0xfe, 0xdb, 0x20, 0xe1, 0xeb, 0xd6, 0xe4, 0xdd, 0x47, 0x4a, 0x1d,
0x42, 0xed, 0x9e, 0x6e, 0x49, 0x3c, 0xcd, 0x43, 0x27, 0xd2, 0x07, 0xd4, 0xde, 0xc7, 0x67, 0x18,
0x89, 0xcb, 0x30, 0x1f, 0x8d, 0xc6, 0x8f, 0xaa, 0xc8, 0x74, 0xdc, 0xc9, 0x5d, 0x5c, 0x31, 0xa4,
0x70, 0x88, 0x61, 0x2c, 0x9f, 0x0d, 0x2b, 0x87, 0x50, 0x82, 0x54, 0x64, 0x26, 0x7d, 0x03, 0x40,
0x34, 0x4b, 0x1c, 0x73, 0xd1, 0xc4, 0xfd, 0x3b, 0xcc, 0xfb, 0x7f, 0xab, 0xe6, 0x3e, 0x5b, 0xa5,
0xad, 0x04, 0x23, 0x9c, 0x14, 0x51, 0x22, 0xf0, 0x29, 0x79, 0x71, 0x7e, 0xff, 0x8c, 0x0e, 0xe2,
0x0c, 0xef, 0xbc, 0x72, 0x75, 0x6f, 0x37, 0xa1, 0xec, 0xd3, 0x8e, 0x62, 0x8b, 0x86, 0x10, 0xe8,
0x08, 0x77, 0x11, 0xbe, 0x92, 0x4f, 0x24, 0xc5, 0x32, 0x36, 0x9d, 0xcf, 0xf3, 0xa6, 0xbb, 0xac,
0x5e, 0x6c, 0xa9, 0x13, 0x57, 0x25, 0xb5, 0xe3, 0xbd, 0xa8, 0x3a, 0x01, 0x05, 0x59, 0x2a, 0x46
};
uint16 G(uint16 g, uint8 const *key)
{
uint8 g1 = g>>8;
uint8 g2 = g & 255;
g1 ^= F[g2 ^ key[0]];
g2 ^= F[g1 ^ key[1]];
g1 ^= F[g2 ^ key[2]];
g2 ^= F[g1 ^ key[3]];
return (uint16)(g1 << 8) | (uint16)g2;
}
uint16 Ginv(uint16 g, uint8 const *key)
{
uint8 g1 = g>>8;
uint8 g2 = g & 255;
g2 ^= F[g1 ^ key[3]];
g1 ^= F[g2 ^ key[2]];
g2 ^= F[g1 ^ key[1]];
g1 ^= F[g2 ^ key[0]];
return (uint16)(g1 << 8) | (uint16)g2;
}
#define G(l, r, key) \
((l) ^= F[(r) ^ (key)[0]], \
(r) ^= F[(l) ^ (key)[1]], \
(l) ^= F[(r) ^ (key)[2]], \
(r) ^= F[(l) ^ (key)[3]])
/* Round function A */
#define ruleA(data, doff, key, koff, counter) \
(G(data[doff], data[(doff)+1], key+(koff)), \
data[((doff)+6)%8] ^= data[doff], \
data[((doff)+7)%8] ^= data[(doff)+1] ^ counter)
/* round function B */
#define ruleB(data, doff, key, koff, counter) \
(data[((doff)+2)%8] ^= data[doff], \
data[((doff)+3)%8] ^= data[(doff)+1] ^ counter, \
G(data[doff], data[(doff)+1], key+(koff)))
/* Debugging */
#define P(data) \
for (i = 0; i < 8; i++) printf(" %02x", data[i]); putchar('\n')
void
encrypt(uint8 data[8], uint8 const key[12])
{
int i;
ruleA(data, 0, key, 0, 1); P(data);
ruleA(data, 6, key, 4, 2); P(data);
ruleA(data, 4, key, 8, 3); P(data);
ruleA(data, 2, key, 2, 4); P(data);
ruleA(data, 0, key, 6, 5); P(data);
ruleA(data, 6, key, 0, 6); P(data);
ruleA(data, 4, key, 4, 7); P(data);
ruleA(data, 2, key, 8, 8); P(data);
ruleB(data, 0, key, 2, 9); P(data);
ruleB(data, 6, key, 6, 10); P(data);
ruleB(data, 4, key, 0, 11); P(data);
ruleB(data, 2, key, 4, 12); P(data);
ruleB(data, 0, key, 8, 13); P(data);
ruleB(data, 6, key, 2, 14); P(data);
ruleB(data, 4, key, 6, 15); P(data);
ruleB(data, 2, key, 0, 16); P(data);
ruleA(data, 0, key, 4, 17); P(data);
ruleA(data, 6, key, 8, 18); P(data);
ruleA(data, 4, key, 2, 19); P(data);
ruleA(data, 2, key, 6, 20); P(data);
ruleA(data, 0, key, 0, 21); P(data);
ruleA(data, 6, key, 4, 22); P(data);
ruleA(data, 4, key, 8, 23); P(data);
ruleA(data, 2, key, 2, 24); P(data);
ruleB(data, 0, key, 6, 25); P(data);
ruleB(data, 6, key, 0, 26); P(data);
ruleB(data, 4, key, 4, 27); P(data);
ruleB(data, 2, key, 8, 28); P(data);
ruleB(data, 0, key, 2, 29); P(data);
ruleB(data, 6, key, 6, 30); P(data);
ruleB(data, 4, key, 0, 31); P(data);
ruleB(data, 2, key, 4, 32); P(data);
}
int
main(void)
{
uint8 const key[12] = { 0x00, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00, 0x99 };
uint8 const plain[8] = { 0x33, 0x22, 0x11, 0x00, 0xdd, 0xcc, 0xbb, 0xaa };
uint8 cipher[8];
memcpy(cipher, plain, 8);
encrypt(cipher, key);
return 0;
}