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.