code wiki / (root) / nx_stun.nx

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}