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

Recommending CTR is dangerous, if the same key is used twice #113

Open
vbakke opened this issue Jun 22, 2022 · 7 comments
Open

Recommending CTR is dangerous, if the same key is used twice #113

vbakke opened this issue Jun 22, 2022 · 7 comments

Comments

@vbakke
Copy link

vbakke commented Jun 22, 2022

AES-CTR fails catastrophically when one reuses the same key, and the same IV/counter.

Could you please update the README to warn people to never, ever, ever reuse the IV when using AES-CTR?
When using CTR, the developer has to create a method of ensuring that a new IV/counter is used every time, if the is a chance that the key can be used.

Therefore the default should probably not start counting at 1, but at a random number, unless the developer explicitly specify a counter.

@WavePlayz
Copy link

Whats your opinion? What settings should i use with this lib

@lerarosalene
Copy link

lerarosalene commented Aug 11, 2022

@WavePlayz

Whats your opinion? What settings should i use with this lib

Generate new random 16-byte counter for every message and then send it as plaintext alongside encrypted message.

@ricmoo
Copy link
Owner

ricmoo commented Aug 11, 2022

The IV is necessary to decrypt the content in the future, so picking a random IV would be unwise (also, it is hard in many environments to generate a random number portably, so I would recommend against any library not equipped to do so, from generating a random number).

Often the source of the IV comes from either a random source, or from the use of a session (or ephemeral) key mixed with a known public key for another’s private key, and the public key of this session key is prepended to the cipher text.

As an aside, due to the dangers of choosing a “random” number (which is difficult and something easy to hijack), a method similar to RFC-6979 might be preferably, where a hash (HMAC, preferably) of the key and content is used to create a deterministic IV; this means the same plaintext produces the same cipher text and that different plaintexts get different IV. However, do NOT do this if multiple messages will match and you do not want to leak the pattern. You also can’t do this if it is planned as a streaming cipher (one of the advantages of CTR node). This is just an example technique, for very specific cases.

The AES block cipher is a cryptographic primitive, so it’s very important to understand and use it properly, based on its application. It’s a powerful tool, and with great power, yadda, yadda, yadda. :)

Does that make sense?

@vbakke
Copy link
Author

vbakke commented Aug 11, 2022

The IV is necessary to decrypt the content in the future,
Agree. Therefore you need to store the IV. It can be stored as plain text. IVs don't need to be secret.

so picking a random IV would be unwise. (also, it is hard in many environments
Disagree. In JavaScript, use Crypto.getRandomValues() in a browser or in NodeJS.

@WavePlayz

Whats your opinion? What settings should i use with this lib
Sorry, but it depends on what you are encrypting. :-/

Unique secret keys

If you can use different key/passwords for each encryption, you can you CTR, and just count up from 1. (And stop reading the rest of this post. : )

Reusing secret keys

If you have to use the same secret key/password for multiple/all encryptions, you must pay more heed to the IV. Keep reading:

Synthetic IVs

Hashing the plaintext to create an IV (as @ricmoo is explaining above) is a often good solution. (The concept is often called SIV, Synthetic IV, and used in AES-SIV, AES-GCM-SIV.)

The downside of SIV is that two equal massages will be encrypted identically. So an eavesdropper, can pick up that you are sending the same message over and over, and when you do so.

If this is not a problem (or if it is a benefit), feel free to go for the SIV approach. And you'll be free to choose AES-CTR, (or CBC, or another.)

Careful design

If none of these apply, you have to carefully design a IV regime that avoids having the same IV for the same secret key/password. Abd in this case, I would be very careful about using AES-CTR. As it fails catastrophically if you get a collision. The security of AES-CBC is weakened in this situation, but doesn't completely collapse.

Malleable

If I catch an encrypted message, I may not be able to decrypt it. But nothing is stopping me from changing a bit or two in the encrypted message, before passing it on. And if I know something about the contant, I can actually "edit" the encrypted messages to a certain extent, (e.g. turning amount=000000.05 into amount=010000.05). The receiver has no way of knowing that the message has been tampered with. (Malled.)

If this is not an issue for you case, don't worry about it. If it is a worry, you need to use HMAC (as mentioned above), or another AES mode that includes an HMAC, such as AES-GCM.

@lerarosalene
Copy link

@vbakke

As it fails catastrophically if you get a collision

How catastrophically is catastrophically, by the way? For example, of 1000 encrypted messages I've got 2 that share same key and IV; and attacker intercepted all 1000 messages, but doesn't know about IV collissions beforehand - how bad is this situation for me?

@vbakke
Copy link
Author

vbakke commented Aug 14, 2022

It's hard to find proper sources for this. Most just say it "fails catastrophically".

From what I understand, The attacker can manage to decode the two messages with the colliding IVs, but not retrieve the secret key, and therefor not decode the other messages.

One source I've found is this Stack Exchange post: https://crypto.stackexchange.com/questions/2991/why-must-iv-key-pairs-not-be-reused-in-ctr-mode

Which links to another answer: https://crypto.stackexchange.com/questions/2249/how-does-one-attack-a-two-time-pad-i-e-one-time-pad-with-key-reuse

I presume an attack in your example could be XORing all the encrypted messages against each other. If the plain text are mostly plain text, any colliding IVs would yield an XOR-result with very few of the bit 7 ever being set. And then you start digging. A bit of work involved, though. :)

@yy0931
Copy link

yy0931 commented Sep 29, 2022

I agree that the library shouldn't recommend CTR.
It is easy to attack CTR with nonce (or IV) reuse. If you know a single ciphertext-plaintext pair, you can decode any data encrypted with the same key and nonce.

const aesjs = require("aes-js")

const newCtr = () => new aesjs.ModeOfOperation.ctr(aesjs.utils.utf8.toBytes("password12345678"))

// The known ciphertext-plaintext pair:
const cipher = aesjs.utils.utf8.toBytes("hello")
const plain = newCtr().encrypt(aesjs.utils.utf8.toBytes("hello"))

// Decode a ciphertext:
const target = newCtr().encrypt(aesjs.utils.utf8.toBytes("world"))
console.log(aesjs.utils.utf8.fromBytes(target.map((t, i) => t ^ plain[i] ^ cipher[i])))  // "world"

Even if the attacker does not know a pair of plaintext and ciphertext, the plaintext for a ciphertext may be deduced from the length of the ciphertext. 1

For example, of 1000 encrypted messages I've got 2 that share the same key and IV.

This case is somewhat more difficult, but still, for every plaintext-ciphertext pair you found, you can decrypt 0.2% of the messages. On the other hand, CBC has no such problem.

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

No branches or pull requests

5 participants