antiTree | posts and projects
posted by antitree on Aug 21, 2017

This post goes into what tor’s onion service authentication features do, how they work, and when they should be used based on your threat model because I couldn’t find any other documentation about it besides reading the spec. “Stealth” only provides “stealthy” properties against malicious HSDirs and basic has some additional obfuscation measures that might make your service better protected.

Onion Service Authentication Feature

An onion/hidden service supports two types of built in authentication:

  • Basic: A scalable way of protecting your service with a password
  • Stealth: Less scalable but more obscure and able to revoke access

There are a variety of posts from people writing about these functions but I didn’t find much detail about how they work and how they should be used.

Unauthenticated Onion Services

Hidden Services

A quick summary of how the hidden services protocol works:

  1. Alice creates an onion service
  2. Server generates keys
  3. Server establishes Introduction Points(IPs) on the Tor Network
  4. Server publishes those IPs as a Hidden Service Descriptor so they can be resolved.
  5. Bob receives the onion service address
  6. Client downloads Alice’s service descriptor with its IPs
  7. Client sends a request to the server via the IP and setups up a shared key and a Rendezvous Point (RP)
  8. Client and Server connect to RP
  9. Server serves Bob service

Attacking Unauthenticated Onion Services

There are two types of attacks that default onion services are susceptible to in the case of malicious HSDIRs: * Disclosure of your onion address * Passive monitoring of the onion address’ activity

As shown in the past, if someone is able to read the service descriptors (ie running a lot of HSDir hosts), they can find your onion addresses and connect to the servers. This is useful for attackers that want to profile how many people are visiting a service and to uncover services that incorrectly rely on the obscurity of the onion address for security.

So if you are concerned about other services finding and accessing your onion service or about someone monitoring when and how often your service is used, you can setup either Basic or Stealth authentication controls.


In “basic” mode, you’re doing one thing differently from the standard onion service flow: Symmetrically encrypting the Introduction Points published in the service descriptor.

This defends against the service descriptor being intercepted and granting access to your onion service.

Even in the case of a malicious HSDir, where the service descriptor is intercepted the adversary will only be able to see the onion address but won’t be able to establish a connection to it because they won’t have the IPs.

This mode is ideal if your service has no built-in authentication and you are concerned about someone else discovering your service. The use case for this would be a service you want to share with a group of people and you’re not concerned about those people sharing the key.

Stealth mode

The poorly named “stealth” authentication mode, operates similarly to above where a key is generated and IPs are encrypted but instead of having a single onion address that can be visited by anyone with the key, it is creating separate keys and addresses for each individual visiting the service. This means that for each client you create, you’re functionally running a dedicated onion service for them.

The differences are:

  1. A separate hidden service key pair is generated for each client
  2. A separate symmetric key is generated for each client
  3. Each client will have a separate service descriptor (I’m not sure if they also choose their own IPs) and they will be published separately.

The benefit is “stealth” mode authentication is service operators can revoke access to clients instead of having to shutdown entire services. So in the case of a compromise, you remove that client, and continue with the service.

You might say to yourself, “OOh Stealth, that sounds more private, I should use that” but let me point something out: When you run separate onion Services dedicated to each individual, the service operator will be able to know exactly which client is connecting and when. If you’re setting up an SSH service that only you plan on connecting to, maybe that’s not a big deal. If you’re setting up an anonymous drop and you want some plausible deniability between you and your 16 friends, then this isn’t ideal.

This mode is ideal for small services with a limited amount of users. In fact, the spec only allows for 16 clients max. If you think of stealth authentication like running multiple instances of basic authentication, you can also imagine giving groups of people access to your service so that in the case of a compromise, you can revoke access to one portion of the server without disrupting others.

Additional Details

If you’re into the nitty gritty details, from what I understand reading the spec the symmetric keys are using 128 bit AES-CTR and one interesting feature of basic mode is that when a service descriptor is uploaded, it is padded with a bunch of fake descriptors. I’m not sure if this is trying to increase the amount of effort to brute force the key or to minimize the risk of an attacker passively discovering a valid authenticated service.

I also didn’t trace down exactly how descriptor cookies are generated. We know that symmetric 128bit AES will depend on the key size but AFAIK, these cookies are just random.

To configure a server to require authentication, add lines like this to your TORRC:

## Stealth
HiddenServiceDir /var/lib/tor/stealth_service/
HiddenServicePort 22
HiddenServiceAuthorizeClient stealth alice,bob,charlie
## Basic:
HiddenServiceDir /var/lib/tor/basic_service/
HiddenServicePort 80
HiddenServiceAuthorizeClient basic mybasicclient

If you take a look those folders you created, you’ll see client_keys, hostname and private_key.

For your basic client_keys, because you don’t need to generate much key material, it will look like this:

client-name mybasicclient
descriptor-cookie /tuvPBTFHoWzwQCSXTgXxg==

And for stealth client_keys it’ll look like this:

client-name alice
descriptor-cookie h2JCZAzzEBxobjPLOOFTkg==
client-name bob
descriptor-cookie LPTX88EEGDvpcedXiYEmqw==

To configure your TORRC to connect to your services:

HidServAuth lxqluyty24ijfg4p.onion rJcrR/ZbCMDdJqTImOBvxB stealth1
HidServAuth tkwk5o5n4eud3vwd.onion /tuvPBTFHoWzwQCSXTgXxg basic1

Security of Stealth vs Basic

There’s one line that bugged me in the documentation. When talking about how to revoke someone’s access to a stealth auth protected service:

As long as a removed client cannot link descriptors issued for other clients to the service, it cannot derive service activity any more (spec)

It’s saying that in basic mode, there is no defense from someone monitoring your onion address and watching its activity. While in stealth, there is. This is phrased very odd but the point is that because stealth mode will generate separate service descriptors, there’s no way that a malicious HSDir could watch the traffic related to your service, because they won’t know the other clients’ dedicated onion address.

But why?

Why not replace stealth authentication by running multiple instances of your own onion address with basic authentication? The answer is that there’s a lot more overhead to do this and that’s the point of this service. I don’t know how much this improves performance but that’s one of the purposes of stealth mode so I can only assume it’s substantial enough to warrant it.

Why don’t you just have your service provide authentication? This is a valid response but with all of the discussion about putting your IoT on the Tor Network, maybe you want an extra layer of defense especially if you’re going to be the only one that uses them. This is protocol agnostic so even if your IoT crapware has an unauthenticated exploit, this would seemingly protect you.