The Grumpy Troll

Ramblings of a grumpy troll.

Golang, TLS & Comodo

Had an interesting spot of debugging today, which highlighted a few issues. One of them is my “Oh, I knew about that feature, interesting to see how it interacts here.”, which might cause some programmers to chuckle darkly.

One server component which my employer maintains talks to a third-party API for an ancillary service; this is over HTTPS with secret API keys. All certificates and hostnames are validated, etc. Recently, the connectivity broke.

The certificate issued to that third party is more than a couple of years old and had not changed, but one of the certificates sent in the intermediate trust chain by the server was much newer, being a Comodo certificate from February of this year. Further, that certificate used SHA2-384 as a hash algorithm (sha384WithRSAEncryption). This aroused my suspicions.

A given key can be used for newer certificates without changing the validity of any signatures made by the key. The chain of trust is “key signs certificate”, so if you as a site operator have a certificate from a Certificate Authority, the CA might have multiple types of certs, each presenting the same public key, which can be used for a point in the trust chain.

The server, written in Go, gave what at first sight might be a quite comprehensive error message:

x509: certificate signed by unknown authority
 (possibly because of "x509: cannot verify signature: algorithm unimplemented"
  while trying to verify candidate authority certificate
  "COMODO RSA Certification Authority")

The first step was, of course, to write a short tool which did the bare minimum to isolate the error and confirm that nothing unexpected was interacting. For reasons which will become clear, it was particularly ironic that this reduction of dependencies worked.

Alas, the error being returned is a "crypto/x509".ErrUnsupportedAlgorithm object, which can be returned from multiple places in the code, which appears to support SHA2-384.

Here, the benefit of Go’s standard library being fully available, small and comprehensible came to the fore. I copied crypto to, roughly, trollcrypto in my $GOPATH and used some shell/sed to quickly change all of the imports within crypto to pull in trollcrypto instead. I then changed all of the error returns inside trollcrypto/x509/x509.go to wrap the error in a new error; for instance:

  if !hashType.Available() {
    return fmt.Errorf("%s: hashtype unavailable for %#v // %#v",
      ErrUnsupportedAlgorithm, algo, hashType)
  }

One invocation of the test program later, and it became clear which point in the code was returning the message: the one I used as an example just above. So what is the .Available() method checking?

Well, Golang’s crypto library maintains a map of all hash types registered with the system. Importing a hash package will typically cause the hash functions it provides to be registered in the map and made available, via an init() function in the package.

Add import _ "crypto/sha512" and the problem disappears. Yes, Go bundles up batches of the SHA2 family hashes, so SHA2-384 is together with SHA2-512 inside crypto/sha512.

Cue one small patch explicitly pulling in every crypto hash library which we want to support with TLS into our common base library.

-The Grumpy Troll

Updates

  • 2014-05-17: Adam Langley, who is always worth reading, has a good post up on SHA-256 deployment and pointing to timescales for migrating towards SHA2-256 and the problems in timescales for doing so while maintaining interoperability; note that SHA2-256 support is much more prevalent than SHA2-384 or SHA2-512.
Categories: TLS Golang Go X.509 Comodo