nx_ws_crypto.nx source
↩ module page · 226 lines · 6877 B
1// nx_ws_crypto.nx -- minimal SHA-1 + Base64 used by nx_websocket.
2// Standalone copy that imports nx_syscalls_x86_64.nx directly so the
3// x86_64 codec/comms toolchain doesn't pull in the RV64 syscall
4// numbers via runtime/nx_sha1.nx + runtime/nx_base64.nx (which
5// import "nx_syscalls.nx" -- the RV64 file). Same math, same
6// constants; functions renamed `_wsx_*` to avoid symbol collision
7// when both ecosystems coexist in one binary.
8//
9// genealogy_id: rfc_3174_sha1 + rfc_4648_base64 + nx_sha1_q10 + nx_base64_q10
10// lineage_id: nishi_ws_crypto_q10
11
12// nx_safety_envelope:
13// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
14// sil_target: SIL1
15// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
16// verdict: NOT_YET_EVALUATED
17
18import "nx_syscalls_x86_64.nx"
19
20const _WSX_SHA1_H0: i64 = 0x67452301
21const _WSX_SHA1_H1: i64 = 0xEFCDAB89
22const _WSX_SHA1_H2: i64 = 0x98BADCFE
23const _WSX_SHA1_H3: i64 = 0x10325476
24const _WSX_SHA1_H4: i64 = 0xC3D2E1F0
25
26const _WSX_SHA1_K0: i64 = 0x5A827999
27const _WSX_SHA1_K1: i64 = 0x6ED9EBA1
28const _WSX_SHA1_K2: i64 = 0x8F1BBCDC
29const _WSX_SHA1_K3: i64 = 0xCA62C1D6
30
31const _WSX_M32: i64 = 0xFFFFFFFF
32
33func _wsx_rotl(x: i64, k: i64) -> i64 {
34 let x32: i64 = x & _WSX_M32
35 let lo: i64 = (x32 << k) & _WSX_M32
36 // BUG FIX (2026-05-15): original mask `(1 << (32 - k)) - 1` was
37 // inverted -- it kept only the LOW (32 - k) bits of the shifted-down
38 // top, dropping the actual rotated-in bits. Just mask to MASK32
39 // (the shifted-down value is at most 32 bits anyway).
40 let hi: i64 = (x32 >> (32 - k)) & _WSX_M32
41 return lo | hi
42}
43
44func _wsx_sha1_block(buf: *u8, off: i64, state: *i64) -> i64 {
45 let w_raw: *u8 = sys_mmap(80 * 8)
46 let w: *i64 = w_raw as *i64
47
48 var t: i64 = 0
49 while t < 16 {
50 let b0: i64 = buf[off + t * 4]
51 let b1: i64 = buf[off + t * 4 + 1]
52 let b2: i64 = buf[off + t * 4 + 2]
53 let b3: i64 = buf[off + t * 4 + 3]
54 w[t] = ((b0 << 24) | (b1 << 16) | (b2 << 8) | b3) & _WSX_M32
55 t = t + 1
56 }
57 t = 16
58 while t < 80 {
59 let v: i64 = w[t-3] ^ w[t-8] ^ w[t-14] ^ w[t-16]
60 w[t] = _wsx_rotl(v, 1)
61 t = t + 1
62 }
63
64 var a: i64 = state[0]
65 var b: i64 = state[1]
66 var c: i64 = state[2]
67 var d: i64 = state[3]
68 var e: i64 = state[4]
69
70 t = 0
71 while t < 80 {
72 var f: i64 = 0
73 var k: i64 = 0
74 if t < 20 {
75 f = (b & c) | ((b ^ _WSX_M32) & d)
76 k = _WSX_SHA1_K0
77 }
78 if t >= 20 {
79 if t < 40 {
80 f = b ^ c ^ d
81 k = _WSX_SHA1_K1
82 }
83 }
84 if t >= 40 {
85 if t < 60 {
86 f = (b & c) | (b & d) | (c & d)
87 k = _WSX_SHA1_K2
88 }
89 }
90 if t >= 60 {
91 f = b ^ c ^ d
92 k = _WSX_SHA1_K3
93 }
94
95 let temp: i64 = (_wsx_rotl(a, 5) + f + e + k + w[t]) & _WSX_M32
96 e = d
97 d = c
98 c = _wsx_rotl(b, 30)
99 b = a
100 a = temp
101 t = t + 1
102 }
103
104 state[0] = (state[0] + a) & _WSX_M32
105 state[1] = (state[1] + b) & _WSX_M32
106 state[2] = (state[2] + c) & _WSX_M32
107 state[3] = (state[3] + d) & _WSX_M32
108 state[4] = (state[4] + e) & _WSX_M32
109 return 0
110}
111
112func _wsx_if_ge(a: i64, b: i64, v1: i64, v2: i64) -> i64 {
113 if a >= b { return v1 }
114 return v2
115}
116
117// Compute SHA-1 of data[0..n] into out[0..20].
118func nx_wsx_sha1(data: *u8, n: i64, out: *u8) -> i64 {
119 let state_raw: *u8 = sys_mmap(40)
120 let state: *i64 = state_raw as *i64
121 state[0] = _WSX_SHA1_H0
122 state[1] = _WSX_SHA1_H1
123 state[2] = _WSX_SHA1_H2
124 state[3] = _WSX_SHA1_H3
125 state[4] = _WSX_SHA1_H4
126
127 let full_blocks: i64 = n / 64
128 var i: i64 = 0
129 while i < full_blocks {
130 _wsx_sha1_block(data, i * 64, state)
131 i = i + 1
132 }
133
134 let tail_off: i64 = full_blocks * 64
135 let tail_len: i64 = n - tail_off
136
137 let pad_size: i64 = 128
138 let pad_raw: *u8 = sys_mmap(pad_size)
139 var j: i64 = 0
140 while j < tail_len {
141 pad_raw[j] = data[tail_off + j]
142 j = j + 1
143 }
144 pad_raw[tail_len] = 0x80
145 j = tail_len + 1
146
147 let blocks_needed: i64 = _wsx_if_ge(tail_len + 1, 57, 2, 1)
148 let total_padded: i64 = blocks_needed * 64
149 while j < total_padded - 8 {
150 pad_raw[j] = 0
151 j = j + 1
152 }
153 let bits: i64 = n * 8
154 pad_raw[total_padded - 8] = (bits >> 56) & 0xFF
155 pad_raw[total_padded - 7] = (bits >> 48) & 0xFF
156 pad_raw[total_padded - 6] = (bits >> 40) & 0xFF
157 pad_raw[total_padded - 5] = (bits >> 32) & 0xFF
158 pad_raw[total_padded - 4] = (bits >> 24) & 0xFF
159 pad_raw[total_padded - 3] = (bits >> 16) & 0xFF
160 pad_raw[total_padded - 2] = (bits >> 8) & 0xFF
161 pad_raw[total_padded - 1] = bits & 0xFF
162
163 var bb: i64 = 0
164 while bb < blocks_needed {
165 _wsx_sha1_block(pad_raw, bb * 64, state)
166 bb = bb + 1
167 }
168
169 var w2: i64 = 0
170 while w2 < 5 {
171 out[w2 * 4] = (state[w2] >> 24) & 0xFF
172 out[w2 * 4 + 1] = (state[w2] >> 16) & 0xFF
173 out[w2 * 4 + 2] = (state[w2] >> 8) & 0xFF
174 out[w2 * 4 + 3] = state[w2] & 0xFF
175 w2 = w2 + 1
176 }
177 return 0
178}
179
180// Base64 standard alphabet.
181const _WSX_B64_PAD: i64 = 0x3D
182
183func _wsx_b64_char(n: i64) -> i64 {
184 let v: i64 = n & 0x3F
185 if v < 26 { return 0x41 + v }
186 if v < 52 { return 0x61 + (v - 26) }
187 if v < 62 { return 0x30 + (v - 52) }
188 if v == 62 { return 0x2B }
189 return 0x2F
190}
191
192// Encode `n` bytes from `in_bytes` to `out`; returns written length.
193func nx_wsx_b64_encode(in_bytes: *u8, n: i64, out: *u8) -> i64 {
194 var pos: i64 = 0
195 var out_pos: i64 = 0
196 while pos + 3 <= n {
197 let b0: i64 = in_bytes[pos]
198 let b1: i64 = in_bytes[pos + 1]
199 let b2: i64 = in_bytes[pos + 2]
200 out[out_pos + 0] = _wsx_b64_char((b0 >> 2) & 0x3F)
201 out[out_pos + 1] = _wsx_b64_char(((b0 << 4) | (b1 >> 4)) & 0x3F)
202 out[out_pos + 2] = _wsx_b64_char(((b1 << 2) | (b2 >> 6)) & 0x3F)
203 out[out_pos + 3] = _wsx_b64_char(b2 & 0x3F)
204 pos = pos + 3
205 out_pos = out_pos + 4
206 }
207 let remain: i64 = n - pos
208 if remain == 1 {
209 let b0: i64 = in_bytes[pos]
210 out[out_pos + 0] = _wsx_b64_char((b0 >> 2) & 0x3F)
211 out[out_pos + 1] = _wsx_b64_char((b0 << 4) & 0x3F)
212 out[out_pos + 2] = _WSX_B64_PAD
213 out[out_pos + 3] = _WSX_B64_PAD
214 out_pos = out_pos + 4
215 }
216 if remain == 2 {
217 let b0: i64 = in_bytes[pos]
218 let b1: i64 = in_bytes[pos + 1]
219 out[out_pos + 0] = _wsx_b64_char((b0 >> 2) & 0x3F)
220 out[out_pos + 1] = _wsx_b64_char(((b0 << 4) | (b1 >> 4)) & 0x3F)
221 out[out_pos + 2] = _wsx_b64_char((b1 << 2) & 0x3F)
222 out[out_pos + 3] = _WSX_B64_PAD
223 out_pos = out_pos + 4
224 }
225 return out_pos
226}