Have a look here for more information:
http://en.wikipedia.org/wiki/GOST_%28block_cipher%29
GOST 28147-89.pbi
Code: Select all
;/-----------------------------------------------------------------------------------
;
; Include description : The GOST 28147-89 cipher in ECB and CBC modes
; More info : http://en.wikipedia.org/wiki/GOST_28147-89
; Cryptanalysis : As of December 2012 the best known attack on GOST (2^101) is
; on par with the best known attack (2^100), based on another
; weakness noted by Nicolas Courtois) on widely used AES.
; Impl. author : Alexandru Trutia
; Date : 21 may 2014
; Version : 1.0
; Target Compiler : PureBasic 5.22+
; Target OS : Windows x86 (not tested on any other operating systems)
; License : Free, unrestricted, no warranty whatsoever
; Use at your own risk
; Additional notes : 1. This implementation does not stretch the key! Please supply
; the full 256 bit key (32 bytes) for maximum security.
; 2. gost_encoder / gost_decoder need minimum 8 bytes buffers
; 3. The output buffer will be exactly as big as the input one
; because this code uses Residual block termination padding.
; (http://www.technology-base.org/wiki/index.php?n=Main.IRBT)
;
;/-----------------------------------------------------------------------------------
;
; Original header:
;
; The GOST 28147-89 cipher
;
; This is based on the 25 Movember 1993 draft translation
; by Aleksandr Malchik, with Whitfield Diffie, of the Government
; Standard of the U.S.S.R. GOST 28149-89, "Cryptographic Transformation
; Algorithm", effective 1 July 1990. (Whitfield.Diffie@eng.sun.com)
;
; That is a draft, and may contain errors, which will be faithfully
; reflected here, along with possible exciting new bugs.
;
; Some details have been cleared up by the paper "Soviet Encryption
; Algorithm" by Josef Pieprzyk and Leonid Tombak of the University
; of Wollongong, New South Wales. (josef/leo@cs.adfa.oz.au)
;
; The standard is written by A. Zabotin (project leader), G.P. Glazkov,
; and V.B. Isaeva. It was accepted and introduced into use by the
; action of the State Standards Committee of the USSR on 2 June 89 as
; No. 1409. It was to be reviewed in 1993, but whether anyone wishes
; to take on this obligation from the USSR is questionable.
;
; This code is placed in the public domain.
;
; If you read the standard, it belabors the point of copying corresponding
; bits from point A to point B quite a bit. It helps to understand that
; the standard is uniformly little-endian, although it numbers bits from
; 1 rather than 0, so bit n has value 2^(n-1). The least significant bit
; of the 32-bit words that are manipulated in the algorithm is the first,
; lowest-numbered, in the bit string.
;
;/-----------------------------------------------------------------------------------
EnableExplicit
#GOST_BLOCK_SIZE = 8 ;(in bytes)
Enumeration
#GOST_TST_SBOX ;Test S-box, not recommended for production code. Used by the Central Bank of Russian Federation though.
#GOST_CPR_SBOX ;CryptoPro S-box, specified as part of RFC 4357, section 11.2.
#GOST_DES_SBOX ;DES S-boxes (Applied Cryptography 2nd Ed., p. 644)
EndEnumeration
Enumeration
#GOST_CBC ;Recommended, uses a 64 bit IV (8 bytes)
#GOST_ECB
EndEnumeration
#GOST_USE_SBOX = #GOST_TST_SBOX ;[!] This is used just for the tests vectors. Change to #GOST_CPR_SBOX for normal use.
Structure asciiarray
a.a[0]
EndStructure
Structure longarray
l.l[0]
EndStructure
Structure gost_ctx
k87.a[256]
k65.a[256]
k43.a[256]
k21.a[256]
key.l[8]
EndStructure
;-----------------------------------------------------------------------------------
Procedure shl32(value.l, count.l = 1)
;by mk-soft
;found here: http://forums.purebasic.com/english/viewtopic.php?p=272695&sid=0269f75f2190bc2765a21c29c86e8ae5#p272695
!mov eax, dword [p.v_value]
!mov ecx, dword [p.v_count]
!shl eax, cl
ProcedureReturn
EndProcedure
Procedure shr32(value.l, count.l = 1)
;by mk-soft
;found here: http://forums.purebasic.com/english/viewtopic.php?p=272695&sid=0269f75f2190bc2765a21c29c86e8ae5#p272695
!mov eax, dword [p.v_value]
!mov ecx, dword [p.v_count]
!shr eax, cl
ProcedureReturn
EndProcedure
Procedure bswap32(val.l)
!mov eax,dword[p.v_val]
!bswap eax
ProcedureReturn
EndProcedure
Procedure xorbuffer(*buff1.asciiarray, *buff2.asciiarray, bufflen)
;Note: Result returned in *buff1
;Vars
Protected i
;Begin
While i < bufflen
*buff1\a[i] ! *buff2\a[i]
i + 1
Wend
EndProcedure
;-----------------------------------------------------------------------------------
Procedure gost_f(*ctx.gost_ctx, x.l)
x = *ctx\k87[x >> 24 & $FF] << 24 |
*ctx\k65[x >> 16 & $FF] << 16 |
*ctx\k43[x >> 8 & $FF] << 8 |
*ctx\k21[x & $FF]
ProcedureReturn shl32(x,11) | shr32(x,21)
EndProcedure
;-----------------------------------------------------------------------------------
Procedure gost_encrypt(*ctx.gost_ctx, *in.longarray, *out.longarray) ;8 bytes in, 8 out
Protected n1.l, n2.l
n1 = bswap32(*in\l[0])
n2 = bswap32(*in\l[1])
n2 ! gost_f(*ctx, n1 + *ctx\key[0])
n1 ! gost_f(*ctx, n2 + *ctx\key[1])
n2 ! gost_f(*ctx, n1 + *ctx\key[2])
n1 ! gost_f(*ctx, n2 + *ctx\key[3])
n2 ! gost_f(*ctx, n1 + *ctx\key[4])
n1 ! gost_f(*ctx, n2 + *ctx\key[5])
n2 ! gost_f(*ctx, n1 + *ctx\key[6])
n1 ! gost_f(*ctx, n2 + *ctx\key[7])
n2 ! gost_f(*ctx, n1 + *ctx\key[0])
n1 ! gost_f(*ctx, n2 + *ctx\key[1])
n2 ! gost_f(*ctx, n1 + *ctx\key[2])
n1 ! gost_f(*ctx, n2 + *ctx\key[3])
n2 ! gost_f(*ctx, n1 + *ctx\key[4])
n1 ! gost_f(*ctx, n2 + *ctx\key[5])
n2 ! gost_f(*ctx, n1 + *ctx\key[6])
n1 ! gost_f(*ctx, n2 + *ctx\key[7])
n2 ! gost_f(*ctx, n1 + *ctx\key[0])
n1 ! gost_f(*ctx, n2 + *ctx\key[1])
n2 ! gost_f(*ctx, n1 + *ctx\key[2])
n1 ! gost_f(*ctx, n2 + *ctx\key[3])
n2 ! gost_f(*ctx, n1 + *ctx\key[4])
n1 ! gost_f(*ctx, n2 + *ctx\key[5])
n2 ! gost_f(*ctx, n1 + *ctx\key[6])
n1 ! gost_f(*ctx, n2 + *ctx\key[7])
n2 ! gost_f(*ctx, n1 + *ctx\key[7])
n1 ! gost_f(*ctx, n2 + *ctx\key[6])
n2 ! gost_f(*ctx, n1 + *ctx\key[5])
n1 ! gost_f(*ctx, n2 + *ctx\key[4])
n2 ! gost_f(*ctx, n1 + *ctx\key[3])
n1 ! gost_f(*ctx, n2 + *ctx\key[2])
n2 ! gost_f(*ctx, n1 + *ctx\key[1])
n1 ! gost_f(*ctx, n2 + *ctx\key[0])
*out\l[0] = bswap32(n2)
*out\l[1] = bswap32(n1)
EndProcedure
Procedure gost_decrypt(*ctx.gost_ctx, *in.longarray, *out.longarray) ;8 bytes in, 8 out
Protected n1.l, n2.l
n1 = bswap32(*in\l[0])
n2 = bswap32(*in\l[1])
n2 ! gost_f(*ctx, n1 + *ctx\key[0])
n1 ! gost_f(*ctx, n2 + *ctx\key[1])
n2 ! gost_f(*ctx, n1 + *ctx\key[2])
n1 ! gost_f(*ctx, n2 + *ctx\key[3])
n2 ! gost_f(*ctx, n1 + *ctx\key[4])
n1 ! gost_f(*ctx, n2 + *ctx\key[5])
n2 ! gost_f(*ctx, n1 + *ctx\key[6])
n1 ! gost_f(*ctx, n2 + *ctx\key[7])
n2 ! gost_f(*ctx, n1 + *ctx\key[7])
n1 ! gost_f(*ctx, n2 + *ctx\key[6])
n2 ! gost_f(*ctx, n1 + *ctx\key[5])
n1 ! gost_f(*ctx, n2 + *ctx\key[4])
n2 ! gost_f(*ctx, n1 + *ctx\key[3])
n1 ! gost_f(*ctx, n2 + *ctx\key[2])
n2 ! gost_f(*ctx, n1 + *ctx\key[1])
n1 ! gost_f(*ctx, n2 + *ctx\key[0])
n2 ! gost_f(*ctx, n1 + *ctx\key[7])
n1 ! gost_f(*ctx, n2 + *ctx\key[6])
n2 ! gost_f(*ctx, n1 + *ctx\key[5])
n1 ! gost_f(*ctx, n2 + *ctx\key[4])
n2 ! gost_f(*ctx, n1 + *ctx\key[3])
n1 ! gost_f(*ctx, n2 + *ctx\key[2])
n2 ! gost_f(*ctx, n1 + *ctx\key[1])
n1 ! gost_f(*ctx, n2 + *ctx\key[0])
n2 ! gost_f(*ctx, n1 + *ctx\key[7])
n1 ! gost_f(*ctx, n2 + *ctx\key[6])
n2 ! gost_f(*ctx, n1 + *ctx\key[5])
n1 ! gost_f(*ctx, n2 + *ctx\key[4])
n2 ! gost_f(*ctx, n1 + *ctx\key[3])
n1 ! gost_f(*ctx, n2 + *ctx\key[2])
n2 ! gost_f(*ctx, n1 + *ctx\key[1])
n1 ! gost_f(*ctx, n2 + *ctx\key[0])
*out\l[0] = bswap32(n2)
*out\l[1] = bswap32(n1)
EndProcedure
;-----------------------------------------------------------------------------------
;*ctx(in) = A context buffer allocated with sizeof(gost_ctx) bytes
;*key(in) = The key (password) buffer. Never pass a *key buffer smaller than 32 bytes!
Procedure gost_keysetup(*ctx.gost_ctx, *key.longarray)
Protected i, *k1.asciiarray, *k2.asciiarray, *k3.asciiarray, *k4.asciiarray, *k5.asciiarray, *k6.asciiarray
Protected *k7.asciiarray, *k8.asciiarray
;Note that the S-boxes are reversed (because of the order of bits)
*k1 = ?gost_k8 : *k2 = ?gost_k7 : *k3 = ?gost_k6 : *k4 = ?gost_k5
*k5 = ?gost_k4 : *k6 = ?gost_k3 : *k7 = ?gost_k2 : *k8 = ?gost_k1
While i < 256
*ctx\k87[i] = *k8\a[i >> 4] << 4 | *k7\a[i & 15]
*ctx\k65[i] = *k6\a[i >> 4] << 4 | *k5\a[i & 15]
*ctx\k43[i] = *k4\a[i >> 4] << 4 | *k3\a[i & 15]
*ctx\k21[i] = *k2\a[i >> 4] << 4 | *k1\a[i & 15]
i + 1
Wend
i = 0
While i < 8
*ctx\key[i] = bswap32(*key\l[i])
i + 1
Wend
EndProcedure
;*ctx(in) = A context buffer initialized with gost_keysetup above
;*in(in) = Input buffer, must be bigger than 8 bytes
;*out(out) = Output buffer, 8 bytes minimum. Must be different from *in, same size as *in
;size(in) = Input (and output) buffer size in bytes
;*iv(in) = Initialization vector, 8 bytes of random data. Needed only in CBC mode
;*cb(in) = Optional callback procedure address for monitoring progress. Takes 2 integer parameters.
Procedure gost_encoder(*ctx.gost_ctx, *in, *out, size, *iv, mode = #GOST_CBC, *cb=0)
;Vars
Protected totalrounds, remainingbytes, *temp, encrypt, currentblock, result, *remaining, *remainingout
;Begin
If *in <> 0 And *out <> 0 And size >= 8
If mode = #GOST_CBC
If *iv = 0 ;Check IV
Debug "[!] GOST in CBC mode needs an initialization vector!"
CallDebugger
EndIf
EndIf
;See how many rounds to make and how many bytes remain
totalrounds = size / #GOST_BLOCK_SIZE
remainingbytes = size % #GOST_BLOCK_SIZE
*temp = AllocateMemory(#GOST_BLOCK_SIZE)
If *temp <> 0
;Encrypt the full rounds
While encrypt < totalrounds
;Copy the block in the temp buffer
CopyMemory(*in + currentblock, *temp, 8)
If mode = #GOST_CBC
If encrypt = 0 ;For the first block, XOR with the IV
xorbuffer(*temp, *iv, 8)
Else ;For the subsequent blocks, XOR with the previous block
xorbuffer(*temp, *out + currentblock - 8, 8)
EndIf
EndIf
;Encrypt the block
gost_encrypt(*ctx, *temp, *out + currentblock)
;Call the callback
If *cb <> 0 And (encrypt % 64) = 1 ;called every 64 encrypted blocks
CallFunctionFast(*cb, encrypt, totalrounds)
EndIf
;Increment vars
currentblock + 8
encrypt + 1
Wend
;The remaining bytes will be padded in RBT-mode then encrypted
If remainingbytes > 0
;Allocate memory for the operations
*remaining = AllocateMemory(remainingbytes)
*remainingout = AllocateMemory(#GOST_BLOCK_SIZE)
If *remaining <> 0 And *remainingout <> 0
;Copy the remaining bytes in a temporary buffer
CopyMemory(*in + size - remainingbytes, *remaining, remainingbytes)
;Re-encrypt the last block
gost_encrypt(*ctx, *out + currentblock - 8, *remainingout)
;Use the leftmost bytes of this re-encrypted block as a key for XOR-ing the remaining data
xorbuffer(*remaining, *remainingout, remainingbytes)
;Finally, copy the XORed remaining data to the output buffer
CopyMemory(*remaining, *out + currentblock, remainingbytes)
;Clean up
FreeMemory(*remaining)
FreeMemory(*remainingout)
result = 1
EndIf
Else
result = 1
EndIf
;Call the callback
If *cb <> 0
CallFunctionFast(*cb, totalrounds, totalrounds)
EndIf
;Clean up
FreeMemory(*temp)
EndIf
EndIf
;Return
ProcedureReturn result
EndProcedure
;*ctx(in) = A context buffer initialized with gost_keysetup above
;*in(in) = Input buffer, must be bigger than 8 bytes
;*out(out) = Output buffer, 8 bytes minimum. Must be different from *in, same size as *in
;size(in) = Input (and output) buffer size in bytes
;*iv(in) = Initialization vector that was used for encryption. Needed only in CBC mode
;*cb(in) = Optional callback procedure address for monitoring progress. Takes 2 integer parameters.
Procedure gost_decoder(*ctx.gost_ctx, *in, *out, size, *iv, mode = #GOST_CBC, *cb=0)
;Vars
Protected totalrounds, remainingbytes, *temp, decrypt, currentblock, result, *remaining, *remainingout
;Begin
If *in <> 0 And *out <> 0 And size >= 8
If mode = #GOST_CBC
If *iv = 0 ;Check IV
Debug "[!] GOST in CBC mode needs an initialization vector!"
CallDebugger
EndIf
EndIf
;See how many rounds to make and how many bytes remain
totalrounds = size / #GOST_BLOCK_SIZE
remainingbytes = size % #GOST_BLOCK_SIZE
*temp = AllocateMemory(#GOST_BLOCK_SIZE)
If *temp <> 0
;Decrypt the full rounds
While decrypt < totalrounds
;Copy the block in the temp buffer
CopyMemory(*in + currentblock, *temp, 8)
;Decrypt the block
gost_decrypt(*ctx, *temp, *out + currentblock)
If mode = #GOST_CBC
If decrypt = 0 ;For the first block, XOR with the IV
xorbuffer(*out + currentblock, *iv, 8)
Else ;For the subsequent blocks, XOR with the previous input block
xorbuffer(*out + currentblock, *in + currentblock - 8, 8)
EndIf
EndIf
;Call the callback
If *cb <> 0 And (decrypt % 64) = 1 ;called every 64 encrypted blocks
CallFunctionFast(*cb, decrypt, totalrounds)
EndIf
;Increment vars
currentblock + 8
decrypt + 1
Wend
;For RBT mode, we need to decrypt the remaining bytes
If remainingbytes > 0
;Allocate memory for the operations
*remaining = AllocateMemory(remainingbytes)
If *remaining <> 0
;Copy the remaining bytes in a temporary buffer
CopyMemory(*in + size - remainingbytes, *remaining, remainingbytes)
;Re-encrypt the last block
gost_encrypt(*ctx, *in + currentblock - 8, *temp)
;Use the leftmost bytes of this re-encrypted block as a key for XOR-ing the remaining data
xorbuffer(*remaining, *temp, remainingbytes)
;Copy the XORed remaining data to the output buffer
CopyMemory(*remaining, *out + currentblock, remainingbytes)
;Clean up
FreeMemory(*remaining)
result = 1
EndIf
Else
result = 1
EndIf
;Call the callback
If *cb <> 0
CallFunctionFast(*cb, totalrounds, totalrounds)
EndIf
;Clean up
FreeMemory(*temp)
EndIf
EndIf
;Return
ProcedureReturn result
EndProcedure
DisableExplicit
;-----------------------------------------------------------------------------------
DataSection
CompilerSelect #GOST_USE_SBOX
CompilerCase #GOST_CPR_SBOX
;The following are the CryptoPro S-boxes and they come from a production
;ready parameter set developed by the CryptoPro company. They are also
;specified as part of (http://tools.ietf.org/html/rfc4357), section 11.2.
gost_k1:
Data.a $A,$4,$5,$6,$8,$1,$3,$7,$D,$C,$E,$0,$9,$2,$B,$F
gost_k2:
Data.a $5,$F,$4,$0,$2,$D,$B,$9,$1,$7,$6,$3,$C,$E,$A,$8
gost_k3:
Data.a $7,$F,$C,$E,$9,$4,$1,$0,$3,$B,$5,$2,$6,$A,$8,$D
gost_k4:
Data.a $4,$A,$7,$C,$0,$F,$2,$8,$E,$1,$6,$5,$D,$B,$9,$3
gost_k5:
Data.a $7,$6,$4,$B,$9,$C,$2,$A,$1,$8,$0,$E,$F,$D,$3,$5
gost_k6:
Data.a $7,$6,$2,$4,$D,$9,$F,$0,$A,$1,$5,$B,$8,$E,$C,$3
gost_k7:
Data.a $D,$E,$4,$1,$7,$0,$5,$A,$3,$C,$8,$F,$6,$2,$9,$B
gost_k8:
Data.a $1,$3,$A,$9,$5,$B,$4,$F,$8,$6,$7,$E,$D,$0,$2,$C
CompilerCase #GOST_DES_SBOX
;The following are the DES(?) S-boxes given in the GOST source code listing
;(Applied Cryptography 2nd Ed., p. 644)
gost_k1:
Data.a $E,$4,$D,$1,$2,$F,$B,$8,$3,$A,$6,$C,$5,$9,$0,$7
gost_k2:
Data.a $F,$1,$8,$E,$6,$B,$3,$4,$9,$7,$2,$D,$C,$0,$5,$A
gost_k3:
Data.a $A,$0,$9,$E,$6,$3,$F,$5,$1,$D,$C,$7,$B,$4,$2,$8
gost_k4:
Data.a $7,$D,$E,$3,$0,$6,$9,$A,$1,$2,$8,$5,$B,$C,$4,$F
gost_k5:
Data.a $2,$C,$4,$1,$7,$A,$B,$6,$8,$5,$3,$F,$D,$0,$E,$9
gost_k6:
Data.a $C,$1,$A,$F,$9,$2,$6,$8,$0,$D,$3,$4,$E,$7,$5,$B
gost_k7:
Data.a $4,$B,$2,$E,$F,$0,$8,$D,$3,$C,$9,$7,$5,$A,$6,$1
gost_k8:
Data.a $D,$2,$8,$4,$6,$F,$B,$1,$A,$9,$3,$E,$5,$0,$C,$7
CompilerCase #GOST_TST_SBOX
;These are test S-boxes (http://tools.ietf.org/html/rfc5831). Please
;note that (http://tools.ietf.org/html/rfc4357) says that they should
;not be used for production code
gost_k1:
Data.a $1,$F,$D,$0,$5,$7,$A,$4,$9,$2,3$,$E,$6,$B,$8,$C
gost_k2:
Data.a $D,$B,$4,$1,$3,$F,$5,$9,$0,$A,$E,$7,$6,$8,$2,$C
gost_k3:
Data.a $4,$B,$A,$0,$7,$2,$1,$D,$3,$6,$8,$5,$9,$C,$F,$E
gost_k4:
Data.a $6,$C,$7,$1,$5,$F,$D,$8,$4,$A,$9,$E,$0,$3,$B,$2
gost_k5:
Data.a $7,$D,$A,$1,$0,$8,$9,$F,$E,$4,$6,$C,$B,$2,$5,$3
gost_k6:
Data.a $5,$8,$1,$D,$A,$3,$4,$2,$E,$F,$C,$7,$6,$0,$9,$B
gost_k7:
Data.a $e,$B,$4,$C,$6,$D,$F,$A,$2,$3,$8,$1,$0,$7,$5,$9
gost_k8:
Data.a $4,$A,$9,$2,$D,$8,$0,$E,$6,$B,$1,$C,$7,$F,$5,$3
CompilerEndSelect
EndDataSection
Code: Select all
;Tests from: http://cryptomanager.com/tv.html#ciphs.
EnableExplicit
XIncludeFile "GOST 28147-89.pbi"
Define *ctx, *in, *out, *iv, *dec
;ECB test
*ctx = AllocateMemory(SizeOf(gost_ctx))
*out = AllocateMemory(8)
*dec = AllocateMemory(8)
gost_keysetup(*ctx, ?key_ecb)
gost_encoder(*ctx, ?in_ecb, *out, 8, 0, #GOST_ECB)
gost_decoder(*ctx, *out, *dec, 8, 0, #GOST_ECB)
If CompareMemory(*out, ?expected_ecb, 8) <> 0 And CompareMemory(?in_ecb, *dec, 8) <> 0
Debug "ECB test passsed!"
Else
Debug "ECB test failed!"
EndIf
FreeMemory(*ctx)
FreeMemory(*out)
FreeMemory(*dec)
;CBC test
*ctx = AllocateMemory(SizeOf(gost_ctx))
*out = AllocateMemory(13)
*dec = AllocateMemory(13)
gost_keysetup(*ctx, ?key_cbc)
gost_encoder(*ctx, ?in_cbc, *out, 13, ?iv_cbc, #GOST_CBC)
gost_decoder(*ctx, *out, *dec, 13, ?iv_cbc, #GOST_CBC)
If CompareMemory(*out, ?expected_cbc, 13) <> 0 And CompareMemory(?in_cbc, *dec, 13) <> 0
Debug "CBC test passsed!"
Else
Debug "CBC test failed!"
EndIf
DataSection
;ECB test
in_ecb:
Data.a $44,$33,$22,$11,$88,$77,$66,$55
key_ecb:
Data.a $34,$31,$71,$75,$45,$EC,$0F,$B6,$83,$BB,$07,$A6,$Af,$46,$37,$AA
Data.a $A6,$9D,$F9,$4F,$5B,$3B,$B5,$D1,$1b,$2A,$40,$1B,$1B,$0D,$03,$AA
expected_ecb:
Data.a $14,$1E,$25,$03,$CB,$8A,$D2,$F9
;CBC test
in_cbc:
Data.a $44,$33,$22,$11,$88,$77,$66,$55,$CC,$BB,$AA,$99,$DD
key_cbc:
Data.a $34,$31,$71,$75,$45,$EC,$0F,$B6,$83,$BB,$07,$A6,$AF,$46,$37,$AA
Data.a $A6,$9D,$F9,$4F,$5B,$3B,$B5,$D1,$1B,$2A,$40,$1B,$1B,$0D,$03,$AA
iv_cbc:
Data.a $04,$03,$02,$01,$08,$07,$06,$05
expected_cbc:
Data.a $78,$1C,$03,$93,$AB,$E1,$D5,$32,$7E,$3C,$EE,$1A,$88 ;For RBT padding
;Data.a $78,$1C,$03,$93,$AB,$E1,$D5,$32,$E5,$1E,$A7,$46,$11,$4F,$61,$71 ;For classic PKCS5 padding
EndDataSection
Code: Select all
EnableExplicit
;[!] Run with debugger off!
DisableDebugger
XIncludeFile "GOST 28147-89.pbi"
Procedure.s peekhex(*buff.asciiarray, bufflen)
Protected loop, result.s
While loop < bufflen
Result + RSet(Hex(*buff\a[loop] & $FF ,#PB_Byte), 2, "0")
loop + 1
Wend
ProcedureReturn result
EndProcedure
Define TotalLoops, Bar.s, BarC, TotalData, StartTime, Loops, Progress.f, TotalTime, Speed.f, *Ctx.gost_ctx, KeyLen, *Key
Define OriginalLen, *Original, *Output, *Decrypted, *IV, Success, Clr
TotalLoops = 128
#MinLen = 512 * 1024
#MaxLen = #MinLen * 2
OpenConsole()
EnableGraphicalConsole(1)
Bar = "-\|/"
BarC = 1
TotalData = 1
If OpenCryptRandom() = 0
PrintN("Error opening random generator!"):Beep_(1500,500):Input():End
EndIf
StartTime = ElapsedMilliseconds()
For Loops = 1 To TotalLoops
Progress = Loops / TotalLoops * 100
TotalTime = ElapsedMilliseconds() - StartTime
Speed.f = TotalData / (TotalTime + 1) / 1000
ConsoleLocate(0, 0)
PrintN("-------------------------------")
PrintN("GOST 28147-89 for PureBasic stress test, min buffer len: "+Str(#MinLen)+" ("+Str(#MinLen / 1024)+" KB)")
PrintN("Rounds: "+Str(Loops)+" of "+Str(TotalLoops)+", "+StrF(Progress,2)+"% ready "+Mid(Bar, BarC, 1)) : BarC + 1 : If BarC = 5 : BarC = 1 : EndIf
PrintN("Elapsed: "+Str(TotalTime/1000)+" s, speed: aprox. "+StrF(Speed,2)+" MB/s")
PrintN("Total processed data: "+Str(TotalData)+" bytes ("+Str(TotalData / 1024 / 1024)+") MB")
PrintN("The test is pretty slow, but it does random data encryption and")
PrintN("decryption. Still, the implementation needs some speed improvements :-)")
PrintN("Ctrl + C to end!")
PrintN("-------------------------------")
;Create a context
*Ctx = AllocateMemory(SizeOf(gost_ctx))
;Make a random key
KeyLen = 32
*Key = AllocateMemory(KeyLen)
CryptRandomData(*Key, KeyLen)
;Make a buffer with random data
OriginalLen = Random(#MaxLen, #MinLen)
*Original = AllocateMemory(OriginalLen)
CryptRandomData(*Original, OriginalLen)
;Make an output and decryption buffer
*Output = AllocateMemory(OriginalLen)
*Decrypted = AllocateMemory(OriginalLen)
;Make an IV
*IV = AllocateMemory(8)
CryptRandomData(*IV, 8)
;Do the work
gost_keysetup(*Ctx, *Key)
gost_encoder(*Ctx, *Original, *Output, OriginalLen, *IV, #GOST_CBC)
TotalData + OriginalLen
gost_decoder(*Ctx, *Output, *Decrypted , OriginalLen, *IV, #GOST_CBC)
TotalData + OriginalLen
;Check the work
If CompareMemory(*Original, *Decrypted, OriginalLen) = 1
Success = 1
Else
Success = 0
EndIf
;Write status
ConsoleLocate(0, 9)
For Clr = 0 To 4
PrintN(Space(80))
Next
ConsoleLocate(0, 9)
PrintN("Key: "+PeekHEX(*Key, KeyLen))
PrintN("IV: "+PeekHEX(*IV, 8))
PrintN("Encrypted: "+Str(OriginalLen)+" bytes, output size: "+Str(OriginalLen))
PrintN("Decrypted: "+Str(OriginalLen)+" bytes, output size: "+Str(OriginalLen))
If Success = 1
PrintN("Decryption successful!")
Else
PrintN("Error at decryption!"):Beep_(1500,500):Input():End
EndIf
PrintN("-------------------------------")
ConsoleLocate(0, 16)
;Clean up
FreeMemory(*Ctx)
FreeMemory(*Key)
FreeMemory(*Original)
FreeMemory(*Output)
FreeMemory(*Decrypted)
FreeMemory(*IV)
Next
ConsoleLocate(0, 16)
PrintN("All rounds finished. Press Enter to quit.")
Input()