One of the obvious ideas I (and several others had) as soon as signify was released was to extend it to do more. After all, no program is complete until it can read email. Or at least munge up your email real bad.
Enter reop - reasonable expectation of privacy.
With some curiosity I read Creating the perfect GPG keypair. My conclusion is that there’s no such thing has a perfect GPG key pair. And we wonder why people leak secrets using hotmail. This shouldn’t be hard. More ranting about GPG at the bottom. Moving on.
reop is clearly influenced by signify (What can I say? I like my own designs.), but it’s not a clone. Its handling of keys is the most significant difference (besides the obvious, more features). Default keys are supported, and you can even add all your pals to ~/.reop/pubkeyring and verify their messages automatically, just like a normal PGP program.
Supported operations include signing -S and verifying -V messages, plus a variety of options for encrypting messages (-D -E). It does everything you’d expect a PGP program to do. More accurately, it does everything I expect you to expect a PGP program to do. I may be wrong, but it kills me to see people reaching for the gpg or openssl hammer of infinite possibilities for the simplest of tasks. Limitations below.
There is a (short) manual, of course, but there aren’t so many options that you should need to consult it more than once. Usually the short help text should be sufficient to get you started. I’ve tried to keep the option mnemonics reasonable.
I had a short lived plan to support the real OpenPGP standard, but as I was scrolling through it, I came across the words “ASN.1 Object Identifiers” and my monitor went blank to prevent permanent damage. As it is, reop implements a sort of look-alike/feel-alike facsimile of the standard.
A reop key technically consists of two keys (one for signing, one for encrypting). The interesting part of a reop public key fits in a tweet (the ----- decoration stuff is too much though).
You don’t get to pick your algorithms. I pick them (sort of; nacl picked them). There is theoretical support for future algorithm changes. In general, reop only asks questions that only the user can answer, and which the user should be able to answer. Fewer features -> fewer feature options -> fewer commands to edit, adjust, and otherwise tweak those options.
I’m guessing you’d rather hear about the fun crypto bits than my infallible programming skills.
All the crypto comes from nacl (indirectly via libsodium). Specifically, reop uses crypto_sign (Ed25519), crypto_box (Curve25519, Salsa20, and Poly1305) and crypto_secretbox (Salsa20 and Poly1305). I have not personally vetted these functions. Internet told me they were safe. ChaCha20 would be nice; maybe someday.
One thing to note is that the crypto_box construction (reop -E) may not behave like other public key encryption schemes you are familiar with. It takes two key pairs; the receiver’s public key as expected and the sender’s secret key, which offers a measure of authentication.
What the documentation doesn’t really make clear is that same set of keys used for encrypting work for decrypting (i.e., it’s not asymmetric). For instance, if Alice, sending a message to Bob, encrypts with secAlice and pubBob, that would normally be decrypted by Bob with pubAlice and secBob. But secAlice and pubBob work just as well to decrypt. If you were expecting to encrypt some secret with a public key and then have that computer “forget” how to access the secret, that won’t work.
Earlier versions of reop used to work around this half-assedly. Now the workaround is at least 3/4-assed. An ephemeral key is generated. The meat of the message is encrypted with secEph and pubBob. Then secEph is discarded. Now only pubEph and secBob can decrypt. To authenticate the message, we encrypt pubEph with secAlice and pubBob. Bob can authenticate the message with secBob and pubAlice, but he could also have forged the message, giving Alice deniability. This is something like what Noise Boxes would do, but which pub keys get encrypted are swapped. (reop doesn’t hide sender identity.)
While it’s possible to do things the other way around and try to provide sender confidentiality, I’m not sure it’s a good fit for reop. It doesn’t align with how the tool is likely to be used, and sender identity will most likely leak some other way. Better IMO to simply not make that promise.
Nonces, where necessary, are generated randomly.
The nacl functions are all accessed via wrappers, very similar to the C++ wrappers. The C nacl API requires the caller to provide padded buffers (i.e., ciphertext, auth tag, and zeroed scratch space all contiguous in memory), which is somewhat inconvenient for a program like reop. As a result, more memory is allocated and data copied than strictly mathematically necessary. Additionally, nacl has a “packet” interface, not a “stream” interface, which imposes some other limits on message size, but for most practical purposes it should be fine.
It’s unfortunate, but I think nacl is the closest I’ve ever seen to a software interface that is perpendicular to the rest of the program. For a program that is essentially a CLI for nacl, reop spends considerable effort making sure that things are just so. The ZEROBYTES vs BOXZEROBYTES nonsense is just this side of ridiculous.
There’s no support for key revocation, or long chains of certificates, or partial trust, or key servers. For the most part, I think this is feel good wankery that doesn’t accomplish as much as one would like. I wonder how many people have ever revoked their PGP keys to see how it works in practice. The reop trust model is very simple. You can probably even understand it.
Keys don’t expire. If we expand the scope of inquiry slightly to TLS certs, I’ve lost count of the problems I’ve seen caused by prematurely expiring certs. Number of times an expired cert has saved my ass? Zero. This is arguably shortsighted, I know.
You can’t embed a JPG selfie into reop keys. Not even a tiny one.
reop doesn’t include a Tempest resistant font for viewing top zecret messages.
Developed on OpenBSD. Builds elsewhere. Uses libsodium.
I have endeavored to keep the code modular, such that it could be used in a library, but this is generally thwarted by the knowledge that top-level code has the privilege of simply giving up when things don’t go its way. Returning error codes -> having to check error codes.
I’m not enamored with the parsing code. Too much pointer banging.
As promised, a rant. I’m sure there are people who use GPG. I can’t. Let’s run gpg2 --gen-key.
Why, in 2014, is GPG prompting me to create an RSA sign only key, which an RFC (4880) written in 2007 says are deprecated and SHOULD NOT be generated?
Help, I’m trapped in an infinite loop. If the default isn’t the right choice, it shouldn’t be the default. But here comes my favorite part.
Holy shit! What kind of toy random number generator is GPG using? Never mind that the message is printed twice. Never mind that both messages flick by before I have half a chance to read them, let alone do anything to entropize my system. “Wiggle the mouse for moar entropies.” I wouldn’t trust a program that prints messages like this to protect my middle school slambook.