feat(stdlib): finish Crypto (CSPRNG + base64) and add Uuid.* library (#19 #16)
All checks were successful
docs / build-and-deploy (push) Successful in 2s

Crypto (#19): add the OS cryptographically-secure random surface
(random_bytes/random_hex/random_u32, reading /dev/urandom) and a standard
base64 encoder, completing the library alongside the existing SHA-256/
HMAC-SHA256/verify_hmac/hex/ct_equal. All pure integer IR, C-free.

Uuid (#16): a new namespace for stable, collision-free IDs — v4 (random) and
v7 (time-ordered) generation, plus parse/is_valid/to_text/equals/nil. UUIDs are
canonical lowercase 36-char strings; v4 and v7's random tail draw from the
crypto CSPRNG, so both carry the documented determinism caveat (mint at the
edges, never inside lockstep simulation). Reuses the crypto prelude's
fn_secure_bytes / fn_hex_encode.

- examples/library/{crypto,uuid}.ludic: known-answer vectors (SHA-256, HMAC,
  base64 per RFC 4231/4648) and structural invariants (uuid version/variant
  bits, parse/equals), wired into `x test` (now 51 passed).
- docs: per-symbol pages for every new method + a new Uuid section; inventory
  and impl-vs-docs coverage check pass.
- seed regenerated; `x bootstrap-cfree` fixpoint holds.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-08-30 21:40:51 +03:00
parent a422ef4375
commit 2ddf830f0b
26 changed files with 12185 additions and 10720 deletions

View file

@ -0,0 +1,30 @@
# crypto.ludic — Crypto.* known-answer vectors + the secure-random surface.
# SHA-256 / HMAC-SHA256 / base64 are checked against published test vectors, so a
# regression in the integer IR shows up as a changed line. The secure-random
# helpers are non-deterministic, so we assert their SHAPE (length, distinctness),
# never a fixed value. Running it prints: 1 2 3 4 5 6 7 8 9
program Crypto {
entry {
# SHA-256 (FIPS 180-4 examples)
if Crypto.sha256("abc") == "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" { print(1) }
if Crypto.sha256("") == "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" { print(2) }
# HMAC-SHA256 (RFC 4231 test case 2: key "Jefe", data "what do ya want for nothing?")
if Crypto.hmac_sha256("Jefe", "what do ya want for nothing?") == "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843" { print(3) }
# constant-time verify: the right MAC verifies, a tampered one does not
let mac = Crypto.hmac_sha256("k", "payload")
if Crypto.verify_hmac("k", "payload", mac) { print(4) }
if not Crypto.verify_hmac("k", "payload", "00") { print(5) }
# base64 (RFC 4648 vectors), covering every padding remainder
if Crypto.base64("") == "" { print(6) }
if Crypto.base64("f") == "Zg==" { print(7) }
if Crypto.base64("foobar") == "Zm9vYmFy" { print(8) }
# secure random: two 16-byte draws are 32 hex chars each and (near-certainly) differ
let a = Crypto.random_bytes(16)
let b = Crypto.random_bytes(16)
if len(a) == 32 and a != b { print(9) }
}
}

View file

@ -0,0 +1,32 @@
# uuid.ludic — Uuid.* format round-trip, version/variant bits, validation and
# equality. v4/v7 values are random, so we assert their STRUCTURE (length, the
# version nibble, the variant, that two draws differ), never a fixed value.
# Running it prints: 1 2 3 4 5 6 7 8 9 10
program Uuid {
entry {
let a = Uuid.new()
let b = Uuid.new()
# 36 chars, canonical hyphen positions, and two draws differ
if len(a) == 36 { print(1) }
if a[8..9] == "-" and a[13..14] == "-" and a[18..19] == "-" and a[23..24] == "-" { print(2) }
if a != b { print(3) }
# v4: version nibble is '4' (index 14), variant is one of 8/9/a/b (index 19)
if a[14..15] == "4" { print(4) }
let var4 = a[19..20]
if var4 == "8" or var4 == "9" or var4 == "a" or var4 == "b" { print(5) }
# v7: version nibble is '7'; still a well-formed UUID
let c = Uuid.new_v7()
if c[14..15] == "7" and Uuid.is_valid(c) { print(6) }
# validation of untrusted input
if Uuid.is_valid("550e8400-e29b-41d4-a716-446655440000") { print(7) }
if not Uuid.is_valid("not-a-uuid") { print(8) }
# parse normalises case; equals is case-insensitive; bad input -> nil
if Uuid.equals(Uuid.parse("550E8400-E29B-41D4-A716-446655440000"), "550e8400-e29b-41d4-a716-446655440000") { print(9) }
if Uuid.parse("garbage") == Uuid.nil() { print(10) }
}
}