Crypto
Crypto module: general-purpose cryptographic primitives.
Exposes hashing, HMAC, password hashing/verification, random generation, and Base64 encoding as a public API. The underlying implementations are provided by the March runtime (Digestif SHA-256/512, PBKDF2, CSPRNG).
Password hashing uses PBKDF2-SHA256 with a 16-byte random salt and
- The stored
format is: "pbkdf2:sha256:600000:<salt_hex>:<hash_hex>" The iteration count is embedded in the stored hash, so existing hashes produced with a different count still verify correctly via verify_password/2. This is intentionally simple to verify without external libraries.
All hex output is lowercase.
Functions
Decode a standard Base64 string to Bytes.
Crypto.base64_decode("SGVsbG8=") -- Ok(Bytes for "Hello")
Crypto.base64_decode("!!") -- Err("base64_decode: ...")Base64-encode a Bytes value (standard alphabet with padding).
let b = Crypto.random_bytes(12)
Crypto.base64_encode(b) -- 16-character Base64 stringDecode a URL-safe Base64 string (- and _, no padding) to Bytes.
Crypto.base64_url_decode("SGVsbG8") -- Ok(Bytes)
Crypto.base64_url_decode("!!") -- Err(...)Base64-encode using the URL-safe alphabet (- and _ instead of + and /), with no padding characters.
Crypto.base64_url_encode(Crypto.random_bytes(16))
-- URL-safe 22-character stringHash a plaintext password using PBKDF2-SHA256 with a random salt.
let h = Crypto.hash_password("my-secret")
-- "pbkdf2:sha256:600000:<salt_hex>:<hash_hex>"
The result is safe to store in a database. Use `verify_password/2` to check
a candidate password against the stored hash.
Uses 600 000 PBKDF2 iterations (OWASP 2023 recommendation for SHA-256) with
a 16-byte random salt and 32-byte output. Existing hashes stored with a
different iteration count continue to verify correctly because the count is
embedded in the hash string.Compute an HMAC using the given algorithm.
Crypto.hmac(:sha256, "secret", "message")
-- lowercase hex string of the HMAC-SHA256 result
Supported algorithms: `:sha256`. Returns the HMAC as a lowercase hex string.
The key and data may each be a String or Bytes value.Generate n cryptographically secure random bytes. The bytes come from the operating system's CSPRNG (/dev/urandom on Unix), so the output is suitable for cryptographic keys, IVs, nonces, salts, and session tokens.
Panics if `n` is negative or if `/dev/urandom` cannot be opened.
Crypto.random_bytes(16) -- Bytes value with 16 random bytesGenerate n random bytes and return them as a lowercase hex string (2n chars).
Crypto.random_hex(16) -- 32-character lowercase hex stringCompare two strings for equality in time proportional to max(|a|, |b|), never short-circuiting on the first byte difference or on length mismatch.
Prevents timing attacks that would otherwise reveal (a) the length of the
expected value or (b) the position of the first differing byte when
validating a secret such as a token, HMAC digest, or session cookie.
Returns `true` iff `a` and `b` have the same bytes.
Note: the interpreter does not guarantee constant time at the machine level;
this function provides *algorithmic* constant-time behaviour that translates
to machine-level constant time under the native compiler.
Crypto.secure_compare("abc", "abc") -- true
Crypto.secure_compare("abc", "abd") -- false
Crypto.secure_compare("abc", "abcd") -- false (no length-based early exit)Compute the SHA-256 hash of data, returning a lowercase hex string.
Crypto.sha256("hello")
-- "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
The argument may be a String or Bytes value.Compute the SHA-512 hash of data, returning a lowercase hex string.
The argument may be a String or Bytes value.Verify a plaintext password against a stored hash produced by hash_password/1.
let h = Crypto.hash_password("correct-horse-battery-staple")
Crypto.verify_password("correct-horse-battery-staple", h) -- true
Crypto.verify_password("wrong-password", h) -- false
Returns `false` if the hash format is unrecognised.