SHA256Fingerprint und HMAC256, 32-Bit

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Benutzeravatar
Helle
Beiträge: 566
Registriert: 11.11.2004 16:13
Wohnort: Magdeburg

SHA256Fingerprint und HMAC256, 32-Bit

Beitrag 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
Benutzeravatar
Kukulkan
Beiträge: 1066
Registriert: 09.09.2004 07:07
Wohnort: Süddeutschland
Kontaktdaten:

Re: SHA256Fingerprint und HMAC256, 32-Bit

Beitrag 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
Benutzeravatar
Helle
Beiträge: 566
Registriert: 11.11.2004 16:13
Wohnort: Magdeburg

Re: SHA256Fingerprint und HMAC256, 32-Bit

Beitrag 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
Antworten