ring icon indicating copy to clipboard operation
ring copied to clipboard

AES in CTR mode?

Open SergioBenitez opened this issue 8 years ago • 18 comments

Hello!

I'm considering using ring for a project, but I require AES CTR 128. It looks like the boringssl code for this is being compiled, but I can't find any reference to it in the Rustdocs. Is this being exposed by ring in any way? And if not, are there plans for this?

Thanks! Sergio

SergioBenitez avatar Jan 16 '17 21:01 SergioBenitez

What construct are you trying to implement on top of AES-CTR? If it's AES-CTR-HMAC-SHA-{256, 384, 512}, we can definitely explore adding that construct to ring. If it's a (userspace) AES-CTR PRNG then we could definitely explore adding one of those too. Or maybe it's something I'm overlooking?

Regarding the BoringSSL code, we removed the AES-CTR C code with the intent that, if we were to do something with CTR mode in ring, we'd do it in Rust instead of C. I believe it would be easy to do, especially after #369 is done.

briansmith avatar Jan 17 '17 09:01 briansmith

It is indeed a PRNG.

Out of curiosity, why not expose AES-CTR directly?

SergioBenitez avatar Jan 17 '17 16:01 SergioBenitez

Out of curiosity, why not expose AES-CTR directly?

ring tries to expose crypto at a level of abstraction that is (far) higher than cipher modes, in an attempt to minimize mistakes in usage of our APIs.

For example, let's say we want to support applications that use AES-128-CBC-HMAC, and we want to use a userspace PRNG to generate the CBC IV to avoid syscall overhead. In that case, we wouldn't expose AES-CBC or the PRNG. Instead we would expose some kind of "AES-128-CBC-HMAC-SHA-{256, 384, 512}-with-IVs-generated-using-a-userspace-PRNG" AEAD-ish interface, which would be roughly equivalent in safety to our AES-GCM and ChaCha20-Poly1305.

It is indeed a PRNG.

I hope the previous paragraph demonstrates why it's useful to be as specific as possible in what you need so we can figure out exactly what to add.

What are you planning to use the PRNG for? And why doesn't ring::rand::SystemRandom work well enough for that use case?

Thanks for taking the time to share.

briansmith avatar Jan 17 '17 22:01 briansmith

Thank you for the generous response. :)

I'm using the PRNG to generate deterministic random numbers, given a seed, as a component of a the generation of a hash value. Because the numbers need to be deterministic to recompute the hash in the future, SystemRandom does not suffice.

SergioBenitez avatar Jan 17 '17 22:01 SergioBenitez

OK, I'm going to close this, but not because I don't want to help you. Rather, this is just outside the scope of what we're trying to do in ring. But I am open to adding a higher-level feature that you can use, if it is needed and if it would be generally useful.

I don't have any plans to add any "deterministic PRNG" feature to ring, except maybe in the testing module, ring::test, where we already implement multiple deterministic implementations of ring::rand::SecureRandom for testing purposes only. I don't know of any real-world protocol that uses a deterministic PRNG construct. However, maybe I'm overlooking something. If so, please file an issue about adding a deterministic PRNG to ring.

It sounds like what you want is more of a KDF (key derivation function) than a PRNG. We already have a KDF implementation in ring::hkdf that is quite good. If you don't need specifically AES-CTR then I recommend you use that. If you need an AES-CTR-based KDF then please file a new issue with more details about the motivation (i.e. which protocol you're implementing).

Thanks!

briansmith avatar Jan 19 '17 02:01 briansmith

Sorry, but I explicitly need AES-CTR 128. I'm not using it as a KDF.

I was under the impression that ring would serve as a replacement for, as an example, openssl's crypto primitives. Is that not the case? Based on what you're saying, your intent is not for ring to be used as a bag of crypto primitives, but instead, as a set of fully constructed crypto functions, similar to libsodium.

SergioBenitez avatar Jan 20 '17 01:01 SergioBenitez

similar to libsodium.

ring will probably be even more high-level than libsodium, according to the current plans. That's why I redirect feature requests to be for higher-level things, not the low-level primitives.

briansmith avatar Jan 20 '17 01:01 briansmith

Just want to mention that, theoretically, crypto libraries can be both high-level and low-level. To protect and guide users, the two parts should have telling names like "recipes" and "hazardous materials" (see https://cryptography.io/en/latest/).

On the other hand, since BoringSSL stripped a lot of functionality, ring might not be in the best position to provide all needed primitives. And ring is rather new, so the focus right now is probably on the "recipes" part. And I'm sure @briansmith has a couple more good reasons?

Philipp91 avatar Jan 20 '17 09:01 Philipp91

In general, I am very open to adding functionality that people need into ring. But at least for now, I encourage one way and discourage the other way.

Here's the way I discourage: I need to do something. The minimal thing to add to ring needed to do that is Y, so I'll ask for Y to be added to ring so I can implement the rest of the construct outside of ring.

Here's the way I encourage: I need to do something. I'll add a misuse-resistant way of doing that to ring.

Let me given an example:

Some people want to implement Noise protocols using ring, and I want to encourage them to do so. Noise usually uses a combination of static and ephemeral Diffie-Hellman. ring currently only supports ephemeral Diffie-Hellman. So, we need to add something to ring:

The discouraged way: Add a static Diffie-Hellman API to ring.

The encouraged way: Add a hard(er)-to-misuse ephemeral-static API to ring. Maybe this requires putting X3DH and other ephemeral-static Diffie-Hellman constructs directly into ring, but I hope instead there's a more general safe(r) construct on top of which X3DH and other things can be implemented.

In terms of what @Philipp91 mentions, ring does have a "hazardous materials" API already, but it is intentionally restricted to internal use within ring.

Another way of thinking about this is that ring is a framework for implementing useful and interoperable crypto protocols, not a framework for implementing crypto primitives.

Also, if you need a feature added to ring and you don't want to publicly talk about your use case(s) in detail (e.g. because your project isn't open source), you can always email me at [email protected] and I'll explain how that works.

