3 ms·
const encrypted = await window.crypto.subtle.encrypt( { name: "AES-GCM", iv: new Uint8Array(12) /* don't reuse key! */ }, key, new TextEncoder
by lukeschlather 7y ago
const encrypted = await window.crypto.subtle.encrypt(
{ name: "AES-GCM", iv: new Uint8Array(12) /* don't reuse key! */ },
key,
new TextEncoder().encode(JSON.stringify(content))
);
This looks wrong. The iv should be a randomly generated string that is only used once. I'm not super familiar with modern Javascript, but I think you're just initializing a bunch of null bytes.
Honestly, I feel like whoever designed AES intended for people to make this mistake, because half of the sample code I see has this error, and it would have been easily avoided by specifying the IV length in the standard and requiring libraries to automatically generate it and prepend it to the ciphertext rather than letting callers have control over how it is initialized and stored.
- prophesi 7y agoYeah, the WebCrypto API has a built-in function for this. I hope the author fixes the code, because this is otherwise a dangerous tutorial. window.crypto.getRandomValues(new Uint8Array(12)); If the author followed the MDN guides for it, they would have noticed that the example code also uses a random array. https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/encrypt#AES-GCM_2 https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypt...
- dchest 7y agoNo, it doesn't need to be random if the key is used only once. PS The author didn't just copy something from a tutorial without knowing what he was doing, he actually asked for advice https://github.com/excalidraw/excalidraw/issues/610 https://github.com/excalidraw/excalidraw/issues/610
- prophesi 7y agoHm, I stand corrected! Though I have two thoughts on that: 1. It wouldn't add very much complexity to implement random IV's. You can send it with the ciphertext, or in the generated URL. In my own project, I use btoa(JSON.stringify({ key: generatedKey, iv: randomIv })) and reverse the operation when the URL is accessed: const fragmentData = window.location.hash.substr(1); const keyParameters = JSON.parse(atob(fragmentData)); const key = HexMix.hexToUint8(keyParameters.key); const iv = HexMix.hexToUint8(keyParameters.iv); Edit: I'm also converting the key/iv to hexadecimal. Don't remember why, but that's not necessary lol. Edit #2: Ah-ha, and now I've learned something else from the Excalidraw team; I can skip my hex/uint & base64 conversions if I export the key as jwk. 2. There are a _lot_ of tutorials out there that incorrectly reuse the key with a fixed IV. I was more concerned about this being yet another tutorial that someone blindly follows and ends up reusing the key regardless.
- wildduck 7y agoWell on the site it was said "We encrypt the content with that random key. In this case, we only encrypt the content once with the random key so we don’t need an iv and can leave it filled with 0 (I hope…)." Anyone think that is a good idea?
- dchest 7y agoIt's a good idea if you encrypt with the same key _once_ — you can avoid attaching nonces to your ciphertext (less code and data), and have only 16-byte key in the URL. In fact, using a random IV with AES-GCM is not exactly safe: 12-byte nonce is too small to avoid collisions with many encryptions. The recommendation is to not encrypt more than 2^32 messages with the same key if you use the random nonce.
- vbezhenar 7y agoWhat would happen if an attacker gets two different messages with identical IV and key?
- dchest 7y agoThey'll get the XOR of the two original messages, and also can break authentication: https://crypto.stackexchange.com/questions/26790/how-bad-it-is-using-the-same-iv-twice-with-aes-gcm/68525#68525 https://crypto.stackexchange.com/questions/26790/how-bad-it-...
- vbezhenar 7y agoThat's scary, thanks!
- SAI_Peregrinus 7y agoIf the key is securely random AND only used once, it won't compromise the encryption. But it's a bad idea, since it requires enforcing that the key is a nonce, instead of just a key. It's a bad habit, and can easily lead to compromise (when someone inevitably uses it as example code in a situation where those guarantees don't hold, for instance.)
- lioeters 7y ago> I think you're just initializing a bunch of null bytes. You're right. From MDN: > The Uint8Array typed array represents an array of 8-bit unsigned integers. The contents are initialized to 0. ..And the function signature for crypto.subtle.encrypt() describes the iv parameter for the algorithm: > iv - A BufferSource — the initialization vector. This must be unique for every encryption operation carried out with a given key. > Put another way: never reuse an IV with the same key. https://developer.mozilla.org/en-US/docs/Web/API/AesGcmParams https://developer.mozilla.org/en-US/docs/Web/API/AesGcmParam... The code comment implies that the author knew the iv parameter should be unique - and yet passed an array of zeros every time.
- dchest 7y agonever reuse an IV with the same key Exactly, the key is always different.
- lioeters 7y agoThank you, I'd misunderstood the author's intention, my fault for not reading the article carefully. > In this case, we only encrypt the content once with the random key so we don’t need an iv and can leave it filled with 0.
- dchest 7y agoIt is not wrong. In AES-GCM, the "iv" is a nonce — any value (random, counter, cat picture) that is different for different encryptions with the same key. If you use the key only for one encryption, as it is used here, it can be static (e.g. all zeros). BTW, regarding the AES comment: the authors of AES designed the block cipher, they didn't design the mode (GCM, which is CTR and GMAC). Nonce is a standard requirement for a stream cipher/stream mode of a block cipher/AEAD. In WebCrypto it's actually hard to skip setting the iv. And again, the author didn't make a mistake here.