Assembly Language Programming

July 19, 2013

FASM – Serpent-1 Encryption

The Serpent-1 Encryption Algorithm was a candidate for the AES and came second to the eventual winner Rijndael.

The homepage for Serpent is here:

http://www.cl.cam.ac.uk/~rja14/serpent.html

Serpent uses a 256 bit key to encrypt 128 bit blocks. The 128 bit input block is encrypted in 32 rounds.

Related Post

The Javascript version can be found here: Serpent-1 Encryption Algorithm

The Key Schedule

The 256 bit input key is used to generate 33 x 128 bit sub-keys. The first 32 sub-keys are used in the 32 rounds of the encryption process. The 33rd sub-key is used in the final round.

The following pseudo-code shows the sub-key generation process. An array of 140 32-bit words is created. The first 8 elements are filled with the original 256 bit input key. Then the remaining 132 words are generated as follows:

for(i = 8 ; i < 140 ; i++)
{
 Key[i] = Key[i-8] xor Key[i-5] xor Key[i-3] xor Key[i-1] xor 0x9e3779b9 xor i;

 Key[i] = ROTL(Key[i],11);
}

The 33 sub-keys (of 128 bits each) are then put through the s-boxes as follows:

s_index = 3;

for(i = 8; i < 136 ; i += 4)
{
 Key[i],Key[i+1],Key[i+2],Key[i+3] = S-Box[s_index](Key[i],Key[i+1],Key[i+2],Key[i+3]);

 s_index--;

 if(s_index < 0) s_index = 7; 
} 

Key[i],Key[i+1],Key[i+2],Key[i+3] = S-Box[s_index](Key[i],Key[i+1],Key[i+2],Key[i+3]);

Encryption

The 128 bit input block is encrypted in 32 rounds as follows:

C = input block;

for(round = 0 ; round < 32 ; round++)
{
 C = C xor SubKey[round];

 C = S-Box[round mod 8](C);

 if(round==31) break;

 C = LT(C);
}

 C = C xor SubKey[32];

C is the 128 bit cipher text. SubKey[round] is one of the 33 x 128 bit sub-keys, indexed from 0 to 32. LT is the linear transform as described below.

Decryption

Decryption is the exact reverse of the encryption process:

C = C xor SubKey[32];

for(round = 31 ; round >= 0 ; round--)
{
 if(round == 31) goto .sub

 C = InvLT(C);

 .sub:

 C = InvS-Box[round mod 8](C);

 C = C xor SubKey[round];
}

The Linear Transform

The linear transform is applied to a 128 bit block. The block is divided into 4 x 32 bit words as follows: |w0|w1|w2|w3|

  w0 = RotLeft(w0,13);

  w2 = RotLeft(w2,3);
  
  w1 = w1 xor w0 xor w2;

  w3 = w3 xor w2 xor ShiftLeft(w0,3);

  w1 = RotLeft(w1,1);

  w3 = RotLeft(w3,7);

  w0 = w0 xor w1 xor w3;

  w2 = w2 xor w3 xor ShiftLeft(w1,7);

  w0 = RotLeft(w0,5);

  w2 = RotLeft(w2,22);

The Inverse Linear Transform

The inverse linear transform is just the linear transform operations in reverse order, but with ROTR instead of ROTL:

  w2 = RotRight(w2,22);

  w0 = RotRight(w0,5);

  w2 = w2 xor w3 xor ShiftLeft(w1,7);

  w0 = w0 xor w1 xor w3;

  w3 = RotRight(w3,7);

  w1 = RotRight(w1,1);

  w3 = w3 xor w2 xor ShiftLeft(w0,3);

  w1 = w1 xor w0 xor w2;

  w2 = RotRight(w2,3);

  w0 = RotRight(w0,13);

The S-Boxes

Serpent uses 8 s-boxes. Each s-box contains 16 elements. The input and output to a s-box is a 4 bit value, as follows:

n’ = s-box[n], (n = 0-15)

My program implements the bitslice mode of the serpent cipher algorithm. The diagram below shows how the s-boxes are implemented in bitslice mode. The 128 bit input block is divided up into 4 x 32 bit words: |w0|w1|w2|w3|. These are then arranged as shown in the diagram. The substitution is then applied by taking 32 slices of 4 bits each and putting then through 32 copies of the s-box in parallel. Bit 0 is the lsb in the 4 bit value.

s-box-bitslice

Input Byte Ordering

