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}