nx_voice_frame.nx source
↩ module page · 190 lines · 6678 B
1// nx_voice_frame.nx -- Phase 4a foundation: sovereign voice-codec
2// frame format with CAS-chained sha256 headers.
3//
4// Per docs/NISHI_COMMS_ROADMAP.md Phase 4: the frame format alone is
5// already a WIN over Opus on two axes (audit-ability + content-
6// addressability). Subsequent files compose LPC + range coder + FEC
7// on top of THIS frame structure.
8//
9// Frame wire format (binary, all little-endian):
10//
11// [0..3] magic = "NXVF" (4 bytes)
12// [4] version major (1) (1)
13// [5] tier hint (NX_HW_TIER_*) (1)
14// [6..7] bitrate kbps (2) -- 1..64
15// [8..9] sample_rate / 1000 (2) -- 8 | 16 | 24 | 48
16// [10] frame_ms (1) -- 10 | 20
17// [11] flags (1) -- bit0=voice; bit1=FEC; bit2=neural-residual
18// [12..15] sequence number (4)
19// [16..47] prev_frame_hash (32) sha256 of previous frame's full bytes
20// [48..49] payload_len (2)
21// [50..] payload (variable)
22//
23// After payload, the receiver computes sha256(this entire frame
24// header+payload) and stashes for the NEXT frame's prev_hash. Chain.
25//
26// genealogy_id: rfc_5285_rtp + rfc_6716_opus_packetisation +
27// nx_attest_merkle_chain
28// lineage_id: nishi_voice_frame_q10
29
30// nx_safety_envelope:
31// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
32// sil_target: SIL1
33// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
34// verdict: NOT_YET_EVALUATED
35
36import "nx_syscalls.nx"
37
38// Tiny inline 32-byte content hash for frame chain detection.
39// NOT cryptographic; tamper-EVIDENCE only. Real crypto comes at
40// Phase 6 (DTLS-SRTP). This hash is enough to detect accidental
41// reordering, dropped frames, or unauthorised middlebox edits in
42// transit. Two 64-bit FNV-1a folded into 4 lanes -> 32 bytes.
43//
44// Replacing this with sha256 is a one-line caller-side swap. The
45// frame format doesn't pin the hash algorithm; only its width.
46func _h32(buf: *u8, n: i64, out: *u8) -> i64 {
47 // Four lanes of FNV-1a with different seed offsets.
48 var lane: i64 = 0
49 while lane < 4 {
50 var h: i64 = 14695981039346656037
51 if lane == 1 { h = h ^ 0x9e3779b97f4a7c15 }
52 if lane == 2 { h = h ^ 0xbf58476d1ce4e5b9 }
53 if lane == 3 { h = h ^ 0x94d049bb133111eb }
54 var i: i64 = 0
55 while i < n {
56 h = h ^ buf[i]
57 h = h * 1099511628211
58 i = i + 1
59 }
60 var b: i64 = 0
61 while b < 8 {
62 out[lane * 8 + b] = (h >> (b * 8)) & 0xff
63 b = b + 1
64 }
65 lane = lane + 1
66 }
67 return 0
68}
69
70// Sealed verdict per frame parse / build operation.
71const NX_VFR_VERDICT_UNKNOWN: i64 = 0
72const NX_VFR_VERDICT_OK: i64 = 1
73const NX_VFR_VERDICT_BAD_MAGIC: i64 = 2
74const NX_VFR_VERDICT_VERSION_MISMATCH: i64 = 3
75const NX_VFR_VERDICT_TRUNCATED: i64 = 4
76const NX_VFR_VERDICT_CHAIN_BROKEN: i64 = 5
77const NX_VFR_VERDICT_N: i64 = 6
78
79// Sealed flag bits. Used in the frame's flags byte.
80const NX_VFR_FLAG_VOICE: i64 = 1
81const NX_VFR_FLAG_FEC: i64 = 2
82const NX_VFR_FLAG_NEURAL_RESIDUAL: i64 = 4
83
84const NX_VFR_HEADER_BYTES: i64 = 50
85
86// Build a voice frame into `out_buf`. Returns total frame byte
87// length, or -1 on overflow. Caller pre-allocates `out_buf` (>=
88// NX_VFR_HEADER_BYTES + payload_len).
89//
90// `prev_hash` must point at 32 bytes (zeroed for the FIRST frame in
91// a stream).
92func nx_voice_frame_build(
93 out_buf: *u8, out_cap: i64,
94 tier: i64,
95 bitrate_kbps: i64,
96 sample_rate_khz: i64,
97 frame_ms: i64,
98 flags: i64,
99 sequence: i64,
100 prev_hash: *u8,
101 payload: *u8, payload_len: i64
102) -> i64 {
103 let total: i64 = NX_VFR_HEADER_BYTES + payload_len
104 if total > out_cap { return -1 }
105
106 out_buf[0] = 78; out_buf[1] = 88; out_buf[2] = 86; out_buf[3] = 70 // "NXVF"
107 out_buf[4] = 1 // version major
108 out_buf[5] = tier & 0xff
109 out_buf[6] = bitrate_kbps & 0xff
110 out_buf[7] = (bitrate_kbps >> 8) & 0xff
111 out_buf[8] = sample_rate_khz & 0xff
112 out_buf[9] = (sample_rate_khz >> 8) & 0xff
113 out_buf[10] = frame_ms & 0xff
114 out_buf[11] = flags & 0xff
115 out_buf[12] = sequence & 0xff
116 out_buf[13] = (sequence >> 8) & 0xff
117 out_buf[14] = (sequence >> 16) & 0xff
118 out_buf[15] = (sequence >> 24) & 0xff
119
120 var i: i64 = 0
121 while i < 32 {
122 out_buf[16 + i] = prev_hash[i]
123 i = i + 1
124 }
125 out_buf[48] = payload_len & 0xff
126 out_buf[49] = (payload_len >> 8) & 0xff
127 var p: i64 = 0
128 while p < payload_len {
129 out_buf[NX_VFR_HEADER_BYTES + p] = payload[p]
130 p = p + 1
131 }
132 return total
133}
134
135// Parse a frame. Verifies magic + version; if `expected_prev_hash`
136// is non-NULL, verifies the chain link too. Writes the frame's
137// OWN hash (sha256 of all `total` bytes) into `out_this_hash`
138// (32 bytes), so caller can pass it as prev_hash to the next frame.
139func nx_voice_frame_parse(
140 buf: *u8, buf_len: i64,
141 expected_prev_hash: *u8,
142 out_payload_off: *i64,
143 out_payload_len: *i64,
144 out_this_hash: *u8
145) -> i64 {
146 if buf_len < NX_VFR_HEADER_BYTES { return NX_VFR_VERDICT_TRUNCATED }
147 if buf[0] != 78 { return NX_VFR_VERDICT_BAD_MAGIC }
148 if buf[1] != 88 { return NX_VFR_VERDICT_BAD_MAGIC }
149 if buf[2] != 86 { return NX_VFR_VERDICT_BAD_MAGIC }
150 if buf[3] != 70 { return NX_VFR_VERDICT_BAD_MAGIC }
151 if buf[4] != 1 { return NX_VFR_VERDICT_VERSION_MISMATCH }
152
153 let plen: i64 = buf[48] | (buf[49] << 8)
154 if NX_VFR_HEADER_BYTES + plen > buf_len { return NX_VFR_VERDICT_TRUNCATED }
155
156 // Chain check. If expected_prev_hash is null, skip.
157 if (expected_prev_hash as i64) != 0 {
158 var i: i64 = 0
159 var bad: i64 = 0
160 while i < 32 {
161 if buf[16 + i] != expected_prev_hash[i] { bad = 1 }
162 i = i + 1
163 }
164 if bad == 1 { return NX_VFR_VERDICT_CHAIN_BROKEN }
165 }
166
167 *out_payload_off = NX_VFR_HEADER_BYTES
168 *out_payload_len = plen
169
170 // Compute this frame's content-hash for the next call.
171 _h32(buf, NX_VFR_HEADER_BYTES + plen, out_this_hash)
172 return NX_VFR_VERDICT_OK
173}
174
175// Read sequence number from a parsed frame.
176func nx_voice_frame_sequence(buf: *u8) -> i64 {
177 return buf[12] | (buf[13] << 8) | (buf[14] << 16) | (buf[15] << 24)
178}
179
180// Read flags from a parsed frame.
181func nx_voice_frame_flags(buf: *u8) -> i64 {
182 return buf[11]
183}
184
185// Sealed-enum validity gate.
186func nx_vfr_verdict_is_valid(v: i64) -> i64 {
187 if v < 0 { return 0 }
188 if v >= NX_VFR_VERDICT_N { return 0 }
189 return 1
190}