Developing software using common cryptographic tools is horrible

While developing poobrains, I have gained a bit of experience dealing with common cryptograhic tools (namely TLS and PGP/GnuPG) from both a user and a developer perspective and I'd like to report the horrors I witnessed and the crimes I've seen comitted.

As the astute observer will already know, all technology is horrible and cryptographic software is certainly no exception.

GnuPG - Oh Eris, why?

Are you a hacker with a masochistic streak? Why not write software using GPG?!

Seriously, this is a calamity of its own. PGP is the de-facto standard for encrypting emails (lel S/MIME) and GnuPG seems to be the only serious FOSS implementation.

Traditionally, applications using GnuPG for their PGP support directly called the gpg executable using something like system(). This might just be me, but this kind of looks like a recipe for injection vulnerabilities.

At some point however, the developers of GnuPG created GPGME in their endless wisdom. Please do note that the ME stands for 'Made Easy'.

GPGME exists to create a stable API for developers who want to use GPG in their applications.

So it seems like we'd finally get around our application calling the gpg executable, right? Not quite, you see the GPGME library does this shit itself.

And while I appreciate the fact that there's theoretically only one place where input sanitation needs to be done, I have the nagging feeling that it's not being done, since I couldn't find any comments yelling about input sanitation in the GPGME sources.

On the bright side, GPGME even adopted an existing python module as their official python bindings some time in late 2016. At least one good thing!

Noooooope. I got it to work on FreeBSD, but it failed on both Gentoo and Fedora, with two different behaviors nonetheless. This is far from being production grade.

So back to the more, ahem, traditional approach, it is.

In terms of python modules we have two strong contenders:

  • python-gnupg
    • This is a fork of Steve Traugotts GPG.py
      • Which is a fork of A.M. Kuchlings GPG.py
  • gnupg
    • This is a fork of python-gnupg by I. Lovecruft.
    • Apparently there's input sanitation
    • The repository and many URLs still say python-gnupg.
      • Because you weren't confused enough already!

So, because I wanted proper security and don't want to fuck up my own input sanitation, I chose gnupg. And lo' and behold, everything worked.

Everything? Of course not, this is software we're talking about.

In June 2016, GnuPG added the KEY_CONSIDERED status, which the gnupg module still doesn't handle. This seems to fuck up the return values in a few places quite badly. When importing a public key for example, the return value indicates nothing worked, while the key was imported just fine. It also leads to the list of public-key fingerprints in the return value to be empty, which is kind of a big deal because you need to know the fingerprint in order to use the corresponding public key for encryption.

There's a pull request dangling in needs-revision state since December and no change in sight. Meaning this module has basically been broken for almost a year now. And it still seems to be the best choice out there.

Update: (Most of) the problems with the gnupg module can actually be avoided by using GnuPG 2.0 instead of 2.1. Only problem for me (because of course there is one) is that FreeBSD doesn't allow me to set a default GnuPG version. This wouldn't be a big deal if I could actually get mutt to read encrypted mails without gpgme, which seems to always pull in 2.1, but apparently I fucking can't. >:(

X.509 - the PDF of IT Security

The specification defining the structure of public-key certificates used by SSL/TLS is X.509. It came out of a series of standards that define the functioning of a directory service (which was the main inspiration for LDAP, by the way).

X.509 is one of those standards that use an arcane language called ASN.1 to define data structures and their constraints. And while I like the concept of having a machine-readable spec, I have developed a sizable dislike of X.509.

Trying to understand X.509 by reading the spec feels a lot like trying to physically wrap your head around a block of concrete.

Two X.509 things I can't get over:

  • The serial is a number! No, it's a printable string! Wait, what?
    • X.509 claims it's a number (see chapter 3.4.14)
    • X.520 which AFAICT should be authoritative and supplies ASN.1 code claims it's a printable string (see chapter 6.2.9)
    • Does this mean developers expectation of serial being a number is wrong?
  • There is no proper definition for what the Common Name is.
    • Haha, just the dns name of the server, right?
      Nope, this isn't specified in X.5xx.
    • From the official definition in X.520, it's not even understandable, what the actual use of this field is.
    • X.520 claims that Common Name is an identifier, which it can't really be, since it states in the same paragraph that Common Name can be ambiguous.
    • To the best of my understanding, the Common Name is just something like a label for the certificate. I'm not really sure why httpds and browsers use it as anything else. Probably hysterical raisins…

Browsers hate you and your pathetic need for privacy

Let's be honest for a second and admit that passwords are fucking horrible.

People choose weak passwords, people re-use passwords. Even if you're using a password manager, you're still vastly less secure than when using public key crypto. You'd need passwords several hundred characters long to approach the same level of entropy and you'd still lack security features when compared to public key based cryptographic authentication.

Additionally, a password manager concentrates a LOT of credentials in one place. If someone compromises it, that party can change your passwords and thus lock you out of your own accounts.

With public key authentication, each device can have it's own key. This means that unless the account is authorized to revoke keys, an attacker will be able to use your account but they won't be able to lock you out.

It also means that a compromised key can be revoked without affecting your ability to log in from your other devices.

Your run-of-the-mill browser even supports public key cryptography to authorize you at a web server via TLS client certificates. Yay, X.509!

Even better, HTML5 included the <keygen> element, which is by far the single most awesome thing that ever was in HTML.

<keygen> can be used in forms and is displayed as a dropdown, allowing users to select the length of a key to be generated. When a form containing it is submitted, the browser generates a private key and a public key and then sends the public key to the server, which in turn can generate and return a client certificate which the browser will put into its certificate storage.

Press button, get a client certificate you can use to securely log in.

It could be that easy. Except, of course, it fucking isn't.

The only browser that ever really supported it was firefox, though only with keylengths of 1024 and 2048 bit. Browser vendors decided to drop it and W3C followed by removing it in HTML 5.2, deprecating it in favor of a bunch of motherfucking javascript APIs. The only good reason I found for this is the claim that SPKAC (the format in which the public key is sent to the server) depends on md5 - something for which I haven't found any evidence yet…

Let me tell you why the deprecation of <keygen> is a not only technically horrible, but why you should be pissed about it.

First off, <keygen> was the only way to have a web server create a client certificate for which it does not know the private key. Now, this shouldn't™ be a problem unless you use the same private key for a different service, but it's still ugly. The user and only the user should have access to the private key. Also, this means the private key has to be submitted over the internet - another vulnerability waiting to happen.

Secondly, the javascript APIs that are supposed to replace <keygen> can't be used to build a replacement for it. There's no way to add certificates to the browser certificate storage other than having the user do a manual import anymore.

Also, they're fucking javascript! This is wrong on so many levels, I'm not even sure where to start… It's untrusted code that can change on every single request (possibly targeted for demographics/specific users) thus making a real audit of it impossible. This is a state of affairs that might be okay for site operators, but it completely fucks over the users agency.

Oh, and let's not even fucking talk about how these shitty APIs are going to give us a completely new class of XSS problems.

I'm one of those people who are very selective about allowing any javascript and there's good reason for this. Javascript enables highly granular surveillance and gives malware a nice entry vector. The "replacement" the W3C and browser vendors have chosen disregards this entirely.

But what really grinds my gears is the fact that <keygen> offered acceptable UX for strong cryptographic authentication of web users for the first time ever. Its death probably delays any widespread adaptation of this sort of thing on the web for at least 10 years.

As if DRM in HTML wasn't already horrible enough, the W3C fucked over the security of every connected citizen of the world for years to come.