8255557: Decouple GCM from CipherCore
Reviewed-by: valeriep
This commit is contained in:
parent
e546ae27ff
commit
c7c77fd32b
src/java.base/share/classes
com/sun/crypto/provider
AESCipher.javaCipherCore.javaFeedbackCipher.javaGCM.javaGCTR.javaGHASH.javaGaloisCounterMode.javaSunJCE.java
sun/security/util
test/jdk/com/sun/crypto/provider/Cipher
@ -138,21 +138,6 @@ abstract class AESCipher extends CipherSpi {
|
|||||||
super(32, "CFB", "NOPADDING");
|
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
|
// utility method used by AESCipher and AESWrapCipher
|
||||||
static final void checkKeySize(Key key, int fixedKeySize)
|
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
|
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
|
* 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)
|
protected void engineInit(int opmode, Key key, SecureRandom random)
|
||||||
throws InvalidKeyException {
|
throws InvalidKeyException {
|
||||||
checkKeySize(key, fixedKeySize);
|
checkKeySize(key, fixedKeySize);
|
||||||
updateCalled = false;
|
|
||||||
core.init(opmode, key, random);
|
core.init(opmode, key, random);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -355,7 +335,6 @@ abstract class AESCipher extends CipherSpi {
|
|||||||
SecureRandom random)
|
SecureRandom random)
|
||||||
throws InvalidKeyException, InvalidAlgorithmParameterException {
|
throws InvalidKeyException, InvalidAlgorithmParameterException {
|
||||||
checkKeySize(key, fixedKeySize);
|
checkKeySize(key, fixedKeySize);
|
||||||
updateCalled = false;
|
|
||||||
core.init(opmode, key, params, random);
|
core.init(opmode, key, params, random);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -364,7 +343,6 @@ abstract class AESCipher extends CipherSpi {
|
|||||||
SecureRandom random)
|
SecureRandom random)
|
||||||
throws InvalidKeyException, InvalidAlgorithmParameterException {
|
throws InvalidKeyException, InvalidAlgorithmParameterException {
|
||||||
checkKeySize(key, fixedKeySize);
|
checkKeySize(key, fixedKeySize);
|
||||||
updateCalled = false;
|
|
||||||
core.init(opmode, key, params, random);
|
core.init(opmode, key, params, random);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -389,7 +367,6 @@ abstract class AESCipher extends CipherSpi {
|
|||||||
*/
|
*/
|
||||||
protected byte[] engineUpdate(byte[] input, int inputOffset,
|
protected byte[] engineUpdate(byte[] input, int inputOffset,
|
||||||
int inputLen) {
|
int inputLen) {
|
||||||
updateCalled = true;
|
|
||||||
return core.update(input, inputOffset, inputLen);
|
return core.update(input, inputOffset, inputLen);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -419,7 +396,6 @@ abstract class AESCipher extends CipherSpi {
|
|||||||
protected int engineUpdate(byte[] input, int inputOffset, int inputLen,
|
protected int engineUpdate(byte[] input, int inputOffset, int inputLen,
|
||||||
byte[] output, int outputOffset)
|
byte[] output, int outputOffset)
|
||||||
throws ShortBufferException {
|
throws ShortBufferException {
|
||||||
updateCalled = true;
|
|
||||||
return core.update(input, inputOffset, inputLen, output,
|
return core.update(input, inputOffset, inputLen, output,
|
||||||
outputOffset);
|
outputOffset);
|
||||||
}
|
}
|
||||||
@ -458,7 +434,6 @@ abstract class AESCipher extends CipherSpi {
|
|||||||
protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
|
protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
|
||||||
throws IllegalBlockSizeException, BadPaddingException {
|
throws IllegalBlockSizeException, BadPaddingException {
|
||||||
byte[] out = core.doFinal(input, inputOffset, inputLen);
|
byte[] out = core.doFinal(input, inputOffset, inputLen);
|
||||||
updateCalled = false;
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -504,7 +479,6 @@ abstract class AESCipher extends CipherSpi {
|
|||||||
BadPaddingException {
|
BadPaddingException {
|
||||||
int outLen = core.doFinal(input, inputOffset, inputLen, output,
|
int outLen = core.doFinal(input, inputOffset, inputLen, output,
|
||||||
outputOffset);
|
outputOffset);
|
||||||
updateCalled = false;
|
|
||||||
return outLen;
|
return outLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -577,86 +551,6 @@ abstract class AESCipher extends CipherSpi {
|
|||||||
wrappedKeyType);
|
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
|
* Finalize crypto operation with ByteBuffers
|
||||||
*
|
*
|
||||||
@ -672,10 +566,6 @@ abstract class AESCipher extends CipherSpi {
|
|||||||
protected int engineDoFinal(ByteBuffer input, ByteBuffer output)
|
protected int engineDoFinal(ByteBuffer input, ByteBuffer output)
|
||||||
throws ShortBufferException, IllegalBlockSizeException,
|
throws ShortBufferException, IllegalBlockSizeException,
|
||||||
BadPaddingException {
|
BadPaddingException {
|
||||||
if (core.getMode() == CipherCore.GCM_MODE && !input.hasArray()) {
|
return super.engineDoFinal(input, output);
|
||||||
return core.gcmDoFinal(input, output);
|
|
||||||
} else {
|
|
||||||
return super.engineDoFinal(input, output);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
|
|
||||||
package com.sun.crypto.provider;
|
package com.sun.crypto.provider;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
@ -83,7 +82,6 @@ final class CipherCore {
|
|||||||
* currently, only the following cases have non-zero values:
|
* currently, only the following cases have non-zero values:
|
||||||
* 1) CTS mode - due to its special handling on the last two blocks
|
* 1) CTS mode - due to its special handling on the last two blocks
|
||||||
* (the last one may be incomplete).
|
* (the last one may be incomplete).
|
||||||
* 2) GCM mode + decryption - due to its trailing tag bytes
|
|
||||||
*/
|
*/
|
||||||
private int minBytes = 0;
|
private int minBytes = 0;
|
||||||
|
|
||||||
@ -125,24 +123,6 @@ final class CipherCore {
|
|||||||
private static final int PCBC_MODE = 4;
|
private static final int PCBC_MODE = 4;
|
||||||
private static final int CTR_MODE = 5;
|
private static final int CTR_MODE = 5;
|
||||||
private static final int CTS_MODE = 6;
|
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
|
* Creates an instance of CipherCore with default ECB mode and
|
||||||
@ -197,15 +177,6 @@ final class CipherCore {
|
|||||||
cipher = new CounterMode(rawImpl);
|
cipher = new CounterMode(rawImpl);
|
||||||
unitBytes = 1;
|
unitBytes = 1;
|
||||||
padding = null;
|
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")) {
|
} else if (modeUpperCase.startsWith("CFB")) {
|
||||||
cipherMode = CFB_MODE;
|
cipherMode = CFB_MODE;
|
||||||
unitBytes = getNumOfUnit(mode, "CFB".length(), blockSize);
|
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)
|
private static int getNumOfUnit(String mode, int offset, int blockSize)
|
||||||
throws NoSuchAlgorithmException {
|
throws NoSuchAlgorithmException {
|
||||||
int result = blockSize; // use blockSize as default value
|
int result = blockSize; // use blockSize as default value
|
||||||
@ -279,17 +241,13 @@ final class CipherCore {
|
|||||||
+ " not implemented");
|
+ " not implemented");
|
||||||
}
|
}
|
||||||
if ((padding != null) &&
|
if ((padding != null) &&
|
||||||
((cipherMode == CTR_MODE) || (cipherMode == CTS_MODE)
|
((cipherMode == CTR_MODE) || (cipherMode == CTS_MODE))) {
|
||||||
|| (cipherMode == GCM_MODE))) {
|
|
||||||
padding = null;
|
padding = null;
|
||||||
String modeStr = null;
|
String modeStr = null;
|
||||||
switch (cipherMode) {
|
switch (cipherMode) {
|
||||||
case CTR_MODE:
|
case CTR_MODE:
|
||||||
modeStr = "CTR";
|
modeStr = "CTR";
|
||||||
break;
|
break;
|
||||||
case GCM_MODE:
|
|
||||||
modeStr = "GCM";
|
|
||||||
break;
|
|
||||||
case CTS_MODE:
|
case CTS_MODE:
|
||||||
modeStr = "CTS";
|
modeStr = "CTS";
|
||||||
break;
|
break;
|
||||||
@ -310,7 +268,7 @@ final class CipherCore {
|
|||||||
* <code>inputLen</code> (in bytes).
|
* <code>inputLen</code> (in bytes).
|
||||||
*
|
*
|
||||||
* <p>This call takes into account any unprocessed (buffered) data from a
|
* <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
|
* <p>The actual output length of the next <code>update</code> or
|
||||||
* <code>doFinal</code> call may be smaller than the length returned by
|
* <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) {
|
private int getOutputSizeByOperation(int inputLen, boolean isDoFinal) {
|
||||||
int totalLen = Math.addExact(buffered, cipher.getBufferedLength());
|
int totalLen = buffered;
|
||||||
totalLen = Math.addExact(totalLen, inputLen);
|
totalLen = Math.addExact(totalLen, inputLen);
|
||||||
switch (cipherMode) {
|
if (padding != null && !decrypting) {
|
||||||
case GCM_MODE:
|
if (unitBytes != blockSize) {
|
||||||
if (isDoFinal) {
|
if (totalLen < diffBlocksize) {
|
||||||
int tagLen = ((GaloisCounterMode) cipher).getTagLen();
|
totalLen = diffBlocksize;
|
||||||
if (!decrypting) {
|
|
||||||
totalLen = Math.addExact(totalLen, tagLen);
|
|
||||||
} else {
|
} 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;
|
return totalLen;
|
||||||
}
|
}
|
||||||
@ -398,26 +339,15 @@ final class CipherCore {
|
|||||||
AlgorithmParameterSpec spec;
|
AlgorithmParameterSpec spec;
|
||||||
byte[] iv = getIV();
|
byte[] iv = getIV();
|
||||||
if (iv == null) {
|
if (iv == null) {
|
||||||
// generate spec using default value
|
iv = new byte[blockSize];
|
||||||
if (cipherMode == GCM_MODE) {
|
|
||||||
iv = new byte[GaloisCounterMode.DEFAULT_IV_LEN];
|
|
||||||
} else {
|
|
||||||
iv = new byte[blockSize];
|
|
||||||
}
|
|
||||||
SunJCE.getRandom().nextBytes(iv);
|
SunJCE.getRandom().nextBytes(iv);
|
||||||
}
|
}
|
||||||
if (cipherMode == GCM_MODE) {
|
if (algName.equals("RC2")) {
|
||||||
algName = "GCM";
|
RC2Crypt rawImpl = (RC2Crypt) cipher.getEmbeddedCipher();
|
||||||
spec = new GCMParameterSpec
|
spec = new RC2ParameterSpec
|
||||||
(((GaloisCounterMode) cipher).getTagLen()*8, iv);
|
(rawImpl.getEffectiveKeyBits(), iv);
|
||||||
} else {
|
} else {
|
||||||
if (algName.equals("RC2")) {
|
spec = new IvParameterSpec(iv);
|
||||||
RC2Crypt rawImpl = (RC2Crypt) cipher.getEmbeddedCipher();
|
|
||||||
spec = new RC2ParameterSpec
|
|
||||||
(rawImpl.getEffectiveKeyBits(), iv);
|
|
||||||
} else {
|
|
||||||
spec = new IvParameterSpec(iv);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
params = AlgorithmParameters.getInstance(algName,
|
params = AlgorithmParameters.getInstance(algName,
|
||||||
@ -504,106 +434,51 @@ final class CipherCore {
|
|||||||
|| (opmode == Cipher.UNWRAP_MODE);
|
|| (opmode == Cipher.UNWRAP_MODE);
|
||||||
|
|
||||||
byte[] keyBytes = getKeyBytes(key);
|
byte[] keyBytes = getKeyBytes(key);
|
||||||
try {
|
byte[] ivBytes = null;
|
||||||
int tagLen = -1;
|
if (params != null) {
|
||||||
byte[] ivBytes = null;
|
if (params instanceof IvParameterSpec) {
|
||||||
if (params != null) {
|
ivBytes = ((IvParameterSpec) params).getIV();
|
||||||
if (cipherMode == GCM_MODE) {
|
if ((ivBytes == null) || (ivBytes.length != blockSize)) {
|
||||||
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) {
|
|
||||||
throw new InvalidAlgorithmParameterException
|
throw new InvalidAlgorithmParameterException
|
||||||
("ECB mode cannot use IV");
|
("Wrong IV length: must be " + blockSize +
|
||||||
|
" bytes long");
|
||||||
}
|
}
|
||||||
} else if (ivBytes == null) {
|
} else if (params instanceof RC2ParameterSpec) {
|
||||||
if (decrypting) {
|
ivBytes = ((RC2ParameterSpec) params).getIV();
|
||||||
throw new InvalidAlgorithmParameterException("Parameters "
|
if ((ivBytes != null) && (ivBytes.length != blockSize)) {
|
||||||
+ "missing");
|
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 {
|
} else {
|
||||||
cipher.init(decrypting, algorithm, keyBytes, ivBytes);
|
throw new InvalidAlgorithmParameterException
|
||||||
}
|
("Unsupported parameter: " + params);
|
||||||
// skip checking key+iv from now on until after doFinal()
|
|
||||||
requireReinit = false;
|
|
||||||
} finally {
|
|
||||||
if (lastEncKey != keyBytes) {
|
|
||||||
Arrays.fill(keyBytes, (byte) 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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,
|
void init(int opmode, Key key, AlgorithmParameters params,
|
||||||
@ -613,16 +488,11 @@ final class CipherCore {
|
|||||||
String paramType = null;
|
String paramType = null;
|
||||||
if (params != null) {
|
if (params != null) {
|
||||||
try {
|
try {
|
||||||
if (cipherMode == GCM_MODE) {
|
// NOTE: RC2 parameters are always handled through
|
||||||
paramType = "GCM";
|
// init(..., AlgorithmParameterSpec,...) method, so
|
||||||
spec = params.getParameterSpec(GCMParameterSpec.class);
|
// we can assume IvParameterSpec type here.
|
||||||
} else {
|
paramType = "IV";
|
||||||
// NOTE: RC2 parameters are always handled through
|
spec = params.getParameterSpec(IvParameterSpec.class);
|
||||||
// init(..., AlgorithmParameterSpec,...) method, so
|
|
||||||
// we can assume IvParameterSpec type here.
|
|
||||||
paramType = "IV";
|
|
||||||
spec = params.getParameterSpec(IvParameterSpec.class);
|
|
||||||
}
|
|
||||||
} catch (InvalidParameterSpecException ipse) {
|
} catch (InvalidParameterSpecException ipse) {
|
||||||
throw new InvalidAlgorithmParameterException
|
throw new InvalidAlgorithmParameterException
|
||||||
("Wrong parameter type: " + paramType + " expected");
|
("Wrong parameter type: " + paramType + " expected");
|
||||||
@ -671,7 +541,6 @@ final class CipherCore {
|
|||||||
* (e.g., has not been initialized)
|
* (e.g., has not been initialized)
|
||||||
*/
|
*/
|
||||||
byte[] update(byte[] input, int inputOffset, int inputLen) {
|
byte[] update(byte[] input, int inputOffset, int inputLen) {
|
||||||
checkReinit();
|
|
||||||
|
|
||||||
byte[] output = null;
|
byte[] output = null;
|
||||||
try {
|
try {
|
||||||
@ -719,7 +588,6 @@ final class CipherCore {
|
|||||||
*/
|
*/
|
||||||
int update(byte[] input, int inputOffset, int inputLen, byte[] output,
|
int update(byte[] input, int inputOffset, int inputLen, byte[] output,
|
||||||
int outputOffset) throws ShortBufferException {
|
int outputOffset) throws ShortBufferException {
|
||||||
checkReinit();
|
|
||||||
|
|
||||||
// figure out how much can be sent to crypto function
|
// figure out how much can be sent to crypto function
|
||||||
int len = Math.addExact(buffered, inputLen);
|
int len = Math.addExact(buffered, inputLen);
|
||||||
@ -854,7 +722,6 @@ final class CipherCore {
|
|||||||
byte[] doFinal(byte[] input, int inputOffset, int inputLen)
|
byte[] doFinal(byte[] input, int inputOffset, int inputLen)
|
||||||
throws IllegalBlockSizeException, BadPaddingException {
|
throws IllegalBlockSizeException, BadPaddingException {
|
||||||
try {
|
try {
|
||||||
checkReinit();
|
|
||||||
byte[] output = new byte[getOutputSizeByOperation(inputLen, true)];
|
byte[] output = new byte[getOutputSizeByOperation(inputLen, true)];
|
||||||
byte[] finalBuf = prepareInputBuffer(input, inputOffset,
|
byte[] finalBuf = prepareInputBuffer(input, inputOffset,
|
||||||
inputLen, output, 0);
|
inputLen, output, 0);
|
||||||
@ -868,7 +735,7 @@ final class CipherCore {
|
|||||||
if (outLen < output.length) {
|
if (outLen < output.length) {
|
||||||
byte[] copy = Arrays.copyOf(output, outLen);
|
byte[] copy = Arrays.copyOf(output, outLen);
|
||||||
if (decrypting) {
|
if (decrypting) {
|
||||||
// Zero out internal (ouput) array
|
// Zero out internal (output) array
|
||||||
Arrays.fill(output, (byte) 0x00);
|
Arrays.fill(output, (byte) 0x00);
|
||||||
}
|
}
|
||||||
return copy;
|
return copy;
|
||||||
@ -921,7 +788,6 @@ final class CipherCore {
|
|||||||
int outputOffset)
|
int outputOffset)
|
||||||
throws IllegalBlockSizeException, ShortBufferException,
|
throws IllegalBlockSizeException, ShortBufferException,
|
||||||
BadPaddingException {
|
BadPaddingException {
|
||||||
checkReinit();
|
|
||||||
|
|
||||||
int estOutSize = getOutputSizeByOperation(inputLen, true);
|
int estOutSize = getOutputSizeByOperation(inputLen, true);
|
||||||
int outputCapacity = checkOutputCapacity(output, outputOffset,
|
int outputCapacity = checkOutputCapacity(output, outputOffset,
|
||||||
@ -943,15 +809,13 @@ final class CipherCore {
|
|||||||
if (outputCapacity < estOutSize) {
|
if (outputCapacity < estOutSize) {
|
||||||
cipher.save();
|
cipher.save();
|
||||||
}
|
}
|
||||||
if (getMode() != GCM_MODE || outputCapacity < estOutSize) {
|
// create temporary output buffer if the estimated size is larger
|
||||||
// create temporary output buffer if the estimated size is larger
|
// than the user-provided buffer.
|
||||||
// than the user-provided buffer.
|
internalOutput = new byte[estOutSize];
|
||||||
internalOutput = new byte[estOutSize];
|
offset = 0;
|
||||||
offset = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
byte[] outBuffer = (internalOutput != null) ? internalOutput : output;
|
|
||||||
|
|
||||||
|
byte[] outBuffer = (internalOutput != null) ? internalOutput : output;
|
||||||
int outLen = fillOutputBuffer(finalBuf, finalOffset, outBuffer,
|
int outLen = fillOutputBuffer(finalBuf, finalOffset, outBuffer,
|
||||||
offset, finalBufLen, input);
|
offset, finalBufLen, input);
|
||||||
|
|
||||||
@ -961,13 +825,13 @@ final class CipherCore {
|
|||||||
// restore so users can retry with a larger buffer
|
// restore so users can retry with a larger buffer
|
||||||
cipher.restore();
|
cipher.restore();
|
||||||
throw new ShortBufferException("Output buffer too short: "
|
throw new ShortBufferException("Output buffer too short: "
|
||||||
+ (outputCapacity)
|
+ (outputCapacity) + " bytes given, " + outLen
|
||||||
+ " bytes given, " + outLen
|
+ " bytes needed");
|
||||||
+ " bytes needed");
|
|
||||||
}
|
}
|
||||||
// copy the result into user-supplied output buffer
|
// copy the result into user-supplied output buffer
|
||||||
if (internalOutput != null) {
|
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
|
// decrypt mode. Zero out output data that's not required
|
||||||
Arrays.fill(internalOutput, (byte) 0x00);
|
Arrays.fill(internalOutput, (byte) 0x00);
|
||||||
}
|
}
|
||||||
@ -1001,7 +865,7 @@ final class CipherCore {
|
|||||||
// calculate total input length
|
// calculate total input length
|
||||||
int len = Math.addExact(buffered, inputLen);
|
int len = Math.addExact(buffered, inputLen);
|
||||||
// calculate padding length
|
// calculate padding length
|
||||||
int totalLen = Math.addExact(len, cipher.getBufferedLength());
|
int totalLen = len;
|
||||||
int paddingLen = 0;
|
int paddingLen = 0;
|
||||||
// will the total input length be a multiple of blockSize?
|
// will the total input length be a multiple of blockSize?
|
||||||
if (unitBytes != blockSize) {
|
if (unitBytes != blockSize) {
|
||||||
@ -1059,30 +923,27 @@ final class CipherCore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private int fillOutputBuffer(byte[] finalBuf, int finalOffset,
|
private int fillOutputBuffer(byte[] finalBuf, int finalOffset,
|
||||||
byte[] output, int outOfs, int finalBufLen,
|
byte[] output, int outOfs, int finalBufLen, byte[] input)
|
||||||
byte[] input)
|
throws ShortBufferException, BadPaddingException,
|
||||||
throws ShortBufferException, BadPaddingException,
|
IllegalBlockSizeException {
|
||||||
IllegalBlockSizeException {
|
|
||||||
int len;
|
int len;
|
||||||
try {
|
try {
|
||||||
len = finalNoPadding(finalBuf, finalOffset, output,
|
len = finalNoPadding(finalBuf, finalOffset, output,
|
||||||
outOfs, finalBufLen);
|
outOfs, finalBufLen);
|
||||||
if (decrypting && padding != null) {
|
if (decrypting && padding != null) {
|
||||||
len = unpad(len, outOfs, output);
|
len = unpad(len, outOfs, output);
|
||||||
}
|
}
|
||||||
return len;
|
return len;
|
||||||
} finally {
|
} finally {
|
||||||
if (!decrypting) {
|
if (!decrypting && finalBuf != input) {
|
||||||
// reset after doFinal() for GCM encryption
|
// done with internal finalBuf array. Copied to output
|
||||||
requireReinit = (cipherMode == GCM_MODE);
|
Arrays.fill(finalBuf, (byte) 0x00);
|
||||||
if (finalBuf != input) {
|
|
||||||
// done with internal finalBuf array. Copied to output
|
|
||||||
Arrays.fill(finalBuf, (byte) 0x00);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private int checkOutputCapacity(byte[] output, int outputOffset,
|
private int checkOutputCapacity(byte[] output, int outputOffset,
|
||||||
int estOutSize) throws ShortBufferException {
|
int estOutSize) throws ShortBufferException {
|
||||||
// check output buffer capacity.
|
// check output buffer capacity.
|
||||||
@ -1098,23 +959,14 @@ final class CipherCore {
|
|||||||
return outputCapacity;
|
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,
|
private int finalNoPadding(byte[] in, int inOfs, byte[] out, int outOfs,
|
||||||
int len)
|
int len)
|
||||||
throws IllegalBlockSizeException, AEADBadTagException,
|
throws IllegalBlockSizeException, ShortBufferException {
|
||||||
ShortBufferException {
|
|
||||||
|
|
||||||
if ((cipherMode != GCM_MODE) && (in == null || len == 0)) {
|
if (in == null || len == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if ((cipherMode != CFB_MODE) && (cipherMode != OFB_MODE) &&
|
if ((cipherMode != CFB_MODE) && (cipherMode != OFB_MODE) &&
|
||||||
(cipherMode != GCM_MODE) &&
|
|
||||||
((len % unitBytes) != 0) && (cipherMode != CTS_MODE)) {
|
((len % unitBytes) != 0) && (cipherMode != CTS_MODE)) {
|
||||||
if (padding != null) {
|
if (padding != null) {
|
||||||
throw new IllegalBlockSizeException
|
throw new IllegalBlockSizeException
|
||||||
@ -1126,7 +978,7 @@ final class CipherCore {
|
|||||||
+ " bytes");
|
+ " bytes");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int outLen = 0;
|
int outLen;
|
||||||
if (decrypting) {
|
if (decrypting) {
|
||||||
outLen = cipher.decryptFinal(in, inOfs, len, out, outOfs);
|
outLen = cipher.decryptFinal(in, inOfs, len, out, outOfs);
|
||||||
} else {
|
} else {
|
||||||
@ -1217,59 +1069,4 @@ final class CipherCore {
|
|||||||
Arrays.fill(encodedKey, (byte)0);
|
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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -25,7 +25,6 @@
|
|||||||
|
|
||||||
package com.sun.crypto.provider;
|
package com.sun.crypto.provider;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
import javax.crypto.*;
|
import javax.crypto.*;
|
||||||
@ -200,70 +199,7 @@ abstract class FeedbackCipher {
|
|||||||
*/
|
*/
|
||||||
int decryptFinal(byte[] cipher, int cipherOffset, int cipherLen,
|
int decryptFinal(byte[] cipher, int cipherOffset, int cipherLen,
|
||||||
byte[] plain, int plainOffset)
|
byte[] plain, int plainOffset)
|
||||||
throws IllegalBlockSizeException, AEADBadTagException,
|
throws IllegalBlockSizeException, ShortBufferException {
|
||||||
ShortBufferException {
|
|
||||||
return decrypt(cipher, cipherOffset, cipherLen, plain, plainOffset);
|
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.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
import javax.crypto.IllegalBlockSizeException;
|
import java.util.Arrays;
|
||||||
import static com.sun.crypto.provider.AESConstants.AES_BLOCK_SIZE;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class represents the GCTR function defined in NIST 800-38D
|
* 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
|
* @since 1.8
|
||||||
*/
|
*/
|
||||||
final class GCTR extends CounterMode {
|
final class GCTR extends CounterMode implements GCM {
|
||||||
|
|
||||||
// Maximum buffer size rotating ByteBuffer->byte[] intrinsic copy
|
// Maximum buffer size rotating ByteBuffer->byte[] intrinsic copy
|
||||||
private static final int MAX_LEN = 1024;
|
private static final int MAX_LEN = 1024;
|
||||||
|
private byte[] block;
|
||||||
|
|
||||||
GCTR(SymmetricCipher cipher, byte[] initialCounterBlk) {
|
GCTR(SymmetricCipher cipher, byte[] initialCounterBlk) {
|
||||||
super(cipher);
|
super(cipher);
|
||||||
if (initialCounterBlk.length != AES_BLOCK_SIZE) {
|
if (initialCounterBlk.length != blockSize) {
|
||||||
throw new RuntimeException("length of initial counter block (" +
|
throw new RuntimeException("length of initial counter block (" +
|
||||||
initialCounterBlk.length + ") not equal to AES_BLOCK_SIZE (" +
|
initialCounterBlk.length + ") not equal to blockSize (" +
|
||||||
AES_BLOCK_SIZE + ")");
|
blockSize + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
iv = initialCounterBlk;
|
iv = initialCounterBlk;
|
||||||
@ -83,30 +83,47 @@ final class GCTR extends CounterMode {
|
|||||||
return blocksLeft;
|
return blocksLeft;
|
||||||
}
|
}
|
||||||
|
|
||||||
// input must be multiples of 128-bit blocks when calling update
|
private void checkBlock() {
|
||||||
int update(byte[] in, int inOfs, int inLen, byte[] out, int outOfs) {
|
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) {
|
if (inLen - inOfs > in.length) {
|
||||||
throw new RuntimeException("input length out of bound");
|
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");
|
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");
|
throw new RuntimeException("output buffer too small");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inLen -= inLen % blockSize;
|
||||||
long blocksLeft = blocksUntilRollover();
|
long blocksLeft = blocksUntilRollover();
|
||||||
int numOfCompleteBlocks = inLen / AES_BLOCK_SIZE;
|
int numOfCompleteBlocks = inLen / blockSize;
|
||||||
if (numOfCompleteBlocks >= blocksLeft) {
|
if (numOfCompleteBlocks >= blocksLeft) {
|
||||||
// Counter Mode encryption cannot be used because counter will
|
// Counter Mode encryption cannot be used because counter will
|
||||||
// roll over incorrectly. Use GCM-specific code instead.
|
// roll over incorrectly. Use GCM-specific code instead.
|
||||||
byte[] encryptedCntr = new byte[AES_BLOCK_SIZE];
|
checkBlock();
|
||||||
for (int i = 0; i < numOfCompleteBlocks; i++) {
|
for (int i = 0; i < numOfCompleteBlocks; i++) {
|
||||||
embeddedCipher.encryptBlock(counter, 0, encryptedCntr, 0);
|
embeddedCipher.encryptBlock(counter, 0, block, 0);
|
||||||
for (int n = 0; n < AES_BLOCK_SIZE; n++) {
|
for (int n = 0; n < blockSize; n++) {
|
||||||
int index = (i * AES_BLOCK_SIZE + n);
|
int index = (i * blockSize + n);
|
||||||
out[outOfs + index] =
|
out[outOfs + index] =
|
||||||
(byte) ((in[inOfs + index] ^ encryptedCntr[n]));
|
(byte) ((in[inOfs + index] ^ block[n]));
|
||||||
}
|
}
|
||||||
GaloisCounterMode.increment32(counter);
|
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) {
|
if (inLen - inOfs > in.length) {
|
||||||
throw new RuntimeException("input length out of bound");
|
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");
|
throw new RuntimeException("input length unsupported");
|
||||||
}
|
}
|
||||||
// See GaloisCounterMode. decryptFinal(bytebuffer, bytebuffer) for
|
// See GaloisCounterMode. decryptFinal(bytebuffer, bytebuffer) for
|
||||||
// details on the check for 'dst' having enough space for the result.
|
// details on the check for 'dst' having enough space for the result.
|
||||||
|
|
||||||
long blocksLeft = blocksUntilRollover();
|
long blocksLeft = blocksUntilRollover();
|
||||||
int numOfCompleteBlocks = inLen / AES_BLOCK_SIZE;
|
int numOfCompleteBlocks = inLen / blockSize;
|
||||||
if (numOfCompleteBlocks >= blocksLeft) {
|
if (numOfCompleteBlocks >= blocksLeft) {
|
||||||
// Counter Mode encryption cannot be used because counter will
|
// Counter Mode encryption cannot be used because counter will
|
||||||
// roll over incorrectly. Use GCM-specific code instead.
|
// roll over incorrectly. Use GCM-specific code instead.
|
||||||
byte[] encryptedCntr = new byte[AES_BLOCK_SIZE];
|
checkBlock();
|
||||||
for (int i = 0; i < numOfCompleteBlocks; i++) {
|
for (int i = 0; i < numOfCompleteBlocks; i++) {
|
||||||
embeddedCipher.encryptBlock(counter, 0, encryptedCntr, 0);
|
embeddedCipher.encryptBlock(counter, 0, block, 0);
|
||||||
for (int n = 0; n < AES_BLOCK_SIZE; n++) {
|
for (int n = 0; n < blockSize; n++) {
|
||||||
int index = (i * AES_BLOCK_SIZE + n);
|
int index = (i * blockSize + n);
|
||||||
dst.put((byte) ((in[inOfs + index] ^ encryptedCntr[n])));
|
dst.put((byte) ((in[inOfs + index] ^ block[n])));
|
||||||
}
|
}
|
||||||
GaloisCounterMode.increment32(counter);
|
GaloisCounterMode.increment32(counter);
|
||||||
}
|
}
|
||||||
return inLen;
|
return inLen;
|
||||||
} else {
|
} else {
|
||||||
int len = inLen - inLen % AES_BLOCK_SIZE;
|
int len = inLen - inLen % blockSize;
|
||||||
int processed = len;
|
int processed = len;
|
||||||
byte[] out = new byte[Math.min(MAX_LEN, len)];
|
byte[] out = new byte[Math.min(MAX_LEN, len)];
|
||||||
int offset = inOfs;
|
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.
|
* Operate on only blocksize data leaving the remainder in the src buffer.
|
||||||
int update(ByteBuffer src, ByteBuffer dst) {
|
*/
|
||||||
|
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();
|
long blocksLeft = blocksUntilRollover();
|
||||||
int numOfCompleteBlocks = src.remaining() / AES_BLOCK_SIZE;
|
int numOfCompleteBlocks = src.remaining() / blockSize;
|
||||||
if (numOfCompleteBlocks >= blocksLeft) {
|
if (numOfCompleteBlocks >= blocksLeft) {
|
||||||
// Counter Mode encryption cannot be used because counter will
|
// Counter Mode encryption cannot be used because counter will
|
||||||
// roll over incorrectly. Use GCM-specific code instead.
|
// roll over incorrectly. Use GCM-specific code instead.
|
||||||
byte[] encryptedCntr = new byte[AES_BLOCK_SIZE];
|
checkBlock();
|
||||||
for (int i = 0; i < numOfCompleteBlocks; i++) {
|
for (int i = 0; i < numOfCompleteBlocks; i++) {
|
||||||
embeddedCipher.encryptBlock(counter, 0, encryptedCntr, 0);
|
embeddedCipher.encryptBlock(counter, 0, block, 0);
|
||||||
for (int n = 0; n < AES_BLOCK_SIZE; n++) {
|
for (int n = 0; n < blockSize; n++) {
|
||||||
dst.put((byte) (src.get() ^ encryptedCntr[n]));
|
dst.put((byte) (src.get() ^ block[n]));
|
||||||
}
|
}
|
||||||
GaloisCounterMode.increment32(counter);
|
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;
|
int processed = len;
|
||||||
byte[] in = new byte[Math.min(MAX_LEN, len)];
|
byte[] in = new byte[Math.min(MAX_LEN, len)];
|
||||||
while (processed > MAX_LEN) {
|
while (processed > MAX_LEN) {
|
||||||
@ -196,50 +240,62 @@ final class GCTR extends CounterMode {
|
|||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
// input can be arbitrary size when calling doFinal
|
/**
|
||||||
int doFinal(byte[] in, int inOfs, int inLen, byte[] out,
|
* doFinal operation by using update() for any full block operations needed,
|
||||||
int outOfs) throws IllegalBlockSizeException {
|
* then operating on the final bytes in the input buffer.
|
||||||
try {
|
*
|
||||||
if (inLen < 0) {
|
* This method will not write any block padding to the output buffer
|
||||||
throw new IllegalBlockSizeException("Negative input size!");
|
*/
|
||||||
} else if (inLen > 0) {
|
public int doFinal(byte[] in, int inOfs, int inLen, byte[] out,
|
||||||
int lastBlockSize = inLen % AES_BLOCK_SIZE;
|
int outOfs) {
|
||||||
int completeBlkLen = inLen - lastBlockSize;
|
if (inLen == 0) {
|
||||||
// process the complete blocks first
|
return 0;
|
||||||
update(in, inOfs, completeBlkLen, out, outOfs);
|
}
|
||||||
if (lastBlockSize != 0) {
|
int lastBlockSize = inLen % blockSize;
|
||||||
// do the last partial block
|
int completeBlkLen = inLen - lastBlockSize;
|
||||||
byte[] encryptedCntr = new byte[AES_BLOCK_SIZE];
|
// process the complete blocks first
|
||||||
embeddedCipher.encryptBlock(counter, 0, encryptedCntr, 0);
|
update(in, inOfs, completeBlkLen, out, outOfs);
|
||||||
for (int n = 0; n < lastBlockSize; n++) {
|
if (lastBlockSize != 0) {
|
||||||
out[outOfs + completeBlkLen + n] =
|
// do the last partial block
|
||||||
(byte) ((in[inOfs + completeBlkLen + n] ^
|
checkBlock();
|
||||||
encryptedCntr[n]));
|
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;
|
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 len = src.remaining();
|
||||||
int lastBlockSize = len % AES_BLOCK_SIZE;
|
int lastBlockSize = len % blockSize;
|
||||||
try {
|
update(src, dst);
|
||||||
update(src, dst);
|
if (lastBlockSize != 0) {
|
||||||
if (lastBlockSize != 0) {
|
checkBlock();
|
||||||
// do the last partial block
|
// do the last partial block
|
||||||
byte[] encryptedCntr = new byte[AES_BLOCK_SIZE];
|
embeddedCipher.encryptBlock(counter, 0, block, 0);
|
||||||
embeddedCipher.encryptBlock(counter, 0, encryptedCntr, 0);
|
for (int n = 0; n < lastBlockSize; n++) {
|
||||||
for (int n = 0; n < lastBlockSize; n++) {
|
dst.put((byte) (src.get() ^ block[n]));
|
||||||
dst.put((byte) (src.get() ^ encryptedCntr[n]));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
reset();
|
|
||||||
}
|
}
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||||
* Copyright (c) 2015 Red Hat, Inc.
|
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -25,11 +24,15 @@
|
|||||||
*/
|
*/
|
||||||
/*
|
/*
|
||||||
* (C) Copyright IBM Corp. 2013
|
* (C) Copyright IBM Corp. 2013
|
||||||
|
* Copyright (c) 2015 Red Hat, Inc.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.sun.crypto.provider;
|
package com.sun.crypto.provider;
|
||||||
|
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.invoke.VarHandle;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
import java.security.ProviderException;
|
import java.security.ProviderException;
|
||||||
|
|
||||||
import jdk.internal.vm.annotation.IntrinsicCandidate;
|
import jdk.internal.vm.annotation.IntrinsicCandidate;
|
||||||
@ -44,27 +47,19 @@ import jdk.internal.vm.annotation.IntrinsicCandidate;
|
|||||||
*
|
*
|
||||||
* @since 1.8
|
* @since 1.8
|
||||||
*/
|
*/
|
||||||
final class GHASH {
|
|
||||||
|
|
||||||
private static long getLong(byte[] buffer, int offset) {
|
final class GHASH implements Cloneable, GCM {
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final int AES_BLOCK_SIZE = 16;
|
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].
|
// Multiplies state[0], state[1] by subkeyH[0], subkeyH[1].
|
||||||
private static void blockMult(long[] st, long[] subH) {
|
private static void blockMult(long[] st, long[] subH) {
|
||||||
long Z0 = 0;
|
long Z0 = 0;
|
||||||
@ -127,15 +122,13 @@ final class GHASH {
|
|||||||
|
|
||||||
/* subkeyHtbl and state are stored in long[] for GHASH intrinsic use */
|
/* 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;
|
private long[] subkeyHtbl;
|
||||||
|
|
||||||
// buffer for storing hash
|
// buffer for storing hash
|
||||||
private final long[] state;
|
private final long[] state;
|
||||||
|
|
||||||
// variables for save/restore calls
|
|
||||||
private long stateSave0, stateSave1;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the cipher in the specified mode with the given key
|
* Initializes the cipher in the specified mode with the given key
|
||||||
* and iv.
|
* and iv.
|
||||||
@ -151,87 +144,106 @@ final class GHASH {
|
|||||||
}
|
}
|
||||||
state = new long[2];
|
state = new long[2];
|
||||||
subkeyHtbl = new long[2*9];
|
subkeyHtbl = new long[2*9];
|
||||||
subkeyHtbl[0] = getLong(subkeyH, 0);
|
subkeyHtbl[0] = (long)asLongView.get(subkeyH, 0);
|
||||||
subkeyHtbl[1] = getLong(subkeyH, 8);
|
subkeyHtbl[1] = (long)asLongView.get(subkeyH, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Cloning constructor
|
||||||
* Resets the GHASH object to its original state, i.e. blank w/
|
private GHASH(GHASH g) {
|
||||||
* the same subkey H. Used after digest() is called and to re-use
|
state = g.state.clone();
|
||||||
* this object for different data w/ the same H.
|
subkeyHtbl = g.subkeyHtbl.clone();
|
||||||
*/
|
|
||||||
void reset() {
|
|
||||||
state[0] = 0;
|
|
||||||
state[1] = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Save the current snapshot of this GHASH object.
|
public GHASH clone() {
|
||||||
*/
|
return new GHASH(this);
|
||||||
void save() {
|
|
||||||
stateSave0 = state[0];
|
|
||||||
stateSave1 = state[1];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private static void processBlock(byte[] data, int ofs, long[] st,
|
||||||
* Restores this object using the saved snapshot.
|
long[] subH) {
|
||||||
*/
|
st[0] ^= (long)asLongView.get(data, ofs);
|
||||||
void restore() {
|
st[1] ^= (long)asLongView.get(data, ofs + 8);
|
||||||
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);
|
|
||||||
blockMult(st, subH);
|
blockMult(st, subH);
|
||||||
}
|
}
|
||||||
|
|
||||||
void update(byte[] in) {
|
int update(byte[] in) {
|
||||||
update(in, 0, in.length);
|
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) {
|
if (inLen == 0) {
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
ghashRangeCheck(in, inOfs, inLen, state, subkeyHtbl);
|
int len = inLen - (inLen % AES_BLOCK_SIZE);
|
||||||
processBlocks(in, inOfs, inLen/AES_BLOCK_SIZE, state, subkeyHtbl);
|
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.
|
// 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);
|
inLen -= (inLen % AES_BLOCK_SIZE);
|
||||||
if (inLen == 0) {
|
if (inLen == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int processed = inLen;
|
// If ct is a direct bytebuffer, send it directly to the intrinsic
|
||||||
byte[] in = new byte[Math.min(MAX_LEN, inLen)];
|
if (ct.isDirect()) {
|
||||||
while (processed > MAX_LEN ) {
|
int processed = inLen;
|
||||||
src.get(in, 0, MAX_LEN);
|
processBlocksDirect(ct, inLen);
|
||||||
update(in, 0 , MAX_LEN);
|
return processed;
|
||||||
processed -= MAX_LEN;
|
} 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;
|
return inLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
void doLastBlock(ByteBuffer src, int inLen) {
|
int doFinal(ByteBuffer src, int inLen) {
|
||||||
int processed = update(src, inLen);
|
int processed = 0;
|
||||||
|
|
||||||
|
if (inLen >= AES_BLOCK_SIZE) {
|
||||||
|
processed = update(src, inLen);
|
||||||
|
}
|
||||||
|
|
||||||
if (inLen == processed) {
|
if (inLen == processed) {
|
||||||
return;
|
return processed;
|
||||||
}
|
}
|
||||||
byte[] block = new byte[AES_BLOCK_SIZE];
|
byte[] block = new byte[AES_BLOCK_SIZE];
|
||||||
src.get(block, 0, inLen - processed);
|
src.get(block, 0, inLen - processed);
|
||||||
update(block, 0, AES_BLOCK_SIZE);
|
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) {
|
if (inLen < 0) {
|
||||||
throw new RuntimeException("invalid input length: " + inLen);
|
throw new RuntimeException("invalid input length: " + inLen);
|
||||||
}
|
}
|
||||||
@ -263,7 +275,8 @@ final class GHASH {
|
|||||||
* throw exceptions or allocate arrays as it will breaking intrinsics
|
* throw exceptions or allocate arrays as it will breaking intrinsics
|
||||||
*/
|
*/
|
||||||
@IntrinsicCandidate
|
@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;
|
int offset = inOfs;
|
||||||
while (blocks > 0) {
|
while (blocks > 0) {
|
||||||
processBlock(data, offset, st, subH);
|
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[] digest() {
|
||||||
byte[] result = new byte[AES_BLOCK_SIZE];
|
byte[] result = new byte[AES_BLOCK_SIZE];
|
||||||
putLong(result, 0, state[0]);
|
asLongView.set(result, 0, state[0]);
|
||||||
putLong(result, 8, state[1]);
|
asLongView.set(result, 8, state[1]);
|
||||||
reset();
|
// Reset state
|
||||||
|
state[0] = 0;
|
||||||
|
state[1] = 0;
|
||||||
return result;
|
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" +
|
"|CFB8|CFB16|CFB24|CFB32|CFB40|CFB48|CFB56|CFB64" +
|
||||||
"|OFB8|OFB16|OFB24|OFB32|OFB40|OFB48|OFB56|OFB64";
|
"|OFB8|OFB16|OFB24|OFB32|OFB40|OFB48|OFB56|OFB64";
|
||||||
final String BLOCK_MODES128 = BLOCK_MODES +
|
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";
|
"|OFB72|OFB80|OFB88|OFB96|OFB104|OFB112|OFB120|OFB128";
|
||||||
final String BLOCK_PADS = "NOPADDING|PKCS5PADDING|ISO10126PADDING";
|
final String BLOCK_PADS = "NOPADDING|PKCS5PADDING|ISO10126PADDING";
|
||||||
|
|
||||||
@ -214,9 +214,6 @@ public final class SunJCE extends Provider {
|
|||||||
psA("Cipher", "AES_128/CFB/NoPadding",
|
psA("Cipher", "AES_128/CFB/NoPadding",
|
||||||
"com.sun.crypto.provider.AESCipher$AES128_CFB_NoPadding",
|
"com.sun.crypto.provider.AESCipher$AES128_CFB_NoPadding",
|
||||||
attrs);
|
attrs);
|
||||||
psA("Cipher", "AES_128/GCM/NoPadding",
|
|
||||||
"com.sun.crypto.provider.AESCipher$AES128_GCM_NoPadding",
|
|
||||||
attrs);
|
|
||||||
psA("Cipher", "AES_128/KW/NoPadding",
|
psA("Cipher", "AES_128/KW/NoPadding",
|
||||||
"com.sun.crypto.provider.KeyWrapCipher$AES128_KW_NoPadding",
|
"com.sun.crypto.provider.KeyWrapCipher$AES128_KW_NoPadding",
|
||||||
attrs);
|
attrs);
|
||||||
@ -239,9 +236,6 @@ public final class SunJCE extends Provider {
|
|||||||
psA("Cipher", "AES_192/CFB/NoPadding",
|
psA("Cipher", "AES_192/CFB/NoPadding",
|
||||||
"com.sun.crypto.provider.AESCipher$AES192_CFB_NoPadding",
|
"com.sun.crypto.provider.AESCipher$AES192_CFB_NoPadding",
|
||||||
attrs);
|
attrs);
|
||||||
psA("Cipher", "AES_192/GCM/NoPadding",
|
|
||||||
"com.sun.crypto.provider.AESCipher$AES192_GCM_NoPadding",
|
|
||||||
attrs);
|
|
||||||
psA("Cipher", "AES_192/KW/NoPadding",
|
psA("Cipher", "AES_192/KW/NoPadding",
|
||||||
"com.sun.crypto.provider.KeyWrapCipher$AES192_KW_NoPadding",
|
"com.sun.crypto.provider.KeyWrapCipher$AES192_KW_NoPadding",
|
||||||
attrs);
|
attrs);
|
||||||
@ -264,9 +258,6 @@ public final class SunJCE extends Provider {
|
|||||||
psA("Cipher", "AES_256/CFB/NoPadding",
|
psA("Cipher", "AES_256/CFB/NoPadding",
|
||||||
"com.sun.crypto.provider.AESCipher$AES256_CFB_NoPadding",
|
"com.sun.crypto.provider.AESCipher$AES256_CFB_NoPadding",
|
||||||
attrs);
|
attrs);
|
||||||
psA("Cipher", "AES_256/GCM/NoPadding",
|
|
||||||
"com.sun.crypto.provider.AESCipher$AES256_GCM_NoPadding",
|
|
||||||
attrs);
|
|
||||||
psA("Cipher", "AES_256/KW/NoPadding",
|
psA("Cipher", "AES_256/KW/NoPadding",
|
||||||
"com.sun.crypto.provider.KeyWrapCipher$AES256_KW_NoPadding",
|
"com.sun.crypto.provider.KeyWrapCipher$AES256_KW_NoPadding",
|
||||||
attrs);
|
attrs);
|
||||||
@ -277,6 +268,23 @@ public final class SunJCE extends Provider {
|
|||||||
"com.sun.crypto.provider.KeyWrapCipher$AES256_KWP_NoPadding",
|
"com.sun.crypto.provider.KeyWrapCipher$AES256_KWP_NoPadding",
|
||||||
attrs);
|
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.clear();
|
||||||
attrs.put("SupportedModes", "CBC");
|
attrs.put("SupportedModes", "CBC");
|
||||||
attrs.put("SupportedPaddings", "NOPADDING");
|
attrs.put("SupportedPaddings", "NOPADDING");
|
||||||
|
@ -217,8 +217,6 @@ public final class SecurityProviderConstants {
|
|||||||
|
|
||||||
store("DiffieHellman", KnownOIDs.DiffieHellman);
|
store("DiffieHellman", KnownOIDs.DiffieHellman);
|
||||||
|
|
||||||
store("AES", KnownOIDs.AES, "Rijndael");
|
|
||||||
|
|
||||||
store("EC", KnownOIDs.EC, "EllipticCurve");
|
store("EC", KnownOIDs.EC, "EllipticCurve");
|
||||||
|
|
||||||
store("X.509", null, "X509");
|
store("X.509", null, "X509");
|
||||||
|
@ -27,6 +27,7 @@ import java.security.Provider;
|
|||||||
import java.security.Security;
|
import java.security.Security;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HexFormat;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
@ -84,7 +85,7 @@ import javax.crypto.KeyGenerator;
|
|||||||
*/
|
*/
|
||||||
public class Encrypt {
|
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 KEY_STRENGTHS[] = { 128, 192, 256 };
|
||||||
private static final int TEXT_LENGTHS[] = { 0, 256, 1024 };
|
private static final int TEXT_LENGTHS[] = { 0, 256, 1024 };
|
||||||
private static final int AAD_LENGTHS[] = { 0, 8, 128, 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);
|
combination_16(outputTexts, mode, AAD, inputText, params);
|
||||||
|
|
||||||
for (int k = 0; k < outputTexts.size(); k++) {
|
for (int k = 0; k < outputTexts.size(); k++) {
|
||||||
|
HexFormat hex = HexFormat.of().withUpperCase();
|
||||||
if (!Arrays.equals(output, outputTexts.get(k))) {
|
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;
|
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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -36,11 +36,11 @@ import javax.crypto.SecretKey;
|
|||||||
import javax.crypto.spec.GCMParameterSpec;
|
import javax.crypto.spec.GCMParameterSpec;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.math.BigInteger;
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HexFormat;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class GCMBufferTest implements Cloneable {
|
public class GCMBufferTest implements Cloneable {
|
||||||
@ -65,6 +65,7 @@ public class GCMBufferTest implements Cloneable {
|
|||||||
boolean theoreticalCheck;
|
boolean theoreticalCheck;
|
||||||
List<Data> dataSet;
|
List<Data> dataSet;
|
||||||
int inOfs = 0, outOfs = 0;
|
int inOfs = 0, outOfs = 0;
|
||||||
|
static HexFormat hex = HexFormat.of();
|
||||||
|
|
||||||
static class Data {
|
static class Data {
|
||||||
int id;
|
int id;
|
||||||
@ -108,14 +109,21 @@ public class GCMBufferTest implements Cloneable {
|
|||||||
new GCMParameterSpec(tag.length * 8, this.iv));
|
new GCMParameterSpec(tag.length * 8, this.iv));
|
||||||
tct = c.doFinal(pt);
|
tct = c.doFinal(pt);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.out.println("Error in generating data for length " +
|
throw new RuntimeException("Error in generating data for length " +
|
||||||
ptlen);
|
ptlen, e);
|
||||||
}
|
}
|
||||||
ct = new byte[ptlen];
|
ct = new byte[ptlen];
|
||||||
System.arraycopy(tct, 0, ct, 0, ct.length);
|
System.arraycopy(tct, 0, ct, 0, ct.length);
|
||||||
System.arraycopy(tct, ct.length, tag, 0, tag.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;
|
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 {
|
void test() throws Exception {
|
||||||
int i = 1;
|
int i = 1;
|
||||||
System.out.println("Algo: " + algo + " \tOps: " + ops.toString());
|
System.err.println("Algo: " + algo + " \tOps: " + ops.toString());
|
||||||
for (Data data : dataSet) {
|
for (Data data : dataSet) {
|
||||||
|
|
||||||
// If incrementalSegments is enabled, run through that test only
|
// If incrementalSegments is enabled, run through that test only
|
||||||
@ -256,31 +264,31 @@ public class GCMBufferTest implements Cloneable {
|
|||||||
sizes = new int[ops.size()];
|
sizes = new int[ops.size()];
|
||||||
|
|
||||||
while (incrementSizes(data.pt.length)) {
|
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) {
|
for (int v : sizes) {
|
||||||
System.out.print(v + " ");
|
System.err.print(v + " ");
|
||||||
}
|
}
|
||||||
System.out.println("]");
|
System.err.println("]");
|
||||||
encrypt(data);
|
encrypt(data);
|
||||||
}
|
}
|
||||||
Arrays.fill(sizes, 0);
|
Arrays.fill(sizes, 0);
|
||||||
|
|
||||||
while (incrementSizes(data.ct.length + data.tag.length)) {
|
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) {
|
for (int v : sizes) {
|
||||||
System.out.print(v + " ");
|
System.err.print(v + " ");
|
||||||
}
|
}
|
||||||
System.out.println("]");
|
System.err.println("]");
|
||||||
decrypt(data);
|
decrypt(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Default test of 0 and 2 offset doing in place and different
|
// Default test of 0 and 2 offset doing in place and different
|
||||||
// i/o buffers
|
// i/o buffers
|
||||||
System.out.println("Encrypt: Data Index: " + i);
|
System.err.println("Encrypt: Data Index: " + i);
|
||||||
encrypt(data);
|
encrypt(data);
|
||||||
|
|
||||||
System.out.println("Decrypt: Data Index: " + i);
|
System.err.println("Decrypt: Data Index: " + i);
|
||||||
decrypt(data);
|
decrypt(data);
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
@ -298,13 +306,13 @@ public class GCMBufferTest implements Cloneable {
|
|||||||
data.tag.length);
|
data.tag.length);
|
||||||
|
|
||||||
// Test different input/output buffers
|
// 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");
|
inOfs + " outOfs " + outOfs + " in/out buffer: different");
|
||||||
crypto(true, data, input, output);
|
crypto(true, data, input, output);
|
||||||
|
|
||||||
// Test with in-place buffers
|
// Test with in-place buffers
|
||||||
if (same) {
|
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");
|
inOfs + " outOfs " + outOfs + " in/out buffer: in-place");
|
||||||
cryptoSameBuffer(true, data, input, output);
|
cryptoSameBuffer(true, data, input, output);
|
||||||
}
|
}
|
||||||
@ -320,13 +328,13 @@ public class GCMBufferTest implements Cloneable {
|
|||||||
output = data.pt;
|
output = data.pt;
|
||||||
|
|
||||||
// Test different input/output buffers
|
// 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-place: different");
|
inOfs + " outOfs " + outOfs + " in/out buffer: different");
|
||||||
crypto(false, data, input, output);
|
crypto(false, data, input, output);
|
||||||
|
|
||||||
// Test with in-place buffers
|
// Test with in-place buffers
|
||||||
if (same) {
|
if (same) {
|
||||||
System.out.println("\tinput len: " + input.length + " inOfs " +
|
System.err.println("\tinput len: " + input.length + " inOfs " +
|
||||||
inOfs + " outOfs " + outOfs + " in-place: same");
|
inOfs + " outOfs " + outOfs + " in-place: same");
|
||||||
cryptoSameBuffer(false, data, input, output);
|
cryptoSameBuffer(false, data, input, output);
|
||||||
}
|
}
|
||||||
@ -484,12 +492,10 @@ public class GCMBufferTest implements Cloneable {
|
|||||||
if (ctresult.length != expectedOut.length ||
|
if (ctresult.length != expectedOut.length ||
|
||||||
Arrays.compare(ctresult, expectedOut) != 0) {
|
Arrays.compare(ctresult, expectedOut) != 0) {
|
||||||
String s = "Ciphertext mismatch (" + v.name() +
|
String s = "Ciphertext mismatch (" + v.name() +
|
||||||
"):\nresult (len=" + ctresult.length + "):" +
|
"):\nresult (len=" + ctresult.length + "): " +
|
||||||
String.format("%0" + (ctresult.length << 1) + "x",
|
hex.formatHex(ctresult) +
|
||||||
new BigInteger(1, ctresult)) +
|
"\nexpected (len=" + output.length + "): " +
|
||||||
"\nexpected (len=" + output.length + "):" +
|
hex.formatHex(output);
|
||||||
String.format("%0" + (output.length << 1) + "x",
|
|
||||||
new BigInteger(1, output));
|
|
||||||
System.err.println(s);
|
System.err.println(s);
|
||||||
throw new Exception(s);
|
throw new Exception(s);
|
||||||
|
|
||||||
@ -605,10 +611,9 @@ public class GCMBufferTest implements Cloneable {
|
|||||||
output.length) != 0) {
|
output.length) != 0) {
|
||||||
String s = "Ciphertext mismatch (" + v.name() +
|
String s = "Ciphertext mismatch (" + v.name() +
|
||||||
"):\nresult (len=" + len + "):\n" +
|
"):\nresult (len=" + len + "):\n" +
|
||||||
byteToHex(out) +
|
hex.formatHex(out) +
|
||||||
"\nexpected (len=" + output.length + "):\n" +
|
"\nexpected (len=" + output.length + "):\n" +
|
||||||
String.format("%0" + (output.length << 1) + "x",
|
hex.formatHex(output);
|
||||||
new BigInteger(1, output));
|
|
||||||
System.err.println(s);
|
System.err.println(s);
|
||||||
throw new Exception(s);
|
throw new Exception(s);
|
||||||
}
|
}
|
||||||
@ -623,7 +628,10 @@ public class GCMBufferTest implements Cloneable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String args[]) throws Exception {
|
public static void main(String args[]) throws Exception {
|
||||||
|
GCMBufferTest t;
|
||||||
|
|
||||||
initTest();
|
initTest();
|
||||||
|
|
||||||
// Test single byte array
|
// Test single byte array
|
||||||
new GCMBufferTest("AES/GCM/NoPadding", List.of(dtype.BYTE)).test();
|
new GCMBufferTest("AES/GCM/NoPadding", List.of(dtype.BYTE)).test();
|
||||||
offsetTests(new GCMBufferTest("AES/GCM/NoPadding", List.of(dtype.BYTE)));
|
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)));
|
List.of(dtype.DIRECT, dtype.DIRECT, dtype.DIRECT)));
|
||||||
|
|
||||||
// Test update-update-doFinal with byte arrays and preset data sizes
|
// 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(
|
List.of(dtype.BYTE, dtype.BYTE, dtype.BYTE)).dataSegments(
|
||||||
new int[] { 1, 1, GCMBufferTest.REMAINDER});
|
new int[] { 1, 1, GCMBufferTest.REMAINDER});
|
||||||
t.clone().test();
|
t.clone().test();
|
||||||
@ -678,6 +686,7 @@ public class GCMBufferTest implements Cloneable {
|
|||||||
List.of(dtype.BYTE, dtype.HEAP, dtype.DIRECT)).differentBufferOnly();
|
List.of(dtype.BYTE, dtype.HEAP, dtype.DIRECT)).differentBufferOnly();
|
||||||
t.clone().test();
|
t.clone().test();
|
||||||
offsetTests(t.clone());
|
offsetTests(t.clone());
|
||||||
|
|
||||||
// Test update-doFinal with a direct bytebuffer and a byte array.
|
// Test update-doFinal with a direct bytebuffer and a byte array.
|
||||||
t = new GCMBufferTest("AES/GCM/NoPadding",
|
t = new GCMBufferTest("AES/GCM/NoPadding",
|
||||||
List.of(dtype.DIRECT, dtype.BYTE)).differentBufferOnly();
|
List.of(dtype.DIRECT, dtype.BYTE)).differentBufferOnly();
|
||||||
@ -710,26 +719,10 @@ public class GCMBufferTest implements Cloneable {
|
|||||||
new GCMBufferTest("AES/GCM/NoPadding",
|
new GCMBufferTest("AES/GCM/NoPadding",
|
||||||
List.of(dtype.DIRECT, dtype.DIRECT, dtype.DIRECT)).
|
List.of(dtype.DIRECT, dtype.DIRECT, dtype.DIRECT)).
|
||||||
incrementalSegments().dataSet(0).test();
|
incrementalSegments().dataSet(0).test();
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] HexToBytes(String hexVal) {
|
new GCMBufferTest("AES/GCM/NoPadding",
|
||||||
if (hexVal == null) {
|
List.of(dtype.DIRECT, dtype.DIRECT, dtype.DIRECT)).
|
||||||
return new byte[0];
|
dataSegments(new int[] { 49, 0, 2 }).dataSet(0).test();
|
||||||
}
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test data
|
// Test data
|
||||||
@ -762,8 +755,7 @@ public class GCMBufferTest implements Cloneable {
|
|||||||
"b6e6f197168f5049aeda32dafbdaeb"),
|
"b6e6f197168f5049aeda32dafbdaeb"),
|
||||||
// zero'd test data
|
// zero'd test data
|
||||||
new Data("AES", 3, "272f16edb81a7abbea887357a58c1917",
|
new Data("AES", 3, "272f16edb81a7abbea887357a58c1917",
|
||||||
"794ec588176c703d3d2a7a07",
|
"794ec588176c703d3d2a7a07", new byte[256], null,
|
||||||
new byte[256], null,
|
|
||||||
"15b461672153270e8ba1e6789f7641c5411f3e642abda731b6086f535c216457" +
|
"15b461672153270e8ba1e6789f7641c5411f3e642abda731b6086f535c216457" +
|
||||||
"e87305bc59a1ff1f7e1e0bbdf302b75549b136606c67d7e5f71277aeca4bc670" +
|
"e87305bc59a1ff1f7e1e0bbdf302b75549b136606c67d7e5f71277aeca4bc670" +
|
||||||
"07a98f78e0cfa002ed183e62f07893ad31fe67aad1bb37e15b957a14d145f14f" +
|
"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
|
// Copy data into shared buffer
|
||||||
input.put(baseBuf);
|
input.put(baseBuf);
|
||||||
input.flip();
|
input.flip();
|
||||||
@ -132,8 +135,6 @@ public class OverlapByteBuffer {
|
|||||||
cipher.doFinal(in, output);
|
cipher.doFinal(in, output);
|
||||||
|
|
||||||
output.flip();
|
output.flip();
|
||||||
System.out.println("inOfsInBuf = " + inOfsInBuf);
|
|
||||||
System.out.println("outOfsInBuf = " + outOfsInBuf);
|
|
||||||
ByteBuffer b = ByteBuffer.wrap(baseBuf);
|
ByteBuffer b = ByteBuffer.wrap(baseBuf);
|
||||||
if (b.compareTo(output) != 0) {
|
if (b.compareTo(output) != 0) {
|
||||||
System.err.println(
|
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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -26,15 +26,16 @@ import java.security.InvalidKeyException;
|
|||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.NoSuchProviderException;
|
import java.security.NoSuchProviderException;
|
||||||
import java.security.spec.AlgorithmParameterSpec;
|
import java.security.spec.AlgorithmParameterSpec;
|
||||||
import java.util.Random;
|
import java.util.HexFormat;
|
||||||
import javax.crypto.BadPaddingException;
|
import javax.crypto.BadPaddingException;
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.IllegalBlockSizeException;
|
import javax.crypto.IllegalBlockSizeException;
|
||||||
import javax.crypto.KeyGenerator;
|
|
||||||
import javax.crypto.NoSuchPaddingException;
|
import javax.crypto.NoSuchPaddingException;
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import javax.crypto.ShortBufferException;
|
import javax.crypto.ShortBufferException;
|
||||||
|
import javax.crypto.spec.GCMParameterSpec;
|
||||||
import javax.crypto.spec.IvParameterSpec;
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
@ -43,7 +44,6 @@ import javax.crypto.spec.IvParameterSpec;
|
|||||||
* doesn't use IV).
|
* doesn't use IV).
|
||||||
* @author Liwen Wang
|
* @author Liwen Wang
|
||||||
* @author Parag Salvi
|
* @author Parag Salvi
|
||||||
* @key randomness
|
|
||||||
*/
|
*/
|
||||||
public class TestAESCipher {
|
public class TestAESCipher {
|
||||||
|
|
||||||
@ -56,7 +56,9 @@ public class TestAESCipher {
|
|||||||
"OFB32", "OFB40", "OFB48", "OFB56", "OFB64", "OFB72", "OFB80",
|
"OFB32", "OFB40", "OFB48", "OFB56", "OFB64", "OFB72", "OFB80",
|
||||||
"OFB88", "OFB96", "OFB104", "OFB112", "OFB120", "GCM" };
|
"OFB88", "OFB96", "OFB104", "OFB112", "OFB120", "GCM" };
|
||||||
private static final String[] PADDING = { "NoPadding", "PKCS5Padding" };
|
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 {
|
public static void main(String argv[]) throws Exception {
|
||||||
TestAESCipher test = new TestAESCipher();
|
TestAESCipher test = new TestAESCipher();
|
||||||
@ -73,32 +75,31 @@ public class TestAESCipher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void runTest(String algo, String mo, String pad) throws Exception {
|
public void runTest(String algo, String mo, String pad) throws Exception {
|
||||||
Cipher ci = null;
|
Cipher ci;
|
||||||
byte[] iv = null;
|
System.out.println("Testing " + algo + "/" + mo + "/" + pad);
|
||||||
AlgorithmParameterSpec aps = null;
|
|
||||||
SecretKey key = null;
|
byte[] iv = new byte[16];
|
||||||
|
AlgorithmParameterSpec aps = new GCMParameterSpec(128, iv);
|
||||||
|
SecretKey key = new SecretKeySpec(this.key, 0, KEY_LENGTH,"AES");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Initialization
|
// Initialization
|
||||||
Random rdm = new Random();
|
|
||||||
byte[] plainText = new byte[128];
|
|
||||||
rdm.nextBytes(plainText);
|
|
||||||
|
|
||||||
ci = Cipher.getInstance(algo + "/" + mo + "/" + pad, PROVIDER);
|
ci = Cipher.getInstance(algo + "/" + mo + "/" + pad, PROVIDER);
|
||||||
KeyGenerator kg = KeyGenerator.getInstance(algo, PROVIDER);
|
|
||||||
kg.init(KEY_LENGTH);
|
|
||||||
key = kg.generateKey();
|
|
||||||
|
|
||||||
// encrypt
|
// encrypt
|
||||||
if (!mo.equalsIgnoreCase("GCM")) {
|
if (mo.equalsIgnoreCase("GCM")) {
|
||||||
ci.init(Cipher.ENCRYPT_MODE, key, aps);
|
ci.init(Cipher.ENCRYPT_MODE, key, aps);
|
||||||
|
} else if (mo.equalsIgnoreCase("ECB")) {
|
||||||
|
ci.init(Cipher.ENCRYPT_MODE, key, (AlgorithmParameterSpec)null);
|
||||||
} else {
|
} else {
|
||||||
ci.init(Cipher.ENCRYPT_MODE, key);
|
ci.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] cipherText = new byte[ci.getOutputSize(plainText.length)];
|
byte[] cipherText = new byte[ci.getOutputSize(plainText.length)];
|
||||||
int offset = ci.update(plainText, 0, plainText.length, cipherText,
|
int offset = ci.update(plainText, 0, plainText.length, cipherText,
|
||||||
0);
|
0);
|
||||||
ci.doFinal(cipherText, offset);
|
ci.doFinal(cipherText, offset);
|
||||||
|
|
||||||
if (!mo.equalsIgnoreCase("ECB")) {
|
if (!mo.equalsIgnoreCase("ECB")) {
|
||||||
@ -117,25 +118,24 @@ public class TestAESCipher {
|
|||||||
byte[] recoveredText = new byte[ci.getOutputSize(cipherText.length)];
|
byte[] recoveredText = new byte[ci.getOutputSize(cipherText.length)];
|
||||||
int len = ci.doFinal(cipherText, 0, cipherText.length,
|
int len = ci.doFinal(cipherText, 0, cipherText.length,
|
||||||
recoveredText);
|
recoveredText);
|
||||||
byte[] tmp = new byte[len];
|
|
||||||
System.arraycopy(recoveredText, 0, tmp, 0, len);
|
|
||||||
|
|
||||||
// Comparison
|
// Comparison
|
||||||
if (!java.util.Arrays.equals(plainText, tmp)) {
|
if (!java.util.Arrays.equals(plainText, 0 , plainText.length,
|
||||||
|
recoveredText, 0, len)) {
|
||||||
System.out.println("Original: ");
|
System.out.println("Original: ");
|
||||||
dumpBytes(plainText);
|
System.out.println(HexFormat.of().formatHex(plainText));
|
||||||
System.out.println("Recovered: ");
|
System.out.println("Recovered: ");
|
||||||
dumpBytes(tmp);
|
System.out.println(HexFormat.of().
|
||||||
throw new RuntimeException(
|
formatHex(recoveredText, 0, len));
|
||||||
"Original text is not equal with recovered text, with mode:"
|
throw new RuntimeException("Original text is not equal with " +
|
||||||
+ mo);
|
"recovered text, with mode:" + mo);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
//CFB7 and OFB150 are for negative testing
|
//CFB7 and OFB150 are for negative testing
|
||||||
if (!mo.equalsIgnoreCase("CFB7") && !mo.equalsIgnoreCase("OFB150")) {
|
if (!mo.equalsIgnoreCase("CFB7") && !mo.equalsIgnoreCase("OFB150")) {
|
||||||
System.out.println("Unexpected NoSuchAlgorithmException with mode: "
|
System.out.println("Unexpected NoSuchAlgorithmException with" +
|
||||||
+ mo);
|
" mode: " + mo);
|
||||||
throw new RuntimeException("Test failed!");
|
throw new RuntimeException("Test failed!");
|
||||||
}
|
}
|
||||||
} catch ( NoSuchProviderException | NoSuchPaddingException
|
} catch ( NoSuchProviderException | NoSuchPaddingException
|
||||||
@ -146,12 +146,4 @@ public class TestAESCipher {
|
|||||||
throw e;
|
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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -46,7 +46,7 @@ import javax.crypto.spec.IvParameterSpec;
|
|||||||
*/
|
*/
|
||||||
public class TestSameBuffer {
|
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 PROVIDER = "SunJCE";
|
||||||
private static final String[] MODES = { "ECb", "CbC", "OFB", "CFB150",
|
private static final String[] MODES = { "ECb", "CbC", "OFB", "CFB150",
|
||||||
"cFB", "CFB7", " cFB8", "cFB16", "cFB24", "cFB32", "Cfb40",
|
"cFB", "CFB7", " cFB8", "cFB16", "cFB24", "cFB32", "Cfb40",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user