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

std.crypto.random — Geração Aleatória Criptograficamente Segura

O std.crypto.random fornece acesso a um gerador de números aleatórios criptograficamente seguro (CSPRNG). Ele utiliza a fonte de entropia do sistema operacional (getrandom no Linux, BCryptGenRandom no Windows, /dev/urandom em outros Unix) para produzir bytes imprevisíveis adequados para chaves criptográficas, tokens, nonces e qualquer cenário onde previsibilidade seria uma vulnerabilidade.

Visão Geral

const std = @import("std");
const random = std.crypto.random;

O std.crypto.random é uma instância global e thread-safe que pode ser usada diretamente sem inicialização.

Funções Principais

// Preenche um buffer com bytes aleatórios
pub fn bytes(buf: []u8) void

// Retorna um inteiro aleatório do tipo T
pub fn int(comptime T: type) T

// Retorna inteiro em range [at_least, at_most]
pub fn intRangeAtMost(comptime T: type, at_least: T, at_most: T) T

// Retorna inteiro em range [0, less_than)
pub fn intRangeLessThan(comptime T: type, less_than: T) T

// Retorna um float em [0.0, 1.0)
pub fn float(comptime T: type) T

// Embaralha os elementos de um slice
pub fn shuffle(comptime T: type, buf: []T) void

// Preenche com valor booleano aleatório
pub fn boolean() bool

// Usa a interface Random para compatibilidade
// std.crypto.random implementa std.Random

Exemplo 1: Geração de Tokens e Chaves

const std = @import("std");
const random = std.crypto.random;

fn gerarTokenHex(comptime n_bytes: usize) [n_bytes * 2]u8 {
    var bytes_raw: [n_bytes]u8 = undefined;
    random.bytes(&bytes_raw);

    var hex: [n_bytes * 2]u8 = undefined;
    const chars = "0123456789abcdef";
    for (bytes_raw, 0..) |byte, i| {
        hex[i * 2] = chars[byte >> 4];
        hex[i * 2 + 1] = chars[byte & 0x0f];
    }
    return hex;
}

fn gerarSenha(buf: []u8) []u8 {
    const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%&*";
    for (buf) |*c| {
        const idx = random.intRangeLessThan(usize, charset.len);
        c.* = charset[idx];
    }
    return buf;
}

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    // Token de sessão (32 bytes = 256 bits)
    const token = gerarTokenHex(32);
    try stdout.print("Token de sessão: {s}\n", .{token});

    // API key (16 bytes)
    const api_key = gerarTokenHex(16);
    try stdout.print("API Key:         {s}\n", .{api_key});

    // Senha aleatória
    var buf_senha: [20]u8 = undefined;
    const senha = gerarSenha(&buf_senha);
    try stdout.print("Senha gerada:    {s}\n", .{senha});

    // UUID v4 (simplificado)
    var uuid_bytes: [16]u8 = undefined;
    random.bytes(&uuid_bytes);
    uuid_bytes[6] = (uuid_bytes[6] & 0x0f) | 0x40; // versão 4
    uuid_bytes[8] = (uuid_bytes[8] & 0x3f) | 0x80; // variante

    try stdout.print("UUID v4:         ", .{});
    for (uuid_bytes, 0..) |b, i| {
        if (i == 4 or i == 6 or i == 8 or i == 10) {
            try stdout.writeByte('-');
        }
        try stdout.print("{x:0>2}", .{b});
    }
    try stdout.writeAll("\n");
}

Exemplo 2: Jogos e Simulações

const std = @import("std");
const random = std.crypto.random;

fn rolarDado(lados: u32) u32 {
    return random.intRangeAtMost(u32, 1, lados);
}

fn sortearCartas(comptime n: usize) [n]u8 {
    var baralho: [52]u8 = undefined;
    for (&baralho, 0..) |*c, i| {
        c.* = @intCast(i);
    }
    random.shuffle(u8, &baralho);

    var mao: [n]u8 = undefined;
    @memcpy(&mao, baralho[0..n]);
    return mao;
}

fn nomeCarta(carta: u8) struct { naipe: []const u8, valor: []const u8 } {
    const naipes = [_][]const u8{ "Espadas", "Copas", "Ouros", "Paus" };
    const valores = [_][]const u8{
        "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K",
    };
    return .{
        .naipe = naipes[carta / 13],
        .valor = valores[carta % 13],
    };
}

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    // Rolagem de dados
    try stdout.writeAll("=== Dados ===\n");
    try stdout.writeAll("3d6: ");
    var soma: u32 = 0;
    for (0..3) |_| {
        const dado = rolarDado(6);
        soma += dado;
        try stdout.print("{d} ", .{dado});
    }
    try stdout.print("= {d}\n", .{soma});

    try stdout.print("1d20: {d}\n", .{rolarDado(20)});

    // Cartas
    try stdout.writeAll("\n=== Mão de Poker ===\n");
    const mao = sortearCartas(5);
    for (mao) |carta| {
        const info = nomeCarta(carta);
        try stdout.print("  {s} de {s}\n", .{ info.valor, info.naipe });
    }

    // Moeda
    try stdout.writeAll("\n=== Moeda ===\n");
    for (0..10) |_| {
        const cara = random.boolean();
        try stdout.print("{s} ", .{if (cara) "Cara" else "Coroa"});
    }
    try stdout.writeAll("\n");
}

Exemplo 3: Monte Carlo — Estimação de Pi

const std = @import("std");
const random = std.crypto.random;

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    try stdout.writeAll("=== Estimação de Pi (Monte Carlo) ===\n\n");

    const amostras_lista = [_]u64{ 1000, 10_000, 100_000, 1_000_000 };

    for (amostras_lista) |n_amostras| {
        var dentro: u64 = 0;

        for (0..n_amostras) |_| {
            const x = random.float(f64); // [0, 1)
            const y = random.float(f64); // [0, 1)

            // Verifica se o ponto está dentro do círculo unitário
            if (x * x + y * y <= 1.0) {
                dentro += 1;
            }
        }

        const pi_estimado = 4.0 * @as(f64, @floatFromInt(dentro)) / @as(f64, @floatFromInt(n_amostras));
        const erro = @abs(pi_estimado - std.math.pi);

        try stdout.print("  n={d:>10}: pi ~ {d:.6} (erro: {d:.6})\n", .{
            n_amostras, pi_estimado, erro,
        });
    }

    try stdout.print("\n  Pi real:      {d:.6}\n", .{std.math.pi});
}

random vs std.Random

Característicastd.crypto.randomstd.Random (PRNG)
SegurançaCriptográficoPseudo-aleatório
DesempenhoMais lentoMais rápido
ReprodutívelNãoSim (com seed)
UsoSegurança, tokensJogos, simulações

Para cenários onde velocidade importa mais que segurança (simulações, jogos), considere usar std.Random.DefaultPrng com uma seed inicial de std.crypto.random.

Módulos Relacionados

Tutoriais e Receitas Relacionados

Continue aprendendo Zig

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