Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8062828

updateAAD always causes AEADBadTagException in AES-GCM decryption

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Not an Issue
    • Icon: P4 P4
    • None
    • 8u25
    • security-libs

      FULL PRODUCT VERSION :
      java version "1.8.0_25"
      Java(TM) SE Runtime Environment (build 1.8.0_25-b17)
      Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)

      ADDITIONAL OS VERSION INFORMATION :
      Mac OS X 10.10 (14A389)

      A DESCRIPTION OF THE PROBLEM :
      JDK 8 documentation explains that we should call Cipher.updateAAD for AES-GCM: http://docs.oracle.com/javase/8/docs/api/javax/crypto/Cipher.html

      Doing this seems to guarantee AEADBadTagException-s in decryption, while never calling updateAAD works fine and validates the tag as expected. It seems like updateAAD causes some internal state corruption or something similar.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      - Set AEAD tag (AAD) through updateAAD when decrypting AES-GCM, as documented.
      OR
      - Call updateAAD when encrypting (as test cases and other sites suggest)

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      Decryption will succeed, unless tag is actually invalid.
      ACTUAL -
      AEADBadTagException will always be thrown when calling doFinal.

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      https://gist.github.com/praseodym/f2499b3e14d872fe5b4a

      import javax.crypto.Cipher;
      import javax.crypto.KeyGenerator;
      import javax.crypto.SecretKey;
      import javax.crypto.spec.GCMParameterSpec;
      import java.nio.ByteBuffer;
      import java.security.SecureRandom;

      /**
       * JDK 8 documentation explains that we should call Cipher.updateAAD:
       * http://docs.oracle.com/javase/8/docs/api/javax/crypto/Cipher.html
       *
       * However, doing this seems to guarantee AEADBadTagException-s,
       * while not doing this works fine and validates the tag as expected.
       */
      public class AESGCMUpdateAAD {

          // AES-GCM parameters
          public static final int AES_KEY_SIZE = 128; // in bits
          public static final int GCM_NONCE_LENGTH = 12; // in bytes
          public static final int GCM_TAG_LENGTH = 16; // in bytes

          public static void main(String[] args) throws Exception {
              byte[] input = "Hello AES-GCM World!".getBytes();

              // Initialise random and generate key
              SecureRandom random = SecureRandom.getInstanceStrong();
              KeyGenerator keyGen = KeyGenerator.getInstance("AES");
              keyGen.init(AES_KEY_SIZE, random);
              SecretKey key = keyGen.generateKey();

              // Encrypt
              Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
              final byte[] nonce = new byte[GCM_NONCE_LENGTH];
              random.nextBytes(nonce);
              GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, nonce);
              cipher.init(Cipher.ENCRYPT_MODE, key, spec);

              final byte[] tag = new byte[GCM_TAG_LENGTH];
              random.nextBytes(tag);
              // UNEXPECTED: Uncommenting this will cause an AEADBadTagException when decrypting
      // cipher.updateAAD(tag);

              byte[] cipherText = cipher.doFinal(input);

              // Decrypt; nonce is shared implicitly
              cipher.init(Cipher.DECRYPT_MODE, key, spec);

              ByteBuffer cipherBuffer = ByteBuffer.wrap(cipherText);
              cipherBuffer.position(cipherText.length - GCM_TAG_LENGTH); // Tag placed at end
              // UNEXPECTED: Uncommenting this will cause an AEADBadTagException when decrypting
      // cipher.updateAAD(cipherBuffer);
              cipherBuffer.rewind();

              // EXPECTED: Uncommenting this will cause an AEADBadTagException when decrypting
      // cipherText[10] = 54; // Mangle cipher text

              byte[] plainText = cipher.doFinal(cipherText);

              // Output result (should equal input)
              System.out.println(new String(plainText));
          }
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Never call updateAAD.

            valeriep Valerie Peng
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

              Created:
              Updated:
              Resolved: