8012900: CICO ignores AAD in GCM mode
Change GCM decryption to not return result until tag verification passed Reviewed-by: xuelei
This commit is contained in:
parent
2f4af22f17
commit
a5add24f51
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -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");
|
||||
}
|
||||
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) {
|
||||
//ByteUtil.dumpArray(Arrays.copyOfRange(in, inOfs, inOfs + len));
|
||||
doLastBlock(in, inOfs, len, out, outOfs, true);
|
||||
}
|
||||
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);
|
||||
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);
|
||||
}
|
||||
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 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);
|
||||
}
|
||||
|
||||
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;
|
||||
// refresh 'in' to all buffered-up bytes
|
||||
in = ibuffer.toByteArray();
|
||||
inOfs = 0;
|
||||
len = in.length;
|
||||
ibuffer.reset();
|
||||
|
||||
if (len > 0) {
|
||||
doLastBlock(in, inOfs, len, out, outOfs, false);
|
||||
}
|
||||
byte[] tag = new byte[tagLenBytes];
|
||||
// get the trailing tag bytes from 'in'
|
||||
System.arraycopy(in, len - tagLenBytes, tag, 0, tagLenBytes);
|
||||
len -= tagLenBytes;
|
||||
|
||||
byte[] lengthBlock = getLengthBlock(sizeOfAAD*8, processed*8);
|
||||
ghashAllToS.update(lengthBlock);
|
||||
if (len > 0) {
|
||||
doLastBlock(in, inOfs, len, out, outOfs, false);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user