The input/output is represented as a hexadecimal number, with each pair of hex digits corresponding directly to a byte in an internal byte array. Let the internal array be called input_array[]. Then the first 2 digits in the input correspond to the byte at input_array[0], the next 2 digits correspond to the byte at input_array[1], and so on. The input bytes are organised internally into 32 bit words, with input_array[0] – input_array[3] being the first word, input_array[4] – input_array[7] being the second word, and so on.

For example:

Byte array (NESSIE) input/output:

16 byte input = 11223344 55667788 9900aabb ccddeeff

Stored as:

input_array[16] = [11,22,33,44,55,66,77,88,99,00,aa,bb,cc,dd,ee,ff];

Test vectors for Serpent-1 with 256 bit keys are specified in byte array (NESSIE) format here:

http://www.cs.technion.ac.il/~biham/Reports/Serpent/Serpent-256-128.verified.test-vectors

The FASM x86 Source Code

To use the program enter a 256 bit key (64 hex digits) and press [Set Key]. This will display the key schedule. Note that the program expects a full 256 bit key, it will not pad keys shorter than this in the way specified by the algorithm.

Then enter the 128 bit input text (32 hex digits) and press [Encrypt] or [Decrypt]. The program will display the result from each round of the 32 rounds of encryption or decryption, with the final round being the result.

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

 format PE GUI 4.0

 entry start

 include 'win32a.inc'

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

 IDD_THE_DIALOG = 102

 IDC_INPUT = 1000
 IDC_KEY = 1001
 IDC_OUTPUT = 1002

 IDC_BTN_ENCRYPT = 1003
 IDC_BTN_DECRYPT = 1004
 IDC_BTN_KEY_256 = 1005
 IDC_BTN_RESET = 1006

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

 section '.code' code readable executable

  start:

    invoke GetModuleHandle,0
    invoke DialogBoxParam,eax,IDD_THE_DIALOG,0,DialogProc,0

  exit:

    invoke  ExitProcess,0 

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

proc DialogProc uses esi edi ebx,hwnddlg,msg,wparam,lparam

	cmp [msg],WM_INITDIALOG
	je .wminitdialog

	cmp [msg],WM_COMMAND
	je .wmcommand

	cmp [msg],WM_CLOSE
	je .wmclose

	xor eax,eax
	jmp .quit

  .wminitdialog:

	invoke SetDlgItemText,[hwnddlg],IDC_INPUT,szInputMessage
	invoke SetDlgItemText,[hwnddlg],IDC_KEY,szKeyMessage
	invoke SetDlgItemText,[hwnddlg],IDC_OUTPUT,""

	jmp .done

  .wmcommand:

	cmp [wparam], BN_CLICKED shl 16 + IDC_BTN_ENCRYPT
	je .ENCRYPT

	cmp [wparam], BN_CLICKED shl 16 + IDC_BTN_DECRYPT
	je .DECRYPT

	cmp [wparam], BN_CLICKED shl 16 + IDC_BTN_KEY_256
	je .SET_KEY_256

	cmp  [wparam], BN_CLICKED shl 16 + IDC_BTN_RESET
	je .RESET

    jmp .done

  .ENCRYPT:

	invoke GetDlgItemText,[hwnddlg],IDC_INPUT,bfDisplay,800

	stdcall onNullInput

	stdcall StrToInputBlock

	stdcall PrintInputBlock

	invoke SetDlgItemText,[hwnddlg],IDC_INPUT,bfDisplay

	stdcall EncryptBlock

	stdcall Print_Rounds

	invoke SetDlgItemText,[hwnddlg],IDC_OUTPUT,bfDisplay

	jmp .done

  .DECRYPT:

	invoke GetDlgItemText,[hwnddlg],IDC_INPUT,bfDisplay,800

	stdcall onNullInput

	stdcall StrToInputBlock

	stdcall PrintInputBlock

	invoke SetDlgItemText,[hwnddlg],IDC_INPUT,bfDisplay

	stdcall DecryptBlock

	stdcall Print_Rounds

	invoke SetDlgItemText,[hwnddlg],IDC_OUTPUT,bfDisplay

	jmp .done

  .SET_KEY_256:

	invoke GetDlgItemText,[hwnddlg],IDC_KEY,bfDisplay,800

	stdcall onNullInput

	stdcall StrToKeySchedule

  .EXPAND_256:

	stdcall Expand_Key_256

	; print the key schedule

	stdcall PrintKeySchedule

	invoke SetDlgItemText,[hwnddlg],IDC_OUTPUT,bfDisplay

	; print the key - null terminate bfDisplay at 71 bytes

	lea esi,[bfDisplay]

	mov [esi+71],byte 0

	invoke SetDlgItemText,[hwnddlg],IDC_KEY,bfDisplay

	jmp .done

  .RESET:

	invoke SetDlgItemText,[hwnddlg],IDC_INPUT,szInputMessage
	invoke SetDlgItemText,[hwnddlg],IDC_KEY,szKeyMessage
	invoke SetDlgItemText,[hwnddlg],IDC_OUTPUT,""

	jmp .done

  .wmclose:

	invoke EndDialog,[hwnddlg],0

  .done:

	mov eax,1

  .quit:

	ret

endp

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

proc Expand_Key_256

  ; Key = 256 bits = 32 bytes

  ; Key Schedule = 33 x 128 bit sub-keys

  ; Total storage (key + key schedule) = 560 bytes

	; generate pre-keys w(i)

	lea esi,[KeySchedule]

	lea edi,[KeySchedule]

	; the first 32 bytes of the key schedule store the input key

	add edi,32

	xor ecx,ecx

  .EXPAND:

	; w[i] = w[i-8] xor w[i-5] xor w[i-3] xor w[i-1] xor 0x9e3779b9 xor i

	; w[i] = ROTL(w[i],11)

	; w[i] is a 32 bit word

	mov eax,[esi]

	; xor w[i-5]

	xor eax,[esi+12]

	; xor w[i-3]

	xor eax,[esi+20]

	; xor w[i-1]

	xor eax,[esi+28]

	; xor 0x9e3779b9

	xor eax,0x9e3779b9

	; xor i

	xor eax,ecx

	; rotl(w[i],11)

	rol eax,11

	mov [edi],eax

	add edi,4

	add esi,4

	inc cx

	cmp cx,132

	jl .EXPAND

	; transform w(i) into words of round key k(i) by applying the s-boxes

	lea edi,[KeySchedule]

	; the first 32 bytes are the input key - skip these

	add edi,32

	xor ecx,ecx

	; the 8 s-boxes are stored in the one array, each taking up 16 bytes

	; ESI points to the relevant s-box

  .SUB:

  .S3:

	lea esi,[S_Box]

	add esi,48

	stdcall Substitute_Block

	add edi,16

  .S2:

	lea esi,[S_Box]

	add esi,32

	stdcall Substitute_Block

	add edi,16

  .S1:

	lea esi,[S_Box]

	add esi,16

	stdcall Substitute_Block

	add edi,16

  .S0:

	lea esi,[S_Box]

	stdcall Substitute_Block

	add edi,16

  .S7:

	lea esi,[S_Box]

	add esi,112

	stdcall Substitute_Block

	add edi,16

  .S6:

	lea esi,[S_Box]

	add esi,96

	stdcall Substitute_Block

	add edi,16

  .S5:

	lea esi,[S_Box]

	add esi,80

	stdcall Substitute_Block

	add edi,16

  .S4:

	lea esi,[S_Box]

	add esi,64

	stdcall Substitute_Block

	add edi,16

	inc cx

	cmp cx,4

	jl .SUB

	; s-box 3

	lea esi,[S_Box]

	add esi,48

	stdcall Substitute_Block

	ret

endp

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

