code wiki / _hdl_build / nx_doc_envelope_gate.nx

nx_doc_envelope_gate.nx source

↩ module page · 274 lines · 14776 B

1// nx_doc_envelope_gate.nx -- GATE for LEGAL D2 (nx_doc_envelope). 2// 3// Drives the e-signature ENVELOPE workflow composing D0 (compliance) + D1 4// (vault) + D5 (real Ed25519 seal over the RFC 8032 KAT keypair) and asserts 5// every workflow invariant WITH a negative control: 6// 7// T1 HAPPY+VAULT : a contract envelope with 2 ordered signers (+1 CC) sends, 8// cannot complete before signing, completes once both have 9// a verified seal, and the sealed version lands in the 10// additive per-tenant vault. 11// T2 NEVER-ROUTE-VOID (the s-class crux) : a Last Will & Testament in a 12// NON-e-wills jurisdiction -> SEND is REFUSED_VOID and the 13// envelope STAYS DRAFT. A generic e-sign clone would send it 14// and collect a legally void signature; this one cannot. 15// T3 ROUTING ORDER: signer 2 cannot sign before signer 1 (OUT_OF_ORDER), then 16// in-order both sign and it completes. 17// T4 TAMPER->NO-COMPLETE : a one-byte-tampered seal is rejected at SIGN 18// (BAD_SEAL) so the envelope cannot complete; restoring the 19// byte lets it sign + complete (forgery can't finish a deal). 20// T5 DECLINE ADDITIVE : a decline makes the envelope terminal-DECLINED while 21// the audit trail only GROWS and both recipients are retained 22// (Rule 13 additive-only). 23// T6 E-WILL NOT OVER-REFUSED : a properly executed e-will (testator + 2 24// witnesses + notary) in an e-wills state sends, signs and 25// completes -- we refuse the VOID path, not the valid one. 26// T7 TENANT ISOLATION : the per-tenant vault prefix discipline rejects 27// '.'/'/'/empty tenant ids (no cross-tenant traversal). 28// 29// NOTE (honest): the gate uses the single RFC 8032 KAT keypair for determinism; 30// binding each signer to their OWN key is a provisioning/PKI rung. What is 31// proven here is the WORKFLOW logic + the legal invariants, not multi-key PKI. 32// 33// Evidence -> knowledge/status/doc_envelope.log 34// license_tier: ORIGINAL 35import "nx_doc_envelope.nx" 36import "nx_doc_seal.nx" 37import "nx_doc_vault.nx" 38import "nx_legal_compliance.nx" 39import "nx_syscalls.nx" 40import "nx_gate_verdict.nx" 41 42const DE_LOG: *u8 = "knowledge/status/doc_envelope.log" 43 44func ew(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 } 45func ewn(fd: i64, v: i64) -> i64 { 46 let bb: *u8 = sys_mmap(28); var m: i64 = v 47 if m < 0 { m = 0 - m; sys_write(fd, "-" as *u8, 1) } 48 let t: *u8 = sys_mmap(28); var k: i64 = 0 49 if m == 0 { t[0] = 48; k = 1 } 50 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 51 var i: i64 = 0; while i < k { bb[i] = t[k - 1 - i]; i = i + 1 } 52 sys_write(fd, bb, k); return 0 53} 54func slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 55 56// build + create a real seal in the shared NxSeal buffer; returns create status. 57func mk_seal(s: *NxSeal, dt: i64, ewills: i64, signer: *u8, ts: i64, 58 intent: i64, consent: i64, attribution: i64, retainable: i64, 59 witnesses: i64, notarized: i64) -> i64 { 60 s.doc_type = dt 61 s.e_wills_allowed = ewills 62 s.intent = intent 63 s.consent = consent 64 s.attribution = attribution 65 s.retainable = retainable 66 s.witnesses = witnesses 67 s.notarized = notarized 68 s.signer_id = signer 69 s.signer_id_len = slen(signer) 70 s.ts = ts 71 return nx_seal_create(s) 72} 73 74func main() -> i64 { 75 var ok: i64 = 1 76 77 // ---- RFC 8032 test-1 keypair (deterministic) ---- 78 let priv: *u8 = sys_mmap(64) 79 priv[0]=0x9d; priv[1]=0x61; priv[2]=0xb1; priv[3]=0x9d; priv[4]=0xef; priv[5]=0xfd; priv[6]=0x5a; priv[7]=0x60 80 priv[8]=0xba; priv[9]=0x84; priv[10]=0x4a; priv[11]=0xf4; priv[12]=0x92; priv[13]=0xec; priv[14]=0x2c; priv[15]=0xc4 81 priv[16]=0x44; priv[17]=0x49; priv[18]=0xc5; priv[19]=0x69; priv[20]=0x7b; priv[21]=0x32; priv[22]=0x69; priv[23]=0x19 82 priv[24]=0x70; priv[25]=0x3b; priv[26]=0xac; priv[27]=0x03; priv[28]=0x1c; priv[29]=0xae; priv[30]=0x7f; priv[31]=0x60 83 let pub: *u8 = sys_mmap(64) 84 pub[0]=0xd7; pub[1]=0x5a; pub[2]=0x98; pub[3]=0x01; pub[4]=0x82; pub[5]=0xb1; pub[6]=0x0a; pub[7]=0xb7 85 pub[8]=0xd5; pub[9]=0x4b; pub[10]=0xfe; pub[11]=0xd3; pub[12]=0xc9; pub[13]=0x64; pub[14]=0x07; pub[15]=0x3a 86 pub[16]=0x0e; pub[17]=0xe1; pub[18]=0x72; pub[19]=0xf3; pub[20]=0xda; pub[21]=0xa6; pub[22]=0x23; pub[23]=0x25 87 pub[24]=0xaf; pub[25]=0x02; pub[26]=0x1a; pub[27]=0x68; pub[28]=0xf7; pub[29]=0x07; pub[30]=0x51; pub[31]=0x1a 88 89 // ---- shared seal buffers + a synthetic 32-byte document hash ---- 90 let canon: *u8 = sys_mmap(512) 91 let sig: *u8 = sys_mmap(128) 92 let dh: *u8 = sys_mmap(64) 93 var di: i64 = 0 94 while di < 32 { dh[di] = ((di * 7 + 3) & 0xff) as u8; di = di + 1 } 95 let s: *NxSeal = sys_mmap(256) as *NxSeal 96 s.doc_hash = dh; s.doc_hash_len = 32 97 s.priv = priv; s.canon = canon; s.sig = sig 98 99 // ---- caller-allocated workflow stores ---- 100 let ecap: i64 = 16 101 let rcap: i64 = 32 102 let acap: i64 = 64 103 let vcap: i64 = 16 104 let eflat: *i64 = sys_mmap(ecap * EF_STRIDE * 8) as *i64 105 let rflat: *i64 = sys_mmap(rcap * RF_STRIDE * 8) as *i64 106 let aflat: *i64 = sys_mmap(acap * AF_STRIDE * 8) as *i64 107 let vflat: *i64 = sys_mmap(vcap * VF_STRIDE * 8) as *i64 108 var ne: i64 = 0 109 var nr: i64 = 0 110 var na: i64 = 0 111 var vcount: i64 = 0 112 113 let DOCID1: i64 = 7101 114 115 // ======== T1: happy path + vault integration ======== 116 var t1: i64 = 1 117 // the contract enters the additive vault as version 1 118 vcount = nx_vault_add(vflat, vcount, vcap, DOCID1, 5001, 1024, 1000) 119 if vcount != 1 { t1 = 0 } 120 ne = nx_env_create(eflat, ne, ecap, 9001, DOCID1, "Services Agreement" as *u8, 1, 1000) 121 if ne != 1 { t1 = 0 } 122 if nx_env_doctype(eflat, ne, 9001) != DT_CONTRACT { t1 = 0 } 123 if nx_env_regime(eflat, ne, 9001) != RG_UETA_GENERAL { t1 = 0 } 124 nr = nx_env_add_recipient(rflat, nr, rcap, 9001, 101, ROLE_SIGNER, 1) 125 nr = nx_env_add_recipient(rflat, nr, rcap, 9001, 102, ROLE_SIGNER, 2) 126 nr = nx_env_add_recipient(rflat, nr, rcap, 9001, 103, ROLE_CC, 3) 127 na = nx_env_audit_add(aflat, na, acap, 9001, EV_CREATED, 1, 1000) 128 if nx_env_send(eflat, ne, rflat, nr, 9001) != ENV_SEND_OK { t1 = 0 } 129 if nx_env_status(eflat, ne, 9001) != ENV_SENT { t1 = 0 } 130 na = nx_env_audit_add(aflat, na, acap, 9001, EV_SENT, 1, 1001) 131 // cannot complete before any signature 132 if nx_env_try_complete(eflat, ne, rflat, nr, 9001) != ENV_COMPLETE_NOT_READY { t1 = 0 } 133 // signer 1 signs (real VALID contract seal) 134 let a1: i64 = mk_seal(s, DT_CONTRACT, 1, "client1@andelinwest.com" as *u8, 1100, 1, 1, 1, 1, 0, 0) 135 if a1 != SEAL_OK { t1 = 0 } 136 if nx_env_sign(eflat, ne, rflat, nr, 9001, 101, s, pub) != ENV_SIGN_OK { t1 = 0 } 137 na = nx_env_audit_add(aflat, na, acap, 9001, EV_SIGNED, 101, 1100) 138 // still not complete (1 of 2) 139 if nx_env_try_complete(eflat, ne, rflat, nr, 9001) != ENV_COMPLETE_NOT_READY { t1 = 0 } 140 // signer 2 signs 141 let a2: i64 = mk_seal(s, DT_CONTRACT, 1, "client2@andelinwest.com" as *u8, 1200, 1, 1, 1, 1, 0, 0) 142 if a2 != SEAL_OK { t1 = 0 } 143 if nx_env_sign(eflat, ne, rflat, nr, 9001, 102, s, pub) != ENV_SIGN_OK { t1 = 0 } 144 na = nx_env_audit_add(aflat, na, acap, 9001, EV_SIGNED, 102, 1200) 145 // now completes 146 if nx_env_try_complete(eflat, ne, rflat, nr, 9001) != ENV_COMPLETE_OK { t1 = 0 } 147 if nx_env_status(eflat, ne, 9001) != ENV_COMPLETED { t1 = 0 } 148 na = nx_env_audit_add(aflat, na, acap, 9001, EV_COMPLETED, 1, 1300) 149 // the completed, sealed version is attached in the additive vault 150 if nx_vault_attach_seal(vflat, vcount, DOCID1) != VAULT_OK { t1 = 0 } 151 let vc: i64 = nx_vault_current_idx(vflat, vcount, DOCID1) 152 if vc < 0 { t1 = 0 } 153 if vflat[vc * VF_STRIDE + VF_SEAL] != 1 { t1 = 0 } 154 // a full audit trail exists (created/sent/signed/signed/completed) 155 if nx_env_audit_count(aflat, na, 9001) != 5 { t1 = 0 } 156 if t1 != 1 { ok = 0 } 157 158 // ======== T2: NEVER ROUTE A VOID INSTRUMENT (the s-class crux) ======== 159 var t2: i64 = 1 160 ne = nx_env_create(eflat, ne, ecap, 9002, 7102, "Last Will and Testament" as *u8, 0, 2000) 161 if nx_env_doctype(eflat, ne, 9002) != DT_WILL { t2 = 0 } 162 if nx_env_regime(eflat, ne, 9002) != RG_REQUIRES_WET { t2 = 0 } 163 nr = nx_env_add_recipient(rflat, nr, rcap, 9002, 201, ROLE_SIGNER, 1) 164 // SEND must refuse and leave the envelope in DRAFT (it never enters e-sign) 165 if nx_env_send(eflat, ne, rflat, nr, 9002) != ENV_SEND_REFUSED_VOID { t2 = 0 } 166 if nx_env_status(eflat, ne, 9002) != ENV_DRAFT { t2 = 0 } 167 if t2 != 1 { ok = 0 } 168 169 // ======== T3: routing order enforced ======== 170 var t3: i64 = 1 171 ne = nx_env_create(eflat, ne, ecap, 9003, 7103, "Partnership Agreement" as *u8, 1, 3000) 172 nr = nx_env_add_recipient(rflat, nr, rcap, 9003, 301, ROLE_SIGNER, 1) 173 nr = nx_env_add_recipient(rflat, nr, rcap, 9003, 302, ROLE_SIGNER, 2) 174 if nx_env_send(eflat, ne, rflat, nr, 9003) != ENV_SEND_OK { t3 = 0 } 175 // signer 2 (order 2) tries first -> OUT_OF_ORDER 176 let b2: i64 = mk_seal(s, DT_CONTRACT, 1, "p2@x.com" as *u8, 3200, 1, 1, 1, 1, 0, 0) 177 if nx_env_sign(eflat, ne, rflat, nr, 9003, 302, s, pub) != ENV_SIGN_OUT_OF_ORDER { t3 = 0 } 178 // signer 1 signs -> OK 179 let b1: i64 = mk_seal(s, DT_CONTRACT, 1, "p1@x.com" as *u8, 3300, 1, 1, 1, 1, 0, 0) 180 if nx_env_sign(eflat, ne, rflat, nr, 9003, 301, s, pub) != ENV_SIGN_OK { t3 = 0 } 181 // now signer 2 -> OK 182 let b3: i64 = mk_seal(s, DT_CONTRACT, 1, "p2@x.com" as *u8, 3400, 1, 1, 1, 1, 0, 0) 183 if nx_env_sign(eflat, ne, rflat, nr, 9003, 302, s, pub) != ENV_SIGN_OK { t3 = 0 } 184 if nx_env_try_complete(eflat, ne, rflat, nr, 9003) != ENV_COMPLETE_OK { t3 = 0 } 185 if t3 != 1 { ok = 0 } 186 187 // ======== T4: a tampered seal cannot sign or complete ======== 188 var t4: i64 = 1 189 ne = nx_env_create(eflat, ne, ecap, 9004, 7104, "Engagement Agreement" as *u8, 1, 4000) 190 nr = nx_env_add_recipient(rflat, nr, rcap, 9004, 401, ROLE_SIGNER, 1) 191 if nx_env_send(eflat, ne, rflat, nr, 9004) != ENV_SEND_OK { t4 = 0 } 192 let c4: i64 = mk_seal(s, DT_CONTRACT, 1, "client@x.com" as *u8, 4100, 1, 1, 1, 1, 0, 0) 193 if c4 != SEAL_OK { t4 = 0 } 194 // flip one canonical byte -> the Ed25519 seal no longer verifies 195 let cb4: *u8 = s.canon 196 cb4[10] = (cb4[10] ^ 0x55) as u8 197 if nx_env_sign(eflat, ne, rflat, nr, 9004, 401, s, pub) != ENV_SIGN_BAD_SEAL { t4 = 0 } 198 if nx_env_try_complete(eflat, ne, rflat, nr, 9004) != ENV_COMPLETE_NOT_READY { t4 = 0 } 199 // restore -> signs + completes 200 cb4[10] = (cb4[10] ^ 0x55) as u8 201 if nx_env_sign(eflat, ne, rflat, nr, 9004, 401, s, pub) != ENV_SIGN_OK { t4 = 0 } 202 if nx_env_try_complete(eflat, ne, rflat, nr, 9004) != ENV_COMPLETE_OK { t4 = 0 } 203 if t4 != 1 { ok = 0 } 204 205 // ======== T5: decline is additive (history retained) ======== 206 var t5: i64 = 1 207 ne = nx_env_create(eflat, ne, ecap, 9005, 7105, "Service Agreement" as *u8, 1, 5000) 208 nr = nx_env_add_recipient(rflat, nr, rcap, 9005, 501, ROLE_SIGNER, 1) 209 nr = nx_env_add_recipient(rflat, nr, rcap, 9005, 502, ROLE_SIGNER, 2) 210 if nx_env_send(eflat, ne, rflat, nr, 9005) != ENV_SEND_OK { t5 = 0 } 211 na = nx_env_audit_add(aflat, na, acap, 9005, EV_CREATED, 1, 5000) 212 na = nx_env_audit_add(aflat, na, acap, 9005, EV_SENT, 1, 5001) 213 na = nx_env_audit_add(aflat, na, acap, 9005, EV_VIEWED, 501, 5002) 214 if nx_env_audit_count(aflat, na, 9005) != 3 { t5 = 0 } 215 if nx_env_decline(eflat, ne, rflat, nr, 9005, 501) != 0 { t5 = 0 } 216 na = nx_env_audit_add(aflat, na, acap, 9005, EV_DECLINED, 501, 5003) 217 if nx_env_status(eflat, ne, 9005) != ENV_DECLINED { t5 = 0 } 218 // audit grew (never shrank) and both recipients are retained (not deleted) 219 if nx_env_audit_count(aflat, na, 9005) != 4 { t5 = 0 } 220 if nx_env_required_signers(rflat, nr, 9005) != 2 { t5 = 0 } 221 if t5 != 1 { ok = 0 } 222 223 // ======== T6: a proper e-will is NOT over-refused ======== 224 var t6: i64 = 1 225 ne = nx_env_create(eflat, ne, ecap, 9006, 7106, "Last Will and Testament" as *u8, 1, 6000) 226 if nx_env_regime(eflat, ne, 9006) != RG_ELECTRONIC_WILLS { t6 = 0 } 227 nr = nx_env_add_recipient(rflat, nr, rcap, 9006, 601, ROLE_SIGNER, 1) 228 // e-wills jurisdiction -> SEND is allowed (routed to the stricter regime) 229 if nx_env_send(eflat, ne, rflat, nr, 9006) != ENV_SEND_OK { t6 = 0 } 230 // properly executed e-will: testator + 2 witnesses + notary -> VALID 231 let e6: i64 = mk_seal(s, DT_WILL, 1, "testator@x.com" as *u8, 6100, 1, 0, 1, 1, 2, 1) 232 if e6 != SEAL_OK { t6 = 0 } 233 if s.regime != RG_ELECTRONIC_WILLS { t6 = 0 } 234 if nx_env_sign(eflat, ne, rflat, nr, 9006, 601, s, pub) != ENV_SIGN_OK { t6 = 0 } 235 if nx_env_try_complete(eflat, ne, rflat, nr, 9006) != ENV_COMPLETE_OK { t6 = 0 } 236 if t6 != 1 { ok = 0 } 237 238 // ======== T7: per-tenant vault prefix discipline (no traversal) ======== 239 var t7: i64 = 1 240 if nx_vault_valid_tid("client_andelinwest" as *u8) != 1 { t7 = 0 } 241 if nx_vault_valid_tid("../secret" as *u8) != 0 { t7 = 0 } 242 if nx_vault_valid_tid("a/b" as *u8) != 0 { t7 = 0 } 243 if nx_vault_valid_tid("" as *u8) != 0 { t7 = 0 } 244 if t7 != 1 { ok = 0 } 245 246 // ---- evidence ---- 247 var fd: i64 = 1 248 while fd >= 1 { 249 ew(fd, "DOCENVELOPEGATE authored=organ composes=D0+D1+D5 happy_complete=" as *u8); ewn(fd, t1) 250 ew(fd, " never_route_void=" as *u8); ewn(fd, t2) 251 ew(fd, " routing_order=" as *u8); ewn(fd, t3) 252 ew(fd, " tamper_no_complete=" as *u8); ewn(fd, t4) 253 ew(fd, " decline_additive=" as *u8); ewn(fd, t5) 254 ew(fd, " ewill_not_overrefused=" as *u8); ewn(fd, t6) 255 ew(fd, " tenant_isolation=" as *u8); ewn(fd, t7) 256 if ok == 1 { ew(fd, " verdict=GREEN\n" as *u8) } else { ew(fd, " verdict=RED\n" as *u8) } 257 if fd == 1 { 258 let lf: i64 = sys_openat_append(DE_LOG, 420) 259 if lf >= 1 { fd = lf } else { fd = 0 } 260 } else { 261 sys_close(fd); fd = 0 262 } 263 } 264 265 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check 266 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled 267 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify. 268 let ctr__dry: *i64 = gv_ctr() 269 ctr__dry[0] = ok 270 ctr__dry[1] = 1 271 let rc__dry: i64 = gv_verdict("DOC-ENVELOPE-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8) 272 sys_exit(rc__dry) 273 return rc__dry 274}