(*--------------------------------------------------------------------------------------------------- Implementation of the VMPC Stream Cipher and the VMPC-MAC Authenticated Encryption Scheme in Pascal/Delphi Author of the algorithms: Bartosz Zoltak Author of the implementation: Bartosz Zoltak www.vmpcfunction.com ----------------------------------------------------------------------------------------------------- ----------------------- Usage of the algorithms: ---------------------------------------------------- ----------------------------------------------------------------------------------------------------- var Key, Vector : array[0..63] of byte; Message : array[0..999] of byte; MessageMAC : array[0..19] of byte; Encryption: VMPCInitKey(Key, Vector, 16, 16); VMPCEncrypt(Message, 1000); Decryption: VMPCInitKey(Key, Vector, 16, 16); VMPCEncrypt(Message, 1000); (the VMPCEncrypt procedure is used for both encryption and decryption). Authenticated Encryption (with the MAC tag): VMPCInitKey(Key, Vector, 16, 16); VMPCInitMAC; VMPCEncryptMAC(Message, 1000); VMPCOutputMAC; //The MAC tag is saved in the 20-byte "MAC" table Move(MAC, MessageMAC, 20); //Save the generated MAC tag in the "MessageMAC" table Decryption and verification of the MAC tag: VMPCInitKey(Key, Vector, 16, 16); VMPCInitMAC; VMPCDecryptMAC(Message, 1000); VMPCOutputMAC; //The MAC tag is saved in the 20-byte "MAC" table If the 20-byte tables "MAC" and "MessageMAC" are identical, the message was correctly decrypted - the correct key was used and the message was not corrupted. ---------------------------------------------------------------------------------------------------- The VMPCInitKey / VMPCInitKey16 functions (employing the VMPC-KSA3 key initialization algorithm) provide higher security level but about 1/3 lower efficiency. than the basic VMPCInitKeyBASIC / VMPCInitKey16BASIC functions. If only the system efficiency allows, the author recommends to use the VMPCInitKey / VMPCInitKey16 functions. At the same time the VMPCInitKeyBASIC / VMPCInitKey16BASIC functions also remain secure. ---------------------------------------------------------------------------------------------------- CAUTION! A DIFFERENT value of the initialization vector ("Vector") should be used for each encryption with the same key ("Key"). Encrypting two messages with THE SAME key and THE SAME initialization vector drastically reduces security level! The key is a secret value. The initialization vector is not secret - it can be passed in plain form along with the encrypted message. ------------------------------------------------------------------------------------------------------------*) //--------------------------------------------------------------------------------------------------- //----------------------------------------- IMPLEMENTATION: ----------------------------------------- //--------------------------------------------------------------------------------------------------- //--- The "byte" type denotes an 8-bit unsigned integer //--- The "word" type denotes a 16-bit unsigned integer //--- The "longword" type denotes a 32-bit unsigned integer //----------- VMPC Stream Cipher variables: ----------- var P : array[0..255] of byte; s, n : byte; //----------- VMPC-MAC Authenticated Encryption Scheme variables: ----------- MAC : array[0..31] of byte; m1, m2, m3, m4, mn : byte; //----------------- Test data: ----------------- TestKey : array[0..15] of byte = ($96, $61, $41, $0A, $B7, $97, $D8, $A9, $EB, $76, $7C, $21, $17, $2D, $F6, $C7); TestVector : array[0..15] of byte = ($4B, $5C, $2F, $00, $3E, $67, $F3, $95, $57, $A8, $D2, $6F, $3D, $A2, $B1, $55); TestOutPInd : array[0..7] of byte = (0, 1, 2, 3, 252, 253, 254, 255); TestOutInd : array[0..15] of longword = (0,1,2,3,252,253,254,255,1020,1021,1022,1023,102396,102397,102398,102399); TestOutPBASIC : array[0..7] of byte = ($3F, $A5, $22, $67, $75, $B3, $D2, $C3); TestOutBASIC : array[0..15] of byte = ($A8, $24, $79, $F5, $B8, $FC, $66, $A4, $E0, $56, $40, $A5, $81, $CA, $49, $9A); //VMPCInitKeyBASIC(TestKey, TestVector, 16, 16); OR VMPCInitKey16BASIC(TestKey, TestVector); //P[TestOutPInd[x]]=TestOutPBASIC[x]; x=0,1,...,7 //Table[x]=0; x=0,1,...,102399 //VMPCEncrypt(Table, 102400); OR VMPCEncryptMAC(Table, 102400); //Table[TestOutInd[x]]=TestOutBASIC[x]; x=0,1,...,15 TestOutP : array[0..7] of byte = ($1F, $00, $E2, $03, $5C, $EE, $C2, $2B); TestOut : array[0..15] of byte = ($B6, $EB, $AE, $FE, $48, $17, $24, $73, $1D, $AE, $C3, $5A, $1D, $A7, $E1, $DC); //VMPCInitKey(TestKey, TestVector, 16, 16); OR VMPCInitKey16(TestKey, TestVector); //P[TestOutPInd[x]]=TestOutP[x]; x=0,1,...,7 //Table[x]=0; x=0,1,...,102399 //VMPCEncrypt(Table, 102400); OR VMPCEncryptMAC(Table, 102400); //Table[TestOutInd[x]]=TestOut[x]; x=0,1,...,15 TestOutMACBASIC : array[0..19] of byte = ($9B, $DA, $16, $E2, $AD, $0E, $28, $47, $74, $A3, $AC, $BC, $88, $35, $A8, $32, $6C, $11, $FA, $AD); //Table[x]=x; x=0,1,2,...,254,255 //VMPCInitKeyBASIC(TestKey, TestVector, 16, 16); OR VMPCInitKey16BASIC(TestKey, TestVector); //VMPCInitMAC; //VMPCEncryptMAC(Table, 256); //VMPCOutputMAC; //MAC[x]=TestOutMACBASIC[x]; x=0,1,...,19 TestOutMAC : array[0..19] of byte = ($A2, $B6, $0D, $B7, $B3, $90, $1D, $5C, $99, $61, $7C, $E2, $A3, $95, $02, $81, $75, $3A, $0C, $98); //Table[x]=x and 255; x=0,1,2,...,999998,999999; (Table[0]=0; Table[1]=1; ...; Table[999998]=62; Table[999999]=63) //VMPCInitKey(TestKey, TestVector, 16, 16); OR VMPCInitKey16(TestKey, TestVector); //VMPCInitMAC; //VMPCEncryptMAC(Table, 1000000); //VMPCOutputMAC; //MAC[x]=TestOutMAC[x]; x=0,1,...,19 //----------------------------------------------------------------------------------------------------------- const Permut123 : array[0..255] of byte = //Permut123[x]=x (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255); //----------------------------------------------------------------------------------------------------------- //---------- VMPC Stream Cipher: ---------- procedure VMPCInitKeyRound(var Data:array of byte; Len, Src :byte); {Data: key or initialization vector Len=1,2,3,...,64: key/initialization vector length (in bytes) Src=0: first initialization of the key (the P table and the s variable will be restored to their initial values first) Src=1: re-initialization of the key, e.g. with the initialization vector} var x : word; t,k : byte; begin if Src=0 then begin Move(Permut123, P, 256); s:=0; end; k:=0; n:=0; for x:=0 to 767 do begin s := P[ (s + P[n] + Data[k]) and 255 ]; t := P[n]; P[n] := P[s]; P[s] := t; inc(k); if k=Len then k:=0; n:=n+1; end; //for x end; procedure VMPCInitKeyRound16(var Data:array of byte; Src :byte); //For 16-byte (128-bit) keys and vectors {Data: key or initialization vector Src=0: first initialization of the key (the P table and the s variable will be restored to their initial values first) Src=1: re-initialization of the key, e.g. with the initialization vector} var x : word; t,k : byte; begin if Src=0 then begin Move(Permut123, P, 256); s:=0; end; k:=0; n:=0; for x:=0 to 767 do begin s := P[ (s + P[n] + Data[k]) and 255 ]; t := P[n]; P[n] := P[s]; P[s] := t; k:=(k+1) and 15; n:=n+1; end; //for x end; procedure VMPCInitKey(var Key, Vec:array of byte; KeyLen, VecLen:byte); //KeyLen, VecLen = 1,2,3,...,64 begin VMPCInitKeyRound(Key, KeyLen, 0); VMPCInitKeyRound(Vec, VecLen, 1); VMPCInitKeyRound(Key, KeyLen, 1); end; procedure VMPCInitKey16(var Key, Vec:array of byte); //For 16-byte (128-bit) keys and vectors begin VMPCInitKeyRound16(Key, 0); VMPCInitKeyRound16(Vec, 1); VMPCInitKeyRound16(Key, 1); end; procedure VMPCInitKeyBASIC(var Key, Vec:array of byte; KeyLen, VecLen:byte); //KeyLen, VecLen = 1,2,3,...,64 begin VMPCInitKeyRound(Key, KeyLen, 0); VMPCInitKeyRound(Vec, VecLen, 1); end; procedure VMPCInitKey16BASIC(var Key, Vec:array of byte); //For 16-byte (128-bit) keys and vectors begin VMPCInitKeyRound16(Key, 0); VMPCInitKeyRound16(Vec, 1); end; procedure VMPCEncrypt(var Data:array of byte; Len:longword); var t:byte; x:longword; begin for x:=0 to Len-1 do begin s:=P[ (s+P[n]) and 255 ]; Data[x]:=Data[x] xor P[(P[P[ s ]]+1) and 255];; t := P[n]; P[n] := P[s]; P[s] := t; n:=n+1; end; //for x end; //---------- VMPC-MAC Authenticated Encryption Scheme: ---------- procedure VMPCInitMAC; begin m1:=0; m2:=0; m3:=0; m4:=0; mn:=0; Fillchar(MAC, 32, 0); end; procedure VMPCEncryptMAC(var Data:array of byte; Len:longword); var t:byte; x:longword; begin for x:=0 to Len-1 do begin s:=P[ (s+P[n]) and 255 ]; Data[x]:=Data[x] xor P[(P[P[ s ]]+1) and 255]; m4:=P[ (m4+m3) and 255 ]; m3:=P[ (m3+m2) and 255 ]; m2:=P[ (m2+m1) and 255 ]; m1:=P[ (m1 + s + Data[x]) and 255 ]; MAC[mn] := MAC[mn] xor m1; MAC[mn+1] := MAC[mn+1] xor m2; MAC[mn+2] := MAC[mn+2] xor m3; MAC[mn+3] := MAC[mn+3] xor m4; t := P[n]; P[n] := P[s]; P[s] := t; mn:=(mn+4) and 31; n:=n+1; end; //for x end; procedure VMPCDecryptMAC(var Data:array of byte; Len:longword); var t:byte; x:longword; begin for x:=0 to Len-1 do begin s:=P[ (s+P[n]) and 255 ]; m4:=P[ (m4+m3) and 255 ]; m3:=P[ (m3+m2) and 255 ]; m2:=P[ (m2+m1) and 255 ]; m1:=P[ (m1 + s + Data[x]) and 255 ]; MAC[mn] := MAC[mn] xor m1; MAC[mn+1] := MAC[mn+1] xor m2; MAC[mn+2] := MAC[mn+2] xor m3; MAC[mn+3] := MAC[mn+3] xor m4; Data[x]:=Data[x] xor P[(P[P[ s ]]+1) and 255]; t := P[n]; P[n] := P[s]; P[s] := t; mn:=(mn+4) and 31; n:=n+1; end; //for x end; procedure VMPCOutputMAC; var t:byte; x:longword; begin for x:=1 to 24 do begin s:=P[ (s+P[n]) and 255 ]; m4:=P[ (m4 + m3 + x) and 255 ]; m3:=P[ (m3 + m2 + x) and 255 ]; m2:=P[ (m2 + m1 + x) and 255 ]; m1:=P[ (m1 + s + x) and 255 ]; MAC[mn] := MAC[mn] xor m1; MAC[mn+1] := MAC[mn+1] xor m2; MAC[mn+2] := MAC[mn+2] xor m3; MAC[mn+3] := MAC[mn+3] xor m4; t := P[n]; P[n] := P[s]; P[s] := t; mn:=(mn+4) and 31; n:=n+1; end; //for x VMPCInitKeyRound(MAC, 32, 1); FillChar(MAC, 20, 0); VMPCEncrypt(MAC, 20); end; procedure VMPCEraseKey; begin FillChar(P, sizeof(P), 0); FillChar(MAC, sizeof(MAC), 0); s:=0; n:=0; m1:=0; m2:=0; m3:=0; m4:=0; mn:=0; end; //--------------------------------------------------------------------------------------------------- //----------------------------------------------- END ----------------------------------------------- //---------------------------------------------------------------------------------------------------