proc EncryptBlock uses esi edi

  ; Key = 256 bits = 32 bytes

  ; Key Schedule = 33 x 128 bit sub-keys

  ; Total storage (key + key schedule) = 560 bytes

  ; for round = 0 to 31 - XOR the sub-key[round] into the 128 bit data block

  ; then apply the s-boxes

  ; then apply the linear transform - except for the last round

  ; for the last round - XOR the sub-key[32] into the data block

	lea edi,[InputBlock]

	; ECX gives the offset in bytes from the start of the key schedule array

	; the first 32 bytes store the 256 bit input key - skip these bytes

	mov ecx,32

  .ROUNDS:

	; 32 rounds

	lea esi,[KeySchedule]

	add esi,ecx

	; xor the sub-key into the cipher text

	mov eax,[esi]

	xor [edi],eax

	mov eax,[esi+4]

	xor [edi+4],eax

	mov eax,[esi+8]

	xor [edi+8],eax

	mov eax,[esi+12]

	xor [edi+12],eax

	; apply the S-box

	lea esi,[S_Box]

	; calculate the s-box number = (ECX-32)/16 mod 8

	mov eax,ecx

	sub eax,32

	shr eax,4

	and eax,0x7

	; multiply by 16 to get the byte offset from the start of the s-box array

	shl eax,4

	add esi,eax

	stdcall Substitute_Block

	add ecx,16

	; the sub-keys are indexed from 0 to 32

	; the last sub-key (32) is at the byte offset of 544 in the key schedule

	; at ECX = 544, skip the linear transform and do the final sub-key XOR

	cmp ecx,544

	je .FINAL

	stdcall Transform

	; store the round output

	; EDX gives the byte offset from the start of Roundsx32

	mov edx,ecx

	sub edx,48

	stdcall StoreRoundOutput

	jmp .ROUNDS

  .FINAL:

	; XOR the last sub-key into the data block

	; the index of the last sub-key is 32

	lea esi,[KeySchedule]

	add esi,ecx

	mov eax,[esi]

	xor [edi],eax

	mov eax,[esi+4]

	xor [edi+4],eax

	mov eax,[esi+8]

	xor [edi+8],eax

	mov eax,[esi+12]

	xor [edi+12],eax

	; store the round output

	; EDX gives the byte offset from the start of Roundsx32

	mov edx,31

	shl edx,4

	stdcall StoreRoundOutput

	ret

endp

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

proc Transform

  ; apply the linear transform to the cipher text block

  ; address of the block is passed in EDI

	; x0 = rotl(x0,13)

	mov eax,[edi]

	rol eax,13

	mov [edi],eax

	; x2 = rotl(x2,3)

	mov eax,[edi+8]

	rol eax,3

	mov [edi+8],eax

	; x1 = x1 xor x0 xor x2

	; x2 already in EAX

	xor eax,[edi]

	xor eax,[edi+4]

	mov [edi+4],eax

	; x3 = x3 xor x2 xor shl(x0,3)

	mov eax,[edi]

	shl eax,3

	xor eax,[edi+8]

	xor eax,[edi+12]

	mov [edi+12],eax

	; x1 = rotl(x1,1)

	mov eax,[edi+4]

	rol eax,1

	mov [edi+4],eax

	; x3 = rotl(x3,7)

	mov eax,[edi+12]

	rol eax,7

	mov [edi+12],eax

	; x0 = x0 xor x1 xor x3

	; x3 already in EAX

	xor eax,[edi]

	xor eax,[edi+4]

	mov [edi],eax

	; x2 = x2 xor x3 xor shl(x1,7)

	mov eax,[edi+4]

	shl eax,7

	xor eax,[edi+8]

	xor eax,[edi+12]

	mov [edi+8],eax

	; x0 = rotl(x0,5)

	mov eax,[edi]

	rol eax,5

	mov [edi],eax

	; x2 = rotl(x2,22)

	mov eax,[edi+8]

	rol eax,22

	mov [edi+8],eax

	ret

endp

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

proc DecryptBlock uses esi edi

  ; Key = 256 bits = 32 bytes

  ; Key Schedule = 33 x 128 bit sub-keys

  ; Total storage (key + key schedule) = 560 bytes

  ; the sub-keys are indexed from 0 to 32

  ; for the first round - XOR the 128 bit sub-key[32] into the data block

  ; then apply the inverse s-box and XOR the sub-key[31] into the data block

  ; for the next 31 rounds:

  ; (i) apply the inverse linear transform

  ; (ii) apply the inverse s-box

  ; (iii) XOR the sub-key into the data block

	locals

	  round db 0

	endl

	lea edi,[InputBlock]

	; ECX gives the offset in bytes from the start of the key schedule array

	; the first 32 bytes store the 256 bit input key - skip these bytes

	; so start from the last sub-key at (32 + (32 x 16)) = 544 bytes

	mov ecx,544

	lea esi,[KeySchedule]

	add esi,ecx

	; XOR this sub-key into the data block

	mov eax,[esi]

	xor [edi],eax

	mov eax,[esi+4]

	xor [edi+4],eax

	mov eax,[esi+8]

	xor [edi+8],eax

	mov eax,[esi+12]

	xor [edi+12],eax

	sub ecx,16

	jmp .ISUB

  .ROUNDS:

	; 31 rounds

	; apply the inverse linear transform

	stdcall InverseTransform

  .ISUB:

	; apply the inverse S-box

	lea esi,[IS_Box]

	; calculate the inverse s-box number = (ECX-32)/16 mod 8

	mov eax,ecx

	sub eax,32

	shr eax,4

	and eax,0x7

	; multiply by 16 to get the byte offset from the start of the inverse s-box array

	shl eax,4

	add esi,eax

	stdcall Substitute_Block

	; xor the sub-key into the cipher text

	lea esi,[KeySchedule]

	add esi,ecx

	mov eax,[esi]

	xor [edi],eax

	mov eax,[esi+4]

	xor [edi+4],eax

	mov eax,[esi+8]

	xor [edi+8],eax

	mov eax,[esi+12]

	xor [edi+12],eax

	; store the round output

	; EDX gives the byte offset from the start of Roundsx32

	xor edx,edx

	mov dl,[round]

	shl edx,4

	stdcall StoreRoundOutput

	inc byte [round]

	; the first sub-key is at a byte offset of 32 from the start of the key schedule

	; exit the loop when ECX becomes less then 32

	sub ecx,16

	cmp ecx,32

	jge .ROUNDS

	ret

