8255557: Decouple GCM from CipherCore

Reviewed-by: valeriep
This commit is contained in:
Anthony Scarpino 2021-06-07 22:22:28 +00:00
parent e546ae27ff
commit c7c77fd32b
15 changed files with 2087 additions and 1545 deletions

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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");
}
}

View 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);
}

View File

@ -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;
}

View File

@ -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());
}
}

View File

@ -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");

View File

@ -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");

View File

@ -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;

View File

@ -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" +

View 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));
}
}

View File

@ -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(

View File

@ -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();
}
}

View File

@ -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",