These libraries are experimental. APIs may change without notice. Generated from source with koruc 0.1.7 on 8/19/2026.
EVP
@korulang/evp@0.0.1OpenSSL message digests for Koru — leak-proof SHA-256/512/1 and MD5 with phantom obligations
evp/index.kz · 8 tors
@korulang/openssl — the definitive Koru OpenSSL edition (v0: message digests) · 18 more lines
@korulang/openssl — the definitive Koru OpenSSL edition (v0: message digests)
Lifts OpenSSL's EVP message-digest API (SHA-256/512/1, MD5) behind a phantom
obligation. The notorious C footguns this compiles away:
1. EVP_MD_CTX_new() returns a context that MUST be freed with
EVP_MD_CTX_free() — forget it and you leak on every hash. Here the
`hashing` obligation makes forgetting uncompilable.
2. Reading a digest before EVP_DigestFinal_ex, or calling update() after
final, is undefined behaviour. Here `final` CONSUMES the handle and
never re-grants it, so use-after-final is a build error.
3. Picking a digest by runtime string ("sha266") is a runtime failure.
Here the algorithm is a distinct event — an invalid one won't parse.
House style follows the phantom-obligation cursor in the Koru suite
(tests/regression/900_EXAMPLES_SHOWCASE/910_LANGUAGE_SHOOTOUT/2104_*/db.kz
and sqlite3/index.kz): `<state!>` grants an obligation, `<!state>` consumes
it, re-granting `<state!>` keeps the resource alive across a loop.
Phantom lifecycles
Derived from the phantom labels in the declarations below — state! issues an
obligation the compiler will chase, !state discharges it, a bare state holds it without moving it. Nothing here is hand-drawn.
Digest 1 state hashing!// Algorithm selection — one event per digest. There is no runtime algorithm
// name, so "unknown digest" is not a reachable state.
~pub tor sha256.init {} -> *Digest<hashing!>~pub tor sha512.init {} -> *Digest<hashing!>~pub tor sha1.init {} -> *Digest<hashing!>~pub tor md5.init {} -> *Digest<hashing!>// Streaming — feed the digest in chunks. BORROWS the handle (bare <hashing>):
// the obligation stays with the caller, so `d` remains live after the call and
// keeps flowing down the pipeline. Feed as many chunks as you like, then
// finalise the same binding — no re-issued handle to thread.
~pub tor update { d: *Digest<hashing>, bytes: string }// Finalisation — consumes the handle for good. It is NOT re-granted, so the
// `hashing` obligation is discharged here and the EVP context is freed. Any
// use of the handle after `final` is a compile error.
~pub tor final.hex { d: *Digest<!hashing> } -> string~pub tor final.bytes { d: *Digest<!hashing> } -> stringsha256.hex
index.kz:155// One-shot convenience — the whole obligation lifecycle in one call for the
// common "hash this buffer" case. No handle escapes, so nothing to forget.
~pub tor sha256.hex { input: string } -> string