8255557: Decouple GCM from CipherCore
Reviewed-by: valeriep
This commit is contained in:
parent
e546ae27ff
commit
c7c77fd32b
@ -138,21 +138,6 @@ abstract class AESCipher extends CipherSpi {
|
||||
super(32, "CFB", "NOPADDING");
|
||||
}
|
||||
}
|
||||
public static final class AES128_GCM_NoPadding extends OidImpl {
|
||||
public AES128_GCM_NoPadding() {
|
||||
super(16, "GCM", "NOPADDING");
|
||||
}
|
||||
}
|
||||
public static final class AES192_GCM_NoPadding extends OidImpl {
|
||||
public AES192_GCM_NoPadding() {
|
||||
super(24, "GCM", "NOPADDING");
|
||||
}
|
||||
}
|
||||
public static final class AES256_GCM_NoPadding extends OidImpl {
|
||||
public AES256_GCM_NoPadding() {
|
||||
super(32, "GCM", "NOPADDING");
|
||||
}
|
||||
}
|
||||
|
||||
// utility method used by AESCipher and AESWrapCipher
|
||||
static final void checkKeySize(Key key, int fixedKeySize)
|
||||
@ -185,10 +170,6 @@ abstract class AESCipher extends CipherSpi {
|
||||
*/
|
||||
private final int fixedKeySize; // in bytes, -1 if no restriction
|
||||
|
||||
/*
|
||||
* needed to enforce ISE thrown when updateAAD is called after update for GCM mode.
|
||||
*/
|
||||
private boolean updateCalled;
|
||||
|
||||
/**
|
||||
* Creates an instance of AES cipher with default ECB mode and
|
||||
@ -322,7 +303,6 @@ abstract class AESCipher extends CipherSpi {
|
||||
protected void engineInit(int opmode, Key key, SecureRandom random)
|
||||
throws InvalidKeyException {
|
||||
checkKeySize(key, fixedKeySize);
|
||||
updateCalled = false;
|
||||
core.init(opmode, key, random);
|
||||
}
|
||||
|
||||
@ -355,7 +335,6 @@ abstract class AESCipher extends CipherSpi {
|
||||
SecureRandom random)
|
||||
throws InvalidKeyException, InvalidAlgorithmParameterException {
|
||||
checkKeySize(key, fixedKeySize);
|
||||
updateCalled = false;
|
||||
core.init(opmode, key, params, random);
|
||||
}
|
||||
|
||||
@ -364,7 +343,6 @@ abstract class AESCipher extends CipherSpi {
|
||||
SecureRandom random)
|
||||
throws InvalidKeyException, InvalidAlgorithmParameterException {
|
||||
checkKeySize(key, fixedKeySize);
|
||||
updateCalled = false;
|
||||
core.init(opmode, key, params, random);
|
||||
}
|
||||
|
||||
@ -389,7 +367,6 @@ abstract class AESCipher extends CipherSpi {
|
||||
*/
|
||||
protected byte[] engineUpdate(byte[] input, int inputOffset,
|
||||
int inputLen) {
|
||||
updateCalled = true;
|
||||
return core.update(input, inputOffset, inputLen);
|
||||
}
|
||||
|
||||
@ -419,7 +396,6 @@ abstract class AESCipher extends CipherSpi {
|
||||
protected int engineUpdate(byte[] input, int inputOffset, int inputLen,
|
||||
byte[] output, int outputOffset)
|
||||
throws ShortBufferException {
|
||||
updateCalled = true;
|
||||
return core.update(input, inputOffset, inputLen, output,
|
||||
outputOffset);
|
||||
}
|
||||
@ -458,7 +434,6 @@ abstract class AESCipher extends CipherSpi {
|
||||
protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
|
||||
throws IllegalBlockSizeException, BadPaddingException {
|
||||
byte[] out = core.doFinal(input, inputOffset, inputLen);
|
||||
updateCalled = false;
|
||||
return out;
|
||||
}
|
||||
|
||||
@ -504,7 +479,6 @@ abstract class AESCipher extends CipherSpi {
|
||||
BadPaddingException {
|
||||
int outLen = core.doFinal(input, inputOffset, inputLen, output,
|
||||
outputOffset);
|
||||
updateCalled = false;
|
||||
return outLen;
|
||||
}
|
||||
|
||||
@ -577,86 +551,6 @@ abstract class AESCipher extends CipherSpi {
|
||||
wrappedKeyType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Continues a multi-part update of the Additional Authentication
|
||||
* Data (AAD), using a subset of the provided buffer.
|
||||
* <p>
|
||||
* Calls to this method provide AAD to the cipher when operating in
|
||||
* modes such as AEAD (GCM/CCM). If this cipher is operating in
|
||||
* either GCM or CCM mode, all AAD must be supplied before beginning
|
||||
* operations on the ciphertext (via the {@code update} and {@code
|
||||
* doFinal} methods).
|
||||
*
|
||||
* @param src the buffer containing the AAD
|
||||
* @param offset the offset in {@code src} where the AAD input starts
|
||||
* @param len the number of AAD bytes
|
||||
*
|
||||
* @throws IllegalStateException if this cipher is in a wrong state
|
||||
* (e.g., has not been initialized), does not accept AAD, or if
|
||||
* operating in either GCM or CCM mode and one of the {@code update}
|
||||
* methods has already been called for the active
|
||||
* encryption/decryption operation
|
||||
* @throws UnsupportedOperationException if this method
|
||||
* has not been overridden by an implementation
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
@Override
|
||||
protected void engineUpdateAAD(byte[] src, int offset, int len) {
|
||||
if (core.getMode() == CipherCore.GCM_MODE && updateCalled) {
|
||||
throw new IllegalStateException("AAD must be supplied before encryption/decryption starts");
|
||||
}
|
||||
core.updateAAD(src, offset, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Continues a multi-part update of the Additional Authentication
|
||||
* Data (AAD).
|
||||
* <p>
|
||||
* Calls to this method provide AAD to the cipher when operating in
|
||||
* modes such as AEAD (GCM/CCM). If this cipher is operating in
|
||||
* either GCM or CCM mode, all AAD must be supplied before beginning
|
||||
* operations on the ciphertext (via the {@code update} and {@code
|
||||
* doFinal} methods).
|
||||
* <p>
|
||||
* All {@code src.remaining()} bytes starting at
|
||||
* {@code src.position()} are processed.
|
||||
* Upon return, the input buffer's position will be equal
|
||||
* to its limit; its limit will not have changed.
|
||||
*
|
||||
* @param src the buffer containing the AAD
|
||||
*
|
||||
* @throws IllegalStateException if this cipher is in a wrong state
|
||||
* (e.g., has not been initialized), does not accept AAD, or if
|
||||
* operating in either GCM or CCM mode and one of the {@code update}
|
||||
* methods has already been called for the active
|
||||
* encryption/decryption operation
|
||||
* @throws UnsupportedOperationException if this method
|
||||
* has not been overridden by an implementation
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
@Override
|
||||
protected void engineUpdateAAD(ByteBuffer src) {
|
||||
if (core.getMode() == CipherCore.GCM_MODE && updateCalled) {
|
||||
throw new IllegalStateException("AAD must be supplied before encryption/decryption starts");
|
||||
}
|
||||
if (src != null) {
|
||||
int aadLen = src.limit() - src.position();
|
||||
if (aadLen > 0) {
|
||||
if (src.hasArray()) {
|
||||
int aadOfs = Math.addExact(src.arrayOffset(), src.position());
|
||||
core.updateAAD(src.array(), aadOfs, aadLen);
|
||||
src.position(src.limit());
|
||||
} else {
|
||||
byte[] aad = new byte[aadLen];
|
||||
src.get(aad);
|
||||
core.updateAAD(aad, 0, aadLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalize crypto operation with ByteBuffers
|
||||
*
|
||||
@ -672,10 +566,6 @@ abstract class AESCipher extends CipherSpi {
|
||||
protected int engineDoFinal(ByteBuffer input, ByteBuffer output)
|
||||
throws ShortBufferException, IllegalBlockSizeException,
|
||||
BadPaddingException {
|
||||
if (core.getMode() == CipherCore.GCM_MODE && !input.hasArray()) {
|
||||
return core.gcmDoFinal(input, output);
|
||||
} else {
|
||||
return super.engineDoFinal(input, output);
|
||||
}
|
||||
return super.engineDoFinal(input, output);
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,6 @@
|
||||
|
||||
package com.sun.crypto.provider;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
||||
@ -83,7 +82,6 @@ final class CipherCore {
|
||||
* currently, only the following cases have non-zero values:
|
||||
* 1) CTS mode - due to its special handling on the last two blocks
|
||||
* (the last one may be incomplete).
|
||||
* 2) GCM mode + decryption - due to its trailing tag bytes
|
||||
*/
|
||||
private int minBytes = 0;
|
||||
|
||||
@ -125,24 +123,6 @@ final class CipherCore {
|
||||
private static final int PCBC_MODE = 4;
|
||||
private static final int CTR_MODE = 5;
|
||||
private static final int CTS_MODE = 6;
|
||||
static final int GCM_MODE = 7;
|
||||
|
||||
/*
|
||||
* variables used for performing the GCM (key+iv) uniqueness check.
|
||||
* To use GCM mode safely, the cipher object must be re-initialized
|
||||
* with a different combination of key + iv values for each
|
||||
* encryption operation. However, checking all past key + iv values
|
||||
* isn't feasible. Thus, we only do a per-instance check of the
|
||||
* key + iv values used in previous encryption.
|
||||
* For decryption operations, no checking is necessary.
|
||||
* NOTE: this key+iv check have to be done inside CipherCore class
|
||||
* since CipherCore class buffers potential tag bytes in GCM mode
|
||||
* and may not call GaloisCounterMode when there isn't sufficient
|
||||
* input to process.
|
||||
*/
|
||||
private boolean requireReinit = false;
|
||||
private byte[] lastEncKey = null;
|
||||
private byte[] lastEncIv = null;
|
||||
|
||||
/**
|
||||
* Creates an instance of CipherCore with default ECB mode and
|
||||
@ -197,15 +177,6 @@ final class CipherCore {
|
||||
cipher = new CounterMode(rawImpl);
|
||||
unitBytes = 1;
|
||||
padding = null;
|
||||
} else if (modeUpperCase.equals("GCM")) {
|
||||
// can only be used for block ciphers w/ 128-bit block size
|
||||
if (blockSize != 16) {
|
||||
throw new NoSuchAlgorithmException
|
||||
("GCM mode can only be used for AES cipher");
|
||||
}
|
||||
cipherMode = GCM_MODE;
|
||||
cipher = new GaloisCounterMode(rawImpl);
|
||||
padding = null;
|
||||
} else if (modeUpperCase.startsWith("CFB")) {
|
||||
cipherMode = CFB_MODE;
|
||||
unitBytes = getNumOfUnit(mode, "CFB".length(), blockSize);
|
||||
@ -224,15 +195,6 @@ final class CipherCore {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mode of this cipher.
|
||||
*
|
||||
* @return the parsed cipher mode
|
||||
*/
|
||||
int getMode() {
|
||||
return cipherMode;
|
||||
}
|
||||
|
||||
private static int getNumOfUnit(String mode, int offset, int blockSize)
|
||||
throws NoSuchAlgorithmException {
|
||||
int result = blockSize; // use blockSize as default value
|
||||
@ -279,17 +241,13 @@ final class CipherCore {
|
||||
+ " not implemented");
|
||||
}
|
||||
if ((padding != null) &&
|
||||
((cipherMode == CTR_MODE) || (cipherMode == CTS_MODE)
|
||||
|| (cipherMode == GCM_MODE))) {
|
||||
((cipherMode == CTR_MODE) || (cipherMode == CTS_MODE))) {
|
||||
padding = null;
|
||||
String modeStr = null;
|
||||
switch (cipherMode) {
|
||||
case CTR_MODE:
|
||||
modeStr = "CTR";
|
||||
break;
|
||||
case GCM_MODE:
|
||||
modeStr = "GCM";
|
||||
break;
|
||||
case CTS_MODE:
|
||||
modeStr = "CTS";
|
||||
break;
|
||||
@ -310,7 +268,7 @@ final class CipherCore {
|
||||
* <code>inputLen</code> (in bytes).
|
||||
*
|
||||
* <p>This call takes into account any unprocessed (buffered) data from a
|
||||
* previous <code>update</code> call, padding, and AEAD tagging.
|
||||
* previous <code>update</code> call, and padding.
|
||||
*
|
||||
* <p>The actual output length of the next <code>update</code> or
|
||||
* <code>doFinal</code> call may be smaller than the length returned by
|
||||
@ -326,36 +284,19 @@ final class CipherCore {
|
||||
}
|
||||
|
||||
private int getOutputSizeByOperation(int inputLen, boolean isDoFinal) {
|
||||
int totalLen = Math.addExact(buffered, cipher.getBufferedLength());
|
||||
int totalLen = buffered;
|
||||
totalLen = Math.addExact(totalLen, inputLen);
|
||||
switch (cipherMode) {
|
||||
case GCM_MODE:
|
||||
if (isDoFinal) {
|
||||
int tagLen = ((GaloisCounterMode) cipher).getTagLen();
|
||||
if (!decrypting) {
|
||||
totalLen = Math.addExact(totalLen, tagLen);
|
||||
if (padding != null && !decrypting) {
|
||||
if (unitBytes != blockSize) {
|
||||
if (totalLen < diffBlocksize) {
|
||||
totalLen = diffBlocksize;
|
||||
} else {
|
||||
totalLen -= tagLen;
|
||||
int residue = (totalLen - diffBlocksize) % blockSize;
|
||||
totalLen = Math.addExact(totalLen, (blockSize - residue));
|
||||
}
|
||||
} else {
|
||||
totalLen = Math.addExact(totalLen, padding.padLength(totalLen));
|
||||
}
|
||||
if (totalLen < 0) {
|
||||
totalLen = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (padding != null && !decrypting) {
|
||||
if (unitBytes != blockSize) {
|
||||
if (totalLen < diffBlocksize) {
|
||||
totalLen = diffBlocksize;
|
||||
} else {
|
||||
int residue = (totalLen - diffBlocksize) % blockSize;
|
||||
totalLen = Math.addExact(totalLen, (blockSize - residue));
|
||||
}
|
||||
} else {
|
||||
totalLen = Math.addExact(totalLen, padding.padLength(totalLen));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return totalLen;
|
||||
}
|
||||
@ -398,26 +339,15 @@ final class CipherCore {
|
||||
AlgorithmParameterSpec spec;
|
||||
byte[] iv = getIV();
|
||||
if (iv == null) {
|
||||
// generate spec using default value
|
||||
if (cipherMode == GCM_MODE) {
|
||||
iv = new byte[GaloisCounterMode.DEFAULT_IV_LEN];
|
||||
} else {
|
||||
iv = new byte[blockSize];
|
||||
}
|
||||
iv = new byte[blockSize];
|
||||
SunJCE.getRandom().nextBytes(iv);
|
||||
}
|
||||
if (cipherMode == GCM_MODE) {
|
||||
algName = "GCM";
|
||||
spec = new GCMParameterSpec
|
||||
(((GaloisCounterMode) cipher).getTagLen()*8, iv);
|
||||
if (algName.equals("RC2")) {
|
||||
RC2Crypt rawImpl = (RC2Crypt) cipher.getEmbeddedCipher();
|
||||
spec = new RC2ParameterSpec
|
||||
(rawImpl.getEffectiveKeyBits(), iv);
|
||||
} else {
|
||||
if (algName.equals("RC2")) {
|
||||
RC2Crypt rawImpl = (RC2Crypt) cipher.getEmbeddedCipher();
|
||||
spec = new RC2ParameterSpec
|
||||
(rawImpl.getEffectiveKeyBits(), iv);
|
||||
} else {
|
||||
spec = new IvParameterSpec(iv);
|
||||
}
|
||||
spec = new IvParameterSpec(iv);
|
||||
}
|
||||
try {
|
||||
params = AlgorithmParameters.getInstance(algName,
|
||||
@ -504,106 +434,51 @@ final class CipherCore {
|
||||
|| (opmode == Cipher.UNWRAP_MODE);
|
||||
|
||||
byte[] keyBytes = getKeyBytes(key);
|
||||
try {
|
||||
int tagLen = -1;
|
||||
byte[] ivBytes = null;
|
||||
if (params != null) {
|
||||
if (cipherMode == GCM_MODE) {
|
||||
if (params instanceof GCMParameterSpec) {
|
||||
tagLen = ((GCMParameterSpec) params).getTLen();
|
||||
if (tagLen < 96 || tagLen > 128 || ((tagLen & 0x07) != 0)) {
|
||||
throw new InvalidAlgorithmParameterException
|
||||
("Unsupported TLen value; must be one of " +
|
||||
"{128, 120, 112, 104, 96}");
|
||||
}
|
||||
tagLen = tagLen >> 3;
|
||||
ivBytes = ((GCMParameterSpec) params).getIV();
|
||||
} else {
|
||||
throw new InvalidAlgorithmParameterException
|
||||
("Unsupported parameter: " + params);
|
||||
}
|
||||
} else {
|
||||
if (params instanceof IvParameterSpec) {
|
||||
ivBytes = ((IvParameterSpec) params).getIV();
|
||||
if ((ivBytes == null) || (ivBytes.length != blockSize)) {
|
||||
throw new InvalidAlgorithmParameterException
|
||||
("Wrong IV length: must be " + blockSize +
|
||||
" bytes long");
|
||||
}
|
||||
} else if (params instanceof RC2ParameterSpec) {
|
||||
ivBytes = ((RC2ParameterSpec) params).getIV();
|
||||
if ((ivBytes != null) && (ivBytes.length != blockSize)) {
|
||||
throw new InvalidAlgorithmParameterException
|
||||
("Wrong IV length: must be " + blockSize +
|
||||
" bytes long");
|
||||
}
|
||||
} else {
|
||||
throw new InvalidAlgorithmParameterException
|
||||
("Unsupported parameter: " + params);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cipherMode == ECB_MODE) {
|
||||
if (ivBytes != null) {
|
||||
byte[] ivBytes = null;
|
||||
if (params != null) {
|
||||
if (params instanceof IvParameterSpec) {
|
||||
ivBytes = ((IvParameterSpec) params).getIV();
|
||||
if ((ivBytes == null) || (ivBytes.length != blockSize)) {
|
||||
throw new InvalidAlgorithmParameterException
|
||||
("ECB mode cannot use IV");
|
||||
("Wrong IV length: must be " + blockSize +
|
||||
" bytes long");
|
||||
}
|
||||
} else if (ivBytes == null) {
|
||||
if (decrypting) {
|
||||
throw new InvalidAlgorithmParameterException("Parameters "
|
||||
+ "missing");
|
||||
} else if (params instanceof RC2ParameterSpec) {
|
||||
ivBytes = ((RC2ParameterSpec) params).getIV();
|
||||
if ((ivBytes != null) && (ivBytes.length != blockSize)) {
|
||||
throw new InvalidAlgorithmParameterException
|
||||
("Wrong IV length: must be " + blockSize +
|
||||
" bytes long");
|
||||
}
|
||||
|
||||
if (random == null) {
|
||||
random = SunJCE.getRandom();
|
||||
}
|
||||
if (cipherMode == GCM_MODE) {
|
||||
ivBytes = new byte[GaloisCounterMode.DEFAULT_IV_LEN];
|
||||
} else {
|
||||
ivBytes = new byte[blockSize];
|
||||
}
|
||||
random.nextBytes(ivBytes);
|
||||
}
|
||||
|
||||
buffered = 0;
|
||||
diffBlocksize = blockSize;
|
||||
|
||||
String algorithm = key.getAlgorithm();
|
||||
|
||||
// GCM mode needs additional handling
|
||||
if (cipherMode == GCM_MODE) {
|
||||
if (tagLen == -1) {
|
||||
tagLen = GaloisCounterMode.DEFAULT_TAG_LEN;
|
||||
}
|
||||
if (decrypting) {
|
||||
minBytes = tagLen;
|
||||
} else {
|
||||
// check key+iv for encryption in GCM mode
|
||||
requireReinit =
|
||||
Arrays.equals(ivBytes, lastEncIv) &&
|
||||
MessageDigest.isEqual(keyBytes, lastEncKey);
|
||||
if (requireReinit) {
|
||||
throw new InvalidAlgorithmParameterException
|
||||
("Cannot reuse iv for GCM encryption");
|
||||
}
|
||||
lastEncIv = ivBytes;
|
||||
if (lastEncKey != null) {
|
||||
Arrays.fill(lastEncKey, (byte) 0);
|
||||
}
|
||||
lastEncKey = keyBytes;
|
||||
}
|
||||
((GaloisCounterMode) cipher).init
|
||||
(decrypting, algorithm, keyBytes, ivBytes, tagLen);
|
||||
} else {
|
||||
cipher.init(decrypting, algorithm, keyBytes, ivBytes);
|
||||
}
|
||||
// skip checking key+iv from now on until after doFinal()
|
||||
requireReinit = false;
|
||||
} finally {
|
||||
if (lastEncKey != keyBytes) {
|
||||
Arrays.fill(keyBytes, (byte) 0);
|
||||
throw new InvalidAlgorithmParameterException
|
||||
("Unsupported parameter: " + params);
|
||||
}
|
||||
}
|
||||
if (cipherMode == ECB_MODE) {
|
||||
if (ivBytes != null) {
|
||||
throw new InvalidAlgorithmParameterException
|
||||
("ECB mode cannot use IV");
|
||||
}
|
||||
} else if (ivBytes == null) {
|
||||
if (decrypting) {
|
||||
throw new InvalidAlgorithmParameterException("Parameters "
|
||||
+ "missing");
|
||||
}
|
||||
|
||||
if (random == null) {
|
||||
random = SunJCE.getRandom();
|
||||
}
|
||||
|
||||
ivBytes = new byte[blockSize];
|
||||
random.nextBytes(ivBytes);
|
||||
}
|
||||
|
||||
buffered = 0;
|
||||
diffBlocksize = blockSize;
|
||||
|
||||
String algorithm = key.getAlgorithm();
|
||||
cipher.init(decrypting, algorithm, keyBytes, ivBytes);
|
||||
}
|
||||
|
||||
void init(int opmode, Key key, AlgorithmParameters params,
|
||||
@ -613,16 +488,11 @@ final class CipherCore {
|
||||
String paramType = null;
|
||||
if (params != null) {
|
||||
try {
|
||||
if (cipherMode == GCM_MODE) {
|
||||
paramType = "GCM";
|
||||
spec = params.getParameterSpec(GCMParameterSpec.class);
|
||||
} else {
|
||||
// NOTE: RC2 parameters are always handled through
|
||||
// init(..., AlgorithmParameterSpec,...) method, so
|
||||
// we can assume IvParameterSpec type here.
|
||||
paramType = "IV";
|
||||
spec = params.getParameterSpec(IvParameterSpec.class);
|
||||
}
|
||||
// NOTE: RC2 parameters are always handled through
|
||||
// init(..., AlgorithmParameterSpec,...) method, so
|
||||
// we can assume IvParameterSpec type here.
|
||||
paramType = "IV";
|
||||
spec = params.getParameterSpec(IvParameterSpec.class);
|
||||
} catch (InvalidParameterSpecException ipse) {
|
||||
throw new InvalidAlgorithmParameterException
|
||||
("Wrong parameter type: " + paramType + " expected");
|
||||
@ -671,7 +541,6 @@ final class CipherCore {
|
||||
* (e.g., has not been initialized)
|
||||
*/
|
||||
byte[] update(byte[] input, int inputOffset, int inputLen) {
|
||||
checkReinit();
|
||||
|
||||
byte[] output = null;
|
||||
try {
|
||||
@ -719,7 +588,6 @@ final class CipherCore {
|
||||
*/
|
||||
int update(byte[] input, int inputOffset, int inputLen, byte[] output,
|
||||
int outputOffset) throws ShortBufferException {
|
||||
checkReinit();
|
||||
|
||||
// figure out how much can be sent to crypto function
|
||||
int len = Math.addExact(buffered, inputLen);
|
||||
@ -854,7 +722,6 @@ final class CipherCore {
|
||||
byte[] doFinal(byte[] input, int inputOffset, int inputLen)
|
||||
throws IllegalBlockSizeException, BadPaddingException {
|
||||
try {
|
||||
checkReinit();
|
||||
byte[] output = new byte[getOutputSizeByOperation(inputLen, true)];
|
||||
byte[] finalBuf = prepareInputBuffer(input, inputOffset,
|
||||
inputLen, output, 0);
|
||||
@ -868,7 +735,7 @@ final class CipherCore {
|
||||
if (outLen < output.length) {
|
||||
byte[] copy = Arrays.copyOf(output, outLen);
|
||||
if (decrypting) {
|
||||
// Zero out internal (ouput) array
|
||||
// Zero out internal (output) array
|
||||
Arrays.fill(output, (byte) 0x00);
|
||||
}
|
||||
return copy;
|
||||
@ -921,7 +788,6 @@ final class CipherCore {
|
||||
int outputOffset)
|
||||
throws IllegalBlockSizeException, ShortBufferException,
|
||||
BadPaddingException {
|
||||
checkReinit();
|
||||
|
||||
int estOutSize = getOutputSizeByOperation(inputLen, true);
|
||||
int outputCapacity = checkOutputCapacity(output, outputOffset,
|
||||
@ -943,15 +809,13 @@ final class CipherCore {
|
||||
if (outputCapacity < estOutSize) {
|
||||
cipher.save();
|
||||
}
|
||||
if (getMode() != GCM_MODE || outputCapacity < estOutSize) {
|
||||
// create temporary output buffer if the estimated size is larger
|
||||
// than the user-provided buffer.
|
||||
internalOutput = new byte[estOutSize];
|
||||
offset = 0;
|
||||
}
|
||||
// create temporary output buffer if the estimated size is larger
|
||||
// than the user-provided buffer.
|
||||
internalOutput = new byte[estOutSize];
|
||||
offset = 0;
|
||||
}
|
||||
byte[] outBuffer = (internalOutput != null) ? internalOutput : output;
|
||||
|
||||
byte[] outBuffer = (internalOutput != null) ? internalOutput : output;
|
||||
int outLen = fillOutputBuffer(finalBuf, finalOffset, outBuffer,
|
||||
offset, finalBufLen, input);
|
||||
|
||||
@ -961,13 +825,13 @@ final class CipherCore {
|
||||
// restore so users can retry with a larger buffer
|
||||
cipher.restore();
|
||||
throw new ShortBufferException("Output buffer too short: "
|
||||
+ (outputCapacity)
|
||||
+ " bytes given, " + outLen
|
||||
+ " bytes needed");
|
||||
+ (outputCapacity) + " bytes given, " + outLen
|
||||
+ " bytes needed");
|
||||
}
|
||||
// copy the result into user-supplied output buffer
|
||||
if (internalOutput != null) {
|
||||
System.arraycopy(internalOutput, 0, output, outputOffset, outLen);
|
||||
System.arraycopy(internalOutput, 0, output, outputOffset,
|
||||
outLen);
|
||||
// decrypt mode. Zero out output data that's not required
|
||||
Arrays.fill(internalOutput, (byte) 0x00);
|
||||
}
|
||||
@ -1001,7 +865,7 @@ final class CipherCore {
|
||||
// calculate total input length
|
||||
int len = Math.addExact(buffered, inputLen);
|
||||
// calculate padding length
|
||||
int totalLen = Math.addExact(len, cipher.getBufferedLength());
|
||||
int totalLen = len;
|
||||
int paddingLen = 0;
|
||||
// will the total input length be a multiple of blockSize?
|
||||
if (unitBytes != blockSize) {
|
||||
@ -1059,30 +923,27 @@ final class CipherCore {
|
||||
}
|
||||
|
||||
private int fillOutputBuffer(byte[] finalBuf, int finalOffset,
|
||||
byte[] output, int outOfs, int finalBufLen,
|
||||
byte[] input)
|
||||
throws ShortBufferException, BadPaddingException,
|
||||
IllegalBlockSizeException {
|
||||
byte[] output, int outOfs, int finalBufLen, byte[] input)
|
||||
throws ShortBufferException, BadPaddingException,
|
||||
IllegalBlockSizeException {
|
||||
|
||||
int len;
|
||||
try {
|
||||
len = finalNoPadding(finalBuf, finalOffset, output,
|
||||
outOfs, finalBufLen);
|
||||
outOfs, finalBufLen);
|
||||
if (decrypting && padding != null) {
|
||||
len = unpad(len, outOfs, output);
|
||||
}
|
||||
return len;
|
||||
} finally {
|
||||
if (!decrypting) {
|
||||
// reset after doFinal() for GCM encryption
|
||||
requireReinit = (cipherMode == GCM_MODE);
|
||||
if (finalBuf != input) {
|
||||
// done with internal finalBuf array. Copied to output
|
||||
Arrays.fill(finalBuf, (byte) 0x00);
|
||||
}
|
||||
if (!decrypting && finalBuf != input) {
|
||||
// done with internal finalBuf array. Copied to output
|
||||
Arrays.fill(finalBuf, (byte) 0x00);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private int checkOutputCapacity(byte[] output, int outputOffset,
|
||||
int estOutSize) throws ShortBufferException {
|
||||
// check output buffer capacity.
|
||||
@ -1098,23 +959,14 @@ final class CipherCore {
|
||||
return outputCapacity;
|
||||
}
|
||||
|
||||
private void checkReinit() {
|
||||
if (requireReinit) {
|
||||
throw new IllegalStateException
|
||||
("Must use either different key or iv for GCM encryption");
|
||||
}
|
||||
}
|
||||
|
||||
private int finalNoPadding(byte[] in, int inOfs, byte[] out, int outOfs,
|
||||
int len)
|
||||
throws IllegalBlockSizeException, AEADBadTagException,
|
||||
ShortBufferException {
|
||||
throws IllegalBlockSizeException, ShortBufferException {
|
||||
|
||||
if ((cipherMode != GCM_MODE) && (in == null || len == 0)) {
|
||||
if (in == null || len == 0) {
|
||||
return 0;
|
||||
}
|
||||
if ((cipherMode != CFB_MODE) && (cipherMode != OFB_MODE) &&
|
||||
(cipherMode != GCM_MODE) &&
|
||||
((len % unitBytes) != 0) && (cipherMode != CTS_MODE)) {
|
||||
if (padding != null) {
|
||||
throw new IllegalBlockSizeException
|
||||
@ -1126,7 +978,7 @@ final class CipherCore {
|
||||
+ " bytes");
|
||||
}
|
||||
}
|
||||
int outLen = 0;
|
||||
int outLen;
|
||||
if (decrypting) {
|
||||
outLen = cipher.decryptFinal(in, inOfs, len, out, outOfs);
|
||||
} else {
|
||||
@ -1217,59 +1069,4 @@ final class CipherCore {
|
||||
Arrays.fill(encodedKey, (byte)0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Continues a multi-part update of the Additional Authentication
|
||||
* Data (AAD), using a subset of the provided buffer.
|
||||
* <p>
|
||||
* Calls to this method provide AAD to the cipher when operating in
|
||||
* modes such as AEAD (GCM/CCM). If this cipher is operating in
|
||||
* either GCM or CCM mode, all AAD must be supplied before beginning
|
||||
* operations on the ciphertext (via the {@code update} and {@code
|
||||
* doFinal} methods).
|
||||
*
|
||||
* @param src the buffer containing the AAD
|
||||
* @param offset the offset in {@code src} where the AAD input starts
|
||||
* @param len the number of AAD bytes
|
||||
*
|
||||
* @throws IllegalStateException if this cipher is in a wrong state
|
||||
* (e.g., has not been initialized), does not accept AAD, or if
|
||||
* operating in either GCM or CCM mode and one of the {@code update}
|
||||
* methods has already been called for the active
|
||||
* encryption/decryption operation
|
||||
* @throws UnsupportedOperationException if this method
|
||||
* has not been overridden by an implementation
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
void updateAAD(byte[] src, int offset, int len) {
|
||||
checkReinit();
|
||||
cipher.updateAAD(src, offset, len);
|
||||
}
|
||||
|
||||
// This must only be used with GCM.
|
||||
// If some data has been buffered from an update call, operate on the buffer
|
||||
// then run doFinal.
|
||||
int gcmDoFinal(ByteBuffer src, ByteBuffer dst) throws ShortBufferException,
|
||||
IllegalBlockSizeException, BadPaddingException {
|
||||
int estOutSize = getOutputSizeByOperation(src.remaining(), true);
|
||||
if (estOutSize > dst.remaining()) {
|
||||
throw new ShortBufferException("output buffer too small");
|
||||
}
|
||||
|
||||
int len;
|
||||
if (decrypting) {
|
||||
if (buffered > 0) {
|
||||
cipher.decrypt(buffer, 0, buffered, new byte[0], 0);
|
||||
}
|
||||
len = cipher.decryptFinal(src, dst);
|
||||
} else {
|
||||
if (buffered > 0) {
|
||||
((GaloisCounterMode)cipher).encrypt(buffer, 0, buffered);
|
||||
}
|
||||
len = cipher.encryptFinal(src, dst);
|
||||
}
|
||||
endDoFinal();
|
||||
return len;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2021, 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
|
||||
@ -25,7 +25,6 @@
|
||||
|
||||
package com.sun.crypto.provider;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import javax.crypto.*;
|
||||
@ -200,70 +199,7 @@ abstract class FeedbackCipher {
|
||||
*/
|
||||
int decryptFinal(byte[] cipher, int cipherOffset, int cipherLen,
|
||||
byte[] plain, int plainOffset)
|
||||
throws IllegalBlockSizeException, AEADBadTagException,
|
||||
ShortBufferException {
|
||||
throws IllegalBlockSizeException, ShortBufferException {
|
||||
return decrypt(cipher, cipherOffset, cipherLen, plain, plainOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Continues a multi-part update of the Additional Authentication
|
||||
* Data (AAD), using a subset of the provided buffer. If this
|
||||
* cipher is operating in either GCM or CCM mode, all AAD must be
|
||||
* supplied before beginning operations on the ciphertext (via the
|
||||
* {@code update} and {@code doFinal} methods).
|
||||
* <p>
|
||||
* NOTE: Given most modes do not accept AAD, default impl for this
|
||||
* method throws IllegalStateException.
|
||||
*
|
||||
* @param src the buffer containing the AAD
|
||||
* @param offset the offset in {@code src} where the AAD input starts
|
||||
* @param len the number of AAD bytes
|
||||
*
|
||||
* @throws IllegalStateException if this cipher is in a wrong state
|
||||
* (e.g., has not been initialized), does not accept AAD, or if
|
||||
* operating in either GCM or CCM mode and one of the {@code update}
|
||||
* methods has already been called for the active
|
||||
* encryption/decryption operation
|
||||
* @throws UnsupportedOperationException if this method
|
||||
* has not been overridden by an implementation
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
void updateAAD(byte[] src, int offset, int len) {
|
||||
throw new IllegalStateException("No AAD accepted");
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
/*
|
||||
* ByteBuffer methods should not be accessed as CipherCore and AESCipher
|
||||
* copy the data to byte arrays. These methods are to satisfy the compiler.
|
||||
*/
|
||||
int encrypt(ByteBuffer src, ByteBuffer dst) {
|
||||
throw new UnsupportedOperationException("ByteBuffer not supported");
|
||||
};
|
||||
|
||||
int decrypt(ByteBuffer src, ByteBuffer dst) {
|
||||
throw new UnsupportedOperationException("ByteBuffer not supported");
|
||||
};
|
||||
|
||||
int encryptFinal(ByteBuffer src, ByteBuffer dst)
|
||||
throws IllegalBlockSizeException, ShortBufferException {
|
||||
throw new UnsupportedOperationException("ByteBuffer not supported");
|
||||
};
|
||||
|
||||
int decryptFinal(ByteBuffer src, ByteBuffer dst)
|
||||
throws IllegalBlockSizeException, AEADBadTagException,
|
||||
ShortBufferException {
|
||||
throw new UnsupportedOperationException("ByteBuffer not supported");
|
||||
}
|
||||
}
|
||||
|
41
src/java.base/share/classes/com/sun/crypto/provider/GCM.java
Normal file
41
src/java.base/share/classes/com/sun/crypto/provider/GCM.java
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package com.sun.crypto.provider;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* This interface allows GHASH.java and GCTR.java to easily operate to
|
||||
* better operate with GaloisCounterMode.java
|
||||
*/
|
||||
|
||||
public interface GCM {
|
||||
int update(byte[] in, int inOfs, int inLen, byte[] out, int outOfs);
|
||||
int update(byte[] in, int inOfs, int inLen, ByteBuffer dst);
|
||||
int update(ByteBuffer src, ByteBuffer dst);
|
||||
int doFinal(byte[] in, int inOfs, int inLen, byte[] out, int outOfs);
|
||||
int doFinal(ByteBuffer src, ByteBuffer dst);
|
||||
}
|
@ -31,8 +31,7 @@ package com.sun.crypto.provider;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import static com.sun.crypto.provider.AESConstants.AES_BLOCK_SIZE;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* This class represents the GCTR function defined in NIST 800-38D
|
||||
@ -52,17 +51,18 @@ import static com.sun.crypto.provider.AESConstants.AES_BLOCK_SIZE;
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
final class GCTR extends CounterMode {
|
||||
final class GCTR extends CounterMode implements GCM {
|
||||
|
||||
// Maximum buffer size rotating ByteBuffer->byte[] intrinsic copy
|
||||
private static final int MAX_LEN = 1024;
|
||||
private byte[] block;
|
||||
|
||||
GCTR(SymmetricCipher cipher, byte[] initialCounterBlk) {
|
||||
super(cipher);
|
||||
if (initialCounterBlk.length != AES_BLOCK_SIZE) {
|
||||
if (initialCounterBlk.length != blockSize) {
|
||||
throw new RuntimeException("length of initial counter block (" +
|
||||
initialCounterBlk.length + ") not equal to AES_BLOCK_SIZE (" +
|
||||
AES_BLOCK_SIZE + ")");
|
||||
initialCounterBlk.length + ") not equal to blockSize (" +
|
||||
blockSize + ")");
|
||||
}
|
||||
|
||||
iv = initialCounterBlk;
|
||||
@ -83,30 +83,47 @@ final class GCTR extends CounterMode {
|
||||
return blocksLeft;
|
||||
}
|
||||
|
||||
// input must be multiples of 128-bit blocks when calling update
|
||||
int update(byte[] in, int inOfs, int inLen, byte[] out, int outOfs) {
|
||||
private void checkBlock() {
|
||||
if (block == null) {
|
||||
block = new byte[blockSize];
|
||||
} else {
|
||||
Arrays.fill(block, (byte)0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Using the given inLen, this operates only on blockSize data, leaving
|
||||
* the remainder in 'in'.
|
||||
* The return value will be (inLen - (inLen % blockSize))
|
||||
*/
|
||||
public int update(byte[] in, int inOfs, int inLen, byte[] out, int outOfs) {
|
||||
if (inLen == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (inLen - inOfs > in.length) {
|
||||
throw new RuntimeException("input length out of bound");
|
||||
}
|
||||
if (inLen < 0 || inLen % AES_BLOCK_SIZE != 0) {
|
||||
if (inLen < 0) {
|
||||
throw new RuntimeException("input length unsupported");
|
||||
}
|
||||
if (out.length - outOfs < inLen) {
|
||||
if (out.length - outOfs < (inLen - (inLen % blockSize))) {
|
||||
throw new RuntimeException("output buffer too small");
|
||||
}
|
||||
|
||||
inLen -= inLen % blockSize;
|
||||
long blocksLeft = blocksUntilRollover();
|
||||
int numOfCompleteBlocks = inLen / AES_BLOCK_SIZE;
|
||||
int numOfCompleteBlocks = inLen / blockSize;
|
||||
if (numOfCompleteBlocks >= blocksLeft) {
|
||||
// Counter Mode encryption cannot be used because counter will
|
||||
// roll over incorrectly. Use GCM-specific code instead.
|
||||
byte[] encryptedCntr = new byte[AES_BLOCK_SIZE];
|
||||
checkBlock();
|
||||
for (int i = 0; i < numOfCompleteBlocks; i++) {
|
||||
embeddedCipher.encryptBlock(counter, 0, encryptedCntr, 0);
|
||||
for (int n = 0; n < AES_BLOCK_SIZE; n++) {
|
||||
int index = (i * AES_BLOCK_SIZE + n);
|
||||
embeddedCipher.encryptBlock(counter, 0, block, 0);
|
||||
for (int n = 0; n < blockSize; n++) {
|
||||
int index = (i * blockSize + n);
|
||||
out[outOfs + index] =
|
||||
(byte) ((in[inOfs + index] ^ encryptedCntr[n]));
|
||||
(byte) ((in[inOfs + index] ^ block[n]));
|
||||
}
|
||||
GaloisCounterMode.increment32(counter);
|
||||
}
|
||||
@ -116,34 +133,46 @@ final class GCTR extends CounterMode {
|
||||
}
|
||||
}
|
||||
|
||||
// input must be multiples of AES blocks, 128-bit, when calling update
|
||||
int update(byte[] in, int inOfs, int inLen, ByteBuffer dst) {
|
||||
/**
|
||||
* Operate on only blocksize data leaving the remainder in 'in' .
|
||||
*/
|
||||
public int update(byte[] in, int inOfs, int inLen, ByteBuffer dst) {
|
||||
// If the bytebuffer is backed by arrays, use that instead of
|
||||
// allocating and copying for direct bytebuffers
|
||||
if (!dst.isDirect()) {
|
||||
int len = update(in, inOfs, inLen, dst.array(),
|
||||
dst.arrayOffset() + dst.position());
|
||||
dst.position(dst.position() + len);
|
||||
return len;
|
||||
}
|
||||
|
||||
// Direct ByteBuffer operation
|
||||
if (inLen - inOfs > in.length) {
|
||||
throw new RuntimeException("input length out of bound");
|
||||
}
|
||||
if (inLen < 0 || inLen % AES_BLOCK_SIZE != 0) {
|
||||
if (inLen < 0) {
|
||||
throw new RuntimeException("input length unsupported");
|
||||
}
|
||||
// See GaloisCounterMode. decryptFinal(bytebuffer, bytebuffer) for
|
||||
// details on the check for 'dst' having enough space for the result.
|
||||
|
||||
long blocksLeft = blocksUntilRollover();
|
||||
int numOfCompleteBlocks = inLen / AES_BLOCK_SIZE;
|
||||
int numOfCompleteBlocks = inLen / blockSize;
|
||||
if (numOfCompleteBlocks >= blocksLeft) {
|
||||
// Counter Mode encryption cannot be used because counter will
|
||||
// roll over incorrectly. Use GCM-specific code instead.
|
||||
byte[] encryptedCntr = new byte[AES_BLOCK_SIZE];
|
||||
checkBlock();
|
||||
for (int i = 0; i < numOfCompleteBlocks; i++) {
|
||||
embeddedCipher.encryptBlock(counter, 0, encryptedCntr, 0);
|
||||
for (int n = 0; n < AES_BLOCK_SIZE; n++) {
|
||||
int index = (i * AES_BLOCK_SIZE + n);
|
||||
dst.put((byte) ((in[inOfs + index] ^ encryptedCntr[n])));
|
||||
embeddedCipher.encryptBlock(counter, 0, block, 0);
|
||||
for (int n = 0; n < blockSize; n++) {
|
||||
int index = (i * blockSize + n);
|
||||
dst.put((byte) ((in[inOfs + index] ^ block[n])));
|
||||
}
|
||||
GaloisCounterMode.increment32(counter);
|
||||
}
|
||||
return inLen;
|
||||
} else {
|
||||
int len = inLen - inLen % AES_BLOCK_SIZE;
|
||||
int len = inLen - inLen % blockSize;
|
||||
int processed = len;
|
||||
byte[] out = new byte[Math.min(MAX_LEN, len)];
|
||||
int offset = inOfs;
|
||||
@ -162,26 +191,41 @@ final class GCTR extends CounterMode {
|
||||
}
|
||||
}
|
||||
|
||||
// input operates on multiples of AES blocks, 128-bit, when calling update.
|
||||
// The remainder is left in the src buffer.
|
||||
int update(ByteBuffer src, ByteBuffer dst) {
|
||||
/**
|
||||
* Operate on only blocksize data leaving the remainder in the src buffer.
|
||||
*/
|
||||
public int update(ByteBuffer src, ByteBuffer dst) {
|
||||
int len;
|
||||
|
||||
// If the bytebuffer is backed by arrays, use that instead of
|
||||
// allocating and copying for direct bytebuffers
|
||||
if (src.hasArray() && dst.hasArray()) {
|
||||
len = update(src.array(), src.arrayOffset() + src.position(),
|
||||
src.remaining() - (src.remaining() % blockSize),
|
||||
dst.array(), dst.arrayOffset() + dst.position());
|
||||
src.position(src.position() + len);
|
||||
dst.position(dst.position() + len);
|
||||
return len;
|
||||
}
|
||||
|
||||
// Direct bytebuffer operation
|
||||
long blocksLeft = blocksUntilRollover();
|
||||
int numOfCompleteBlocks = src.remaining() / AES_BLOCK_SIZE;
|
||||
int numOfCompleteBlocks = src.remaining() / blockSize;
|
||||
if (numOfCompleteBlocks >= blocksLeft) {
|
||||
// Counter Mode encryption cannot be used because counter will
|
||||
// roll over incorrectly. Use GCM-specific code instead.
|
||||
byte[] encryptedCntr = new byte[AES_BLOCK_SIZE];
|
||||
checkBlock();
|
||||
for (int i = 0; i < numOfCompleteBlocks; i++) {
|
||||
embeddedCipher.encryptBlock(counter, 0, encryptedCntr, 0);
|
||||
for (int n = 0; n < AES_BLOCK_SIZE; n++) {
|
||||
dst.put((byte) (src.get() ^ encryptedCntr[n]));
|
||||
embeddedCipher.encryptBlock(counter, 0, block, 0);
|
||||
for (int n = 0; n < blockSize; n++) {
|
||||
dst.put((byte) (src.get() ^ block[n]));
|
||||
}
|
||||
GaloisCounterMode.increment32(counter);
|
||||
}
|
||||
return numOfCompleteBlocks * AES_BLOCK_SIZE;
|
||||
return numOfCompleteBlocks * blockSize;
|
||||
}
|
||||
|
||||
int len = src.remaining() - (src.remaining() % AES_BLOCK_SIZE);
|
||||
len = src.remaining() - (src.remaining() % blockSize);
|
||||
int processed = len;
|
||||
byte[] in = new byte[Math.min(MAX_LEN, len)];
|
||||
while (processed > MAX_LEN) {
|
||||
@ -196,50 +240,62 @@ final class GCTR extends CounterMode {
|
||||
return len;
|
||||
}
|
||||
|
||||
// input can be arbitrary size when calling doFinal
|
||||
int doFinal(byte[] in, int inOfs, int inLen, byte[] out,
|
||||
int outOfs) throws IllegalBlockSizeException {
|
||||
try {
|
||||
if (inLen < 0) {
|
||||
throw new IllegalBlockSizeException("Negative input size!");
|
||||
} else if (inLen > 0) {
|
||||
int lastBlockSize = inLen % AES_BLOCK_SIZE;
|
||||
int completeBlkLen = inLen - lastBlockSize;
|
||||
// process the complete blocks first
|
||||
update(in, inOfs, completeBlkLen, out, outOfs);
|
||||
if (lastBlockSize != 0) {
|
||||
// do the last partial block
|
||||
byte[] encryptedCntr = new byte[AES_BLOCK_SIZE];
|
||||
embeddedCipher.encryptBlock(counter, 0, encryptedCntr, 0);
|
||||
for (int n = 0; n < lastBlockSize; n++) {
|
||||
out[outOfs + completeBlkLen + n] =
|
||||
(byte) ((in[inOfs + completeBlkLen + n] ^
|
||||
encryptedCntr[n]));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* doFinal operation by using update() for any full block operations needed,
|
||||
* then operating on the final bytes in the input buffer.
|
||||
*
|
||||
* This method will not write any block padding to the output buffer
|
||||
*/
|
||||
public int doFinal(byte[] in, int inOfs, int inLen, byte[] out,
|
||||
int outOfs) {
|
||||
if (inLen == 0) {
|
||||
return 0;
|
||||
}
|
||||
int lastBlockSize = inLen % blockSize;
|
||||
int completeBlkLen = inLen - lastBlockSize;
|
||||
// process the complete blocks first
|
||||
update(in, inOfs, completeBlkLen, out, outOfs);
|
||||
if (lastBlockSize != 0) {
|
||||
// do the last partial block
|
||||
checkBlock();
|
||||
embeddedCipher.encryptBlock(counter, 0, block, 0);
|
||||
for (int n = 0; n < lastBlockSize; n++) {
|
||||
out[outOfs + completeBlkLen + n] =
|
||||
(byte) ((in[inOfs + completeBlkLen + n] ^ block[n]));
|
||||
}
|
||||
} finally {
|
||||
reset();
|
||||
}
|
||||
return inLen;
|
||||
}
|
||||
|
||||
// src can be arbitrary size when calling doFinal
|
||||
int doFinal(ByteBuffer src, ByteBuffer dst) {
|
||||
/**
|
||||
* doFinal operation by using update() for any full block operations needed,
|
||||
* then operating on the final bytes in the input buffer.
|
||||
*
|
||||
* If src and dst are array-backed bytebuffers, call doFinal(byte[]...) for
|
||||
* less memory usage.
|
||||
*/
|
||||
public int doFinal(ByteBuffer src, ByteBuffer dst) {
|
||||
// If the bytebuffer is backed by arrays, use that instead of
|
||||
// allocating and copying for direct bytebuffers
|
||||
if (src.hasArray() && dst.hasArray()) {
|
||||
int len = doFinal(src.array(), src.arrayOffset() + src.position(),
|
||||
src.remaining(), dst.array(),
|
||||
dst.arrayOffset() + dst.position());
|
||||
src.position(src.position() + len);
|
||||
dst.position(dst.position() + len);
|
||||
return len;
|
||||
}
|
||||
|
||||
int len = src.remaining();
|
||||
int lastBlockSize = len % AES_BLOCK_SIZE;
|
||||
try {
|
||||
update(src, dst);
|
||||
if (lastBlockSize != 0) {
|
||||
// do the last partial block
|
||||
byte[] encryptedCntr = new byte[AES_BLOCK_SIZE];
|
||||
embeddedCipher.encryptBlock(counter, 0, encryptedCntr, 0);
|
||||
for (int n = 0; n < lastBlockSize; n++) {
|
||||
dst.put((byte) (src.get() ^ encryptedCntr[n]));
|
||||
}
|
||||
int lastBlockSize = len % blockSize;
|
||||
update(src, dst);
|
||||
if (lastBlockSize != 0) {
|
||||
checkBlock();
|
||||
// do the last partial block
|
||||
embeddedCipher.encryptBlock(counter, 0, block, 0);
|
||||
for (int n = 0; n < lastBlockSize; n++) {
|
||||
dst.put((byte) (src.get() ^ block[n]));
|
||||
}
|
||||
} finally {
|
||||
reset();
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015 Red Hat, Inc.
|
||||
* Copyright (c) 2013, 2021, 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
|
||||
@ -25,11 +24,15 @@
|
||||
*/
|
||||
/*
|
||||
* (C) Copyright IBM Corp. 2013
|
||||
* Copyright (c) 2015 Red Hat, Inc.
|
||||
*/
|
||||
|
||||
package com.sun.crypto.provider;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.security.ProviderException;
|
||||
|
||||
import jdk.internal.vm.annotation.IntrinsicCandidate;
|
||||
@ -44,27 +47,19 @@ import jdk.internal.vm.annotation.IntrinsicCandidate;
|
||||
*
|
||||
* @since 1.8
|
||||
*/
|
||||
final class GHASH {
|
||||
|
||||
private static long getLong(byte[] buffer, int offset) {
|
||||
long result = 0;
|
||||
int end = offset + 8;
|
||||
for (int i = offset; i < end; ++i) {
|
||||
result = (result << 8) + (buffer[i] & 0xFF);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void putLong(byte[] buffer, int offset, long value) {
|
||||
int end = offset + 8;
|
||||
for (int i = end - 1; i >= offset; --i) {
|
||||
buffer[i] = (byte) value;
|
||||
value >>= 8;
|
||||
}
|
||||
}
|
||||
final class GHASH implements Cloneable, GCM {
|
||||
|
||||
private static final int AES_BLOCK_SIZE = 16;
|
||||
|
||||
// Handle for converting byte[] <-> long
|
||||
private static final VarHandle asLongView =
|
||||
MethodHandles.byteArrayViewVarHandle(long[].class,
|
||||
ByteOrder.BIG_ENDIAN);
|
||||
|
||||
// Maximum buffer size rotating ByteBuffer->byte[] intrinsic copy
|
||||
private static final int MAX_LEN = 1024;
|
||||
|
||||
// Multiplies state[0], state[1] by subkeyH[0], subkeyH[1].
|
||||
private static void blockMult(long[] st, long[] subH) {
|
||||
long Z0 = 0;
|
||||
@ -127,15 +122,13 @@ final class GHASH {
|
||||
|
||||
/* subkeyHtbl and state are stored in long[] for GHASH intrinsic use */
|
||||
|
||||
// hashtable subkeyHtbl; holds 2*9 powers of subkeyH computed using carry-less multiplication
|
||||
// hashtable subkeyHtbl holds 2*9 powers of subkeyH computed using
|
||||
// carry-less multiplication
|
||||
private long[] subkeyHtbl;
|
||||
|
||||
// buffer for storing hash
|
||||
private final long[] state;
|
||||
|
||||
// variables for save/restore calls
|
||||
private long stateSave0, stateSave1;
|
||||
|
||||
/**
|
||||
* Initializes the cipher in the specified mode with the given key
|
||||
* and iv.
|
||||
@ -151,87 +144,106 @@ final class GHASH {
|
||||
}
|
||||
state = new long[2];
|
||||
subkeyHtbl = new long[2*9];
|
||||
subkeyHtbl[0] = getLong(subkeyH, 0);
|
||||
subkeyHtbl[1] = getLong(subkeyH, 8);
|
||||
subkeyHtbl[0] = (long)asLongView.get(subkeyH, 0);
|
||||
subkeyHtbl[1] = (long)asLongView.get(subkeyH, 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the GHASH object to its original state, i.e. blank w/
|
||||
* the same subkey H. Used after digest() is called and to re-use
|
||||
* this object for different data w/ the same H.
|
||||
*/
|
||||
void reset() {
|
||||
state[0] = 0;
|
||||
state[1] = 0;
|
||||
// Cloning constructor
|
||||
private GHASH(GHASH g) {
|
||||
state = g.state.clone();
|
||||
subkeyHtbl = g.subkeyHtbl.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the current snapshot of this GHASH object.
|
||||
*/
|
||||
void save() {
|
||||
stateSave0 = state[0];
|
||||
stateSave1 = state[1];
|
||||
@Override
|
||||
public GHASH clone() {
|
||||
return new GHASH(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores this object using the saved snapshot.
|
||||
*/
|
||||
void restore() {
|
||||
state[0] = stateSave0;
|
||||
state[1] = stateSave1;
|
||||
}
|
||||
|
||||
private static void processBlock(byte[] data, int ofs, long[] st, long[] subH) {
|
||||
st[0] ^= getLong(data, ofs);
|
||||
st[1] ^= getLong(data, ofs + 8);
|
||||
private static void processBlock(byte[] data, int ofs, long[] st,
|
||||
long[] subH) {
|
||||
st[0] ^= (long)asLongView.get(data, ofs);
|
||||
st[1] ^= (long)asLongView.get(data, ofs + 8);
|
||||
blockMult(st, subH);
|
||||
}
|
||||
|
||||
void update(byte[] in) {
|
||||
update(in, 0, in.length);
|
||||
int update(byte[] in) {
|
||||
return update(in, 0, in.length);
|
||||
}
|
||||
|
||||
void update(byte[] in, int inOfs, int inLen) {
|
||||
int update(byte[] in, int inOfs, int inLen) {
|
||||
if (inLen == 0) {
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
ghashRangeCheck(in, inOfs, inLen, state, subkeyHtbl);
|
||||
processBlocks(in, inOfs, inLen/AES_BLOCK_SIZE, state, subkeyHtbl);
|
||||
int len = inLen - (inLen % AES_BLOCK_SIZE);
|
||||
ghashRangeCheck(in, inOfs, len, state, subkeyHtbl);
|
||||
processBlocks(in, inOfs, len / AES_BLOCK_SIZE, state, subkeyHtbl);
|
||||
return len;
|
||||
}
|
||||
|
||||
// Maximum buffer size rotating ByteBuffer->byte[] intrinsic copy
|
||||
private static final int MAX_LEN = 1024;
|
||||
|
||||
// Will process as many blocks it can and will leave the remaining.
|
||||
int update(ByteBuffer src, int inLen) {
|
||||
int update(ByteBuffer ct, int inLen) {
|
||||
inLen -= (inLen % AES_BLOCK_SIZE);
|
||||
if (inLen == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int processed = inLen;
|
||||
byte[] in = new byte[Math.min(MAX_LEN, inLen)];
|
||||
while (processed > MAX_LEN ) {
|
||||
src.get(in, 0, MAX_LEN);
|
||||
update(in, 0 , MAX_LEN);
|
||||
processed -= MAX_LEN;
|
||||
// If ct is a direct bytebuffer, send it directly to the intrinsic
|
||||
if (ct.isDirect()) {
|
||||
int processed = inLen;
|
||||
processBlocksDirect(ct, inLen);
|
||||
return processed;
|
||||
} else if (!ct.isReadOnly()) {
|
||||
// If a non-read only heap bytebuffer, use the array update method
|
||||
int processed = update(ct.array(),
|
||||
ct.arrayOffset() + ct.position(),
|
||||
inLen);
|
||||
ct.position(ct.position() + processed);
|
||||
return processed;
|
||||
}
|
||||
src.get(in, 0, processed);
|
||||
update(in, 0, processed);
|
||||
|
||||
// Read only heap bytebuffers have to be copied and operated on
|
||||
int to_process = inLen;
|
||||
byte[] in = new byte[Math.min(MAX_LEN, inLen)];
|
||||
while (to_process > MAX_LEN ) {
|
||||
ct.get(in, 0, MAX_LEN);
|
||||
update(in, 0 , MAX_LEN);
|
||||
to_process -= MAX_LEN;
|
||||
}
|
||||
ct.get(in, 0, to_process);
|
||||
update(in, 0, to_process);
|
||||
return inLen;
|
||||
}
|
||||
|
||||
void doLastBlock(ByteBuffer src, int inLen) {
|
||||
int processed = update(src, inLen);
|
||||
int doFinal(ByteBuffer src, int inLen) {
|
||||
int processed = 0;
|
||||
|
||||
if (inLen >= AES_BLOCK_SIZE) {
|
||||
processed = update(src, inLen);
|
||||
}
|
||||
|
||||
if (inLen == processed) {
|
||||
return;
|
||||
return processed;
|
||||
}
|
||||
byte[] block = new byte[AES_BLOCK_SIZE];
|
||||
src.get(block, 0, inLen - processed);
|
||||
update(block, 0, AES_BLOCK_SIZE);
|
||||
return inLen;
|
||||
}
|
||||
|
||||
private static void ghashRangeCheck(byte[] in, int inOfs, int inLen, long[] st, long[] subH) {
|
||||
int doFinal(byte[] in, int inOfs, int inLen) {
|
||||
int remainder = inLen % AES_BLOCK_SIZE;
|
||||
inOfs += update(in, inOfs, inLen - remainder);
|
||||
if (remainder > 0) {
|
||||
byte[] block = new byte[AES_BLOCK_SIZE];
|
||||
System.arraycopy(in, inOfs, block, 0,
|
||||
remainder);
|
||||
update(block, 0, AES_BLOCK_SIZE);
|
||||
}
|
||||
return inLen;
|
||||
}
|
||||
|
||||
private static void ghashRangeCheck(byte[] in, int inOfs, int inLen,
|
||||
long[] st, long[] subH) {
|
||||
if (inLen < 0) {
|
||||
throw new RuntimeException("invalid input length: " + inLen);
|
||||
}
|
||||
@ -263,7 +275,8 @@ final class GHASH {
|
||||
* throw exceptions or allocate arrays as it will breaking intrinsics
|
||||
*/
|
||||
@IntrinsicCandidate
|
||||
private static void processBlocks(byte[] data, int inOfs, int blocks, long[] st, long[] subH) {
|
||||
private static void processBlocks(byte[] data, int inOfs, int blocks,
|
||||
long[] st, long[] subH) {
|
||||
int offset = inOfs;
|
||||
while (blocks > 0) {
|
||||
processBlock(data, offset, st, subH);
|
||||
@ -272,11 +285,61 @@ final class GHASH {
|
||||
}
|
||||
}
|
||||
|
||||
// ProcessBlock for Direct ByteBuffers
|
||||
private void processBlocksDirect(ByteBuffer ct, int inLen) {
|
||||
byte[] data = new byte[Math.min(MAX_LEN, inLen)];
|
||||
while (inLen > MAX_LEN) {
|
||||
ct.get(data, 0, MAX_LEN);
|
||||
processBlocks(data, 0, MAX_LEN / AES_BLOCK_SIZE, state,
|
||||
subkeyHtbl);
|
||||
inLen -= MAX_LEN;
|
||||
}
|
||||
if (inLen >= AES_BLOCK_SIZE) {
|
||||
int len = inLen - (inLen % AES_BLOCK_SIZE);
|
||||
ct.get(data, 0, len);
|
||||
processBlocks(data, 0, len / AES_BLOCK_SIZE, state,
|
||||
subkeyHtbl);
|
||||
}
|
||||
}
|
||||
|
||||
byte[] digest() {
|
||||
byte[] result = new byte[AES_BLOCK_SIZE];
|
||||
putLong(result, 0, state[0]);
|
||||
putLong(result, 8, state[1]);
|
||||
reset();
|
||||
asLongView.set(result, 0, state[0]);
|
||||
asLongView.set(result, 8, state[1]);
|
||||
// Reset state
|
||||
state[0] = 0;
|
||||
state[1] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* None of the out or dst values are necessary, they are to satisfy the
|
||||
* GCM interface requirement
|
||||
*/
|
||||
@Override
|
||||
public int update(byte[] in, int inOfs, int inLen, byte[] out, int outOfs) {
|
||||
return update(in, inOfs, inLen);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(byte[] in, int inOfs, int inLen, ByteBuffer dst) {
|
||||
return update(in, inOfs, inLen);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(ByteBuffer src, ByteBuffer dst) {
|
||||
return update(src, src.remaining());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int doFinal(byte[] in, int inOfs, int inLen, byte[] out,
|
||||
int outOfs) {
|
||||
return doFinal(in, inOfs, inLen);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int doFinal(ByteBuffer src, ByteBuffer dst) {
|
||||
return doFinal(src, src.remaining());
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -165,7 +165,7 @@ public final class SunJCE extends Provider {
|
||||
"|CFB8|CFB16|CFB24|CFB32|CFB40|CFB48|CFB56|CFB64" +
|
||||
"|OFB8|OFB16|OFB24|OFB32|OFB40|OFB48|OFB56|OFB64";
|
||||
final String BLOCK_MODES128 = BLOCK_MODES +
|
||||
"|GCM|CFB72|CFB80|CFB88|CFB96|CFB104|CFB112|CFB120|CFB128" +
|
||||
"|CFB72|CFB80|CFB88|CFB96|CFB104|CFB112|CFB120|CFB128" +
|
||||
"|OFB72|OFB80|OFB88|OFB96|OFB104|OFB112|OFB120|OFB128";
|
||||
final String BLOCK_PADS = "NOPADDING|PKCS5PADDING|ISO10126PADDING";
|
||||
|
||||
@ -214,9 +214,6 @@ public final class SunJCE extends Provider {
|
||||
psA("Cipher", "AES_128/CFB/NoPadding",
|
||||
"com.sun.crypto.provider.AESCipher$AES128_CFB_NoPadding",
|
||||
attrs);
|
||||
psA("Cipher", "AES_128/GCM/NoPadding",
|
||||
"com.sun.crypto.provider.AESCipher$AES128_GCM_NoPadding",
|
||||
attrs);
|
||||
psA("Cipher", "AES_128/KW/NoPadding",
|
||||
"com.sun.crypto.provider.KeyWrapCipher$AES128_KW_NoPadding",
|
||||
attrs);
|
||||
@ -239,9 +236,6 @@ public final class SunJCE extends Provider {
|
||||
psA("Cipher", "AES_192/CFB/NoPadding",
|
||||
"com.sun.crypto.provider.AESCipher$AES192_CFB_NoPadding",
|
||||
attrs);
|
||||
psA("Cipher", "AES_192/GCM/NoPadding",
|
||||
"com.sun.crypto.provider.AESCipher$AES192_GCM_NoPadding",
|
||||
attrs);
|
||||
psA("Cipher", "AES_192/KW/NoPadding",
|
||||
"com.sun.crypto.provider.KeyWrapCipher$AES192_KW_NoPadding",
|
||||
attrs);
|
||||
@ -264,9 +258,6 @@ public final class SunJCE extends Provider {
|
||||
psA("Cipher", "AES_256/CFB/NoPadding",
|
||||
"com.sun.crypto.provider.AESCipher$AES256_CFB_NoPadding",
|
||||
attrs);
|
||||
psA("Cipher", "AES_256/GCM/NoPadding",
|
||||
"com.sun.crypto.provider.AESCipher$AES256_GCM_NoPadding",
|
||||
attrs);
|
||||
psA("Cipher", "AES_256/KW/NoPadding",
|
||||
"com.sun.crypto.provider.KeyWrapCipher$AES256_KW_NoPadding",
|
||||
attrs);
|
||||
@ -277,6 +268,23 @@ public final class SunJCE extends Provider {
|
||||
"com.sun.crypto.provider.KeyWrapCipher$AES256_KWP_NoPadding",
|
||||
attrs);
|
||||
|
||||
attrs.clear();
|
||||
attrs.put("SupportedModes", "GCM");
|
||||
attrs.put("SupportedKeyFormats", "RAW");
|
||||
|
||||
ps("Cipher", "AES/GCM/NoPadding",
|
||||
"com.sun.crypto.provider.GaloisCounterMode$AESGCM", null,
|
||||
attrs);
|
||||
psA("Cipher", "AES_128/GCM/NoPadding",
|
||||
"com.sun.crypto.provider.GaloisCounterMode$AES128",
|
||||
attrs);
|
||||
psA("Cipher", "AES_192/GCM/NoPadding",
|
||||
"com.sun.crypto.provider.GaloisCounterMode$AES192",
|
||||
attrs);
|
||||
psA("Cipher", "AES_256/GCM/NoPadding",
|
||||
"com.sun.crypto.provider.GaloisCounterMode$AES256",
|
||||
attrs);
|
||||
|
||||
attrs.clear();
|
||||
attrs.put("SupportedModes", "CBC");
|
||||
attrs.put("SupportedPaddings", "NOPADDING");
|
||||
|
@ -217,8 +217,6 @@ public final class SecurityProviderConstants {
|
||||
|
||||
store("DiffieHellman", KnownOIDs.DiffieHellman);
|
||||
|
||||
store("AES", KnownOIDs.AES, "Rijndael");
|
||||
|
||||
store("EC", KnownOIDs.EC, "EllipticCurve");
|
||||
|
||||
store("X.509", null, "X509");
|
||||
|
@ -27,6 +27,7 @@ import java.security.Provider;
|
||||
import java.security.Security;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HexFormat;
|
||||
import java.util.List;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.Cipher;
|
||||
@ -84,7 +85,7 @@ import javax.crypto.KeyGenerator;
|
||||
*/
|
||||
public class Encrypt {
|
||||
|
||||
private static final String ALGORITHMS[] = { "AES", "Rijndael" };
|
||||
private static final String ALGORITHMS[] = { "AES" };
|
||||
private static final int KEY_STRENGTHS[] = { 128, 192, 256 };
|
||||
private static final int TEXT_LENGTHS[] = { 0, 256, 1024 };
|
||||
private static final int AAD_LENGTHS[] = { 0, 8, 128, 256, 1024 };
|
||||
@ -230,8 +231,12 @@ public class Encrypt {
|
||||
combination_16(outputTexts, mode, AAD, inputText, params);
|
||||
|
||||
for (int k = 0; k < outputTexts.size(); k++) {
|
||||
HexFormat hex = HexFormat.of().withUpperCase();
|
||||
if (!Arrays.equals(output, outputTexts.get(k))) {
|
||||
throw new RuntimeException("Combination #" + k + " failed");
|
||||
System.out.println("Combination #" + (k + 1) + "\nresult " +
|
||||
hex.formatHex(outputTexts.get(k)) +
|
||||
"\nexpected: " + hex.formatHex(output));
|
||||
throw new RuntimeException("Combination #" + (k + 1) + " failed");
|
||||
}
|
||||
}
|
||||
return output;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 2021, 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
|
||||
@ -36,11 +36,11 @@ import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.GCMParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HexFormat;
|
||||
import java.util.List;
|
||||
|
||||
public class GCMBufferTest implements Cloneable {
|
||||
@ -65,6 +65,7 @@ public class GCMBufferTest implements Cloneable {
|
||||
boolean theoreticalCheck;
|
||||
List<Data> dataSet;
|
||||
int inOfs = 0, outOfs = 0;
|
||||
static HexFormat hex = HexFormat.of();
|
||||
|
||||
static class Data {
|
||||
int id;
|
||||
@ -108,14 +109,21 @@ public class GCMBufferTest implements Cloneable {
|
||||
new GCMParameterSpec(tag.length * 8, this.iv));
|
||||
tct = c.doFinal(pt);
|
||||
} catch (Exception e) {
|
||||
System.out.println("Error in generating data for length " +
|
||||
ptlen);
|
||||
throw new RuntimeException("Error in generating data for length " +
|
||||
ptlen, e);
|
||||
}
|
||||
ct = new byte[ptlen];
|
||||
System.arraycopy(tct, 0, ct, 0, ct.length);
|
||||
System.arraycopy(tct, ct.length, tag, 0, tag.length);
|
||||
}
|
||||
|
||||
private static final byte[] HexToBytes(String hexVal) {
|
||||
if (hexVal == null) {
|
||||
return new byte[0];
|
||||
}
|
||||
return hex.parseHex(hexVal);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -176,7 +184,7 @@ public class GCMBufferTest implements Cloneable {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
throw new Exception("Unaeble to find dataSet id = " + id);
|
||||
throw new Exception("Unable to find dataSet id = " + id);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -244,7 +252,7 @@ public class GCMBufferTest implements Cloneable {
|
||||
|
||||
void test() throws Exception {
|
||||
int i = 1;
|
||||
System.out.println("Algo: " + algo + " \tOps: " + ops.toString());
|
||||
System.err.println("Algo: " + algo + " \tOps: " + ops.toString());
|
||||
for (Data data : dataSet) {
|
||||
|
||||
// If incrementalSegments is enabled, run through that test only
|
||||
@ -256,31 +264,31 @@ public class GCMBufferTest implements Cloneable {
|
||||
sizes = new int[ops.size()];
|
||||
|
||||
while (incrementSizes(data.pt.length)) {
|
||||
System.out.print("Encrypt: Data Index: " + i + " \tSizes[ ");
|
||||
System.err.print("Encrypt: Data Index: " + i + " \tSizes[ ");
|
||||
for (int v : sizes) {
|
||||
System.out.print(v + " ");
|
||||
System.err.print(v + " ");
|
||||
}
|
||||
System.out.println("]");
|
||||
System.err.println("]");
|
||||
encrypt(data);
|
||||
}
|
||||
Arrays.fill(sizes, 0);
|
||||
|
||||
while (incrementSizes(data.ct.length + data.tag.length)) {
|
||||
System.out.print("Decrypt: Data Index: " + i + " \tSizes[ ");
|
||||
System.err.print("Decrypt: Data Index: " + i + " \tSizes[ ");
|
||||
for (int v : sizes) {
|
||||
System.out.print(v + " ");
|
||||
System.err.print(v + " ");
|
||||
}
|
||||
System.out.println("]");
|
||||
System.err.println("]");
|
||||
decrypt(data);
|
||||
}
|
||||
|
||||
} else {
|
||||
// Default test of 0 and 2 offset doing in place and different
|
||||
// i/o buffers
|
||||
System.out.println("Encrypt: Data Index: " + i);
|
||||
System.err.println("Encrypt: Data Index: " + i);
|
||||
encrypt(data);
|
||||
|
||||
System.out.println("Decrypt: Data Index: " + i);
|
||||
System.err.println("Decrypt: Data Index: " + i);
|
||||
decrypt(data);
|
||||
}
|
||||
i++;
|
||||
@ -298,13 +306,13 @@ public class GCMBufferTest implements Cloneable {
|
||||
data.tag.length);
|
||||
|
||||
// Test different input/output buffers
|
||||
System.out.println("\tinput len: " + input.length + " inOfs " +
|
||||
System.err.println("\tinput len: " + input.length + " inOfs " +
|
||||
inOfs + " outOfs " + outOfs + " in/out buffer: different");
|
||||
crypto(true, data, input, output);
|
||||
|
||||
// Test with in-place buffers
|
||||
if (same) {
|
||||
System.out.println("\tinput len: " + input.length + " inOfs " +
|
||||
System.err.println("\tinput len: " + input.length + " inOfs " +
|
||||
inOfs + " outOfs " + outOfs + " in/out buffer: in-place");
|
||||
cryptoSameBuffer(true, data, input, output);
|
||||
}
|
||||
@ -320,13 +328,13 @@ public class GCMBufferTest implements Cloneable {
|
||||
output = data.pt;
|
||||
|
||||
// Test different input/output buffers
|
||||
System.out.println("\tinput len: " + input.length + " inOfs " +
|
||||
inOfs + " outOfs " + outOfs + " in-place: different");
|
||||
System.err.println("\tinput len: " + input.length + " inOfs " +
|
||||
inOfs + " outOfs " + outOfs + " in/out buffer: different");
|
||||
crypto(false, data, input, output);
|
||||
|
||||
// Test with in-place buffers
|
||||
if (same) {
|
||||
System.out.println("\tinput len: " + input.length + " inOfs " +
|
||||
System.err.println("\tinput len: " + input.length + " inOfs " +
|
||||
inOfs + " outOfs " + outOfs + " in-place: same");
|
||||
cryptoSameBuffer(false, data, input, output);
|
||||
}
|
||||
@ -484,12 +492,10 @@ public class GCMBufferTest implements Cloneable {
|
||||
if (ctresult.length != expectedOut.length ||
|
||||
Arrays.compare(ctresult, expectedOut) != 0) {
|
||||
String s = "Ciphertext mismatch (" + v.name() +
|
||||
"):\nresult (len=" + ctresult.length + "):" +
|
||||
String.format("%0" + (ctresult.length << 1) + "x",
|
||||
new BigInteger(1, ctresult)) +
|
||||
"\nexpected (len=" + output.length + "):" +
|
||||
String.format("%0" + (output.length << 1) + "x",
|
||||
new BigInteger(1, output));
|
||||
"):\nresult (len=" + ctresult.length + "): " +
|
||||
hex.formatHex(ctresult) +
|
||||
"\nexpected (len=" + output.length + "): " +
|
||||
hex.formatHex(output);
|
||||
System.err.println(s);
|
||||
throw new Exception(s);
|
||||
|
||||
@ -605,10 +611,9 @@ public class GCMBufferTest implements Cloneable {
|
||||
output.length) != 0) {
|
||||
String s = "Ciphertext mismatch (" + v.name() +
|
||||
"):\nresult (len=" + len + "):\n" +
|
||||
byteToHex(out) +
|
||||
hex.formatHex(out) +
|
||||
"\nexpected (len=" + output.length + "):\n" +
|
||||
String.format("%0" + (output.length << 1) + "x",
|
||||
new BigInteger(1, output));
|
||||
hex.formatHex(output);
|
||||
System.err.println(s);
|
||||
throw new Exception(s);
|
||||
}
|
||||
@ -623,7 +628,10 @@ public class GCMBufferTest implements Cloneable {
|
||||
}
|
||||
|
||||
public static void main(String args[]) throws Exception {
|
||||
GCMBufferTest t;
|
||||
|
||||
initTest();
|
||||
|
||||
// Test single byte array
|
||||
new GCMBufferTest("AES/GCM/NoPadding", List.of(dtype.BYTE)).test();
|
||||
offsetTests(new GCMBufferTest("AES/GCM/NoPadding", List.of(dtype.BYTE)));
|
||||
@ -662,7 +670,7 @@ public class GCMBufferTest implements Cloneable {
|
||||
List.of(dtype.DIRECT, dtype.DIRECT, dtype.DIRECT)));
|
||||
|
||||
// Test update-update-doFinal with byte arrays and preset data sizes
|
||||
GCMBufferTest t = new GCMBufferTest("AES/GCM/NoPadding",
|
||||
t = new GCMBufferTest("AES/GCM/NoPadding",
|
||||
List.of(dtype.BYTE, dtype.BYTE, dtype.BYTE)).dataSegments(
|
||||
new int[] { 1, 1, GCMBufferTest.REMAINDER});
|
||||
t.clone().test();
|
||||
@ -678,6 +686,7 @@ public class GCMBufferTest implements Cloneable {
|
||||
List.of(dtype.BYTE, dtype.HEAP, dtype.DIRECT)).differentBufferOnly();
|
||||
t.clone().test();
|
||||
offsetTests(t.clone());
|
||||
|
||||
// Test update-doFinal with a direct bytebuffer and a byte array.
|
||||
t = new GCMBufferTest("AES/GCM/NoPadding",
|
||||
List.of(dtype.DIRECT, dtype.BYTE)).differentBufferOnly();
|
||||
@ -710,26 +719,10 @@ public class GCMBufferTest implements Cloneable {
|
||||
new GCMBufferTest("AES/GCM/NoPadding",
|
||||
List.of(dtype.DIRECT, dtype.DIRECT, dtype.DIRECT)).
|
||||
incrementalSegments().dataSet(0).test();
|
||||
}
|
||||
|
||||
private static byte[] HexToBytes(String hexVal) {
|
||||
if (hexVal == null) {
|
||||
return new byte[0];
|
||||
}
|
||||
byte[] result = new byte[hexVal.length()/2];
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
String byteVal = hexVal.substring(2*i, 2*i +2);
|
||||
result[i] = Integer.valueOf(byteVal, 16).byteValue();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String byteToHex(byte[] barray) {
|
||||
StringBuilder s = new StringBuilder();
|
||||
for (byte b : barray) {
|
||||
s.append(String.format("%02x", b));
|
||||
}
|
||||
return s.toString();
|
||||
new GCMBufferTest("AES/GCM/NoPadding",
|
||||
List.of(dtype.DIRECT, dtype.DIRECT, dtype.DIRECT)).
|
||||
dataSegments(new int[] { 49, 0, 2 }).dataSet(0).test();
|
||||
}
|
||||
|
||||
// Test data
|
||||
@ -762,8 +755,7 @@ public class GCMBufferTest implements Cloneable {
|
||||
"b6e6f197168f5049aeda32dafbdaeb"),
|
||||
// zero'd test data
|
||||
new Data("AES", 3, "272f16edb81a7abbea887357a58c1917",
|
||||
"794ec588176c703d3d2a7a07",
|
||||
new byte[256], null,
|
||||
"794ec588176c703d3d2a7a07", new byte[256], null,
|
||||
"15b461672153270e8ba1e6789f7641c5411f3e642abda731b6086f535c216457" +
|
||||
"e87305bc59a1ff1f7e1e0bbdf302b75549b136606c67d7e5f71277aeca4bc670" +
|
||||
"07a98f78e0cfa002ed183e62f07893ad31fe67aad1bb37e15b957a14d145f14f" +
|
||||
|
123
test/jdk/com/sun/crypto/provider/Cipher/AEAD/GCMShortBuffer.java
Normal file
123
test/jdk/com/sun/crypto/provider/Cipher/AEAD/GCMShortBuffer.java
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.ShortBufferException;
|
||||
import javax.crypto.spec.GCMParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.HexFormat;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Call decrypt doFinal() with different output values to see if the
|
||||
* the operation can complete after a ShortBufferException
|
||||
*/
|
||||
public class GCMShortBuffer {
|
||||
static Cipher c;
|
||||
static final GCMParameterSpec iv = new GCMParameterSpec(128, new byte[16]);
|
||||
static final SecretKeySpec keySpec = new SecretKeySpec(new byte[16], "AES");
|
||||
static byte cipherText[], plaintext[] = new byte[51];
|
||||
boolean error = false;
|
||||
|
||||
GCMShortBuffer(byte[] out) throws Exception {
|
||||
int len = cipherText.length - 1;
|
||||
|
||||
c.init(Cipher.DECRYPT_MODE, keySpec, iv);
|
||||
byte[] pt = new byte[c.getOutputSize(cipherText.length)];
|
||||
c.update(cipherText, 0, 1);
|
||||
try {
|
||||
c.doFinal(cipherText, 1, len, out, 0);
|
||||
} catch (ShortBufferException e) {
|
||||
System.out.println("ShortBuffer caught");
|
||||
} catch (Exception e) {
|
||||
throw e;
|
||||
}
|
||||
int r = c.doFinal(cipherText, 1, len, pt, 0);
|
||||
if (r != pt.length) {
|
||||
System.out.println(
|
||||
"doFinal() return ( " + r + ") is not the same" +
|
||||
"as getOutputSize returned" + pt.length);
|
||||
error = true;
|
||||
}
|
||||
if (Arrays.compare(pt, plaintext) != 0) {
|
||||
System.out.println("output : " + HexFormat.of().formatHex(pt));
|
||||
System.out.println("expected: " +
|
||||
HexFormat.of().formatHex(plaintext));
|
||||
System.out.println("output and plaintext do not match");
|
||||
error = true;
|
||||
}
|
||||
if (error) {
|
||||
throw new Exception("An error has occurred");
|
||||
}
|
||||
}
|
||||
|
||||
GCMShortBuffer(ByteBuffer dst) throws Exception {
|
||||
int len = cipherText.length - 1;
|
||||
ByteBuffer out = ByteBuffer.allocate(plaintext.length);
|
||||
|
||||
c.init(Cipher.DECRYPT_MODE, keySpec, iv);
|
||||
c.update(cipherText, 0, 1);
|
||||
ByteBuffer ct = ByteBuffer.wrap(cipherText, 1, len);
|
||||
try {
|
||||
c.doFinal(ct , dst);
|
||||
} catch (ShortBufferException e) {
|
||||
System.out.println("ShortBuffer caught");
|
||||
} catch (Exception e) {
|
||||
throw e;
|
||||
}
|
||||
int r = c.doFinal(ByteBuffer.wrap(cipherText, 1, len), out);
|
||||
out.flip();
|
||||
if (r != out.capacity()) {
|
||||
System.out.println(
|
||||
"doFinal() return ( " + r + ") is not the same" +
|
||||
" as getOutputSize returned" + out.capacity());
|
||||
error = true;
|
||||
}
|
||||
if (out.compareTo(ByteBuffer.wrap(plaintext)) != 0) {
|
||||
System.out.println("output and plaintext do not match");
|
||||
System.out.println("output : " +
|
||||
HexFormat.of().formatHex(out.array()));
|
||||
System.out.println("expected: " +
|
||||
HexFormat.of().formatHex(plaintext));
|
||||
error = true;
|
||||
}
|
||||
if (error) {
|
||||
throw new Exception("An error has occurred");
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String args[]) throws Exception {
|
||||
c = Cipher.getInstance("AES/GCM/NoPadding");
|
||||
c.init(Cipher.ENCRYPT_MODE, keySpec, iv);
|
||||
cipherText = c.doFinal(plaintext);
|
||||
|
||||
new GCMShortBuffer(new byte[13]);
|
||||
new GCMShortBuffer(new byte[50]);
|
||||
new GCMShortBuffer(ByteBuffer.allocate(13));
|
||||
new GCMShortBuffer(ByteBuffer.allocate(50));
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -107,6 +107,9 @@ public class OverlapByteBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("inOfsInBuf = " + inOfsInBuf);
|
||||
System.out.println("outOfsInBuf = " + outOfsInBuf);
|
||||
|
||||
// Copy data into shared buffer
|
||||
input.put(baseBuf);
|
||||
input.flip();
|
||||
@ -132,8 +135,6 @@ public class OverlapByteBuffer {
|
||||
cipher.doFinal(in, output);
|
||||
|
||||
output.flip();
|
||||
System.out.println("inOfsInBuf = " + inOfsInBuf);
|
||||
System.out.println("outOfsInBuf = " + outOfsInBuf);
|
||||
ByteBuffer b = ByteBuffer.wrap(baseBuf);
|
||||
if (b.compareTo(output) != 0) {
|
||||
System.err.println(
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2021, 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
|
||||
@ -26,15 +26,16 @@ import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import java.util.Random;
|
||||
import java.util.HexFormat;
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.KeyGenerator;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.ShortBufferException;
|
||||
import javax.crypto.spec.GCMParameterSpec;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
/**
|
||||
* @test
|
||||
@ -43,7 +44,6 @@ import javax.crypto.spec.IvParameterSpec;
|
||||
* doesn't use IV).
|
||||
* @author Liwen Wang
|
||||
* @author Parag Salvi
|
||||
* @key randomness
|
||||
*/
|
||||
public class TestAESCipher {
|
||||
|
||||
@ -56,7 +56,9 @@ public class TestAESCipher {
|
||||
"OFB32", "OFB40", "OFB48", "OFB56", "OFB64", "OFB72", "OFB80",
|
||||
"OFB88", "OFB96", "OFB104", "OFB112", "OFB120", "GCM" };
|
||||
private static final String[] PADDING = { "NoPadding", "PKCS5Padding" };
|
||||
private static final int KEY_LENGTH = 128;
|
||||
private static final int KEY_LENGTH = 16;
|
||||
static byte[] plainText = new byte[128];
|
||||
static byte[] key = new byte[KEY_LENGTH];
|
||||
|
||||
public static void main(String argv[]) throws Exception {
|
||||
TestAESCipher test = new TestAESCipher();
|
||||
@ -73,32 +75,31 @@ public class TestAESCipher {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void runTest(String algo, String mo, String pad) throws Exception {
|
||||
Cipher ci = null;
|
||||
byte[] iv = null;
|
||||
AlgorithmParameterSpec aps = null;
|
||||
SecretKey key = null;
|
||||
Cipher ci;
|
||||
System.out.println("Testing " + algo + "/" + mo + "/" + pad);
|
||||
|
||||
byte[] iv = new byte[16];
|
||||
AlgorithmParameterSpec aps = new GCMParameterSpec(128, iv);
|
||||
SecretKey key = new SecretKeySpec(this.key, 0, KEY_LENGTH,"AES");
|
||||
|
||||
try {
|
||||
// Initialization
|
||||
Random rdm = new Random();
|
||||
byte[] plainText = new byte[128];
|
||||
rdm.nextBytes(plainText);
|
||||
|
||||
ci = Cipher.getInstance(algo + "/" + mo + "/" + pad, PROVIDER);
|
||||
KeyGenerator kg = KeyGenerator.getInstance(algo, PROVIDER);
|
||||
kg.init(KEY_LENGTH);
|
||||
key = kg.generateKey();
|
||||
|
||||
// encrypt
|
||||
if (!mo.equalsIgnoreCase("GCM")) {
|
||||
if (mo.equalsIgnoreCase("GCM")) {
|
||||
ci.init(Cipher.ENCRYPT_MODE, key, aps);
|
||||
} else if (mo.equalsIgnoreCase("ECB")) {
|
||||
ci.init(Cipher.ENCRYPT_MODE, key, (AlgorithmParameterSpec)null);
|
||||
} else {
|
||||
ci.init(Cipher.ENCRYPT_MODE, key);
|
||||
ci.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
|
||||
}
|
||||
|
||||
byte[] cipherText = new byte[ci.getOutputSize(plainText.length)];
|
||||
int offset = ci.update(plainText, 0, plainText.length, cipherText,
|
||||
0);
|
||||
0);
|
||||
ci.doFinal(cipherText, offset);
|
||||
|
||||
if (!mo.equalsIgnoreCase("ECB")) {
|
||||
@ -117,25 +118,24 @@ public class TestAESCipher {
|
||||
byte[] recoveredText = new byte[ci.getOutputSize(cipherText.length)];
|
||||
int len = ci.doFinal(cipherText, 0, cipherText.length,
|
||||
recoveredText);
|
||||
byte[] tmp = new byte[len];
|
||||
System.arraycopy(recoveredText, 0, tmp, 0, len);
|
||||
|
||||
// Comparison
|
||||
if (!java.util.Arrays.equals(plainText, tmp)) {
|
||||
if (!java.util.Arrays.equals(plainText, 0 , plainText.length,
|
||||
recoveredText, 0, len)) {
|
||||
System.out.println("Original: ");
|
||||
dumpBytes(plainText);
|
||||
System.out.println(HexFormat.of().formatHex(plainText));
|
||||
System.out.println("Recovered: ");
|
||||
dumpBytes(tmp);
|
||||
throw new RuntimeException(
|
||||
"Original text is not equal with recovered text, with mode:"
|
||||
+ mo);
|
||||
System.out.println(HexFormat.of().
|
||||
formatHex(recoveredText, 0, len));
|
||||
throw new RuntimeException("Original text is not equal with " +
|
||||
"recovered text, with mode:" + mo);
|
||||
}
|
||||
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
//CFB7 and OFB150 are for negative testing
|
||||
if (!mo.equalsIgnoreCase("CFB7") && !mo.equalsIgnoreCase("OFB150")) {
|
||||
System.out.println("Unexpected NoSuchAlgorithmException with mode: "
|
||||
+ mo);
|
||||
System.out.println("Unexpected NoSuchAlgorithmException with" +
|
||||
" mode: " + mo);
|
||||
throw new RuntimeException("Test failed!");
|
||||
}
|
||||
} catch ( NoSuchProviderException | NoSuchPaddingException
|
||||
@ -146,12 +146,4 @@ public class TestAESCipher {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void dumpBytes(byte[] bytes) {
|
||||
for (byte b : bytes) {
|
||||
System.out.print(Integer.toHexString(b));
|
||||
}
|
||||
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2021, 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
|
||||
@ -46,7 +46,7 @@ import javax.crypto.spec.IvParameterSpec;
|
||||
*/
|
||||
public class TestSameBuffer {
|
||||
|
||||
private static final String ALGORITHM = "Rijndael";
|
||||
private static final String ALGORITHM = "AES";
|
||||
private static final String PROVIDER = "SunJCE";
|
||||
private static final String[] MODES = { "ECb", "CbC", "OFB", "CFB150",
|
||||
"cFB", "CFB7", " cFB8", "cFB16", "cFB24", "cFB32", "Cfb40",
|
||||
|
Loading…
x
Reference in New Issue
Block a user