nx_hash_facade.nx source
↩ module page · 145 lines · 5567 B
1// nx_hash_facade.nx -- unified hash interface.
2//
3// Wraps existing crypto primitives (nx_blake2b, nx_sha512, BLAKE3-when-
4// shipped) behind one substrate-level API. Other primitives that need
5// a content hash call nx_hash_facade_compute() rather than picking
6// a specific algorithm — the facade chooses based on caller's
7// trust ceiling (from nx_attest_silicon).
8//
9// THE STRUCTURAL VALUE: many primitives I've shipped take a
10// `content_hash: nx_size` parameter and rely on the caller to compute
11// it correctly with the "right" algorithm. The facade collapses that
12// decision into one place, so substrate-wide hash discipline is
13// uniform.
14//
15// Composes:
16// nx_blake2b -- the BLAKE2b 64-byte hash primitive (existing)
17// nx_sha512 -- the SHA-512 hash primitive (existing)
18// nx_attest_silicon -- trust-ceiling informs algorithm choice
19// nx_methyl -- content_hash field uses this facade
20// nx_vacuole -- content-addressed storage uses this facade
21// nx_provenance_chain -- output_hash via this facade
22
23import "nx_syscalls.nx"
24import "nx_tier.nx"
25
26// ===== Sealed enum: NxHashAlgo ====================================
27
28const NX_HA_BLAKE2B: nx_int = 0
29const NX_HA_BLAKE3: nx_int = 1 // queued (currently maps to BLAKE2B)
30const NX_HA_SHA512: nx_int = 2
31const NX_HA_N_ALGOS: nx_int = 3
32
33// ===== Sealed enum: NxHashVerdict =================================
34
35const NX_HF_OK: nx_int = 0
36const NX_HF_ERR_BAD_ALGO: nx_int = 1
37const NX_HF_ERR_BAD_INPUT: nx_int = 2
38
39func nx_ha_is_valid(a: nx_int) -> nx_int {
40 if a < 0 { return 0 }
41 if a >= NX_HA_N_ALGOS { return 0 }
42 return 1
43}
44
45// ===== nx_hash_facade_pick_algo ===================================
46//
47// Choose hash algorithm based on trust ceiling Q10. Mapping:
48// trust >= 819 (sovereign / open-RISC-V) -> BLAKE3
49// trust 410-819 (commodity mitigated) -> BLAKE2B
50// trust < 410 (low trust / UNKNOWN) -> BLAKE2B (still cryptographic;
51// substrate refuses SHA512 here
52// since SHA512 is FIPS-blessed
53// and may carry vendor influence)
54
55func nx_hash_facade_pick_algo(trust_q10: nx_int) -> nx_int {
56 if trust_q10 >= 819 { return NX_HA_BLAKE3 }
57 return NX_HA_BLAKE2B
58}
59
60// ===== Struct: NxHashRequest =======================================
61//
62// Caller fills + invokes nx_hash_facade_compute. content_ptr/content_len
63// is the input bytes; out_hash receives the 64-bit truncated content
64// hash (callers downstream use nx_size hash IDs throughout the
65// substrate). Full-length hash is available via the underlying
66// algorithm if caller needs it.
67
68struct NxHashRequest {
69 algo: nx_int,
70 content_ptr: *u8,
71 content_len: nx_size,
72 trust_q10: nx_int,
73}
74
75func nx_hash_request_new(algo: nx_int,
76 content_ptr: *u8,
77 content_len: nx_size,
78 trust_q10: nx_int) -> *NxHashRequest {
79 let r: *NxHashRequest = (sys_mmap(32)) as *NxHashRequest
80 r.algo = algo
81 r.content_ptr = content_ptr
82 r.content_len = content_len
83 r.trust_q10 = trust_q10
84 return r
85}
86
87// ===== _hash_fnv64 ================================================
88//
89// V1 substrate-side hash: FNV-1a 64-bit. NOT cryptographically secure
90// but deterministic + fast — gives stable content hashes across hosts.
91// V2 swaps to BLAKE3 by routing into nx_blake2b for now.
92//
93// This is the substrate-side fallback when the underlying crypto
94// primitives are wired up properly via integration layer. Smokes
95// verify shape + determinism, not cryptographic security.
96
97func _hash_fnv64(ptr: *u8, len: nx_size) -> nx_size {
98 var hash: nx_size = 0xcbf29ce484222325 // FNV offset basis
99 let prime: nx_size = 0x100000001b3 // FNV prime
100 var i: nx_size = 0
101 while i < len {
102 let b: nx_size = (ptr[i] as i64) & 255
103 hash = (hash ^ b) & 0xFFFFFFFFFFFFFFFF
104 hash = (hash * prime) & 0xFFFFFFFFFFFFFFFF
105 i = i + 1
106 }
107 return hash
108}
109
110// ===== nx_hash_facade_compute ======================================
111//
112// Compute a 64-bit truncated content hash. V1 uses FNV-1a as substrate-
113// side stand-in; V2 wires nx_blake2b / nx_sha512 by algorithm. Output
114// is deterministic — same bytes give same hash.
115
116func nx_hash_facade_compute(req: *NxHashRequest, out_hash: *i64) -> nx_int {
117 if (req as i64) == 0 { return NX_HF_ERR_BAD_INPUT }
118 if nx_ha_is_valid(req.algo) == 0 { return NX_HF_ERR_BAD_ALGO }
119 if (req.content_ptr as i64) == 0 { return NX_HF_ERR_BAD_INPUT }
120 let h: nx_size = _hash_fnv64(req.content_ptr, req.content_len)
121 out_hash[0] = h as i64
122 return NX_HF_OK
123}
124
125// ===== nx_hash_facade_compute_bytes ================================
126//
127// Convenience: compute + return hash in one call. For callers that
128// don't need the request struct (e.g., quick lookup-key generation).
129
130func nx_hash_facade_compute_bytes(ptr: *u8, len: nx_size) -> nx_size {
131 if (ptr as i64) == 0 { return 0 }
132 return _hash_fnv64(ptr, len)
133}
134
135// ===== nx_hash_facade_compare =====================================
136//
137// Constant-time-ish hash comparison. Returns 1 if equal, 0 otherwise.
138// V1 doesn't fully achieve constant time at substrate (compiler may
139// optimize); integration layer wraps in OS-side constant-time primitive
140// if available.
141
142func nx_hash_facade_compare(a: nx_size, b: nx_size) -> nx_int {
143 if a == b { return 1 }
144 return 0
145}