News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

Spritz - a spongy RC4-like stream cipher

Started by lorenzo, February 08, 2015, 09:03:05 PM

Previous topic - Next topic

lorenzo

A new cipher and hash function was published in October last year called Spritz which is based on RC4, the most popular cryptography cipher ever used. I've been trying to implement it but not getting the same results as test vectors provided. Attached is some C code and the assembler version I'm stuck with. Would be nice to have extra set of eyes on code to see what exactly I'm doing wrong. Any advice appreciated.

Published document with specification/design : http://people.csail.mit.edu/rivest/pubs/RS14.pdf



; Spritz - a spongy RC4-like stream cipher and hash function
; Designed by Ron Rivest and Jacob Schuldt
;

.686
.model flat, stdcall
 
.nolist
.nocref

WIN32_LEAN_AND_MEAN equ 1

include windows.inc
include stdio.inc

includelib kernel32.lib
includelib msvcrt.lib

.list
.cref

option epilogue:none
option prologue:none

N equ 256

STATE struct
    a byte ?
    i byte ?
    j byte ?
    k byte ?
    z byte ?
    w byte ?
    s byte N dup (?)
STATE ends

.code

InitializeState proc fastcall state:dword
    pushad
    mov    edi, state
    xor    eax, eax
    stosb                  ; a = 0
    stosb                  ; i = 0
    stosb                  ; j = 0
    stosb                  ; k = 0
    stosb                  ; z = 0
    inc    eax
    stosb                  ; w = 1
    dec    eax
; for v = 0 to N - 1
;   s[v] = v
init_sbox:                 ; fixed for 256 bytes
    stosb
    inc    al
    jnz    init_sbox
    popad
    ret
InitializeState endp
InitializeStateLen equ $ - InitializeState

Absorb proc fastcall state:dword, M:dword, len:dword
    pushad
    ; for v = 0 to I.length - 1
    xor    ebx, ebx
    mov    esi, M
absorb_loop:
    ; AbsorbByte(I[v])
    movzx  edx, byte ptr[esi+ebx] ; I[v]
    call   AbsorbByte
    inc    ebx
    cmp    ebx, [esp+32+4]  ; v < len
    jnz    absorb_loop
    popad
    retn   4
Absorb endp
AbsorbLen equ $ - Absorb

AbsorbByte proc fastcall state:dword, b:dword
    pushad
    mov    ebx, b
   
    ; AbsorbNibble (LOW (b))
    and    edx, 15
    call   AbsorbNibble
   
    ; AbsorbNibble (HIGH (b))
    mov    edx, ebx
    shr    edx, 4
    call   AbsorbNibble
   
    popad
    ret
AbsorbByte endp
AbsorbByteLen equ $ - AbsorbByte

AbsorbNibble proc fastcall state:dword, x:dword
    pushad
    ; if a == [N/2]
    ;   Shuffle()
    .if [state][STATE.a] == N/2
      call Shuffle
    .endif
    ; Swap(S[a], S[[N/2] + x])
    add    dl, N/2
    mov    al, [state][STATE.s][edx]
    xchg   al, [state][STATE.a]
    mov    [state][STATE.s][edx], al
    ; a = a + 1
    inc    [state][STATE.a]
    popad
    ret
AbsorbNibble endp
AbsorbNibbleLen equ $ - AbsorbNibble

AbsorbStop proc fastcall state:dword
    pushad
    ; if a == [N/2]
    ;   Shuffle()
    .if [state][STATE.a] == N/2
      call Shuffle
    .endif
    ; a = a + 1
    inc    [state][STATE.a]
    popad
    ret
AbsorbStop endp
AbsorbStopLen equ $ - AbsorbStop

Shuffle proc fastcall state:dword
    call   Whip
    call   Crush
   
    call   Whip
    call   Crush
   
    call   Whip
   
    ; a = 0
    and    [state][STATE.a], 0
    ret
Shuffle endp
ShuffleLen equ $ - Shuffle

Whip proc fastcall state:dword
    pushad
    ; for v = 0 to r - 1
    ;   Update()
    xor    ebx, ebx
whip_loop:
    call   Update
    inc    ebx
    cmp    ebx, 2*N               ; 2N
    jnz    whip_loop
    ; w = w + 1
    add    [state][STATE.w], 2    ; when N is power of 2
    popad
    ret
Whip endp
WhipLen equ $ - Whip

Crush proc fastcall state:dword
    pushad
    ; for v = 0 to [N/2] - 1
    xor    esi, esi       
crush_loop:
    ; if S[v] > S[N - 1 - v]
    mov    edx, esi
    neg    edx
    add    edx, N-1
    mov    al, [state][STATE.s][esi]  ; s[v]
    mov    bl, [state][STATE.s][edx]  ; s[N - 1 - v]
   
    .if al > bl
      ; Swap (S[v], S[N - 1 - v])
      mov  [state][STATE.s][edx], al
      mov  [state][STATE.s][esi], bl
    .endif
   
    inc    esi
    cmp    esi, N/2
    jnz    crush_loop
   
    popad
    ret
Crush endp
CrushLen equ $ - Crush

Squeeze proc fastcall state:dword, p:dword, r:dword
    pushad
    ; if a > 0
    ;   Shuffle ()
    .if [state][STATE.a] > 0
      call Shuffle
    .endif
   
    ; for v = 0 to r - 1
    mov    ebx, [esp+32+4] ; r
    mov    edi, edx        ; p
squeeze_loop:
    call   Drip
    ; p[v] = Drip()
    stosb
    dec    ebx   
    jnz    squeeze_loop
   
    ; return P
    popad
    retn   4
Squeeze endp
SqueezeLen equ $ - Squeeze

Drip proc fastcall state:dword
    ; if a > 0
    ;   Shuffle()
    .if [state][STATE.a] > 0
      call Shuffle
    .endif
    ; Update()
    call Update
    ; return Output()
    call Output
    ret
Drip endp
DripLen equ $ - Drip

Update proc fastcall state:dword
    pushad
    movzx  eax, byte ptr[state][STATE.i]
    movzx  ebx, byte ptr[state][STATE.j]
    mov    dh, [state][STATE.k]
       
    ; i = i + w
    add    al, [state][STATE.w]
   
    ; j = k + S[j + S[i]]
    mov    dl, [state][STATE.s][eax]
    add    bl, dl
    mov    bl, [state][STATE.s][ebx]
    add    bl, dh
   
    ; Swap(S[i], S[j])
    xchg   dl, [state][STATE.s][ebx]
    mov    [state][STATE.s][eax], dl
   
    ; k = i + k + S[j]
    add    dl, al
    add    dl, dh
   
    mov    [state][STATE.i], al
    mov    [state][STATE.j], bl
    mov    [state][STATE.k], dl
    popad
    ret
Update endp
UpdateLen equ $ - Update

; z = S[j + S[i + S[z + k]]]
; return z;
Output proc fastcall state:dword
    pushad
    movzx   eax, byte ptr[state][STATE.i]
    movzx   ebx, byte ptr[state][STATE.j]
    movzx   edx, byte ptr[state][STATE.z]
    ; z + k
    add     dl, [state][STATE.k]
    ; i + s[z + k]
    add     al, [state][STATE.s][edx]
    ; j + s[i + s[z + k]]
    add     bl, [state][STATE.s][eax]
    ; z = s[j]
    mov     al, [state][STATE.s][ebx]
    mov     [state][STATE.z], al
    ; return z
    mov     [esp+1ch], eax
    popad
    ret
Output endp
OutputLen equ $ - Output

    public _Stream
_Stream:
Stream proc fastcall p:dword, r:dword, key:dword, len:dword
    pushad
    mov    edi, p        ; edi = p
    mov    esi, r        ; esi = r
    mov    ebp, esp
    sub    esp, sizeof STATE
   
    mov    ecx, esp
    call   InitializeState
   
    push   [ebp+32+8]      ; len
    mov    edx, [ebp+32+4] ; key
    call   Absorb
   
    push   esi             ; r
    mov    edx, edi        ; edx = p
    call   Squeeze
   
    mov    esp, ebp
    popad
    ret
Stream endp
StreamLen equ $ - Stream

; Produces an r-byte hash of the input message (byte sequence) M
Hash proc fastcall h:dword, M:dword, r:dword
    pushad
    mov    esi, [esp+32+4]   ; esi = r
    mov    ebp, esp
    sub    esp, sizeof STATE
    mov    edi, ecx          ; edi = h
    ; InitializeState()
    mov    ecx, esp          ; ecx = &state
    call   InitializeState
    ; Absorb(M); AbsorbStop()
    push   esi               ; r, edx = M
    call   Absorb
    call   AbsorbStop
    ; Absorb(r)
    push   sizeof byte
    mov    edx, esi
    call   Absorb
    ; return Squeeze(r)
    push   esi               ; r
    mov    edx, edi          ; edx = h
    call   Squeeze
    mov    esp, ebp
    popad
    ret
Hash endp
HashLen equ $ - Hash

TotalLen equ InitializeStateLen + \
             AbsorbLen + \
             AbsorbByteLen + \
             AbsorbNibbleLen + \
             AbsorbStopLen + \
             ShuffleLen + \
             WhipLen + \
             CrushLen + \
             SqueezeLen + \
             DripLen + \
             UpdateLen + \
             OutputLen + \
             HashLen + \
             StreamLen
             
%echo ======================
%echo Total Size : @CatStr(%TotalLen) Bytes
%echo ======================

.data

fmt db "%02x ", 0
newline db 13, 10, 0

plaintext db "arcfour"
plaintext_len equ $-plaintext

ciphertext db 32 dup (?)

.code
spritzx:
    mov    ecx, offset ciphertext
    mov    edx, 32
    push   plaintext_len
    push   offset plaintext
    call   Stream
   
    xor    ebx, ebx
