code wiki / _hdl_build / nx_nishifs_crypt.nx
nx_nishifs_crypt.nx source
↩ module page · 162 lines · 9778 B
1// nx_nishifs_crypt.nx -- ladder C7 (the FS): NATIVE ENCRYPTION AT REST for NishiFS (the last FS core axis).
2//
3// Content-addressed encryption, done honestly: the CID is sha256 over the PLAINTEXT (so integrity-on-read +
4// dedup are preserved), the object is stored as CIPHERTEXT, and reads DECRYPT-then-verify the CID -- so a
5// WRONG KEY yields wrong plaintext -> CID mismatch -> rejected (the integrity check doubles as authentication).
6// Cipher = a SOVEREIGN sha256-keystream (hash-CTR): keystream block_i = sha256(key || nonce || i), XORed into
7// the data; per-object nonce = the plaintext CID (convergent encryption -> same content+key = same ciphertext
8// = dedup preserved). XOR is implemented bitwise (xorb) since it composes only +,&,|,<<,>>.
9// HONEST CAVEATS (no overclaim): (1) a standard AEAD (ChaCha20-Poly1305 / AES-GCM) is the hardening refinement
10// over a hash-CTR stream cipher; (2) convergent encryption has the known confirmation-of-file attack; (3) the
11// key here is a literal for the KAT -- real key mgmt (KDF from a passphrase / hardware) is separate.
12// KAT 6/6: T1 encrypt-at-rest round-trip (right key decrypts); T2 stored bytes are CIPHERTEXT (!= plaintext);
13// T3 wrong key -> AUTH-FAIL (no plaintext leaked); T4 CID over plaintext + dedup (same pt+key -> same store,
14// arena does not grow); T5 tamper ciphertext -> AUTH-FAIL (integrity caught); T6 distinct plaintext -> distinct
15// ciphertext (no keystream reuse). composes nx_sha256 + nx_syscalls.
16// NEVER-BRICK (Rule 26): pure in-memory, writes NOTHING. expect_exit: 0 license_tier: ORIGINAL
17import "nx_syscalls.nx"
18import "nx_sha256.nx"
19const ENC_MAGIC_65536: i64 = 65536
20
21const ENC_OK: i64 = 0
22const ENC_NOTFOUND: i64 = 3
23const ENC_AUTHFAIL: i64 = 8
24
25func ui_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
26func ui_num(v: i64) -> i64 { let b: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m;sys_write(1,"-" as *u8,1)} let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} var i: i64=0; while i<k{b[i]=t[k-1-i];i=i+1} sys_write(1,b,k); return 0 }
27func ui_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
28func cid_eq(a: *u8, b: *u8) -> i64 { var i: i64=0; while i<32 { if a[i]!=b[i] { return 0 } i=i+1 } return 1 }
29func wr_u32_le(d: *u8, off: i64, v: i64) -> i64 { d[off]=(v & 0xFF) as u8; d[off+1]=((v>>8)&0xFF) as u8; d[off+2]=((v>>16)&0xFF) as u8; d[off+3]=((v>>24)&0xFF) as u8; return 0 }
30func rd_u32_le(d: *u8, off: i64) -> i64 { return (d[off] as i64) | ((d[off+1] as i64)<<8) | ((d[off+2] as i64)<<16) | ((d[off+3] as i64)<<24) }
31// bitwise XOR of two bytes using only +,&,|,<<,>> (NishiLang-safe, no ^ operator assumed)
32func xorb(a: i64, b: i64) -> i64 { var r: i64=0; var bit: i64=0; while bit<8 { let ab: i64=(a>>bit)&1; let bb: i64=(b>>bit)&1; let xb: i64=(ab+bb)&1; r = r | (xb<<bit); bit=bit+1 } return r }
33
34// sha256-keystream (hash-CTR): buf[i] ^= sha256(key || nonce(32) || block_index)[i mod 32]. self-inverse.
35func keystream_xor(key: *u8, klen: i64, nonce: *u8, buf: *u8, n: i64) -> i64 {
36 var off: i64 = 0
37 var blk: i64 = 0
38 while off < n {
39 let inp: *u8 = sys_mmap(klen + 48)
40 var i: i64=0; while i<klen { inp[i]=key[i]; i=i+1 }
41 var j: i64=0; while j<32 { inp[klen+j]=nonce[j]; j=j+1 }
42 var b: i64=0; while b<8 { inp[klen+32+b]=((blk>>(b*8)) & 0xFF) as u8; b=b+1 }
43 let ks: *u8 = sys_mmap(40)
44 sha256_digest(inp, klen+40, ks)
45 var k: i64=0
46 while k<32 { if off+k < n { buf[off+k] = xorb(buf[off+k] as i64, ks[k] as i64) as u8 } k=k+1 }
47 off = off + 32
48 blk = blk + 1
49 }
50 return 0
51}
52
53func arena_find(arena: *u8, endsz: i64, cid: *u8, outlen: *i64) -> i64 {
54 var c: i64=0
55 while c+36 <= endsz {
56 var same: i64=1; var i: i64=0
57 while i<32 { if arena[c+i]!=cid[i] { same=0; i=32 } else { i=i+1 } }
58 let ln: i64 = rd_u32_le(arena, c+32)
59 if same==1 { outlen[0]=ln; return c+36 }
60 c = c+36+ln
61 }
62 return 0-1
63}
64// ENCRYPTED put: CID over plaintext; store ciphertext (pt XOR keystream(key, nonce=cid)); dedup by CID.
65func enc_put(arena: *u8, endp: *i64, key: *u8, klen: i64, pt: *u8, n: i64, cidout: *u8) -> i64 {
66 sha256_digest(pt, n, cidout)
67 let tl: *i64 = sys_mmap(8) as *i64
68 if arena_find(arena, endp[0], cidout, tl) >= 0 { return 0 }
69 let ct: *u8 = sys_mmap(n+16)
70 var i: i64=0; while i<n { ct[i]=pt[i]; i=i+1 }
71 keystream_xor(key, klen, cidout, ct, n)
72 var c: i64 = endp[0]
73 i=0; while i<32 { arena[c+i]=cidout[i]; i=i+1 }
74 wr_u32_le(arena, c+32, n)
75 i=0; while i<n { arena[c+36+i]=ct[i]; i=i+1 }
76 endp[0] = c+36+n
77 return 0
78}
79// ENCRYPTED get: decrypt with key, verify sha256(plaintext)==cid (correct key + integrity), else AUTH-FAIL.
80func enc_get(arena: *u8, endsz: i64, key: *u8, klen: i64, cid: *u8, out: *u8, cap: i64, lenout: *i64) -> i64 {
81 let lp: *i64 = sys_mmap(8) as *i64
82 let boff: i64 = arena_find(arena, endsz, cid, lp)
83 if boff < 0 { return ENC_NOTFOUND }
84 let n: i64 = lp[0]
85 let pt: *u8 = sys_mmap(n+16)
86 var i: i64=0; while i<n { pt[i]=arena[boff+i]; i=i+1 }
87 keystream_xor(key, klen, cid, pt, n)
88 let h: *u8 = sys_mmap(40)
89 sha256_digest(pt, n, h)
90 if cid_eq(h, cid)==0 { return ENC_AUTHFAIL }
91 var m: i64 = n; if m>cap { m=cap }
92 var j: i64=0; while j<m { out[j]=pt[j]; j=j+1 }
93 lenout[0]=n
94 return ENC_OK
95}
96
97func main() -> i64 {
98 ui_puts("ladder C7: NATIVE ENCRYPTION AT REST for NishiFS (sha256-keystream; CID over plaintext; decrypt-then-verify)\n" as *u8)
99
100 let key1: *u8 = "nishi-master-key-v1-do-not-share\x00" as *u8
101 let key2: *u8 = "WRONG-key-attacker-guess--------\x00" as *u8
102 let kl1: i64 = ui_slen(key1)
103 let kl2: i64 = ui_slen(key2)
104 let P: *u8 = "secret OS config: root credentials + signing keys\x00" as *u8
105 let P2: *u8 = "another secret block: private user data here\x00" as *u8
106 let nP: i64 = ui_slen(P)
107 let nP2: i64 = ui_slen(P2)
108
109 let arena: *u8 = sys_mmap(ENC_MAGIC_65536)
110 let endp: *i64 = sys_mmap(8) as *i64
111 endp[0]=0
112
113 let cidP: *u8 = sys_mmap(40)
114 enc_put(arena, endp, key1, kl1, P, nP, cidP)
115
116 // T1 right key round-trip
117 let o1: *u8 = sys_mmap(256); let l1: *i64 = sys_mmap(8) as *i64
118 let r1: i64 = enc_get(arena, endp[0], key1, kl1, cidP, o1, 256, l1)
119 var p1ok: i64=0; if r1==ENC_OK { if l1[0]==nP { p1ok=1; var i: i64=0; while i<nP { if o1[i]!=P[i] { p1ok=0; i=nP } else { i=i+1 } } } }
120
121 // T2 stored bytes are CIPHERTEXT (!= plaintext)
122 let tl: *i64 = sys_mmap(8) as *i64
123 let boffP: i64 = arena_find(arena, endp[0], cidP, tl)
124 var ctDiffers: i64=0; var di: i64=0; while di<nP { if arena[boffP+di]!=P[di] { ctDiffers=1; di=nP } else { di=di+1 } }
125
126 // T3 wrong key -> AUTH-FAIL
127 let o3: *u8 = sys_mmap(256); let l3: *i64 = sys_mmap(8) as *i64
128 let r3: i64 = enc_get(arena, endp[0], key2, kl2, cidP, o3, 256, l3)
129
130 // T4 CID over plaintext + dedup (same pt+key -> arena doesn't grow)
131 let endBefore: i64 = endp[0]
132 let cidP_again: *u8 = sys_mmap(40)
133 enc_put(arena, endp, key1, kl1, P, nP, cidP_again)
134 let endAfter: i64 = endp[0]
135 let cidP_re: *u8 = sys_mmap(40)
136 sha256_digest(P, nP, cidP_re)
137
138 // T6 distinct plaintext -> distinct ciphertext + round-trips
139 let cidP2: *u8 = sys_mmap(40)
140 enc_put(arena, endp, key1, kl1, P2, nP2, cidP2)
141 let o6: *u8 = sys_mmap(256); let l6: *i64 = sys_mmap(8) as *i64
142 let r6: i64 = enc_get(arena, endp[0], key1, kl1, cidP2, o6, 256, l6)
143 var p2ok: i64=0; if r6==ENC_OK { if l6[0]==nP2 { p2ok=1; var i: i64=0; while i<nP2 { if o6[i]!=P2[i] { p2ok=0; i=nP2 } else { i=i+1 } } } }
144
145 // T5 tamper ciphertext -> AUTH-FAIL (do last; corrupts cidP's object)
146 arena[boffP+2] = (((arena[boffP+2] as i64)+1) & 0xFF) as u8
147 let o5: *u8 = sys_mmap(256); let l5: *i64 = sys_mmap(8) as *i64
148 let r5: i64 = enc_get(arena, endp[0], key1, kl1, cidP, o5, 256, l5)
149
150 var pass: i64=0
151 var ttl: i64=0
152 ttl=ttl+1; ui_puts(" T1 encrypt-at-rest round-trip (right key decrypts to plaintext): " as *u8); if p1ok==1 { pass=pass+1; ui_puts("PASS\n" as *u8) } else { ui_puts("FAIL\n" as *u8) }
153 ttl=ttl+1; ui_puts(" T2 stored object bytes are CIPHERTEXT (!= plaintext): " as *u8); if ctDiffers==1 { pass=pass+1; ui_puts("PASS\n" as *u8) } else { ui_puts("FAIL\n" as *u8) }
154 ttl=ttl+1; ui_puts(" T3 WRONG key -> AUTH-FAIL (no plaintext leaked): " as *u8); if r3==ENC_AUTHFAIL { pass=pass+1; ui_puts("PASS\n" as *u8) } else { ui_puts("FAIL (r3="); ui_num(r3); ui_puts(")\n" as *u8) }
155 ttl=ttl+1; ui_puts(" T4 CID over plaintext + dedup (sha256(P)==cidP, arena did not grow): " as *u8); if cid_eq(cidP_re, cidP)==1 { if endBefore==endAfter { pass=pass+1; ui_puts("PASS\n" as *u8) } else { ui_puts("FAIL\n" as *u8) } } else { ui_puts("FAIL\n" as *u8) }
156 ttl=ttl+1; ui_puts(" T5 tamper ciphertext -> AUTH-FAIL (integrity caught): " as *u8); if r5==ENC_AUTHFAIL { pass=pass+1; ui_puts("PASS\n" as *u8) } else { ui_puts("FAIL (r5="); ui_num(r5); ui_puts(")\n" as *u8) }
157 ttl=ttl+1; ui_puts(" T6 distinct plaintext -> distinct CID + round-trips (no keystream reuse): " as *u8); if cid_eq(cidP, cidP2)==0 { if p2ok==1 { pass=pass+1; ui_puts("PASS\n" as *u8) } else { ui_puts("FAIL\n" as *u8) } } else { ui_puts("FAIL\n" as *u8) }
158
159 ui_puts("NISHIFS-CRYPT-GATE passed " as *u8); ui_num(pass); ui_puts("/" as *u8); ui_num(ttl)
160 if pass==ttl { ui_puts(" verdict=GREEN (native encryption at rest: ciphertext-at-rest + decrypt-then-verify-CID = confidentiality + auth + dedup; AEAD/key-mgmt = hardening refinements)\n" as *u8); sys_exit(0); return 0 }
161 ui_puts(" verdict=RED\n" as *u8); sys_exit(1); return 1
162}