~/openssl-1.1.1i % gcov crypto/x509/x509_vfy.c -bm
File 'crypto/x509/x509_vfy.c'
Lines executed:63.20% of 1625
Branches executed:73.70% of 1194
Taken at least once:53.85% of 1194
Calls executed:58.47% of 496
Creating 'x509_vfy.c.gcov'
~/openssl-1.1.1k % gcov crypto/x509/x509_vfy.c -bm
File 'crypto/x509/x509_vfy.c'
Lines executed:63.21% of 1628
Branches executed:73.75% of 1196
Taken at least once:53.93% of 1196
Calls executed:58.47% of 496
Creating 'x509_vfy.c.gcov'
There are still hundreds of never-taken branches in this file alone. ~/openssl-1.1.1k % grep -c never\ executed x509_vfy.c.gcov
520This is a weakly held opinion. I'm mostly motivated by the knee-jerk response OpenSSL gets on threads like these; I think those responses are based on a reputation that doesn't capture the level of work that has gone into the project in the years since 2012. That doesn't mean it's the apogee of what we can do with a TLS library.
I happen to think the safe way forward is to agree that OK then, we can't do all these other weird things, too bad. But this is an extremely unpopular position because lots of people have at least one weird thing they very much want to do.
Should one even write these kinds of libraries in C?
Frankly, C has gotten us quite far in the systems engineering space. No matter what language, bugs will surface. Starting over on the hype-train isn’t always the best idea!
We live in a fallen world. For a lot of software, the Rust-Go/legacy-C decision is trivial; you'd always take the Rust or Go. But OpenSSL is a tricky case and you can see why in today's announcement: there's a crasher memory corruption problem (but you can crash Rust programs too! today's bug is like a bad "unwrap"). But the first bug is an X.509 logic problem and I don't believe any practical language forecloses on those.
Meanwhile: OpenSSL gets a lot of attention and has a lot of built-up institutional knowledge that other libraries don't. I probably trust it more than I trust any of the other C-language TLS libraries.
Of course OpenSSL is a huge project and mixes many concerns, hard-to-use-right APIs and support for many platforms. This is basically the opposite of what cryptographers and security minded engineers strive for. On the other hand, I am not a cryptographer myself, I am not a C/ Rust/ Assembly or whatever master and I am very far from really understanding modern CPUs though I probably understand a lot more than the average programmer or sysadmin.
I just have the feeling with security, that it is a game of whack-a-mole rather than systematic engineering at times and that doesn't seem to be a sustainable approach.
I'm likely wrong (this isn't my field), but isn't this particular logic bug avoided by exhaustive enums?
I understand this bug took the form of
(1) a var was assigned an enum type, encoding a logic state ("which X.509 error")
(2) the var gets passed around to different functions, which handle the enum values case-by-case
(3) an update adds a new logic state at (1) (explicit ECC params), but doesn't update the case handling at (2) to handle the new value. Plus, since the error types overwrite each other into the same field, the new error type can clobber an older error type so that they both go unhandled.
If you use strict, exhaustive enums, this type of bug is excluded (isn't it?) The type of the enum will propagate to every place it's read, and the compiler will flag the non-exhaustive case handling in the stale code.