endp

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

proc InverseTransform

  ; apply the inverse linear transform to the cipher text block

  ; address of the block is passed in EDI

	; x2 = rotr(x2,22)

	mov eax,[edi+8]

	ror eax,22

	mov [edi+8],eax

	; x0 = rotr(x0,5)

	mov eax,[edi]

	ror eax,5

	mov [edi],eax

	; x2 = x2 xor x3 xor shl(x1,7)

	mov eax,[edi+4]

	shl eax,7

	xor eax,[edi+8]

	xor eax,[edi+12]

	mov [edi+8],eax

	; x0 = x0 xor x1 xor x3

	mov eax,[edi+12]

	xor eax,[edi]

	xor eax,[edi+4]

	mov [edi],eax

	; x3 = rotr(x3,7)

	mov eax,[edi+12]

	ror eax,7

	mov [edi+12],eax

	; x1 = rotr(x1,1)

	mov eax,[edi+4]

	ror eax,1

	mov [edi+4],eax

	; x3 = x3 xor x2 xor shl(x0,3)

	mov eax,[edi]

	shl eax,3

	xor eax,[edi+8]

	xor eax,[edi+12]

	mov [edi+12],eax

	; x1 = x1 xor x0 xor x2

	mov eax,[edi+8]

	xor eax,[edi]

	xor eax,[edi+4]

	mov [edi+4],eax

	; x2 = rotr(x2,3)

	mov eax,[edi+8]

	ror eax,3

	mov [edi+8],eax

	; x0 = rotr(x0,13)

	mov eax,[edi]

	ror eax,13

	mov [edi],eax

	ret

endp

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

proc Substitute_Block uses ecx edx

  ; use the S-Box to substitute each bit in a 128 bit block

  ; the address of the block is passed in EDI

  ; the address of the S-Box is passed in ESI

  ; this proc is used for both the s-boxes and inverse s-boxes

	locals

	  bitslice db 0

	endl

	xor ecx,ecx

	; the bitslice is constructed by taking the lsb from each of the 32 bit words

	; each of the 4 input words are then right shifted by 1 bit

	; the 4-bit bitslice is put through the s-box

	; the 4 output bits are used to set the msb of each of the 4 input words

	; after 32 iterations all of the input bits will have been substituted 

  .SUB:

	; construct the bit-slice for input into the s-box

	; bit 3:

	mov al,[edi+12]

	and al,1

	shl al,3

	mov [bitslice],al

	; bit 2:

	mov al,[edi+8]

	and al,1

	shl al,2

	or [bitslice],al

	; bit 1:

	mov al,[edi+4]

	and al,1

	shl al,1

	or [bitslice],al

	; bit 0:

	mov al,[edi]

	and al,1

	or [bitslice],al

	; right shift the bits in each of the 4 x 32 bit words of the input block

	mov eax,[edi]

	shr eax,1

	mov [edi],eax

	mov eax,[edi+4]

	shr eax,1

	mov [edi+4],eax

	mov eax,[edi+8]

	shr eax,1

	mov [edi+8],eax

	mov eax,[edi+12]

	shr eax,1

	mov [edi+12],eax

	; substitute the bits

	xor eax,eax

	mov al,[bitslice]

	mov dl,[esi+eax]

	; insert the output bits back into the input block

	; bit 0:

	mov al,dl

	and al,1

	shl eax,31

	or [edi],eax

	; bit 1:

	mov al,dl

	and al,2

	shl eax,30

	or [edi+4],eax

	; bit 2:

	mov al,dl

	and al,4

	shl eax,29

	or [edi+8],eax

	; bit 3:

	mov al,dl

	and al,8

	shl eax,28

	or [edi+12],eax

	inc cx

	cmp cx,32

	jl .SUB

	ret

