code wiki / _hdl_build / nx_doc_at_rest.nx

nx_doc_at_rest.nx source

↩ module page · 109 lines · 5233 B

1// nx_doc_at_rest.nx -- AT-REST WIRING: the thin store layer that seals CONFIDENTIAL docs with the crypto core 2// (nx_doc_crypto) and reads plaintext + encrypted TRANSPARENTLY, so existing plaintext confidential docs never 3// break. Composes the sovereign seg_store (dp_prefix/dp_key, ss_begin/ss_add/ss_commit/ss_open/ss_hget) exactly 4// like dp_set_policy / the reclassify organ -- NO daemon edit; the future docportal change is a call-swap 5// (dp_ingest's ss_add -> doc_put ; dp_read -> doc_get) the operator approves later. 6// 7// FORMAT: an 8-byte magic "NXENC1\0\0" prefixes an encrypted value. Encrypted stored value = 8// magic(8) || nonce(12) || ciphertext || tag(16) (the nonce||ct||tag is doc_encrypt's envelope) 9// A value WITHOUT the magic is LEGACY plaintext -> passed through verbatim on read. Plaintext + encrypted coexist. 10// 11// SCOPE: confidential shards only (DP_VIS_PRIVATE and any non-public class); the PUBLIC corpus path is unchanged. 12// license_tier: ORIGINAL 13import "nx_doc_crypto.nx" // doc_encrypt / doc_decrypt / dek_gen / dek_load / crypto_shred 14import "nx_docportal_lib.nx" // dp_prefix / dp_key / DP_VIS_PUBLIC / DP_VIS_PRIVATE (+ ss_*/sys_* transitively) 15 16const AT_MAGIC_LEN: i64 = 8 // "NXENC1\0\0" 17 18// write the 8-byte encrypted-value magic into out[0..8] 19func at_write_magic(out: *u8) -> i64 { 20 out[0] = 78 as u8 // N 21 out[1] = 88 as u8 // X 22 out[2] = 69 as u8 // E 23 out[3] = 78 as u8 // N 24 out[4] = 67 as u8 // C 25 out[5] = 49 as u8 // 1 26 out[6] = 0 as u8 27 out[7] = 0 as u8 28 return AT_MAGIC_LEN 29} 30// does the value begin with the encrypted-value magic? 1/0. (A legacy plaintext doc would need to literally start 31// with "NXENC1\0\0" -- including two NUL bytes -- to collide; real confidential text never does.) 32func at_is_magic(v: *u8, vlen: i64) -> i64 { 33 if vlen < AT_MAGIC_LEN { return 0 } 34 if v[0] != (78 as u8) { return 0 } 35 if v[1] != (88 as u8) { return 0 } 36 if v[2] != (69 as u8) { return 0 } 37 if v[3] != (78 as u8) { return 0 } 38 if v[4] != (67 as u8) { return 0 } 39 if v[5] != (49 as u8) { return 0 } 40 if v[6] != (0 as u8) { return 0 } 41 if v[7] != (0 as u8) { return 0 } 42 return 1 43} 44 45// is this shard class CONFIDENTIAL (encrypt-at-rest applies)? Everything that is NOT the public corpus: 46// DP_VIS_PRIVATE (the client vault) and any future non-public class (secret / uploads) -- all confidential. 47func at_rest_confidential(vis: i64) -> i64 { 48 if vis != DP_VIS_PUBLIC { return 1 } 49 return 0 50} 51 52// provision the tenant DEK IF ABSENT (never rotates an existing key -- rotation would orphan prior ciphertext). 53// After a crypto_shred, a later doc_put re-provisions a fresh DEK (a new tenant lifecycle); old ciphertext stays 54// permanently unrecoverable. 0 ok / <0 fail. 55func dek_ensure(domain: *u8) -> i64 { 56 let dek: *u8 = sys_mmap(32) 57 if dek_load(domain, dek) == 0 { return 0 } 58 return dek_gen(domain) 59} 60 61// STORE a document into the (domain, vis) shard under doc:<cid>. Confidential -> encrypt then store magic||envelope; 62// public -> store plaintext exactly as today. 0 ok / <0 fail. 63func doc_put(domain: *u8, vis: i64, cid: i64, pt: *u8, ptlen: i64) -> i64 { 64 let prefix: *u8 = sys_mmap(512); dp_prefix(domain, vis, prefix) 65 let dkey: *u8 = sys_mmap(64); dp_key(cid, dkey) 66 if at_rest_confidential(vis) == 1 { 67 if dek_ensure(domain) != 0 { return 0 - 1 } 68 let env: *u8 = sys_mmap(ptlen + 64) 69 let envlen: i64 = doc_encrypt(domain, pt, ptlen, env) 70 if envlen < 0 { return 0 - 2 } 71 let vbuf: *u8 = sys_mmap(ptlen + 64) 72 at_write_magic(vbuf) 73 var i: i64 = 0 74 while i < envlen { vbuf[AT_MAGIC_LEN + i] = env[i]; i = i + 1 } 75 let w: *i64 = ss_begin() 76 if ss_add(w, 1, dkey, vbuf, AT_MAGIC_LEN + envlen) < 0 { return 0 - 3 } 77 let segid: i64 = ss_next_segid(prefix) 78 if ss_commit(prefix, w, segid) != 0 { return 0 - 4 } 79 return 0 80 } 81 let w2: *i64 = ss_begin() 82 if ss_add(w2, 1, dkey, pt, ptlen) < 0 { return 0 - 3 } 83 let segid2: i64 = ss_next_segid(prefix) 84 if ss_commit(prefix, w2, segid2) != 0 { return 0 - 4 } 85 return 0 86} 87 88// READ a document from the (domain, vis) shard. If the stored value carries the magic -> strip + decrypt -> 89// plaintext; else -> passthrough the LEGACY plaintext bytes verbatim. Returns plaintext length, or <0 90// (not found, or decrypt FAIL = shredded/tampered/wrong-tenant). out gets the plaintext only on success. 91func doc_get(domain: *u8, vis: i64, cid: i64, out: *u8) -> i64 { 92 let prefix: *u8 = sys_mmap(512); dp_prefix(domain, vis, prefix) 93 let h: *i64 = ss_open(prefix) 94 if (h as i64) == 0 { return 0 - 1 } 95 let dkey: *u8 = sys_mmap(64); dp_key(cid, dkey) 96 let pq: *i64 = sys_mmap(16) as *i64 97 let lq: *i64 = sys_mmap(16) as *i64 98 if ss_hget(h, dkey, pq, lq) != 1 { return 0 - 2 } 99 let vp: *u8 = pq[0] as *u8 100 let vlen: i64 = lq[0] 101 if at_is_magic(vp, vlen) == 1 { 102 let n: i64 = doc_decrypt(domain, (vp as i64 + AT_MAGIC_LEN) as *u8, vlen - AT_MAGIC_LEN, out) 103 if n < 0 { return 0 - 3 } 104 return n 105 } 106 var i: i64 = 0 107 while i < vlen { out[i] = vp[i]; i = i + 1 } 108 return vlen 109}