code wiki / _hdl_build / nx_doc_crypto_gate.nx
nx_doc_crypto_gate.nx source
↩ module page · 107 lines · 6816 B
1// nx_doc_crypto_gate.nx -- MEASURED hermetic gate for encryption-at-rest (nx_doc_crypto). Fresh nonce domain per
2// run (sys_now_realtime_sec) so it never depends on prior state. Proves the AEAD envelope + crypto-shred:
3// T1 round-trip : doc_encrypt -> doc_decrypt returns the plaintext BYTE-IDENTICAL
4// T2 tamper : a flipped ciphertext byte -> doc_decrypt FAILS (Poly1305 tag reject)
5// T3 wrong tenant : a different domain's DEK -> doc_decrypt FAILS (per-tenant isolation)
6// T4 crypto-shred : after crypto_shred, doc_decrypt FAILS (DEK destroyed) while the ciphertext bytes survive
7// Print pass=N/4 + GREEN/RED, append knowledge/status/dp_crypto_gate.log. STANDALONE -- no daemon touched.
8// (Diagnostic note: nx_chacha20_poly1305_decrypt writes a single 'A' to STDERR on its first call -- scratch-alloc
9// marker in the shipped AEAD; it does not affect stdout or the verdict.) license_tier: ORIGINAL
10import "nx_doc_crypto.nx" // dek_gen / doc_encrypt / doc_decrypt / crypto_shred (+ AEAD/csprng/syscalls transitively)
11
12func g_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
13func g_num(v: i64) -> i64 {
14 let bb: *u8 = sys_mmap(28); var m: i64 = v; if m < 0 { m = 0 - m; sys_write(1, "-" as *u8, 1) }
15 let t: *u8 = sys_mmap(28); var k: i64 = 0; if m == 0 { t[0] = 48 as u8; k = 1 }
16 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
17 var i: i64 = 0; while i < k { bb[i] = t[k - 1 - i]; i = i + 1 } sys_write(1, bb, k); return 0
18}
19func g_w(fd: i64, s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(fd, s, n); return 0 }
20func g_wn(fd: i64, v: i64) -> i64 {
21 let bb: *u8 = sys_mmap(28); var m: i64 = v; if m < 0 { m = 0 - m }
22 let t: *u8 = sys_mmap(28); var k: i64 = 0; if m == 0 { t[0] = 48 as u8; k = 1 }
23 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
24 var i: i64 = 0; while i < k { bb[i] = t[k - 1 - i]; i = i + 1 } sys_write(fd, bb, k); return 0
25}
26func g_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
27func g_cat(out: *u8, o: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { out[o] = s[i]; o = o + 1; i = i + 1 } return o }
28func g_catnum(out: *u8, o: i64, v: i64) -> i64 {
29 if v == 0 { out[o] = 48 as u8; return o + 1 }
30 var m: i64 = v; let t: *u8 = sys_mmap(28); var k: i64 = 0
31 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
32 var i: i64 = 0; while i < k { out[o] = t[k - 1 - i]; o = o + 1; i = i + 1 } return o
33}
34func g_eqbytes(a: *u8, b: *u8, n: i64) -> i64 { var i: i64 = 0; while i < n { if a[i] != b[i] { return 0 } i = i + 1 } return 1 }
35
36func main() -> i64 {
37 g_puts("=== DP CRYPTO GATE (encryption-at-rest: ChaCha20-Poly1305 envelope + crypto-shred) ===\n" as *u8)
38 // fresh per-run tenant domains
39 let nonce: i64 = sys_now_realtime_sec()
40 let domA: *u8 = sys_mmap(64)
41 var da: i64 = g_cat(domA, 0, "dpcryptoa" as *u8); da = g_catnum(domA, da, nonce); domA[da] = 0 as u8
42 let domB: *u8 = sys_mmap(64)
43 var db: i64 = g_cat(domB, 0, "dpcryptob" as *u8); db = g_catnum(domB, db, nonce); domB[db] = 0 as u8
44
45 let pt: *u8 = "ENC-AT-REST FIXTURE: confidential client record -- protected health information sample bytes." as *u8
46 let ptlen: i64 = g_slen(pt)
47
48 // provision tenant A's DEK and seal the document
49 let ga: i64 = dek_gen(domA)
50 let ct: *u8 = sys_mmap(ptlen + 64)
51 let ctlen: i64 = doc_encrypt(domA, pt, ptlen, ct)
52
53 var pass: i64 = 0
54
55 // T1: round-trip byte-identical
56 let pt2: *u8 = sys_mmap(ptlen + 64)
57 let n1: i64 = doc_decrypt(domA, ct, ctlen, pt2)
58 var t1: i64 = 0
59 if ga == 0 { if ctlen == 12 + ptlen + 16 { if n1 == ptlen { if g_eqbytes(pt2, pt, ptlen) == 1 { t1 = 1 } } } }
60 pass = pass + t1
61 g_puts(" T1 encrypt->decrypt round-trips (ctlen=" as *u8); g_num(ctlen); g_puts(" ptlen=" as *u8); g_num(n1); g_puts(" identical=" as *u8); g_num(g_eqbytes(pt2, pt, ptlen)); g_puts("): " as *u8)
62 if t1 == 1 { g_puts("PASS\n" as *u8) } else { g_puts("FAIL\n" as *u8) }
63
64 // T2: tampered ciphertext -> AEAD rejects
65 let ctT: *u8 = sys_mmap(ptlen + 64)
66 var c: i64 = 0
67 while c < ctlen { ctT[c] = ct[c]; c = c + 1 }
68 let b0: i64 = ctT[12] as i64 // first ciphertext-body byte (after the 12-byte nonce)
69 ctT[12] = ((b0 + 1) & 0xff) as u8 // flip it deterministically (no XOR operator needed)
70 let pt3: *u8 = sys_mmap(ptlen + 64)
71 let n2: i64 = doc_decrypt(domA, ctT, ctlen, pt3)
72 var t2: i64 = 0; if n2 < 0 { t2 = 1 } pass = pass + t2
73 g_puts(" T2 tampered ciphertext -> decrypt FAILS (rc=" as *u8); g_num(n2); g_puts(", expect <0): " as *u8)
74 if t2 == 1 { g_puts("PASS\n" as *u8) } else { g_puts("FAIL\n" as *u8) }
75
76 // T3: wrong tenant DEK -> AEAD rejects (per-tenant isolation)
77 dek_gen(domB)
78 let pt4: *u8 = sys_mmap(ptlen + 64)
79 let n3: i64 = doc_decrypt(domB, ct, ctlen, pt4)
80 var t3: i64 = 0; if n3 < 0 { t3 = 1 } pass = pass + t3
81 g_puts(" T3 wrong-tenant DEK -> decrypt FAILS (rc=" as *u8); g_num(n3); g_puts(", expect <0): " as *u8)
82 if t3 == 1 { g_puts("PASS\n" as *u8) } else { g_puts("FAIL\n" as *u8) }
83
84 // T4: crypto-shred -> decrypt FAILS (DEK gone) while ciphertext bytes survive
85 let save: *u8 = sys_mmap(32)
86 var s: i64 = 0
87 while s < 16 { save[s] = ct[s]; s = s + 1 }
88 let sh: i64 = crypto_shred(domA)
89 let pt5: *u8 = sys_mmap(ptlen + 64)
90 let n4: i64 = doc_decrypt(domA, ct, ctlen, pt5)
91 let ct_survives: i64 = g_eqbytes(ct, save, 16)
92 var t4: i64 = 0; if sh == 0 { if n4 < 0 { if ct_survives == 1 { t4 = 1 } } } pass = pass + t4
93 g_puts(" T4 crypto-shred -> decrypt FAILS + ciphertext survives (shred=" as *u8); g_num(sh); g_puts(" decrypt=" as *u8); g_num(n4); g_puts(" ct_survives=" as *u8); g_num(ct_survives); g_puts("): " as *u8)
94 if t4 == 1 { g_puts("PASS\n" as *u8) } else { g_puts("FAIL\n" as *u8) }
95
96 g_puts("----\nDP-CRYPTO rows=4 pass=" as *u8); g_num(pass); g_puts("\n" as *u8)
97 let lg: i64 = sys_openat_append("knowledge/status/dp_crypto_gate.log" as *u8, 0x1a4)
98 if lg >= 0 {
99 g_w(lg, "DP-CRYPTO roundtrip=" as *u8); g_wn(lg, t1); g_w(lg, " tamper_reject=" as *u8); g_wn(lg, t2)
100 g_w(lg, " tenant_isolation=" as *u8); g_wn(lg, t3); g_w(lg, " crypto_shred=" as *u8); g_wn(lg, t4)
101 g_w(lg, " rows=4 pass=" as *u8); g_wn(lg, pass)
102 if pass == 4 { g_w(lg, " verdict=GREEN\n" as *u8) } else { g_w(lg, " verdict=RED\n" as *u8) }
103 sys_close(lg)
104 }
105 if pass == 4 { g_puts("DP-CRYPTO GREEN (AEAD-at-rest round-trips, rejects tamper + wrong-tenant, crypto-shred destroys access -- measured)\n" as *u8); sys_exit(0); return 0 }
106 g_puts("DP-CRYPTO RED\n" as *u8); sys_exit(1); return 1
107}