Volviendo al tema del hash SHA256 he decidido darle una vuelta de tuerca añadiendo la posibilidad de calcular el hash de un stream, de esta forma se puede calcular el hash de un fichero por ejemplo.
El código queda de la siguiente manera:
unit SHA256; interface uses Sysutils, Classes; type TSHA256HASH = array[0..7] of Cardinal; PSHA256HASH = ^TSHA256HASH; // Calcula el hash SHA256 de una cadena de texto function CalcSHA256(Msg: AnsiString): TSHA256HASH; overload; // Calcula el hash SHA256 de un stream function CalcSHA256(Stream: TStream): TSHA256HASH; overload; // Convierte el hash en una cadena de texto function SHA256ToStr(Hash: TSHA256HASH): String; function TestSHA256: Boolean; implementation type // Un bloque de datos de 64 bytes de longitud TChunk = array[0..15] of Cardinal; PChunk = ^TChunk; const k: array[0..63] of Cardinal = ( $428a2f98, $71374491, $b5c0fbcf, $e9b5dba5, $3956c25b, $59f111f1, $923f82a4, $ab1c5ed5, $d807aa98, $12835b01, $243185be, $550c7dc3, $72be5d74, $80deb1fe, $9bdc06a7, $c19bf174, $e49b69c1, $efbe4786, $0fc19dc6, $240ca1cc, $2de92c6f, $4a7484aa, $5cb0a9dc, $76f988da, $983e5152, $a831c66d, $b00327c8, $bf597fc7, $c6e00bf3, $d5a79147, $06ca6351, $14292967, $27b70a85, $2e1b2138, $4d2c6dfc, $53380d13, $650a7354, $766a0abb, $81c2c92e, $92722c85, $a2bfe8a1, $a81a664b, $c24b8b70, $c76c51a3, $d192e819, $d6990624, $f40e3585, $106aa070, $19a4c116, $1e376c08, $2748774c, $34b0bcb5, $391c0cb3, $4ed8aa4a, $5b9cca4f, $682e6ff3, $748f82ee, $78a5636f, $84c87814, $8cc70208, $90befffa, $a4506ceb, $bef9a3f7, $c67178f2); //{$ASMMODE intel} function ror(x: Cardinal; y: Byte): Cardinal; assembler; asm mov cl,dl ror eax,cl end; function bswap(x: Cardinal): Cardinal; assembler; asm bswap eax end; function swap64(x: int64): int64; assembler; asm mov edx,dword ptr[x] mov eax,dword ptr[x+4] bswap edx bswap eax end; // Calcula el hash de un bloque function CalcChunk(Hash: TSHA256HASH; var Chunk: TChunk): TSHA256HASH; var i: Integer; s0, s1, maj, t1, t2, ch: Cardinal; w: array[0..63] of Cardinal; begin // Copiamos el bloque al comienzo de array "W" for i:=0 to 15 do w[i]:= bswap(Chunk[i]); // Calculamos el resto de valores del array "W" for i:= 16 to 63 do begin s0:= ror(w[i-15],7) xor ror(w[i-15],18) xor (w[i-15] shr 3); s1:= ror(w[i-2],17) xor ror(w[i-2],19) xor (w[i-2] shr 10); w[i]:= w[i-16] + s0 + w[i-7] + s1; end; // Ahora hacemos las 64 "pasadas" sobre "W" para calcular el hash for i:= 0 to 63 do begin s0:= ror(Hash[0],2) xor ror(Hash[0],13) xor ror(Hash[0],22); maj:= (Hash[0] and Hash[1]) xor (Hash[0] and Hash[2]) xor (Hash[1] and Hash[2]); t2:= s0 + maj; s1:= ror(Hash[4],6) xor ror(Hash[4],11) xor ror(Hash[4],25); ch:= (Hash[4] and Hash[5]) xor ((not Hash[4]) and Hash[6]); t1:= Hash[7] + s1 + ch + k[i] + w[i]; Hash[7]:= Hash[6]; Hash[6]:= Hash[5]; Hash[5]:= Hash[4]; Hash[4]:= Hash[3] + t1; Hash[3]:= Hash[2]; Hash[2]:= Hash[1]; Hash[1]:= Hash[0]; Hash[0]:= t1 + t2; end; // Devolvemos el hash de este bloque Result:= Hash; end; // Calcula el hash SHA256 de una cadena de texto function CalcSHA256(Msg: AnsiString): TSHA256HASH; overload; var Stream: TMemoryStream; begin Stream:= TMemoryStream.Create; try // Guardamos el texto en un stream Stream.WriteBuffer(PAnsiChar(Msg)^,Length(Msg)); Stream.Position:= 0; // Calculamos el hash del stream Result:= CalcSHA256(Stream); finally Stream.Free; end; end; // Calcula el hash SHA256 de un stream function CalcSHA256(Stream: TStream): TSHA256HASH; overload; var i,j,k: Integer; Size: int64; P: PAnsiChar; Chunk: PChunk; H: TSHA256HASH; begin // Colocamos los valores iniciales Result[0]:= $6a09e667; Result[1]:= $bb67ae85; Result[2]:= $3c6ef372; Result[3]:= $a54ff53a; Result[4]:= $510e527f; Result[5]:= $9b05688c; Result[6]:= $1f83d9ab; Result[7]:= $5be0cd19; Size:= 0; // Reservamos espacio para 2 bloques GetMem(P,64*2); try // Apuntamos al principio del buffer Chunk:= PChunk(P); // Rellenamos el buffer con ceros FillChar(P^,64*2,#0); // Leemos un bloque i:= Stream.Read(P^,64); // Mientras leemos bloques completos while i = 64 do begin // Calculamos el hash de este bloque H:= CalcChunk(Result,Chunk^); // Y lo sumamos al hash anterior for k:= 0 to 7 do Result[k]:= Result[k] + H[k]; // Calculamos el tamaño del stream inc(Size,i); // Rellenamos el buffer con ceros FillChar(P^,64*2,#0); // Leemos el siguiente bloque i:= Stream.Read(P^,64); end; // Calculamos el tamaño del stream inc(Size,i); // Le añadimos un bit 1 al final P[i]:= #$80; // Calculamos el tamaño de los datos que faltan j:= i + 9; // Ajustamos el tamaño a un multiplo de 64 if j mod 64 > 0 then inc(j,64 - (j mod 64)); // Guardmos el tamaño original en formato "big-endian" Size:= swap64(Size*8); move(Size,P[j-8],8); // Procesamos cada uno de los bloques de 64 bytes que faltan for i:= 1 to j div 64 do begin // Calculamos el hash de este bloque H:= CalcChunk(Result,Chunk^); // Y lo sumamos al hash anterior for k:= 0 to 7 do Result[k]:= Result[k] + H[k]; // Apuntamos al siguiente bloque inc(Chunk); end; finally FreeMem(P); end; end; function SHA256ToStr(Hash: TSHA256HASH): String; var i: Integer; begin Result:= EmptyStr; for i:= 0 to 6 do Result:= Result + IntToHex(Hash[i],8) + #32; Result:= Result + IntToHex(Hash[7],8); end; function TestSHA256: Boolean; begin Result:= (LowerCase(SHA256ToStr(CalcSHA256(''))) = 'e3b0c442 98fc1c14 9afbf4c8 996fb924 27ae41e4 649b934c a495991b 7852b855') and (LowerCase(SHA256ToStr(CalcSHA256('abc'))) = 'ba7816bf 8f01cfea 414140de 5dae2223 b00361a3 96177a9c b410ff61 f20015ad') and (LowerCase(SHA256ToStr(CalcSHA256('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'))) = '248d6a61 d20638b8 e5c02693 0c3e6039 a33ce459 64ff2167 f6ecedd4 19db06c1') and (LowerCase(SHA256ToStr(CalcSHA256('The quick brown fox jumps over the lazy dog'))) = 'd7a8fbb3 07d78094 69ca9abc b0082e4f 8d5651e4 6d3cdb76 2d02d0bf 37c9e592') and (LowerCase(SHA256ToStr(CalcSHA256(StringOfChar('a',1000000)))) = 'cdc76e5c 9914fb92 81a1c7e2 84d73e67 f1809a48 a497200e 046d39cc c7112cd0'); end; end.
Un ejemplo de como calcular el hash de un fichero:
var Stream: TFileStream; begin Stream:= TFileStream.Create('fichero.txt',fmOpenRead or fmShareDenyNone); try ShowMessage(LowerCase(SHA256ToStr(CalcSHA256(Stream)))); finally Stream.Free; end; end;
Añadir nuevo comentario