code wiki / (root) / nx_turn_msgintegrity.nx

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}