Add Cipher support for AES through OpenSSL
Timings using encrypt with 256-bit key in CTR mode and PKCS5Padding:
implementation inputSize us linear runtime
OpenSSL 16 11.4 =
OpenSSL 32 12.1 =
OpenSSL 64 13.2 =
OpenSSL 128 15.1 =
OpenSSL 1024 44.0 =
OpenSSL 8192 275.0 ===
BouncyCastle 16 11.5 =
BouncyCastle 32 15.9 =
BouncyCastle 64 24.6 =
BouncyCastle 128 41.5 =
BouncyCastle 1024 277.2 ===
BouncyCastle 8192 2196.9 ==============================
Change-Id: I4aa6e3a2ca2b368fab2c602733b4f97e740d04fd
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
index 84f33f2..8d06929 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
@@ -172,14 +172,22 @@
public static native int EVP_get_cipherbyname(String string);
- public static native int EVP_CipherInit_ex(int cipherNid, byte[] key, byte[] iv,
+ public static native void EVP_CipherInit_ex(int ctx, int evpCipher, byte[] key, byte[] iv,
boolean encrypting);
public static native int EVP_CipherUpdate(int ctx, byte[] out, int outOffset, byte[] in,
- int inOffset);
+ int inOffset, int inLength);
public static native int EVP_CipherFinal_ex(int ctx, byte[] out, int outOffset);
+ public static native int EVP_CIPHER_iv_length(int evpCipher);
+
+ public static native int EVP_CIPHER_CTX_new();
+
+ public static native int EVP_CIPHER_CTX_block_size(int ctx);
+
+ public static native void EVP_CIPHER_CTX_set_padding(int ctx, boolean enablePadding);
+
public static native void EVP_CIPHER_CTX_cleanup(int ctx);
// --- RAND seeding --------------------------------------------------------
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLCipher.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLCipher.java
new file mode 100644
index 0000000..c65cbdc
--- /dev/null
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLCipher.java
@@ -0,0 +1,690 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.InvalidParameterSpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.Arrays;
+import java.util.Locale;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.CipherSpi;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import libcore.util.EmptyArray;
+
+public abstract class OpenSSLCipher extends CipherSpi {
+
+ /**
+ * Modes that a block cipher may support.
+ */
+ protected static enum Mode {
+ CBC,
+ CFB, CFB1, CFB8, CFB128,
+ CTR,
+ CTS,
+ ECB,
+ OFB, OFB64, OFB128,
+ PCBC,
+ }
+
+ /**
+ * Paddings that a block cipher may support.
+ */
+ protected static enum Padding {
+ NOPADDING,
+ PKCS5PADDING,
+ ISO10126PADDING,
+ }
+
+ /**
+ * Native pointer for the OpenSSL EVP_CIPHER context.
+ */
+ private OpenSSLCipherContext cipherCtx = new OpenSSLCipherContext(
+ NativeCrypto.EVP_CIPHER_CTX_new());
+
+ /**
+ * The current cipher mode.
+ */
+ private Mode mode = Mode.ECB;
+
+ /**
+ * The current cipher padding.
+ */
+ private Padding padding = Padding.PKCS5PADDING;
+
+ /**
+ * The Initial Vector (IV) used for the current cipher.
+ */
+ private byte[] iv;
+
+ /**
+ * Current cipher mode: encrypting or decrypting.
+ */
+ private boolean encrypting;
+
+ /**
+ * The block size of the current cipher.
+ */
+ private int blockSize;
+
+ /**
+ * The block size of the current mode.
+ */
+ private int modeBlockSize;
+
+ /**
+ * Buffer to hold a block-sized entry before calling into OpenSSL.
+ */
+ private byte[] buffer;
+
+ /**
+ * Current offset in the buffer.
+ */
+ private int bufferOffset;
+
+ protected OpenSSLCipher() {
+ }
+
+ protected OpenSSLCipher(Mode mode, Padding padding) {
+ this.mode = mode;
+ this.padding = padding;
+ blockSize = getCipherBlockSize();
+ }
+
+ /**
+ * 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.
+ */
+ protected abstract void checkSupportedKeySize(int keySize) throws InvalidKeyException;
+
+ /**
+ * Checks whether the cipher supports this particular cipher {@code mode}
+ * and throws {@code NoSuchAlgorithmException} if it doesn't.
+ */
+ protected abstract void checkSupportedMode(Mode mode) throws NoSuchAlgorithmException;
+
+ /**
+ * Checks whether the cipher supports this particular cipher {@code padding}
+ * and throws {@code NoSuchPaddingException} if it doesn't.
+ */
+ protected abstract void checkSupportedPadding(Padding padding) throws NoSuchPaddingException;
+
+ protected abstract int getCipherBlockSize();
+
+ @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);
+ newE.initCause(e);
+ throw newE;
+ }
+ checkSupportedMode(mode);
+ this.mode = mode;
+ }
+
+ @Override
+ protected void engineSetPadding(String paddingStr) throws NoSuchPaddingException {
+ final String paddingStrUpper = paddingStr.toUpperCase(Locale.US);
+ final Padding padding;
+ try {
+ padding = Padding.valueOf(paddingStrUpper);
+ } catch (IllegalArgumentException e) {
+ NoSuchPaddingException newE = new NoSuchPaddingException("No such padding: "
+ + paddingStr);
+ newE.initCause(e);
+ throw newE;
+ }
+ checkSupportedPadding(padding);
+ this.padding = padding;
+ }
+
+ @Override
+ protected int engineGetBlockSize() {
+ return blockSize;
+ }
+
+ /**
+ * The size of output if {@code doFinal()} 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.
+ */
+ private final int getFinalOutputSize(int inputLen) {
+ final int totalLen = bufferOffset + inputLen;
+ final int overrunLen = totalLen % blockSize;
+
+ if (overrunLen == 0) {
+ if ((padding == Padding.NOPADDING) && (totalLen > 0)) {
+ return totalLen;
+ } else {
+ return totalLen + blockSize;
+ }
+ } else {
+ return totalLen - overrunLen + blockSize;
+ }
+ }
+
+ @Override
+ protected int engineGetOutputSize(int inputLen) {
+ return getFinalOutputSize(inputLen);
+ }
+
+ @Override
+ protected byte[] engineGetIV() {
+ return iv;
+ }
+
+ @Override
+ protected AlgorithmParameters engineGetParameters() {
+ return null;
+ }
+
+ private void engineInitInternal(int opmode, Key key, byte[] iv) 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);
+
+ final int 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 ivLength = NativeCrypto.EVP_CIPHER_iv_length(cipherType);
+ if (iv == null) {
+ iv = new byte[ivLength];
+ } else if (iv.length != ivLength) {
+ throw new InvalidAlgorithmParameterException("expected IV length of " + ivLength);
+ }
+
+ this.iv = iv;
+
+ NativeCrypto.EVP_CipherInit_ex(cipherCtx.getContext(), cipherType, encodedKey, iv,
+ encrypting);
+
+ // OpenSSL only supports PKCS5 Padding.
+ NativeCrypto.EVP_CIPHER_CTX_set_padding(cipherCtx.getContext(),
+ padding == Padding.PKCS5PADDING);
+ modeBlockSize = NativeCrypto.EVP_CIPHER_CTX_block_size(cipherCtx.getContext());
+
+ buffer = new byte[blockSize];
+ bufferOffset = 0;
+ }
+
+ @Override
+ protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
+ try {
+ engineInitInternal(opmode, key, null);
+ } catch (InvalidAlgorithmParameterException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @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);
+ }
+
+ @Override
+ protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random)
+ throws InvalidKeyException, InvalidAlgorithmParameterException {
+ final AlgorithmParameterSpec spec;
+ try {
+ spec = params.getParameterSpec(IvParameterSpec.class);
+ } catch (InvalidParameterSpecException e) {
+ throw new InvalidAlgorithmParameterException(e);
+ }
+
+ engineInit(opmode, key, spec, random);
+ }
+
+ private final int updateInternal(byte[] input, int inputOffset, int inputLen, byte[] output,
+ int outputOffset, int totalLen, int fullBlocksSize) throws ShortBufferException {
+ final int intialOutputOffset = outputOffset;
+
+ /* Take care of existing buffered bytes. */
+ final int remainingBuffer = buffer.length - bufferOffset;
+ if (bufferOffset > 0 && inputLen >= remainingBuffer) {
+ System.arraycopy(input, inputOffset, buffer, bufferOffset, remainingBuffer);
+ final int writtenBytes = NativeCrypto.EVP_CipherUpdate(cipherCtx.getContext(), output,
+ outputOffset, buffer, 0, blockSize);
+ fullBlocksSize -= writtenBytes;
+ outputOffset += writtenBytes;
+
+ inputLen -= remainingBuffer;
+ inputOffset += remainingBuffer;
+
+ bufferOffset = 0;
+ }
+
+ /* Take care of the bytes that would fill up our block-sized buffer. */
+ if (fullBlocksSize > 0) {
+ final int bytesLeft = output.length - outputOffset;
+ if (bytesLeft < fullBlocksSize) {
+ throw new ShortBufferException("output buffer too small during update: "
+ + bytesLeft + " < " + fullBlocksSize);
+ }
+
+ outputOffset += NativeCrypto.EVP_CipherUpdate(cipherCtx.getContext(), output,
+ outputOffset, input, inputOffset, fullBlocksSize);
+ inputLen -= fullBlocksSize;
+ inputOffset += fullBlocksSize;
+ }
+
+ /* Put the rest into the buffer for next time. */
+ if (inputLen > 0) {
+ System.arraycopy(input, inputOffset, buffer, bufferOffset, inputLen);
+ bufferOffset += inputLen;
+ }
+
+ return outputOffset - intialOutputOffset;
+ }
+
+ @Override
+ protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
+ final int totalLen = bufferOffset + inputLen;
+ final int fullBlocksSize = totalLen - (totalLen % blockSize);
+
+ /* See how large our output buffer would need to be. */
+ final byte[] output;
+ if (fullBlocksSize > 0) {
+ output = new byte[fullBlocksSize];
+ } else {
+ output = EmptyArray.BYTE;
+ }
+
+ try {
+ updateInternal(input, inputOffset, inputLen, output, 0, totalLen, fullBlocksSize);
+ } catch (ShortBufferException e) {
+ /* This shouldn't happen. */
+ throw new AssertionError("calculated buffer size was wrong: " + fullBlocksSize);
+ }
+
+ return output;
+ }
+
+ @Override
+ protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output,
+ int outputOffset) throws ShortBufferException {
+ final int totalLen = bufferOffset + inputLen;
+ final int fullBlocksSize = totalLen - (totalLen % modeBlockSize);
+ return updateInternal(input, inputOffset, inputLen, output, outputOffset, totalLen, fullBlocksSize);
+ }
+
+ private int doFinalInternal(byte[] input, int inputOffset, int inputLen, byte[] output,
+ int outputOffset, int totalLen, int trailingLen, int maximumPossibleSize)
+ throws IllegalBlockSizeException, BadPaddingException, ShortBufferException {
+ if ((trailingLen != 0) && (padding == Padding.NOPADDING)) {
+ throw new IllegalBlockSizeException("not multiple of block size " + trailingLen
+ + " != " + modeBlockSize);
+ }
+
+ /* Remember this so we can tell how many characters were written. */
+ final int initialOutputOffset = outputOffset;
+
+ if (inputLen > 0) {
+ /*
+ * First run update to set up our invariant that we have less than
+ * {@code blockSize} worth of bytes for the next CipherUpdate call.
+ */
+ final int updateSize;
+ if (trailingLen == 0 && maximumPossibleSize >= blockSize) {
+ updateSize = maximumPossibleSize - blockSize;
+ } else {
+ updateSize = maximumPossibleSize - trailingLen;
+ }
+ final int updateBytesWritten = updateInternal(input, inputOffset, inputLen, output,
+ outputOffset, totalLen, updateSize);
+ outputOffset += updateBytesWritten;
+ }
+
+ /* Take care of existing buffered bytes. */
+ if (bufferOffset > 0) {
+ final int bytesLeft = output.length - outputOffset;
+
+ final int bytesNeeded = bufferOffset + modeBlockSize - 1;
+ final int writtenBytes;
+ if (bytesLeft < bytesNeeded) {
+ final byte[] tmpBuf = new byte[bytesNeeded];
+ writtenBytes = NativeCrypto.EVP_CipherUpdate(cipherCtx.getContext(), tmpBuf, 0,
+ buffer, 0, bufferOffset);
+ if (writtenBytes > 0) {
+ if (writtenBytes > bytesLeft) {
+ System.arraycopy(tmpBuf, 0, output, outputOffset, bytesLeft);
+ } else {
+ System.arraycopy(tmpBuf, 0, output, outputOffset, writtenBytes);
+ }
+ }
+ } else {
+ writtenBytes = NativeCrypto.EVP_CipherUpdate(cipherCtx.getContext(), output,
+ outputOffset, buffer, 0, bufferOffset);
+ }
+
+ outputOffset += writtenBytes;
+ bufferOffset = 0;
+ }
+
+ /* Allow OpenSSL to pad if necessary and clean up state. */
+ final int bytesLeft = output.length - outputOffset;
+ final int writtenBytes;
+ if (bytesLeft >= blockSize) {
+ writtenBytes = NativeCrypto.EVP_CipherFinal_ex(cipherCtx.getContext(), output,
+ outputOffset);
+ } else {
+ writtenBytes = NativeCrypto.EVP_CipherFinal_ex(cipherCtx.getContext(), buffer, 0);
+ if (writtenBytes > bytesLeft) {
+ throw new ShortBufferException("buffer is too short: " + writtenBytes + " > "
+ + bytesLeft);
+ } else if (writtenBytes > 0) {
+ System.arraycopy(buffer, 0, output, outputOffset, writtenBytes);
+ }
+ }
+ outputOffset += writtenBytes;
+
+ /* Re-initialize the cipher for the next time. */
+ NativeCrypto.EVP_CipherInit_ex(cipherCtx.getContext(), 0, null, null, encrypting);
+
+ return outputOffset - initialOutputOffset;
+ }
+
+ @Override
+ protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
+ throws IllegalBlockSizeException, BadPaddingException {
+ final int totalLen = bufferOffset + inputLen;
+ final int trailingLen = totalLen % modeBlockSize;
+
+ final int maximumPossibleSize = calculateMaximumPossibleSize(totalLen, trailingLen);
+ /* Assume that we'll output exactly on a byte boundary. */
+ byte[] output = new byte[maximumPossibleSize];
+ final int bytesWritten;
+ try {
+ bytesWritten = doFinalInternal(input, inputOffset, inputLen, output, 0, totalLen,
+ trailingLen, maximumPossibleSize);
+ } catch (ShortBufferException e) {
+ /* This should not happen since we sized our own buffer. */
+ throw new RuntimeException("our calculated buffer was too small", e);
+ }
+
+ if (bytesWritten == output.length) {
+ return output;
+ } else {
+ return Arrays.copyOfRange(output, 0, bytesWritten);
+ }
+ }
+
+ @Override
+ protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output,
+ int outputOffset) throws ShortBufferException, IllegalBlockSizeException,
+ BadPaddingException {
+ if (output == null) {
+ throw new NullPointerException("output == null");
+ }
+
+ final int totalLen = bufferOffset + inputLen;
+ final int trailingLen = totalLen % modeBlockSize;
+
+ final int maximumPossibleSize = calculateMaximumPossibleSize(totalLen, trailingLen);
+
+ return doFinalInternal(input, inputOffset, inputLen, output, outputOffset, totalLen,
+ trailingLen, maximumPossibleSize);
+ }
+
+ private int calculateMaximumPossibleSize(final int totalLen, final int trailingLen) {
+ if (encrypting && (modeBlockSize > 1) && (padding != Padding.NOPADDING)) {
+ return totalLen - trailingLen + modeBlockSize;
+ } else {
+ return totalLen;
+ }
+ }
+
+ @Override
+ protected byte[] engineWrap(Key key) throws IllegalBlockSizeException, InvalidKeyException {
+ try {
+ byte[] encoded = key.getEncoded();
+ return engineDoFinal(encoded, 0, encoded.length);
+ } catch (BadPaddingException e) {
+ IllegalBlockSizeException newE = new IllegalBlockSizeException();
+ newE.initCause(e);
+ throw newE;
+ }
+ }
+
+ @Override
+ protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, int wrappedKeyType)
+ throws InvalidKeyException, NoSuchAlgorithmException {
+ try {
+ byte[] encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length);
+ if (wrappedKeyType == Cipher.PUBLIC_KEY) {
+ KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm);
+ return keyFactory.generatePublic(new X509EncodedKeySpec(encoded));
+ } else if (wrappedKeyType == Cipher.PRIVATE_KEY) {
+ KeyFactory keyFactory = KeyFactory.getInstance(wrappedKeyAlgorithm);
+ return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(encoded));
+ } else if (wrappedKeyType == Cipher.SECRET_KEY) {
+ return new SecretKeySpec(encoded, wrappedKeyAlgorithm);
+ } else {
+ throw new UnsupportedOperationException("wrappedKeyType == " + wrappedKeyType);
+ }
+ } catch (IllegalBlockSizeException e) {
+ throw new InvalidKeyException(e);
+ } catch (BadPaddingException e) {
+ throw new InvalidKeyException(e);
+ } catch (InvalidKeySpecException e) {
+ throw new InvalidKeyException(e);
+ }
+ }
+
+ public static class AES extends OpenSSLCipher {
+ 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 CFB extends AES {
+ public CFB(Padding padding) {
+ super(Mode.CFB, padding);
+ }
+
+ public static class NoPadding extends CFB {
+ public NoPadding() {
+ super(Padding.NOPADDING);
+ }
+ }
+
+ public static class PKCS5Padding extends CFB {
+ public PKCS5Padding() {
+ super(Padding.PKCS5PADDING);
+ }
+ }
+ }
+
+ public static class CTR extends AES {
+ public CTR(Padding padding) {
+ super(Mode.CTR, padding);
+ }
+
+ public static class NoPadding extends CTR {
+ public NoPadding() {
+ super(Padding.NOPADDING);
+ }
+ }
+
+ public static class PKCS5Padding extends CTR {
+ public PKCS5Padding() {
+ super(Padding.PKCS5PADDING);
+ }
+ }
+ }
+
+ 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);
+ }
+ }
+ }
+
+ public static class OFB extends AES {
+ public OFB(Padding padding) {
+ super(Mode.OFB, padding);
+ }
+
+ public static class NoPadding extends OFB {
+ public NoPadding() {
+ super(Padding.NOPADDING);
+ }
+ }
+
+ public static class PKCS5Padding extends OFB {
+ 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 CFB:
+ case CFB1:
+ case CFB8:
+ case CFB128:
+ case CTR:
+ case ECB:
+ case OFB:
+ 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 getCipherName(int keyLength, Mode mode) {
+ return "aes-" + (keyLength * 8) + "-" + mode.toString().toLowerCase(Locale.US);
+ }
+
+ @Override
+ protected int getCipherBlockSize() {
+ return AES_BLOCK_SIZE;
+ }
+ }
+}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLCipherContext.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLCipherContext.java
new file mode 100644
index 0000000..d2980b1
--- /dev/null
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLCipherContext.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.xnet.provider.jsse;
+
+public class OpenSSLCipherContext {
+ private final int context;
+
+ OpenSSLCipherContext(int ctx) {
+ if (ctx == 0) {
+ throw new NullPointerException("ctx == 0");
+ }
+
+ this.context = ctx;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ NativeCrypto.EVP_CIPHER_CTX_cleanup(context);
+ } finally {
+ super.finalize();
+ }
+ }
+
+ int getContext() {
+ return context;
+ }
+}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLProvider.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLProvider.java
index e1d0fb0..4a2f9b7 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLProvider.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLProvider.java
@@ -125,5 +125,20 @@
put("Alg.Alias.Cipher.RSA/None/NoPadding", "RSA/ECB/NoPadding");
put("Cipher.RSA/ECB/PKCS1Padding", OpenSSLCipherRSA.PKCS1.class.getName());
put("Alg.Alias.Cipher.RSA/None/PKCS1Padding", "RSA/ECB/PKCS1Padding");
+
+ /*
+ * OpenSSL only supports a subset of modes, so we'll name them
+ * explicitly here.
+ */
+ put("Cipher.AES/ECB/NoPadding", OpenSSLCipher.AES.ECB.NoPadding.class.getName());
+ put("Cipher.AES/ECB/PKCS5Padding", OpenSSLCipher.AES.ECB.PKCS5Padding.class.getName());
+ put("Cipher.AES/CBC/NoPadding", OpenSSLCipher.AES.CBC.NoPadding.class.getName());
+ put("Cipher.AES/CBC/PKCS5Padding", OpenSSLCipher.AES.CBC.PKCS5Padding.class.getName());
+ put("Cipher.AES/CFB/NoPadding", OpenSSLCipher.AES.CFB.NoPadding.class.getName());
+ put("Cipher.AES/CFB/PKCS5Padding", OpenSSLCipher.AES.CFB.PKCS5Padding.class.getName());
+ put("Cipher.AES/CTR/NoPadding", OpenSSLCipher.AES.CTR.NoPadding.class.getName());
+ put("Cipher.AES/CTR/PKCS5Padding", OpenSSLCipher.AES.CTR.PKCS5Padding.class.getName());
+ put("Cipher.AES/OFB/NoPadding", OpenSSLCipher.AES.OFB.NoPadding.class.getName());
+ put("Cipher.AES/OFB/PKCS5Padding", OpenSSLCipher.AES.OFB.PKCS5Padding.class.getName());
}
}