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.
- Adam works for Google, apparently on both the HTTPS serving side of security and on the Chrome client stack.
- Connect to https://cert-test.sandbox.google.com/ to see if your client can work with SHA2-256 certificates.