print_bytes:
    movzx  eax, byte ptr[ciphertext+ebx]
    invoke printf, addr fmt, eax
    inc    ebx
    cmp    ebx, 32
    jnz    print_bytes
   
    invoke ExitProcess, 0
   
    end spritzx
   


Using C code, the 32-byte result for stream is
ff 8c f2 68 09 4c 87 b9 5f 74 ce 6f ee 9d 30 03 a5 f9 fe 69 44 65 3c d5 0e 66 bf 18 9c 63 f6 99
But with asm code, it's
29 b2 10 fe 7f 2a 6a f4 a4 45 a0 1d a0 d7 bd 50 f2 49 b3 88 a0 a8 b7 40 6b 4a 84 9f 95 88 eb 6d

lorenzo

I used stdcall for functions instead and changed code to make it easier to read but still get wrong values.



; Spritz - a spongy RC4-like stream cipher and hash function
; Designed by Ron Rivest and Jacob Schuldt
;

.686
.model flat, stdcall
 
.nolist
.nocref

WIN32_LEAN_AND_MEAN equ 1

include windows.inc
include stdio.inc

includelib kernel32.lib
includelib msvcrt.lib

.list
.cref

N equ 256

STATE struct
    a byte ?
    i byte ?
    j byte ?
    k byte ?
    z byte ?
    w byte ?
    s byte N dup (?)
STATE ends

.code

InitializeState proc state:dword
    pushad
    mov    edi, [state]
    xor    eax, eax
    mov    [edi][STATE.a], al
    mov    [edi][STATE.i], al
    mov    [edi][STATE.j], al
    mov    [edi][STATE.k], al
    mov    [edi][STATE.z], al
    mov    [edi][STATE.w], 1
; for v = 0 to N - 1
;   s[v] = v
init_sbox:
    mov    [edi][STATE.s][eax], al
    inc    eax
    cmp    eax, N
    jnz    init_sbox
    popad
    ret
InitializeState endp
InitializeStateLen equ $ - InitializeState

Absorb proc state:dword, M:dword, len:dword
    pushad
    ; for v = 0 to I.length - 1
    xor    ebx, ebx
    mov    esi, [M]
absorb_loop:
    ; AbsorbByte(I[v])
    movzx  edx, byte ptr[esi+ebx] ; I[v]
    push   edx
    push   [state]
    call   AbsorbByte
    inc    ebx
    cmp    ebx, [len]  ; v < len
    jnz    absorb_loop
    popad
    ret
Absorb endp
AbsorbLen equ $ - Absorb

AbsorbByte proc state:dword, b:dword
    pushad
    mov    esi, [state]
    mov    edi, [b]
   
    ; AbsorbNibble (LOW (b))
    mov    ebx, edi
    and    ebx, 15
    push   ebx
    push   esi
    call   AbsorbNibble
   
    ; AbsorbNibble (HIGH (b))
    shr    edi, 4
    push   edi
    push   esi
    call   AbsorbNibble
   
    popad
    ret
AbsorbByte endp
AbsorbByteLen equ $ - AbsorbByte

AbsorbNibble proc state:dword, x:dword
    pushad
    mov    esi, [state]
    ; if a == [N/2]
    ;   Shuffle()
    .if [esi][STATE.a] == N/2
      push esi
      call Shuffle
    .endif
    ; Swap(S[a], S[[N/2] + x])
    mov    edx, [x]
    add    dl, N/2
    mov    al, [esi][STATE.s][edx]
    xchg   al, [esi][STATE.a]
    mov    [esi][STATE.s][edx], al
    ; a = a + 1
    mov    al, byte ptr[esi][STATE.a]
    add    al, 1
    mov    [esi][STATE.a], al
    popad
    ret
AbsorbNibble endp
AbsorbNibbleLen equ $ - AbsorbNibble

AbsorbStop proc state:dword
    pushad
    mov    esi, [state]
    ; if a == [N/2]
    ;   Shuffle()
    .if [esi][STATE.a] == N/2
      push esi
      call Shuffle
    .endif
    ; a = a + 1
    mov    al, byte ptr[esi][STATE.a]
    add    al, 1
    mov    [esi][STATE.a], al
    popad
    ret
AbsorbStop endp
AbsorbStopLen equ $ - AbsorbStop

Shuffle proc state:dword
    pushad
    mov    ebx, [state]
   
    push   ebx
    call   Whip
   
    push   ebx
    call   Crush
   
    push   ebx
    call   Whip
   
    push   ebx
    call   Crush
   
    push   ebx
    call   Whip
   
    ; a = 0
    and    [ebx][STATE.a], 0
    popad
    ret
Shuffle endp
ShuffleLen equ $ - Shuffle

Whip proc state:dword
    pushad
    ; for v = 0 to r - 1
    ;   Update()
    mov    esi, [state]
    xor    ebx, ebx
