// My (slow) implementation of DES // Currently set for 3 rounds with no IP or inverse IP perms. // The plaintext is 748502CD 38451097 and the key is 1a624c89 520dec46. // Copyright 2007 by John Black // // Permission is granted to students of CSCI 7000 at CU to use, modify, // and freely take pieces of this code for use in homework. #define NUMRNDS 3 // Set these to 1 to turn them on; 0 turns them off #define APPLY_IP 0 #define APPLY_IPI 0 #define FINAL_REVERSE 0 main() { int pt[2]={0x748502CD, 0x38451097}; int ct[2]; int key[2]={0x1a624c89, 0x520dec46}; des_encrypt(pt, ct, key); printf("Ciphertext: %08x, %08x\n", ct[0], ct[1]); } // the expansion function E() char exp[]={ 0, 32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9, 8, 9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17, 16, 17, 18, 19, 20, 21, 20, 21, 22, 23, 24, 25, 24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32, 1}; // these permutations, pc1 and pc2, are used in key scheduling static unsigned char pc1[] = { 0, 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36, 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4 }; static unsigned char pc2[] = { 0, 14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48, 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32 }; // The S-boxes (the heart of DES) // static unsigned char s[][64] = { { // S1 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13 }, { // S2 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9 }, { // S3 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12 }, { // S4 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14 }, { // S5 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3 }, { // S6 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13 }, { // S7 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12 }, { // S8 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 } }; // the permutation P is applied after the S-boxes static char p[] = { 0, 16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25 }; des_encrypt(int *pt, int *ct, int *key) { char ip[65]; char ipi[65]; char pta[65], ptb[65]; char cta[65]; char ex[49], rk[49]; char rval[33], mask[33], temp[33]; int i, j; int x=0; int y=8; int round = 1; // set up ip and ip inverse for (i=1; i <= 64; i += 2) { ip[((x+4) << 3) + y] = i; ip[(x << 3) + y] = i+1; ipi[i] = ((x+4) << 3) + y; ipi[i+1] = (x << 3) + y; x++; if (i % 8 == 7) { y--; x=0; } } // to avoid horrendous bit masking we break everything into char arrays unpack(pt, pta); // apply ip to the plaintext pta to get ptb if (APPLY_IP) { for (i = 1; i <= 64; i++) ptb[i] = pta[ip[i]]; printf("After IP: "); dump(ptb, 64); } else for (i = 1; i <= 64; i++) ptb[i] = pta[i]; for (round=1; round <= NUMRNDS; round++) { printf("\n\n*** Round %d:\n", round); // apply the expansion function E() to get ex from ptb's right half for (i = 1; i <= 48; i++) ex[i] = ptb[exp[i]+32]; printf("Expansion: "); dump(ex, 48); // get key for this round getkey(key, rk, round); printf("Round Key: "); dump(rk, 48); // xor the round key rk into the exanded right half ex for (i = 1; i <= 48; i++) ex[i] ^= rk[i]; printf("XOR output: "); dump(ex, 48); // and now apply the 8 S-boxes for (i = 1; i <= 8; i++) { int sval; int j; j = (i-1)*6; sval = s[i-1][(ex[j+1]*2+ex[j+6])*16 + ex[j+2]*8 + ex[j+3]*4 + ex[j+4]*2 + ex[j+5]]; for (j=4; j >= 1; j--) { rval[(i-1)*4 + j] = (sval & 1); sval >>= 1; } } printf("S-Box output: "); dump(rval, 32); // finally apply the P permutation for (i = 1; i <= 32; i++) mask[i] = rval[p[i]]; printf("Mask Value: "); dump(mask, 32); // now we do the Feistel dance: move right side of ptb to // the left, then store mask xor that left side in the right for (i = 1; i <= 32; i++) temp[i] = ptb[i]; // copy the left side for (i = 1; i <= 32; i++) ptb[i] = ptb[i+32]; // move right half to left for (i = 1; i <= 32; i++) ptb[i+32] = temp[i] ^ mask[i]; printf("Round Output: "); dump(ptb, 64); } // Almost done; now apply IP^-1 to ptb (with halves reversed) if (FINAL_REVERSE) { for (i=1; i <= 32; i++) temp[i] = ptb[i]; // copy the left half for (i = 1; i <= 32; i++) ptb[i] = ptb[i+32]; // move right half to left for (i=1; i <= 32; i++) ptb[i+32] = temp[i]; // put right half back } if (APPLY_IPI) for (i=1; i <= 64; i++) cta[i] = ptb[ipi[i]]; else for (i=1; i <= 64; i++) cta[i] = ptb[i]; printf("\n\nCiphertext: "); dump(cta, 64); pack(ct, cta); } getkey(int *key, char *rk, int round) { char t[65], s[57]; int i; char p; int k; // we transfer k1 and k2 (the key) into the t array unpack(key, t); // now apply the pc1 permutation for (i=1; i <= 56; i++) s[i] = t[pc1[i]]; // next circular shift each half once for i up to the round number // but double shift all except 1, 2, 9, and 16 for (k=1; k <= round; k++) { p = s[1]; for (i=1; i <=27; i++) s[i] = s[i+1]; s[28] = p; p = s[29]; for (i=29; i <=55; i++) s[i] = s[i+1]; s[56] = p; if (k == 1 || k == 2 || k == 9 || k == 16) continue; p = s[1]; for (i=1; i <=27; i++) s[i] = s[i+1]; s[28] = p; p = s[29]; for (i=29; i <=55; i++) s[i] = s[i+1]; s[56] = p; } // finally, apply pc2 to these 56 bits to produce the 48-bit round key for (i=1; i <= 48; i++) rk[i] = s[pc2[i]]; } // this routine unpacks 64-bit values into a char array unpack(int *pt, char *ca) { int i; int a = pt[0]; int b = pt[1]; for (i=32; i >= 1; i--) { ca[i] = (a & 1); a >>= 1; ca[i+32] = (b & 1); b >>= 1; } //dump(ca, 64); } // this routine packs a 0-1 char array into two 32-bit ints pack(int *ct, char *ca) { int i; ct[0] = ct[1] = 0; for (i=1; i <= 32; i++) { ct[0] <<= 1; ct[0] += ca[i]; ct[1] <<= 1; ct[1] += ca[i+32]; } } // dump a 0-1 char array for debugging purposes dump(char *ca, int len) { int i; for (i=1; i <= len; i++) { printf("%d", ca[i]); if (i % 4 == 0) printf(" "); } printf("\n"); }