提交 aaff973b 编写于 作者: V valeriep

8012900: CICO ignores AAD in GCM mode

Summary: Change GCM decryption to not return result until tag verification passed
Reviewed-by: xuelei
上级 6bf752f5
/*
* Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
......@@ -135,9 +135,10 @@ class CipherBlockChaining extends FeedbackCipher {
* @param plainLen the length of the input data
* @param cipher the buffer for the result
* @param cipherOffset the offset in <code>cipher</code>
* @return the length of the encrypted data
*/
void encrypt(byte[] plain, int plainOffset, int plainLen,
byte[] cipher, int cipherOffset)
int encrypt(byte[] plain, int plainOffset, int plainLen,
byte[] cipher, int cipherOffset)
{
int i;
int endIndex = plainOffset + plainLen;
......@@ -150,6 +151,7 @@ class CipherBlockChaining extends FeedbackCipher {
embeddedCipher.encryptBlock(k, 0, cipher, cipherOffset);
System.arraycopy(cipher, cipherOffset, r, 0, blockSize);
}
return plainLen;
}
/**
......@@ -174,13 +176,14 @@ class CipherBlockChaining extends FeedbackCipher {
* @param cipherLen the length of the input data
* @param plain the buffer for the result
* @param plainOffset the offset in <code>plain</code>
* @return the length of the decrypted data
*
* @exception IllegalBlockSizeException if input data whose length does
* not correspond to the embedded cipher's block size is passed to the
* embedded cipher
*/
void decrypt(byte[] cipher, int cipherOffset, int cipherLen,
byte[] plain, int plainOffset)
int decrypt(byte[] cipher, int cipherOffset, int cipherLen,
byte[] plain, int plainOffset)
{
int i;
byte[] cipherOrig=null;
......@@ -195,7 +198,6 @@ class CipherBlockChaining extends FeedbackCipher {
// the plaintext result.
cipherOrig = cipher.clone();
}
for (; cipherOffset < endIndex;
cipherOffset += blockSize, plainOffset += blockSize) {
embeddedCipher.decryptBlock(cipher, cipherOffset, k, 0);
......@@ -208,5 +210,6 @@ class CipherBlockChaining extends FeedbackCipher {
System.arraycopy(cipherOrig, cipherOffset, r, 0, blockSize);
}
}
return cipherLen;
}
}
......@@ -310,49 +310,20 @@ final class CipherCore {
* @return the required output buffer size (in bytes)
*/
int getOutputSize(int inputLen) {
int totalLen = buffered + inputLen;
// GCM: this call may be for either update() or doFinal(), so have to
// return the larger value of both
// Encryption: based on doFinal value: inputLen + tag
// Decryption: based on update value: inputLen
if (!decrypting && (cipherMode == GCM_MODE)) {
return (totalLen + ((GaloisCounterMode) cipher).getTagLen());
}
if (padding == null) {
return totalLen;
}
if (decrypting) {
return totalLen;
}
if (unitBytes != blockSize) {
if (totalLen < diffBlocksize) {
return diffBlocksize;
} else {
return (totalLen + blockSize -
((totalLen - diffBlocksize) % blockSize));
}
} else {
return totalLen + padding.padLength(totalLen);
}
// estimate based on the maximum
return getOutputSizeByOperation(inputLen, true);
}
private int getOutputSizeByOperation(int inputLen, boolean isDoFinal) {
int totalLen = 0;
int totalLen = buffered + inputLen + cipher.getBufferedLength();
switch (cipherMode) {
case GCM_MODE:
totalLen = buffered + inputLen;
if (isDoFinal) {
int tagLen = ((GaloisCounterMode) cipher).getTagLen();
if (decrypting) {
// need to get the actual value from cipher??
// deduct tagLen
totalLen -= tagLen;
} else {
if (!decrypting) {
totalLen += tagLen;
} else {
totalLen -= tagLen;
}
}
if (totalLen < 0) {
......@@ -360,8 +331,19 @@ final class CipherCore {
}
break;
default:
totalLen = getOutputSize(inputLen);
break;
if (padding != null && !decrypting) {
if (unitBytes != blockSize) {
if (totalLen < diffBlocksize) {
totalLen = diffBlocksize;
} else {
int residue = (totalLen - diffBlocksize) % blockSize;
totalLen += (blockSize - residue);
}
} else {
totalLen += padding.padLength(totalLen);
}
}
break;
}
return totalLen;
}
......@@ -729,36 +711,52 @@ final class CipherCore {
len = (len > 0 ? (len - (len%unitBytes)) : 0);
// check output buffer capacity
if ((output == null) || ((output.length - outputOffset) < len)) {
if ((output == null) ||
((output.length - outputOffset) < len)) {
throw new ShortBufferException("Output buffer must be "
+ "(at least) " + len
+ " bytes long");
}
if (len != 0) {
// there is some work to do
byte[] in = new byte[len];
int inputConsumed = len - buffered;
int bufferedConsumed = buffered;
if (inputConsumed < 0) {
inputConsumed = 0;
bufferedConsumed = len;
}
if (buffered != 0) {
System.arraycopy(buffer, 0, in, 0, bufferedConsumed);
}
if (inputConsumed > 0) {
System.arraycopy(input, inputOffset, in,
bufferedConsumed, inputConsumed);
}
if (decrypting) {
cipher.decrypt(in, 0, len, output, outputOffset);
} else {
cipher.encrypt(in, 0, len, output, outputOffset);
int outLen = 0;
if (len != 0) { // there is some work to do
if (len <= buffered) {
// all to-be-processed data are from 'buffer'
if (decrypting) {
outLen = cipher.decrypt(buffer, 0, len, output, outputOffset);
} else {
outLen = cipher.encrypt(buffer, 0, len, output, outputOffset);
}
buffered -= len;
if (buffered != 0) {
System.arraycopy(buffer, len, buffer, 0, buffered);
}
} else { // len > buffered
if (buffered == 0) {
// all to-be-processed data are from 'input'
if (decrypting) {
outLen = cipher.decrypt(input, inputOffset, len, output, outputOffset);
} else {
outLen = cipher.encrypt(input, inputOffset, len, output, outputOffset);
}
inputOffset += len;
inputLen -= len;
} else {
// assemble the data using both 'buffer' and 'input'
byte[] in = new byte[len];
System.arraycopy(buffer, 0, in, 0, buffered);
int inConsumed = len - buffered;
System.arraycopy(input, inputOffset, in, buffered, inConsumed);
buffered = 0;
inputOffset += inConsumed;
inputLen -= inConsumed;
if (decrypting) {
outLen = cipher.decrypt(in, 0, len, output, outputOffset);
} else {
outLen = cipher.encrypt(in, 0, len, output, outputOffset);
}
}
}
// Let's keep track of how many bytes are needed to make
// the total input length a multiple of blocksize when
// padding is applied
......@@ -770,23 +768,14 @@ final class CipherCore {
((len - diffBlocksize) % blockSize);
}
}
inputLen -= inputConsumed;
inputOffset += inputConsumed;
outputOffset += len;
buffered -= bufferedConsumed;
if (buffered > 0) {
System.arraycopy(buffer, bufferedConsumed, buffer, 0,
buffered);
}
}
// left over again
// Store remaining input into 'buffer' again
if (inputLen > 0) {
System.arraycopy(input, inputOffset, buffer, buffered,
inputLen);
buffered += inputLen;
}
buffered += inputLen;
return len;
return outLen;
}
/**
......@@ -881,11 +870,24 @@ final class CipherCore {
("Must use either different key or iv for GCM encryption");
}
// calculate the total input length
int totalLen = buffered + inputLen;
int paddedLen = totalLen;
int paddingLen = 0;
int estOutSize = getOutputSizeByOperation(inputLen, true);
// check output buffer capacity.
// if we are decrypting with padding applied, we can perform this
// check only after we have determined how many padding bytes there
// are.
int outputCapacity = output.length - outputOffset;
int minOutSize = (decrypting? (estOutSize - blockSize):estOutSize);
if ((output == null) || (outputCapacity < minOutSize)) {
throw new ShortBufferException("Output buffer must be "
+ "(at least) " + minOutSize + " bytes long");
}
// calculate total input length
int len = buffered + inputLen;
// calculate padding length
int totalLen = len + cipher.getBufferedLength();
int paddingLen = 0;
// will the total input length be a multiple of blockSize?
if (unitBytes != blockSize) {
if (totalLen < diffBlocksize) {
......@@ -898,40 +900,23 @@ final class CipherCore {
paddingLen = padding.padLength(totalLen);
}
if ((paddingLen > 0) && (paddingLen != blockSize) &&
(padding != null) && decrypting) {
if (decrypting && (padding != null) &&
(paddingLen > 0) && (paddingLen != blockSize)) {
throw new IllegalBlockSizeException
("Input length must be multiple of " + blockSize +
" when decrypting with padded cipher");
}
// if encrypting and padding not null, add padding
if (!decrypting && padding != null) {
paddedLen += paddingLen;
}
// check output buffer capacity.
// if we are decrypting with padding applied, we can perform this
// check only after we have determined how many padding bytes there
// are.
if (output == null) {
throw new ShortBufferException("Output buffer is null");
}
int outputCapacity = output.length - outputOffset;
if (((!decrypting) && (outputCapacity < paddedLen)) ||
(decrypting && (outputCapacity < (paddedLen - blockSize)))) {
throw new ShortBufferException("Output buffer too short: "
+ outputCapacity + " bytes given, "
+ paddedLen + " bytes needed");
}
// prepare the final input avoiding copying if possible
byte[] finalBuf = input;
int finalOffset = inputOffset;
int finalBufLen = inputLen;
if ((buffered != 0) || (!decrypting && padding != null)) {
if (decrypting || padding == null) {
paddingLen = 0;
}
finalBuf = new byte[len + paddingLen];
finalOffset = 0;
finalBuf = new byte[paddedLen];
if (buffered != 0) {
System.arraycopy(buffer, 0, finalBuf, 0, buffered);
}
......@@ -939,50 +924,50 @@ final class CipherCore {
System.arraycopy(input, inputOffset, finalBuf,
buffered, inputLen);
}
if (!decrypting && padding != null) {
padding.padWithLen(finalBuf, totalLen, paddingLen);
if (paddingLen != 0) {
padding.padWithLen(finalBuf, (buffered+inputLen), paddingLen);
}
finalBufLen = finalBuf.length;
}
int outLen = 0;
if (decrypting) {
// if the size of specified output buffer is less than
// the length of the cipher text, then the current
// content of cipher has to be preserved in order for
// users to retry the call with a larger buffer in the
// case of ShortBufferException.
if (outputCapacity < paddedLen) {
if (outputCapacity < estOutSize) {
cipher.save();
}
// create temporary output buffer so that only "real"
// data bytes are passed to user's output buffer.
byte[] outWithPadding = new byte[totalLen];
totalLen = finalNoPadding(finalBuf, finalOffset, outWithPadding,
0, totalLen);
byte[] outWithPadding = new byte[estOutSize];
outLen = finalNoPadding(finalBuf, finalOffset, outWithPadding,
0, finalBufLen);
if (padding != null) {
int padStart = padding.unpad(outWithPadding, 0, totalLen);
int padStart = padding.unpad(outWithPadding, 0, outLen);
if (padStart < 0) {
throw new BadPaddingException("Given final block not "
+ "properly padded");
}
totalLen = padStart;
outLen = padStart;
}
if ((output.length - outputOffset) < totalLen) {
if (outputCapacity < outLen) {
// restore so users can retry with a larger buffer
cipher.restore();
throw new ShortBufferException("Output buffer too short: "
+ (output.length-outputOffset)
+ " bytes given, " + totalLen
+ (outputCapacity)
+ " bytes given, " + outLen
+ " bytes needed");
}
for (int i = 0; i < totalLen; i++) {
output[outputOffset + i] = outWithPadding[i];
}
// copy the result into user-supplied output buffer
System.arraycopy(outWithPadding, 0, output, outputOffset, outLen);
} else { // encrypting
try {
totalLen = finalNoPadding(finalBuf, finalOffset, output,
outputOffset, paddedLen);
outLen = finalNoPadding(finalBuf, finalOffset, output,
outputOffset, finalBufLen);
} finally {
// reset after doFinal() for GCM encryption
requireReinit = (cipherMode == GCM_MODE);
......@@ -994,12 +979,13 @@ final class CipherCore {
if (cipherMode != ECB_MODE) {
cipher.reset();
}
return totalLen;
return outLen;
}
private int finalNoPadding(byte[] in, int inOfs, byte[] out, int outOfs,
int len)
throws IllegalBlockSizeException, AEADBadTagException {
throws IllegalBlockSizeException, AEADBadTagException,
ShortBufferException {
if ((cipherMode != GCM_MODE) && (in == null || len == 0)) {
return 0;
......
/*
* Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
......@@ -150,9 +150,10 @@ final class CipherFeedback extends FeedbackCipher {
* @param plainLen the length of the input data
* @param cipher the buffer for the result
* @param cipherOffset the offset in <code>cipher</code>
* @return the length of the encrypted data
*/
void encrypt(byte[] plain, int plainOffset, int plainLen,
byte[] cipher, int cipherOffset)
int encrypt(byte[] plain, int plainOffset, int plainLen,
byte[] cipher, int cipherOffset)
{
int i, len;
len = blockSize - numBytes;
......@@ -194,6 +195,7 @@ final class CipherFeedback extends FeedbackCipher {
}
}
}
return plainLen;
}
/**
......@@ -218,9 +220,10 @@ final class CipherFeedback extends FeedbackCipher {
* @param cipherLen the length of the input data
* @param plain the buffer for the result
* @param plainOffset the offset in <code>plain</code>
* @return the length of the decrypted data
*/
void decrypt(byte[] cipher, int cipherOffset, int cipherLen,
byte[] plain, int plainOffset)
int decrypt(byte[] cipher, int cipherOffset, int cipherLen,
byte[] plain, int plainOffset)
{
int i, len;
len = blockSize - numBytes;
......@@ -268,5 +271,6 @@ final class CipherFeedback extends FeedbackCipher {
}
}
}
return cipherLen;
}
}
/*
* Copyright (c) 2002, 2007, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 201313, 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
......@@ -149,9 +149,10 @@ final class CounterMode extends FeedbackCipher {
* @param len the length of the input data
* @param out the buffer for the result
* @param outOff the offset in <code>cipher</code>
* @return the length of the encrypted data
*/
void encrypt(byte[] in, int inOff, int len, byte[] out, int outOff) {
crypt(in, inOff, len, out, outOff);
int encrypt(byte[] in, int inOff, int len, byte[] out, int outOff) {
return crypt(in, inOff, len, out, outOff);
}
/**
......@@ -176,9 +177,10 @@ final class CounterMode extends FeedbackCipher {
* @param len the length of the input data
* @param out the buffer for the result
* @param outOff the offset in <code>plain</code>
* @return the length of the decrypted data
*/
void decrypt(byte[] in, int inOff, int len, byte[] out, int outOff) {
crypt(in, inOff, len, out, outOff);
int decrypt(byte[] in, int inOff, int len, byte[] out, int outOff) {
return crypt(in, inOff, len, out, outOff);
}
/**
......@@ -197,7 +199,8 @@ final class CounterMode extends FeedbackCipher {
* keystream generated by encrypting the counter values. Counter values
* are encrypted on demand.
*/
private void crypt(byte[] in, int inOff, int len, byte[] out, int outOff) {
private int crypt(byte[] in, int inOff, int len, byte[] out, int outOff) {
int result = len;
while (len-- > 0) {
if (used >= blockSize) {
embeddedCipher.encryptBlock(counter, 0, encryptedCounter, 0);
......@@ -206,5 +209,6 @@ final class CounterMode extends FeedbackCipher {
}
out[outOff++] = (byte)(in[inOff++] ^ encryptedCounter[used++]);
}
return result;
}
}
/*
* Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
......@@ -115,14 +115,15 @@ final class ElectronicCodeBook extends FeedbackCipher {
* @param len the length of the input data
* @param out the buffer for the result
* @param outOff the offset in <code>cipher</code>
* @return the length of the encrypted data
*/
void encrypt(byte[] in, int inOff, int len, byte[] out, int outOff) {
while (len >= blockSize) {
int encrypt(byte[] in, int inOff, int len, byte[] out, int outOff) {
for (int i = len; i >= blockSize; i -= blockSize) {
embeddedCipher.encryptBlock(in, inOff, out, outOff);
len -= blockSize;
inOff += blockSize;
outOff += blockSize;
}
return len;
}
/**
......@@ -147,14 +148,14 @@ final class ElectronicCodeBook extends FeedbackCipher {
* @param len the length of the input data
* @param out the buffer for the result
* @param outOff the offset in <code>plain</code>
* @return the length of the decrypted data
*/
void decrypt(byte[] in, int inOff, int len, byte[] out, int outOff) {
while (len >= blockSize) {
int decrypt(byte[] in, int inOff, int len, byte[] out, int outOff) {
for (int i = len; i >= blockSize; i -= blockSize) {
embeddedCipher.decryptBlock(in, inOff, out, outOff);
len -= blockSize;
inOff += blockSize;
outOff += blockSize;
}
return len;
}
}
......@@ -133,9 +133,10 @@ abstract class FeedbackCipher {
* @param plainLen the length of the input data
* @param cipher the buffer for the encryption result
* @param cipherOffset the offset in <code>cipher</code>
* @return the number of bytes placed into <code>cipher</code>
*/
abstract void encrypt(byte[] plain, int plainOffset, int plainLen,
byte[] cipher, int cipherOffset);
abstract int encrypt(byte[] plain, int plainOffset, int plainLen,
byte[] cipher, int cipherOffset);
/**
* Performs encryption operation for the last time.
*
......@@ -154,10 +155,9 @@ abstract class FeedbackCipher {
*/
int encryptFinal(byte[] plain, int plainOffset, int plainLen,
byte[] cipher, int cipherOffset)
throws IllegalBlockSizeException {
encrypt(plain, plainOffset, plainLen, cipher, cipherOffset);
return plainLen;
}
throws IllegalBlockSizeException, ShortBufferException {
return encrypt(plain, plainOffset, plainLen, cipher, cipherOffset);
}
/**
* Performs decryption operation.
*
......@@ -174,9 +174,10 @@ abstract class FeedbackCipher {
* @param cipherLen the length of the input data
* @param plain the buffer for the decryption result
* @param plainOffset the offset in <code>plain</code>
* @return the number of bytes placed into <code>plain</code>
*/
abstract void decrypt(byte[] cipher, int cipherOffset, int cipherLen,
byte[] plain, int plainOffset);
abstract int decrypt(byte[] cipher, int cipherOffset, int cipherLen,
byte[] plain, int plainOffset);
/**
* Performs decryption operation for the last time.
......@@ -196,9 +197,9 @@ abstract class FeedbackCipher {
*/
int decryptFinal(byte[] cipher, int cipherOffset, int cipherLen,
byte[] plain, int plainOffset)
throws IllegalBlockSizeException, AEADBadTagException {
decrypt(cipher, cipherOffset, cipherLen, plain, plainOffset);
return cipherLen;
throws IllegalBlockSizeException, AEADBadTagException,
ShortBufferException {
return decrypt(cipher, cipherOffset, cipherLen, plain, plainOffset);
}
/**
......@@ -228,4 +229,15 @@ abstract class FeedbackCipher {
void updateAAD(byte[] src, int offset, int len) {
throw new IllegalStateException("No AAD accepted");
}
/**
* @return the number of bytes that are buffered internally inside
* this FeedbackCipher instance.
* @since 1.8
*/
int getBufferedLength() {
// Currently only AEAD cipher impl, e.g. GCM, buffers data
// internally during decryption mode
return 0;
}
}
......@@ -54,7 +54,7 @@ final class GCTR {
private byte[] counter;
// needed for save/restore calls
private byte[] counterSave;
private byte[] counterSave = null;
// NOTE: cipher should already be initialized
GCTR(SymmetricCipher cipher, byte[] initialCounterBlk) {
......@@ -98,17 +98,16 @@ final class GCTR {
throw new IllegalBlockSizeException("Negative input size!");
} else if (inLen > 0) {
int lastBlockSize = inLen % AES_BLOCK_SIZE;
int completeBlkLen = inLen - lastBlockSize;
// process the complete blocks first
update(in, inOfs, inLen - lastBlockSize, out, outOfs);
update(in, inOfs, completeBlkLen, 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] ^
out[outOfs + completeBlkLen + n] =
(byte) ((in[inOfs + completeBlkLen + n] ^
encryptedCntr[n]));
}
}
......@@ -120,12 +119,11 @@ final class GCTR {
}
/**
* 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.
* Resets the content of this object to when it's first constructed.
*/
void reset() {
System.arraycopy(icb, 0, counter, 0, icb.length);
counterSave = null;
}
/**
......
......@@ -35,10 +35,12 @@ 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.
* Although no checking is done, caller should only pass AES
* Cipher to the constructor.
*
* <p>NOTE: This class does not deal with buffering or padding.
* <p>NOTE: Unlike other modes, when used for decryption, this class
* will buffer all processed outputs internally and won't return them
* until the tag has been successfully verified.
*
* @since 1.8
*/
......@@ -51,6 +53,9 @@ final class GaloisCounterMode extends FeedbackCipher {
private ByteArrayOutputStream aadBuffer = new ByteArrayOutputStream();
private int sizeOfAAD = 0;
// buffer for storing input in decryption, not used for encryption
private ByteArrayOutputStream ibuffer = null;
// in bytes; need to convert to bits (default value 128) when needed
private int tagLenBytes = DEFAULT_TAG_LEN;
......@@ -68,12 +73,14 @@ final class GaloisCounterMode extends FeedbackCipher {
// additional variables for save/restore calls
private byte[] aadBufferSave = null;
private int sizeOfAADSave = 0;
private byte[] ibufferSave = null;
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");
// should never happen
throw new ProviderException("Illegal counter block length");
}
// start from last byte and only go over 4 bytes, i.e. total 32 bits
int n = value.length - 1;
......@@ -171,6 +178,9 @@ final class GaloisCounterMode extends FeedbackCipher {
if (ghashAllToS != null) ghashAllToS.reset();
processed = 0;
sizeOfAAD = 0;
if (ibuffer != null) {
ibuffer.reset();
}
}
/**
......@@ -184,6 +194,9 @@ final class GaloisCounterMode extends FeedbackCipher {
null : aadBuffer.toByteArray());
if (gctrPAndC != null) gctrPAndC.save();
if (ghashAllToS != null) ghashAllToS.save();
if (ibuffer != null) {
ibufferSave = ibuffer.toByteArray();
}
}
/**
......@@ -198,8 +211,12 @@ final class GaloisCounterMode extends FeedbackCipher {
aadBuffer.write(aadBufferSave, 0, aadBufferSave.length);
}
}
if (gctrPAndC != null) gctrPAndC.restore();
if (ghashAllToS != null) ghashAllToS.restore();
if (gctrPAndC != null) gctrPAndC.restore();
if (ghashAllToS != null) ghashAllToS.restore();
if (ibuffer != null) {
ibuffer.reset();
ibuffer.write(ibufferSave, 0, ibufferSave.length);
}
}
/**
......@@ -261,6 +278,9 @@ final class GaloisCounterMode extends FeedbackCipher {
}
processed = 0;
sizeOfAAD = 0;
if (decrypting) {
ibuffer = new ByteArrayOutputStream();
}
}
/**
......@@ -299,7 +319,7 @@ final class GaloisCounterMode extends FeedbackCipher {
// Feed the AAD data to GHASH, pad if necessary
void processAAD() {
if (aadBuffer != null) {
if (aadBuffer != null && aadBuffer.size() > 0) {
byte[] aad = aadBuffer.toByteArray();
sizeOfAAD = aad.length;
aadBuffer = null;
......@@ -365,13 +385,14 @@ final class GaloisCounterMode extends FeedbackCipher {
* @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) {
int 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);
}
return len;
}
/**
......@@ -387,28 +408,28 @@ final class GaloisCounterMode extends FeedbackCipher {
* @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);
}
int encryptFinal(byte[] in, int inOfs, int len, byte[] out, int outOfs)
throws IllegalBlockSizeException, ShortBufferException {
if (out.length - outOfs < (len + tagLenBytes)) {
throw new ShortBufferException("Output buffer too small");
}
processAAD();
if (len > 0) {
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.
......@@ -432,14 +453,16 @@ final class GaloisCounterMode extends FeedbackCipher {
* @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) {
int 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);
if (len > 0) {
// store internally until decryptFinal is called because
// spec mentioned that only return recovered data after tag
// is successfully verified
ibuffer.write(in, inOfs, len);
}
return 0;
}
/**
......@@ -458,44 +481,62 @@ final class GaloisCounterMode extends FeedbackCipher {
* @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;
}
int decryptFinal(byte[] in, int inOfs, int len,
byte[] out, int outOfs)
throws IllegalBlockSizeException, AEADBadTagException,
ShortBufferException {
if (len < tagLenBytes) {
throw new AEADBadTagException("Input too short - need tag");
}
if (out.length - outOfs < ((ibuffer.size() + len) - tagLenBytes)) {
throw new ShortBufferException("Output buffer too small");
}
processAAD();
if (len != 0) {
ibuffer.write(in, inOfs, len);
}
// refresh 'in' to all buffered-up bytes
in = ibuffer.toByteArray();
inOfs = 0;
len = in.length;
ibuffer.reset();
byte[] tag = new byte[tagLenBytes];
// get the trailing tag bytes from 'in'
System.arraycopy(in, 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;
}
int getBufferedLength() {
if (ibuffer == null) {
return 0;
} else {
return ibuffer.size();
}
}
}
/*
* Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
......@@ -149,9 +149,10 @@ final class OutputFeedback extends FeedbackCipher {
* @param plainLen the length of the input data
* @param cipher the buffer for the result
* @param cipherOffset the offset in <code>cipher</code>
* @return the length of the encrypted data
*/
void encrypt(byte[] plain, int plainOffset, int plainLen,
byte[] cipher, int cipherOffset)
int encrypt(byte[] plain, int plainOffset, int plainLen,
byte[] cipher, int cipherOffset)
{
int i;
int len = blockSize - numBytes;
......@@ -195,6 +196,7 @@ final class OutputFeedback extends FeedbackCipher {
System.arraycopy(k, 0, register, len, numBytes);
}
}
return plainLen;
}
/**
......@@ -219,11 +221,12 @@ final class OutputFeedback extends FeedbackCipher {
* @param cipherLen the length of the input data
* @param plain the buffer for the result
* @param plainOffset the offset in <code>plain</code>
* @return the length of the decrypted data
*/
void decrypt(byte[] cipher, int cipherOffset, int cipherLen,
int decrypt(byte[] cipher, int cipherOffset, int cipherLen,
byte[] plain, int plainOffset)
{
// OFB encrypt and decrypt are identical
encrypt(cipher, cipherOffset, cipherLen, plain, plainOffset);
return encrypt(cipher, cipherOffset, cipherLen, plain, plainOffset);
}
}
......@@ -136,8 +136,8 @@ final class PCBC extends FeedbackCipher {
* @param cipher the buffer for the result
* @param cipherOffset the offset in <code>cipher</code>
*/
void encrypt(byte[] plain, int plainOffset, int plainLen,
byte[] cipher, int cipherOffset)
int encrypt(byte[] plain, int plainOffset, int plainLen,
byte[] cipher, int cipherOffset)
{
int i;
int endIndex = plainOffset + plainLen;
......@@ -152,6 +152,7 @@ final class PCBC extends FeedbackCipher {
k[i] = (byte)(plain[i+plainOffset] ^ cipher[i+cipherOffset]);
}
}
return plainLen;
}
/**
......@@ -177,8 +178,8 @@ final class PCBC extends FeedbackCipher {
* @param plain the buffer for the result
* @param plainOffset the offset in <code>plain</code>
*/
void decrypt(byte[] cipher, int cipherOffset, int cipherLen,
byte[] plain, int plainOffset)
int decrypt(byte[] cipher, int cipherOffset, int cipherLen,
byte[] plain, int plainOffset)
{
int i;
int endIndex = cipherOffset + cipherLen;
......@@ -194,5 +195,6 @@ final class PCBC extends FeedbackCipher {
k[i] = (byte)(plain[i+plainOffset] ^ cipher[i+cipherOffset]);
}
}
return cipherLen;
}
}
......@@ -786,7 +786,9 @@ public abstract class CipherSpi {
int total = 0;
do {
int chunk = Math.min(inLen, inArray.length);
input.get(inArray, 0, chunk);
if (chunk > 0) {
input.get(inArray, 0, chunk);
}
int n;
if (isUpdate || (inLen != chunk)) {
n = engineUpdate(inArray, 0, chunk, outArray, outOfs);
......@@ -814,8 +816,9 @@ public abstract class CipherSpi {
int total = 0;
boolean resized = false;
do {
int chunk = Math.min(inLen, outSize);
if ((a1 == false) && (resized == false)) {
int chunk =
Math.min(inLen, (outSize == 0? inArray.length : outSize));
if (!a1 && !resized && chunk > 0) {
input.get(inArray, 0, chunk);
inOfs = 0;
}
......@@ -829,8 +832,10 @@ public abstract class CipherSpi {
resized = false;
inOfs += chunk;
inLen -= chunk;
output.put(outArray, 0, n);
total += n;
if (n > 0) {
output.put(outArray, 0, n);
total += n;
}
} catch (ShortBufferException e) {
if (resized) {
// we just resized the output buffer, but it still
......@@ -840,11 +845,13 @@ public abstract class CipherSpi {
}
// output buffer is too small, realloc and try again
resized = true;
int newOut = engineGetOutputSize(chunk);
outArray = new byte[newOut];
outSize = engineGetOutputSize(chunk);
outArray = new byte[outSize];
}
} while (inLen > 0);
input.position(inLimit);
if (a1) {
input.position(inLimit);
}
return total;
}
}
......
/*
* 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
* questions.
*/
/*
* @test
* @bug 8012900
* @library ../UTIL
* @build TestUtil
* @run main TestCICOWithGCMAndAAD
* @summary Test CipherInputStream/OutputStream with AES GCM mode with AAD.
* @author Valerie Peng
*/
import java.io.*;
import java.security.*;
import java.util.*;
import javax.crypto.*;
public class TestCICOWithGCMAndAAD {
public static void main(String[] args) throws Exception {
//init Secret Key
KeyGenerator kg = KeyGenerator.getInstance("AES", "SunJCE");
kg.init(128);
SecretKey key = kg.generateKey();
//Do initialization of the plainText
byte[] plainText = new byte[700];
Random rdm = new Random();
rdm.nextBytes(plainText);
byte[] aad = new byte[128];
rdm.nextBytes(aad);
byte[] aad2 = aad.clone();
aad2[50]++;
Cipher encCipher = Cipher.getInstance("AES/GCM/NoPadding", "SunJCE");
encCipher.init(Cipher.ENCRYPT_MODE, key);
encCipher.updateAAD(aad);
Cipher decCipher = Cipher.getInstance("AES/GCM/NoPadding", "SunJCE");
decCipher.init(Cipher.DECRYPT_MODE, key, encCipher.getParameters());
decCipher.updateAAD(aad);
byte[] recovered = test(encCipher, decCipher, plainText);
if (!Arrays.equals(plainText, recovered)) {
throw new Exception("sameAAD: diff check failed!");
} else System.out.println("sameAAD: passed");
encCipher.init(Cipher.ENCRYPT_MODE, key);
encCipher.updateAAD(aad2);
recovered = test(encCipher, decCipher, plainText);
if (recovered != null && recovered.length != 0) {
throw new Exception("diffAAD: no data should be returned!");
} else System.out.println("diffAAD: passed");
}
private static byte[] test(Cipher encCipher, Cipher decCipher, byte[] plainText)
throws Exception {
//init cipher streams
ByteArrayInputStream baInput = new ByteArrayInputStream(plainText);
CipherInputStream ciInput = new CipherInputStream(baInput, encCipher);
ByteArrayOutputStream baOutput = new ByteArrayOutputStream();
CipherOutputStream ciOutput = new CipherOutputStream(baOutput, decCipher);
//do test
byte[] buffer = new byte[200];
int len = ciInput.read(buffer);
System.out.println("read " + len + " bytes from input buffer");
while (len != -1) {
ciOutput.write(buffer, 0, len);
System.out.println("wite " + len + " bytes to output buffer");
len = ciInput.read(buffer);
if (len != -1) {
System.out.println("read " + len + " bytes from input buffer");
} else {
System.out.println("finished reading");
}
}
ciOutput.flush();
ciInput.close();
ciOutput.close();
return baOutput.toByteArray();
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册