Cryptographic primitives based on std.crypto (Zig 0.16.0).
Capability Boundaries
✅ Strengths
- Hash computation (SHA-2, SHA-3, Blake3, BLAKE2)
- AEAD encryption/decryption (AES-GCM, ChaCha20-Poly1305)
- Digital signatures (Ed25519, ECDSA)
- Key exchange (X25519, ML-KEM post-quantum)
- Password hashing (Argon2, scrypt, bcrypt, PBKDF2)
- Message authentication codes (HMAC, SipHash)
- KDF(HKDF)
- Secure random number generation
⚠️ Prerequisites
- Confirm the Zig version (
zig version) - Understand basic cryptography concepts (keys, IV, nonce)
❌ Out of Scope
- Random numbers for non-security scenarios → use
zig-0.16's std.Random - HTTP encrypted transport → use the
zig-httpskill (std.http has built-in TLS)
When to Use
- "Help me encrypt this data"
- "How do I compute a SHA256 hash?"
- "Generate an Ed25519 key pair and sign"
- "Securely store user passwords"
Data Privacy
This skill does not collect, store, or transmit any user data. All cryptographic operations are performed locally.
Workflow
Step 1. Clarify the requirement — hash/encryption/signature/password? Step 2. Choose the algorithm — refer to the algorithm selection guide below Step 3. Implement — use the std.crypto.* module Step 4. Security verification — confirm key management and nonce uniqueness
Algorithm Selection Guide
需要加密?
├── 带认证 → AEAD(Aes256Gcm, ChaCha20Poly1305)
└── 流加密 → stream.chacha.*(通常应使用 AEAD)
需要哈希?
├── 通用 → Sha256, Sha512, Blake3
├── 密码存储 → argon2, scrypt, bcrypt
└── 遗留兼容 → Md5, Sha1(不推荐新设计)
需要签名?
├── 标准 → Ed25519
└── ECDSA 兼容 → ecdsa.EcdsaP256Sha256
需要密钥交换?
├── 标准 → X25519
└── 后量子 → ml_kem.*(Kyber)
需要 MAC?
├── 带密钥 → HmacSha256
└── 哈希表键控 → siphash
Hashing
SHA-256 (one-shot)
var digest: [std.crypto.hash.sha2.Sha256.digest_length]u8 = undefined;
std.crypto.hash.sha2.Sha256.hash("hello world", &digest, .{});
SHA-256 (streaming)
var hasher = std.crypto.hash.sha2.Sha256.init(.{});
hasher.update("hello ");
hasher.update("world");
var digest: [32]u8 = undefined;
hasher.final(&digest);
Blake3
var out: [32]u8 = undefined;
std.crypto.hash.Blake3.hash("hello world", &out, .{});
AEAD encryption/decryption
ChaCha20-Poly1305
const key: [32]u8 = .{ ... }; // 32 字节密钥
const nonce: [12]u8 = .{ ... }; // 12 字节 nonce(每次加密必须唯一)
var plaintext = "hello";
var ciphertext: [64]u8 = undefined;
var tag: [16]u8 = undefined;
// 加密
try std.crypto.aead.chacha_poly.ChaCha20Poly1305.encrypt(
ciphertext[0..plaintext.len], &tag, plaintext, nonce, key, "", .{},
);
// 解密
try std.crypto.aead.chacha_poly.ChaCha20Poly1305.decrypt(
plaintext, ciphertext[0..plaintext.len], tag, nonce, key, "", .{},
);
AES-256-GCM
const key: [32]u8 = .{ ... };
const nonce: [12]u8 = .{ ... };
var pt = "secret data";
var ct: [64]u8 = undefined;
var tag: [16]u8 = undefined;
try std.crypto.aead.aes_gcm.Aes256Gcm.encrypt(ct[0..pt.len], &tag, pt, nonce, key, "", .{});
try std.crypto.aead.aes_gcm.Aes256Gcm.decrypt(pt, ct[0..pt.len], tag, nonce, key, "", .{});
Digital signatures
Ed25519 key generation + signing + verification
// 密钥生成
var keypair = try std.crypto.sign.Ed25519.KeyPair.generate(null);
// 签名
const msg = "hello";
var sig: [std.crypto.sign.Ed25519.Signature.encoded_length]u8 = undefined;
try keypair.sign(&sig, msg, .{});
// 验证
try keypair.public_key.verify(&sig, msg, .{});
Key exchange
X25519
// Alice 生成密钥对
const alice_kp = try std.crypto.dh.X25519.KeyPair.generate(null);
// Bob 生成密钥对
const bob_kp = try std.crypto.dh.X25519.KeyPair.generate(null);
// 共享密钥
var alice_shared: [32]u8 = undefined;
try alice_kp.sharedSecret(&alice_shared, bob_kp.public_key, .{});
var bob_shared: [32]u8 = undefined;
try bob_kp.sharedSecret(&bob_shared, alice_kp.public_key, .{});
// alice_shared == bob_shared
Password hashing
Argon2 (recommended)
const password = "user_password";
var hash: [64]u8 = undefined;
try std.crypto.pwhash.argon2.strHash(password, &hash, .{});
// 验证
try std.crypto.pwhash.argon2.strVerify(&hash, password, .{});
scrypt
var dk: [32]u8 = undefined;
try std.crypto.pwhash.scrypt.kdf(&dk, password, salt, .{});
KDF
HKDF
var dk: [32]u8 = undefined;
try std.crypto.kdf.hkdf.HkdfSha256.derive(&dk, ikm, salt, info, .{});
Secure random numbers
// 生成随机字节
var buf: [32]u8 = undefined;
std.crypto.random.bytes(&buf);
// 生成随机整数
const val = std.crypto.random.int(u64);
// 生成随机浮点数
const f = std.crypto.random.float(f64);
Utility functions
// 安全清零(防止编译器优化)
std.crypto.utils.secureZero(&secret_buffer);
// 常量时间比较(防止时序攻击)
const eq = std.crypto.utils.timingSafeEql([8]u8, a, b);
Gotchas
- Nonce must be unique — AEAD encryption usesa different nonce each time, and a repeated nonce will cause the encryption to be broken
- Key management is the biggest risk — std.crypto does not handle key storage; keys must be securely kept by yourself
- Md5 and Sha1 are insecure — for legacy compatibility only; new designs should use Sha256/Blake3
secureZeroprevents optimization — ordinary zeroing may be optimized away by the compiler, so you must usesecureZerotimingSafeEqlprevents timing attacks — when comparing keys/MACs you must use constant-time comparison
FAQ
Q: Should I choose ChaCha20-Poly1305 or AES-GCM? A: AES-GCM is faster when the hardware supports AES-NI; without hardware acceleration, ChaCha20-Poly1305 performs better and is simpler.
Q: Why not use SHA256 for password hashing? A: SHA256 is too fast and is easily brute-forced with a GPU. Argon2/scrypt/bcrypt are designed to be computationally intensive and are naturally resistant to brute force.
Q: How do I generate a secure random key?
A: Use std.crypto.random.bytes(&key). Do not use std.Random.DefaultPrng, that is pseudorandom and not suitable for encryption.