This demonstration shows a real differential cryptanalysis attack on a modifed version of S-DES.

Script started on Thu May 30 21:56:26 2002

[crandaj@node52-60 cryptanalysis]$ cat dpc.cpp

/******************************

*

* dpc.cpp

* Jedidiah Crandall, crandaj@erau.edu

* Generates plaintext/ciphertext pairs in a modified version of S-DES

* which has three rounds and no initial or final permutation

* The plaintext/ciphertext pairs differ only in the first bit

*

*******************************/

 

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <stdlib.h>

 

#define BYTE unsigned char

 

char key[25];

char *K1 = &key[0];

char *K2 = &key[8];

char *K3 = &key[16];

 

void SetKey(unsigned long int SetMe)

{

            int i;

            unsigned long int n;

 

            for (i = 0, n = 0x800000; i <= 31; i++, n /= 2)

            {

                        if (SetMe & n)

                                    key[i] = '1';

                        else

                                    key[i] = '0';

            }

            key[32] = '\0';

}

 

int SBox0[4][4] =

{

{1, 0, 3, 2},

{3, 2, 1, 0},

{0, 2, 1, 3},

{3, 1, 3, 2}

};

 

int SBox1[4][4] =

{

{0, 1, 2, 3},

{2, 0, 1, 3},

{3, 0, 1, 0},

{2, 1, 0, 3}

};

 

BYTE BinaryToByte(char *Binary)

{

            int n, i;

            BYTE ReturnMe = 0;

 

            for (i = 0, n = 128; i <= 7; i++, n /= 2)

            {

                        switch(Binary[i])

                        {

                        case '0': break;

                        case '1':

                                    ReturnMe += n;

                        break;

                        default:

                                    puts("BinaryToByte failed");

                                    exit(0);

                        break;

                        }

            }

 

            return ReturnMe;

}

 

void ByteToBinary(char *pBinary, BYTE TheByte)

{

            int i;

            BYTE n;

 

            for (i = 0, n = 0x80; i <= 7; i++, n >>= 1)

            {

                        if (TheByte & n)

                                    pBinary[i] = '1';

                        else

                                    pBinary[i] = '0';

            }

            pBinary[8] = '\0';

}

 

void S0(char *pDest, char *pSrc)

{

            int Row = (pSrc[0] - '0') * 2 + (pSrc[3] - '0');

            int Col = (pSrc[1] - '0') * 2 + (pSrc[2] - '0');

            BYTE LookUp = SBox0[Row][Col];

           

            pDest[0] = '0' + ((LookUp & 0x02) >> 1);

            pDest[1] = '0' + (LookUp & 0x01);

            pDest[2] = '\0';

}

 

void S1(char *pDest, char *pSrc)

{

            int Row = (pSrc[0] - '0') * 2 + (pSrc[3] - '0');

            int Col = (pSrc[1] - '0') * 2 + (pSrc[2] - '0');

            BYTE LookUp = SBox1[Row][Col];

           

            pDest[0] = '0' + ((LookUp & 0x02) >> 1);

            pDest[1] = '0' + (LookUp & 0x01);

            pDest[2] = '\0';

}

 

void P4(char *pDest, char *pS0Output, char *pS1Output)

{

            pDest[0] = pS0Output[1];

            pDest[1] = pS1Output[1];

            pDest[2] = pS1Output[0];

            pDest[3] = pS0Output[0];

            pDest[4] = '\0';

}

 

void XOR4(char *pDest, char *Operand1, char *Operand2)

{

            int i;

            BYTE a, b;

 

            for (i = 0; i <= 3; i++)

            {

                        a = Operand1[i] - '0';

                        b = Operand2[i] - '0';

                        pDest[i] = '0' + (a ^ b);

            }

            pDest[4] = '\0';

}

 

void EP(char *pDest, char *pSrc)

{

            pDest[0] = pSrc[3];

            pDest[1] = pSrc[0];

            pDest[2] = pSrc[1];

            pDest[3] = pSrc[2];

            pDest[4] = pSrc[1];

            pDest[5] = pSrc[2];

            pDest[6] = pSrc[3];

            pDest[7] = pSrc[0];

            pDest[8] = '\0';

}

 

void f(char *pDest, char *pSrc, char *Subkey)

