Program ProgrammingTask3; {$APPTYPE CONSOLE} {$R *.res} {$ZEROBASEDSTRINGS ON} Uses System.SysUtils; Type TByteArray = Array[0..255] Of Byte; Var S, KeyStream : TByteArray; Key, PlainText, CipherText, RecoveredPlainText : String; i : Integer; Ch : Char; ChByteValue : Byte; {Expand key to 256 bytes} Procedure KeyScheduling(Var S : TByteArray; Key : String); Var i, j, Temp : Byte; KeyLength : Integer; Begin For i := 0 To 255 Do S[i] := i; j := 0; KeyLength := Length(Key); For i := 0 To 255 Do Begin j := (j + S[i] + Ord(Key[i Mod KeyLength])) Mod 256; Temp := S[i]; S[i] := S[j]; S[j] := Temp; End; End; {Generate pseudorandom key stream that will be used as a one-time pad to encrypt given plaintext} Procedure PseudoRandomNoGenerator(Var PseudoRandomNoArray : TByteArray; Var S : TByteArray; PlainTextLength : Integer); Var i, j, k, Temp : Byte; Begin i := 0; j := 0; For k := 0 To PlainTextLength - 1 Do Begin i := (i + 1) Mod 256; j := (j + S[i]) Mod 256; Temp := S[i]; S[i] := S[j]; S[j] := Temp; PseudoRandomNoArray[k] := S[(S[i] + S[j]) Mod 256]; End; End; Begin Try Repeat Writeln('RC4 stream cipher exercise'); Writeln('--------------------------'); Write('Enter key value: '); Readln(Key); Write('Enter plaintext: '); Readln(PlainText); Writeln; Writeln('Values that will be used:'); Writeln; Write(' Key: '); {Use longhand/indirect method for showing key in order to illustrate that zero-based indexing of strings is used} For i := 0 To Length(Key) - 1 Do Write(Key[i]); Writeln; {Use direct method for showing plaintext because it is more convenient} Writeln(' Plaintext: ', PlainText); Writeln; KeyScheduling(S, Key); {Expand the Key to produce 256 seeds for the pseudorandom no generator} PseudoRandomNoGenerator(KeyStream, S, Length(PlainText)); {Generate the pseudorandom key stream, store in KeyStream} Ciphertext := ''; For i := 0 To Length(PlainText) - 1 Do Begin Ch := PlainText[i]; ChByteValue := Ord(Ch); CipherText := CipherText + Chr(ChByteValue XOR KeyStream[i]); End; Writeln('Encryption - Key stream and ordinal values of plaintext exclusive-ored'); Writeln('------------------------------------------------------------------------'); Write('Key stream: '); For i := 0 To Length(PlainText) - 1 Do Write(IntToHex(Ord(KeyStream[i]), 2)); Writeln; Write('Plaintext: '); For i := 0 To Length(PlainText) - 1 Do Write(IntToHex(Ord(Plaintext[i]), 2)); Writeln; Write('Ciphertext: '); For i := 0 To Length(CipherText) - 1 Do Write(IntToHex(Ord(CipherText[i]), 2)); Writeln; Writeln('------------------------------------------------------------------------'); Writeln('Press return to continue'); Readln; Writeln('Ciphertext in character form: ', Ciphertext); Writeln; Writeln('Decryption - Key stream and ordinal values of ciphertext exclusive-ored'); Writeln('------------------------------------------------------------------------'); Write('Enter key value to be used to decrypt ciphertext: '); Readln(Key); KeyScheduling(S, Key); PseudoRandomNoGenerator(KeyStream, S, Length(CipherText)); RecoveredPlainText := ''; For i := 0 To Length(CipherText) - 1 Do Begin Ch := CipherText[i]; ChByteValue := Ord(Ch); RecoveredPlainText := RecoveredPlainText + Chr(ChByteValue XOR KeyStream[i]); End; Write('Keystream: '); For i := 0 To Length(PlainText) - 1 Do Write(IntToHex(Ord(KeyStream[i]), 2)); Writeln; Write('Ciphertext: '); For i := 0 To Length(CipherText) - 1 Do Write(IntToHex(Ord(CipherText[i]), 2)); Writeln; Write('Plaintext: '); For i := 0 To Length(PlainText) - 1 Do Write(IntToHex(Ord(Plaintext[i]), 2)); Writeln; Writeln('------------------------------------------------------------------------'); Writeln('Press return to continue'); Writeln; Write('Recovered plain text: '); Writeln(RecoveredPlainText); Writeln('Press return to continue'); Readln; Write('Another go? '); Readln(Ch); Until Ch In ['N', 'n']; Except On E: Exception Do Writeln(E.ClassName, ': ', E.Message); End; End.