reop - reasonable expectation of privacy
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.
The current version of the source README is likely more up to date than this post.
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. I had my own rant about GPG here, but I think others have explained the situation better. What’s the matter with PGP? and GPG And Me are two places to start.
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.
reop # print usage
usage: reop -G [-n] [-i ident] [-p pubkey -s seckey]
reop -D [-i ident] [-p pubkey -s seckey] -m message [-x encfile]
reop -E [-i ident] [-p pubkey -s seckey] -m message [-x encfile]
reop -S [-e] [-x sigfile] -s seckey -m message
reop -V [-eq] [-x sigfile] -p pubkey -m message
reop -G -i tedu # create tedu key pair
reop -E -i ralph -m message # encrypt a message for ralph
reop -D -x message.enc # ralph decrypts my message
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.
-----BEGIN REOP SIGNED MESSAGE-----
"So, you're the UNIX guru."
At the time, Randy was still stupid enough to be flattered by this attention,
when he should have recognized them as bone-chilling words.
-----BEGIN REOP SIGNATURE-----
-----END REOP SIGNED MESSAGE-----
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).
-----BEGIN REOP PUBLIC KEY-----
-----END REOP PUBLIC KEY-----
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.
$ wc reop/* reop/*/*
3038 11969 89452 total
$ man gpg2 | wc
3138 15876 150953
$ wc rfc4880.txt
5043 28451 203706 rfc4880.txt
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. 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.
The last release was reop 2.1.1.
There’s also a snapshot of source for a possibly forthcoming, but almost certainly stalled 3.0.
Development is now halted. Fixing the remaining problems is quite challenging to do securely. A future better version of reop would be incompatible with existing releases. (This doesn’t mean they are insecure, just that the design doesn’t accommodate new features easily.) The current release accomplishes what I set out to accomplish. With that in mind, I think it’s best to stop here. Maybe someday I’ll give it another go, but for now, adios.