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}