The design of Thrussh

Wednesday, July 20, 2016
By Pierre-Étienne Meunier

I’ve recently written an SSH library called Thrussh, and I’ve also started to use it for actual tasks like replacing the SCP client in Pijul.

Not doing our own crypto

This is an important rule of cryptography: do not do your own crypto. Cryptography is full of subtleties, and it is frighteningly easy to introduce more problems than you solve.

For example supporting outdated cryptography, even if you don’t really plan to use it, can be harmful to your legitimate crypto, as shown by the Drown attack on HTTPS.

Or side-channel attacks, by which an attacker can get bits of a secret key by monitoring differences in the heat, electromagnetic, or time patterns of a processor.

This is why I’m using libsodium. Also, this is why I’m using the raw version of it, with as little fuss as possible in the bindings themselves.

The design

Thrussh was designed to have the lowest possible code size and dependency set, so as to make it easily readable. It has a server and a client side, sharing a datastructure called CommonSession (see src/session.rs in our darcs repository). Sessions maintain a state, essentially when exchanging keys in the beginning, or when re-exchanging keys during an SSH connection.

Thrussh does not depend on any network library: users of this library are instead expected to provide it with instances of std::io::BufRead and std::io::Write. This design allows it to be used with synchronous or asynchronous sockets. In both cases (synchronous or asynchronous), the interface is exactly the same: users need to implement either the Server trait (if they’re writing a server) or the Client trait (if writing a client). Then, they just have to create an instance of thrussh::server::Connection (respectively thrussh::client::Connection), and then successively calling the .read and .write methods of the connections, in a loop (starting with .write for synchronous sockets).

A number of methods (for instance .is_authenticated) are supplied for the client to be able to stop the loop at some stages of the protocol, for instance when user input is needed.

Writing an SCP client

Thrussh has been mostly tested with the mio crate, which allows efficient and portable asynchronous I/O. Writing an SCP client was actually fairly easy, see the result on its darcs:

darcs get https://pijul.org/darcs/scp

It’s not a full scp program: in particular, it does not have the -f and -t options required to implement SCP on the server side. This is probably a relatively easy task anyway, as it does not use SSH: it’s just a parser of stdin that can write to files. See this description of the SCP protocol for more details.

Still using darcs

We’ve received a number of comments suggesting that we should move to github as soon as possible. This is simply not going to happen. We might switch to Pijul when it’s ready, of course.

The main benefit we see in darcs is that we already know it, and it can be learnt completely in almost no time by anyone.

This is not the case of any 3-way-merge-based systems, although people who’ve been using such systems for years usually reply that “once you know it it’s easy”. This is probably true, but unrelated to the above claim.

Also, in darcs, patches are associative, which is a really useful property to have in a VCS. This is not a bug/implementation mistake in 3-way merge/git, as suggested by some. It’s a problem in the algorithm itself.