Page 1 of 1

SHA256/224 for OS X

Posted: Tue Aug 30, 2011 6:40 pm
by wilbert
The code I post here, is adapted from the code from Netmaestro in the Tips 'n' Tricks forum ( http://www.purebasic.fr/english/viewtop ... 12&t=47120 )
It works around a few bugs the OS X version of PureBasic has. The functionality is the same as the original with the difference that the callback procedure has to be a ProcedureC instead of a Procedure.

Code: Select all

;================================================================
;
; Library Commands:         Sha256FingerPrint()
;                           Sha256FileFingerPrint()
;                           Sha224FingerPrint()
;                           Sha224FileFingerPrint()
;
; Author:                   Lloyd Gallant (netmaestro)
;                           adapted for OS X by Wilbert
;
; Contributors:             Thanks to Danilo and kenmo for 
;                           help with the ROTR and SHR macros,
;                           idle and wilbert for their help 
;                           with asm routines.
;                           and to Christopher Devine for the
;                           c code this program is based on.
; 
; Date:                     August 7, 2011
; Target Compiler:          Purebasic 4 and up
; Target OS:                Windows, Linux, MacOS
; 
; License:                  GNU General Public License
;
; This program is 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; either version 2 of the License, or
; (at your option) any later version.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY Or FITNESS For A PARTICULAR PURPOSE.  See the
; GNU General Public License For more details.
;
; The logic for this program is based on sha256.c found here:
;
; http://www.spale.com/download/scrypt/scrypt1.0/
;
; You can test the accuracy of this program by generating 
; test data at:
;
; http://hashgenerator.de/
;
;================================================================
;
; 
; Usage: 
;
; result$ = SHA256Fingerprint(*address, length, [ ,*progress ] )
; result$ = SHA256FileFingerprint(file$, [ ,*progress ] )
;
; result$ = SHA224Fingerprint(*address, length, [ ,*progress ] )
; result$ = SHA224FileFingerprint(file$, [ ,*progress ] )
;
; Progress callback function:
;
; ProcedureC MyCallBack(value.i)
;   ; value is 0 to 100 representing percentage completed
; Endprocedure 
;
;
;================================================================
;                      STRUCTURE                    
;================================================================

Structure sha256_context
  state.l [8]     ; offset 0
  buffer.a [64]   ; offset 32
  padding.a [64]  ; offset 96
  processed.q     ; offset 160
  jobsize.q       ; offset 168
  digestlen.l     ; offset 176
  callback.l      ; offset 180
EndStructure

;================================================================
;                      HELPER MACROS                     
;================================================================

Macro S256_R_(t)
  !mov ebx, [ebp+(4*t)]
  !bswap ebx
  !mov [esp+(4*t)], ebx  
EndMacro

Macro S256_R(t)
  !mov ebx, [esp+(4*t)-64]     
  !mov eax, [esp+(4*t)-60]
  !add ebx, [esp+(4*t)-28]     
  !mov ecx, eax
  !mov edx, eax
  !ror eax, 7
  !ror ecx, 18
  !shr edx, 3
  !xor eax, ecx
  !xor eax, edx
  !add ebx, eax
  !mov eax, [esp+(4*t)-8]
  !mov ecx, eax
  !mov edx, eax
  !ror eax, 17
  !ror ecx, 19
  !shr edx, 10
  !xor eax, ecx
  !xor eax, edx
  !add ebx, eax
  !mov [esp+(4*t)], ebx
EndMacro

Macro S256_P(a,b,c,d,e,f,g,h,x,K)

  !movd edx, e
  !movd eax, h  ;   h = h + Sigma3(e) + F1(e,f,g) + K + x
  !add ebx, eax 
  !add ebx, k
  
  !mov eax, edx ; Sigma 3
  !mov ecx, eax
  !ror eax, 6
  !ror ecx, 11
  !xor eax, ecx
  !ror ecx, 14
  !xor eax, ecx
  !add ebx, eax
  
  !movq h, f; F1
  !pxor h, g
  !pand h, e
  !pxor h, g
  
  !movd e, ebx
  !paddd h, e
  !paddd d, h   ; d = d + h
  
  !movq e, a; F0  ; h = h + Sigma2(a) + F0(a,b,c) 
  !por e, b
  !pand e, c
  !movd eax, a
  !pand a, b
  !por e, a
  !movd a, eax
  !paddd h, e
  
  !mov ecx, eax ; Sigma 2
  !ror eax, 2
  !ror ecx, 13
  !xor eax, ecx
  !ror ecx, 9
  !xor eax, ecx
  !movd e, eax
  !paddd h,e
  !movd e, edx
  
EndMacro

;================================================================
;                  LOCAL FUNCTIONS
;================================================================

Procedure sha224_starts( *ctx.sha256_context )
  
  *ctx\state[0] = $c1059ed8
  *ctx\state[1] = $367cd507
  *ctx\state[2] = $3070dd17
  *ctx\state[3] = $f70e5939
  *ctx\state[4] = $ffc00b31
  *ctx\state[5] = $68581511
  *ctx\state[6] = $64f98fa7
  *ctx\state[7] = $befa4fa4
  *ctx\digestlen = 28
  
EndProcedure

Procedure sha256_starts( *ctx.sha256_context )
  
  *ctx\state[0] = $6A09E667
  *ctx\state[1] = $BB67AE85
  *ctx\state[2] = $3C6EF372
  *ctx\state[3] = $A54FF53A
  *ctx\state[4] = $510E527F
  *ctx\state[5] = $9B05688C
  *ctx\state[6] = $1F83D9AB
  *ctx\state[7] = $5BE0CD19
  *ctx\digestlen = 32
  
EndProcedure

Procedure sha256_process_addr__()
  
  !mov eax, sha256_process_start
  ProcedureReturn
  !sha256_process_start:
  
  !sub esp, 12
  !mov [esp    ], ebx
  !mov [esp + 4], esi
  !mov [esp + 8], ebp
  
  !mov esi, [esp + 16] ; *ctx
  !mov ebp, [esp + 20] ; *bytes
  
  !sub esp, 256 ; Allocate 256 bytes of memory for 64 dwords
  
  !movd mm0, [esi     ]
  !movd mm1, [esi +  4]
  !movd mm2, [esi +  8]
  !movd mm3, [esi + 12]
  !movd mm4, [esi + 16]
  !movd mm5, [esi + 20]
  !movd mm6, [esi + 24]
  !movd mm7, [esi + 28]
  
  !sha256_process_loop:
  
  S256_R_(0)
  S256_P( mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7,  0, 0x428A2F98 )
  S256_R_(1)
  S256_P( mm7, mm0, mm1, mm2, mm3, mm4, mm5, mm6,  1, 0x71374491 )
  S256_R_(2)
  S256_P( mm6, mm7, mm0, mm1, mm2, mm3, mm4, mm5,  2, 0xB5C0FBCF )
  S256_R_(3)
  S256_P( mm5, mm6, mm7, mm0, mm1, mm2, mm3, mm4,  3, 0xE9B5DBA5 )
  S256_R_(4)
  S256_P( mm4, mm5, mm6, mm7, mm0, mm1, mm2, mm3,  4, 0x3956C25B )
  S256_R_(5)
  S256_P( mm3, mm4, mm5, mm6, mm7, mm0, mm1, mm2,  5, 0x59F111F1 )
  S256_R_(6)
  S256_P( mm2, mm3, mm4, mm5, mm6, mm7, mm0, mm1,  6, 0x923F82A4 )
  S256_R_(7)
  S256_P( mm1, mm2, mm3, mm4, mm5, mm6, mm7, mm0,  7, 0xAB1C5ED5 )
  S256_R_(8)
  S256_P( mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7,  8, 0xD807AA98 )
  S256_R_(9)
  S256_P( mm7, mm0, mm1, mm2, mm3, mm4, mm5, mm6,  9, 0x12835B01 )
  S256_R_(10)
  S256_P( mm6, mm7, mm0, mm1, mm2, mm3, mm4, mm5, 10, 0x243185BE )
  S256_R_(11)
  S256_P( mm5, mm6, mm7, mm0, mm1, mm2, mm3, mm4, 11, 0x550C7DC3 )
  S256_R_(12)
  S256_P( mm4, mm5, mm6, mm7, mm0, mm1, mm2, mm3, 12, 0x72BE5D74 )
  S256_R_(13)
  S256_P( mm3, mm4, mm5, mm6, mm7, mm0, mm1, mm2, 13, 0x80DEB1FE )
  S256_R_(14)
  S256_P( mm2, mm3, mm4, mm5, mm6, mm7, mm0, mm1, 14, 0x9BDC06A7 )
  S256_R_(15)
  S256_P( mm1, mm2, mm3, mm4, mm5, mm6, mm7, mm0, 15, 0xC19BF174 )
  S256_R(16)
  S256_P( mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7, 16, 0xE49B69C1 )
  S256_R(17)
  S256_P( mm7, mm0, mm1, mm2, mm3, mm4, mm5, mm6, 17, 0xEFBE4786 )
  S256_R(18)
  S256_P( mm6, mm7, mm0, mm1, mm2, mm3, mm4, mm5, 18, 0x0FC19DC6 )
  S256_R(19)
  S256_P( mm5, mm6, mm7, mm0, mm1, mm2, mm3, mm4, 19, 0x240CA1CC )
  S256_R(20)
  S256_P( mm4, mm5, mm6, mm7, mm0, mm1, mm2, mm3, 20, 0x2DE92C6F )
  S256_R(21)
  S256_P( mm3, mm4, mm5, mm6, mm7, mm0, mm1, mm2, 21, 0x4A7484AA )
  S256_R(22)
  S256_P( mm2, mm3, mm4, mm5, mm6, mm7, mm0, mm1, 22, 0x5CB0A9DC )
  S256_R(23)
  S256_P( mm1, mm2, mm3, mm4, mm5, mm6, mm7, mm0, 23, 0x76F988DA )
  S256_R(24)
  S256_P( mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7, 24, 0x983E5152 )
  S256_R(25)
  S256_P( mm7, mm0, mm1, mm2, mm3, mm4, mm5, mm6, 25, 0xA831C66D )
  S256_R(26)
  S256_P( mm6, mm7, mm0, mm1, mm2, mm3, mm4, mm5, 26, 0xB00327C8 )
  S256_R(27)
  S256_P( mm5, mm6, mm7, mm0, mm1, mm2, mm3, mm4, 27, 0xBF597FC7 )
  S256_R(28)
  S256_P( mm4, mm5, mm6, mm7, mm0, mm1, mm2, mm3, 28, 0xC6E00BF3 )
  S256_R(29)
  S256_P( mm3, mm4, mm5, mm6, mm7, mm0, mm1, mm2, 29, 0xD5A79147 )
  S256_R(30)
  S256_P( mm2, mm3, mm4, mm5, mm6, mm7, mm0, mm1, 30, 0x06CA6351 )
  S256_R(31)
  S256_P( mm1, mm2, mm3, mm4, mm5, mm6, mm7, mm0, 31, 0x14292967 )
  S256_R(32)
  S256_P( mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7, 32, 0x27B70A85 )
  S256_R(33)
  S256_P( mm7, mm0, mm1, mm2, mm3, mm4, mm5, mm6, 33, 0x2E1B2138 )
  S256_R(34)
  S256_P( mm6, mm7, mm0, mm1, mm2, mm3, mm4, mm5, 34, 0x4D2C6DFC )
  S256_R(35)
  S256_P( mm5, mm6, mm7, mm0, mm1, mm2, mm3, mm4, 35, 0x53380D13 )
  S256_R(36)
  S256_P( mm4, mm5, mm6, mm7, mm0, mm1, mm2, mm3, 36, 0x650A7354 )
  S256_R(37)
  S256_P( mm3, mm4, mm5, mm6, mm7, mm0, mm1, mm2, 37, 0x766A0ABB )
  S256_R(38)
  S256_P( mm2, mm3, mm4, mm5, mm6, mm7, mm0, mm1, 38, 0x81C2C92E )
  S256_R(39)
  S256_P( mm1, mm2, mm3, mm4, mm5, mm6, mm7, mm0, 39, 0x92722C85 )
  S256_R(40)
  S256_P( mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7, 40, 0xA2BFE8A1 )
  S256_R(41)
  S256_P( mm7, mm0, mm1, mm2, mm3, mm4, mm5, mm6, 41, 0xA81A664B )
  S256_R(42)
  S256_P( mm6, mm7, mm0, mm1, mm2, mm3, mm4, mm5, 42, 0xC24B8B70 )
  S256_R(43)
  S256_P( mm5, mm6, mm7, mm0, mm1, mm2, mm3, mm4, 43, 0xC76C51A3 )
  S256_R(44)
  S256_P( mm4, mm5, mm6, mm7, mm0, mm1, mm2, mm3, 44, 0xD192E819 )
  S256_R(45)
  S256_P( mm3, mm4, mm5, mm6, mm7, mm0, mm1, mm2, 45, 0xD6990624 )
  S256_R(46)
  S256_P( mm2, mm3, mm4, mm5, mm6, mm7, mm0, mm1, 46, 0xF40E3585 )
  S256_R(47)
  S256_P( mm1, mm2, mm3, mm4, mm5, mm6, mm7, mm0, 47, 0x106AA070 )
  S256_R(48)
  S256_P( mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7, 48, 0x19A4C116 )
  S256_R(49)
  S256_P( mm7, mm0, mm1, mm2, mm3, mm4, mm5, mm6, 49, 0x1E376C08 )
  S256_R(50)
  S256_P( mm6, mm7, mm0, mm1, mm2, mm3, mm4, mm5, 50, 0x2748774C )
  S256_R(51)
  S256_P( mm5, mm6, mm7, mm0, mm1, mm2, mm3, mm4, 51, 0x34B0BCB5 )
  S256_R(52)
  S256_P( mm4, mm5, mm6, mm7, mm0, mm1, mm2, mm3, 52, 0x391C0CB3 )
  S256_R(53)
  S256_P( mm3, mm4, mm5, mm6, mm7, mm0, mm1, mm2, 53, 0x4ED8AA4A )
  S256_R(54)
  S256_P( mm2, mm3, mm4, mm5, mm6, mm7, mm0, mm1, 54, 0x5B9CCA4F )
  S256_R(55)
  S256_P( mm1, mm2, mm3, mm4, mm5, mm6, mm7, mm0, 55, 0x682E6FF3 )
  S256_R(56)
  S256_P( mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7, 56, 0x748F82EE )
  S256_R(57)
  S256_P( mm7, mm0, mm1, mm2, mm3, mm4, mm5, mm6, 57, 0x78A5636F )
  S256_R(58)
  S256_P( mm6, mm7, mm0, mm1, mm2, mm3, mm4, mm5, 58, 0x84C87814 )
  S256_R(59)
  S256_P( mm5, mm6, mm7, mm0, mm1, mm2, mm3, mm4, 59, 0x8CC70208 )
  S256_R(60)
  S256_P( mm4, mm5, mm6, mm7, mm0, mm1, mm2, mm3, 60, 0x90BEFFFA )
  S256_R(61)
  S256_P( mm3, mm4, mm5, mm6, mm7, mm0, mm1, mm2, 61, 0xA4506CEB )
  S256_R(62)
  S256_P( mm2, mm3, mm4, mm5, mm6, mm7, mm0, mm1, 62, 0xBEF9A3F7 )
  S256_R(63)
  S256_P( mm1, mm2, mm3, mm4, mm5, mm6, mm7, mm0, 63, 0xC67178F2 )
  
  !paddd mm0, [esi     ]
  !movd [esi     ], mm0
  !paddd mm1, [esi +  4]
  !movd [esi +  4], mm1
  !paddd mm2, [esi +  8]
  !movd [esi +  8], mm2
  !paddd mm3, [esi + 12]
  !movd [esi + 12], mm3
  !paddd mm4, [esi + 16]
  !movd [esi + 16], mm4
  !paddd mm5, [esi + 20]
  !movd [esi + 20], mm5
  !paddd mm6, [esi + 24]
  !movd [esi + 24], mm6
  !paddd mm7, [esi + 28]
  !movd [esi + 28], mm7
  
  !add ebp, 64
  !dec dword [esp + 24 + 256]
  !jnz sha256_process_loop
  
  !add esp, 256 
  !mov ebx, [esp    ]
  !mov esi, [esp + 4]
  !mov ebp, [esp + 8]
  !add esp, 12
  !emms
  !ret
  
EndProcedure

Procedure sha256_swap_endian_addr__()
  !mov eax, sha256_swap_endian_start
  ProcedureReturn
  !sha256_swap_endian_start:
  !mov edx, [esp + 4]
  !mov eax, [esp + 8]
  !bswap eax
  !bswap edx
  !ret
EndProcedure

Procedure sha256_set_digest_addr__()
  !mov eax, sha256_set_digest_start
  ProcedureReturn
  !sha256_set_digest_start:
  !push ebx
  !mov ebx, [esp + 8]
  !mov edx, [esp + 12]
  !mov ecx, [ebx + 176]
  !dec ecx
  !sha256_set_digest_loop:
  !xor ecx, 3
  !mov al, [ebx + ecx]
  !xor ecx, 3
  !mov ah, al
  !shr ah, 4
  !and ax, 0x0f0f
  !or ax, 0x9090
  !daa
  !adc al, 0x40
  !daa
  !ror ax, 8
  !daa
  !adc al, 0x40
  !daa
  !or ax, 0x2020
  !mov [edx + ecx * 2], ax
  !sub ecx, 1
  !jnc sha256_set_digest_loop
  !pop ebx
  !ret
EndProcedure

PrototypeC sha256_process_proto(*ctx, *bytes, blocks)
PrototypeC.q sha256_swap_endian_proto(value.q)
PrototypeC sha256_set_digest_proto(*ctx, *output)
Global sha256_process.sha256_process_proto = sha256_process_addr__()
Global sha256_swap_endian.sha256_swap_endian_proto = sha256_swap_endian_addr__()
Global sha256_set_digest.sha256_set_digest_proto = sha256_set_digest_addr__()

Procedure sha256_update(*ctx.sha256_context, *input, length)
  
  Protected left, fill, blocks, blocks_1M
  Protected processed.q = *ctx\processed
  
  If length = 0 : ProcedureReturn : EndIf
  
  left = *ctx\processed & $3f
  fill = 64 - left
  
  *ctx\processed + length
  
  If left And length >= fill
    CopyMemory(*input, @*ctx\buffer + left, fill)
    sha256_process(*ctx, @*ctx\buffer, 1)
    length - fill
    *input + fill
    left = 0
  EndIf

  blocks = length >> 6
  length & $3f
  If blocks
    blocks_1M = blocks >> 14
    blocks & $3fff
    While blocks_1M
      sha256_process(*ctx, *input, $4000)
      blocks_1M - 1
      *input + $100000
      If *ctx\callback
        processed + $100000
        CallCFunctionFast(*ctx\callback, 100 * processed / *ctx\jobsize)
      EndIf
    Wend  
    sha256_process(*ctx, *input, blocks)
    *input + blocks << 6
    If *ctx\callback
      processed + blocks << 6
      CallCFunctionFast(*ctx\callback, 100 * processed / *ctx\jobsize)
    EndIf
  EndIf
  
  If length 
    CopyMemory(*input, @*ctx\buffer + left, length)
  EndIf
  
EndProcedure

Procedure sha256_finish(*ctx.sha256_context, *output)
  
  Protected msglen.q, padn
  
  *ctx\padding[0] = $80
  padn = 56 - *ctx\processed & 63
  If padn <= 0 : padn + 64 : EndIf
  
  msglen = sha256_swap_endian(*ctx\jobsize << 3)
  
  sha256_update(*ctx, @*ctx\padding, padn)
  sha256_update(*ctx, @msglen, 8)
  sha256_set_digest(*ctx, *output)
  
  If *ctx\callback
    CallCFunctionFast(*ctx\callback, 100)
  EndIf
  
EndProcedure

Procedure.s SHA256LongFingerprint(*datapointer, length, full, *callback = 0) ; Data address, data size, [ ,<procaddress> ]
  
  Protected digest.s
  Protected *output, *ctx.sha256_context
  
  *ctx = AllocateMemory(SizeOf(sha256_context))
  *ctx\jobsize = length
  *ctx\callback = *callback
  
  *output = AllocateMemory(64)
    
  If full
    sha256_starts(*ctx)
  Else
    sha224_starts(*ctx)
  EndIf
        
  sha256_update(*ctx, *datapointer, length)
  sha256_finish(*ctx, *output)
  digest = PeekS(*output, *ctx\digestlen << 1, #PB_Ascii)
  
  FreeMemory(*output)
  FreeMemory(*ctx)
  
  ProcedureReturn digest
  
EndProcedure

Procedure.s SHA256LongFileFingerprint(filename.s, full, *callback = 0) ; filename$, [ ,<procaddress> ]
  
  Protected digest.s
  Protected *datapointer, *output, *ctx.sha256_context
  Protected bytesread, fresult
  
  fresult = OpenFile(#PB_Any, filename)
  If fresult
    
    *ctx = AllocateMemory(SizeOf(sha256_context))
    *ctx\jobsize = Lof(fresult)
    *ctx\callback = *callback
    
    *datapointer = AllocateMemory($40000)
    *output = AllocateMemory(64)
    
    If full
      sha256_starts(*ctx)
    Else
      sha224_starts(*ctx)
    EndIf
    
    While Not Eof(fresult)
      bytesread = ReadData(fresult, *datapointer, $40000)
      sha256_update(*ctx, *datapointer, bytesread)
    Wend
    CloseFile(fresult)
    
    sha256_finish(*ctx, *output)
    digest = PeekS(*output, *ctx\digestlen << 1, #PB_Ascii)
    
    FreeMemory(*output)
    FreeMemory(*datapointer)
    FreeMemory(*ctx)
    
  EndIf
  ProcedureReturn digest
  
EndProcedure

;================================================================
;                  EXPORTED FUNCTIONS
;================================================================

ProcedureDLL.s SHA256FileFingerprint(filename.s, *callback = 0) ; filename$, [ ,<procaddress> ]
  ProcedureReturn SHA256LongFileFingerprint(filename.s, 1, *callback)
EndProcedure

ProcedureDLL.s SHA224FileFingerprint(filename.s, *callback = 0) ; filename$, [ ,<procaddress> ]
  ProcedureReturn SHA256LongFileFingerprint(filename.s, 0, *callback)
EndProcedure

ProcedureDLL.s SHA256Fingerprint(*datapointer, Length, *callback = 0) ; Data address, data size, [ ,<procaddress> ]
  ProcedureReturn SHA256LongFingerprint(*datapointer, Length, 1, *callback)
EndProcedure

ProcedureDLL.s SHA224Fingerprint(*datapointer, Length, *callback = 0) ; Data address, data size, [ ,<procaddress> ]
  ProcedureReturn SHA256LongFingerprint(*datapointer, Length, 0, *callback)
EndProcedure