Filename: 331-res-tokens-for-anti-dos.md
Title: Res tokens: Anonymous Credentials for Onion Service DoS Resilience
Author: George Kadianakis, Mike Perry
Created: 11-02-2021
Status: Draft
+--------------+ +------------------+
| Token Issuer | | Onion Service |
+--------------+ +------------------+
^ ^
| +----------+ |
Issuance | 1. | | 2. | Redemption
+------->| Alice |<-------+
| |
+----------+
This proposal specifies a simple anonymous credential scheme based on Blind RSA signatures designed to fight DoS abuse against onion services. We call the scheme "Res tokens".
Res tokens are issued by third-party issuance services, and are verified by onion services during the introduction protocol (through the INTRODUCE1 cell).
While Res tokens are used for denial of service protection in this proposal, we demonstrate how they can have application in other Tor areas as well, like improving the IP reputation of Tor exit nodes.
Denial of service attacks against onion services have been explored in the past and various defenses have been proposed:
- Tor proposal #305 specifies network-level rate-limiting mechanisms.
- Onionbalance allows operators to scale their onions horizontally.
- Tor proposal #327 increases the attacker's computational requirements (not implemented yet).
While the above proposals in tandem should provide reasonable protection against many DoS attackers, they fundamentally work by reducing the asymmetry between the onion service and the attacker. This won't work if the attacker is extremely powerful because the asymmetry is already huge and cutting it down does not help.
We believe that a proposal based on cryptographic guarantees -- like Res tokens -- can offer protection against even extremely strong attackers.
In this proposal we introduce an anonymous credential scheme -- Res tokens -- that is well fitted for protecting onion services against DoS attacks. We also introduce a system where clients can acquire such anonymous credentials from various types of Token Issuers and then redeem them at the onion service to gain access even when under DoS conditions.
In section [TOKEN_DESIGN], we list our requirements from an anonymous credential scheme and provide a high-level overview of how the Res token scheme works.
In section [PROTOCOL_SPEC], we specify the token issuance and redemption protocols, as well as the mathematical operations that need to be conducted for these to work.
In section [TOKEN_ISSUERS], we provide a few examples and guidelines for various token issuer services that could exist.
In section [DISCUSSION], we provide more use cases for Res tokens as well as future improvements we can conduct to the scheme.
In this section we will go over the high-level design of the system, and in the next section we will delve into the lower-level details of the protocol.
Anonymous credentials or tokens are cryptographic identifiers that allow their bearer to maintain an identity while also preserving anonymity.
Clients can acquire a token in a variety of ways (e.g. registering on a third-party service, solving a CAPTCHA, completing a PoW puzzle) and then redeem it at the onion service proving this way that work was done, but without linking the act of token acquisition with the act of token redemption.
The anonymous credential literature is vast and there are dozens of credential schemes with different properties REF_TOKEN_ZOO, in this section we detail the properties we care about for this use case:
-
Public Verifiability: Because of the distributed trust properties of the Tor network, we need anonymous credentials that can be issued by one party (the token issuer) and verified by a different party (in this case the onion service).
-
Perfect unlinkability: Unlinkability between token issuance and token redemption is vital in private settings like Tor. For this reason we want our scheme to preserve its unlinkability even if its fundamental security assumption is broken. We want unlinkability to be protected by information theoretic security or random oracle, and not just computational security.
-
Small token size: The tokens will be transfered to the service through the INTRODUCE1 cell which is not flexible and has only a limited amount of space (about 200 bytes) REF_INTRO_SPACE. We need tokens to be small.
-
Quick Verification: Onions are already experiencing resource starvation because of the DoS attacks so it's important that the process of verifying a token should be as quick as possible. In section [TOKEN_PERF] we will go deeper into this requirement.
After careful consideration of the above requirements, we have leaned towards using Blind RSA as the primitive for our tokens, since it's the fastest scheme by far that also allows public verifiability. See also Appendix B [BLIND_RSA_PROOF] for a security proof sketch of Blind RSA perfect unlinkability.
Apart from the above properties we also want:
-
Double spending protection: We don't want Malory to be able to double spend her tokens in various onion services thereby amplifying her attack. For this reason our tokens are not global, and can only be redeemed at a specific destination onion service.
-
Metadata: We want to encode metadata/attributes in the tokens. In particular, we want to encode the destination onion service and an expiration date. For more information see section [DEST_DIGEST]. For blind RSA tokens this is usually done using "partially blind signatures" but to keep it simple we instead encode the destination directly in the message to be blind-signed and the expiration date using a set of rotating signing keys.
-
One-show: There are anonymous credential schemes with multi-show support where one token can be used multiple times in an unlinkable fashion. However, that might allow an adversary to use a single token to launch a DoS attack, since revocation solutions are complex and inefficient in anonymous credentials. For this reason, in this work we use one-show tokens that can only be redeemed once. That takes care of the revocation problem but it means that a client will have to get more tokens periodically.
Throughout this proposal we will be using our own token scheme, named "Res", which is based on blind RSA signatures. In this modern cryptographic world, not only we have the audacity of using Chaum's oldest blind signature scheme of all times, but we are also using RSA with a modulus of 1024 bits...
The reason that Res uses only 1024-bits RSA is because we care most about small token size and quick verification rather than the unforgeability of the token. This means that if the attacker breaks the issuer's RSA signing key and issues tokens for herself, this will enable the adversary to launch DoS attacks against onion services, but it won't allow her to link users (because of the "perfect unlinkability" property).
Furthermore, Res tokens get a short implicit expiration date by having the issuer rapidly rotate issuance keys every few hours. This means that even if an adversary breaks an issuance key, she will be able to forge tokens for just a few hours before that key expires.
For more ideas on future schemes and improvements see section [FUTURE_RES].
As discussed above, verification performance is extremely important in the anti-DoS use case. In this section we provide some concrete numbers on what we are looking for.
In proposal #327 [REF_POW_PERF] we measured that the total time spent by the onion service on processing a single INTRODUCE2 cell ranges from 5 msec to 15 msecs with a mean time around 5.29 msec. This time also includes the launch of a rendezvous circuit, but does not include the additional blocking and time it takes to process future cells from the rendezvous point.
We also measured that the parsing and validation of INTRODUCE2 cell ("top half") takes around 0.26 msec; that's the lightweight part before the onion service decides to open a rendezvous circuit and do all the path selection and networking.
This means that any defenses introduced by this proposal should add minimal overhead to the above "top half" procedure, so as to apply access control in the lightest way possible.
For this reason we implemented a basic version of the Res token scheme in Rust and benchmarked the verification and issuance procedure [REF_RES_BENCH].
We measured that the verification procedure from section [RES_VERIFY] takes about 0.104 ms, which we believe is a reasonable verification overhead for the purposes of this proposal.
We also measured that the issuance procedure from [RES_ISSUANCE] takes about 0.614 ms.
+--------------+ +------------------+
| Token Issuer | | Onion Service |
+--------------+ +------------------+
^ ^
| +----------+ |
Issuance | 1. | | 2. | Redemption
+------->| Alice |<-------+
| |
+----------+
Let a || b
be the concatenation of a with b.
Let a^b
denote the exponentiation of a to the bth power.
Let a == b
denote a check for equality between a and b.
Let FDH_N(msg) be a Full Domain Hash (FDH) of 'msg' using SHA256 and stretching the digest to be equal to the size of an RSA modulus N.
The Issuer creates a set of ephemeral RSA-1024 "issuance keys" that will be used during the issuance protocol. Issuers will be rotating these ephemeral keys every 6 hours.
The Issuer exposes the set of active issuance public keys through a REST HTTP API that can be accessed by visiting /issuers.keys.
Tor directory authorities periodically fetch the issuer's public keys and vote for those keys in the consensus so that they are readily available by clients. The keys in the current consensus are considered active, whereas the ones that have fallen off have expired.
XXX how many issuance public keys are active each time? how does overlapping keys work? clients and onions need to know precise expiration date for each key. this needs to be specified and tested for robustness.
XXX every how often does the fetch work? how does the voting work? which issuers are considered official? specify consensus method.
XXX An alternative approach: Issuer has a long-term ed25519 certification key that creates expiring certificates for the ephemeral issuance keys. Alice shows the certificate to the service to prove that the token comes from an issuer. The consensus includes the long-term certification key of the issuers to establish ground truth. This way we avoid the synchronization between dirauths and issuers, and the multiple overlapping active issuance keys. However, certificates might not fit in the INTRODUCE1 cell (prop220 certs take 104 bytes on their own). Also certificate metadata might create a vector for linkability attacks between the issuer and the verifier.
When an onion service is under DoS attack it adds the following line in the "encrypted" (inner) part of the v3 descriptor as a way to signal to its clients that tokens are required for gaining access:
"token-required" SP token-type SP issuer-list NL
[At most once]
token-type: Is the type of token supported ("res" for this proposal)
issuer-list: A comma separated list of issuers which are supported by this onion service
When Alice visits an onion service with an active "token-required" line in its descriptor it checks whether there are any tokens available for this onion service in its token store. If not, it needs to acquire some and hence the token issuance protocol commences.
Alice first chooses an issuer supported by the onion service depending on her preferences by looking at the consensus and her Tor configuration file for the current list of active issuers.
After picking a supported issuer, she performs the following preparation before contacting the issuer:
-
Alice extracts the issuer's public key (N,e) from the consensus
-
Alice computes a destination digest as follows:
dest_digest = FDH_N(destination || salt) where: - 'destination' is the 32-byte ed25519 public identity key of the destination onion - 'salt' is a random 32-byte value,
-
Alice samples a blinding factor 'r' uniformly at random from [1, N)
-
Alice computes: blinded_message = dest_digest * r^e (mod N)
After this phase is completed, Alice has a blinded message that is tailored specifically for the destination onion service. Alice will send the blinded message to the Token Issuer, but because of the blinding the Issuer does not get to learn the dest_digest value.
XXX Is the salt needed? Reevaluate.
Alice now initiates contact with the Token Issuer and spends the resources required to get issued a token (e.g. solve a CAPTCHA or a PoW, create an account, etc.). After that step is complete, Alice sends the blinded_message to the issuer through a JSON-RPC API.
After the Issuer receives the blinded_message it signs it as follows:
blinded_signature = blinded_message ^ d (mod N)
where:
- 'd' is the private RSA exponent.
and returns the blinded_signature to Alice.
XXX specify API (JSON-RPC? Needs SSL + pubkey pinning.)
Alice verifies the received blinded signature, and unblinds it to get the final token as follows:
token = blinded_signature * r^{-1} (mod N)
= blinded_message ^ d * r^{-1] (mod N)
= (dest_digest * r^e) ^d * r^{-1} (mod N)
= dest_digest ^ d * r * r^{-1} (mod N)
= dest_digest ^ d (mod N)
where:
- r^{-1} is the multiplicative inverse of the blinding factor 'r'
Alice will now use the 'token' to get access to the onion service.
By verifying the received signature using the issuer keys in the consensus, Alice ensures that a legitimate token was received and that it has not expired (since the issuer keys are still in the consensus).
Now that Alice has a valid 'token' it can request access to the onion service. It does so by embedding the token into the INTRODUCE1 cell to the onion service.
To do so, Alice adds an extension to the encrypted portion of the INTRODUCE1 cell by using the EXTENSIONS field (see [PROCESS_INTRO2] section in rend-spec-v3.txt). The encrypted portion of the INTRODUCE1 cell only gets read by the onion service and is ignored by the introduction point.
We propose a new EXT_FIELD_TYPE value:
[02] -- ANON_TOKEN
The EXT_FIELD content format is:
TOKEN_VERSION [1 byte]
ISSUER_KEY [4 bytes]
DEST_DIGEST [32 bytes]
TOKEN [128 bytes]
SALT [32 bytes]
where:
- TOKEN_VERSION is the version of the token ([0x01] for Res tokens)
- ISSUER_KEY is the public key of the chosen issuer (truncated to 4 bytes)
- DEST_DIGEST is the 'dest_digest' from above
- TOKEN is the 'token' from above
- SALT is the 32-byte 'salt' added during blinding
This will increase the INTRODUCE1 payload size by 199 bytes since the data above is 197 bytes, the extension type and length is 2 extra bytes, and the N_EXTENSIONS field is always present. According to ticket #33650, INTRODUCE1 cells currently have more than 200 bytes available so we should be able to fit the above fields in the cell.
XXX maybe we don't need to pass DEST_DIGEST and we can just derive it
XXX maybe with a bit of tweaking we can even use a 1536-bit RSA signature here...
Upon receiving an INTRODUCE1 cell with the above extension the service verifies the token. It does so as follows:
- The service checks its double spend protection cache for an element that matches DEST_DIGEST. If one is found, verification fails.
- The service checks: DEST_DIGEST == FDH_N(service_pubkey || SALT), where 'service_pubkey' is its own long-term public identity key.
- The service finds the corresponding issuer public key 'e' based on ISSUER_KEY from the consensus or its configuration file
- The service checks: TOKEN ^ e == DEST_DIGEST
Finally the onion service adds the DEST_DIGEST to its double spend protection cache to avoid the same token getting redeemed twice. Onion services keep a double spend protection cache by maintaining a sorted array of truncated DEST_DIGEST elements.
If any of the above steps fails, the verification process aborts and the introduction request gets discarded.
If all the above verification steps have been completed successfully, the service knows that this a valid token issued by the token issuer, and that the token has been created for this onion service specifically. The service considers the token valid and the rest of the onion service protocol carries out as normal.
In this section we go over some example token issuers. While we can have official token issuers that are supported by the Tor directory authorities, it is also possible to have unofficial token issuers between communities that can be embedded directly into the configuration file of the onion service and the client.
In general, we consider the design of token issuers to be independent from this proposal so we will touch the topic but not go too deep into it.
A use case resembling the setup of Cloudflare's PrivacyPass would be to have a CAPTCHA service that issues tokens after a successful CAPTCHA solution.
Tor Project, Inc runs https://ctokens.torproject.org which serves hCaptcha CAPTCHAs. When the user solves a CAPTCHA the server gives back a list of tokens. The amount of tokens rewarded for each solution can be tuned based on abuse level.
Clients reach this service via a regular Tor Exit connection, possibly via a dedicated exit enclave-like relay that can only connect to https://ctokens.torproject.org.
Upon receiving tokens, Tor Browser delivers them to the Tor client via the control port, which then stores the tokens into a token cache to be used when connecting to onion services.
In terms of UX, most of the above procedure can be hidden from the user by having Tor Browser do most of the things under the scenes and only present the CAPTCHA to the user if/when needed (if the user doesn't have tokens available for that destination).
XXX specify control port API between browser and tor
An idea that mixes the CAPTCHA issuer with proposal#327, would be to have a token issuer that accepts PoW solutions and provides tokens as a reward.
This solution tends to be less optimal than applying proposal#327 directly because it doesn't allow us to fine-tune the PoW difficulty based on the attack severity; which is something we are able to do with proposal#327.
However, we can use the fact that token issuance happens over HTTP to introduce more advanced PoW-based concepts. For example, we can design token issuers that accept blockchain shares as a reward for tokens. For example, a system like Monero's Primo could be used to provide DoS protection and also incentivize the token issuer by being able to use those shares for pool mining REF_PRIMO.
The onion service itself can also issue tokens to its users and then use itself as an issuer for verification. This way it can reward trusted users by giving it tokens for the future. The tokens can be rewarded from within the website of the onion service and passed to the Tor Client through the control port, or they can be provided in an out-of-bands way for future use (e.g. from a journalist to a future source using a QR code).
Unfortunately, the anonymous credential scheme specified in this proposal is one-show, so the onion service cannot provide a single token that will work for multiple "logins". In the future we can design multi-show credential systems that also have revocation to further facilitate this use case (see [FUTURE_RES] for more info).
This proposal has user facing UX consequences.
Ideally we want this process to be invisible to the user and things to "just work". This can be achieved with token issuers that don't require manual work by the user (e.g. the PoW issuer, or the onion service itself), since both the token issuance and the token redemption protocols don't require any manual work.
In the cases where manual work is needed by the user (e.g. solving a CAPTCHA) it's ideal if the work is presented to the user right before visiting the destination and only if it's absolutely required. An explanation about the service being under attack should be given to the user when the CAPTCHA is provided.
In this section we analyze potential security threats of the above system:
-
An evil client can hoard tokens for hours and unleash them all at once to cause a denial of service attack. We might want to make the key rotation even more frequent if we think that's a possible threat.
-
A trusted token issuer can always DoS an onion service by forging tokens.
-
Overwhelming attacks like "top half attacks" and "hybrid attacks" from proposal#327 is valid for this proposal as well.
-
A bad RNG can completely wreck the linkability properties of this proposal.
XXX Actually analyze the above if we think there is merit to listing them
There are more scenarios within Tor that could benefit from Res tokens however we didn't expand on those use cases to keep the proposal short. In the future, we might want to split this document into two proposals: one proposal that specifies the token scheme, and another that specifies how to use it in the context of onion services, so that we can then write more proposals that use the token scheme as a primitive.
An extremely relevant use case would be to use Res tokens as a way to protect and improve the IP reputation of Exit relays. We can introduce an exit pool that requires tokens in exchange for circuit streams. The idea is that exits that require tokens will see less abuse, and will not have low scores in the various IP address reputation systems that now govern who gets access to websites and web services on the public Internet. We hope that this way we will see less websites blocking Tor.
The Res token scheme is a pragmatic scheme that works for the space/time constraints of this use case but it's far from ideal for the greater future (RSA? RSA-1024?).
After Tor proposal#319 gets implemented we will be able to pack more data in RELAY cells and that opens the door to token schemes with bigger token sizes. For example, we could design schemes based on BBS+ that can provide more advanced features like multi-show and complex attributes but currently have bigger token sizes (300+ bytes). That would greatly improve UX since the client won't have to solve multiple CAPTCHAs to gain access. Unfortunately, another problem here is that right now pairing-based schemes have significantly worse verification performance than RSA (e.g. in the order of 4-5 ms compared to <0.5 ms). We expect pairing-based cryptography performance to only improve in the future and we are looking forward to these advances.
When we switch to a multi-show scheme, we will also need revocation support otherwise a single client can abuse the service with a single multi-show token. To achieve this we would need to use blacklisting schemes based on accumulators (or other primitives) that can provide more flexible revocation and blacklisting; however these come at the cost of additional verification time which is not something we can spare at this time. We warmly welcome research on revocation schemes that are lightweight on the verification side but can be heavy on the proving side.
There is more use cases for tokens in Tor but we think that other token schemes with different properties would be better suited for those.
In particular we could use tokens as authentication mechanisms for logging into services (e.g. acquiring bridges, or logging into Wikipedia). However for those use cases we would ideally need multi-show tokens with revocation support. We can also introduce token schemes that help us build a secure name system for onion services.
We hope that more research will be done on how to combine various token schemes together, and how we can maintain agility while using schemes with different primitives and properties.
Thanks to Jeff Burdges for all the information about Blind RSA and anonymous credentials.
Thanks to Michele Orrù for the help with the unlinkability proof and for the discussions about anonymous credentials.
Thanks to Chelsea Komlo for pointing towards anonymous credentials in the context of DoS defenses for onion services.
This proof sketch was provided by Michele Orrù:
RSA Blind Sigs: https://en.wikipedia.org/wiki/Blind_signature#Blind_RSA_signatures
As you say, blind RSA should be perfectly blind.
I tried to look at Boneh-Shoup, Katz-Lindell, and Bellare-Goldwasser for a proof, but didn't find any :(
The basic idea is proving that:
for any message "m0" that is blinded with "r0^e" to obtain "b" (that is sent to the server), it is possible to freely choose another message "m1" that blinded with another opening "r1^e" to obtain the same "b".
As long as r1, r0 are chosen uniformly at random, you have no way of telling if what message was picked and therefore it is *perfectly* blind.
To do so:
Assume the messages ("m0" and "m1") are invertible mod N=pq (this happens at most with overwhelming probability phi(N)/N if m is uniformly distributed as a result of a hash, or you can enforce it at signing time).
Blinding happens by computing:
b = m0 * (r0^e).
However, I can also write:
b = m0 * r0^e = (m1/m1) * m0 * r0^e = m1 * (m0/m1*r0^e).
This means that r1 = (m0/m1)^d * r0 is another valid blinding factor for b, and it's distributed exactly as r0 in the group of invertibles (it's unif at random, because r0 is so).
https://www.monerooutreach.org/stories/RPC-Pay.html [REF_POW_PERF]: https://gitlab.torproject.org/tpo/core/torspec/-/blob/master/proposals/327-pow-over-intro.txt#L1050 [REF_RES_BENCH]: https://github.com/asn-d6/res_tokens_benchmark