(*--------------------------------------------------------------------------------------------------- Implementation of the Cryptographically Secure Pseudo-Random Number Generator VMPC-R in ASSEMBLER embodied in the Pascal/Delphi environment, using the "register" calling convention Author of the algorithms: Bartosz Zoltak Author of the implementation: Bartosz Zoltak www.vmpcfunction.com ----------------------------------------------------------------------------------------------------- ----------------------- Usage of the algorithm: ----------------------------------------------------- ----------------------------------------------------------------------------------------------------- var Key, Vector : array[0..255] of byte; Message : array[0..999] of byte; Pseudo-Random Number Generation / Encryption: VMPCRInitKey32_ASM(Key, Vector); VMPCREncrypt_ASM(Message, 1000); Decryption: VMPCRInitKey32_ASM(Key, Vector); VMPCREncrypt_ASM(Message, 1000); (the VMPRCEncrypt_ASM procedure is used for pseudo-random number generation, encryption and decryption). The (**) sign marks the code which decides whether the algorithm works in the pseudo-random number generation or encryption/decryption mode. ---------------------------------------------------------------------------------------------------- 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 "longword" type denotes a 32-bit unsigned integer //----------- VMPC-R variables: ----------- var P, S : array[0..255] of byte; a, b, c, d, e, f, n : byte; //----------------- Test data: ----------------- TestOutPSInd : array[0..7] of byte = (0, 1, 2, 3, 252, 253, 254, 255); TestOutInd : array[0..15] of longword = (0,1,2,3,254,255,256,257,1000, 1001, 10000, 10001, 100000, 100001, 1000000, 1000001); TestKey : array[0..8] of byte = (11, 22, 33, 144, 155, 166, 233, 244, 255); TestVector : array[0..7] of byte = (255, 250, 200, 150, 100, 50, 5, 1); TestKey32 : array[0..31] of byte = (104, 9, 46, 231, 132, 149, 234, 147, 224, 97, 230, 127, 124, 109, 34, 171, 88, 185, 158, 23, 116, 69, 90, 195, 208, 17, 86, 175, 108, 29, 146, 219); //RND=123; repeat 32 times:{RND:=RND*134775813+1; output=(RND and 255)} TestVector32 : array[0..31] of byte = (149, 234, 147, 224, 97, 230, 127, 124, 109, 34, 171, 88, 185, 158, 23, 116, 69, 90, 195, 208, 17, 86, 175, 108, 29, 146, 219, 72, 105, 14, 71, 100); //RND=132; repeat 32 times:{RND:=RND*134775813+1; output=(RND and 255)} TestKey256 : array[0..255] of byte = (147, 224, 97, 230, 127, 124, 109, 34, 171, 88, 185, 158, 23, 116, 69, 90, 195, 208, 17, 86, 175, 108, 29, 146, 219, 72, 105, 14, 71, 100, 245, 202, 243, 192, 193, 198, 223, 92, 205, 2, 11, 56, 25, 126, 119, 84, 165, 58, 35, 176, 113, 54, 15, 76, 125, 114, 59, 40, 201, 238, 167, 68, 85, 170, 83, 160, 33, 166, 63, 60, 45, 226, 107, 24, 121, 94, 215, 52, 5, 26, 131, 144, 209, 22, 111, 44, 221, 82, 155, 8, 41, 206, 7, 36, 181, 138, 179, 128, 129, 134, 159, 28, 141, 194, 203, 248, 217, 62, 55, 20, 101, 250, 227, 112, 49, 246, 207, 12, 61, 50, 251, 232, 137, 174, 103, 4, 21, 106, 19, 96, 225, 102, 255, 252, 237, 162, 43, 216, 57, 30, 151, 244, 197, 218, 67, 80, 145, 214, 47, 236, 157, 18, 91, 200, 233, 142, 199, 228, 117, 74, 115, 64, 65, 70, 95, 220, 77, 130, 139, 184, 153, 254, 247, 212, 37, 186, 163, 48, 241, 182, 143, 204, 253, 242, 187, 168, 73, 110, 39, 196, 213, 42, 211, 32, 161, 38, 191, 188, 173, 98, 235, 152, 249, 222, 87, 180, 133, 154, 3, 16, 81, 150, 239, 172, 93, 210, 27, 136, 169, 78, 135, 164, 53, 10, 51, 0, 1, 6, 31, 156, 13, 66, 75, 120, 89, 190, 183, 148, 229, 122, 99, 240, 177, 118, 79, 140, 189, 178, 123, 104, 9, 46, 231, 132, 149, 234); //RND=234; repeat 256 times:{RND:=RND*134775813+1; output=(RND and 255)} TestOutP : array[0..7] of byte = (97,218,106,125,139,86,36,126); TestOutS : array[0..7] of byte = (152,143,19,154,92,25,24,157); TestOut : array[0..15] of byte = (49, 161, 79, 69, 85, 237, 96, 243, 181, 184, 136, 99, 67, 27, 253, 231); //VMPCRInitKey_ASM(TestKey, TestVector, 9, 8); //P[TestOutPSInd[x]]=TestOutP[x]; x=0,1,...,7 //S[TestOutPSInd[x]]=TestOutS[x]; x=0,1,...,7 //VMPCREncrypt_ASM(Table, 1000002); //Table[TestOutInd[x]]=TestOut[x]; x=0,1,...,15 TestOutP32 : array[0..7] of byte = (76, 44, 167, 7, 250, 147, 240, 51); TestOutS32 : array[0..7] of byte = (239, 59, 110, 207, 98, 23, 178, 227); TestOut32 : array[0..15] of byte = (219, 178, 157, 119, 2, 155, 62, 20, 3, 239, 236, 81, 195, 11, 186, 127); //VMPCRInitKey_ASM(TestKey32, TestVector32, 32, 32); //P[TestOutPSInd[x]]=TestOutP32[x]; x=0,1,...,7 //S[TestOutPSInd[x]]=TestOutS32[x]; x=0,1,...,7 //VMPCREncrypt_ASM(Table, 1000002); //Table[TestOutInd[x]]=TestOut32[x]; x=0,1,...,15 TestOutP256 : array[0..7] of byte = (10, 34, 13, 239, 209, 9, 154, 220); TestOutS256 : array[0..7] of byte = (253, 106, 200, 178, 75, 251, 129, 209); TestOut256 : array[0..15] of byte = (201, 85, 155, 17, 187, 48, 55, 198, 110, 179, 189, 210, 4, 15, 253, 83); //VMPCRInitKey_ASM(TestKey256, TestVector, 256, 8); //P[TestOutPSInd[x]]=TestOutP256[x]; x=0,1,...,7 //S[TestOutPSInd[x]]=TestOutS256[x]; x=0,1,...,7 //VMPCREncrypt_ASM(Table, 1000002); //Table[TestOutInd[x]]=TestOut256[x]; x=0,1,...,15 //------------------------------------------------------------------------------------------------------------- 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); InitKeyRounds : array[0..255] of byte = //InitKeyRounds[x]=Ceiling((x+1)*(x+1) / (6*256)) (1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 24, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 31, 31, 31, 31, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 36, 37, 37, 37, 38, 38, 38, 39, 39, 39, 40, 40, 40, 41, 41, 41, 42, 42, 42, 43, 43, 43); //------------------------------------------------------------------------------------ procedure VMPCRInitKeyRound32_ASM(var Data); //[Data]=eax {in the code below: edi = Data esi = n ebp = iteration counter bl = i Instructions determining the 32-byte size of the processed key are written in CAPITAL LETTERS (AND EBX, 31)} asm cmp edx, 0 jz @Finish; push edi push esi push ebp push ebx mov edi, eax mov eax, 256 movzx ebx, byte ptr [InitKeyRounds + 31] mul bx //caution - "mul" modifies the edx register mov ebp, eax movzx esi, n xor ebx, ebx @Loop: //============= variables: ======= //----- a ----- movzx ecx, byte ptr [edi + ebx] add cl, a add cl, f mov cl, byte ptr [P+ecx] add cl, bl mov a, cl //a=P[a+f+Data[i]]+i inc ebx AND EBX, 31 @i1: //----- b ----- movzx eax, byte ptr [edi + ebx] add al, b add al, a mov al, byte ptr [S+eax] add al, bl mov b, al //b=S[b+a+Data[i]]+i inc ebx AND EBX, 31 @i2: //----- c ----- movzx ecx, byte ptr [edi + ebx] add cl, c add cl, b mov cl, byte ptr [P+ecx] add cl, bl mov c, cl //c=P[c+b+Data[i]]+i inc ebx AND EBX, 31 @i3: //----- d ----- movzx eax, byte ptr [edi + ebx] add al, d add al, c mov al, byte ptr [S+eax] add al, bl mov d, al //d=S[d+c+Data[i]]+i inc ebx AND EBX, 31 @i4: //----- e ----- movzx ecx, byte ptr [edi + ebx] add cl, e add cl, d mov cl, byte ptr [P+ecx] add cl, bl mov e, cl //e=P[e+d+Data[i]]+i inc ebx AND EBX, 31 @i5: //----- f ----- movzx eax, byte ptr [edi + ebx] add al, f add al, e mov al, byte ptr [S+eax] add al, bl mov f, al //f=S[f+e+Data[i]]+i inc ebx AND EBX, 31 @i6: //============= swap: ========== movzx eax, b mov dl, byte ptr [P+eax] mov dh, byte ptr [P+esi] mov byte ptr [P+eax], dh mov byte ptr [P+esi], dl //P[x <--> b] movzx eax, e mov dh, byte ptr [S+eax] mov dl, byte ptr [S+esi] mov byte ptr [S+eax], dl mov byte ptr [S+esi], dh //S[x <--> e] movzx eax, a movzx ecx, c mov dl, byte ptr [S+eax] mov dh, byte ptr [S+ecx] mov byte ptr [S+eax], dh mov byte ptr [S+ecx], dl //S[a <--> c] movzx eax, d movzx ecx, f mov dh, byte ptr [P+eax] mov dl, byte ptr [P+ecx] mov byte ptr [P+eax], dl mov byte ptr [P+ecx], dh //P[d <--> f] //============================== inc esi and esi, 255 dec ebp jnz @Loop mov eax, esi mov n, al pop ebx pop ebp pop esi pop edi @Finish: end; procedure VMPCRInitKeyFinalize_ASM; {in the code below: edi = iteration counter esi = n. Assigning esi to n at the end of the procedure is redundant as n (after 256 iterations) will return to its initial value} asm push edi push esi push ebx mov edi, 256 movzx esi, n movzx ebx, a movzx edx, f xor eax, eax xor ecx, ecx @Loop: //============= variables: ======= add bl, c add bl, byte ptr [S+esi] mov bl, byte ptr [P+ebx] //a=P[a+c+S[n]]; mov al, b add al, bl mov al, byte ptr [P+eax] //b=P[b+a]; mov b, al add al, c mov al, byte ptr [P+eax] //c=P[c+b]; mov c, al //------------- mov cl, d add cl, dl add cl, byte ptr [P+esi] mov cl, byte ptr [S+ecx] //d=S[d+f+P[n]]; mov d, cl add cl, e mov cl, byte ptr [S+ecx] //e=S[e+d]; mov e, cl add cl, dl mov dl, byte ptr [S+ecx] //f=S[f+e]]; //============= swap: ========== mov al, byte ptr [P+edx] mov cl, byte ptr [P+esi] mov byte ptr [P+edx], cl mov byte ptr [P+esi], al //P[n <--> f] mov al, byte ptr [S+ebx] mov cl, byte ptr [S+esi] mov byte ptr [S+ebx], cl mov byte ptr [S+esi], al //S[n <--> a] //============================== inc esi and esi, 255 dec edi jnz @Loop mov a, bl mov f, dl pop ebx pop esi pop edi @Finish: end; procedure VMPCRInitKey32_ASM(var Key,Vec:array of byte); var x:byte; begin Move(Permut123, P, 256); Move(Permut123, S, 256); a:=0; b:=0; c:=0; d:=0; e:=0; f:=0; n:=0; VMPCRInitKeyRound32_ASM(Key); VMPCRInitKeyRound32_ASM(Vec); VMPCRInitKeyRound32_ASM(Key); n:=S[(S[S[(c+d) and 255]]+1) and 255]; VMPCRInitKeyFinalize_ASM; end; procedure VMPCREncrypt_ASM(var Data; Len :longword); //[Data]=eax Len=edx {in the code below: edi = Data ebp = Len + Data (max address - for the loop) esi = n} asm cmp edx, 0 jz @Finish; push edi push esi push ebp push ebx mov edi, eax mov ebp, edx add ebp, edi movzx esi, n movzx ebx, a movzx edx, f xor eax, eax xor ecx, ecx @Loop: //============= variables: ======= add bl, c add bl, byte ptr [S+esi] mov bl, byte ptr [P+ebx] //a=P[a+c+S[n]] mov al, b add al, bl mov al, byte ptr [P+eax] //b=P[b+a] mov b, al add al, c mov al, byte ptr [P+eax] //c=P[c+b] mov c, al //------------- mov cl, d add cl, dl add cl, byte ptr [P+esi] mov cl, byte ptr [S+ecx] //d=S[d+f+P[n]] mov d, cl add cl, e mov cl, byte ptr [S+ecx] //e=S[e+d] mov e, cl add cl, dl mov dl, byte ptr [S+ecx] //f=S[f+e] //============= VMPC output: === add al, d mov al, byte ptr [S +eax] mov al, byte ptr [S +eax] inc al mov al, byte ptr [S +eax] mov byte ptr [edi], al //Data[x]=S[S[S[c+d]]+1] - pseudo-random number generation (**) //xor byte ptr [edi], al //Data[x]=Data[x] xor S[S[S[c+d]]+1] - encryption / decryption (**) //============= swap: ========== mov al, byte ptr [P+edx] mov cl, byte ptr [P+esi] mov byte ptr [P+edx], cl mov byte ptr [P+esi], al //P[n <--> f] mov al, byte ptr [S+ebx] mov cl, byte ptr [S+esi] mov byte ptr [S+ebx], cl mov byte ptr [S+esi], al //S[n <--> a] //============================== inc edi inc esi and esi, 255 cmp edi, ebp jb @Loop mov a, bl mov f, dl mov eax, esi mov n, al pop ebx pop ebp pop esi pop edi @Finish: end; procedure VMPCREraseKey_ASM; asm push edi xor eax, eax mov edi, offset P mov ecx, 64 cld rep stosd mov edi, offset S mov ecx, 64 rep stosd mov a, 0 mov b, 0 mov c, 0 mov d, 0 mov e, 0 mov f, 0 mov n, 0 pop edi end; //--------------------------------------------------------------------------------------------------- //----------------------------------------------- END ----------------------------------------------- //---------------------------------------------------------------------------------------------------