Hacker News new | ask | show | jobs
by Chiba-City 4304 days ago
Please, assembly is OK. It's not even magic or special wizardry. My dad programmed and maintained insurance industry applications in assembly side by side with many other normal office workers for decades. Assembly is OK.
2 comments

Assembly is bad for auditability, which is important in crypto to prevent subtle errors.
As a seasoned and experienced reverse-engineer myself, I'm (genuinely) curious where you got that impression. Do you find it unapproachable?

Assembly is the simplest language you can write a computer program in, for a certain very textbook definition of "simple" - it's just that you actually have to do everything by hand that you normally wouldn't. And yes, that can be a pain in the ass, and yes, you do have to watch out for not seeing the wood for the trees - but one thing it most definitely is, is auditable.

Bearing in mind, say, the utter briar-patch that is OpenSSL: a crufty intractably complex library written in a high-level language with myriad compiler bug workarounds, compatibility kludges and where - despite it being open source, and "many eyes making bugs shallow" - few eyes ever actually looked, or saw, or wanted to see, and when attention was finally paid to it, it was found wanting... might not assembly be perhaps better for a compact, high-assurance crypto library? Radical, I know, but perhaps an approach that's worthy of consideration.

I understand you may well be more familiar with high-level languages, and I don't know if you're confident about your ability to audit that - but I must point out, if you're auditing it from source, you're trusting the compiler to faithfully translate it. So to actually audit the code, you need to include the compiler in that audit. Compilers have (lots of) bugs and oversights too (lots of OpenSSL cruft is compiler bug workarounds, it seems?): as the article points out, existing compilers just weren't really designed to accommodate writing secure code.

Meanwhile an assembler makes a direct translation from source assembly to object machine code - that is deterministic (a perniciously-hard process with compilers) and much more easily, and automatically, auditable and indeed directly reversible.

To be clear, I'm not suggesting we replace, say, libsodium with something written in assembly language tomorrow! There are good high-level language implementations. And inline assembly is already used in some places for certain functions, including this exact one (zeroing memory), to try to minimise the compiler second-guessing us. But as the article points out, that approach only takes us so far, and it's something we need to be guarded against when trying to write secure code.

The briar patch of OpenSSL is more in the high level protocol code, and not the asm crypto (the perl obfuscation layer makes it fun, but isn't a major source of bugs). I would not want to write a robust asn.1 parser in assembly. Lots of other cruft works around the presence or absence of various #define values in header files. Rewriting in assembly is not going to solve the problem of deciding how big socklen_t is.
Mm, true: the long grass of the libcrypto part pales in comparison to the thorny nightmare that is the rest of it. Thank you to OpenBSD's LibReSSL for beginning to clear away the worst of the bramble (and uncovering the occasional juicy blackberry in the Valhalla rampage process).

There's a crypto library, and then there's a protocol library. And TLS, to put it politely, has lots of hairy bits, and I hope and pray TLS 1.3 makes a positive impact on that, but I'm not yet sure if it will.

If one were developing from scratch (and I am not) I'd wonder if a reasonable approach would be a ridiculously low-level approach for the primitives, but a ridiculously high-level approach for the protocol. I might consider writing the first in assembly, but the second? If it involved an ASN.1 parser? I would prefer to do something else, anything but that! :-) At the very least, if someone did do it, we'd be able to see exactly how. We just might not want to! I would suggest perhaps instead, maybe something involving formal correctness proofs and then converting those to assembly, because miTLS indicates that this can be an enlightening approach, and seL4 proves that it's possible to actually make use of? (For someone else. I think it is outwith my expertise!)

In light of the current discussion, it's hard to make such a clean distinction. Your private key is going to be stored in a file that goes through the PEM and ASN.1 parsers. It's going to hang around for a bit while you sign stuff (using some sweet asm code), but now you need to dispose of it. The object lifetime is often much longer than we'd like even with perfect zeroing, and there are some ways to address that, but it casts a long "shadow" on the call graph, not all of which can be made minimal.

In short: imperfect buffer zeroing probably reduces risk enough that it drops below several other concerns.

If you restrict yourself to super simple things like zeroing chunks of memory and the unused stack then it still might be acceptable.
Assembly is not really portable and error prone. I don't consider it anywhere wizardry (or hard) but corner cases are hard in C and in assembly even harder.
In the cases where the alternative is fighting the compiler every step of the way, assembly may very well be less error prone as long as you limit it to exactly the small areas where it will help.