nx_turn_msgintegrity.nx source
↩ module page · 134 lines · 5196 B
1// nx_turn_msgintegrity.nx -- TURN MESSAGE-INTEGRITY (RFC 5389 ยง15.4).
2// Arc 4 Phase A.4 layer; split from nx_turn_relay.nx so the framing
3// layer can be imported by x86_64 daemons without dragging in
4// nx_hmac_sha1 (which uses the RISC-V-numbered nx_syscalls clan and
5// would clash with nx_udp's nx_syscalls_x86_64 clan).
6//
7// See [[reference-syscall-module-duality-nx_syscalls-vs-syscalls]] for
8// why this split is necessary. Daemons targeting x86_64 import
9// nx_turn_relay (framing only); WASM-target smokes can import both
10// since the JS shim resolves either clan.
11//
12// HMAC-SHA1 over the message, with key = SASLprep(password) for
13// short-term creds. Long-term creds (MD5(user:realm:pw)) queued.
14
15import "nx_turn_relay.nx"
16import "nx_hmac_sha1.nx"
17
18// Append MESSAGE-INTEGRITY to a STUN message in-place.
19// buf: pointer to message buffer (>= cur_len + 24 capacity)
20// cur_len: current message length WITHOUT MI (header + existing attrs)
21// key: HMAC key bytes (short-term: just password ASCII)
22// key_len: key length in bytes
23// Returns new total length (cur_len + 24).
24func nx_turn_append_message_integrity(buf: *u8, cur_len: i64,
25 key: *u8, key_len: i64) -> i64 {
26 // Set Length field to (existing_attrs_len + 24) = cur_len + 4.
27 let new_attr_len: i64 = cur_len + 4
28 buf[2] = (new_attr_len >> 8) & 0xff
29 buf[3] = new_attr_len & 0xff
30 let mac: *u8 = sys_mmap(32)
31 hmac_sha1(key, key_len, buf, cur_len, mac)
32 buf[cur_len] = 0
33 buf[cur_len + 1] = 0x08
34 buf[cur_len + 2] = 0
35 buf[cur_len + 3] = 20
36 var i: i64 = 0
37 while i < 20 {
38 buf[cur_len + 4 + i] = mac[i]
39 i = i + 1
40 }
41 return cur_len + 24
42}
43
44// Verify MESSAGE-INTEGRITY on an incoming STUN message.
45// Returns 0 if valid, non-zero error:
46// 1: too short
47// 2: MI attribute not found
48// 3: MAC mismatch (constant-time XOR-OR compare)
49func nx_turn_verify_message_integrity(buf: *u8, n: i64,
50 key: *u8, key_len: i64) -> i64 {
51 if n < 20 { return 1 }
52 let msg_len: i64 = (buf[2] << 8) | buf[3]
53 if n < 20 + msg_len { return 1 }
54 var pos: i64 = 20
55 let end: i64 = 20 + msg_len
56 var mi_off: i64 = -1
57 while pos + 4 <= end {
58 let attr_type: i64 = (buf[pos] << 8) | buf[pos + 1]
59 let attr_len: i64 = (buf[pos + 2] << 8) | buf[pos + 3]
60 let val_off: i64 = pos + 4
61 if val_off + attr_len > end { return 1 }
62 if attr_type == 0x0008 {
63 if attr_len == 20 {
64 mi_off = val_off
65 }
66 }
67 var padded: i64 = attr_len
68 let rem: i64 = attr_len & 3
69 if rem != 0 { padded = attr_len + (4 - rem) }
70 pos = val_off + padded
71 }
72 if mi_off < 0 { return 2 }
73 let saved_len_hi: i64 = buf[2]
74 let saved_len_lo: i64 = buf[3]
75 buf[2] = (mi_off >> 8) & 0xff
76 buf[3] = mi_off & 0xff
77 let mac: *u8 = sys_mmap(32)
78 hmac_sha1(key, key_len, buf, mi_off - 4, mac)
79 buf[2] = saved_len_hi
80 buf[3] = saved_len_lo
81 var diff: i64 = 0
82 var i: i64 = 0
83 while i < 20 {
84 let a: i64 = mac[i]
85 let b: i64 = buf[mi_off + i]
86 diff = diff | (a ^ b)
87 i = i + 1
88 }
89 if diff != 0 { return 3 }
90 return 0
91}
92
93// Smoke export: build ALLOCATE request, append MI with a known key,
94// verify MI; tamper-detect + wrong-key-detect. Returns 0 on full
95// success, non-zero bitmap otherwise.
96func nx_turn_message_integrity_test(scratch: *u8) -> i64 {
97 let buf: *u8 = scratch
98 let key: *u8 = (scratch as i64 + 4096) as *u8
99 let tx_id: *u8 = (scratch as i64 + 4160) as *u8
100 let pw_bytes: i64 = 16
101 var pi: i64 = 0
102 while pi < pw_bytes { key[pi] = 0x70 + pi; pi = pi + 1 }
103 var ti: i64 = 0
104 while ti < 12 { tx_id[ti] = 0xc0 + ti; ti = ti + 1 }
105 // ALLOCATE request bytes (28 total)
106 buf[0] = 0; buf[1] = 0x03 // ALLOCATE
107 buf[2] = 0; buf[3] = 8 // attr length = 8
108 buf[4] = 0x21; buf[5] = 0x12; buf[6] = 0xa4; buf[7] = 0x42
109 var bi: i64 = 0
110 while bi < 12 { buf[8 + bi] = tx_id[bi]; bi = bi + 1 }
111 buf[20] = 0; buf[21] = 0x19 // REQUESTED-TRANSPORT
112 buf[22] = 0; buf[23] = 4
113 buf[24] = 17 // UDP
114 buf[25] = 0; buf[26] = 0; buf[27] = 0
115 let cur_len: i64 = 28
116 let total: i64 = nx_turn_append_message_integrity(buf, cur_len, key, pw_bytes)
117 var fail: i64 = 0
118 if total != cur_len + 24 { fail = fail | 1 }
119 let v_ok: i64 = nx_turn_verify_message_integrity(buf, total, key, pw_bytes)
120 if v_ok != 0 { fail = fail | 2 }
121 let orig_byte: i64 = buf[24]
122 buf[24] = (buf[24] ^ 0xff) & 0xff
123 let v_tamper: i64 = nx_turn_verify_message_integrity(buf, total, key, pw_bytes)
124 if v_tamper == 0 { fail = fail | 4 }
125 buf[24] = orig_byte
126 let orig_k: i64 = key[5]
127 key[5] = (key[5] ^ 0xff) & 0xff
128 let v_wrongkey: i64 = nx_turn_verify_message_integrity(buf, total, key, pw_bytes)
129 if v_wrongkey == 0 { fail = fail | 8 }
130 key[5] = orig_k
131 let v_again: i64 = nx_turn_verify_message_integrity(buf, total, key, pw_bytes)
132 if v_again != 0 { fail = fail | 16 }
133 return fail
134}