From 82cdd6a7f3f51ee4f0f5975369516dc8c07f59ab Mon Sep 17 00:00:00 2001 From: Harsh J Date: Thu, 6 Oct 2016 11:22:22 +0530 Subject: [PATCH 1/2] HADOOP-13694. Add support for AES-192 in OpenSSL native Cipher. --- .../org/apache/hadoop/crypto/OpensslCipher.c | 26 +- .../hadoop/crypto/org_apache_hadoop_crypto.h | 3 +- .../apache/hadoop/crypto/TestCryptoCodec.java | 322 ++++++++++-------- .../hadoop/crypto/TestOpensslCipher.java | 49 ++- 4 files changed, 256 insertions(+), 144 deletions(-) diff --git a/hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/crypto/OpensslCipher.c b/hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/crypto/OpensslCipher.c index 5cb5bba9aeee1..566f1a4e179f2 100644 --- a/hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/crypto/OpensslCipher.c +++ b/hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/crypto/OpensslCipher.c @@ -36,6 +36,7 @@ static int (*dlsym_EVP_CipherUpdate)(EVP_CIPHER_CTX *, unsigned char *, \ int *, const unsigned char *, int); static int (*dlsym_EVP_CipherFinal_ex)(EVP_CIPHER_CTX *, unsigned char *, int *); static EVP_CIPHER * (*dlsym_EVP_aes_256_ctr)(void); +static EVP_CIPHER * (*dlsym_EVP_aes_192_ctr)(void); static EVP_CIPHER * (*dlsym_EVP_aes_128_ctr)(void); static void *openssl; #endif @@ -54,6 +55,7 @@ typedef int (__cdecl *__dlsym_EVP_CipherUpdate)(EVP_CIPHER_CTX *, \ typedef int (__cdecl *__dlsym_EVP_CipherFinal_ex)(EVP_CIPHER_CTX *, \ unsigned char *, int *); typedef EVP_CIPHER * (__cdecl *__dlsym_EVP_aes_256_ctr)(void); +typedef EVP_CIPHER * (__cdecl *__dlsym_EVP_aes_192_ctr)(void); typedef EVP_CIPHER * (__cdecl *__dlsym_EVP_aes_128_ctr)(void); static __dlsym_EVP_CIPHER_CTX_new dlsym_EVP_CIPHER_CTX_new; static __dlsym_EVP_CIPHER_CTX_free dlsym_EVP_CIPHER_CTX_free; @@ -64,6 +66,7 @@ static __dlsym_EVP_CipherInit_ex dlsym_EVP_CipherInit_ex; static __dlsym_EVP_CipherUpdate dlsym_EVP_CipherUpdate; static __dlsym_EVP_CipherFinal_ex dlsym_EVP_CipherFinal_ex; static __dlsym_EVP_aes_256_ctr dlsym_EVP_aes_256_ctr; +static __dlsym_EVP_aes_192_ctr dlsym_EVP_aes_192_ctr; static __dlsym_EVP_aes_128_ctr dlsym_EVP_aes_128_ctr; static HMODULE openssl; #endif @@ -72,12 +75,15 @@ static void loadAesCtr(JNIEnv *env) { #ifdef UNIX LOAD_DYNAMIC_SYMBOL(dlsym_EVP_aes_256_ctr, env, openssl, "EVP_aes_256_ctr"); + LOAD_DYNAMIC_SYMBOL(dlsym_EVP_aes_192_ctr, env, openssl, "EVP_aes_192_ctr"); LOAD_DYNAMIC_SYMBOL(dlsym_EVP_aes_128_ctr, env, openssl, "EVP_aes_128_ctr"); #endif #ifdef WINDOWS LOAD_DYNAMIC_SYMBOL(__dlsym_EVP_aes_256_ctr, dlsym_EVP_aes_256_ctr, \ env, openssl, "EVP_aes_256_ctr"); + LOAD_DYNAMIC_SYMBOL(__dlsym_EVP_aes_192_ctr, dlsym_EVP_aes_192_ctr, \ + env, openssl, "EVP_aes_192_ctr"); LOAD_DYNAMIC_SYMBOL(__dlsym_EVP_aes_128_ctr, dlsym_EVP_aes_128_ctr, \ env, openssl, "EVP_aes_128_ctr"); #endif @@ -165,7 +171,7 @@ JNIEXPORT jlong JNICALL Java_org_apache_hadoop_crypto_OpensslCipher_initContext return (jlong)0; } - if (dlsym_EVP_aes_256_ctr == NULL || dlsym_EVP_aes_128_ctr == NULL) { + if (dlsym_EVP_aes_256_ctr == NULL || dlsym_EVP_aes_192_ctr == NULL || dlsym_EVP_aes_128_ctr == NULL) { THROW(env, "java/security/NoSuchAlgorithmException", \ "Doesn't support AES CTR."); return (jlong)0; @@ -188,6 +194,8 @@ static EVP_CIPHER * getEvpCipher(int alg, int keyLen) if (alg == AES_CTR) { if (keyLen == KEY_LENGTH_256) { cipher = dlsym_EVP_aes_256_ctr(); + } else if (keyLen == KEY_LENGTH_192) { + cipher = dlsym_EVP_aes_192_ctr(); } else if (keyLen == KEY_LENGTH_128) { cipher = dlsym_EVP_aes_128_ctr(); } @@ -201,12 +209,22 @@ JNIEXPORT jlong JNICALL Java_org_apache_hadoop_crypto_OpensslCipher_init { int jKeyLen = (*env)->GetArrayLength(env, key); int jIvLen = (*env)->GetArrayLength(env, iv); - if (jKeyLen != KEY_LENGTH_128 && jKeyLen != KEY_LENGTH_256) { - THROW(env, "java/lang/IllegalArgumentException", "Invalid key length."); + if (jKeyLen != KEY_LENGTH_128 && jKeyLen != KEY_LENGTH_192 && jKeyLen != KEY_LENGTH_256) { + char* keyLenErrMsg; + if (asprintf(&keyLenErrMsg, "Invalid key length: %d bytes", jKeyLen) < 0) { + THROW(env, "java/lang/IllegalArgumentException", "Invalid key length"); + } else { + THROW(env, "java/lang/IllegalArgumentException", keyLenErrMsg); + } return (jlong)0; } if (jIvLen != IV_LENGTH) { - THROW(env, "java/lang/IllegalArgumentException", "Invalid iv length."); + char* ivLenErrMsg; + if (asprintf(&ivLenErrMsg, "Invalid iv length: %d bytes", jIvLen) < 0) { + THROW(env, "java/lang/IllegalArgumentException", "Invalid iv length."); + } else { + THROW(env, "java/lang/IllegalArgumentException", ivLenErrMsg); + } return (jlong)0; } diff --git a/hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/crypto/org_apache_hadoop_crypto.h b/hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/crypto/org_apache_hadoop_crypto.h index 0afab021dac3c..433a18b14faa4 100644 --- a/hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/crypto/org_apache_hadoop_crypto.h +++ b/hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/crypto/org_apache_hadoop_crypto.h @@ -47,6 +47,7 @@ #define JLONG(context) ((jlong)((ptrdiff_t)(context))) #define KEY_LENGTH_128 16 +#define KEY_LENGTH_192 24 #define KEY_LENGTH_256 32 #define IV_LENGTH 16 @@ -58,4 +59,4 @@ #define NOPADDING 0 #define PKCSPADDING 1 -#endif //ORG_APACHE_HADOOP_CRYPTO_H \ No newline at end of file +#endif //ORG_APACHE_HADOOP_CRYPTO_H diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/crypto/TestCryptoCodec.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/crypto/TestCryptoCodec.java index 52e547ba4051e..6a632465037fb 100644 --- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/crypto/TestCryptoCodec.java +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/crypto/TestCryptoCodec.java @@ -25,6 +25,7 @@ import java.io.IOException; import java.math.BigInteger; import java.security.GeneralSecurityException; +import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Arrays; import java.util.HashMap; @@ -47,12 +48,17 @@ import com.google.common.primitives.Longs; +import javax.crypto.Cipher; + public class TestCryptoCodec { private static final Log LOG= LogFactory.getLog(TestCryptoCodec.class); - private static byte[] key = new byte[16]; + private static byte[] key16Bytes = new byte[16]; + private static byte[] key24Bytes = new byte[24]; + private static byte[] key32Bytes = new byte[32]; private static byte[] iv = new byte[16]; private static final int bufferSize = 4096; - + private static boolean unlimitedJCE = false; + private Configuration conf = new Configuration(); private int count = 10000; private int seed = new Random().nextInt(); @@ -64,8 +70,18 @@ public class TestCryptoCodec { @Before public void setUp() throws IOException { Random random = new SecureRandom(); - random.nextBytes(key); + random.nextBytes(key16Bytes); + random.nextBytes(key24Bytes); + random.nextBytes(key32Bytes); random.nextBytes(iv); + try { + int aesKeyLength = Cipher.getMaxAllowedKeyLength("AES"); + if (aesKeyLength >= 256) { + unlimitedJCE = true; + } + } catch (NoSuchAlgorithmException ex) { + throw new IOException("Could not find AES codec available in JCE", ex); + } } @Test(timeout=120000) @@ -76,15 +92,25 @@ public void testJceAesCtrCryptoCodec() throws Exception { Assume.assumeTrue(false); } Assert.assertEquals(null, OpensslCipher.getLoadingFailureReason()); - cryptoCodecTest(conf, seed, 0, jceCodecClass, jceCodecClass, iv); - cryptoCodecTest(conf, seed, count, jceCodecClass, jceCodecClass, iv); - cryptoCodecTest(conf, seed, count, jceCodecClass, opensslCodecClass, iv); - // Overflow test, IV: xx xx xx xx xx xx xx xx ff ff ff ff ff ff ff ff + cryptoCodecTest( + conf, seed, 0, jceCodecClass, + jceCodecClass, iv, unlimitedJCE); + cryptoCodecTest( + conf, seed, count, jceCodecClass, + jceCodecClass, iv, unlimitedJCE); + cryptoCodecTest( + conf, seed, count, jceCodecClass, + opensslCodecClass, iv, unlimitedJCE); + // Overflow test, IV: xx xx xx xx xx xx xx xx ff ff ff ff ff ff ff ff for(int i = 0; i < 8; i++) { iv[8 + i] = (byte) 0xff; } - cryptoCodecTest(conf, seed, count, jceCodecClass, jceCodecClass, iv); - cryptoCodecTest(conf, seed, count, jceCodecClass, opensslCodecClass, iv); + cryptoCodecTest( + conf, seed, count, jceCodecClass, + jceCodecClass, iv, unlimitedJCE); + cryptoCodecTest( + conf, seed, count, jceCodecClass, + opensslCodecClass, iv, unlimitedJCE); } @Test(timeout=120000) @@ -95,136 +121,166 @@ public void testOpensslAesCtrCryptoCodec() throws Exception { Assume.assumeTrue(false); } Assert.assertEquals(null, OpensslCipher.getLoadingFailureReason()); - cryptoCodecTest(conf, seed, 0, opensslCodecClass, opensslCodecClass, iv); - cryptoCodecTest(conf, seed, count, opensslCodecClass, opensslCodecClass, iv); - cryptoCodecTest(conf, seed, count, opensslCodecClass, jceCodecClass, iv); - // Overflow test, IV: xx xx xx xx xx xx xx xx ff ff ff ff ff ff ff ff - for(int i = 0; i < 8; i++) { + + // OpenSSL offers all 3 forms of AES keys (128, 192, 256) + // so we'll test all 3 forms wherever applicable + + cryptoCodecTest( + conf, seed, 0, opensslCodecClass, + opensslCodecClass, iv, true); + + cryptoCodecTest( + conf, seed, count, opensslCodecClass, + opensslCodecClass, iv, true); + + cryptoCodecTest( + conf, seed, count, opensslCodecClass, + jceCodecClass, iv, unlimitedJCE); + + // Overflow test, IV: xx xx xx xx xx xx xx xx ff ff ff ff ff ff ff ff + for (int i = 0; i < 8; i++) { iv[8 + i] = (byte) 0xff; } - cryptoCodecTest(conf, seed, count, opensslCodecClass, opensslCodecClass, iv); - cryptoCodecTest(conf, seed, count, opensslCodecClass, jceCodecClass, iv); + + cryptoCodecTest( + conf, seed, count, opensslCodecClass, + opensslCodecClass, iv, true); + + cryptoCodecTest( + conf, seed, count, opensslCodecClass, + jceCodecClass, iv, unlimitedJCE); } - private void cryptoCodecTest(Configuration conf, int seed, int count, - String encCodecClass, String decCodecClass, byte[] iv) throws IOException, - GeneralSecurityException { - CryptoCodec encCodec = null; - try { - encCodec = (CryptoCodec)ReflectionUtils.newInstance( - conf.getClassByName(encCodecClass), conf); - } catch (ClassNotFoundException cnfe) { - throw new IOException("Illegal crypto codec!"); - } - LOG.info("Created a Codec object of type: " + encCodecClass); - - // Generate data - DataOutputBuffer data = new DataOutputBuffer(); - RandomDatum.Generator generator = new RandomDatum.Generator(seed); - for(int i = 0; i < count; ++i) { - generator.next(); - RandomDatum key = generator.getKey(); - RandomDatum value = generator.getValue(); - - key.write(data); - value.write(data); - } - LOG.info("Generated " + count + " records"); - - // Encrypt data - DataOutputBuffer encryptedDataBuffer = new DataOutputBuffer(); - CryptoOutputStream out = new CryptoOutputStream(encryptedDataBuffer, - encCodec, bufferSize, key, iv); - out.write(data.getData(), 0, data.getLength()); - out.flush(); - out.close(); - LOG.info("Finished encrypting data"); - - CryptoCodec decCodec = null; - try { - decCodec = (CryptoCodec)ReflectionUtils.newInstance( - conf.getClassByName(decCodecClass), conf); - } catch (ClassNotFoundException cnfe) { - throw new IOException("Illegal crypto codec!"); - } - LOG.info("Created a Codec object of type: " + decCodecClass); - - // Decrypt data - DataInputBuffer decryptedDataBuffer = new DataInputBuffer(); - decryptedDataBuffer.reset(encryptedDataBuffer.getData(), 0, - encryptedDataBuffer.getLength()); - CryptoInputStream in = new CryptoInputStream(decryptedDataBuffer, - decCodec, bufferSize, key, iv); - DataInputStream dataIn = new DataInputStream(new BufferedInputStream(in)); - - // Check - DataInputBuffer originalData = new DataInputBuffer(); - originalData.reset(data.getData(), 0, data.getLength()); - DataInputStream originalIn = new DataInputStream( - new BufferedInputStream(originalData)); - - for(int i=0; i < count; ++i) { - RandomDatum k1 = new RandomDatum(); - RandomDatum v1 = new RandomDatum(); - k1.readFields(originalIn); - v1.readFields(originalIn); - - RandomDatum k2 = new RandomDatum(); - RandomDatum v2 = new RandomDatum(); - k2.readFields(dataIn); - v2.readFields(dataIn); - assertTrue("original and encrypted-then-decrypted-output not equal", - k1.equals(k2) && v1.equals(v2)); - - // original and encrypted-then-decrypted-output have the same hashCode - Map m = new HashMap(); - m.put(k1, k1.toString()); - m.put(v1, v1.toString()); - String result = m.get(k2); - assertEquals("k1 and k2 hashcode not equal", result, k1.toString()); - result = m.get(v2); - assertEquals("v1 and v2 hashcode not equal", result, v1.toString()); + private void cryptoCodecTest(Configuration conf, int seed, int count, + String encCodecClass, String decCodecClass, + byte[] ivBytes, boolean allKeys) + throws IOException, GeneralSecurityException { + byte[][] encKeys; + if (allKeys) { + encKeys = new byte[][] {key16Bytes, key24Bytes, key32Bytes}; + } else { + encKeys = new byte[][] {key16Bytes}; } + for (int keyIndex = 0; keyIndex < encKeys.length; keyIndex++) { + byte[] encKey = encKeys[keyIndex]; + CryptoCodec encCodec = null; + try { + encCodec = (CryptoCodec) ReflectionUtils.newInstance( + conf.getClassByName(encCodecClass), conf); + } catch (ClassNotFoundException cnfe) { + throw new IOException("Illegal crypto codec!"); + } + LOG.info("Created a Codec object of type: " + encCodecClass); - // Decrypt data byte-at-a-time - originalData.reset(data.getData(), 0, data.getLength()); - decryptedDataBuffer.reset(encryptedDataBuffer.getData(), 0, - encryptedDataBuffer.getLength()); - in = new CryptoInputStream(decryptedDataBuffer, - decCodec, bufferSize, key, iv); - - // Check - originalIn = new DataInputStream(new BufferedInputStream(originalData)); - int expected; - do { - expected = originalIn.read(); - assertEquals("Decrypted stream read by byte does not match", - expected, in.read()); - } while (expected != -1); - - // Seek to a certain position and decrypt - originalData.reset(data.getData(), 0, data.getLength()); - decryptedDataBuffer.reset(encryptedDataBuffer.getData(), 0, - encryptedDataBuffer.getLength()); - in = new CryptoInputStream(new TestCryptoStreams.FakeInputStream( - decryptedDataBuffer), decCodec, bufferSize, key, iv); - int seekPos = data.getLength() / 3; - in.seek(seekPos); - - // Check - TestCryptoStreams.FakeInputStream originalInput = - new TestCryptoStreams.FakeInputStream(originalData); - originalInput.seek(seekPos); - do { - expected = originalInput.read(); - assertEquals("Decrypted stream read by byte does not match", - expected, in.read()); - } while (expected != -1); - - LOG.info("SUCCESS! Completed checking " + count + " records"); - - // Check secure random generator - testSecureRandom(encCodec); + // Generate data + DataOutputBuffer data = new DataOutputBuffer(); + RandomDatum.Generator generator = new RandomDatum.Generator(seed); + for (int i = 0; i < count; ++i) { + generator.next(); + RandomDatum key = generator.getKey(); + RandomDatum value = generator.getValue(); + + key.write(data); + value.write(data); + } + LOG.info("Generated " + count + " records"); + + // Encrypt data + DataOutputBuffer encryptedDataBuffer = new DataOutputBuffer(); + CryptoOutputStream out = new CryptoOutputStream(encryptedDataBuffer, + encCodec, bufferSize, encKey, ivBytes); + out.write(data.getData(), 0, data.getLength()); + out.flush(); + out.close(); + LOG.info("Finished encrypting data"); + + CryptoCodec decCodec = null; + try { + decCodec = (CryptoCodec) ReflectionUtils.newInstance( + conf.getClassByName(decCodecClass), conf); + } catch (ClassNotFoundException cnfe) { + throw new IOException("Illegal crypto codec!"); + } + LOG.info("Created a Codec object of type: " + decCodecClass); + + // Decrypt data + DataInputBuffer decryptedDataBuffer = new DataInputBuffer(); + decryptedDataBuffer.reset(encryptedDataBuffer.getData(), 0, + encryptedDataBuffer.getLength()); + CryptoInputStream in = new CryptoInputStream(decryptedDataBuffer, + decCodec, bufferSize, encKey, ivBytes); + DataInputStream dataIn = + new DataInputStream(new BufferedInputStream(in)); + + // Check + DataInputBuffer originalData = new DataInputBuffer(); + originalData.reset(data.getData(), 0, data.getLength()); + DataInputStream originalIn = new DataInputStream( + new BufferedInputStream(originalData)); + + for (int i = 0; i < count; ++i) { + RandomDatum k1 = new RandomDatum(); + RandomDatum v1 = new RandomDatum(); + k1.readFields(originalIn); + v1.readFields(originalIn); + + RandomDatum k2 = new RandomDatum(); + RandomDatum v2 = new RandomDatum(); + k2.readFields(dataIn); + v2.readFields(dataIn); + assertTrue("original and encrypted-then-decrypted-output not equal", + k1.equals(k2) && v1.equals(v2)); + + // original and encrypted-then-decrypted-output have the same hashCode + Map m = new HashMap(); + m.put(k1, k1.toString()); + m.put(v1, v1.toString()); + String result = m.get(k2); + assertEquals("k1 and k2 hashcode not equal", result, k1.toString()); + result = m.get(v2); + assertEquals("v1 and v2 hashcode not equal", result, v1.toString()); + } + + // Decrypt data byte-at-a-time + originalData.reset(data.getData(), 0, data.getLength()); + decryptedDataBuffer.reset(encryptedDataBuffer.getData(), 0, + encryptedDataBuffer.getLength()); + in = new CryptoInputStream(decryptedDataBuffer, + decCodec, bufferSize, encKey, ivBytes); + + // Check + originalIn = new DataInputStream(new BufferedInputStream(originalData)); + int expected; + do { + expected = originalIn.read(); + assertEquals("Decrypted stream read by byte does not match", + expected, in.read()); + } while (expected != -1); + + // Seek to a certain position and decrypt + originalData.reset(data.getData(), 0, data.getLength()); + decryptedDataBuffer.reset(encryptedDataBuffer.getData(), 0, + encryptedDataBuffer.getLength()); + in = new CryptoInputStream(new TestCryptoStreams.FakeInputStream( + decryptedDataBuffer), decCodec, bufferSize, encKey, ivBytes); + int seekPos = data.getLength() / 3; + in.seek(seekPos); + + // Check + TestCryptoStreams.FakeInputStream originalInput = + new TestCryptoStreams.FakeInputStream(originalData); + originalInput.seek(seekPos); + do { + expected = originalInput.read(); + assertEquals("Decrypted stream read by byte does not match", + expected, in.read()); + } while (expected != -1); + + LOG.info("SUCCESS! Completed checking " + count + " records"); + + // Check secure random generator + testSecureRandom(encCodec); + } } /** Test secure random generator */ diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/crypto/TestOpensslCipher.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/crypto/TestOpensslCipher.java index 966a88723a223..755af809ca37e 100644 --- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/crypto/TestOpensslCipher.java +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/crypto/TestOpensslCipher.java @@ -29,10 +29,21 @@ import org.junit.Test; public class TestOpensslCipher { - private static final byte[] key = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, - 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16}; - private static final byte[] iv = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, - 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; + private static final byte[] KEY_16_BYTES = {0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16}; + private static final byte[] KEY_24_BYTES = {0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x20, 0x21, 0x22, 0x23, 0x24}; + private static final byte[] KEY_32_BYTES = {0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, + 0x30, 0x31, 0x32}; + private static final byte[] KEY_INVALID_BYTES = {0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, + 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x30, 0x31, 0x32, 0x33}; + private static final byte[] IV = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; @Test(timeout=120000) public void testGetInstance() throws Exception { @@ -55,13 +66,39 @@ public void testGetInstance() throws Exception { } } + @Test(timeout=120000) + public void testAllKeySizes() throws Exception { + Assume.assumeTrue(OpensslCipher.getLoadingFailureReason() == null); + OpensslCipher cipher = OpensslCipher.getInstance("AES/CTR/NoPadding"); + // These 3 should work just fine + cipher.init(OpensslCipher.ENCRYPT_MODE, KEY_16_BYTES, IV); + cipher.init(OpensslCipher.ENCRYPT_MODE, KEY_24_BYTES, IV); + cipher.init(OpensslCipher.ENCRYPT_MODE, KEY_32_BYTES, IV); + // This one should not + try { + cipher.init(OpensslCipher.ENCRYPT_MODE, KEY_INVALID_BYTES, IV); + Assert.fail("Loading an invalid (" + KEY_INVALID_BYTES.length + + " bytes) length key should not have worked!"); + } catch (IllegalArgumentException ex) { + Assert.assertTrue( + "Incorrect error msg. for invalid key length test:" + + ex.getMessage(), + ex.getMessage().contains("Invalid key length") + ); + Assert.assertTrue( + "Key length not in error msg.: " + ex, + ex.getMessage().contains( + Integer.toString(KEY_INVALID_BYTES.length))); + } + } + @Test(timeout=120000) public void testUpdateArguments() throws Exception { Assume.assumeTrue(OpensslCipher.getLoadingFailureReason() == null); OpensslCipher cipher = OpensslCipher.getInstance("AES/CTR/NoPadding"); Assert.assertTrue(cipher != null); - cipher.init(OpensslCipher.ENCRYPT_MODE, key, iv); + cipher.init(OpensslCipher.ENCRYPT_MODE, KEY_16_BYTES, IV); // Require direct buffers ByteBuffer input = ByteBuffer.allocate(1024); @@ -94,7 +131,7 @@ public void testDoFinalArguments() throws Exception { OpensslCipher cipher = OpensslCipher.getInstance("AES/CTR/NoPadding"); Assert.assertTrue(cipher != null); - cipher.init(OpensslCipher.ENCRYPT_MODE, key, iv); + cipher.init(OpensslCipher.ENCRYPT_MODE, KEY_16_BYTES, IV); // Require direct buffer ByteBuffer output = ByteBuffer.allocate(1024); From 542965aa483d64f71a3cd9c24a4aebad82f2f409 Mon Sep 17 00:00:00 2001 From: Harsh J Date: Tue, 15 Nov 2016 06:03:57 +0530 Subject: [PATCH 2/2] Changes addressing @karth295's review comments - Added missing free calls to the messages generated, after the throw --- .../main/native/src/org/apache/hadoop/crypto/OpensslCipher.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/crypto/OpensslCipher.c b/hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/crypto/OpensslCipher.c index 566f1a4e179f2..87e5216266b50 100644 --- a/hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/crypto/OpensslCipher.c +++ b/hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/crypto/OpensslCipher.c @@ -215,6 +215,7 @@ JNIEXPORT jlong JNICALL Java_org_apache_hadoop_crypto_OpensslCipher_init THROW(env, "java/lang/IllegalArgumentException", "Invalid key length"); } else { THROW(env, "java/lang/IllegalArgumentException", keyLenErrMsg); + free(keyLenErrMsg); } return (jlong)0; } @@ -224,6 +225,7 @@ JNIEXPORT jlong JNICALL Java_org_apache_hadoop_crypto_OpensslCipher_init THROW(env, "java/lang/IllegalArgumentException", "Invalid iv length."); } else { THROW(env, "java/lang/IllegalArgumentException", ivLenErrMsg); + free(ivLenErrMsg); } return (jlong)0; }