Hacker News new | ask | show | jobs
by hwbehrens 1666 days ago
> It is also hard to convince people to do a ground up rewrite of code that is seemingly working fine.

I think this is an understatement, considering that it's a core cryptographic library. It appears to have gone through at least five audits (though none since 2010), and includes integration with hardware cryptographic accelerators.

Suggesting a tabula rasa rewrite of NSS would more likely be met with genuine concern for your mental well-being, than by incredulity or skepticism.

4 comments

> Suggesting a tabula rasa rewrite of NSS would more likely be met with genuine concern for your mental well-being, than by incredulity or skepticism.

In my experience, porting code more or less directly from one language to another is faster and easier than people assume. Its certainly way faster than I assumed. I hand ported chipmunk2d to javascript a few years ago. Its ~30k LOC and it took me about a month to get my JS port working correctly & cleaned up. I spend up throughout the process. By the end I had a bunch of little regexes and things which took care of most of the grunt work.

If we assume that rate (about 1kloc / person-day) then porting boringssl (at 356kloc[1]) to rust would take about one man-year (though maybe much less). This is probably well worth doing. If we removed one heartbleed-style bug from the source code on net as a result, it would be a massive boon.

(But it would probably be much more expensive than that because the new code would need to be re-audited.)

[1] https://www.openhub.net/p/boringssl/analyses/latest/language...

> In my experience, porting code more or less directly from one language to another is faster and easier than people assume.

That's often true right up to the point where you have to be keenly aware of and exceptionally careful with details such as underlying memory management functionality or how comparisons are performed. With this in mind, cryptographic code is likely a pathological case for porting. It would be very easy to accidentally introduce an exploitable bug by missing, for example, that something intentionally reads from uninitialized memory.

On top of the re-audit being expensive.

> for example, that something intentionally reads from uninitialized memory.

Sounds terrible. This should never happen in any program, so any behavior relying on it is already broken.

I'm way more concerned by memory safety issues than cryptographic issues. Frankly, history has shown that cryptographic bugs are far easier to shake out and manage than memory safety bugs.

> Frankly, history has shown that cryptographic bugs are far easier to shake out and manage than memory safety bugs.

and yet, we had the debian/ubuntu openssl bug of 2008... due to someone not wanting to intentionally read from uninitialized memory. Really, it kind of proved the opposite. Valgrind and other tools can tell you about memory safety bugs. Understanding that the fix would result in a crypto bug was harder.

OpenSSL's use of uninitialized memory to seed entropy was always a terrible idea. The PRNG was fundamentally flawed to begin with.

> Really, it kind of proved the opposite.

Not really. Exploited bugs in cryptographic protocols are extremely rare. Exploited memory safety bugs are extremely common.

> Valgrind and other tools can tell you about memory safety bugs.

Not really.

> Understanding that the fix would result in a crypto bug was harder.

Like I said, OpenSSL's PRNG was brutally flawed already and could have been broken on a ton of machines already without anyone knowing it. A compiler update, an OS update, or just unluckiness could have just as easily broken the PRNG.

Building memory unsafety into the prng was the issue.

Memory safety issues are exploited orders of magnitude more often than crypto bugs.

edit: Also, memory safety bugs typically have higher impact than crypto bugs. An attacker who can read arbitrary memory of a service doesn't need a crypto bug, they can just extract the private key, or take over the system.

Crypto bugs are bad. Memory safety bugs are way, way worse.

If a programs reads from uninitialised memory, I hope for its sake that it does not do it in C/C++. Setting aside that uninitialised memory is a hopelessly broken RNG seed, or the fact that the OS might zero out the pages it gives you before you can read your "uninitialised" zeroes…

Reading uninitialised memory in C/C++ is Undefined Behaviour, plain and simple. That means Nasal Demons, up to and including arbitrary code execution vulnerabilities if you're unlucky enough.

Genuinely curious what the use case(s) of reading from uninitialized are. Performance?
It was used as a source of randomness. Someone blindly fixing a "bug" as reported by a linter famously resulted in a major vulnerability in Debian: https://www.debian.org/security/2008/dsa-1571
This is incorrect.

If they had simply removed the offending line (or, indeed, set a preprocessor flag that was provided explicitly for that purpose) it would have been fine. The problem was that they also removed a similar looking line that was the path providing actual randomness.

> In my experience, porting code more or less directly from one language to another is faster and easier than people assume

Converting code to Rust while keeping the logic one-to-one wouldn't work. Rust isn't ensuring memory safety by just adding some runtime checks where C/C++ aren't. It (the borrow checker) relies on static analysis that effectively tells you that the way you wrote the code is unsound and needs to be redesigned.

Sounds like a feature of the porting process. Not a bug. And I’d like to think that BoringSSL would be designed well enough internally to make that less of an issue.

I agree that this might slow down the process of porting the code though. I wonder how much by?

The article says Chromium replaced this in 2015 in their codebase. (With another memory-unsafe component, granted...)
BoringSSL started as a stripped down OpenSSL. That's very different from a ground-up replacement. The closest attempt here is https://github.com/briansmith/ring but even that borrows heavily the cryptographic operations from BoringSSL. Those algorithms themselves are generally considered to be more thoroughly vetted than the pieces like ASN.1 validation.
This sounds like a nightmare for any downstream users of this library. Any one of those bullet points in that section would be a major concern for me using it in anything other than a hobby project, but all of them together seem almost willfully antagonistic to users.

This is especially true given it’s a security library, which perhaps more than any other category I would want to be stable, compatible, and free of surprises.

