(I apologize if I'll be glossing over something; I've just read the slides and haven't gotten around to the presentation yet. These issues might actually be addressed in the talk.)
Some of the problems that invariably crops up with libraries that hide implementation details:
a. If the algorithms ever need swapping, you might not even find out because your code compiles cleanly (assuming you don't obsessively read changelogs for libraries); only when you go live after the update you see that suddenly all data is garbled and spend a few hours debugging in your own code. How do you replace what's inside something like crypto_box while also breaking existing implementations so that they don't even build?
b. Interoperability across programming languages becomes difficult. AES-256-CTR with HMAC-SHA256 authentication is something you can scramble to get algorithms together because you know them. But crypto_box is just opaque; you'll be stuck reading source code and shoddily porting it to $language_that_you_must_use because policy or technical requirements or whatever dictate you must not use native extensions to bind to your library of choice. If your library of choice is also not written in C (though libsodium, which is what the presented library wraps, is), but rather in Java or some other even higher level language, you'll be completely locked in a monoculture and dependent on that particular library.
They mean well, but the interoperability problem seems to a very unsolved problem. The modern non-FIPS world appears to slowly be gravitating towards de-facto standardizing libsodium, though.
Problem (a) is pretty silly. If you use a library with a vulnerability in it, you need to be prepared to upgrade the library. How many people using (for instance) Markdown libraries understand how their library handles memory management or recursion? Meanwhile, the alternative in cryptography is hand-hacked direct-to-primitives coding that is likely to create more bugs. Further, the primitives that are most likely to "need swapping" aren't algorithms (AES and SHA2 may never fall, just like 3DES, but for its block size, still hasn't) but constructions, which are hidden in hand-rolled crypto code.
If you "scramble to get algorithms together because you know them" and use AES-256-CTR with HMAC-SHA256, in my experience auditing crypto code, you're very likely to have scrambled together at leave two game-over crypto flaws that libraries like Tink and libsodium avoid for you.
While I agree with your second paragraph, I don't think you need it to justify the position that
crypto.secret_box.encrypt
is almost always better to use than a combination of
crypto.primitives.aes.ctr
and
crypto.primitives.hmac.sha256
Aside from the obvious argument that rolling your own cryptography is dangerous, at a more abstract level you just generally don't want to work with raw primitives in most types of software. Specialization isn't just the lifeblood of the economy, it's also the way software productivity works. If your core competency isn't X, why should you be re-implementing X using its raw primitives? You should generally be importing known-good library implementations if possible. Even in adversarial scenarios (like cryptography) this principle remains the same.
Given that I don't really understand the parent commenter's point. You touched on this a little with your example of a Markdown library - as software engineers we use things which are substantially black boxes all the time. It seems strange to me that you'd want to do surgery on the raw primitives of a library instead of updating it to suit your needs. In this case, that means just use a reputable crypto library and update it when there's a reported security vulnerability.
You don't even need to buy into, "Don't roll your own crypto" to lean into this.
I took the original argument to be "if you use secret_box, you don't even know that you're using Salsa20, so if a cryptanalysis of Salsa20 is published, you might not realize and know to fix that problem".
Which is, I think, a silly argument; if someone finds a stack overflow in a Markdown library, I still need the maintainers of that library to tell me about it --- nobody is going to tell me about it directly. I have more information to act on in the secret_box scenario, not less.
But I was a little confused by the argument too, so maybe I'm not making that much sense either.
For issue a) you can create a v2 version and, if you really really want to keep the crypto_box calls in your source code, you
#define crypto_box crypto_box_v2 -- your code won't change, but when you compile, the symbols will change. After that, if you try to link against this library with code that isn't updated you'll fail at build time.
That being said, I think replacing all instances of crypto_box to crypto_box_v2 is much better than #defining
The #define trick only makes problem b) bigger -- I think that if/when a new cryptobox is needed the upgrade strategy will be the following:
1) the new crypto_box will be named crypto_box_v2
2) create a new api endpoint using crypto_box_v2 that isn't being used. If you semver your api endpoints, that means doing a major version bump since it won't be backwards compatible with the older crypto. Does this suck? yes.
3) migrate traffic to the new api endpoint
4) EOL the old api endpoint
When you think about it, it's the only way to do it and cipher negotiation is only masking steps 1-4 (i.e doing it for you, but in an opaque way)
Helping developers avoid the pitfalls of cryptography is the main problem we at Tozny (http://tozny.com) are trying to solve.
We found that there is so much mis-information available online it's too easy to shoot yourself in the foot with cryptography so we're trying to make tools that make strong crypto easy to implement.
Cryptography is so very easy to get wrong. This is the kind of thing that should be outsourced to someone who only does this and is known to do it well and with known and trusted algorithms.
Designing cryptographic libraries should definitely be left to cryptographers. However, the information here also seems pretty useful for the programmers using those libraries.
This is not about coding AES, it's about designing APIs for cryptographic libraries. If you're a programmer using those APIs, it would be helpful to know whether they're well-designed, and what pitfalls might be there if they're not.
"Security at the expense of usability, comes at the expense of security."
Misuse-resistant cryptography API design is a particular hobby horse of mine. (My other hobby horse is software update security.)
A lot of the arguments in favor of cipher agility (a.k.a. runtime negotiation and risk of downgrade attacks) come from well-intended people who haven't internalized the past few decades of real world cryptographic failures.
I liken it to building a brick wall that's expected to hold up a roof.
Would you rather have a wall made by carefully placing bricks and mortar in place once, and if need be, rebuild the brick wall later with better materials?
Or would a lattice of mortar that lets you hot-swap bricks as needed be a preferable design?
The first one is versioned protocols. The latter is what keeps people typing AES into their code.
Some of the problems that invariably crops up with libraries that hide implementation details:
a. If the algorithms ever need swapping, you might not even find out because your code compiles cleanly (assuming you don't obsessively read changelogs for libraries); only when you go live after the update you see that suddenly all data is garbled and spend a few hours debugging in your own code. How do you replace what's inside something like crypto_box while also breaking existing implementations so that they don't even build?
b. Interoperability across programming languages becomes difficult. AES-256-CTR with HMAC-SHA256 authentication is something you can scramble to get algorithms together because you know them. But crypto_box is just opaque; you'll be stuck reading source code and shoddily porting it to $language_that_you_must_use because policy or technical requirements or whatever dictate you must not use native extensions to bind to your library of choice. If your library of choice is also not written in C (though libsodium, which is what the presented library wraps, is), but rather in Java or some other even higher level language, you'll be completely locked in a monoculture and dependent on that particular library.
They mean well, but the interoperability problem seems to a very unsolved problem. The modern non-FIPS world appears to slowly be gravitating towards de-facto standardizing libsodium, though.