code wiki / _hdl_build / nx_tor_ntor.nx
nx_tor_ntor.nx
buildroot/runtime/_hdl_build/nx_tor_ntor.nx
about
nx_tor_ntor.nx -- SOVEREIGN Tor ntor / CREATE2 circuit handshake (tor-spec.txt §5.1.4 + KDF §5.2.2).
Phase 1 of the anonymizing-transport arc: the foundational Tor circuit key-agreement. It COMPOSES the
ecosystem's already-gated primitives -- x25519 (RFC 7748), hmac_sha256 (RFC 2104/4231), hkdf_expand
(RFC 5869) -- into Tor's exact ntor construction. NO new crypto is rolled here.
tor-spec §5.1.4 (verbatim mapping):
PROTOID = "ntor-curve25519-sha256-1"
t_mac = PROTOID | ":mac" t_key = PROTOID | ":key_extract"
t_verify = PROTOID | ":verify" m_expand = PROTOID | ":key_expand"
H(msg, tweak) = HMAC-SHA256(key=tweak, msg=msg)
client onion skin = NODEID(ID,20) | KEYID(B,32) | CLIENT_PK(X,32) [CREATE2 HDATA, htype=ntor]
server: secret_input = EXP(X,y) | EXP(X,b) | ID | B | X | Y | PROTOID
client: secret_input = EXP(Y,x) | EXP(B,x) | ID | B | X | Y | PROTOID (equal by DH commutativity)
KEY_SEED = H(secret_input, t_key) verify = H(secret_input, t_verify)
auth_input = verify | ID | B | Y | X | PROTOID | "Server"
AUTH = H(auth_input, t_mac) server reply = SERVER_PK(Y,32) | AUTH(32) [CREATED2 HDATA]
key material = HKDF-Expand(PRK=KEY_SEED, info=m_expand) -> Df|Db|Kf|Kb|KH (20|20|16|16|20 = 92)
EXP(pk, sk) in the spec = X25519(sk, pk) here (scalar sk on point pk).
HONEST SCOPE: this is the ntor COMPOSITION, spec-faithful. It does NOT by itself prove byte-exact interop
with the live Tor network -- that requires (1) Tor's own published ntor test vector (not present in-tree;
zero external calls this run) AND (2) the x25519 base-point fix (nx_x25519_test.nx documents a scalar-
pattern bug in base-point multiplication that ntor's X=x*G / Y=y*G / B=b*G derivations depend on). Until
a full circuit works, anon_is_anonymizing() stays 0. Sovereign: nx_syscalls + x25519 + hmac + hkdf.
license_tier: ORIGINAL
dependencies 4 imports · 3 importers
imports: nx_syscalls.nxnx_x25519_ephemeral.nxnx_hmac.nxnx_hkdf.nx
imported by: nx_tor_ntor_cli.nxnx_tor_ntor_gate.nxnx_tor_relay_gate.nx
structs
| none |
consts
| 31 | const NTOR_MAGIC_2104: i64 = 2104 |
| 32 | const NTOR_MAGIC_4231: i64 = 4231 |
| 33 | const NTOR_MAGIC_5869: i64 = 5869 |
| 35 | const NTOR_ID_LEN: i64 = 20 // router identity digest length |
| 36 | const NTOR_PUB: i64 = 32 // curve25519 public/secret length |
| 37 | const NTOR_H: i64 = 32 // SHA-256 / HMAC output length |
| 38 | const NTOR_KEYS_LEN: i64 = 92 // Df(20)|Db(20)|Kf(16)|Kb(16)|KH(20) |
| 39 | const NTOR_SKIN_LEN: i64 = 84 // ID(20)|B(32)|X(32) |
| 40 | const NTOR_REPLY_LEN:i64 = 64 // Y(32)|AUTH(32) |
| 42 | const NTOR_OK: i64 = 0 |
| 43 | const NTOR_E_AUTH: i64 = 0 - 1 // client: recomputed AUTH != received AUTH (reject the relay) |
| 44 | const NTOR_E_BADPOINT:i64 = 0 - 2 // degenerate DH result (all-zero shared secret) -> reject |
functions
| 47 | func nt_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){ n=n+1 } return n } |
| 49 | func nt_cat(dst: *u8, off: i64, src: *u8, n: i64) -> i64 { var i: i64=0; while i<n { dst[off+i]=src[i]; i=i+1 } return off+n } |
| 51 | func nt_ct_eq(a: *u8, b: *u8, n: i64) -> i64 { var d: i64=0; var i: i64=0; while i<n { d = d | ((a[i] as i64) ^ (b[i] as i64)); i=i+1 } if d==0 { return 1 } return 0 } called by 1: ntor_client_finish |
| 53 | func nt_is_zero(buf: *u8, n: i64) -> i64 { var i: i64=0; while i<n { if buf[i]!=(0 as u8) { return 0 } i=i+1 } return 1 } |
| 56 | func ntor_build_secret_input(xy: *u8, xb: *u8, id: *u8, bpub: *u8, xpub: *u8, ypub: *u8, buf: *u8) -> i64 |
| 70 | func ntor_build_auth_input(verify: *u8, id: *u8, bpub: *u8, ypub: *u8, xpub: *u8, buf: *u8) -> i64 |
| 85 | func ntor_kdf_seed_verify(si: *u8, si_len: i64, key_seed_out: *u8, verify_out: *u8) -> i64 |
| 94 | func ntor_compute_auth(verify: *u8, id: *u8, bpub: *u8, ypub: *u8, xpub: *u8, auth_out: *u8) -> i64 called by 2: ntor_server_respondntor_client_finish calls 4: sys_mmapntor_build_auth_inputhmac_sha256nt_slen |
| 103 | func ntor_expand_keys(key_seed: *u8, keys_out: *u8) -> i64 |
| 110 | func ntor_client_init(id: *u8, bpub: *u8, x_priv: *u8, x_pub_out: *u8, onion_skin_out: *u8) -> i64 |
| 121 | func ntor_server_respond(id: *u8, bpub: *u8, b_priv: *u8, x_pub: *u8, y_priv: *u8, |
| 140 | func ntor_client_finish(id: *u8, bpub: *u8, x_priv: *u8, x_pub: *u8, y_pub: *u8, auth_recv: *u8, |