#tor #anonymity #networking #arti #network-protocol

tor-proto

Asynchronous client-side implementation of the central Tor network protocols

37 releases (breaking)

0.25.0 Dec 2, 2024
0.24.0 Oct 31, 2024
0.23.0 Sep 30, 2024
0.20.0 Jun 27, 2024
0.0.0 Jun 24, 2021

#102 in Cryptography

Download history 1481/week @ 2024-08-21 1461/week @ 2024-08-28 1843/week @ 2024-09-04 1064/week @ 2024-09-11 1240/week @ 2024-09-18 1892/week @ 2024-09-25 1189/week @ 2024-10-02 850/week @ 2024-10-09 1020/week @ 2024-10-16 1099/week @ 2024-10-23 2178/week @ 2024-10-30 1045/week @ 2024-11-06 1988/week @ 2024-11-13 1981/week @ 2024-11-20 2235/week @ 2024-11-27 1426/week @ 2024-12-04

7,730 downloads per month
Used in 39 crates (17 directly)

MIT/Apache and maybe LGPL-3.0-only

2.5MB
43K SLoC

tor-proto

Implementations for the core Tor protocol

Overview

The tor-proto crate lies at the core of Arti, a project to implement Tor in Rust. Most people shouldn't use this crate directly, since its APIs are needlessly low-level for most purposes, and it is easy to misuse them in an insecure or privacy-violating way.

Most people should use the arti-client crate instead. This crate is of interest mainly for those that want to access the Tor protocols at a low level.

Core concepts

At its essence, Tor makes connections called "channels" to other Tor instances. These channels are implemented using TLS. Each of these channels multiplexes a number of anonymized multihop "circuits" that act as reliable transports for "relay messages" that are sent between clients and the different relays on the circuits. Finally, each circuit multiplexes a number of "streams", each corresponding roughly to an application-level request.

This crate implements the logic, protocols, and cryptography that implement these channel::Channels, circuit::ClientCircs, and stream::DataStreams. It uses rust async code and future-related traits, and is intended to work with (nearly) any executor implementation that complies with the futures API. It should also work with nearly any TLS implementation that exposes AsyncRead and AsyncWrite traits.

Not in this crate

This crate does not implement higher level protocols, like onion services or the Tor directory protocol, that are based on the Tor protocol here. Nor does it decide when, how, or where to build channels and circuits: that's the role of higher-level crates.

This crate also has no support for timeouts, so every network operation here has the potential to block the current task indefinitely. Timeouts are another necessary piece that gets added at a higher level.

In order to create channels and circuits, you'll need to know about some Tor relays, and expose their information via tor_linkspec::ChanTarget and tor_linkspec::CircTarget. Currently, the tor-netdir crate is the easiest way to do so.

For an example of this crate in action, see the arti-client library, or the arti CLI.

Design notes

This crate's APIs are structured to limit usage of an asynchronous runtime: It doesn't launch tasks or create timers except when necessary.

To the extent possible, this crate avoids doing public-key cryptography in the same functions it uses for network activity. This makes it easier for higher-level code to parallelize or yield around public-key operations.

Also, this crate tries to avoid knowing or encoding information about what its objects (channels, circuits, streams) are "used for". That is, whenever possible, we encode how an object should behave, not the reason that it should behave that way. For example, the Circuit object in this crate remembers the path through which the circuit was built, but not the purpose that the circuit serves, or what it may be used for. It's the responsibility of other crates to enforce that kind of rule.

Why separate behavior from purpose in this way? We do so in order to prevent a kind of logical overloading that we ran into with the C tor implementation, where usage information is not separate from behavioral settings. Since usage information is available, at all points in the codebase the C tor code has converged in many places on complex logic involving that usage information in order to set individual behaviors. Because of that, adding a new kinds usage or behavior in C tor has become quite complex. We're trying to avoid that kind of complexity in Arti.

Limitations

This is all a work in progress, and will need severe refactoring before it's done.

This is a client-only implementation; there is no support for the operations that Relays need.

There are too many missing features to list.

There isn't enough documentation or examples.

This crate was my first attempt to use async in rust, and is probably pretty kludgy.

I bet that there are deadlocks somewhere in this code. I fixed all the ones I could find or think of, but it would be great to find a good way to eliminate every lock that we have.

License: MIT OR Apache-2.0

Dependencies

~20–33MB
~501K SLoC