diff --git a/jdk/src/share/classes/javax/crypto/AEADBadTagException.java b/jdk/src/share/classes/javax/crypto/AEADBadTagException.java new file mode 100644 index 00000000000..8587fcf3ce6 --- /dev/null +++ b/jdk/src/share/classes/javax/crypto/AEADBadTagException.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2011, 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 javax.crypto; + +/** + * This exception is thrown when a {@link Cipher} operating in + * an AEAD mode (such as GCM/CCM) is unable to verify the supplied + * authentication tag. + * + * @since 1.7 + */ +public class AEADBadTagException extends BadPaddingException { + + private static final long serialVersionUID = -488059093241685509L; + + /** + * Constructs a AEADBadTagException with no detail message. + */ + public AEADBadTagException() { + super(); + } + + /** + * Constructs a AEADBadTagException with the specified + * detail message. + * + * @param msg the detail message. + */ + public AEADBadTagException(String msg) { + super(msg); + } +} diff --git a/jdk/src/share/classes/javax/crypto/Cipher.java b/jdk/src/share/classes/javax/crypto/Cipher.java index d6878d23eaf..bd17f023639 100644 --- a/jdk/src/share/classes/javax/crypto/Cipher.java +++ b/jdk/src/share/classes/javax/crypto/Cipher.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2011, 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 @@ -88,8 +88,35 @@ import sun.security.jca.GetInstance.Instance; * example, the SunJCE provider uses a default of 64 bits for DES.) * Thus, block ciphers can be turned into byte-oriented stream ciphers by * using an 8 bit mode such as CFB8 or OFB8. + *
+ * Modes such as Authenticated Encryption with Associated Data (AEAD) + * provide authenticity assurances for both confidential data and + * Additional Associated Data (AAD) that is not encrypted. (Please see + * RFC 5116 for more + * information on AEAD and AEAD algorithms such as GCM/CCM.) Both + * confidential and AAD data can be used when calculating the + * authentication tag (similar to a {@link Mac}). This tag is appended + * to the ciphertext during encryption, and is verified on decryption. + *
+ * AEAD modes such as GCM/CCM perform all AAD authenticity calculations + * before starting the ciphertext authenticity calculations. To avoid + * implementations having to internally buffer ciphertext, all AAD data + * must be supplied to GCM/CCM implementations (via the {@code + * updateAAD} methods) before the ciphertext is processed (via + * the {@code update} and {@code doFinal} methods). * - *
Every implementation of the Java platform is required to support + *
+ * GCMParameterSpec s = new GCMParameterSpec(...); + * cipher.init(..., s); + * + * // If the GCMParameterSpec is needed again + * cipher.getParameters().getParameterSpec(GCMParameterSpec.class)); + * + * cipher.updateAAD(...); // AAD + * cipher.update(...); // Multi-part update + * cipher.doFinal(...); // conclusion of operation + *+ * Every implementation of the Java platform is required to support * the following standard
Cipher transformations with the keysizes
* in parentheses:
* inputLen (in bytes).
*
* This call takes into account any unprocessed (buffered) data from a
- * previous update call, and padding.
+ * previous update call, padding, and AEAD tagging.
*
*
The actual output length of the next update or
* doFinal call may be smaller than the length returned by
@@ -1090,6 +1117,11 @@ public class Cipher {
* {@link #getParameters() getParameters} or
* {@link #getIV() getIV} (if the parameter is an IV).
*
+ *
If this cipher requires algorithm parameters that cannot be + * derived from the input parameters, and there are no reasonable + * provider-specific default values, initialization will + * necessarily fail. + * *
If this cipher (including its underlying feedback or padding scheme)
* requires any random bytes (e.g., for parameter generation), it will get
* them using the {@link SecureRandom SecureRandom}
@@ -1110,8 +1142,8 @@ public class Cipher {
* @param key the key
*
* @exception InvalidKeyException if the given key is inappropriate for
- * initializing this cipher, or if this cipher is being initialized for
- * decryption and requires algorithm parameters that cannot be
+ * initializing this cipher, or requires
+ * algorithm parameters that cannot be
* determined from the given key, or if the given key has a keysize that
* exceeds the maximum allowable keysize (as determined from the
* configured jurisdiction policy files).
@@ -1138,6 +1170,11 @@ public class Cipher {
* {@link #getParameters() getParameters} or
* {@link #getIV() getIV} (if the parameter is an IV).
*
+ *
If this cipher requires algorithm parameters that cannot be + * derived from the input parameters, and there are no reasonable + * provider-specific default values, initialization will + * necessarily fail. + * *
If this cipher (including its underlying feedback or padding scheme)
* requires any random bytes (e.g., for parameter generation), it will get
* them from random.
@@ -1155,8 +1192,8 @@ public class Cipher {
* @param random the source of randomness
*
* @exception InvalidKeyException if the given key is inappropriate for
- * initializing this cipher, or if this cipher is being initialized for
- * decryption and requires algorithm parameters that cannot be
+ * initializing this cipher, or requires
+ * algorithm parameters that cannot be
* determined from the given key, or if the given key has a keysize that
* exceeds the maximum allowable keysize (as determined from the
* configured jurisdiction policy files).
@@ -1202,6 +1239,11 @@ public class Cipher {
* {@link #getParameters() getParameters} or
* {@link #getIV() getIV} (if the parameter is an IV).
*
+ *
If this cipher requires algorithm parameters that cannot be + * derived from the input parameters, and there are no reasonable + * provider-specific default values, initialization will + * necessarily fail. + * *
If this cipher (including its underlying feedback or padding scheme)
* requires any random bytes (e.g., for parameter generation), it will get
* them using the {@link SecureRandom SecureRandom}
@@ -1227,7 +1269,7 @@ public class Cipher {
* keysize (as determined from the configured jurisdiction policy files).
* @exception InvalidAlgorithmParameterException if the given algorithm
* parameters are inappropriate for this cipher,
- * or this cipher is being initialized for decryption and requires
+ * or this cipher requires
* algorithm parameters and params is null, or the given
* algorithm parameters imply a cryptographic strength that would exceed
* the legal limits (as determined from the configured jurisdiction
@@ -1258,6 +1300,11 @@ public class Cipher {
* {@link #getParameters() getParameters} or
* {@link #getIV() getIV} (if the parameter is an IV).
*
+ *
If this cipher requires algorithm parameters that cannot be + * derived from the input parameters, and there are no reasonable + * provider-specific default values, initialization will + * necessarily fail. + * *
If this cipher (including its underlying feedback or padding scheme)
* requires any random bytes (e.g., for parameter generation), it will get
* them from random.
@@ -1280,7 +1327,7 @@ public class Cipher {
* keysize (as determined from the configured jurisdiction policy files).
* @exception InvalidAlgorithmParameterException if the given algorithm
* parameters are inappropriate for this cipher,
- * or this cipher is being initialized for decryption and requires
+ * or this cipher requires
* algorithm parameters and params is null, or the given
* algorithm parameters imply a cryptographic strength that would exceed
* the legal limits (as determined from the configured jurisdiction
@@ -1323,6 +1370,11 @@ public class Cipher {
* {@link #getParameters() getParameters} or
* {@link #getIV() getIV} (if the parameter is an IV).
*
+ *
If this cipher requires algorithm parameters that cannot be + * derived from the input parameters, and there are no reasonable + * provider-specific default values, initialization will + * necessarily fail. + * *
If this cipher (including its underlying feedback or padding scheme)
* requires any random bytes (e.g., for parameter generation), it will get
* them using the {@link SecureRandom SecureRandom}
@@ -1348,7 +1400,7 @@ public class Cipher {
* keysize (as determined from the configured jurisdiction policy files).
* @exception InvalidAlgorithmParameterException if the given algorithm
* parameters are inappropriate for this cipher,
- * or this cipher is being initialized for decryption and requires
+ * or this cipher requires
* algorithm parameters and params is null, or the given
* algorithm parameters imply a cryptographic strength that would exceed
* the legal limits (as determined from the configured jurisdiction
@@ -1379,6 +1431,11 @@ public class Cipher {
* {@link #getParameters() getParameters} or
* {@link #getIV() getIV} (if the parameter is an IV).
*
+ *
If this cipher requires algorithm parameters that cannot be + * derived from the input parameters, and there are no reasonable + * provider-specific default values, initialization will + * necessarily fail. + * *
If this cipher (including its underlying feedback or padding scheme)
* requires any random bytes (e.g., for parameter generation), it will get
* them from random.
@@ -1401,7 +1458,7 @@ public class Cipher {
* keysize (as determined from the configured jurisdiction policy files).
* @exception InvalidAlgorithmParameterException if the given algorithm
* parameters are inappropriate for this cipher,
- * or this cipher is being initialized for decryption and requires
+ * or this cipher requires
* algorithm parameters and params is null, or the given
* algorithm parameters imply a cryptographic strength that would exceed
* the legal limits (as determined from the configured jurisdiction
@@ -1444,7 +1501,7 @@ public class Cipher {
* derived from the public key in the given certificate, the underlying
* cipher
* implementation is supposed to generate the required parameters itself
- * (using provider-specific default or ramdom values) if it is being
+ * (using provider-specific default or random values) if it is being
* initialized for encryption or key wrapping, and raise an
* InvalidKeyException if it is being initialized for decryption or
* key unwrapping.
@@ -1452,6 +1509,11 @@ public class Cipher {
* {@link #getParameters() getParameters} or
* {@link #getIV() getIV} (if the parameter is an IV).
*
+ *
If this cipher requires algorithm parameters that cannot be + * derived from the input parameters, and there are no reasonable + * provider-specific default values, initialization will + * necessarily fail. + * *
If this cipher (including its underlying feedback or padding scheme) * requires any random bytes (e.g., for parameter generation), it will get * them using the @@ -1474,8 +1536,7 @@ public class Cipher { * * @exception InvalidKeyException if the public key in the given * certificate is inappropriate for initializing this cipher, or this - * cipher is being initialized for decryption or unwrapping keys and - * requires algorithm parameters that cannot be determined from the + * cipher requires algorithm parameters that cannot be determined from the * public key in the given certificate, or the keysize of the public key * in the given certificate has a keysize that exceeds the maximum * allowable keysize (as determined by the configured jurisdiction policy @@ -1518,6 +1579,11 @@ public class Cipher { * {@link #getParameters() getParameters} or * {@link #getIV() getIV} (if the parameter is an IV). * + *
If this cipher requires algorithm parameters that cannot be + * derived from the input parameters, and there are no reasonable + * provider-specific default values, initialization will + * necessarily fail. + * *
If this cipher (including its underlying feedback or padding scheme)
* requires any random bytes (e.g., for parameter generation), it will get
* them from random.
@@ -1536,7 +1602,7 @@ public class Cipher {
*
* @exception InvalidKeyException if the public key in the given
* certificate is inappropriate for initializing this cipher, or this
- * cipher is being initialized for decryption or unwrapping keys and
+ * cipher
* requires algorithm parameters that cannot be determined from the
* public key in the given certificate, or the keysize of the public key
* in the given certificate has a keysize that exceeds the maximum
@@ -1865,6 +1931,9 @@ public class Cipher {
*
Input data that may have been buffered during a previous
* update operation is processed, with padding (if requested)
* being applied.
+ * If an AEAD mode such as GCM/CCM is being used, the authentication
+ * tag is appended in the case of encryption, or verified in the
+ * case of decryption.
* The result is stored in a new buffer.
*
*
Upon finishing, this method resets this cipher object to the state @@ -1888,6 +1957,9 @@ public class Cipher { * @exception BadPaddingException if this cipher is in decryption mode, * and (un)padding has been requested, but the decrypted data is not * bounded by the appropriate padding bytes + * @exception AEADBadTagException if this cipher is decrypting in an + * AEAD mode (such as GCM/CCM), and the received authentication tag + * does not match the calculated value */ public final byte[] doFinal() throws IllegalBlockSizeException, BadPaddingException { @@ -1904,6 +1976,9 @@ public class Cipher { *
Input data that may have been buffered during a previous
* update operation is processed, with padding (if requested)
* being applied.
+ * If an AEAD mode such as GCM/CCM is being used, the authentication
+ * tag is appended in the case of encryption, or verified in the
+ * case of decryption.
* The result is stored in the output buffer, starting at
* outputOffset inclusive.
*
@@ -1940,6 +2015,9 @@ public class Cipher {
* @exception BadPaddingException if this cipher is in decryption mode,
* and (un)padding has been requested, but the decrypted data is not
* bounded by the appropriate padding bytes
+ * @exception AEADBadTagException if this cipher is decrypting in an
+ * AEAD mode (such as GCM/CCM), and the received authentication tag
+ * does not match the calculated value
*/
public final int doFinal(byte[] output, int outputOffset)
throws IllegalBlockSizeException, ShortBufferException,
@@ -1963,6 +2041,9 @@ public class Cipher {
*
The bytes in the input buffer, and any input bytes that
* may have been buffered during a previous update operation,
* are processed, with padding (if requested) being applied.
+ * If an AEAD mode such as GCM/CCM is being used, the authentication
+ * tag is appended in the case of encryption, or verified in the
+ * case of decryption.
* The result is stored in a new buffer.
*
*
Upon finishing, this method resets this cipher object to the state
@@ -1988,6 +2069,9 @@ public class Cipher {
* @exception BadPaddingException if this cipher is in decryption mode,
* and (un)padding has been requested, but the decrypted data is not
* bounded by the appropriate padding bytes
+ * @exception AEADBadTagException if this cipher is decrypting in an
+ * AEAD mode (such as GCM/CCM), and the received authentication tag
+ * does not match the calculated value
*/
public final byte[] doFinal(byte[] input)
throws IllegalBlockSizeException, BadPaddingException {
@@ -2011,6 +2095,9 @@ public class Cipher {
* buffer, starting at inputOffset inclusive, and any input
* bytes that may have been buffered during a previous update
* operation, are processed, with padding (if requested) being applied.
+ * If an AEAD mode such as GCM/CCM is being used, the authentication
+ * tag is appended in the case of encryption, or verified in the
+ * case of decryption.
* The result is stored in a new buffer.
*
*
Upon finishing, this method resets this cipher object to the state
@@ -2039,6 +2126,9 @@ public class Cipher {
* @exception BadPaddingException if this cipher is in decryption mode,
* and (un)padding has been requested, but the decrypted data is not
* bounded by the appropriate padding bytes
+ * @exception AEADBadTagException if this cipher is decrypting in an
+ * AEAD mode (such as GCM/CCM), and the received authentication tag
+ * does not match the calculated value
*/
public final byte[] doFinal(byte[] input, int inputOffset, int inputLen)
throws IllegalBlockSizeException, BadPaddingException {
@@ -2063,6 +2153,9 @@ public class Cipher {
* buffer, starting at inputOffset inclusive, and any input
* bytes that may have been buffered during a previous update
* operation, are processed, with padding (if requested) being applied.
+ * If an AEAD mode such as GCM/CCM is being used, the authentication
+ * tag is appended in the case of encryption, or verified in the
+ * case of decryption.
* The result is stored in the output buffer.
*
*
If the output buffer is too small to hold the result,
@@ -2105,6 +2198,9 @@ public class Cipher {
* @exception BadPaddingException if this cipher is in decryption mode,
* and (un)padding has been requested, but the decrypted data is not
* bounded by the appropriate padding bytes
+ * @exception AEADBadTagException if this cipher is decrypting in an
+ * AEAD mode (such as GCM/CCM), and the received authentication tag
+ * does not match the calculated value
*/
public final int doFinal(byte[] input, int inputOffset, int inputLen,
byte[] output)
@@ -2133,6 +2229,9 @@ public class Cipher {
* bytes that may have been buffered during a previous
* update operation, are processed, with padding
* (if requested) being applied.
+ * If an AEAD mode such as GCM/CCM is being used, the authentication
+ * tag is appended in the case of encryption, or verified in the
+ * case of decryption.
* The result is stored in the output buffer, starting at
* outputOffset inclusive.
*
@@ -2178,6 +2277,9 @@ public class Cipher {
* @exception BadPaddingException if this cipher is in decryption mode,
* and (un)padding has been requested, but the decrypted data is not
* bounded by the appropriate padding bytes
+ * @exception AEADBadTagException if this cipher is decrypting in an
+ * AEAD mode (such as GCM/CCM), and the received authentication tag
+ * does not match the calculated value
*/
public final int doFinal(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset)
@@ -2203,8 +2305,11 @@ public class Cipher {
* depending on how this cipher was initialized.
*
*
All input.remaining() bytes starting at
- * input.position() are processed. The result is stored
- * in the output buffer.
+ * input.position() are processed.
+ * If an AEAD mode such as GCM/CCM is being used, the authentication
+ * tag is appended in the case of encryption, or verified in the
+ * case of decryption.
+ * The result is stored in the output buffer.
* Upon return, the input buffer's position will be equal
* to its limit; its limit will not have changed. The output buffer's
* position will have advanced by n, where n is the value returned
@@ -2250,6 +2355,10 @@ public class Cipher {
* @exception BadPaddingException if this cipher is in decryption mode,
* and (un)padding has been requested, but the decrypted data is not
* bounded by the appropriate padding bytes
+ * @exception AEADBadTagException if this cipher is decrypting in an
+ * AEAD mode (such as GCM/CCM), and the received authentication tag
+ * does not match the calculated value
+ *
* @since 1.5
*/
public final int doFinal(ByteBuffer input, ByteBuffer output)
@@ -2441,4 +2550,128 @@ public class Cipher {
CryptoPermission cp = getConfiguredPermission(transformation);
return cp.getAlgorithmParameterSpec();
}
+
+ /**
+ * Continues a multi-part update of the Additional Authentication
+ * Data (AAD).
+ *
+ * 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 Additional Authentication Data + * + * @throws IllegalArgumentException if the {@code src} + * byte array is null + * @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 the corresponding method + * in the {@code CipherSpi} has not been overridden by an + * implementation + * + * @since 1.7 + */ + public final void updateAAD(byte[] src) { + if (src == null) { + throw new IllegalArgumentException("src buffer is null"); + } + + updateAAD(src, 0, src.length); + } + + /** + * Continues a multi-part update of the Additional Authentication + * Data (AAD), using a subset of the provided buffer. + *
+ * 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 IllegalArgumentException if the {@code src} + * byte array is null, or the {@code offset} or {@code length} + * is less than 0, or the sum of the {@code offset} and + * {@code len} is greater than the length of the + * {@code src} byte array + * @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 the corresponding method + * in the {@code CipherSpi} has not been overridden by an + * implementation + * + * @since 1.7 + */ + public final void updateAAD(byte[] src, int offset, int len) { + checkCipherState(); + + // Input sanity check + if ((src == null) || (offset < 0) || (len < 0) + || ((len + offset) > src.length)) { + throw new IllegalArgumentException("Bad arguments"); + } + + chooseFirstProvider(); + if (len == 0) { + return; + } + spi.engineUpdateAAD(src, offset, len); + } + + /** + * Continues a multi-part update of the Additional Authentication + * Data (AAD). + *
+ * 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). + *
+ * 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 IllegalArgumentException if the {@code src ByteBuffer}
+ * is null
+ * @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 the corresponding method
+ * in the {@code CipherSpi} has not been overridden by an
+ * implementation
+ *
+ * @since 1.7
+ */
+ public final void updateAAD(ByteBuffer src) {
+ checkCipherState();
+
+ // Input sanity check
+ if (src == null) {
+ throw new IllegalArgumentException("src ByteBuffer is null");
+ }
+
+ chooseFirstProvider();
+ if (src.remaining() == 0) {
+ return;
+ }
+ spi.engineUpdateAAD(src);
+ }
}
diff --git a/jdk/src/share/classes/javax/crypto/CipherSpi.java b/jdk/src/share/classes/javax/crypto/CipherSpi.java
index f46cafa955d..c3442ea2d81 100644
--- a/jdk/src/share/classes/javax/crypto/CipherSpi.java
+++ b/jdk/src/share/classes/javax/crypto/CipherSpi.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2011, 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
@@ -266,7 +266,7 @@ public abstract class CipherSpi {
* inputLen (in bytes).
*
*
This call takes into account any unprocessed (buffered) data from a
- * previous update call, and padding.
+ * previous update call, padding, and AEAD tagging.
*
*
The actual output length of the next update or
* doFinal call may be smaller than the length returned by
@@ -322,6 +322,11 @@ public abstract class CipherSpi {
* {@link #engineGetParameters() engineGetParameters} or
* {@link #engineGetIV() engineGetIV} (if the parameter is an IV).
*
+ *
If this cipher requires algorithm parameters that cannot be + * derived from the input parameters, and there are no reasonable + * provider-specific default values, initialization will + * necessarily fail. + * *
If this cipher (including its underlying feedback or padding scheme)
* requires any random bytes (e.g., for parameter generation), it will get
* them from random.
@@ -339,8 +344,8 @@ public abstract class CipherSpi {
* @param random the source of randomness
*
* @exception InvalidKeyException if the given key is inappropriate for
- * initializing this cipher, or if this cipher is being initialized for
- * decryption and requires algorithm parameters that cannot be
+ * initializing this cipher, or requires
+ * algorithm parameters that cannot be
* determined from the given key.
*/
protected abstract void engineInit(int opmode, Key key,
@@ -366,6 +371,11 @@ public abstract class CipherSpi {
* {@link #engineGetParameters() engineGetParameters} or
* {@link #engineGetIV() engineGetIV} (if the parameter is an IV).
*
+ *
If this cipher requires algorithm parameters that cannot be + * derived from the input parameters, and there are no reasonable + * provider-specific default values, initialization will + * necessarily fail. + * *
If this cipher (including its underlying feedback or padding scheme)
* requires any random bytes (e.g., for parameter generation), it will get
* them from random.
@@ -387,7 +397,7 @@ public abstract class CipherSpi {
* initializing this cipher
* @exception InvalidAlgorithmParameterException if the given algorithm
* parameters are inappropriate for this cipher,
- * or if this cipher is being initialized for decryption and requires
+ * or if this cipher requires
* algorithm parameters and params is null.
*/
protected abstract void engineInit(int opmode, Key key,
@@ -414,6 +424,11 @@ public abstract class CipherSpi {
* {@link #engineGetParameters() engineGetParameters} or
* {@link #engineGetIV() engineGetIV} (if the parameter is an IV).
*
+ *
If this cipher requires algorithm parameters that cannot be + * derived from the input parameters, and there are no reasonable + * provider-specific default values, initialization will + * necessarily fail. + * *
If this cipher (including its underlying feedback or padding scheme)
* requires any random bytes (e.g., for parameter generation), it will get
* them from random.
@@ -435,7 +450,7 @@ public abstract class CipherSpi {
* initializing this cipher
* @exception InvalidAlgorithmParameterException if the given algorithm
* parameters are inappropriate for this cipher,
- * or if this cipher is being initialized for decryption and requires
+ * or if this cipher requires
* algorithm parameters and params is null.
*/
protected abstract void engineInit(int opmode, Key key,
@@ -548,6 +563,9 @@ public abstract class CipherSpi {
* buffer, starting at inputOffset inclusive, and any input
* bytes that may have been buffered during a previous update
* operation, are processed, with padding (if requested) being applied.
+ * If an AEAD mode such as GCM/CCM is being used, the authentication
+ * tag is appended in the case of encryption, or verified in the
+ * case of decryption.
* The result is stored in a new buffer.
*
*
Upon finishing, this method resets this cipher object to the state
@@ -575,6 +593,9 @@ public abstract class CipherSpi {
* @exception BadPaddingException if this cipher is in decryption mode,
* and (un)padding has been requested, but the decrypted data is not
* bounded by the appropriate padding bytes
+ * @exception AEADBadTagException if this cipher is decrypting in an
+ * AEAD mode (such as GCM/CCM), and the received authentication tag
+ * does not match the calculated value
*/
protected abstract byte[] engineDoFinal(byte[] input, int inputOffset,
int inputLen)
@@ -590,6 +611,9 @@ public abstract class CipherSpi {
* buffer, starting at inputOffset inclusive, and any input
* bytes that may have been buffered during a previous update
* operation, are processed, with padding (if requested) being applied.
+ * If an AEAD mode such as GCM/CCM is being used, the authentication
+ * tag is appended in the case of encryption, or verified in the
+ * case of decryption.
* The result is stored in the output buffer, starting at
* outputOffset inclusive.
*
@@ -626,6 +650,9 @@ public abstract class CipherSpi {
* @exception BadPaddingException if this cipher is in decryption mode,
* and (un)padding has been requested, but the decrypted data is not
* bounded by the appropriate padding bytes
+ * @exception AEADBadTagException if this cipher is decrypting in an
+ * AEAD mode (such as GCM/CCM), and the received authentication tag
+ * does not match the calculated value
*/
protected abstract int engineDoFinal(byte[] input, int inputOffset,
int inputLen, byte[] output,
@@ -640,8 +667,11 @@ public abstract class CipherSpi {
* initialized.
*
*
All input.remaining() bytes starting at
- * input.position() are processed. The result is stored
- * in the output buffer.
+ * input.position() are processed.
+ * If an AEAD mode such as GCM/CCM is being used, the authentication
+ * tag is appended in the case of encryption, or verified in the
+ * case of decryption.
+ * The result is stored in the output buffer.
* Upon return, the input buffer's position will be equal
* to its limit; its limit will not have changed. The output buffer's
* position will have advanced by n, where n is the value returned
@@ -678,6 +708,9 @@ public abstract class CipherSpi {
* @exception BadPaddingException if this cipher is in decryption mode,
* and (un)padding has been requested, but the decrypted data is not
* bounded by the appropriate padding bytes
+ * @exception AEADBadTagException if this cipher is decrypting in an
+ * AEAD mode (such as GCM/CCM), and the received authentication tag
+ * does not match the calculated value
*
* @throws NullPointerException if either parameter is null
* @since 1.5
@@ -892,4 +925,67 @@ public abstract class CipherSpi {
{
throw new UnsupportedOperationException();
}
+
+ /**
+ * Continues a multi-part update of the Additional Authentication
+ * Data (AAD), using a subset of the provided buffer.
+ *
+ * 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.7 + */ + protected void engineUpdateAAD(byte[] src, int offset, int len) { + throw new UnsupportedOperationException( + "The underlying Cipher implementation " + + "does not support this method"); + } + + /** + * Continues a multi-part update of the Additional Authentication + * Data (AAD). + *
+ * 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). + *
+ * 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.7 + */ + protected void engineUpdateAAD(ByteBuffer src) { + throw new UnsupportedOperationException( + "The underlying Cipher implementation " + + "does not support this method"); + } } diff --git a/jdk/src/share/classes/javax/crypto/spec/GCMParameterSpec.java b/jdk/src/share/classes/javax/crypto/spec/GCMParameterSpec.java new file mode 100644 index 00000000000..01cb1cc7028 --- /dev/null +++ b/jdk/src/share/classes/javax/crypto/spec/GCMParameterSpec.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2011, 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 javax.crypto.spec; + +import java.security.spec.AlgorithmParameterSpec; + +/** + * Specifies the set of parameters required by a {@link + * javax.crypto.Cipher} using the Galois/Counter Mode (GCM) mode. + *
+ * Simple block cipher modes (such as CBC) generally require only an + * initialization vector (such as {@code IvParameterSpec}), + * but GCM needs these parameters: + *
+ * In addition to the parameters described here, other GCM inputs/output + * (Additional Authenticated Data (AAD), Keys, block ciphers, + * plain/ciphertext and authentication tags) are handled in the {@code + * Cipher} class. +
+ * Please see RFC 5116 + * for more information on the Authenticated Encryption with + * Associated Data (AEAD) algorithm, and + * NIST Special Publication 800-38D, "NIST Recommendation for Block + * Cipher Modes of Operation: Galois/Counter Mode (GCM) and GMAC." + *
+ * The GCM specification states that {@code tLen} may only have the + * values {128, 120, 112, 104, 96}, or {64, 32} for certain + * applications. Other values can be specified for this class, but not + * all CSP implementations will support them. + * + * @see javax.crypto.Cipher + * + * @since 1.7 + */ +public class GCMParameterSpec implements AlgorithmParameterSpec { + + // Initialization Vector. Could use IvParameterSpec, but that + // would add extra copies. + private byte[] iv; + + // Required Tag length (in bits). + private int tLen; + + /** + * Constructs a GCMParameterSpec using the specified authentication + * tag bit-length and IV buffer. + * + * @param tLen the authentication tag length (in bits) + * @param src the IV source buffer. The contents of the buffer are + * copied to protect against subsequent modification. + * + * @throws IllegalArgumentException if {@code tLen} is negative, + * or {@code src} is null. + */ + public GCMParameterSpec(int tLen, byte[] src) { + if (src == null) { + throw new IllegalArgumentException("src array is null"); + } + + init(tLen, src, 0, src.length); + } + + /** + * Constructs a GCMParameterSpec object using the specified + * authentication tag bit-length and a subset of the specified + * buffer as the IV. + * + * @param tLen the authentication tag length (in bits) + * @param src the IV source buffer. The contents of the + * buffer are copied to protect against subsequent modification. + * @param offset the offset in {@code src} where the IV starts + * @param len the number of IV bytes + * + * @throws IllegalArgumentException if {@code tLen} is negative, + * {@code src} is null, {@code len} or {@code offset} is negative, + * or the sum of {@code offset} and {@code len} is greater than the + * length of the {@code src} byte array. + */ + public GCMParameterSpec(int tLen, byte[] src, int offset, int len) { + init(tLen, src, offset, len); + } + + /* + * Check input parameters. + */ + private void init(int tLen, byte[] src, int offset, int len) { + if (tLen < 0) { + throw new IllegalArgumentException( + "Length argument is negative"); + } + this.tLen = tLen; + + // Input sanity check + if ((src == null) ||(len < 0) || (offset < 0) + || ((len + offset) > src.length)) { + throw new IllegalArgumentException("Invalid buffer arguments"); + } + + iv = new byte[len]; + System.arraycopy(src, offset, iv, 0, len); + } + + /** + * Returns the authentication tag length. + * + * @return the authentication tag length (in bits) + */ + public int getTLen() { + return tLen; + } + + /** + * Returns the Initialization Vector (IV). + * + * @return the IV. Creates a new array each time this method + * is called. + */ + public byte[] getIV() { + return iv.clone(); + } +} diff --git a/jdk/test/javax/crypto/Cipher/GCMAPI.java b/jdk/test/javax/crypto/Cipher/GCMAPI.java new file mode 100644 index 00000000000..1ccb230d328 --- /dev/null +++ b/jdk/test/javax/crypto/Cipher/GCMAPI.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2011, 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 7031343 + * @summary Provide API changes to support GCM AEAD ciphers + * @author Brad Wetmore + */ + +import javax.crypto.*; +import javax.crypto.spec.*; +import java.nio.ByteBuffer; + +/* + * At this point in time, we can't really do any testing since only the API + * is available, the underlying implementation doesn't exist yet. Test + * what we can... + */ +public class GCMAPI { + + // 16 elements + private static byte[] bytes = new byte[] { + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f }; + + private static int failed = 0; + private static Cipher c; + + public static void main(String[] args) throws Exception { + c = Cipher.getInstance("AES"); + c.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(new byte[16], "AES")); + + updateAADFail((byte[]) null); + updateAADPass(bytes); + + updateAADFail(null, 2, 4); + updateAADFail(bytes, -2, 4); + updateAADFail(bytes, 2, -4); + updateAADFail(bytes, 2, 15); // one too many + + updateAADPass(bytes, 2, 14); // ok. + updateAADPass(bytes, 4, 4); + updateAADPass(bytes, 0, 0); + + ByteBuffer bb = ByteBuffer.wrap(bytes); + + updateAADFail((ByteBuffer) null); + updateAADPass(bb); + + if (failed != 0) { + throw new Exception("Test(s) failed"); + } + } + + private static void updateAADPass(byte[] src) { + try { + c.updateAAD(src); + } catch (UnsupportedOperationException e) { + // swallow + }catch (Exception e) { + e.printStackTrace(); + failed++; + } + } + + private static void updateAADFail(byte[] src) { + try { + c.updateAAD(src); + new Exception("Didn't Fail as Expected").printStackTrace(); + failed++; + } catch (IllegalArgumentException e) { + // swallow + } + } + + private static void updateAADPass(byte[] src, int offset, int len) { + try { + c.updateAAD(src, offset, len); + } catch (UnsupportedOperationException e) { + // swallow + } catch (Exception e) { + e.printStackTrace(); + failed++; + } + } + + private static void updateAADFail(byte[] src, int offset, int len) { + try { + c.updateAAD(src, offset, len); + new Exception("Didn't Fail as Expected").printStackTrace(); + failed++; + } catch (IllegalArgumentException e) { + // swallow + } + } + + private static void updateAADPass(ByteBuffer src) { + try { + c.updateAAD(src); + } catch (UnsupportedOperationException e) { + // swallow + }catch (Exception e) { + e.printStackTrace(); + failed++; + } + } + + private static void updateAADFail(ByteBuffer src) { + try { + c.updateAAD(src); + new Exception("Didn't Fail as Expected").printStackTrace(); + failed++; + } catch (IllegalArgumentException e) { + // swallow + } + } +} diff --git a/jdk/test/javax/crypto/spec/GCMParameterSpec/GCMParameterSpecTest.java b/jdk/test/javax/crypto/spec/GCMParameterSpec/GCMParameterSpecTest.java new file mode 100644 index 00000000000..3991c8f29a0 --- /dev/null +++ b/jdk/test/javax/crypto/spec/GCMParameterSpec/GCMParameterSpecTest.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2011, 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 7031343 + * @summary Provide API changes to support GCM AEAD ciphers + * @author Brad Wetmore + */ + +import javax.crypto.AEADBadTagException; +import javax.crypto.spec.GCMParameterSpec; +import java.util.Arrays; + +public class GCMParameterSpecTest { + + // 16 elements + private static byte[] bytes = new byte[] { + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f }; + + private static int failed = 0; + + public static void main(String[] args) throws Exception { + newGCMParameterSpecFail(-1, bytes); + newGCMParameterSpecFail(128, null); + newGCMParameterSpecPass(128, bytes); + + newGCMParameterSpecFail(-1, bytes, 2, 4); + newGCMParameterSpecFail(128, null, 2, 4); + newGCMParameterSpecFail(128, bytes, -2, 4); + newGCMParameterSpecFail(128, bytes, 2, -4); + newGCMParameterSpecFail(128, bytes, 2, 15); // one too many + + newGCMParameterSpecPass(128, bytes, 2, 14); // ok. + newGCMParameterSpecPass(96, bytes, 4, 4); + newGCMParameterSpecPass(96, bytes, 0, 0); + + // Might as well check the Exception constructors. + try { + new AEADBadTagException(); + new AEADBadTagException("Bad Tag Seen"); + } catch (Exception e) { + e.printStackTrace(); + failed++; + } + + if (failed != 0) { + throw new Exception("Test(s) failed"); + } + } + + private static void newGCMParameterSpecPass( + int tLen, byte[] src) { + try { + GCMParameterSpec gcmps = new GCMParameterSpec(tLen, src); + if (gcmps.getTLen() != tLen) { + throw new Exception("tLen's not equal"); + } + if (!Arrays.equals(gcmps.getIV(), src)) { + throw new Exception("IV's not equal"); + } + } catch (Exception e) { + e.printStackTrace(); + failed++; + } + } + + private static void newGCMParameterSpecFail( + int tLen, byte[] src) { + try { + new GCMParameterSpec(tLen, src); + new Exception("Didn't Fail as Expected").printStackTrace(); + failed++; + } catch (IllegalArgumentException e) { + // swallow + } + } + + private static void newGCMParameterSpecPass( + int tLen, byte[] src, int offset, int len) { + try { + GCMParameterSpec gcmps = + new GCMParameterSpec(tLen, src, offset, len); + if (gcmps.getTLen() != tLen) { + throw new Exception("tLen's not equal"); + } + if (!Arrays.equals(gcmps.getIV(), + Arrays.copyOfRange(src, offset, offset + len))) { + System.out.println(offset + " " + len); + System.out.println(Arrays.copyOfRange(src, offset, len)[0]); + throw new Exception("IV's not equal"); + } + } catch (Exception e) { + e.printStackTrace(); + failed++; + } + } + + private static void newGCMParameterSpecFail( + int tLen, byte[] src, int offset, int len) { + try { + new GCMParameterSpec(tLen, src, offset, len); + new Exception("Didn't Fail as Expected").printStackTrace(); + failed++; + } catch (IllegalArgumentException e) { + // swallow + } + } +}