⚡ Zig Guide LiveUnofficial
✓ Zig 0.17.0-dev.1454+5faa79730On an older Zig?

Crypto

const std = @import("std");
const expect = std.testing.expect;

test "sha256" {
    var digest: [32]u8 = undefined;
    std.crypto.hash.sha2.Sha256.hash("abc", &digest, .{});

    // The well-known SHA-256 of "abc".
    var hex: [64]u8 = undefined;
    const text = try std.fmt.bufPrint(&hex, "{x}", .{digest});
    try expect(std.mem.startsWith(u8, text, "ba7816bf"));
}

test "incremental hashing" {
    var hasher = std.crypto.hash.sha2.Sha256.init(.{});
    hasher.update("a");
    hasher.update("bc");

    var digest: [32]u8 = undefined;
    hasher.final(&digest);

    var one_shot: [32]u8 = undefined;
    std.crypto.hash.sha2.Sha256.hash("abc", &one_shot, .{});
    try expect(std.mem.eql(u8, &digest, &one_shot));
}

test "compare secrets in constant time" {
    const a = [_]u8{ 1, 2, 3 };
    const b = [_]u8{ 1, 2, 3 };
    // `std.mem.eql` short-circuits, which leaks how much matched via timing.
    try expect(std.crypto.timing_safe.eql([3]u8, a, b));
}

test "hmac" {
    const Hmac = std.crypto.auth.hmac.sha2.HmacSha256;
    var mac: [Hmac.mac_length]u8 = undefined;
    Hmac.create(&mac, "message", "key");
    try expect(mac.len == 32);
}

test "password hashing is deliberately slow" {
    // bcrypt/scrypt/argon2 are the right tools for passwords; a bare hash
    // is not. They are omitted from this runnable example precisely because
    // they are designed to take a long time.
    try expect(@hasDecl(std.crypto.pwhash, "argon2"));
}

Zig ships a substantial crypto library in std.crypto (hashes, AEADs, key exchange, signatures) with no external dependency.

One-shot and incremental

Sha256.hash(data, &digest, .{});          // one-shot

var hasher = Sha256.init(.{});            // incremental
hasher.update(part1);
hasher.update(part2);
hasher.final(&digest);

Both produce the same digest; use the incremental form when the input arrives in pieces or is too large to hold at once.

Constant-time comparison

std.crypto.timing_safe.eql([32]u8, a, b)

std.mem.eql returns as soon as it finds a difference, so how long it took reveals how many bytes matched. For MACs, tokens, and password hashes that is a real attack. Use the timing-safe form for anything secret.

Passwords are a different problem

Do not hash passwords with SHA-256. std.crypto.pwhash provides argon2, bcrypt, and scrypt, which are deliberately slow and salted. That slowness is the feature: it is what makes an offline guessing attack expensive.