endp

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

proc StoreRoundOutput uses esi edi

  ; store the 16 byte output from each of the 32 rounds

  ; EDX gives the offset in bytes from the start of [Roundsx32]

	lea esi,[InputBlock]

	lea edi,[Roundsx32]

	add edi,edx

	mov eax,[esi]

	mov [edi],eax

	mov eax,[esi+4]

	mov [edi+4],eax

	mov eax,[esi+8]

	mov [edi+8],eax

	mov eax,[esi+12]

	mov [edi+12],eax

	ret

endp

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

proc Print_Rounds uses esi edi

  ; print the data in the Roundsx32 buffer to the bfDisplay string

	locals

	  count db 0

	endl

	lea esi,[Roundsx32]

	lea edi,[bfDisplay]

	mov ecx,512

  .PRINT:

	; byte to digits

	mov al,[esi]

	stdcall ByteToDigits

	; add the 2 digits to the string

	mov [edi],ah
	inc edi

	mov [edi],al
	inc edi

	dec cx

	jcxz .DONE

	; update ESI

	inc esi

	; add spaces and CRLF to the output string

	; a space after every 8 chars
	; a CRLF after every 32 chars

	add [count],2

	test [count],7

	jne .PRINT

	test [count],31

	je .ADD_CRLF

	mov [edi],byte 32

	inc edi

	jmp .PRINT

  .ADD_CRLF:

	mov [edi],byte 13

	inc edi

	mov [edi],byte 10

	inc edi

	jmp .PRINT

  .DONE:

	; zero terminate the string

	mov [edi],byte 0

	ret

endp

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

proc StrToBuffer

  ; load the buffer in memory from the string bfDisplay

  ; address of buffer is passed in EAX

  ; max number of hex digits to read passed in EDX

	locals

	  count dw 0

	endl

	lea esi,[bfDisplay]

	mov edi,eax

	mov ecx,0

  .GET_CHARS:

	mov al,byte [esi]

	cmp al,0

	je .DONE

	stdcall HexDigitToValue

	; just skip and get the next char if not a hex digit

	cmp eax,0xffff

	je .NEXT

	; AL contains a nibble, determine where to place it in the dest byte

	; if count is even, the nibble is mapped to the lower 4 bits in dest
	; if count is odd, the nibble is mapped to the upper 4 bits in dest

	inc word [count]

	; even if count AND 1 == 0

	test [count],1

	jz .EVEN

  .ODD:

	inc cx

	shl al,4

	mov [edi],al

	jmp .NEXT

  .EVEN:

	inc cx

	or [edi],al

	inc edi

  .NEXT:

	inc esi

	cmp cx,dx

	jge .DONE

	jmp .GET_CHARS

  .DONE:

	; CX contains the number of hex digits that were read, return in EDX

	mov edx,ecx

	ret

endp

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

proc ResetInputBlock uses edi ecx

  ; clear the 16 byte input block buffer

	lea edi,[InputBlock]

	mov cx,0

  .CLEAR:

	mov [edi],byte 0

	inc edi

	inc cx

	cmp cx,16

	jl .CLEAR

	ret

endp

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

proc ResetKeySchedule uses edi ecx

  ; clear the 560 byte key schedule buffer

	lea edi,[KeySchedule]

	mov cx,0

  .CLEAR:

	mov [edi],byte 0

	inc edi

	inc cx

	cmp cx,560

	jl .CLEAR

	ret

endp

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

proc StrToInputBlock

  ; load the input block buffer from the input string

  ; input string is a hex value

	; zero the input text value

	stdcall ResetInputBlock

	stdcall StrToHexStr

	lea eax,[InputBlock]

	; read a max of 32 hex digits

	mov edx,32

	stdcall StrToBuffer

	ret

endp

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

proc StrToKeySchedule

  ; load the key schedule buffer from the input string

  ; input string is a hex value

	; zero the cipher text value

	stdcall ResetKeySchedule

	stdcall StrToHexStr

	lea eax,[KeySchedule]

	; read a max of 64 hex digits: 64 x 4 = 256 bits

	mov edx,64

	stdcall StrToBuffer

	ret

