Skip to content

Add VPN protocols - #2

Open
Titlehhhh wants to merge 4 commits into
masterfrom
feature/add-vpn-protocols
Open

Add VPN protocols#2
Titlehhhh wants to merge 4 commits into
masterfrom
feature/add-vpn-protocols

Conversation

@Titlehhhh

Copy link
Copy Markdown
Owner

No description provided.

@Titlehhhh Titlehhhh changed the title Add VPN protocol documentation Add VPN protocols Jun 25, 2026
Titlehhhh and others added 3 commits July 23, 2026 19:40
Implements two VPN-style proxy protocols on the existing
ConnectAsync(...) -> Stream model, plus supporting infrastructure.

VLESS (security=none/tls):
- VlessOptions + VlessShareLink single-pass span vless:// parser
- VlessHelper zero-alloc request builder; VlessClient (none + TLS)
- UuidCodec: big-endian RFC 4122 encoding (avoids the Guid.ToByteArray
  mixed-endian trap)

Trojan (TLS-mandatory):
- Sha224 primitive (absent from the BCL): scalar + guarded Vector128
  message-schedule path, NIST-verified with a scalar-vs-vector sweep
- TrojanOptions/parser, TrojanHelper, TrojanClient
- ProxyAddress: shared address writer (atyp codes passed by protocol)

Hardening (from subagent review):
- reject unknown vless security= (no silent plaintext downgrade)
- bracket IPv6 proxy hosts in ProxyClient
- clear credential/password buffers before ArrayPool return
- wrap a truncated VLESS response as ProxyProtocolException

107 unit tests; multi-target net8.0/net9.0/net10.0; benchmarks included.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Implements the full VMessAEAD client on the existing
ConnectAsync(...) -> Stream model, alongside VLESS and Trojan.

Crypto primitives:
- VmessKdf: the nested/recursive HMAC-SHA256 KDF (an HMAC whose hash
  function is another HMAC — not expressible with HMACSHA256)
- VmessCmdKey, VmessAuthId (CRC32-IEEE + AES-128-ECB), Crc32, Fnv1a32,
  VmessBodyKeys (ChaCha20 MD5 key expansion, response key/IV)

Request path:
- VmessRequest: command section (port before address, atyp 01/02/03,
  FNV-1a-32 checksum) + AEAD envelope
  authid(16) | encLen(18) | connNonce(8) | encHeader(L+16)
- All randomness/time injectable via VmessRequestMaterial so the wire
  bytes are pinned byte-exactly in tests

Body path:
- VmessStream: chunked AEAD stream, per-chunk nonce
  (uint16 BE counter | bodyIV[2..12]), independent read/write counters,
  AES-128-GCM and ChaCha20-Poly1305 (gated on IsSupported)
- Clean EOF is ONLY the authenticated empty chunk; truncation and tag
  failure are hard errors, never EOF
- VmessResponse + VmessResponseStream: the response header is read
  LAZILY on first read. Reading it eagerly in ConnectAsync deadlocks
  every client-speaks-first protocol (HTTP, TLS, Minecraft), because
  v2ray/Xray only flush it after the target replies.

Config/client:
- VmessOptions, VmessShareLink (base64 JSON, URL-safe + unpadded),
  VmessClient, vmess scheme in the factory
- alterId != 0 rejected: legacy MD5 auth is not implemented
- Request option byte is 0x01 (ChunkStream only), never 0x1D — the
  stream implements baseline framing, so announcing M/P/A would make
  the server mask chunk lengths and desync

Ground truth for every vector comes from an independent Python
reference that first reproduces the previously committed KDF vectors.
334 tests; multi-target net8.0/net9.0/net10.0; benchmarks included.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Internal/ had grown to 18 files mixing protocol-agnostic crypto
primitives, shared utilities and per-protocol helpers.

- Internal/Crypto/: Crc32, Fnv1a32, Sha224, UuidCodec
- Internal/Vmess/:  VmessAuthId, VmessBodyKeys, VmessCmdKey, VmessKdf,
                    VmessRequest, VmessResponse, VmessResponseStream,
                    VmessStream
- Internal/ root keeps the six shared/single-helper files
  (ProxyAddress, HttpHelper, HttpResponseParser, SocksHelper,
   VlessHelper, TrojanHelper) — no folder-per-single-file.

Pure file moves: all 12 are git renames with byte-identical content and
every file still declares the flat `namespace QuickProxyNet;`. That flat
namespace is load-bearing — it is what lets files be reorganised without
touching the public API or forcing `using` churn on consumers — so
.editorconfig now records the decision explicitly
(dotnet_style_namespace_match_folder = false) instead of leaving the
IDE0130 guidance to be "fixed" by a later renaming that would break the
API.

No behaviour change: 334/334 tests, clean Release build on
net8.0/net9.0/net10.0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant