Skip to content

add rand-bytes for cryptographically random tokens#519

Merged
danieljohnmorris merged 1 commit into
mainfrom
feature/rand-bytes
May 21, 2026
Merged

add rand-bytes for cryptographically random tokens#519
danieljohnmorris merged 1 commit into
mainfrom
feature/rand-bytes

Conversation

@danieljohnmorris
Copy link
Copy Markdown
Collaborator

Summary

Adds rand-bytes n > t, a CSPRNG-backed bytes builtin distinct from rnd / rndn. Output is n cryptographically random bytes from getrandom, encoded as base64url-no-pad text. Targets the JWT jti / CSRF token / session ID / nonce class of use cases, hit by the jwt-signer-and-verifier persona (pending #5ad).

  • rnd and rndn are seedable uniform / Normal floats - for simulations and Monte Carlo, NOT tokens.
  • rand-bytes is the cryptographic path: platform CSPRNG, never seeded.
  • Output is URL-safe (RFC 4648 §5, no = padding) so it drops straight into headers, cookies, and query strings.
  • Encoded length is deterministic: ceil(n * 4 / 3) chars. len (rand-bytes 16) == 22 always.

Repro before / after

Before this PR, agents reaching for a jti / CSRF token had three bad options: rnd (seedable, wrong tool), build it from rnd a b + chr (slow, biased, allocs), or shell out to run "openssl" ["rand" "-base64" "16"] (heavy, fork cost, format mismatch).

After:

% ilo 'f>n;len (rand-bytes 16)' f
22
% ilo 'f>t;rand-bytes 16' f
Ir6unfk5Gql5D2lb0fM_Zg
% ilo 'f>t;rand-bytes -1' f
ILO-R009: rand-bytes: n must be non-negative, got -1

What's in the diff

  • src/builtins.rs - Builtin::RandBytes variant + from_name + name + appended to ALL (preserves on-wire tags).
  • src/interpreter/mod.rs - tree-walker dispatch, eval_rand_bytes (#[inline(never)] from the start), and a hand-rolled b64url_no_pad_encode (no base64 dep needed on main; folds into the shared encoder when the crypto-primitives branch lands).
  • src/verify.rs - signature ("rand-bytes", &["n"], "t").
  • src/vm/mod.rs - tree-bridge eligibility (Builtin::RandBytes, 1). VM and JIT inherit; no new opcodes.
  • Cargo.toml - getrandom = "0.2" (small, std-adjacent, already in transitive deps).
  • Tests: tests/regression_rand_bytes.rs (11 cross-engine integration tests) + 12 unit tests in src/interpreter/mod.rs pinning the encoder against canonical RFC 4648 §5 vectors (b"hello" -> "aGVsbG8", [0xfb, 0xff, 0xbf] -> "-_-_") and the err paths.
  • Example: examples/rand-bytes.ilo exercised by the engine harness across VM + JIT.
  • Doc sync: SPEC.md, ai.txt (regenerated), skills/ilo/SKILL.md, skills/ilo/ilo-builtins-math.md. Site docs updated in a parallel commit on /Users/dan/code/ilo-lang/site (crypto.md adds a Cryptographic random section).

Test plan

  • cargo build --release --features cranelift clean
  • cargo test --release --features cranelift - 7488 pass / 0 fail (was 7476; +12 unit tests, +11 regression, +1 example harness entry already counted)
  • cargo fmt --check clean
  • cargo clippy --release --features cranelift --all-targets -- -D warnings clean
  • Cross-engine: VM + Cranelift JIT both produce 22 chars for rand-bytes 16
  • URL-safe alphabet check on a 256-byte draw (no +, /, =)
  • Negative n and n > 1 MiB cap surface as ILO-R009 on VM (JIT swallows tree-bridge errors as nil per known limitation - documented inline)
  • Example harness runs rand-bytes.ilo across every engine and matches -- out: markers

Follow-ups

  • When the crypto-primitives branch (b64u / b64u-dec / sha256 / hmac-sha256) lands, fold b64url_no_pad_encode into the shared encoder.
  • JIT error propagation for tree-bridge builtins is a separate known issue (#5av-class); not in scope here.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 21, 2026

Codecov Report

❌ Patch coverage is 95.77465% with 6 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/interpreter/mod.rs 95.62% 6 Missing ⚠️

📢 Thoughts on this report? Let us know!

@danieljohnmorris danieljohnmorris force-pushed the feature/rand-bytes branch 3 times, most recently from a515121 to 97bcfcf Compare May 21, 2026 08:34
`rand-bytes n > t` returns n CSPRNG bytes from `getrandom`, encoded as
base64url-no-pad. Distinct from `rnd` (seedable uniform float for
simulations) and `rndn` (seedable Normal float): this is the path for
JWT jti claims, CSRF tokens, session IDs, and nonces.

Output is base64url-no-pad so it drops straight into headers, cookies,
and query strings without further encoding. Encoded length is
deterministic: ceil(n * 4 / 3) chars.

Tree-bridge eligible (arity 1, no FnRef, no I/O wrap) - VM and Cranelift
JIT inherit at zero opcode cost. Cap at 1 MiB; negative or non-finite
n surfaces as ILO-R009. CSPRNG output is never seeded.

The base64url-no-pad encoder is hand-rolled (no `base64` dep yet on
main); when the crypto-primitives branch lands it can fold into the
shared encoder without changing rand-bytes semantics.
@danieljohnmorris danieljohnmorris merged commit 0139747 into main May 21, 2026
5 checks passed
@danieljohnmorris danieljohnmorris deleted the feature/rand-bytes branch May 21, 2026 09:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant