std.crypto.hash — Funções de Hash Criptográficas
As funções de hash criptográficas do Zig produzem um resumo (digest) de tamanho fixo a partir de dados de tamanho arbitrário. Elas são fundamentais para verificação de integridade, armazenamento seguro de senhas, assinaturas digitais e estruturas de dados como árvores de Merkle. Todas as implementações são escritas em Zig puro e otimizadas para cada arquitetura de processador.
Visão Geral
const std = @import("std");
const hash = std.crypto.hash;
Algoritmos Disponíveis
| Algoritmo | Módulo | Digest (bytes) | Uso Recomendado |
|---|---|---|---|
| SHA-256 | hash.sha2.Sha256 | 32 | Uso geral, compatibilidade |
| SHA-384 | hash.sha2.Sha384 | 48 | Segurança intermediária |
| SHA-512 | hash.sha2.Sha512 | 64 | Máxima segurança SHA-2 |
| SHA3-256 | hash.sha3.Sha3_256 | 32 | Padrão moderno |
| BLAKE2b | hash.Blake2b256 | 32 | Alto desempenho |
| BLAKE3 | hash.Blake3 | 32 | Mais rápido, paralelizável |
Interface Comum
Todos os algoritmos implementam a mesma interface:
// Hash de uma vez só
pub fn hash(data: []const u8, out: *[digest_length]u8, options: Options) void
// Hash incremental (streaming)
pub fn init(options: Options) Self
pub fn update(self: *Self, data: []const u8) void
pub fn final(self: *Self, out: *[digest_length]u8) void
Exemplo 1: Comparação de Algoritmos de Hash
const std = @import("std");
const crypto = std.crypto;
fn hashToHex(comptime H: type, data: []const u8) [H.digest_length * 2]u8 {
var digest: [H.digest_length]u8 = undefined;
H.hash(data, &digest, .{});
var hex: [H.digest_length * 2]u8 = undefined;
for (digest, 0..) |byte, i| {
const chars = "0123456789abcdef";
hex[i * 2] = chars[byte >> 4];
hex[i * 2 + 1] = chars[byte & 0x0f];
}
return hex;
}
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
const dados = "Zig: linguagem de programação de sistemas";
try stdout.print("Dados: \"{s}\"\n\n", .{dados});
const sha256 = hashToHex(crypto.hash.sha2.Sha256, dados);
try stdout.print("SHA-256: {s}\n", .{sha256});
const sha512 = hashToHex(crypto.hash.sha2.Sha512, dados);
try stdout.print("SHA-512: {s}\n", .{sha512});
const blake3 = hashToHex(crypto.hash.Blake3, dados);
try stdout.print("BLAKE3: {s}\n", .{blake3});
// Propriedade: hash muda completamente com 1 bit diferente
const dados2 = "Zig: linguagem de programação de sistema!"; // última letra diferente
const sha256_2 = hashToHex(crypto.hash.sha2.Sha256, dados2);
try stdout.print("\nHash alterado (1 char diferente):\n", .{});
try stdout.print("SHA-256: {s}\n", .{sha256_2});
}
Exemplo 2: Hash Incremental para Grandes Volumes de Dados
const std = @import("std");
const Sha256 = std.crypto.hash.sha2.Sha256;
fn hashStream(reader: anytype) ![Sha256.digest_length]u8 {
var hasher = Sha256.init(.{});
var buf: [4096]u8 = undefined;
while (true) {
const n = try reader.read(&buf);
if (n == 0) break;
hasher.update(buf[0..n]);
}
var digest: [Sha256.digest_length]u8 = undefined;
hasher.final(&digest);
return digest;
}
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
// Simula hashing de dados grandes usando fixedBufferStream
const dados = "A" ** 10000 ++ "B" ** 10000; // 20KB de dados
var stream = std.io.fixedBufferStream(dados);
const digest = try hashStream(stream.reader());
try stdout.print("SHA-256 de {d} bytes: ", .{dados.len});
for (digest) |byte| {
try stdout.print("{x:0>2}", .{byte});
}
try stdout.writeAll("\n");
// Verifica: hash de blocos = hash de tudo junto
var hash_direto: [Sha256.digest_length]u8 = undefined;
Sha256.hash(dados, &hash_direto, .{});
try stdout.print("Verificação: ", .{});
for (hash_direto) |byte| {
try stdout.print("{x:0>2}", .{byte});
}
try stdout.writeAll("\n");
try stdout.print("Iguais: {}\n", .{
std.mem.eql(u8, &digest, &hash_direto),
});
}
Exemplo 3: Verificação de Integridade de Dados
const std = @import("std");
const Sha256 = std.crypto.hash.sha2.Sha256;
const Registro = struct {
dados: []const u8,
checksum: [Sha256.digest_length]u8,
fn criar(dados: []const u8) Registro {
var checksum: [Sha256.digest_length]u8 = undefined;
Sha256.hash(dados, &checksum, .{});
return .{ .dados = dados, .checksum = checksum };
}
fn verificar(self: Registro) bool {
var hash_atual: [Sha256.digest_length]u8 = undefined;
Sha256.hash(self.dados, &hash_atual, .{});
return std.crypto.utils.timingSafeEql(
[Sha256.digest_length]u8,
self.checksum,
hash_atual,
);
}
};
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
// Cria registros com checksum
const registros = [_]Registro{
Registro.criar("Transação: R$100 para conta A"),
Registro.criar("Transação: R$250 para conta B"),
Registro.criar("Transação: R$50 para conta C"),
};
try stdout.writeAll("Verificação de integridade:\n");
for (registros, 0..) |reg, i| {
const status = if (reg.verificar()) "OK" else "CORROMPIDO";
try stdout.print(" Registro {d}: [{s}] {s}\n", .{
i, status, reg.dados,
});
}
// Simula corrupção
var corrompido = Registro.criar("Dados originais");
// Altera o checksum manualmente (simula adulteração)
corrompido.checksum[0] ^= 0xFF;
try stdout.print("\n Registro adulterado: [{s}]\n", .{
if (corrompido.verificar()) "OK" else "CORROMPIDO",
});
}
Escolha do Algoritmo
| Cenário | Algoritmo Recomendado |
|---|---|
| Compatibilidade com sistemas existentes | SHA-256 |
| Máximo desempenho | BLAKE3 |
| Padrão governamental (NIST) | SHA3-256 |
| Hashing de senhas | Use std.crypto.pwhash (Argon2) |
| Verificação de integridade | SHA-256 ou BLAKE3 |
Módulos Relacionados
- std.crypto — Visão geral da criptografia
- std.crypto.random — Geração aleatória
- std.base64 — Codificação de hashes
- std.fmt — Formatação hexadecimal