# singbox-aura A Go port of the AuraVPN client, byte-for-byte compatible with the Rust server in `crates/aura-transport/src/udp.rs`. Scope of v1: - the **UDP transport only** (the primary path), with the same wire layout (`0x01` HS + `0x02` DATA) and the same DTLS-flight-style reliable handshake adapter, - the **client side** of the Aura handshake (hybrid X25519 + ML-KEM-768, HKDF-SHA256, mutual ECDSA-P256 / SHA-256 X.509), - the **datagram data path** with the sliding-window replay check, - an optional **port-knock** prefix on HS datagrams, - a tiny **CLI** (`cmd/aura-client`) that loads a TOML config and dials a Rust-side server, and - a sing-box-shaped **outbound shim** (`aura/outbound`) that does not yet import the sing-box module — see `aura/outbound/README.md` for the next step. ### Why this exists Mobile sing-box embeds the Go core; it cannot easily spawn a Rust helper. Implementing the AuraVPN protocol natively in Go is the only path to a phone-friendly client. This module is that implementation. ## Layout ``` singbox-aura/ ├── go.mod / go.sum ├── README.md ├── aura/ │ ├── frame/ - 5-byte header + Frame{Data,Ping,Pong,Close} + control envelope │ ├── crypto/ - hybrid KEM + HKDF + ChaCha20-Poly1305 (LE(u64)||0x00000000 nonce) │ ├── handshake/ - client side of the §6.2 state machine │ ├── session/ - replay window + DatagramSender/Receiver │ ├── transport/ - reliable UDP HS adapter + post-HS data path + knock token │ └── outbound/ - sing-box-shaped wrapper (no sing-box dep yet — see its README) ├── cmd/aura-client/ - standalone CLI └── kat/vectors.json - KAT exported from `tools/export-kat` (Rust) ``` ## Build + test Requires **Go 1.24+** (stdlib `crypto/mlkem`). On older Go you would swap the post-quantum imports in `aura/crypto/kem.go` to `github.com/cloudflare/circl/kem/mlkem/mlkem768` — the rest of the package is dialect-agnostic. ```sh # from the workspace root cargo run -p export-kat # writes singbox-aura/kat/vectors.json cd singbox-aura go build ./... go test ./... ``` `aura/crypto/crypto_test.go` loads `kat/vectors.json` and asserts byte-for-byte that: - HKDF reproduces both session keys, - the hybrid decapsulate reproduces the two halves of the shared secret, - `HMAC-SHA256(c2s, transcript)` and `HMAC-SHA256(s2c, transcript)` match the Rust outputs, - one ChaCha20-Poly1305 datagram record (seq=2, frame = `Data{stream=0, payload="hello"}`) matches the Rust sealed bytes, including the 16-byte Poly1305 tag, - the 16-byte port-knock token for a fixed minute matches the Rust value. If any of these diverges, the Go port has a byte-level interop bug — fix it before proceeding. ## Standalone CLI `cmd/aura-client` mirrors a thin slice of the production Rust `client.toml`: ```toml [client] server_addr = "203.0.113.10:443" sni = "cdn.example.com" [pki] ca_cert = "~/.aura/ca.crt" cert = "~/.aura/client.crt" key = "~/.aura/client.key" [transport.knock] enabled = false # set to match the server knock_secret_source = "ca_fingerprint" ``` To dial a local Rust server (see `aura server --config server.toml` in the parent workspace): ```sh ./aura-client --config client.toml --message "hello aura" ``` The CLI completes the post-quantum handshake and sends one application packet. It exits after the send; this is intentional — proving the wire path is the v1 deliverable. ## Integrating as a sing-box outbound See `aura/outbound/README.md` for the registration sketch. The summary: 1. Vendor `github.com/sagernet/sing-box`. 2. In a tiny adapter package, call `sing-box.RegisterOutbound(outbound.Tag, ...)`. 3. Translate the chosen `option.Outbound` JSON schema into `handshake.ClientConfig` + `transport.Options`. 4. The packet path is opaque IP — the sing-box router writes IP packets to the returned `net.PacketConn`; the same conn yields incoming packets on `ReadFrom`. ## Known limitations (v1) These are intentionally out of scope and tracked as follow-ups: - **No TCP/443 or QUIC fallback** — only UDP. The Rust dialer's `order = [udp, tcp, quic]` fallback chain is not ported. - **No relay / exit role** — client-only. Multi-hop / onion routing is a separate project. - **No cell padding** — `[transport.obfuscate]` and the `HTTPS_SIZE_BUCKETS` padding profile are not emitted; the wire is just `0x02 || rec_len || sealed_record`. - **No cover traffic** — the idle-time `Frame::Ping` chaff in `cover_traffic_loop` is not ported. - **No CRL push handling** — the control-envelope decoder is in `aura/frame/control.go`, but the client does not process `CrlPush` envelopes (they are not currently sent on the data path the standalone CLI exercises). - **Single-peer server** — the Go client connects to one server at a time. The Rust v2 master-loop multi-peer demuxer is server-side and is not relevant to a client port. Each is a contained patch from this scaffold; the KAT-vector regime makes additions safe.