Seite 1 von 1

SHA256Fingerprint und HMAC256, 32-Bit

Verfasst: 07.08.2011 10:48
von Helle
Da PB bisher nur den SHA1Fingerprint bereitstellt, hier eine Version für SHA256 (32-Bit):

Code: Alles auswählen

;- SHA256, based (Pseudo-Code) on engl. Wikipedia
;- "Helle" Klaus Helbing, 07.08.2011, PB 4.51 (x86)
;- 32-Bit-Windows-Version with SSE2

Procedure Padding(BufferA)
  ;Size wird auf Vielfaches von 512 Bit (64 Byte) gebracht (Blocklänge ist 512 Bit)
  !mov eax,[p.v_BufferA]

  !push ebx
  !push edi  
  !push esi

  !mov ebx,eax

  !mov edi,[ebx+352]
  !add edi,ebx

  !mov eax,[ebx+12]          ;MemAv
  !mov esi,eax

  !mov ecx,64                ;512 Bit
  !xor edx,edx               ;hier mal so (ist ja unsigned)
  !div ecx                   ;Modulo steht in edx

  !inc eax                   ;eine Runde wollen wir ja wenigstens! 
  !mov [ebx+8],eax           ;Chunks
  !mov eax,[ebx+12]          ;MemAv
  !mov byte[edi+eax],80h     ;1 gesetztes Bit anhängen
  !or edx,edx                ;edx=0?
  ;1.Fall: EDX=0. Letzter 512-Bit-Block ist Vielfaches von 512 Bit (64 Byte). Es wird ein kompletter 512-Bit-Block angehängt   
  !jz @f 
  ;2.Fall: EDX<56. Letzter 512-Bit-Block ist kleiner als 448 Bit (56 Byte), aber größer Null
  !sub esi,edx
  !cmp edx,56                ;448 Bit
  !jb @f
  ;3.Fall: EDX>=56. Letzter 512-Bit-Block ist größer/gleich als 448 Bit (56 Byte). Dieser Block wird aufgefüllt (Bitwert 1 drangehängt,
  ; Rest Null) und ein weiterer Block drangehängt mit Nullen und am Ende Original-Länge als 64-Bit-Big-Endian-Wert
  !add edi,64
  !add dword[ebx+8],1        ;Chunks, es wurde ja ein Block drangehängt
!@@:  
  !add edi,esi

  !mov edx,8
  !mov eax,[ebx]             ;Size, ist Quad!
  !mul edx                   ;nicht SHL bei großen Dateien
  !mov ecx,eax
  !mov esi,edx

  !mov edx,8                 ;"Kaskadierung" für größere Dateien 
  !mov eax,[ebx+4]           ;Size, ist Quad!
  !mul edx         

  !add eax,esi               ;reicht so für heutige Festplatten/Filelängen

  !bswap ecx
  !bswap eax
  !mov [edi+56],eax          ;Original-Länge als 64-Bit-Big-Endian-Wert anhängen
  !mov [edi+60],ecx 
  
  !pop esi   
  !pop edi
  !pop ebx
EndProcedure 

Procedure Main(BufferA)
  !mov eax,[p.v_BufferA]

  !push ebp
  !push ebx
  !push edi  
  !push esi

  !mov ebp,eax

  ;W[0] bis W[15]
  !mov esi,[eax+352]
  !add esi,eax
  !mov edi,eax
  !add edi,96
  !mov ecx,8
!@@:
  !mov eax,[esi]
  !mov edx,[esi+4]
  !bswap eax
  !bswap edx
  !mov [edi],eax 
  !mov [edi+4],edx
  !add edi,8
  !add esi,8
  !dec ecx
  !jnz @b
  ;W[16] bis W[63]
  !mov ecx,48
  !mov esi,ebp
  !add esi,96+64
!@@:
  ;s0
  !mov eax,dword[esi-60]
  !mov edx,eax
  !mov edi,eax
  !ror eax,7
  !ror edx,18 
  !shr edi,3
  !xor eax,edx
  !xor edi,eax
  ;s1
  !mov eax,dword[esi-8]
  !mov edx,eax
  !mov ebx,eax
  !ror eax,17
  !ror edx,19
  !xor eax,edx
  !shr ebx,10
  !xor eax,ebx
  ;W[i]
  !mov edx,dword[esi-64]
  !add edx,edi               ;edi=s0
  !add edx,dword[esi-28]
  !add edx,eax               ;eax=s1
  !mov dword[esi],edx
  !add esi,4
  !dec ecx
  !jnz @b
  ;Initialisierung
  !mov esi,ebp               ;[p.v_BufferA]
  !add esi,16                ;p_a_h_A
  !mov eax,ebp
  !add eax,64
  !movdqa xmm0,[eax]
  !movdqa xmm1,[eax+16]
  !movdqa [esi],xmm0
  !movdqa [esi+16],xmm1
  ;Main Loop
  !xor ecx,ecx               ;hier mal so
!@@:
  ;s0
  !mov eax,[esi]
  !mov edx,eax
  !mov edi,eax
  !ror eax,2
  !ror edx,13
  !xor eax,edx
  !ror edi,22
  !xor edi,eax               ;edi=s0
  ;maj (major)               maj = (a and b) + (c and (a xor b))
  !mov eax,[esi]             ;a
  !mov edx,eax
  !mov ebx,[esi+4]            ;b
  !xor eax,ebx               ;(a xor b)
  !and ebx,edx               ;(a and b)
  !and eax,[esi+8]           ;c
  !add ebx,eax               ;ebx=maj
  ;t2
  !add ebx,edi               ;ebx=t2, edi=s0
  ;s1
  !mov eax,[esi+16]
  !mov edx,eax
  !mov edi,eax
  !ror eax,6
  !ror edx,11
  !xor eax,edx
  !ror edi,25
  !xor edi,eax               ;edi=s1
  ;ch                         ch = g xor (e and (f xor g))
  !mov eax,[esi+24]          ;g
  !mov edx,eax
  !xor edx,[esi+20]          ;f
  !and edx,[esi+16]          ;e
  !xor edx,eax
  ;t1
  !mov eax,[esi+28]
  !add eax,edi               ;s1
  !add eax,edx               ;ch
  !lea edi,[k_256]           ;Data
  !add eax,dword[edi+ecx]    ;k[i]
  !mov edi,ebp
  !add edi,96
  !add eax,dword[edi+ecx]    ;eax=t1, edi+ecx=W[i]
  ;Vertauschungen
  !mov edx,[esi+12]          ;"altes" d
  !add edx,eax               ;eax=t1
  !movdqa xmm0,[esi]
  !movdqa xmm1,[esi+16]
  !movdqu [esi+4],xmm0       ;mit Versatz von 4 Bytes zurückkopieren 
  !movdqu [esi+20],xmm1
  !add eax,ebx               ;eax=t1, ebx=t2
  !mov [esi],eax             ;"neues" a
  !mov [esi+16],edx          ;"neues" e 

  !add ecx,4
  !cmp ecx,256
  !jb @b
  ;am Ende jedes Chunks Hashs zu vorhandenen Werten aufaddieren (Überträge werden ignoriert!)
  !mov edi,ebp
  !add edi,64
  !movdqa xmm0,[edi]
  !paddd xmm0,[esi]
  !movdqa [edi],xmm0
  !movdqa xmm0,[edi+16]
  !paddd xmm0,[esi+16]
  !movdqa [edi+16],xmm0

  !mov eax,ebp
  !add dword[eax+352],64     ;512 Bit (64 Byte) weiter, nächster Chunk
   
  !pop esi
  !pop edi
  !pop ebx
  !pop ebp
EndProcedure   

Procedure.s SHA256Fingerprint_32(*Source, Length)
  SizeAv = Length

  If Length > $40000 
    MemAv = $40000
    LCopy = $40000
    Chunks = $1000
    ChunksOld = Chunks
   Else
    MemAv = Length
    LCopy = Length
  EndIf

  Buffer = AllocateMemory(356 + MemAv + 128)
  i = Buffer % 16
  If i 
    BufferA = Buffer - i + 16          ;Alignment 16
   Else
    BufferA = Buffer
  EndIf

  CopyMemory(?HashBase_256, BufferA + 64, 32)    ;to pHashA

  PokeQ(BufferA, Length)

  pAllBlocksStart = 356
  PokeL(BufferA + 352, pAllBlocksStart)     ;Pointer in pAllBlocksA

  If Length = 0
    PokeL(BufferA + 12, MemAv)
    Padding(BufferA)
    Chunks = PeekL(BufferA + 8)
    Main(BufferA)
  EndIf  
  
  While SizeAv

    CopyMemory(*Source + Seek, BufferA + pAllBlocksStart, LCopy) ;to pAllBlocksA

    BytesCopy = LCopy
    Seek + BytesCopy
    SizeAv - BytesCopy
    
    If SizeAv < LCopy    
      LCopy = SizeAv  
    EndIf  
    
    If SizeAv = 0 
      PokeL(BufferA + 12, MemAv)
      Padding(BufferA)  
      Chunks = PeekL(BufferA + 8)
    EndIf

    While Chunks                       ;512 Bit (64 Byte)
      Main(BufferA)
      Chunks - 1
    Wend

    PokeL(BufferA + 352, pAllBlocksStart)

    If SizeAv <= MemAv 
      MemAv = SizeAv
      NewMem = MemAv + 128
      For i = 0 To NewMem
        PokeB(BufferA + 356 + i, 0)
      Next
     Else
      Chunks = ChunksOld
    EndIf

  Wend 

  SHA256$ = ""
  For i = 0 To 28 Step 4
    SHA256$ + RSet(Hex(PeekL(BufferA + 64 + i) & $FFFFFFFF), 8, "0")
  Next

  FreeMemory(Buffer)

 ProcedureReturn SHA256$

DataSection   ;Read only, evtl. ohne
HashBase_256: ;The first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19, Big-Endian!
  Data.l $6a09e667, $bb67ae85, $3c6ef372, $a54ff53a, $510e527f, $9b05688c, $1f83d9ab, $5be0cd19
!k_256:       ;The first 32 bits of the fractional parts of the cube roots of the first 64 primes 2...311, Big-Endian!
  Data.l $428a2f98, $71374491, $b5c0fbcf, $e9b5dba5, $3956c25b, $59f111f1, $923f82a4, $ab1c5ed5
  Data.l $d807aa98, $12835b01, $243185be, $550c7dc3, $72be5d74, $80deb1fe, $9bdc06a7, $c19bf174
  Data.l $e49b69c1, $efbe4786, $0fc19dc6, $240ca1cc, $2de92c6f, $4a7484aa, $5cb0a9dc, $76f988da
  Data.l $983e5152, $a831c66d, $b00327c8, $bf597fc7, $c6e00bf3, $d5a79147, $06ca6351, $14292967
  Data.l $27b70a85, $2e1b2138, $4d2c6dfc, $53380d13, $650a7354, $766a0abb, $81c2c92e, $92722c85
  Data.l $a2bfe8a1, $a81a664b, $c24b8b70, $c76c51a3, $d192e819, $d6990624, $f40e3585, $106aa070
  Data.l $19a4c116, $1e376c08, $2748774c, $34b0bcb5, $391c0cb3, $4ed8aa4a, $5b9cca4f, $682e6ff3
  Data.l $748f82ee, $78a5636f, $84c87814, $8cc70208, $90befffa, $a4506ceb, $bef9a3f7, $c67178f2
EndDataSection
EndProcedure

;SHA256-Test
Test256$ = "The quick brown fox jumps over the lazy dog"   ;D7A8FBB307D7809469CA9ABCB0082E4F8D5651E46D3CDB762D02D0BF37C9E592
SHA256$ = SHA256Fingerprint_32(@Test256$, StringByteLength(Test256$))
MessageRequester("SHA256 Fingerprint", SHA256$)

Test256$ = "The quick brown fox jumps over the lazy dog."  ;EF537F25C895BFA782526529A9B63D97AA631564D5D789C2B765448C8635FB6C
SHA256$ = SHA256Fingerprint_32(@Test256$, StringByteLength(Test256$))
MessageRequester("SHA256 Fingerprint", SHA256$)
Damit lässt sich dann der HMAC (Keyed-Hash Message Authentication Code) realisieren. Benötigt werden obige 3 Proceduren sowie:

Code: Alles auswählen

;- HMAC256, based (Pseudo-Code) on engl. Wikipedia
;- "Helle" Klaus Helbing, 07.08.2011, PB 4.51 (x86)
;- 32-Bit-Windows-Version with SSE2

Procedure Key_XOR(Buffer, Source)
  !mov eax,[p.v_Source]
  !movdqu xmm0,[eax]
  !mov ecx,4
  !mov edx,[p.v_Buffer]
!@@:  
  !movdqu xmm1,[edx]
  !pxor xmm1,xmm0
  !movdqu [edx],xmm1
  !add edx,16
  !dec ecx
  !jnz @b
EndProcedure

Procedure Concatenation(Buffer, Hash)
  !mov eax,[p.v_Hash]
  !mov edx,[p.v_Buffer]
  !mov ecx,8
  !push ebx
!@@:  
  !mov ebx,[eax]
  !bswap ebx
  !mov [edx],ebx
  !add eax,4
  !add edx,4
  !dec ecx
  !jne @b
  !pop ebx
EndProcedure

Procedure.s HMAC256_32(*Source, LengthSource, *Key, LengthKey)
  pMem = AllocateMemory(64 + LengthSource)

  If LengthKey <= 64
    CopyMemory(*Key, pMem, LengthKey)
   Else
    HashKey$ = SHA256Fingerprint_32(*Key, LengthKey)  
    ;HashKey$ wieder zurück in Bytes
    HashKey = AllocateMemory(32)
    For i = 0 To 28 Step 4
      PokeL(HashKey + i, Val("$" + Mid(HashKey$, (i * 2 ) + 1, 8)) & $FFFFFFFF) 
    Next
    Concatenation(pMem, HashKey)  ;bswap!    
  EndIf
  
  ;Key XOR ipad
  Key_XOR(pMem, ?ipad)       ;$36

  CopyMemory(*Source, pMem + 64, LengthSource)

  Hash1$ = SHA256Fingerprint_32(pMem, LengthSource + 64)
  FreeMemory(pMem)

  pMem = AllocateMemory(96)

  If LengthKey <= 64
    CopyMemory(*Key, pMem, LengthKey)
   Else
    Concatenation(pMem, HashKey)  ;bswap! 
    FreeMemory(HashKey)
  EndIf

  ;Key XOR opad
  Key_XOR(pMem, ?opad)       ;$5C

  Hash1 = AllocateMemory(32)
  ;Hash1$ wieder zurück in Bytes
  For i = 0 To 28 Step 4
    PokeL(Hash1 + i, Val("$" + Mid(Hash1$, (i * 2 ) + 1, 8)) & $FFFFFFFF) 
  Next

  Concatenation(pMem + 64, Hash1) ;bswap!

  FreeMemory(Hash1)

  HMAC$ = SHA256Fingerprint_32(pMem, 96)

  FreeMemory(pMem)

 ProcedureReturn HMAC$

DataSection
ipad:
  Data.q $3636363636363636, $3636363636363636
opad:
  Data.q $5c5c5c5c5c5c5c5c, $5c5c5c5c5c5c5c5c  
EndDataSection
EndProcedure

;HMAC256-Test
Test256$ = "The quick brown fox jumps over the lazy dog"   ;45B777B61761A73762CA620A2F9FAD388117A89C59DEA32D9AE207827E26C174
Key$ = "Yappadappadu"
HMAC256$ = HMAC256_32(@Test256$, StringByteLength(Test256$), @Key$, StringByteLength(Key$))
MessageRequester("HMAC256_32", HMAC256$)

Test256$ = "The quick brown fox jumps over the lazy dog"   ;F7BC83F430538424B13298E6AA6FB143EF4D59A14946175997479DBC2D1A3CD8
Key$ = "key"
HMAC256$ = HMAC256_32(@Test256$, StringByteLength(Test256$), @Key$, StringByteLength(Key$))
MessageRequester("HMAC256_32", HMAC256$)

Test256$ = "The quick brown fox jumps over the lazy dog"   ;E7FE7008B311BFD50176A4CA546838B9E90A42094F5577EB58194FC0307651AE
Key$ = "This is a very, very, very, very, very, very, very, very, very, long key"
HMAC256$ = HMAC256_32(@Test256$, StringByteLength(Test256$), @Key$, StringByteLength(Key$))
MessageRequester("HMAC256_32", HMAC256$)
Viel Spaß!
Helle

Re: SHA256Fingerprint und HMAC256, 32-Bit

Verfasst: 08.08.2011 08:42
von Kukulkan
Hallo Helle,

vielen Dank! Sehr interessant!

Kannst Du mir bitte noch ein paar Fragen beantworten?

- Läuft das auch unter Win32/64?
- Mit Unicode?
- Wie schaut es unter Linux/MacOS aus? Kann das auch dort compilieren?
- Was für eine Lizenz ist dein Code? Darf ich das auch kommerziell verwenden?

Danke!

Kukulkan

Re: SHA256Fingerprint und HMAC256, 32-Bit

Verfasst: 08.08.2011 12:34
von Helle
Hallo Kukulkan,
hier die Antworten:
1. Oben steht die 32-Bit Windows-Version. Bei Bedarf kann ich ohne viel Aufwand eine 64-Bit Version (Windows) nachreichen (ist eine reine Fingerübung)
2. Mit Unicode habe ich (immer noch) nichts am Hut; müsste mal getestet werden. Aber sollte funktionieren
3. Bei Linux/MacOS muss ich passen; habe ich einfach nicht zur Verfügung
4. Alles was ich hier veröffentliche ist von meiner Seite so frei, freier geht es nicht. Wenn ich z.B. Fremd-DLLs verwende, weise ich ausdrücklich darauf hin. Ob die Verwendung eines Wikipedia-Pseudo-Codes irgendwelchen rechtlichen Einschränkungen unterliegt, entzieht sich allerdings meiner Kenntnis. Also: Von meiner Seite aus mach damit, was Du willst :mrgreen: ! Wenn Du mit Deinem Programm Millionär geworden bist, kannste mir mal ein Bier ausgeben :lol: !

Danke für das Feedback!
Helle