diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/OpensslCipher.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/OpensslCipher.java index 7cf2afdc31624..517b804fb16db 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/OpensslCipher.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/OpensslCipher.java @@ -178,7 +178,21 @@ private static Transform tokenizeTransformation(String transformation) } return new Transform(parts[0], parts[1], parts[2]); } - + + public static boolean isSupported(CipherSuite suite) { + Transform transform; + int algMode; + int padding; + try { + transform = tokenizeTransformation(suite.getName()); + algMode = AlgMode.get(transform.alg, transform.mode); + padding = Padding.get(transform.padding); + } catch (NoSuchAlgorithmException|NoSuchPaddingException e) { + return false; + } + return isSupportedSuite(algMode, padding); + } + /** * Initialize this cipher with a key and IV. * @@ -300,5 +314,7 @@ private native int doFinal(long context, ByteBuffer output, int offset, private native void clean(long ctx, long engineNum); + private native static boolean isSupportedSuite(int alg, int padding); + public native static String getLibraryName(); } diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/OpensslSm4CtrCryptoCodec.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/OpensslSm4CtrCryptoCodec.java index f6b2f6a802556..38fd9ceabdcef 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/OpensslSm4CtrCryptoCodec.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/OpensslSm4CtrCryptoCodec.java @@ -41,6 +41,9 @@ public OpensslSm4CtrCryptoCodec() { if (loadingFailureReason != null) { throw new RuntimeException(loadingFailureReason); } + if (!OpensslCipher.isSupported(CipherSuite.SM4_CTR_NOPADDING)) { + throw new RuntimeException("Doesn't support SM4 CTR."); + } } @Override 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 f60a19a662c4c..442468eae65b1 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 @@ -125,7 +125,7 @@ static void loadSm4Ctr(JNIEnv *env) { #ifdef UNIX #if OPENSSL_VERSION_NUMBER >= 0x10101001L - LOAD_DYNAMIC_SYMBOL(dlsym_EVP_sm4_ctr, env, openssl, "EVP_sm4_ctr"); + MAYBE_LOAD_DYNAMIC_SYMBOL(dlsym_EVP_sm4_ctr, env, openssl, "EVP_sm4_ctr"); #endif #endif } @@ -554,3 +554,24 @@ JNIEXPORT jstring JNICALL Java_org_apache_hadoop_crypto_OpensslCipher_getLibrary } #endif } + +JNIEXPORT jboolean JNICALL Java_org_apache_hadoop_crypto_OpensslCipher_isSupportedSuite + (JNIEnv *env, jclass clazz, jint alg, jint padding) +{ + if (padding != NOPADDING) { + return JNI_FALSE; + } + + if (alg == AES_CTR && (dlsym_EVP_aes_256_ctr != NULL && dlsym_EVP_aes_128_ctr != NULL)) { + return JNI_TRUE; + } + + if (alg == SM4_CTR) { +#if OPENSSL_VERSION_NUMBER >= 0x10101001L + if (dlsym_EVP_sm4_ctr != NULL) { + return JNI_TRUE; + } +#endif + } + return JNI_FALSE; +} diff --git a/hadoop-common-project/hadoop-common/src/main/native/src/org_apache_hadoop.h b/hadoop-common-project/hadoop-common/src/main/native/src/org_apache_hadoop.h index 92a6b27235322..30f264b993dae 100644 --- a/hadoop-common-project/hadoop-common/src/main/native/src/org_apache_hadoop.h +++ b/hadoop-common-project/hadoop-common/src/main/native/src/org_apache_hadoop.h @@ -95,6 +95,12 @@ void *do_dlsym(JNIEnv *env, void *handle, const char *symbol) { if ((func_ptr = do_dlsym(env, handle, symbol)) == NULL) { \ return; \ } + +/* A helper macro to dlsym the requisite dynamic symbol ignoring error. */ +#define MAYBE_LOAD_DYNAMIC_SYMBOL(func_ptr, env, handle, symbol) \ + if ((func_ptr = do_dlsym(env, handle, symbol)) == NULL) { \ + (*env)->ExceptionClear(env); \ + } #endif // Unix part end 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 c0fdc51b1389b..c5b493390a968 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 @@ -106,31 +106,21 @@ public void testJceAesCtrCryptoCodec() throws Exception { @Test(timeout=120000) public void testJceSm4CtrCryptoCodec() throws Exception { - GenericTestUtils.assumeInNativeProfile(); - if (!NativeCodeLoader.buildSupportsOpenssl()) { - LOG.warn("Skipping test since openSSL library not loaded"); - Assume.assumeTrue(false); - } conf.set(HADOOP_SECURITY_CRYPTO_CIPHER_SUITE_KEY, "SM4/CTR/NoPadding"); conf.set(HADOOP_SECURITY_CRYPTO_CODEC_CLASSES_SM4_CTR_NOPADDING_KEY, JceSm4CtrCryptoCodec.class.getName()); conf.set(HADOOP_SECURITY_CRYPTO_JCE_PROVIDER_KEY, BouncyCastleProvider.PROVIDER_NAME); - Assert.assertEquals(null, OpensslCipher.getLoadingFailureReason()); cryptoCodecTest(conf, seed, 0, jceSm4CodecClass, jceSm4CodecClass, iv); cryptoCodecTest(conf, seed, count, jceSm4CodecClass, jceSm4CodecClass, iv); - cryptoCodecTest(conf, seed, count, - jceSm4CodecClass, opensslSm4CodecClass, 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++) { iv[8 + i] = (byte) 0xff; } cryptoCodecTest(conf, seed, count, jceSm4CodecClass, jceSm4CodecClass, iv); - cryptoCodecTest(conf, seed, count, - jceSm4CodecClass, opensslSm4CodecClass, iv); } @Test(timeout=120000) @@ -164,6 +154,7 @@ public void testOpensslSm4CtrCryptoCodec() throws Exception { LOG.warn("Skipping test since openSSL library not loaded"); Assume.assumeTrue(false); } + Assume.assumeTrue(OpensslCipher.isSupported(CipherSuite.SM4_CTR_NOPADDING)); conf.set(HADOOP_SECURITY_CRYPTO_JCE_PROVIDER_KEY, BouncyCastleProvider.PROVIDER_NAME); Assert.assertEquals(null, OpensslCipher.getLoadingFailureReason()); @@ -181,6 +172,8 @@ public void testOpensslSm4CtrCryptoCodec() throws Exception { opensslSm4CodecClass, opensslSm4CodecClass, iv); cryptoCodecTest(conf, seed, count, opensslSm4CodecClass, jceSm4CodecClass, iv); + cryptoCodecTest(conf, seed, count, + jceSm4CodecClass, opensslSm4CodecClass, iv); } private void cryptoCodecTest(Configuration conf, int seed, int count, 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..b97ddee9424d1 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 @@ -107,4 +107,11 @@ public void testDoFinalArguments() throws Exception { "Direct buffer is required", e); } } + + @Test(timeout=120000) + public void testIsSupportedSuite() throws Exception { + Assume.assumeTrue(OpensslCipher.getLoadingFailureReason() == null); + Assert.assertFalse(OpensslCipher.isSupported(CipherSuite.UNKNOWN)); + Assert.assertTrue(OpensslCipher.isSupported(CipherSuite.AES_CTR_NOPADDING)); + } }