Page 1 of 1

GOST (28147-89) cipher include (x86) (22 May 2014)

Posted: Thu May 22, 2014 4:23 am
by Inf0Byt3
Here's a PureBasic implementation of the GOST 28147-89 block cipher that originated in the USSR. It is kind of old and rather simple when compared to modern ciphers, but while it has been attacked for such a long time, it has't been broken. It's even been submitted to ISO standardization in 2010. This implementation is not very fast, but it gets the job done.

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
GOST_VectorTest.pb

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
GOST_StressTest.pb

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()
Tested only on Win32, but it should also work on the other os.

Re: GOST (28147-89) cipher include (x86) (22 May 2013)

Posted: Thu May 22, 2014 4:33 pm
by marroh
Thanks, good work.