nx_stun.nx source
↩ module page · 89 lines · 4704 B
1// nx_stun.nx -- SOVEREIGN STUN (RFC 5389 / RFC 8489) message codec, NishiLang from the first byte up.
2//
3// This is the FIRST primitive of a Nishi-OS / Nishi-Browser peer-to-peer stack that INTEROPERATES with the
4// real STUN protocol -- so a Nishi endpoint can discover its public reflexive address and NAT-traverse to
5// ANY standard peer -- while using ZERO third-party code. No libjuice, no libwebrtc, no Google STUN client:
6// every byte is ours, emitted in NishiLang, KAT'd byte-exact against the IETF's own RFC 5769 test vectors.
7//
8// Provides the four things a STUN Binding exchange needs:
9// - the 20-byte message header (type, length, magic cookie 0x2112A442, 96-bit transaction id)
10// - XOR-MAPPED-ADDRESS (RFC 5389 15.2) -- the reflexive IP:port a peer learns about itself
11// - FINGERPRINT (15.5) = CRC-32(message) XOR 0x5354554E (integrity/demux; our own CRC-32)
12// - MESSAGE-INTEGRITY (15.4) = HMAC-SHA1(key, message) (auth; composes the sovereign nx_hmac_sha1)
13//
14// KAT: nx_stun_gate proves decode+encode of the RFC 5769 2.2 sample response byte-exact (XOR-MAPPED-ADDRESS
15// -> 192.0.2.1:32853, FINGERPRINT -> 0xB5BE215B, MESSAGE-INTEGRITY matches with the RFC's password), plus a
16// CRC-32 check-value KAT (crc32("123456789")==0xCBF43926).
17//
18// license_tier: INDEPENDENT_REDERIVE
19// genealogy_id: international-research-sources/ietf/rfc_5389 + rfc_5769 (test vectors)
20import "nx_syscalls.nx"
21import "nx_hmac_sha1.nx" // hmac_sha1(key,klen,msg,mlen,out20) -- composes the sovereign nx_sha1
22
23const STUN_MAGIC: i64 = 0x2112A442
24const STUN_FP_XOR: i64 = 0x5354554E
25const STUN_BINDING_REQUEST: i64 = 0x0001
26const STUN_BINDING_RESPONSE: i64 = 0x0101
27const STUN_ATTR_XOR_MAPPED_ADDRESS: i64 = 0x0020
28const STUN_ATTR_MESSAGE_INTEGRITY: i64 = 0x0008
29const STUN_ATTR_FINGERPRINT: i64 = 0x8028
30
31// ---- big-endian field access (STUN is network byte order) ----
32func st_put16(b: *u8, off: i64, v: i64) -> i64 { b[off]=((v>>8)&0xff) as u8; b[off+1]=(v&0xff) as u8; return off+2 }
33func st_put32(b: *u8, off: i64, v: i64) -> i64 { b[off]=((v>>24)&0xff) as u8; b[off+1]=((v>>16)&0xff) as u8; b[off+2]=((v>>8)&0xff) as u8; b[off+3]=(v&0xff) as u8; return off+4 }
34func st_get16(b: *u8, off: i64) -> i64 { return (((b[off] as i64) & 0xff)<<8) | ((b[off+1] as i64) & 0xff) }
35func st_get32(b: *u8, off: i64) -> i64 {
36 return (((b[off] as i64) & 0xff)<<24) | (((b[off+1] as i64) & 0xff)<<16) | (((b[off+2] as i64) & 0xff)<<8) | ((b[off+3] as i64) & 0xff)
37}
38
39// ---- header: type(2) length(2) magic(4) txid(12) = 20 bytes ----
40func st_write_header(b: *u8, msg_type: i64, msg_len: i64, txid: *u8) -> i64 {
41 st_put16(b, 0, msg_type)
42 st_put16(b, 2, msg_len)
43 st_put32(b, 4, STUN_MAGIC)
44 var i: i64 = 0
45 while i<12 { b[8+i]=txid[i]; i=i+1 }
46 return 20
47}
48func st_type(b: *u8) -> i64 { return st_get16(b, 0) }
49func st_len(b: *u8) -> i64 { return st_get16(b, 2) }
50func st_magic(b: *u8) -> i64 { return st_get32(b, 4) }
51
52// ---- XOR-MAPPED-ADDRESS (IPv4): 00, family(01), port^(magic>>16), addr^magic = 8 bytes ----
53func st_xma_encode(out: *u8, port: i64, addr: i64) -> i64 {
54 out[0]=0 as u8
55 out[1]=1 as u8 // family = IPv4
56 let xport: i64 = (port ^ ((STUN_MAGIC>>16)&0xffff)) & 0xffff
57 st_put16(out, 2, xport)
58 let xaddr: i64 = (addr ^ STUN_MAGIC) & 0xffffffff
59 st_put32(out, 4, xaddr)
60 return 8
61}
62func st_xma_family(val: *u8) -> i64 { return (val[1] as i64) & 0xff }
63func st_xma_port(val: *u8) -> i64 { return (st_get16(val,2) ^ ((STUN_MAGIC>>16)&0xffff)) & 0xffff }
64func st_xma_addr(val: *u8) -> i64 { return (st_get32(val,4) ^ STUN_MAGIC) & 0xffffffff }
65
66// ---- CRC-32 (ISO-HDLC, reflected poly 0xEDB88320) -- the STUN FINGERPRINT CRC. crc32("123456789")=0xCBF43926 ----
67func nx_crc32(data: *u8, len: i64) -> i64 {
68 var crc: i64 = 0xFFFFFFFF
69 var i: i64 = 0
70 while i<len {
71 crc = crc ^ ((data[i] as i64) & 0xff)
72 var k: i64 = 0
73 while k<8 {
74 if (crc & 1)==1 { crc = (crc>>1) ^ 0xEDB88320 } else { crc = crc>>1 }
75 crc = crc & 0xffffffff
76 k=k+1
77 }
78 i=i+1
79 }
80 return (crc ^ 0xFFFFFFFF) & 0xffffffff
81}
82// FINGERPRINT attribute value = CRC-32(message-prefix) XOR 0x5354554E
83func st_fingerprint(msg: *u8, prefix_len: i64) -> i64 { return (nx_crc32(msg, prefix_len) ^ STUN_FP_XOR) & 0xffffffff }
84
85// MESSAGE-INTEGRITY value = HMAC-SHA1(key, message-prefix) -> 20 bytes. The caller sets the header length
86// field to the length up to AND INCLUDING the MESSAGE-INTEGRITY attribute before calling (RFC 5389 15.4).
87func st_message_integrity(key: *u8, klen: i64, msg: *u8, prefix_len: i64, out20: *u8) -> i64 {
88 return hmac_sha1(key, klen, msg, prefix_len, out20)
89}