nx_accept_release_binding.nx source
↩ module page · 88 lines · 4120 B
1// Candidate additive acceptance binding for nx_accept owner; legacy subject records unchanged.
2// This predicate verifies evidence identity, not evaluator authority. The workflow must authorize
3// the evidence producer and retain stage-specific policy/test receipts. No implicit promotion.
4// Wire schema (exactly seven TSV fields, one terminal LF):
5// acceptance-release/v1 SUBJECT CANDIDATE_SHA256 STAGE VERDICT POLICY_SHA256 EVIDENCE_REF
6// EVIDENCE_REF identifies the detailed test bundle; its contents are validated by the workflow.
7// Expected receipt SHA256 binds every byte, including the evidence reference.
8import "nx_sha256.nx"
9
10const ARB_ACCEPTED: i64 = 0
11const ARB_INCOMPLETE: i64 = 1
12const ARB_REJECTED: i64 = 2
13const ARB_INVALID: i64 = 3
14const ARB_IDENTITY: i64 = 4
15const ARB_IO: i64 = 5
16const ARB_FIELDS: i64 = 7
17const ARB_SPAN_WORDS: i64 = 2
18const ARB_WORD_BYTES: i64 = 8
19const ARB_HASH_HEX: i64 = SHA256_DIGEST_BYTES*2
20
21func arb_len(s: *u8) -> i64 {
22 if (s as i64)<=0{return 0};var n:i64=0;while s[n]!=0{n=n+1};return n
23}
24func arb_eq(a:*u8,an:i64,b:*u8,bn:i64)->i64 {
25 if an!=bn{return 0};var i:i64=0;while i<an{if a[i]!=b[i]{return 0};i=i+1};return 1
26}
27func arb_hex_ok(s:*u8,n:i64)->i64 {
28 if n!=ARB_HASH_HEX{return 0};var i:i64=0
29 while i<n {let c:i64=s[i] as i64;if (c<48||c>57)&&(c<97||c>102){return 0};i=i+1};return 1
30}
31func arb_stage(s:*u8,n:i64)->i64 {
32 if arb_eq(s,n,"alpha",5)==1{return 1}
33 if arb_eq(s,n,"beta",4)==1{return 1}
34 return arb_eq(s,n,"production",10)
35}
36func arb_field_eq(b:*u8,fields:*i64,field:i64,s:*u8)->i64 {
37 return arb_eq(((b as i64)+fields[field*ARB_SPAN_WORDS]) as *u8,fields[field*ARB_SPAN_WORDS+1],s,arb_len(s))
38}
39func arb_parse(b:*u8,n:i64,fields:*i64)->i64 {
40 if n<=0||(b as i64)<=0{return ARB_INCOMPLETE}
41 var i:i64=0;var start:i64=0;var f:i64=0
42 while i<n {
43 let c:i64=b[i] as i64
44 if c==9||c==10 {
45 if f>=ARB_FIELDS||i==start{return ARB_INVALID}
46 fields[f*ARB_SPAN_WORDS]=start;fields[f*ARB_SPAN_WORDS+1]=i-start;f=f+1;start=i+1
47 if c==10 {if i!=n-1||f!=ARB_FIELDS{return ARB_INVALID};return ARB_ACCEPTED}
48 } else {if c<32||c==127{return ARB_INVALID}}
49 i=i+1
50 }
51 return ARB_INVALID
52}
53func arb_check_fields(subject:*u8,candidate:*u8,stage:*u8,policy:*u8,b:*u8,n:i64,fields:*i64)->i64 {
54 let parsed:i64=arb_parse(b,n,fields);if parsed!=ARB_ACCEPTED{return parsed}
55 if arb_field_eq(b,fields,0,"acceptance-release/v1")!=1{return ARB_INVALID}
56 if arb_field_eq(b,fields,1,subject)!=1{return ARB_IDENTITY}
57 if arb_field_eq(b,fields,2,candidate)!=1{return ARB_IDENTITY}
58 if arb_field_eq(b,fields,3,stage)!=1{return ARB_IDENTITY}
59 if arb_field_eq(b,fields,5,policy)!=1{return ARB_IDENTITY}
60 if arb_field_eq(b,fields,4,"incomplete")==1{return ARB_INCOMPLETE}
61 if arb_field_eq(b,fields,4,"fail")==1{return ARB_REJECTED}
62 if arb_field_eq(b,fields,4,"pass")!=1{return ARB_INVALID}
63 return ARB_ACCEPTED
64}
65func arb_receipt(subject:*u8,candidate:*u8,stage:*u8,policy:*u8,receipt_sha:*u8,b:*u8,n:i64)->i64 {
66 if arb_len(subject)==0||arb_stage(stage,arb_len(stage))!=1{return ARB_INVALID}
67 if arb_hex_ok(candidate,arb_len(candidate))!=1||arb_hex_ok(policy,arb_len(policy))!=1{return ARB_INVALID}
68 if arb_hex_ok(receipt_sha,arb_len(receipt_sha))!=1{return ARB_INVALID}
69 if n<=0||(b as i64)<=0{return ARB_INCOMPLETE}
70 let digest:*u8=sys_mmap(SHA256_DIGEST_BYTES)
71 if (digest as i64)<=0{return ARB_IO}
72 let hashed:i64=sha256_digest_checked_native(b,n,digest)
73 if hashed!=0{sys_munmap(digest,SHA256_DIGEST_BYTES);return ARB_IO}
74 let alphabet:*u8="0123456789abcdef"
75 var i:i64=0;var matches:i64=1
76 while i<SHA256_DIGEST_BYTES {
77 let c:i64=digest[i] as i64
78 if alphabet[(c>>4)&15]!=receipt_sha[i*2]||alphabet[c&15]!=receipt_sha[i*2+1]{matches=0}
79 i=i+1
80 }
81 let released:i64=sys_munmap(digest,SHA256_DIGEST_BYTES)
82 if released!=0{return ARB_IO};if matches!=1{return ARB_IDENTITY}
83 let fields:*i64=sys_mmap(ARB_FIELDS*ARB_SPAN_WORDS*ARB_WORD_BYTES) as *i64
84 if (fields as i64)<=0{return ARB_IO}
85 let result:i64=arb_check_fields(subject,candidate,stage,policy,b,n,fields)
86 if sys_munmap(fields as *u8,ARB_FIELDS*ARB_SPAN_WORDS*ARB_WORD_BYTES)!=0{return ARB_IO}
87 return result
88}