OpenSSLCipher: refactor in preparation for AEAD
BoringSSL uses a different interface for AEAD that is much simplier
called EVP_AEAD. Separate out the EVP_CIPHER usage so that we can have
another subclass with the EVP_AEAD usage.
Bug: 20636336
Change-Id: I661d92bd449f2fcc3c4a6e511155490917ecef0c
diff --git a/src/main/java/org/conscrypt/OpenSSLCipher.java b/src/main/java/org/conscrypt/OpenSSLCipher.java
index 2d7ae21..a1d7faf 100644
--- a/src/main/java/org/conscrypt/OpenSSLCipher.java
+++ b/src/main/java/org/conscrypt/OpenSSLCipher.java
@@ -42,6 +42,8 @@
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.conscrypt.util.EmptyArray;
+import org.conscrypt.NativeConstants;
+import org.conscrypt.NativeRef.EVP_CIPHER_CTX;
public abstract class OpenSSLCipher extends CipherSpi {
@@ -64,15 +66,9 @@
}
/**
- * Native pointer for the OpenSSL EVP_CIPHER context.
- */
- private NativeRef.EVP_CIPHER_CTX cipherCtx = new NativeRef.EVP_CIPHER_CTX(
- NativeCrypto.EVP_CIPHER_CTX_new());
-
- /**
* The current cipher mode.
*/
- private Mode mode = Mode.ECB;
+ protected Mode mode = Mode.ECB;
/**
* The current cipher padding.
@@ -83,12 +79,12 @@
* May be used when reseting the cipher instance after calling
* {@code doFinal}.
*/
- private byte[] encodedKey;
+ protected byte[] encodedKey;
/**
* The Initial Vector (IV) used for the current cipher.
*/
- private byte[] iv;
+ protected byte[] iv;
/**
* Current cipher mode: encrypting or decrypting.
@@ -100,17 +96,6 @@
*/
private int blockSize;
- /**
- * The block size of the current mode.
- */
- private int modeBlockSize;
-
- /**
- * Whether the cipher has processed any data yet. OpenSSL doesn't like
- * calling "doFinal()" in decryption mode without processing any updates.
- */
- private boolean calledUpdate;
-
protected OpenSSLCipher() {
}
@@ -121,17 +106,40 @@
}
/**
+ * API-specific implementation of initializing the cipher. The
+ * {@link #isEncrypting()} function will tell whether it should be
+ * initialized for encryption or decryption. The {@code encodedKey} will be
+ * the bytes of a supported key size.
+ */
+ protected abstract void engineInitInternal(byte[] encodedKey, AlgorithmParameterSpec params,
+ SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException;
+
+ /**
+ * API-specific implementation of updating the cipher. The
+ * {@code maximumLen} will be the maximum length of the output as returned
+ * by {@link #getOutputSizeForUpdate(int)}. The return value must be the
+ * number of bytes processed and placed into {@code output}. On error, an
+ * exception must be thrown.
+ */
+ protected abstract int updateInternal(byte[] input, int inputOffset, int inputLen,
+ byte[] output, int outputOffset, int maximumLen) throws ShortBufferException;
+
+ /**
+ * API-specific implementation of the final block. The {@code maximumLen}
+ * will be the maximum length of the possible output as returned by
+ * {@link #getOutputSizeForFinal(int)}. The return value must be the number
+ * of bytes processed and placed into {@code output}. On error, an exception
+ * must be thrown.
+ */
+ protected abstract int doFinalInternal(byte[] output, int outputOffset, int maximumLen)
+ throws IllegalBlockSizeException, BadPaddingException, ShortBufferException;
+
+ /**
* Returns the standard name for the particular algorithm.
*/
protected abstract String getBaseCipherName();
/**
- * Returns the OpenSSL cipher name for the particular {@code keySize} and
- * cipher {@code mode}.
- */
- protected abstract String getCipherName(int keySize, Mode mode);
-
- /**
* Checks whether the cipher supports this particular {@code keySize} (in
* bytes) and throws {@code InvalidKeyException} if it doesn't.
*/
@@ -155,14 +163,17 @@
return false;
}
+ protected boolean supportsVariableSizeIv() {
+ return false;
+ }
+
@Override
protected void engineSetMode(String modeStr) throws NoSuchAlgorithmException {
final Mode mode;
try {
mode = Mode.valueOf(modeStr.toUpperCase(Locale.US));
} catch (IllegalArgumentException e) {
- NoSuchAlgorithmException newE = new NoSuchAlgorithmException("No such mode: "
- + modeStr);
+ NoSuchAlgorithmException newE = new NoSuchAlgorithmException("No such mode: " + modeStr);
newE.initCause(e);
throw newE;
}
@@ -186,6 +197,13 @@
this.padding = padding;
}
+ /**
+ * Returns the padding type for which this cipher is initialized.
+ */
+ protected Padding getPadding() {
+ return padding;
+ }
+
@Override
protected int engineGetBlockSize() {
return blockSize;
@@ -196,23 +214,18 @@
* {@code inputLen}. If padding is enabled and the size of the input puts it
* right at the block size, it will add another block for the padding.
*/
- private int getOutputSize(int inputLen) {
- if (modeBlockSize == 1) {
- return inputLen;
- } else {
- final int buffered = NativeCrypto.get_EVP_CIPHER_CTX_buf_len(cipherCtx);
- if (padding == Padding.NOPADDING) {
- return buffered + inputLen;
- } else {
- final int totalLen = inputLen + buffered + modeBlockSize;
- return totalLen - (totalLen % modeBlockSize);
- }
- }
- }
+ protected abstract int getOutputSizeForFinal(int inputLen);
+
+ /**
+ * The size of output if {@code update()} is called with this
+ * {@code inputLen}. If padding is enabled and the size of the input puts it
+ * right at the block size, it will add another block for the padding.
+ */
+ protected abstract int getOutputSizeForUpdate(int inputLen);
@Override
protected int engineGetOutputSize(int inputLen) {
- return getOutputSize(inputLen);
+ return getOutputSizeForFinal(inputLen);
}
@Override
@@ -236,74 +249,13 @@
return null;
}
- private void engineInitInternal(int opmode, Key key, byte[] iv, SecureRandom random)
- throws InvalidKeyException, InvalidAlgorithmParameterException {
- if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE) {
- encrypting = true;
- } else if (opmode == Cipher.DECRYPT_MODE || opmode == Cipher.UNWRAP_MODE) {
- encrypting = false;
- } else {
- throw new InvalidParameterException("Unsupported opmode " + opmode);
- }
-
- if (!(key instanceof SecretKey)) {
- throw new InvalidKeyException("Only SecretKey is supported");
- }
-
- final byte[] encodedKey = key.getEncoded();
- if (encodedKey == null) {
- throw new InvalidKeyException("key.getEncoded() == null");
- }
- checkSupportedKeySize(encodedKey.length);
- this.encodedKey = encodedKey;
-
- final long cipherType = NativeCrypto.EVP_get_cipherbyname(getCipherName(encodedKey.length,
- mode));
- if (cipherType == 0) {
- throw new InvalidAlgorithmParameterException("Cannot find name for key length = "
- + (encodedKey.length * 8) + " and mode = " + mode);
- }
-
- final int expectedIvLength = NativeCrypto.EVP_CIPHER_iv_length(cipherType);
- if (iv == null && expectedIvLength != 0) {
- if (!encrypting) {
- throw new InvalidAlgorithmParameterException("IV must be specified in " + mode
- + " mode");
- }
-
- iv = new byte[expectedIvLength];
- if (random == null) {
- random = new SecureRandom();
- }
- random.nextBytes(iv);
- } else if (expectedIvLength == 0 && iv != null) {
- throw new InvalidAlgorithmParameterException("IV not used in " + mode + " mode");
- } else if (iv != null && iv.length != expectedIvLength) {
- throw new InvalidAlgorithmParameterException("expected IV length of "
- + expectedIvLength + " but was " + iv.length);
- }
-
- this.iv = iv;
-
- if (supportsVariableSizeKey()) {
- NativeCrypto.EVP_CipherInit_ex(cipherCtx, cipherType, null, null, encrypting);
- NativeCrypto.EVP_CIPHER_CTX_set_key_length(cipherCtx, encodedKey.length);
- NativeCrypto.EVP_CipherInit_ex(cipherCtx, 0, encodedKey, iv, encrypting);
- } else {
- NativeCrypto.EVP_CipherInit_ex(cipherCtx, cipherType, encodedKey, iv, encrypting);
- }
-
- // OpenSSL only supports PKCS5 Padding.
- NativeCrypto.EVP_CIPHER_CTX_set_padding(cipherCtx, padding == Padding.PKCS5PADDING);
- modeBlockSize = NativeCrypto.EVP_CIPHER_CTX_block_size(cipherCtx);
- calledUpdate = false;
- }
-
@Override
protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
+ checkAndSetEncodedKey(opmode, key);
try {
- engineInitInternal(opmode, key, null, random);
+ engineInitInternal(this.encodedKey, null, random);
} catch (InvalidAlgorithmParameterException e) {
+ // This can't actually happen since we pass in null.
throw new RuntimeException(e);
}
}
@@ -311,26 +263,20 @@
@Override
protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params,
SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
- final byte[] iv;
- if (params instanceof IvParameterSpec) {
- IvParameterSpec ivParams = (IvParameterSpec) params;
- iv = ivParams.getIV();
- } else {
- iv = null;
- }
-
- engineInitInternal(opmode, key, iv, random);
+ checkAndSetEncodedKey(opmode, key);
+ engineInitInternal(this.encodedKey, params, random);
}
@Override
protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
- final AlgorithmParameterSpec spec;
+ AlgorithmParameterSpec spec;
if (params != null) {
try {
spec = params.getParameterSpec(IvParameterSpec.class);
} catch (InvalidParameterSpecException e) {
- throw new InvalidAlgorithmParameterException(e);
+ throw new InvalidAlgorithmParameterException(
+ "Params must be convertible to IvParameterSpec", e);
}
} else {
spec = null;
@@ -339,27 +285,9 @@
engineInit(opmode, key, spec, random);
}
- private final int updateInternal(byte[] input, int inputOffset, int inputLen, byte[] output,
- int outputOffset, int maximumLen) throws ShortBufferException {
- final int intialOutputOffset = outputOffset;
-
- final int bytesLeft = output.length - outputOffset;
- if (bytesLeft < maximumLen) {
- throw new ShortBufferException("output buffer too small during update: " + bytesLeft
- + " < " + maximumLen);
- }
-
- outputOffset += NativeCrypto.EVP_CipherUpdate(cipherCtx, output, outputOffset, input,
- inputOffset, inputLen);
-
- calledUpdate = true;
-
- return outputOffset - intialOutputOffset;
- }
-
@Override
protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
- final int maximumLen = getOutputSize(inputLen);
+ final int maximumLen = getOutputSizeForUpdate(inputLen);
/* See how large our output buffer would need to be. */
final byte[] output;
@@ -389,79 +317,31 @@
@Override
protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output,
int outputOffset) throws ShortBufferException {
- final int maximumLen = getOutputSize(inputLen);
+ final int maximumLen = getOutputSizeForUpdate(inputLen);
return updateInternal(input, inputOffset, inputLen, output, outputOffset, maximumLen);
}
- /**
- * Reset this Cipher instance state to process a new chunk of data.
- */
- private void reset() {
- NativeCrypto.EVP_CipherInit_ex(cipherCtx, 0, encodedKey, iv, encrypting);
- calledUpdate = false;
- }
-
- private int doFinalInternal(byte[] input, int inputOffset, int inputLen, byte[] output,
- int outputOffset, int maximumLen) throws IllegalBlockSizeException,
- BadPaddingException, ShortBufferException {
- /* Remember this so we can tell how many characters were written. */
- final int initialOutputOffset = outputOffset;
-
- if (inputLen > 0) {
- final int updateBytesWritten = updateInternal(input, inputOffset, inputLen, output,
- outputOffset, maximumLen);
- outputOffset += updateBytesWritten;
- maximumLen -= updateBytesWritten;
- }
-
- /*
- * If we're decrypting and haven't had any input, we should return null.
- * Otherwise OpenSSL will complain if we call final.
- */
- if (!encrypting && !calledUpdate) {
- return 0;
- }
-
- /* Allow OpenSSL to pad if necessary and clean up state. */
- final int bytesLeft = output.length - outputOffset;
- final int writtenBytes;
- if (bytesLeft >= maximumLen) {
- writtenBytes = NativeCrypto.EVP_CipherFinal_ex(cipherCtx, output, outputOffset);
- } else {
- final byte[] lastBlock = new byte[maximumLen];
- writtenBytes = NativeCrypto.EVP_CipherFinal_ex(cipherCtx, lastBlock, 0);
- if (writtenBytes > bytesLeft) {
- throw new ShortBufferException("buffer is too short: " + writtenBytes + " > "
- + bytesLeft);
- } else if (writtenBytes > 0) {
- System.arraycopy(lastBlock, 0, output, outputOffset, writtenBytes);
- }
- }
- outputOffset += writtenBytes;
-
- reset();
-
- return outputOffset - initialOutputOffset;
- }
-
@Override
protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
throws IllegalBlockSizeException, BadPaddingException {
- /*
- * Other implementations return null if we've never called update()
- * while decrypting.
- */
- if (!encrypting && !calledUpdate && inputLen == 0) {
- reset();
- return null;
- }
-
- final int maximumLen = getOutputSize(inputLen);
+ final int maximumLen = getOutputSizeForFinal(inputLen);
/* Assume that we'll output exactly on a byte boundary. */
final byte[] output = new byte[maximumLen];
- final int bytesWritten;
+
+ int bytesWritten;
+ if (inputLen > 0) {
+ try {
+ bytesWritten = updateInternal(input, inputOffset, inputLen, output, 0, maximumLen);
+ } catch (ShortBufferException e) {
+ /* This should not happen since we sized our own buffer. */
+ throw new RuntimeException("our calculated buffer was too small", e);
+ }
+ } else {
+ bytesWritten = 0;
+ }
+
try {
- bytesWritten = doFinalInternal(input, inputOffset, inputLen, output, 0, maximumLen);
+ bytesWritten += doFinalInternal(output, bytesWritten, maximumLen - bytesWritten);
} catch (ShortBufferException e) {
/* This should not happen since we sized our own buffer. */
throw new RuntimeException("our calculated buffer was too small", e);
@@ -484,8 +364,19 @@
throw new NullPointerException("output == null");
}
- final int maximumLen = getOutputSize(inputLen);
- return doFinalInternal(input, inputOffset, inputLen, output, outputOffset, maximumLen);
+ int maximumLen = getOutputSizeForFinal(inputLen);
+
+ final int bytesWritten;
+ if (inputLen > 0) {
+ bytesWritten = updateInternal(input, inputOffset, inputLen, output, outputOffset,
+ maximumLen);
+ outputOffset += bytesWritten;
+ maximumLen -= bytesWritten;
+ } else {
+ bytesWritten = 0;
+ }
+
+ return bytesWritten + doFinalInternal(output, outputOffset, maximumLen);
}
@Override
@@ -525,218 +416,416 @@
}
}
- public static class AES extends OpenSSLCipher {
- private static final int AES_BLOCK_SIZE = 16;
-
- protected AES(Mode mode, Padding padding) {
- super(mode, padding);
+ private byte[] checkAndSetEncodedKey(int opmode, Key key) throws InvalidKeyException {
+ if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE) {
+ encrypting = true;
+ } else if (opmode == Cipher.DECRYPT_MODE || opmode == Cipher.UNWRAP_MODE) {
+ encrypting = false;
+ } else {
+ throw new InvalidParameterException("Unsupported opmode " + opmode);
}
- public static class CBC extends AES {
- public CBC(Padding padding) {
- super(Mode.CBC, padding);
- }
-
- public static class NoPadding extends CBC {
- public NoPadding() {
- super(Padding.NOPADDING);
- }
- }
-
- public static class PKCS5Padding extends CBC {
- public PKCS5Padding() {
- super(Padding.PKCS5PADDING);
- }
- }
+ if (!(key instanceof SecretKey)) {
+ throw new InvalidKeyException("Only SecretKey is supported");
}
- public static class CTR extends AES {
- public CTR() {
- super(Mode.CTR, Padding.NOPADDING);
- }
+ final byte[] encodedKey = key.getEncoded();
+ if (encodedKey == null) {
+ throw new InvalidKeyException("key.getEncoded() == null");
}
-
- public static class ECB extends AES {
- public ECB(Padding padding) {
- super(Mode.ECB, padding);
- }
-
- public static class NoPadding extends ECB {
- public NoPadding() {
- super(Padding.NOPADDING);
- }
- }
-
- public static class PKCS5Padding extends ECB {
- public PKCS5Padding() {
- super(Padding.PKCS5PADDING);
- }
- }
- }
-
- @Override
- protected void checkSupportedKeySize(int keyLength) throws InvalidKeyException {
- switch (keyLength) {
- case 16: // AES 128
- case 24: // AES 192
- case 32: // AES 256
- return;
- default:
- throw new InvalidKeyException("Unsupported key size: " + keyLength + " bytes");
- }
- }
-
- @Override
- protected void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException {
- switch (mode) {
- case CBC:
- case CTR:
- case ECB:
- return;
- default:
- throw new NoSuchAlgorithmException("Unsupported mode " + mode.toString());
- }
- }
-
- @Override
- protected void checkSupportedPadding(Padding padding) throws NoSuchPaddingException {
- switch (padding) {
- case NOPADDING:
- case PKCS5PADDING:
- return;
- default:
- throw new NoSuchPaddingException("Unsupported padding " + padding.toString());
- }
- }
-
- @Override
- protected String getBaseCipherName() {
- return "AES";
- }
-
- @Override
- protected String getCipherName(int keyLength, Mode mode) {
- return "aes-" + (keyLength * 8) + "-" + mode.toString().toLowerCase(Locale.US);
- }
-
- @Override
- protected int getCipherBlockSize() {
- return AES_BLOCK_SIZE;
- }
+ checkSupportedKeySize(encodedKey.length);
+ this.encodedKey = encodedKey;
+ return encodedKey;
}
- public static class DESEDE extends OpenSSLCipher {
- private static int DES_BLOCK_SIZE = 8;
+ protected boolean isEncrypting() {
+ return encrypting;
+ }
- public DESEDE(Mode mode, Padding padding) {
+ public static abstract class EVP_CIPHER extends OpenSSLCipher {
+ /**
+ * Native pointer for the OpenSSL EVP_CIPHER context.
+ */
+ private final EVP_CIPHER_CTX cipherCtx = new EVP_CIPHER_CTX(
+ NativeCrypto.EVP_CIPHER_CTX_new());
+
+ /**
+ * Whether the cipher has processed any data yet. EVP_CIPHER doesn't
+ * like calling "doFinal()" in decryption mode without processing any
+ * updates.
+ */
+ protected boolean calledUpdate;
+
+ /**
+ * The block size of the current mode.
+ */
+ private int modeBlockSize;
+
+ public EVP_CIPHER(Mode mode, Padding padding) {
super(mode, padding);
}
- public static class CBC extends DESEDE {
- public CBC(Padding padding) {
- super(Mode.CBC, padding);
- }
-
- public static class NoPadding extends CBC {
- public NoPadding() {
- super(Padding.NOPADDING);
- }
- }
-
- public static class PKCS5Padding extends CBC {
- public PKCS5Padding() {
- super(Padding.PKCS5PADDING);
- }
- }
- }
-
@Override
- protected String getBaseCipherName() {
- return "DESede";
- }
-
- @Override
- protected String getCipherName(int keySize, Mode mode) {
- final String baseCipherName;
- if (keySize == 16) {
- baseCipherName = "des-ede";
+ protected void engineInitInternal(byte[] encodedKey, AlgorithmParameterSpec params,
+ SecureRandom random) throws InvalidKeyException,
+ InvalidAlgorithmParameterException {
+ byte[] iv;
+ if (params instanceof IvParameterSpec) {
+ IvParameterSpec ivParams = (IvParameterSpec) params;
+ iv = ivParams.getIV();
} else {
- baseCipherName = "des-ede3";
+ iv = null;
}
- return baseCipherName + "-" + mode.toString().toLowerCase(Locale.US);
+ final long cipherType = NativeCrypto.EVP_get_cipherbyname(getCipherName(
+ encodedKey.length, mode));
+ if (cipherType == 0) {
+ throw new InvalidAlgorithmParameterException("Cannot find name for key length = "
+ + (encodedKey.length * 8) + " and mode = " + mode);
+ }
+
+ final boolean encrypting = isEncrypting();
+
+ final int expectedIvLength = NativeCrypto.EVP_CIPHER_iv_length(cipherType);
+ if (iv == null && expectedIvLength != 0) {
+ if (!encrypting) {
+ throw new InvalidAlgorithmParameterException("IV must be specified in " + mode
+ + " mode");
+ }
+
+ iv = new byte[expectedIvLength];
+ if (random == null) {
+ random = new SecureRandom();
+ }
+ random.nextBytes(iv);
+ } else if (expectedIvLength == 0 && iv != null) {
+ throw new InvalidAlgorithmParameterException("IV not used in " + mode + " mode");
+ } else if (iv != null && iv.length != expectedIvLength) {
+ throw new InvalidAlgorithmParameterException("expected IV length of "
+ + expectedIvLength + " but was " + iv.length);
+ }
+
+ this.iv = iv;
+
+ if (supportsVariableSizeKey()) {
+ NativeCrypto.EVP_CipherInit_ex(cipherCtx, cipherType, null, null, encrypting);
+ NativeCrypto.EVP_CIPHER_CTX_set_key_length(cipherCtx, encodedKey.length);
+ NativeCrypto.EVP_CipherInit_ex(cipherCtx, 0, encodedKey, iv, isEncrypting());
+ } else {
+ NativeCrypto.EVP_CipherInit_ex(cipherCtx, cipherType, encodedKey, iv, encrypting);
+ }
+
+ // OpenSSL only supports PKCS5 Padding.
+ NativeCrypto
+ .EVP_CIPHER_CTX_set_padding(cipherCtx, getPadding() == Padding.PKCS5PADDING);
+ modeBlockSize = NativeCrypto.EVP_CIPHER_CTX_block_size(cipherCtx);
+ calledUpdate = false;
}
@Override
- protected void checkSupportedKeySize(int keySize) throws InvalidKeyException {
- if (keySize != 16 && keySize != 24) {
- throw new InvalidKeyException("key size must be 128 or 192 bits");
+ protected int updateInternal(byte[] input, int inputOffset, int inputLen, byte[] output,
+ int outputOffset, int maximumLen) throws ShortBufferException {
+ final int intialOutputOffset = outputOffset;
+
+ final int bytesLeft = output.length - outputOffset;
+ if (bytesLeft < maximumLen) {
+ throw new ShortBufferException("output buffer too small during update: "
+ + bytesLeft + " < " + maximumLen);
+ }
+
+ outputOffset += NativeCrypto.EVP_CipherUpdate(cipherCtx, output, outputOffset, input,
+ inputOffset, inputLen);
+
+ calledUpdate = true;
+
+ return outputOffset - intialOutputOffset;
+ }
+
+ @Override
+ protected int doFinalInternal(byte[] output, int outputOffset, int maximumLen)
+ throws IllegalBlockSizeException, BadPaddingException, ShortBufferException {
+ /* Remember this so we can tell how many characters were written. */
+ final int initialOutputOffset = outputOffset;
+
+ /*
+ * If we're decrypting and haven't had any input, we should return
+ * null. Otherwise OpenSSL will complain if we call final.
+ */
+ if (!isEncrypting() && !calledUpdate) {
+ return 0;
+ }
+
+ /* Allow OpenSSL to pad if necessary and clean up state. */
+ final int bytesLeft = output.length - outputOffset;
+ final int writtenBytes;
+ if (bytesLeft >= maximumLen) {
+ writtenBytes = NativeCrypto.EVP_CipherFinal_ex(cipherCtx, output, outputOffset);
+ } else {
+ final byte[] lastBlock = new byte[maximumLen];
+ writtenBytes = NativeCrypto.EVP_CipherFinal_ex(cipherCtx, lastBlock, 0);
+ if (writtenBytes > bytesLeft) {
+ throw new ShortBufferException("buffer is too short: " + writtenBytes + " > "
+ + bytesLeft);
+ } else if (writtenBytes > 0) {
+ System.arraycopy(lastBlock, 0, output, outputOffset, writtenBytes);
+ }
+ }
+ outputOffset += writtenBytes;
+
+ reset();
+
+ return outputOffset - initialOutputOffset;
+ }
+
+ @Override
+ protected int getOutputSizeForFinal(int inputLen) {
+ if (modeBlockSize == 1) {
+ return inputLen;
+ } else {
+ final int buffered = NativeCrypto.get_EVP_CIPHER_CTX_buf_len(cipherCtx);
+ if (getPadding() == Padding.NOPADDING) {
+ return buffered + inputLen;
+ } else {
+ final int totalLen = inputLen + buffered + modeBlockSize;
+ return totalLen - (totalLen % modeBlockSize);
+ }
}
}
@Override
- protected void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException {
- switch (mode) {
- case CBC:
- return;
- default:
+ protected int getOutputSizeForUpdate(int inputLen) {
+ return getOutputSizeForFinal(inputLen);
+ }
+
+ /**
+ * Returns the OpenSSL cipher name for the particular {@code keySize}
+ * and cipher {@code mode}.
+ */
+ protected abstract String getCipherName(int keySize, Mode mode);
+
+ /**
+ * Reset this Cipher instance state to process a new chunk of data.
+ */
+ private void reset() {
+ NativeCrypto.EVP_CipherInit_ex(cipherCtx, 0, encodedKey, iv, isEncrypting());
+ calledUpdate = false;
+ }
+
+ public static class AES extends EVP_CIPHER {
+ private static final int AES_BLOCK_SIZE = 16;
+
+ protected AES(Mode mode, Padding padding) {
+ super(mode, padding);
+ }
+
+ public static class CBC extends AES {
+ public CBC(Padding padding) {
+ super(Mode.CBC, padding);
+ }
+
+ public static class NoPadding extends CBC {
+ public NoPadding() {
+ super(Padding.NOPADDING);
+ }
+ }
+
+ public static class PKCS5Padding extends CBC {
+ public PKCS5Padding() {
+ super(Padding.PKCS5PADDING);
+ }
+ }
+ }
+
+ public static class CTR extends AES {
+ public CTR() {
+ super(Mode.CTR, Padding.NOPADDING);
+ }
+ }
+
+ public static class ECB extends AES {
+ public ECB(Padding padding) {
+ super(Mode.ECB, padding);
+ }
+
+ public static class NoPadding extends ECB {
+ public NoPadding() {
+ super(Padding.NOPADDING);
+ }
+ }
+
+ public static class PKCS5Padding extends ECB {
+ public PKCS5Padding() {
+ super(Padding.PKCS5PADDING);
+ }
+ }
+ }
+
+ @Override
+ protected void checkSupportedKeySize(int keyLength) throws InvalidKeyException {
+ switch (keyLength) {
+ case 16: // AES 128
+ case 24: // AES 192
+ case 32: // AES 256
+ return;
+ default:
+ throw new InvalidKeyException("Unsupported key size: " + keyLength
+ + " bytes");
+ }
+ }
+
+ @Override
+ protected void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException {
+ switch (mode) {
+ case CBC:
+ case CTR:
+ case ECB:
+ return;
+ default:
+ throw new NoSuchAlgorithmException("Unsupported mode " + mode.toString());
+ }
+ }
+
+ @Override
+ protected void checkSupportedPadding(Padding padding) throws NoSuchPaddingException {
+ switch (padding) {
+ case NOPADDING:
+ case PKCS5PADDING:
+ return;
+ default:
+ throw new NoSuchPaddingException("Unsupported padding "
+ + padding.toString());
+ }
+ }
+
+ @Override
+ protected String getBaseCipherName() {
+ return "AES";
+ }
+
+ @Override
+ protected String getCipherName(int keyLength, Mode mode) {
+ return "aes-" + (keyLength * 8) + "-" + mode.toString().toLowerCase(Locale.US);
+ }
+
+ @Override
+ protected int getCipherBlockSize() {
+ return AES_BLOCK_SIZE;
+ }
+ }
+
+ public static class DESEDE extends EVP_CIPHER {
+ private static int DES_BLOCK_SIZE = 8;
+
+ public DESEDE(Mode mode, Padding padding) {
+ super(mode, padding);
+ }
+
+ public static class CBC extends DESEDE {
+ public CBC(Padding padding) {
+ super(Mode.CBC, padding);
+ }
+
+ public static class NoPadding extends CBC {
+ public NoPadding() {
+ super(Padding.NOPADDING);
+ }
+ }
+
+ public static class PKCS5Padding extends CBC {
+ public PKCS5Padding() {
+ super(Padding.PKCS5PADDING);
+ }
+ }
+ }
+
+ @Override
+ protected String getBaseCipherName() {
+ return "DESede";
+ }
+
+ @Override
+ protected String getCipherName(int keySize, Mode mode) {
+ final String baseCipherName;
+ if (keySize == 16) {
+ baseCipherName = "des-ede";
+ } else {
+ baseCipherName = "des-ede3";
+ }
+
+ return baseCipherName + "-" + mode.toString().toLowerCase(Locale.US);
+ }
+
+ @Override
+ protected void checkSupportedKeySize(int keySize) throws InvalidKeyException {
+ if (keySize != 16 && keySize != 24) {
+ throw new InvalidKeyException("key size must be 128 or 192 bits");
+ }
+ }
+
+ @Override
+ protected void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException {
+ if (mode != Mode.CBC) {
throw new NoSuchAlgorithmException("Unsupported mode " + mode.toString());
+ }
+ }
+
+ @Override
+ protected void checkSupportedPadding(Padding padding) throws NoSuchPaddingException {
+ switch (padding) {
+ case NOPADDING:
+ case PKCS5PADDING:
+ return;
+ default:
+ throw new NoSuchPaddingException("Unsupported padding "
+ + padding.toString());
+ }
+ }
+
+ @Override
+ protected int getCipherBlockSize() {
+ return DES_BLOCK_SIZE;
}
}
- @Override
- protected void checkSupportedPadding(Padding padding) throws NoSuchPaddingException {
- switch (padding) {
- case NOPADDING:
- case PKCS5PADDING:
- return;
- default:
- throw new NoSuchPaddingException("Unsupported padding " + padding.toString());
+ public static class ARC4 extends EVP_CIPHER {
+ public ARC4() {
+ // Modes and padding don't make sense for ARC4.
+ super(Mode.ECB, Padding.NOPADDING);
}
- }
- @Override
- protected int getCipherBlockSize() {
- return DES_BLOCK_SIZE;
- }
- }
+ @Override
+ protected String getBaseCipherName() {
+ return "ARCFOUR";
+ }
- public static class ARC4 extends OpenSSLCipher {
- public ARC4() {
- }
+ @Override
+ protected String getCipherName(int keySize, Mode mode) {
+ return "rc4";
+ }
- @Override
- protected String getBaseCipherName() {
- return "ARCFOUR";
- }
+ @Override
+ protected void checkSupportedKeySize(int keySize) throws InvalidKeyException {
+ }
- @Override
- protected String getCipherName(int keySize, Mode mode) {
- return "rc4";
- }
+ @Override
+ protected void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException {
+ throw new NoSuchAlgorithmException("ARC4 does not support modes");
+ }
- @Override
- protected void checkSupportedKeySize(int keySize) throws InvalidKeyException {
- }
+ @Override
+ protected void checkSupportedPadding(Padding padding) throws NoSuchPaddingException {
+ throw new NoSuchPaddingException("ARC4 does not support padding");
+ }
- @Override
- protected void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException {
- throw new NoSuchAlgorithmException("ARC4 does not support modes");
- }
+ @Override
+ protected int getCipherBlockSize() {
+ return 0;
+ }
- @Override
- protected void checkSupportedPadding(Padding padding) throws NoSuchPaddingException {
- throw new NoSuchPaddingException("ARC4 does not support padding");
- }
-
- @Override
- protected int getCipherBlockSize() {
- return 0;
- }
-
- @Override
- protected boolean supportsVariableSizeKey() {
- return true;
+ @Override
+ protected boolean supportsVariableSizeKey() {
+ return true;
+ }
}
}
}
diff --git a/src/main/java/org/conscrypt/OpenSSLProvider.java b/src/main/java/org/conscrypt/OpenSSLProvider.java
index 077cdbd..48eb532 100644
--- a/src/main/java/org/conscrypt/OpenSSLProvider.java
+++ b/src/main/java/org/conscrypt/OpenSSLProvider.java
@@ -208,19 +208,25 @@
* than 64 bits. We solve this confusion by making PKCS7Padding an
* alias for PKCS5Padding.
*/
- putSymmetricCipherImplClass("AES/ECB/NoPadding", "OpenSSLCipher$AES$ECB$NoPadding");
- putSymmetricCipherImplClass("AES/ECB/PKCS5Padding", "OpenSSLCipher$AES$ECB$PKCS5Padding");
+ putSymmetricCipherImplClass("AES/ECB/NoPadding",
+ "OpenSSLCipher$EVP_CIPHER$AES$ECB$NoPadding");
+ putSymmetricCipherImplClass("AES/ECB/PKCS5Padding",
+ "OpenSSLCipher$EVP_CIPHER$AES$ECB$PKCS5Padding");
put("Alg.Alias.Cipher.AES/ECB/PKCS7Padding", "AES/ECB/PKCS5Padding");
- putSymmetricCipherImplClass("AES/CBC/NoPadding", "OpenSSLCipher$AES$CBC$NoPadding");
- putSymmetricCipherImplClass("AES/CBC/PKCS5Padding", "OpenSSLCipher$AES$CBC$PKCS5Padding");
+ putSymmetricCipherImplClass("AES/CBC/NoPadding",
+ "OpenSSLCipher$EVP_CIPHER$AES$CBC$NoPadding");
+ putSymmetricCipherImplClass("AES/CBC/PKCS5Padding",
+ "OpenSSLCipher$EVP_CIPHER$AES$CBC$PKCS5Padding");
put("Alg.Alias.Cipher.AES/CBC/PKCS7Padding", "AES/CBC/PKCS5Padding");
- putSymmetricCipherImplClass("AES/CTR/NoPadding", "OpenSSLCipher$AES$CTR");
+ putSymmetricCipherImplClass("AES/CTR/NoPadding", "OpenSSLCipher$EVP_CIPHER$AES$CTR");
- putSymmetricCipherImplClass("DESEDE/CBC/NoPadding", "OpenSSLCipher$DESEDE$CBC$NoPadding");
- putSymmetricCipherImplClass("DESEDE/CBC/PKCS5Padding", "OpenSSLCipher$DESEDE$CBC$PKCS5Padding");
+ putSymmetricCipherImplClass("DESEDE/CBC/NoPadding",
+ "OpenSSLCipher$EVP_CIPHER$DESEDE$CBC$NoPadding");
+ putSymmetricCipherImplClass("DESEDE/CBC/PKCS5Padding",
+ "OpenSSLCipher$EVP_CIPHER$DESEDE$CBC$PKCS5Padding");
put("Alg.Alias.Cipher.DESEDE/CBC/PKCS7Padding", "DESEDE/CBC/PKCS5Padding");
- putSymmetricCipherImplClass("ARC4", "OpenSSLCipher$ARC4");
+ putSymmetricCipherImplClass("ARC4", "OpenSSLCipher$EVP_CIPHER$ARC4");
/* === Mac === */