code wiki / _hdl_build / nx_tor_cell.nx

nx_tor_cell.nx source

↩ module page · 172 lines · 8791 B

1// nx_tor_cell.nx -- SOVEREIGN Tor link-protocol CELL codec (tor-spec.txt sec.3 + sec.4 handshake cells). 2// Phase 2 of the anonymizing-transport arc: the byte framing that carries the Tor link handshake 3// (VERSIONS / CERTS / AUTH_CHALLENGE / NETINFO) over the ALREADY-BUILT sovereign TLS 1.3 client 4// (nx_tls13_client_session_run gives the encrypted app-data channel; these cells are its plaintext). 5// 6// Cell shapes (tor-spec sec.3): 7// FIXED-length cell: CircID[CIRCID_LEN] | Command[1] | Payload[509] (512B if CircID=2 [link v<=3], 8// 514B if CircID=4 [link v4+]) 9// VARIABLE-length cell: CircID[CIRCID_LEN] | Command[1] | Length[2] | Payload[Length] 10// VERSIONS (cmd 7) is variable-length and ALWAYS uses a 2-byte CircID (sent before version negotiation). 11// 12// HONEST SCOPE: this is the CELL FRAMING (encode/decode), gated byte-exact vs the spec layout. It is NOT a 13// live Tor connection (no relay reachable; zero external calls). anon_is_anonymizing() stays 0. Sovereign: 14// nx_syscalls only (pure byte codec). license_tier: ORIGINAL 15import "nx_syscalls.nx" 16 17// ---- cell commands (tor-spec sec.3/sec.5) ---- 18const TCELL_PADDING: i64 = 0 19const TCELL_CREATE: i64 = 1 20const TCELL_CREATED: i64 = 2 21const TCELL_RELAY: i64 = 3 22const TCELL_DESTROY: i64 = 4 23const TCELL_CREATE_FAST: i64 = 5 24const TCELL_CREATED_FAST: i64 = 6 25const TCELL_VERSIONS: i64 = 7 // variable-length; CircID always 2 bytes 26const TCELL_NETINFO: i64 = 8 // fixed-length 27const TCELL_RELAY_EARLY: i64 = 9 28const TCELL_CREATE2: i64 = 10 // ntor onion skin rides here (Phase 3) 29const TCELL_CREATED2: i64 = 11 30const TCELL_VPADDING: i64 = 128 // variable-length 31const TCELL_CERTS: i64 = 129 // variable-length 32const TCELL_AUTH_CHALLENGE: i64 = 130 // variable-length 33const TCELL_AUTHENTICATE: i64 = 131 // variable-length 34 35const TCELL_PAYLOAD_LEN: i64 = 509 // fixed-cell payload is always 509 bytes (tor-spec sec.3) 36const TCELL_NETINFO_ATYPE_IPV4: i64 = 4 37const TCELL_NETINFO_ATYPE_IPV6: i64 = 6 38 39const TCELL_E_SHORT: i64 = 0 - 1 // buffer shorter than the declared/required length 40const TCELL_E_CMD: i64 = 0 - 2 // unexpected command 41const TCELL_E_FORMAT: i64 = 0 - 3 // malformed field (odd length, overrun, ...) 42 43// ---- big-endian field helpers ---- 44func tc_put_u16(buf: *u8, off: i64, v: i64) -> i64 { buf[off]=((v>>8)&0xff) as u8; buf[off+1]=(v&0xff) as u8; return off+2 } 45func tc_put_u32(buf: *u8, off: i64, v: i64) -> i64 { buf[off]=((v>>24)&0xff) as u8; buf[off+1]=((v>>16)&0xff) as u8; buf[off+2]=((v>>8)&0xff) as u8; buf[off+3]=(v&0xff) as u8; return off+4 } 46func tc_get_u16(buf: *u8, off: i64) -> i64 { return ((buf[off] as i64)<<8) | (buf[off+1] as i64) } 47func tc_get_u32(buf: *u8, off: i64) -> i64 { return ((buf[off] as i64)<<24)|((buf[off+1] as i64)<<16)|((buf[off+2] as i64)<<8)|(buf[off+3] as i64) } 48 49// circid header length for a negotiated link version: 4 for v4+, else 2 (tor-spec sec.3). 50func tc_circid_len(link_version: i64) -> i64 { if link_version >= 4 { return 4 } return 2 } 51 52// ---- generic framing ---- 53 54// FIXED-length cell: CircID | Command | Payload(paylen, zero-padded to 509). Returns total cell length. 55func tc_build_fixed(circid: i64, cmd: i64, payload: *u8, paylen: i64, circid_len: i64, out: *u8) -> i64 { 56 var o: i64 = 0 57 if circid_len == 4 { o = tc_put_u32(out, o, circid) } else { o = tc_put_u16(out, o, circid) } 58 out[o] = cmd as u8; o = o + 1 59 var i: i64 = 0 60 while i < paylen { out[o+i] = payload[i]; i = i + 1 } 61 while i < TCELL_PAYLOAD_LEN { out[o+i] = 0 as u8; i = i + 1 } 62 return o + TCELL_PAYLOAD_LEN 63} 64 65// VARIABLE-length cell: CircID | Command | Length[2] | Payload[Length]. Returns total cell length. 66func tc_build_var(circid: i64, cmd: i64, payload: *u8, paylen: i64, circid_len: i64, out: *u8) -> i64 { 67 var o: i64 = 0 68 if circid_len == 4 { o = tc_put_u32(out, o, circid) } else { o = tc_put_u16(out, o, circid) } 69 out[o] = cmd as u8; o = o + 1 70 o = tc_put_u16(out, o, paylen) 71 var i: i64 = 0 72 while i < paylen { out[o+i] = payload[i]; i = i + 1 } 73 return o + paylen 74} 75 76// parse a VARIABLE-length cell header; writes cmd + payload offset + payload length via out params. 77// Returns total cell length (header+payload) on success, negative on truncation. 78func tc_parse_var(buf: *u8, buflen: i64, circid_len: i64, out_cmd: *i64, out_ploff: *i64, out_plen: *i64) -> i64 { 79 let hdr: i64 = circid_len + 3 // CircID + CMD + Length 80 if buflen < hdr { return TCELL_E_SHORT } 81 out_cmd[0] = buf[circid_len] as i64 82 let plen: i64 = tc_get_u16(buf, circid_len + 1) 83 if buflen < hdr + plen { return TCELL_E_SHORT } 84 out_ploff[0] = hdr 85 out_plen[0] = plen 86 return hdr + plen 87} 88 89// ---- VERSIONS (cmd 7) ---- 90// Build a VERSIONS cell offering `vers[0..n)`. CircID = 2 bytes (always, pre-negotiation). Returns length. 91func tc_build_versions(vers: *i64, n: i64, out: *u8) -> i64 { 92 let pl: *u8 = sys_mmap(256); var po: i64 = 0 93 var i: i64 = 0 94 while i < n { po = tc_put_u16(pl, po, vers[i]); i = i + 1 } 95 return tc_build_var(0, TCELL_VERSIONS, pl, po, 2, out) 96} 97 98// Parse a VERSIONS cell (2-byte CircID). Extracts up to `max` versions into out_vers. Returns count, or neg. 99func tc_parse_versions(buf: *u8, buflen: i64, out_vers: *i64, max: i64) -> i64 { 100 if buflen < 5 { return TCELL_E_SHORT } 101 if (buf[2] as i64) != TCELL_VERSIONS { return TCELL_E_CMD } 102 let plen: i64 = tc_get_u16(buf, 3) 103 if buflen < 5 + plen { return TCELL_E_SHORT } 104 if (plen % 2) != 0 { return TCELL_E_FORMAT } 105 var n: i64 = plen / 2 106 var i: i64 = 0 107 while i < n { if i < max { out_vers[i] = tc_get_u16(buf, 5 + i*2) } i = i + 1 } 108 return n 109} 110 111// Pick the highest common link version between two version lists (0 if none). 112func tc_negotiate_version(a: *i64, na: i64, b: *i64, nb: i64) -> i64 { 113 var best: i64 = 0; var i: i64 = 0 114 while i < na { var j: i64 = 0; while j < nb { if a[i]==b[j] { if a[i]>best { best=a[i] } } j=j+1 } i=i+1 } 115 return best 116} 117 118// ---- NETINFO (cmd 8, fixed) ---- 119// Client form: Timestamp[4] | OtherAddr(type=4,len=4,ip) | MyAddrCount=0. Padded to 509. Returns cell length. 120func tc_build_netinfo(circid_len: i64, timestamp: i64, other_ip4: *u8, out: *u8) -> i64 { 121 let pl: *u8 = sys_mmap(64); var po: i64 = 0 122 po = tc_put_u32(pl, po, timestamp) 123 pl[po] = TCELL_NETINFO_ATYPE_IPV4 as u8; po = po + 1 // OtherAddr type = IPv4 124 pl[po] = 4 as u8; po = po + 1 // OtherAddr length = 4 125 var i: i64 = 0 126 while i < 4 { pl[po] = other_ip4[i]; po = po + 1; i = i + 1 } 127 pl[po] = 0 as u8; po = po + 1 // MyAddrCount = 0 (client) 128 return tc_build_fixed(0, TCELL_NETINFO, pl, po, circid_len, out) 129} 130 131// Parse NETINFO payload: extract timestamp + the OtherAddr (type/len/value). Returns 0 or neg. 132func tc_parse_netinfo(pl: *u8, pllen: i64, out_ts: *i64, out_atype: *i64, out_addr: *u8, out_alen: *i64) -> i64 { 133 if pllen < 6 { return TCELL_E_SHORT } 134 out_ts[0] = tc_get_u32(pl, 0) 135 let atype: i64 = pl[4] as i64 136 let alen: i64 = pl[5] as i64 137 if pllen < 6 + alen { return TCELL_E_SHORT } 138 out_atype[0] = atype; out_alen[0] = alen 139 var i: i64 = 0 140 while i < alen { out_addr[i] = pl[6 + i]; i = i + 1 } 141 return 0 142} 143 144// ---- CERTS (cmd 129) parse: N certs, each [CertType:1][CertLen:2][Cert:CertLen] ---- 145// Writes cert types + lengths (up to max). Returns cert count, or neg on overrun. 146func tc_parse_certs(pl: *u8, pllen: i64, out_types: *i64, out_lens: *i64, max: i64) -> i64 { 147 if pllen < 1 { return TCELL_E_SHORT } 148 let n: i64 = pl[0] as i64 149 var o: i64 = 1; var i: i64 = 0 150 while i < n { 151 if o + 3 > pllen { return TCELL_E_FORMAT } 152 let ctype: i64 = pl[o] as i64 153 let clen: i64 = tc_get_u16(pl, o + 1) 154 if o + 3 + clen > pllen { return TCELL_E_FORMAT } 155 if i < max { out_types[i] = ctype; out_lens[i] = clen } 156 o = o + 3 + clen; i = i + 1 157 } 158 return n 159} 160 161// ---- AUTH_CHALLENGE (cmd 130) parse: Challenge[32] | N_Methods[2] | Methods[2*N] ---- 162// Copies the 32-byte challenge, extracts up to `max` method ids. Returns method count, or neg. 163func tc_parse_auth_challenge(pl: *u8, pllen: i64, out_chal32: *u8, out_methods: *i64, max: i64) -> i64 { 164 if pllen < 34 { return TCELL_E_SHORT } 165 var i: i64 = 0 166 while i < 32 { out_chal32[i] = pl[i]; i = i + 1 } 167 let nm: i64 = tc_get_u16(pl, 32) 168 if pllen < 34 + nm*2 { return TCELL_E_SHORT } 169 var j: i64 = 0 170 while j < nm { if j < max { out_methods[j] = tc_get_u16(pl, 34 + j*2) } j = j + 1 } 171 return nm 172}