Files
ww/lib/crypto/sha256/sha256_test.ww
Hojun-Cho aadc6618f0 lib: banner purge + WHY-only comment sweep (rule 8)
Every // ---- section banner dies (132 -> 0): names carry the WHAT.
Narration deleted (filename restatements, run-with lines, what-the-
next-line-does); every ref/hare cite, task cite, divergence, ABI/
layout contract, and ownership qualifier kept (borrowed-view lines
restored where the sweep over-cut). Comment-only proven: all 442
walk-workdir .s and 32 import-probe .s byte-identical before/after;
libbyteid 56-roster all-ID.
2026-08-08 21:10:18 +09:00

124 lines
3.8 KiB
Plaintext

// Vectors: the standard NIST SHA-256 set (FIPS 180-4 examples + the
// classic "one million a's").
//
// The digest is the cgen-correctness oracle for u32 wrapping arithmetic
// + the hash-vtable dispatch: any u32-overflow / rotate miscompile shows
// up as a byte mismatch. A failing row aborts via the assert/abort
// builtin (task #5 @test conversion). Expected digests come through the
// (separately tested) hex.decodestr so the vectors stay readable.
package sha256_test;
import bytes;
import crypto.sha256;
import errors;
import hash;
import encoding.hex;
import strings;
// dohash — one-shot hash of `msg` into the caller's `out` (>= 32 bytes).
// Mirrors the sum()-writes-into-a-buffer API (no array-by-value return).
fn dohash(msg: []u8, out: []u8) void = {
let st: sha256.state = sha256.sha256();
let h: *hash.hash = (&st): *hash.hash;
hash.write(h, msg);
hash.sum(h, out);
};
fn checkbytes(got: []u8, want: str) void = {
match (hex.decodestr(want)) {
case let w: []u8 => {
assert(!(!bytes.equal(got, w)));
};
case let e: errors.invalid => abort();
};
};
fn check(msg: []u8, want: str) void = {
let out: [32]u8;
dohash(msg, out[0:32]);
checkbytes(out[0:32], want);
};
// FIPS 180-4 Appendix B.1/B.2/B.3 vectors.
@test fn empty() void = {
let e: [1]u8;
check(e[0:0],
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
};
@test fn abc() void = {
check(strings.toutf8("abc"),
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
};
// 56-byte message: crosses no block boundary but lands exactly on the
// padding edge (56 == BLOCKSZ - 8), the worst case for the pad length
// branch in sum().
@test fn twoblockpad() void = {
check(strings.toutf8(
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"),
"248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1");
};
// One million 'a' fed 1000 bytes at a time. 1000 is not a multiple of
// BLOCKSZ, so this drives the partial-block carry in write() across many
// calls and many full blocks — the strongest streaming + u32-wrapping
// stress in the set.
@test fn millionas() void = {
let st: sha256.state = sha256.sha256();
let h: *hash.hash = (&st): *hash.hash;
let chunk: [1000]u8;
let i: i32 = 0;
for (i < 1000) {
chunk[i] = 'a': u8;
i += 1;
};
i = 0;
for (i < 1000) {
hash.write(h, chunk[0:1000]);
i += 1;
};
let out: [32]u8;
hash.sum(h, out[0:32]);
checkbytes(out[0:32],
"cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0");
};
// reentrant — sum() is non-destructive: it pads+finalizes a snapshot, so
// the live hash survives. Summing twice yields the same digest, and a
// write after a sum() continues the same stream. Pins the faithful
// state-snapshot restored once #265 fold-1 unblocked the deref-rhs
// aggregate copy `let copy = *h` (see sha256.ww [[sumfn]]).
@test fn reentrant() void = {
let st: sha256.state = sha256.sha256();
let h: *hash.hash = (&st): *hash.hash;
hash.write(h, strings.toutf8("abc"));
let out1: [32]u8;
let out2: [32]u8;
hash.sum(h, out1[0:32]);
hash.sum(h, out2[0:32]);
assert(!(!bytes.equal(out1[0:32], out2[0:32])));
checkbytes(out1[0:32],
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
// Write more after the sum(): the post-sum stream is uncorrupted, so
// the running digest of "abc"+"def" matches a one-shot hash of "abcdef".
hash.write(h, strings.toutf8("def"));
let out3: [32]u8;
hash.sum(h, out3[0:32]);
let want: [32]u8;
dohash(strings.toutf8("abcdef"), want[0:32]);
assert(!(!bytes.equal(out3[0:32], want[0:32])));
};
// sz()/bsz() report the SHA-256 constants regardless of state.
@test fn sizes() void = {
let st: sha256.state = sha256.sha256();
let h: *hash.hash = (&st): *hash.hash;
assert(!(hash.sz(h) != 32: size));
assert(!(hash.bsz(h) != 64: size));
};