{

            char *pLeft = &pSrc[0], *pRight = &pSrc[4];

            char OutputFromEP[9];

            char OutputFromXOR8[9];

            BYTE XOR8Result;

            char *pS0Input = &OutputFromXOR8[0];

            char *pS1Input = &OutputFromXOR8[4];

            char S0Output[3];

            char S1Output[3];

            char P4Output[5];

            char XOR4Output[5];

 

            EP(OutputFromEP, pRight);

 

            XOR8Result = BinaryToByte(OutputFromEP) ^ BinaryToByte(Subkey);

            ByteToBinary(OutputFromXOR8, XOR8Result);

            S0(S0Output, pS0Input);

            S1(S1Output, pS1Input);

 

            P4(P4Output, S0Output, S1Output);

            XOR4(XOR4Output, P4Output, pLeft);

            memcpy(pDest, XOR4Output, 4);

            memcpy(&pDest[4], pRight, 4);

            pDest[8] = '\0';

}

 

void SW(char *pDest, char *pSrc)

{

            pDest[0] = pSrc[4];

            pDest[1] = pSrc[5];

            pDest[2] = pSrc[6];

            pDest[3] = pSrc[7];

            pDest[4] = pSrc[0];

            pDest[5] = pSrc[1];

            pDest[6] = pSrc[2];

            pDest[7] = pSrc[3];

            pDest[8] = '\0';

}

 

BYTE SDES(BYTE nPlaintext)

{

            BYTE nCiphertext;

            char Plaintext[9];

            char OutOffK1[9];

            char IntofK2[9];

            char OutOffK2[9];

            char IntofK3[9];

            char OutOffK3[9];

 

            ByteToBinary(Plaintext, nPlaintext);

           

 

            f(OutOffK1, Plaintext, K1);

            SW(IntofK2, OutOffK1);

            f(OutOffK2, IntofK2, K2);

            SW(IntofK3, OutOffK2);

            f(OutOffK3, IntofK3, K3);

 

            nCiphertext = BinaryToByte(OutOffK3);

            return nCiphertext;

}

 

int main()

{

            unsigned int DidItAlready[128];

            unsigned int Plaintext;

            unsigned int NumberDone;

            unsigned long int TheKey;

 

            memset(DidItAlready, 0, 128 * sizeof(unsigned int));

 

            fprintf(stderr, "You must enter a hexadecimal key, such as 22B8FA\n");

            fprintf(stderr, "The last two hex digits (FA) are the 8-bit subkey for the last round.\n");

            fprintf(stderr, "Please enter the key you'd like to use:\n");

            scanf("%x", &TheKey);

            SetKey(TheKey);

 

            srand(2233);

 

            NumberDone = 0;

            while (NumberDone < 25)

            {

                        Plaintext = rand() % 128;

                        if (!DidItAlready[Plaintext])

                        {

                                    DidItAlready[Plaintext] = 1;

                                    printf("%d\t%d\n", Plaintext, SDES(Plaintext));

                                    Plaintext |= 0x80;

                                    printf("%d\t%d\n", Plaintext, SDES(Plaintext));

                                    NumberDone++;

                        }

            }

 

            printf("\n");

 

            return 0;

}

 

[crandaj@node52-60 cryptanalysis]$ cat diff.cpp

/**********************************************

* diff.cpp

* Jedidiah Crandall, crandaj@erau.edu

* Does differential cryptanalysis on a modified version of S-DES which

* uses three rounds instead of two, uses independent subkeys, and has

* no initial or final permutation

***********************************************/

 

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

 

#define BYTE unsigned char

 

int SBox0[4][4] =

{

{1, 0, 3, 2},

{3, 2, 1, 0},

{0, 2, 1, 3},

{3, 1, 3, 2}

};

 

int SBox1[4][4] =

{

{0, 1, 2, 3},

{2, 0, 1, 3},

{3, 0, 1, 0},

{2, 1, 0, 3}

};

 

BYTE BinaryToByte(char *Binary)

{

            int n, i;

            BYTE ReturnMe = 0;

 

            for (i = 0, n = 128; i <= 7; i++, n /= 2)

            {

                        switch(Binary[i])

                        {

                        case '0': break;

                        case '1':

                                    ReturnMe += n;

                        break;

                        default:

                                    puts("BinaryToByte failed");

                                    exit(0);

                        break;

                        }

            }

 

            return ReturnMe;

}

 

void ByteToBinary(char *pBinary, BYTE TheByte)

{

            int i;

            BYTE n;

 

            for (i = 0, n = 0x80; i <= 7; i++, n >>= 1)

            {

                        if (TheByte & n)

                                    pBinary[i] = '1';

                        else

                                    pBinary[i] = '0';

            }

            pBinary[8] = '\0';

}

void EP(char *pDest, char *pSrc)

{      

            pDest[0] = pSrc[3];

            pDest[1] = pSrc[0];

            pDest[2] = pSrc[1];

            pDest[3] = pSrc[2];

            pDest[4] = pSrc[1];

            pDest[5] = pSrc[2];

            pDest[6] = pSrc[3];

            pDest[7] = pSrc[0];

            pDest[8] = '\0';

}

 

void S0(char *pDest, char *pSrc)

{

            int Row = (pSrc[0] - '0') * 2 + (pSrc[3] - '0');

            int Col = (pSrc[1] - '0') * 2 + (pSrc[2] - '0');

            BYTE LookUp = SBox0[Row][Col];

           

            pDest[0] = '0' + ((LookUp & 0x02) >> 1);

            pDest[1] = '0' + (LookUp & 0x01);

            pDest[2] = '\0';

}

 

void S1(char *pDest, char *pSrc)

{

            int Row = (pSrc[0] - '0') * 2 + (pSrc[3] - '0');

            int Col = (pSrc[1] - '0') * 2 + (pSrc[2] - '0');

            BYTE LookUp = SBox1[Row][Col];

           

            pDest[0] = '0' + ((LookUp & 0x02) >> 1);

            pDest[1] = '0' + (LookUp & 0x01);

            pDest[2] = '\0';

}

 

void P4(char *pDest, char *pS0Output, char *pS1Output)

{

            pDest[0] = pS0Output[1];

            pDest[1] = pS1Output[1];

            pDest[2] = pS1Output[0];

            pDest[3] = pS0Output[0];

            pDest[4] = '\0';

}

 

void P4Inverse(char *pDest, char *pSrc)

{

            pDest[0] = pSrc[3];

            pDest[1] = pSrc[0];

            pDest[2] = pSrc[2];

            pDest[3] = pSrc[1];

            pDest[4] = '\0';

}

 

void XOR4(char *pDest, char *Operand1, char *Operand2)

{

            int i;

            BYTE a, b;

 

            for (i = 0; i <= 3; i++)

            {

                        a = Operand1[i] - '0';

                        b = Operand2[i] - '0';

                        pDest[i] = '0' + (a ^ b);

            }

            pDest[4] = '\0';

}

 

struct DiffPair

{

            unsigned int Plaintext0;

            unsigned int Ciphertext0;

            unsigned int Plaintext1;

            unsigned int Ciphertext1;

};

 

DiffPair ThePairs[50];

 

int main()

{

            unsigned int Loop;

            char sPlaintext[9];

            char sCiphertext[9];

            unsigned int Subkey;

            unsigned int TryPair;

            unsigned int Bad;

            unsigned int RightHalf0, RightHalf1;

            unsigned int LeftHalf0, LeftHalf1;

            char sRightHalf0[9], sRightHalf1[9];

            char sEPOutput0[9], sEPOutput1[9];

            unsigned int EPOutput0, EPOutput1;

            unsigned int SBox0Input0, SBox0Input1;

            unsigned int SBox1Input0, SBox1Input1;

            char sSBox0Input0[9], sSBox0Input1[9];

            char sSBox1Input0[9], sSBox1Input1[9];

            char sSBoxOutput0[9], sSBoxOutput1[9];

            unsigned int ActualDifference;

            unsigned int ExpectedDifference;

            char sExpectedDifference[9];

            char sP4InverseOutput[9];

           

 

            // First, read in about 25 plaintext/ciphertext pairs

            // we could probably get away with less, but 25 is not a bad number

            for (Loop = 0; Loop <= 24; Loop++)

            {

                        scanf("%s\t%s", sPlaintext, sCiphertext);

                        ThePairs[Loop].Plaintext0 = atoi(sPlaintext);

                        ThePairs[Loop].Ciphertext0 = atoi(sCiphertext);

                        scanf("%s\t%s", sPlaintext, sCiphertext);

                        ThePairs[Loop].Plaintext1 = atoi(sPlaintext);

                        ThePairs[Loop].Ciphertext1 = atoi(sCiphertext);

            }

 

            // This prints a few of the ciphertext/plaintext pairs in binary

            // so you can see what they look like

            for (Loop = 10; Loop <= 12; Loop++)

            {

                        ByteToBinary(sPlaintext, ThePairs[Loop].Plaintext0);

                        ByteToBinary(sCiphertext, ThePairs[Loop].Ciphertext0);

                        printf("%s -> %s\n", sPlaintext, sCiphertext);

                        ByteToBinary(sPlaintext, ThePairs[Loop].Plaintext1);

                        ByteToBinary(sCiphertext, ThePairs[Loop].Ciphertext1);

                        printf("%s -> %s\n", sPlaintext, sCiphertext);

            }

 

            // Now try every possible subkey

            for (Subkey = 0; Subkey <= 0xFF; Subkey++)

            {

                        Bad = 0;

                        for (TryPair = 0; TryPair <= 24; TryPair++)

                        {

                                    // The first thing we have to do is take the right halves of the

                                    // ciphertexts in the pair and put them back through E/P and everything

                                    // to find an ACTUAL XOR difference of the S-box output for this subkey

                                    RightHalf0 = ThePairs[TryPair].Ciphertext0 & 0x0f;

                                    RightHalf1 = ThePairs[TryPair].Ciphertext1 & 0x0f;

                                    ByteToBinary(sRightHalf0, RightHalf0);

                                    ByteToBinary(sRightHalf1, RightHalf1);

                                    EP(sEPOutput0, &sRightHalf0[4]);

                                    EP(sEPOutput1, &sRightHalf1[4]);

                                    EPOutput0 = BinaryToByte(sEPOutput0);

                                    EPOutput1 = BinaryToByte(sEPOutput1);

                                    EPOutput0 ^= Subkey;

                                    EPOutput1 ^= Subkey;

                                    SBox0Input0 = (EPOutput0 & 0xf0) >> 4;

                                    SBox1Input0 = EPOutput0 & 0x0f;

                                    SBox0Input1 = (EPOutput1 & 0xf0) >> 4;

                                    SBox1Input1 = EPOutput1 & 0x0f;

                                    ByteToBinary(sSBox0Input0, SBox0Input0);

                                    ByteToBinary(sSBox1Input0, SBox1Input0);

                                    ByteToBinary(sSBox0Input1, SBox0Input1);

                                    ByteToBinary(sSBox1Input1, SBox1Input1);

                                    memset(sSBoxOutput0, '0', 4);

                                    memset(sSBoxOutput1, '0', 4);

                                    S0(&sSBoxOutput0[4], &sSBox0Input0[4]);

                                    S1(&sSBoxOutput0[6], &sSBox1Input0[4]);

                                    S0(&sSBoxOutput1[4], &sSBox0Input1[4]);

                                    S1(&sSBoxOutput1[6], &sSBox1Input1[4]);

                                    ActualDifference = BinaryToByte(sSBoxOutput0) ^ BinaryToByte(sSBoxOutput1);

 

                                    // Then we take the left half of the two ciphertexts in the pair and

                                    // use them to trace back to an EXPECTED XOR difference

                                    LeftHalf0 = (ThePairs[TryPair].Ciphertext0 & 0xf0) >> 4;

                                    LeftHalf1 = (ThePairs[TryPair].Ciphertext1 & 0xf0) >> 4;

                                    ExpectedDifference = LeftHalf0 ^ LeftHalf1;

                                    ExpectedDifference ^= 0x08;

                                    ByteToBinary(sExpectedDifference, ExpectedDifference);

                                    memset(sP4InverseOutput, '0', 4);

                                    P4Inverse(&sP4InverseOutput[4], &sExpectedDifference[4]);

                                    ExpectedDifference = BinaryToByte(sP4InverseOutput);

 

                                    // If it just so happens that what we got wasn't what we were expecting

                                    // then this probably isn't the right subkey

                                    if (ActualDifference != ExpectedDifference)

                                                Bad++;

                                   

                        }

                        if (Bad == 0)

                                    printf("K3 might be 0x%02x?\n", Subkey);

            }

 

            return 0;

}

 

 

[crandaj@node52-60 cryptanalysis]$ ./dpc > dpcout.txt

You must enter a hexadecimal key, such as 22B8FA

The last two hex digits (FA) are the 8-bit subkey for the last round.

Please enter the key you'd like to use:

ba48c2

[crandaj@node52-60 cryptanalysis]$ cat dpcout.txt

77      232

205     127

72      208

200     204

100     107

228     29

82      227

210     0

39      173

167     8

63      26

191     77

40      111

168     230

53      252

181     137

96      149

224     146

87      89

215     176

34      235

162     157

103     181

231     86

58      200

186     155

56      154

184     205

1       102

129     239

127     182

255     131

90      3

218     54

61      57

189     101

50      206

178     123

20      214

148     53

86      47

214     25

118     254

246     98

81      13

209     168

114     160

242     167

37      74

165     108

 

[crandaj@node52-60 cryptanalysis]$ cat dpcout.txt | ./diff

00100010 -> 11101011

10100010 -> 10011101

01100111 -> 10110101

11100111 -> 01010110

00111010 -> 11001000

10111010 -> 10011011

K3 might be 0xc2?

[crandaj@node52-60 cryptanalysis]$ exit

exit

 

Script done on Thu May 30 21:57:18 2002



This was created as part of the Cryptography Module of NSF Award No. 0113627: "Increasing Security Expertise in Aviation-oriented Computing Education: A Modular Approach", at Embry-Riddle Aeronautical University in Prescott, Arizona.

Project InfoModulesLinksPapersTeamNSF

Last update: August 1, 2002