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.

reop

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.

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 -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.

Example:

-----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-----
ident:tedu
RWS1l0sm+eG0IZ7/JZ7V3Ct584XleF33BQkIiXmHNHjHKWTBZprpVPeiLsCpkRFL1m0y3z7xFBkx
nzoNVbTELwB932C1rdllJwQ=
-----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-----
ident:tedu
RWRDU7WXSyb54bQhy9CZ7Qq6kUZMeOkxDeFNDOU/jl6oQp+vfgGbIP9mRinCQ/pnpvqCMjLnDG7I
I8gMZw/P6zJ+jEaFZX+9pTyCYA==
-----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.

security

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.

One thing to note is that the crypto_box construction 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 nacl 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.

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.)
https://github.com/trevp/noise/wiki/Boxes

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 next two paragraphs are a little dated;
	reop is using libsodium wrappers now.]

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.

limitations

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.

code

Should build most where, if you pick the right Makefile. 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.

