Announcement

Collapse

Forum Guidelines

This forum is for finished source code that is working properly. If you have questions about this or any other source code, please post it in one of the Discussion Forums, not here.
See more
See less

SHA256 for PBCC and PBDLL: hash functions & test bed

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • SHA256 for PBCC and PBDLL: hash functions & test bed

    Code:
    #IF 0
    == SHA256 ==
    Secure 256-bit hashing for PowerBASIC
       
    Code for the following two files appears below:
    SHA256.INC  Hash routines for returning 32-byte SHA256 hashes 
                of buffers and files
    SHA256.BAS  Test bed EXE illustrating buffer and file hashing
    All code compiles with either PBDLL 7.0+ or PBCC 3.0+.
       
       
    Available at the following URL is a PDF file containing the NIST
    specifications for the 256-bit, 384-bit, and 512-bit extensions to 
    the SHA standard:
     http://csrc.nist.gov/cryptval/shs.html 
       
    A hash is considered secure when it possesses the following qualities.
    -- Determining the input string from the hash (i.e., working backward 
       from the hash alone to determine the string which generated it) is not 
       considered feasible.
    -- Given an input string, it is not considered feasible to find another
       string which hashes to the same value.  
    -- It is not considered feasible to find two random strings which hash 
       to the same value.
       
       
    An input string can be of any length and thus far longer than its  
    resulting hash.  This is accomplished through use of a compression 
    function which treats the input as a combination of any previously 
    hashed input and the current input.  Designing this feature is one of 
    the most important challenges when creating a secure hash algorithm.  
       
    Secure hashes have many uses, among them validation of passphrases. 
    In this arrangement, a user enters a passphrase when setting up an 
    account.  The input string is hashed, and the hash is stored (not the 
    passphrase).  When the user seeks readmission, the passphrase entered 
    is hashed, and the hash is compared with the one on record.  If the 
    hashes match, the strings which generated them also must match.  If 
    the collection of stored hashes is compromised, the passphrases 
    remain unknown. 
       
    Secure hashes are not designed for speed.  The implementation below  
    relies on assembly language to improve speed in its most heavily 
    traveled section of code, but unless security is required, a secure 
    hash is a poor choice when compared with the many simpler, far more 
    efficient hash algorithms in widespread use. 
       
    Greg Turgeon
    1/10/2001
       
    Revised 4/2006
    -- Static variables no longer used.
    -- Minor revisions for efficiency and to accomodate later compiler 
       releases.
       
    gturgeon at ssge dot net
       
    #ENDIF
       
    '=====================================================================
    '-- SHA256.INC
    '-- WIN32 API not required
    '-- Uses no global data
    '=====================================================================
       
    TYPE tSHA_STATE
       state0   AS DWORD
       state1   AS DWORD
       state2   AS DWORD
       state3   AS DWORD
       state4   AS DWORD
       state5   AS DWORD
       state6   AS DWORD
       state7   AS DWORD
       pbuf     AS BYTE PTR
       buflen   AS LONG
       pstate   AS DWORD PTR
       k_array  AS LONG PTR
    END TYPE
    %PBUF_OFFSET      = 32
       
    %BLOCKSIZE        = 64     'bytes
    %FILE_BUFFERSIZE  = 32000  'bytes
       
    DECLARE SUB SHA_Buffer(BYVAL pBuffer AS BYTE PTR, BYVAL Length&, BYVAL Hash AS DWORD PTR)
    DECLARE FUNCTION SHA_File&(File_Name$, Hash$)
    DECLARE SUB SHA_Init(BYVAL pSHA_STATE AS tSHA_STATE PTR)
    DECLARE SUB SHA_Compress(BYVAL pSHA_State AS tSHA_STATE PTR)
    DECLARE FUNCTION MakePadding$(BYVAL TotalBytes&)
       
       
    '====================
    SUB SHA_Init(BYVAL pSHA_STATE AS tSHA_STATE PTR)
    LOCAL p&
       
    @pSHA_STATE.k_array = codeptr(K_Array_Data)
    p& = codeptr(Init_Values)
    @pSHA_STATE.pstate = pSHA_STATE
    !  push  esi
    !  push  edi
    !  mov   esi, p&
    !  mov   edi, pSHA_STATE
    !  mov   ecx, 8
    !  cld
    !  rep   movsd
    !  pop   edi
    !  pop   esi
    exit sub
    '============
    Init_Values:
    ! DD  &h6A09E667???, &hBB67AE85???, &h3C6EF372???, &hA54FF53A???
    ! DD  &h510E527F???, &h9B05688C???, &h1F83D9AB???, &h5BE0CD19???
    K_Array_Data:
    ! DD  &h428a2f98???, &h71374491???, &hb5c0fbcf???, &he9b5dba5???, &h3956c25b???, &h59f111f1???
    ! DD  &h923f82a4???, &hab1c5ed5???, &hd807aa98???, &h12835b01???, &h243185be???, &h550c7dc3???
    ! DD  &h72be5d74???, &h80deb1fe???, &h9bdc06a7???, &hc19bf174???, &he49b69c1???, &hefbe4786???
    ! DD  &h0fc19dc6???, &h240ca1cc???, &h2de92c6f???, &h4a7484aa???, &h5cb0a9dc???, &h76f988da???
    ! DD  &h983e5152???, &ha831c66d???, &hb00327c8???, &hbf597fc7???, &hc6e00bf3???, &hd5a79147???
    ! DD  &h06ca6351???, &h14292967???, &h27b70a85???, &h2e1b2138???, &h4d2c6dfc???, &h53380d13???
    ! DD  &h650a7354???, &h766a0abb???, &h81c2c92e???, &h92722c85???, &ha2bfe8a1???, &ha81a664b???
    ! DD  &hc24b8b70???, &hc76c51a3???, &hd192e819???, &hd6990624???, &hf40e3585???, &h106aa070???
    ! DD  &h19a4c116???, &h1e376c08???, &h2748774c???, &h34b0bcb5???, &h391c0cb3???, &h4ed8aa4a???
    ! DD  &h5b9cca4f???, &h682e6ff3???, &h748f82ee???, &h78a5636f???, &h84c87814???, &h8cc70208???
    ! DD  &h90befffa???, &ha4506ceb???, &hbef9a3f7???, &hc67178f2???
    END SUB
       
       
    '====================
    SUB SHA_Compress(BYVAL pST AS tSHA_STATE PTR)
    LOCAL s_array&(), w_array&()
    LOCAL s AS LONG PTR, w AS LONG PTR
    LOCAL pk_array&, t0&, t1&, wi&, ki&, i&
       
    redim w_array&(63) : w = varptr(w_array&(0))
    redim s_array&(7)  : s = varptr(s_array&(0))
       
    '-- Copy for inline assembler
    pk_array& = @pST.k_array
       
    !  push  esi
    !  push  edi
    !  push  ebx
       
    '-- Copy current state into s&()
    !  mov   esi, pST           ;esi -> pST.state0
    !  mov   edi, s
    !  push  esi
    !  mov   ecx, 8
    !  cld
    !  rep   movsd
       
    '-- Copy current data block to w&() w/little-to-big endian conversion
    !  pop   esi               ;esi -> st.state0
    !  add   esi, %PBUF_OFFSET ;esi -> st.buf
    !  mov   edi, w
    !  mov   eax, [esi]
    !  mov   esi, eax
    !  mov   ecx, 16*4
    SwapCopyTop:
    !  sub   ecx, 4
    !  mov   eax, [esi+ecx]
    !  bswap eax
    !  mov   [edi+ecx], eax
    !  test  ecx, ecx
    !  jnz   SwapCopyTop
       
    '-- Fill W[16to63]
    ! mov   esi, w
    ! push  ebp
    ! mov   ebp, 16
    FillTop:
    !  mov   ebx, ebp
    !  sub   ebx, 2
    !  mov   eax, [esi]+[ebx*4]
    !  mov   ecx, eax
    !  mov   edx, ecx
    !  sub   ebx, 13           ;prep for next access: (-2)+(-13) = -15
    !  ror   eax, 17
    !  ror   ecx, 19
    !  shr   edx, 10
    !  xor   eax, ecx
    !  xor   eax, edx
    !  mov   edi, eax          ;edi = temp total
    !  mov   eax, [esi]+[ebx*4]
    !  mov   ecx, eax
    !  mov   edx, ecx
    !  ror   eax, 7
    !  ror   ecx, 18
    !  shr   edx, 3
    !  add   ebx, 8            ;= (-15)+8 = -7
    !  xor   eax, ecx
    !  xor   eax, edx
    !  add   edi, eax
       
    !  mov   eax, [esi]+[ebx*4]
    !  sub   ebx, 9            ;= (-7)+(-9) = -16
    !  add   edi, eax
       
    !  mov   eax, [esi]+[ebx*4]
    !  mov   ebx, ebp
    !  add   eax, edi
       
    !  inc   ebp
    !  mov   [esi]+[ebx*4], eax
    !  cmp   ebp, 63
    !  jg    FillDone
    !  jmp   FillTop
    FillDone:
    !  pop   ebp
       
    '-- Compress: i& = 0 to 63
    !  xor   eax, eax
    !  mov   esi, s            ;here to CompressDone & END SUB: esi -> s[0]
    !  mov   i&, eax
    CompressTop:
    !  mov   ebx, eax
    !  mov   edx, w
    !  mov   eax, [edx]+[ebx*4]
    !  mov   wi&, eax
    !  mov   edx, pk_array&
    !  mov   eax, [edx]+[ebx*4]
    !  mov   ki&, eax
       
    !  mov   eax, [esi+16]
    !  mov   ecx, [esi+20]
    !  mov   ebx, eax
    !  mov   edx, eax
    !  and   eax, [esi+20]
       
    !  not   edx
    !  and   edx, [esi+24]
    !  xor   eax, edx
       
    !  mov   ecx, ebx
    !  mov   ebx, eax
    !  mov   eax, ecx
       
    !  mov   edx, ecx
    !  ror   eax, 6
    !  ror   ecx, 11
    !  ror   edx, 25
       
    !  add   ebx, ki&
    !  xor   eax, ecx
    !  add   ebx, wi&
    !  xor   eax, edx
       
    !  add   eax, ebx
    !  add   eax, [esi+28]
       
    !  mov   t0&, eax
       
    !  mov   eax, [esi]
    !  mov   ecx, [esi+4]
    !  mov   ebx, eax
       
    !  mov   edx, eax
    !  and   eax, ecx
       
    !  and   edx, [esi+8]
    !  xor   eax, edx
       
    !  and   ecx, [esi+8]
    !  xor   eax, ecx
    !  mov   edx, eax
       
    !  mov   eax, ebx
    !  mov   ecx, ebx
    !  ror   eax, 2
    !  ror   ecx, 13
    !  ror   ebx, 22
    !  xor   eax, ecx
    !  xor   eax, ebx
       
    !  add   eax, edx
    !  mov   t1&, eax
       
    '  @s[7] = @s[6] : @s[6] = @s[5] : @s[5] = @s[4] : @s[4] = @s[3] + t0&
    '  @s[3] = @s[2] : @s[2] = @s[1] : @s[1] = @s[0] : @s[0] = t0& + t1&
    !  mov   edi, esi
    !  mov   ecx, t0&
       
    !  mov   eax, [esi+24]     ;@s[7] = @s[6]
    !  mov   ebx, [esi+20]     ;@s[6] = @s[5]
    !  mov   edx, [esi+16]     ;@s[5] = @s[4]
    !  mov   [edi+28], eax
    !  mov   [edi+24], ebx
    !  mov   [edi+20], edx
       
    !  mov   eax, [esi+12]     ;@s[4] = @s[3] + T0&
    !  mov   ebx, [esi+8]      ;@s[3] = @s[2]
    !  add   eax, ecx
    !  mov   edx, [esi]        ;@s[1] = @s[0]
    !  mov   [edi+12], ebx
    !  mov   [edi+16], eax
    !  mov   ebx, ecx          ;@s[0] = T0& + T1&
    !  mov   eax, [esi+4]      ;@s[2] = @s[1]
    !  mov   [edi+4], edx
    !  add   ebx, t1&
    !  mov   [edi+8], eax
    !  mov   [edi], ebx
       
    !  mov   eax, i&
    !  inc   eax
    !  cmp   eax, 63
    !  jg    CompressDone
    !  mov   i&, eax
    !  jmp   CompressTop
    CompressDone:
       
    '-- Add current state s() to context
    '  for i& = 0 to 7 : @pST.@pstate[i&] = @pST.@pstate[i&] + @s[i&] : next i&
    !  mov   edi, pST
    !  mov   ecx, 8*4
    AddCopyTop:
    !  sub   ecx, 4
    !  mov   eax, [edi+ecx]
    !  add   eax, [esi+ecx]
    !  mov   [edi+ecx], eax
    !  test  ecx, ecx
    !  jnz   AddCopyTop
       
    !  pop   ebx
    !  pop   edi
    !  pop   esi
    END SUB
       
       
    '====================
    SUB SHA_Buffer(BYVAL DataBuffer AS BYTE PTR, BYVAL Length&, BYVAL Hash AS DWORD PTR) EXPORT
    '-- Expects parameter Hash to point to buffer of correct size of 32 bytes (256bits \ 8)
    REGISTER i&
    LOCAL lastbuff$
    LOCAL st AS tSHA_STATE, p&
       
    i& = Length& AND (%BLOCKSIZE-1)
    lastbuff$ = peek$((DataBuffer + Length&) - i&, i&)
    lastbuff$ = lastbuff$ + MakePadding$(Length&)
       
    SHA_Init byval varptr(st)
       
    st.buflen = Length&
    st.pbuf = DataBuffer
       
    i& = Length& AND (NOT %BLOCKSIZE-1)
    do while i&
       SHA_Compress byval varptr(st)
       st.pbuf = st.pbuf + %BLOCKSIZE
       i& = i& - %BLOCKSIZE
    loop
       
    st.buflen = len(lastbuff$)
    st.pbuf = strptr(lastbuff$)
       
    do while st.buflen
       SHA_Compress byval varptr(st)
       st.pbuf = st.pbuf + %BLOCKSIZE
       st.buflen = st.buflen - %BLOCKSIZE
    loop
       
    '-- Copy current state (as dwords) from s&() to Hash
    '   for i& = 0 to 7 : @Hash[i&] = st.@pstate[i&] : next i&
    p& = st.pstate
    !  push  esi
    !  push  edi
    !  mov   esi, p&           ;esi -> st.state0
    !  mov   edi, Hash
    !  mov   ecx, 8
    !  cld
    !  rep   movsd
    !  pop   edi
    !  pop   esi
    END SUB
       
       
    '====================
    FUNCTION SHA_File&(File_Name$, Hash$) EXPORT
    '-- Returns 0 on success, or PB (not OS) error code
    REGISTER i&, bytesleft&
    LOCAL buffer$, padding$
    LOCAL st AS tSHA_STATE, phash AS DWORD PTR
    LOCAL infile&, ecode&, lastpass&, maxstring&
       
    '-- If file not found, return PB error code
    if len(dir$(File_Name$)) = 0 then
       function = 53 : exit function
    end if
       
    buffer$ = string$(%FILE_BUFFERSIZE, 0)
    maxstring& = %FILE_BUFFERSIZE
       
    st.buflen = %BLOCKSIZE
    SHA_Init byval varptr(st)
       
    infile& = freefile
    open File_Name$ for binary lock shared as infile& base=0
    if err then goto SHA_File_Error
    bytesleft& = lof(infile&)
    padding$ = MakePadding$(bytesleft&)
       
    do
       'Resize if necessary & flag final buffer
       if bytesleft& =< maxstring& then
          maxstring& = bytesleft&
          buffer$ = string$(maxstring&, 0)
          incr lastpass&
       end if
       get infile&,, buffer$ : if err then goto SHA_File_Error
       if lastpass& then buffer$ = buffer$ + padding$
       st.pbuf = strptr(buffer$)
       for i& = 1 to (len(buffer$) \ %BLOCKSIZE)
          SHA_Compress byval varptr(st)
          st.pbuf = st.pbuf + %BLOCKSIZE
       next i&
       bytesleft& = bytesleft& - maxstring&
    loop until lastpass&
    close infile& : if err then goto SHA_File_Error
       
    '-- Copy current state (as dwords) from s&() to Hash$
    'for i& = 0 to 7 : @Hash[i&] = st.@pstate[i&] : next i&
    Hash$ = string$(32,0)
    phash = strptr(Hash$)
    lastpass& = st.pstate
    !  push  esi
    !  push  edi
    !  mov   esi, lastpass&           ;esi -> st.state0
    !  mov   edi, phash
    !  mov   ecx, 8
    !  cld
    !  rep   movsd
    !  pop   edi
    !  pop   esi
       
    Exit_SHA_File:
    function = ecode&
    exit function
       
    '============
    SHA_File_Error:
    if err then 
       ecode& = errclear
    else
       ecode& = -1
    end if
    resume Exit_SHA_File
    END FUNCTION
       
       
    '=========================
    FUNCTION MakePadding$(BYVAL TotalBytes&)
    '-- Creates the necessary string to append to buffer being hashed
    LOCAL buffbits&&, padding$
    LOCAL pbyte1 AS BYTE PTR, pbyte2 AS BYTE PTR, padbytes&, i&
       
    buffbits&& = TotalBytes& * 8
    padding$ = string$(8,0)
    pbyte1 = strptr(padding$) : pbyte2 = varptr(buffbits&&)
       
    '-- Copy bytes in reverse
    for i& = 0 to 7
       @pbyte1[i&] = @pbyte2[7 - i&]
    next i&
       
    padbytes& = %BLOCKSIZE - ((TotalBytes&+9) AND (%BLOCKSIZE-1))
    function = chr$(&h80) + string$(padbytes&,0) + padding$
    END FUNCTION
    '-- END SHA256.INC ---------------------------------------------------      
       
          
    '=====================================================================
    '-- SHA256.BAS
    '-- Test bed for SHA256.INC
    '-- Compiles with either PBDLL or PBCC 
    '=====================================================================
    #COMPILE EXE
    #DIM ALL
    #REGISTER NONE
       
    '============
    #INCLUDE "WIN32API.INC"
    #INCLUDE "SHA256.INC"
       
    DECLARE SUB VerifyImplementation()
    DECLARE FUNCTION ShowHash&(ShouldBe$, Hash$)
    DECLARE FUNCTION ShowText&(T$)
       
    '====================
    FUNCTION PBMain&()
    REGISTER i&
    LOCAL file_name$, t$, hash$, ecode&
    LOCAL pdword AS DWORD PTR
       
    #IF %DEF(%PB_CC32)
    LOCAL launched&
    if (cursory = 1) and (cursorx = 1) then launched& = -1
    #ENDIF
       
    call VerifyImplementation
       
    file_name$ = "c:\autoexec.bat"
    ecode& = SHA_File&(file_name$, hash$)
       
    if ecode& then 
       ShowText file_name$ + $CR + "Error: " + str$(ecode&)
    else
       pdword = strptr(hash$)
       t$ = file_name$ + $CR
       for i& = 0 to 7
          t$ = t$ + hex$(@pdword[i&], 8) + " "
       next i&
       t$ = rtrim$(t$," ") + $CR
       ShowText t$
    end if
       
    function = ecode&
    '============
    ExitPBMain:
    #IF %DEF(%PB_CC32)
    if launched& then
       input flush
       mouse 1, down : mouse on
       print "Click or press any key to end";
       waitkey$
    end if
    #ENDIF
    END FUNCTION
       
       
    '====================
    SUB VerifyImplementation()
    LOCAL buffer$, sha$, shouldbe$
    gosub TestVectors1
    gosub TestVectors2
    gosub TestVectors3
    gosub TestVectors4
    exit sub
       
    '============
    TestVectors1:
    buffer$ = "abc" 
    sha$ = string$(32,0)
    SHA_Buffer byval strptr(buffer$), len(buffer$), byval strptr(sha$)
    shouldbe$ = "BA7816BF 8F01CFEA 414140DE 5DAE2223 B00361A3 96177A9C B410FF61 F20015AD"
    ShowHash shouldbe$, sha$
    RETURN
       
    '============
    TestVectors2:
    buffer$ = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
    sha$ = string$(32,0)
    SHA_Buffer byval strptr(buffer$), len(buffer$), byval strptr(sha$)
    shouldbe$ = "248D6A61 D20638B8 E5C02693 0C3E6039 A33CE459 64FF2167 F6ECEDD4 19DB06C1"
    ShowHash shouldbe$, sha$
    RETURN
       
    '============
    TestVectors3:
    buffer$ = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhij"
    buffer$ = buffer$ + "klmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"
    sha$ = string$(32,0)
    SHA_Buffer byval strptr(buffer$), len(buffer$), byval strptr(sha$)
    shouldbe$ = "CF5B16A7 78AF8380 036CE59E 7B049237 0B249B11 E8F07A51 AFAC4503 7AFEE9D1"
    ShowHash shouldbe$, sha$
    RETURN
       
    '============
    TestVectors4:
    buffer$ = string$(1000000,"a")
    sha$ = string$(32,0)
    SHA_Buffer byval strptr(buffer$), len(buffer$), byval strptr(sha$)
    shouldbe$ = "CDC76E5C 9914FB92 81A1C7E2 84D73E67 F1809A48 A497200E 046D39CC C7112CD0"
    ShowHash shouldbe$, sha$
    RETURN
    END SUB
       
       
    '====================
    FUNCTION ShowHash&(ShouldBe$, Hash$)
    REGISTER i&
    LOCAL t$, pdword AS DWORD PTR
    t$ = "Should be:" + $CR + shouldbe$ + $CR
    pdword = strptr(Hash$)
    t$ = t$ + "Actual: " + $CR
    for i& = 0 to 7
       t$ = t$ +  hex$(@pdword[i&], 8) + " "
    next i&
    ShowText t$
    END FUNCTION
       
       
    '====================
    FUNCTION ShowText&(T$)
    '-- Text display for PBCC and PBDLL
    '-- Note: Destroys T$ under PBCC
       
    #IF %DEF(%PB_CC32)
    if instr(T$, $CR) = 0 then
       print T$
    else
       LOCAL tt$
       '-- Be sure of trailing $CR
       T$ = rtrim$(T$, $CR) + $CR
       do while len(T$)
          tt$ = extract$(T$, $CR)
          print tt$
          T$ = remain$(T$, $CR)
       loop
    end if
       
    #ELSEIF %DEF(%PB_DLL32)
    msgbox T$
    #ENDIF
    END FUNCTION
    '-- END SHA256.BAS ---------------------------------------------------

    [This message has been edited by Greg Turgeon (edited April 06, 2006).]
Working...
X