Skip to content

Crypto and encoding

Use this when hashing, signing, encoding, or verifying secrets. All crypto builtins are tree-bridge eligible: pure text/bytes operations with no I/O, so VM and Cranelift inherit them automatically.

FunctionSignatureDescription
sha256t > tSHA-256 hex digest (lowercase, 64 chars) of the UTF-8 bytes of s
hmac-sha256t t > tHMAC-SHA256 of body under key, returns lowercase hex
ilo
sha256 "" -- e3b0c44298fc1c149afbf4c8996fb924...
sha256 "abc" -- ba7816bf8f01cfea414140de5dae2223...
len (sha256 "x") -- 64 (always)
hmac-sha256 "secret" "payload" -- lowercase hex digest

sha256 always returns a 64-character lowercase hex string. Input is always treated as text (UTF-8 bytes).

hmac-sha256 is the standard choice for webhook signature verification and API request signing. Use ct-eq (not ==) to compare the result against an untrusted value.

FunctionSignatureDescription
ct-eqt t > bConstant-time text equality - no timing side-channel
ilo
-- Webhook signature verification
verify sig:t body:t>b
expected=hmac-sha256 "secret" body
ct-eq expected sig

Always use ct-eq instead of == when comparing secrets (HMAC digests, tokens, API keys). == short-circuits on the first differing byte, which leaks timing information that an attacker can exploit. ct-eq always reads every byte.

FunctionSignatureDescription
b64ut > tBase64url encode (RFC 4648 section 5, no padding, URL-safe -/_ alphabet). Total.
b64u-dect > R t tBase64url decode; Err on input outside the alphabet, on = padding (strict no-pad round-trip), or on non-UTF-8 decoded bytes
ilo
b64u "hello" -- "aGVsbG8" (no padding)
b64u-dec! (b64u "hello") -- "hello"
b64u "??>" -- "Pz8-" (URL-safe; standard b64 would emit "Pz8+")

b64u-dec returns R t t. Use ! to auto-unwrap or handle the Err arm for invalid input. The Ok branch holds a decoded text string (valid UTF-8); non-UTF-8 decoded bytes always Err.

Use b64u/b64u-dec for URL-safe base64 (JWT headers, URL parameters, webhook payloads).

FunctionSignatureDescription
rand-bytesn > tn cryptographically random bytes from the platform CSPRNG (via getrandom), encoded as base64url-no-pad. Distinct from rnd / rndn (seedable, for simulations). Capped at 1 MiB; non-negative n only
ilo
rand-bytes 16 -- 22-char b64url, 128 bits of entropy (jti / CSRF token)
rand-bytes 32 -- 43-char b64url, 256 bits of entropy (session id)
len (rand-bytes 16) -- always 22 (output length is deterministic)

Use rand-bytes for JWT jti claims, CSRF tokens, session IDs, and nonces. Output is base64url-no-pad so the result drops into HTTP headers, cookies, and query strings without further encoding. Encoded length is deterministic: ceil(n * 4 / 3) characters with the trailing = chars stripped. The bytes themselves are unpredictable.

rand-bytes is NOT the same as rnd. rnd is a seedable PRNG suitable for simulations and Monte Carlo work, NOT for tokens or keys. If a value will be checked against an attacker-controlled comparison, reach for rand-bytes, never rnd.

FunctionSignatureDescription
urlenct > tRFC 3986 percent-encode; unreserved chars (ALPHA/DIGIT/-._~) pass through, everything else becomes %HH. Total.
urldect > R t tInverse of urlenc; Err on invalid percent escape or non-UTF-8 decoded bytes
ilo
urlenc "a b&c=d" -- "a%20b%26c%3Dd"
urldec! "a%20b%26c%3Dd" -- "a b&c=d"
urlenc "café" -- "caf%C3%A9" (UTF-8 byte-by-byte)

Both are tree-bridge eligible (no I/O, no FnRef). The encoder is total; the decoder returns Result so malformed input ("abc%", "abc%2", or escapes that decode to invalid UTF-8) surfaces typed at the boundary.

FunctionSignatureDescription
hex-encL n > tEncode list of integers 0-255 as lowercase hex string
hex-dect > R (L n) tDecode hex string to list of byte values 0-255; Err on invalid input
ilo
hex-enc [104 101 108 108 111] -- "68656c6c6f"
hex-dec! "68656c6c6f" -- [104, 101, 108, 108, 111]

hex-enc takes a list of integers in the range 0-255. Values outside this range produce ILO-R009. hex-dec accepts upper- or lowercase hex; odd-length or non-hex input returns Err.

Webhook verification (GitHub-style):

ilo
verify sig:t body:t>b
expected=hmac-sha256 "your-secret" body
ct-eq expected sig

JWT header construction:

ilo
header=b64u (jdmp (pt alg:"HS256" typ:"JWT"))
payload=b64u (jdmp data)

OAuth query string:

ilo
q=cat [(urlenc "client_id") "=" (urlenc cid) "&" (urlenc "redirect_uri") "=" (urlenc ru)] ""

Round-trip check:

ilo
s="hello world & friends=42"
urldec! (urlenc s) -- "hello world & friends=42"
b64u-dec! (b64u s) -- "hello world & friends=42"