The transition of the Tor network from the legacy version 2 (v2) onion protocol to the modern version 3 (v3) protocol marked a critical leap in anonymity, forward secrecy, and metadata resistance. Deprecated officially in late 2021, the v2 protocol relied on 80-bit RSA keys and truncated SHA-1 hashes, leaving the directory structure vulnerable to enumeration attacks and cryptanalytic degradation. The v3 onion architecture—specified in Tor rendezvous specification version 3 (rend-spec-v3)—replaces these legacy primitives with modern elliptic-curve cryptography, an overhaul of the hidden service directory (HSDir) storage model, and an address encoding scheme engineered to prevent unauthorized service discovery.
Cryptographic Anatomy of a v3 Onion Address
A v3 onion address is not an arbitrarily assigned domain name registered through a central authority. Instead, it is an entirely self-authenticating cryptographic identifier. When a client connects to a 56-character .onion domain, the address itself contains the raw material required to authenticate the remote peer without trusting any external public key infrastructure (PKI) or Certificate Authority (CA).
Byte Layout and Encoding
A standard v3 onion address represents a 35-byte binary structure encoded in Base32 (using the RFC 4648 alphabet, spanning lowercase characters a-z and digits 2-7). The 35 bytes are partitioned strictly as follows:
- Public Key (32 bytes): The master Ed25519 public key of the onion service.
- Checksum (2 bytes): A truncated SHA3-256 digest designed to catch typos and mitigate accidental route failures.
- Version Flag (1 byte): The single byte
0x03, explicitly denoting the protocol version.
The total bit length before encoding is 35 bytes multiplied by 8 bits per byte, yielding exactly 280 bits. Because standard Base32 encodes 5 bits per character, the final string length is precisely 280 divided by 5, resulting in the 56-character domain name (excluding the .onion top-level label).
Checksum Construction
The two-byte checksum enforces strict structural integrity before a Tor client initiates any multi-hop circuits. The checksum calculation uses the cryptographic hash function SHA3-256 with a domain-separation string prefix:
checksum = SHA3-256(".onion checksum" || ed25519_pubkey || version)[0:2]
The constant string .onion checksum is 15 bytes long. The public key is 32 bytes, and the version byte is 0x03. The client takes the first 16 bits (two bytes) of the resulting SHA3-256 output. When a user enters an address, the client immediately decodes the Base32 string into 35 bytes, verifies that the final byte is 0x03, executes this hash over the decoded fields, and compares the output with the embedded two-byte checksum. If they do not match, the connection aborts before any traffic touches the network.
Preventing Enumeration: Blinded Keys and Distributed Hash Tables
One of the primary vulnerabilities of the v2 protocol was directory harvesting. Relay nodes operating as Hidden Service Directories (HSDirs) could index every onion address that published a descriptor to them, allowing adversaries to catalog active hidden services on the network passively. The v3 protocol completely eliminates this vulnerability through keyed cryptographic blinding.
Key Blinding and Subcredentials
An onion service does not publish its master Ed25519 public key to the distributed hash table (DHT). Instead, both the service and the connecting client mathematically derive a daily blinded public key. An HSDir responsible for storing the descriptor receives only this blinded key and an associated signature, rendering it cryptographically impossible for the directory to extract the underlying permanent master key or discover the human-readable .onion address.
The key blinding scheme operates over the Ed25519 twisted Edwards curve:
- A global time period parameter (derived from the current UTC timestamp) and a network-wide consensus parameter are combined to form a period-specific derivation input.
- A blinding factor, or scalar factor s, is calculated deterministically:
s = H("derivation-string" || master_ed25519_pubkey || time_period). - The blinded public key A' is calculated as
A' = s * A, where A is the master public key point on the curve.
Because the discrete logarithm problem is intractable on Ed25519, an HSDir possessing A' cannot recover A. Only an entity that already knows the original address (and therefore A) can compute the correct A' for the current time period to determine the service's storage index on the HSDir ring.
Descriptor Encryption and Client Authorization
Even if an attacker controls an HSDir and observes a blinded key, the service descriptor itself is doubly encrypted. The descriptor contains routing metadata, including the list of active Introduction Points (IPs) where the service can be reached.
Two-Layer Descriptor Shielding
The outer layer of the v3 descriptor includes the blinded key and minimal metadata needed by the HSDir to handle expiration and validation. The entire payload—the inner layer containing introduction points, authentication keys, and protocol extensions—is encrypted using an ephemeral key exchange:
- The service generates an ephemeral key pair and computes a shared secret with the blinded key using a standard Key Derivation Function (HKDF-SHA256).
- The descriptor payload is encrypted using 256-bit authenticated encryption (AES-256-GCM or ChaCha20-Poly1305).
Restricted Access via Client Authorization
For high-security operations, administrators can enforce Client Authorization. Under this framework, authorized clients are provisioned an asymmetric key pair (typically x25519). The hidden service encrypts the inner descriptor layer specifically for the public keys of authorized clients. Even if an adversary compromises an HSDir and predicts the blinded key, the contents of the descriptor remain entirely opaque without the client's private decryption key. This renders the hidden service fully invisible and non-routable to unauthorized observers.
Circuit Establishment: The Cryptographic Handshake
Connecting to a v3 onion address involves establishing an ephemeral, end-to-end encrypted rendezvous circuit across six relay nodes, three selected by the client and three selected by the service. This ensures that neither party learns the other's IP address or physical location.
The rendezvous dance proceeds across distinct phases:
- Introduction Point Establishment: The service establishes long-lived, multi-hop circuits to several Tor relays designated as Introduction Points (IPs), authorizing them using the
ESTABLISH_INTROcommand. - Publishing: The service encrypts its descriptor containing these IPs and publishes it to the corresponding HSDirs derived from its current blinded public key.
- Descriptor Retrieval: The client decodes the target
.onionaddress, calculates the blinded public key, locates the correct HSDir node via the DHT ring, and downloads the encrypted descriptor. - Rendezvous Point Creation: The client builds a three-hop circuit to a randomly chosen relay, designating it as the Rendezvous Point (RP), and leaves an ephemeral secret token (cookie) via the
ESTABLISH_RENDEZVOUScell. - Introduction: The client builds a three-hop circuit to one of the service's IPs and delivers an
INTRODUCE1cell. This message is encrypted to the service’s master key and contains the address of the chosen RP and the secret cookie. - Rendezvous Connection: The IP forwards the payload to the service inside an
INTRODUCE2cell. The service builds a three-hop circuit to the RP, presenting the cookie inside aRENDEZVOUS1cell. - Circuit Merging: The RP verifies the matching cookies from the client and the service, bridges the two three-hop circuits into a unified six-hop circuit, and sends a
RENDEZVOUS2confirmation back to the client.
The rendezvous architecture guarantees that the client's guard and the service's guard never interact, and neither endpoint can determine the routing topology beyond the designated Rendezvous Point.
Implementation: Verifying a v3 Address Programmatically
To defend against address spoofing and check for manual transcription errors, software libraries and verification scripts must parse and validate the v3 structure deterministically. Below is a reference Python implementation illustrating the manual decoding and checksum verification pipeline using standard libraries.
import base64
import hashlib
def verify_v3_onion_address(onion_domain: str) -> bool:
# Strip protocol prefix, path, and TLD
domain = onion_domain.lower().strip()
if domain.endswith(".onion"):
domain = domain[:-6]
# Structural check: must be strictly 56 base32 characters
if len(domain) != 56:
return False
try:
# Base32 decoding requires padding to full 8-character boundaries
# 56 chars requires no padding because 56 % 8 == 0
raw_bytes = base64.b32decode(domain, casefold=True)
except Exception:
return False
# v3 payload must be exactly 35 bytes
if len(raw_bytes) != 35:
return False
pubkey = raw_bytes[0:32]
checksum = raw_bytes[32:34]
version = raw_bytes[34:35]
# Validate version byte
if version != b"\x03":
return False
# Compute expected SHA3-256 checksum
prefix = b".onion checksum"
digest = hashlib.sha3_256(prefix + pubkey + version).digest()
expected_checksum = digest[0:2]
return checksum == expected_checksum
Defensive Hygiene and Modern Anti-Spoofing Practices
While the v3 architecture provides absolute defense against mathematical collision attacks under current computing paradigms, the human element remains a point of exploitation. Because 56-character base32 strings are difficult for human eyes to parse at a glance, threat actors rely on vanity generation tools (such as mkp224o) to compute partially matching addresses. An adversary can generate a key where the first 8 to 14 characters match a legitimate service, tricking users who fail to verify the full address string.
Operational Countermeasures
Organizations and users operating within hostile environments should enforce specific protocols to ensure address authenticity:
- Full-String Bookmarking: Never rely on human memory or visual inspection of partial prefixes. Users should bookmark verified onion services directly inside the Tor Browser.
- Cryptographic Distribution: Operators should publish their full 56-character v3 address signed under an established OpenPGP key, distributed across independent channels (e.g., cleared keyservers, authenticated source repositories).
- Onion-Location Headers: Legitimate HTTPS web platforms running complementary onion services should deploy the
Onion-LocationHTTP header, allowing Tor Browser to securely detect and switch to the verified onion address automatically. - Proof-of-Work (PoW) Defenses: Modern Tor relays support dynamic Proof-of-Work configurations within the descriptor. In high-traffic scenarios or during Layer 7 Denial-of-Service attacks, operators can enforce cryptographic puzzles directly in the v3 handshake, prioritizing honest traffic without compromising user identity.
Conclusion
The Tor v3 onion address specification resolves the structural, cryptographic, and operational deficiencies of its predecessor. By shifting to Ed25519 public keys, mandating SHA3-256 checksum verification, and deploying blind key derivation across the HSDir layer, v3 addresses prevent network enumeration and resist active interception. For researchers, journalists, and system administrators, understanding this low-level architecture ensures that security guarantees are maintained through proper address verification and defensive configuration.