whip_loop:
    push   esi
    call   Update
    inc    ebx
    cmp    ebx, 2*N               ; 2N
    jnz    whip_loop
    ; w = w + 1
    mov    al, byte ptr[esi][STATE.w]
    add    al, 2
    mov    [esi][STATE.w], al
    popad
    ret
Whip endp
WhipLen equ $ - Whip

Crush proc state:dword
    pushad
    ; for v = 0 to [N/2] - 1
    xor    esi, esi     
    mov    edi, [state]   
crush_loop:
    ; if S[v] > S[N - 1 - v]
    mov    edx, esi
    neg    edx
    add    edx, N-1
    mov    al, [edi][STATE.s][esi]  ; s[v]
    mov    bl, [edi][STATE.s][edx]  ; s[N - 1 - v]
   
    .if al > bl
      ; Swap (S[v], S[N - 1 - v])
      mov  [edi][STATE.s][edx], al
      mov  [edi][STATE.s][esi], bl
    .endif
   
    inc    esi
    cmp    esi, N/2
    jnz    crush_loop
   
    popad
    ret
Crush endp
CrushLen equ $ - Crush

Squeeze proc state:dword, p:dword, r:dword
    pushad
    mov    esi, [state]
    ; if a > 0
    ;   Shuffle ()
    .if [esi][STATE.a] > 0
      push esi
      call Shuffle
    .endif
   
    ; for v = 0 to r - 1
    mov    ebx, [r] ; r
    mov    edi, [p] ; p
squeeze_loop:
    push   esi
    call   Drip
    ; p[v] = Drip()
    stosb
    dec    ebx   
    jnz    squeeze_loop
   
    ; return P
    popad
    ret
Squeeze endp
SqueezeLen equ $ - Squeeze

Drip proc uses esi state:dword
    mov    esi, [state]
    ; if a > 0
    ;   Shuffle()
    .if [esi][STATE.a] > 0
      push esi
      call Shuffle
    .endif
    ; Update()
    push esi
    call Update
    ; return Output()
    push esi
    call Output
    ret
Drip endp
DripLen equ $ - Drip

Update proc state:dword
    pushad
    mov    esi, [state]
    xor    eax, eax
    xor    ebx, ebx
   
    ; i = i + w
    mov    al, byte ptr[esi][STATE.i]
    add    al, byte ptr[esi][STATE.w]
    mov    byte ptr[esi][STATE.i], al
   
    ; j = k + S[j + S[i]]
    mov    al, byte ptr[esi][STATE.i]
    mov    bl, byte ptr[esi][STATE.j]
    add    bl, byte ptr[esi][STATE.s][eax]
    mov    bl, byte ptr[esi][STATE.s][ebx]
    add    bl, byte ptr[esi][STATE.k]
    mov    byte ptr[esi][STATE.j], bl
   
    ; k = i + k + S[j]
    mov    bl, byte ptr[esi][STATE.j]
    mov    bl, byte ptr[esi][STATE.s][ebx]
    add    bl, byte ptr[esi][STATE.k]
    add    bl, byte ptr[esi][STATE.i]
    mov    byte ptr[esi][STATE.k], bl
   
    ; Swap (S[i], S[j])
    mov    al, byte ptr[esi][STATE.i]
    mov    cl, byte ptr[esi][STATE.s][eax]
   
    mov    bl, byte ptr[esi][STATE.j]
    mov    dl, byte ptr[esi][STATE.s][ebx]
   
    mov    byte ptr[esi][STATE.s][eax], dl
    mov    byte ptr[esi][STATE.s][ebx], cl
    popad
    ret
Update endp
UpdateLen equ $ - Update

; z = S[j + S[i + S[z + k]]]
; return z;
Output proc uses esi state:dword
    mov     esi, [state]
    xor     eax, eax
    mov     al, byte ptr[esi][STATE.k]
    add     al, byte ptr[esi][STATE.z]
    mov     al, byte ptr[esi][STATE.s][eax]
    add     al, byte ptr[esi][STATE.i]
    mov     al, byte ptr[esi][STATE.s][eax]
    add     al, byte ptr[esi][STATE.j]
    mov     al, byte ptr[esi][STATE.s][eax]
    mov     byte ptr[esi][STATE.z], al
    ret
Output endp
OutputLen equ $ - Output

    public _Stream
_Stream:
Stream proc p:dword, r:dword, key:dword, len:dword
    local state:STATE
   
    pushad
    lea    esi, [state]
    push   esi
    call   InitializeState
   
    push   [len]
    push   [key]
    push   esi
    call   Absorb
   
    push   [r]
    push   [p]
    push   esi
    call   Squeeze
   
    popad
    ret
Stream endp
StreamLen equ $ - Stream

; Produces an r-byte hash of the input message (byte sequence) M
Hash proc h:dword, M:dword, r:dword
    pushad
    mov    esi, [esp+32+4]   ; esi = r
    mov    ebp, esp
    sub    esp, sizeof STATE
    mov    edi, ecx          ; edi = h
    ; InitializeState()
    mov    ecx, esp          ; ecx = &state
    call   InitializeState
    ; Absorb(M); AbsorbStop()
    push   esi               ; r, edx = M
    call   Absorb
    call   AbsorbStop
    ; Absorb(r)
    push   sizeof byte
    mov    edx, esi
    call   Absorb
    ; return Squeeze(r)
    push   esi               ; r
    call   Squeeze
    mov    esp, ebp
    popad
    ret
Hash endp
HashLen equ $ - Hash

TotalLen equ InitializeStateLen + \
             AbsorbLen + \
             AbsorbByteLen + \
             AbsorbNibbleLen + \
             AbsorbStopLen + \
             ShuffleLen + \
             WhipLen + \
             CrushLen + \
             SqueezeLen + \
             DripLen + \
             UpdateLen + \
             OutputLen + \
             HashLen + \
             StreamLen
             
%echo ======================
%echo Total Size : @CatStr(%TotalLen) Bytes
%echo ======================

.data

fmt db "%02x ", 0
newline db 13, 10, 0

plaintext db "arcfour"
plaintext_len equ $-plaintext

ciphertext db 32 dup (?)

.code
spritzy:
    push   plaintext_len
    push   offset plaintext
    push   32
    push   offset ciphertext
    call   Stream
   
    xor    ebx, ebx
print_bytes:
    movzx  eax, byte ptr[ciphertext+ebx]
    invoke printf, addr fmt, eax
    inc    ebx
    cmp    ebx, 32
    jnz    print_bytes
   
    invoke ExitProcess, 0
   
    end spritzy
   

lorenzo

Had a nice walk and spotted problem in AbsorbNibble. Should have been using S[a] and not a itself..


AbsorbNibble proc state:dword, x:dword
    pushad
    mov    esi, [state]
    ; if a == [N/2]
    ;   Shuffle()
    .if [esi][STATE.a] == N/2
      push esi
      call Shuffle
    .endif
    ; Swap(S[a], S[[N/2] + x])
    mov    edx, [x]
    xor    eax, eax
    add    dl, N/2
    mov    al, [esi][STATE.s][edx]
    mov    bl, [esi][STATE.a]            ; ebx = a
    xchg   al, [esi][STATE.s][ebx]
    mov    [esi][STATE.s][edx], al
    ; a = a + 1
    inc    [esi][STATE.a]
    popad
    ret
AbsorbNibble endp

GoneFishing

I rebuilt your stdcall version with modifyed AbsorbNibble proc .
NOW OUTPUT IS:
Quote1a fa 8b 5e e3 37 db c7 22 59 7f 0f dc 3a 42 c7 75 4b f1 03 6f 54 fb 4a eb 03 35 d4 a4 e9 a3 6e
BEFORE IT WAS :
Quote29 b2 10 fe 7f 2a 6a f4 a4 45 a0 1d a0 d7 bd 50 f2 49 b3 88 a0 a8 b7 40 6b 4a 84 9f 95 88 eb 6d

lorenzo

Yep, initially posted wrong bytes too..


Spritz Hash = ff 8c f2 68 09 4c 87 b9 5f 74 ce 6f ee 9d 30 03 a5 f9 fe 69 44 65 3c d5 0e 66 bf 18 9c 63 f6 99
Spritz Stream = 1a fa 8b 5e e3 37 db c7 22 59 7f 0f dc 3a 42 c7 75 4b f1 03 6f 54 fb 4a eb 03 35 d4 a4 e9 a3 6e


This is hash function using stdcall


Hash proc h:dword, len:dword, M:dword, r:dword
    local state:STATE
   
    pushad
    ; InitializeState()
    lea    esi, [state]
    push   esi
    call   InitializeState
    ; Absorb(M); AbsorbStop()
    push   dword ptr[r]
    push   dword ptr[M]
    push   esi               ; r, edx = M
    call   Absorb
    push   esi
    call   AbsorbStop
    ; Absorb(r)
    push   sizeof byte
    lea    eax, [len]
    push   eax
    push   esi
    call   Absorb
    ; return Squeeze(r)
    push   dword ptr[len]
    push   dword ptr[h]
    push   esi               ; r
    call   Squeeze
    popad
    ret
Hash endp


Overall, the comments/code needs cleaning up.

Adamanteus

Won't wait :
Spritz Hash = ff 8c f2 68 09 4c 87 b9 5f 74 ce 6f ee 9d 30 03 a5 f9 fe 69 44 65 3c d5 0e 66 bf 18 9c 63 f6 99
Spritz Stream = 1a fa 8b 5e e3 37 db c7 22 59 7f 0f dc 3a 42 c7 75 4b f1 03 6f 54 fb 4a eb 03 35 d4 a4 e9 a3 6e

- because for such work is need to pay  :badgrin:

But comments is free : Lorenzo with RSA have made really great work, and my opinion that is really best for beginner - from such to begin, stayed catch bugs as sub esp/add esp - not disturb ebp, and so on. But that's for security reasons is need to do for myself, as it save critical code for cipher programs  :eusa_boohoo: Also, I could remark, that RSA developers, noticed needs for such type of algos, in programs passwords - that is in nonce parameter, as key is user password - or opposite   :eusa_dance:

lorenzo

True, I haven't finished this completely yet. Below is last bit I wrote with some test code in C.
It's not work, I just wanted to implement it.   :icon_cool:



; *********************************************************
; Spritz - a spongy RC4-like stream cipher and hash function
; Designed by Ron Rivest and Jacob Schuldt
; *********************************************************
;
; 369 bytes

.686
.model flat

option epilogue:none
option prologue:none

N equ 256

STATE struct
    a byte ?
    i byte ?
    j byte ?
    k byte ?
    z byte ?
    w byte ?
    s byte N dup (?)
STATE ends

.code
   
; *********************************************************
    public _spritz_init
_spritz_init:
spritz_init proc fastcall state:dword
    pushad
    mov    edi, state
    xor    eax, eax
    stosb                  ; a = 0
    stosb                  ; i = 0
    stosb                  ; j = 0
    stosb                  ; k = 0
    stosb                  ; z = 0
    inc    eax
    stosb                  ; w = 1
    dec    eax
; for v = 0 to N - 1
;   s[v] = v
init_sbox:                 ; fixed for 256 bytes
    stosb
    inc    al
    jnz    init_sbox
    popad
    ret
spritz_init endp
spritz_initLen equ $ - spritz_init

; *********************************************************
    public _spritz_update
_spritz_update:
spritz_update proc fastcall state:dword
    pushad
    movzx  eax, byte ptr[state][STATE.i]
    movzx  ebx, byte ptr[state][STATE.j]
    mov    dh, [state][STATE.k]
       
    ; i = i + w
    add    al, [state][STATE.w]
   
    ; j = k + S[j + S[i]]
    mov    dl, [state][STATE.s][eax]
    add    bl, dl
    mov    bl, [state][STATE.s][ebx]
    add    bl, dh
   
    ; Swap(S[i], S[j])
    xchg   dl, [state][STATE.s][ebx]
    mov    [state][STATE.s][eax], dl
   
    ; k = i + k + S[j]
    add    dl, al
    add    dl, dh
   
    mov    [state][STATE.i], al
    mov    [state][STATE.j], bl
    mov    [state][STATE.k], dl
    popad
    ret
spritz_update endp
spritz_updateLen equ $ - spritz_update

; *********************************************************
    public _spritz_whip
_spritz_whip:
spritz_whip proc fastcall state:dword
    pushad
    ; for v = 0 to r - 1
    ;   spritz_update()
    xor    ebx, ebx
whip_loop:
    call   spritz_update
    inc    ebx
    cmp    ebx, 2*N               ; 2N
    jnz    whip_loop
    ; w = w + 1
    add    [state][STATE.w], 2    ; when N is power of 2
    popad
    ret
spritz_whip endp
spritz_whipLen equ $ - spritz_whip

; *********************************************************
    public _spritz_crush
_spritz_crush:
spritz_crush proc fastcall state:dword
    pushad
    ; for v = 0 to [N/2] - 1
    xor    esi, esi       
crush_loop:
    ; if S[v] > S[N - 1 - v]
    mov    edx, N - 1
    sub    edx, esi
    mov    al, [state][STATE.s][esi]  ; s[v]
    mov    bl, [state][STATE.s][edx]  ; s[N - 1 - v]
   
    .if al > bl
      ; Swap (S[v], S[N - 1 - v])
      mov  [state][STATE.s][edx], al
      mov  [state][STATE.s][esi], bl
    .endif
   
    inc    esi
    cmp    esi, N/2
    jnz    crush_loop
   
    popad
    ret
spritz_crush endp
spritz_crushLen equ $ - spritz_crush

; *********************************************************
    public _spritz_shuffle
_spritz_shuffle:
spritz_shuffle proc fastcall state:dword
    call   spritz_whip
    call   spritz_crush
   
    call   spritz_whip
    call   spritz_crush
   
    call   spritz_whip
   
    ; a = 0
    and    [state][STATE.a], 0
    ret
spritz_shuffle endp
spritz_shuffleLen equ $ - spritz_shuffle

; *********************************************************
    public _spritz_shuffle_absorb
_spritz_shuffle_absorb:
spritz_shuffle_absorb proc fastcall state:dword
    .if [state][STATE.a] == N/2
      call  spritz_shuffle
    .endif
    ret
spritz_shuffle_absorb endp
spritz_shuffle_absorbLen equ $ - spritz_shuffle_absorb

; *********************************************************
spritz_absorb_nibble proc fastcall state:dword, x:dword
    pushad
    call   spritz_shuffle_absorb
    ; Swap(S[a], S[[N/2] + x])
    xor    eax, eax
    add    dl, N/2
    mov    al, [state][STATE.s][edx]
    mov    bl, [state][STATE.a]            ; ebx = a
    xchg   al, [state][STATE.s][ebx]
    mov    [state][STATE.s][edx], al
    ; a = a + 1
    inc    [state][STATE.a]
    popad
    ret
spritz_absorb_nibble endp
spritz_absorb_nibbleLen equ $ - spritz_absorb_nibble

; *********************************************************
    public _spritz_absorb_byte
_spritz_absorb_byte:
spritz_absorb_byte proc fastcall state:dword, b:dword
    pushad
    mov    ebx, b
    ; spritz_absorb_nibble (LOW (b))
    and    edx, 15
    call   spritz_absorb_nibble
    ; spritz_absorb_nibble (HIGH (b))
    mov    edx, ebx
    shr    edx, 4
    call   spritz_absorb_nibble
   
    popad
    ret
spritz_absorb_byte endp
spritz_absorb_byteLen equ $ - spritz_absorb_byte

; *********************************************************
    public _spritz_absorb
_spritz_absorb:
spritz_absorb proc fastcall state:dword, I:dword, I_len:dword
    pushad
    ; for v = 0 to I.length - 1
    xor    ebx, ebx
    mov    esi, I
absorb_loop:
    ; spritz_absorb_byte(I[v])
    movzx  edx, byte ptr[esi+ebx] ; I[v]
    call   spritz_absorb_byte
    inc    ebx
    cmp    ebx, [esp+32+4]  ; v < I_len
    jnz    absorb_loop
    popad
    retn   4
spritz_absorb endp
spritz_absorbLen equ $ - spritz_absorb

; *********************************************************
    public _spritz_absorb_stop
_spritz_absorb_stop:
spritz_absorb_stop proc fastcall state:dword
    call spritz_shuffle_absorb
    ; a = a + 1
    inc    [state][STATE.a]
    ret
spritz_absorb_stop endp
spritz_absorb_stopLen equ $ - spritz_absorb_stop

; *********************************************************
    public _spritz_absorb_and_stop
_spritz_absorb_and_stop:
spritz_absorb_and_stop proc fastcall state:dword, I:dword, I_len:dword
    push   [esp+4]
    call   spritz_absorb
    call   spritz_absorb_stop
    ret
spritz_absorb_and_stop endp
spritz_absorb_and_stopLen equ $ - spritz_absorb_and_stop

; *********************************************************
    public _spritz_shuffle_squeeze
_spritz_shuffle_squeeze:
spritz_shuffle_squeeze proc fastcall state:dword
    .if [state][STATE.a] > 0
      call  spritz_shuffle
    .endif
    ret
spritz_shuffle_squeeze endp
spritz_shuffle_squeezeLen equ $ - spritz_shuffle_squeeze

; *********************************************************
; z = S[j + S[i + S[z + k]]]
; return z;
    public _spritz_output
_spritz_output:
spritz_output proc fastcall state:dword
    pushad
    movzx   eax, byte ptr[state][STATE.i]
    movzx   ebx, byte ptr[state][STATE.j]
    movzx   edx, byte ptr[state][STATE.z]
    ; z + k
    add     dl, [state][STATE.k]
    ; i + s[z + k]
    add     al, [state][STATE.s][edx]
    ; j + s[i + s[z + k]]
    add     bl, [state][STATE.s][eax]
    ; z = s[j]
    mov     al, [state][STATE.s][ebx]
    mov     [state][STATE.z], al
    ; return z
    mov     [esp+1ch], eax
    popad
    ret
spritz_output endp
spritz_outputLen equ $ - spritz_output

; *********************************************************
    public _spritz_drip_nosqueeze
_spritz_drip_nosqueeze:
spritz_drip_nosqueeze proc fastcall state:dword
    call   spritz_update
    call   spritz_output
    ret
spritz_drip_nosqueeze endp
spritz_drip_nosqueezeLen equ $ - spritz_drip_nosqueeze

; *********************************************************
    public _spritz_squeeze
_spritz_squeeze:
spritz_squeeze proc fastcall state:dword, P:dword, P_len:dword
    pushad
    call   spritz_shuffle_squeeze
   
    ; for v = 0 to r - 1
    mov    ebx, [esp+32+4] ; r
    mov    edi, edx        ; p
squeeze_loop:
    call   spritz_drip_nosqueeze
    ; P[v] = spritz_drip_nosqueeze()
    stosb
    dec    ebx   
    jnz    squeeze_loop
   
    ; return P
    popad
    retn   4
spritz_squeeze endp
spritz_squeezeLen equ $ - spritz_squeeze

; *********************************************************
    public _spritz_drip
_spritz_drip:
spritz_drip proc fastcall state:dword
    call spritz_shuffle_squeeze
    call spritz_drip_nosqueeze
    ret
spritz_drip endp
spritz_dripLen equ $ - spritz_drip

    end
   





#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#include <windows.h>

char *keys[] = { "ABC", "spam", "arcfour" };

void dump_bytes (uint8_t b[], size_t len)
{
    for (size_t i=0; i<len; i++)
    {
        printf ("%02x ", b[i]);
    }
    putchar ('\n');
}

enum {
  spritz_N = 256
};

typedef struct
{
  uint8_t a, i, j, k, z, w;
  uint8_t S[spritz_N];
} spritz_state;

/*******************************************************************************/
/* the spritz primitives */
extern "C" void    __fastcall spritz_init            (spritz_state *s);
extern "C" void    __fastcall spritz_shuffle_squeeze (spritz_state *s);
extern "C" void    __fastcall spritz_update          (spritz_state *s);
extern "C" void    __fastcall spritz_whip            (spritz_state *s, uint_fast16_t r);
extern "C" void    __fastcall spritz_crush           (spritz_state *s);
extern "C" void    __fastcall spritz_shuffle         (spritz_state *s);
extern "C" void    __fastcall spritz_absorb          (spritz_state *s, const void *I, size_t I_len);
extern "C" void    __fastcall spritz_absorb_byte     (spritz_state *s, uint32_t b);
extern "C" void    __fastcall spritz_absorb_stop     (spritz_state *s);
extern "C" void    __fastcall spritz_absorb_and_stop (spritz_state *s, const void *I, size_t I_len); /* commonly used helper function */
extern "C" uint8_t __fastcall spritz_output          (spritz_state *s);
extern "C" void    __fastcall spritz_squeeze         (spritz_state *s, void *P, size_t P_len);
extern "C" uint8_t __fastcall spritz_drip            (spritz_state *s);
extern "C" uint8_t __fastcall spritz_drip_nosqueeze  (spritz_state *s);

/* the spritz hash inline functions */

static void
spritz_hash_init (spritz_state *s)
{
  spritz_init (s);
}

static void
spritz_hash_add (spritz_state *s, const void *M, size_t M_len)
{
  spritz_absorb (s, M, M_len);
}

void
spritz_hash_finish (spritz_state *s, void *H, size_t H_len)
{
  spritz_absorb_stop (s);
  spritz_absorb_byte (s, H_len);
  spritz_squeeze (s, H, H_len);
}

void
spritz_cipher_init (spritz_state *s, const void *K, size_t K_len, const void *IV, size_t IV_len)
{
  spritz_init (s);
  spritz_absorb (s, K, K_len);

  if (IV)
    {
      spritz_absorb_stop (s);
      spritz_absorb (s, IV, IV_len);
    }

  spritz_shuffle_squeeze (s);
}

void
spritz_cipher_encrypt (spritz_state *s, const void *I, void *O, size_t len)
{
  const uint8_t *i = (const uint8_t *)I;
        uint8_t *o = (      uint8_t *)O;

  while (len--)
    *o++ = *i++ + spritz_drip_nosqueeze (s);
}

void
spritz_cipher_decrypt (spritz_state *s, const void *I, void *O, size_t len)
{
  const uint8_t *i = (const uint8_t *)I;
        uint8_t *o = (      uint8_t *)O;

  while (len--)
    *o++ = *i++ - spritz_drip_nosqueeze (s);
}

/*****************************************************************************/

void
spritz_cipher_xor_crypt (spritz_state *s, const void *I, void *O, size_t len)
{
  const uint8_t *i = (const uint8_t *)I;
        uint8_t *o = (      uint8_t *)O;

  while (len--)
    *o++ = *i++ ^ spritz_drip_nosqueeze (s);
}

void dump_hash (void)
{
    uint8_t      out[32];
    spritz_state s;
   
    for (int i=0; i<sizeof(keys) / sizeof(char*); i++)
    {
        memset (out, 0, sizeof (out));
        spritz_hash_init (&s);
        spritz_hash_add (&s, keys[i], strlen(keys[i]));
        spritz_hash_finish (&s, out, sizeof (out));
       
        dump_bytes (out, sizeof (out));
    }
}

int main (int argc, char *argv[])
{
    spritz_state s;
    uint8_t      ct[128]={0};
    uint8_t      ptx[128]={0};
    char         pt[]="The Spritz's recipe is shrouded in mystery";
    const        char key[]="secret password";
    size_t       keylen = strlen (key);
    size_t       ptlen = strlen (pt);
   
    dump_bytes ((uint8_t*)pt, ptlen);
       
    spritz_cipher_init (&s, key, keylen, 0, 0);
    spritz_cipher_encrypt (&s, pt, ct, ptlen);
    dump_bytes (ct, ptlen);
   
    spritz_cipher_init (&s, key, keylen, 0, 0);
    spritz_cipher_decrypt (&s, ct, ptx, ptlen);
    dump_bytes (ptx, ptlen);
   
    return 0;
}


Adamanteus

 That's good deal, but I can say only - that I can't understand this code with comments, so won't help you to implement it - but idea is great, I agree  :icon_eek: