Skip to content

v0.1.3

Choose a tag to compare

@github-actions github-actions released this 27 Jul 03:40
· 9 commits to main since this release

Changelog

All notable changes to CupriTor are recorded here. This project adheres to Semantic Versioning.

0.1.3

Tunnelled directory fetches — closes the largest remaining anonymity gap (per-circuit relay selection leaking
over the clearnet directory channel). No breaking changes.

Anonymity

  • Download-all microdescriptor warm at bootstrap. After verifying the consensus, every listed relay's
    microdescriptor is fetched once (over the clearnet bootstrap source) into the cache, so subsequent circuit
    builds hit the cache and never fetch per-hop descriptors over the directory channel. Fetching all of them
    also means an observer of the bootstrap can't infer which relays a circuit uses (including the guard).
  • Over-circuit directory refreshes. After bootstrap, consensus refreshes go over a Tor circuit (BEGIN_DIR
    to a V2Dir cache) via the new internal CircuitDirectorySource, with a clearnet fallback — so directory
    traffic stops signalling "Tor user" to an on-path observer on every refresh. Each refresh re-warms the cache
    over a circuit for the new consensus.
  • Net effect: the only clearnet directory traffic is the one-time bootstrap (consensus + keys + all
    microdescriptors), which reveals that a Tor client is bootstrapping but not its path selection. Build-time
    microdescriptor resolution stays cache-first (clearnet only as a rare fallback for a brand-new relay).

Correctness

  • PADDING keepalives no longer break circuit builds. Build-time CREATE2/EXTEND2 reads now drop PADDING/VPADDING
    cells (matching the receive loop and tor-spec) instead of failing — a relay with connection padding can send a
    keepalive mid-handshake, which previously aborted the build. Surfaced live by the long over-circuit consensus
    transfer; hardens every circuit build (onion + exit included).
  • Onion descriptor lookup fixed in the morning UTC window. The client computed the HSDir ring with the current
    shared-random value unconditionally; in the [00:00–12:00) UTC window the current time period pairs with the
    previous SRV (now selected via IsBetweenTpAndSrv, mirroring the service's two-period publish). Previously,
    onion lookups in that window fetched from the wrong HSDirs and got a 404 from every one — a pre-existing latent
    bug that made onion connect fail all morning and succeed in the afternoon.

0.1.2

Hardening and DX for onion-only, high-fan-out transports. No breaking changes (all new members are additive).

Anonymity / correctness

  • Microdescriptor cache — per-hop microdescriptors are cached by content digest and pruned on consensus
    refresh, so building a circuit no longer re-fetches (over the clearnet directory channel) the descriptors of
    relays already seen. Reduces the repeated on-path leak of relay selection and cuts per-dial latency.
  • Cancellation cleanup — fixed two paths that could leak an OrConnection until GC when a dial was cancelled
    mid-handshake (the guard first hop in BuildOverPathAsync, and INTRODUCE1 in the onion connector); partially
    built circuits are now always disposed, including on cancellation.

Safety / API

  • TorClientOptions.OnionOnly — when set, any clearnet/exit dial (including a malformed address) throws
    ClearnetBlockedException instead of routing through a Tor exit, so an onion-only transport can't silently
    leave Tor. Covers ConnectAsync, the SOCKS5 server (replies "connection not allowed by ruleset"), and the
    HttpClient integration, since all dial through the same seam.
  • FileStateStore — a durable, atomically-written (temp + rename) IStateStore so entry guards persist
    across restarts. The in-memory default now emits a one-time StatusChanged warning; set
    TorClientOptions.RequirePersistentState to refuse to start without a persistent store.
  • InvalidOnionAddressException (derives from ArgumentException) is now thrown for malformed onion
    addresses by ConnectToOnionAsync/LookupOnionAsync.
  • Per-call timeoutsConnectToOnionAsync/ConnectAsync/ConnectViaExitAsync gained overloads taking an
    explicit TimeSpan timeout (overriding TorClientOptions.Timeout), convenient for a racing dialer.

0.1.1

Licensing and packaging only — no code or API changes; the library is identical to 0.1.0.

  • Add a repository LICENSE (MIT) and THIRD-PARTY-NOTICES.md attributing the redistributed
    dependencies (BouncyCastle, CupriCurve, and the .NET runtime bundled into the sample apps).
  • The self-contained sample bundles now include LICENSE and THIRD-PARTY-NOTICES.md.

0.1.0

First feature-complete release: a 100%-managed Tor client, v3 onion service, exit-capable dialer, and SOCKS5 proxy.
Live-validated end to end against the real Tor network (onion client, onion service, exit — over the default managed
TLS transport). 175 tests.

Client (use Tor)

  • TorClient — bootstraps and verifies the microdescriptor consensus against the 9 hard-coded directory
    authorities (strict 5-of-9 majority) before trusting it; maintains entry guards; auto-refreshes the consensus.
    new TorClient() works with no configuration (built-in directory authorities).
  • Onion clientConnectToOnionAsync (descriptor lookup → rendezvous → introduce → Stream), including
    connecting to private (client-authorized) onions.
  • Exit / clearnetConnectViaExitAsync / ConnectAsync route non-onion hosts through exit relays
    (exit-policy aware, remote DNS at the exit).
  • HttpClient integrationITorDialer.CreateTorHttpClient() / CreateTorHttpHandler().
  • SOCKS5 proxySocks5ProxyServer (NO-AUTH, CONNECT; onion + clearnet), with a handshake timeout and a
    connection cap.
  • ProgressTorClient.StatusChanged / CurrentStatus for bootstrap + connect phases.

Onion services (host on Tor)

  • PublishOnionAsync — durable v3 onion service: establishes intro points, publishes descriptors (two-period),
    self-heals, and serves inbound streams (raw handler or reverse proxy to a local TCP app).
  • Private onionsOnionClientAuthorization (tor-format descriptor:x25519:… keys).
  • Persistent / vanity identitiesOnionServiceKey (tor hs_ed25519_secret_key interop).

Hosting integrations

  • CupriTor.AspNetCoreUseCupriTorOnion(...) hosts an ASP.NET Core app on an onion in-process (no
    loopback), binding clearnet + onion side by side.
  • CupriTor.Host — cross-platform sidecar (dotnet tool / Windows Service / systemd / Docker): config-driven
    ClearnetOnly / TorOnly / Both front door for any local app, plus an optional SOCKS5 port.

Path selection & anonymity

  • Bandwidth-weighted path selection with /16 subnet and relay-family distinctness (mutual), and persistent
    entry guards with correct up/down attribution.
  • See docs/THREAT-MODEL.md for what CupriTor does and does not protect.

Managed crypto

  • No tor.exe and no OS crypto on the critical path: SHA3/SHAKE/AES/X25519/Ed25519-verify via BouncyCastle,
    Ed25519 signing / key blinding via CupriCurve.

Known limitations

  • Not independently audited; pre-1.0. No relay/node support, IPv6 exit, RELAY_RESOLVE, or traffic-analysis
    padding yet — see the roadmap.

Full Changelog: v0.1.1...v0.1.3