changeset 6339:46e6a4b7ca26

6996769: support AEAD cipher Summary: Added implementation for GCM mode under AES cipher Reviewed-by: weijun
author valeriep
date Mon, 07 Jan 2013 11:11:54 -0800
parents 0c89465b656a
children 5333a4c8cade
files src/share/classes/com/sun/crypto/provider/AESCipher.java src/share/classes/com/sun/crypto/provider/CipherCore.java src/share/classes/com/sun/crypto/provider/CipherTextStealing.java src/share/classes/com/sun/crypto/provider/FeedbackCipher.java src/share/classes/com/sun/crypto/provider/GCMParameters.java src/share/classes/com/sun/crypto/provider/GCTR.java src/share/classes/com/sun/crypto/provider/GHASH.java src/share/classes/com/sun/crypto/provider/GaloisCounterMode.java src/share/classes/com/sun/crypto/provider/SunJCE.java src/share/classes/javax/crypto/Cipher.java src/share/classes/javax/crypto/spec/GCMParameterSpec.java test/com/sun/crypto/provider/Cipher/AES/Test4512524.java test/com/sun/crypto/provider/Cipher/AES/Test4512704.java test/com/sun/crypto/provider/Cipher/AES/Test4517355.java test/com/sun/crypto/provider/Cipher/AES/Test4626070.java test/com/sun/crypto/provider/Cipher/AES/TestGCMKeyAndIvCheck.java test/com/sun/crypto/provider/Cipher/AES/TestKATForGCM.java test/javax/crypto/Cipher/GCMAPI.java
diffstat 18 files changed, 2009 insertions(+), 204 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/crypto/provider/AESCipher.java	Sat Jan 05 17:06:54 2013 +0000
+++ b/src/share/classes/com/sun/crypto/provider/AESCipher.java	Mon Jan 07 11:11:54 2013 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -30,6 +30,7 @@
 import javax.crypto.*;
 import javax.crypto.spec.*;
 import javax.crypto.BadPaddingException;
+import java.nio.ByteBuffer;
 
 /**
  * This class implements the AES algorithm in its various modes
@@ -127,6 +128,21 @@
             super(32, "CFB", "NOPADDING");
         }
     }
+    public static final class AES128_GCM_NoPadding extends OidImpl {
+        public AES128_GCM_NoPadding() {
+            super(16, "GCM", "NOPADDING");
+        }
+    }
+    public static final class AES192_GCM_NoPadding extends OidImpl {
+        public AES192_GCM_NoPadding() {
+            super(24, "GCM", "NOPADDING");
+        }
+    }
+    public static final class AES256_GCM_NoPadding extends OidImpl {
+        public AES256_GCM_NoPadding() {
+            super(32, "GCM", "NOPADDING");
+        }
+    }
 
     // utility method used by AESCipher and AESWrapCipher
     static final void checkKeySize(Key key, int fixedKeySize)
@@ -531,4 +547,79 @@
         return core.unwrap(wrappedKey, wrappedKeyAlgorithm,
                            wrappedKeyType);
     }
+
+    /**
+     * Continues a multi-part update of the Additional Authentication
+     * Data (AAD), using a subset of the provided buffer.
+     * <p>
+     * Calls to this method provide AAD to the cipher when operating in
+     * modes such as AEAD (GCM/CCM).  If this cipher is operating in
+     * either GCM or CCM mode, all AAD must be supplied before beginning
+     * operations on the ciphertext (via the {@code update} and {@code
+     * doFinal} methods).
+     *
+     * @param src the buffer containing the AAD
+     * @param offset the offset in {@code src} where the AAD input starts
+     * @param len the number of AAD bytes
+     *
+     * @throws IllegalStateException if this cipher is in a wrong state
+     * (e.g., has not been initialized), does not accept AAD, or if
+     * operating in either GCM or CCM mode and one of the {@code update}
+     * methods has already been called for the active
+     * encryption/decryption operation
+     * @throws UnsupportedOperationException if this method
+     * has not been overridden by an implementation
+     *
+     * @since 1.8
+     */
+    @Override
+    protected void engineUpdateAAD(byte[] src, int offset, int len) {
+        core.updateAAD(src, offset, len);
+    }
+
+    /**
+     * Continues a multi-part update of the Additional Authentication
+     * Data (AAD).
+     * <p>
+     * Calls to this method provide AAD to the cipher when operating in
+     * modes such as AEAD (GCM/CCM).  If this cipher is operating in
+     * either GCM or CCM mode, all AAD must be supplied before beginning
+     * operations on the ciphertext (via the {@code update} and {@code
+     * doFinal} methods).
+     * <p>
+     * All {@code src.remaining()} bytes starting at
+     * {@code src.position()} are processed.
+     * Upon return, the input buffer's position will be equal
+     * to its limit; its limit will not have changed.
+     *
+     * @param src the buffer containing the AAD
+     *
+     * @throws IllegalStateException if this cipher is in a wrong state
+     * (e.g., has not been initialized), does not accept AAD, or if
+     * operating in either GCM or CCM mode and one of the {@code update}
+     * methods has already been called for the active
+     * encryption/decryption operation
+     * @throws UnsupportedOperationException if this method
+     * has not been overridden by an implementation
+     *
+     * @since 1.8
+     */
+    @Override
+    protected void engineUpdateAAD(ByteBuffer src) {
+        if (src != null) {
+            int aadLen = src.limit() - src.position();
+            if (aadLen != 0) {
+                if (src.hasArray()) {
+                    int aadOfs = src.arrayOffset() + src.position();
+                    core.updateAAD(src.array(), aadOfs, aadLen);
+                    src.position(src.limit());
+                } else {
+                    byte[] aad = new byte[aadLen];
+                    src.get(aad);
+                    core.updateAAD(aad, 0, aadLen);
+                }
+            }
+        }
+    }
 }
+
--- a/src/share/classes/com/sun/crypto/provider/CipherCore.java	Sat Jan 05 17:06:54 2013 +0000
+++ b/src/share/classes/com/sun/crypto/provider/CipherCore.java	Mon Jan 07 11:11:54 2013 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,7 @@
 
 package com.sun.crypto.provider;
 
+import java.util.Arrays;
 import java.util.Locale;
 
 import java.security.*;
@@ -59,7 +60,7 @@
     private byte[] buffer = null;
 
     /*
-     * internal buffer
+     * block size of cipher in bytes
      */
     private int blockSize = 0;
 
@@ -76,10 +77,12 @@
     /*
      * minimum number of bytes in the buffer required for
      * FeedbackCipher.encryptFinal()/decryptFinal() call.
-     * update() must buffer this many bytes before before starting
+     * update() must buffer this many bytes before starting
      * to encrypt/decrypt data.
-     * currently, only CTS mode has a non-zero value due to its special
-     * handling on the last two blocks (the last one may be incomplete).
+     * currently, only the following cases have non-zero values:
+     * 1) CTS mode - due to its special handling on the last two blocks
+     * (the last one may be incomplete).
+     * 2) GCM mode + decryption - due to its trailing tag bytes
      */
     private int minBytes = 0;
 
@@ -121,6 +124,24 @@
     private static final int PCBC_MODE = 4;
     private static final int CTR_MODE = 5;
     private static final int CTS_MODE = 6;
+    private static final int GCM_MODE = 7;
+
+    /*
+     * variables used for performing the GCM (key+iv) uniqueness check.
+     * To use GCM mode safely, the cipher object must be re-initialized
+     * with a different combination of key + iv values for each
+     * encryption operation. However, checking all past key + iv values
+     * isn't feasible. Thus, we only do a per-instance check of the
+     * key + iv values used in previous encryption.
+     * For decryption operations, no checking is necessary.
+     * NOTE: this key+iv check have to be done inside CipherCore class
+     * since CipherCore class buffers potential tag bytes in GCM mode
+     * and may not call GaloisCounterMode when there isn't sufficient
+     * input to process.
+     */
+    private boolean requireReinit = false;
+    private byte[] lastEncKey = null;
+    private byte[] lastEncIv = null;
 
     /**
      * Creates an instance of CipherCore with default ECB mode and
@@ -149,7 +170,7 @@
      * @param mode the cipher mode
      *
      * @exception NoSuchAlgorithmException if the requested cipher mode does
-     * not exist
+     * not exist for this cipher
      */
     void setMode(String mode) throws NoSuchAlgorithmException {
         if (mode == null)
@@ -165,30 +186,34 @@
         if (modeUpperCase.equals("CBC")) {
             cipherMode = CBC_MODE;
             cipher = new CipherBlockChaining(rawImpl);
-        }
-        else if (modeUpperCase.equals("CTS")) {
+        } else if (modeUpperCase.equals("CTS")) {
             cipherMode = CTS_MODE;
             cipher = new CipherTextStealing(rawImpl);
             minBytes = blockSize+1;
             padding = null;
-        }
-        else if (modeUpperCase.equals("CTR")) {
+        } else if (modeUpperCase.equals("CTR")) {
             cipherMode = CTR_MODE;
             cipher = new CounterMode(rawImpl);
             unitBytes = 1;
             padding = null;
-        }
-        else if (modeUpperCase.startsWith("CFB")) {
+        }  else if (modeUpperCase.startsWith("GCM")) {
+            // can only be used for block ciphers w/ 128-bit block size
+            if (blockSize != 16) {
+                throw new NoSuchAlgorithmException
+                    ("GCM mode can only be used for AES cipher");
+            }
+            cipherMode = GCM_MODE;
+            cipher = new GaloisCounterMode(rawImpl);
+            padding = null;
+        } else if (modeUpperCase.startsWith("CFB")) {
             cipherMode = CFB_MODE;
             unitBytes = getNumOfUnit(mode, "CFB".length(), blockSize);
             cipher = new CipherFeedback(rawImpl, unitBytes);
-        }
-        else if (modeUpperCase.startsWith("OFB")) {
+        } else if (modeUpperCase.startsWith("OFB")) {
             cipherMode = OFB_MODE;
             unitBytes = getNumOfUnit(mode, "OFB".length(), blockSize);
             cipher = new OutputFeedback(rawImpl, unitBytes);
-        }
-        else if (modeUpperCase.equals("PCBC")) {
+        } else if (modeUpperCase.equals("PCBC")) {
             cipherMode = PCBC_MODE;
             cipher = new PCBC(rawImpl);
         }
@@ -219,6 +244,7 @@
         return result;
     }
 
+
     /**
      * Sets the padding mechanism of this cipher.
      *
@@ -242,11 +268,27 @@
                                              + " not implemented");
         }
         if ((padding != null) &&
-            ((cipherMode == CTR_MODE) || (cipherMode == CTS_MODE))) {
+            ((cipherMode == CTR_MODE) || (cipherMode == CTS_MODE)
+             || (cipherMode == GCM_MODE))) {
             padding = null;
-            throw new NoSuchPaddingException
-                ((cipherMode == CTR_MODE? "CTR":"CTS") +
-                 " mode must be used with NoPadding");
+            String modeStr = null;
+            switch (cipherMode) {
+            case CTR_MODE:
+                modeStr = "CTR";
+                break;
+            case GCM_MODE:
+                modeStr = "GCM";
+                break;
+            case CTS_MODE:
+                modeStr = "CTS";
+                break;
+            default:
+                // should never happen
+            }
+            if (modeStr != null) {
+                throw new NoSuchPaddingException
+                    (modeStr + " mode must be used with NoPadding");
+            }
         }
     }
 
@@ -257,7 +299,7 @@
      * <code>inputLen</code> (in bytes).
      *
      * <p>This call takes into account any unprocessed (buffered) data from a
-     * previous <code>update</code> call, and padding.
+     * previous <code>update</code> call, padding, and AEAD tagging.
      *
      * <p>The actual output length of the next <code>update</code> or
      * <code>doFinal</code> call may be smaller than the length returned by
@@ -270,23 +312,60 @@
     int getOutputSize(int inputLen) {
         int totalLen = buffered + inputLen;
 
-        if (padding == null)
+        // GCM: this call may be for either update() or doFinal(), so have to
+        // return the larger value of both
+        // Encryption: based on doFinal value: inputLen + tag
+        // Decryption: based on update value: inputLen
+        if (!decrypting && (cipherMode == GCM_MODE)) {
+            return (totalLen + ((GaloisCounterMode) cipher).getTagLen());
+        }
+
+        if (padding == null) {
             return totalLen;
+        }
 
-        if (decrypting)
+        if (decrypting) {
             return totalLen;
+        }
 
         if (unitBytes != blockSize) {
-            if (totalLen < diffBlocksize)
+            if (totalLen < diffBlocksize) {
                 return diffBlocksize;
-            else
+            } else {
                 return (totalLen + blockSize -
                         ((totalLen - diffBlocksize) % blockSize));
+            }
         } else {
             return totalLen + padding.padLength(totalLen);
         }
     }
 
+    private int getOutputSizeByOperation(int inputLen, boolean isDoFinal) {
+        int totalLen = 0;
+        switch (cipherMode) {
+        case GCM_MODE:
+            totalLen = buffered + inputLen;
+            if (isDoFinal) {
+                int tagLen = ((GaloisCounterMode) cipher).getTagLen();
+                if (decrypting) {
+                    // need to get the actual value from cipher??
+                    // deduct tagLen
+                    totalLen -= tagLen;
+                } else {
+                    totalLen += tagLen;
+                }
+            }
+            if (totalLen < 0) {
+                totalLen = 0;
+            }
+            break;
+        default:
+             totalLen  = getOutputSize(inputLen);
+             break;
+        }
+        return totalLen;
+    }
+
     /**
      * Returns the initialization vector (IV) in a new buffer.
      *
@@ -318,34 +397,49 @@
      * does not use any parameters.
      */
     AlgorithmParameters getParameters(String algName) {
+        if (cipherMode == ECB_MODE) {
+            return null;
+        }
         AlgorithmParameters params = null;
-        if (cipherMode == ECB_MODE) return null;
+        AlgorithmParameterSpec spec;
         byte[] iv = getIV();
-        if (iv != null) {
-            AlgorithmParameterSpec ivSpec;
-            if (algName.equals("RC2")) {
-                RC2Crypt rawImpl = (RC2Crypt) cipher.getEmbeddedCipher();
-                ivSpec = new RC2ParameterSpec(rawImpl.getEffectiveKeyBits(),
-                                              iv);
+        if (iv == null) {
+            // generate spec using default value
+            if (cipherMode == GCM_MODE) {
+                iv = new byte[GaloisCounterMode.DEFAULT_IV_LEN];
             } else {
-                ivSpec = new IvParameterSpec(iv);
+                iv = new byte[blockSize];
             }
-            try {
-                params = AlgorithmParameters.getInstance(algName, "SunJCE");
-            } catch (NoSuchAlgorithmException nsae) {
-                // should never happen
-                throw new RuntimeException("Cannot find " + algName +
-                    " AlgorithmParameters implementation in SunJCE provider");
-            } catch (NoSuchProviderException nspe) {
-                // should never happen
-                throw new RuntimeException("Cannot find SunJCE provider");
-            }
-            try {
-                params.init(ivSpec);
-            } catch (InvalidParameterSpecException ipse) {
-                // should never happen
-                throw new RuntimeException("IvParameterSpec not supported");
-            }
+            SunJCE.RANDOM.nextBytes(iv);
+        }
+        if (cipherMode == GCM_MODE) {
+            algName = "GCM";
+            spec = new GCMParameterSpec
+                (((GaloisCounterMode) cipher).getTagLen()*8, iv);
+        } else {
+           if (algName.equals("RC2")) {
+               RC2Crypt rawImpl = (RC2Crypt) cipher.getEmbeddedCipher();
+               spec = new RC2ParameterSpec
+                   (rawImpl.getEffectiveKeyBits(), iv);
+           } else {
+               spec = new IvParameterSpec(iv);
+           }
+        }
+        try {
+            params = AlgorithmParameters.getInstance(algName, "SunJCE");
+        } catch (NoSuchAlgorithmException nsae) {
+            // should never happen
+            throw new RuntimeException("Cannot find " + algName +
+                " AlgorithmParameters implementation in SunJCE provider");
+        } catch (NoSuchProviderException nspe) {
+            // should never happen
+            throw new RuntimeException("Cannot find SunJCE provider");
+        }
+        try {
+            params.init(spec);
+        } catch (InvalidParameterSpecException ipse) {
+            // should never happen
+            throw new RuntimeException(spec.getClass() + " not supported");
         }
         return params;
     }
@@ -420,44 +514,63 @@
                   || (opmode == Cipher.UNWRAP_MODE);
 
         byte[] keyBytes = getKeyBytes(key);
-
-        byte[] ivBytes;
-        if (params == null) {
-            ivBytes = null;
-        } else if (params instanceof IvParameterSpec) {
-            ivBytes = ((IvParameterSpec)params).getIV();
-            if ((ivBytes == null) || (ivBytes.length != blockSize)) {
-                throw new InvalidAlgorithmParameterException
-                    ("Wrong IV length: must be " + blockSize +
-                    " bytes long");
+        int tagLen = -1;
+        byte[] ivBytes = null;
+        if (params != null) {
+            if (cipherMode == GCM_MODE) {
+                if (params instanceof GCMParameterSpec) {
+                    tagLen = ((GCMParameterSpec)params).getTLen();
+                    if (tagLen < 96 || tagLen > 128 || ((tagLen & 0x07) != 0)) {
+                        throw new InvalidAlgorithmParameterException
+                            ("Unsupported TLen value; must be one of " +
+                             "{128, 120, 112, 104, 96}");
+                    }
+                    tagLen = tagLen >> 3;
+                    ivBytes = ((GCMParameterSpec)params).getIV();
+                } else {
+                    throw new InvalidAlgorithmParameterException
+                        ("Unsupported parameter: " + params);
+               }
+            } else {
+                if (params instanceof IvParameterSpec) {
+                    ivBytes = ((IvParameterSpec)params).getIV();
+                    if ((ivBytes == null) || (ivBytes.length != blockSize)) {
+                        throw new InvalidAlgorithmParameterException
+                            ("Wrong IV length: must be " + blockSize +
+                             " bytes long");
+                    }
+                } else if (params instanceof RC2ParameterSpec) {
+                    ivBytes = ((RC2ParameterSpec)params).getIV();
+                    if ((ivBytes != null) && (ivBytes.length != blockSize)) {
+                        throw new InvalidAlgorithmParameterException
+                            ("Wrong IV length: must be " + blockSize +
+                             " bytes long");
+                    }
+                } else {
+                    throw new InvalidAlgorithmParameterException
+                        ("Unsupported parameter: " + params);
+                }
             }
-        } else if (params instanceof RC2ParameterSpec) {
-            ivBytes = ((RC2ParameterSpec)params).getIV();
-            if ((ivBytes != null) && (ivBytes.length != blockSize)) {
-                throw new InvalidAlgorithmParameterException
-                    ("Wrong IV length: must be " + blockSize +
-                    " bytes long");
-            }
-        } else {
-            throw new InvalidAlgorithmParameterException("Wrong parameter "
-                                                         + "type: IV "
-                                                         + "expected");
         }
-
         if (cipherMode == ECB_MODE) {
             if (ivBytes != null) {
                 throw new InvalidAlgorithmParameterException
                                                 ("ECB mode cannot use IV");
             }
-        } else if (ivBytes == null) {
+        } else if (ivBytes == null)  {
             if (decrypting) {
                 throw new InvalidAlgorithmParameterException("Parameters "
                                                              + "missing");
             }
+
             if (random == null) {
                 random = SunJCE.RANDOM;
             }
-            ivBytes = new byte[blockSize];
+            if (cipherMode == GCM_MODE) {
+                ivBytes = new byte[GaloisCounterMode.DEFAULT_IV_LEN];
+            } else {
+                ivBytes = new byte[blockSize];
+            }
             random.nextBytes(ivBytes);
         }
 
@@ -466,23 +579,57 @@
 
         String algorithm = key.getAlgorithm();
 
-        cipher.init(decrypting, algorithm, keyBytes, ivBytes);
+        // GCM mode needs additional handling
+        if (cipherMode == GCM_MODE) {
+            if(tagLen == -1) {
+                tagLen = GaloisCounterMode.DEFAULT_TAG_LEN;
+            }
+            if (decrypting) {
+                minBytes = tagLen;
+            } else {
+                // check key+iv for encryption in GCM mode
+                requireReinit =
+                    Arrays.equals(ivBytes, lastEncIv) &&
+                    Arrays.equals(keyBytes, lastEncKey);
+                if (requireReinit) {
+                    throw new InvalidAlgorithmParameterException
+                        ("Cannot reuse iv for GCM encryption");
+                }
+                lastEncIv = ivBytes;
+                lastEncKey = keyBytes;
+            }
+            ((GaloisCounterMode) cipher).init
+                (decrypting, algorithm, keyBytes, ivBytes, tagLen);
+        } else {
+            cipher.init(decrypting, algorithm, keyBytes, ivBytes);
+        }
+        // skip checking key+iv from now on until after doFinal()
+        requireReinit = false;
     }
 
     void init(int opmode, Key key, AlgorithmParameters params,
               SecureRandom random)
         throws InvalidKeyException, InvalidAlgorithmParameterException {
-        IvParameterSpec ivSpec = null;
+        AlgorithmParameterSpec spec = null;
+        String paramType = null;
         if (params != null) {
             try {
-                ivSpec = params.getParameterSpec(IvParameterSpec.class);
+                if (cipherMode == GCM_MODE) {
+                    paramType = "GCM";
+                    spec = params.getParameterSpec(GCMParameterSpec.class);
+                } else {
+                    // NOTE: RC2 parameters are always handled through
+                    // init(..., AlgorithmParameterSpec,...) method, so
+                    // we can assume IvParameterSpec type here.
+                    paramType = "IV";
+                    spec = params.getParameterSpec(IvParameterSpec.class);
+                }
             } catch (InvalidParameterSpecException ipse) {
-                throw new InvalidAlgorithmParameterException("Wrong parameter "
-                                                             + "type: IV "
-                                                             + "expected");
+                throw new InvalidAlgorithmParameterException
+                    ("Wrong parameter type: " + paramType + " expected");
             }
         }
-        init(opmode, key, ivSpec, random);
+        init(opmode, key, spec, random);
     }
 
     /**
@@ -504,6 +651,7 @@
         return keyBytes;
     }
 
+
     /**
      * Continues a multiple-part encryption or decryption operation
      * (depending on how this cipher was initialized), processing another data
@@ -524,22 +672,25 @@
      * (e.g., has not been initialized)
      */
     byte[] update(byte[] input, int inputOffset, int inputLen) {
+        if (requireReinit) {
+            throw new IllegalStateException
+                ("Must use either different key or iv for GCM encryption");
+        }
+
         byte[] output = null;
-        byte[] out = null;
         try {
-            output = new byte[getOutputSize(inputLen)];
+            output = new byte[getOutputSizeByOperation(inputLen, false)];
             int len = update(input, inputOffset, inputLen, output,
                              0);
             if (len == output.length) {
-                out = output;
+                return output;
             } else {
-                out = new byte[len];
-                System.arraycopy(output, 0, out, 0, len);
+                return Arrays.copyOf(output, len);
             }
         } catch (ShortBufferException e) {
-            // never thrown
+            // should never happen
+            throw new ProviderException("Unexpected exception", e);
         }
-        return out;
     }
 
     /**
@@ -567,6 +718,11 @@
      */
     int update(byte[] input, int inputOffset, int inputLen, byte[] output,
                int outputOffset) throws ShortBufferException {
+        if (requireReinit) {
+            throw new IllegalStateException
+                ("Must use either different key or iv for GCM encryption");
+        }
+
         // figure out how much can be sent to crypto function
         int len = buffered + inputLen - minBytes;
         if (padding != null && decrypting) {
@@ -582,6 +738,7 @@
                                            + "(at least) " + len
                                            + " bytes long");
         }
+
         if (len != 0) {
             // there is some work to do
             byte[] in = new byte[len];
@@ -600,7 +757,6 @@
                 System.arraycopy(input, inputOffset, in,
                                  bufferedConsumed, inputConsumed);
             }
-
             if (decrypting) {
                 cipher.decrypt(in, 0, len, output, outputOffset);
             } else {
@@ -611,11 +767,12 @@
             // the total input length a multiple of blocksize when
             // padding is applied
             if (unitBytes != blockSize) {
-                if (len < diffBlocksize)
+                if (len < diffBlocksize) {
                     diffBlocksize -= len;
-                else
+                } else {
                     diffBlocksize = blockSize -
                         ((len - diffBlocksize) % blockSize);
+                }
             }
 
             inputLen -= inputConsumed;
@@ -669,21 +826,18 @@
     byte[] doFinal(byte[] input, int inputOffset, int inputLen)
         throws IllegalBlockSizeException, BadPaddingException {
         byte[] output = null;
-        byte[] out = null;
         try {
-            output = new byte[getOutputSize(inputLen)];
+            output = new byte[getOutputSizeByOperation(inputLen, true)];
             int len = doFinal(input, inputOffset, inputLen, output, 0);
             if (len < output.length) {
-                out = new byte[len];
-                if (len != 0)
-                    System.arraycopy(output, 0, out, 0, len);
+                return Arrays.copyOf(output, len);
             } else {
-                out = output;
+                return output;
             }
         } catch (ShortBufferException e) {
             // never thrown
+            throw new ProviderException("Unexpected exception", e);
         }
-        return out;
     }
 
     /**
@@ -726,6 +880,10 @@
                 int outputOffset)
         throws IllegalBlockSizeException, ShortBufferException,
                BadPaddingException {
+        if (requireReinit) {
+            throw new IllegalStateException
+                ("Must use either different key or iv for GCM encryption");
+        }
 
         // calculate the total input length
         int totalLen = buffered + inputLen;
@@ -752,8 +910,9 @@
         }
 
         // if encrypting and padding not null, add padding
-        if (!decrypting && padding != null)
+        if (!decrypting && padding != null) {
             paddedLen += paddingLen;
+        }
 
         // check output buffer capacity.
         // if we are decrypting with padding applied, we can perform this
@@ -763,8 +922,8 @@
             throw new ShortBufferException("Output buffer is null");
         }
         int outputCapacity = output.length - outputOffset;
-        if (((!decrypting) || (padding == null)) &&
-            (outputCapacity < paddedLen) ||
+
+        if (((!decrypting) && (outputCapacity < paddedLen)) ||
             (decrypting && (outputCapacity < (paddedLen - blockSize)))) {
             throw new ShortBufferException("Output buffer too short: "
                                            + outputCapacity + " bytes given, "
@@ -812,6 +971,7 @@
                 }
                 totalLen = padStart;
             }
+
             if ((output.length - outputOffset) < totalLen) {
                 // restore so users can retry with a larger buffer
                 cipher.restore();
@@ -824,8 +984,13 @@
                 output[outputOffset + i] = outWithPadding[i];
             }
         } else { // encrypting
-            totalLen = finalNoPadding(finalBuf, finalOffset, output,
-                                      outputOffset, paddedLen);
+            try {
+                totalLen = finalNoPadding(finalBuf, finalOffset, output,
+                                          outputOffset, paddedLen);
+            } finally {
+                // reset after doFinal() for GCM encryption
+                requireReinit = (cipherMode == GCM_MODE);
+            }
         }
 
         buffered = 0;
@@ -836,33 +1001,33 @@
         return totalLen;
     }
 
-    private int finalNoPadding(byte[] in, int inOff, byte[] out, int outOff,
+    private int finalNoPadding(byte[] in, int inOfs, byte[] out, int outOfs,
                                int len)
-        throws IllegalBlockSizeException
-    {
-        if (in == null || len == 0)
+        throws IllegalBlockSizeException, AEADBadTagException {
+
+        if ((cipherMode != GCM_MODE) && (in == null || len == 0)) {
             return 0;
-
-        if ((cipherMode != CFB_MODE) && (cipherMode != OFB_MODE)
-            && ((len % unitBytes) != 0) && (cipherMode != CTS_MODE)) {
-            if (padding != null) {
-                throw new IllegalBlockSizeException
-                    ("Input length (with padding) not multiple of " +
-                     unitBytes + " bytes");
-            } else {
-                throw new IllegalBlockSizeException
-                    ("Input length not multiple of " + unitBytes
-                     + " bytes");
-            }
         }
-
+        if ((cipherMode != CFB_MODE) && (cipherMode != OFB_MODE) &&
+            (cipherMode != GCM_MODE) &&
+            ((len % unitBytes) != 0) && (cipherMode != CTS_MODE)) {
+                if (padding != null) {
+                    throw new IllegalBlockSizeException
+                        ("Input length (with padding) not multiple of " +
+                         unitBytes + " bytes");
+                } else {
+                    throw new IllegalBlockSizeException
+                        ("Input length not multiple of " + unitBytes
+                         + " bytes");
+                }
+        }
+        int outLen = 0;
         if (decrypting) {
-            cipher.decryptFinal(in, inOff, len, out, outOff);
+            outLen = cipher.decryptFinal(in, inOfs, len, out, outOfs);
         } else {
-            cipher.encryptFinal(in, inOff, len, out, outOff);
+            outLen = cipher.encryptFinal(in, inOfs, len, out, outOfs);
         }
-
-        return len;
+        return outLen;
     }
 
     // Note: Wrap() and Unwrap() are the same in
@@ -939,4 +1104,36 @@
         return ConstructKeys.constructKey(encodedKey, wrappedKeyAlgorithm,
                                           wrappedKeyType);
     }
+
+    /**
+     * Continues a multi-part update of the Additional Authentication
+     * Data (AAD), using a subset of the provided buffer.
+     * <p>
+     * Calls to this method provide AAD to the cipher when operating in
+     * modes such as AEAD (GCM/CCM).  If this cipher is operating in
+     * either GCM or CCM mode, all AAD must be supplied before beginning
+     * operations on the ciphertext (via the {@code update} and {@code
+     * doFinal} methods).
+     *
+     * @param src the buffer containing the AAD
+     * @param offset the offset in {@code src} where the AAD input starts
+     * @param len the number of AAD bytes
+     *
+     * @throws IllegalStateException if this cipher is in a wrong state
+     * (e.g., has not been initialized), does not accept AAD, or if
+     * operating in either GCM or CCM mode and one of the {@code update}
+     * methods has already been called for the active
+     * encryption/decryption operation
+     * @throws UnsupportedOperationException if this method
+     * has not been overridden by an implementation
+     *
+     * @since 1.8
+     */
+    void updateAAD(byte[] src, int offset, int len) {
+        if (requireReinit) {
+            throw new IllegalStateException
+                ("Must use either different key or iv for GCM encryption");
+        }
+        cipher.updateAAD(src, offset, len);
+    }
 }
--- a/src/share/classes/com/sun/crypto/provider/CipherTextStealing.java	Sat Jan 05 17:06:54 2013 +0000
+++ b/src/share/classes/com/sun/crypto/provider/CipherTextStealing.java	Mon Jan 07 11:11:54 2013 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, 2007, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -83,9 +83,10 @@
      * @param plainLen the length of the input data
      * @param cipher the buffer for the result
      * @param cipherOffset the offset in <code>cipher</code>
+     * @return the number of bytes placed into <code>cipher</code>
      */
-    void encryptFinal(byte[] plain, int plainOffset, int plainLen,
-                      byte[] cipher, int cipherOffset)
+    int encryptFinal(byte[] plain, int plainOffset, int plainLen,
+                     byte[] cipher, int cipherOffset)
         throws IllegalBlockSizeException {
 
         if (plainLen < blockSize) {
@@ -134,6 +135,7 @@
                 embeddedCipher.encryptBlock(tmp2, 0, cipher, cipherOffset);
             }
         }
+        return plainLen;
     }
 
     /**
@@ -158,9 +160,10 @@
      * @param cipherLen the length of the input data
      * @param plain the buffer for the result
      * @param plainOffset the offset in <code>plain</code>
+     * @return the number of bytes placed into <code>plain</code>
      */
-    void decryptFinal(byte[] cipher, int cipherOffset, int cipherLen,
-                      byte[] plain, int plainOffset)
+    int decryptFinal(byte[] cipher, int cipherOffset, int cipherLen,
+                     byte[] plain, int plainOffset)
         throws IllegalBlockSizeException {
         if (cipherLen < blockSize) {
             throw new IllegalBlockSizeException("input is too short!");
@@ -211,5 +214,6 @@
                 }
             }
         }
+        return cipherLen;
     }
 }
--- a/src/share/classes/com/sun/crypto/provider/FeedbackCipher.java	Sat Jan 05 17:06:54 2013 +0000
+++ b/src/share/classes/com/sun/crypto/provider/FeedbackCipher.java	Mon Jan 07 11:11:54 2013 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,7 +26,7 @@
 package com.sun.crypto.provider;
 
 import java.security.InvalidKeyException;
-import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.*;
 
 /**
  * This class represents a block cipher in one of its modes. It wraps
@@ -150,11 +150,13 @@
      * @param plainLen the length of the input data
      * @param cipher the buffer for the encryption result
      * @param cipherOffset the offset in <code>cipher</code>
+     * @return the number of bytes placed into <code>cipher</code>
      */
-     void encryptFinal(byte[] plain, int plainOffset, int plainLen,
-                       byte[] cipher, int cipherOffset)
+     int encryptFinal(byte[] plain, int plainOffset, int plainLen,
+                      byte[] cipher, int cipherOffset)
          throws IllegalBlockSizeException {
          encrypt(plain, plainOffset, plainLen, cipher, cipherOffset);
+         return plainLen;
      }
     /**
      * Performs decryption operation.
@@ -190,10 +192,40 @@
      * @param cipherLen the length of the input data
      * @param plain the buffer for the decryption result
      * @param plainOffset the offset in <code>plain</code>
+     * @return the number of bytes placed into <code>plain</code>
      */
-     void decryptFinal(byte[] cipher, int cipherOffset, int cipherLen,
-                       byte[] plain, int plainOffset)
-         throws IllegalBlockSizeException {
+     int decryptFinal(byte[] cipher, int cipherOffset, int cipherLen,
+                      byte[] plain, int plainOffset)
+         throws IllegalBlockSizeException, AEADBadTagException {
          decrypt(cipher, cipherOffset, cipherLen, plain, plainOffset);
+         return cipherLen;
      }
+
+    /**
+     * Continues a multi-part update of the Additional Authentication
+     * Data (AAD), using a subset of the provided buffer. If this
+     * cipher is operating in either GCM or CCM mode, all AAD must be
+     * supplied before beginning operations on the ciphertext (via the
+     * {@code update} and {@code doFinal} methods).
+     * <p>
+     * NOTE: Given most modes do not accept AAD, default impl for this
+     * method throws IllegalStateException.
+     *
+     * @param src the buffer containing the AAD
+     * @param offset the offset in {@code src} where the AAD input starts
+     * @param len the number of AAD bytes
+     *
+     * @throws IllegalStateException if this cipher is in a wrong state
+     * (e.g., has not been initialized), does not accept AAD, or if
+     * operating in either GCM or CCM mode and one of the {@code update}
+     * methods has already been called for the active
+     * encryption/decryption operation
+     * @throws UnsupportedOperationException if this method
+     * has not been overridden by an implementation
+     *
+     * @since 1.8
+     */
+    void updateAAD(byte[] src, int offset, int len) {
+        throw new IllegalStateException("No AAD accepted");
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/crypto/provider/GCMParameters.java	Mon Jan 07 11:11:54 2013 -0800
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.crypto.provider;
+
+import java.io.IOException;
+import java.security.AlgorithmParametersSpi;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+import javax.crypto.spec.GCMParameterSpec;
+import sun.misc.HexDumpEncoder;
+import sun.security.util.*;
+
+/**
+ * This class implements the parameter set used with
+ * GCM encryption, which is defined in RFC 5084 as follows:
+ *
+ * <pre>
+ *    GCMParameters ::= SEQUENCE {
+ *      aes-iv      OCTET STRING, -- recommended size is 12 octets
+ *      aes-tLen    AES-GCM-ICVlen DEFAULT 12 }
+ *
+ *    AES-GCM-ICVlen ::= INTEGER (12 | 13 | 14 | 15 | 16)
+ *
+ * </pre>
+ *
+ * @author Valerie Peng
+ * @since 1.8
+ */
+public final class GCMParameters extends AlgorithmParametersSpi {
+
+    // the iv
+    private byte[] iv;
+    // the tag length in bytes
+    private int tLen;
+
+    public GCMParameters() {}
+
+    protected void engineInit(AlgorithmParameterSpec paramSpec)
+        throws InvalidParameterSpecException {
+
+        if (!(paramSpec instanceof GCMParameterSpec)) {
+            throw new InvalidParameterSpecException
+                ("Inappropriate parameter specification");
+        }
+        GCMParameterSpec gps = (GCMParameterSpec) paramSpec;
+        // need to convert from bits to bytes for ASN.1 encoding
+        this.tLen = gps.getTLen()/8;
+        this.iv = gps.getIV();
+    }
+
+    protected void engineInit(byte[] encoded) throws IOException {
+        DerValue val = new DerValue(encoded);
+        // check if IV or params
+        if (val.tag == DerValue.tag_Sequence) {
+            byte[] iv = val.data.getOctetString();
+            int tLen;
+            if (val.data.available() != 0) {
+                tLen = val.data.getInteger();
+                if (tLen < 12 || tLen > 16 ) {
+                    throw new IOException
+                        ("GCM parameter parsing error: unsupported tag len: " +
+                         tLen);
+                }
+                if (val.data.available() != 0) {
+                    throw new IOException
+                        ("GCM parameter parsing error: extra data");
+                }
+            } else {
+                tLen = 12;
+            }
+            this.iv = iv.clone();
+            this.tLen = tLen;
+        } else {
+            throw new IOException("GCM parameter parsing error: no SEQ tag");
+        }
+    }
+
+    protected void engineInit(byte[] encoded, String decodingMethod)
+        throws IOException {
+        engineInit(encoded);
+    }
+
+    protected <T extends AlgorithmParameterSpec>
+            T engineGetParameterSpec(Class<T> paramSpec)
+        throws InvalidParameterSpecException {
+
+        if (GCMParameterSpec.class.isAssignableFrom(paramSpec)) {
+            return paramSpec.cast(new GCMParameterSpec(tLen * 8, iv));
+        } else {
+            throw new InvalidParameterSpecException
+                ("Inappropriate parameter specification");
+        }
+    }
+
+    protected byte[] engineGetEncoded() throws IOException {
+        DerOutputStream out = new DerOutputStream();
+        DerOutputStream bytes = new DerOutputStream();
+
+        bytes.putOctetString(iv);
+        bytes.putInteger(tLen);
+        out.write(DerValue.tag_Sequence, bytes);
+        return out.toByteArray();
+    }
+
+    protected byte[] engineGetEncoded(String encodingMethod)
+        throws IOException {
+        return engineGetEncoded();
+    }
+
+    /*
+     * Returns a formatted string describing the parameters.
+     */
+    protected String engineToString() {
+        String LINE_SEP = System.getProperty("line.separator");
+        HexDumpEncoder encoder = new HexDumpEncoder();
+        StringBuilder sb
+            = new StringBuilder(LINE_SEP + "    iv:" + LINE_SEP + "["
+                + encoder.encodeBuffer(iv) + "]");
+
+        sb.append(LINE_SEP + "tLen(bits):" + LINE_SEP + tLen*8 + LINE_SEP);
+        return sb.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/crypto/provider/GCTR.java	Mon Jan 07 11:11:54 2013 -0800
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * (C) Copyright IBM Corp. 2013
+ */
+
+package com.sun.crypto.provider;
+
+import java.security.*;
+import javax.crypto.*;
+import static com.sun.crypto.provider.AESConstants.AES_BLOCK_SIZE;
+
+/**
+ * This class represents the GCTR function defined in NIST 800-38D
+ * under section 6.5. It needs to be constructed w/ an initialized
+ * cipher object, and initial counter block(ICB). Given an input X
+ * of arbitrary length, it processes and returns an output which has
+ * the same length as X.
+ *
+ * <p>This function is used in the implementation of GCM mode.
+ *
+ * @since 1.8
+ */
+final class GCTR {
+
+    // these fields should not change after the object has been constructed
+    private final SymmetricCipher aes;
+    private final byte[] icb;
+
+    // the current counter value
+    private byte[] counter;
+
+    // needed for save/restore calls
+    private byte[] counterSave;
+
+    // NOTE: cipher should already be initialized
+    GCTR(SymmetricCipher cipher, byte[] initialCounterBlk) {
+        this.aes = cipher;
+        this.icb = initialCounterBlk;
+        this.counter = icb.clone();
+    }
+
+    // input must be multiples of 128-bit blocks when calling update
+    int update(byte[] in, int inOfs, int inLen, byte[] out, int outOfs) {
+        if (inLen - inOfs > in.length) {
+            throw new RuntimeException("input length out of bound");
+        }
+        if (inLen < 0 || inLen % AES_BLOCK_SIZE != 0) {
+            throw new RuntimeException("input length unsupported");
+        }
+        if (out.length - outOfs < inLen) {
+            throw new RuntimeException("output buffer too small");
+        }
+
+        byte[] encryptedCntr = new byte[AES_BLOCK_SIZE];
+
+        int numOfCompleteBlocks = inLen / AES_BLOCK_SIZE;
+        for (int i = 0; i < numOfCompleteBlocks; i++) {
+            aes.encryptBlock(counter, 0, encryptedCntr, 0);
+            for (int n = 0; n < AES_BLOCK_SIZE; n++) {
+                int index = (i * AES_BLOCK_SIZE + n);
+                out[outOfs + index] =
+                    (byte) ((in[inOfs + index] ^ encryptedCntr[n]));
+            }
+            GaloisCounterMode.increment32(counter);
+        }
+        return inLen;
+    }
+
+    // input can be arbitrary size when calling doFinal
+    protected int doFinal(byte[] in, int inOfs, int inLen, byte[] out,
+                          int outOfs) throws IllegalBlockSizeException {
+        try {
+            if (inLen < 0) {
+                throw new IllegalBlockSizeException("Negative input size!");
+            } else if (inLen > 0) {
+                int lastBlockSize = inLen % AES_BLOCK_SIZE;
+                // process the complete blocks first
+                update(in, inOfs, inLen - lastBlockSize, out, outOfs);
+                if (lastBlockSize != 0) {
+                    // do the last partial block
+                    byte[] encryptedCntr = new byte[AES_BLOCK_SIZE];
+                    aes.encryptBlock(counter, 0, encryptedCntr, 0);
+
+                    int processed = inLen - lastBlockSize;
+                    for (int n = 0; n < lastBlockSize; n++) {
+                        out[outOfs + processed + n] =
+                            (byte) ((in[inOfs + processed + n] ^
+                                     encryptedCntr[n]));
+                    }
+                }
+            }
+        } finally {
+            reset();
+        }
+        return inLen;
+    }
+
+    /**
+     * Resets the current counter to its initial value.
+     * This is used after the doFinal() is called so this object can be
+     * reused w/o explicit re-initialization.
+     */
+    void reset() {
+        System.arraycopy(icb, 0, counter, 0, icb.length);
+    }
+
+    /**
+     * Save the current content of this object.
+     */
+    void save() {
+        this.counterSave = this.counter.clone();
+    }
+
+    /**
+     * Restores the content of this object to the previous saved one.
+     */
+    void restore() {
+        this.counter = this.counterSave;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/crypto/provider/GHASH.java	Mon Jan 07 11:11:54 2013 -0800
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+/*
+ * (C) Copyright IBM Corp. 2013
+ */
+
+package com.sun.crypto.provider;
+
+import java.util.Arrays;
+import java.security.*;
+import static com.sun.crypto.provider.AESConstants.AES_BLOCK_SIZE;
+
+/**
+ * This class represents the GHASH function defined in NIST 800-38D
+ * under section 6.4. It needs to be constructed w/ a hash subkey, i.e.
+ * block H. Given input of 128-bit blocks, it will process and output
+ * a 128-bit block.
+ *
+ * <p>This function is used in the implementation of GCM mode.
+ *
+ * @since 1.8
+ */
+final class GHASH {
+
+    private static final byte P128 = (byte) 0xe1; //reduction polynomial
+
+    private static boolean getBit(byte[] b, int pos) {
+        int p = pos / 8;
+        pos %= 8;
+        int i = (b[p] >>> (7 - pos)) & 1;
+        return i != 0;
+    }
+
+    private static void shift(byte[] b) {
+        byte temp, temp2;
+        temp2 = 0;
+        for (int i = 0; i < b.length; i++) {
+            temp = (byte) ((b[i] & 0x01) << 7);
+            b[i] = (byte) ((b[i] & 0xff) >>> 1);
+            b[i] = (byte) (b[i] | temp2);
+            temp2 = temp;
+        }
+    }
+
+    // Given block X and Y, returns the muliplication of X * Y
+    private static byte[] blockMult(byte[] x, byte[] y) {
+        if (x.length != AES_BLOCK_SIZE || y.length != AES_BLOCK_SIZE) {
+            throw new RuntimeException("illegal input sizes");
+        }
+        byte[] z = new byte[AES_BLOCK_SIZE];
+        byte[] v = y.clone();
+        // calculate Z1-Z127 and V1-V127
+        for (int i = 0; i < 127; i++) {
+            // Zi+1 = Zi if bit i of x is 0
+            if (getBit(x, i)) {
+                for (int n = 0; n < z.length; n++) {
+                    z[n] ^= v[n];
+                }
+            }
+            boolean lastBitOfV = getBit(v, 127);
+            shift(v);
+            if (lastBitOfV) v[0] ^= P128;
+        }
+        // calculate Z128
+        if (getBit(x, 127)) {
+            for (int n = 0; n < z.length; n++) {
+                z[n] ^= v[n];
+            }
+        }
+        return z;
+    }
+
+    // hash subkey H; should not change after the object has been constructed
+    private final byte[] subkeyH;
+
+    // buffer for storing hash
+    private byte[] state;
+
+    // variables for save/restore calls
+    private byte[] stateSave = null;
+
+    /**
+     * Initializes the cipher in the specified mode with the given key
+     * and iv.
+     *
+     * @param subkeyH the hash subkey
+     *
+     * @exception ProviderException if the given key is inappropriate for
+     * initializing this digest
+     */
+    GHASH(byte[] subkeyH) throws ProviderException {
+        if ((subkeyH == null) || subkeyH.length != AES_BLOCK_SIZE) {
+            throw new ProviderException("Internal error");
+        }
+        this.subkeyH = subkeyH;
+        this.state = new byte[AES_BLOCK_SIZE];
+    }
+
+    /**
+     * Resets the GHASH object to its original state, i.e. blank w/
+     * the same subkey H. Used after digest() is called and to re-use
+     * this object for different data w/ the same H.
+     */
+    void reset() {
+        Arrays.fill(state, (byte) 0);
+    }
+
+    /**
+     * Save the current snapshot of this GHASH object.
+     */
+    void save() {
+        stateSave = state.clone();
+    }
+
+    /**
+     * Restores this object using the saved snapshot.
+     */
+    void restore() {
+        state = stateSave;
+    }
+
+    private void processBlock(byte[] data, int ofs) {
+        if (data.length - ofs < AES_BLOCK_SIZE) {
+            throw new RuntimeException("need complete block");
+        }
+        for (int n = 0; n < state.length; n++) {
+            state[n] ^= data[ofs + n];
+        }
+        state = blockMult(state, subkeyH);
+    }
+
+    void update(byte[] in) {
+        update(in, 0, in.length);
+    }
+
+    void update(byte[] in, int inOfs, int inLen) {
+        if (inLen - inOfs > in.length) {
+            throw new RuntimeException("input length out of bound");
+        }
+        if (inLen % AES_BLOCK_SIZE != 0) {
+            throw new RuntimeException("input length unsupported");
+        }
+
+        for (int i = inOfs; i < (inOfs + inLen); i += AES_BLOCK_SIZE) {
+            processBlock(in, i);
+        }
+    }
+
+    byte[] digest() {
+        try {
+            return state.clone();
+        } finally {
+            reset();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/com/sun/crypto/provider/GaloisCounterMode.java	Mon Jan 07 11:11:54 2013 -0800
@@ -0,0 +1,501 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.S
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.crypto.provider;
+
+import java.util.Arrays;
+import java.io.*;
+import java.security.*;
+import javax.crypto.*;
+import static com.sun.crypto.provider.AESConstants.AES_BLOCK_SIZE;
+
+/**
+ * This class represents ciphers in GaloisCounter (GCM) mode.
+ *
+ * <p>This mode currently should only be used w/ AES cipher.
+ * Although no checking is done here, caller should only
+ * pass AES Cipher to the constructor.
+ *
+ * <p>NOTE: This class does not deal with buffering or padding.
+ *
+ * @since 1.8
+ */
+final class GaloisCounterMode extends FeedbackCipher {
+
+    static int DEFAULT_TAG_LEN = AES_BLOCK_SIZE;
+    static int DEFAULT_IV_LEN = 12; // in bytes
+
+    // buffer for AAD data; if null, meaning update has been called
+    private ByteArrayOutputStream aadBuffer = new ByteArrayOutputStream();
+    private int sizeOfAAD = 0;
+
+    // in bytes; need to convert to bits (default value 128) when needed
+    private int tagLenBytes = DEFAULT_TAG_LEN;
+
+    // these following 2 fields can only be initialized after init() is
+    // called, e.g. after cipher key k is set, and STAY UNCHANGED
+    private byte[] subkeyH = null;
+    private byte[] preCounterBlock = null;
+
+    private GCTR gctrPAndC = null;
+    private GHASH ghashAllToS = null;
+
+    // length of total data, i.e. len(C)
+    private int processed = 0;
+
+    // additional variables for save/restore calls
+    private byte[] aadBufferSave = null;
+    private int sizeOfAADSave = 0;
+    private int processedSave = 0;
+
+    // value must be 16-byte long; used by GCTR and GHASH as well
+    static void increment32(byte[] value) {
+        if (value.length != AES_BLOCK_SIZE) {
+            throw new RuntimeException("Unexpected counter block length");
+        }
+        // start from last byte and only go over 4 bytes, i.e. total 32 bits
+        int n = value.length - 1;
+        while ((n >= value.length - 4) && (++value[n] == 0)) {
+            n--;
+        }
+    }
+
+    // ivLen in bits
+    private static byte[] getLengthBlock(int ivLen) {
+        byte[] out = new byte[AES_BLOCK_SIZE];
+        out[12] = (byte)(ivLen >>> 24);
+        out[13] = (byte)(ivLen >>> 16);
+        out[14] = (byte)(ivLen >>> 8);
+        out[15] = (byte)ivLen;
+        return out;
+    }
+
+    // aLen and cLen both in bits
+    private static byte[] getLengthBlock(int aLen, int cLen) {
+        byte[] out = new byte[AES_BLOCK_SIZE];
+        out[4] = (byte)(aLen >>> 24);
+        out[5] = (byte)(aLen >>> 16);
+        out[6] = (byte)(aLen >>> 8);
+        out[7] = (byte)aLen;
+        out[12] = (byte)(cLen >>> 24);
+        out[13] = (byte)(cLen >>> 16);
+        out[14] = (byte)(cLen >>> 8);
+        out[15] = (byte)cLen;
+        return out;
+    }
+
+    private static byte[] expandToOneBlock(byte[] in, int inOfs, int len) {
+        if (len > AES_BLOCK_SIZE) {
+            throw new ProviderException("input " + len + " too long");
+        }
+        if (len == AES_BLOCK_SIZE && inOfs == 0) {
+            return in;
+        } else {
+            byte[] paddedIn = new byte[AES_BLOCK_SIZE];
+            System.arraycopy(in, inOfs, paddedIn, 0, len);
+            return paddedIn;
+        }
+    }
+
+    private static byte[] getJ0(byte[] iv, byte[] subkeyH) {
+        byte[] j0;
+        if (iv.length == 12) { // 96 bits
+            j0 = expandToOneBlock(iv, 0, iv.length);
+            j0[AES_BLOCK_SIZE - 1] = 1;
+        } else {
+            GHASH g = new GHASH(subkeyH);
+            int lastLen = iv.length % AES_BLOCK_SIZE;
+            if (lastLen != 0) {
+                g.update(iv, 0, iv.length - lastLen);
+                byte[] padded =
+                    expandToOneBlock(iv, iv.length - lastLen, lastLen);
+                g.update(padded);
+            } else {
+                g.update(iv);
+            }
+            byte[] lengthBlock = getLengthBlock(iv.length*8);
+            g.update(lengthBlock);
+            j0 = g.digest();
+        }
+        return j0;
+    }
+
+    GaloisCounterMode(SymmetricCipher embeddedCipher) {
+        super(embeddedCipher);
+        aadBuffer = new ByteArrayOutputStream();
+    }
+
+    /**
+     * Gets the name of the feedback mechanism
+     *
+     * @return the name of the feedback mechanism
+     */
+    String getFeedback() {
+        return "GCM";
+    }
+
+    /**
+     * Resets the cipher object to its original state.
+     * This is used when doFinal is called in the Cipher class, so that the
+     * cipher can be reused (with its original key and iv).
+     */
+    void reset() {
+        if (aadBuffer == null) {
+            aadBuffer = new ByteArrayOutputStream();
+        } else {
+            aadBuffer.reset();
+        }
+        if (gctrPAndC != null) gctrPAndC.reset();
+        if (ghashAllToS != null) ghashAllToS.reset();
+        processed = 0;
+        sizeOfAAD = 0;
+    }
+
+    /**
+     * Save the current content of this cipher.
+     */
+    void save() {
+        processedSave = processed;
+        sizeOfAADSave = sizeOfAAD;
+        aadBufferSave =
+            ((aadBuffer == null || aadBuffer.size() == 0)?
+             null : aadBuffer.toByteArray());
+        if (gctrPAndC != null) gctrPAndC.save();
+        if (ghashAllToS != null) ghashAllToS.save();
+    }
+
+    /**
+     * Restores the content of this cipher to the previous saved one.
+     */
+    void restore() {
+        processed = processedSave;
+        sizeOfAAD = sizeOfAADSave;
+        if (aadBuffer != null) {
+            aadBuffer.reset();
+            if (aadBufferSave != null) {
+                aadBuffer.write(aadBufferSave, 0, aadBufferSave.length);
+            }
+        }
+       if (gctrPAndC != null) gctrPAndC.restore();
+       if (ghashAllToS != null) ghashAllToS.restore();
+    }
+
+    /**
+     * Initializes the cipher in the specified mode with the given key
+     * and iv.
+     *
+     * @param decrypting flag indicating encryption or decryption
+     * @param algorithm the algorithm name
+     * @param key the key
+     * @param iv the iv
+     * @param tagLenBytes the length of tag in bytes
+     *
+     * @exception InvalidKeyException if the given key is inappropriate for
+     * initializing this cipher
+     */
+    void init(boolean decrypting, String algorithm, byte[] key, byte[] iv)
+            throws InvalidKeyException {
+        init(decrypting, algorithm, key, iv, DEFAULT_TAG_LEN);
+    }
+
+    /**
+     * Initializes the cipher in the specified mode with the given key
+     * and iv.
+     *
+     * @param decrypting flag indicating encryption or decryption
+     * @param algorithm the algorithm name
+     * @param key the key
+     * @param iv the iv
+     * @param tagLenBytes the length of tag in bytes
+     *
+     * @exception InvalidKeyException if the given key is inappropriate for
+     * initializing this cipher
+     */
+    void init(boolean decrypting, String algorithm, byte[] keyValue,
+              byte[] ivValue, int tagLenBytes)
+              throws InvalidKeyException {
+        if (keyValue == null || ivValue == null) {
+            throw new InvalidKeyException("Internal error");
+        }
+
+        // always encrypt mode for embedded cipher
+        this.embeddedCipher.init(false, algorithm, keyValue);
+        this.subkeyH = new byte[AES_BLOCK_SIZE];
+        this.embeddedCipher.encryptBlock(new byte[AES_BLOCK_SIZE], 0,
+                this.subkeyH, 0);
+
+        this.iv = ivValue.clone();
+        preCounterBlock = getJ0(iv, subkeyH);
+        byte[] j0Plus1 = preCounterBlock.clone();
+        increment32(j0Plus1);
+        gctrPAndC = new GCTR(embeddedCipher, j0Plus1);
+        ghashAllToS = new GHASH(subkeyH);
+
+        this.tagLenBytes = tagLenBytes;
+        if (aadBuffer == null) {
+            aadBuffer = new ByteArrayOutputStream();
+        } else {
+            aadBuffer.reset();
+        }
+        processed = 0;
+        sizeOfAAD = 0;
+    }
+
+    /**
+     * Continues a multi-part update of the Additional Authentication
+     * Data (AAD), using a subset of the provided buffer. If this
+     * cipher is operating in either GCM or CCM mode, all AAD must be
+     * supplied before beginning operations on the ciphertext (via the
+     * {@code update} and {@code doFinal} methods).
+     * <p>
+     * NOTE: Given most modes do not accept AAD, default impl for this
+     * method throws IllegalStateException.
+     *
+     * @param src the buffer containing the AAD
+     * @param offset the offset in {@code src} where the AAD input starts
+     * @param len the number of AAD bytes
+     *
+     * @throws IllegalStateException if this cipher is in a wrong state
+     * (e.g., has not been initialized), does not accept AAD, or if
+     * operating in either GCM or CCM mode and one of the {@code update}
+     * methods has already been called for the active
+     * encryption/decryption operation
+     * @throws UnsupportedOperationException if this method
+     * has not been overridden by an implementation
+     *
+     * @since 1.8
+     */
+    void updateAAD(byte[] src, int offset, int len) {
+        if (aadBuffer != null) {
+            aadBuffer.write(src, offset, len);
+        } else {
+            // update has already been called
+            throw new IllegalStateException
+                ("Update has been called; no more AAD data");
+        }
+    }
+
+    // Feed the AAD data to GHASH, pad if necessary
+    void processAAD() {
+        if (aadBuffer != null) {
+            byte[] aad = aadBuffer.toByteArray();
+            sizeOfAAD = aad.length;
+            aadBuffer = null;
+
+            int lastLen = aad.length % AES_BLOCK_SIZE;
+            if (lastLen != 0) {
+                ghashAllToS.update(aad, 0, aad.length - lastLen);
+                byte[] padded = expandToOneBlock(aad, aad.length - lastLen,
+                                                 lastLen);
+                ghashAllToS.update(padded);
+            } else {
+                ghashAllToS.update(aad);
+            }
+        }
+    }
+
+    // Utility to process the last block; used by encryptFinal and decryptFinal
+    void doLastBlock(byte[] in, int inOfs, int len, byte[] out, int outOfs,
+                     boolean isEncrypt) throws IllegalBlockSizeException {
+        // process data in 'in'
+        gctrPAndC.doFinal(in, inOfs, len, out, outOfs);
+        processed += len;
+
+        byte[] ct;
+        int ctOfs;
+        if (isEncrypt) {
+            ct = out;
+            ctOfs = outOfs;
+        } else {
+            ct = in;
+            ctOfs = inOfs;
+        }
+        int lastLen = len  % AES_BLOCK_SIZE;
+        if (lastLen != 0) {
+            ghashAllToS.update(ct, ctOfs, len - lastLen);
+            byte[] padded =
+                expandToOneBlock(ct, (ctOfs + len - lastLen), lastLen);
+            ghashAllToS.update(padded);
+        } else {
+            ghashAllToS.update(ct, ctOfs, len);
+        }
+    }
+
+
+    /**
+     * Performs encryption operation.
+     *
+     * <p>The input plain text <code>in</code>, starting at <code>inOff</code>
+     * and ending at <code>(inOff + len - 1)</code>, is encrypted. The result
+     * is stored in <code>out</code>, starting at <code>outOfs</code>.
+     *
+     * <p>It is the application's responsibility to make sure that
+     * <code>len</code> is a multiple of the embedded cipher's block size,
+     * otherwise, a ProviderException will be thrown.
+     *
+     * <p>It is also the application's responsibility to make sure that
+     * <code>init</code> has been called before this method is called.
+     * (This check is omitted here, to avoid double checking.)
+     *
+     * @param in the buffer with the input data to be encrypted
+     * @param inOfs the offset in <code>in</code>
+     * @param len the length of the input data
+     * @param out the buffer for the result
+     * @param outOfs the offset in <code>out</code>
+     */
+    void encrypt(byte[] in, int inOfs, int len, byte[] out, int outOfs) {
+        processAAD();
+        if (len > 0) {
+            gctrPAndC.update(in, inOfs, len, out, outOfs);
+            processed += len;
+            ghashAllToS.update(out, outOfs, len);
+        }
+    }
+
+    /**
+     * Performs encryption operation for the last time.
+     *
+     * <p>NOTE: <code>len</code> may not be multiple of the embedded
+     * cipher's block size for this call.
+     *
+     * @param in the input buffer with the data to be encrypted
+     * @param inOfs the offset in <code>in</code>
+     * @param len the length of the input data
+     * @param out the buffer for the encryption result
+     * @param outOfs the offset in <code>out</code>
+     * @return the number of bytes placed into the <code>out</code> buffer
+     */
+     int encryptFinal(byte[] in, int inOfs, int len, byte[] out, int outOfs)
+         throws IllegalBlockSizeException {
+         if (out.length - outOfs < (len + tagLenBytes)) {
+             throw new RuntimeException("Output buffer too small");
+         }
+
+         processAAD();
+         if (len > 0) {
+             //ByteUtil.dumpArray(Arrays.copyOfRange(in, inOfs, inOfs + len));
+             doLastBlock(in, inOfs, len, out, outOfs, true);
+         }
+
+         byte[] lengthBlock = getLengthBlock(sizeOfAAD*8, processed*8);
+         ghashAllToS.update(lengthBlock);
+         byte[] s = ghashAllToS.digest();
+         byte[] sOut = new byte[s.length];
+         GCTR gctrForSToTag = new GCTR(embeddedCipher, this.preCounterBlock);
+         gctrForSToTag.doFinal(s, 0, s.length, sOut, 0);
+
+         System.arraycopy(sOut, 0, out, (outOfs + len), tagLenBytes);
+         return (len + tagLenBytes);
+     }
+
+    /**
+     * Performs decryption operation.
+     *
+     * <p>The input cipher text <code>in</code>, starting at
+     * <code>inOfs</code> and ending at <code>(inOfs + len - 1)</code>,
+     * is decrypted. The result is stored in <code>out</code>, starting at
+     * <code>outOfs</code>.
+     *
+     * <p>It is the application's responsibility to make sure that
+     * <code>len</code> is a multiple of the embedded cipher's block
+     * size, as any excess bytes are ignored.
+     *
+     * <p>It is also the application's responsibility to make sure that
+     * <code>init</code> has been called before this method is called.
+     * (This check is omitted here, to avoid double checking.)
+     *
+     * @param in the buffer with the input data to be decrypted
+     * @param inOfs the offset in <code>in</code>
+     * @param len the length of the input data
+     * @param out the buffer for the result
+     * @param outOfs the offset in <code>out</code>
+     */
+    void decrypt(byte[] in, int inOfs, int len, byte[] out, int outOfs) {
+        processAAD();
+
+        if (len > 0) { // must be at least AES_BLOCK_SIZE bytes long
+            gctrPAndC.update(in, inOfs, len, out, outOfs);
+            processed += len;
+            ghashAllToS.update(in, inOfs, len);
+        }
+    }
+
+    /**
+     * Performs decryption operation for the last time.
+     *
+     * <p>NOTE: For cipher feedback modes which does not perform
+     * special handling for the last few blocks, this is essentially
+     * the same as <code>encrypt(...)</code>. Given most modes do
+     * not do special handling, the default impl for this method is
+     * to simply call <code>decrypt(...)</code>.
+     *
+     * @param in the input buffer with the data to be decrypted
+     * @param inOfs the offset in <code>cipher</code>
+     * @param len the length of the input data
+     * @param out the buffer for the decryption result
+     * @param outOfs the offset in <code>plain</code>
+     * @return the number of bytes placed into the <code>out</code> buffer
+     */
+     int decryptFinal(byte[] in, int inOfs, int len,
+                      byte[] out, int outOfs)
+         throws IllegalBlockSizeException, AEADBadTagException {
+         if (len < tagLenBytes) {
+             throw new RuntimeException("Input buffer too short - need tag");
+         }
+         if (out.length - outOfs < (len - tagLenBytes)) {
+             throw new RuntimeException("Output buffer too small");
+         }
+         processAAD();
+
+         int processedOld = processed;
+         byte[] tag = new byte[tagLenBytes];
+         // get the trailing tag bytes from 'in'
+         System.arraycopy(in, inOfs + len - tagLenBytes, tag, 0, tagLenBytes);
+         len -= tagLenBytes;
+
+         if (len > 0) {
+             doLastBlock(in, inOfs, len, out, outOfs, false);
+         }
+
+         byte[] lengthBlock = getLengthBlock(sizeOfAAD*8, processed*8);
+         ghashAllToS.update(lengthBlock);
+
+         byte[] s = ghashAllToS.digest();
+         byte[] sOut = new byte[s.length];
+         GCTR gctrForSToTag = new GCTR(embeddedCipher, this.preCounterBlock);
+         gctrForSToTag.doFinal(s, 0, s.length, sOut, 0);
+         for (int i = 0; i < tagLenBytes; i++) {
+             if (tag[i] != sOut[i]) {
+                 throw new AEADBadTagException("Tag mismatch!");
+             }
+         }
+         return len;
+     }
+
+    // return tag length in bytes
+    int getTagLen() {
+        return this.tagLenBytes;
+    }
+}
--- a/src/share/classes/com/sun/crypto/provider/SunJCE.java	Sat Jan 05 17:06:54 2013 +0000
+++ b/src/share/classes/com/sun/crypto/provider/SunJCE.java	Mon Jan 07 11:11:54 2013 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -57,6 +57,7 @@
  * - ARCFOUR (RC4 compatible)
  *
  * - Cipher modes ECB, CBC, CFB, OFB, PCBC, CTR, and CTS for all block ciphers
+ *   and mode GCM for AES cipher
  *
  * - Cipher padding ISO10126Padding for non-PKCS#5 block ciphers and
  *   NoPadding and PKCS5Padding for all block ciphers
@@ -100,7 +101,7 @@
             "|CFB8|CFB16|CFB24|CFB32|CFB40|CFB48|CFB56|CFB64" +
             "|OFB8|OFB16|OFB24|OFB32|OFB40|OFB48|OFB56|OFB64";
         final String BLOCK_MODES128 = BLOCK_MODES +
-            "|CFB72|CFB80|CFB88|CFB96|CFB104|CFB112|CFB120|CFB128" +
+            "|GCM|CFB72|CFB80|CFB88|CFB96|CFB104|CFB112|CFB120|CFB128" +
             "|OFB72|OFB80|OFB88|OFB96|OFB104|OFB112|OFB120|OFB128";
         final String BLOCK_PADS = "NOPADDING|PKCS5PADDING|ISO10126PADDING";
 
@@ -258,6 +259,9 @@
                     put("Cipher.AES_128/CFB/NoPadding", "com.sun.crypto.provider.AESCipher$AES128_CFB_NoPadding");
                     put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.4", "AES_128/CFB/NoPadding");
                     put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.4", "AES_128/CFB/NoPadding");
+                    put("Cipher.AES_128/GCM/NoPadding", "com.sun.crypto.provider.AESCipher$AES128_GCM_NoPadding");
+                    put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.6", "AES_128/GCM/NoPadding");
+                    put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.6", "AES_128/GCM/NoPadding");
 
                     put("Cipher.AES_192/ECB/NoPadding", "com.sun.crypto.provider.AESCipher$AES192_ECB_NoPadding");
                     put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.21", "AES_192/ECB/NoPadding");
@@ -271,7 +275,9 @@
                     put("Cipher.AES_192/CFB/NoPadding", "com.sun.crypto.provider.AESCipher$AES192_CFB_NoPadding");
                     put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.24", "AES_192/CFB/NoPadding");
                     put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.24", "AES_192/CFB/NoPadding");
-
+                    put("Cipher.AES_192/GCM/NoPadding", "com.sun.crypto.provider.AESCipher$AES192_GCM_NoPadding");
+                    put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.26", "AES_192/GCM/NoPadding");
+                    put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.26", "AES_192/GCM/NoPadding");
 
                     put("Cipher.AES_256/ECB/NoPadding", "com.sun.crypto.provider.AESCipher$AES256_ECB_NoPadding");
                     put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.41", "AES_256/ECB/NoPadding");
@@ -285,6 +291,9 @@
                     put("Cipher.AES_256/CFB/NoPadding", "com.sun.crypto.provider.AESCipher$AES256_CFB_NoPadding");
                     put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.44", "AES_256/CFB/NoPadding");
                     put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.44", "AES_256/CFB/NoPadding");
+                    put("Cipher.AES_256/GCM/NoPadding", "com.sun.crypto.provider.AESCipher$AES256_GCM_NoPadding");
+                    put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.46", "AES_256/GCM/NoPadding");
+                    put("Alg.Alias.Cipher.OID.2.16.840.1.101.3.4.1.46", "AES_256/GCM/NoPadding");
 
                     put("Cipher.AESWrap", "com.sun.crypto.provider.AESWrapCipher$General");
                     put("Cipher.AESWrap SupportedModes", "ECB");
@@ -509,6 +518,8 @@
                     put("AlgorithmParameters.AES",
                         "com.sun.crypto.provider.AESParameters");
                     put("Alg.Alias.AlgorithmParameters.Rijndael", "AES");
+                    put("AlgorithmParameters.GCM",
+                        "com.sun.crypto.provider.GCMParameters");
 
 
                     put("AlgorithmParameters.RC2",
--- a/src/share/classes/javax/crypto/Cipher.java	Sat Jan 05 17:06:54 2013 +0000
+++ b/src/share/classes/javax/crypto/Cipher.java	Mon Jan 07 11:11:54 2013 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -104,17 +104,30 @@
  * must be supplied to GCM/CCM implementations (via the {@code
  * updateAAD} methods) <b>before</b> the ciphertext is processed (via
  * the {@code update} and {@code doFinal} methods).
- *
+ * <p>
+ * Note that GCM mode has a uniqueness requirement on IVs used in
+ * encryption with a given key. When IVs are repeated for GCM
+ * encryption, such usages are subject to forgery attacks. Thus, after
+ * each encryption operation using GCM mode, callers should re-initialize
+ * the cipher objects with GCM parameters which has a different IV value.
  * <pre>
- *     GCMParameterSpec s = new GCMParameterSpec(...);
+ *     GCMParameterSpec s = ...;
  *     cipher.init(..., s);
  *
- *     // If the GCMParameterSpec is needed again
- *     cipher.getParameters().getParameterSpec(GCMParameterSpec.class));
+ *     // If the GCM parameters were generated by the provider, it can
+ *     // be retrieved by:
+ *     // cipher.getParameters().getParameterSpec(GCMParameterSpec.class);
  *
  *     cipher.updateAAD(...);  // AAD
  *     cipher.update(...);     // Multi-part update
  *     cipher.doFinal(...);    // conclusion of operation
+ *
+ *     // Use a different IV value for every encryption
+ *     byte[] newIv = ...;
+ *     s = new GCMParameterSpec(s.getTLen(), newIv);
+ *     cipher.init(..., s);
+ *     ...
+ *
  * </pre>
  * Every implementation of the Java platform is required to support
  * the following standard <code>Cipher</code> transformations with the keysizes
--- a/src/share/classes/javax/crypto/spec/GCMParameterSpec.java	Sat Jan 05 17:06:54 2013 +0000
+++ b/src/share/classes/javax/crypto/spec/GCMParameterSpec.java	Mon Jan 07 11:11:54 2013 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -43,7 +43,7 @@
  * (Additional Authenticated Data (AAD), Keys, block ciphers,
  * plain/ciphertext and authentication tags) are handled in the {@code
  * Cipher} class.
-  <p>
+ * <p>
  * Please see <a href="http://www.ietf.org/rfc/rfc5116.txt"> RFC 5116
  * </a> for more information on the Authenticated Encryption with
  * Associated Data (AEAD) algorithm, and <a href=
--- a/test/com/sun/crypto/provider/Cipher/AES/Test4512524.java	Sat Jan 05 17:06:54 2013 +0000
+++ b/test/com/sun/crypto/provider/Cipher/AES/Test4512524.java	Mon Jan 07 11:11:54 2013 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2007, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -40,13 +40,13 @@
 public class Test4512524 {
 
     private static final String ALGO = "AES";
-    private static final String MODE = "CBC";
     private static final String PADDING = "NoPadding";
     private static final int KEYSIZE = 16; // in bytes
 
-    public boolean execute() throws Exception {
+    public void execute(String mode) throws Exception {
 
-        Cipher ci = Cipher.getInstance(ALGO+"/"+MODE+"/"+PADDING, "SunJCE");
+        String transformation = ALGO+"/"+mode+"/"+PADDING;
+        Cipher ci = Cipher.getInstance(transformation, "SunJCE");
 
         // TEST FIX 4512524
         KeyGenerator kg = KeyGenerator.getInstance(ALGO, "SunJCE");
@@ -61,17 +61,14 @@
         }
 
         // passed all tests...hooray!
-        return true;
+        System.out.println(transformation + ": Passed");
     }
 
     public static void main (String[] args) throws Exception {
         Security.addProvider(new com.sun.crypto.provider.SunJCE());
 
         Test4512524 test = new Test4512524();
-        String testName = test.getClass().getName() + "[" + ALGO +
-            "/" + MODE + "/" + PADDING + "]";
-        if (test.execute()) {
-            System.out.println(testName + ": Passed!");
-        }
+        test.execute("CBC");
+        test.execute("GCM");
     }
 }
--- a/test/com/sun/crypto/provider/Cipher/AES/Test4512704.java	Sat Jan 05 17:06:54 2013 +0000
+++ b/test/com/sun/crypto/provider/Cipher/AES/Test4512704.java	Mon Jan 07 11:11:54 2013 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2007, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -39,14 +39,14 @@
 
 public class Test4512704 {
     private static final String ALGO = "AES";
-    private static final String MODE = "CBC";
-    private static final String PADDING = "PKCS5Padding";
+    private static final String PADDING = "NoPadding";
     private static final int KEYSIZE = 16; // in bytes
 
-    public boolean execute() throws Exception {
+    public void execute(String mode) throws Exception {
+
         AlgorithmParameterSpec aps = null;
-
-        Cipher ci = Cipher.getInstance(ALGO+"/"+MODE+"/"+PADDING, "SunJCE");
+        String transformation = ALGO + "/" + mode + "/"+PADDING;
+        Cipher ci = Cipher.getInstance(transformation, "SunJCE");
 
         KeyGenerator kg = KeyGenerator.getInstance(ALGO, "SunJCE");
         kg.init(KEYSIZE*8);
@@ -57,19 +57,14 @@
         } catch(InvalidAlgorithmParameterException ex) {
             throw new Exception("parameter should be generated when null is specified!");
         }
-
-        // passed all tests...hooray!
-        return true;
+        System.out.println(transformation + ": Passed");
     }
 
     public static void main (String[] args) throws Exception {
         Security.addProvider(new com.sun.crypto.provider.SunJCE());
 
         Test4512704 test = new Test4512704();
-        String testName = test.getClass().getName() + "[" + ALGO +
-            "/" + MODE + "/" + PADDING + "]";
-        if (test.execute()) {
-            System.out.println(testName + ": Passed!");
-        }
+        test.execute("CBC");
+        test.execute("GCM");
     }
 }
--- a/test/com/sun/crypto/provider/Cipher/AES/Test4517355.java	Sat Jan 05 17:06:54 2013 +0000
+++ b/test/com/sun/crypto/provider/Cipher/AES/Test4517355.java	Mon Jan 07 11:11:54 2013 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2007, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -41,16 +41,14 @@
 public class Test4517355 {
 
     private static final String ALGO = "AES";
-    private static final String MODE = "CBC";
-    private static final String PADDING = "PKCS5Padding";
     private static final int KEYSIZE = 16; // in bytes
 
-    public boolean execute() throws Exception {
-        Random rdm = new Random();
-        byte[] plainText=new byte[125];
-        rdm.nextBytes(plainText);
+    private static byte[] plainText = new byte[125];
 
-        Cipher ci = Cipher.getInstance(ALGO+"/"+MODE+"/"+PADDING, "SunJCE");
+    public void execute(String mode, String padding) throws Exception {
+        String transformation = ALGO + "/" + mode + "/" + padding;
+
+        Cipher ci = Cipher.getInstance(transformation, "SunJCE");
         KeyGenerator kg = KeyGenerator.getInstance(ALGO, "SunJCE");
         kg.init(KEYSIZE*8);
         SecretKey key = kg.generateKey();
@@ -59,9 +57,14 @@
         ci.init(Cipher.ENCRYPT_MODE, key);
         byte[] cipherText = ci.doFinal(plainText);
 
-        byte[] iv = ci.getIV();
-        AlgorithmParameterSpec aps = new IvParameterSpec(iv);
-        ci.init(Cipher.DECRYPT_MODE, key, aps);
+        if (mode.equalsIgnoreCase("GCM")) {
+            AlgorithmParameters params = ci.getParameters();
+            ci.init(Cipher.DECRYPT_MODE, key, params);
+        } else {
+            byte[] iv = ci.getIV();
+            AlgorithmParameterSpec aps = new IvParameterSpec(iv);
+            ci.init(Cipher.DECRYPT_MODE, key, aps);
+        }
         byte[] recoveredText = new byte[plainText.length];
         try {
             int len = ci.doFinal(cipherText, 0, cipherText.length,
@@ -80,21 +83,22 @@
             throw new Exception("encryption does not work!");
         }
         // 3. make sure padding is working
-        if ((cipherText.length/16)*16 != cipherText.length) {
-            throw new Exception("padding does not work!");
+        if (padding.equalsIgnoreCase("PKCS5Padding")) {
+            if ((cipherText.length/16)*16 != cipherText.length) {
+                throw new Exception("padding does not work!");
+            }
         }
-        // passed all tests...hooray!
-        return true;
+        System.out.println(transformation + ": Passed");
     }
 
     public static void main (String[] args) throws Exception {
         Security.addProvider(new com.sun.crypto.provider.SunJCE());
 
         Test4517355 test = new Test4517355();
-        String testName = test.getClass().getName() + "[" + ALGO +
-            "/" + MODE + "/" + PADDING + "]";
-        if (test.execute()) {
-            System.out.println(testName + ": Passed!");
-        }
+        Random rdm = new Random();
+        rdm.nextBytes(test.plainText);
+
+        test.execute("CBC", "PKCS5Padding");
+        test.execute("GCM", "NoPadding");
     }
 }
--- a/test/com/sun/crypto/provider/Cipher/AES/Test4626070.java	Sat Jan 05 17:06:54 2013 +0000
+++ b/test/com/sun/crypto/provider/Cipher/AES/Test4626070.java	Mon Jan 07 11:11:54 2013 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2007, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -34,13 +34,11 @@
 
 public class Test4626070 {
     private static final String ALGO = "AES";
-    private static final String MODE = "CBC";
-    private static final String PADDING = "PKCS5Padding";
     private static final int KEYSIZE = 16; // in bytes
 
-    public boolean execute() throws Exception {
-
-        Cipher ci = Cipher.getInstance(ALGO+"/"+MODE+"/"+PADDING, "SunJCE");
+    public void execute(String mode, String padding) throws Exception {
+        String transformation = ALGO + "/" + mode + "/" + padding;
+        Cipher ci = Cipher.getInstance(transformation, "SunJCE");
         KeyGenerator kg = KeyGenerator.getInstance(ALGO, "SunJCE");
         kg.init(KEYSIZE*8);
         SecretKey key = kg.generateKey();
@@ -58,18 +56,14 @@
             throw new Exception(
                 "key after wrap/unwrap is different from the original!");
         }
-        // passed all tests...hooray!
-        return true;
+        System.out.println(transformation + ": Passed");
     }
 
     public static void main (String[] args) throws Exception {
         Security.addProvider(new com.sun.crypto.provider.SunJCE());
 
         Test4626070 test = new Test4626070();
-        String testName = test.getClass().getName() + "[" + ALGO +
-            "/" + MODE + "/" + PADDING + "]";
-        if (test.execute()) {
-            System.out.println(testName + ": Passed!");
-        }
+        test.execute("CBC", "PKCS5Padding");
+        test.execute("GCM", "NoPadding");
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/com/sun/crypto/provider/Cipher/AES/TestGCMKeyAndIvCheck.java	Mon Jan 07 11:11:54 2013 -0800
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 6996769
+ * @library ../UTIL
+ * @build TestUtil
+ * @run main TestGCMKeyAndIvCheck
+ * @summary Ensure that same key+iv can't be repeated used for encryption.
+ * @author Valerie Peng
+ */
+
+
+import java.security.*;
+import javax.crypto.*;
+import javax.crypto.spec.*;
+import java.math.*;
+import com.sun.crypto.provider.*;
+
+import java.util.*;
+
+public class TestGCMKeyAndIvCheck {
+
+    private static final byte[] AAD = new byte[5];
+    private static final byte[] PT = new byte[18];
+
+    private static void checkISE(Cipher c) throws Exception {
+        // Subsequent encryptions should fail
+        try {
+            c.updateAAD(AAD);
+            throw new Exception("Should throw ISE for updateAAD()");
+        } catch (IllegalStateException ise) {
+            // expected
+        }
+
+        try {
+            c.update(PT);
+            throw new Exception("Should throw ISE for update()");
+        } catch (IllegalStateException ise) {
+            // expected
+        }
+        try {
+            c.doFinal(PT);
+            throw new Exception("Should throw ISE for doFinal()");
+        } catch (IllegalStateException ise) {
+            // expected
+        }
+    }
+
+    public void test() throws Exception {
+        Cipher c = Cipher.getInstance("AES/GCM/NoPadding", "SunJCE");
+
+        SecretKey key = new SecretKeySpec(new byte[16], "AES");
+        // First try parameter-less init.
+        c.init(Cipher.ENCRYPT_MODE, key);
+        c.updateAAD(AAD);
+        byte[] ctPlusTag = c.doFinal(PT);
+
+        // subsequent encryption should fail unless re-init w/ different key+iv
+        checkISE(c);
+
+        // Validate the retrieved parameters against the IV and tag length.
+        AlgorithmParameters params = c.getParameters();
+        if (params == null) {
+            throw new Exception("getParameters() should not return null");
+        }
+        GCMParameterSpec spec = params.getParameterSpec(GCMParameterSpec.class);
+        if (spec.getTLen() != (ctPlusTag.length - PT.length)*8) {
+            throw new Exception("Parameters contains incorrect TLen value");
+        }
+        if (!Arrays.equals(spec.getIV(), c.getIV())) {
+            throw new Exception("Parameters contains incorrect IV value");
+        }
+
+        // Should be ok to use the same key+iv for decryption
+        c.init(Cipher.DECRYPT_MODE, key, params);
+        c.updateAAD(AAD);
+        byte[] recovered = c.doFinal(ctPlusTag);
+        if (!Arrays.equals(recovered, PT)) {
+            throw new Exception("decryption result mismatch");
+        }
+
+        // Now try to encrypt again using the same key+iv; should fail also
+        try {
+            c.init(Cipher.ENCRYPT_MODE, key, params);
+            throw new Exception("Should throw exception when same key+iv is used");
+        } catch (InvalidAlgorithmParameterException iape) {
+            // expected
+        }
+
+        // Now try to encrypt again using parameter-less init; should work
+        c.init(Cipher.ENCRYPT_MODE, key);
+        c.doFinal(PT);
+
+        // make sure a different iv is used
+        byte[] iv = c.getIV();
+        if (Arrays.equals(spec.getIV(), iv)) {
+            throw new Exception("IV should be different now");
+        }
+
+        // Now try to encrypt again using a different parameter; should work
+        c.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(128, new byte[30]));
+        c.updateAAD(AAD);
+        c.doFinal(PT);
+        // subsequent encryption should fail unless re-init w/ different key+iv
+        checkISE(c);
+
+        // Now try decryption twice in a row; no re-init required and
+        // same parameters is used.
+        c.init(Cipher.DECRYPT_MODE, key, params);
+        c.updateAAD(AAD);
+        recovered = c.doFinal(ctPlusTag);
+
+        c.updateAAD(AAD);
+        recovered = c.doFinal(ctPlusTag);
+        if (!Arrays.equals(recovered, PT)) {
+            throw new Exception("decryption result mismatch");
+        }
+
+        // Now try decryption again and re-init using the same parameters
+        c.init(Cipher.DECRYPT_MODE, key, params);
+        c.updateAAD(AAD);
+        recovered = c.doFinal(ctPlusTag);
+
+        // init to decrypt w/o parameters; should fail with IKE as
+        // javadoc specified
+        try {
+            c.init(Cipher.DECRYPT_MODE, key);
+            throw new Exception("Should throw IKE for dec w/o params");
+        } catch (InvalidKeyException ike) {
+            // expected
+        }
+
+        // Lastly, try encryption AND decryption w/ wrong type of parameters,
+        // e.g. IvParameterSpec
+        try {
+            c.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
+            throw new Exception("Should throw IAPE");
+        } catch (InvalidAlgorithmParameterException iape) {
+            // expected
+        }
+        try {
+            c.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
+            throw new Exception("Should throw IAPE");
+        } catch (InvalidAlgorithmParameterException iape) {
+            // expected
+        }
+
+        System.out.println("Test Passed!");
+    }
+
+    public static void main (String[] args) throws Exception {
+        TestGCMKeyAndIvCheck t = new TestGCMKeyAndIvCheck();
+        t.test();
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/com/sun/crypto/provider/Cipher/AES/TestKATForGCM.java	Mon Jan 07 11:11:54 2013 -0800
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 6996769
+ * @library ../UTIL
+ * @build TestUtil
+ * @run main TestKATForGCM
+ * @summary Known Answer Test for AES cipher with GCM mode support in
+ * SunJCE provider.
+ * @author Valerie Peng
+ */
+
+
+import java.security.*;
+import javax.crypto.*;
+import javax.crypto.spec.*;
+import java.math.*;
+import com.sun.crypto.provider.*;
+
+import java.util.*;
+
+public class TestKATForGCM {
+
+    // Utility methods
+    private static byte[] HexToBytes(String hexVal) {
+        if (hexVal == null) return new byte[0];
+        byte[] result = new byte[hexVal.length()/2];
+        for (int i = 0; i < result.length; i++) {
+            // 2 characters at a time
+            String byteVal = hexVal.substring(2*i, 2*i +2);
+            result[i] = Integer.valueOf(byteVal, 16).byteValue();
+        }
+        return result;
+    }
+
+    private static class TestVector {
+        SecretKey key;
+        byte[] plainText;
+        byte[] aad;
+        byte[] cipherText;
+        byte[] tag;
+        GCMParameterSpec spec;
+        String info;
+
+        TestVector(String key, String iv, String pt, String aad,
+                   String ct, String tag) {
+            this.key = new SecretKeySpec(HexToBytes(key), "AES");
+            this.plainText = HexToBytes(pt);
+            this.aad = HexToBytes(aad);
+            this.cipherText = HexToBytes(ct);
+            this.tag = HexToBytes(tag);
+            this.spec = new GCMParameterSpec(this.tag.length * 8, HexToBytes(iv));
+            this.info = "key=" + key + ", iv=" + iv + ", pt=" + pt +
+                ",aad=" + aad + ", ct=" + ct + ", tag=" + tag;
+        }
+
+        public String toString() {
+            return info;
+        }
+    }
+
+    // These test vectors are found off NIST's CAVP page
+    // http://csrc.nist.gov/groups/STM/cavp/index.html
+    // inside the link named "GCM Test Vectors", i.e.
+    // http://csrc.nist.gov/groups/STM/cavp/documents/mac/gcmtestvectors.zip
+    // CAVS 14.0, set of test vectors w/ count = 0, keysize = 128
+    private static TestVector[] testValues = {
+        // 96-bit iv w/ 128/120/112/104/96-bit tags
+        // no plain text, no aad
+        new TestVector("11754cd72aec309bf52f7687212e8957",
+                       "3c819d9a9bed087615030b65",
+                       null, null, null,
+                       "250327c674aaf477aef2675748cf6971"),
+        new TestVector("272f16edb81a7abbea887357a58c1917",
+                       "794ec588176c703d3d2a7a07",
+                       null, null, null,
+                       "b6e6f197168f5049aeda32dafbdaeb"),
+        new TestVector("81b6844aab6a568c4556a2eb7eae752f",
+                       "ce600f59618315a6829bef4d",
+                       null, null, null,
+                       "89b43e9dbc1b4f597dbbc7655bb5"),
+        new TestVector("cde2f9a9b1a004165ef9dc981f18651b",
+                       "29512c29566c7322e1e33e8e",
+                       null, null, null,
+                       "2e58ce7dabd107c82759c66a75"),
+        new TestVector("b01e45cc3088aaba9fa43d81d481823f",
+                       "5a2c4a66468713456a4bd5e1",
+                       null, null, null,
+                       "014280f944f53c681164b2ff"),
+        // 96-bit iv w/ 128/120/112/104/96-bit tags
+        // no plain text, 16-byte aad
+        new TestVector("77be63708971c4e240d1cb79e8d77feb",
+                       "e0e00f19fed7ba0136a797f3",
+                       null,
+                       "7a43ec1d9c0a5a78a0b16533a6213cab",
+                       null,
+                       "209fcc8d3675ed938e9c7166709dd946"),
+        new TestVector("da0b615656135194ba6d3c851099bc48",
+                       "d39d4b4d3cc927885090e6c3",
+                       null,
+                       "e7e5e6f8dac913036cb2ff29e8625e0e",
+                       null,
+                       "ab967711a5770461724460b07237e2"),
+        new TestVector("7e0986937a88eef894235aba4a2f43b2",
+                       "92c4a631695907166b422d60",
+                       null,
+                       "85c185f8518f9f2cd597a8f9208fc76b",
+                       null,
+                       "3bb916b728df94fe9d1916736be1"),
+        new TestVector("c3db570d7f0c21e86b028f11465d1dc9",
+                       "f86970f58ceef89fc7cb679e",
+                       null,
+                       "c095240708c0f57c288d86090ae34ee1",
+                       null,
+                       "e043c52160d652e82c7262fcf4"),
+        new TestVector("bea48ae4980d27f357611014d4486625",
+                       "32bddb5c3aa998a08556454c",
+                       null,
+                       "8a50b0b8c7654bced884f7f3afda2ead",
+                       null,
+                       "8e0f6d8bf05ffebe6f500eb1"),
+        // 96-bit iv w/ 128/120/112/104/96-bit tags
+        // no plain text, 20-byte aad
+        new TestVector("2fb45e5b8f993a2bfebc4b15b533e0b4",
+                       "5b05755f984d2b90f94b8027",
+                       null,
+                       "e85491b2202caf1d7dce03b97e09331c32473941",
+                       null,
+                       "c75b7832b2a2d9bd827412b6ef5769db"),
+        new TestVector("9bf406339fcef9675bbcf156aa1a0661",
+                       "8be4a9543d40f542abacac95",
+                       null,
+                       "7167cbf56971793186333a6685bbd58d47d379b3",
+                       null,
+                       "5e7968d7bbd5ba58cfcc750e2ef8f1"),
+        new TestVector("a2e962fff70fd0f4d63be728b80556fc",
+                       "1fa7103483de43d09bc23db4",
+                       null,
+                       "2a58edf1d53f46e4e7ee5e77ee7aeb60fc360658",
+                       null,
+                       "fa37f2dbbefab1451eae1d0d74ca"),
+        new TestVector("6bf4fdce82926dcdfc52616ed5f23695",
+                       "cc0f5899a10615567e1193ed",
+                       null,
+                       "3340655592374c1da2f05aac3ee111014986107f",
+                       null,
+                       "8ad3385cce3b5e7c985908192c"),
+        new TestVector("4df7a13e43c3d7b66b1a72fac5ba398e",
+                       "97179a3a2d417908dcf0fb28",
+                       null,
+                       "cbb7fc0010c255661e23b07dbd804b1e06ae70ac",
+                       null,
+                       "37791edae6c137ea946cfb40"),
+        // 96-bit iv w/ 128-bit tags, 13/16/32/51-byte plain text, no aad
+        new TestVector("fe9bb47deb3a61e423c2231841cfd1fb",
+                       "4d328eb776f500a2f7fb47aa",
+                       "f1cc3818e421876bb6b8bbd6c9",
+                       null,
+                       "b88c5c1977b35b517b0aeae967",
+                       "43fd4727fe5cdb4b5b42818dea7ef8c9"),
+        new TestVector("7fddb57453c241d03efbed3ac44e371c",
+                       "ee283a3fc75575e33efd4887",
+                       "d5de42b461646c255c87bd2962d3b9a2",
+                       null,
+                       "2ccda4a5415cb91e135c2a0f78c9b2fd",
+                       "b36d1df9b9d5e596f83e8b7f52971cb3"),
+        new TestVector("9971071059abc009e4f2bd69869db338",
+                       "07a9a95ea3821e9c13c63251",
+                       "f54bc3501fed4f6f6dfb5ea80106df0bd836e6826225b75c0222f6e859b35983",
+                       null,
+                       "0556c159f84ef36cb1602b4526b12009c775611bffb64dc0d9ca9297cd2c6a01",
+                       "7870d9117f54811a346970f1de090c41"),
+        new TestVector("594157ec4693202b030f33798b07176d",
+                       "49b12054082660803a1df3df",
+
+"3feef98a976a1bd634f364ac428bb59cd51fb159ec1789946918dbd50ea6c9d594a3a31a5269b0da6936c29d063a5fa2cc8a1c",
+                      null,
+
+"c1b7a46a335f23d65b8db4008a49796906e225474f4fe7d39e55bf2efd97fd82d4167de082ae30fa01e465a601235d8d68bc69",
+                      "ba92d3661ce8b04687e8788d55417dc2"),
+        // 96-bit iv w/ 128-bit tags, 16-byte plain text, 16/20/48/90-byte aad
+        new TestVector("c939cc13397c1d37de6ae0e1cb7c423c",
+                       "b3d8cc017cbb89b39e0f67e2",
+                       "c3b3c41f113a31b73d9a5cd432103069",
+                       "24825602bd12a984e0092d3e448eda5f",
+                       "93fe7d9e9bfd10348a5606e5cafa7354",
+                       "0032a1dc85f1c9786925a2e71d8272dd"),
+        new TestVector("d4a22488f8dd1d5c6c19a7d6ca17964c",
+                       "f3d5837f22ac1a0425e0d1d5",
+                       "7b43016a16896497fb457be6d2a54122",
+                       "f1c5d424b83f96c6ad8cb28ca0d20e475e023b5a",
+                       "c2bd67eef5e95cac27e3b06e3031d0a8",
+                       "f23eacf9d1cdf8737726c58648826e9c"),
+        new TestVector("89850dd398e1f1e28443a33d40162664",
+                       "e462c58482fe8264aeeb7231",
+                       "2805cdefb3ef6cc35cd1f169f98da81a",
+
+"d74e99d1bdaa712864eec422ac507bddbe2b0d4633cd3dff29ce5059b49fe868526c59a2a3a604457bc2afea866e7606",
+                       "ba80e244b7fc9025cd031d0f63677e06",
+                       "d84a8c3eac57d1bb0e890a8f461d1065"),
+        new TestVector("bd7c5c63b7542b56a00ebe71336a1588",
+                       "87721f23ba9c3c8ea5571abc",
+                       "de15ddbb1e202161e8a79af6a55ac6f3",
+
+"a6ec8075a0d3370eb7598918f3b93e48444751624997b899a87fa6a9939f844e008aa8b70e9f4c3b1a19d3286bf543e7127bfecba1ad17a5ec53fccc26faecacc4c75369498eaa7d706aef634d0009279b11e4ba6c993e5e9ed9",
+                       "41eb28c0fee4d762de972361c863bc80",
+                       "9cb567220d0b252eb97bff46e4b00ff8"),
+        // 8/1024-bit iv w/ 128-bit tag, no plain text, no aad
+        new TestVector("1672c3537afa82004c6b8a46f6f0d026",
+                       "05",
+                       null, null, null,
+                       "8e2ad721f9455f74d8b53d3141f27e8e"),
+        new TestVector("d0f1f4defa1e8c08b4b26d576392027c",
+
+"42b4f01eb9f5a1ea5b1eb73b0fb0baed54f387ecaa0393c7d7dffc6af50146ecc021abf7eb9038d4303d91f8d741a11743166c0860208bcc02c6258fd9511a2fa626f96d60b72fcff773af4e88e7a923506e4916ecbd814651e9f445adef4ad6a6b6c7290cc13b956130eef5b837c939fcac0cbbcc9656cd75b13823ee5acdac",
+                       null, null, null,
+                       "7ab49b57ddf5f62c427950111c5c4f0d"),
+        // 8-bit iv w/ 128-bit tag, 13-byte plain text, 90-byte aad
+        new TestVector("9f79239f0904eace50784b863e723f6b",
+                       "d9",
+                       "bdb0bb10c87965acd34d146171",
+
+"44db436089327726c5f01139e1f339735c9e85514ccc2f167bad728010fb34a9072a9794c8a5e7361b1d0dbcdc9ac4091e354bb2896561f0486645252e9c78c86beece91bfa4f7cc4a8794ce1f305b1b735efdbf1ed1563c0be0",
+                       "7e5a7c8dadb3f0c7335b4d9d8d",
+                       "6b6ef1f53723a89f3bb7c6d043840717"),
+        // 1024-bit iv w/ 128-bit tag, 51-byte plain text, 48-byte aad
+        new TestVector("141f1ce91989b07e7eb6ae1dbd81ea5e",
+
+"49451da24bd6074509d3cebc2c0394c972e6934b45a1d91f3ce1d3ca69e194aa1958a7c21b6f21d530ce6d2cc5256a3f846b6f9d2f38df0102c4791e57df038f6e69085646007df999751e248e06c47245f4cd3b8004585a7470dee1690e9d2d63169a58d243c0b57b3e5b4a481a3e4e8c60007094ef3adea2e8f05dd3a1396f",
+
+"d384305af2388699aa302f510913fed0f2cb63ba42efa8c5c9de2922a2ec2fe87719dadf1eb0aef212b51e74c9c5b934104a43",
+
+"630cf18a91cc5a6481ac9eefd65c24b1a3c93396bd7294d6b8ba323951727666c947a21894a079ef061ee159c05beeb4",
+
+"f4c34e5fbe74c0297313268296cd561d59ccc95bbfcdfcdc71b0097dbd83240446b28dc088abd42b0fc687f208190ff24c0548",
+                      "dbb93bbb56d0439cd09f620a57687f5d"),
+    };
+
+    public boolean execute(TestVector[] testValues) throws Exception {
+        boolean testFailed = false;
+        Cipher c = Cipher.getInstance("AES/GCM/NoPadding", "SunJCE");
+        for (int i = 0; i < testValues.length; i++) {
+            try {
+                c.init(Cipher.ENCRYPT_MODE, testValues[i].key, testValues[i].spec);
+                c.updateAAD(testValues[i].aad);
+                byte[] ctPlusTag = c.doFinal(testValues[i].plainText);
+
+                c.init(Cipher.DECRYPT_MODE, testValues[i].key, testValues[i].spec);
+                c.updateAAD(testValues[i].aad);
+                byte[] pt = c.doFinal(ctPlusTag); // should fail if tag mismatched
+
+                // check encryption/decryption results just to be sure
+                if (!Arrays.equals(testValues[i].plainText, pt)) {
+                    System.out.println("PlainText diff failed for test# " + i);
+                    testFailed = true;
+                }
+                int ctLen = testValues[i].cipherText.length;
+                if (!Arrays.equals(testValues[i].cipherText,
+                                   Arrays.copyOf(ctPlusTag, ctLen))) {
+                    System.out.println("CipherText diff failed for test# " + i);
+                    testFailed = true;
+                }
+                int tagLen = testValues[i].tag.length;
+                if (!Arrays.equals
+                    (testValues[i].tag,
+                     Arrays.copyOfRange(ctPlusTag, ctLen, ctLen+tagLen))) {
+                    System.out.println("Tag diff failed for test# " + i);
+                    testFailed = true;
+                }
+            } catch (Exception ex) {
+                // continue testing other test vectors
+                System.out.println("Failed Test Vector: " + testValues[i]);
+                ex.printStackTrace();
+                testFailed = true;
+                continue;
+            }
+        }
+        if (testFailed) {
+            throw new Exception("Test Failed");
+        }
+        // passed all tests...hooray!
+        return true;
+    }
+
+    public static void main (String[] args) throws Exception {
+        TestKATForGCM test = new TestKATForGCM();
+        if (test.execute(testValues)) {
+            System.out.println("Test Passed!");
+        }
+    }
+}
+
--- a/test/javax/crypto/Cipher/GCMAPI.java	Sat Jan 05 17:06:54 2013 +0000
+++ b/test/javax/crypto/Cipher/GCMAPI.java	Mon Jan 07 11:11:54 2013 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -78,6 +78,8 @@
             c.updateAAD(src);
         } catch (UnsupportedOperationException e) {
             // swallow
+        } catch (IllegalStateException ise) {
+            // swallow
         }catch (Exception e) {
             e.printStackTrace();
             failed++;
@@ -99,6 +101,8 @@
             c.updateAAD(src, offset, len);
         } catch (UnsupportedOperationException e) {
             // swallow
+        } catch (IllegalStateException ise) {
+            // swallow
         } catch (Exception e) {
             e.printStackTrace();
             failed++;
@@ -120,6 +124,8 @@
             c.updateAAD(src);
         } catch (UnsupportedOperationException e) {
             // swallow
+        } catch (IllegalStateException ise) {
+            // swallow
         }catch (Exception e) {
             e.printStackTrace();
             failed++;