提交 de5cc3f6 编写于 作者: V valeriep

6996769: support AEAD cipher

Summary: Added implementation for GCM mode under AES cipher
Reviewed-by: weijun
上级 4e886604
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -30,6 +30,7 @@ import java.security.spec.*; ...@@ -30,6 +30,7 @@ import java.security.spec.*;
import javax.crypto.*; import javax.crypto.*;
import javax.crypto.spec.*; import javax.crypto.spec.*;
import javax.crypto.BadPaddingException; import javax.crypto.BadPaddingException;
import java.nio.ByteBuffer;
/** /**
* This class implements the AES algorithm in its various modes * This class implements the AES algorithm in its various modes
...@@ -127,6 +128,21 @@ abstract class AESCipher extends CipherSpi { ...@@ -127,6 +128,21 @@ abstract class AESCipher extends CipherSpi {
super(32, "CFB", "NOPADDING"); 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 // utility method used by AESCipher and AESWrapCipher
static final void checkKeySize(Key key, int fixedKeySize) static final void checkKeySize(Key key, int fixedKeySize)
...@@ -531,4 +547,79 @@ abstract class AESCipher extends CipherSpi { ...@@ -531,4 +547,79 @@ abstract class AESCipher extends CipherSpi {
return core.unwrap(wrappedKey, wrappedKeyAlgorithm, return core.unwrap(wrappedKey, wrappedKeyAlgorithm,
wrappedKeyType); 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);
}
}
}
}
} }
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -83,9 +83,10 @@ final class CipherTextStealing extends CipherBlockChaining { ...@@ -83,9 +83,10 @@ final class CipherTextStealing extends CipherBlockChaining {
* @param plainLen the length of the input data * @param plainLen the length of the input data
* @param cipher the buffer for the result * @param cipher the buffer for the result
* @param cipherOffset the offset in <code>cipher</code> * @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, int encryptFinal(byte[] plain, int plainOffset, int plainLen,
byte[] cipher, int cipherOffset) byte[] cipher, int cipherOffset)
throws IllegalBlockSizeException { throws IllegalBlockSizeException {
if (plainLen < blockSize) { if (plainLen < blockSize) {
...@@ -134,6 +135,7 @@ final class CipherTextStealing extends CipherBlockChaining { ...@@ -134,6 +135,7 @@ final class CipherTextStealing extends CipherBlockChaining {
embeddedCipher.encryptBlock(tmp2, 0, cipher, cipherOffset); embeddedCipher.encryptBlock(tmp2, 0, cipher, cipherOffset);
} }
} }
return plainLen;
} }
/** /**
...@@ -158,9 +160,10 @@ final class CipherTextStealing extends CipherBlockChaining { ...@@ -158,9 +160,10 @@ final class CipherTextStealing extends CipherBlockChaining {
* @param cipherLen the length of the input data * @param cipherLen the length of the input data
* @param plain the buffer for the result * @param plain the buffer for the result
* @param plainOffset the offset in <code>plain</code> * @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, int decryptFinal(byte[] cipher, int cipherOffset, int cipherLen,
byte[] plain, int plainOffset) byte[] plain, int plainOffset)
throws IllegalBlockSizeException { throws IllegalBlockSizeException {
if (cipherLen < blockSize) { if (cipherLen < blockSize) {
throw new IllegalBlockSizeException("input is too short!"); throw new IllegalBlockSizeException("input is too short!");
...@@ -211,5 +214,6 @@ final class CipherTextStealing extends CipherBlockChaining { ...@@ -211,5 +214,6 @@ final class CipherTextStealing extends CipherBlockChaining {
} }
} }
} }
return cipherLen;
} }
} }
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
package com.sun.crypto.provider; package com.sun.crypto.provider;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import javax.crypto.IllegalBlockSizeException; import javax.crypto.*;
/** /**
* This class represents a block cipher in one of its modes. It wraps * This class represents a block cipher in one of its modes. It wraps
...@@ -150,11 +150,13 @@ abstract class FeedbackCipher { ...@@ -150,11 +150,13 @@ abstract class FeedbackCipher {
* @param plainLen the length of the input data * @param plainLen the length of the input data
* @param cipher the buffer for the encryption result * @param cipher the buffer for the encryption result
* @param cipherOffset the offset in <code>cipher</code> * @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, int encryptFinal(byte[] plain, int plainOffset, int plainLen,
byte[] cipher, int cipherOffset) byte[] cipher, int cipherOffset)
throws IllegalBlockSizeException { throws IllegalBlockSizeException {
encrypt(plain, plainOffset, plainLen, cipher, cipherOffset); encrypt(plain, plainOffset, plainLen, cipher, cipherOffset);
return plainLen;
} }
/** /**
* Performs decryption operation. * Performs decryption operation.
...@@ -190,10 +192,40 @@ abstract class FeedbackCipher { ...@@ -190,10 +192,40 @@ abstract class FeedbackCipher {
* @param cipherLen the length of the input data * @param cipherLen the length of the input data
* @param plain the buffer for the decryption result * @param plain the buffer for the decryption result
* @param plainOffset the offset in <code>plain</code> * @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, int decryptFinal(byte[] cipher, int cipherOffset, int cipherLen,
byte[] plain, int plainOffset) byte[] plain, int plainOffset)
throws IllegalBlockSizeException { throws IllegalBlockSizeException, AEADBadTagException {
decrypt(cipher, cipherOffset, cipherLen, plain, plainOffset); 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");
}
} }
/*
* 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();
}
}
/*
* 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;
}
}
/*
* 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();
}
}
}
/*
* 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;
}
}
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -57,6 +57,7 @@ import java.security.SecureRandom; ...@@ -57,6 +57,7 @@ import java.security.SecureRandom;
* - ARCFOUR (RC4 compatible) * - ARCFOUR (RC4 compatible)
* *
* - Cipher modes ECB, CBC, CFB, OFB, PCBC, CTR, and CTS for all block ciphers * - 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 * - Cipher padding ISO10126Padding for non-PKCS#5 block ciphers and
* NoPadding and PKCS5Padding for all block ciphers * NoPadding and PKCS5Padding for all block ciphers
...@@ -100,7 +101,7 @@ public final class SunJCE extends Provider { ...@@ -100,7 +101,7 @@ public final class SunJCE extends Provider {
"|CFB8|CFB16|CFB24|CFB32|CFB40|CFB48|CFB56|CFB64" + "|CFB8|CFB16|CFB24|CFB32|CFB40|CFB48|CFB56|CFB64" +
"|OFB8|OFB16|OFB24|OFB32|OFB40|OFB48|OFB56|OFB64"; "|OFB8|OFB16|OFB24|OFB32|OFB40|OFB48|OFB56|OFB64";
final String BLOCK_MODES128 = BLOCK_MODES + 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"; "|OFB72|OFB80|OFB88|OFB96|OFB104|OFB112|OFB120|OFB128";
final String BLOCK_PADS = "NOPADDING|PKCS5PADDING|ISO10126PADDING"; final String BLOCK_PADS = "NOPADDING|PKCS5PADDING|ISO10126PADDING";
...@@ -258,6 +259,9 @@ public final class SunJCE extends Provider { ...@@ -258,6 +259,9 @@ public final class SunJCE extends Provider {
put("Cipher.AES_128/CFB/NoPadding", "com.sun.crypto.provider.AESCipher$AES128_CFB_NoPadding"); 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.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("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("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"); put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.21", "AES_192/ECB/NoPadding");
...@@ -271,7 +275,9 @@ public final class SunJCE extends Provider { ...@@ -271,7 +275,9 @@ public final class SunJCE extends Provider {
put("Cipher.AES_192/CFB/NoPadding", "com.sun.crypto.provider.AESCipher$AES192_CFB_NoPadding"); 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.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("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("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"); put("Alg.Alias.Cipher.2.16.840.1.101.3.4.1.41", "AES_256/ECB/NoPadding");
...@@ -285,6 +291,9 @@ public final class SunJCE extends Provider { ...@@ -285,6 +291,9 @@ public final class SunJCE extends Provider {
put("Cipher.AES_256/CFB/NoPadding", "com.sun.crypto.provider.AESCipher$AES256_CFB_NoPadding"); 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.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("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", "com.sun.crypto.provider.AESWrapCipher$General");
put("Cipher.AESWrap SupportedModes", "ECB"); put("Cipher.AESWrap SupportedModes", "ECB");
...@@ -509,6 +518,8 @@ public final class SunJCE extends Provider { ...@@ -509,6 +518,8 @@ public final class SunJCE extends Provider {
put("AlgorithmParameters.AES", put("AlgorithmParameters.AES",
"com.sun.crypto.provider.AESParameters"); "com.sun.crypto.provider.AESParameters");
put("Alg.Alias.AlgorithmParameters.Rijndael", "AES"); put("Alg.Alias.AlgorithmParameters.Rijndael", "AES");
put("AlgorithmParameters.GCM",
"com.sun.crypto.provider.GCMParameters");
put("AlgorithmParameters.RC2", put("AlgorithmParameters.RC2",
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -104,17 +104,30 @@ import sun.security.jca.*; ...@@ -104,17 +104,30 @@ import sun.security.jca.*;
* must be supplied to GCM/CCM implementations (via the {@code * must be supplied to GCM/CCM implementations (via the {@code
* updateAAD} methods) <b>before</b> the ciphertext is processed (via * updateAAD} methods) <b>before</b> the ciphertext is processed (via
* the {@code update} and {@code doFinal} methods). * 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> * <pre>
* GCMParameterSpec s = new GCMParameterSpec(...); * GCMParameterSpec s = ...;
* cipher.init(..., s); * cipher.init(..., s);
* *
* // If the GCMParameterSpec is needed again * // If the GCM parameters were generated by the provider, it can
* cipher.getParameters().getParameterSpec(GCMParameterSpec.class)); * // be retrieved by:
* // cipher.getParameters().getParameterSpec(GCMParameterSpec.class);
* *
* cipher.updateAAD(...); // AAD * cipher.updateAAD(...); // AAD
* cipher.update(...); // Multi-part update * cipher.update(...); // Multi-part update
* cipher.doFinal(...); // conclusion of operation * 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> * </pre>
* Every implementation of the Java platform is required to support * Every implementation of the Java platform is required to support
* the following standard <code>Cipher</code> transformations with the keysizes * the following standard <code>Cipher</code> transformations with the keysizes
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -43,7 +43,7 @@ import java.security.spec.AlgorithmParameterSpec; ...@@ -43,7 +43,7 @@ import java.security.spec.AlgorithmParameterSpec;
* (Additional Authenticated Data (AAD), Keys, block ciphers, * (Additional Authenticated Data (AAD), Keys, block ciphers,
* plain/ciphertext and authentication tags) are handled in the {@code * plain/ciphertext and authentication tags) are handled in the {@code
* Cipher} class. * Cipher} class.
<p> * <p>
* Please see <a href="http://www.ietf.org/rfc/rfc5116.txt"> RFC 5116 * Please see <a href="http://www.ietf.org/rfc/rfc5116.txt"> RFC 5116
* </a> for more information on the Authenticated Encryption with * </a> for more information on the Authenticated Encryption with
* Associated Data (AEAD) algorithm, and <a href= * Associated Data (AEAD) algorithm, and <a href=
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -40,13 +40,13 @@ import com.sun.crypto.provider.*; ...@@ -40,13 +40,13 @@ import com.sun.crypto.provider.*;
public class Test4512524 { public class Test4512524 {
private static final String ALGO = "AES"; private static final String ALGO = "AES";
private static final String MODE = "CBC";
private static final String PADDING = "NoPadding"; private static final String PADDING = "NoPadding";
private static final int KEYSIZE = 16; // in bytes 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 // TEST FIX 4512524
KeyGenerator kg = KeyGenerator.getInstance(ALGO, "SunJCE"); KeyGenerator kg = KeyGenerator.getInstance(ALGO, "SunJCE");
...@@ -61,17 +61,14 @@ public class Test4512524 { ...@@ -61,17 +61,14 @@ public class Test4512524 {
} }
// passed all tests...hooray! // passed all tests...hooray!
return true; System.out.println(transformation + ": Passed");
} }
public static void main (String[] args) throws Exception { public static void main (String[] args) throws Exception {
Security.addProvider(new com.sun.crypto.provider.SunJCE()); Security.addProvider(new com.sun.crypto.provider.SunJCE());
Test4512524 test = new Test4512524(); Test4512524 test = new Test4512524();
String testName = test.getClass().getName() + "[" + ALGO + test.execute("CBC");
"/" + MODE + "/" + PADDING + "]"; test.execute("GCM");
if (test.execute()) {
System.out.println(testName + ": Passed!");
}
} }
} }
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -39,14 +39,14 @@ import com.sun.crypto.provider.*; ...@@ -39,14 +39,14 @@ import com.sun.crypto.provider.*;
public class Test4512704 { public class Test4512704 {
private static final String ALGO = "AES"; private static final String ALGO = "AES";
private static final String MODE = "CBC"; private static final String PADDING = "NoPadding";
private static final String PADDING = "PKCS5Padding";
private static final int KEYSIZE = 16; // in bytes 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"); AlgorithmParameterSpec aps = null;
String transformation = ALGO + "/" + mode + "/"+PADDING;
Cipher ci = Cipher.getInstance(transformation, "SunJCE");
KeyGenerator kg = KeyGenerator.getInstance(ALGO, "SunJCE"); KeyGenerator kg = KeyGenerator.getInstance(ALGO, "SunJCE");
kg.init(KEYSIZE*8); kg.init(KEYSIZE*8);
...@@ -57,19 +57,14 @@ public class Test4512704 { ...@@ -57,19 +57,14 @@ public class Test4512704 {
} catch(InvalidAlgorithmParameterException ex) { } catch(InvalidAlgorithmParameterException ex) {
throw new Exception("parameter should be generated when null is specified!"); throw new Exception("parameter should be generated when null is specified!");
} }
System.out.println(transformation + ": Passed");
// passed all tests...hooray!
return true;
} }
public static void main (String[] args) throws Exception { public static void main (String[] args) throws Exception {
Security.addProvider(new com.sun.crypto.provider.SunJCE()); Security.addProvider(new com.sun.crypto.provider.SunJCE());
Test4512704 test = new Test4512704(); Test4512704 test = new Test4512704();
String testName = test.getClass().getName() + "[" + ALGO + test.execute("CBC");
"/" + MODE + "/" + PADDING + "]"; test.execute("GCM");
if (test.execute()) {
System.out.println(testName + ": Passed!");
}
} }
} }
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -41,16 +41,14 @@ import com.sun.crypto.provider.*; ...@@ -41,16 +41,14 @@ import com.sun.crypto.provider.*;
public class Test4517355 { public class Test4517355 {
private static final String ALGO = "AES"; 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 private static final int KEYSIZE = 16; // in bytes
public boolean execute() throws Exception { private static byte[] plainText = new byte[125];
Random rdm = new Random();
byte[] plainText=new byte[125]; public void execute(String mode, String padding) throws Exception {
rdm.nextBytes(plainText); String transformation = ALGO + "/" + mode + "/" + padding;
Cipher ci = Cipher.getInstance(ALGO+"/"+MODE+"/"+PADDING, "SunJCE"); Cipher ci = Cipher.getInstance(transformation, "SunJCE");
KeyGenerator kg = KeyGenerator.getInstance(ALGO, "SunJCE"); KeyGenerator kg = KeyGenerator.getInstance(ALGO, "SunJCE");
kg.init(KEYSIZE*8); kg.init(KEYSIZE*8);
SecretKey key = kg.generateKey(); SecretKey key = kg.generateKey();
...@@ -59,9 +57,14 @@ public class Test4517355 { ...@@ -59,9 +57,14 @@ public class Test4517355 {
ci.init(Cipher.ENCRYPT_MODE, key); ci.init(Cipher.ENCRYPT_MODE, key);
byte[] cipherText = ci.doFinal(plainText); byte[] cipherText = ci.doFinal(plainText);
byte[] iv = ci.getIV(); if (mode.equalsIgnoreCase("GCM")) {
AlgorithmParameterSpec aps = new IvParameterSpec(iv); AlgorithmParameters params = ci.getParameters();
ci.init(Cipher.DECRYPT_MODE, key, aps); 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]; byte[] recoveredText = new byte[plainText.length];
try { try {
int len = ci.doFinal(cipherText, 0, cipherText.length, int len = ci.doFinal(cipherText, 0, cipherText.length,
...@@ -80,21 +83,22 @@ public class Test4517355 { ...@@ -80,21 +83,22 @@ public class Test4517355 {
throw new Exception("encryption does not work!"); throw new Exception("encryption does not work!");
} }
// 3. make sure padding is working // 3. make sure padding is working
if ((cipherText.length/16)*16 != cipherText.length) { if (padding.equalsIgnoreCase("PKCS5Padding")) {
throw new Exception("padding does not work!"); if ((cipherText.length/16)*16 != cipherText.length) {
throw new Exception("padding does not work!");
}
} }
// passed all tests...hooray! System.out.println(transformation + ": Passed");
return true;
} }
public static void main (String[] args) throws Exception { public static void main (String[] args) throws Exception {
Security.addProvider(new com.sun.crypto.provider.SunJCE()); Security.addProvider(new com.sun.crypto.provider.SunJCE());
Test4517355 test = new Test4517355(); Test4517355 test = new Test4517355();
String testName = test.getClass().getName() + "[" + ALGO + Random rdm = new Random();
"/" + MODE + "/" + PADDING + "]"; rdm.nextBytes(test.plainText);
if (test.execute()) {
System.out.println(testName + ": Passed!"); test.execute("CBC", "PKCS5Padding");
} test.execute("GCM", "NoPadding");
} }
} }
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -34,13 +34,11 @@ import java.util.*; ...@@ -34,13 +34,11 @@ import java.util.*;
public class Test4626070 { public class Test4626070 {
private static final String ALGO = "AES"; 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 private static final int KEYSIZE = 16; // in bytes
public boolean execute() throws Exception { public void execute(String mode, String padding) throws Exception {
String transformation = ALGO + "/" + mode + "/" + padding;
Cipher ci = Cipher.getInstance(ALGO+"/"+MODE+"/"+PADDING, "SunJCE"); Cipher ci = Cipher.getInstance(transformation, "SunJCE");
KeyGenerator kg = KeyGenerator.getInstance(ALGO, "SunJCE"); KeyGenerator kg = KeyGenerator.getInstance(ALGO, "SunJCE");
kg.init(KEYSIZE*8); kg.init(KEYSIZE*8);
SecretKey key = kg.generateKey(); SecretKey key = kg.generateKey();
...@@ -58,18 +56,14 @@ public class Test4626070 { ...@@ -58,18 +56,14 @@ public class Test4626070 {
throw new Exception( throw new Exception(
"key after wrap/unwrap is different from the original!"); "key after wrap/unwrap is different from the original!");
} }
// passed all tests...hooray! System.out.println(transformation + ": Passed");
return true;
} }
public static void main (String[] args) throws Exception { public static void main (String[] args) throws Exception {
Security.addProvider(new com.sun.crypto.provider.SunJCE()); Security.addProvider(new com.sun.crypto.provider.SunJCE());
Test4626070 test = new Test4626070(); Test4626070 test = new Test4626070();
String testName = test.getClass().getName() + "[" + ALGO + test.execute("CBC", "PKCS5Padding");
"/" + MODE + "/" + PADDING + "]"; test.execute("GCM", "NoPadding");
if (test.execute()) {
System.out.println(testName + ": Passed!");
}
} }
} }
/*
* 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();
}
}
/*
* 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!");
}
}
}
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -78,6 +78,8 @@ public class GCMAPI { ...@@ -78,6 +78,8 @@ public class GCMAPI {
c.updateAAD(src); c.updateAAD(src);
} catch (UnsupportedOperationException e) { } catch (UnsupportedOperationException e) {
// swallow // swallow
} catch (IllegalStateException ise) {
// swallow
}catch (Exception e) { }catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
failed++; failed++;
...@@ -99,6 +101,8 @@ public class GCMAPI { ...@@ -99,6 +101,8 @@ public class GCMAPI {
c.updateAAD(src, offset, len); c.updateAAD(src, offset, len);
} catch (UnsupportedOperationException e) { } catch (UnsupportedOperationException e) {
// swallow // swallow
} catch (IllegalStateException ise) {
// swallow
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
failed++; failed++;
...@@ -120,6 +124,8 @@ public class GCMAPI { ...@@ -120,6 +124,8 @@ public class GCMAPI {
c.updateAAD(src); c.updateAAD(src);
} catch (UnsupportedOperationException e) { } catch (UnsupportedOperationException e) {
// swallow // swallow
} catch (IllegalStateException ise) {
// swallow
}catch (Exception e) { }catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
failed++; failed++;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册