#include #include typedef unsigned char byte; typedef unsigned char nibble; /* A nibble is 4 bits. One byte is two nibbles. =) */ /* The following assumes a 64-bit (8-byte) blocksize, with blocks represented as an array of 8 unsigned chars (bytes). Bit positions are indexed from 0-63. */ /* You should not need to directly use the next four macros */ #define BITMASK(b) (1 << (7-((b) % 8))) #define BITSLOT(b) ((b) / 8) #define BIT_ON(a,b) ((a)[BITSLOT(b)] |= BITMASK(b)) #define BIT_OFF(a,b) ((a)[BITSLOT(b)] &= (~BITMASK(b))) // set the bth bit in block equal to c #define BIT_SET(block,b,c) ((c) ? BIT_ON(block,b) : BIT_OFF(block,b)) // get the value of the bth bit in block #define BIT_AT(block,b) (((block)[BITSLOT(b)] >> (7-((b)%8))) & 1) /* 4-bit to 4-bit S-boxes implemented as look-up tables. 8 distinct S-boxes; each S-box will be used twice */ byte S1[16] = {1, 6, 9, 10, 11, 2, 7, 3, 15, 4, 5, 14, 8, 12, 0, 13}; byte S2[16] = {5, 4, 1, 9, 0, 13, 12, 6, 10, 2, 11, 3, 7, 14, 8, 15}; byte S3[16] = {5, 1, 0, 12, 10, 11, 7, 8, 15, 14, 3, 2, 6, 13, 9, 4}; byte S4[16] = {12, 7, 14, 6, 4, 9, 2, 15, 11, 0, 5, 1, 10, 8, 3, 13}; byte S5[16] = {7, 9, 0, 8, 13, 1, 15, 6, 12, 14, 4, 2, 11, 5, 10, 3}; byte S6[16] = {10, 11, 4, 12, 15, 13, 0, 7, 9, 8, 1, 6, 14, 2, 5, 3}; byte S7[16] = {1, 2, 10, 12, 0, 8, 14, 3, 4, 13, 9, 6, 5, 11, 15, 7}; byte S8[16] = {15, 1, 12, 3, 5, 11, 7, 6, 2, 10, 9, 13, 0, 14, 8, 4}; // print a string void print_string(byte *x, int num_bytes); void cipher(byte *key, byte *input_block); /* expects key to be a 16-byte array, and input_block to be an 8-byte array. Evaluates the cipher on input_block, and stores the result in input_block */ void cipher_hiddenkey(byte *input_block); /* expects input_block to be an 8-byte array. The key is chosen uniformly the first time this is called. */ void Sbox(int i, byte *x); /* applies the appropriate S-box to the ith byte of x */ /*******************************************************************/ /* The following main() just gives an example of how the code is used. Feel free to delete */ main(){ byte x[8]; int i; for (i=0; i < 64; i++) BIT_SET(x,i,i%2); print_string(x, 8); cipher_hiddenkey(x); print_string(x, 8); } /*******************************************************************/ /* Note: no input validation is done in cipher_hiddenkey(). It expects x to be an 8-byte string. As usual, buffer overflows are out of scope here. */ void cipher_hiddenkey(byte *x){ static byte key[16]; static char flag=0; FILE *randfile; int i; if (flag==0) { flag=1; randfile = fopen("/dev/urandom", "r"); // urandom is NOT safe for real-world crypto applications for (i=0; i<16; i++) fscanf(randfile, "%c", &key[i]); /* the key is printed to stdout. Obviously, this is insecure. I do it here so you can check whether your attack works */ print_string(key, 16); } cipher(key, x); return; } /* Note: no input validation is done in cipher(). It expects key to be a 16-byte string, and x to be an 8-byte string. As usual, buffer overflows are out of scope. */ void cipher(byte * key, byte * x){ byte ka[8], kb[8], tmp[8]; int i,j; /* ka is the first half of key; kb is the second half */ for (i=0; i<8; i++) { ka[i] = key[i]; kb[i] = key[i+8]; } for (j=1; j<=2; j++){ /* Use 2 rounds */ /* XOR appropriate sub-key */ if (j%2) { for (i=0; i<8; i++) x[i] ^= ka[i]; } else { for (i=0; i<8; i++) x[i] ^= kb[i]; } /* Apply S-boxes to the bytes of x */ for (i=0; i<8; i++) Sbox(i,x); /* apply mixing permutation */ for (i=0; i < 8; i++) tmp[i] = x[i]; for (i=0; i < 64; i+=4) { BIT_SET(x,i,BIT_AT(tmp,i)); BIT_SET(x,i+1,BIT_AT(tmp,((i+1)+60)%64)); BIT_SET(x,i+2,BIT_AT(tmp,((i+2)+56)%64)); BIT_SET(x,i+3,BIT_AT(tmp,((i+3)+52)%64)); } } return; } /* Note: no input validation is done. The input x is expected to be an 8-byte string */ void Sbox(int i, byte *x){ nibble in1, in2, out1, out2; in1 = x[i] >> 4; // left nibble of x[i] in2 = x[i] & 15; // right nibble of x[i] /* Our S-boxes operate on nibbles. So to substitute a byte, we just substitute both its nibbles */ if ((i==0) || (i==7)) x[i] = 16*S1[in1] + S2[in2]; if ((i==1) || (i==6)) x[i] = 16*S3[in1] + S4[in2]; if ((i==2) || (i==5)) x[i] = 16*S5[in1] + S6[in2]; if ((i==3) || (i==4)) x[i] = 16*S7[in1] + S8[in2]; return; } // Note: no input validation is done. void print_string(byte * x, int num_bytes){ int i; for (i=0; i < 8*num_bytes; i++) { printf("%d", BIT_AT(x,i)); if ((i%8) == 7) printf(" "); } printf("\n"); return; }