Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How are the IV and authentication tag handled for "AES/GCM/NoPadding"?

I'm using AES/GCM/NoPadding encryption in Java 8 and I'm wondering whether my code has a security flaw. My code seems to work, in that it encrypts and decrypts text, but a few details are unclear.

My main question is this:

Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); cipher.init(Cipher.ENCRYPT_MODE, key); byte[] iv = cipher.getIV(); // ????? 

Does that IV satisfy the requirement of "For a given key, the IV MUST NOT repeat." from RFC 4106?

I'd also appreciate any answers / insight for my related questions (see below), but that first question is bugging me the most. I don't know where to find source code or documentation that answers this.


Here is the full code, roughly. I apologize in case I introduced errors while writing this post:

class Encryptor {   Key key;    Encryptor(byte[] key) {     if (key.length != 32) throw new IllegalArgumentException();     this.key = new SecretKeySpec(key, "AES");   }    // the output is sent to users   byte[] encrypt(byte[] src) throws Exception {     Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");     cipher.init(Cipher.ENCRYPT_MODE, key);     byte[] iv = cipher.getIV(); // See question #1     assert iv.length == 12; // See question #2     byte[] cipherText = cipher.doFinal(src);     assert cipherText.length == src.length + 16; // See question #3     byte[] message = new byte[12 + src.length + 16]; // See question #4     System.arraycopy(iv, 0, message, 0, 12);     System.arraycopy(cipherText, 0, message, 12, cipherText.length);     return message;   }    // the input comes from users   byte[] decrypt(byte[] message) throws Exception {     if (message.length < 12 + 16) throw new IllegalArgumentException();     Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");     GCMParameterSpec params = new GCMParameterSpec(128, message, 0, 12);     cipher.init(Cipher.DECRYPT_MODE, key, params);     return cipher.doFinal(message, 12, message.length - 12);   } } 

Suppose that users cracking my secret key = game over.


More detailed questions / related questions:

  1. Is the IV returned by cipher.getIV() safe for me to use in this way?
  • Does it avoid the catastrophe of reusing the IV,key combination in Galois/Counter Mode?
  • Is it still safe when I have multiple applications running this code at once, all displaying encrypted messages to users from the same src data (possibly in the same millisecond)?
  • What's the returned IV made of? Is it an atomic counter plus some random noise?
  • Do I need to avoid cipher.getIV() and construct an IV myself, with my own counter?
  • Is the source code implementing cipher.getIV() available online somewhere, assuming I'm using Oracle JDK 8 + JCE Unlimited Strength extension?
  1. Is that IV always 12 bytes long?

  2. Is the authentication tag always 16 bytes (128 bits) long?

  3. With #2 and #3, and the lack of padding, does that mean my encrypted messages are always 12 + src.length + 16 bytes long? (And so I can safely squish them into one byte array, for which I know the correct length?)

  4. Is it safe for me to display an unbounded number of src data encryptions to users, given constant src data that the users know?

  5. Is it safe for me to display an unbounded number of src data encryptions to users, if the src data is different every time (e.g. including System.currentTimeMillis() or random numbers)?

  6. Would it help if I padded the src data with random numbers before encryption? Say 8 random bytes in front and back, or only on one end? Or would that not help at all / make my encryption worse?

(Because these questions are all about the same block of my own code, and they are strongly related to each other, and others might/should have the same set of questions when implementing the same functionality, it felt wrong to split the questions into multiple posts. I can re-post them separately if that is more appropriate for StackOverflow's format. Let me know!)

like image 899
Michael Hixson Avatar asked Aug 06 '15 09:08

Michael Hixson


People also ask

How does AES-GCM provide authentication?

AES-GCM have two main functions are block cipher encryption and multiplication over the field . The authenticated encryption operation takes Initialization Vector (IV), Additional Authenticated Data (AAD),secret key and plaintext as an input in128-bit and gives a 128-bit ciphertext and authentication tag,T.

What is authentication tag in AES-GCM?

AES-CCM generates two outputs: a ciphertext and a message authentication code (also called an authentication tag). The nonce is generated by the party performing the authenticated encryption operation. Within the scope of any authenticated- encryption key, the nonce value MUST be unique.

What is AES-GCM NoPadding?

2.1 In Java, we use AES/GCM/NoPadding to represent the AES-GCM algorithm. For the encrypted output, we prefix the 16 bytes IV to the encrypted text (ciphertext), because we need the same IV for decryption.

What is the use of IV in AES encryption?

The use of an IV prevents repetition in data encryption, making it more difficult for a hacker using a dictionary attack to find patterns and break a cipher. For example, a sequence might appear twice or more within the body of a message.


1 Answers

Q1: Is the IV returned by cipher.getIV() safe for me to use in this way?

Yes, it is at least for the Oracle provided implementation. It is generated separately using the default SecureRandom implementation. As it is 12 bytes in size (the default for GCM) then you have 96 bits of randomness. The chance that the counter repeats is abysmally small. You can look up the source in the OpenJDK (GPL'ed) which the Oracle JDK is based on.

I would however still recommend you to generate your own 12 random bytes as other providers may behave differently.


Q2: Is that IV always 12 bytes long?

It's extremely likely as it is the GCM default, but other lengths are valid for GCM. The algorithm will however have to do additional calculations for any other size than 12 bytes. Due to weaknesses it is strongly recommended to keep it at 12 bytes / 96 bits and API's may restrict you to that choice of IV size.


Q3: Is the authentication tag always 16 bytes (128 bits) long?

No, it can have any size in bytes ranging from 64 bits to 128 bits with 8 bit increments. If it is smaller it simply consists of the leftmost bytes of the authentication tag though. You can specify another size of tag using GCMParameterSpec as third parameter for your init call.

Note that the strength of GCM is strongly dependent on the size of the tag. I would recommend keeping it to 128 bits. 96 bits should be the minimum especially if you want to generate a lot of ciphertext.


Q4: With #2 and #3, and the lack of padding, does that mean my encrypted messages are always 12 + src.length + 16 bytes long? (And so I can safely squish them into one byte array, for which I know the correct length?)

See above. For the Oracle provider this is the case. Use GCMParameterSpec to be sure of it.


Q5: Is it safe for me to display an unbounded number of src data encryptions to users, given constant src data that the users know?

Virtually unbound, yes. I would start worrying after about 2^48 encryptions. In general you should however design for key change.


Q6: Is it safe for me to display an unbounded number of src data encryptions to users, if the src data is different every time (e.g. including System.currentTimeMillis() or random numbers)?

See answer to Q5 & Q7


Q7: Would it help if I padded the src data with random numbers before encryption? Say 8 random bytes in front and back, or only on one end? Or would that not help at all / make my encryption worse?

No, it would not help at all. GCM uses CTR mode underneath, so it would just be encrypted with the key stream. It would not act as an IV. Nowadays you could look at AES-GCM-SIV if you have an ever changing message to encrypt, but note that that algorithm is not implemented in any of the JCA providers.


If you need a lot of ciphertexts (higher than 2^48!, or 2^32 - ~4 billion - for the cautious) then I would suggest you use that random number and your key for a key derivation function or KDF. HKDF is currently best of breed, but you may need to use Bouncy Castle or implement it yourself.

like image 70
Maarten Bodewes Avatar answered Oct 05 '22 19:10

Maarten Bodewes