提交 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. * 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
...@@ -135,9 +135,10 @@ class CipherBlockChaining extends FeedbackCipher { ...@@ -135,9 +135,10 @@ class CipherBlockChaining extends FeedbackCipher {
* @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 length of the encrypted data
*/ */
void encrypt(byte[] plain, int plainOffset, int plainLen, int encrypt(byte[] plain, int plainOffset, int plainLen,
byte[] cipher, int cipherOffset) byte[] cipher, int cipherOffset)
{ {
int i; int i;
int endIndex = plainOffset + plainLen; int endIndex = plainOffset + plainLen;
...@@ -150,6 +151,7 @@ class CipherBlockChaining extends FeedbackCipher { ...@@ -150,6 +151,7 @@ class CipherBlockChaining extends FeedbackCipher {
embeddedCipher.encryptBlock(k, 0, cipher, cipherOffset); embeddedCipher.encryptBlock(k, 0, cipher, cipherOffset);
System.arraycopy(cipher, cipherOffset, r, 0, blockSize); System.arraycopy(cipher, cipherOffset, r, 0, blockSize);
} }
return plainLen;
} }
/** /**
...@@ -174,13 +176,14 @@ class CipherBlockChaining extends FeedbackCipher { ...@@ -174,13 +176,14 @@ class CipherBlockChaining extends FeedbackCipher {
* @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 length of the decrypted data
* *
* @exception IllegalBlockSizeException if input data whose length does * @exception IllegalBlockSizeException if input data whose length does
* not correspond to the embedded cipher's block size is passed to the * not correspond to the embedded cipher's block size is passed to the
* embedded cipher * embedded cipher
*/ */
void decrypt(byte[] cipher, int cipherOffset, int cipherLen, int decrypt(byte[] cipher, int cipherOffset, int cipherLen,
byte[] plain, int plainOffset) byte[] plain, int plainOffset)
{ {
int i; int i;
byte[] cipherOrig=null; byte[] cipherOrig=null;
...@@ -195,7 +198,6 @@ class CipherBlockChaining extends FeedbackCipher { ...@@ -195,7 +198,6 @@ class CipherBlockChaining extends FeedbackCipher {
// the plaintext result. // the plaintext result.
cipherOrig = cipher.clone(); cipherOrig = cipher.clone();
} }
for (; cipherOffset < endIndex; for (; cipherOffset < endIndex;
cipherOffset += blockSize, plainOffset += blockSize) { cipherOffset += blockSize, plainOffset += blockSize) {
embeddedCipher.decryptBlock(cipher, cipherOffset, k, 0); embeddedCipher.decryptBlock(cipher, cipherOffset, k, 0);
...@@ -208,5 +210,6 @@ class CipherBlockChaining extends FeedbackCipher { ...@@ -208,5 +210,6 @@ class CipherBlockChaining extends FeedbackCipher {
System.arraycopy(cipherOrig, cipherOffset, r, 0, blockSize); System.arraycopy(cipherOrig, cipherOffset, r, 0, blockSize);
} }
} }
return cipherLen;
} }
} }
...@@ -310,49 +310,20 @@ final class CipherCore { ...@@ -310,49 +310,20 @@ final class CipherCore {
* @return the required output buffer size (in bytes) * @return the required output buffer size (in bytes)
*/ */
int getOutputSize(int inputLen) { int getOutputSize(int inputLen) {
int totalLen = buffered + inputLen; // estimate based on the maximum
return getOutputSizeByOperation(inputLen, true);
// 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);
}
} }
private int getOutputSizeByOperation(int inputLen, boolean isDoFinal) { private int getOutputSizeByOperation(int inputLen, boolean isDoFinal) {
int totalLen = 0; int totalLen = buffered + inputLen + cipher.getBufferedLength();
switch (cipherMode) { switch (cipherMode) {
case GCM_MODE: case GCM_MODE:
totalLen = buffered + inputLen;
if (isDoFinal) { if (isDoFinal) {
int tagLen = ((GaloisCounterMode) cipher).getTagLen(); int tagLen = ((GaloisCounterMode) cipher).getTagLen();
if (decrypting) { if (!decrypting) {
// need to get the actual value from cipher??
// deduct tagLen
totalLen -= tagLen;
} else {
totalLen += tagLen; totalLen += tagLen;
} else {
totalLen -= tagLen;
} }
} }
if (totalLen < 0) { if (totalLen < 0) {
...@@ -360,8 +331,19 @@ final class CipherCore { ...@@ -360,8 +331,19 @@ final class CipherCore {
} }
break; break;
default: default:
totalLen = getOutputSize(inputLen); if (padding != null && !decrypting) {
break; 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; return totalLen;
} }
...@@ -729,36 +711,52 @@ final class CipherCore { ...@@ -729,36 +711,52 @@ final class CipherCore {
len = (len > 0 ? (len - (len%unitBytes)) : 0); len = (len > 0 ? (len - (len%unitBytes)) : 0);
// check output buffer capacity // 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 " throw new ShortBufferException("Output buffer must be "
+ "(at least) " + len + "(at least) " + len
+ " bytes long"); + " bytes long");
} }
if (len != 0) { int outLen = 0;
// there is some work to do if (len != 0) { // there is some work to do
byte[] in = new byte[len]; if (len <= buffered) {
// all to-be-processed data are from 'buffer'
int inputConsumed = len - buffered; if (decrypting) {
int bufferedConsumed = buffered; outLen = cipher.decrypt(buffer, 0, len, output, outputOffset);
if (inputConsumed < 0) { } else {
inputConsumed = 0; outLen = cipher.encrypt(buffer, 0, len, output, outputOffset);
bufferedConsumed = len; }
} buffered -= len;
if (buffered != 0) {
if (buffered != 0) { System.arraycopy(buffer, len, buffer, 0, buffered);
System.arraycopy(buffer, 0, in, 0, bufferedConsumed); }
} } else { // len > buffered
if (inputConsumed > 0) { if (buffered == 0) {
System.arraycopy(input, inputOffset, in, // all to-be-processed data are from 'input'
bufferedConsumed, inputConsumed); if (decrypting) {
} outLen = cipher.decrypt(input, inputOffset, len, output, outputOffset);
if (decrypting) { } else {
cipher.decrypt(in, 0, len, output, outputOffset); outLen = cipher.encrypt(input, inputOffset, len, output, outputOffset);
} else { }
cipher.encrypt(in, 0, 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 // Let's keep track of how many bytes are needed to make
// the total input length a multiple of blocksize when // the total input length a multiple of blocksize when
// padding is applied // padding is applied
...@@ -770,23 +768,14 @@ final class CipherCore { ...@@ -770,23 +768,14 @@ final class CipherCore {
((len - diffBlocksize) % blockSize); ((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) { if (inputLen > 0) {
System.arraycopy(input, inputOffset, buffer, buffered, System.arraycopy(input, inputOffset, buffer, buffered,
inputLen); inputLen);
buffered += inputLen;
} }
buffered += inputLen; return outLen;
return len;
} }
/** /**
...@@ -881,11 +870,24 @@ final class CipherCore { ...@@ -881,11 +870,24 @@ final class CipherCore {
("Must use either different key or iv for GCM encryption"); ("Must use either different key or iv for GCM encryption");
} }
// calculate the total input length int estOutSize = getOutputSizeByOperation(inputLen, true);
int totalLen = buffered + inputLen; // check output buffer capacity.
int paddedLen = totalLen; // if we are decrypting with padding applied, we can perform this
int paddingLen = 0; // 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? // will the total input length be a multiple of blockSize?
if (unitBytes != blockSize) { if (unitBytes != blockSize) {
if (totalLen < diffBlocksize) { if (totalLen < diffBlocksize) {
...@@ -898,40 +900,23 @@ final class CipherCore { ...@@ -898,40 +900,23 @@ final class CipherCore {
paddingLen = padding.padLength(totalLen); paddingLen = padding.padLength(totalLen);
} }
if ((paddingLen > 0) && (paddingLen != blockSize) && if (decrypting && (padding != null) &&
(padding != null) && decrypting) { (paddingLen > 0) && (paddingLen != blockSize)) {
throw new IllegalBlockSizeException throw new IllegalBlockSizeException
("Input length must be multiple of " + blockSize + ("Input length must be multiple of " + blockSize +
" when decrypting with padded cipher"); " 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 // prepare the final input avoiding copying if possible
byte[] finalBuf = input; byte[] finalBuf = input;
int finalOffset = inputOffset; int finalOffset = inputOffset;
int finalBufLen = inputLen;
if ((buffered != 0) || (!decrypting && padding != null)) { if ((buffered != 0) || (!decrypting && padding != null)) {
if (decrypting || padding == null) {
paddingLen = 0;
}
finalBuf = new byte[len + paddingLen];
finalOffset = 0; finalOffset = 0;
finalBuf = new byte[paddedLen];
if (buffered != 0) { if (buffered != 0) {
System.arraycopy(buffer, 0, finalBuf, 0, buffered); System.arraycopy(buffer, 0, finalBuf, 0, buffered);
} }
...@@ -939,50 +924,50 @@ final class CipherCore { ...@@ -939,50 +924,50 @@ final class CipherCore {
System.arraycopy(input, inputOffset, finalBuf, System.arraycopy(input, inputOffset, finalBuf,
buffered, inputLen); buffered, inputLen);
} }
if (!decrypting && padding != null) { if (paddingLen != 0) {
padding.padWithLen(finalBuf, totalLen, paddingLen); padding.padWithLen(finalBuf, (buffered+inputLen), paddingLen);
} }
finalBufLen = finalBuf.length;
} }
int outLen = 0;
if (decrypting) { if (decrypting) {
// if the size of specified output buffer is less than // if the size of specified output buffer is less than
// the length of the cipher text, then the current // the length of the cipher text, then the current
// content of cipher has to be preserved in order for // content of cipher has to be preserved in order for
// users to retry the call with a larger buffer in the // users to retry the call with a larger buffer in the
// case of ShortBufferException. // case of ShortBufferException.
if (outputCapacity < paddedLen) { if (outputCapacity < estOutSize) {
cipher.save(); cipher.save();
} }
// create temporary output buffer so that only "real" // create temporary output buffer so that only "real"
// data bytes are passed to user's output buffer. // data bytes are passed to user's output buffer.
byte[] outWithPadding = new byte[totalLen]; byte[] outWithPadding = new byte[estOutSize];
totalLen = finalNoPadding(finalBuf, finalOffset, outWithPadding, outLen = finalNoPadding(finalBuf, finalOffset, outWithPadding,
0, totalLen); 0, finalBufLen);
if (padding != null) { if (padding != null) {
int padStart = padding.unpad(outWithPadding, 0, totalLen); int padStart = padding.unpad(outWithPadding, 0, outLen);
if (padStart < 0) { if (padStart < 0) {
throw new BadPaddingException("Given final block not " throw new BadPaddingException("Given final block not "
+ "properly padded"); + "properly padded");
} }
totalLen = padStart; outLen = padStart;
} }
if ((output.length - outputOffset) < totalLen) { if (outputCapacity < outLen) {
// restore so users can retry with a larger buffer // restore so users can retry with a larger buffer
cipher.restore(); cipher.restore();
throw new ShortBufferException("Output buffer too short: " throw new ShortBufferException("Output buffer too short: "
+ (output.length-outputOffset) + (outputCapacity)
+ " bytes given, " + totalLen + " bytes given, " + outLen
+ " bytes needed"); + " bytes needed");
} }
for (int i = 0; i < totalLen; i++) { // copy the result into user-supplied output buffer
output[outputOffset + i] = outWithPadding[i]; System.arraycopy(outWithPadding, 0, output, outputOffset, outLen);
}
} else { // encrypting } else { // encrypting
try { try {
totalLen = finalNoPadding(finalBuf, finalOffset, output, outLen = finalNoPadding(finalBuf, finalOffset, output,
outputOffset, paddedLen); outputOffset, finalBufLen);
} finally { } finally {
// reset after doFinal() for GCM encryption // reset after doFinal() for GCM encryption
requireReinit = (cipherMode == GCM_MODE); requireReinit = (cipherMode == GCM_MODE);
...@@ -994,12 +979,13 @@ final class CipherCore { ...@@ -994,12 +979,13 @@ final class CipherCore {
if (cipherMode != ECB_MODE) { if (cipherMode != ECB_MODE) {
cipher.reset(); cipher.reset();
} }
return totalLen; return outLen;
} }
private int finalNoPadding(byte[] in, int inOfs, byte[] out, int outOfs, private int finalNoPadding(byte[] in, int inOfs, byte[] out, int outOfs,
int len) int len)
throws IllegalBlockSizeException, AEADBadTagException { throws IllegalBlockSizeException, AEADBadTagException,
ShortBufferException {
if ((cipherMode != GCM_MODE) && (in == null || len == 0)) { if ((cipherMode != GCM_MODE) && (in == null || len == 0)) {
return 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. * 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
...@@ -150,9 +150,10 @@ final class CipherFeedback extends FeedbackCipher { ...@@ -150,9 +150,10 @@ final class CipherFeedback extends FeedbackCipher {
* @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 length of the encrypted data
*/ */
void encrypt(byte[] plain, int plainOffset, int plainLen, int encrypt(byte[] plain, int plainOffset, int plainLen,
byte[] cipher, int cipherOffset) byte[] cipher, int cipherOffset)
{ {
int i, len; int i, len;
len = blockSize - numBytes; len = blockSize - numBytes;
...@@ -194,6 +195,7 @@ final class CipherFeedback extends FeedbackCipher { ...@@ -194,6 +195,7 @@ final class CipherFeedback extends FeedbackCipher {
} }
} }
} }
return plainLen;
} }
/** /**
...@@ -218,9 +220,10 @@ final class CipherFeedback extends FeedbackCipher { ...@@ -218,9 +220,10 @@ final class CipherFeedback extends FeedbackCipher {
* @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 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) byte[] plain, int plainOffset)
{ {
int i, len; int i, len;
len = blockSize - numBytes; len = blockSize - numBytes;
...@@ -268,5 +271,6 @@ final class CipherFeedback extends FeedbackCipher { ...@@ -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. * 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
...@@ -149,9 +149,10 @@ final class CounterMode extends FeedbackCipher { ...@@ -149,9 +149,10 @@ final class CounterMode extends FeedbackCipher {
* @param len the length of the input data * @param len the length of the input data
* @param out the buffer for the result * @param out the buffer for the result
* @param outOff the offset in <code>cipher</code> * @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) { int encrypt(byte[] in, int inOff, int len, byte[] out, int outOff) {
crypt(in, inOff, len, out, outOff); return crypt(in, inOff, len, out, outOff);
} }
/** /**
...@@ -176,9 +177,10 @@ final class CounterMode extends FeedbackCipher { ...@@ -176,9 +177,10 @@ final class CounterMode extends FeedbackCipher {
* @param len the length of the input data * @param len the length of the input data
* @param out the buffer for the result * @param out the buffer for the result
* @param outOff the offset in <code>plain</code> * @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) { int decrypt(byte[] in, int inOff, int len, byte[] out, int outOff) {
crypt(in, inOff, len, out, outOff); return crypt(in, inOff, len, out, outOff);
} }
/** /**
...@@ -197,7 +199,8 @@ final class CounterMode extends FeedbackCipher { ...@@ -197,7 +199,8 @@ final class CounterMode extends FeedbackCipher {
* keystream generated by encrypting the counter values. Counter values * keystream generated by encrypting the counter values. Counter values
* are encrypted on demand. * 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) { while (len-- > 0) {
if (used >= blockSize) { if (used >= blockSize) {
embeddedCipher.encryptBlock(counter, 0, encryptedCounter, 0); embeddedCipher.encryptBlock(counter, 0, encryptedCounter, 0);
...@@ -206,5 +209,6 @@ final class CounterMode extends FeedbackCipher { ...@@ -206,5 +209,6 @@ final class CounterMode extends FeedbackCipher {
} }
out[outOff++] = (byte)(in[inOff++] ^ encryptedCounter[used++]); 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. * 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
...@@ -115,14 +115,15 @@ final class ElectronicCodeBook extends FeedbackCipher { ...@@ -115,14 +115,15 @@ final class ElectronicCodeBook extends FeedbackCipher {
* @param len the length of the input data * @param len the length of the input data
* @param out the buffer for the result * @param out the buffer for the result
* @param outOff the offset in <code>cipher</code> * @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) { int encrypt(byte[] in, int inOff, int len, byte[] out, int outOff) {
while (len >= blockSize) { for (int i = len; i >= blockSize; i -= blockSize) {
embeddedCipher.encryptBlock(in, inOff, out, outOff); embeddedCipher.encryptBlock(in, inOff, out, outOff);
len -= blockSize;
inOff += blockSize; inOff += blockSize;
outOff += blockSize; outOff += blockSize;
} }
return len;
} }
/** /**
...@@ -147,14 +148,14 @@ final class ElectronicCodeBook extends FeedbackCipher { ...@@ -147,14 +148,14 @@ final class ElectronicCodeBook extends FeedbackCipher {
* @param len the length of the input data * @param len the length of the input data
* @param out the buffer for the result * @param out the buffer for the result
* @param outOff the offset in <code>plain</code> * @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) { int decrypt(byte[] in, int inOff, int len, byte[] out, int outOff) {
while (len >= blockSize) { for (int i = len; i >= blockSize; i -= blockSize) {
embeddedCipher.decryptBlock(in, inOff, out, outOff); embeddedCipher.decryptBlock(in, inOff, out, outOff);
len -= blockSize;
inOff += blockSize; inOff += blockSize;
outOff += blockSize; outOff += blockSize;
} }
return len;
} }
} }
...@@ -133,9 +133,10 @@ abstract class FeedbackCipher { ...@@ -133,9 +133,10 @@ 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>
*/ */
abstract void encrypt(byte[] plain, int plainOffset, int plainLen, abstract int encrypt(byte[] plain, int plainOffset, int plainLen,
byte[] cipher, int cipherOffset); byte[] cipher, int cipherOffset);
/** /**
* Performs encryption operation for the last time. * Performs encryption operation for the last time.
* *
...@@ -154,10 +155,9 @@ abstract class FeedbackCipher { ...@@ -154,10 +155,9 @@ abstract class FeedbackCipher {
*/ */
int 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, ShortBufferException {
encrypt(plain, plainOffset, plainLen, cipher, cipherOffset); return encrypt(plain, plainOffset, plainLen, cipher, cipherOffset);
return plainLen; }
}
/** /**
* Performs decryption operation. * Performs decryption operation.
* *
...@@ -174,9 +174,10 @@ abstract class FeedbackCipher { ...@@ -174,9 +174,10 @@ 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>
*/ */
abstract void decrypt(byte[] cipher, int cipherOffset, int cipherLen, abstract int decrypt(byte[] cipher, int cipherOffset, int cipherLen,
byte[] plain, int plainOffset); byte[] plain, int plainOffset);
/** /**
* Performs decryption operation for the last time. * Performs decryption operation for the last time.
...@@ -196,9 +197,9 @@ abstract class FeedbackCipher { ...@@ -196,9 +197,9 @@ abstract class FeedbackCipher {
*/ */
int 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, AEADBadTagException { throws IllegalBlockSizeException, AEADBadTagException,
decrypt(cipher, cipherOffset, cipherLen, plain, plainOffset); ShortBufferException {
return cipherLen; return decrypt(cipher, cipherOffset, cipherLen, plain, plainOffset);
} }
/** /**
...@@ -228,4 +229,15 @@ abstract class FeedbackCipher { ...@@ -228,4 +229,15 @@ abstract class FeedbackCipher {
void updateAAD(byte[] src, int offset, int len) { void updateAAD(byte[] src, int offset, int len) {
throw new IllegalStateException("No AAD accepted"); 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 { ...@@ -54,7 +54,7 @@ final class GCTR {
private byte[] counter; private byte[] counter;
// needed for save/restore calls // needed for save/restore calls
private byte[] counterSave; private byte[] counterSave = null;
// NOTE: cipher should already be initialized // NOTE: cipher should already be initialized
GCTR(SymmetricCipher cipher, byte[] initialCounterBlk) { GCTR(SymmetricCipher cipher, byte[] initialCounterBlk) {
...@@ -98,17 +98,16 @@ final class GCTR { ...@@ -98,17 +98,16 @@ final class GCTR {
throw new IllegalBlockSizeException("Negative input size!"); throw new IllegalBlockSizeException("Negative input size!");
} else if (inLen > 0) { } else if (inLen > 0) {
int lastBlockSize = inLen % AES_BLOCK_SIZE; int lastBlockSize = inLen % AES_BLOCK_SIZE;
int completeBlkLen = inLen - lastBlockSize;
// process the complete blocks first // process the complete blocks first
update(in, inOfs, inLen - lastBlockSize, out, outOfs); update(in, inOfs, completeBlkLen, out, outOfs);
if (lastBlockSize != 0) { if (lastBlockSize != 0) {
// do the last partial block // do the last partial block
byte[] encryptedCntr = new byte[AES_BLOCK_SIZE]; byte[] encryptedCntr = new byte[AES_BLOCK_SIZE];
aes.encryptBlock(counter, 0, encryptedCntr, 0); aes.encryptBlock(counter, 0, encryptedCntr, 0);
int processed = inLen - lastBlockSize;
for (int n = 0; n < lastBlockSize; n++) { for (int n = 0; n < lastBlockSize; n++) {
out[outOfs + processed + n] = out[outOfs + completeBlkLen + n] =
(byte) ((in[inOfs + processed + n] ^ (byte) ((in[inOfs + completeBlkLen + n] ^
encryptedCntr[n])); encryptedCntr[n]));
} }
} }
...@@ -120,12 +119,11 @@ final class GCTR { ...@@ -120,12 +119,11 @@ final class GCTR {
} }
/** /**
* Resets the current counter to its initial value. * Resets the content of this object to when it's first constructed.
* This is used after the doFinal() is called so this object can be
* reused w/o explicit re-initialization.
*/ */
void reset() { void reset() {
System.arraycopy(icb, 0, counter, 0, icb.length); System.arraycopy(icb, 0, counter, 0, icb.length);
counterSave = null;
} }
/** /**
......
...@@ -35,10 +35,12 @@ import static com.sun.crypto.provider.AESConstants.AES_BLOCK_SIZE; ...@@ -35,10 +35,12 @@ import static com.sun.crypto.provider.AESConstants.AES_BLOCK_SIZE;
* This class represents ciphers in GaloisCounter (GCM) mode. * This class represents ciphers in GaloisCounter (GCM) mode.
* *
* <p>This mode currently should only be used w/ AES cipher. * <p>This mode currently should only be used w/ AES cipher.
* Although no checking is done here, caller should only * Although no checking is done, caller should only pass AES
* pass AES Cipher to the constructor. * 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 * @since 1.8
*/ */
...@@ -51,6 +53,9 @@ final class GaloisCounterMode extends FeedbackCipher { ...@@ -51,6 +53,9 @@ final class GaloisCounterMode extends FeedbackCipher {
private ByteArrayOutputStream aadBuffer = new ByteArrayOutputStream(); private ByteArrayOutputStream aadBuffer = new ByteArrayOutputStream();
private int sizeOfAAD = 0; 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 // in bytes; need to convert to bits (default value 128) when needed
private int tagLenBytes = DEFAULT_TAG_LEN; private int tagLenBytes = DEFAULT_TAG_LEN;
...@@ -68,12 +73,14 @@ final class GaloisCounterMode extends FeedbackCipher { ...@@ -68,12 +73,14 @@ final class GaloisCounterMode extends FeedbackCipher {
// additional variables for save/restore calls // additional variables for save/restore calls
private byte[] aadBufferSave = null; private byte[] aadBufferSave = null;
private int sizeOfAADSave = 0; private int sizeOfAADSave = 0;
private byte[] ibufferSave = null;
private int processedSave = 0; private int processedSave = 0;
// value must be 16-byte long; used by GCTR and GHASH as well // value must be 16-byte long; used by GCTR and GHASH as well
static void increment32(byte[] value) { static void increment32(byte[] value) {
if (value.length != AES_BLOCK_SIZE) { 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 // start from last byte and only go over 4 bytes, i.e. total 32 bits
int n = value.length - 1; int n = value.length - 1;
...@@ -171,6 +178,9 @@ final class GaloisCounterMode extends FeedbackCipher { ...@@ -171,6 +178,9 @@ final class GaloisCounterMode extends FeedbackCipher {
if (ghashAllToS != null) ghashAllToS.reset(); if (ghashAllToS != null) ghashAllToS.reset();
processed = 0; processed = 0;
sizeOfAAD = 0; sizeOfAAD = 0;
if (ibuffer != null) {
ibuffer.reset();
}
} }
/** /**
...@@ -184,6 +194,9 @@ final class GaloisCounterMode extends FeedbackCipher { ...@@ -184,6 +194,9 @@ final class GaloisCounterMode extends FeedbackCipher {
null : aadBuffer.toByteArray()); null : aadBuffer.toByteArray());
if (gctrPAndC != null) gctrPAndC.save(); if (gctrPAndC != null) gctrPAndC.save();
if (ghashAllToS != null) ghashAllToS.save(); if (ghashAllToS != null) ghashAllToS.save();
if (ibuffer != null) {
ibufferSave = ibuffer.toByteArray();
}
} }
/** /**
...@@ -198,8 +211,12 @@ final class GaloisCounterMode extends FeedbackCipher { ...@@ -198,8 +211,12 @@ final class GaloisCounterMode extends FeedbackCipher {
aadBuffer.write(aadBufferSave, 0, aadBufferSave.length); aadBuffer.write(aadBufferSave, 0, aadBufferSave.length);
} }
} }
if (gctrPAndC != null) gctrPAndC.restore(); if (gctrPAndC != null) gctrPAndC.restore();
if (ghashAllToS != null) ghashAllToS.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 { ...@@ -261,6 +278,9 @@ final class GaloisCounterMode extends FeedbackCipher {
} }
processed = 0; processed = 0;
sizeOfAAD = 0; sizeOfAAD = 0;
if (decrypting) {
ibuffer = new ByteArrayOutputStream();
}
} }
/** /**
...@@ -299,7 +319,7 @@ final class GaloisCounterMode extends FeedbackCipher { ...@@ -299,7 +319,7 @@ final class GaloisCounterMode extends FeedbackCipher {
// Feed the AAD data to GHASH, pad if necessary // Feed the AAD data to GHASH, pad if necessary
void processAAD() { void processAAD() {
if (aadBuffer != null) { if (aadBuffer != null && aadBuffer.size() > 0) {
byte[] aad = aadBuffer.toByteArray(); byte[] aad = aadBuffer.toByteArray();
sizeOfAAD = aad.length; sizeOfAAD = aad.length;
aadBuffer = null; aadBuffer = null;
...@@ -365,13 +385,14 @@ final class GaloisCounterMode extends FeedbackCipher { ...@@ -365,13 +385,14 @@ final class GaloisCounterMode extends FeedbackCipher {
* @param out the buffer for the result * @param out the buffer for the result
* @param outOfs the offset in <code>out</code> * @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(); processAAD();
if (len > 0) { if (len > 0) {
gctrPAndC.update(in, inOfs, len, out, outOfs); gctrPAndC.update(in, inOfs, len, out, outOfs);
processed += len; processed += len;
ghashAllToS.update(out, outOfs, len); ghashAllToS.update(out, outOfs, len);
} }
return len;
} }
/** /**
...@@ -387,28 +408,28 @@ final class GaloisCounterMode extends FeedbackCipher { ...@@ -387,28 +408,28 @@ final class GaloisCounterMode extends FeedbackCipher {
* @param outOfs the offset in <code>out</code> * @param outOfs the offset in <code>out</code>
* @return the number of bytes placed into the <code>out</code> buffer * @return the number of bytes placed into the <code>out</code> buffer
*/ */
int encryptFinal(byte[] in, int inOfs, int len, byte[] out, int outOfs) int encryptFinal(byte[] in, int inOfs, int len, byte[] out, int outOfs)
throws IllegalBlockSizeException { throws IllegalBlockSizeException, ShortBufferException {
if (out.length - outOfs < (len + tagLenBytes)) { if (out.length - outOfs < (len + tagLenBytes)) {
throw new RuntimeException("Output buffer too small"); throw new ShortBufferException("Output buffer too small");
} }
processAAD(); processAAD();
if (len > 0) { if (len > 0) {
//ByteUtil.dumpArray(Arrays.copyOfRange(in, inOfs, inOfs + len)); doLastBlock(in, inOfs, len, out, outOfs, true);
doLastBlock(in, inOfs, len, out, outOfs, true); }
}
byte[] lengthBlock =
byte[] lengthBlock = getLengthBlock(sizeOfAAD*8, processed*8); getLengthBlock(sizeOfAAD*8, processed*8);
ghashAllToS.update(lengthBlock); ghashAllToS.update(lengthBlock);
byte[] s = ghashAllToS.digest(); byte[] s = ghashAllToS.digest();
byte[] sOut = new byte[s.length]; byte[] sOut = new byte[s.length];
GCTR gctrForSToTag = new GCTR(embeddedCipher, this.preCounterBlock); GCTR gctrForSToTag = new GCTR(embeddedCipher, this.preCounterBlock);
gctrForSToTag.doFinal(s, 0, s.length, sOut, 0); gctrForSToTag.doFinal(s, 0, s.length, sOut, 0);
System.arraycopy(sOut, 0, out, (outOfs + len), tagLenBytes); System.arraycopy(sOut, 0, out, (outOfs + len), tagLenBytes);
return (len + tagLenBytes); return (len + tagLenBytes);
} }
/** /**
* Performs decryption operation. * Performs decryption operation.
...@@ -432,14 +453,16 @@ final class GaloisCounterMode extends FeedbackCipher { ...@@ -432,14 +453,16 @@ final class GaloisCounterMode extends FeedbackCipher {
* @param out the buffer for the result * @param out the buffer for the result
* @param outOfs the offset in <code>out</code> * @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(); processAAD();
if (len > 0) { // must be at least AES_BLOCK_SIZE bytes long if (len > 0) {
gctrPAndC.update(in, inOfs, len, out, outOfs); // store internally until decryptFinal is called because
processed += len; // spec mentioned that only return recovered data after tag
ghashAllToS.update(in, inOfs, len); // is successfully verified
ibuffer.write(in, inOfs, len);
} }
return 0;
} }
/** /**
...@@ -458,44 +481,62 @@ final class GaloisCounterMode extends FeedbackCipher { ...@@ -458,44 +481,62 @@ final class GaloisCounterMode extends FeedbackCipher {
* @param outOfs the offset in <code>plain</code> * @param outOfs the offset in <code>plain</code>
* @return the number of bytes placed into the <code>out</code> buffer * @return the number of bytes placed into the <code>out</code> buffer
*/ */
int decryptFinal(byte[] in, int inOfs, int len, int decryptFinal(byte[] in, int inOfs, int len,
byte[] out, int outOfs) byte[] out, int outOfs)
throws IllegalBlockSizeException, AEADBadTagException { throws IllegalBlockSizeException, AEADBadTagException,
if (len < tagLenBytes) { ShortBufferException {
throw new RuntimeException("Input buffer too short - need tag"); if (len < tagLenBytes) {
} throw new AEADBadTagException("Input too short - need tag");
if (out.length - outOfs < (len - tagLenBytes)) { }
throw new RuntimeException("Output buffer too small"); if (out.length - outOfs < ((ibuffer.size() + len) - tagLenBytes)) {
} throw new ShortBufferException("Output buffer too small");
processAAD(); }
processAAD();
int processedOld = processed; if (len != 0) {
byte[] tag = new byte[tagLenBytes]; ibuffer.write(in, inOfs, len);
// get the trailing tag bytes from 'in' }
System.arraycopy(in, inOfs + len - tagLenBytes, tag, 0, tagLenBytes);
len -= tagLenBytes; // refresh 'in' to all buffered-up bytes
in = ibuffer.toByteArray();
if (len > 0) { inOfs = 0;
doLastBlock(in, inOfs, len, out, outOfs, false); len = in.length;
} ibuffer.reset();
byte[] lengthBlock = getLengthBlock(sizeOfAAD*8, processed*8); byte[] tag = new byte[tagLenBytes];
ghashAllToS.update(lengthBlock); // get the trailing tag bytes from 'in'
System.arraycopy(in, len - tagLenBytes, tag, 0, tagLenBytes);
byte[] s = ghashAllToS.digest(); len -= tagLenBytes;
byte[] sOut = new byte[s.length];
GCTR gctrForSToTag = new GCTR(embeddedCipher, this.preCounterBlock); if (len > 0) {
gctrForSToTag.doFinal(s, 0, s.length, sOut, 0); doLastBlock(in, inOfs, len, out, outOfs, false);
for (int i = 0; i < tagLenBytes; i++) { }
if (tag[i] != sOut[i]) {
throw new AEADBadTagException("Tag mismatch!"); byte[] lengthBlock =
} getLengthBlock(sizeOfAAD*8, processed*8);
} ghashAllToS.update(lengthBlock);
return len;
} 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 // return tag length in bytes
int getTagLen() { int getTagLen() {
return this.tagLenBytes; 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. * 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
...@@ -149,9 +149,10 @@ final class OutputFeedback extends FeedbackCipher { ...@@ -149,9 +149,10 @@ final class OutputFeedback extends FeedbackCipher {
* @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 length of the encrypted data
*/ */
void encrypt(byte[] plain, int plainOffset, int plainLen, int encrypt(byte[] plain, int plainOffset, int plainLen,
byte[] cipher, int cipherOffset) byte[] cipher, int cipherOffset)
{ {
int i; int i;
int len = blockSize - numBytes; int len = blockSize - numBytes;
...@@ -195,6 +196,7 @@ final class OutputFeedback extends FeedbackCipher { ...@@ -195,6 +196,7 @@ final class OutputFeedback extends FeedbackCipher {
System.arraycopy(k, 0, register, len, numBytes); System.arraycopy(k, 0, register, len, numBytes);
} }
} }
return plainLen;
} }
/** /**
...@@ -219,11 +221,12 @@ final class OutputFeedback extends FeedbackCipher { ...@@ -219,11 +221,12 @@ final class OutputFeedback extends FeedbackCipher {
* @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 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) byte[] plain, int plainOffset)
{ {
// OFB encrypt and decrypt are identical // 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 { ...@@ -136,8 +136,8 @@ final class PCBC extends FeedbackCipher {
* @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>
*/ */
void encrypt(byte[] plain, int plainOffset, int plainLen, int encrypt(byte[] plain, int plainOffset, int plainLen,
byte[] cipher, int cipherOffset) byte[] cipher, int cipherOffset)
{ {
int i; int i;
int endIndex = plainOffset + plainLen; int endIndex = plainOffset + plainLen;
...@@ -152,6 +152,7 @@ final class PCBC extends FeedbackCipher { ...@@ -152,6 +152,7 @@ final class PCBC extends FeedbackCipher {
k[i] = (byte)(plain[i+plainOffset] ^ cipher[i+cipherOffset]); k[i] = (byte)(plain[i+plainOffset] ^ cipher[i+cipherOffset]);
} }
} }
return plainLen;
} }
/** /**
...@@ -177,8 +178,8 @@ final class PCBC extends FeedbackCipher { ...@@ -177,8 +178,8 @@ final class PCBC extends FeedbackCipher {
* @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>
*/ */
void decrypt(byte[] cipher, int cipherOffset, int cipherLen, int decrypt(byte[] cipher, int cipherOffset, int cipherLen,
byte[] plain, int plainOffset) byte[] plain, int plainOffset)
{ {
int i; int i;
int endIndex = cipherOffset + cipherLen; int endIndex = cipherOffset + cipherLen;
...@@ -194,5 +195,6 @@ final class PCBC extends FeedbackCipher { ...@@ -194,5 +195,6 @@ final class PCBC extends FeedbackCipher {
k[i] = (byte)(plain[i+plainOffset] ^ cipher[i+cipherOffset]); k[i] = (byte)(plain[i+plainOffset] ^ cipher[i+cipherOffset]);
} }
} }
return cipherLen;
} }
} }
...@@ -786,7 +786,9 @@ public abstract class CipherSpi { ...@@ -786,7 +786,9 @@ public abstract class CipherSpi {
int total = 0; int total = 0;
do { do {
int chunk = Math.min(inLen, inArray.length); int chunk = Math.min(inLen, inArray.length);
input.get(inArray, 0, chunk); if (chunk > 0) {
input.get(inArray, 0, chunk);
}
int n; int n;
if (isUpdate || (inLen != chunk)) { if (isUpdate || (inLen != chunk)) {
n = engineUpdate(inArray, 0, chunk, outArray, outOfs); n = engineUpdate(inArray, 0, chunk, outArray, outOfs);
...@@ -814,8 +816,9 @@ public abstract class CipherSpi { ...@@ -814,8 +816,9 @@ public abstract class CipherSpi {
int total = 0; int total = 0;
boolean resized = false; boolean resized = false;
do { do {
int chunk = Math.min(inLen, outSize); int chunk =
if ((a1 == false) && (resized == false)) { Math.min(inLen, (outSize == 0? inArray.length : outSize));
if (!a1 && !resized && chunk > 0) {
input.get(inArray, 0, chunk); input.get(inArray, 0, chunk);
inOfs = 0; inOfs = 0;
} }
...@@ -829,8 +832,10 @@ public abstract class CipherSpi { ...@@ -829,8 +832,10 @@ public abstract class CipherSpi {
resized = false; resized = false;
inOfs += chunk; inOfs += chunk;
inLen -= chunk; inLen -= chunk;
output.put(outArray, 0, n); if (n > 0) {
total += n; output.put(outArray, 0, n);
total += n;
}
} catch (ShortBufferException e) { } catch (ShortBufferException e) {
if (resized) { if (resized) {
// we just resized the output buffer, but it still // we just resized the output buffer, but it still
...@@ -840,11 +845,13 @@ public abstract class CipherSpi { ...@@ -840,11 +845,13 @@ public abstract class CipherSpi {
} }
// output buffer is too small, realloc and try again // output buffer is too small, realloc and try again
resized = true; resized = true;
int newOut = engineGetOutputSize(chunk); outSize = engineGetOutputSize(chunk);
outArray = new byte[newOut]; outArray = new byte[outSize];
} }
} while (inLen > 0); } while (inLen > 0);
input.position(inLimit); if (a1) {
input.position(inLimit);
}
return total; 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.
先完成此消息的编辑!
想要评论请 注册