Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add block ciphers #5

Open
joshlf opened this issue Nov 13, 2018 · 3 comments
Open

Add block ciphers #5

joshlf opened this issue Nov 13, 2018 · 3 comments

Comments

@joshlf
Copy link
Member

joshlf commented Nov 13, 2018

Open questions:

  • Presumably individual block operations should be behind a feature flag since they're low level, but we still want to be able to expose high-level cipher modes (e.g., AES-GCM) via composition. How do we do that?
  • What block cipher modes do we have to support?
  • Do we want to build in padding?
    • Do we want to expose a non-padding variant as well?
    • How do we ensure that padding is only with safe block cipher modes (e.g., don't allow PKCS11 padding w/ CBC, which is vulnerable to a padding oracle attack)
@etrexel
Copy link

etrexel commented Jan 31, 2019

Hello,
I am interested in AES-CBC and would be willing to give it a crack. Have you guys done anything on the block cipher front recently?

@ojhunt
Copy link

ojhunt commented Feb 5, 2019

You could do something absolutely brutal:


trait BlockCipher {
...
}
trait Counter {}
trait PaddingSafeCounter :Counter {}

pub struct CBCCounter {}
pub struct GCMCounter {}

impl Counter for CBCCounter {}
impl Counter for GCMCounter {}
impl PaddingSafeCounter for GCMCounter {}

struct CipherContext<Cipher: BlockCipher, CounterMode: Counter> {
...
}

impl <Cipher, CounterMode> CipherContext<Cipher, CounterMode>
where Cipher: BlockCipher, CounterMode:PaddingSafeCounter {
    fn new_with_padding(_c:&Cipher,_m:&CounterMode)  {}
}

impl <Cipher, CounterMode> CipherContext<Cipher, CounterMode>
where Cipher: BlockCipher, CounterMode:Counter {
    fn new_without_padding(_c:&Cipher,_m:&CounterMode)  {}
}

This is just for illustrative purposes -- but you can reasonably easily do something like this to prevent mode misuse.

@joshlf
Copy link
Member Author

joshlf commented Feb 21, 2019

Hey @etrexel, sorry for taking so long to get to this.

Since I wrote this issue, I came to realize that this is going to be much harder than I'd originally thought. Now, that statement has some caveats, so let me try to explain my thinking:

Mundane's first goal is to be as misuse proof as possible. Block cipher-based cryptosystems are absolutely rife with opportunities for misuse, stemming mostly from cipher modes and initialization vectors. What that means is that, just like the other primitives we already provide, if we're going to provide block ciphers, we need to figure out a high-level API that is both misuse-resistant and also flexible enough to serve developers' needs. It can't just be "here's a aes_encrypt_block now have fun" because that leaves all of the subtle hard parts up to the developer. We'd much prefer something like "here's a primitive that takes a plaintext and gives you a ciphertext, and makes all of the cipher mode choices and does all of the IV generating for you."

Now, such a thing wouldn't be too hard to build. It would require some very careful thought about the safety properties of the various primitives (e.g., which block ciphers/modes are safe to be used with an incrementing IV? Which ones are safe to be used with randomly generated IVs? Since we're a stateless library, is there anything we can do to prevent IV reuse across multiple runs of a program?), but it's within the realm of possibility.

However, where this really gets tricky is serving developers' needs. We would like for Mundane to be able to serve as the basis for higher-level crypto implementations, and that means that we can't simply design with the goal of making a clean API. It also means that whatever we build has to be compatible with existing protocols. E.g., protocols like TLS and SSH specify very precisely how cipher modes and IVs are to be handled, so we have to, at the very least, support whatever they mandate.

As far as I can tell, there are a few ways we could accomplish this, in descending order of preference:

  • The nicest would be to figure out some API which both has the properties we want and also happens to provide the functionality required to implement common protocols. E.g., perhaps most protocols in practice use IV schemes which we both consider to be secure and are amenable to a misuse resistant API. That'd be nice. However, a) I suspect that that will only be true for newer protocols like TLS 1.3 and, b) something being well-designed from a cryptographic standpoint does not mean it makes for a good API. The recent WiFi KRACK attack is a perfect example of this - the vulnerability had nothing to do with the crypto, but instead with the fact that the crypto was easy to implement incorrectly.
  • The second best thing would be a split API like we have with password hashing vs KDFs. We would provide low-level primitives behind feature flags, and provide higher-level primitives as the defaults. The low-level primitives would themselves be designed as misuse-resistant as possible, of course, but we would meet the existing protocols where they're at. We could then provide a higher-level API to folks who are implementing more reasonable protocols, or doing their own one-off crypto thing.

The first thing that's required here is a comprehensive survey of the landscape. Ideally, we could get:

  • A comprehensive list of block cipher attacks (padding oracle, IV reuse, etc) so we know what we're trying to avoid
  • A comprehensive list of protocols that we explicitly want to support, along with a detailed understanding of how they make use of block ciphers so that we can evaluate any potential API designs for compatibility
  • Prior art. E.g., ring is much farther along wrt block ciphers than we are, and is similarly interested in supporting protocol development. Tink is less interested in protocol development and more interested in the one-off crypto use case, but is really interested in misuse resistance. I've talked to the Tink folks about this, and I'm afraid that the answer there may simply be that they are able to do things we aren't because they aren't trying to support existing protocols, but it's still worth looking in to.

Anyway, very long winded answer, but the TLDR is that this is a lot harder than I originally expected. To be clear, this is not meant to scare you off - if, given that diatribe, you're still interested, then I absolutely encourage you to try it. I just don't want to give a false impression that this is just going to be a matter of exposing some primitives from BoringSSL and calling it a day. And now that you know what you've gotten yourself into, welcome to Mundane and thanks for your interest in contributing! :P

PS: Feel free to reach out by email if you'd like to get into more details beyond what can reasonably go in an issue comment thread.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants