Serpent cipher include (x86) (24 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

Serpent cipher include (x86) (24 May 2014)

Post by Inf0Byt3 »

Here is another secure cipher for use in your programs along the default encryption that PureBasic offers. Serpent was an AES finalist and while it lost to Rijndael, it actually is considered to be more secure. It is a block cipher with a block size of 128 bit (16 bytes), and keys of 128, 192 and 256 bits. You can find more information about it here: http://en.wikipedia.org/wiki/Serpent_%28cipher%29.

Regarding the implementation, you might want to read the notes about the endianness. Also, you will need the 3 files called
Serpent-128-128.verified.test-vectors
Serpent-192-128.verified.test-vectors
Serpent-256-128.verified.test-vectors
that are available here: http://www.cs.technion.ac.il/~biham/Reports/Serpent/ to test it. You should copy them to the same dir where "Serpent_VectorTest.pb" resides. One more thing: this code is pretty slow, but maybe someone will find a solution to speed it up a bit :).

That's about it, hope it will be useful.

Serpent.pbi

Code: Select all

;/----------------------------------------------------------------------------------
;
; Include description : The Serpent cipher in ECB and CBC modes
; Algo designers      : Ross Anderson, Eli Biham and Lars Knudsen
; More info           : http://en.wikipedia.org/wiki/Serpent_%28cipher%29
; Cryptanalysis       : All known attacks are computationally infeasible. A 2011 attack
;                       breaks 11 round Serpent (all key sizes) with 2^116 known plaintexts,
;                       2^107.5 time and 2^104 memory.
; Impl. author        : Alexandru Trutia
; Date                : 24 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. The key can have a minimum of 128 bits (16 bytes) to a
;                          maximum of 256 bits (32 bytes). 192 bit encryption is also
;                          possible.
;                       2. serpent_encoder / serpent_decoder need minimum 16 bytes buffers
;                       3. This implementation can work in LE or BE modes by setting the
;                          #SERPENT_BO constant as shown below.
;                       4. 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:
;
;/* This is an independent implementation of the encryption algorithm:   */
;/*                                                                      */
;/*         Serpent by Ross Anderson, Eli Biham and Lars Knudsen         */
;/*                                                                      */
;/* which is a candidate algorithm in the Advanced Encryption Standard   */
;/* programme of the US National Institute of Standards and Technology.  */
;/*                                                                      */
;/* Copyright in this implementation is held by Dr B R Gladman but I     */
;/* hereby give permission for its free direct or derivative use subject */
;/* to acknowledgment of its origin and compliance with any conditions   */
;/* that the originators of the algorithm place on its exploitation.     */
;/*                                                                      */
;/* Dr Brian Gladman (gladman@seven77.demon.co.uk) 14th January 1999     */
;
;/----------------------------------------------------------------------------------

EnableExplicit

#SERPENT_BLOCK_SIZE = 16

;/----------------------------------------------------------------------------------

;These values have no effect on the security. Like some other ciphers out there, Serpent
;was designed on big endian machines. This implementation was created for Intel x86
;processors that use the little endian format. The problem is that some test vectors for
;Serpent are in little endian and some in big endian format, so make sure to switch to the
;needed mode with these values. For production code you might want to use
;#SERPENT_ENDIAN_AS_IS since it's a bit faster.

Enumeration            
  #SERPENT_ENDIAN_AS_IS ;This will leave data as-is.
  #SERPENT_ENDIAN_BSWAP ;This will convert the key, input and output data to little endian, adds overhead.
EndEnumeration
Enumeration
  #SERPENT_CBC          ;Recommended, uses a 128 bit IV (16 bytes)
  #SERPENT_ECB
EndEnumeration
#SERPENT_BO = #SERPENT_ENDIAN_AS_IS

;/----------------------------------------------------------------------------------

Structure serpent_ctx
  l_key.l[140]
EndStructure
Structure asciiarray
  a.a[0]
EndStructure
Structure longarray
  l.l[0]
EndStructure

;-----------------------------------------------------------------------------------

Procedure.l rotl32(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]
  !rol eax, cl
  ProcedureReturn
EndProcedure

Procedure.l rotr32(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]
  !ror eax, cl
  ProcedureReturn
EndProcedure

Procedure.l 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.l 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.l 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

;-----------------------------------------------------------------------------------

Macro sb0(a,b,c,d,e,f,g,h)
  t1 = a ! d
  t2 = a & d
  t3 = c ! t1
  t6 = b & t1
  t4 = b ! t3
  t10 = ~t3
  h = t2 ! t4
  t7 = a ! t6
  t14 = ~t7
  t8 = c | t7
  t11 = t3 ! t7
  g = t4 ! t8
  t12 = h & t11
  f = t10 ! t12
  e = t12 ! t14
EndMacro

Macro ib0(a,b,c,d,e,f,g,h)
  t1 = ~a
  t2 = a ! b
  t3 = t1 | t2
  t4 = d ! t3
  t7 = d & t2
  t5 = c ! t4
  t8 = t1 ! t7
  g = t2 ! t5
  t11 = a & t4
  t9 = g & t8
  t14 = t5 ! t8
  f = t4 ! t9
  t12 = t5 | f
  h = t11 ! t12
  e = h ! t14
EndMacro
   
Macro sb1(a,b,c,d,e,f,g,h)
  t1 = ~a
  t2 = b ! t1
  t3 = a | t2
  t4 = d | t2
  t5 = c ! t3
  g = d ! t5
  t7 = b ! t4
  t8 = t2 ! g
  t9 = t5 & t7
  h = t8 ! t9
  t11 = t5 ! t7
  f = h ! t11
  t13 = t8 & t11
  e = t5 ! t13
EndMacro
   
Macro ib1(a,b,c,d,e,f,g,h)
  t1 = a ! d
  t2 = a & b
  t3 = b ! c
  t4 = a ! t3
  t5 = b | d
  t7 = c | t1
  h = t4 ! t5
  t8 = b ! t7
  t11 = ~t2
  t9 = t4 & t8
  f = t1 ! t9
  t13 = t9 ! t11
  t12 = h & f
  g = t12 ! t13
  t15 = a & d
  t16 = c ! t13
  e = t15 ! t16
EndMacro

Macro sb2(a,b,c,d,e,f,g,h)
  t1 = ~a
  t2 = b ! d
  t3 = c & t1
  t13 = d | t1
  e = t2 ! t3
  t5 = c ! t1
  t6 = c ! e
  t7 = b & t6
  t10 = e | t5
  h = t5 ! t7
  t9 = d | t7
  t11 = t9 & t10
  t14 = t2 ! h
  g = a ! t11
  t15 = g ! t13
  f = t14 ! t15
EndMacro

Macro ib2(a,b,c,d,e,f,g,h)
  t1 = b ! d
  t2 = ~t1
  t3 = a ! c
  t4 = c ! t1
  t7 = a | t2
  t5 = b & t4
  t8 = d ! t7
  t11 = ~t4
  e = t3 ! t5
  t9 = t3 | t8
  t14 = d & t11
  h = t1 ! t9
  t12 = e | h
  f = t11 ! t12
  t15 = t3 ! t12
  g = t14 ! t15
EndMacro

Macro sb3(a,b,c,d,e,f,g,h)
  t1 = a ! c
  t2 = d ! t1
  t3 = a & t2
  t4 = d ! t3
  t5 = b & t4
  g = t2 ! t5
  t7 = a | g
  t8 = b | d
  t11 = a | d
  t9 = t4 & t7
  f = t8 ! t9
  t12 = b ! t11
  t13 = g ! t9
  t15 = t3 ! t8
  h = t12 ! t13
  t16 = c & t15
  e = t12 ! t16
EndMacro

Macro ib3(a,b,c,d,e,f,g,h)
  t1 = b ! c
  t2 = b | c
  t3 = a ! c
  t7 = a ! d
  t4 = t2 ! t3
  t5 = d | t4
  t9 = t2 ! t7
  e = t1 ! t5
  t8 = t1 | t5
  t11 = a & t4
  g = t8 ! t9
  t12 = e | t9
  f = t11 ! t12
  t14 = a & g
  t15 = t2 ! t14
  t16 = e & t15
  h = t4 ! t16
EndMacro

Macro sb4(a,b,c,d,e,f,g,h)
  t1 = a ! d
  t2 = d & t1
  t3 = c ! t2
  t4 = b | t3
  h = t1 ! t4
  t6 = ~b
  t7 = t1 | t6
  e = t3 ! t7
  t9 = a & e
  t10 = t1 ! t6
  t11 = t4 & t10
  g = t9 ! t11
  t13 = a ! t3
  t14 = t10 & g
  f = t13 ! t14
EndMacro

Macro ib4(a,b,c,d,e,f,g,h)
  t1 = c ! d
  t2 = c | d
  t3 = b ! t2
  t4 = a & t3
  f = t1 ! t4
  t6 = a ! d
  t7 = b | d
  t8 = t6 & t7
  h = t3 ! t8
  t10 = ~a
  t11 = c ! h
  t12 = t10 | t11
  e = t3 ! t12
  t14 = c | t4
  t15 = t7 ! t14
  t16 = h | t10
  g = t15 ! t16
EndMacro

Macro sb5(a,b,c,d,e,f,g,h)
  t1 = ~a
  t2 = a ! b
  t3 = a ! d
  t4 = c ! t1
  t5 = t2 | t3
  e = t4 ! t5
  t7 = d & e
  t8 = t2 ! e
  t10 = t1 | e
  f = t7 ! t8
  t11 = t2 | t7
  t12 = t3 ! t10
  t14 = b ! t7
  g = t11 ! t12
  t15 = f & t12
  h = t14 ! t15
EndMacro

Macro ib5(a,b,c,d,e,f,g,h)
  t1 = ~c
  t2 = b & t1
  t3 = d ! t2
  t4 = a & t3
  t5 = b ! t1
  h = t4 ! t5
  t7 = b | h
  t8 = a & t7
  f = t3 ! t8
  t10 = a | d
  t11 = t1 ! t7
  e = t10 ! t11
  t13 = a ! c
  t14 = b & t10
  t15 = t4 | t13
  g = t14 ! t15
EndMacro

Macro sb6(a,b,c,d,e,f,g,h)
  t1 = ~a
  t2 = a ! d
  t3 = b ! t2
  t4 = t1 | t2
  t5 = c ! t4
  f = b ! t5
  t13 = ~t5
  t7 = t2 | f
  t8 = d ! t7
  t9 = t5 & t8
  g = t3 ! t9
  t11 = t5 ! t8
  e = g ! t11
  t14 = t3 & t11
  h = t13 ! t14
EndMacro

Macro ib6(a,b,c,d,e,f,g,h)
  t1 = ~a
  t2 = a ! b
  t3 = c ! t2
  t4 = c | t1
  t5 = d ! t4
  t13 = d & t1
  f = t3 ! t5
  t7 = t3 & t5
  t8 = t2 ! t7
  t9 = b | t8
  h = t5 ! t9
  t11 = b | h
  e = t8 ! t11
  t14 = t3 ! t11
  g = t13 ! t14
EndMacro

Macro sb7(a,b,c,d,e,f,g,h)
  t1 = ~c
  t2 = b ! c
  t3 = b | t1
  t4 = d ! t3
  t5 = a & t4
  t7 = a ! d
  h = t2 ! t5
  t8 = b ! t5
  t9 = t2 | t8
  t11 = d & t3
  f = t7 ! t9
  t12 = t5 ! f
  t15 = t1 | t4
  t13 = h & t12
  g = t11 ! t13
  t16 = t12 ! g
  e = t15 ! t16
EndMacro

Macro ib7(a,b,c,d,e,f,g,h)
  t1 = a & b
  t2 = a | b
  t3 = c | t1
  t4 = d & t2
  h = t3 ! t4
  t6 = ~d
  t7 = b ! t4
  t8 = h ! t6
  t11 = c ! t7
  t9 = t7 | t8
  f = a ! t9
  t12 = d | f
  e = t11 ! t12
  t14 = a & h
  t15 = t3 ! f
  t16 = e ! t14
  g = t15 ! t16
EndMacro

Macro k_xor(r,a,b,c,d)
  a ! *ctx\l_key[4 * r +  8]
  b ! *ctx\l_key[4 * r +  9]
  c ! *ctx\l_key[4 * r + 10]
  d ! *ctx\l_key[4 * r + 11]
EndMacro

Macro k_set(r,a,b,c,d)
  a = *ctx\l_key[4 * r +  8]
  b = *ctx\l_key[4 * r +  9]
  c = *ctx\l_key[4 * r + 10]
  d = *ctx\l_key[4 * r + 11]
EndMacro

Macro k_get(r,a,b,c,d)
  *ctx\l_key[4 * r +  8] = a
  *ctx\l_key[4 * r +  9] = b
  *ctx\l_key[4 * r + 10] = c
  *ctx\l_key[4 * r + 11] = d
EndMacro

Macro rot(a,b,c,d)   
  a = rotl32(a, 13)
  c = rotl32(c, 3)
  d ! c ! (a << 3)
  b ! a ! c
  d = rotl32(d, 7)
  b = rotl32(b, 1)
  a ! b ! d
  c ! d ! (b << 7)
  a = rotl32(a, 5)
  c = rotl32(c, 22)
EndMacro

Macro irot(a,b,c,d)
  c = rotr32(c, 22)
  a = rotr32(a, 5)
  c ! d ! shl32(b, 7)
  a ! b ! d
  d = rotr32(d, 7)
  b = rotr32(b, 1)
  d ! c ! shl32(a, 3)
  b ! a ! c
  c = rotr32(c, 3)
  a = rotr32(a, 13)
EndMacro

;-----------------------------------------------------------------------------------

Procedure serpent_encrypt(*ctx.serpent_ctx, *in_blk.longarray, *out_blk.longarray)
  Protected.l a, b, c, d, e, f, g, h, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16
  CompilerIf #SERPENT_BO = #SERPENT_ENDIAN_AS_IS
    a = *in_blk\l[0]
    b = *in_blk\l[1]
    c = *in_blk\l[2]
    d = *in_blk\l[3]
  CompilerElseIf #SERPENT_BO = #SERPENT_ENDIAN_BSWAP
    a = bswap32(*in_blk\l[3])
    b = bswap32(*in_blk\l[2])
    c = bswap32(*in_blk\l[1])
    d = bswap32(*in_blk\l[0])
  CompilerEndIf
  k_xor( 0,a,b,c,d) : sb0(a,b,c,d,e,f,g,h) : rot(e,f,g,h)
  k_xor( 1,e,f,g,h) : sb1(e,f,g,h,a,b,c,d) : rot(a,b,c,d)
  k_xor( 2,a,b,c,d) : sb2(a,b,c,d,e,f,g,h) : rot(e,f,g,h)
  k_xor( 3,e,f,g,h) : sb3(e,f,g,h,a,b,c,d) : rot(a,b,c,d)
  k_xor( 4,a,b,c,d) : sb4(a,b,c,d,e,f,g,h) : rot(e,f,g,h)
  k_xor( 5,e,f,g,h) : sb5(e,f,g,h,a,b,c,d) : rot(a,b,c,d)
  k_xor( 6,a,b,c,d) : sb6(a,b,c,d,e,f,g,h) : rot(e,f,g,h)
  k_xor( 7,e,f,g,h) : sb7(e,f,g,h,a,b,c,d) : rot(a,b,c,d)
  k_xor( 8,a,b,c,d) : sb0(a,b,c,d,e,f,g,h) : rot(e,f,g,h)
  k_xor( 9,e,f,g,h) : sb1(e,f,g,h,a,b,c,d) : rot(a,b,c,d)
  k_xor(10,a,b,c,d) : sb2(a,b,c,d,e,f,g,h) : rot(e,f,g,h)
  k_xor(11,e,f,g,h) : sb3(e,f,g,h,a,b,c,d) : rot(a,b,c,d)
  k_xor(12,a,b,c,d) : sb4(a,b,c,d,e,f,g,h) : rot(e,f,g,h)
  k_xor(13,e,f,g,h) : sb5(e,f,g,h,a,b,c,d) : rot(a,b,c,d)
  k_xor(14,a,b,c,d) : sb6(a,b,c,d,e,f,g,h) : rot(e,f,g,h)
  k_xor(15,e,f,g,h) : sb7(e,f,g,h,a,b,c,d) : rot(a,b,c,d)
  k_xor(16,a,b,c,d) : sb0(a,b,c,d,e,f,g,h) : rot(e,f,g,h)
  k_xor(17,e,f,g,h) : sb1(e,f,g,h,a,b,c,d) : rot(a,b,c,d)
  k_xor(18,a,b,c,d) : sb2(a,b,c,d,e,f,g,h) : rot(e,f,g,h)
  k_xor(19,e,f,g,h) : sb3(e,f,g,h,a,b,c,d) : rot(a,b,c,d)
  k_xor(20,a,b,c,d) : sb4(a,b,c,d,e,f,g,h) : rot(e,f,g,h)
  k_xor(21,e,f,g,h) : sb5(e,f,g,h,a,b,c,d) : rot(a,b,c,d)
  k_xor(22,a,b,c,d) : sb6(a,b,c,d,e,f,g,h) : rot(e,f,g,h)
  k_xor(23,e,f,g,h) : sb7(e,f,g,h,a,b,c,d) : rot(a,b,c,d)
  k_xor(24,a,b,c,d) : sb0(a,b,c,d,e,f,g,h) : rot(e,f,g,h)
  k_xor(25,e,f,g,h) : sb1(e,f,g,h,a,b,c,d) : rot(a,b,c,d)
  k_xor(26,a,b,c,d) : sb2(a,b,c,d,e,f,g,h) : rot(e,f,g,h)
  k_xor(27,e,f,g,h) : sb3(e,f,g,h,a,b,c,d) : rot(a,b,c,d)
  k_xor(28,a,b,c,d) : sb4(a,b,c,d,e,f,g,h) : rot(e,f,g,h)
  k_xor(29,e,f,g,h) : sb5(e,f,g,h,a,b,c,d) : rot(a,b,c,d)
  k_xor(30,a,b,c,d) : sb6(a,b,c,d,e,f,g,h) : rot(e,f,g,h)
  k_xor(31,e,f,g,h) : sb7(e,f,g,h,a,b,c,d) : k_xor(32,a,b,c,d)
  CompilerIf #SERPENT_BO = #SERPENT_ENDIAN_AS_IS
    *out_blk\l[0] = a
    *out_blk\l[1] = b
    *out_blk\l[2] = c
    *out_blk\l[3] = d
  CompilerElseIf #SERPENT_BO = #SERPENT_ENDIAN_BSWAP
    *out_blk\l[3] = bswap32(a)
    *out_blk\l[2] = bswap32(b)
    *out_blk\l[1] = bswap32(c)
    *out_blk\l[0] = bswap32(d)
  CompilerEndIf
EndProcedure

Procedure serpent_decrypt(*ctx.serpent_ctx, *in_blk.longarray, *out_blk.longarray)
  Protected.l a, b, c, d, e, f, g, h, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16
  CompilerIf #SERPENT_BO = #SERPENT_ENDIAN_AS_IS
    a = *in_blk\l[0]
    b = *in_blk\l[1]
    c = *in_blk\l[2]
    d = *in_blk\l[3]
  CompilerElseIf #SERPENT_BO = #SERPENT_ENDIAN_BSWAP
    a = bswap32(*in_blk\l[3])
    b = bswap32(*in_blk\l[2])
    c = bswap32(*in_blk\l[1])
    d = bswap32(*in_blk\l[0])
  CompilerEndIf
  k_xor(32,a,b,c,d) : ib7(a,b,c,d,e,f,g,h) : k_xor(31,e,f,g,h)
  irot(e,f,g,h) : ib6(e,f,g,h,a,b,c,d) : k_xor(30,a,b,c,d)
  irot(a,b,c,d) : ib5(a,b,c,d,e,f,g,h) : k_xor(29,e,f,g,h)
  irot(e,f,g,h) : ib4(e,f,g,h,a,b,c,d) : k_xor(28,a,b,c,d)
  irot(a,b,c,d) : ib3(a,b,c,d,e,f,g,h) : k_xor(27,e,f,g,h)
  irot(e,f,g,h) : ib2(e,f,g,h,a,b,c,d) : k_xor(26,a,b,c,d)
  irot(a,b,c,d) : ib1(a,b,c,d,e,f,g,h) : k_xor(25,e,f,g,h)
  irot(e,f,g,h) : ib0(e,f,g,h,a,b,c,d) : k_xor(24,a,b,c,d)
  irot(a,b,c,d) : ib7(a,b,c,d,e,f,g,h) : k_xor(23,e,f,g,h)
  irot(e,f,g,h) : ib6(e,f,g,h,a,b,c,d) : k_xor(22,a,b,c,d)
  irot(a,b,c,d) : ib5(a,b,c,d,e,f,g,h) : k_xor(21,e,f,g,h)
  irot(e,f,g,h) : ib4(e,f,g,h,a,b,c,d) : k_xor(20,a,b,c,d)
  irot(a,b,c,d) : ib3(a,b,c,d,e,f,g,h) : k_xor(19,e,f,g,h)
  irot(e,f,g,h) : ib2(e,f,g,h,a,b,c,d) : k_xor(18,a,b,c,d)
  irot(a,b,c,d) : ib1(a,b,c,d,e,f,g,h) : k_xor(17,e,f,g,h)
  irot(e,f,g,h) : ib0(e,f,g,h,a,b,c,d) : k_xor(16,a,b,c,d)
  irot(a,b,c,d) : ib7(a,b,c,d,e,f,g,h) : k_xor(15,e,f,g,h)
  irot(e,f,g,h) : ib6(e,f,g,h,a,b,c,d) : k_xor(14,a,b,c,d)
  irot(a,b,c,d) : ib5(a,b,c,d,e,f,g,h) : k_xor(13,e,f,g,h)
  irot(e,f,g,h) : ib4(e,f,g,h,a,b,c,d) : k_xor(12,a,b,c,d)
  irot(a,b,c,d) : ib3(a,b,c,d,e,f,g,h) : k_xor(11,e,f,g,h)
  irot(e,f,g,h) : ib2(e,f,g,h,a,b,c,d) : k_xor(10,a,b,c,d)
  irot(a,b,c,d) : ib1(a,b,c,d,e,f,g,h) : k_xor( 9,e,f,g,h)
  irot(e,f,g,h) : ib0(e,f,g,h,a,b,c,d) : k_xor( 8,a,b,c,d)
  irot(a,b,c,d) : ib7(a,b,c,d,e,f,g,h) : k_xor( 7,e,f,g,h)
  irot(e,f,g,h) : ib6(e,f,g,h,a,b,c,d) : k_xor( 6,a,b,c,d)
  irot(a,b,c,d) : ib5(a,b,c,d,e,f,g,h) : k_xor( 5,e,f,g,h)
  irot(e,f,g,h) : ib4(e,f,g,h,a,b,c,d) : k_xor( 4,a,b,c,d)
  irot(a,b,c,d) : ib3(a,b,c,d,e,f,g,h) : k_xor( 3,e,f,g,h)
  irot(e,f,g,h) : ib2(e,f,g,h,a,b,c,d) : k_xor( 2,a,b,c,d)
  irot(a,b,c,d) : ib1(a,b,c,d,e,f,g,h) : k_xor( 1,e,f,g,h)
  irot(e,f,g,h) : ib0(e,f,g,h,a,b,c,d) : k_xor( 0,a,b,c,d)
  CompilerIf #SERPENT_BO = #SERPENT_ENDIAN_AS_IS
    *out_blk\l[0] = a
    *out_blk\l[1] = b
    *out_blk\l[2] = c
    *out_blk\l[3] = d
  CompilerElseIf #SERPENT_BO = #SERPENT_ENDIAN_BSWAP
    *out_blk\l[3] = bswap32(a)
    *out_blk\l[2] = bswap32(b)
    *out_blk\l[1] = bswap32(c)
    *out_blk\l[0] = bswap32(d)
  CompilerEndIf
EndProcedure

;-----------------------------------------------------------------------------------

;*ctx(in)    = A context buffer allocated with sizeof(serpent_ctx) bytes
;*in_key(in) = The key (password) buffer. Never pass a *in_key buffer smaller than 128 or bigger than 256 bytes!
;key_len(in) = The length of the key in bits (e.g. 128, 192 or 256)

Procedure serpent_keysetup(*ctx.serpent_ctx, *in_key.longarray, key_len)
  Protected.l i, lk, a, b, c, d, e, f, g, h, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16
  If key_len > 256 Or key_len < 0
    ProcedureReturn 0
  EndIf
  i = 0
  lk = (key_len + 31) / 32
  While i < lk
    CompilerIf #SERPENT_BO = #SERPENT_ENDIAN_AS_IS
      *ctx\l_key[i] = *in_key\l[i]
    CompilerElseIf #SERPENT_BO = #SERPENT_ENDIAN_BSWAP
      *ctx\l_key[i] = bswap32(*in_key\l[lk - i - 1])
    CompilerEndIf
    i + 1
  Wend
  If key_len < 256
    While i < 8
      *ctx\l_key[i + 1] = 0
      i + 1
    Wend
    i = key_len / 32
    lk = shl32(1 , key_len) % 32
    *ctx\l_key[i] = *ctx\l_key[i] & (lk - 1) | lk
  EndIf
  i = 0
  While i < 132
    lk = *ctx\l_key[i] ! *ctx\l_key[i + 3] ! *ctx\l_key[i + 5] ! *ctx\l_key[i + 7] ! $9E3779B9 ! i
    *ctx\l_key[i + 8] = shl32(lk , 11) | shr32(lk , 21)
    i + 1
  Wend
  k_set( 0,a,b,c,d) : sb3(a,b,c,d,e,f,g,h) : k_get( 0,e,f,g,h)
  k_set( 1,a,b,c,d) : sb2(a,b,c,d,e,f,g,h) : k_get( 1,e,f,g,h)
  k_set( 2,a,b,c,d) : sb1(a,b,c,d,e,f,g,h) : k_get( 2,e,f,g,h)
  k_set( 3,a,b,c,d) : sb0(a,b,c,d,e,f,g,h) : k_get( 3,e,f,g,h)
  k_set( 4,a,b,c,d) : sb7(a,b,c,d,e,f,g,h) : k_get( 4,e,f,g,h)
  k_set( 5,a,b,c,d) : sb6(a,b,c,d,e,f,g,h) : k_get( 5,e,f,g,h)
  k_set( 6,a,b,c,d) : sb5(a,b,c,d,e,f,g,h) : k_get( 6,e,f,g,h)
  k_set( 7,a,b,c,d) : sb4(a,b,c,d,e,f,g,h) : k_get( 7,e,f,g,h)
  k_set( 8,a,b,c,d) : sb3(a,b,c,d,e,f,g,h) : k_get( 8,e,f,g,h)
  k_set( 9,a,b,c,d) : sb2(a,b,c,d,e,f,g,h) : k_get( 9,e,f,g,h)
  k_set(10,a,b,c,d) : sb1(a,b,c,d,e,f,g,h) : k_get(10,e,f,g,h)
  k_set(11,a,b,c,d) : sb0(a,b,c,d,e,f,g,h) : k_get(11,e,f,g,h)
  k_set(12,a,b,c,d) : sb7(a,b,c,d,e,f,g,h) : k_get(12,e,f,g,h)
  k_set(13,a,b,c,d) : sb6(a,b,c,d,e,f,g,h) : k_get(13,e,f,g,h)
  k_set(14,a,b,c,d) : sb5(a,b,c,d,e,f,g,h) : k_get(14,e,f,g,h)
  k_set(15,a,b,c,d) : sb4(a,b,c,d,e,f,g,h) : k_get(15,e,f,g,h)
  k_set(16,a,b,c,d) : sb3(a,b,c,d,e,f,g,h) : k_get(16,e,f,g,h)
  k_set(17,a,b,c,d) : sb2(a,b,c,d,e,f,g,h) : k_get(17,e,f,g,h)
  k_set(18,a,b,c,d) : sb1(a,b,c,d,e,f,g,h) : k_get(18,e,f,g,h)
  k_set(19,a,b,c,d) : sb0(a,b,c,d,e,f,g,h) : k_get(19,e,f,g,h)
  k_set(20,a,b,c,d) : sb7(a,b,c,d,e,f,g,h) : k_get(20,e,f,g,h)
  k_set(21,a,b,c,d) : sb6(a,b,c,d,e,f,g,h) : k_get(21,e,f,g,h)
  k_set(22,a,b,c,d) : sb5(a,b,c,d,e,f,g,h) : k_get(22,e,f,g,h)
  k_set(23,a,b,c,d) : sb4(a,b,c,d,e,f,g,h) : k_get(23,e,f,g,h)
  k_set(24,a,b,c,d) : sb3(a,b,c,d,e,f,g,h) : k_get(24,e,f,g,h)
  k_set(25,a,b,c,d) : sb2(a,b,c,d,e,f,g,h) : k_get(25,e,f,g,h)
  k_set(26,a,b,c,d) : sb1(a,b,c,d,e,f,g,h) : k_get(26,e,f,g,h)
  k_set(27,a,b,c,d) : sb0(a,b,c,d,e,f,g,h) : k_get(27,e,f,g,h)
  k_set(28,a,b,c,d) : sb7(a,b,c,d,e,f,g,h) : k_get(28,e,f,g,h)
  k_set(29,a,b,c,d) : sb6(a,b,c,d,e,f,g,h) : k_get(29,e,f,g,h)
  k_set(30,a,b,c,d) : sb5(a,b,c,d,e,f,g,h) : k_get(30,e,f,g,h)
  k_set(31,a,b,c,d) : sb4(a,b,c,d,e,f,g,h) : k_get(31,e,f,g,h)
  k_set(32,a,b,c,d) : sb3(a,b,c,d,e,f,g,h) : k_get(32,e,f,g,h)
  ProcedureReturn 1
EndProcedure

;*ctx(in)  = A context buffer initialized with serpent_keysetup above
;*in(in)   = Input buffer, must be bigger than 16 bytes
;*out(out) = Output buffer, 16 bytes minimum. Must be different from *in, same size as *in
;size(in)  = Input (and output) buffer size in bytes
;mode(in)  = Encryption mode. Can be either #SERPENT_CBC (recommended) or #SERPENT_ECB (to be avoided)
;*iv(in)   = Initialization vector, 16 bytes of random data. Needed only in CBC mode
;*cb(in)   = Optional callback procedure address for monitoring progress. Takes 2 integer parameters.

Procedure serpent_encoder(*ctx.serpent_ctx, *in, *out, size, *iv, mode = #SERPENT_CBC, *cb=0)
  ;Vars
  Protected totalrounds, remainingbytes, *temp, encrypt, currentblock, result, *remaining, *remainingout
  ;Begin
  If *in <> 0 And *out <> 0 And size >= #SERPENT_BLOCK_SIZE
    If mode = #SERPENT_CBC
      If *iv = 0 ;Check IV
        Debug "[!] Serpent in CBC mode needs an initialization vector!"
        CallDebugger
      EndIf
    EndIf
    ;See how many rounds to make and how many bytes remain
    totalrounds = size / #SERPENT_BLOCK_SIZE
    remainingbytes = size % #SERPENT_BLOCK_SIZE
    *temp = AllocateMemory(#SERPENT_BLOCK_SIZE)
    If *temp <> 0
      ;Encrypt the full rounds
      While encrypt < totalrounds
        ;Copy the block in the temp buffer
        CopyMemory(*in + currentblock, *temp, #SERPENT_BLOCK_SIZE)
        If mode = #SERPENT_CBC
          If encrypt = 0 ;For the first block, XOR with the IV
            xorbuffer(*temp, *iv, #SERPENT_BLOCK_SIZE)
          Else ;For the subsequent blocks, XOR with the previous block
            xorbuffer(*temp, *out + currentblock - #SERPENT_BLOCK_SIZE, #SERPENT_BLOCK_SIZE)
          EndIf
        EndIf
        ;Encrypt the block
        serpent_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 + #SERPENT_BLOCK_SIZE
        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(#SERPENT_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
          serpent_encrypt(*ctx, *out + currentblock - #SERPENT_BLOCK_SIZE, *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 serpent_keysetup above
;*in(in)   = Input buffer, must be bigger than 16 bytes
;*out(out) = Output buffer, 16 bytes minimum. Must be different from *in, same size as *in
;size(in)  = Input (and output) buffer size in bytes
;mode(in)  = Decryption mode. Can be either #SERPENT_CBC or #SERPENT_ECB
;*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 serpent_decoder(*ctx.serpent_ctx, *in, *out, size, *iv, mode = #SERPENT_CBC, *cb=0)
  ;Vars
  Protected totalrounds, remainingbytes, *temp, decrypt, currentblock, result, *remaining, *remainingout
  ;Begin
  If *in <> 0 And *out <> 0 And size >= #SERPENT_BLOCK_SIZE
    If mode = #SERPENT_CBC
      If *iv = 0 ;Check IV
        Debug "[!] Serpent in CBC mode needs an initialization vector!"
        CallDebugger
      EndIf
    EndIf
    ;See how many rounds to make and how many bytes remain
    totalrounds = size / #SERPENT_BLOCK_SIZE
    remainingbytes = size % #SERPENT_BLOCK_SIZE
    *temp = AllocateMemory(#SERPENT_BLOCK_SIZE)
    If *temp <> 0
      ;Decrypt the full rounds
      While decrypt < totalrounds
        ;Copy the block in the temp buffer
        CopyMemory(*in + currentblock, *temp, #SERPENT_BLOCK_SIZE)
        ;Decrypt the block
        serpent_decrypt(*ctx, *temp, *out + currentblock)
        If mode = #SERPENT_CBC
          If decrypt = 0 ;For the first block, XOR with the IV
            xorbuffer(*out + currentblock, *iv, #SERPENT_BLOCK_SIZE)
          Else ;For the subsequent blocks, XOR with the previous input block
            xorbuffer(*out + currentblock, *in + currentblock - #SERPENT_BLOCK_SIZE, #SERPENT_BLOCK_SIZE)
          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 + #SERPENT_BLOCK_SIZE
        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
          serpent_encrypt(*ctx, *in + currentblock - #SERPENT_BLOCK_SIZE, *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
Examples below:
Last edited by Inf0Byt3 on Sun Jun 08, 2014 12:28 pm, edited 2 times in total.
None are more hopelessly enslaved than those who falsely believe they are free. (Goethe)
Inf0Byt3
PureBasic Fanatic
PureBasic Fanatic
Posts: 2236
Joined: Fri Dec 09, 2005 12:15 pm
Location: Elbonia

Re: Serpent cipher include (x86) (24 May 2013)

Post by Inf0Byt3 »

Serpent_VectorTest.pb

Code: Select all

;Tests from: http://www.cs.technion.ac.il/~biham/Reports/Serpent/
;Sorry for the spaghetti code

EnableExplicit
DisableDebugger ;Need for speed

XIncludeFile "Serpent.pbi"

Structure chararray
  c.c[0]
EndStructure
Structure enctest
  KeySize.i
  SetName.s
  Key.s
  Plain.s
  Cipher.s
  IterC.s
  IterM.s
EndStructure
Structure dectest
  KeySize.i
  SetName.s
  Key.s
  Cipher.s
  Plain.s
  Encrypted.s
EndStructure

NewList Vectors.s() : NewList EncTests.enctest() : NewList DecTests.dectest()

AddElement(Vectors()) : Vectors() = "Serpent-128-128.verified.test-vectors"
AddElement(Vectors()) : Vectors() = "Serpent-192-128.verified.test-vectors"
AddElement(Vectors()) : Vectors() = "Serpent-256-128.verified.test-vectors"

Procedure.i pokehex(*buff.asciiarray, *hexstring.chararray, hexstringlen)
  Protected byte1.c, byte2.c, counter1, counter2
  Repeat
    byte1 = *hexstring\c[counter1] - 48
    byte2 = *hexstring\c[counter1 + 1] - 48
    If byte1 > 10 : byte1 - 7 : EndIf
    If byte2 > 10 : byte2 - 7 : EndIf
    *buff\a[counter2] = (byte1 * 16) + byte2
    counter1 + 2
    counter2 + 1
  Until counter1 = hexstringlen
  ProcedureReturn counter2
EndProcedure

Define Line.s, KeySize, SetNameS.s, KeyS.s, PlainS.s, CipherS.s, IterCS.s, IterMS.s, EncryptedS.s, *Key, *Plain
Define *Cipher, *IterC, *IterM, *Encrypted, *Enc, *Dec, PassedVector, TotalPassed, TotalFailed, Iter

OpenConsole()
PrintN("--------------------")
PrintN("Serpent for PureBasic vector test, NESSIE format")
PrintN("--------------------")
PrintN("Reading test vector files...")
ForEach Vectors()
  If ReadFile(0, Vectors()) ;128 bit key
    While Not Eof(0)
      Line = Trim(ReadString(0))
      If Left(Line, 10) = "Key size: "
        KeySize = Val(StringField(Line, 3, " ")) : If KeySize = 0 : PrintN("Error. Keysize cannot be 0.") : Break : EndIf
      ElseIf Left(Line, 3) = "Set" : SetNameS = Line
      ElseIf Left(Line, 3) = "key" : KeyS = StringField(Line, 2, "=") : If KeySize = 256 : KeyS + Trim(ReadString(0)) : EndIf
      ElseIf Left(Line, 5) = "plain" : PlainS = StringField(Line, 2, "=")
      ElseIf Left(Line, 6) = "cipher" : CipherS = StringField(Line, 2, "=")
      ElseIf Left(Line, 18) = "Iterated 100 times" : IterCS = StringField(Line, 2, "=")
      ElseIf Left(Line, 19) = "Iterated 1000 times" : IterMS = StringField(Line, 2, "=")
      ElseIf Left(Line, 9) = "encrypted" : EncryptedS = StringField(Line, 2, "=")
      ElseIf KeyS <> "" And PlainS <> "" And CipherS <> "" And IterCS <> "" And IterMS <> "" And EncryptedS = "" ;enc
        AddElement(EncTests()) : EncTests()\KeySize = KeySize : EncTests()\SetName = SetNameS : EncTests()\Key = KeyS
        EncTests()\Plain = PlainS : EncTests()\Cipher = CipherS : EncTests()\IterC = IterCS : EncTests()\IterM = IterMS
        KeyS = "" : PlainS = "" : CipherS = "" : IterCS = "" : IterMS = ""
      ElseIf KeyS <> "" And PlainS <> "" And CipherS <> "" And IterCS = "" And IterMS = "" And EncryptedS <> "" ;dec
        AddElement(DecTests()) : DecTests()\KeySize = KeySize : DecTests()\SetName = SetNameS : DecTests()\Key = KeyS
        DecTests()\Cipher = CipherS : DecTests()\Plain = PlainS : DecTests()\Encrypted = EncryptedS
        KeyS = "" : PlainS = "" : CipherS = "" : EncryptedS = ""
      EndIf
    Wend
    CloseFile(0)
  Else
    PrintN("Cannot read "+Vectors())
    Beep_(1500,150) : Delay(15) : Beep_(1400,150) : Delay(15) : Beep_(1300,150) : Delay(15)
  EndIf
Next
ForEach EncTests() ;Using #PB_Memory_NoClear so errors will pop-up if something goes wrong with the enc or dec
  *Ctx = AllocateMemory(SizeOf(serpent_ctx)) : *Key = AllocateMemory(EncTests()\KeySize,1) : *Plain = AllocateMemory(16,1)
  *Cipher = AllocateMemory(16,1) : *IterC = AllocateMemory(16,1) : *IterM = AllocateMemory(16,1)
  *Enc = AllocateMemory(16,1) : *Dec = AllocateMemory(16,1) : pokehex(*Key, @EncTests()\Key, (EncTests()\KeySize / 8) * 2)
  pokehex(*Plain, @EncTests()\Plain, 32) : pokehex(*Cipher, @EncTests()\Cipher, 32)
  pokehex(*IterC, @EncTests()\IterC, 32) : pokehex(*IterM, @EncTests()\IterM, 32)
  ;==========================================
  Print("Encode, "+Str(EncTests()\KeySize)+"bit "+EncTests()\SetName+" -> ")
  serpent_keysetup(*Ctx, *Key, EncTests()\KeySize)
  serpent_encoder(*Ctx, *Plain, *Enc, 16, 0, #SERPENT_ECB) ;Encode
  PassedVector = 0
  If CompareMemory(*Enc, *Cipher, 16) <> 0 ;See if the ciphertexts match
    serpent_decoder(*Ctx, *Enc, *Dec, 16, 0, #SERPENT_ECB) ;Decode
    If CompareMemory(*Enc, *Cipher, 16) <> 0 ;See if the text decoded ok
      ;Encrypt 100 times
      For Iter = 0 To 99
        serpent_encoder(*Ctx, *Plain, *Enc, 16, 0, #SERPENT_ECB) ;Encode
        CopyMemory(*Enc, *Plain, 16)
      Next
      If CompareMemory(*Enc, *IterC, 16) <> 0 ;See if the iterated ciphertexts match
        ;Encrypt 1000 times
        For Iter = 0 To 999 - 100
          serpent_encoder(*Ctx, *Plain, *Enc, 16, 0, #SERPENT_ECB) ;Encode
          CopyMemory(*Enc, *Plain, 16)
        Next
        If CompareMemory(*Enc, *IterM, 16) <> 0 ;See if the iterated ciphertexts match
          PassedVector = 1
        EndIf
      EndIf
    EndIf
  EndIf
  If PassedVector = 1
    PrintN("Passed!")
    TotalPassed + 1
  Else
    PrintN("Failed!") : Beep_(1500,150) : Delay(15) : Beep_(1400,150) : Delay(15) : Beep_(1300,150) : Delay(15)
    TotalFailed + 1 : Break
  EndIf
  ;==========================================
  FreeMemory(*Ctx) : FreeMemory(*Key) : FreeMemory(*Plain) : FreeMemory(*Cipher) : FreeMemory(*IterC)
  FreeMemory(*IterM) : FreeMemory(*Enc) : FreeMemory(*Dec)
Next
ClearList(EncTests())
ForEach DecTests() ;Using #PB_Memory_NoClear so errors will pop-up if something goes wrong with the enc or dec
  *Ctx = AllocateMemory(SizeOf(serpent_ctx)) : *Key = AllocateMemory(DecTests()\KeySize,1) : *Cipher = AllocateMemory(16,1)
  *Plain = AllocateMemory(16,1) : *Encrypted = AllocateMemory(16,1) : *Enc = AllocateMemory(16,1) : *Dec = AllocateMemory(16,1)
  pokehex(*Key, @DecTests()\Key, (DecTests()\KeySize / 8) * 2) : pokehex(*Cipher, @DecTests()\Cipher, 32)
  pokehex(*Plain, @DecTests()\Plain, 32) : pokehex(*Encrypted, @DecTests()\Encrypted, 32)
  ;==========================================
  Print("Decode, "+Str(DecTests()\KeySize)+"bit "+DecTests()\SetName+" -> ")
  serpent_keysetup(*Ctx, *Key, DecTests()\KeySize)
  serpent_decoder(*Ctx, *Cipher, *Dec, 16, 0, #SERPENT_ECB) ;Decode
  PassedVector = 0
  If CompareMemory(*Dec, *Plain, 16) <> 0 ;See if the plaintexts match
    serpent_encoder(*Ctx, *Dec, *Enc, 16, 0, #SERPENT_ECB) ;Encode
    If CompareMemory(*Enc, *Encrypted, 16) <> 0 ;See if the ciphertexts match
      PassedVector = 1
    EndIf
  EndIf
  If PassedVector = 1
    PrintN("Passed!")
    TotalPassed + 1
  Else
    PrintN("Failed!") : Beep_(1500,150) : Delay(15) : Beep_(1400,150) : Delay(15) : Beep_(1300,150) : Delay(15)
    TotalFailed + 1 : Break
  EndIf
  ;==========================================
  FreeMemory(*Ctx) : FreeMemory(*Key) : FreeMemory(*Cipher) : FreeMemory(*Plain) :  FreeMemory(*Encrypted)
  FreeMemory(*Enc) : FreeMemory(*Dec)
Next
PrintN("--------------------")
If TotalFailed = 0 And TotalPassed > 0
  PrintN("All tests passed ("+Str(TotalPassed)+")") : Beep_(1300,150) : Delay(15) : Beep_(1600,150) : Delay(15) : Beep_(1900,150) : Delay(15)
ElseIf TotalFailed > 0
  PrintN("Not all tests passed, sorry!")
ElseIf TotalPassed = 0 And TotalFailed = 0
  PrintN("No tests were made! Please download the test vectors from:")
  PrintN("http://www.cs.technion.ac.il/~biham/Reports/Serpent/")
  Print("Press Y and the Enter to open the webpage now: ")
  If LCase(Input()) = "y" : RunProgram("http://www.cs.technion.ac.il/~biham/Reports/Serpent/") : EndIf : End
Else
  PrintN("Nope...")
EndIf : Input()
Serpent_StressTest.pb

Code: Select all

;This code encrypts and decrypts random data of arbitrary length

EnableExplicit
DisableDebugger ;Need for speed

XIncludeFile "Serpent.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.serpent_ctx, KeyLen, *Key
Define OriginalLen, *Original, *Output, *Decrypted, *IV, Success, Clr

TotalLoops = 512

#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("Serpent 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(serpent_ctx))

  ;Make a random key
  Select Random(2,0)
    Case 0
      KeyLen = 16
    Case 1
      KeyLen = 24
    Case 2
      KeyLen = 32
  EndSelect
  *Key = AllocateMemory(KeyLen)
  CryptRandomData(*Key, KeyLen)

  ;Make a buffer with random data
  OriginalLen = Random(#MaxLen, #MinLen)
  *Original = AllocateMemory(OriginalLen, #PB_Memory_NoClear)
  CryptRandomData(*Original, OriginalLen)
 
  ;Make an output and decryption buffer
  *Output = AllocateMemory(OriginalLen, #PB_Memory_NoClear)
  *Decrypted = AllocateMemory(OriginalLen, #PB_Memory_NoClear)

  ;Make an IV
  *IV = AllocateMemory(16)
  CryptRandomData(*IV, 16)

  ;Do the work
  serpent_keysetup(*Ctx, *Key, KeyLen)
  serpent_encoder(*Ctx, *Original, *Output, OriginalLen, *IV, #SERPENT_CBC)
  TotalData + OriginalLen
  serpent_decoder(*Ctx, *Output, *Decrypted , OriginalLen, *IV, #SERPENT_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)+" ("+Str(KeyLen * 8)+" bit)")
  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()
[Edit]
Added a check until the vector test code will be Unicode compatible.

[Edit 2]
Fixed the code to be Unicode compatible. Please report any problems :).
Last edited by Inf0Byt3 on Sun Jun 08, 2014 1:08 pm, edited 2 times in total.
None are more hopelessly enslaved than those who falsely believe they are free. (Goethe)
coco2
Enthusiast
Enthusiast
Posts: 461
Joined: Mon Nov 25, 2013 5:38 am
Location: Australia

Re: Serpent cipher include (x86) (24 May 2013)

Post by coco2 »

When I downloaded the test vectors I get an error with set 1 and set 5.

Code: Select all

--------------------
Serpent for PureBasic vector test, NESSIE format
--------------------
Reading test vector files...
Encode, 128bit Set 1, vector#  0: -> Failed!
Decode, 128bit Set 5, vector#  0: -> Failed!
--------------------
Not all tests passed, sorry!
EDIT: stress test runs well 3.5Mib/s
Inf0Byt3
PureBasic Fanatic
PureBasic Fanatic
Posts: 2236
Joined: Fri Dec 09, 2005 12:15 pm
Location: Elbonia

Re: Serpent cipher include (x86) (24 May 2013)

Post by Inf0Byt3 »

Thanks for taking a look at it. Found the culprit. The example fails if you compile with Unicode enabled in compiler options. This problem however, is not with the include itself, but with the test. I didn't take Unicode into account and the "pokehex" function seems to fail. The stress test on the other hand works directly with binary data and memory buffers instead of strings so no problems can arise there.

[Edit]

By the way, to gain more speed you can disable the Debugger (Debugger menu) before running the code.
None are more hopelessly enslaved than those who falsely believe they are free. (Goethe)
coco2
Enthusiast
Enthusiast
Posts: 461
Joined: Mon Nov 25, 2013 5:38 am
Location: Australia

Re: Serpent cipher include (x86) (24 May 2013)

Post by coco2 »

Would someone be able to do an iASM version :?::smile:
Post Reply