Status: this page is currently a draft
We want to make sharing bridges easier.
Therefore, we would like to implement a clickable URI format to automatically add bridges to Tor Browser (or any other Tor-based application).
bridge://
scheme
The Having a dedicated scheme has the advantage that it could contain only the information we need, which is an advantage when dealing with QR codes.
Also, it allows registering a handler application basically in all OS.
Bridge Address
Keeping the bridge address in the authority part is semantically correct and guarantees compatibility with existing URI parsers. It also makes the URI appear more familiar.
Fingerprint
The fingerprint is just after the address in the bridge string. Therefore, it makes sense to have it as the path.
Please notice that the fingerprint is optional, according to tor(1)
.
Transport
Additional parameters depend on the transport. Therefore, a proposal was to have it as a scheme. But we want to stay transport-agnostic, to keep them pluggable. In other words, if a new transport is added, any apps already recognizing our scheme will not need to be updated.
This prevents compound schemes like bridge+obfs4
or bridge+snowflake
(which would not have any advantages anyway).
A proposal is to have it as the user information, which would have two benefits:
- it would match the order on the bridge string;
- we would have a separate URI part for it.
The alternatives are placing it after the fingerprint, separated with a slash, or to have the transport in the query string.
Other parameters
Other parameters depend on the transport. They can be added to the query, percent-encoded, and separated by an ampersand. On the final bridge string, they will be separated by a space. The parameter order must be preserved when converting the bridge URL to the bridge string.
Examples
A vanilla bridge without transport:
bridge://38.229.1.78:80/C8CBDB2464FC9804A69531437BCF2BE31FDD2EE4
An obfs4 bridge:
bridge://38.229.1.78:80/C8CBDB2464FC9804A69531437BCF2BE31FDD2EE4/obfs4?cert=Hmyfd2ev46gGY7NoVxA9ngrPF2zCZtzskRTzoWXbxNkzeVnGFPWmrTtILRyqCTjHR+s9dg&iat-mode=1
The builtin snowflake bridge:
bridge://0.0.3.0:1/2B280B23E1107BB62ABFC40DDCC8824814F80A72/snowflake
A meek bridge:
bridge://0.0.2.0:2/97700DFE9F483596DDA6264C4D7DF7641E1E39CE/meek?url=https%3A%2F%2Fmeek.azureedge.net%2F&front=ajax.aspnetcdn.com
Recommendations to implementers
There is a long history of attacks based on custom URIs123, they tend to open a door for remote attackers to execute programs locally. We have to assume a bridge URI might be forged by a remote attacker, and sanitize them before using them.
Every implementation should sanitize the URIs carefully, for example remove any exploitable characters, like %0A
(the new line character) that might be used to inject commands into the torrc
.
The HTTP(S) fallback
The disadvantage of a custom scheme is that applications may only recognize a few common ones (http
, https
, mailto
, etc...).
On Android, apps can be registered as handlers for HTTP links with a specific host4.
For other platforms, the URL could be opened on the browser, which could, in turn, redirect to the bridge://
URL. We should also add the decoded bridge string, the instructions to add it to Tor Browser/other apps, and a small introduction to bridges/Tor.
In any case, all the information should be on the fragment to avoid it being transferred through the network.
As for the format, we could just base64-encode the bridge string.
The QR code
We could use the bridge://
scheme also for the QR codes.
However, we should implement its decoding and ask for camera permission on Android. This may be annoying for users of a security/privacy-focused app, but we could explain that it is needed only for this reason and that they can revoke it on the settings.
Bridge URIs do not address the problem of multiple bridges in the same QR. An idea could be to separate them by newlines.
The human-friendly ID
Bridge sharing is prone to errors, especially if done through copy&paste.
Therefore, we have added a checksum of the bridge string to Tor Browser.
It is the FNV-1a hash5 of the bridge string (trimmed of any leading and trailing white spaces). Since FNV-1a works on bytes, the bridge string should be converted to UTF-8, even though bridge strings use only ASCII characters currently.
For maximum compatibility amongst platforms, architectures, and programming languages, the hash is computed using signed 32-bit integers. We checked, for example, that the Tor Browser JS implementation is interoperable with a C++ implementation.
For reference, here is the JS implementation:
const prime = 0x01000193;
const offset = 0x811c9dc5;
let hash = offset;
const encoder = new TextEncoder();
for (const charCode of encoder.encode(bridgeString)) {
hash = Math.imul(hash ^ charCode, prime);
}
Finally, the 32-bit hash is split into 4 (unsigned) bytes, which are used as indices on an array of 256 emojis. The emojis are shown from the most significant byte to the least significant one.
References
- The original discussion, on legacy/trac#15035
- Some implementations based on that discussion