“You must upgrade to the latest release the moment we release it or else you risk security vulnerabilities and won’t be able to link against any library that uses a different version of ring. Also, we don’t ‘do’ stable APIs and remove APIs the instant we create a new one, so any given release may break your codebase. Good luck have fun!”

Note that the readme is outdated. With the upcoming 0.17 release, which is in the making for almost a year already, you can link multiple versions of ring in one executable: https://github.com/briansmith/ring/issues/1268

Similarly, while the policy is still that ring only supports the newest rust compiler version, due to the fact that there has been no update for months already, you can use it with older compiler versions.

Last, the author used to yank old versions of its library, which caused tons of downstream pains (basically, if you are a library and are using ring, I recommend you have a Cargo.lock checked into git). This yanking has stopped since 3 years already, too. Don't think this was mentioned in the readme, but I feel it's an important improvements for users.

So a great deal of things has improved, although I'd say only the first bullet point is a permanent improvement, while the second two might be regressed upon. idk.

Yeah, that is pretty wild. Total prioritization of developer convenience over actual users of the library.
Or rust-crypto
nss was also generally considered to be thoroughly vetted though
There’s a world of difference between ASN.1 validation and validation of cryptographic primitives. The serialization/deserialization routines for cryptographic data formats or protocols are where you typically get problems. Things like AES and ECDSA itself, less so, especially when you’re talking about the code in BoringSSL. Maybe some more obscure algorithms but I imagine BoringSSL has already stripped them and ring would be unlikely to copy those.

Why? Cryptographic primitives don’t really have a lot of complexity. It a bytes in/bytes out system with little chance for overflows. The bigger issues are things like side channel leaks or incorrect implementations. The former is where validation helps and the latter is validated by round-tripping with one half using a known-working reference implementation. Additionally, the failure mode is typically safe - if you encrypt incorrectly then no one else can read your data (typically). If you decrypt incorrectly, then decryption will just fail. Ciphers that could encrypt in an unsafe way (ie implementation “encrypts” but the encryption can be broken/key recovered) typically implies the cipher design itself is bad and I don’t think such ciphers are around these days. Now of course something like AES-GCM can still be misused by reusing the nonce but that’s nothing to do with the cipher code itself. You can convince yourself by looking for CVEs of cryptographic libraries and where they live. I’m not saying it’s impossible, but cipher and digest implementations from BoringSSL seem like a much less likely place for vulnerabilities to exist (and thus the security/performance tradeoff probably tilts in a different direction unless you can write code that’s both safer while maintaining competitive performance).

For symmetric cryptography (ciphers & hashes), I agree. I'd say as far as to say they're stupidly easy to test.

Polynomial hashes, elliptic curves, and anything involving huge numbers however are more delicate. Depending on how you implement them, you could have subtle limb overflow issues, that occur so extremely rarely by chance that random test don't catch them. For those you're stuck with either proving that your code does not overflow, or reverting to simpler, slower, safer implementation techniques.

That's a very good point. Thanks for the correction!
The Ring readme doesn't really cover its functionality but sounds like it may be a lower level crypto lib than NSS? And it also seems to be partly written in C.

Anyway, NSS wouldn't necessarily need to be replaced with a Rust component, it could well be an existing lib written in another (possibly GCd) safeish language, or some metaprogramming system or translator that generated safe C or Rust, etc. There might be something to use in Go or .net lands for example.

ring incorporates a barebones ASN.1 parser that also webpki uses, which is probably the crate you want to use if you want to do certificate verification in Rust. webpki is C-free but it does use ring for cryptographic primitives so that will have to be replaced if you don't like ring. More generally, I think Firefox wants to have great control over this specific component so they likely want to write it themselves, or at least maintain a fork.
Perl generated assembly. Hehh…
> Suggesting a tabula rasa rewrite of NSS would more likely be met with genuine concern for your mental well-being, than by incredulity or skepticism.

Why? I don't get it. Maintenance of NSS has to be seriously expensive.

The NSA was caught intentionally complicating that spec. The idea was to ensure it was impossible to implement correctly, and therefore be a bottomless well of zero days for them to exploit.

Gotta love the US government’s war against crypto.

Sorry for sounding like a broken record, but source please?
Can you provide more information on this? I'd be interested to read about this topic.
He's confused it with Dual_EC_DRBG, a backdoored random number generator in a different non-international standard.

SSL is complicated because we didn't understand how to design secure protocols in the 90s. Didn't need help.

No; this predated that by about a decade. They had moles on the committees that codified SSL in the 90’s. Those moles added a bunch of extensions specifically to increase the likelihood of implementation bugs in the handshake.

I’m reasonably sure it was covered in cryptogram a few decades ago. These days, it’s not really discoverable via internet search, since the EC thing drowned it out.

Edit: Here’s the top secret budget for the program from 2013. It alludes to ensuring 4G implementations are exploitable, and to some other project that was adding exploits to something, but ramping down. This is more than a decade after the SSL standards sabotage campaign that was eventually uncovered:

http://s3.documentcloud.org/documents/784159/sigintenabling-...

With SSL, the moles kept vetoing attempts to simplify the spec, and also kept adding complications, citing secret knowledge. It sounds like they did the same thing to 4G.

Note the headcount numbers: Over 100 moles, spanning multiple industries.

To be fair you don't need to rewrite the whole thing at once. And clearly the audits are not perfect, so I don't think it's insane to want to write it in a safer language.

It may be too much work to be worth the time, but that's an entirely different matter.

> may be too much work

I wonder, how many work years could be too much

(What would you or sbd else guess)