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

Share your advanced PureBasic knowledge/code with the community.
Inf0Byt3
PureBasic Fanatic
PureBasic Fanatic
Posts: 2236
Joined: Fri Dec 09, 2005 12:15 pm
Location: Elbonia

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

Post 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.
Last edited by Inf0Byt3 on Sun Jun 08, 2014 12:28 pm, edited 1 time in total.
None are more hopelessly enslaved than those who falsely believe they are free. (Goethe)
marroh
User
User
Posts: 72
Joined: Wed Aug 06, 2008 8:21 am

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

Post by marroh »

Thanks, good work.
PureBASIC v5.41 LTS , Windows v8.1 x64
Forget UNICODE - Keep it BASIC !
Post Reply