endp

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

proc PrintInputBlock

  ; print the 16 byte input block to the display string

	lea eax,[InputBlock]

	xor edx,edx

	mov dx,16

	stdcall PrintBuffer

	ret

endp

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

proc PrintKeySchedule

  ; print the key schedule to the display string

	lea eax,[KeySchedule]

	xor edx,edx

	mov dx,560

	stdcall PrintBuffer

	ret

endp

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

proc PrintBuffer uses esi edi

  ; print the hex value in the buffer to the bfDisplay string

  ; address of buffer passed in EAX

  ; number of bytes in buffer passed in EDX

	locals

	  count db 0

	endl

	mov esi,eax

	lea edi,[bfDisplay]

	mov ecx,edx

  .PRINT:

	; byte to digits

	mov al,[esi]

	stdcall ByteToDigits

	; add the 2 digits to the string

	mov [edi],ah
	inc edi

	mov [edi],al
	inc edi

	dec cx

	jcxz .DONE

	; update ESI

	inc esi

	; add spaces and CRLF to the output string

	; a space after every 8 chars
	; a CRLF after every 64 chars

	add [count],2

	test [count],7

	jne .PRINT

	test [count],63

	je .ADD_CRLF

	mov [edi],byte 32

	inc edi

	jmp .PRINT

  .ADD_CRLF:

	mov [edi],byte 13

	inc edi

	mov [edi],byte 10

	inc edi

	jmp .PRINT

  .DONE:

	; zero terminate the string

	mov [edi],byte 0

	ret

endp

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

proc StrToHexStr uses esi edi

  ; filter out any non hex digit characters from the bfDisplay string

	lea esi,[bfDisplay]
	lea edi,[bfDisplay]

  .FILTER:

	mov al,[esi]

	cmp al,0

	je .DONE

	cmp al,48

	jl .NOT_HEX

	cmp al,57

	jle .COPY

	cmp al,65

	jl .NOT_HEX

	cmp al,70

	jle .COPY

	cmp al,97

	jl .NOT_HEX

	cmp al,102

	jg .NOT_HEX

  .COPY:

	mov [edi],al

	inc edi

  .NOT_HEX:

	inc esi

	jmp .FILTER

  .DONE:

	; null terminate the new string

	mov [edi],byte 0

	ret

endp

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

proc HexDigitToValue

  ; convert a hex digit to a 4 bit value

  ; input in AL - output in AL

	; is AL in the range: 48 - 57 (0 - 9) ?

	cmp al,48

	jl .NOT_HEX

	cmp al,57

	jg .A_Z

	sub al,48

	jmp .DONE

  .A_Z:

	; is AL in the range: 65 - 70 (A - B) ?

	cmp al,65

	jl .NOT_HEX

	cmp al,70

	jg .a_z

	sub al,55

	jmp .DONE

  .a_z:

	; is AL in the range: 97 - 102 (a - b) ?

	cmp al,97

	jl .NOT_HEX

	cmp al,102

	jg .NOT_HEX

	sub al,87

	jmp .DONE

  .NOT_HEX:

	; set EAX to 0xffff if input is not a valid hex digit

	mov eax,0xffff

  .DONE:

	ret

endp

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

proc ByteToDigits

  ; convert 1 byte to 2 hex digits

  ; input in AL - output in AX

	; copy AL to AH

	mov ah,al

	; AH: get the upper 4 bits of the byte

	shr ah,4

	; nibble to hex digit

	add ah,48

	cmp ah,57

	jle .NEXT

	add ah,7

  .NEXT:

	; AL: get the lower 4 bits of the byte

	and al,0xf

	; nibble to hex digit

	add al,48

	cmp al,57

	jle .DONE

	add al,7

  .DONE:

	; output is in AX

	ret

endp

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

proc onNullInput

  ; if the bfDisplay string is empty, set it to "0"

	lea edi,[bfDisplay]

	cmp [edi],byte 0

	jne .DONE

	mov [edi],byte 48
	mov [edi+1],byte 0

  .DONE:

	ret

endp

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

section '.sbox' readable writeable

  S_Box db\
	    3,8,15,1,10,6,5,11,14,13,4,2,7,0,9,12,\
	    15,12,2,7,9,0,5,10,1,11,14,8,6,13,3,4,\
	    8,6,7,9,3,12,10,15,13,1,14,4,0,11,5,2,\
	    0,15,11,8,12,9,6,3,13,1,2,4,10,7,5,14,\
	    1,15,8,3,12,0,11,6,2,5,4,10,9,14,7,13,\
	    15,5,2,11,4,10,9,12,0,3,14,8,13,6,7,1,\
	    7,2,12,5,8,4,6,11,14,9,1,15,13,3,10,0,\
	    1,13,15,0,14,8,2,11,7,4,12,10,9,3,5,6

  IS_Box db\
	    13,3,11,0,10,6,5,12,1,14,4,7,15,9,8,2,\
	    5,8,2,14,15,6,12,3,11,4,7,9,1,13,10,0,\
	    12,9,15,4,11,14,1,2,0,3,6,13,5,8,10,7,\
	    0,9,10,7,11,14,6,13,3,5,12,2,4,8,15,1,\
	    5,0,8,3,10,9,7,14,2,12,11,6,4,15,13,1,\
	    8,15,2,9,4,1,13,14,11,6,5,3,7,12,10,0,\
	    15,10,1,13,5,3,6,0,4,9,14,7,2,12,8,11,\
	    3,0,6,13,9,14,15,8,5,12,11,7,10,1,4,2

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

section '.idata' import data readable writeable

  library kernel,'KERNEL32.DLL',\
	  user,'USER32.DLL'

  import kernel,\
	 GetModuleHandle,'GetModuleHandleA',\
	 ExitProcess,'ExitProcess'

  import user,\
	 DialogBoxParam,'DialogBoxParamA',\ 
	 SetDlgItemText,'SetDlgItemTextA',\
	 GetDlgItemText,'GetDlgItemTextA',\
	 EndDialog,'EndDialog'

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

section '.data' readable writeable

  bfDisplay rb 1600

  KeySchedule rb 560

  InputBlock rb 16

  Roundsx32 rb 512

  szInputMessage db "Enter 16 bytes of data:",0
  szKeyMessage db "Enter 32 bytes of key data:",0

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

section '.rc' resource data readable

  directory RT_DIALOG,dialogs

  resource dialogs,IDD_THE_DIALOG,LANG_ENGLISH+SUBLANG_DEFAULT,the_dialog

  dialog the_dialog,\
  'FASM - Serpent One Encryption Algorithm',50,50,360,400,\
  DS_MODALFRAME+WS_MINIMIZEBOX+WS_POPUP+WS_VISIBLE+WS_CAPTION+WS_SYSMENU,\
  0,0,"Lucida Console",11

  dialogitem 'BUTTON','Key',-1,7,5,346,40,BS_GROUPBOX+WS_VISIBLE,0
  dialogitem 'BUTTON','Input',-1,7,50,346,30,BS_GROUPBOX+WS_VISIBLE,0
  dialogitem 'BUTTON',"Output",-1,7,86,346,288,BS_GROUPBOX+WS_VISIBLE,0

  dialogitem 'EDIT',0,IDC_KEY,13,16,335,22,ES_MULTILINE+ES_AUTOVSCROLL+ES_WANTRETURN+WS_VSCROLL+WS_BORDER+WS_VISIBLE,0
  dialogitem 'EDIT',0,IDC_INPUT,13,61,335,12,ES_MULTILINE+ES_AUTOVSCROLL+ES_WANTRETURN+WS_VSCROLL+WS_BORDER+WS_VISIBLE,0
  dialogitem 'EDIT',0,IDC_OUTPUT,13,97,335,268,ES_MULTILINE+ES_AUTOVSCROLL+ES_WANTRETURN+WS_VSCROLL+WS_BORDER+WS_VISIBLE,0

  dialogitem 'BUTTON',"Encrypt",IDC_BTN_ENCRYPT,7,380,50,14,BS_PUSHBUTTON+WS_VISIBLE,0
  dialogitem 'BUTTON',"Decrypt",IDC_BTN_DECRYPT,59,380,50,14,BS_PUSHBUTTON+WS_VISIBLE,0

  dialogitem 'BUTTON',"Set Key",IDC_BTN_KEY_256,111,380,50,14,BS_PUSHBUTTON+WS_VISIBLE,0
  dialogitem 'BUTTON',"Reset",IDC_BTN_RESET,163,380,50,14,BS_PUSHBUTTON+WS_VISIBLE,0

  enddialog

; -------------------------------------------------------------------------------------
Advertisements

Blog at WordPress.com.

%d bloggers like this: