Files
sx/examples/0710-modules-sha256.sx
agra 8f9691c206 F1.1: std.hash — streaming SHA-256 in library/modules/std/hash.sx
Add a pure-sx streaming SHA-256 (FIPS 180-4) stdlib module, importable
as `#import "modules/std/hash.sx";`. All 32-bit word arithmetic is done
in s64 and masked back with `& MASK32`, so digests are deterministic and
platform-independent — no shelling out, no native crypto.

API:
- init() -> Sha256          (by-value *self pattern)
- update(*Sha256, string)   (multi-block + partial-block buffering)
- final(*Sha256) -> string  (32-byte digest as lowercase hex)
- sha256_hex(string) -> string             (one-shot)
- sha256_file([:0]u8) -> ?string           (digest of a file via fs.read_file)

Verified against FIPS/NIST known-answer vectors and `shasum -a 256`:
"" , "abc", the 56- and 112-byte multi-block vectors, 1000×'a', and the
64/65-byte block boundaries; chunked update() matches the one-shot call.

examples/0710-modules-sha256.sx pins the KAT vectors + the streaming
invariant; gate green (zig build, zig build test, run_examples 370/0/0/0).
2026-06-03 22:38:58 +03:00

38 lines
1.5 KiB
Plaintext

// Streaming SHA-256 (FIPS 180-4) from `modules/std/hash.sx`.
//
// Known-answer vectors (empty, "abc", the 112-byte NIST multi-block
// vector) plus the streaming invariant: feeding the same bytes in
// several `update` chunks yields the same digest as the one-shot call.
#import "modules/std.sx";
#import "modules/std/hash.sx";
check :: (label: string, got: string, want: string) {
if got == want {
print("{}: ok\n", label);
} else {
print("{}: FAIL got {} want {}\n", label, got, want);
}
}
main :: () {
check("empty", sha256_hex(""),
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
check("abc", sha256_hex("abc"),
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
// 112-byte input — spans more than one 64-byte block.
multi := "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu";
check("multi", sha256_hex(multi),
"cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1");
// Streaming must equal one-shot regardless of chunk boundaries.
h := init();
h.update("abcdefghbcdefghicdefghijdefghijke"); // 33 bytes
h.update("fghijklfghijklmghijklmnhijklmno"); // crosses block edge
h.update("ijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu");
streamed := h.final();
check("stream-eq-oneshot",
if streamed == sha256_hex(multi) then "yes" else "no", "yes");
}