; Password handling in 7-Zip: "7zAES" filter (SHA256 + AES256).
; Ported from C++ sources of 7-Zip (c) Igor Pavlov.
aes7z_decoder:
virtual at 0
.outStream      rb      streamInfo.size
.inStream       dd      ?
.inLen          dd      ?
.inPtr          dd      ?
.bufRest        dd      ?
; key data
.NumCyclesPower dd      ?
.SaltSize       dd      ?
.Salt           rb      16
; AES data
.iv             rb      16
.Key            rb      32
.nr             dd      ?
.KeyExpanded    rb      32*15
.size = $
end virtual

.fillBuf:
        mov     esi, [eax+.inPtr]
        mov     ebp, eax
        add     edi, [eax+.bufRest]
        sub     ecx, [eax+.bufRest]
        js      .rest1
        and     [eax+.bufRest], 0
.mainloop:
        test    ecx, ecx
        jz      .done
        sub     [ebp+.inLen], 16
        js      .refill
.refilled:
        push    esi edi ecx
        mov     ebx, edi
        lea     edi, [ebp+.nr]
        call    aes_decode
        pop     ecx edi esi
        mov     eax, dword [ebp+.iv]
        xor     [edi], eax
        lodsd
        mov     dword [ebp+.iv], eax
        mov     eax, dword [ebp+.iv+4]
        xor     [edi+4], eax
        lodsd
        mov     dword [ebp+.iv+4], eax
        mov     eax, dword [ebp+.iv+8]
        xor     [edi+8], eax
        lodsd
        mov     dword [ebp+.iv+8], eax
        mov     eax, dword [ebp+.iv+12]
        xor     [edi+12], eax
        lodsd
        mov     dword [ebp+.iv+12], eax
        add     edi, 16
        sub     ecx, 16
        jns     .mainloop
.rest1:
        neg     ecx
        mov     [ebp+.bufRest], ecx
.done:
        mov     [ebp+.inPtr], esi
        popad
        ret

.refill:
        mov     edx, [ebp+.inLen]
        add     edx, 16
        jnz     .rest
        js      return.err
        mov     eax, [ebp+.inStream]
        call    fillBuf
        mov     edx, [eax+streamInfo.bufDataLen]
        test    edx, edx
        jz      return.err
        mov     esi, [eax+streamInfo.bufPtr]
        mov     [ebp+.inLen], edx
        sub     [ebp+.inLen], 16
        jns     .refilled
.rest:
; ASSERT([eax+streamInfo.fullSize] == 0);
        sub     edx, ecx
        jb      return.err
        add     ecx, edx
        rep     movsb
        mov     [ebp+.bufRest], edx
        jmp     .done

aes7z_get_buf_size:
        mov     eax, aes7z_decoder.size
        mov     edx, 0x4000
        ret

aes7z_init_decoder:
; zero all
        xor     eax, eax
        mov     [ebp+aes7z_decoder.inLen], eax
        mov     [ebp+aes7z_decoder.bufRest], eax
        mov     [ebp+aes7z_decoder.NumCyclesPower], eax
        mov     [ebp+aes7z_decoder.SaltSize], eax
        lea     edi, [ebp+aes7z_decoder.Salt]
        push    8
        pop     ecx
        rep     stosd   ; zero .Salt and .iv
        mov     [ebp+streamInfo.fillBuf], aes7z_decoder.fillBuf
; parse parameters
        cmp     dword [esi-4], eax
        jz      .parok  ; no parameters - OK
        lodsb
        mov     cl, al
        and     al, 0x3F
        mov     byte [ebp+aes7z_decoder.NumCyclesPower], al
        test    cl, 0xC0
        jz      .parok
        test    cl, 0x80
        setnz   byte [ebp+aes7z_decoder.SaltSize]
        shr     cl, 6
        and     ecx, 1
        cmp     dword [esi-1-4], 2
        jb      return.err
        lodsb
        mov     dl, al
        shr     al, 4
        add     byte [ebp+aes7z_decoder.SaltSize], al
        and     edx, 0xF
        add     ecx, edx
        lea     edx, [ecx+2]
        push    ecx
        mov     ecx, [ebp+aes7z_decoder.SaltSize]
        add     edx, ecx
        cmp     dword [esi-2-4], edx
        jb      return.err
        lea     edi, [ebp+aes7z_decoder.Salt]
        rep     movsb
        pop     ecx
        lea     edi, [ebp+aes7z_decoder.iv]
        rep     movsb
.parok:
        test    bl, bl
        jnz     .ret    ; if reinitializing - all calculations have been already done
        call    query_password
        jz      return.clear
;.CalculateDigest:
        mov     cl, byte [ebp+aes7z_decoder.NumCyclesPower]
        cmp     cl, 0x3F
        jnz     .sha
        lea     edi, [ebp+aes7z_decoder.Key]
        mov     ecx, [ebp+aes7z_decoder.SaltSize]
        push    32
        pop     edx
        sub     edx, ecx
        lea     esi, [ebp+aes7z_decoder.Salt]
        rep     movsb
        mov     ecx, [password_size]
        add     ecx, ecx
        cmp     ecx, edx
        jbe     @f
        mov     ecx, edx
@@:
        sub     edx, ecx
        mov     esi, password_unicode
        rep     movsb
        mov     ecx, edx
        xor     eax, eax
        rep     stosb
        jmp     .setkey
.sha:
        cmp     cl, 32
        jb      .normal
        push    1
        shl     dword [esp], cl
        push    0
        jmp     @f
.normal:
        push    0
        push    1
        shl     dword [esp], cl
@@:
        push    0
        push    0
        call    sha256_init
.loop:
        lea     esi, [ebp+aes7z_decoder.Salt]
        mov     edx, [ebp+aes7z_decoder.SaltSize]
        call    sha256_update
        mov     esi, password_unicode
        mov     edx, [password_size]
        add     edx, edx
        call    sha256_update
        mov     esi, esp
        push    8
        pop     edx
        call    sha256_update
        mov     esi, esp
        dec     esi
@@:
        inc     esi
        inc     byte [esi]
        jz      @b
        sub     dword [esp+8], 1
        sbb     dword [esp+12], 0
        mov     eax, [esp+8]
        or      eax, [esp+12]
        jnz     .loop
        lea     edi, [ebp+aes7z_decoder.Key]
        call    sha256_final
        add     esp, 16
.setkey:
        lea     esi, [ebp+aes7z_decoder.Key]
        push    8
        pop     edx     ; 7z uses 256-bit keys
        lea     edi, [ebp+aes7z_decoder.nr]
        call    aes_setkey
.ret:
        ret
