; ; Copyright © B. Ege, 2012. ; e-mail: . ; ; This program is a free software: you can redistribute it ; and/or modify it under the terms of the GNU General Public ; License as published by the Free Software Foundation. ; ; It is distributed without any warranty of correctness nor ; fintess for any particular purpose. See the GNU General ; Public License for more details. ; ; . ; ; Description: SPONGENT 160/160/80 hash function. ; Version 2 - May 2012. ; ; ---------------------------------------------------- ; | User interface: | ; |----------------------------------------------------| ; |(1) Data to hash must be in SRAM with the first byte| ; | at the location pointed by SRAM_DATA. The data | ; | has to be updated before each update or final | ; | call and the number of bytes needed in SRAM by | ; | each update call is equal to DATA_NUM_BYTE | ; | | ; | If final routine is called, r24 must contain | ; | the number of bytes of data passed to the | ; | function. | ; |----------------------------------------------------| ; |(2) Call init, update or final routine | ; |----------------------------------------------------| ; |(3) After each call, the intermediate (or final) | ; | hash function state is at location pointed by | ; | SRAM_STATE. Length of hash intermediate states | ; | is given by STATE_NUM_BYTE constant. | ; | Lenght of final hash value is given by | ; | HASH_NUM_BYTE. | ; ---------------------------------------------------- ; ;.include "tn45def.inc" ;TO_UPD, comment if used in main, uncomment to assemble standing alone ; Constants ; .org 0x200 sbox: .db 0x0E, 0x0D, 0x0B, 0x00, 0x02, 0x01, 0x04, 0x0F, 0x07, 0x0A, 0x08, 0x05, 0x09, 0x0C, 0x03, 0x06 .EQU DATA_NUM_BYTE = 10 ;Number of bytes that will be processed by the update function ; .EQU STATE_NUM_BYTE = 30 ;Memory needed for hash function intermediate state (in bytes) ; .EQU ADD_MEM_NUM_BYTE = 30 ;Optional additional memory for internal computation (in bytes) ; .EQU HASH_NUM_BYTE = 20 ;Size of output hash (in bytes) ; .DSEG SRAM_DATA: .BYTE DATA_NUM_BYTE SRAM_ADD_MEM : .BYTE ADD_MEM_NUM_BYTE SRAM_STATE: .BYTE STATE_NUM_BYTE .CSEG ; ; Register declarations ; temp registers to keep the first block of hash value #define hash0 R1 #define hash1 R2 #define hash2 R3 #define hash3 R4 #define hash4 R5 #define hash5 R6 #define hash6 R7 #define hash7 R8 #define hash8 R9 #define hash9 R10 ; LFSR state register #define tLFSR R16 ; Temporary registers (general purpose) #define tmp0 R24 ; #define tmp1 R25 ; #define counter R17 ; #define counter_pb R18 #define inc_counter R19 ; ; Temporary registers to be used in pLayer #define p_inp0 R20 ; #define p_inp1 R21 ; #define p_out0 R22 ; #define p_out1 R23 ; ; Temporary registers to be used in sBoxLayer #define s_inp0 R20 ; #define s_inp1 R21 ; ; Initialisation ; Initialise SRAM contents to 0x00 init: LDI tmp0 , STATE_NUM_BYTE CLR tmp1 LDI ZH , high (SRAM_STATE) LDI ZL , low (SRAM_STATE) RCALL initialise_state ; initialise state in memory to all zeros ret ; Update Hash ; as the state itself covers 30 of the 32 registers, ; we use a 6 byte buffer to make space for tmp variables update: LDI tLFSR , 0x01 ; set the initial value of the LFSR state ; RCALL XOR_msg ; XOR 80-bit message block to the state (state 20-29) RJMP first_round round: CPI tLFSR , 0x7F ; check if the last round has been computed BREQ end_update ; next state of the LFSR after 120 rounds is 0x7F first_round: RCALL XOR_lCnttnCl ; XOR lCounter and retnuoCl to state ; RCALL sBoxLayer ; perform sbox (byte-wise) ; RCALL pLayer ; perform bit permutation ; RJMP round end_update: ret ; Final ; first apply padding to the message and store it in the memory ; padding either consists of one block (80 bits), or ; how much needed to complete the message block to 80 bits ; NOTE: r24 contains the number of bytes passed to the final function. ; NOTE: there is NO msg size in SPONGENT padding final: RCALL prepare_final_msg_block ; padding is applied and stored in SRAM_DATA LDI tLFSR , 0x01 ; set the initial value of the LFSR state ; RCALL XOR_msg ; XOR the padded message block (or only padding) RJMP first_round_final1 round_final1: CPI tLFSR , 0x7F ; next state of the LFSR after 120 rounds is 0x7F BREQ end_final1 ; first_round_final1: RCALL XOR_lCnttnCl ; XOR lCounter and retnuoCl to state ; RCALL sBoxLayer ; perform sbox (byte-wise) ; RCALL pLayer ; perform bit permutation ; RJMP round_final1 end_final1: RCALL produce_hash1 ; write the first 80 bits of the hash value to SRAM_STATE LDI tLFSR , 0x01 ; set the initial value of the LFSR state ; RJMP first_round_final2 round_final2: CPI tLFSR , 0x7F ; next state of the LFSR after 120 rounds is 0x7F BREQ end_final2 ; first_round_final2: RCALL XOR_lCnttnCl ; XOR lCounter and retnuoCl to state ; RCALL sBoxLayer ; perform sbox (byte-wise) ; RCALL pLayer ; perform bit permutation ; RJMP round_final2 end_final2: RCALL produce_hash2 ; write the next 80 bits of the hash value to SRAM_STATE ret ; Any_other_function_name ; Description + code prepare_final_msg_block: ; r24 contains the number of bytes passed to the final function. LDI ZH , high (SRAM_DATA) ; apply padding to the message or form an 80 bit padding block LDI ZL , low (SRAM_DATA) ; depending on the size of the message CLR tmp1 ADD ZL , R24 ; set Z to point to the end of the message ADC ZH , tmp1 SUBI R24 , 10 ; find out how many bytes need to be filled (always < 0) NEG R24 LDI tmp1 , 0x80 ; first pad a 1, and then necessary number of zeros ST Z+ , tmp1 CLR tmp1 padd_zeros: DEC R24 BRBS 1 , end_final_msg_block ST Z+ , tmp1 RJMP padd_zeros end_final_msg_block: ret ; writes the first half of the hash value to temporary registers produce_hash1: LDI ZH , high (SRAM_STATE) LDI ZL , low (SRAM_STATE) ADIW Z , 30 LD hash0 , -Z LD hash1 , -Z LD hash2 , -Z LD hash3 , -Z LD hash4 , -Z LD hash5 , -Z LD hash6 , -Z LD hash7 , -Z LD hash8 , -Z LD hash9 , -Z ret ; writes the final hash value to SRAM_STATE produce_hash2: LDI ZH , high (SRAM_STATE) LDI ZL , low (SRAM_STATE) MOVW XH:XL , ZH:ZL CLR R0 LDI counter , 10 ST Z+ , hash0 ST Z+ , hash1 ST Z+ , hash2 ST Z+ , hash3 ST Z+ , hash4 ST Z+ , hash5 ST Z+ , hash6 ST Z+ , hash7 ST Z+ , hash8 ST Z+ , hash9 ADIW X , 30 copy_values: LD hash0 , -X ST Z+ , hash0 DEC counter BRNE copy_values ret ; Message block addition is done according to ; match the convention used in the test vector file XOR_msg: LDI XH , high (SRAM_STATE) LDI XL , low (SRAM_STATE) LDI YH , high (SRAM_DATA) LDI YL , low (SRAM_DATA) LDI counter , 10 ADIW X , 30 do_XOR_msg: LD tmp0 , Y+ LD tmp1 , -X EOR tmp1 , tmp0 ST X , tmp1 DEC counter BRNE do_XOR_msg ret update_LFSR: LSL tLFSR ; LFSR_state <<= 1 ; MOV tmp1 , tLFSR ; tmp1 = LFSR_state ; ANDI tLFSR , 0x7F ; LFSR_state &= 0x7F ANDI tmp1 , 0x80 ; tmp1 &= 0x80 LSR tmp1 ; tmp1 >>= 1 MOV tmp0 , tLFSR ; tmp0 = LFSR_state ANDI tmp0 , 0x40 ; tmp0 &= 0x40 SUB tmp1 , tmp0 ; tmp1 ^= tmp0 BREQ store_LFSR_state ; skip if tmp1 ^ tmp0 == 0 LDI tmp1 , 0x01 EOR tLFSR , tmp1 ; otherwise LFSR_state ^= 0x01 store_LFSR_state: ret initialise_state: ; initialise the state to 0x00 ST Z+ , tmp1 DEC tmp0 BRBC 1 , initialise_state ; as long as tmp0 != 0 ret XOR_lCnttnCl: LDI ZH , high (SRAM_STATE) LDI ZL , low (SRAM_STATE) MOV tmp1 , tLFSR ; copy tLFSR to temp to compute retnuoCl SWAP tmp1 ; compute retnuoCl without altering tLFSR reg BST tmp1 , 7 ; retnuoCl = lCounter bits in reverse order BLD tmp1 , 3 LSL tmp1 BST tLFSR , 4 BLD tmp1 , 3 BST tLFSR , 6 BLD tmp1 , 1 BST tLFSR , 0 BLD tmp1 , 7 BST tLFSR , 2 BLD tmp1 , 5 ; compute retnuoCl LD tmp0 , Z ; XOR retnuoCl to 'leftmost' byte EOR tmp0 , tmp1 ST Z , tmp0 ADIW Z , 29 ; XOR lCounter to 'rightmost' byte LD tmp0 , Z EOR tmp0 , tLFSR ST Z , tmp0 RCALL update_LFSR ; update the LFSR after counter XOR ret sBoxLayer: LDI XH , high (SRAM_STATE) LDI XL , low (SRAM_STATE) LDI YH , high (SRAM_ADD_MEM) LDI YL , low (SRAM_ADD_MEM) CLR R0 CLR counter LDI ZH , high (sbox << 1) do_sbox: LD s_inp0 , X MOV s_inp1 , s_inp0 ANDI s_inp1 , 0x0F ; take the nibble on the right MOV ZL , s_inp1 LPM s_inp1 , Z ; compute the substituted value SWAP s_inp0 ; take the nibble on the left ANDI s_inp0 , 0x0F MOV ZL , s_inp0 LPM s_inp0 , Z ; compute the substituted value SWAP s_inp0 EOR s_inp0 , s_inp1 ST X+ , s_inp0 INC counter CPI counter , 30 BRMI do_sbox ret ; pLayer loop pLayer: LDI ZH , high (SRAM_STATE) LDI ZL , low (SRAM_STATE) LDI YH , high (SRAM_ADD_MEM) LDI YL , low (SRAM_ADD_MEM) CLR R0 CLR counter MOVW XH:XL , ZH:ZL ; perform bit permutation on each 2 byte ; and write the result back to SRAM_STATE do_permute_2bytes: LD p_inp0 , Z LDD p_inp1 , Z+1 RCALL permute_2bytes ST Z+ , p_out0 ST Z+ , p_out1 INC counter CPI counter , 15 BRMI do_permute_2bytes CLR counter LDI inc_counter , 2 ; bit permutation is done ; now permute nibbles jump_here: MOVW ZH:ZL , XH:XL ; reset the pointer to the original CPI counter , 29 BRMI go_on1 SUBI counter , 29 go_on1: ADD ZL , counter ADC ZH , R0 LD p_out0 , Z ADD counter , inc_counter CPI counter , 29 BREQ last_nibble BRMI go_on2 SUBI counter , 29 go_on2: MOVW ZH:ZL , XH:XL ; reset the pointer to the original ADD ZL , counter ADC ZH , R0 LD p_out1 , Z RCALL switch_nibbles ST Y , p_out0 CPI counter , 1 BREQ sub_27 BRPL add_2 sub_27: SBIW Y , 27 ST Y , p_out1 ADD counter , inc_counter ADIW Y , 2 RJMP jump_here add_2: ADIW Y , 2 ST Y , p_out1 ADD counter , inc_counter ADIW Y , 2 RJMP jump_here last_nibble: MOVW ZH:ZL , XH:XL ; reset the pointer to the original ADD ZL , counter ADC ZH , R0 LD p_out1 , Z RCALL switch_nibbles ST Y , p_out0 ADIW Y , 2 ST Y , p_out1 ; now the bytes have the correct nibbles ; re-order the bytes and write back to SRAM_STATE LDI YH , high (SRAM_ADD_MEM) LDI YL , low (SRAM_ADD_MEM) MOVW ZH:ZL , XH:XL CLR counter LDI inc_counter , 4 jump_here_byte: MOVW XH:XL , YH:YL ; reset the pointer to the original CPI counter , 29 BREQ final_byte BRMI go_on_byte SUBI counter , 29 go_on_byte: ADD XL , counter ADC XH , R0 LD p_out0 , X ST Z , p_out0 ADIW Z , 1 ADD counter , inc_counter RJMP jump_here_byte final_byte: ADD XL , counter ADC XH , R0 LD p_out0 , X ST Z , p_out0 ret ; apply the modified permutation layer to 2 bytes ; then we will distribute the nibbles through (switch_nibbles function) permute_2bytes: LDI counter_pb , 2 CLR p_out0 CLR p_out1 permute_again: CLC SBRC p_inp1 , 0 SEC ROR p_out1 SBRC p_inp1 , 4 SEC ROR p_out1 SBRC p_inp0 , 0 SEC ROR p_out1 SBRC p_inp0 , 4 SEC ROR p_out1 LSR p_inp0 LSR p_inp1 CLC SBRC p_inp1 , 0 SEC ROR p_out0 SBRC p_inp1 , 4 SEC ROR p_out0 SBRC p_inp0 , 0 SEC ROR p_out0 SBRC p_inp0 , 4 SEC ROR p_out0 LSR p_inp0 LSR p_inp1 DEC counter_pb BRNE permute_again ret ; takes 2 bytes (4 nibbles) (a,b) and (c,d) as input ; outputs (a,c) and (b,d) switch_nibbles: SWAP p_out1 ROR p_out0 ROR p_out1 ROR p_out0 ROR p_out1 ROR p_out0 ROR p_out1 ROR p_out0 ROR p_out1 ROL p_out0 ROL p_out0 ROL p_out0 ROL p_out0 ret