std.crypto.hash em Zig — Referência e Exemplos

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

AlgoritmoMóduloDigest (bytes)Uso Recomendado
SHA-256hash.sha2.Sha25632Uso geral, compatibilidade
SHA-384hash.sha2.Sha38448Segurança intermediária
SHA-512hash.sha2.Sha51264Máxima segurança SHA-2
SHA3-256hash.sha3.Sha3_25632Padrão moderno
BLAKE2bhash.Blake2b25632Alto desempenho
BLAKE3hash.Blake332Mais 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árioAlgoritmo Recomendado
Compatibilidade com sistemas existentesSHA-256
Máximo desempenhoBLAKE3
Padrão governamental (NIST)SHA3-256
Hashing de senhasUse std.crypto.pwhash (Argon2)
Verificação de integridadeSHA-256 ou BLAKE3

Módulos Relacionados

Tutoriais e Receitas Relacionados

Continue aprendendo Zig

Explore mais tutoriais e artigos em português para dominar a linguagem Zig.