briansmith avatar Jan 20 '17 21:01 briansmith

Thanks again for the thoughtful reply.

While I believe I understand where you'd like to go with ring, I propose that you take measures to make this more explicit. The ring API is currently nowhere near as high-level as libsodium, for instance. libsodium rarely exposes algorithm details, but ring gives me direct access to things like PBKDF2 and SHA*. Why not hide these? Based on your heuristics, if I feel "I need to get a cryptographic hash value," ring shouldn't answer with: "choose from these hash functions," but instead, provide some very-high level hashing API, likely with different variants, depending on the input and requirements. libsodium does just this, for instance.

Best of luck with ring! I was hoping ring would be the answer to crypto in Rust, but unfortunately, it looks like it will only be one part of the story.

SergioBenitez avatar Jan 27 '17 02:01 SergioBenitez

@SergioBenitez I saw your PR for the cookie-rs crate. Did you want AES-CTR for use in that crate, so that you could implement AES-CTR-HMAC-SHA256? If so, I'd be happy to accept a PR to ring that implements an AES-CTR-HMAC-SHA256 AEAD using the same API as AES-GCM.

briansmith avatar Feb 21 '17 19:02 briansmith

I also have a need for direct access to AES CTR and ECB.

I understand your argument of "expose higher level constructions, not primitives", but in this case these are simply files encrypted using AES-CTR. It is not used as part of a higher construct. I'm currently using rust-crypto, but I'd like to transition all my crypto to ring.

Since this is based on reverse engineering, I also have cases where AES is used in ECB or CTR modes as part of some higher level construct, but I've only been able to replicate it without identifying it.

Would you consider adding a primitives module, with a big warning redirecting users to the high level constructs ?

plietar avatar Mar 06 '17 18:03 plietar

@plietar In the case of https://github.com/plietar/librespot/blob/d940ed161a0d026d293275f6cfc28c32fada2880/src/authentication/discovery.rs#L130, isn't that AES-CTR-HMAC? Also, in the case of https://github.com/plietar/librespot/blob/d940ed161a0d026d293275f6cfc28c32fada2880/src/authentication/mod.rs#L91, isn't that AES-192-CBC?

Right now we have to concentrate on improving the really good crypto that we want to become ubiquitous, and there's no time available for obsolete and/or proprietary crypto and/or unusual crypto.

briansmith avatar Mar 07 '17 01:03 briansmith

I re-opened this. If somebody wants to work on a public API for AES-CTR, I am open to accepting that change.

ring already has AES-CTR implemented in aead::aes. In order to implement AES-CTR with 96-bit nonces and 32-bit counters, we'd basically just need to:

  1. Copy-paste aead::aes_gcm to a new aesd::aes_ctr32.
  2. Remove the GCM-related bits from aead::aes_ctr32.
  3. Expose aead::aes_ctr::Key publicly.
  4. Write integration tests for the new aes_ctr API, in tests/aes_ctr_tests.rs. There are probably test vector files from BoringSSL that we can use for this, copy-pasting from another similar test.

briansmith avatar Jan 24 '25 18:01 briansmith

If/when we have a public API for AES-CTR with 96 bit nonces, it might also make sense to replace the direct uses of aead:::aes in aead::aes_gcm with use of the new public API.

Also, above, I mentioned aead::aes_ctr32 as the module name. But it shouldn't be a submodule of aead:: since it isn't an AEAD cipher. Also, if/when aead::aes is used outside of aead::aes_gcm, aes should be moved out of aead. I will investigate doing this now.

briansmith avatar Jan 24 '25 18:01 briansmith

Follow up to https://github.com/briansmith/ring/discussions/2414#discussioncomment-13120917.

It would be good to write up an outline of your planned approach, e.g. whether 96-bit nonces and 32-bit counters are sufficient or whether you intend to support larger counters, if/how the AES-GCM code and AES-CTR code will avoid duplicate logic w/o regressing performance of the AES-GCM code, how you will test it, the proposed API, etc.

For our use case we need a 96 bit nonces and 32 bit counter see here so I'd go with that.

Implementation wise I'd follow approach you propose here, create aead::aes_ctr32 (or aes::aes_ctr32) and move ctr logic from aead::aes_gcm into it to avoid code duplication. I'm not sure what performance pitfalls are here, but I'd rather duplicate part of the codepath if needed.

Migrated codepath (used by GCM) will be tested by existing GCM tests. New codepath would be covered with new tests and with BoringSSL tests.

As for the proposed API I'd look into approach aws-lc-rs took.

My colleague already worked on this, but currently has other priorities so I'll try to take over .

rok avatar May 13 '25 18:05 rok

If there's no objections I'll try to open a draft PR next week

rok avatar May 21 '25 07:05 rok

I'm not sure what performance pitfalls are here, but I'd rather duplicate part of the codepath if needed.

The AES-GCM implementation has benchmarks that you can run to verify there are no performance regressions.

The AES-GCM implementation does check for 32-bit counter overflow at the start of each seal/open operation. This overflow check needs to happen for the separate AES-CTR implementation too, probably by duplicating it there.

New codepath would be covered with new tests and with BoringSSL tests.

I think this makes sense. It is important to have tests for the counter overflow case. Since that involves inputs/outputs that are too large to use as test vectors, I recommend you look at the test at the bottom of aes.rs.

One thing that isn't obvious is that aes_gcm.rs implements single-block AES encryption by converting the block to a counter and then AES-CTR encrypting the all-zero block with that counter.

briansmith avatar May 21 '25 16:05 briansmith