Monocypher

Boring crypto that simply works

Why Monocypher?

New cryptographic code is generally frowned upon in information security circles. You’re not supposed to write new code. New code means more work for auditors, and until it has been properly vetted, it can also be a security risk. A new cryptographic library written by someone with no existing reputation will necessarily draw suspicion.

So why did I write a new cryptographic library?

At first, I just wanted a simple (and free) file encryption utility. What I found in 2016 didn’t look simple. I figured I could write my own and searched for a crypto library. OpenSSL was way too complex to even look at, but I eventually found the NaCl crypto libraries: NaCl itself, libsodium, and TweetNaCl.

NaCl broke new ground, but it had a reputation of being difficult to package or install. libsodium solved that problem but still felt fairly big (version 1.18 comprises almost 30K lines of code in 230 files). But then I saw TweetNaCl. Only 715 lines of code in a single source file. So cryptographic code can be simple.

I wanted the simplicity of TweetNaCl, only with the latest and greatest at the time (Chacha20, BLAKE2b, Argon2i). That’s what I started with: an unassuming clone of TweetNaCl with a couple tweaks. Then I began to improve performance, and Monocypher quickly became its own thing: a compact, portable, opinionated, fast crypto library.

Why not libsodium?

Libsodium has such a good reputation that it is recommended for anything that doesn’t need TLS. It is much smaller and simpler than OpenSSL. It has no timing leaks. It is available on a wide range of platforms, with bindings to every programming language out there. It has been audited and found secure. And it is fast.

On the other hand, it tends to be an all-or-nothing proposition. To use any of it, you effectively need to take the full binary (3.3MB & 600 exported symbols vs. Monocypher’s 100KB & 59 symbols). You can’t easily extract the code you want, partly because determining what code actually runs is not trivial: platform specific code is selected at compile time both with #ifdefs and at runtime by sodium_init().

Libsodium is also mostly absent from the embedded scene, probably because it’s too big or too hard to port. On those devices, Monocypher tends to be the fastest library around.

Why not NaCl?

The primary author of NaCl is Daniel J. Bernstein. That’s a stamp of approval if there ever was one. On the other hand, it has a reputation of being difficult to install or package; as of 2023/02, the signature code is marked as experimental; and by now, NaCl has mostly been subsumed by libsodium.

Why not TweetNaCl?

TweetNaCl is slower than Monocypher by an order of magnitude (give or take), doesn’t wipe internal buffers, and doesn’t have password hashing. It also has two harmless instances of undefined behaviour (left shifts of negative integers on lines 281 and 685).

Why not LibHydrogen?

LibHydrogen is a lightweight cryptographic library written by Frank Denis, the main author of libsodium. It is suitable for constrained environments like micro-controllers, on which it takes little program space and a tiny amount of stack space.

On the other hand, it is not compatible with libsodium, which thanks to its reputation and speed is a typical first choice on the server. It also runs slower than Monocypher.

Why Monocypher?

Like NaCl & TweetNaCl (and unlike libsodium & LibHydrogen), Monocypher is dedicated to the public domain; Monocypher does this via CC-0. This may not sound like much but not requiring attribution have some advantages, such as lack of tedium, or the ability to obscure which library you use (to slow down attacks on your software or crackme). In jurisdictions that do not recognise the public domain and where you do not trust CC-0 to provide an equivalent effect, you can obtain Monocypher under a 2-clause BSD licence.

Monocypher is small. One source file, one header file, less than 2000 lines of code (counted with sloccount), 45 exported functions. Zero dependencies. Just drop it into your C or C++ project and forget about it forever after. That, and a consistent API, makes Monocypher easy to learn, easy to use, easy to deploy, and easy to bind to high level programming languages.

Monocypher is portable. Written in standard C99, it can compile as C or C++ on a very wide range of targets, and its size even makes it suitable for micro-controllers and other such constrained environments. That portability wasn’t achieved by using #ifdef but by sticking to strictly conforming C code and avoiding dependencies, including libc.

Monocypher is fast. Not as fast as libsodium on modern desktop processors, but even there the difference isn’t big enough to matter in many cases. (Of course, some applications do require the fastest implementation possible.) On smaller processors like the Raspberry Pi the difference is often barely noticeable. On even smaller targets Monocypher is often the fastest thing around. By far.

Monocypher is compatible with libsodium (libsodium only, not (Tweet)NaCl). This has two advantages: the first is the ability to upgrade to a faster library, should you ever need to. The second is the ability to talk to the faster library across the network. For instance, Monocypher can be used on IoT devices and libsodium on the server.

Monocypher has man pages. You can install the man pages, type man crypto_lock, and get what you expect! How neat is that?

Why not Monocypher?

libsodium runs faster on native x86_64 CPUs. In some cases (big files, server under heavy load), we need that extra speed.

Libsodium provides a number of convenience functions to encode and decode Base64 and hexadecimal formats, pad data, and do large number arithmetic (which is useful for handling nonces).

Because it has no dependency, Monocypher cannot handle memory locking and random number generation—users have to do it themselves. Libsodium provides utilities for memory locking and handles random numbers itself.