ES version is available. Content is displayed in original English for accuracy.
Advertisement
Advertisement
⚡ Community Insights
Discussion Sentiment
35% Positive
Analyzed from 1278 words in the discussion.
Trending Topics
#openssl#behavior#certificate#different#byte#thing#security#certificates#subject#issuer

Discussion (29 Comments)Read Original on HackerNews
The reason it doesn't validate in Go is that the Subject field in the CA certificate uses a different string encoding than the Issuer field in the leaf certificate, so the fields are not byte-for-byte equal.
Go requires the Issuer and Subject to be byte-for-byte equal. This was permitted by older specs, but RFC 5280 changed the rules to require the use of RFC 4518 (LDAP stringprep) for comparing strings. This turned a simple memcmp into a complicated algorithm that requires implementing Unicode normalization, for virtually zero benefit. That's the last thing you want in your security-critical certificate verifier, so Go quite sensibly chose to follow the older specs in this regard. The CA/Browser Forum's Baseline Requirements also mandate byte-for-byte equality, so Go's behavior won't cause publicly-trusted certificates to be incorrectly rejected.
Note that LDAP stringprep is so complicated that OpenSSL doesn't even try to implement it properly and uses an approximation instead. So you would also be able to "fool" OpenSSL into rejecting certificate chains that RFC 5280 says are valid.
The blog post says that this is an "ongoing debate" in the Go project but I don't think that's accurate. I'd be shocked if they ever changed this behavior, given that crypto/x509 targets publicly-trusted certificates and the current behavior is so much simpler.
I first noticed this when I had to implement a C++ client for a custom RPC protocol and the dev “on the other end of the wire” added one new “convenience” in the data types supported… which would have required me to include the entire Java runtime in my client!
All protocol specs are vulnerable to this effect where it’s all too easy to require clients to include half a dozen different regex engines, three byte code virtual machines, and most of LLVM for good measure.
Because that way we not only get rid of language's smell in how stuff is implemented, but also the act of implementing the spec will quickly show any cases where it looked simple in spec but turns out to be mess implementation wise.
Too much work ? Well, make your spec be tighter and simpler before you burden the rest of programming community with implementing it
If we were to make a new version of the spec for X.509 certificates, I would hope that we would eliminate all the non-UTF8 encodings so that this would be a non-issue.
Differential parsing is a whole class of security bugs and they matter a lot. Take a look at HTTP Request Smuggling for examples.
Also, I am pretty sure there are more non-web x509 certificates out there than all the "browser trusted CAs" combined have signed. :)
Source: having had the displeasure of implementing an RFC 5280 and CABF-conforming path validator.
The same thing shows up in PDFs: two tools can read the same file and disagree about what's actually in it because they interpret the structure differently.
X.509, PDFs, Office docs. Whenever multiple implementations are making judgment calls on the same bytes, those edge cases become very interesting from a security perspective.
The rules for DNs are "there is only one encoding rule and that is memcpy(); there is only one matching rule and that is memcmp()". Given that Go has fallen into the decades-old trap of trying to re-encode strings, it's bound to be vulnerable to any number of other issues like evading excludedSubtrees through string-encoding tricks.
Simply following Chrome/OpenSSL/etc. yields the best compatibility, but the Go behavior is much simpler in this very security critical path, and I'm not sure if it really occurs on the web. It seems most people are reporting it for other uses of X.509 like TPM. You can certainly see the argument in favor of Go's vastly simpler approach.
Go has a lot of trade-offs like this. If you can't decode from or encode into UTF-8 a given filename, Go's high level I/O APIs can't deal with it - depending on your exact point of view, the kinds of software you write and certainly some level of taste, you may see this as horrible, or you may see it as a totally reasonable trade-off. Some programs can go ahead and say "Sorry, we only support UTF-8 filenames" and some really can't.
But when being rational and reasoned it's really challenging to make the argument that Go's choices are bad due to inexperience from the language designers or standard library authors or general poor attention to detail. Calling the developers "Go kids" is already amusing considering it is very famously a project originally created by Rob Pike, Ken Thompson and Robert Griesemer - not foolish young developers who are likely to repeat mistakes of the past by way of ignorance. Of course, that doesn't mean say, the TLS stack, deserves to carry some special weight just because the language was designed by computer science stalwarts, but I've dug into the Go TLS stack a fair bit in my leisure and to me personally it definitely does not feel like the work of people who are ignorant or foolish.
I may be over-indexing on two recent comments here, but it does sort of feel like there's an undercurrent of this, and it bums me out because I particularly like Go for the exact opposite reason, I really think a lot of the tradeoffs, love them or hate them, are very well thought out.
edit: For posterity sake, it is worth noting that it was wrong for me to say "Chrome/OpenSSL/etc." as Chrome actually has a similar behavior to Go here.
[1] Ken Thompson (over 80), Rob Pike (around 70), Robert Griesemer (over 60)