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.
|
* 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,8 +135,9 @@ 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;
|
||||||
@ -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,12 +176,13 @@ 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;
|
||||||
@ -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 {
|
|||||||
System.arraycopy(cipherOrig, cipherOffset, r, 0, blockSize);
|
System.arraycopy(cipherOrig, cipherOffset, r, 0, blockSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return cipherLen;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,7 +331,18 @@ final class CipherCore {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
totalLen = getOutputSize(inputLen);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
return totalLen;
|
return totalLen;
|
||||||
@ -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;
|
|
||||||
int bufferedConsumed = buffered;
|
|
||||||
if (inputConsumed < 0) {
|
|
||||||
inputConsumed = 0;
|
|
||||||
bufferedConsumed = len;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buffered != 0) {
|
|
||||||
System.arraycopy(buffer, 0, in, 0, bufferedConsumed);
|
|
||||||
}
|
|
||||||
if (inputConsumed > 0) {
|
|
||||||
System.arraycopy(input, inputOffset, in,
|
|
||||||
bufferedConsumed, inputConsumed);
|
|
||||||
}
|
|
||||||
if (decrypting) {
|
if (decrypting) {
|
||||||
cipher.decrypt(in, 0, len, output, outputOffset);
|
outLen = cipher.decrypt(buffer, 0, len, output, outputOffset);
|
||||||
} else {
|
} else {
|
||||||
cipher.encrypt(in, 0, len, output, outputOffset);
|
outLen = cipher.encrypt(buffer, 0, len, output, outputOffset);
|
||||||
|
}
|
||||||
|
buffered -= len;
|
||||||
|
if (buffered != 0) {
|
||||||
|
System.arraycopy(buffer, len, buffer, 0, buffered);
|
||||||
|
}
|
||||||
|
} else { // len > buffered
|
||||||
|
if (buffered == 0) {
|
||||||
|
// all to-be-processed data are from 'input'
|
||||||
|
if (decrypting) {
|
||||||
|
outLen = cipher.decrypt(input, inputOffset, len, output, outputOffset);
|
||||||
|
} else {
|
||||||
|
outLen = cipher.encrypt(input, inputOffset, len, output, outputOffset);
|
||||||
|
}
|
||||||
|
inputOffset += len;
|
||||||
|
inputLen -= len;
|
||||||
|
} else {
|
||||||
|
// assemble the data using both 'buffer' and 'input'
|
||||||
|
byte[] in = new byte[len];
|
||||||
|
System.arraycopy(buffer, 0, in, 0, buffered);
|
||||||
|
int inConsumed = len - buffered;
|
||||||
|
System.arraycopy(input, inputOffset, in, buffered, inConsumed);
|
||||||
|
buffered = 0;
|
||||||
|
inputOffset += inConsumed;
|
||||||
|
inputLen -= inConsumed;
|
||||||
|
if (decrypting) {
|
||||||
|
outLen = cipher.decrypt(in, 0, len, output, outputOffset);
|
||||||
|
} else {
|
||||||
|
outLen = cipher.encrypt(in, 0, len, output, outputOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let's keep track of how many bytes are needed to make
|
// 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 {
|
|||||||
((len - diffBlocksize) % blockSize);
|
((len - diffBlocksize) % blockSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inputLen -= inputConsumed;
|
|
||||||
inputOffset += inputConsumed;
|
|
||||||
outputOffset += len;
|
|
||||||
buffered -= bufferedConsumed;
|
|
||||||
if (buffered > 0) {
|
|
||||||
System.arraycopy(buffer, bufferedConsumed, buffer, 0,
|
|
||||||
buffered);
|
|
||||||
}
|
}
|
||||||
}
|
// Store remaining input into 'buffer' again
|
||||||
// left over 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 len;
|
}
|
||||||
|
return outLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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;
|
||||||
|
@ -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.
|
* 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,8 +150,9 @@ 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;
|
||||||
@ -194,6 +195,7 @@ final class CipherFeedback extends FeedbackCipher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return plainLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -218,8 +220,9 @@ 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;
|
||||||
@ -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.
|
* 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 {
|
|||||||
* @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 {
|
|||||||
* @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 {
|
|||||||
* 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 {
|
|||||||
}
|
}
|
||||||
out[outOff++] = (byte)(in[inOff++] ^ encryptedCounter[used++]);
|
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.
|
* 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 {
|
|||||||
* @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 {
|
|||||||
* @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,8 +133,9 @@ 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,9 +155,8 @@ 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,8 +174,9 @@ 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);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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;
|
|||||||
* 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 {
|
|||||||
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 {
|
|||||||
// 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 {
|
|||||||
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 {
|
|||||||
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -200,6 +213,10 @@ final class GaloisCounterMode extends FeedbackCipher {
|
|||||||
}
|
}
|
||||||
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 {
|
|||||||
}
|
}
|
||||||
processed = 0;
|
processed = 0;
|
||||||
sizeOfAAD = 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
|
// 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 {
|
|||||||
* @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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -388,18 +409,18 @@ final class GaloisCounterMode extends FeedbackCipher {
|
|||||||
* @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 = getLengthBlock(sizeOfAAD*8, processed*8);
|
byte[] lengthBlock =
|
||||||
|
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];
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -460,26 +483,36 @@ final class GaloisCounterMode extends FeedbackCipher {
|
|||||||
*/
|
*/
|
||||||
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,
|
||||||
|
ShortBufferException {
|
||||||
if (len < tagLenBytes) {
|
if (len < tagLenBytes) {
|
||||||
throw new RuntimeException("Input buffer too short - need tag");
|
throw new AEADBadTagException("Input too short - need tag");
|
||||||
}
|
}
|
||||||
if (out.length - outOfs < (len - tagLenBytes)) {
|
if (out.length - outOfs < ((ibuffer.size() + len) - tagLenBytes)) {
|
||||||
throw new RuntimeException("Output buffer too small");
|
throw new ShortBufferException("Output buffer too small");
|
||||||
}
|
}
|
||||||
processAAD();
|
processAAD();
|
||||||
|
if (len != 0) {
|
||||||
|
ibuffer.write(in, inOfs, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
// refresh 'in' to all buffered-up bytes
|
||||||
|
in = ibuffer.toByteArray();
|
||||||
|
inOfs = 0;
|
||||||
|
len = in.length;
|
||||||
|
ibuffer.reset();
|
||||||
|
|
||||||
int processedOld = processed;
|
|
||||||
byte[] tag = new byte[tagLenBytes];
|
byte[] tag = new byte[tagLenBytes];
|
||||||
// get the trailing tag bytes from 'in'
|
// get the trailing tag bytes from 'in'
|
||||||
System.arraycopy(in, inOfs + len - tagLenBytes, tag, 0, tagLenBytes);
|
System.arraycopy(in, len - tagLenBytes, tag, 0, tagLenBytes);
|
||||||
len -= tagLenBytes;
|
len -= tagLenBytes;
|
||||||
|
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
doLastBlock(in, inOfs, len, out, outOfs, false);
|
doLastBlock(in, inOfs, len, out, outOfs, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] lengthBlock = getLengthBlock(sizeOfAAD*8, processed*8);
|
byte[] lengthBlock =
|
||||||
|
getLengthBlock(sizeOfAAD*8, processed*8);
|
||||||
ghashAllToS.update(lengthBlock);
|
ghashAllToS.update(lengthBlock);
|
||||||
|
|
||||||
byte[] s = ghashAllToS.digest();
|
byte[] s = ghashAllToS.digest();
|
||||||
@ -498,4 +531,12 @@ final class GaloisCounterMode extends FeedbackCipher {
|
|||||||
int getTagLen() {
|
int getTagLen() {
|
||||||
return this.tagLenBytes;
|
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.
|
* 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,8 +149,9 @@ 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;
|
||||||
@ -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 {
|
|||||||
* @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,7 +136,7 @@ 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;
|
||||||
@ -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,7 +178,7 @@ 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;
|
||||||
@ -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 {
|
|||||||
int total = 0;
|
int total = 0;
|
||||||
do {
|
do {
|
||||||
int chunk = Math.min(inLen, inArray.length);
|
int chunk = Math.min(inLen, inArray.length);
|
||||||
|
if (chunk > 0) {
|
||||||
input.get(inArray, 0, chunk);
|
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 {
|
|||||||
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 {
|
|||||||
resized = false;
|
resized = false;
|
||||||
inOfs += chunk;
|
inOfs += chunk;
|
||||||
inLen -= chunk;
|
inLen -= chunk;
|
||||||
|
if (n > 0) {
|
||||||
output.put(outArray, 0, n);
|
output.put(outArray, 0, n);
|
||||||
total += 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 {
|
|||||||
}
|
}
|
||||||
// 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);
|
||||||
|
if (a1) {
|
||||||
input.position(inLimit);
|
input.position(inLimit);
|
||||||
|
}
|
||||||
return total;
|
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