From 781562d5e5b70a18d12ea9488be80d6c641f8e72 Mon Sep 17 00:00:00 2001 Subject: [PATCH] jdk17 Add KAE provider --- make/ZipSecurity.gmk | 1 + make/autoconf/.spec.gmk.in.swp | Bin 0 -> 16384 bytes make/autoconf/configure.ac | 1 + make/autoconf/jdk-options.m4 | 21 + make/autoconf/spec.gmk.in | 1 + make/common/Modules.gmk | 13 + make/conf/module-loader-map.conf | 1 + .../hotspot/nbproject/configurations.xml | 104 +++ make/modules/jdk.crypto.kaeprovider/Copy.gmk | 46 + make/modules/jdk.crypto.kaeprovider/Lib.gmk | 52 ++ src/java.base/share/classes/module-info.java | 9 +- .../share/lib/security/default.policy | 5 + .../share/classes/module-info.java | 6 + .../linux/classes/module-info.java | 39 + .../security/openssl/KAEAESCipher.java | 365 ++++++++ .../openeuler/security/openssl/KAEConfig.java | 393 +++++++++ .../security/openssl/KAEDHKeyAgreement.java | 289 +++++++ .../openssl/KAEDHKeyPairGenerator.java | 182 ++++ .../openeuler/security/openssl/KAEDigest.java | 264 ++++++ .../security/openssl/KAEECDHKeyAgreement.java | 146 ++++ .../openssl/KAEECKeyPairGenerator.java | 151 ++++ .../openeuler/security/openssl/KAEHMac.java | 227 +++++ .../openeuler/security/openssl/KAELog.java | 188 +++++ .../openeuler/security/openssl/KAEMGF1.java | 94 +++ .../security/openssl/KAEProvider.java | 326 +++++++ .../security/openssl/KAERSACipher.java | 796 ++++++++++++++++++ .../openssl/KAERSAKeyPairGenerator.java | 164 ++++ .../security/openssl/KAERSAPSSSignature.java | 683 +++++++++++++++ .../security/openssl/KAERSAPaddingType.java | 83 ++ .../security/openssl/KAERSASignature.java | 365 ++++++++ .../openssl/KAERSASignatureNative.java | 46 + .../security/openssl/KAESM4Cipher.java | 370 ++++++++ .../security/openssl/KAESM4KeyGenerator.java | 74 ++ .../openssl/KAESymmetricCipherBase.java | 791 +++++++++++++++++ .../openeuler/security/openssl/KAEUtils.java | 220 +++++ .../linux/conf/security/kaeprovider.conf | 75 ++ .../security/openssl/kae_cipher_rsa.c | 471 +++++++++++ .../openeuler/security/openssl/kae_digest.c | 232 +++++ .../security/openssl/kae_exception.c | 135 +++ .../security/openssl/kae_exception.h | 57 ++ .../org/openeuler/security/openssl/kae_hmac.c | 208 +++++ .../security/openssl/kae_keyagreement_dh.c | 141 ++++ .../security/openssl/kae_keyagreement_ecdh.c | 121 +++ .../openssl/kae_keypairgenerator_dh.c | 132 +++ .../openssl/kae_keypairgenerator_ec.c | 505 +++++++++++ .../openssl/kae_keypairgenerator_rsa.c | 173 ++++ .../org/openeuler/security/openssl/kae_log.h | 33 + .../openeuler/security/openssl/kae_provider.c | 103 +++ .../security/openssl/kae_signature_rsa.c | 366 ++++++++ .../security/openssl/kae_symmetric_cipher.c | 415 +++++++++ .../org/openeuler/security/openssl/kae_util.c | 246 ++++++ .../org/openeuler/security/openssl/kae_util.h | 94 +++ .../openeuler/security/openssl/openssl_ad.h | 10 + test/jdk/TEST.groups | 3 + .../KeyAgreement/KeyAgreementTest.java | 11 +- .../Signature/SignatureGetInstance.java | 5 +- .../openeuler/security/openssl/AESTest.java | 115 +++ .../openeuler/security/openssl/DHTest.java | 122 +++ .../security/openssl/DigestTest.java | 61 ++ .../openeuler/security/openssl/ECDHTest.java | 114 +++ .../openeuler/security/openssl/HmacTest.java | 89 ++ .../security/openssl/KAEConfTest.java | 122 +++ .../openssl/KAEDisabledAlgorithmsTest.java | 165 ++++ .../security/openssl/KAEEngineIdTest.java | 77 ++ .../security/openssl/KAELogTest.java | 127 +++ .../security/openssl/KAETestHelper.java | 209 +++++ .../security/openssl/KAEUseEngineTest.java | 263 ++++++ .../security/openssl/KaeDebugLogTest.java | 89 ++ .../security/openssl/KaeProviderTest.java | 171 ++++ .../openeuler/security/openssl/RSATest.java | 115 +++ .../openeuler/security/openssl/SM3Test.java | 55 ++ .../openeuler/security/openssl/SM4Test.java | 153 ++++ .../jca/PreferredProviderNegativeTest.java | 7 +- .../jdk/sun/security/krb5/auto/BasicProc.java | 4 +- test/jdk/sun/security/pkcs11/Secmod/policy | 1 + test/jdk/sun/security/pkcs11/policy | 1 + .../ssl/CipherSuite/DisabledCurve.java | 4 + .../SSLSocketImpl/NotifyHandshakeTest.policy | 2 + .../bench/security/openssl/AESBenchmark.java | 108 +++ .../security/openssl/AESGCMBenchmark.java | 133 +++ .../bench/security/openssl/BenchmarkBase.java | 106 +++ .../openssl/DHKeyAgreementBenchMark.java | 139 +++ .../openssl/DHKeyPairGeneratorBenchmark.java | 64 ++ .../security/openssl/DigestBenchmark.java | 69 ++ .../openssl/ECKeyAgreementBenchmark.java | 88 ++ .../openssl/ECKeyPairGeneratorBenchmark.java | 63 ++ .../bench/security/openssl/HMacBenchmark.java | 73 ++ .../security/openssl/RSACipherBenchmark.java | 107 +++ .../openssl/RSAKeyPairGeneratorBenchmark.java | 64 ++ .../openssl/RSAPSSSignatureBenchmark.java | 118 +++ .../openssl/RSASignatureBenchmark.java | 90 ++ .../bench/security/openssl/SM3Benchmark.java | 98 +++ .../bench/security/openssl/SM4Benchmark.java | 157 ++++ 93 files changed, 13590 insertions(+), 5 deletions(-) create mode 100644 make/autoconf/.spec.gmk.in.swp create mode 100644 make/modules/jdk.crypto.kaeprovider/Copy.gmk create mode 100644 make/modules/jdk.crypto.kaeprovider/Lib.gmk create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/module-info.java create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEAESCipher.java create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEConfig.java create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEDHKeyAgreement.java create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEDHKeyPairGenerator.java create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEDigest.java create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEECDHKeyAgreement.java create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEECKeyPairGenerator.java create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEHMac.java create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAELog.java create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEMGF1.java create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEProvider.java create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSACipher.java create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSAKeyPairGenerator.java create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSAPSSSignature.java create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSAPaddingType.java create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSASignature.java create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSASignatureNative.java create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAESM4Cipher.java create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAESM4KeyGenerator.java create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAESymmetricCipherBase.java create mode 100644 src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEUtils.java create mode 100644 src/jdk.crypto.kaeprovider/linux/conf/security/kaeprovider.conf create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_cipher_rsa.c create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_digest.c create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_exception.c create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_exception.h create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_hmac.c create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keyagreement_dh.c create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keyagreement_ecdh.c create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keypairgenerator_dh.c create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keypairgenerator_ec.c create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keypairgenerator_rsa.c create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_log.h create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_provider.c create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_signature_rsa.c create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_symmetric_cipher.c create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_util.c create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_util.h create mode 100644 src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/openssl_ad.h create mode 100644 test/jdk/org/openeuler/security/openssl/AESTest.java create mode 100644 test/jdk/org/openeuler/security/openssl/DHTest.java create mode 100644 test/jdk/org/openeuler/security/openssl/DigestTest.java create mode 100644 test/jdk/org/openeuler/security/openssl/ECDHTest.java create mode 100644 test/jdk/org/openeuler/security/openssl/HmacTest.java create mode 100644 test/jdk/org/openeuler/security/openssl/KAEConfTest.java create mode 100644 test/jdk/org/openeuler/security/openssl/KAEDisabledAlgorithmsTest.java create mode 100644 test/jdk/org/openeuler/security/openssl/KAEEngineIdTest.java create mode 100644 test/jdk/org/openeuler/security/openssl/KAELogTest.java create mode 100644 test/jdk/org/openeuler/security/openssl/KAETestHelper.java create mode 100644 test/jdk/org/openeuler/security/openssl/KAEUseEngineTest.java create mode 100644 test/jdk/org/openeuler/security/openssl/KaeDebugLogTest.java create mode 100644 test/jdk/org/openeuler/security/openssl/KaeProviderTest.java create mode 100644 test/jdk/org/openeuler/security/openssl/RSATest.java create mode 100644 test/jdk/org/openeuler/security/openssl/SM3Test.java create mode 100644 test/jdk/org/openeuler/security/openssl/SM4Test.java create mode 100644 test/micro/org/openeuler/bench/security/openssl/AESBenchmark.java create mode 100644 test/micro/org/openeuler/bench/security/openssl/AESGCMBenchmark.java create mode 100644 test/micro/org/openeuler/bench/security/openssl/BenchmarkBase.java create mode 100644 test/micro/org/openeuler/bench/security/openssl/DHKeyAgreementBenchMark.java create mode 100644 test/micro/org/openeuler/bench/security/openssl/DHKeyPairGeneratorBenchmark.java create mode 100644 test/micro/org/openeuler/bench/security/openssl/DigestBenchmark.java create mode 100644 test/micro/org/openeuler/bench/security/openssl/ECKeyAgreementBenchmark.java create mode 100644 test/micro/org/openeuler/bench/security/openssl/ECKeyPairGeneratorBenchmark.java create mode 100644 test/micro/org/openeuler/bench/security/openssl/HMacBenchmark.java create mode 100644 test/micro/org/openeuler/bench/security/openssl/RSACipherBenchmark.java create mode 100644 test/micro/org/openeuler/bench/security/openssl/RSAKeyPairGeneratorBenchmark.java create mode 100644 test/micro/org/openeuler/bench/security/openssl/RSAPSSSignatureBenchmark.java create mode 100644 test/micro/org/openeuler/bench/security/openssl/RSASignatureBenchmark.java create mode 100644 test/micro/org/openeuler/bench/security/openssl/SM3Benchmark.java create mode 100644 test/micro/org/openeuler/bench/security/openssl/SM4Benchmark.java diff --git a/make/ZipSecurity.gmk b/make/ZipSecurity.gmk index f7435f05e..21970928b 100644 --- a/make/ZipSecurity.gmk +++ b/make/ZipSecurity.gmk @@ -42,6 +42,7 @@ $(eval $(call SetupZipArchive,BUILD_SEC_BIN_ZIP, \ modules/java.base/sun/security/internal/spec \ modules/java.base/com/sun/crypto/provider \ modules/jdk.crypto.ec/sun/security/ec \ + modules/jdk.crypto.kaeprovider/org/openeuler/security/openssl \ modules/jdk.crypto.mscapi/sun/security/mscapi \ modules/jdk.crypto.cryptoki/sun/security/pkcs11 \ modules/jdk.crypto.cryptoki/sun/security/pkcs11/wrapper \ diff --git a/make/autoconf/configure.ac b/make/autoconf/configure.ac index 52baed8af..71aadc3e5 100644 --- a/make/autoconf/configure.ac +++ b/make/autoconf/configure.ac @@ -243,6 +243,7 @@ HOTSPOT_SETUP_MISC LIB_TESTS_ENABLE_DISABLE_FAILURE_HANDLER +JDKOPT_DETECT_KAE JDKOPT_ENABLE_DISABLE_GENERATE_CLASSLIST JDKOPT_EXCLUDE_TRANSLATIONS JDKOPT_ENABLE_DISABLE_MANPAGES diff --git a/make/autoconf/jdk-options.m4 b/make/autoconf/jdk-options.m4 index a5557fe0a..483733814 100644 --- a/make/autoconf/jdk-options.m4 +++ b/make/autoconf/jdk-options.m4 @@ -348,6 +348,27 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_DEBUG_SYMBOLS], AC_SUBST(SHIP_DEBUG_SYMBOLS) ]) +############################################################################### +# +# Enable or disable the kae crypto implementation +# +AC_DEFUN_ONCE([JDKOPT_DETECT_KAE], +[ + AC_ARG_ENABLE(kae, [AS_HELP_STRING([--enable-kae], + [enable KAE support on aarch64 @<:@disabled@:>@])], + [ENABLE_KAE="true"], [ENABLE_KAE="false"]) + AC_MSG_CHECKING([if kae has been enabled]) + if test "x$enable_kae" = "xyes"; then + AC_MSG_RESULT([yes]) + elif test "x$enable_kae" = "x" || test "x$enable_kae" = "xno"; then + AC_MSG_RESULT([no]) + else + AC_MSG_ERROR([Invalid value for --enable-kae: $enable_kae]) + fi + + AC_SUBST(ENABLE_KAE) +]) + ################################################################################ # # Native and Java code coverage diff --git a/make/autoconf/spec.gmk.in b/make/autoconf/spec.gmk.in index 807ba2758..2fdda9655 100644 --- a/make/autoconf/spec.gmk.in +++ b/make/autoconf/spec.gmk.in @@ -775,6 +775,7 @@ TAR_INCLUDE_PARAM:=@TAR_INCLUDE_PARAM@ TAR_SUPPORTS_TRANSFORM:=@TAR_SUPPORTS_TRANSFORM@ # Build setup +ENABLE_KAE:=@ENABLE_KAE@ USE_EXTERNAL_LIBJPEG:=@USE_EXTERNAL_LIBJPEG@ USE_EXTERNAL_LIBGIF:=@USE_EXTERNAL_LIBGIF@ USE_EXTERNAL_LIBZ:=@USE_EXTERNAL_LIBZ@ diff --git a/make/common/Modules.gmk b/make/common/Modules.gmk index 0eb0fb2dd..c3c6a1bf4 100644 --- a/make/common/Modules.gmk +++ b/make/common/Modules.gmk @@ -312,6 +312,19 @@ define ReadImportMetaData $$(eval $$(call ReadSingleImportMetaData, $$m))) endef +ifeq ($(ENABLE_KAE), true) + ifeq ($(OPENJDK_TARGET_CPU_ARCH), aarch64) + PLATFORM_MODULES += jdk.crypto.kaeprovider + endif +endif + +################################################################################ +# Filter out kae specific modules if kae is disabled or cpu is not aarch64 + +ifneq ($(ENABLE_KAE)-$(OPENJDK_TARGET_CPU_ARCH), true-aarch64) + MODULES_FILTER += jdk.crypto.kaeprovider +endif + ################################################################################ endif # _MODULES_GMK diff --git a/make/conf/module-loader-map.conf b/make/conf/module-loader-map.conf index c86ebf954..f72d96123 100644 --- a/make/conf/module-loader-map.conf +++ b/make/conf/module-loader-map.conf @@ -78,6 +78,7 @@ PLATFORM_MODULES= \ jdk.accessibility \ jdk.charsets \ jdk.crypto.cryptoki \ + jdk.crypto.kaeprovide \ jdk.crypto.ec \ jdk.dynalink \ jdk.httpserver \ diff --git a/make/ide/netbeans/hotspot/nbproject/configurations.xml b/make/ide/netbeans/hotspot/nbproject/configurations.xml index 4f64db4ec..29c5e668b 100644 --- a/make/ide/netbeans/hotspot/nbproject/configurations.xml +++ b/make/ide/netbeans/hotspot/nbproject/configurations.xml @@ -3,6 +3,110 @@ + + + + + kae_cipher_rsa.c + kae_digest.c + kae_exception.c + kae_exception.h + kae_hmac.c + kae_keyagreement_dh.c + kae_keyagreement_ecdh.c + kae_keypairgenerator_dh.c + kae_keypairgenerator_ec.c + kae_keypairgenerator_rsa.c + kae_provider.c + kae_signature_rsa.c + kae_symmetric_cipher.c + kae_util.c + kae_util.h + kae_log.h + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + nopadding in sunjce + if (mode == Mode.GCM) { + this.padding = Padding.NOPADDING; + } else if (paddingStr.equalsIgnoreCase("NOPADDING")) { + this.padding = Padding.NOPADDING; + } else if(paddingStr.equalsIgnoreCase("PKCS5PADDING")) { + if (mode == Mode.CTR) { + throw new NoSuchPaddingException("PKCS#5 padding not supported with CTR mode"); + } + this.padding = Padding.PKCS5PADDING; + } else { + throw new NoSuchPaddingException("Unsupported padding " + paddingStr); + } + } + + protected void checkIvBytes(byte[] ivBytes) throws InvalidAlgorithmParameterException { + if ((ivBytes == null) || (ivBytes.length != blockSize)) { + throw new InvalidAlgorithmParameterException("Wrong IV length: must be " + blockSize + " bytes long."); + } + } +} + diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEConfig.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEConfig.java new file mode 100644 index 000000000..a4eb57770 --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEConfig.java @@ -0,0 +1,393 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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. + */ + +package org.openeuler.security.openssl; + +import sun.security.util.Debug; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +public class KAEConfig { + private static final Debug kaeDebug = Debug.getInstance("kae"); + + // these property names indicates whether each algorithm uses KAEProvider + private static final String[] useKaeProviderPropertyNames = new String[]{ + "kae.md5", + "kae.sha256", + "kae.sha384", + "kae.sm3", + "kae.aes", + "kae.sm4", + "kae.hmac", + "kae.rsa", + "kae.dh", + "kae.ec" + }; + + // these property names indicate whether KAE hardware acceleration is enabled for each algorithm + private static final String[] useKaeEnginePropertyNames = new String[]{ + "kae.digest.useKaeEngine", + "kae.aes.useKaeEngine", + "kae.sm4.useKaeEngine", + "kae.hmac.useKaeEngine", + "kae.rsa.useKaeEngine", + "kae.dh.useKaeEngine", + "kae.ec.useKaeEngine" + }; + + // algorithm names + private static final String[] algorithmNames = new String[]{ + "md5", + "sha256", + "sha384", + "sm3", + "aes-128-ecb", + "aes-128-cbc", + "aes-128-ctr", + "aes-128-gcm", + "aes-192-ecb", + "aes-192-cbc", + "aes-192-ctr", + "aes-192-gcm", + "aes-256-ecb", + "aes-256-cbc", + "aes-256-ctr", + "aes-256-gcm", + "sm4-ecb", + "sm4-cbc", + "sm4-ctr", + "sm4-ofb", + "hmac-md5", + "hmac-sha1", + "hmac-sha224", + "hmac-sha256", + "hmac-sha384", + "hmac-sha512", + "rsa", + "dh", + "ec" + }; + + // algorithm name and algorithm index mapping + private static final Map algorithmNameIndexMap = new HashMap<>(); + + // algorithm name and algorithm category index mapping + private static final Map algorithmNameCategoryMap = new HashMap<>(); + + // whether use KAEProvider for each algorithm + private static final boolean[] useKaeProviderFlags = new boolean[algorithmNames.length]; + + // whether use KAEProvider for each category algorithm + private static final Map useKaeProviderCategoryMap = new HashMap<>(); + + // whether enable the Kunpeng acceleration engine for each algorithm + private static final boolean[] useKaeEngineFlags = new boolean[algorithmNames.length]; + + // The kaeprovider.cnf properties + private static Properties props; + + private KAEConfig() { + + } + + static { + kaeConfigInit(); + } + + @SuppressWarnings("removal") + private static void kaeConfigInit() { + AccessController.doPrivileged(new PrivilegedAction() { + public Void run() { + initialize(); + return null; + } + }); + } + + private static File kaePropFile(String filename) { + String sep = File.separator; + String defaultKaeConf = System.getProperty("java.home") + sep + "conf" + sep + filename; + String kaeConf = System.getProperty("kae.conf", defaultKaeConf); + return new File(kaeConf); + } + + private static void initialize() { + initProperties(); + initAlgorithmNameMap(); + initUseKaeProviderFlags(); + initUseKaeEngineFlags(); + } + + private static void initProperties() { + props = new Properties(); + File propFile = kaePropFile("kaeprovider.conf"); + if (propFile.exists()) { + InputStream is = null; + try { + FileInputStream fis = new FileInputStream(propFile); + is = new BufferedInputStream(fis); + props.load(is); + + if (kaeDebug != null) { + kaeDebug.println("reading kae properties file: " + + propFile); + } + } catch (IOException e) { + if (kaeDebug != null) { + kaeDebug.println("unable to load kae properties from " + + propFile); + e.printStackTrace(); + } + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException ioe) { + if (kaeDebug != null) { + kaeDebug.println("unable to close input stream"); + } + } + } + } + } else { + if (kaeDebug != null) { + kaeDebug.println("not found kae properties file: " + + propFile); + } + } + } + + public static Boolean useKaeProvider(String key) { + return useKaeProviderCategoryMap.getOrDefault(key, Boolean.TRUE); + } + + private static void initUseKaeProviderFlags() { + boolean[] categoryFlagsForProvider = new boolean[useKaeProviderPropertyNames.length]; + Arrays.fill(categoryFlagsForProvider, true); + for (int i = 0; i < useKaeProviderPropertyNames.length; i++) { + String configValue = privilegedGetOverridable(useKaeProviderPropertyNames[i]); + if (configValue != null) { + categoryFlagsForProvider[i] = Boolean.parseBoolean(configValue); + } + useKaeProviderCategoryMap.put(useKaeProviderPropertyNames[i], categoryFlagsForProvider[i]); + } + int offset = useKaeProviderPropertyNames.length - useKaeEnginePropertyNames.length; + int digestAlgorithmLen = offset + 1; + // digest + System.arraycopy(categoryFlagsForProvider, 0, useKaeProviderFlags, 0, digestAlgorithmLen); + + // non-digest + for (int i = digestAlgorithmLen; i < useKaeProviderFlags.length; i++) { + Integer algorithmCategoryIndex = algorithmNameCategoryMap.get(algorithmNames[i]); + if (categoryFlagsForProvider[algorithmCategoryIndex + offset]) { + useKaeProviderFlags[i] = true; + } + } + + if (kaeDebug != null) { + kaeDebug.println("useKaeProviderPropertyNames: "); + for (int i = 0; i < categoryFlagsForProvider.length; i++) { + kaeDebug.println(useKaeProviderPropertyNames[i] + "=" + categoryFlagsForProvider[i]); + } + + kaeDebug.println("useKaeProviderFlags: "); + for (int i = 0; i < useKaeProviderFlags.length; i++) { + kaeDebug.println(algorithmNames[i] + "=" + useKaeProviderFlags[i]); + } + } + } + + public static boolean[] getUseKaeProviderFlags() { + return useKaeProviderFlags; + } + + private static void initUseKaeEngineFlags() { + boolean[] categoryFlagsForEngine = new boolean[]{ + true, // digest + false, // aes + true, // sm4 + false, // hmac + true, // rsa + true, // dh + false // ec + }; + for (int i = 0; i < useKaeEnginePropertyNames.length; i++) { + String configValue = privilegedGetOverridable(useKaeEnginePropertyNames[i]); + if (configValue != null) { + categoryFlagsForEngine[i] = Boolean.parseBoolean(configValue); + } + } + + // EC algorithm currently does not support KAE hardware acceleration, temporarily use openssl soft calculation. + categoryFlagsForEngine[useKaeEnginePropertyNames.length - 1] = false; + + for (int i = 0; i < useKaeEngineFlags.length; i++) { + Integer algorithmCategoryIndex = algorithmNameCategoryMap.get(algorithmNames[i]); + if (categoryFlagsForEngine[algorithmCategoryIndex]) { + useKaeEngineFlags[i] = true; + } + } + + String[] disabledAlgorithms = getDisabledAlgorithms(); + for (String disabledAlgorithm : disabledAlgorithms) { + Integer algorithmIndex = algorithmNameIndexMap.get(disabledAlgorithm); + if (algorithmIndex != null) { + useKaeEngineFlags[algorithmIndex] = false; + } + } + if (kaeDebug != null) { + kaeDebug.println("useKaeEnginePropertyNames: "); + for (int i = 0; i < categoryFlagsForEngine.length; i++) { + kaeDebug.println(useKaeEnginePropertyNames[i] + "=" + categoryFlagsForEngine[i]); + } + + kaeDebug.println("disabledAlgorithms: "); + for (int i = 0; i < disabledAlgorithms.length; i++) { + kaeDebug.println(disabledAlgorithms[i]); + } + + kaeDebug.println("useKaeEngineFlags: "); + for (int i = 0; i < useKaeEngineFlags.length; i++) { + kaeDebug.println(algorithmNames[i] + "=" + useKaeEngineFlags[i]); + } + } + } + + public static boolean[] getUseKaeEngineFlags() { + return useKaeEngineFlags; + } + + private static void initAlgorithmNameIndexMap() { + for (int i = 0; i < algorithmNames.length; i++) { + algorithmNameIndexMap.put(algorithmNames[i], i); + } + } + + /* + * 0 : digest + * 1 : aes + * 2 : sm4 + * 3 : hmac + * 4 : rsa + * 5 : dh + * 6 : ec + */ + private static void initAlgorithmNameCategoryMap() { + algorithmNameCategoryMap.put("md5", 0); + algorithmNameCategoryMap.put("sha256", 0); + algorithmNameCategoryMap.put("sha384", 0); + algorithmNameCategoryMap.put("sm3", 0); + algorithmNameCategoryMap.put("aes-128-ecb", 1); + algorithmNameCategoryMap.put("aes-128-cbc", 1); + algorithmNameCategoryMap.put("aes-128-ctr", 1); + algorithmNameCategoryMap.put("aes-128-gcm", 1); + algorithmNameCategoryMap.put("aes-192-ecb", 1); + algorithmNameCategoryMap.put("aes-192-cbc", 1); + algorithmNameCategoryMap.put("aes-192-ctr", 1); + algorithmNameCategoryMap.put("aes-192-gcm", 1); + algorithmNameCategoryMap.put("aes-256-ecb", 1); + algorithmNameCategoryMap.put("aes-256-cbc", 1); + algorithmNameCategoryMap.put("aes-256-ctr", 1); + algorithmNameCategoryMap.put("aes-256-gcm", 1); + algorithmNameCategoryMap.put("sm4-ecb", 2); + algorithmNameCategoryMap.put("sm4-cbc", 2); + algorithmNameCategoryMap.put("sm4-ctr", 2); + algorithmNameCategoryMap.put("sm4-ofb", 2); + algorithmNameCategoryMap.put("hmac-md5", 3); + algorithmNameCategoryMap.put("hmac-sha1", 3); + algorithmNameCategoryMap.put("hmac-sha224", 3); + algorithmNameCategoryMap.put("hmac-sha256", 3); + algorithmNameCategoryMap.put("hmac-sha384", 3); + algorithmNameCategoryMap.put("hmac-sha512", 3); + algorithmNameCategoryMap.put("rsa", 4); + algorithmNameCategoryMap.put("dh", 5); + algorithmNameCategoryMap.put("ec", 6); + } + + private static void initAlgorithmNameMap() { + initAlgorithmNameIndexMap(); + initAlgorithmNameCategoryMap(); + } + + private static String[] getDisabledAlgorithms() { + String disabledAlgorithms = privilegedGetOverridable("kae.engine.disabledAlgorithms", + "sha256,sha384"); + return disabledAlgorithms.replaceAll(" ", "").split("\\,"); + } + + @SuppressWarnings("removal") + public static String privilegedGetProperty(String key) { + if (System.getSecurityManager() == null) { + return getProperty(key); + } else { + return AccessController.doPrivileged((PrivilegedAction) () -> getOverridableProperty(key)); + } + } + + @SuppressWarnings("removal") + public static String privilegedGetOverridable(String key) { + if (System.getSecurityManager() == null) { + return getOverridableProperty(key); + } else { + return AccessController.doPrivileged((PrivilegedAction) () -> getOverridableProperty(key)); + } + } + + public static String privilegedGetOverridable(String key, String defaultValue) { + String val = privilegedGetOverridable(key); + return (val == null) ? defaultValue : val; + } + + private static String getProperty(String key) { + String val = props.getProperty(key); + if (val != null) + val = val.trim(); + return val; + } + + private static String getOverridableProperty(String key) { + String val = System.getProperty(key); + if (val == null) { + return getProperty(key); + } else { + return val; + } + } + + public static String getAlgorithmName(int index) { + if (index < 0 || index >= algorithmNames.length) { + throw new IndexOutOfBoundsException(); + } + return algorithmNames[index]; + } +} diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEDHKeyAgreement.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEDHKeyAgreement.java new file mode 100644 index 000000000..541df07d6 --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEDHKeyAgreement.java @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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 org.openeuler.security.openssl; + +import sun.security.util.KeyUtil; +import java.math.BigInteger; +import java.security.KeyFactory; +import java.security.InvalidKeyException; +import java.security.SecureRandom; +import java.security.Key; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.security.AccessController; +import java.security.ProviderException; +import java.security.PrivilegedAction; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.AlgorithmParameterSpec; +import javax.crypto.KeyAgreementSpi; +import javax.crypto.SecretKey; +import javax.crypto.ShortBufferException; +import javax.crypto.interfaces.DHPrivateKey; +import javax.crypto.interfaces.DHPublicKey; +import javax.crypto.spec.DHParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import javax.crypto.spec.DHPublicKeySpec; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.DESedeKeySpec; +import javax.crypto.spec.DESKeySpec; + +public class KAEDHKeyAgreement extends KeyAgreementSpi { + private boolean generateSecret = false; + private BigInteger p; + private BigInteger g; + private BigInteger x; + private BigInteger y; + static final int[] AES_KEYSIZES = {16, 24, 32}; + static final int BLOWFISH_MAX_KEYSIZE = 56; + + private static class AllowKDF { + private static final boolean VALUE = getValue(); + + @SuppressWarnings("removal") + private static boolean getValue() { + return AccessController.doPrivileged( + (PrivilegedAction) + () -> Boolean.getBoolean("jdk.crypto.KeyAgreement.legacyKDF")); + } + } + + public KAEDHKeyAgreement() { + } + + @Override + protected void engineInit(Key key, SecureRandom random) throws InvalidKeyException { + try { + engineInit(key, null, random); + } catch (InvalidAlgorithmParameterException e) { + // never happens, because we did not pass any parameters + } + } + + @Override + protected void engineInit(Key key, AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException { + + // ignore "random" parameter, because our implementation does not + // require any source of randomness + generateSecret = false; + p = null; + g = null; + + if ((params != null) && !(params instanceof DHParameterSpec)) { + throw new InvalidAlgorithmParameterException("Diffie-Hellman parameters expected"); + } + + if (!(key instanceof DHPrivateKey)) { + throw new InvalidKeyException("Diffie-Hellman private key expected"); + } + + DHPrivateKey privateKey = (DHPrivateKey) key; + + // check if private key parameters are compatible with + // initialized ones + if (params != null) { + p = ((DHParameterSpec) params).getP(); + g = ((DHParameterSpec) params).getG(); + } + + BigInteger priv_p = privateKey.getParams().getP(); + BigInteger priv_g = privateKey.getParams().getG(); + if (p != null && priv_p != null && !(p.equals(priv_p))) { + throw new InvalidKeyException("Incompatible parameters"); + } + if (g != null && priv_g != null && !(g.equals(priv_g))) { + throw new InvalidKeyException("Incompatible parameters"); + } + if ((p == null && priv_p == null) || + (g == null) && priv_g == null) { + throw new InvalidKeyException("Missing parameters"); + } + p = priv_p; + g = priv_g; + + // store the x value + x = privateKey.getX(); + } + + @Override + protected Key engineDoPhase(Key key, boolean lastPhase) + throws InvalidKeyException, IllegalStateException { + if (!(key instanceof DHPublicKey)) { + throw new InvalidKeyException("Diffie-Hellman public " + + "expected"); + } + DHPublicKey publicKey = (DHPublicKey) key; + if (p == null || g == null) { + throw new IllegalStateException("Not initialized"); + } + BigInteger pub_p = publicKey.getParams().getP(); + BigInteger pub_g = publicKey.getParams().getG(); + if (pub_p != null && !(p.equals(pub_p))) { + throw new InvalidKeyException("Incompatible parameters"); + } + if (pub_g != null && !(g.equals(pub_g))) { + throw new InvalidKeyException("Incompatible parameters"); + } + KeyUtil.validate(publicKey); + y = publicKey.getY(); + generateSecret = true; + if (lastPhase == false) { + byte[] intermediate = engineGenerateSecret(); + try { + KeyFactory fk = KeyFactory.getInstance("DH"); + DHPublicKey newPublicKey = (DHPublicKey) fk.generatePublic( + new DHPublicKeySpec(new BigInteger(1, intermediate), p, g)); + return newPublicKey; + } catch (NoSuchAlgorithmException noalg) { + throw new ProviderException(noalg); + } catch (InvalidKeySpecException ikse) { + throw new ProviderException(ikse); + } + } else { + return null; + } + } + + @Override + protected byte[] engineGenerateSecret() + throws IllegalStateException { + int expectedLen = (p.bitLength() + 7) >>> 3; + byte[] result = new byte[expectedLen]; + try { + engineGenerateSecret(result, 0); + } catch (ShortBufferException shortBufferException) { + // should never happen since length are identical + } + return result; + } + + @Override + protected int engineGenerateSecret(byte[] sharedSecret, int offset) + throws IllegalStateException, ShortBufferException { + if (!generateSecret) { + throw new IllegalStateException("Key agreement has not bee complated yet"); + } + if (sharedSecret == null) { + throw new ShortBufferException("No buffer provided for shared secret"); + } + BigInteger modulus = p; + int expectedLen = (modulus.bitLength() + 7) >>> 3; + if ((sharedSecret.length - offset) < expectedLen) { + throw new ShortBufferException("Buffer too short for shared secret"); + } + generateSecret = false; + byte[] secret = nativeComputeKey(y.toByteArray(), x.toByteArray(), + p.toByteArray(), g.toByteArray(), modulus.bitLength()); + + if (secret.length == expectedLen) { + System.arraycopy(secret, 0, sharedSecret, offset, + secret.length); + } else { + // Array too short, pad it w/ leading 0s + if (secret.length < expectedLen) { + System.arraycopy(secret, 0, sharedSecret, + offset + (expectedLen - secret.length), + secret.length); + } else { + // Array too long, check and trim off the excess + if ((secret.length == (expectedLen + 1)) && secret[0] == 0) { + // ignore the leading sign byte + System.arraycopy(secret, 1, sharedSecret, offset, expectedLen); + } else { + throw new ProviderException("Generated secret is out-of-range"); + } + } + } + + return expectedLen; + } + + @Override + protected SecretKey engineGenerateSecret(String algorithm) + throws IllegalStateException, NoSuchAlgorithmException, InvalidKeyException { + if (algorithm == null) { + throw new NoSuchAlgorithmException("null algorithm"); + } + + if (!algorithm.equalsIgnoreCase("TlsPremasterSecret") && + !AllowKDF.VALUE) { + + throw new NoSuchAlgorithmException("Unsupported secret key " + + "algorithm: " + algorithm); + } + + byte[] secret = engineGenerateSecret(); + if (algorithm.equalsIgnoreCase("DES")) { + // DES + try { + SecretKeyFactory factory = SecretKeyFactory.getInstance("DES"); + return factory.generateSecret(new DESKeySpec(secret)); + } catch (InvalidKeySpecException e) { + throw new ProviderException("Generate DES Secret failed.", e); + } + } else if (algorithm.equalsIgnoreCase("DESede") + || algorithm.equalsIgnoreCase("TripleDES")) { + // Triple DES + try { + SecretKeyFactory factory = SecretKeyFactory.getInstance("DESede"); + return factory.generateSecret(new DESedeKeySpec(secret)); + } catch (InvalidKeySpecException e) { + throw new ProviderException("Generate DESede Secret failed.", e); + } + } else if (algorithm.equalsIgnoreCase("Blowfish")) { + // Blowfish + int keysize = secret.length; + if (keysize >= BLOWFISH_MAX_KEYSIZE) + keysize = BLOWFISH_MAX_KEYSIZE; + return new SecretKeySpec(secret, 0, keysize, "Blowfish"); + } else if (algorithm.equalsIgnoreCase("AES")) { + int idx = AES_KEYSIZES.length - 1; + int keysize = secret.length; + SecretKeySpec secretKey = null; + while (secretKey == null && idx >= 0) { + if (keysize >= AES_KEYSIZES[idx]) { + keysize = AES_KEYSIZES[idx]; + secretKey = new SecretKeySpec(secret, 0, keysize, "AES"); + } + idx--; + } + if (secretKey == null) { + throw new InvalidKeyException("Key material is too short"); + } + return secretKey; + } else if (algorithm.equals("TlsPremasterSecret")) { + // remove leading zero bytes per RFC 5246 Section 8.1.2 + return new SecretKeySpec( + KeyUtil.trimZeroes(secret), "TlsPremasterSecret"); + } else { + throw new NoSuchAlgorithmException("Unsupported secret key " + + "algorithm: " + algorithm); + } + } + protected native byte[] nativeComputeKey(byte[] y, byte[] x, byte[] p, byte[] g, int pSize); +} diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEDHKeyPairGenerator.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEDHKeyPairGenerator.java new file mode 100644 index 000000000..74134eef5 --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEDHKeyPairGenerator.java @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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 org.openeuler.security.openssl; + +import java.math.BigInteger; +import java.security.spec.AlgorithmParameterSpec; +import java.security.KeyPairGeneratorSpi; +import java.security.SecureRandom; +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyPair; +import java.security.NoSuchAlgorithmException; +import java.security.InvalidParameterException; +import java.security.GeneralSecurityException; +import java.security.ProviderException; +import java.security.KeyFactory; +import java.security.spec.InvalidKeySpecException; +import sun.security.jca.JCAUtil; +import sun.security.provider.ParameterCache; +import javax.crypto.spec.DHPublicKeySpec; +import javax.crypto.spec.DHPrivateKeySpec; +import javax.crypto.spec.DHParameterSpec; +import javax.crypto.interfaces.DHPublicKey; +import javax.crypto.interfaces.DHPrivateKey; + +import static sun.security.util.SecurityProviderConstants.DEF_DH_KEY_SIZE; + +public class KAEDHKeyPairGenerator + extends KeyPairGeneratorSpi { + private DHParameterSpec parameterSpec; + // The size in bits of the random exponent (private value) + private int pSize; + // The size in bits of the random exponent (private value) + private int lSize; + private SecureRandom random; + + public KAEDHKeyPairGenerator() { + super(); + initialize(DEF_DH_KEY_SIZE, null); + } + + private static void checkKeySize(int keySize) { + if ((keySize < 512) || (keySize > 8192) || ((keySize & 0x3F) != 0)) { + throw new InvalidParameterException( + "DH key size must be multiple of 64, and can only range " + + "from 512 to 8192(inclusize). " + + "The specific key size " + keySize + " is not supported"); + } + } + @Override + public void initialize(AlgorithmParameterSpec params, SecureRandom random) + throws InvalidAlgorithmParameterException { + + if (!(params instanceof DHParameterSpec)){ + throw new InvalidAlgorithmParameterException + ("Inappropriate parameter type"); + } + + parameterSpec = (DHParameterSpec) params; + pSize = parameterSpec.getP().bitLength(); + + try { + checkKeySize(pSize); + } catch (InvalidParameterException e) { + throw new InvalidAlgorithmParameterException(e.getMessage()); + } + + // exponent size is optional, could be 0 + lSize = parameterSpec.getL(); + + // Require exponentSize < primeSize + if ((lSize != 0) && (lSize > pSize)) { + throw new InvalidAlgorithmParameterException + ("Exponent size must not be larger than modulus size"); + } + + this.random = random; + } + + @Override + public void initialize(int keysize, SecureRandom random) { + checkKeySize(keysize); + this.parameterSpec = ParameterCache.getCachedDHParameterSpec(keysize); + if ((this.parameterSpec == null) && (keysize > 1024)) { + throw new InvalidParameterException("Unsupported " + keysize + "-bit DH parameter generation."); + } + this.pSize = keysize; + this.lSize = 0; + this.random = random; + } + + @Override + public KeyPair generateKeyPair() { + + if (random == null) { + random = JCAUtil.getSecureRandom(); + } + + if (parameterSpec == null) { + try { + parameterSpec = ParameterCache.getDHParameterSpec(pSize, random); + } catch (GeneralSecurityException e) { + // should never happen + throw new ProviderException(e); + } + } + + BigInteger p = parameterSpec.getP(); + BigInteger g = parameterSpec.getG(); + + if (lSize <= 0) { + lSize = pSize >> 1; + // use an exponent size of (pSize / 2) but at least 384 bits + if (lSize < 384) { + lSize = 384; + } + } + byte[][] keys; + try { + keys = nativeGenerateKeyPair(p.toByteArray(), g.toByteArray(), lSize); + } catch (Exception e){ + throw new ProviderException("Invoke nativeGenerateKeyPair failed.", e); + } + + // check keys + checkKeys(keys); + + BigInteger pubKey = new BigInteger(keys[0]); + BigInteger priKey = new BigInteger(keys[1]); + + try{ + KeyFactory fk = KeyFactory.getInstance("DH"); + DHPublicKey publicKey = (DHPublicKey)fk.generatePublic(new DHPublicKeySpec(pubKey, p , g)); + DHPrivateKey privateKey = (DHPrivateKey)fk.generatePrivate(new DHPrivateKeySpec(priKey, p, g)); + return new KeyPair(publicKey, privateKey); + } catch (NoSuchAlgorithmException noalg) { + throw new ProviderException(noalg); + } catch (InvalidKeySpecException ikse) { + throw new ProviderException(ikse); + } + } + + private void checkKeys(byte[][] keys) { + if (keys == null) { + throw new ProviderException("Invalid keys, keys is null."); + } + // The keys needs to contain at least 2 byte arrays, which are public and private keys. + if (keys.length < 2) { + throw new ProviderException("Invalid keys, keys length is less than 2."); + } + for (int i = 0; i < keys.length; i++) { + if (keys[i] == null) { + throw new ProviderException("Invalid keys, keys[" + i + "]" + "is null."); + } + } + } + + protected native static byte[][] nativeGenerateKeyPair(byte[] p, byte[] g, int lSize); +} diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEDigest.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEDigest.java new file mode 100644 index 000000000..aca5f4b29 --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEDigest.java @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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 org.openeuler.security.openssl; + +import java.lang.ref.PhantomReference; +import java.lang.ref.ReferenceQueue; +import java.security.DigestException; +import java.security.MessageDigestSpi; +import java.security.ProviderException; +import java.util.Set; +import java.util.concurrent.ConcurrentSkipListSet; + +/** + * KAE Digest + */ +abstract class KAEDigest extends MessageDigestSpi implements Cloneable { + + public static final class MD5 extends KAEDigest { + private static final long initContext = nativeInit("md5"); + + public MD5() { + super("md5", 16, initContext); + } + } + + public static final class SM3 extends KAEDigest { + private static final long initContext = nativeInit("sm3"); + + public SM3() { + super("sm3", 32, initContext); + } + } + + public static final class SHA256 extends KAEDigest { + private static final long initContext = nativeInit("sha256"); + + public SHA256() { + super("sha256", 32, initContext); + } + } + + public static final class SHA384 extends KAEDigest { + private static final long initContext = nativeInit("sha384"); + + public SHA384() { + super("sha384", 48, initContext); + } + } + + private final int digestLength; + + private final String algorithm; + private final long initContext; + + // field for ensuring native memory is freed + private DigestContextRef contextRef = null; + + KAEDigest(String algorithm, int digestLength, long initContext) { + this.algorithm = algorithm; + this.digestLength = digestLength; + this.initContext = initContext; + } + + private static class DigestContextRef extends PhantomReference + implements Comparable { + + private static ReferenceQueue referenceQueue = new ReferenceQueue<>(); + private static Set referenceList = new ConcurrentSkipListSet<>(); + private static boolean disableKaeDispose = Boolean.getBoolean("kae.disableKaeDispose"); + + private final long ctxAddress; + + DigestContextRef(KAEDigest kaeDigest, long ctxAddress) { + super(kaeDigest, referenceQueue); + this.ctxAddress = ctxAddress; + if (!disableKaeDispose) { + referenceList.add(this); + drainRefQueueBounded(); + } + } + + @Override + public int compareTo(DigestContextRef other) { + if (this.ctxAddress == other.ctxAddress) { + return 0; + } else { + return (this.ctxAddress < other.ctxAddress) ? -1 : 1; + } + } + + private static void drainRefQueueBounded() { + while (true) { + DigestContextRef next = (DigestContextRef) referenceQueue.poll(); + if (next == null) { + break; + } + next.dispose(); + } + } + + void dispose() { + if (!disableKaeDispose) { + referenceList.remove(this); + try { + nativeFree(ctxAddress); + } finally { + this.clear(); + } + } else { + nativeFree(ctxAddress); + } + } + } + + // single byte update. See JCA doc. + @Override + protected synchronized void engineUpdate(byte input) { + byte[] oneByte = new byte[]{input}; + engineUpdate(oneByte, 0, 1); + } + + + // array update. See JCA doc. + @Override + protected synchronized void engineUpdate(byte[] input, int offset, int len) { + if (len == 0 || input == null) { + return; + } + if ((offset < 0) || (len < 0) || (offset > input.length - len)) { + throw new ArrayIndexOutOfBoundsException(); + } + if (contextRef == null) { + contextRef = createDigestContext(this); + } + + try { + nativeUpdate(contextRef.ctxAddress, input, offset, len); + } catch (Exception e) { + engineReset(); + throw new ProviderException("nativeUpdate failed for " + algorithm, e); + } + } + + + // return the digest. See JCA doc. + @Override + protected synchronized byte[] engineDigest() { + final byte[] output = new byte[digestLength]; + try { + engineDigest(output, 0, digestLength); + } catch (Exception e) { + throw new ProviderException("Internal error", e); + } + return output; + } + + // return the digest in the specified array. See JCA doc. + @Override + protected int engineDigest(byte[] output, int offset, int len) throws DigestException { + if (output == null) { + return 0; + } + if (len < digestLength) { + throw new DigestException("Length must be at least " + + digestLength + " for " + algorithm + " digests"); + } + if ((offset < 0) || (len < 0) || (offset > output.length - len)) { + throw new DigestException("Buffer too short to store digest"); + } + if (contextRef == null) { + contextRef = createDigestContext(this); + } + try { + nativeDigest(contextRef.ctxAddress, output, offset, digestLength); + } catch (Exception e) { + throw new ProviderException("Invoke nativeDigest failed for " + algorithm, e); + } finally { + engineReset(); + } + return digestLength; + } + + // reset this object. See JCA doc. + @Override + protected synchronized void engineReset() { + if (contextRef != null) { + contextRef.dispose(); + contextRef = null; + } + } + + // return digest length. See JCA doc. + @Override + protected int engineGetDigestLength() { + return digestLength; + } + + @Override + public synchronized Object clone() throws CloneNotSupportedException { + KAEDigest kaeDigest = (KAEDigest) super.clone(); + if (kaeDigest.contextRef != null && kaeDigest.contextRef.ctxAddress != 0) { + long addr; + try { + addr = nativeClone(kaeDigest.contextRef.ctxAddress); + } catch (Exception e) { + throw new ProviderException("Invoke nativeClone failed for " + algorithm, e); + } + kaeDigest.contextRef = new DigestContextRef(kaeDigest, addr); + } + return kaeDigest; + } + + private DigestContextRef createDigestContext(KAEDigest kaeDigest) { + long addr; + try { + addr = nativeClone(initContext); + } catch (Exception e) { + throw new ProviderException("Invoke nativeInit failed for " + algorithm, e); + } + if (addr == 0) { + throw new RuntimeException("Cannot initialize EVP_MD_CTX for " + algorithm); + } + return new DigestContextRef(kaeDigest, addr); + } + + // return pointer to the context + protected native static long nativeInit(String algorithmName); + + // update the input byte + protected native static void nativeUpdate(long ctxAddress, byte[] input, int offset, int inLen); + + // digest and store the digest message to output + protected native static int nativeDigest(long ctxAddress, byte[] output, int offset, int len); + + // digest clone + protected static native long nativeClone(long ctxAddress); + + // free the specified context + protected native static void nativeFree(long ctxAddress); +} diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEECDHKeyAgreement.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEECDHKeyAgreement.java new file mode 100644 index 000000000..29dc09889 --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEECDHKeyAgreement.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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 org.openeuler.security.openssl; + +import sun.security.ec.ECKeyFactory; + +import java.math.BigInteger; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.SecureRandom; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import javax.crypto.KeyAgreementSpi; +import javax.crypto.SecretKey; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.SecretKeySpec; + +public class KAEECDHKeyAgreement extends KeyAgreementSpi { + private ECPrivateKey privateKey; + private ECPublicKey publicKey; + + // Length of the secret to be derived. + private int expectedSecretLen; + private String curveName; + + @Override + protected void engineInit(Key key, SecureRandom random) throws InvalidKeyException { + if (!(key instanceof PrivateKey)) { + throw new InvalidKeyException("Key must be instance of PrivateKey"); + } + privateKey = (ECPrivateKey) ECKeyFactory.toECKey(key); + publicKey = null; + } + + @Override + protected void engineInit(Key key, AlgorithmParameterSpec params, SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException { + if (params != null) { + throw new InvalidAlgorithmParameterException("Parameters not supported"); + } + engineInit(key, random); + } + + @Override + protected Key engineDoPhase(Key key, boolean lastPhase) throws InvalidKeyException, IllegalStateException { + if (privateKey == null) { + throw new IllegalStateException("Not initialized"); + } + if (publicKey != null) { + throw new IllegalStateException("Phase already executed"); + } + if (!lastPhase) { + throw new IllegalStateException + ("Only two party agreement supported, lastPhase must be true"); + } + if (!(key instanceof ECPublicKey)) { + throw new InvalidKeyException + ("Key must be a PublicKey with algorithm EC"); + } + + publicKey = (ECPublicKey) key; + ECParameterSpec params = publicKey.getParams(); + int keyLenBits = params.getCurve().getField().getFieldSize(); + // Bits to bytes. + expectedSecretLen = (keyLenBits + 7) >> 3; + + curveName = KAEUtils.getCurveBySize(keyLenBits); + if (curveName == null) { + throw new InvalidParameterException("unknown keyLenBits " + keyLenBits); + } + if (KAEUtils.getCurveByAlias(curveName) != null) { + curveName = KAEUtils.getCurveByAlias(curveName); + } + return null; + } + + @Override + protected byte[] engineGenerateSecret() throws IllegalStateException { + if ((privateKey == null) || (publicKey == null)) { + throw new IllegalStateException("Not initialized correctly"); + } + ECPoint w = publicKey.getW(); + BigInteger wX = w.getAffineX(); + BigInteger wY = w.getAffineY(); + + BigInteger s = privateKey.getS(); + byte[] secret = nativeGenerateSecret(curveName, wX.toByteArray(), wY.toByteArray(), s.toByteArray()); + if (secret == null || secret.length != expectedSecretLen) { + throw new RuntimeException("nativeGenerateSecret error. Expected: " + expectedSecretLen + ", actual: " + (secret == null ? "null" : secret.length)); + } + return secret; + } + + @Override + protected int engineGenerateSecret(byte[] sharedSecret, int offset) throws IllegalStateException, ShortBufferException { + if (offset + expectedSecretLen > sharedSecret.length) { + throw new ShortBufferException("Need " + expectedSecretLen + + " bytes, only " + (sharedSecret.length - offset) + "available"); + } + byte[] secret = engineGenerateSecret(); + System.arraycopy(secret, 0, sharedSecret, offset, secret.length); + return secret.length; + } + + @Override + protected SecretKey engineGenerateSecret(String algorithm) throws IllegalStateException, + NoSuchAlgorithmException, InvalidKeyException { + if (algorithm == null) { + throw new NoSuchAlgorithmException("Algorithm must not be null"); + } + return new SecretKeySpec(engineGenerateSecret(), algorithm); + } + + protected static native byte[] nativeGenerateSecret(String curveName, byte[] wX, byte[] wY, byte[] s); +} diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEECKeyPairGenerator.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEECKeyPairGenerator.java new file mode 100644 index 000000000..73d8551b1 --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEECKeyPairGenerator.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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 org.openeuler.security.openssl; + +import sun.security.ec.ECPrivateKeyImpl; +import sun.security.ec.ECPublicKeyImpl; + +import java.math.BigInteger; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidParameterException; +import java.security.KeyPair; +import java.security.KeyPairGeneratorSpi; +import java.security.ProviderException; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.ECFieldFp; +import java.security.spec.ECField; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.EllipticCurve; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +public class KAEECKeyPairGenerator extends KeyPairGeneratorSpi { + private ECParameterSpec param = null; + private final int defaultKeySize = 256; + + @Override + public void initialize(int keysize, SecureRandom random) { + String curveName = KAEUtils.getCurveBySize(keysize); + if (curveName == null) { + throw new InvalidParameterException("unknown key size " + keysize); + } + if (KAEUtils.getCurveByAlias(curveName) != null) { + curveName = KAEUtils.getCurveByAlias(curveName); + } + this.param = getParamsByCurve(curveName); + } + + private ECParameterSpec getParamsByCurve(String curveName) { + byte[][] params = nativeGenerateParam(curveName); + if (params == null) { + throw new InvalidParameterException("unknown curve " + curveName); + } + BigInteger p = new BigInteger(params[0]); + BigInteger a = new BigInteger(params[1]); + BigInteger b = new BigInteger(params[2]); + BigInteger x = new BigInteger(params[3]); + BigInteger y = new BigInteger(params[4]); + BigInteger order = new BigInteger(params[5]); + BigInteger cofactor = new BigInteger(params[6]); + ECField field = new ECFieldFp(p); + EllipticCurve curve = new EllipticCurve(field, a, b); + ECPoint g = new ECPoint(x, y); + ECParameterSpec spec = new ECParameterSpec(curve, g, order, cofactor.intValue()); + return spec; + } + + @Override + public void initialize(AlgorithmParameterSpec param, SecureRandom random) throws InvalidAlgorithmParameterException { + if (param instanceof ECParameterSpec) { + this.param = (ECParameterSpec) param; + } else if (param instanceof ECGenParameterSpec) { + ECGenParameterSpec ecParam = (ECGenParameterSpec)param; + String curveName = ecParam.getName(); + if (KAEUtils.getCurveByAlias(curveName) != null) { + curveName = KAEUtils.getCurveByAlias(curveName); + } + this.param = getParamsByCurve(curveName); + } else { + throw new InvalidAlgorithmParameterException("ECParameterSpec or ECGenParameterSpec for EC"); + } + } + + @Override + public KeyPair generateKeyPair() { + if (param == null) { + String curveName = KAEUtils.getCurveBySize(defaultKeySize); + param = getParamsByCurve(curveName); + } + EllipticCurve curve = param.getCurve(); + ECFieldFp field = (ECFieldFp) curve.getField(); + BigInteger p = field.getP(); + BigInteger a = curve.getA(); + BigInteger b = curve.getB(); + ECPoint generator = param.getGenerator(); + BigInteger x = generator.getAffineX(); + BigInteger y = generator.getAffineY(); + BigInteger order = param.getOrder(); + int cofactor = param.getCofactor(); + + byte[][] keys = nativeGenerateKeyPair(p.toByteArray(), a.toByteArray(), + b.toByteArray(), x.toByteArray(), y.toByteArray(), order.toByteArray(), cofactor); + if (keys == null) { + throw new RuntimeException("nativeGenerateKeyPair failed"); + } + BigInteger wX = new BigInteger(keys[0]); + BigInteger wY = new BigInteger(keys[1]); + BigInteger s = new BigInteger(keys[2]); + ECPoint w = new ECPoint(wX, wY); + + ECPrivateKeyImpl privateKey = null; + ECPublicKeyImpl publicKey = null; + try { + Class pubKeyImpl = Class.forName("sun.security.ec.ECPublicKeyImpl"); + Constructor conPubKeyImpl = pubKeyImpl.getDeclaredConstructor(ECPoint.class, ECParameterSpec.class); + conPubKeyImpl.setAccessible(true); + publicKey = (ECPublicKeyImpl) conPubKeyImpl.newInstance(w, param); + + Class priKeyImpl = Class.forName("sun.security.ec.ECPrivateKeyImpl"); + Constructor conPriKeyImpl = priKeyImpl.getDeclaredConstructor(BigInteger.class, ECParameterSpec.class); + conPriKeyImpl.setAccessible(true); + privateKey = (ECPrivateKeyImpl) conPriKeyImpl.newInstance(s, param); + } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | + IllegalAccessException | InvocationTargetException e) { + throw new ProviderException(e); + } + return new KeyPair(publicKey, privateKey); + } + + protected static native byte[][] nativeGenerateParam(String curveName); + + protected static native byte[][] nativeGenerateKeyPair(byte[] p, byte[] a, byte[] b, byte[] x, byte[] y, + byte[] order, int cofactor); +} diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEHMac.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEHMac.java new file mode 100644 index 000000000..3ca909aa1 --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEHMac.java @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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. + */ + +package org.openeuler.security.openssl; + +import javax.crypto.MacSpi; +import javax.crypto.SecretKey; +import java.lang.ref.PhantomReference; +import java.lang.ref.ReferenceQueue; +import java.security.*; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Set; +import java.util.concurrent.ConcurrentSkipListSet; + +public abstract class KAEHMac extends MacSpi { + + private final String algorithm; + + /** + * The secret key used in this keyed HMAC. + */ + private byte[] keyBytes; + + /** + * Holds the output size of the message digest. + */ + private final int digestSize; + + /** + * Holds a dummy buffer for writing single bytes to the digest. + */ + private final byte[] singleByte = new byte[1]; + + private HmacContextRef contextRef = null; + + private KAEHMac(String algo, int size) { + this.algorithm = algo; + this.digestSize = size; + } + + private static class HmacContextRef extends PhantomReference + implements Comparable { + + private static ReferenceQueue referenceQueue = new ReferenceQueue<>(); + private static Set referenceList = new ConcurrentSkipListSet<>(); + private static boolean disableKaeDispose = Boolean.getBoolean("kae.disableKaeDispose"); + + private final long address; + + HmacContextRef(KAEHMac kaeHMac, long address) { + super(kaeHMac, referenceQueue); + this.address = address; + if (!disableKaeDispose) { + referenceList.add(this); + drainRefQueueBounded(); + } + } + + @Override + public int compareTo(HmacContextRef other) { + if (this.address == other.address) { + return 0; + } else { + return (this.address < other.address) ? -1 : 1; + } + } + + private static void drainRefQueueBounded() { + while (true) { + HmacContextRef next = (HmacContextRef) referenceQueue.poll(); + if (next == null) break; + next.dispose(true); + } + } + + void dispose(boolean needFree) { + if (!disableKaeDispose) { + referenceList.remove(this); + try { + if (needFree) { + nativeFree(address); + } + } finally { + this.clear(); + } + } else { + nativeFree(address); + } + } + } + + private void checkAndInitHmacContext () { + try { + if (contextRef == null) { + long ctxAddr = nativeInit(keyBytes, keyBytes.length, algorithm); + contextRef = new HmacContextRef(this, ctxAddr); + } + } + catch (Exception e) { + throw new ProviderException(e.getMessage()) ; + } + } + + @Override + protected int engineGetMacLength() { + return digestSize; + } + + @Override + protected void engineInit(Key key, AlgorithmParameterSpec params) + throws InvalidKeyException, InvalidAlgorithmParameterException { + if (!(key instanceof SecretKey)) { + throw new InvalidKeyException("key must be a SecretKey"); + } + if (params != null) { + throw new InvalidAlgorithmParameterException("unknown parameter type"); + } + keyBytes = key.getEncoded(); + if (keyBytes == null) { + throw new InvalidKeyException("key cannot be encoded"); + } + } + + @Override + protected void engineUpdate(byte input) { + singleByte[0] = input; + engineUpdate(singleByte, 0, 1); + } + + @Override + protected void engineUpdate(byte[] input, int offset, int len) { + checkAndInitHmacContext(); + try { + nativeUpdate(contextRef.address, input, offset, len); + } + catch (Exception e) { + engineReset(); + throw new ProviderException(e.getMessage()); + } + } + + + @Override + protected byte[] engineDoFinal() { + final byte[] output = new byte[digestSize]; + checkAndInitHmacContext(); + final byte[] res; + try { + int bytesWritten = nativeFinal(contextRef.address, output, 0, digestSize); + res = new byte[bytesWritten]; + System.arraycopy(output, 0, res, 0, bytesWritten); + } + catch (Exception e) { + engineReset(); + throw new ProviderException(e.getMessage()); + } + return res; + } + + @Override + protected void engineReset() { + if (contextRef != null) { + contextRef.dispose(true); + contextRef = null; + } + } + + public static final class HmacMD5 extends KAEHMac { + public HmacMD5() { + super("MD5", 16); + } + } + public static final class HmacSHA1 extends KAEHMac { + public HmacSHA1() { + super("SHA1", 20); + } + } + public static final class HmacSHA224 extends KAEHMac { + public HmacSHA224() throws NoSuchAlgorithmException { + super("SHA224", 28); + } + } + public static final class HmacSHA256 extends KAEHMac { + public HmacSHA256() throws NoSuchAlgorithmException { + super("SHA256", 32); + } + } + public static final class HmacSHA384 extends KAEHMac { + public HmacSHA384() throws NoSuchAlgorithmException { + super("SHA384", 48); + } + } + public static final class HmacSHA512 extends KAEHMac { + public HmacSHA512() throws NoSuchAlgorithmException { + super("SHA512", 64); + } + } + + protected static native long nativeInit(byte[] key, int len, String algo); + + protected static native void nativeUpdate(long ctxAddr, byte[] input, int inOffset, int inLen); + + protected static native int nativeFinal(long ctxAddr, byte[] output, int outOffset, int inLen); + + protected static native void nativeFree(long ctxAddr); +} diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAELog.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAELog.java new file mode 100644 index 000000000..33c1c430f --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAELog.java @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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. + */ + +package org.openeuler.security.openssl; + +import sun.security.util.Debug; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; + +public class KAELog { + private static final Debug kaeDebug = Debug.getInstance("kae"); + private static File logFile; + private static boolean exist; + + private KAELog() { + + } + + static { + kaeLogInit(); + } + + @SuppressWarnings("removal") + private static void kaeLogInit() { + AccessController.doPrivileged(new PrivilegedAction() { + public Void run() { + initialize(); + return null; + } + }); + } + + private static void initialize() { + if (!enableKaeLog()) { + if (kaeDebug != null) { + kaeDebug.println("kae logging is not enabled"); + } + return; + } + + logFile = kaeLogFile("kae.log"); + File parentFile = logFile.getParentFile(); + if (!parentFile.exists()) { + try { + Files.createDirectories(parentFile.toPath()); + } catch (IOException e) { + if (kaeDebug != null) { + kaeDebug.println("failed to create directory :" + parentFile); + e.printStackTrace(); + } + return; + } + } + + if (logFile.exists()) { + if (kaeDebug != null) { + kaeDebug.println("found kae log file :" + logFile); + } + exist = true; + } else { + if (kaeDebug != null) { + kaeDebug.println("not found kae log file :" + logFile); + } + try { + Path path = Files.createFile(logFile.toPath()); + if (path != null) { + exist = true; + } + } catch (IOException e) { + if (kaeDebug != null) { + kaeDebug.println("unable to create new kae log file :" + logFile); + e.printStackTrace(); + } + } + + if (exist) { + if (kaeDebug != null) { + kaeDebug.println("create new kae log file :" + logFile); + } + } + } + } + + public static boolean enableKaeLog() { + String debug = KAEConfig.privilegedGetOverridable("kae.log"); + return Boolean.parseBoolean(debug); + } + + private static File kaeLogFile(String filename) { + String sep = File.separator; + String defaultKaeLog = System.getProperty("user.dir") + sep + filename; + String kaeLog = KAEConfig.privilegedGetOverridable("kae.log.file", defaultKaeLog); + return new File(kaeLog); + } + + private static String getLogTime() { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + return simpleDateFormat.format(new Date()); + } + + public static void log(String engineId, Throwable throwable, boolean[] engineFlags, boolean[] kaeProviderFlags) { + if (engineFlags.length != kaeProviderFlags.length) { + if (kaeDebug != null) { + kaeDebug.println("The length of engineFlags is not equal to the length of kaeProviderFlags."); + kaeDebug.println(String.format("engineFlags : %s", Arrays.toString(engineFlags))); + kaeDebug.println(String.format("kaeProviderFlags : %s", Arrays.toString(kaeProviderFlags))); + } + return; + } + if (!exist) { + return; + } + + try (BufferedWriter writer = Files.newBufferedWriter(logFile.toPath(), + StandardOpenOption.APPEND)) { + logEngine(writer, engineId, throwable); + writer.newLine(); + logAlgorithmStrategy(writer, engineFlags, kaeProviderFlags); + writer.newLine(); + } catch (IOException e) { + if (kaeDebug != null) { + kaeDebug.println("write kae log failed"); + e.printStackTrace(); + } + } + } + + // log engine + private static void logEngine(BufferedWriter writer, String engineId, Throwable throwable) throws IOException { + writer.write(String.format("[%s] ", getLogTime())); + if (throwable == null) { + writer.write(String.format("%s engine was found.", engineId)); + } else if (throwable instanceof RuntimeException) { + writer.write(String.format("%s engine was not found. %s", engineId, throwable.getMessage())); + } else { + writer.write(throwable.getMessage()); + } + } + + // log algorithm strategy + private static void logAlgorithmStrategy(BufferedWriter writer, boolean[] engineFlags, boolean[] kaeProviderFlags) + throws IOException { + writer.write(String.format("[%s] ", getLogTime())); + writer.write("The implementation strategy of each algorithm is as follows : "); + for (int i = 0; i < engineFlags.length; i++) { + writer.newLine(); + String algorithmName = KAEConfig.getAlgorithmName(i); + String message; + if (kaeProviderFlags[i]) { + String detail = engineFlags[i] ? "enable KAE hardware acceleration" : "Use openssl soft calculation"; + message = String.format(" %-11s => %s: %s", algorithmName, "KAEProvider", detail); + } else { + message = String.format(" %-11s => %s", algorithmName, "Non-KAEProvider"); + } + writer.write(message); + } + } +} \ No newline at end of file diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEMGF1.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEMGF1.java new file mode 100644 index 000000000..dc79a3f62 --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEMGF1.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2018, 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 org.openeuler.security.openssl; + +import java.security.*; + +/** + * This class implements the MGF1 mask generation function defined in PKCS#1 + * v2.2 B.2.1 (https://tools.ietf.org/html/rfc8017#appendix-B.2.1). A mask + * generation function takes an octet string of variable length and a + * desired output length as input and outputs an octet string of the + * desired length. MGF1 is a mask generation function based on a hash + * function, i.e. message digest algorithm. + * + * @since 11 + */ +final class KAEMGF1 { + + private final MessageDigest md; + + /** + * Construct an instance of MGF1 based on the specified digest algorithm. + */ + KAEMGF1(String mdAlgo) throws NoSuchAlgorithmException { + this.md = MessageDigest.getInstance(mdAlgo); + } + + /** + * Using the specified seed bytes, generate the mask, xor the mask + * with the specified output buffer and store the result into the + * output buffer (essentially replaced in place). + * + * @param seed the buffer holding the seed bytes + * @param seedOfs the index of the seed bytes + * @param seedLen the length of the seed bytes to be used by MGF1 + * @param maskLen the intended length of the generated mask + * @param out the output buffer holding the mask + * @param outOfs the index of the output buffer for the mask + */ + void generateAndXor(byte[] seed, int seedOfs, int seedLen, int maskLen, + byte[] out, int outOfs) throws RuntimeException { + byte[] C = new byte[4]; // 32 bit counter + byte[] digest = new byte[md.getDigestLength()]; + while (maskLen > 0) { + md.update(seed, seedOfs, seedLen); + md.update(C); + try { + md.digest(digest, 0, digest.length); + } catch (DigestException e) { + // should never happen + throw new RuntimeException(e.toString()); + } + for (int i = 0; (i < digest.length) && (maskLen > 0); maskLen--) { + out[outOfs++] ^= digest[i++]; + } + if (maskLen > 0) { + // increment counter + for (int i = C.length - 1; (++C[i] == 0) && (i > 0); i--) { + // empty + } + } + } + } + + /** + * Returns the name of this MGF1 instance, i.e. "MGF1" followed by the + * digest algorithm it based on. + */ + String getName() { + return "KAEMGF1" + md.getAlgorithm(); + } +} diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEProvider.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEProvider.java new file mode 100644 index 000000000..b3c1fb022 --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEProvider.java @@ -0,0 +1,326 @@ +/* + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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. + */ + +package org.openeuler.security.openssl; + +import sun.security.util.Debug; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.Provider; +import static sun.security.util.SecurityConstants.PROVIDER_VER; + +/** + * KAE Provider + */ +@SuppressWarnings("serial") +public class KAEProvider extends Provider { + private static final Debug kaeDebug = Debug.getInstance("kae"); + + // default engine id + private static final String DEFAULT_ENGINE_ID = "kae"; + + static { + initialize(); + } + + private static void initialize() { + loadLibrary(); + initOpenssl(); + } + + // load kae.so + @SuppressWarnings("removal") + private static void loadLibrary() { + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Object run() { + System.loadLibrary("j2kae"); + return null; + } + }); + } + + // init openssl + private static void initOpenssl() { + boolean useGlobalMode = useGlobalMode(); + String engineId = getEngineId(); + boolean[] algorithmKaeFlags = KAEConfig.getUseKaeEngineFlags(); + Throwable throwable = null; + try { + initOpenssl(useGlobalMode, engineId, algorithmKaeFlags); + } catch (Throwable t) { + throwable = t; + if (kaeDebug != null) { + kaeDebug.println("initOpenssl failed : " + throwable.getMessage()); + } + } + boolean[] engineFlags = getEngineFlags(); + boolean[] kaeProviderFlags = KAEConfig.getUseKaeProviderFlags(); + KAELog.log(engineId, throwable, engineFlags, kaeProviderFlags); + } + + // get engine id + private static String getEngineId() { + return KAEConfig.privilegedGetOverridable("kae.engine.id", DEFAULT_ENGINE_ID); + } + + // whether to set libcrypto.so to GLOBAL mode, by default libcrypto.so is LOCAL mode + private static boolean useGlobalMode() { + String explicitLoad = KAEConfig.privilegedGetOverridable( + "kae.libcrypto.useGlobalMode", "false"); + return Boolean.parseBoolean(explicitLoad); + } + + @SuppressWarnings("deprecation") + public KAEProvider() { + super("KAEProvider", PROVIDER_VER, "KAE provider"); + if (KAEConfig.useKaeProvider("kae.md5")) { + putMD5(); + } + if (KAEConfig.useKaeProvider("kae.sha256")) { + putSHA256(); + } + if (KAEConfig.useKaeProvider("kae.sha384")) { + putSHA384(); + } + if (KAEConfig.useKaeProvider("kae.sm3")) { + putSM3(); + } + if (KAEConfig.useKaeProvider("kae.aes")) { + putAES(); + } + if (KAEConfig.useKaeProvider("kae.sm4")) { + putSM4(); + } + if (KAEConfig.useKaeProvider("kae.hmac")) { + putHMAC(); + } + if (KAEConfig.useKaeProvider("kae.rsa")) { + putRSA(); + putSignatureRSA(); + } + if (KAEConfig.useKaeProvider("kae.dh")) { + putDH(); + } + if (KAEConfig.useKaeProvider("kae.ec")) { + putEC(); + } + } + + private void putAES() { + final String blockModes = "ECB|CBC|CTR|GCM"; + final String blockPads = "NOPADDING|PKCS5PADDING"; + + put("Cipher.AES SupportedModes", blockModes); + put("Cipher.AES SupportedPaddings", blockPads); + put("Cipher.AES", "org.openeuler.security.openssl.KAEAESCipher$Aes$Ecb$PKCS5Padding"); + + put("Cipher.AES/CBC/PKCS5Padding", "org.openeuler.security.openssl.KAEAESCipher$Aes$Cbc$PKCS5Padding"); + put("Cipher.AES/CBC/NoPadding", "org.openeuler.security.openssl.KAEAESCipher$Aes$Cbc$NoPadding"); + put("Alg.Alias.Cipher.AES/CBC/PKCS7Padding", "AES/CBC/PKCS5Padding"); + put("Cipher.AES/ECB/NoPadding", "org.openeuler.security.openssl.KAEAESCipher$Aes$Ecb$NoPadding"); + put("Cipher.AES/ECB/PKCS5Padding", "org.openeuler.security.openssl.KAEAESCipher$Aes$Ecb$PKCS5Padding"); + put("Alg.Alias.Cipher.AES/ECB/PKCS7Padding", "AES/ECB/PKCS5Padding"); + put("Cipher.AES/CTR/NoPadding", "org.openeuler.security.openssl.KAEAESCipher$Aes$Ctr$NoPadding"); + put("Cipher.AES/GCM/NoPadding", "org.openeuler.security.openssl.KAEAESCipher$Aes$Gcm$NoPadding"); + put("Alg.Alias.Cipher.AES/GCM/PKCS5Padding", "AES/GCM/NoPadding"); // PKCS5Padding -> noPadding + + put("Cipher.AES_128/CBC/PKCS5Padding", "org.openeuler.security.openssl.KAEAESCipher$Aes_128$Cbc$PKCS5Padding"); + put("Cipher.AES_128/CBC/NoPadding", "org.openeuler.security.openssl.KAEAESCipher$Aes_128$Cbc$NoPadding"); + put("Alg.Alias.Cipher.AES_128/CBC/PKCS7Padding", "AES_128/CBC/PKCS5Padding"); + put("Cipher.AES_128/ECB/NoPadding", "org.openeuler.security.openssl.KAEAESCipher$Aes_128$Ecb$NoPadding"); + put("Cipher.AES_128/ECB/PKCS5Padding", "org.openeuler.security.openssl.KAEAESCipher$Aes_128$Ecb$PKCS5Padding"); + put("Alg.Alias.Cipher.AES_128/ECB/PKCS7Padding", "AES_128/ECB/PKCS5Padding"); + put("Cipher.AES_128/CTR/NoPadding", "org.openeuler.security.openssl.KAEAESCipher$Aes_128$Ctr$NoPadding"); + put("Cipher.AES_128/GCM/NoPadding", "org.openeuler.security.openssl.KAEAESCipher$Aes_128$Gcm$NoPadding"); + put("Alg.Alias.Cipher.AES_128/GCM/PKCS5Padding", "AES/GCM/NoPadding"); + + put("Cipher.AES_192/CBC/PKCS5Padding", "org.openeuler.security.openssl.KAEAESCipher$Aes_192$Cbc$PKCS5Padding"); + put("Cipher.AES_192/CBC/NoPadding", "org.openeuler.security.openssl.KAEAESCipher$Aes_192$Cbc$NoPadding"); + put("Alg.Alias.Cipher.AES_192/CBC/PKCS7Padding", "AES_192/CBC/PKCS5Padding"); + put("Cipher.AES_192/ECB/NoPadding", "org.openeuler.security.openssl.KAEAESCipher$Aes_192$Ecb$NoPadding"); + put("Cipher.AES_192/ECB/PKCS5Padding", "org.openeuler.security.openssl.KAEAESCipher$Aes_192$Ecb$PKCS5Padding"); + put("Alg.Alias.Cipher.AES_192/ECB/PKCS7Padding", "AES_192/ECB/PKCS5Padding"); + put("Cipher.AES_192/CTR/NoPadding", "org.openeuler.security.openssl.KAEAESCipher$Aes_192$Ctr$NoPadding"); + put("Cipher.AES_192/GCM/NoPadding", "org.openeuler.security.openssl.KAEAESCipher$Aes_192$Gcm$NoPadding"); + put("Alg.Alias.Cipher.AES_192/GCM/PKCS5Padding", "AES/GCM/NoPadding"); + + put("Cipher.AES_256/CBC/PKCS5Padding", "org.openeuler.security.openssl.KAEAESCipher$Aes_256$Cbc$PKCS5Padding"); + put("Cipher.AES_256/CBC/NoPadding", "org.openeuler.security.openssl.KAEAESCipher$Aes_256$Cbc$NoPadding"); + put("Alg.Alias.Cipher.AES_256/CBC/PKCS7Padding", "AES_256/CBC/PKCS5Padding"); + put("Cipher.AES_256/ECB/NoPadding", "org.openeuler.security.openssl.KAEAESCipher$Aes_256$Ecb$NoPadding"); + put("Cipher.AES_256/ECB/PKCS5Padding", "org.openeuler.security.openssl.KAEAESCipher$Aes_256$Ecb$PKCS5Padding"); + put("Alg.Alias.Cipher.AES_256/ECB/PKCS7Padding", "AES_256/ECB/PKCS5Padding"); + put("Cipher.AES_256/CTR/NoPadding", "org.openeuler.security.openssl.KAEAESCipher$Aes_256$Ctr$NoPadding"); + put("Cipher.AES_256/GCM/NoPadding", "org.openeuler.security.openssl.KAEAESCipher$Aes_256$Gcm$NoPadding"); + put("Alg.Alias.Cipher.AES_256/GCM/PKCS5Padding", "AES/GCM/NoPadding"); + } + + private void putMD5() { + put("MessageDigest.MD5", "org.openeuler.security.openssl.KAEDigest$MD5"); + } + + private void putSHA256() { + put("MessageDigest.SHA-256", "org.openeuler.security.openssl.KAEDigest$SHA256"); + } + + private void putSHA384() { + put("MessageDigest.SHA-384", "org.openeuler.security.openssl.KAEDigest$SHA384"); + } + + private void putSM3() { + put("MessageDigest.SM3", "org.openeuler.security.openssl.KAEDigest$SM3"); + } + + private void putSM4() { + final String blockModes = "ECB|CBC|CTR|OFB"; + final String blockPads = "NOPADDING|PKCS5PADDING"; + + put("Cipher.SM4 SupportedModes", blockModes); + put("Cipher.SM4 SupportedPaddings", blockPads); + put("Cipher.SM4", "org.openeuler.security.openssl.KAESM4Cipher$Sm4$Ecb$PKCS5Padding"); + + put("Cipher.SM4/CBC/PKCS5Padding", "org.openeuler.security.openssl.KAESM4Cipher$Sm4$Cbc$PKCS5Padding"); + put("Cipher.SM4/CBC/NoPadding", "org.openeuler.security.openssl.KAESM4Cipher$Sm4$Cbc$NoPadding"); + put("Alg.Alias.Cipher.SM4/CBC/PKCS7Padding", "SM4/CBC/PKCS5Padding"); + put("Cipher.SM4/ECB/NoPadding", "org.openeuler.security.openssl.KAESM4Cipher$Sm4$Ecb$NoPadding"); + put("Cipher.SM4/ECB/PKCS5Padding", "org.openeuler.security.openssl.KAESM4Cipher$Sm4$Ecb$PKCS5Padding"); + put("Alg.Alias.Cipher.SM4/ECB/PKCS7Padding", "SM4/ECB/PKCS5Padding"); + put("Cipher.SM4/CTR/NoPadding", "org.openeuler.security.openssl.KAESM4Cipher$Sm4$Ctr$NoPadding"); + put("Cipher.SM4/OFB/NoPadding", "org.openeuler.security.openssl.KAESM4Cipher$Sm4$Ofb$NoPadding"); + put("Cipher.SM4/OFB/PKCS5Padding", "org.openeuler.security.openssl.KAESM4Cipher$Sm4$Ofb$PKCS5Padding"); + put("Alg.Alias.Cipher.SM4/OFB/PKCS7Padding", "SM4/OFB/PKCS5Padding"); + + put("KeyGenerator.SM4", "org.openeuler.security.openssl.KAESM4KeyGenerator"); + put("AlgorithmParameters.SM4", "com.sun.crypto.provider.AESParameters"); + } + + private void putRSA() { + // rsa + put("KeyPairGenerator.RSA", "org.openeuler.security.openssl.KAERSAKeyPairGenerator$Legacy"); + put("Alg.Alias.KeyPairGenerator.1.2.840.113549.1.1", "RSA"); + put("Alg.Alias.KeyPairGenerator.OID.1.2.840.113549.1.1", "RSA"); + + put("KeyPairGenerator.RSASSA-PSS", "org.openeuler.security.openssl.KAERSAKeyPairGenerator$PSS"); + put("Alg.Alias.KeyPairGenerator.1.2.840.113549.1.1.10", "RSASSA-PSS"); + put("Alg.Alias.KeyPairGenerator.OID.1.2.840.113549.1.1.10", "RSASSA-PSS"); + + put("Cipher.RSA", "org.openeuler.security.openssl.KAERSACipher"); + put("Cipher.RSA SupportedModes", "ECB"); + put("Cipher.RSA SupportedPaddings", + "NOPADDING|PKCS1PADDING|OAEPPADDING" + + "|OAEPWITHMD5ANDMGF1PADDING" + + "|OAEPWITHSHA1ANDMGF1PADDING" + + "|OAEPWITHSHA-1ANDMGF1PADDING" + + "|OAEPWITHSHA-224ANDMGF1PADDING" + + "|OAEPWITHSHA-256ANDMGF1PADDING" + + "|OAEPWITHSHA-384ANDMGF1PADDING" + + "|OAEPWITHSHA-512ANDMGF1PADDING" + + "|OAEPWITHSHA-512/224ANDMGF1PADDING" + + "|OAEPWITHSHA-512/256ANDMGF1PADDING"); + put("Cipher.RSA SupportedKeyClasses", + "java.security.interfaces.RSAPublicKey" + + "|java.security.interfaces.RSAPrivateKey"); + } + + private void putHMAC() { + put("MAC.HmacMD5", "org.openeuler.security.openssl.KAEHMac$HmacMD5"); + put("MAC.HmacSHA1", "org.openeuler.security.openssl.KAEHMac$HmacSHA1"); + put("MAC.HmacSHA224", "org.openeuler.security.openssl.KAEHMac$HmacSHA224"); + put("MAC.HmacSHA256", "org.openeuler.security.openssl.KAEHMac$HmacSHA256"); + put("MAC.HmacSHA384", "org.openeuler.security.openssl.KAEHMac$HmacSHA384"); + put("MAC.HmacSHA512", "org.openeuler.security.openssl.KAEHMac$HmacSHA512"); + } + + private void putDH() { + put("KeyPairGenerator.DiffieHellman", "org.openeuler.security.openssl.KAEDHKeyPairGenerator"); + put("Alg.Alias.KeyPairGenerator.DH", "DiffieHellman"); + put("KeyAgreement.DiffieHellman", "org.openeuler.security.openssl.KAEDHKeyAgreement"); + put("Alg.Alias.KeyAgreement.DH", "DiffieHellman"); + } + + private void putSignatureRSA() { + put("Signature.MD5withRSA", + "org.openeuler.security.openssl.KAERSASignature$MD5withRSA"); + put("Signature.SHA1withRSA", + "org.openeuler.security.openssl.KAERSASignature$SHA1withRSA"); + put("Signature.SHA224withRSA", + "org.openeuler.security.openssl.KAERSASignature$SHA224withRSA"); + put("Signature.SHA256withRSA", + "org.openeuler.security.openssl.KAERSASignature$SHA256withRSA"); + put("Signature.SHA384withRSA", + "org.openeuler.security.openssl.KAERSASignature$SHA384withRSA"); + put("Signature.SHA512withRSA", + "org.openeuler.security.openssl.KAERSASignature$SHA512withRSA"); + + // alias + put("Alg.Alias.Signature.1.2.840.113549.1.1.4", "MD5withRSA"); + put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.4", "MD5withRSA"); + + put("Alg.Alias.Signature.1.2.840.113549.1.1.5", "SHA1withRSA"); + put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.5", "SHA1withRSA"); + put("Alg.Alias.Signature.1.3.14.3.2.29", "SHA1withRSA"); + + put("Alg.Alias.Signature.1.2.840.113549.1.1.14", "SHA224withRSA"); + put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.14", "SHA224withRSA"); + + put("Alg.Alias.Signature.1.2.840.113549.1.1.11", "SHA256withRSA"); + put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.11", "SHA256withRSA"); + + put("Alg.Alias.Signature.1.2.840.113549.1.1.12", "SHA384withRSA"); + put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.12", "SHA384withRSA"); + + put("Alg.Alias.Signature.1.2.840.113549.1.1.13", "SHA512withRSA"); + put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.13", "SHA512withRSA"); + + put("Signature.RSASSA-PSS", "org.openeuler.security.openssl.KAERSAPSSSignature"); + + put("Alg.Alias.Signature.1.2.840.113549.1.1.10", "RSASSA-PSS"); + put("Alg.Alias.Signature.OID.1.2.840.113549.1.1.10", "RSASSA-PSS"); + + // attributes for supported key classes + String rsaKeyClasses = "java.security.interfaces.RSAPublicKey" + + "|java.security.interfaces.RSAPrivateKey"; + put("Signature.MD5withRSA SupportedKeyClasses", rsaKeyClasses); + put("Signature.SHA1withRSA SupportedKeyClasses", rsaKeyClasses); + put("Signature.SHA224withRSA SupportedKeyClasses", rsaKeyClasses); + put("Signature.SHA256withRSA SupportedKeyClasses", rsaKeyClasses); + put("Signature.SHA384withRSA SupportedKeyClasses", rsaKeyClasses); + put("Signature.SHA512withRSA SupportedKeyClasses", rsaKeyClasses); + put("Signature.RSASSA-PSS SupportedKeyClasses", rsaKeyClasses); + } + + private void putEC() { + put("KeyPairGenerator.EC", "org.openeuler.security.openssl.KAEECKeyPairGenerator"); + put("Alg.Alias.KeyPairGenerator.EllipticCurve", "EC"); + put("KeyAgreement.ECDH", "org.openeuler.security.openssl.KAEECDHKeyAgreement"); + } + + // init openssl + static native void initOpenssl(boolean useGlobalMode, String engineId, boolean[] algorithmKaeFlags) + throws RuntimeException; + + static native boolean[] getEngineFlags(); +} \ No newline at end of file diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSACipher.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSACipher.java new file mode 100644 index 000000000..a45fb0dac --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSACipher.java @@ -0,0 +1,796 @@ +/* + * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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 org.openeuler.security.openssl; + +import sun.security.internal.spec.TlsRsaPremasterSecretParameterSpec; +import sun.security.jca.Providers; +import sun.security.rsa.RSACore; +import sun.security.rsa.RSAKeyFactory; +import sun.security.rsa.RSAPadding; +import sun.security.util.KeyUtil; + +import javax.crypto.*; +import javax.crypto.spec.OAEPParameterSpec; +import javax.crypto.spec.PSource; +import java.lang.ref.PhantomReference; +import java.lang.ref.ReferenceQueue; +import java.security.*; +import java.security.interfaces.RSAKey; +import java.security.interfaces.RSAPrivateCrtKey; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidParameterSpecException; +import java.security.spec.MGF1ParameterSpec; +import java.util.Arrays; +import java.util.Locale; +import java.util.Set; +import java.util.concurrent.ConcurrentSkipListSet; + + +/** + * RSA cipher implementation. Supports RSA en/decryption and signing/verifying + * using both PKCS#1 v1.5 and OAEP (v2.2) paddings and without padding (raw RSA). + * Note that raw RSA is supported mostly for completeness and should only be + * used in rare cases. + *

+ * Objects should be instantiated by calling Cipher.getInstance() using the + * following algorithm names: + * . "RSA/ECB/PKCS1Padding" (or "RSA") for PKCS#1 v1.5 padding. + * . "RSA/ECB/OAEPwithandMGF1Padding" (or "RSA/ECB/OAEPPadding") for + * PKCS#1 v2.2 padding. + * . "RSA/ECB/NoPadding" for rsa RSA. + *

+ * We only do one RSA operation per doFinal() call. If the application passes + * more data via calls to update() or doFinal(), we throw an + * IllegalBlockSizeException when doFinal() is called (see JCE API spec). + * Bulk encryption using RSA does not make sense and is not standardized. + *

+ * Note: RSA keys should be at least 512 bits long + */ +public final class KAERSACipher extends CipherSpi { + // constant for an empty byte array + private final static byte[] B0 = new byte[0]; + + // mode constant for public key encryption + private final static int MODE_ENCRYPT = 1; + + // mode constant for private key decryption + private final static int MODE_DECRYPT = 2; + + // mode constant for private key encryption (signing) + private final static int MODE_SIGN = 3; + + // mode constant for public key decryption (verifying) + private final static int MODE_VERIFY = 4; + + // current mode, one of MODE_* above. Set when init() is called + private int mode; + + // active padding type, one of PAD_* above. Set by setPadding() + private KAERSAPaddingType paddingType; + + // padding object + private RSAPadding padding; + + // cipher parameter for OAEP padding and TLS RSA premaster secret + private AlgorithmParameterSpec spec = null; + + // buffer for the data + private byte[] buffer; + + // offset into the buffer (number of bytes buffered) + private int bufOfs; + + // size of the output + private int outputSize; + + // hash algorithm for OAEP + private String oaepHashAlgorithm = "SHA-1"; + + // the source of randomness + private SecureRandom random; + + private RSAKey rsaKey; + + // rsa key holder + private KAERSAKeyHolder rsaKeyHolder; + + + public KAERSACipher() { + paddingType = KAERSAPaddingType.PKCS1Padding; + } + + // modes do not make sense for RSA, but allow ECB + // see JCE spec + @Override + protected void engineSetMode(String mode) throws NoSuchAlgorithmException { + if (!mode.equalsIgnoreCase("ECB")) { + throw new NoSuchAlgorithmException("Unsupported mode " + mode); + } + } + + // set the padding type + // see JCE spec + @Override + protected void engineSetPadding(String paddingName) + throws NoSuchPaddingException { + if (KAERSAPaddingType.NoPadding.getName().equalsIgnoreCase(paddingName)) { + paddingType = KAERSAPaddingType.NoPadding; + } else if (KAERSAPaddingType.PKCS1Padding.getName().equalsIgnoreCase(paddingName)) { + paddingType = KAERSAPaddingType.PKCS1Padding; + } else { + String lowerPadding = paddingName.toLowerCase(Locale.ENGLISH); + if ("oaeppadding".equals(lowerPadding)) { + paddingType = KAERSAPaddingType.OAEP; + } else if (lowerPadding.startsWith("oaepwith") && + lowerPadding.endsWith("andmgf1padding")) { + paddingType = KAERSAPaddingType.OAEP; + // "oaepwith" length is 8 + // "andmgf1padding" length is 14 + oaepHashAlgorithm = + paddingName.substring(8, paddingName.length() - 14); + // check if MessageDigest appears to be available + // avoid getInstance() call here + if (Providers.getProviderList().getService + ("MessageDigest", oaepHashAlgorithm) == null) { + throw new NoSuchPaddingException + ("MessageDigest not available for " + paddingName); + } + } else { + throw new NoSuchPaddingException + ("Padding " + paddingName + " not supported"); + } + } + } + + // return 0 as block size, we are not a block cipher + // see JCE spec + @Override + protected int engineGetBlockSize() { + return 0; + } + + // return the output size + // see JCE spec + @Override + protected int engineGetOutputSize(int inputLen) { + return outputSize; + } + + // no iv, return null + // see JCE spec + @Override + protected byte[] engineGetIV() { + return null; + } + + // see JCE spec + @Override + protected AlgorithmParameters engineGetParameters() { + if (spec != null && spec instanceof OAEPParameterSpec) { + try { + AlgorithmParameters params = + AlgorithmParameters.getInstance("OAEP"); + params.init(spec); + return params; + } catch (NoSuchAlgorithmException nsae) { + // should never happen + throw new RuntimeException("Cannot find OAEP " + + " AlgorithmParameters implementation in SunJCE provider"); + } catch (InvalidParameterSpecException ipse) { + // should never happen + throw new RuntimeException("OAEPParameterSpec not supported"); + } + } else { + return null; + } + } + + // see JCE spec + @Override + protected void engineInit(int opmode, Key key, SecureRandom random) + throws InvalidKeyException { + try { + init(opmode, key, random, null); + } catch (InvalidAlgorithmParameterException iape) { + // never thrown when null parameters are used; + // but re-throw it just in case + throw new InvalidKeyException("Wrong parameters", iape); + } + } + + // see JCE spec + @Override + protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params, SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException { + init(opmode, key, random, params); + } + + // see JCE spec + @Override + protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException { + if (params == null) { + init(opmode, key, random, null); + } else { + try { + OAEPParameterSpec oaepParameterSpec = + params.getParameterSpec(OAEPParameterSpec.class); + init(opmode, key, random, oaepParameterSpec); + } catch (InvalidParameterSpecException ipse) { + InvalidAlgorithmParameterException iape = + new InvalidAlgorithmParameterException("Wrong parameter"); + iape.initCause(ipse); + throw iape; + } + } + } + + // check TlsRsaPremasterSecretParameterSpec + @SuppressWarnings("deprecation") + private void checkTlsRsaPremasterSecretParameterSpec(AlgorithmParameterSpec params) + throws InvalidAlgorithmParameterException { + if (!(params instanceof TlsRsaPremasterSecretParameterSpec)) { + throw new InvalidAlgorithmParameterException( + "Parameters not supported"); + } + } + + // check OAEPParameterSpec + private void checkOAEPParameterSpec(AlgorithmParameterSpec params) + throws InvalidAlgorithmParameterException { + if (!(params instanceof OAEPParameterSpec)) { + throw new InvalidAlgorithmParameterException + ("Wrong Parameters for OAEP Padding"); + } + + // check MGF algorithm + OAEPParameterSpec oaepParameterSpec = (OAEPParameterSpec) params; + String mgfName = oaepParameterSpec.getMGFAlgorithm(); + if (!mgfName.equalsIgnoreCase("MGF1")) { + throw new InvalidAlgorithmParameterException + ("Unsupported MGF algo: " + mgfName); + } + + // check PSource algorithm + PSource pSource = oaepParameterSpec.getPSource(); + String pSourceAlgorithm = pSource.getAlgorithm(); + if (!pSourceAlgorithm.equalsIgnoreCase("PSpecified")) { + throw new InvalidAlgorithmParameterException + ("Unsupported pSource algo: " + pSourceAlgorithm); + } + } + + // compute OAEP data buffer length + private int getOAEPBufferLen(int outputSize, OAEPParameterSpec oaepParameterSpec, boolean encrypt) + throws InvalidKeyException { + if (!encrypt) { + return outputSize; + } + String mdName = oaepParameterSpec.getDigestAlgorithm(); + String mgfMdName = ((MGF1ParameterSpec) oaepParameterSpec.getMGFParameters()) + .getDigestAlgorithm(); + int digestLen = KAEUtils.getDigestLength(mdName); + int bufferLen = outputSize - 2 - 2 * digestLen; + if (bufferLen < 0) { + throw new InvalidKeyException + ("Key is too short for encryption using OAEPPadding" + + " with " + mdName + " and MGF1" + mgfMdName); + } + return bufferLen; + } + + // non-CRT private key, use the jdk soft calculation. + private boolean useJdkSoftCalculation() { + return (rsaKey instanceof RSAPrivateKey) && !(rsaKey instanceof RSAPrivateCrtKey); + } + + // get the rsa padding + private RSAPadding getRSAPadding(KAERSAPaddingType paddingType, int paddedSize, + SecureRandom random, AlgorithmParameterSpec spec) + throws InvalidKeyException, InvalidAlgorithmParameterException { + RSAPadding rsaPadding; + if (KAERSAPaddingType.NoPadding.equals(paddingType)) { + rsaPadding = RSAPadding.getInstance(RSAPadding.PAD_NONE, paddedSize, random); + } else if (KAERSAPaddingType.PKCS1Padding.equals(paddingType)) { + int blockType = (mode <= MODE_DECRYPT) ? RSAPadding.PAD_BLOCKTYPE_2 + : RSAPadding.PAD_BLOCKTYPE_1; + rsaPadding = RSAPadding.getInstance(blockType, paddedSize, random); + } else { + rsaPadding = RSAPadding.getInstance(RSAPadding.PAD_OAEP_MGF1, paddedSize, + random, (OAEPParameterSpec) spec); + } + return rsaPadding; + } + + private boolean isEncrypt(int opmode) throws InvalidKeyException { + boolean encrypt; + switch (opmode) { + case Cipher.ENCRYPT_MODE: + case Cipher.WRAP_MODE: + encrypt = true; + break; + case Cipher.DECRYPT_MODE: + case Cipher.UNWRAP_MODE: + encrypt = false; + break; + default: + throw new InvalidKeyException("Unknown mode: " + opmode); + } + return encrypt; + } + + // initialize this cipher + private void init(int opmode, Key key, SecureRandom random, AlgorithmParameterSpec params) + throws InvalidKeyException, InvalidAlgorithmParameterException { + // check the key, and convert to RSAKey + rsaKey = RSAKeyFactory.toRSAKey(key); + + // init mode + boolean encrypt = isEncrypt(opmode); + if (key instanceof RSAPublicKey) { + mode = encrypt ? MODE_ENCRYPT : MODE_VERIFY; + } else { + mode = encrypt ? MODE_SIGN : MODE_DECRYPT; + } + + int bufferLen = RSACore.getByteLength(rsaKey.getModulus()); + outputSize = bufferLen; + bufOfs = 0; + if (KAERSAPaddingType.PKCS1Padding.equals(paddingType)) { + if (params != null) { + checkTlsRsaPremasterSecretParameterSpec(params); + spec = params; + this.random = random; // for TLS RSA premaster secret + } + if (encrypt) { + bufferLen -= 11; + } + } else if (KAERSAPaddingType.OAEP.equals(paddingType)) { + if ((mode == MODE_SIGN) || (mode == MODE_VERIFY)) { + throw new InvalidKeyException + ("OAEP cannot be used to sign or verify signatures"); + } + if (params != null) { + checkOAEPParameterSpec(params); + spec = params; + } else { + spec = new OAEPParameterSpec(oaepHashAlgorithm, "MGF1", + MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT); + } + bufferLen = getOAEPBufferLen(bufferLen, (OAEPParameterSpec) spec, encrypt); + } + buffer = new byte[bufferLen]; + + if (useJdkSoftCalculation()) { + this.padding = getRSAPadding(paddingType, outputSize, random, spec); + } + } + + // internal update method + private void update(byte[] in, int inOfs, int inLen) { + if ((inLen == 0) || (in == null)) { + return; + } + if (inLen > (buffer.length - bufOfs)) { + bufOfs = buffer.length + 1; + return; + } + System.arraycopy(in, inOfs, buffer, bufOfs, inLen); + bufOfs += inLen; + } + + // encrypt or decrypt for NoPadding or PKCS1Padding + private int doCryptNotOAEPPadding(long keyAddress, byte[] input, byte[] output) throws BadPaddingException { + int resultSize; + switch (mode) { + case MODE_SIGN: + resultSize = nativeRSAPrivateEncrypt(keyAddress, input.length, input, output, paddingType.getId()); + break; + case MODE_VERIFY: + resultSize = nativeRSAPublicDecrypt(keyAddress, input.length, input, output, paddingType.getId()); + break; + case MODE_ENCRYPT: + resultSize = nativeRSAPublicEncrypt(keyAddress, input.length, input, output, paddingType.getId()); + break; + case MODE_DECRYPT: + resultSize = nativeRSAPrivateDecrypt(keyAddress, input.length, input, output, paddingType.getId()); + break; + default: + throw new AssertionError("Internal error"); + } + return resultSize; + } + + + // encrypt or decrypt for OAEPPadding + private int doCryptOAEPPadding(long keyAddress, byte[] input, byte[] output, OAEPParameterSpec oaepParameterSpec) + throws BadPaddingException { + // oaep digest algorithm + String oaepMdAlgorithm = KAEUtils.getKAEDigestName(oaepParameterSpec.getDigestAlgorithm()); + // mgf1 digest algorithm + MGF1ParameterSpec mgf1ParameterSpec = (MGF1ParameterSpec) oaepParameterSpec.getMGFParameters(); + String mgf1MdAlgorithm = KAEUtils.getKAEDigestName(mgf1ParameterSpec.getDigestAlgorithm()); + // label + PSource pSource = oaepParameterSpec.getPSource(); + byte[] label = ((PSource.PSpecified) pSource).getValue(); + int resultSize; + switch (mode) { + case MODE_ENCRYPT: + resultSize = nativeRSAEncryptOAEPPadding(keyAddress, input.length, input, output, paddingType.getId(), + oaepMdAlgorithm, mgf1MdAlgorithm, label); + break; + case MODE_DECRYPT: + resultSize = nativeRSADecryptOAEPPadding(keyAddress, input.length, input, output, paddingType.getId(), + oaepMdAlgorithm, mgf1MdAlgorithm, label); + break; + default: + throw new AssertionError("Internal error"); + } + return resultSize; + } + + // get input bytes + private byte[] getInputBytes(byte[] buffer, int bufOfs, KAERSAPaddingType paddingType) { + if (bufOfs == buffer.length) { + return buffer; + } + + // if padding type is NoPadding , data should move to end + final byte[] input; + if (KAERSAPaddingType.NoPadding.equals(paddingType)) { + input = new byte[buffer.length]; + System.arraycopy(buffer, 0, input, buffer.length - bufOfs, bufOfs); + } else { + input = Arrays.copyOf(buffer, bufOfs); + } + return input; + } + + // internal doFinal() method. Here we perform the actual RSA operation + private byte[] doFinal() throws BadPaddingException, IllegalBlockSizeException { + if (bufOfs > buffer.length) { + throw new IllegalBlockSizeException("Data must not be longer " + + "than " + buffer.length + " bytes"); + } + + if (useJdkSoftCalculation()) { + return doFinalForJdkSoftCalculation(padding); + } + + // get input bytes + final byte[] input = getInputBytes(buffer, bufOfs, paddingType); + + try { + rsaKeyHolder = new KAERSAKeyHolder(this, rsaKey); + } catch (InvalidKeyException e) { + throw new RuntimeException(e.getMessage()); + } + + long keyAddress = rsaKeyHolder.keyAddress; + byte[] output = new byte[outputSize]; + int cipherTextLength; + try { + if (KAERSAPaddingType.OAEP.equals(paddingType)) { + // do crypt for OAEPPadding + cipherTextLength = doCryptOAEPPadding(keyAddress, input, output, (OAEPParameterSpec) spec); + } else { + // do crypt for NoPadding or PKCS1Padding + cipherTextLength = doCryptNotOAEPPadding(keyAddress, input, output); + } + + // If mode is signing or verifying , and the length of the ciphertext is less than output length, + // just keep output length ciphertext. + if ((mode == MODE_VERIFY || mode == MODE_DECRYPT) && cipherTextLength != output.length) { + output = Arrays.copyOf(output, cipherTextLength); + } + } finally { + bufOfs = 0; + resetKeyHolder(); + } + return output; + } + + private byte[] doFinalForJdkSoftCalculation(RSAPadding padding) throws BadPaddingException { + try { + byte[] data; + switch (mode) { + case MODE_SIGN: + data = padding.pad(buffer, 0, bufOfs); + return RSACore.rsa(data, (RSAPrivateKey) rsaKey, true); + case MODE_DECRYPT: + byte[] decryptBuffer = RSACore.convert(buffer, 0, bufOfs); + data = RSACore.rsa(decryptBuffer, (RSAPrivateKey) rsaKey, false); + return padding.unpad(data); + default: + throw new AssertionError("Internal error"); + } + } finally { + bufOfs = 0; + } + } + + // see JCE spec + @Override + protected byte[] engineUpdate(byte[] in, int inOfs, int inLen) { + update(in, inOfs, inLen); + return B0; + } + + // see JCE spec + @Override + protected int engineUpdate(byte[] in, int inOfs, int inLen, byte[] out, + int outOfs) { + update(in, inOfs, inLen); + return 0; + } + + // see JCE spec + @Override + protected byte[] engineDoFinal(byte[] in, int inOfs, int inLen) + throws BadPaddingException, IllegalBlockSizeException { + update(in, inOfs, inLen); + return doFinal(); + } + + // see JCE spec + @Override + protected int engineDoFinal(byte[] in, int inOfs, int inLen, byte[] out, int outOfs) + throws ShortBufferException, BadPaddingException, IllegalBlockSizeException { + if (outputSize > out.length - outOfs) { + throw new ShortBufferException + ("Need " + outputSize + " bytes for output"); + } + update(in, inOfs, inLen); + byte[] result = doFinal(); + int length = result.length; + System.arraycopy(result, 0, out, outOfs, length); + return length; + } + + // see JCE spec + @Override + protected byte[] engineWrap(Key key) throws InvalidKeyException, + IllegalBlockSizeException { + byte[] encoded = key.getEncoded(); + if ((encoded == null) || (encoded.length == 0)) { + throw new InvalidKeyException("Could not obtain encoded key"); + } + if (encoded.length > buffer.length) { + throw new InvalidKeyException("Key is too long for wrapping"); + } + update(encoded, 0, encoded.length); + try { + return doFinal(); + } catch (BadPaddingException e) { + // should not occur + throw new InvalidKeyException("Wrapping failed", e); + } + } + + // see JCE spec + @Override + @SuppressWarnings("deprecation") + protected Key engineUnwrap(byte[] wrappedKey, String algorithm, int type) + throws InvalidKeyException, NoSuchAlgorithmException { + if (wrappedKey.length > buffer.length) { + throw new InvalidKeyException("Key is too long for unwrapping"); + } + + boolean isTlsRsaPremasterSecret = "TlsRsaPremasterSecret".equals(algorithm); + Exception failover = null; + byte[] encoded = null; + + update(wrappedKey, 0, wrappedKey.length); + try { + encoded = doFinal(); + } catch (BadPaddingException e) { + if (isTlsRsaPremasterSecret) { + failover = e; + } else { + throw new InvalidKeyException("Unwrapping failed", e); + } + } catch (IllegalBlockSizeException e) { + // should not occur, handled with length check above + throw new InvalidKeyException("Unwrapping failed", e); + } + + if (isTlsRsaPremasterSecret) { + if (!(spec instanceof TlsRsaPremasterSecretParameterSpec)) { + throw new IllegalStateException( + "No TlsRsaPremasterSecretParameterSpec specified"); + } + + // polish the TLS premaster secret + encoded = KeyUtil.checkTlsPreMasterSecretKey( + ((TlsRsaPremasterSecretParameterSpec) spec).getClientVersion(), + ((TlsRsaPremasterSecretParameterSpec) spec).getServerVersion(), + random, encoded, (failover != null)); + } + return KAEUtils.ConstructKeys.constructKey(encoded, algorithm, type); + } + + // see JCE spec + @Override + protected int engineGetKeySize(Key key) throws InvalidKeyException { + RSAKey newRSAKey = RSAKeyFactory.toRSAKey(key); + return newRSAKey.getModulus().bitLength(); + } + + // reset the key holder + private void resetKeyHolder() { + if (rsaKeyHolder != null) { + rsaKeyHolder.dispose(true); + rsaKeyHolder = null; + } + } + + // create KAE rsa key + protected static native long nativeCreateRSAPrivateCrtKey(byte[] n, byte[] e, byte[] d, byte[] p, byte[] q, + byte[] dmp1, byte[] dmq1, byte[] iqmp); + + // create KAE rsa public key + protected static native long nativeCreateRSAPublicKey(byte[] n, byte[] e); + + // encrypt by private key for padding type (NOPADDING|PKCS1PADDING) + protected static native int nativeRSAPrivateEncrypt(long keyAddress, int inLen, byte[] in, byte[] out, + int paddingType) throws BadPaddingException; + + // decrypt by private key for padding type (NOPADDING|PKCS1PADDING) + protected static native int nativeRSAPrivateDecrypt(long keyAddress, int inLen, byte[] in, byte[] out, + int paddingType) throws BadPaddingException; + + // encrypt by public key for padding type (NOPADDING|PKCS1PADDING) + protected static native int nativeRSAPublicEncrypt(long keyAddress, int inLen, byte[] in, byte[] out, + int paddingType) throws BadPaddingException; + + // decrypt by public key for padding type (NOPADDING|PKCS1PADDING) + protected static native int nativeRSAPublicDecrypt(long keyAddress, int inLen, byte[] in, byte[] out, + int paddingType) throws BadPaddingException; + + // encrypt by public for padding type (OAEPPADDING) + protected static native int nativeRSAEncryptOAEPPadding(long keyAddress, int inLen, byte[] in, byte[] out, + int paddingType, String oaepMdAlgo, String mgf1MdAlgo, + byte[] label) throws BadPaddingException; + + // decrypt by public for padding type (OAEPPADDING) + protected static native int nativeRSADecryptOAEPPadding(long keyAddress, int inLen, byte[] in, byte[] out, + int paddingType, String oaepMdAlgo, String mgf1MdAlgo, + byte[] label) throws BadPaddingException; + + // free the key + protected static native void nativeFreeKey(long keyAddress); + + /** + * The rsa openssl key holder , use PhantomReference in case of native memory leaks + */ + private static class KAERSAKeyHolder extends PhantomReference + implements Comparable { + private static ReferenceQueue referenceQueue = new ReferenceQueue<>(); + private static Set referenceList = new ConcurrentSkipListSet<>(); + private final long keyAddress; + + KAERSAKeyHolder(KAERSACipher rsaCipher, RSAKey rsaKey) throws InvalidKeyException { + super(rsaCipher, referenceQueue); + this.keyAddress = getKeyAddress(rsaKey); + referenceList.add(this); + drainRefQueueBounded(); + } + + private static void drainRefQueueBounded() { + while (true) { + KAERSAKeyHolder next = (KAERSAKeyHolder) referenceQueue.poll(); + if (next == null) { + break; + } + next.dispose(true); + } + } + + void dispose(boolean needFree) { + referenceList.remove(this); + try { + if (needFree) { + nativeFreeKey(keyAddress); + } + } finally { + this.clear(); + } + } + + @Override + public int compareTo(KAERSAKeyHolder other) { + if (this.keyAddress == other.keyAddress) { + return 0; + } else { + return (this.keyAddress < other.keyAddress) ? -1 : 1; + } + } + + private long getKeyAddress(RSAKey rsaKey) throws InvalidKeyException { + long address; + if (rsaKey instanceof RSAPrivateCrtKey) { // RSAPrivateCrtKeyImpl + address = getKeyAddress((RSAPrivateCrtKey) rsaKey); + } else if (rsaKey instanceof RSAPublicKey) { // RSAPublicKeyImpl + address = getKeyAddress((RSAPublicKey) rsaKey); + } else { + throw new InvalidKeyException("Invalid RSAKey implement " + rsaKey.getClass()); + } + return address; + } + + private long getKeyAddress(RSAPrivateCrtKey key) throws InvalidKeyException { + checkKey(key); + try { + return nativeCreateRSAPrivateCrtKey( + key.getModulus().toByteArray(), + key.getPublicExponent().toByteArray(), + key.getPrivateExponent().toByteArray(), + key.getPrimeP().toByteArray(), + key.getPrimeQ().toByteArray(), + key.getPrimeExponentP().toByteArray(), + key.getPrimeExponentQ().toByteArray(), + key.getCrtCoefficient().toByteArray()); + } catch (Exception e) { + throw new InvalidKeyException(e); + } + } + + private long getKeyAddress(RSAPublicKey key) throws InvalidKeyException { + checkKey(key); + try { + return nativeCreateRSAPublicKey( + key.getModulus().toByteArray(), + key.getPublicExponent().toByteArray() + ); + } catch (Exception e) { + throw new InvalidKeyException(e); + } + } + + private void checkKey(RSAPrivateCrtKey key) throws InvalidKeyException { + boolean isInValidKey = key.getModulus() == null + || key.getPublicExponent() == null + || key.getPrivateExponent() == null + || key.getPrimeP() == null + || key.getPrimeQ() == null + || key.getPrimeExponentP() == null + || key.getPrimeExponentQ() == null + || key.getCrtCoefficient() == null; + if (isInValidKey) { + throw new InvalidKeyException("Invalid RSA private key"); + } + } + + private void checkKey(RSAPublicKey key) throws InvalidKeyException { + if (key.getModulus() == null || key.getPublicExponent() == null) { + throw new InvalidKeyException("Invalid RSA public key"); + } + } + } +} diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSAKeyPairGenerator.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSAKeyPairGenerator.java new file mode 100644 index 000000000..88bd30574 --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSAKeyPairGenerator.java @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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 org.openeuler.security.openssl; + +import sun.security.rsa.*; +import sun.security.rsa.RSAUtil.KeyType; +import sun.security.util.SecurityProviderConstants; + +import java.math.BigInteger; +import java.security.*; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.RSAKeyGenParameterSpec; + +public abstract class KAERSAKeyPairGenerator extends KeyPairGeneratorSpi { + // public exponent to use + private BigInteger publicExponent; + + // size of the key to generate, >= KAERSAKeyFactory.MIN_MODLEN + private int keySize; + + private final KeyType type; + + private AlgorithmParameterSpec keyParams; + + + KAERSAKeyPairGenerator(KeyType keyType, int keySize) { + this.type = keyType; + initialize(keySize, null); + } + + // initialize the generator. See JCA doc + @Override + public void initialize(int keySize, SecureRandom random) { + try { + initialize(new RSAKeyGenParameterSpec(keySize, + RSAKeyGenParameterSpec.F4), null); + } catch (InvalidAlgorithmParameterException iape) { + throw new InvalidParameterException(iape.getMessage()); + } + } + + // second initialize method. See JCA doc + @Override + public void initialize(AlgorithmParameterSpec params, SecureRandom random) + throws InvalidAlgorithmParameterException { + if (!(params instanceof RSAKeyGenParameterSpec)) { + throw new InvalidAlgorithmParameterException + ("Params must be instance of RSAKeyGenParameterSpec"); + } + + RSAKeyGenParameterSpec rsaSpec = (RSAKeyGenParameterSpec) params; + int tmpKeySize = rsaSpec.getKeysize(); + BigInteger tmpPublicExponent = rsaSpec.getPublicExponent(); + keyParams = rsaSpec.getKeyParams(); + + if (tmpPublicExponent == null) { + tmpPublicExponent = RSAKeyGenParameterSpec.F4; + } else { + if (tmpPublicExponent.compareTo(RSAKeyGenParameterSpec.F0) < 0) { + throw new InvalidAlgorithmParameterException + ("Public exponent must be 3 or larger"); + } + if (tmpPublicExponent.bitLength() > tmpKeySize) { + throw new InvalidAlgorithmParameterException + ("Public exponent must be smaller than key size"); + } + } + + // do not allow unreasonably large key sizes, probably user error + try { + RSAKeyFactory.checkKeyLengths(tmpKeySize, tmpPublicExponent, + 512, 64 * 1024); + } catch (InvalidKeyException e) { + throw new InvalidAlgorithmParameterException( + "Invalid key sizes", e); + } + + this.keySize = tmpKeySize; + this.publicExponent = tmpPublicExponent; + } + + // generate the keypair. See JCA doc + @Override + public KeyPair generateKeyPair() { + // get the KAE RSA key Parameters + byte[][] params = nativeGenerateKeyPair(keySize, publicExponent.toByteArray()); + + try { + // check KAE RSA key Parameters + checkKAERSAParams(params); + + BigInteger n = new BigInteger(params[0]); + BigInteger e = new BigInteger(params[1]); + BigInteger d = new BigInteger(params[2]); + BigInteger p = new BigInteger(params[3]); + BigInteger q = new BigInteger(params[4]); + BigInteger pe = new BigInteger(params[5]); + BigInteger qe = new BigInteger(params[6]); + BigInteger coeff = new BigInteger(params[7]); + + // public key + PublicKey publicKey = RSAPublicKeyImpl.newKey(type, keyParams, n, e); + + // private key + PrivateKey privateKey = RSAPrivateCrtKeyImpl.newKey(type, keyParams, n, e, d, p, q, pe, qe, coeff); + + return new KeyPair(publicKey, privateKey); + } catch (InvalidKeyException ex) { + throw new RuntimeException(ex); + } + } + + // check KAE RSA key Parameters + private void checkKAERSAParams(byte[][] params) throws InvalidKeyException { + if (params == null || params.length < 8) { + throw new InvalidKeyException("Invalid KAE RSA key Parameter"); + } + + for (int i = 0; i < params.length; i++) { + if (params[i] == null) { + throw new InvalidKeyException("Invalid KAE RSA key Parameter , params[" + i + "] = null"); + } + } + } + + public static final class Legacy extends KAERSAKeyPairGenerator { + public Legacy() { + super(KeyType.RSA, SecurityProviderConstants.DEF_RSA_KEY_SIZE); + } + } + + public static final class PSS extends KAERSAKeyPairGenerator { + public PSS() { + super(KeyType.PSS, SecurityProviderConstants.DEF_RSASSA_PSS_KEY_SIZE); + } + } + + // generate key pair + static native byte[][] nativeGenerateKeyPair(int keySize, byte[] publicExponent) throws RuntimeException; +} diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSAPSSSignature.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSAPSSSignature.java new file mode 100644 index 000000000..065317148 --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSAPSSSignature.java @@ -0,0 +1,683 @@ +/* + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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 org.openeuler.security.openssl; + +import java.io.IOException; +import java.nio.ByteBuffer; + +import java.security.*; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.PSSParameterSpec; +import java.security.spec.MGF1ParameterSpec; +import java.security.interfaces.*; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; + +import sun.security.rsa.PSSParameters; +import sun.security.rsa.RSACore; +import sun.security.jca.JCAUtil; + +import javax.crypto.BadPaddingException; + +/** + * PKCS#1 v2.2 RSASSA-PSS signatures with various message digest algorithms. + * RSASSA-PSS implementation takes the message digest algorithm, MGF algorithm, + * and salt length values through the required signature PSS parameters. + * We support SHA-1, SHA-224, SHA-256, SHA-384, SHA-512. + * The Openssl does not support rsa signatures with SHA-512/224 and SHA-512/256 as the digest algorithm, + * so we have not implemented them. + * The Openssl does not support non-CRT private key , when signing with a non-CRT private key, we use the sun sign. + */ +@SuppressWarnings({"deprecation", "removal"}) +public class KAERSAPSSSignature extends SignatureSpi { + // openssl unsupport rsa sign with digest algorithm + private static final Set UNSUPPORTED_DIGEST_ALGORITHM = new HashSet<>( + Arrays.asList("SHA-512/224", "SHA-512/256")); + + private static final byte[] EIGHT_BYTES_OF_ZEROS = new byte[8]; + + // message digest implementation we use for hashing the data + private MessageDigest md; + + // flag indicating whether the digest is reset + private boolean digestReset = true; + + // private key, if initialized for signing + private RSAPrivateKey privKey = null; + + // public key, if initialized for verifying + private RSAPublicKey pubKey = null; + + // PSS parameters from signatures and keys respectively + private PSSParameterSpec sigParams = null; + + // PRNG used to generate salt bytes if none given + private SecureRandom random; + + /** + * Construct a new RSAPSSSignatur with arbitrary digest algorithm + */ + public KAERSAPSSSignature() { + this.md = null; + } + + // initialize for verification. See JCA doc + @Override + protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException { + if (!(publicKey instanceof RSAPublicKey)) { + throw new InvalidKeyException("key must be RSAPublicKey"); + } + this.pubKey = (RSAPublicKey) isValid((RSAKey) publicKey); + this.privKey = null; + resetDigest(); + } + + // initialize for signing. See JCA doc + @Override + protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException { + engineInitSign(privateKey, null); + } + + // initialize for signing. See JCA doc + @Override + protected void engineInitSign(PrivateKey privateKey, SecureRandom random) throws InvalidKeyException { + if (!(privateKey instanceof RSAPrivateKey)) { + throw new InvalidKeyException("key must be RSAPrivateKey"); + } + this.privKey = (RSAPrivateKey) isValid((RSAKey) privateKey); + this.pubKey = null; + this.random = + (random == null ? JCAUtil.getSecureRandom() : random); + resetDigest(); + } + + /** + * Utility method for checking the key PSS parameters against signature + * PSS parameters. + * Returns false if any of the digest/MGF algorithms and trailerField + * values does not match or if the salt length in key parameters is + * larger than the value in signature parameters. + */ + private static boolean isCompatible(AlgorithmParameterSpec keyParams, PSSParameterSpec sigParams) { + if (keyParams == null) { + // key with null PSS parameters means no restriction + return true; + } + if (!(keyParams instanceof PSSParameterSpec)) { + return false; + } + // nothing to compare yet, defer the check to when sigParams is set + if (sigParams == null) { + return true; + } + PSSParameterSpec pssKeyParams = (PSSParameterSpec) keyParams; + // first check the salt length requirement + if (pssKeyParams.getSaltLength() > sigParams.getSaltLength()) { + return false; + } + + // compare equality of the rest of fields based on DER encoding + PSSParameterSpec keyParams2 = + new PSSParameterSpec(pssKeyParams.getDigestAlgorithm(), + pssKeyParams.getMGFAlgorithm(), + pssKeyParams.getMGFParameters(), + sigParams.getSaltLength(), + pssKeyParams.getTrailerField()); + + // skip the JCA overhead + try { + byte[] encoded = PSSParameters.getEncoded(keyParams2); + byte[] encoded2 = PSSParameters.getEncoded(sigParams); + return Arrays.equals(encoded, encoded2); + } catch (IOException e) { + return false; + } + } + + /** + * Validate the specified RSAKey and its associated parameters against + * internal signature parameters. + */ + private RSAKey isValid(RSAKey rsaKey) throws InvalidKeyException { + try { + AlgorithmParameterSpec keyParams = rsaKey.getParams(); + // validate key parameters + if (!isCompatible(rsaKey.getParams(), this.sigParams)) { + throw new InvalidKeyException + ("Key contains incompatible PSS parameter values"); + } + // validate key length + if (this.sigParams != null) { + Integer hLen = + KAEUtils.getDigestLength(this.sigParams.getDigestAlgorithm()); + if (hLen == null) { + throw new ProviderException("Unsupported digest algo: " + + this.sigParams.getDigestAlgorithm()); + } + checkKeyLength(rsaKey, hLen, this.sigParams.getSaltLength()); + } + return rsaKey; + } catch (SignatureException e) { + throw new InvalidKeyException(e); + } + } + + /** + * Validate the specified Signature PSS parameters. + */ + private PSSParameterSpec validateSigParams(AlgorithmParameterSpec p) throws InvalidAlgorithmParameterException { + if (p == null) { + throw new InvalidAlgorithmParameterException + ("Parameters cannot be null"); + } + if (!(p instanceof PSSParameterSpec)) { + throw new InvalidAlgorithmParameterException + ("parameters must be type PSSParameterSpec"); + } + // no need to validate again if same as current signature parameters + PSSParameterSpec params = (PSSParameterSpec) p; + if (params == this.sigParams) { + return params; + } + + RSAKey key = (this.privKey == null ? this.pubKey : this.privKey); + // check against keyParams if set + if (key != null) { + if (!isCompatible(key.getParams(), params)) { + throw new InvalidAlgorithmParameterException + ("Signature parameters does not match key parameters"); + } + } + // now sanity check the parameter values + if (!(params.getMGFAlgorithm().equalsIgnoreCase("MGF1"))) { + throw new InvalidAlgorithmParameterException("Only supports MGF1"); + } + if (params.getTrailerField() != PSSParameterSpec.TRAILER_FIELD_BC) { + throw new InvalidAlgorithmParameterException("Only supports TrailerFieldBC(1)"); + } + String digestAlgo = params.getDigestAlgorithm(); + // check key length again + if (key != null) { + try { + int hLen = KAEUtils.getDigestLength(digestAlgo); + checkKeyLength(key, hLen, params.getSaltLength()); + } catch (SignatureException e) { + throw new InvalidAlgorithmParameterException(e); + } + } + return params; + } + + /** + * Ensure the object is initialized with key and parameters and + * reset digest + */ + private void ensureInit() throws SignatureException { + RSAKey key = (this.privKey == null ? this.pubKey : this.privKey); + if (key == null) { + throw new SignatureException("Missing key"); + } + if (this.sigParams == null) { + // Parameters are required for signature verification + throw new SignatureException + ("Parameters required for RSASSA-PSS signatures"); + } + } + + /** + * Utility method for checking key length against digest length and + * salt length + */ + private static void checkKeyLength(RSAKey key, int digestLen, int saltLen) throws SignatureException { + if (key != null) { + int keyLength = getKeyLengthInBits(key) >> 3; + int minLength = Math.addExact(Math.addExact(digestLen, saltLen), 2); + if (keyLength < minLength) { + throw new SignatureException + ("Key is too short, need min " + minLength); + } + } + } + + /** + * Reset the message digest if it is not already reset. + */ + private void resetDigest() { + if (digestReset == false) { + this.md.reset(); + digestReset = true; + } + } + + /** + * Return the message digest value. + */ + private byte[] getDigestValue() { + digestReset = true; + return this.md.digest(); + } + + // update the signature with the plaintext data. See JCA doc + @Override + protected void engineUpdate(byte b) throws SignatureException { + ensureInit(); + this.md.update(b); + digestReset = false; + } + + // update the signature with the plaintext data. See JCA doc + @Override + protected void engineUpdate(byte[] b, int off, int len) throws SignatureException { + ensureInit(); + this.md.update(b, off, len); + digestReset = false; + } + + // update the signature with the plaintext data. See JCA doc + @Override + protected void engineUpdate(ByteBuffer b) { + try { + ensureInit(); + } catch (SignatureException se) { + // hack for working around API bug + throw new RuntimeException(se.getMessage()); + } + this.md.update(b); + digestReset = false; + } + + // determine whether the digest is valid + private boolean isValidDigest() { + String digestName = this.md.getAlgorithm(); + if (UNSUPPORTED_DIGEST_ALGORITHM.contains(digestName.toUpperCase(Locale.ROOT))) { + return false; + } + AlgorithmParameterSpec mgfParams = this.sigParams.getMGFParameters(); + String mgfDigestName = ""; + if (mgfParams != null) { + mgfDigestName = + ((MGF1ParameterSpec) mgfParams).getDigestAlgorithm(); + } + return !UNSUPPORTED_DIGEST_ALGORITHM.contains(mgfDigestName.toUpperCase(Locale.ROOT)); + } + + // determine whether use kae sign + private boolean useKaeSign() { + return (privKey instanceof RSAPrivateCrtKey) && isValidDigest(); + } + + // sun sign + private byte[] sunSign(byte[] mHash) throws SignatureException { + try { + byte[] encoded = encodeSignature(mHash); + return RSACore.rsa(encoded, privKey, true); + } catch (GeneralSecurityException e) { + throw new SignatureException("Could not sign data", e); + } catch (IOException e) { + throw new SignatureException("Could not encode data", e); + } + } + + // kae sign + private byte[] kaeSign(byte[] mHash) throws SignatureException { + String kaeDigestName = KAEUtils.getKAEDigestName(this.sigParams.getDigestAlgorithm()); + String mgfDigestName = kaeDigestName; + AlgorithmParameterSpec mgfParams = this.sigParams.getMGFParameters(); + if (mgfParams != null) { + mgfDigestName = + ((MGF1ParameterSpec) mgfParams).getDigestAlgorithm(); + } + String mgf1DigestName = KAEUtils.getKAEDigestName(mgfDigestName); + + RSAPrivateCrtKey privateCrtKey = (RSAPrivateCrtKey) privKey; + long keyAddress = KAERSACipher.nativeCreateRSAPrivateCrtKey( + privateCrtKey.getModulus().toByteArray(), + privateCrtKey.getPublicExponent().toByteArray(), + privateCrtKey.getPrivateExponent().toByteArray(), + privateCrtKey.getPrimeP().toByteArray(), + privateCrtKey.getPrimeQ().toByteArray(), + privateCrtKey.getPrimeExponentP().toByteArray(), + privateCrtKey.getPrimeExponentQ().toByteArray(), + privateCrtKey.getCrtCoefficient().toByteArray()); + byte[] bytes; + try { + bytes = KAERSASignatureNative.pssSign(keyAddress, kaeDigestName, mHash, + KAERSAPaddingType.PKCS1PssPadding.getId(), + mgf1DigestName, this.sigParams.getSaltLength()); + } catch (SignatureException e) { + throw e; + } finally { + KAERSACipher.nativeFreeKey(keyAddress); + } + return bytes; + } + + // sign the data and return the signature. See JCA doc + @Override + protected byte[] engineSign() throws SignatureException { + ensureInit(); + byte[] mHash = getDigestValue(); + if (useKaeSign()) { + return kaeSign(mHash); + } + return sunSign(mHash); + } + + // determine whether use kae verify + private boolean useKaeVerify() { + return isValidDigest(); + } + + // sun verify + private boolean sunVerify(byte[] mHash, byte[] sigBytes) throws SignatureException { + try { + byte[] decrypted = RSACore.rsa(sigBytes, this.pubKey); + return decodeSignature(mHash, decrypted); + } catch (javax.crypto.BadPaddingException e) { + // occurs if the app has used the wrong RSA public key + // or if sigBytes is invalid + // return false rather than propagating the exception for + // compatibility/ease of use + return false; + } catch (IOException e) { + throw new SignatureException("Signature encoding error", e); + } finally { + resetDigest(); + } + } + + // kae verify + private boolean kaeVerify(byte[] mHash, byte[] sigBytes) throws SignatureException { + String kaeDigestName = KAEUtils.getKAEDigestName(this.sigParams.getDigestAlgorithm()); + String mgfDigestName = kaeDigestName; + AlgorithmParameterSpec mgfParams = this.sigParams.getMGFParameters(); + if (mgfParams != null) { + mgfDigestName = + ((MGF1ParameterSpec) mgfParams).getDigestAlgorithm(); + } + String mgf1KaeDigestName = KAEUtils.getKAEDigestName(mgfDigestName); + + long keyAddress = KAERSACipher.nativeCreateRSAPublicKey(this.pubKey.getModulus().toByteArray(), + this.pubKey.getPublicExponent().toByteArray()); + boolean verify; + try { + verify = KAERSASignatureNative.pssVerify(keyAddress, kaeDigestName, mHash, + KAERSAPaddingType.PKCS1PssPadding.getId(), mgf1KaeDigestName, + this.sigParams.getSaltLength(), sigBytes); + } catch (SignatureException e) { + throw e; + } catch (BadPaddingException e) { + // occurs if the app has used the wrong RSA public key + // or if sigBytes is invalid or sourceBytes is invalid + // return false rather than propagating the exception for + // compatibility/ease of use + return false; + } finally { + resetDigest(); + KAERSACipher.nativeFreeKey(keyAddress); + } + return verify; + } + + // verify the data and return the result. See JCA doc + // should be reset to the state after engineInitVerify call. + @Override + protected boolean engineVerify(byte[] sigBytes) throws SignatureException { + ensureInit(); + if (sigBytes.length != RSACore.getByteLength(this.pubKey)) { + throw new SignatureException("Signature length not correct: got " + + sigBytes.length + " but was expecting " + + RSACore.getByteLength(this.pubKey)); + } + byte[] mHash = getDigestValue(); + if (useKaeVerify()) { + return kaeVerify(mHash, sigBytes); + } + return sunVerify(mHash, sigBytes); + } + + // return the modulus length in bits + private static int getKeyLengthInBits(RSAKey k) { + if (k != null) { + return k.getModulus().bitLength(); + } + return -1; + } + + /** + * Encode the digest 'mHash', return the to-be-signed data. + * Also used by the PKCS#11 provider. + */ + private byte[] encodeSignature(byte[] mHash) throws IOException, DigestException { + AlgorithmParameterSpec mgfParams = this.sigParams.getMGFParameters(); + String mgfDigestAlgo; + if (mgfParams != null) { + mgfDigestAlgo = + ((MGF1ParameterSpec) mgfParams).getDigestAlgorithm(); + } else { + mgfDigestAlgo = this.md.getAlgorithm(); + } + try { + int emBits = getKeyLengthInBits(this.privKey) - 1; + int emLen = (emBits + 7) >> 3; + int hLen = this.md.getDigestLength(); + int dbLen = emLen - hLen - 1; + int sLen = this.sigParams.getSaltLength(); + + // maps DB into the corresponding region of EM and + // stores its bytes directly into EM + byte[] em = new byte[emLen]; + + // step7 and some of step8 + em[dbLen - sLen - 1] = (byte) 1; // set DB's padding2 into EM + em[em.length - 1] = (byte) 0xBC; // set trailer field of EM + + if (!digestReset) { + throw new ProviderException("Digest should be reset"); + } + // step5: generates M' using padding1, mHash, and salt + this.md.update(EIGHT_BYTES_OF_ZEROS); + digestReset = false; // mark digest as it now has data + this.md.update(mHash); + if (sLen != 0) { + // step4: generate random salt + byte[] salt = new byte[sLen]; + this.random.nextBytes(salt); + this.md.update(salt); + + // step8: set DB's salt into EM + System.arraycopy(salt, 0, em, dbLen - sLen, sLen); + } + // step6: generate H using M' + this.md.digest(em, dbLen, hLen); // set H field of EM + digestReset = true; + + // step7 and 8 are already covered by the code which setting up + // EM as above + + // step9 and 10: feed H into MGF and xor with DB in EM + KAEMGF1 mgf1 = new KAEMGF1(mgfDigestAlgo); + mgf1.generateAndXor(em, dbLen, hLen, dbLen, em, 0); + // step11: set the leftmost (8emLen - emBits) bits of the leftmost + // octet to 0 + int numZeroBits = (emLen << 3) - emBits; + if (numZeroBits != 0) { + byte mask = (byte) (0xff >>> numZeroBits); + em[0] = (byte) (em[0] & mask); + } + + // step12: em should now holds maskedDB || hash h || 0xBC + return em; + } catch (NoSuchAlgorithmException | RuntimeException e) { + e.printStackTrace(); + throw new IOException(e.toString()); + } + } + + private String getMgfDigestAlgo() { + String mgfDigestAlgo; + AlgorithmParameterSpec mgfParams = this.sigParams.getMGFParameters(); + if (mgfParams != null) { + mgfDigestAlgo = + ((MGF1ParameterSpec) mgfParams).getDigestAlgorithm(); + } else { + mgfDigestAlgo = this.md.getAlgorithm(); + } + return mgfDigestAlgo; + } + + private void generateAndXor(String mgfDigestAlgo, byte[] em, int dbLen, int hLen) throws IOException { + try { + KAEMGF1 mgf1 = new KAEMGF1(mgfDigestAlgo); + mgf1.generateAndXor(em, dbLen, hLen, dbLen, em, 0); + } catch (NoSuchAlgorithmException | RuntimeException e) { + e.printStackTrace(); + throw new IOException(e.toString()); + } + } + + /** + * Decode the signature data. Verify that the object identifier matches + * and return the message digest. + */ + private boolean decodeSignature(byte[] mHash, byte[] em) throws IOException { + int hLen = mHash.length; + int sLen = this.sigParams.getSaltLength(); + int emLen = em.length; + int emBits = getKeyLengthInBits(this.pubKey) - 1; + + // step3 + if (emLen < (hLen + sLen + 2)) { + return false; + } + + // step4 + if (em[emLen - 1] != (byte) 0xBC) { + return false; + } + + // step6: check if the leftmost (8emLen - emBits) bits of the leftmost + // octet are 0 + int numZeroBits = (emLen << 3) - emBits; + if (numZeroBits != 0) { + byte mask = (byte) (0xff << (8 - numZeroBits)); + if ((em[0] & mask) != 0) { + return false; + } + } + + // step 7 and 8 + int dbLen = emLen - hLen - 1; + + // generateAndXor + String mgfDigestAlgo = getMgfDigestAlgo(); + generateAndXor(mgfDigestAlgo, em, dbLen, hLen); + + // step9: set the leftmost (8emLen - emBits) bits of the leftmost + // octet to 0 + if (numZeroBits != 0) { + byte mask = (byte) (0xff >>> numZeroBits); + em[0] = (byte) (em[0] & mask); + } + + // step10 + int index = 0; + for (; index < dbLen - sLen - 1; index++) { + if (em[index] != 0) { + return false; + } + } + if (em[index] != 0x01) { + return false; + } + // step12 and 13 + this.md.update(EIGHT_BYTES_OF_ZEROS); + digestReset = false; + this.md.update(mHash); + if (sLen > 0) { + this.md.update(em, (dbLen - sLen), sLen); + } + byte[] digest2 = this.md.digest(); + digestReset = true; + + // step14 + byte[] digestInEM = Arrays.copyOfRange(em, dbLen, emLen - 1); + return MessageDigest.isEqual(digest2, digestInEM); + } + + // set parameter, not supported. See JCA doc + @Deprecated + @Override + protected void engineSetParameter(String param, Object value) throws InvalidParameterException { + throw new UnsupportedOperationException("setParameter() not supported"); + } + + @Override + protected void engineSetParameter(AlgorithmParameterSpec params) throws InvalidAlgorithmParameterException { + this.sigParams = validateSigParams(params); + // disallow changing parameters when digest has been used + if (!digestReset) { + throw new ProviderException + ("Cannot set parameters during operations"); + } + String newHashAlg = this.sigParams.getDigestAlgorithm(); + // re-allocate md if not yet assigned or algorithm changed + if ((this.md == null) || + !(this.md.getAlgorithm().equalsIgnoreCase(newHashAlg))) { + try { + this.md = MessageDigest.getInstance(newHashAlg); + } catch (NoSuchAlgorithmException nsae) { + // should not happen as we pick default digest algorithm + throw new InvalidAlgorithmParameterException("Unsupported digest algorithm " + newHashAlg, nsae); + } + } + } + + // get parameter, not supported. See JCA doc + @Deprecated + @Override + protected Object engineGetParameter(String param) throws InvalidParameterException { + throw new UnsupportedOperationException("getParameter() not supported"); + } + + @Override + protected AlgorithmParameters engineGetParameters() { + AlgorithmParameters ap = null; + if (this.sigParams != null) { + try { + ap = AlgorithmParameters.getInstance("RSASSA-PSS"); + ap.init(this.sigParams); + } catch (GeneralSecurityException gse) { + throw new ProviderException(gse.getMessage()); + } + } + return ap; + } +} + diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSAPaddingType.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSAPaddingType.java new file mode 100644 index 000000000..35e7f2f67 --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSAPaddingType.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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. + */ + +package org.openeuler.security.openssl; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +enum KAERSAPaddingType { + // raw RSA + PKCS1Padding(1, "PKCS1Padding"), + + // PKCS#1 v1.5 RSA + NoPadding(3, "NoPadding"), + + // PKCS#2 v2.2 OAEP with MGF1 + OAEP(4, "OAEP", new HashSet<>( + Arrays.asList( + "OAEPPADDING", + "OAEPWITHMD5ANDMGF1PADDING", + "OAEPWITHSHA1ANDMGF1PADDING", + "OAEPWITHMD5ANDMGF1PADDING", + "OAEPWITHSHA1ANDMGF1PADDING", + "OAEPWITHSHA-1ANDMGF1PADDING", + "OAEPWITHSHA-224ANDMGF1PADDING", + "OAEPWITHSHA-256ANDMGF1PADDING", + "OAEPWITHSHA-384ANDMGF1PADDING", + "OAEPWITHSHA-512ANDMGF1PADDING", + "OAEPWITHSHA-512/224ANDMGF1PADDING", + "OAEPWITHSHA-512/256ANDMGF1PADDING")) + ), + + // PSS + PKCS1PssPadding(6, "RSA_PKCS1_PSS_PADDING"); + + private final int id; + private final String name; + private final Set supportPaddings; + + public int getId() { + return id; + } + + public String getName() { + return name; + } + + KAERSAPaddingType(int id, String name) { + this(id, name, Collections.singleton(name)); + } + + KAERSAPaddingType(int id, String name, Set supportPaddings) { + this.id = id; + this.name = name; + this.supportPaddings = supportPaddings; + } + + public Set getSupportPaddings() { + return supportPaddings; + } +} diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSASignature.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSASignature.java new file mode 100644 index 000000000..b91a8cdaa --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSASignature.java @@ -0,0 +1,365 @@ +/* + * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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 org.openeuler.security.openssl; + +import java.io.IOException; +import java.nio.ByteBuffer; + +import java.security.*; +import java.security.interfaces.*; +import java.security.spec.AlgorithmParameterSpec; + +import sun.security.rsa.RSACore; +import sun.security.rsa.RSAKeyFactory; +import sun.security.rsa.RSAPadding; +import sun.security.rsa.RSAUtil; +import sun.security.rsa.RSAUtil.KeyType; +import sun.security.util.*; +import sun.security.x509.AlgorithmId; + +import javax.crypto.BadPaddingException; + +/** + * We support support rsa signatures with MD2, MD5, SHA-1, SHA-224, SHA-256, SHA-384, SHA-512 as the digest algorithm. + * The Openssl does not support rsa signatures with SHA-512/224 and SHA-512/256 as the digest algorithm, + * so we have not implemented them. + * The Openssl does not support non-CRT private key , when signing with a non-CRT private key, we use the sun sign. + */ +public abstract class KAERSASignature extends SignatureSpi { + // we sign an ASN.1 SEQUENCE of AlgorithmId and digest + // it has the form 30:xx:30:xx:[digestOID]:05:00:04:xx:[digest] + // this means the encoded length is (8 + digestOID.length + digest.length) + private static final int BASE_LENGTH = 8; + + private String digestAlgorithm; + + // object identifier for the message digest algorithm used + private final ObjectIdentifier digestOID; + + // length of the encoded signature blob + private final int encodedLength; + + // message digest implementation we use + private final MessageDigest md; + + // flag indicating whether the digest is reset + private boolean digestReset; + + // private key, if initialized for signing + private RSAPrivateKey privateKey; + + // public key, if initialized for verifying + private RSAPublicKey publicKey; + + // padding to use, set when the initSign/initVerify is called + private RSAPadding padding; + + /** + * Construct a new RSASignature. Used by subclasses. + */ + KAERSASignature(String algorithm, ObjectIdentifier digestOID, int oidLength) { + this.digestAlgorithm = algorithm; + this.digestOID = digestOID; + try { + md = MessageDigest.getInstance(algorithm); + } catch (NoSuchAlgorithmException e) { + throw new ProviderException(e); + } + digestReset = true; + encodedLength = BASE_LENGTH + oidLength + md.getDigestLength(); + } + + // initialize for verification. See JCA doc + @Override + protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException { + RSAPublicKey rsaKey = (RSAPublicKey) RSAKeyFactory.toRSAKey(publicKey); + this.privateKey = null; + this.publicKey = rsaKey; + initCommon(rsaKey, null); + } + + // initialize for signing. See JCA doc + @Override + protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException { + engineInitSign(privateKey, null); + } + + // initialize for signing. See JCA doc + @Override + protected void engineInitSign(PrivateKey privateKey, SecureRandom random) throws InvalidKeyException { + RSAPrivateKey rsaKey = + (RSAPrivateKey) RSAKeyFactory.toRSAKey(privateKey); + this.privateKey = rsaKey; + this.publicKey = null; + initCommon(rsaKey, random); + } + + /** + * Init code common to sign and verify. + */ + private void initCommon(RSAKey rsaKey, SecureRandom random) throws InvalidKeyException { + try { + RSAUtil.checkParamsAgainstType(KeyType.RSA, rsaKey.getParams()); + } catch (ProviderException e) { + throw new InvalidKeyException("Invalid key for RSA signatures", e); + } + resetDigest(); + int keySize = RSACore.getByteLength(rsaKey); + try { + padding = RSAPadding.getInstance + (RSAPadding.PAD_BLOCKTYPE_1, keySize, random); + } catch (InvalidAlgorithmParameterException iape) { + throw new InvalidKeyException(iape.getMessage()); + } + int maxDataSize = padding.getMaxDataSize(); + if (encodedLength > maxDataSize) { + throw new InvalidKeyException + ("Key is too short for this signature algorithm"); + } + } + + /** + * Reset the message digest if it is not already reset. + */ + private void resetDigest() { + if (digestReset == false) { + md.reset(); + digestReset = true; + } + } + + /** + * Return the message digest value. + */ + private byte[] getDigestValue() { + digestReset = true; + return md.digest(); + } + + // update the signature with the plaintext data. See JCA doc + @Override + protected void engineUpdate(byte b) throws SignatureException { + md.update(b); + digestReset = false; + } + + // update the signature with the plaintext data. See JCA doc + @Override + protected void engineUpdate(byte[] b, int off, int len) throws SignatureException { + md.update(b, off, len); + digestReset = false; + } + + // update the signature with the plaintext data. See JCA doc + @Override + protected void engineUpdate(ByteBuffer b) { + md.update(b); + digestReset = false; + } + + // sign the data and return the signature. See JCA doc + @Override + protected byte[] engineSign() throws SignatureException { + if (privateKey == null) { + throw new SignatureException("Missing private key"); + } + + byte[] digest = getDigestValue(); + if (useKaeSign()) { + return kaeSign(digest); + } + return sunSign(digest); + } + + // determine if use kae sign , openssl do not support non-CRT private key + private boolean useKaeSign() { + return privateKey instanceof RSAPrivateCrtKey; + } + + // sun sign + private byte[] sunSign(byte[] digest) throws SignatureException { + try { + byte[] encoded = encodeSignature(digestOID, digest); + byte[] padded = padding.pad(encoded); + return RSACore.rsa(padded, privateKey, true); + } catch (GeneralSecurityException e) { + throw new SignatureException("Could not sign data", e); + } catch (IOException e) { + throw new SignatureException("Could not encode data", e); + } + } + + // kae sign + private byte[] kaeSign(byte[] digest) throws SignatureException { + String kaeDigestName = KAEUtils.getKAEDigestName(this.digestAlgorithm); + RSAPrivateCrtKey privateCrtKey = (RSAPrivateCrtKey) privateKey; + long keyAddress = KAERSACipher.nativeCreateRSAPrivateCrtKey( + privateCrtKey.getModulus().toByteArray(), + privateCrtKey.getPublicExponent().toByteArray(), + privateCrtKey.getPrivateExponent().toByteArray(), + privateCrtKey.getPrimeP().toByteArray(), + privateCrtKey.getPrimeQ().toByteArray(), + privateCrtKey.getPrimeExponentP().toByteArray(), + privateCrtKey.getPrimeExponentQ().toByteArray(), + privateCrtKey.getCrtCoefficient().toByteArray()); + byte[] sigBytes; + try { + sigBytes = KAERSASignatureNative.rsaSign(keyAddress, + kaeDigestName, digest, KAERSAPaddingType.PKCS1Padding.getId()); + } catch (SignatureException e) { + throw e; + } finally { + // free keyAddress + KAERSACipher.nativeFreeKey(keyAddress); + } + return sigBytes; + } + + // verify the data and return the result. See JCA doc + @Override + protected boolean engineVerify(byte[] sigBytes) throws SignatureException { + if (publicKey == null) { + throw new SignatureException("Missing public key"); + } + + boolean verify; + long keyAddress = 0L; + try { + if (sigBytes.length != RSACore.getByteLength(publicKey)) { + throw new SignatureException("Signature length not correct: got " + + sigBytes.length + " but was expecting " + + RSACore.getByteLength(publicKey)); + } + + String kaeDigestName = KAEUtils.getKAEDigestName(this.digestAlgorithm); + byte[] digest = getDigestValue(); + keyAddress = KAERSACipher.nativeCreateRSAPublicKey(publicKey.getModulus().toByteArray(), + publicKey.getPublicExponent().toByteArray()); + verify = KAERSASignatureNative.rsaVerify(keyAddress, + kaeDigestName, digest, KAERSAPaddingType.PKCS1Padding.getId(), sigBytes); + } catch (SignatureException e) { + throw e; + } catch (BadPaddingException e) { + // occurs if the app has used the wrong RSA public key + // or if sigBytes is invalid or sourceBytes is invalid + // return false rather than propagating the exception for + // compatibility/ease of use + return false; + } finally { + // reset digest + resetDigest(); + // free keyAddress + if (keyAddress != 0L) { + KAERSACipher.nativeFreeKey(keyAddress); + } + } + return verify; + } + + /** + * Encode the digest, return the to-be-signed data. + * Also used by the PKCS#11 provider. + */ + public static byte[] encodeSignature(ObjectIdentifier oid, byte[] digest) throws IOException { + DerOutputStream out = new DerOutputStream(); + new AlgorithmId(oid).encode(out); + out.putOctetString(digest); + DerValue result = + new DerValue(DerValue.tag_Sequence, out.toByteArray()); + return result.toByteArray(); + } + + // set parameter, not supported. See JCA doc + @Deprecated + @Override + protected void engineSetParameter(String param, Object value) throws InvalidParameterException { + throw new UnsupportedOperationException("setParameter() not supported"); + } + + // See JCA doc + @Override + protected void engineSetParameter(AlgorithmParameterSpec params) throws InvalidAlgorithmParameterException { + if (params != null) { + throw new InvalidAlgorithmParameterException("No parameters accepted"); + } + } + + // get parameter, not supported. See JCA doc + @Deprecated + @Override + protected Object engineGetParameter(String param) throws InvalidParameterException { + throw new UnsupportedOperationException("getParameter() not supported"); + } + + // See JCA doc + @Override + protected AlgorithmParameters engineGetParameters() { + return null; + } + + // Nested class for MD5withRSA signatures + public static final class MD5withRSA extends KAERSASignature { + public MD5withRSA() { + super("MD5", AlgorithmId.MD5_oid, 10); + } + } + + // Nested class for SHA1withRSA signatures + public static final class SHA1withRSA extends KAERSASignature { + public SHA1withRSA() { + super("SHA-1", AlgorithmId.SHA_oid, 7); + } + } + + // Nested class for SHA224withRSA signatures + public static final class SHA224withRSA extends KAERSASignature { + public SHA224withRSA() { + super("SHA-224", AlgorithmId.SHA224_oid, 11); + } + } + + // Nested class for SHA256withRSA signatures + public static final class SHA256withRSA extends KAERSASignature { + public SHA256withRSA() { + super("SHA-256", AlgorithmId.SHA256_oid, 11); + } + } + + // Nested class for SHA384withRSA signatures + public static final class SHA384withRSA extends KAERSASignature { + public SHA384withRSA() { + super("SHA-384", AlgorithmId.SHA384_oid, 11); + } + } + + // Nested class for SHA512withRSA signatures + public static final class SHA512withRSA extends KAERSASignature { + public SHA512withRSA() { + super("SHA-512", AlgorithmId.SHA512_oid, 11); + } + } +} diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSASignatureNative.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSASignatureNative.java new file mode 100644 index 000000000..dda2f068d --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAERSASignatureNative.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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. + */ + +package org.openeuler.security.openssl; + +import javax.crypto.BadPaddingException; +import java.security.SignatureException; + +public class KAERSASignatureNative { + // rsa sign + protected static native byte[] rsaSign(long keyAddress, String digestName, byte[] digestBytes, int paddingType) + throws SignatureException; + + // rsa verify + protected static native boolean rsaVerify(long keyAddress, String digestName, byte[] digestBytes, int paddingType, + byte[] sigBytes) throws SignatureException, BadPaddingException; + + // rsa pss sign + protected static native byte[] pssSign(long keyAddress, String digestName, byte[] digestBytes, int paddingType, + String mgf1DigestName, int saltLen) throws SignatureException; + + // rsa pss verify + protected static native boolean pssVerify(long keyAddress, String digestName, byte[] digestBytes, int paddingType, + String mgf1DigestName, int saltLen, byte[] sigBytes) throws SignatureException, BadPaddingException; +} diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAESM4Cipher.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAESM4Cipher.java new file mode 100644 index 000000000..a3e47606d --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAESM4Cipher.java @@ -0,0 +1,370 @@ +/* + * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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 org.openeuler.security.openssl; + +import sun.security.util.Debug; + +import java.nio.ByteBuffer; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.Key; +import java.security.ProviderException; +import java.util.Locale; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.ShortBufferException; + +/* + * This class currently supports: + * - SM4/ECB/NOPADDING + * - SM4/ECB/PKCS5PADDING + * - SM4/CBC/NOPADDING + * - SM4/CBC/PKCS5PADDING + * - SM4/CTR/NOPADDING + * - SM4/OFB/NOPADDING + * - SM4/OFB/PKCS5PADDING + */ +abstract class KAESM4Cipher extends KAESymmetricCipherBase { + + private static final Debug debug = Debug.getInstance("kae"); + + /* + * SM4 max chunk size of each encryption or decryption + * when input data does not have an accessible byte[] + */ + private static final int DEFAULT_KAE_SM4_MAX_CHUNK_SIZE = 4096; + private static int KAE_SM4_MAX_CHUNK_SIZE; + static { + initSM4MaxChunkSize(); + } + + private static void initSM4MaxChunkSize() { + String maxChunkSize = KAEConfig.privilegedGetOverridable("kae.sm4.maxChunkSize", + DEFAULT_KAE_SM4_MAX_CHUNK_SIZE + ""); + try { + KAE_SM4_MAX_CHUNK_SIZE = Integer.parseInt(maxChunkSize); + } catch (NumberFormatException e) { + // When parsing string argument to signed decimal integer fails, uses the default chunk size (4096) + KAE_SM4_MAX_CHUNK_SIZE = DEFAULT_KAE_SM4_MAX_CHUNK_SIZE; + if (debug != null) { + debug.println("The configured block size (" + maxChunkSize + ") cannot be converted to an integer, " + + "uses the default chunk size (" + DEFAULT_KAE_SM4_MAX_CHUNK_SIZE + ")"); + e.printStackTrace(); + } + return; + } + // when the configured chunk size is less than or equal to 0, uses the default chunk size (4096) + if (KAE_SM4_MAX_CHUNK_SIZE <= 0) { + KAE_SM4_MAX_CHUNK_SIZE = DEFAULT_KAE_SM4_MAX_CHUNK_SIZE; + if (debug != null) { + debug.println("The configured chunk size (" + KAE_SM4_MAX_CHUNK_SIZE + ") is less than " + + "or equal to 0, uses the default chunk size (" + DEFAULT_KAE_SM4_MAX_CHUNK_SIZE + ")"); + } + return; + } + if (debug != null) { + debug.println("The configured chunk size is " + KAE_SM4_MAX_CHUNK_SIZE); + } + } + + /** + * Used by the engineUpdate(ByteBuffer, ByteBuffer) and + * engineDoFinal(ByteBuffer, ByteBuffer) methods. + */ + private static int getSM4MaxChunkSize(int totalSize) { + return Math.min(KAE_SM4_MAX_CHUNK_SIZE, totalSize); + } + + public static class Sm4 extends KAESM4Cipher { + public Sm4(Mode mode, Padding padding) { + super(mode, padding, 16); + } + + public static class Cbc extends Sm4 { + 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 Ecb extends Sm4 { + 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 Ctr extends Sm4 { + public Ctr(Padding padding) { + super(Mode.CTR, padding); + } + public static class NoPadding extends Ctr { + public NoPadding() { + super(Padding.NOPADDING); + } + } + } + + public static class Ofb extends Sm4 { + 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); + } + } + } + } + + KAESM4Cipher(Mode mode, Padding padding, int fixedKeySize) { + super(mode, padding, fixedKeySize, "SM4"); + } + + protected void checkKey(Key key) throws InvalidKeyException { + if (key == null || key.getEncoded() == null) { + throw new InvalidKeyException("Key cannot be null"); + } else { + int keyLen = key.getEncoded().length; + if (keyLen != fixedKeySize) { + throw new InvalidKeyException("Only " + fixedKeySize + "-byte keys are accepted. Got: " + keyLen); + } + } + } + + protected String getCipherName(int keyLength, Mode mode) { + return "sm4" + "-" + mode.toString().toLowerCase(Locale.US); + } + + @Override + protected void engineSetMode(String modeStr) throws NoSuchAlgorithmException { + if (modeStr == null) { + throw new NoSuchAlgorithmException("null mode"); + } + + if (modeStr.equalsIgnoreCase("ECB")) { + mode = Mode.ECB; + } else if (modeStr.equalsIgnoreCase("CBC")) { + mode = Mode.CBC; + } else if (modeStr.equalsIgnoreCase("CTR")) { + mode = Mode.CTR; + } else if (modeStr.equalsIgnoreCase("OFB")) { + mode = Mode.OFB; + } else { + throw new NoSuchAlgorithmException("Unsupported mode " + mode); + } + } + + @Override + protected void engineSetPadding(String paddingStr) throws NoSuchPaddingException { + if (paddingStr == null) { + throw new NoSuchPaddingException("null padding"); + } + if (paddingStr.equalsIgnoreCase("PKCS7PADDING")) { + paddingStr = "PKCS5Padding"; + } + + if (paddingStr.equalsIgnoreCase("NOPADDING")) { + this.padding = Padding.NOPADDING; + } else if(paddingStr.equalsIgnoreCase("PKCS5PADDING")) { + if (mode == Mode.CTR) { + throw new NoSuchPaddingException("PKCS#5 padding not supported with CTR mode"); + } + this.padding = Padding.PKCS5PADDING; + } else { + throw new NoSuchPaddingException("Unsupported padding " + paddingStr); + } + } + + @Override + protected int engineUpdate(ByteBuffer input, ByteBuffer output) throws ShortBufferException { + try { + return bufferCrypt(input, output, true); + } catch (IllegalBlockSizeException e) { + // never thrown for engineUpdate() + throw new ProviderException("Internal error in update()"); + } catch (BadPaddingException e) { + // never thrown for engineUpdate() + throw new ProviderException("Internal error in update()"); + } + } + + @Override + protected int engineDoFinal(ByteBuffer input, ByteBuffer output) + throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { + return bufferCrypt(input, output, false); + } + + /** + * Implementation for encryption using ByteBuffers. Used for both + * engineUpdate() and engineDoFinal(). + */ + private int bufferCrypt(ByteBuffer input, ByteBuffer output, + boolean isUpdate) throws ShortBufferException, + IllegalBlockSizeException, BadPaddingException { + if ((input == null) || (output == null)) { + throw new NullPointerException + ("Input and output buffers must not be null"); + } + int inPos = input.position(); + int inLimit = input.limit(); + int inLen = inLimit - inPos; + if (isUpdate && (inLen == 0)) { + return 0; + } + int outLenNeeded = engineGetOutputSize(inLen); + + if (output.remaining() < outLenNeeded) { + throw new ShortBufferException("Need at least " + outLenNeeded + + " bytes of space in output buffer"); + } + + // detecting input and output buffer overlap may be tricky + // we can only write directly into output buffer when we + // are 100% sure it's safe to do so + + boolean a1 = input.hasArray(); + boolean a2 = output.hasArray(); + int total = 0; + + if (a1) { // input has an accessible byte[] + byte[] inArray = input.array(); + int inOfs = input.arrayOffset() + inPos; + + byte[] outArray; + if (a2) { // output has an accessible byte[] + outArray = output.array(); + int outPos = output.position(); + int outOfs = output.arrayOffset() + outPos; + + // check array address and offsets and use temp output buffer + // if output offset is larger than input offset and + // falls within the range of input data + boolean useTempOut = false; + if (inArray == outArray && + ((inOfs < outOfs) && (outOfs < inOfs + inLen))) { + useTempOut = true; + outArray = new byte[outLenNeeded]; + outOfs = 0; + } + if (isUpdate) { + total = engineUpdate(inArray, inOfs, inLen, outArray, outOfs); + } else { + total = engineDoFinal(inArray, inOfs, inLen, outArray, outOfs); + } + if (useTempOut) { + output.put(outArray, outOfs, total); + } else { + // adjust output position manually + output.position(outPos + total); + } + } else { // output does not have an accessible byte[] + if (isUpdate) { + outArray = engineUpdate(inArray, inOfs, inLen); + } else { + outArray = engineDoFinal(inArray, inOfs, inLen); + } + if (outArray != null && outArray.length != 0) { + output.put(outArray); + total = outArray.length; + } + } + // adjust input position manually + input.position(inLimit); + } else { // input does not have an accessible byte[] + // have to assume the worst, since we have no way of determine + // if input and output overlaps or not + byte[] tempOut = new byte[outLenNeeded]; + int outOfs = 0; + + byte[] tempIn = new byte[getSM4MaxChunkSize(inLen)]; + do { + int chunk = Math.min(inLen, tempIn.length); + if (chunk > 0) { + input.get(tempIn, 0, chunk); + } + int n; + if (isUpdate || (inLen > chunk)) { + n = engineUpdate(tempIn, 0, chunk, tempOut, outOfs); + } else { + n = engineDoFinal(tempIn, 0, chunk, tempOut, outOfs); + } + outOfs += n; + total += n; + inLen -= chunk; + } while (inLen > 0); + if (total > 0) { + output.put(tempOut, 0, total); + } + } + + return total; + } + + protected void checkIvBytes(byte[] ivBytes) throws InvalidAlgorithmParameterException { + if (ivBytes == null) { + throw new InvalidAlgorithmParameterException("Wrong IV length: iv is null "); + } + if (mode == Mode.CTR) { + // For compatibility, SM4 CTR allows 8 < IV < blockSize, the remaining bytes will be filled with 0 in engineInit + if (ivBytes.length < 8 || ivBytes.length > blockSize) { + throw new InvalidAlgorithmParameterException("Wrong IV length: CTR mode requires IV of at least" + + "8 bytes, and no greater than " + blockSize + "bytes"); + } + return; + } + if (ivBytes.length != blockSize) { + throw new InvalidAlgorithmParameterException("Wrong IV length: must be " + blockSize + " bytes long."); + } + } +} \ No newline at end of file diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAESM4KeyGenerator.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAESM4KeyGenerator.java new file mode 100644 index 000000000..3f60e804f --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAESM4KeyGenerator.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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. Huawei designates this + * particular file as subject to the "Classpath" exception as provided + * by Huawei 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 visit https://gitee.com/openeuler/bgmprovider if you need additional + * information or have any questions. + */ + +package org.openeuler.security.openssl; + +import javax.crypto.KeyGeneratorSpi; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidParameterException; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +public class KAESM4KeyGenerator extends KeyGeneratorSpi { + private byte[] key; + private int keySize = 16; // default keysize (in number of bytes) + private SecureRandom random; + + @Override + protected void engineInit(SecureRandom random) { + this.random = random; + } + + @Override + protected void engineInit(AlgorithmParameterSpec params, SecureRandom random) + throws InvalidAlgorithmParameterException { + throw new InvalidAlgorithmParameterException + ("SM4 key generation does not take any parameters"); + } + + @Override + protected void engineInit(int keysize, SecureRandom random) { + if (keysize != 128) { + throw new InvalidParameterException("SM4 requires a 128 bit key"); + } + this.keySize = keysize / 8; + engineInit(random); + } + + private static class SecureRandomHolder { + static final SecureRandom RANDOM = new SecureRandom(); + } + + @Override + protected SecretKey engineGenerateKey() { + key = new byte[keySize]; + if (random == null) { + random = SecureRandomHolder.RANDOM; + } + random.nextBytes(key); + return new SecretKeySpec(key, "SM4"); + } +} diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAESymmetricCipherBase.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAESymmetricCipherBase.java new file mode 100644 index 000000000..7b5433b38 --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAESymmetricCipherBase.java @@ -0,0 +1,791 @@ +/* + * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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 org.openeuler.security.openssl; + +import sun.security.jca.JCAUtil; + +import java.lang.ref.PhantomReference; +import java.lang.ref.ReferenceQueue; +import java.nio.ByteBuffer; +import java.security.*; +import java.security.spec.*; +import java.util.Arrays; +import java.util.Set; +import java.util.concurrent.ConcurrentSkipListSet; + +import javax.crypto.*; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +/* + * Cipher wrapper class utilizing openssl APIs. + */ +abstract class KAESymmetricCipherBase extends CipherSpi { + enum Padding { + NOPADDING, + PKCS5PADDING + } + + enum Mode { + ECB, + CBC, + CTR, + OFB, + GCM + } + + protected final String keyAlgo; + protected final int blockSize = 16; + protected Mode mode; + protected Padding padding; + protected int fixedKeySize; + + private CipherContextRef pCtx = null; + private byte[] keyValue; + protected byte[] iv; + private boolean initialized = false; + private boolean encrypt = false; + private int bytesBuffered = 0; + + private boolean calledUpdate; + private String cipherName; + + // for gcm + private final int defaultGcmTagLen = blockSize; + private final int defaultGcmIvLen = 12; + private int tagLengthInBytes; + private byte[] lastEncKey = null; + private byte[] lastEncIv = null; + private byte[] aad; + + private static PublicKey constructPublicKey(byte[] encodedKey, String encodedKeyAlgorithm) + throws NoSuchAlgorithmException, InvalidKeyException { + PublicKey key; + try { + KeyFactory keyFactory = KeyFactory.getInstance(encodedKeyAlgorithm); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encodedKey); + key = keyFactory.generatePublic(keySpec); + } catch (NoSuchAlgorithmException e) { + throw new NoSuchAlgorithmException("No provider found for " + encodedKeyAlgorithm + " KeyFactory"); + } catch (InvalidKeySpecException e) { + throw new InvalidKeyException("Cannot construct public key", e); + } + return key; + } + + private static PrivateKey constructPrivateKey(byte[] encodedKey, + String encodedKeyAlgorithm) throws InvalidKeyException, NoSuchAlgorithmException { + PrivateKey key = null; + try { + KeyFactory keyFactory = KeyFactory.getInstance(encodedKeyAlgorithm); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedKey); + key = keyFactory.generatePrivate(keySpec); + } catch (NoSuchAlgorithmException e) { + throw new NoSuchAlgorithmException("No provider found for " + encodedKeyAlgorithm + " KeyFactory"); + } catch (InvalidKeySpecException e) { + throw new InvalidKeyException("Cannot construct private key", e); + } + return key; + } + + private static SecretKey constructSecretKey(byte[] encodedKey, String encodedKeyAlgorithm) { + return new SecretKeySpec(encodedKey, encodedKeyAlgorithm); + } + + static final Key constructKey(int keyType, byte[] encodedKey, + String encodedKeyAlgorithm) throws NoSuchAlgorithmException, InvalidKeyException { + Key res = null; + switch (keyType) { + case Cipher.SECRET_KEY: + res = constructSecretKey(encodedKey, encodedKeyAlgorithm); + break; + case Cipher.PRIVATE_KEY: + res = constructPrivateKey(encodedKey, encodedKeyAlgorithm); + break; + case Cipher.PUBLIC_KEY: + res = constructPublicKey(encodedKey, encodedKeyAlgorithm); + break; + default: + throw new InvalidKeyException("Unknown keytype " + keyType); + } + return res; + } + + KAESymmetricCipherBase(Mode mode, Padding padding, int fixedKeySize, String keyAlgo) { + this.mode = mode; + this.padding = padding; + this.fixedKeySize = fixedKeySize; + this.keyAlgo = keyAlgo; + } + + private static class CipherContextRef extends PhantomReference + implements Comparable { + private static ReferenceQueue refQueue = new ReferenceQueue<>(); + private static Set refList = new ConcurrentSkipListSet<>(); + private static boolean disableKaeDispose = Boolean.getBoolean("kae.disableKaeDispose"); + + final long ctxAddress; + + private static void drainRefQueueBounded() { + while (true) { + CipherContextRef next = (CipherContextRef) refQueue.poll(); + if (next == null) { + break; + } + next.dispose(true); + } + } + + CipherContextRef(KAESymmetricCipherBase kaeCipher, long ctxAddress) { + super(kaeCipher, refQueue); + this.ctxAddress = ctxAddress; + if (!disableKaeDispose) { + refList.add(this); + drainRefQueueBounded(); + } + } + + @Override + public int compareTo(CipherContextRef o) { + if (this.ctxAddress == o.ctxAddress) { + return 0; + } else { + return (this.ctxAddress < o.ctxAddress) ? -1 : 1; + } + } + + void dispose(boolean needFree) { + if (!disableKaeDispose) { + refList.remove(this); + try { + if (needFree) { + nativeFree(ctxAddress); + } + } finally { + this.clear(); + } + } else { + nativeFree(ctxAddress); + } + } + } + + @Override + protected int engineGetBlockSize() { + return blockSize; + } + + @Override + protected int engineGetOutputSize(int inputLen) { + return getOutputSizeByOperation(inputLen, true); + } + + @Override + protected byte[] engineGetIV() { + return iv == null ? null : iv.clone(); + } + + @Override + protected AlgorithmParameters engineGetParameters() { + if (iv == null) { + return null; + } + AlgorithmParameterSpec spec; + AlgorithmParameters params; + String algName = keyAlgo; + if (mode == Mode.GCM) { + algName = "GCM"; + spec = new GCMParameterSpec(tagLengthInBytes * 8, iv.clone()); + } else { + spec = new IvParameterSpec(iv.clone()); + } + try { + params = AlgorithmParameters.getInstance(algName); + params.init(spec); + return params; + } catch (GeneralSecurityException e) { + throw new RuntimeException("Could not encode parameters", e); + } + } + + @Override + protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException { + try { + engineInit(opmode, key, (AlgorithmParameterSpec) null, random); + } catch (InvalidAlgorithmParameterException e) { + throw new InvalidKeyException("init() failed", e); + } + } + + @Override + protected void engineInit(int opmode, Key key, AlgorithmParameters params, + SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { + AlgorithmParameterSpec spec = null; + String paramType = null; + if (params != null) { + try { + if (mode == Mode.GCM) { + spec = params.getParameterSpec(GCMParameterSpec.class); + paramType = "GCM"; + } else { + spec = params.getParameterSpec(IvParameterSpec.class); + paramType = "IV"; + } + } catch (InvalidParameterSpecException e) { + throw new InvalidAlgorithmParameterException("Could not decode " + paramType, e); + } + } + engineInit(opmode, key, spec, random); + } + + @Override + protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params, + SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { + checkKey(key); + boolean doEncrypt = (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE); + + byte[] ivBytes = null; + int tagLen = -1; + if (params != null) { + if (mode == Mode.GCM) { + if (params instanceof GCMParameterSpec) { + tagLen = ((GCMParameterSpec)params).getTLen(); + checkTagLen(tagLen); + tagLen = tagLen >> 3; + ivBytes = ((GCMParameterSpec)params).getIV(); + } else { + throw new InvalidAlgorithmParameterException("Unsupported parameter: " + params); + } + } else { + if (params instanceof IvParameterSpec) { + ivBytes = ((IvParameterSpec) params).getIV(); + checkIvBytes(ivBytes); + } else { + throw new InvalidKeyException("IvParameterSpec required. Received: " + params.getClass().getName()); + } + } + } + if (mode == Mode.ECB) { + if (params != null) { + throw new InvalidAlgorithmParameterException("No Parameters for ECB mode"); + } + } else if (ivBytes == null) { + if (doEncrypt) { + if (mode == Mode.GCM) { + ivBytes = new byte[defaultGcmIvLen]; + } else { + ivBytes = new byte[blockSize]; + } + if (random == null) { + random = JCAUtil.getSecureRandom(); + } + random.nextBytes(ivBytes); + } else { + throw new InvalidAlgorithmParameterException("Parameters required for decryption"); + } + } else if (keyAlgo.equalsIgnoreCase("SM4") && ivBytes.length < blockSize) { + byte[] temp = new byte[blockSize]; + System.arraycopy(ivBytes, 0, temp, 0, ivBytes.length); + ivBytes = temp; + } + implInit(doEncrypt, key.getEncoded(), ivBytes, tagLen); + } + + private void checkTagLen(int tagLen) throws InvalidAlgorithmParameterException { + if ((tagLen < 96) || (tagLen > 128) || ((tagLen & 0x07) != 0)) { + throw new InvalidAlgorithmParameterException + ("Unsupported TLen value; must be one of {128, 120, 112, 104, 96}"); + } + } + + protected abstract void checkIvBytes(byte[] ivBytes) throws InvalidAlgorithmParameterException; + + protected abstract String getCipherName(int keyLength, Mode mode); + + private void implInit(boolean encrypt, byte[] keyVal, byte[] ivVal, int tagLen) + throws InvalidAlgorithmParameterException { + reset(true); + this.encrypt = encrypt; + this.keyValue = keyVal; + this.iv = ivVal; + this.cipherName = getCipherName(keyValue.length * 8, mode); + + if (mode == Mode.GCM) { + if (tagLen == -1) { + tagLen = defaultGcmTagLen; + } + this.tagLengthInBytes = tagLen; + if (encrypt) { + // Check key+iv for encryption in GCM mode. + boolean requireReinit = Arrays.equals(ivVal, lastEncIv) && MessageDigest.isEqual(keyVal, lastEncKey); + if (requireReinit) { + throw new InvalidAlgorithmParameterException("Cannot reuse iv for GCM encryption"); + } + lastEncIv = ivVal; + lastEncKey = keyVal; + } + } + + // OpenSSL only supports PKCS5 Padding. + long pCtxVal; + try { + pCtxVal = nativeInit(cipherName, encrypt, keyValue, iv, padding == Padding.PKCS5PADDING); + } catch (RuntimeException e) { + throw new ProviderException("Invoke nativeInit failed for " + cipherName, e); + } + + initialized = (pCtxVal != 0L); + if (initialized) { + pCtx = new CipherContextRef(this, pCtxVal); + } else { + throw new NullPointerException("pCtxVal == 0"); + } + calledUpdate = false; + } + + protected abstract void checkKey(Key key) throws InvalidKeyException; + + @Override + protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { + byte[] out = new byte[getOutputSizeByOperation(inputLen, false)]; + int outLen = implUpdate(input, inputOffset, inputLen, out, 0); + if (outLen == 0) { + return new byte[0]; + } else if (out.length != outLen) { + out = Arrays.copyOf(out, outLen); + } + return out; + } + + @Override + protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, + int outputOffset) throws ShortBufferException { + int min = getOutputSizeByOperation(inputLen, false); + if (output == null || output.length - outputOffset < min) { + throw new ShortBufferException("min " + min + "-byte buffer needed"); + } + return implUpdate(input, inputOffset, inputLen, output, outputOffset); + } + + private int implUpdate(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) { + ensureInitialized(); + if (inputLen <= 0) { + return 0; + } + int outLen; + try { + outLen = nativeUpdate(pCtx.ctxAddress, input, inputOffset, inputLen, output, outputOffset, + mode == Mode.GCM, aad); + aad = null; + } catch (ArrayIndexOutOfBoundsException e) { + reset(true); + throw new ProviderException("Invoke nativeUpdate failed for " + cipherName, e); + } + bytesBuffered += (inputLen - outLen); + + calledUpdate = true; + return outLen; + } + + protected int getOutputSizeByOperation(int inLen, boolean isDoFinal) { + int ret; + + if (inLen <= 0) { + inLen = 0; + } + if (padding == Padding.NOPADDING) { + ret = inLen + bytesBuffered; + } else { + int len = inLen + bytesBuffered; + + // The amount of data written may be anything from zero bytes to (inl + cipher_block_size - 1) for encrypt. + // Refer to {@link https://www.openssl.org/docs/man1.1.0/man3/EVP_CipherUpdate.html} for details. + len += (len % blockSize != 0 || encrypt) ? blockSize : 0; + ret = len - (len % blockSize); + } + if (mode == Mode.GCM && isDoFinal) { + if (encrypt) { + ret = ret + tagLengthInBytes; + } else { + ret = Math.max(0, ret - tagLengthInBytes); + } + } + return ret; + } + + @Override + protected byte[] engineDoFinal(byte[] input, int inputOffset, + int inputLen) throws IllegalBlockSizeException, BadPaddingException { + byte[] out = new byte[getOutputSizeByOperation(inputLen, true)]; + try { + int outLen = engineDoFinal(input, inputOffset, inputLen, out, 0); + if (out.length != outLen) { + out = Arrays.copyOf(out, outLen); + } + return out; + } catch (ShortBufferException e) { + throw new ProviderException(e); + } + } + + @Override + protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output, + int outputOffset) throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { + int outLen = 0; + int min = getOutputSizeByOperation(inputLen, true); + if (output == null || output.length - outputOffset < min) { + throw new ShortBufferException("min " + min + "-byte buffer needed"); + } + + int updateLen = inputLen; + if (mode == Mode.GCM && !encrypt) { + // Remove tagLengthInBytes suffix in GCM decrypt. + updateLen = inputLen - tagLengthInBytes; + } + outLen = implUpdate(input, inputOffset, updateLen, output, outputOffset); + outputOffset += outLen; + + byte[] gcmTag = null; + if (mode == Mode.GCM && !encrypt) { + if (inputLen - outLen != tagLengthInBytes) { + throw new AEADBadTagException("Tag mismatch!"); + } + // The last tagLengthInBytees in the input arg gcmTag. + gcmTag = Arrays.copyOfRange(input, inputOffset + inputLen - tagLengthInBytes, inputOffset + inputLen); + } + + outLen += implDoFinal(output, outputOffset, gcmTag); + return outLen; + } + + @Override + protected byte[] engineWrap(Key key) throws IllegalBlockSizeException, InvalidKeyException { + byte[] res = null; + try { + byte[] encodedKey = key.getEncoded(); + if (encodedKey == null || encodedKey.length == 0) { + throw new InvalidKeyException("Cannot get an encoding of the key to be wrapped"); + } + res = engineDoFinal(encodedKey, 0, encodedKey.length); + } catch (BadPaddingException e) { + // Should never happen + } + return res; + } + + @Override + protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm, + int wrappedKeyType) throws InvalidKeyException, NoSuchAlgorithmException { + byte[] encodedKey; + try { + encodedKey = engineDoFinal(wrappedKey, 0, wrappedKey.length); + } catch (IllegalBlockSizeException | BadPaddingException e) { + throw (InvalidKeyException) (new InvalidKeyException()).initCause(e); + } + return constructKey(wrappedKeyType, encodedKey, wrappedKeyAlgorithm); + } + + @Override + protected void engineUpdateAAD(ByteBuffer byteBuffer) { + if (aad == null) { + aad = new byte[byteBuffer.remaining()]; + byteBuffer.get(aad); + } else { + int newSize = aad.length + byteBuffer.remaining(); + byte[] newaad = new byte[newSize]; + System.arraycopy(aad, 0, newaad, 0, aad.length); + byteBuffer.get(newaad, aad.length, byteBuffer.remaining()); + aad = newaad; + } + } + + @Override + protected void engineUpdateAAD(byte[] input, int inputOffset, int inputLen) { + if (aad == null) { + aad = Arrays.copyOfRange(input, inputOffset, inputOffset + inputLen); + } else { + int newSize = aad.length + inputLen; + byte[] newaad = new byte[newSize]; + System.arraycopy(aad, 0, newaad, 0, aad.length); + System.arraycopy(input, inputOffset, newaad, aad.length, inputLen); + aad = newaad; + } + } + + private int implDoFinal(byte[] out, int outputOffset, byte[] gcmTag) + throws BadPaddingException, IllegalBlockSizeException { + if (!encrypt && !calledUpdate) { + return 0; + } + ensureInitialized(); + + int outLen; + try { + if (mode == Mode.GCM) { + outLen = nativeFinalGcm(pCtx.ctxAddress, out, outputOffset, mode == Mode.GCM, tagLengthInBytes, + gcmTag, encrypt); + } else { + outLen = nativeFinal(pCtx.ctxAddress, out, outputOffset); + } + } catch (ArrayIndexOutOfBoundsException | BadPaddingException e) { + if (e instanceof AEADBadTagException) { + throw e; // AEADBadTagException is expected for some tests + } else if (e instanceof BadPaddingException) { + if (padding == Padding.NOPADDING || e.getMessage().contains("wrong final block length")) { + throw new IllegalBlockSizeException("Input length not multiple of " + blockSize + " bytes"); + } else { + throw e; + } + } else { + throw new ProviderException("Invoke nativeFinal failed for " + cipherName, e); + } + } finally { + reset(true); + } + + return outLen; + } + + protected void reset(boolean doCancel) { + initialized = false; + bytesBuffered = 0; + calledUpdate = false; + + // for gcm + aad = null; + + if (pCtx != null) { + pCtx.dispose(doCancel); + pCtx = null; + } + } + + protected static native long nativeInit(String cipherType, boolean encrypt, byte[] key, byte[] iv, boolean padding) + throws RuntimeException; + + protected static native int nativeUpdate(long pContext, byte[] in, int inOfs, int inLen, byte[] out, + int outOfs, boolean gcm, byte[] aad) throws ArrayIndexOutOfBoundsException; + + protected static native int nativeFinal(long pContext, byte[] out, + int outOfs) throws ArrayIndexOutOfBoundsException, BadPaddingException; + + protected static native void nativeFree(long pContext); + + protected static native int nativeFinalGcm(long pContext, byte[] out, int outOfs, boolean gcm, + int tagLength, byte[] gcmTag, boolean encrypt) throws ArrayIndexOutOfBoundsException, BadPaddingException; + + protected void ensureInitialized() { + if (!initialized) { + reset(true); + long pCtxVal = nativeInit(cipherName, encrypt, keyValue, iv, padding == Padding.PKCS5PADDING); + initialized = (pCtxVal != 0L); + if (initialized) { + pCtx = new CipherContextRef(this, pCtxVal); + } else { + throw new RuntimeException("Cannot initialize Cipher"); + } + } + } + + // copied from sun.security.jca.JCAUtil + // will be changed to reference that method once that code has been + // integrated and promoted + static int getTempArraySize(int totalSize) { + return Math.min(4096, totalSize); + } + + /** + * Continues a multiple-part encryption or decryption operation + * (depending on how this cipher was initialized), processing another data + * part. + * + *

All input.remaining() bytes starting at + * input.position() are processed. The result is stored + * in the output buffer. + * Upon return, the input buffer's position will be equal + * to its limit; its limit will not have changed. The output buffer's + * position will have advanced by n, where n is the value returned + * by this method; the output buffer's limit will not have changed. + * + *

If output.remaining() bytes are insufficient to + * hold the result, a ShortBufferException is thrown. + * + *

Subclasses should consider overriding this method if they can + * process ByteBuffers more efficiently than byte arrays. + * + * @param input the input ByteBuffer + * @param output the output ByteByffer + * + * @return the number of bytes stored in output + * + * @exception ShortBufferException if there is insufficient space in the + * output buffer + * + * @throws NullPointerException if either parameter is null + * @since 1.5 + */ + protected int engineUpdate(ByteBuffer input, ByteBuffer output) + throws ShortBufferException { + try { + return bufferCrypt(input, output, true); + } catch (IllegalBlockSizeException e) { + // never thrown for engineUpdate() + throw new ProviderException("Internal error in update()"); + } catch (BadPaddingException e) { + // never thrown for engineUpdate() + throw new ProviderException("Internal error in update()"); + } + } + + /** + * Finalize crypto operation with ByteBuffers + * + * @param input the input ByteBuffer + * @param output the output ByteBuffer + * + * @return output length + * @throws ShortBufferException + * @throws IllegalBlockSizeException + * @throws BadPaddingException + */ + @Override + protected int engineDoFinal(ByteBuffer input, ByteBuffer output) + throws ShortBufferException, IllegalBlockSizeException, + BadPaddingException { + return bufferCrypt(input, output, false); + } + + /** + * Implementation for encryption using ByteBuffers. Used for both + * engineUpdate() and engineDoFinal(). + */ + private int bufferCrypt(ByteBuffer input, ByteBuffer output, + boolean isUpdate) throws ShortBufferException, + IllegalBlockSizeException, BadPaddingException { + if ((input == null) || (output == null)) { + throw new NullPointerException + ("Input and output buffers must not be null"); + } + int inPos = input.position(); + int inLimit = input.limit(); + int inLen = inLimit - inPos; + if (isUpdate && (inLen == 0)) { + return 0; + } + int outLenNeeded = getOutputSizeByOperation(inLen, !isUpdate); + + if (output.remaining() < outLenNeeded) { + throw new ShortBufferException("Need at least " + outLenNeeded + + " bytes of space in output buffer"); + } + + // detecting input and output buffer overlap may be tricky + // we can only write directly into output buffer when we + // are 100% sure it's safe to do so + + boolean a1 = input.hasArray(); + boolean a2 = output.hasArray(); + int total = 0; + + if (a1) { // input has an accessible byte[] + byte[] inArray = input.array(); + int inOfs = input.arrayOffset() + inPos; + + if (a2) { // output has an accessible byte[] + byte[] outArray = output.array(); + int outPos = output.position(); + int outOfs = output.arrayOffset() + outPos; + + // check array address and offsets and use temp output buffer + // if output offset is larger than input offset and + // falls within the range of input data + boolean useTempOut = false; + if (inArray == outArray && + ((inOfs < outOfs) && (outOfs < inOfs + inLen))) { + useTempOut = true; + outArray = new byte[outLenNeeded]; + outOfs = 0; + } + if (isUpdate) { + total = engineUpdate(inArray, inOfs, inLen, outArray, outOfs); + } else { + total = engineDoFinal(inArray, inOfs, inLen, outArray, outOfs); + } + if (useTempOut) { + output.put(outArray, outOfs, total); + } else { + // adjust output position manually + output.position(outPos + total); + } + // adjust input position manually + input.position(inLimit); + } else { // output does not have an accessible byte[] + byte[] outArray = null; + if (isUpdate) { + outArray = engineUpdate(inArray, inOfs, inLen); + } else { + outArray = engineDoFinal(inArray, inOfs, inLen); + } + if (outArray != null && outArray.length != 0) { + output.put(outArray); + total = outArray.length; + } + // adjust input position manually + input.position(inLimit); + } + } else { // input does not have an accessible byte[] + // have to assume the worst, since we have no way of determine + // if input and output overlaps or not + byte[] tempOut = new byte[outLenNeeded]; + int outOfs = 0; + + byte[] tempIn = new byte[getTempArraySize(inLen)]; + do { + int chunk = Math.min(inLen, tempIn.length); + if (chunk > 0) { + input.get(tempIn, 0, chunk); + } + int n; + if (isUpdate || (inLen > chunk)) { + n = engineUpdate(tempIn, 0, chunk, tempOut, outOfs); + } else { + n = engineDoFinal(tempIn, 0, chunk, tempOut, outOfs); + } + outOfs += n; + total += n; + inLen -= chunk; + } while (inLen > 0); + if (total > 0) { + output.put(tempOut, 0, total); + } + } + + return total; + } +} + diff --git a/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEUtils.java b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEUtils.java new file mode 100644 index 000000000..a4a005285 --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/classes/org/openeuler/security/openssl/KAEUtils.java @@ -0,0 +1,220 @@ +/* + * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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. + */ + +package org.openeuler.security.openssl; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.security.*; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.*; + +class KAEUtils { + enum MessageDigestType { + MD2("MD2", "md2", 16), + MD5("MD5", "md5", 16), + SHA1("SHA-1", "sha1", 20, + new HashSet<>(Arrays.asList("SHA1", "1.3.14.3.2.26", "OID.1.3.14.3.2.26"))), + SHA224("SHA-224", "sha224", 28, + new HashSet<>(Arrays.asList("2.16.840.1.101.3.4.2.4", "OID.2.16.840.1.101.3.4.2.4"))), + SHA256("SHA-256", "sha256", 32, + new HashSet<>(Arrays.asList("2.16.840.1.101.3.4.2.1", "OID.2.16.840.1.101.3.4.2.1"))), + SHA384("SHA-384", "sha384", 48, + new HashSet<>(Arrays.asList("2.16.840.1.101.3.4.2.2", "OID.2.16.840.1.101.3.4.2.2"))), + SHA512("SHA-512", "sha512", 64, + new HashSet<>(Arrays.asList("2.16.840.1.101.3.4.2.3", "OID.2.16.840.1.101.3.4.2.3"))), + SHA512_224("SHA-512/224", "sha512-224", 28, + new HashSet<>(Arrays.asList("2.16.840.1.101.3.4.2.5", "OID.2.16.840.1.101.3.4.2.5"))), + SHA_512_256("SHA-512/256", "sha512-256", 32, + new HashSet<>(Arrays.asList("2.16.840.1.101.3.4.2.6", "OID.2.16.840.1.101.3.4.2.6"))); + + final String digestName; + final String kaeDigestName; + final int digestLen; + final Set aliasNames; + + public String getDigestName() { + return digestName; + } + + public String getKaeDigestName() { + return kaeDigestName; + } + + public int getDigestLen() { + return digestLen; + } + + public Set getAliasNames() { + return aliasNames; + } + + MessageDigestType(String digestName, String kaeDigestName, int digestLen, Set aliasNames) { + this.digestName = digestName; + this.kaeDigestName = kaeDigestName; + this.digestLen = digestLen; + this.aliasNames = aliasNames; + } + + MessageDigestType(String digestName, String kaeDigestName, int digestLen) { + this(digestName, kaeDigestName, digestLen, Collections.emptySet()); + } + } + + /** + * kae digest algorithm info map + */ + private static final Map DIGEST_ALGORITHM_NAME_MAP = new HashMap<>(); + private static final Map DIGEST_ALGORITHM_LENGTH_MAP = new HashMap<>(); + + private static final Map SIZE_TO_CURVE = new HashMap<>(); + private static final Map CURVE_ALIAS = new HashMap<>(); + + static { + initDigest(); + initECDH(); + } + + private static void initDigest() { + MessageDigestType[] messageDigestTypes = MessageDigestType.values(); + for (MessageDigestType messageDigestType : messageDigestTypes) { + DIGEST_ALGORITHM_NAME_MAP.put(messageDigestType.getDigestName(), messageDigestType.getKaeDigestName()); + DIGEST_ALGORITHM_LENGTH_MAP.put(messageDigestType.getDigestName(), messageDigestType.getDigestLen()); + for (String aliasName : messageDigestType.getAliasNames()) { + DIGEST_ALGORITHM_NAME_MAP.put(aliasName, messageDigestType.getKaeDigestName()); + DIGEST_ALGORITHM_LENGTH_MAP.put(aliasName, messageDigestType.getDigestLen()); + } + } + } + + // get the kae digest algorithm name + static String getKAEDigestName(String digestName) { + return digestName == null ? null : DIGEST_ALGORITHM_NAME_MAP.get(digestName.toUpperCase(Locale.ROOT)); + } + + static Integer getDigestLength(String digestName) { + return digestName == null ? null : DIGEST_ALGORITHM_LENGTH_MAP.get(digestName.toUpperCase(Locale.ROOT)); + } + + static class ConstructKeys { + /** + * Construct a public key from its encoding. + * + * @param encodedKey the encoding of a public key. + * @param encodedKeyAlgorithm the algorithm the encodedKey is for. + * @return a public key constructed from the encodedKey. + */ + private static PublicKey constructPublicKey(byte[] encodedKey, + String encodedKeyAlgorithm) + throws InvalidKeyException, NoSuchAlgorithmException { + try { + KeyFactory keyFactory = + KeyFactory.getInstance(encodedKeyAlgorithm); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encodedKey); + return keyFactory.generatePublic(keySpec); + } catch (NoSuchAlgorithmException nsae) { + throw new NoSuchAlgorithmException("No installed providers " + + "can create keys for the " + + encodedKeyAlgorithm + + "algorithm", nsae); + } catch (InvalidKeySpecException ike) { + throw new InvalidKeyException("Cannot construct public key", ike); + } + } + + /** + * Construct a private key from its encoding. + * + * @param encodedKey the encoding of a private key. + * @param encodedKeyAlgorithm the algorithm the wrapped key is for. + * @return a private key constructed from the encodedKey. + */ + private static PrivateKey constructPrivateKey(byte[] encodedKey, + String encodedKeyAlgorithm) throws InvalidKeyException, + NoSuchAlgorithmException { + try { + KeyFactory keyFactory = + KeyFactory.getInstance(encodedKeyAlgorithm); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedKey); + return keyFactory.generatePrivate(keySpec); + } catch (NoSuchAlgorithmException nsae) { + throw new NoSuchAlgorithmException("No installed providers " + + "can create keys for the " + + encodedKeyAlgorithm + + "algorithm", nsae); + } catch (InvalidKeySpecException ike) { + throw new InvalidKeyException("Cannot construct private key", ike); + } + } + + /** + * Construct a secret key from its encoding. + * + * @param encodedKey the encoding of a secret key. + * @param encodedKeyAlgorithm the algorithm the secret key is for. + * @return a secret key constructed from the encodedKey. + */ + private static SecretKey constructSecretKey(byte[] encodedKey, + String encodedKeyAlgorithm) { + return new SecretKeySpec(encodedKey, encodedKeyAlgorithm); + } + + static Key constructKey(byte[] encoding, String keyAlgorithm, + int keyType) throws InvalidKeyException, NoSuchAlgorithmException { + switch (keyType) { + case Cipher.SECRET_KEY: + return constructSecretKey(encoding, keyAlgorithm); + case Cipher.PRIVATE_KEY: + return constructPrivateKey(encoding, keyAlgorithm); + case Cipher.PUBLIC_KEY: + return constructPublicKey(encoding, keyAlgorithm); + default: + throw new InvalidKeyException("Unknown keytype " + keyType); + } + } + } + + private static void initECDH() { + SIZE_TO_CURVE.put(224, "secp224r1"); + SIZE_TO_CURVE.put(256, "prime256v1"); + SIZE_TO_CURVE.put(384, "secp384r1"); + SIZE_TO_CURVE.put(521, "secp521r1"); + CURVE_ALIAS.put("secp256r1", "prime256v1"); + CURVE_ALIAS.put("1.3.132.0.33", "secp224r1"); + CURVE_ALIAS.put("1.3.132.0.34", "secp384r1"); + CURVE_ALIAS.put("1.3.132.0.35", "secp521r1"); + CURVE_ALIAS.put("1.2.840.10045.3.1.7", "prime256v1"); + } + + static String getCurveBySize(int size) { + return SIZE_TO_CURVE.get(size); + } + + static String getCurveByAlias(String alias) { + return CURVE_ALIAS.get(alias); + } +} diff --git a/src/jdk.crypto.kaeprovider/linux/conf/security/kaeprovider.conf b/src/jdk.crypto.kaeprovider/linux/conf/security/kaeprovider.conf new file mode 100644 index 000000000..49ff98fd8 --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/conf/security/kaeprovider.conf @@ -0,0 +1,75 @@ +# +# This is the config file for KAEProvider. +# These configuration properties support the use of jdk system properties, +# and jdk system properties take precedence over file configuration properties. +# For detailed usage, please refer to the user manual: +# https://gitee.com/openeuler/bishengjdk-8/wikis/%E4%B8%AD%E6%96%87%E6%96%87%E6%A1%A3/KAE%20Provider%E7%94%A8%E6%88%B7%E4%BD%BF%E7%94%A8%E6%89%8B%E5%86%8C +# + +# Algorithms are enabled by default if KAEProvider is used. +# Delete # if you want to disable certain algorithm. +# kae.md5=false +# kae.sha256=false +# kae.sha384=false +# kae.sm3=false +# kae.aes=false +# kae.sm4=false +# kae.hmac=false +# kae.rsa=false +# kae.dh=false +# kae.ec=false + +# Configure engine id, the default value is kae. +# kae.engine.id=kae + +# Configure whether libcrypto.so uses GLOBAL mode, uses LOCAL mode by default. +# If you use uadk_engine, you need to enable this option. +# kae.libcrypto.useGlobalMode=false + +# The following configuration will only take effect when using KAEProvider. +# Configure whether to enable KAE hardware acceleration for each category of algorithm. +# The configurable value are as follows: +# true : enable KAE hardware acceleration by default +# false: use openssl soft calculation by default +# The digest/sm4/rsa/dh category algorithm enable KAE hardware acceleration by default. +# The aes/hmac/ec category algorithm use openssl soft calculation by default. +# The ec category algorithm configuration does not take effect temporarily. and it +# currently does not support KAE hardware acceleration, temporarily use openssl soft calculation. +# kae.digest.useKaeEngine=true +# kae.aes.useKaeEngine=false +# kae.sm4.useKaeEngine=true +# kae.hmac.useKaeEngine=false +# kae.rsa.useKaeEngine=true +# kae.dh.useKaeEngine=true +# kae.ec.useKaeEngine=false +# +# Some engines do not fully support certain categories of algorithms, for example, the digest +# algorithm implemented by kae engine only supports md5 and sm3.For more information, please refer to: +# KAE : https://github.com/kunpengcompute/KAE#:~:text=Digest%20algorithm%3A%20SM3/MD5 +# UADK: https://gitee.com/openeuler/uadk/wikis/%E4%BD%BF%E7%94%A8%E6%96%87%E6%A1%A3/UADK%20quick%20start#11-uadk +# +# Users can disable unsupported algorithms through the following property configuration. +# Disable algorithm to enable KAE hardware acceleration, use openssl soft algorithm instead. +# The sha256, sha384 algorithms are disabled by default. +# digest : md5,sha256,sha384,sm3 +# aes : aes-128-ecb,aes-128-cbc,aes-128-ctr,aes-128-gcm, +# aes-192-ecb,aes-192-cbc,aes-192-ctr,aes-192-gcm, +# aes-256-ecb,aes-256-cbc,aes-256-ctr,aes-256-gcm +# sm4 : sm4-ecb,sm4-cbc,sm4-ctr,sm4-ofb +# hmac : hmac-md5,hmac-sha1,hmac-sha224,hmac-sha256,hmac-sha384,hmac-sha512 +# rsa : rsa +# dh : dh +# ec : ec +# kae.engine.disabledAlgorithms=sha256,sha384 + +# SM4 max chunk size of each encryption or decryption. +# when input data does not have an accessible byte[]. +# The default value is 4096, when configuring a non-positive Integer type, use the default value of 4096. +# kae.sm4.maxChunkSize=4096 + +# Enable engine load log. +# kae.log=true +# +# It only takes effect when the property kae.log value is true. +# Configure log file path, default value is System.getProperty("user.dir") + "/ + "kae.log". +# kae.log.file=/home/user/kae.log \ No newline at end of file diff --git a/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_cipher_rsa.c b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_cipher_rsa.c new file mode 100644 index 000000000..557d3965b --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_cipher_rsa.c @@ -0,0 +1,471 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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. + */ + +#include +#include +#include +#include "kae_log.h" +#include "kae_util.h" +#include "kae_exception.h" +#include "org_openeuler_security_openssl_KAERSACipher.h" + +typedef int RSACryptOperation(int, const unsigned char*, unsigned char*, RSA*, int); + +typedef int EvpPkeyCryptOperation(EVP_PKEY_CTX*, unsigned char*, size_t*, const unsigned char*, size_t); + +typedef int EvpPkeyCryptInitOperation(EVP_PKEY_CTX*); + +/* + * RSA encrypt or decrypt for NoPadding or PKCS1Padding , follow the steps below + * + */ +static int RSACryptNotOAEPPadding(JNIEnv* env, jlong keyAddress, jint inLen, jbyteArray in, jbyteArray out, + jint paddingType, RSACryptOperation rsaCryptOperation, char* cryptName) { + jbyte* inBytes = NULL; + jbyte* outBytes = NULL; + int resultSize = 0; + + // get RSA + EVP_PKEY* pkey = (EVP_PKEY*) keyAddress; + + // rsa = pkey->rsa + RSA* rsa = EVP_PKEY_get1_RSA(pkey); + if (rsa == NULL) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_get1_RSA", KAE_ThrowRuntimeException); + return 0; + } + + // do encrypt or decrypt + inBytes = (*env)->GetByteArrayElements(env, in, NULL); + if (inBytes == NULL) { + KAE_ThrowNullPointerException(env, "GetByteArrayElements failed"); + goto cleanup; + } + outBytes = (*env)->GetByteArrayElements(env, out, NULL); + if (outBytes == NULL) { + KAE_ThrowNullPointerException(env, "GetByteArrayElements failed"); + goto cleanup; + } + resultSize = rsaCryptOperation(inLen, (unsigned char*)inBytes, (unsigned char*)outBytes, rsa, paddingType); + if (resultSize <= 0) { + KAE_ThrowFromOpenssl(env, cryptName, KAE_ThrowBadPaddingException); + goto cleanup; + } + jsize outLen = (*env)->GetArrayLength(env, out); + (*env)->SetByteArrayRegion(env, out, 0, outLen, outBytes); + +cleanup: + if (outBytes != NULL) { + (*env)->ReleaseByteArrayElements(env, out, outBytes, 0); + } + if (inBytes != NULL) { + (*env)->ReleaseByteArrayElements(env, in, inBytes, 0); + } + if (rsa != NULL) { + RSA_free(rsa); + } + return resultSize; +} + +/* + * set rsa padding + */ +static bool SetRSAPadding(JNIEnv* env, EVP_PKEY_CTX* pkeyCtx, int paddingType) { + if (EVP_PKEY_CTX_set_rsa_padding(pkeyCtx, paddingType) <= 0) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_CTX_set_rsa_padding", KAE_ThrowInvalidAlgorithmParameterException); + return false; + } + return true; +} + +/* + * set rsa mgf1 md + */ +static bool SetRSAMgf1Md(JNIEnv* env, EVP_PKEY_CTX* pkeyCtx, const char* mgf1MdAlgoUTF) { + EVP_MD* mgf1MD = (EVP_MD*)EVP_get_digestbyname(mgf1MdAlgoUTF); + if (mgf1MD == NULL) { + KAE_ThrowFromOpenssl(env, "EVP_get_digestbyname", KAE_ThrowInvalidAlgorithmParameterException); + return false; + } + if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkeyCtx, mgf1MD) <= 0) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_CTX_set_rsa_mgf1_md", KAE_ThrowInvalidAlgorithmParameterException); + return false; + } + return true; +} + +/* + * set rsa oaep md + */ +static bool SetRSAOaepMd(JNIEnv* env, EVP_PKEY_CTX* pkeyCtx, const char* oaepMdAlgoUTF) { + EVP_MD* oaepMD = (EVP_MD*)EVP_get_digestbyname(oaepMdAlgoUTF); + if (oaepMD == NULL) { + KAE_ThrowFromOpenssl(env, "EVP_get_digestbyname", KAE_ThrowInvalidAlgorithmParameterException); + return false; + } + if (EVP_PKEY_CTX_set_rsa_oaep_md(pkeyCtx, oaepMD) <= 0) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_CTX_set_rsa_oaep_md", KAE_ThrowInvalidAlgorithmParameterException); + return false; + } + return true; +} + +/* + * set rsa oaep label + */ +static bool SetRSAOaepLabel(JNIEnv* env, EVP_PKEY_CTX* pkeyCtx, jbyte* labelBytes, jsize labelSize) { + if (EVP_PKEY_CTX_set0_rsa_oaep_label(pkeyCtx, labelBytes, labelSize) <= 0) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_CTX_set0_rsa_oaep_label", KAE_ThrowInvalidAlgorithmParameterException); + return false; + } + return true; +} + +/* + * release rsa oaep temp resource + */ +static void ReleaseRSACryptOAEPResource(JNIEnv* env, EVP_PKEY_CTX* pkeyCtx, + jstring mgf1MdAlgo, const char* mgf1MdAlgoUTF, jstring oaepMdAlgo, const char* oaepMdAlgoUTF, + jbyteArray in, jbyte* inBytes, jbyteArray out, jbyte* outBytes) { + if (mgf1MdAlgoUTF != NULL) { + (*env)->ReleaseStringUTFChars(env, mgf1MdAlgo, mgf1MdAlgoUTF); + } + if (oaepMdAlgoUTF != NULL) { + (*env)->ReleaseStringUTFChars(env, oaepMdAlgo, oaepMdAlgoUTF); + } + if (outBytes != NULL) { + (*env)->ReleaseByteArrayElements(env, out, outBytes, 0); + } + if (inBytes != NULL) { + (*env)->ReleaseByteArrayElements(env, in, inBytes, 0); + } + EVP_PKEY_CTX_free(pkeyCtx); +} + +static int RSACryptOAEPPadding(JNIEnv* env, jlong keyAddress, jint inLen, jbyteArray in, jbyteArray out, + jint paddingType, jstring oaepMdAlgo, jstring mgf1MdAlgo, jbyteArray label, + EvpPkeyCryptInitOperation cryptInitOperation, char* cryptInitName, + EvpPkeyCryptOperation cryptOperation, char* cryptName) { + EVP_PKEY_CTX* pkeyCtx = NULL; + const char* mgf1MdAlgoUTF = NULL; + const char* oaepMdAlgoUTF = NULL; + jbyte* labelBytes = NULL; + jbyte* outBytes = NULL; + jbyte* inBytes = NULL; + // outLen type should be size_t + // EVP_PKEY_encrypt takes the outLen address as a parameter, and the parameter type is size_t* + // You can refer to the issue #2774 to see more content + size_t outLen = 0; + ENGINE* kaeEngine = GetEngineByAlgorithmIndex(RSA_INDEX); + KAE_TRACE("RSACryptOAEPPadding: kaeEngine => %p", kaeEngine); + + + EVP_PKEY* pkey = (EVP_PKEY*) keyAddress; + + // new ctx + // rsa encrypt/decrypt init + if ((pkeyCtx = EVP_PKEY_CTX_new(pkey, kaeEngine)) == NULL || cryptInitOperation(pkeyCtx) <= 0) { + KAE_ThrowFromOpenssl(env, pkeyCtx == NULL ? "EVP_PKEY_CTX_new" : cryptInitName, KAE_ThrowInvalidKeyException); + goto cleanup; + } + + if ((mgf1MdAlgoUTF = (*env)->GetStringUTFChars(env, mgf1MdAlgo, 0)) == NULL || + (oaepMdAlgoUTF = (*env)->GetStringUTFChars(env, oaepMdAlgo, 0)) == NULL) { + KAE_ThrowOOMException(env, "GetStringUTFChars failed"); + goto cleanup; + } + + /* + * set padding type + * set rsa mgf1 md + * set rsa oaep md + */ + if (!SetRSAPadding(env, pkeyCtx, paddingType) || !SetRSAMgf1Md(env, pkeyCtx, mgf1MdAlgoUTF) || + !SetRSAOaepMd(env, pkeyCtx, oaepMdAlgoUTF)) { + goto cleanup; + } + + // set rsa oaep label + jsize labelSize = (*env)->GetArrayLength(env, label); + if (labelSize > 0) { + // EVP_PKEY_CTX_free will free the labelBytes, so we can not free labelBytes when cleanup. + // Only SetRSAOaepLabel failed , free labelBytes. + if ((labelBytes = malloc(labelSize)) == NULL) { + KAE_ThrowNullPointerException(env, "malloc failed"); + goto cleanup; + } + (*env)->GetByteArrayRegion(env, label, 0, labelSize, labelBytes); + if (!SetRSAOaepLabel(env, pkeyCtx, labelBytes, labelSize)) { + free(labelBytes); + goto cleanup; + } + } + + // do encrypt/decrypt + outLen = (size_t)(*env)->GetArrayLength(env, out); + if ((outBytes = (*env)->GetByteArrayElements(env, out, NULL)) == NULL || + (inBytes = (*env)->GetByteArrayElements(env, in, NULL)) == NULL) { + KAE_ThrowNullPointerException(env, "GetByteArrayElements failed"); + goto cleanup; + } + if (cryptOperation(pkeyCtx, (unsigned char*)outBytes, &outLen, (unsigned char*)inBytes, inLen) <= 0) { + KAE_ThrowFromOpenssl(env, cryptName, KAE_ThrowBadPaddingException); + goto cleanup; + } + (*env)->SetByteArrayRegion(env, out, 0, outLen, outBytes); + +cleanup: + ReleaseRSACryptOAEPResource(env, pkeyCtx, mgf1MdAlgo, mgf1MdAlgoUTF, oaepMdAlgo, oaepMdAlgoUTF, + in, inBytes, out, outBytes); + return outLen; +} + +/* + * Release rsa param n,e,d,p,q,dmp1,dmq1,iqmp + */ +void ReleaseRSAParams(BIGNUM* bnN, BIGNUM* bnE, BIGNUM* bnD, BIGNUM* bnP, BIGNUM* bnQ, + BIGNUM* bnDMP1, BIGNUM* bnDMQ1, BIGNUM* bnIQMP) { + KAE_ReleaseBigNumFromByteArray(bnN); + KAE_ReleaseBigNumFromByteArray(bnE); + KAE_ReleaseBigNumFromByteArray(bnD); + KAE_ReleaseBigNumFromByteArray(bnP); + KAE_ReleaseBigNumFromByteArray(bnQ); + KAE_ReleaseBigNumFromByteArray(bnDMP1); + KAE_ReleaseBigNumFromByteArray(bnDMQ1); + KAE_ReleaseBigNumFromByteArray(bnIQMP); +} + +/* + * Create rsa private crt key + * Class: org_openeuler_security_openssl_KAERSACipher + * Method: nativeCreateRSAPrivateCrtKey + * Signature: ([B[B[B[B[B[B[B[B)J + */ +JNIEXPORT jlong JNICALL Java_org_openeuler_security_openssl_KAERSACipher_nativeCreateRSAPrivateCrtKey(JNIEnv* env, + jclass cls, jbyteArray n, jbyteArray e, jbyteArray d, jbyteArray p, jbyteArray q, + jbyteArray dmp1, jbyteArray dmq1, jbyteArray iqmp) { + BIGNUM* bnN = NULL; + BIGNUM* bnE = NULL; + BIGNUM* bnD = NULL; + BIGNUM* bnP = NULL; + BIGNUM* bnQ = NULL; + BIGNUM* bnDMP1 = NULL; + BIGNUM* bnDMQ1 = NULL; + BIGNUM* bnIQMP = NULL; + RSA* rsa = NULL; + EVP_PKEY* pkey = NULL; + ENGINE* kaeEngine = GetEngineByAlgorithmIndex(RSA_INDEX); + KAE_TRACE("KAERSACipher_nativeCreateRSAPrivateCrtKey: kaeEngine => %p", kaeEngine); + + // convert to big num + if ((bnN = KAE_GetBigNumFromByteArray(env, n)) == NULL || + (bnE = KAE_GetBigNumFromByteArray(env, e)) == NULL || + (bnD = KAE_GetBigNumFromByteArray(env, d)) == NULL || + (bnP = KAE_GetBigNumFromByteArray(env, p)) == NULL || + (bnQ = KAE_GetBigNumFromByteArray(env, q)) == NULL || + (bnDMP1 = KAE_GetBigNumFromByteArray(env, dmp1)) == NULL || + (bnDMQ1 = KAE_GetBigNumFromByteArray(env, dmq1)) == NULL || + (bnIQMP = KAE_GetBigNumFromByteArray(env, iqmp)) == NULL) { + goto cleanup; + } + + // new pkey + pkey = EVP_PKEY_new(); + if (pkey == NULL) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_new", KAE_ThrowRuntimeException); + goto cleanup; + } + + // new rsa + rsa = RSA_new_method(kaeEngine); + if (rsa == NULL) { + KAE_ThrowFromOpenssl(env, "RSA_new_method", KAE_ThrowRuntimeException); + goto cleanup; + } + + // set rsa private crt key params n,e,d,p,q,dmp1,dmp1,iqmp + if (RSA_set0_key(rsa, bnN, bnE, bnD) <= 0 || + RSA_set0_factors(rsa, bnP, bnQ) <= 0 || + RSA_set0_crt_params(rsa, bnDMP1, bnDMQ1, bnIQMP) <= 0) { + KAE_ThrowFromOpenssl(env, "RSA set param", KAE_ThrowRuntimeException); + goto cleanup; + } + + // assign rsa to pkey + int result = EVP_PKEY_assign_RSA(pkey, rsa); + if (result <= 0) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_assign_RSA", KAE_ThrowRuntimeException); + goto cleanup; + } + return (jlong)pkey; +cleanup: + ReleaseRSAParams(bnN, bnE, bnD, bnP, bnQ, bnDMP1, bnDMQ1, bnIQMP); + RSA_free(rsa); + EVP_PKEY_free(pkey); + return 0; +} + +/* + * Create rsa public key + * Class: org_openeuler_security_openssl_KAERSACipher + * Method: nativeCreateRSAPublicKey + * Signature: ([B[B)J + */ +JNIEXPORT jlong JNICALL Java_org_openeuler_security_openssl_KAERSACipher_nativeCreateRSAPublicKey( + JNIEnv* env, jclass cls, jbyteArray n, jbyteArray e) { + BIGNUM* bnN = NULL; + BIGNUM* bnE = NULL; + RSA* rsa = NULL; + EVP_PKEY* pkey = NULL; + ENGINE* kaeEngine = GetEngineByAlgorithmIndex(RSA_INDEX); + KAE_TRACE("KAERSACipher_nativeCreateRSAPublicKey: kaeEngine => %p", kaeEngine); + + // get public key param n + bnN = KAE_GetBigNumFromByteArray(env, n); + if (bnN == NULL) { + goto cleanup; + } + + // get public key param e + bnE = KAE_GetBigNumFromByteArray(env, e); + if (bnE == NULL) { + goto cleanup; + } + + // new rsa + rsa = RSA_new_method(kaeEngine); + if (rsa == NULL) { + KAE_ThrowFromOpenssl(env, "RSA_new_method", KAE_ThrowRuntimeException); + goto cleanup; + } + + // new EVP_PKEY + pkey = EVP_PKEY_new(); + if (pkey == NULL) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_new", KAE_ThrowRuntimeException); + goto cleanup; + } + + // set rsa public key params n and e + if (RSA_set0_key(rsa, bnN, bnE, NULL) <= 0) { + KAE_ThrowFromOpenssl(env, "RSA_set0_key", KAE_ThrowRuntimeException); + goto cleanup; + } + + // assign rsa to pkey + int result = EVP_PKEY_assign_RSA(pkey, rsa); + if (result <= 0) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_assign_RSA", KAE_ThrowRuntimeException); + goto cleanup; + } + return (jlong)pkey; +cleanup: + KAE_ReleaseBigNumFromByteArray(bnN); + KAE_ReleaseBigNumFromByteArray(bnE); + RSA_free(rsa); + EVP_PKEY_free(pkey); + return 0; +} + +/* + * Class: org_openeuler_security_openssl_KAERSACipher + * Method: nativeRSAPrivateEncrypt + * Signature: (JI[B[BI)I + */ +JNIEXPORT jint JNICALL Java_org_openeuler_security_openssl_KAERSACipher_nativeRSAPrivateEncrypt(JNIEnv* env, + jclass cls, jlong keyAddress, jint inLen, jbyteArray in, jbyteArray out, jint paddingType) { + return RSACryptNotOAEPPadding(env, keyAddress, inLen, in, out, paddingType, RSA_private_encrypt, + "RSA_private_encrypt"); +} + +/* + * Class: org_openeuler_security_openssl_KAERSACipher + * Method: nativeRSAPrivateDecrypt + * Signature: (JI[B[BI)I + */ +JNIEXPORT jint JNICALL Java_org_openeuler_security_openssl_KAERSACipher_nativeRSAPrivateDecrypt(JNIEnv* env, + jclass cls, jlong keyAddress, jint inLen, jbyteArray in, jbyteArray out, jint paddingType) { + return RSACryptNotOAEPPadding(env, keyAddress, inLen, in, out, paddingType, RSA_private_decrypt, + "RSA_private_decrypt"); +} + +/* + * Class: org_openeuler_security_openssl_KAERSACipher + * Method: nativeRSAPublicEncrypt + * Signature: (JI[B[BI)I + */ +JNIEXPORT jint JNICALL Java_org_openeuler_security_openssl_KAERSACipher_nativeRSAPublicEncrypt(JNIEnv* env, + jclass cls, jlong keyAddress, jint inLen, jbyteArray in, jbyteArray out, jint paddingType) { + return RSACryptNotOAEPPadding(env, keyAddress, inLen, in, out, paddingType, RSA_public_encrypt, + "RSA_public_encrypt"); +} + +/* + * Class: org_openeuler_security_openssl_KAERSACipher + * Method: nativeRSAPublicDecrypt + * Signature: (JI[B[BI)I + */ +JNIEXPORT jint JNICALL Java_org_openeuler_security_openssl_KAERSACipher_nativeRSAPublicDecrypt(JNIEnv* env, + jclass cls, jlong keyAddress, jint inLen, jbyteArray in, jbyteArray out, jint paddingType) { + return RSACryptNotOAEPPadding(env, keyAddress, inLen, in, out, paddingType, RSA_public_decrypt, + "RSA_public_decrypt"); +} + +/* + * Class: org_openeuler_security_openssl_KAERSACipher + * Method: nativeRSAEncryptOAEPPading + * Signature: (JI[B[BI[B[B[B)I + */ +JNIEXPORT jint JNICALL Java_org_openeuler_security_openssl_KAERSACipher_nativeRSAEncryptOAEPPadding(JNIEnv* env, + jclass cls, jlong keyAddress, jint inLen, jbyteArray in, jbyteArray out, + jint paddingType, jstring oaepMdAlgo, jstring mgf1MdAlgo, jbyteArray label) { + return RSACryptOAEPPadding(env, keyAddress, inLen, in, out, paddingType, oaepMdAlgo, mgf1MdAlgo, label, + EVP_PKEY_encrypt_init, "EVP_PKEY_encrypt_init", + EVP_PKEY_encrypt, "EVP_PKEY_encrypt"); +} + +/* + * Class: org_openeuler_security_openssl_KAERSACipher + * Method: nativeRSADecryptOAEPPadding + * Signature: (JI[B[BILjava/lang/String;Ljava/lang/String;[B)I + */ +JNIEXPORT jint JNICALL Java_org_openeuler_security_openssl_KAERSACipher_nativeRSADecryptOAEPPadding(JNIEnv* env, + jclass cls, jlong keyAddress, jint inLen, jbyteArray in, jbyteArray out, jint paddingType, + jstring oaepMdAlgo, jstring mgf1MdAlgo, jbyteArray label) { + return RSACryptOAEPPadding(env, keyAddress, inLen, in, out, paddingType, oaepMdAlgo, mgf1MdAlgo, label, + EVP_PKEY_decrypt_init, "EVP_PKEY_decrypt_init", + EVP_PKEY_decrypt, "EVP_PKEY_decrypt"); +} + +/* + * Class: org_openeuler_security_openssl_KAERSACipher + * Method: nativeFreeKey + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_org_openeuler_security_openssl_KAERSACipher_nativeFreeKey(JNIEnv* env, + jclass cls, jlong keyAddress) { + EVP_PKEY* pkey = (EVP_PKEY*) keyAddress; + if (pkey != NULL) { + EVP_PKEY_free(pkey); + } +} \ No newline at end of file diff --git a/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_digest.c b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_digest.c new file mode 100644 index 000000000..82bf477da --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_digest.c @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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. + */ + +#include +#include +#include +#include "kae_exception.h" +#include "kae_log.h" +#include "kae_util.h" +#include "org_openeuler_security_openssl_KAEDigest.h" + +#define DIGEST_STACK_SIZE 1024 +#define DIGEST_CHUNK_SIZE 64*1024 +#define DIGEST_LENGTH_THRESHOLD 48 + +/* + * Class: org_openeuler_security_openssl_KAEDigest + * Method: nativeInit + * Signature: (Ljava/lang/String;)J + */ +JNIEXPORT jlong JNICALL +Java_org_openeuler_security_openssl_KAEDigest_nativeInit(JNIEnv *env, jclass cls, jstring algorithmName) +{ + EVP_MD_CTX* ctx = NULL; + ENGINE* kaeEngine = NULL; + + if (algorithmName == NULL) { + KAE_ThrowNullPointerException(env, "algorithm is null"); + return 0; + } + + // EVP_get_digestbyname + const char* algo_utf = (*env)->GetStringUTFChars(env, algorithmName, 0); + kaeEngine = GetDigestEngineByAlgorithmName(algo_utf); + KAE_TRACE("KAEDigest_nativeInit: kaeEngine => %p", kaeEngine); + EVP_MD* md = (EVP_MD*) EVP_get_digestbyname(algo_utf); + (*env)->ReleaseStringUTFChars(env, algorithmName, algo_utf); + if (md == NULL) { + KAE_TRACE("%s not supported", algo_utf); + return 0; + } + KAE_TRACE("KAEDigest_nativeInit: create md => %p", md); + + ctx = EVP_MD_CTX_create(); + if (ctx == NULL) { + KAE_ThrowOOMException(env, "create EVP_MD_CTX fail"); + return 0; + } + KAE_TRACE("KAEDigest_nativeInit: create ctx => %p", ctx); + + // EVP_DigestInit_ex + int result_code = EVP_DigestInit_ex(ctx, md, kaeEngine); + if (result_code == 0) { + KAE_ThrowFromOpenssl(env, "EVP_DigestInit_ex failed", KAE_ThrowRuntimeException); + goto cleanup; + } + KAE_TRACE("KAEDigest_nativeInit EVP_DigestInit_ex(ctx = %p, md = %p) success", ctx, md); + + KAE_TRACE("KAEDigest_nativeInit: finished"); + return (jlong) ctx; + +cleanup: + EVP_MD_CTX_destroy(ctx); + return 0; +} + +/* + * Class: org_openeuler_security_openssl_KAEDigest + * Method: nativeUpdate + * Signature: (Ljava/lang/String;J[BII)I + */ +JNIEXPORT void JNICALL +Java_org_openeuler_security_openssl_KAEDigest_nativeUpdate(JNIEnv *env, jclass cls, jlong ctxAddress, + jbyteArray input, jint offset, jint inLen) +{ + EVP_MD_CTX* ctx = (EVP_MD_CTX*) ctxAddress; + KAE_TRACE("KAEDigest_nativeUpdate(ctx = %p, input = %p, offset = %d, inLen = %d", ctx, input, offset, inLen); + if (ctx == NULL) { + return; + } + + jint in_offset = offset; + jint in_size = inLen; + int result_code = 0; + if (in_size <= DIGEST_STACK_SIZE) { // allocation on the stack + jbyte buffer[DIGEST_STACK_SIZE]; + (*env)->GetByteArrayRegion(env, input, offset, inLen, buffer); + result_code = EVP_DigestUpdate(ctx, buffer, inLen); + } else { // data chunk + jint remaining = in_size; + jint buf_size = (remaining >= DIGEST_CHUNK_SIZE) ? DIGEST_CHUNK_SIZE : remaining; + jbyte* buffer = malloc(buf_size); + if (buffer == NULL) { + KAE_ThrowOOMException(env, "malloc error"); + return; + } + while (remaining > 0) { + jint chunk_size = (remaining >= buf_size) ? buf_size : remaining; + (*env)->GetByteArrayRegion(env, input, in_offset, chunk_size, buffer); + result_code = EVP_DigestUpdate(ctx, buffer, chunk_size); + if (!result_code) { + break; + } + in_offset += chunk_size; + remaining -= chunk_size; + } + free(buffer); + } + if (!result_code) { + KAE_ThrowFromOpenssl(env, "EVP_DigestUpdate failed", KAE_ThrowRuntimeException); + return; + } + KAE_TRACE("KAEDigest_nativeUpdate EVP_DigestUpdate success"); + KAE_TRACE("KAEDigest_nativeUpdate: finished"); +} + +/* + * Class: org_openeuler_security_openssl_KAEDigest + * Method: nativeDigest + * Signature: (Ljava/lang/String;J[BII)I + */ +JNIEXPORT jint JNICALL +Java_org_openeuler_security_openssl_KAEDigest_nativeDigest(JNIEnv *env, jclass cls, + jlong ctxAddress, jbyteArray output, jint offset, jint len) +{ + EVP_MD_CTX* ctx = (EVP_MD_CTX*) ctxAddress; + KAE_TRACE("KAEDigest_nativeDigest(ctx = %p, output = %p, offset = %d, len = %d", ctx, output, offset, len); + unsigned char* md = NULL; + unsigned int bytesWritten = 0; + + if (ctx == NULL) { + return 0; + } + + if (len <= 0 || len > DIGEST_LENGTH_THRESHOLD) { + KAE_ThrowRuntimeException(env, "len out of length"); + return 0; + } + md = malloc(len); + if (md == NULL) { + KAE_ThrowOOMException(env, "malloc error"); + return 0; + } + + // EVP_DigestFinal_ex + int result_code = EVP_DigestFinal_ex(ctx, md, &bytesWritten); + if (result_code == 0) { + KAE_ThrowFromOpenssl(env, "EVP_DigestFinal_ex failed", KAE_ThrowRuntimeException); + goto cleanup; + } + KAE_TRACE("KAEDigest_nativeFinal EVP_DigestFinal_ex success, bytesWritten = %d", bytesWritten); + + (*env)->SetByteArrayRegion(env, output, offset, bytesWritten, (jbyte*) md); + + KAE_TRACE("KAEDigest_nativeFinal: finished"); + +cleanup: + free(md); + return bytesWritten; +} + +/* +* Class: org_openeuler_security_openssl_KAEDigest +* Method: nativeClone +* Signature: (J)J +*/ +JNIEXPORT jlong JNICALL +Java_org_openeuler_security_openssl_KAEDigest_nativeClone(JNIEnv *env, jclass cls, jlong ctxAddress) +{ + EVP_MD_CTX* ctx = (EVP_MD_CTX*) ctxAddress; + KAE_TRACE("KAEDigest_nativeClone: ctx = %p", ctx); + if (ctx == NULL) { + return 0; + } + + EVP_MD_CTX* ctxCopy = EVP_MD_CTX_create(); + if (ctxCopy == NULL) { + KAE_ThrowOOMException(env, "create EVP_MD_CTX fail"); + return 0; + } + KAE_TRACE("KAEDigest_nativeClone: create ctxCopy => %p", ctxCopy); + + int result_code = EVP_MD_CTX_copy_ex(ctxCopy, ctx); + if (result_code == 0) { + KAE_ThrowFromOpenssl(env, "EVP_MD_CTX_copy_ex failed", KAE_ThrowRuntimeException); + goto cleanup; + } + KAE_TRACE("KAEDigest_nativeClone EVP_MD_CTX_copy_ex(ctxCopy = %p, ctx = %p) success", ctxCopy, ctx); + KAE_TRACE("KAEDigest_nativeClone: finished"); + return (jlong) ctxCopy; + +cleanup: + EVP_MD_CTX_destroy(ctxCopy); + return 0; +} + +/* + * Class: org_openeuler_security_openssl_KAEDigest + * Method: nativeFree + * Signature: (J)V + */ +JNIEXPORT void JNICALL +Java_org_openeuler_security_openssl_KAEDigest_nativeFree(JNIEnv *env, jclass cls, jlong ctxAddress) +{ + EVP_MD_CTX* ctx = (EVP_MD_CTX*) ctxAddress; + KAE_TRACE("KAEDigest_nativeFree(ctx = %p)", ctx); + if (ctx != NULL) { + EVP_MD_CTX_destroy(ctx); + } + + KAE_TRACE("KAEDigest_nativeFree: finished"); +} \ No newline at end of file diff --git a/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_exception.c b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_exception.c new file mode 100644 index 000000000..c0579ebf4 --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_exception.c @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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. + */ + +#include +#include +#include +#include "kae_log.h" +#include "kae_exception.h" +#include "openssl_ad.h" + +void KAE_ThrowByName(JNIEnv* env, const char* name, const char* msg) { + jclass cls = (*env)->FindClass(env, name); + if (cls != 0) { + (*env)->ThrowNew(env, cls, msg); + (*env)->DeleteLocalRef(env, cls); + } +} + +void KAE_ThrowOOMException(JNIEnv* env, const char* msg) { + KAE_ThrowByName(env, "java/lang/OutOfMemoryError", msg); +} + +void KAE_ThrowNullPointerException(JNIEnv* env, const char* msg) { + KAE_ThrowByName(env, "java/lang/NullPointerException", msg); +} + +void KAE_ThrowArrayIndexOutOfBoundsException(JNIEnv* env, const char* msg) { + KAE_ThrowByName(env, "java/lang/ArrayIndexOutOfBoundsException", msg); +} + +void KAE_ThrowEvpException(JNIEnv* env, int reason, const char* msg, void (* defaultException)(JNIEnv*, const char*)) { + switch (reason) { + case EVP_R_UNSUPPORTED_ALGORITHM: + KAE_ThrowByName(env, "java/security/NoSuchAlgorithmException", msg); + break; + case EVP_R_MISSING_PARAMETERS: + KAE_ThrowByName(env, "java/security/InvalidKeyException", msg); + break; + case EVP_R_BAD_DECRYPT: + case EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH: + case EVP_F_EVP_PKEY_DECRYPT: + case EVP_R_PUBLIC_KEY_NOT_RSA: + case EVP_R_CTRL_NOT_IMPLEMENTED: + KAE_ThrowByName(env, "javax/crypto/BadPaddingException", msg); + break; + default: + defaultException(env, msg); + break; + } +} + +void KAE_ThrowRuntimeException(JNIEnv* env, const char* msg) { + KAE_ThrowByName(env, "java/lang/RuntimeException", msg); +} + +void KAE_ThrowBadPaddingException(JNIEnv* env, const char* msg) { + KAE_ThrowByName(env, "javax/crypto/BadPaddingException", msg); +} + +void KAE_ThrowInvalidKeyException(JNIEnv* env, const char* msg) { + KAE_ThrowByName(env, "java/security/InvalidKeyException", msg); +} + +void KAE_ThrowInvalidAlgorithmParameterException(JNIEnv* env, const char* msg) { + KAE_ThrowByName(env, "java/security/InvalidAlgorithmParameterException", msg); +} + +void KAE_ThrowFromOpenssl(JNIEnv* env, const char* msg, void (* defaultException)(JNIEnv*, const char*)) { + const char* file = NULL; + const char* data = NULL; + int line = 0; + int flags = 0; + unsigned long err; + static const int ESTRING_SIZE = 256; + + err = ERR_get_error_line_data(&file, &line, &data, &flags); + if (err == 0) { + defaultException(env, msg); + return; + } + + if (!(*env)->ExceptionCheck(env)) { + char estring[ESTRING_SIZE]; + ERR_error_string_n(err, estring, ESTRING_SIZE); + int lib = ERR_GET_LIB(err); + int func = ERR_GET_FUNC(err); + int reason = ERR_GET_REASON(err); + KAE_TRACE("OpenSSL error in %s: err=%lx, lib=%x, func=%x, reason=%x, file=%s, line=%d, estring=%s, data=%s", msg, err, + lib, func, reason, file, line, estring, (flags & ERR_TXT_STRING) ? data : "(no data)"); + // Ignore exceptions in RSA_verify_PKCS1_PSS_mgf1 function + if (lib == ERR_LIB_RSA && func == RSA_F_RSA_VERIFY_PKCS1_PSS_MGF1) { + return; + } + + if (lib == ERR_LIB_EVP || lib == ERR_LIB_RSA) { + KAE_ThrowEvpException(env, reason, estring, defaultException); + } else { + defaultException(env, estring); + } + } + + ERR_clear_error(); +} + +void KAE_ThrowAEADBadTagException(JNIEnv *env, const char *msg) { + KAE_ThrowByName(env, "javax/crypto/AEADBadTagException", msg); +} + +void KAE_ThrowSignatureException(JNIEnv* env, const char* msg) { + KAE_ThrowByName(env, "java/security/SignatureException", msg); +} + +void KAE_ThrowClassNotFoundException(JNIEnv* env, const char* msg) { + KAE_ThrowByName(env, "java/lang/ClassNotFoundException", msg); +} diff --git a/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_exception.h b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_exception.h new file mode 100644 index 000000000..f33f993e4 --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_exception.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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. + */ + +#ifndef KAE_EXCEPTION_H +#define KAE_EXCEPTION_H + +#include + +/* Throw a Java exception by name */ +void KAE_ThrowByName(JNIEnv* env, const char* name, const char* msg); + +void KAE_ThrowOOMException(JNIEnv* env, const char* msg); + +void KAE_ThrowNullPointerException(JNIEnv* env, const char* msg); + +void KAE_ThrowArrayIndexOutOfBoundsException(JNIEnv* env, const char* msg); + +void KAE_ThrowFromOpenssl(JNIEnv* env, const char* msg, void (* defaultException)(JNIEnv*, const char*)); + +void KAE_ThrowEvpException(JNIEnv* env, int reason, const char* msg, void (* defaultException)(JNIEnv*, const char*)); + +void KAE_ThrowRuntimeException(JNIEnv* env, const char* msg); + +void KAE_ThrowBadPaddingException(JNIEnv* env, const char* msg); + +/* Throw InvalidKeyException */ +void KAE_ThrowInvalidKeyException(JNIEnv* env, const char* msg); + +/* Throw AlgorithmParameterException */ +void KAE_ThrowInvalidAlgorithmParameterException(JNIEnv* env, const char* msg); + +void KAE_ThrowAEADBadTagException(JNIEnv* env, const char* msg); + +void KAE_ThrowSignatureException(JNIEnv* env, const char* msg); + +void KAE_ThrowClassNotFoundException(JNIEnv* env, const char* msg); +#endif diff --git a/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_hmac.c b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_hmac.c new file mode 100644 index 000000000..ad7b86ecd --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_hmac.c @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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. + */ + +#include +#include +#include +#include "kae_exception.h" +#include "kae_log.h" +#include "kae_util.h" + +static const EVP_MD* EVPGetDigestByName(JNIEnv* env, const char* algo) +{ + static const EVP_MD* md5 = NULL; + static const EVP_MD* sha1 = NULL; + static const EVP_MD* sha224 = NULL; + static const EVP_MD* sha256 = NULL; + static const EVP_MD* sha384 = NULL; + static const EVP_MD* sha512 = NULL; + + if (strcasecmp(algo, "md5") == 0) { + return md5 == NULL ? md5 = EVP_get_digestbyname(algo) : md5; + } else if (strcasecmp(algo, "sha1") == 0) { + return sha1 == NULL ? sha1 = EVP_get_digestbyname(algo) : sha1; + } else if (strcasecmp(algo, "sha224") == 0) { + return sha224 == NULL ? sha224 = EVP_get_digestbyname(algo) : sha224; + } else if (strcasecmp(algo, "sha256") == 0) { + return sha256 == NULL ? sha256 = EVP_get_digestbyname(algo) : sha256; + } else if (strcasecmp(algo, "sha384") == 0) { + return sha384 == NULL ? sha384 = EVP_get_digestbyname(algo) : sha384; + } else if (strcasecmp(algo, "sha512") == 0) { + return sha512 == NULL ? sha512 = EVP_get_digestbyname(algo) : sha512; + } else { + KAE_ThrowRuntimeException(env, "EVPGetDigestByName error"); + return 0; + } +} + +/* + * Class: org_openeuler_security_openssl_KAEHMac + * Method: nativeInit + * Signature: ([BILjava/lang/String;)J + */ +JNIEXPORT jlong JNICALL Java_org_openeuler_security_openssl_KAEHMac_nativeInit + (JNIEnv* env, jclass cls, jbyteArray key, jint key_len, jstring algoStr) { + if (key == NULL || algoStr == NULL) { + KAE_ThrowNullPointerException(env, "param key or algoStr is null"); + return 0; + } + if (key_len <= 0) { + KAE_ThrowArrayIndexOutOfBoundsException(env, "key"); + return 0; + } + HMAC_CTX* ctx = NULL; + jbyte* key_buffer = NULL; + const EVP_MD* md = NULL; + ENGINE* kaeEngine = NULL; + + const char* algo = (*env)->GetStringUTFChars(env, algoStr, 0); + md = EVPGetDigestByName(env, algo); + + kaeEngine = GetHmacEngineByAlgorithmName(algo); + KAE_TRACE("KAEHMac_nativeInit: kaeEngine => %p", kaeEngine); + + (*env)->ReleaseStringUTFChars(env, algoStr, algo); + if (md == NULL) { + KAE_ThrowRuntimeException(env, "algorithm unsupport"); + return 0; + } + + // get secret-key + key_buffer = malloc(key_len); + if (key_buffer == NULL) { + KAE_ThrowOOMException(env, "malloc failed"); + return 0; + } + (*env)->GetByteArrayRegion(env, key, 0, key_len, key_buffer); + + // create a hmac context + ctx = HMAC_CTX_new(); + if (ctx == NULL) { + KAE_ThrowRuntimeException(env, "Hmac_CTX_new invoked failed"); + goto cleanup; + } + + // init hmac context with sc_key and evp_md + int result_code = HMAC_Init_ex(ctx, key_buffer, key_len, md, kaeEngine); + if (result_code == 0) { + KAE_ThrowRuntimeException(env, "Hmac_Init_ex invoked failed"); + goto cleanup; + } + free(key_buffer); + return (jlong) ctx; + +cleanup: + free(key_buffer); + HMAC_CTX_free(ctx); + return 0; +} + +/* + * Class: org_openeuler_security_openssl_KAEHMac + * Method: nativeUpdate + * Signature: (J[BII)V + */ +JNIEXPORT void JNICALL Java_org_openeuler_security_openssl_KAEHMac_nativeUpdate + (JNIEnv* env, jclass cls, jlong hmac_ctx, jbyteArray input, jint in_offset, jint in_len) { + KAE_TRACE("KAEHMac_nativeUpdate(ctx = %p, input = %p, offset = %d, inLen = %d)", (void*) hmac_ctx, input, in_offset, in_len); + HMAC_CTX* ctx = (HMAC_CTX*) hmac_ctx; + if (ctx == NULL || input == NULL) { + KAE_ThrowNullPointerException(env, "param ctx or input is null"); + return; + } + int input_size = (*env)->GetArrayLength(env, input); + if ((in_offset < 0) || (in_len < 0) || (in_offset > input_size - in_len)) { + KAE_ThrowArrayIndexOutOfBoundsException(env, "input"); + return; + } + // do nothing while in_len is 0 + if (in_len == 0) { + return; + } + + jbyte* buffer = malloc(in_len); + if (buffer == NULL) { + KAE_ThrowOOMException(env, "malloc failed"); + return; + } + (*env)->GetByteArrayRegion(env, input, in_offset, in_len, buffer); + if (!HMAC_Update(ctx, (unsigned char*) buffer, in_len)) { + KAE_ThrowRuntimeException(env, "Hmac_Update invoked failed"); + } + free(buffer); +} + +/* + * Class: org_openeuler_security_openssl_KAEHMac + * Method: nativeFinal + * Signature: (J[BII)I + */ +JNIEXPORT jint JNICALL Java_org_openeuler_security_openssl_KAEHMac_nativeFinal + (JNIEnv* env, jclass cls, jlong hmac_ctx, jbyteArray output, jint out_offset, jint in_len) { + HMAC_CTX* ctx = (HMAC_CTX*) hmac_ctx; + if (ctx == NULL || output == NULL) { + KAE_ThrowNullPointerException(env, "param ctx or input is null"); + return 0; + } + int output_size = (*env)->GetArrayLength(env, output); + if ((out_offset < 0) || (in_len < 0) || (out_offset > output_size - in_len)) { + KAE_ThrowArrayIndexOutOfBoundsException(env, "output"); + return 0; + } + + jbyte* temp_result = NULL; + + temp_result = malloc(in_len); + if (temp_result == NULL) { + KAE_ThrowOOMException(env, "malloc failed"); + return 0; + } + // do final + unsigned int bytesWritten = 0; + int result_code = HMAC_Final(ctx, (unsigned char*) temp_result, &bytesWritten); + if (result_code == 0) { + KAE_ThrowRuntimeException(env, "Hmac_Final invoked failed"); + goto cleanup; + } + + // write back to output_array + (*env)->SetByteArrayRegion(env, output, out_offset, bytesWritten, (jbyte*) temp_result); + KAE_TRACE("KAEHMac_nativeFinal success, output_offset = %d, bytesWritten = %u", out_offset, bytesWritten); + +cleanup: + free(temp_result); + return bytesWritten; +} + +/* + * Class: org_openeuler_security_openssl_KAEHMac + * Method: nativeFree + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_org_openeuler_security_openssl_KAEHMac_nativeFree + (JNIEnv* env, jclass cls, jlong hmac_ctx) { + HMAC_CTX* ctx = (HMAC_CTX*) hmac_ctx; + if (ctx != NULL) { + HMAC_CTX_free(ctx); + } +} diff --git a/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keyagreement_dh.c b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keyagreement_dh.c new file mode 100644 index 000000000..e9dfa4094 --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keyagreement_dh.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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. + */ + +#include +#include +#include +#include +#include +#include +#include "kae_util.h" +#include "kae_exception.h" +#include "kae_log.h" +#include "org_openeuler_security_openssl_KAEDHKeyAgreement.h" + + +/* + * Class: org_openeuler_security_openssl_KAEDHKeyAgreement + * Method: nativeComputeKey + */ +JNIEXPORT jbyteArray JNICALL Java_org_openeuler_security_openssl_KAEDHKeyAgreement_nativeComputeKey(JNIEnv* env, + jobject obj, jbyteArray y, jbyteArray x, jbyteArray p, jbyteArray g, jint pSize) { + + KAE_TRACE("Java_org_openeuler_security_openssl_KAEDHKeyAgreement_nativeComputeKey start."); + + DH* dh = NULL; + BIGNUM* y_bn = NULL; + BIGNUM* x_bn = NULL; + BIGNUM* p_bn = NULL; + BIGNUM* g_bn = NULL; + BIGNUM* computeKeyRetBn = NULL; + int computekeyLength = 0; + unsigned char* secret = NULL; + jbyteArray retByteArray = NULL; + ENGINE* kaeEngine = GetEngineByAlgorithmIndex(DH_INDEX); + KAE_TRACE("KAEDHKeyAgreement_nativeComputeKey: kaeEngine => %p", kaeEngine); + + // bits to Bytes + int pSizeInByte = (pSize +7) >> 3; + + if ((secret = (unsigned char*)malloc(pSizeInByte)) == NULL) { + KAE_ThrowOOMException(env, "malloc secret failed."); + goto cleanup; + } + memset(secret, 0, pSizeInByte); + + if ((dh = DH_new_method(kaeEngine)) == NULL) { + KAE_ThrowOOMException(env, "Allocate DH failed in nativeComputeKey."); + goto cleanup; + } + + if ((y_bn = KAE_GetBigNumFromByteArray(env, y)) == NULL) { + KAE_ThrowOOMException(env, "Convert y to BIGNUM failed."); + goto cleanup; + } + + if ((x_bn = KAE_GetBigNumFromByteArray(env, x)) == NULL) { + KAE_ThrowOOMException(env, "Convert x to BIGNUM failed."); + goto cleanup; + } + + if ((p_bn = KAE_GetBigNumFromByteArray(env, p)) == NULL) { + KAE_ThrowOOMException(env, "Convert p to BIGNUM failed."); + goto cleanup; + } + + if ((g_bn = KAE_GetBigNumFromByteArray(env, g)) == NULL) { + KAE_ThrowOOMException(env, "Convert g to BIGNUM failed."); + goto cleanup; + } + + if ((computeKeyRetBn = BN_new()) == NULL) { + KAE_ThrowOOMException(env, "Allocate BN failed."); + goto cleanup; + } + + if (!DH_set0_pqg(dh, BN_dup(p_bn), NULL, BN_dup(g_bn))) { + KAE_ThrowRuntimeException(env, "DH_set0_pqg failed."); + goto cleanup; + } + + if (!DH_set0_key(dh, NULL, BN_dup(x_bn))) { + KAE_ThrowRuntimeException(env, "DH_set0_key failed."); + goto cleanup; + } + + computekeyLength = DH_compute_key(secret, y_bn, dh); + + if (computekeyLength <= 0 ) { + KAE_ThrowRuntimeException(env, "DH_compute_key failed."); + goto cleanup; + } + + BN_bin2bn(secret, computekeyLength, computeKeyRetBn); + + retByteArray = KAE_GetByteArrayFromBigNum(env, computeKeyRetBn); + if (retByteArray == NULL) { + KAE_ThrowRuntimeException(env, "GetByteArrayFromBigNum failed in nativeComputeKey."); + goto cleanup; + } + KAE_TRACE("Java_org_openeuler_security_openssl_KAEDHKeyAgreement_nativeComputeKey finished!"); + +cleanup: + if (dh != NULL) + DH_free(dh); + if (y_bn != NULL) + KAE_ReleaseBigNumFromByteArray(y_bn); + if (x_bn != NULL) + KAE_ReleaseBigNumFromByteArray(x_bn); + if (p_bn != NULL) + KAE_ReleaseBigNumFromByteArray(p_bn); + if (g_bn != NULL) + KAE_ReleaseBigNumFromByteArray(g_bn); + if (secret != NULL) { + memset(secret, 0, pSizeInByte); + free(secret); + } + if (computeKeyRetBn != NULL) + BN_free(computeKeyRetBn); + + return retByteArray; +} \ No newline at end of file diff --git a/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keyagreement_ecdh.c b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keyagreement_ecdh.c new file mode 100644 index 000000000..b0877a25e --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keyagreement_ecdh.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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. + */ + +#include +#include +#include +#include "kae_log.h" +#include "kae_exception.h" +#include "kae_util.h" +#include "org_openeuler_security_openssl_KAEECDHKeyAgreement.h" + +static void FreeGenerateSecretParam(BIGNUM* s, BIGNUM* wX, BIGNUM* wY, + EC_POINT* pub, EC_KEY* eckey, EC_GROUP* group, unsigned char* shareKey, int shareKeyLen) +{ + KAE_ReleaseBigNumFromByteArray(s); + KAE_ReleaseBigNumFromByteArray(wX); + KAE_ReleaseBigNumFromByteArray(wY); + if (pub != NULL) { + EC_POINT_free(pub); + } + if (eckey != NULL) { + EC_KEY_free(eckey); + } + if (group != NULL) { + EC_GROUP_free(group); + } + if (shareKey != NULL) { + memset(shareKey, 0, shareKeyLen); + free(shareKey); + } +} + +/* + * Class: org_openeuler_security_openssl_KAEECDHKeyAgreement + * Method: nativeGenerateSecret + * Signature: (Ljava/lang/String;[B[B[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_org_openeuler_security_openssl_KAEECDHKeyAgreement_nativeGenerateSecret + (JNIEnv* env, jclass cls, jstring curveName, jbyteArray wXArr, jbyteArray wYArr, jbyteArray sArr) +{ + EC_GROUP* group = NULL; + EC_KEY* eckey = NULL; + BIGNUM* wX = NULL; + BIGNUM* wY = NULL; + BIGNUM* s = NULL; + EC_POINT* pub = NULL; + jbyteArray javaBytes = NULL; + unsigned char* shareKey = NULL; + const char *curve = (*env)->GetStringUTFChars(env, curveName, 0); + int nid = OBJ_sn2nid(curve); + (*env)->ReleaseStringUTFChars(env, curveName, curve); + + // Initialization of secret key. + int expectSecretLen = 0; + + if ((nid == NID_undef) || (group = EC_GROUP_new_by_curve_name(nid)) == NULL) { + goto cleanup; + } + if ((s = KAE_GetBigNumFromByteArray(env, sArr)) == NULL || (wX = KAE_GetBigNumFromByteArray(env, wXArr)) == NULL + || (wY = KAE_GetBigNumFromByteArray(env, wYArr)) == NULL) { + KAE_ThrowOOMException(env, "failed to allocate BN_new"); + goto cleanup; + } + if ((eckey = EC_KEY_new()) == NULL || !EC_KEY_set_group(eckey, group)) { + goto cleanup; + } + if ((pub = EC_POINT_new(group)) == NULL) { + goto cleanup; + } + if (!EC_POINT_set_affine_coordinates_GFp(group, pub, wX, wY, NULL)) { + goto cleanup; + } + if (!EC_KEY_set_public_key(eckey, pub) || !EC_KEY_set_private_key(eckey, s)) { + goto cleanup; + } + + // Get the length of secret key, in bytes. + expectSecretLen = (EC_GROUP_get_degree(group) + 7) / 8; + + if ((shareKey = malloc(expectSecretLen)) == NULL) { + KAE_ThrowOOMException(env, "malloc error"); + goto cleanup; + } + memset(shareKey, 0, expectSecretLen); + + // Perform ecdh keyagreement. + if (ECDH_compute_key(shareKey, expectSecretLen, pub, eckey, NULL) != expectSecretLen) { + goto cleanup; + } + + if ((javaBytes = (*env)->NewByteArray(env, expectSecretLen)) == NULL) { + goto cleanup; + } + (*env)->SetByteArrayRegion(env, javaBytes, 0, expectSecretLen, (jbyte*)shareKey); + FreeGenerateSecretParam(s, wX, wY, pub, eckey, group, shareKey, expectSecretLen); + return javaBytes; + +cleanup: + FreeGenerateSecretParam(s, wX, wY, pub, eckey, group, shareKey, expectSecretLen); + return NULL; +} \ No newline at end of file diff --git a/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keypairgenerator_dh.c b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keypairgenerator_dh.c new file mode 100644 index 000000000..6315cc6ee --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keypairgenerator_dh.c @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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. + */ + +#include +#include +#include +#include +#include +#include "kae_util.h" +#include "kae_log.h" +#include "org_openeuler_security_openssl_KAEDHKeyPairGenerator.h" +#include "kae_exception.h" + + +/* +* Class: org_openeuler_security_openssl_KAEDHKeyPairGenerator +* Method: nativeGenerateKeyPair +* Signature: ([B[BI)[[B +*/ + +JNIEXPORT jobjectArray JNICALL Java_org_openeuler_security_openssl_KAEDHKeyPairGenerator_nativeGenerateKeyPair + (JNIEnv* env, jclass cls, jbyteArray p, jbyteArray g, jint lSize) +{ + DH* dh = NULL; + BIGNUM* p_bn = NULL; + BIGNUM* g_bn = NULL; + const BIGNUM* pri_key_bn = NULL; + const BIGNUM* pub_key_bn = NULL; + jclass byteArrayClass = NULL; + jobjectArray keys = NULL; + jbyteArray pri_key = NULL; + jbyteArray pub_key = NULL; + ENGINE* kaeEngine = GetEngineByAlgorithmIndex(DH_INDEX); + KAE_TRACE("KAEDHKeyPairGenerator_nativeGenerateKeyPair: kaeEngine => %p", kaeEngine); + + KAE_TRACE("Java_org_openeuler_security_openssl_KAEDHKeyPairGenerator_nativeGenerateKeyPair start !"); + + if ((dh = DH_new_method(kaeEngine)) == NULL) { + KAE_ThrowOOMException(env, "Allocate DH failed in nativeGenerateKeyPair!"); + goto cleanup; + } + + if ((p_bn = KAE_GetBigNumFromByteArray(env, p)) == NULL) { + KAE_ThrowOOMException(env, "Allocate p_bn failed in nativeGenerateKeyPair!"); + goto cleanup; + } + + if ((g_bn = KAE_GetBigNumFromByteArray(env, g)) == NULL) { + KAE_ThrowOOMException(env, "Allocate g_bn failed in nativeGenerateKeyPair!"); + goto cleanup; + } + + if (!DH_set0_pqg(dh, BN_dup(p_bn), NULL, BN_dup(g_bn))) { + KAE_ThrowRuntimeException(env, "DH_set0_pqg failed in nativeGenerateKeyPair."); + goto cleanup; + } + + // Return value is fixed to 1, nothing to check. + DH_set_length(dh, lSize); + + if (!DH_generate_key(dh)) { + KAE_ThrowInvalidAlgorithmParameterException(env, "DH generate key failed in nativeGenerateKeyPair."); + goto cleanup; + } + + if ((byteArrayClass = (*env)->FindClass(env, "[B")) == NULL) { + KAE_ThrowClassNotFoundException(env, "Class byte[] not found."); + goto cleanup; + } + + if ((keys = (*env)->NewObjectArray(env, 2, byteArrayClass, NULL)) == NULL) { + KAE_ThrowOOMException(env, "Allocate ByteArray failed in nativeGenerateKeyPair!"); + goto cleanup; + } + + // Return the ptr of private key in dh. + pri_key_bn = DH_get0_priv_key(dh); + pub_key_bn = DH_get0_pub_key(dh); + + pub_key = KAE_GetByteArrayFromBigNum(env, pub_key_bn); + if (pub_key == NULL) { + KAE_ThrowOOMException(env, "PublicKey allocate failed in nativeGenerateKeyPair."); + goto cleanup; + } + + pri_key = KAE_GetByteArrayFromBigNum(env, pri_key_bn); + if (pri_key == NULL) { + KAE_ThrowRuntimeException(env, "GetByteArrayFromBigNum failed in nativeGenerateKeyPair."); + goto cleanup; + } + + (*env)->SetObjectArrayElement(env, keys, 0, pub_key); + (*env)->SetObjectArrayElement(env, keys, 1, pri_key); + + KAE_TRACE("Java_org_openeuler_security_openssl_KAEDHKeyPairGenerator_nativeGenerateKeyPair finished !"); + +cleanup: + if (dh != NULL) + DH_free(dh); + if (p_bn != NULL) + KAE_ReleaseBigNumFromByteArray(p_bn); + if (g_bn != NULL) + KAE_ReleaseBigNumFromByteArray(g_bn); + if (byteArrayClass != NULL) + (*env)->DeleteLocalRef(env, byteArrayClass); + if (pub_key != NULL) + (*env)->DeleteLocalRef(env, pub_key); + if (pri_key != NULL) + (*env)->DeleteLocalRef(env, pri_key); + + return keys; +} \ No newline at end of file diff --git a/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keypairgenerator_ec.c b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keypairgenerator_ec.c new file mode 100644 index 000000000..0449f8a26 --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keypairgenerator_ec.c @@ -0,0 +1,505 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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. + */ + +#include +#include +#include +#include "kae_util.h" +#include "kae_exception.h" +#include "kae_log.h" +#include "org_openeuler_security_openssl_KAEECKeyPairGenerator.h" + +#define KAE_EC_PARAM_NUM_SIZE 7 +#define KAE_EC_KEY_NUM_SIZE 3 + +// ECDH param index. +typedef enum ECDHParamIndex { + ecdhP = 0, + ecdhA, + ecdhB, + ecdhX, + ecdhY, + ecdhOrder, + ecdhCofactor +} ECDHParamIndex; + +// ECDH Key index. +typedef enum ECDHKeyIndex { + ecdhWX = 0, + ecdhWY, + ecdhS +} ECDHKeyIndex; + +static void FreeECDHCurveParam(JNIEnv* env, BIGNUM* p, BIGNUM* a, BIGNUM* b, jbyteArray paramP, + jbyteArray paramA, jbyteArray paramB) +{ + if (p != NULL) { + BN_free(p); + } + if (a != NULL) { + BN_free(a); + } + if (b != NULL) { + BN_free(b); + } + if (paramP != NULL) { + (*env)->DeleteLocalRef(env, paramP); + } + if (paramA != NULL) { + (*env)->DeleteLocalRef(env, paramA); + } + if (paramB != NULL) { + (*env)->DeleteLocalRef(env, paramB); + } +} + +// Set p, a, b in group to params. +static bool SetECDHCurve(JNIEnv* env, EC_GROUP* group, jobjectArray params) +{ + BIGNUM* p = NULL; + BIGNUM* a = NULL; + BIGNUM* b = NULL; + jbyteArray paramP = NULL; + jbyteArray paramA = NULL; + jbyteArray paramB = NULL; + if ((p = BN_new()) == NULL || (a = BN_new()) == NULL || (b = BN_new()) == NULL) { + KAE_ThrowOOMException(env, "failed to allocate BN_new"); + goto cleanup; + } + if (!EC_GROUP_get_curve_GFp(group, p, a, b, NULL)) { + goto cleanup; + } + + // Set p. + if ((paramP = KAE_GetByteArrayFromBigNum(env, p)) == NULL) { + goto cleanup; + } + (*env)->SetObjectArrayElement(env, params, ecdhP, paramP); + + // Set a. + if ((paramA = KAE_GetByteArrayFromBigNum(env, a)) == NULL) { + goto cleanup; + } + (*env)->SetObjectArrayElement(env, params, ecdhA, paramA); + + // Set b. + if ((paramB = KAE_GetByteArrayFromBigNum(env, b)) == NULL) { + goto cleanup; + } + (*env)->SetObjectArrayElement(env, params, ecdhB, paramB); + FreeECDHCurveParam(env, p, a, b, paramP, paramA, paramB); + return true; + +cleanup: + FreeECDHCurveParam(env, p, a, b, paramP, paramA, paramB); + return false; +} + +// Set generator(x, y) in group to params. +static bool SetECDHPoint(JNIEnv* env, EC_GROUP* group, jobjectArray params) +{ + BIGNUM* x = NULL; + BIGNUM* y = NULL; + const EC_POINT* generator = NULL; + jbyteArray paramX = NULL; + jbyteArray paramY = NULL; + if ((x = BN_new()) == NULL || (y = BN_new()) == NULL) { + KAE_ThrowOOMException(env, "failed to allocate BN_new"); + goto cleanup; + } + if ((generator = EC_GROUP_get0_generator(group)) == NULL) { + KAE_ThrowOOMException(env, "failed to allocate ec generator"); + goto cleanup; + } + if (!EC_POINT_get_affine_coordinates_GFp(group, generator, x, y, NULL)) { + KAE_ThrowFromOpenssl(env, "EC_POINT_set_affine_coordinates_GFp", KAE_ThrowRuntimeException); + goto cleanup; + } + + // Set x. + if ((paramX = KAE_GetByteArrayFromBigNum(env, x)) == NULL) { + goto cleanup; + } + (*env)->SetObjectArrayElement(env, params, ecdhX, paramX); + + // Set y. + if ((paramY = KAE_GetByteArrayFromBigNum(env, y)) == NULL) { + goto cleanup; + } + (*env)->SetObjectArrayElement(env, params, ecdhY, paramY); + BN_free(x); + BN_free(y); + (*env)->DeleteLocalRef(env, paramX); + (*env)->DeleteLocalRef(env, paramY); + return true; + +cleanup: + if (x != NULL) { + BN_free(x); + } + if (y != NULL) { + BN_free(y); + } + if (paramX != NULL) { + (*env)->DeleteLocalRef(env, paramX); + } + if (paramY != NULL) { + (*env)->DeleteLocalRef(env, paramY); + } + return false; +} + +// Set order, cofactor in group to params. +static bool SetECDHOrderAndCofactor(JNIEnv* env, EC_GROUP* group, jobjectArray params) +{ + BIGNUM* order = NULL; + BIGNUM* cofactor = NULL; + jbyteArray paramOrder = NULL; + jbyteArray paramCofactor = NULL; + if ((order = BN_new()) == NULL || (cofactor = BN_new()) == NULL) { + goto cleanup; + } + if (!EC_GROUP_get_order(group, order, NULL)) { + goto cleanup; + } + + // Set order. + if ((paramOrder = KAE_GetByteArrayFromBigNum(env, order)) == NULL) { + goto cleanup; + } + (*env)->SetObjectArrayElement(env, params, ecdhOrder, paramOrder); + if (!EC_GROUP_get_cofactor(group, cofactor, NULL)) { + goto cleanup; + } + + // Set cofactor. + if ((paramCofactor = KAE_GetByteArrayFromBigNum(env, cofactor)) == NULL) { + goto cleanup; + } + (*env)->SetObjectArrayElement(env, params, ecdhCofactor, paramCofactor); + BN_free(order); + BN_free(cofactor); + (*env)->DeleteLocalRef(env, paramOrder); + (*env)->DeleteLocalRef(env, paramCofactor); + return true; + +cleanup: + if (order != NULL) { + BN_free(order); + } + if (cofactor != NULL) { + BN_free(cofactor); + } + if (paramOrder != NULL) { + (*env)->DeleteLocalRef(env, paramOrder); + } + if (paramCofactor != NULL) { + (*env)->DeleteLocalRef(env, paramCofactor); + } + return false; +} + +static void FreeECDHKeyParam(JNIEnv* env, + BIGNUM* wX, BIGNUM* wY, jbyteArray keyWX, jbyteArray keyWY, jbyteArray keyS) +{ + if (wX != NULL) { + BN_free(wX); + } + if (wY != NULL) { + BN_free(wY); + } + if (keyWX != NULL) { + (*env)->DeleteLocalRef(env, keyWX); + } + if (keyWY != NULL) { + (*env)->DeleteLocalRef(env, keyWY); + } + if (keyS != NULL) { + (*env)->DeleteLocalRef(env, keyS); + } +} + +// Set publicKey(wX, wY) and privateKey(s) in eckey to params. +static bool SetECDHKey(JNIEnv* env, const EC_GROUP* group, jobjectArray params, + const EC_KEY* eckey) +{ + BIGNUM* wX = NULL; + BIGNUM* wY = NULL; + const EC_POINT* pub = NULL; + const BIGNUM* s = NULL; + jbyteArray keyWX = NULL; + jbyteArray keyWY = NULL; + jbyteArray keyS = NULL; + if ((wX = BN_new()) == NULL || (wY = BN_new()) == NULL) { + KAE_ThrowOOMException(env, "failed to allocate array"); + goto cleanup; + } + + if ((pub = EC_KEY_get0_public_key(eckey)) == NULL || + !EC_POINT_get_affine_coordinates_GFp(group, pub, wX, wY, NULL)) { + goto cleanup; + } + if ((s = EC_KEY_get0_private_key(eckey)) == NULL) { + goto cleanup; + } + + // Set wX. + if ((keyWX = KAE_GetByteArrayFromBigNum(env, wX)) == NULL) { + goto cleanup; + } + (*env)->SetObjectArrayElement(env, params, ecdhWX, keyWX); + + // Set wY. + if ((keyWY = KAE_GetByteArrayFromBigNum(env, wY)) == NULL) { + goto cleanup; + } + (*env)->SetObjectArrayElement(env, params, ecdhWY, keyWY); + + // Set s. + if ((keyS = KAE_GetByteArrayFromBigNum(env, s)) == NULL) { + goto cleanup; + } + (*env)->SetObjectArrayElement(env, params, ecdhS, keyS); + FreeECDHKeyParam(env, wX, wY, keyWX, keyWY, keyS); + return true; + +cleanup: + FreeECDHKeyParam(env, wX, wY, keyWX, keyWY, keyS); + return false; +} + +// Convert EC_GROUP in openssl to byte[][] in java +static jobjectArray NewECDHParam(JNIEnv* env, EC_GROUP* group) +{ + jclass byteArrayClass = (*env)->FindClass(env, "[B"); + jobjectArray params = (*env)->NewObjectArray(env, KAE_EC_PARAM_NUM_SIZE, byteArrayClass, NULL); + if (params == NULL) { + KAE_ThrowOOMException(env, "failed to allocate array"); + goto cleanup; + } + + if (!SetECDHCurve(env, group, params)) { + goto cleanup; + } + if (!SetECDHPoint(env, group, params)) { + goto cleanup; + } + if (!SetECDHOrderAndCofactor(env, group, params)) { + goto cleanup; + } + + (*env)->DeleteLocalRef(env, byteArrayClass); + return params; + +cleanup: + if (byteArrayClass != NULL) { + (*env)->DeleteLocalRef(env, byteArrayClass); + } + if (params != NULL) { + (*env)->DeleteLocalRef(env, params); + } + return NULL; +} + +// Convert EC_KEY in openssl to byte[][] in java +static jobjectArray NewECDHKey(JNIEnv* env, const EC_GROUP* group, const EC_KEY* eckey) +{ + jclass byteArrayClass = (*env)->FindClass(env, "[B"); + jobjectArray params = (*env)->NewObjectArray(env, KAE_EC_KEY_NUM_SIZE, byteArrayClass, NULL); + if (params == NULL) { + KAE_ThrowOOMException(env, "failed to allocate array"); + goto cleanup; + } + if (!SetECDHKey(env, group, params, eckey)) { + goto cleanup; + } + + (*env)->DeleteLocalRef(env, byteArrayClass); + return params; + +cleanup: + if (byteArrayClass != NULL) { + (*env)->DeleteLocalRef(env, byteArrayClass); + } + if (params != NULL) { + (*env)->DeleteLocalRef(env, params); + } + return NULL; +} + +static void FreeECDHParam(BIGNUM* p, BIGNUM* a, BIGNUM* b, BIGNUM* x, BIGNUM* y, BIGNUM* order, BIGNUM* cofactor) +{ + KAE_ReleaseBigNumFromByteArray(p); + KAE_ReleaseBigNumFromByteArray(a); + KAE_ReleaseBigNumFromByteArray(b); + KAE_ReleaseBigNumFromByteArray(x); + KAE_ReleaseBigNumFromByteArray(y); + KAE_ReleaseBigNumFromByteArray(order); + KAE_ReleaseBigNumFromByteArray(cofactor); +} + +// Convert params in java to EC_GROUP in openssl +static EC_GROUP* GetGroupByParam(JNIEnv* env, jbyteArray pArr, jbyteArray aArr, jbyteArray bArr, + jbyteArray xArr, jbyteArray yArr, jbyteArray orderArr, jint cofactorInt) +{ + BIGNUM* p = NULL; + BIGNUM* a = NULL; + BIGNUM* b = NULL; + BIGNUM* x = NULL; + BIGNUM* y = NULL; + BIGNUM* order = NULL; + BIGNUM* cofactor = NULL; + EC_GROUP* group = NULL; + BN_CTX* ctx = NULL; + EC_POINT* generator = NULL; + if ((p = KAE_GetBigNumFromByteArray(env, pArr)) == NULL || (a = KAE_GetBigNumFromByteArray(env, aArr)) == NULL || + (b = KAE_GetBigNumFromByteArray(env, bArr)) == NULL || (x = KAE_GetBigNumFromByteArray(env, xArr)) == NULL || + (y = KAE_GetBigNumFromByteArray(env, yArr)) == NULL || (cofactor = BN_new()) == NULL || + (order = KAE_GetBigNumFromByteArray(env, orderArr)) == NULL || !BN_set_word(cofactor, cofactorInt)) { + goto cleanup; + } + + // Create the curve. + if ((ctx = BN_CTX_new()) == NULL || (group = EC_GROUP_new_curve_GFp(p, a, b, ctx)) == NULL) { + goto cleanup; + } + + // Create the generator and set x, y. + if ((generator = EC_POINT_new(group)) == NULL || + !EC_POINT_set_affine_coordinates_GFp(group, generator, x, y, ctx)) { + goto cleanup; + } + + // Set the generator, order and cofactor. + if (!EC_GROUP_set_generator(group, generator, order, cofactor)) { + goto cleanup; + } + + FreeECDHParam(p, a, b, x, y, order, cofactor); + EC_POINT_free(generator); + BN_CTX_free(ctx); + return group; + +cleanup: + FreeECDHParam(p, a, b, x, y, order, cofactor); + if (group != NULL) { + EC_GROUP_free(group); + } + if (generator != NULL) { + EC_POINT_free(generator); + } + if (ctx != NULL) { + BN_CTX_free(ctx); + } + return NULL; +} + + +/* + * Class: org_openeuler_security_openssl_KAEECKeyPairGenerator + * Method: nativeGenerateParam + * Signature: (Ljava/lang/String;)[[B + */ +JNIEXPORT jobjectArray JNICALL Java_org_openeuler_security_openssl_KAEECKeyPairGenerator_nativeGenerateParam( + JNIEnv* env, jclass cls, jstring curveName) +{ + EC_GROUP* group = NULL; + jobjectArray ecdhParam = NULL; + + const char *curve = (*env)->GetStringUTFChars(env, curveName, 0); + KAE_TRACE("KAEECKeyPairGenerator_nativeGenerateParam(curveName = %s)", curve); + int nid = OBJ_sn2nid(curve); + (*env)->ReleaseStringUTFChars(env, curveName, curve); + if (nid == NID_undef) { + goto cleanup; + } + // Construct a builtin curve. + if ((group = EC_GROUP_new_by_curve_name(nid)) == NULL) { + goto cleanup; + } + ecdhParam = NewECDHParam(env, group); + + if (group != NULL) { + EC_GROUP_free(group); + } + KAE_TRACE("KAEECKeyPairGenerator_nativeGenerateParam success, ecdhParam = %p", ecdhParam); + return ecdhParam; + +cleanup: + if (group != NULL) { + EC_GROUP_free(group); + } + if (ecdhParam != NULL) { + (*env)->DeleteLocalRef(env, ecdhParam); + } + return NULL; +} + +/* + * Class: org_openeuler_security_openssl_KAEECKeyPairGenerator + * Method: nativeGenerateKeyPair + * Signature: ([B[B[B[B[B[BI)[[B + */ +JNIEXPORT jobjectArray JNICALL Java_org_openeuler_security_openssl_KAEECKeyPairGenerator_nativeGenerateKeyPair( + JNIEnv* env, jclass cls, jbyteArray pArr, jbyteArray aArr, jbyteArray bArr, + jbyteArray xArr, jbyteArray yArr, jbyteArray orderArr, jint cofactorInt) +{ + EC_GROUP* group = NULL; + EC_KEY* eckey = NULL; + jobjectArray ecdhKey = NULL; + + if ((group = GetGroupByParam(env, pArr, aArr, bArr, xArr, yArr, orderArr, cofactorInt)) == NULL) { + goto cleanup; + } + if ((eckey = EC_KEY_new()) == NULL) { + goto cleanup; + } + if (!EC_KEY_set_group(eckey, group)) { + goto cleanup; + } + // Generates a new public and private key for the supplied eckey object. + // Refer to {@link https://www.openssl.org/docs/man1.1.0/man3/EC_KEY_generate_key.html} for details. + if (!EC_KEY_generate_key(eckey)) { + goto cleanup; + } + + ecdhKey = NewECDHKey(env, group, eckey); + + EC_KEY_free(eckey); + EC_GROUP_free(group); + + KAE_TRACE("KAEECKeyPairGenerator_nativeGenerateKeyPair success, ecdhKey = %p", ecdhKey); + return ecdhKey; + +cleanup: + if (eckey != NULL) { + EC_KEY_free(eckey); + } + if (group != NULL) { + EC_GROUP_free(group); + } + if (ecdhKey != NULL) { + (*env)->DeleteLocalRef(env, ecdhKey); + } + return NULL; +} \ No newline at end of file diff --git a/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keypairgenerator_rsa.c b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keypairgenerator_rsa.c new file mode 100644 index 000000000..84c2ed109 --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_keypairgenerator_rsa.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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. + */ + +#include +#include +#include "kae_log.h" +#include "kae_util.h" +#include "kae_exception.h" +#include "org_openeuler_security_openssl_KAERSAKeyPairGenerator.h" +#define KAE_RSA_PARAM_SIZE 8 + +// rsa param index +typedef enum RSAParamIndex { + rsaN = 0, + rsaE, + rsaD, + rsaP, + rsaQ, + rsaDmp1, + rsaDmq1, + rsaIqmp +} RSAParamIndex; + +// rsa param name array +static const char* rsaParamNames[] = {"n", "e", "d", "p", "q", "dmp1", "dmq1", "iqmp"}; + +// rsa get rsa param function list +static const BIGNUM* (* GetRSAParamFunctionList[])(const RSA*) = { + RSA_get0_n, + RSA_get0_e, + RSA_get0_d, + RSA_get0_p, + RSA_get0_q, + RSA_get0_dmp1, + RSA_get0_dmq1, + RSA_get0_iqmp +}; + +/* + * New RSA and generate rsa key, follow the steps below + * step 1.New RSA + * step 2.Convert publicExponent to BIGNUM + * step 3.Generate rsa key, and all key information is stored in RSA + */ +static RSA* NewRSA(JNIEnv* env, jint keySize, jbyteArray publicExponent) { + ENGINE* kaeEngine = GetEngineByAlgorithmIndex(RSA_INDEX); + KAE_TRACE("NewRSA: kaeEngine => %p", kaeEngine); + + // new rsa + RSA* rsa = RSA_new_method(kaeEngine); + if (rsa == NULL) { + KAE_ThrowFromOpenssl(env, "RSA_new_method", KAE_ThrowRuntimeException); + return NULL; + } + + // convert publicExponent to BIGNUM + BIGNUM* exponent = KAE_GetBigNumFromByteArray(env, publicExponent); + if (exponent == NULL) { + return NULL; + } + + // generate rsa key + int result_code = RSA_generate_key_ex(rsa, keySize, exponent, NULL); + KAE_ReleaseBigNumFromByteArray(exponent); + if (result_code <= 0) { + RSA_free(rsa); + KAE_ThrowFromOpenssl(env, "RSA_generate_key_ex", KAE_ThrowRuntimeException); + return NULL; + } + return rsa; +} + +/* + * release RSA + */ +static void ReleaseRSA(RSA* rsa) { + if (rsa != NULL) { + RSA_free(rsa); + } +} + +/* + * Set rsa key param, follow the steps below + * step 1. Get rsa param value + * step 2. Convert paramValue (BIGNUM) to jbyteArray + * step 3. Set the rsa param to the param array + */ +static bool SetRSAKeyParam(JNIEnv* env, RSA* rsa, jobjectArray params, RSAParamIndex rsaParamIndex) { + // get rsa param value + const BIGNUM* rsaParamValue = GetRSAParamFunctionList[rsaParamIndex](rsa); + if (rsaParamValue == NULL) { + return false; + } + + // Convert paramValue to jbyteArray + jbyteArray param = KAE_GetByteArrayFromBigNum(env, rsaParamValue); + if (param == NULL) { + return false; + } + + // Set the rsa param to the param array + (*env)->SetObjectArrayElement(env, params, rsaParamIndex, param); + return true; +} + +/* + * New rsa key params, follow the steps below + * step 1. New rsa key param array + * step 2. Set rsa key param + */ +static jobjectArray NewRSAKeyParams(JNIEnv* env, RSA* rsa) { + // new param array + jclass byteArrayClass = (*env)->FindClass(env, "[B"); + jobjectArray params = (*env)->NewObjectArray(env, KAE_RSA_PARAM_SIZE, byteArrayClass, NULL); + if (params == NULL) { + KAE_ThrowOOMException(env, "failed to allocate array"); + return NULL; + } + + // set rsa key param + RSAParamIndex paramIndex; + for (paramIndex = rsaN; paramIndex <= rsaIqmp; paramIndex++) { + if (!SetRSAKeyParam(env, rsa, params, paramIndex)) { + return NULL; + } + } + return params; +} + +/* + * Class: org_openeuler_security_openssl_KAERSAKeyPairGenerator + * Method: nativeGenerateKeyPair + * Signature: (I[B)[[B + */ +JNIEXPORT jobjectArray JNICALL Java_org_openeuler_security_openssl_KAERSAKeyPairGenerator_nativeGenerateKeyPair ( + JNIEnv* env, jclass cls, jint keySize, jbyteArray publicExponent) { + if (publicExponent == NULL) { + return NULL; + } + + // new RSA + RSA* rsa = NewRSA(env, keySize, publicExponent); + if (rsa == NULL) { + return NULL; + } + + // new RSA Key Parameters + jobjectArray rsaParm = NewRSAKeyParams(env, rsa); + + // release rsa + ReleaseRSA(rsa); + return rsaParm; +} \ No newline at end of file diff --git a/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_log.h b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_log.h new file mode 100644 index 000000000..b618546c5 --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_log.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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. + */ + +#ifndef KAE_LOG_H +#define KAE_LOG_H + +#ifdef KAE_DEBUG +#define KAE_TRACE(...) { fprintf(stdout, __VA_ARGS__); fprintf(stdout, "\n"); } +#else +#define KAE_TRACE(...) +#endif + +#endif diff --git a/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_provider.c b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_provider.c new file mode 100644 index 000000000..f4f71005a --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_provider.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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. + */ + +#include +#include +#include +#include +#include "kae_exception.h" +#include "kae_util.h" +#include "org_openeuler_security_openssl_KAEProvider.h" + +#define KAE_OPENSSL_LIBRARY "libcrypto.so" + +/* + * Class: Java_org_openeuler_security_openssl_KAEProvider + * Method: initOpenssl + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_org_openeuler_security_openssl_KAEProvider_initOpenssl + (JNIEnv *env, jclass cls, jboolean useGlobalMode, jstring engineId, jbooleanArray algorithmKaeFlags) { + SSL_load_error_strings(); + ERR_load_BIO_strings(); + OpenSSL_add_all_algorithms(); + + /* + * If the same shared object is opened again with dlopen(), the same object handle is returned. + * The dynamic linker maintains reference counts for object handles. + * An object that was previously opened with RTLD_LOCAL can be promoted to RTLD_GLOBAL in a subsequent dlopen(). + * + * RTLD_GLOBAL + * The symbols defined by this shared object will be made + * available for symbol resolution of subsequently loaded + * shared objects. + * RTLD_LOCAL + * This is the converse of RTLD_GLOBAL, and the default if + * neither flag is specified. Symbols defined in this shared + * object are not made available to resolve references in + * subsequently loaded shared objects. + * For more information see https://man7.org/linux/man-pages/man3/dlopen.3.html. + */ + if (useGlobalMode) { + char msg[1024]; + void *handle = NULL; + // Promote the flags of the loaded libcrypto.so library from RTLD_LOCAL to RTLD_GLOBAL + handle = dlopen(KAE_OPENSSL_LIBRARY, RTLD_LAZY | RTLD_GLOBAL); + if (handle == NULL) { + snprintf(msg, sizeof(msg), "Cannot load %s (%s)!", KAE_OPENSSL_LIBRARY, dlerror()); + KAE_ThrowByName(env, "java/lang/UnsatisfiedLinkError", msg); + return; + } + dlclose(handle); + } + + // check if KaeEngine holder is already set + ENGINE* e = GetKaeEngine(); + if (e != NULL) { + ENGINE_free(e); + e = NULL; + } + + // determine whether KAE is loaded successfully + const char* id = (*env)->GetStringUTFChars(env, engineId, 0); + e = ENGINE_by_id(id); + (*env)->ReleaseStringUTFChars(env, engineId, id); + if (e == NULL) { + KAE_ThrowFromOpenssl(env, "ENGINE_by_id", KAE_ThrowRuntimeException); + return; + } + SetKaeEngine(e); + + // initialize the engine for each algorithm + initEngines(env, algorithmKaeFlags); +} + +/* + * Class: Java_org_openeuler_security_openssl_KAEProvider + * Method: getEngineFlags + * Signature: ()V + */ +JNIEXPORT jbooleanArray JNICALL Java_org_openeuler_security_openssl_KAEProvider_getEngineFlags + (JNIEnv *env, jclass cls) { + return getEngineFlags(env); +} \ No newline at end of file diff --git a/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_signature_rsa.c b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_signature_rsa.c new file mode 100644 index 000000000..496ebc775 --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_signature_rsa.c @@ -0,0 +1,366 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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. + */ +#include +#include +#include +#include +#include "kae_log.h" +#include "kae_util.h" +#include "kae_exception.h" + +// get EVP_MD by digestName +static const EVP_MD* getEvpMd(JNIEnv* env, jstring digestName) { + const char* digestNameUtf = (*env)->GetStringUTFChars(env, digestName, 0); + const EVP_MD* md = (EVP_MD*)EVP_get_digestbyname(digestNameUtf); + (*env)->ReleaseStringUTFChars(env, digestName, digestNameUtf); + if (md == NULL) { + KAE_ThrowSignatureException(env, "Unsupported digest algorithm."); + } + return md; +} + +// sign release +static void signRelease(JNIEnv* env, jbyteArray digestValue, jbyte* digestBytes, jbyte* sigBytes, + EVP_PKEY_CTX* pkeyCtx) { + if (digestBytes != NULL) { + (*env)->ReleaseByteArrayElements(env, digestValue, digestBytes, 0); + } + if (sigBytes != NULL) { + free(sigBytes); + } + if (pkeyCtx != NULL) { + EVP_PKEY_CTX_free(pkeyCtx); + } +} + +// verify release +static void verifyRelease(JNIEnv* env, jbyteArray digestValue, jbyte* digestBytes, jbyteArray sigValue, jbyte* sigBytes, + EVP_PKEY_CTX* pkeyCtx) { + if (digestBytes != NULL) { + (*env)->ReleaseByteArrayElements(env, digestValue, digestBytes, 0); + } + if (sigBytes != NULL) { + (*env)->ReleaseByteArrayElements(env, sigValue, sigBytes, 0); + } + if (pkeyCtx != NULL) { + EVP_PKEY_CTX_free(pkeyCtx); + } +} + +// set rsa PkeyCtx parameters +static bool setRsaPkeyCtxParameters(JNIEnv* env, EVP_PKEY_CTX* pkeyCtx, jint paddingType, jstring digestName) { + // set rsa padding + if (EVP_PKEY_CTX_set_rsa_padding(pkeyCtx, paddingType) <= 0) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_CTX_set_rsa_padding", KAE_ThrowSignatureException); + return false; + } + + // set signature md + const EVP_MD* md = getEvpMd(env, digestName); + if (md == NULL) { + return false; + } + + if (EVP_PKEY_CTX_set_signature_md(pkeyCtx, md) <= 0) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_CTX_set_signature_md", KAE_ThrowSignatureException); + return false; + } + return true; +} + +/* + * Class: org_openeuler_security_openssl_KAERSASignatureNative + * Method: rsaSign + * Signature: (JLjava/lang/String;[BI)[B + */ +JNIEXPORT jbyteArray JNICALL Java_org_openeuler_security_openssl_KAERSASignatureNative_rsaSign(JNIEnv* env, jclass cls, + jlong keyAddress, jstring digestName, jbyteArray digestValue, jint paddingType) { + EVP_PKEY* pkey = (EVP_PKEY*)keyAddress; + EVP_PKEY_CTX* pkeyCtx = NULL; + jbyte* digestBytes = NULL; + jbyte* sigBytes = NULL; + jbyteArray sigByteArray = NULL; + static ENGINE* kaeEngine = NULL; + kaeEngine = (kaeEngine == NULL) ? GetKaeEngine() : kaeEngine; + // new EVP_PKEY_CTX + if ((pkeyCtx = EVP_PKEY_CTX_new(pkey, kaeEngine)) == NULL) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_new", KAE_ThrowSignatureException); + goto cleanup; + } + + // sign init + if (EVP_PKEY_sign_init(pkeyCtx) <= 0) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_sign_init", KAE_ThrowSignatureException); + goto cleanup; + } + + // set rsa PkeyCtx parameters + if (!setRsaPkeyCtxParameters(env, pkeyCtx, paddingType, digestName)) { + goto cleanup; + } + + // sign + size_t sigLen = (size_t)EVP_PKEY_size(pkey); + if (sigLen <= 0) { + KAE_ThrowSignatureException(env, "The sigLen size cannot be zero or negative"); + goto cleanup; + } + if ((sigBytes = malloc(sigLen)) == NULL) { + KAE_ThrowOOMException(env, "malloc failed"); + goto cleanup; + } + if ((digestBytes = (*env)->GetByteArrayElements(env, digestValue, NULL)) == NULL) { + KAE_ThrowOOMException(env, "GetByteArrayElements failed"); + goto cleanup; + } + size_t digestLen = (size_t)(*env)->GetArrayLength(env, digestValue); + if (EVP_PKEY_sign(pkeyCtx, (unsigned char*)sigBytes, &sigLen, + (const unsigned char*)digestBytes, digestLen) <= 0) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_sign", KAE_ThrowSignatureException); + goto cleanup; + } + + // set signature byte to jbyteArray + if ((sigByteArray = (*env)->NewByteArray(env, (jsize)sigLen)) == NULL) { + KAE_ThrowOOMException(env, "NewByteArray failed"); + goto cleanup; + } + (*env)->SetByteArrayRegion(env, sigByteArray, 0, (jsize)sigLen, sigBytes); + +cleanup: + signRelease(env, digestValue, digestBytes, sigBytes, pkeyCtx); + return sigByteArray; +} + +/* + * Class: org_openeuler_security_openssl_KAERSASignatureNative + * Method: rsaVerify + * Signature: (JLjava/lang/String;[BI[B)Z + */ +JNIEXPORT jboolean JNICALL Java_org_openeuler_security_openssl_KAERSASignatureNative_rsaVerify(JNIEnv* env, jclass cls, + jlong keyAddress, jstring digestName, jbyteArray digestValue, jint paddingType, jbyteArray sigValue) { + EVP_PKEY* pkey = (EVP_PKEY*)keyAddress; + EVP_PKEY_CTX* pkeyCtx = NULL; + jbyte* digestBytes = NULL; + jbyte* sigBytes = NULL; + jboolean isSuccess = JNI_FALSE; + static ENGINE* kaeEngine = NULL; + kaeEngine = (kaeEngine == NULL) ? GetKaeEngine() : kaeEngine; + // new EVP_PKEY_CTX + if ((pkeyCtx = EVP_PKEY_CTX_new(pkey, kaeEngine)) == NULL) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_new", KAE_ThrowSignatureException); + goto cleanup; + } + + // verify init + if (EVP_PKEY_verify_init(pkeyCtx) <= 0) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_sign_init", KAE_ThrowSignatureException); + goto cleanup; + } + + // set rsa PkeyCtx parameters + if (!setRsaPkeyCtxParameters(env, pkeyCtx, paddingType, digestName)) { + goto cleanup; + } + + // verify + if ((digestBytes = (*env)->GetByteArrayElements(env, digestValue, NULL)) == NULL) { + KAE_ThrowOOMException(env, "GetByteArrayElements failed"); + goto cleanup; + } + if ((sigBytes = (*env)->GetByteArrayElements(env, sigValue, NULL)) == NULL) { + KAE_ThrowOOMException(env, "GetByteArrayElements failed"); + goto cleanup; + } + size_t sigLen = (size_t)(*env)->GetArrayLength(env, sigValue); + size_t digestLen = (size_t)(*env)->GetArrayLength(env, digestValue); + if (EVP_PKEY_verify(pkeyCtx, (const unsigned char*)sigBytes, sigLen, + (const unsigned char*)digestBytes, digestLen) <= 0) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_verify", KAE_ThrowSignatureException); + goto cleanup; + } + isSuccess = JNI_TRUE; + +cleanup: + verifyRelease(env, digestValue, digestBytes, sigValue, sigBytes, pkeyCtx); + return isSuccess; +} + +// set pss pkeyCtx parameters +static bool setPssPkeyCtxParameters(JNIEnv* env, EVP_PKEY_CTX* pkeyCtx, jint paddingType, jstring digestName, + jstring mgf1DigestName, jint saltLen) { + // set rsa padding + if (EVP_PKEY_CTX_set_rsa_padding(pkeyCtx, paddingType) <= 0) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_CTX_set_rsa_padding", KAE_ThrowSignatureException); + return false; + } + + // set signature md + const EVP_MD* md = getEvpMd(env, digestName); + if (md == NULL) { + return false; + } + if (EVP_PKEY_CTX_set_signature_md(pkeyCtx, md) <= 0) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_CTX_set_signature_md", KAE_ThrowSignatureException); + return false; + } + + // set rsa mgf1 md + const EVP_MD* mgf1Md = getEvpMd(env, mgf1DigestName); + if (mgf1Md == NULL) { + return false; + } + if (EVP_PKEY_CTX_set_rsa_mgf1_md(pkeyCtx, mgf1Md) <= 0) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_CTX_set_rsa_mgf1_md", KAE_ThrowSignatureException); + return false; + } + + // set salt len + if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkeyCtx, saltLen) <= 0) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_CTX_set_rsa_pss_saltlen", KAE_ThrowSignatureException); + return false; + } + return true; +} + +/* + * Class: org_openeuler_security_openssl_KAERSASignatureNative + * Method: pssSign + * Signature: (JLjava/lang/String;[BILjava/lang/String;I)[B + */ +JNIEXPORT jbyteArray JNICALL Java_org_openeuler_security_openssl_KAERSASignatureNative_pssSign(JNIEnv* env, jclass cls, + jlong keyAddress, jstring digestName, jbyteArray digestValue, jint paddingType, jstring mgf1DigestName, + jint saltLen) { + EVP_PKEY* pkey = (EVP_PKEY*)keyAddress; + EVP_PKEY_CTX* pkeyCtx = NULL; + jbyte* digestBytes = NULL; + jbyte* sigBytes = NULL; + jbyteArray sigByteArray = NULL; + ENGINE* kaeEngine = GetEngineByAlgorithmIndex(RSA_INDEX); + KAE_TRACE("KAERSASignatureNative_pssSign: kaeEngine => %p", kaeEngine); + + // new EVP_PKEY_CTX + if ((pkeyCtx = EVP_PKEY_CTX_new(pkey, kaeEngine)) == NULL) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_new", KAE_ThrowSignatureException); + goto cleanup; + } + + // sign init + if (EVP_PKEY_sign_init(pkeyCtx) <= 0) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_sign_init", KAE_ThrowSignatureException); + goto cleanup; + } + + // set pss pkeyCtx parameters + if (!setPssPkeyCtxParameters(env, pkeyCtx, paddingType, digestName, mgf1DigestName, saltLen)) { + goto cleanup; + } + + // sign + size_t sigLen = (size_t)EVP_PKEY_size(pkey); + if (sigLen <= 0) { + KAE_ThrowSignatureException(env, "The sigLen size cannot be zero or negative"); + goto cleanup; + } + if ((sigBytes = malloc(sigLen)) == NULL) { + KAE_ThrowOOMException(env, "malloc failed"); + goto cleanup; + } + if ((digestBytes = (*env)->GetByteArrayElements(env, digestValue, NULL)) == NULL) { + KAE_ThrowOOMException(env, "GetByteArrayElements failed"); + goto cleanup; + } + size_t digestLen = (size_t)(*env)->GetArrayLength(env, digestValue); + if (EVP_PKEY_sign(pkeyCtx, (unsigned char*)sigBytes, &sigLen, + (const unsigned char*)digestBytes, digestLen) <= 0) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_sign", KAE_ThrowSignatureException); + goto cleanup; + } + + // set signature byte to jbyteArray + if ((sigByteArray = (*env)->NewByteArray(env, (jsize)sigLen)) == NULL) { + KAE_ThrowOOMException(env, "NewByteArray failed"); + goto cleanup; + } + (*env)->SetByteArrayRegion(env, sigByteArray, 0, (jsize)sigLen, sigBytes); + +cleanup: + signRelease(env, digestValue, digestBytes, sigBytes, pkeyCtx); + return sigByteArray; +} + +/* + * Class: org_openeuler_security_openssl_KAERSASignatureNative + * Method: pssVerify + * Signature: (JLjava/lang/String;[BILjava/lang/String;I[B)Z + */ +JNIEXPORT jboolean JNICALL Java_org_openeuler_security_openssl_KAERSASignatureNative_pssVerify(JNIEnv* env, jclass cls, + jlong keyAddress, jstring digestName, jbyteArray digestValue, jint paddingType, jstring mgf1DigestName, + jint saltLen, jbyteArray sigValue) { + EVP_PKEY* pkey = (EVP_PKEY*)keyAddress; + EVP_PKEY_CTX* pkeyCtx = NULL; + jbyte* digestBytes = NULL; + jbyte* sigBytes = NULL; + jboolean isSuccess = JNI_FALSE; + ENGINE* kaeEngine = GetEngineByAlgorithmIndex(RSA_INDEX); + KAE_TRACE("KAERSASignatureNative_pssVerify: kaeEngine => %p", kaeEngine); + + // new EVP_PKEY_CTX + if ((pkeyCtx = EVP_PKEY_CTX_new(pkey, kaeEngine)) == NULL) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_new", KAE_ThrowSignatureException); + goto cleanup; + } + + // verify init + if (EVP_PKEY_verify_init(pkeyCtx) <= 0) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_sign_init", KAE_ThrowSignatureException); + goto cleanup; + } + + // set pkeyCtx parameters + if (!setPssPkeyCtxParameters(env, pkeyCtx, paddingType, digestName, mgf1DigestName, saltLen)) { + goto cleanup; + } + + // verify + if ((digestBytes = (*env)->GetByteArrayElements(env, digestValue, NULL)) == NULL) { + KAE_ThrowOOMException(env, "GetByteArrayElements failed"); + goto cleanup; + } + if ((sigBytes = (*env)->GetByteArrayElements(env, sigValue, NULL)) == NULL) { + KAE_ThrowOOMException(env, "GetByteArrayElements failed"); + goto cleanup; + } + size_t sigLen = (size_t)(*env)->GetArrayLength(env, sigValue); + size_t digestLen = (size_t)(*env)->GetArrayLength(env, digestValue); + if (EVP_PKEY_verify(pkeyCtx, (const unsigned char*)sigBytes, sigLen, + (const unsigned char*)digestBytes, digestLen) <= 0) { + KAE_ThrowFromOpenssl(env, "EVP_PKEY_verify", KAE_ThrowSignatureException); + goto cleanup; + } + isSuccess = JNI_TRUE; + +cleanup: + verifyRelease(env, digestValue, digestBytes, sigValue, sigBytes, pkeyCtx); + return isSuccess; +} \ No newline at end of file diff --git a/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_symmetric_cipher.c b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_symmetric_cipher.c new file mode 100644 index 000000000..67151f53a --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_symmetric_cipher.c @@ -0,0 +1,415 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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. + */ + +#include +#include +#include +#include +#include +#include "kae_exception.h" +#include "kae_log.h" +#include "kae_util.h" +#include "org_openeuler_security_openssl_KAESymmetricCipherBase.h" + +bool StartsWith(const char* str1, const char* str2) +{ + if (str1 == NULL || str2 == NULL) { + return 0; + } + int len1 = strlen(str1); + int len2 = strlen(str2); + if (len1 > len2 || (len1 == 0 || len2 == 0)) { + return false; + } + const char *cur = str1; + int i = 0; + while (*cur != '\0') { + if (*cur != str2[i]) { + return 0; + } + cur++; + i++; + } + return true; +} + +static const EVP_CIPHER* EVPGetSm4CipherByName(JNIEnv* env, const char* algo) +{ + static const EVP_CIPHER* sm4Ecb = NULL; + static const EVP_CIPHER* sm4Cbc = NULL; + static const EVP_CIPHER* sm4Ctr = NULL; + static const EVP_CIPHER* sm4Ofb = NULL; + + if (strcasecmp(algo, "sm4-ecb") == 0) { + return sm4Ecb == NULL ? sm4Ecb = EVP_get_cipherbyname(algo) : sm4Ecb; + } else if (strcasecmp(algo, "sm4-cbc") == 0) { + return sm4Cbc == NULL ? sm4Cbc = EVP_get_cipherbyname(algo) : sm4Cbc; + } else if (strcasecmp(algo, "sm4-ctr") == 0) { + return sm4Ctr == NULL ? sm4Ctr = EVP_get_cipherbyname(algo) : sm4Ctr; + } else if (strcasecmp(algo, "sm4-ofb") == 0) { + return sm4Ofb == NULL ? sm4Ofb = EVP_get_cipherbyname(algo) : sm4Ofb; + } else { + KAE_ThrowRuntimeException(env, "EVPGetSm4CipherByName error"); + return 0; + } +} + +static const EVP_CIPHER* EVPGetAesCipherByName(JNIEnv* env, const char* algo) +{ + static const EVP_CIPHER* aes128Ecb = NULL; + static const EVP_CIPHER* aes128Cbc = NULL; + static const EVP_CIPHER* aes128Ctr = NULL; + static const EVP_CIPHER* aes128Gcm = NULL; + static const EVP_CIPHER* aes192Ecb = NULL; + static const EVP_CIPHER* aes192Cbc = NULL; + static const EVP_CIPHER* aes192Ctr = NULL; + static const EVP_CIPHER* aes192Gcm = NULL; + static const EVP_CIPHER* aes256Ecb = NULL; + static const EVP_CIPHER* aes256Cbc = NULL; + static const EVP_CIPHER* aes256Ctr = NULL; + static const EVP_CIPHER* aes256Gcm = NULL; + + if (strcasecmp(algo, "aes-128-ecb") == 0) { + return aes128Ecb == NULL ? aes128Ecb = EVP_get_cipherbyname(algo) : aes128Ecb; + } else if (strcasecmp(algo, "aes-128-cbc") == 0) { + return aes128Cbc == NULL ? aes128Cbc = EVP_get_cipherbyname(algo) : aes128Cbc; + } else if (strcasecmp(algo, "aes-128-ctr") == 0) { + return aes128Ctr == NULL ? aes128Ctr = EVP_get_cipherbyname(algo) : aes128Ctr; + } else if (strcasecmp(algo, "aes-128-gcm") == 0) { + return aes128Gcm == NULL ? aes128Gcm = EVP_get_cipherbyname(algo) : aes128Gcm; + } else if (strcasecmp(algo, "aes-192-ecb") == 0) { + return aes192Ecb == NULL ? aes192Ecb = EVP_get_cipherbyname(algo) : aes192Ecb; + } else if (strcasecmp(algo, "aes-192-cbc") == 0) { + return aes192Cbc == NULL ? aes192Cbc = EVP_get_cipherbyname(algo) : aes192Cbc; + } else if (strcasecmp(algo, "aes-192-ctr") == 0) { + return aes192Ctr == NULL ? aes192Ctr = EVP_get_cipherbyname(algo) : aes192Ctr; + } else if (strcasecmp(algo, "aes-192-gcm") == 0) { + return aes192Gcm == NULL ? aes192Gcm = EVP_get_cipherbyname(algo) : aes192Gcm; + } else if (strcasecmp(algo, "aes-256-ecb") == 0) { + return aes256Ecb == NULL ? aes256Ecb = EVP_get_cipherbyname(algo) : aes256Ecb; + } else if (strcasecmp(algo, "aes-256-cbc") == 0) { + return aes256Cbc == NULL ? aes256Cbc = EVP_get_cipherbyname(algo) : aes256Cbc; + } else if (strcasecmp(algo, "aes-256-ctr") == 0) { + return aes256Ctr == NULL ? aes256Ctr = EVP_get_cipherbyname(algo) : aes256Ctr; + } else if (strcasecmp(algo, "aes-256-gcm") == 0) { + return aes256Gcm == NULL ? aes256Gcm = EVP_get_cipherbyname(algo) : aes256Gcm; + } else { + KAE_ThrowRuntimeException(env, "EVPGetAesCipherByName error"); + return 0; + } +} + +void FreeMemoryFromInit(JNIEnv* env, jbyteArray iv, jbyte* ivBytes, jbyteArray key, jbyte* keyBytes, + int keyLength) +{ + if (ivBytes != NULL) { + (*env)->ReleaseByteArrayElements(env, iv, ivBytes, 0); + } + if (keyBytes != NULL) { + memset(keyBytes, 0, keyLength); + (*env)->ReleaseByteArrayElements(env, key, keyBytes, JNI_ABORT); + } +} + +/* + * Class: org_openeuler_security_openssl_KAESymmetricCipherBase + * Method: nativeInit + * Signature: (Ljava/lang/String;Z[B[B)J + */ +JNIEXPORT jlong JNICALL +Java_org_openeuler_security_openssl_KAESymmetricCipherBase_nativeInit(JNIEnv* env, jclass cls, + jstring cipherType, jboolean encrypt, jbyteArray key, jbyteArray iv, jboolean padding) +{ + EVP_CIPHER_CTX* ctx = NULL; + jbyte* keyBytes = NULL; + jbyte* ivBytes = NULL; + const EVP_CIPHER* cipher = NULL; + ENGINE* kaeEngine = NULL; + int keyLength = (*env)->GetArrayLength(env, key); + + const char* algo = (*env)->GetStringUTFChars(env, cipherType, 0); + if (StartsWith("aes", algo)) { + cipher = EVPGetAesCipherByName(env, algo); + kaeEngine = GetAesEngineByAlgorithmName(algo); + } else { + cipher = EVPGetSm4CipherByName(env, algo); + kaeEngine = GetSm4EngineByAlgorithmName(algo); + } + + KAE_TRACE("KAESymmetricCipherBase_nativeInit: kaeEngine => %p", kaeEngine); + + (*env)->ReleaseStringUTFChars(env, cipherType, algo); + if (cipher == NULL) { + KAE_ThrowOOMException(env, "create EVP_CIPHER fail"); + goto cleanup; + } + if ((ctx = EVP_CIPHER_CTX_new()) == NULL) { + KAE_ThrowOOMException(env, "create EVP_CIPHER_CTX fail"); + goto cleanup; + } + + if (iv != NULL) { + ivBytes = (*env)->GetByteArrayElements(env, iv, NULL); + } + if (key != NULL) { + keyBytes = (*env)->GetByteArrayElements(env, key, NULL); + } + + if (!EVP_CipherInit_ex(ctx, cipher, kaeEngine, (const unsigned char*)keyBytes, + (const unsigned char*)ivBytes, encrypt ? 1 : 0)) { + KAE_ThrowFromOpenssl(env, "EVP_CipherInit_ex failed", KAE_ThrowRuntimeException); + goto cleanup; + } + + EVP_CIPHER_CTX_set_padding(ctx, padding ? 1 : 0); + + FreeMemoryFromInit(env, iv, ivBytes, key, keyBytes, keyLength); + return (jlong)ctx; + +cleanup: + if (ctx != NULL) { + EVP_CIPHER_CTX_free(ctx); + } + FreeMemoryFromInit(env, iv, ivBytes, key, keyBytes, keyLength); + return 0; +} + +static void FreeMemoryFromUpdate(unsigned char* in, unsigned char* aad, unsigned char* out) +{ + if (in != NULL) { + free(in); + } + if (out != NULL) { + free(out); + } + if (aad != NULL) { + free(aad); + } +} + +/* + * Class: org_openeuler_security_openssl_KAESymmetricCipherBase + * Method: nativeUpdate + * Signature: (J[BII[BIZ[B)I + */ +JNIEXPORT jint JNICALL +Java_org_openeuler_security_openssl_KAESymmetricCipherBase_nativeUpdate(JNIEnv* env, jclass cls, jlong ctxAddress, + jbyteArray inArr, jint inOfs, jint inLen, jbyteArray outArr, jint outOfs, jboolean gcm, jbyteArray gcmAAD) +{ + unsigned char* in = NULL; + unsigned char* aad = NULL; + unsigned char* out = NULL; + + EVP_CIPHER_CTX* ctx = (EVP_CIPHER_CTX*)ctxAddress; + if (ctx == NULL || inArr == NULL || outArr == NULL) { + goto cleanup; + } + + in = (unsigned char*)malloc(inLen); + int outLen = (*env)->GetArrayLength(env, outArr) - outOfs; + out = (unsigned char*)malloc(outLen); + if (in == NULL || out == NULL) { + KAE_ThrowOOMException(env, "malloc error"); + goto cleanup; + } + memset(in, 0, inLen); + memset(out, 0, outLen); + (*env)->GetByteArrayRegion(env, inArr, inOfs, inLen, (jbyte*)in); + + int bytesWritten = 0; + if (gcm && (gcmAAD != NULL)) { + int aadLen = (*env)->GetArrayLength(env, gcmAAD); + if ((aad = (unsigned char*)malloc(aadLen)) == NULL) { + KAE_ThrowOOMException(env, "malloc error"); + goto cleanup; + } + memset(aad, 0, aadLen); + (*env)->GetByteArrayRegion(env, gcmAAD, 0, aadLen, (jbyte*)aad); + + // Specify aad. + if (EVP_CipherUpdate(ctx, NULL, &bytesWritten, aad, aadLen) == 0) { + KAE_ThrowFromOpenssl(env, "EVP_CipherUpdate failed", KAE_ThrowRuntimeException); + goto cleanup; + } + } + + if (EVP_CipherUpdate(ctx, out, &bytesWritten, in, inLen) == 0) { + KAE_ThrowFromOpenssl(env, "EVP_CipherUpdate failed", KAE_ThrowRuntimeException); + goto cleanup; + } + (*env)->SetByteArrayRegion(env, outArr, outOfs, bytesWritten, (jbyte*)out); + + FreeMemoryFromUpdate(in, aad, out); + return bytesWritten; + +cleanup: + FreeMemoryFromUpdate(in, aad, out); + return 0; +} + +/* + * Class: org_openeuler_security_openssl_KAESymmetricCipherBase + * Method: nativeFinal + * Signature: (JZ[BI)I + */ +JNIEXPORT jint JNICALL +Java_org_openeuler_security_openssl_KAESymmetricCipherBase_nativeFinal(JNIEnv* env, jclass cls, + jlong ctxAddress, jbyteArray outArr, jint outOfs) +{ + unsigned char* out = NULL; + EVP_CIPHER_CTX* ctx = (EVP_CIPHER_CTX*)ctxAddress; + KAE_TRACE("KAESymmetricCipherBase_nativeFinal(ctxAddress = %p, outArr = %p, outOfs = %d)", + ctx, outArr, outOfs); + if (ctx == NULL || outArr == NULL) { + goto cleanup; + } + int outLen = (*env)->GetArrayLength(env, outArr) - outOfs; + out = (unsigned char*)malloc(outLen); + if (out == NULL) { + KAE_ThrowOOMException(env, "malloc error"); + goto cleanup; + } + memset(out, 0, outLen); + int bytesWritten = 0; + int result_code = EVP_CipherFinal_ex(ctx, out, &bytesWritten); + if (result_code == 0) { + KAE_ThrowFromOpenssl(env, "EVP_CipherFinal_ex failed", KAE_ThrowBadPaddingException); + goto cleanup; + } + KAE_TRACE("KAESymmetricCipherBase_nativeFinal EVP_CipherFinal_ex success, bytesWritten = %d", bytesWritten); + (*env)->SetByteArrayRegion(env, outArr, outOfs, bytesWritten, (jbyte*)out); + free(out); + KAE_TRACE("KAESymmetricCipherBase_nativeFinal: finished"); + return bytesWritten; + +cleanup: + if (out != NULL) { + free(out); + } + return 0; +} + +static void FreeMemoryFromFinalGcm(unsigned char* out, unsigned char* gcmTag, unsigned char* gcmOut) +{ + if (out != NULL) { + free(out); + } + if (gcmTag != NULL) { + free(gcmTag); + } + if (gcmOut != NULL) { + free(gcmOut); + } +} + +/* + * Class: org_openeuler_security_openssl_KAECipherAES + * Method: nativeFinalGcm + * Signature: (J[BIZI[BZ)I + */ +JNIEXPORT jint JNICALL Java_org_openeuler_security_openssl_KAESymmetricCipherBase_nativeFinalGcm(JNIEnv* env, + jclass cls, jlong ctxAddress, jbyteArray outArr, jint outOfs, jboolean gcm, jint tagLength, + jbyteArray gcmTagArr, jboolean encrypt) +{ + unsigned char* out = NULL; + unsigned char* gcmTag = NULL; + unsigned char* gcmOut = NULL; + + EVP_CIPHER_CTX* ctx = (EVP_CIPHER_CTX*)ctxAddress; + if (ctx == NULL || outArr == NULL) { + goto cleanup; + } + + int bytesWritten = 0; + if (encrypt) { + int outLen = (*env)->GetArrayLength(env, outArr) - outOfs; + if ((out = malloc(outLen)) == NULL) { + KAE_ThrowOOMException(env, "malloc error"); + goto cleanup; + } + memset(out, 0, outLen); + if (EVP_CipherFinal_ex(ctx, out, &bytesWritten) == 0) { + KAE_ThrowFromOpenssl(env, "EVP_CipherFinal_ex failed", KAE_ThrowBadPaddingException); + goto cleanup; + } + (*env)->SetByteArrayRegion(env, outArr, outOfs, bytesWritten, (jbyte*)out); + + // Writes tagLength bytes of the tag value to the buffer. + // Refer to {@link https://www.openssl.org/docs/man1.1.0/man3/EVP_CIPHER_CTX_ctrl.html} for details. + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tagLength, out + bytesWritten) == 0) { + KAE_ThrowFromOpenssl(env, "EVP_CIPHER_CTX_ctrl failed", KAE_ThrowRuntimeException); + goto cleanup; + } + (*env)->SetByteArrayRegion(env, outArr, outOfs + bytesWritten, tagLength, (jbyte*)(out + bytesWritten)); + bytesWritten += tagLength; + } else { + // gcmOut is the plaintext that has been decrypted in the EVP_CipherUpdate. + // outOfs is the length of the gcmOut, where it's always > 0. + if ((gcmTag = (unsigned char*)malloc(tagLength)) == NULL || (gcmOut = (unsigned char*)malloc(outOfs)) == NULL) { + KAE_ThrowOOMException(env, "malloc error"); + goto cleanup; + } + memset(gcmTag, 0, tagLength); + memset(gcmOut, 0, outOfs); + + (*env)->GetByteArrayRegion(env, gcmTagArr, 0, tagLength, (jbyte*)gcmTag); + // Sets the expected gcmTag to tagLength bytes from gcmTag. + // Refer to {@link https://www.openssl.org/docs/man1.1.0/man3/EVP_CIPHER_CTX_ctrl.html} for details. + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tagLength, gcmTag) == 0) { + KAE_ThrowFromOpenssl(env, "EVP_CTRL_AEAD_SET_TAG failed", KAE_ThrowRuntimeException); + goto cleanup; + } + + (*env)->GetByteArrayRegion(env, outArr, 0, outOfs, (jbyte*)gcmOut); + // Finalise: note get no output for GCM + if (EVP_CipherFinal_ex(ctx, gcmOut, &bytesWritten) == 0) { + KAE_ThrowFromOpenssl(env, "EVP_CipherFinal_ex failed", KAE_ThrowAEADBadTagException); + goto cleanup; + } + } + FreeMemoryFromFinalGcm(out, gcmTag, gcmOut); + + return bytesWritten; + +cleanup: + FreeMemoryFromFinalGcm(out, gcmTag, gcmOut); + return 0; +} + +/* + * Class: org_openeuler_security_openssl_KAESymmetricCipherBase + * Method: nativeFree + * Signature: (J)V + */ +JNIEXPORT void JNICALL +Java_org_openeuler_security_openssl_KAESymmetricCipherBase_nativeFree(JNIEnv* env, jclass cls, jlong ctxAddress) +{ + EVP_CIPHER_CTX* ctx = (EVP_CIPHER_CTX*)ctxAddress; + KAE_TRACE("KAESymmetricCipherBase_nativeFree(ctx = %p)", ctx); + if (ctx != NULL) { + EVP_CIPHER_CTX_free(ctx); + ctx = NULL; + } + + KAE_TRACE("KAESymmetricCipherBase_nativeFree: finished"); +} diff --git a/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_util.c b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_util.c new file mode 100644 index 000000000..471ae834b --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_util.c @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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. + */ + +#include +#include +#include "kae_util.h" +#include "kae_exception.h" + +static ENGINE* kaeEngine = NULL; + +void SetKaeEngine(ENGINE* engine) { + kaeEngine = engine; +} + +ENGINE* GetKaeEngine() { + return kaeEngine; +} + +BIGNUM* KAE_GetBigNumFromByteArray(JNIEnv* env, jbyteArray byteArray) { + if (byteArray == NULL) { + KAE_ThrowNullPointerException(env, "KAE_GetBigNumFromByteArray byteArray is null"); + return NULL; + } + + jsize len = (*env)->GetArrayLength(env, byteArray); + if (len == 0) { + KAE_ThrowRuntimeException(env, "KAE_GetBigNumFromByteArray byteArray is empty"); + return NULL; + } + + BIGNUM* bn = BN_new(); + if (bn == NULL) { + KAE_ThrowFromOpenssl(env, "BN_new", KAE_ThrowRuntimeException); + return NULL; + } + + jbyte* bytes = (*env)->GetByteArrayElements(env, byteArray, NULL); + if (bytes == NULL) { + KAE_ThrowNullPointerException(env,"GetByteArrayElements failed"); + goto cleanup; + } + BIGNUM* result = BN_bin2bn((const unsigned char*) bytes, len, bn); + (*env)->ReleaseByteArrayElements(env, byteArray, bytes, 0); + if (result == NULL) { + KAE_ThrowFromOpenssl(env, "BN_bin2bn", KAE_ThrowRuntimeException); + goto cleanup; + } + return bn; + +cleanup: + BN_free(bn); + return NULL; +} + +void KAE_ReleaseBigNumFromByteArray(BIGNUM* bn) { + if (bn != NULL) { + BN_free(bn); + } +} + +jbyteArray KAE_GetByteArrayFromBigNum(JNIEnv* env, const BIGNUM* bn) { + if (bn == NULL) { + return NULL; + } + // bn size need plus 1, for example 65535 , BN_num_bytes return 2 + int bnSize = BN_num_bytes(bn); + if (bnSize <= 0) { + return NULL; + } + bnSize += 1; + jbyteArray javaBytes = (*env)->NewByteArray(env, bnSize); + if (javaBytes == NULL) { + KAE_ThrowOOMException(env, "New byte array failed."); + return NULL; + } + jbyte* bytes = (*env)->GetByteArrayElements(env, javaBytes, NULL); + if (bytes == NULL) { + KAE_ThrowNullPointerException(env,"GetByteArrayElements failed."); + return NULL; + } + unsigned char* tmp = (unsigned char*) bytes; + if (BN_bn2bin(bn, tmp + 1) <= 0) { + KAE_ThrowFromOpenssl(env, "BN_bn2bin", KAE_ThrowRuntimeException); + javaBytes = NULL; + goto cleanup; + } + (*env)->SetByteArrayRegion(env, javaBytes, 0, bnSize, bytes); + +cleanup: + (*env)->ReleaseByteArrayElements(env, javaBytes, bytes, 0); + return javaBytes; +} + +#define ENGINE_LENGTH (EC_INDEX + 1) +static ENGINE* engines[ENGINE_LENGTH] = {NULL}; +static jboolean engineFlags[ENGINE_LENGTH] = {JNI_FALSE}; +static KAEAlgorithm kaeAlgorithms[ENGINE_LENGTH] = { + {MD5_INDEX, "md5"}, + {SHA256_INDEX, "sha256"}, + {SHA384_INDEX, "sha384"}, + {SM3_INDEX, "sm3"}, + {AES_128_ECB_INDEX, "aes-128-ecb"}, + {AES_128_CBC_INDEX, "aes-128-cbc"}, + {AES_128_CTR_INDEX, "aes-128-ctr"}, + {AES_128_GCM_INDEX, "aes-128-gcm"}, + {AES_192_ECB_INDEX, "aes-192-ecb"}, + {AES_192_CBC_INDEX, "aes-192-cbc"}, + {AES_192_CTR_INDEX, "aes-192-ctr"}, + {AES_192_GCM_INDEX, "aes-192-gcm"}, + {AES_256_ECB_INDEX, "aes-256-ecb"}, + {AES_256_CBC_INDEX, "aes-256-cbc"}, + {AES_256_CTR_INDEX, "aes-256-ctr"}, + {AES_256_GCM_INDEX, "aes-256-gcm"}, + {SM4_ECB_INDEX, "sm4-ecb"}, + {SM4_CBC_INDEX, "sm4-cbc"}, + {SM4_CTR_INDEX, "sm4-ctr"}, + {SM4_OFB_INDEX, "sm4-ofb"}, + {HMAC_MD5_INDEX, "hmac-md5"}, + {HMAC_SHA1_INDEX, "hmac-sha1"}, + {HMAC_SHA224_INDEX, "hmac-sha224"}, + {HMAC_SHA256_INDEX, "hmac-sha256"}, + {HMAC_SHA384_INDEX, "hmac-sha384"}, + {HMAC_SHA512_INDEX, "hmac-sha512"}, + {RSA_INDEX, "rsa"}, + {DH_INDEX, "dh"}, + {EC_INDEX, "ec"} +}; + +void initEngines(JNIEnv* env, jbooleanArray algorithmKaeFlags) { + if (algorithmKaeFlags == NULL) { + return; + } + + // get jTemp + jboolean* jTemp = NULL; + int length = (*env)->GetArrayLength(env, algorithmKaeFlags); + jTemp = (jboolean*) malloc(length); + if (jTemp == NULL) { + KAE_ThrowOOMException(env, "initEngines GetArrayLength error"); + return; + } + (*env)->GetBooleanArrayRegion(env, algorithmKaeFlags, 0, length, jTemp); + + // assign engines + int minLen = length < ENGINE_LENGTH ? length : ENGINE_LENGTH; + int i; + for (i = 0; i < minLen; i++) { + if (jTemp[i]) { + engines[i] = kaeEngine; + engineFlags[i] = JNI_TRUE; + } + } + if (length < ENGINE_LENGTH) { + for (i = minLen; i < ENGINE_LENGTH; i++) { + engines[i] = kaeEngine; + engineFlags[i] = JNI_TRUE; + } + } + + // free jTemp + if (jTemp != NULL) { + free(jTemp); + } +} + +jbooleanArray getEngineFlags(JNIEnv* env) { + jbooleanArray array = (*env)->NewBooleanArray(env, ENGINE_LENGTH); + (*env)->SetBooleanArrayRegion(env, array, 0, ENGINE_LENGTH, engineFlags); + return array; +} + +ENGINE* GetEngineByAlgorithmIndex(AlgorithmIndex algorithmIndex) { + return engines[algorithmIndex]; +} + +/* + * Get the engine used by the specified algorithm. + * @param beginIndex the beginning index, inclusive. + * @param endIndex the ending index, exclusive. + * @param algorithmName algorithm name + * @return engine + */ +ENGINE* GetEngineByBeginIndexAndEndIndex(int beginIndex, int endIndex, + const char* algorithmName) { + if (beginIndex < 0 || endIndex > ENGINE_LENGTH) { + return NULL; + } + + int i; + for (i = beginIndex; i < endIndex; i++) { + if (strcasecmp(kaeAlgorithms[i].algorithmName, algorithmName) == 0) { + return engines[kaeAlgorithms[i].algorithmIndex]; + } + } + return NULL; +} + +ENGINE* GetHmacEngineByAlgorithmName(const char* algorithmName) { + char prefix[] = {"hmac-"}; + int len = strlen(algorithmName); + int newLen = strlen(algorithmName) + strlen(prefix) + 1; + char* newAlgorithmName = NULL; + newAlgorithmName = malloc(newLen); + if (newAlgorithmName == NULL) { + return NULL; + } + strcpy(newAlgorithmName, prefix); + strcat(newAlgorithmName, algorithmName); + ENGINE* engine = GetEngineByBeginIndexAndEndIndex(HMAC_MD5_INDEX, HMAC_SHA512_INDEX + 1, newAlgorithmName); + if (newAlgorithmName != NULL) { + free(newAlgorithmName); + } + return engine; +} + +ENGINE* GetDigestEngineByAlgorithmName(const char* algorithmName) { + return GetEngineByBeginIndexAndEndIndex(MD5_INDEX, SM3_INDEX + 1, algorithmName); +} + +ENGINE* GetAesEngineByAlgorithmName(const char* algorithmName) { + return GetEngineByBeginIndexAndEndIndex(AES_128_ECB_INDEX, AES_256_GCM_INDEX + 1, algorithmName); +} + +ENGINE* GetSm4EngineByAlgorithmName(const char* algorithmName) { + return GetEngineByBeginIndexAndEndIndex(SM4_ECB_INDEX, SM4_OFB_INDEX + 1, algorithmName); +} diff --git a/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_util.h b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_util.h new file mode 100644 index 000000000..6eb980d62 --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/kae_util.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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. + */ + +#ifndef KAE_UTIL_H +#define KAE_UTIL_H + +#include +#include + +typedef enum { + MD5_INDEX, + SHA256_INDEX, + SHA384_INDEX, + SM3_INDEX, + AES_128_ECB_INDEX, + AES_128_CBC_INDEX, + AES_128_CTR_INDEX, + AES_128_GCM_INDEX, + AES_192_ECB_INDEX, + AES_192_CBC_INDEX, + AES_192_CTR_INDEX, + AES_192_GCM_INDEX, + AES_256_ECB_INDEX, + AES_256_CBC_INDEX, + AES_256_CTR_INDEX, + AES_256_GCM_INDEX, + SM4_ECB_INDEX, + SM4_CBC_INDEX, + SM4_CTR_INDEX, + SM4_OFB_INDEX, + HMAC_MD5_INDEX, + HMAC_SHA1_INDEX, + HMAC_SHA224_INDEX, + HMAC_SHA256_INDEX, + HMAC_SHA384_INDEX, + HMAC_SHA512_INDEX, + RSA_INDEX, + DH_INDEX, + EC_INDEX +} AlgorithmIndex; + +typedef struct { + AlgorithmIndex algorithmIndex; + const char* algorithmName; +} KAEAlgorithm; + +/* jbyteArray convert to BIGNUM */ +BIGNUM* KAE_GetBigNumFromByteArray(JNIEnv* env, jbyteArray byteArray); + +/* release BIGNUM allocat from */ +void KAE_ReleaseBigNumFromByteArray(BIGNUM* bn); + +/* BIGNUM convert to jbyteArray */ +jbyteArray KAE_GetByteArrayFromBigNum(JNIEnv* env, const BIGNUM* bn); + +void SetKaeEngine(ENGINE* engine); + +ENGINE* GetKaeEngine(); + +void initEngines(JNIEnv* env, jbooleanArray algorithmKaeFlags); + +jbooleanArray getEngineFlags(JNIEnv* env); + +ENGINE* GetEngineByAlgorithmIndex(AlgorithmIndex algorithmIndex); + +ENGINE* GetHmacEngineByAlgorithmName(const char* algorithmName); + +ENGINE* GetDigestEngineByAlgorithmName(const char* algorithmName); + +ENGINE* GetAesEngineByAlgorithmName(const char* algorithmName); + +ENGINE* GetSm4EngineByAlgorithmName(const char* algorithmName); + +#endif diff --git a/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/openssl_ad.h b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/openssl_ad.h new file mode 100644 index 000000000..f4b552137 --- /dev/null +++ b/src/jdk.crypto.kaeprovider/linux/native/libj2kae/org/openeuler/security/openssl/openssl_ad.h @@ -0,0 +1,10 @@ +#include +#include + +#if defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3 + +// ERR_GET_FUNC() was removed since Openssl3 +#ifndef ERR_GET_FUNC +#define ERR_GET_FUNC(e) (int)(((e) >> 12L) & 0xFFFL) +#endif +#endif diff --git a/test/jdk/TEST.groups b/test/jdk/TEST.groups index 21793455f..b24070a70 100644 --- a/test/jdk/TEST.groups +++ b/test/jdk/TEST.groups @@ -247,6 +247,9 @@ jdk_security = \ jdk_security_infra = \ security/infra/java/security/cert/CertPathValidator/certification +jdk_kae_security = \ + org/openeuler/security/openssl + jdk_text = \ java/text \ sun/text diff --git a/test/jdk/java/security/KeyAgreement/KeyAgreementTest.java b/test/jdk/java/security/KeyAgreement/KeyAgreementTest.java index 1e16c157f..9ed420d52 100644 --- a/test/jdk/java/security/KeyAgreement/KeyAgreementTest.java +++ b/test/jdk/java/security/KeyAgreement/KeyAgreementTest.java @@ -39,6 +39,7 @@ import java.security.KeyPairGenerator; import java.security.spec.NamedParameterSpec; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.ECGenParameterSpec; +import java.security.Security; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -53,7 +54,13 @@ public class KeyAgreementTest { String kpgAlgo = args[1]; String provider = System.getProperty("test.provider.name", args[2]); System.out.println("Testing " + kaAlgo); - AlgoSpec aSpec = AlgoSpec.valueOf(AlgoSpec.class, kaAlgo); + AlgoSpec aSpec; + if (Security.getProperty("security.provider.1").equals("KAEProvider") && + kaAlgo.equals("ECDH")) { + aSpec = AlgoSpec.valueOf(AlgoSpec.class, "KAEECDH"); + } else { + aSpec = AlgoSpec.valueOf(AlgoSpec.class, kaAlgo); + } List specs = aSpec.getAlgorithmParameterSpecs(); for (AlgorithmParameterSpec spec : specs) { testKeyAgreement(provider, kaAlgo, kpgAlgo, spec); @@ -70,6 +77,7 @@ public class KeyAgreementTest { // "java.base/share/classes/sun/security/util/CurveDB.java" ECDH("secp256r1", "secp384r1", "secp521r1"), + KAEECDH("secp224r1", "secp256r1", "secp384r1", "secp521r1"), XDH("X25519", "X448", "x25519"), // There is no curve for DiffieHellman DiffieHellman(new String[]{}); @@ -81,6 +89,7 @@ public class KeyAgreementTest { for (String crv : curves) { switch (this.name()) { case "ECDH": + case "KAEECDH": specs.add(new ECGenParameterSpec(crv)); break; case "XDH": diff --git a/test/jdk/java/security/Signature/SignatureGetInstance.java b/test/jdk/java/security/Signature/SignatureGetInstance.java index c246773f8..b69258cd9 100644 --- a/test/jdk/java/security/Signature/SignatureGetInstance.java +++ b/test/jdk/java/security/Signature/SignatureGetInstance.java @@ -54,9 +54,13 @@ public class SignatureGetInstance { MyPrivKey testPriv = new MyPrivKey(); MyPubKey testPub = new MyPubKey(); + Provider kaeProvider = Security.getProvider("KAEProvider"); + String expectedProvName = kaeProvider != null ? "KAEProvider" : "SunRsaSign"; + + testDblInit(testPriv, testPub, true, "TestProvider"); testDblInit(kp.getPrivate(), kp.getPublic(), true, - System.getProperty("test.provider.name", "SunRsaSign")); + System.getProperty("test.provider.name", expectedProName)); testDblInit(testPriv, kp.getPublic(), false, null); testDblInit(kp.getPrivate(), testPub, false, null); diff --git a/test/jdk/org/openeuler/security/openssl/AESTest.java b/test/jdk/org/openeuler/security/openssl/AESTest.java new file mode 100644 index 000000000..aac14568d --- /dev/null +++ b/test/jdk/org/openeuler/security/openssl/AESTest.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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 org.openeuler.security.openssl.KAEProvider; + +import java.nio.charset.StandardCharsets; +import java.security.Security; +import java.security.spec.AlgorithmParameterSpec; +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; + +/** + * @test + * @summary Basic test for AES + * @modules jdk.crypto.kaeprovider/org.openeuler.security.openssl + * @requires os.arch=="aarch64" + * @run main AESTest + */ + +public class AESTest { + private static final String[] ALGORITHM = {"AES", "AES_128", "AES_192", "AES_256"}; + private static final String[] MODES = {"ECB", "CBC", "CTR", "GCM"}; + private static final String[] PADDING = {"NoPadding", "PKCS5Padding"}; + private static final int AES_128_KEY_LENGTH = 128; + private static final int AES_192_KEY_LENGTH = 192; + private static final int AES_256_KEY_LENGTH = 256; + private static String plainText = "helloworldhellow"; // 16bytes for NoPadding + private static String shortPlainText = "helloworld"; // 5 bytes for padding + + public static void main(String[] args) throws Exception { + Security.insertProviderAt(new KAEProvider(), 1); + for (String algo : ALGORITHM) { + for (String mode : MODES) { + int padKinds = 2; + if (mode.equalsIgnoreCase("CTR")) { + padKinds = 1; + } + for (int k = 0; k < padKinds; k++) { + test(algo, mode, PADDING[k]); + } + } + } + } + + public static void test(String algo, String mo, String pad) throws Exception { + AlgorithmParameterSpec aps = null; + + Cipher cipher = Cipher.getInstance(algo + "/" + mo + "/" + pad); + + KeyGenerator kg = KeyGenerator.getInstance("AES"); + if (algo.equalsIgnoreCase("AES_192")) { + kg.init(AES_192_KEY_LENGTH); + } else if (algo.equalsIgnoreCase("AES_256")) { + kg.init(AES_256_KEY_LENGTH); + } else { + kg.init(AES_128_KEY_LENGTH); + } + + SecretKey key = kg.generateKey(); + + // encrypt + if (!mo.equalsIgnoreCase("GCM")) { + cipher.init(Cipher.ENCRYPT_MODE, key, aps); + } else { + cipher.init(Cipher.ENCRYPT_MODE, key); + } + + String cipherString = null; + if (!pad.equalsIgnoreCase("NoPadding")) { + cipherString = shortPlainText; + } else { + cipherString = plainText; + } + byte[] cipherText = cipher.doFinal(cipherString.getBytes(StandardCharsets.UTF_8)); + if (!mo.equalsIgnoreCase("ECB")) { + aps = new IvParameterSpec(cipher.getIV()); + } else { + aps = null; + } + + if (!mo.equalsIgnoreCase("GCM")) { + cipher.init(Cipher.DECRYPT_MODE, key, aps); + } else { + cipher.init(Cipher.DECRYPT_MODE, key, cipher.getParameters()); + } + + String decryptPlainText = new String(cipher.doFinal(cipherText)); + + if (!cipherString.equals(decryptPlainText)) { + throw new RuntimeException("aes decryption failed, algo = " + algo + ", mo = " + mo + ", pad = " + pad); + } + } +} diff --git a/test/jdk/org/openeuler/security/openssl/DHTest.java b/test/jdk/org/openeuler/security/openssl/DHTest.java new file mode 100644 index 000000000..51dab7664 --- /dev/null +++ b/test/jdk/org/openeuler/security/openssl/DHTest.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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. + */ + +import java.io.Serializable; +import java.math.BigInteger; +import java.security.*; +import java.util.Arrays; +import javax.crypto.KeyAgreement; +import javax.crypto.spec.*; +import org.openeuler.security.openssl.KAEProvider; + +/** + * This class implements the Diffie-Hellman key exchange algorithm. + * D-H means combining your private key with your partners public key to + * generate a number. The peer does the same with its private key and our + * public key. Through the magic of Diffie-Hellman we both come up with the + * same number. This number is secret (discounting MITM attacks) and hence + * called the shared secret. It has the same length as the modulus, e.g. 512 + * or 1024 bit. Man-in-the-middle attacks are typically countered by an + * independent authentication step using certificates (RSA, DSA, etc.). + * + * The thing to note is that the shared secret is constant for two partners + * with constant private keys. This is often not what we want, which is why + * it is generally a good idea to create a new private key for each session. + * Generating a private key involves one modular exponentiation assuming + * suitable D-H parameters are available. + * + * General usage of this class (TLS DHE case): + * . if we are server, call DHCrypt(keyLength,random). This generates + * an ephemeral keypair of the request length. + * . if we are client, call DHCrypt(modulus, base, random). This + * generates an ephemeral keypair using the parameters specified by + * the server. + * . send parameters and public value to remote peer + * . receive peers ephemeral public key + * . call getAgreedSecret() to calculate the shared secret + * + * In TLS the server chooses the parameter values itself, the client must use + * those sent to it by the server. + * + * The use of ephemeral keys as described above also achieves what is called + * "forward secrecy". This means that even if the authentication keys are + * broken at a later date, the shared secret remains secure. The session is + * compromised only if the authentication keys are already broken at the + * time the key exchange takes place and an active MITM attack is used. + * This is in contrast to straightforward encrypting RSA key exchanges. + * + */ + +/** + * @test + * @summary Basic test for DH + * @modules jdk.crypto.kaeprovider/org.openeuler.security.openssl + * @requires os.arch=="aarch64" + * @run main DHTest + */ + +public class DHTest implements Serializable { + private static BigInteger g512; + private static BigInteger p512; + + private static volatile Provider sunJceProvider; + private static volatile Provider kaeProvider; + + public static void main(String[] args) throws Exception { + Security.addProvider(new KAEProvider()); + sunJceProvider = Security.getProvider("SunJCE"); + kaeProvider = Security.getProvider("KAEProvider"); + + g512 = new BigInteger("30270326776012916323988175351831539351616124181011347910931933302640902603480679235129557617566480716138395926949700593986872757726720164601940036524221141391913433558162442022339559255078339658108149162251643458301671465579040759659507434340437396584664407572026953757806341363255195983514333141770938654900099033797866272818739547343977583089845850158637618703095710047154252655157633638171416516716598940884520592858762209135010804267830977334033327483815694794951984230264309784679409488441905236794443014066406150649287037909246107758452315504212879842042858577191624250834553614056794526338841821045329189780334"); + + p512 = new BigInteger("27672987386729926592037876826877634387173876890702920770064392919138769821035856568775311919542560094764667151024449425954917954337048895981297730855891532066350935045229294626339548842381843985759061682551900379979643117695834175891578650111093016914264824311693147701566019122696621248493126219217339690346346921463135605151471303957324058301097079967414639146647429422884520134312590056632178576758580657240245655739869017244657144448267757255018625514803292549109401806336918448001843022629625467069714240279603204909633404992842479161100500474744098408277938070656334892106100534117209709263785505019003765693651"); + + DHParameterSpec dhParams = new DHParameterSpec(p512, g512); + KeyPairGenerator SunJCEkeyGen = KeyPairGenerator.getInstance("DH", sunJceProvider); + KeyPairGenerator KAEkeyGen = KeyPairGenerator.getInstance("DH", kaeProvider); + SunJCEkeyGen.initialize(dhParams, new SecureRandom()); + KAEkeyGen.initialize(dhParams, new SecureRandom()); + KeyAgreement aKeyAgree = KeyAgreement.getInstance("DH", sunJceProvider); + KeyPair aPair = SunJCEkeyGen.generateKeyPair(); + KeyAgreement bKeyAgree = KeyAgreement.getInstance("DH", kaeProvider); + KeyPair bPair = KAEkeyGen.generateKeyPair(); + + aKeyAgree.init(aPair.getPrivate()); + bKeyAgree.init(bPair.getPrivate()); + + aKeyAgree.doPhase(bPair.getPublic(), true); + bKeyAgree.doPhase(aPair.getPublic(), true); + + MessageDigest hash = MessageDigest.getInstance("MD5"); + byte[] b1 = hash.digest(aKeyAgree.generateSecret()); + byte[] b2 = hash.digest(bKeyAgree.generateSecret()); + if(Arrays.equals(b1, b2)){ + System.out.println("SUCCESS!"); + }else{ + System.out.println("Failed!"); + throw new RuntimeException("Not Equal DH keyagreement ouput from SunJCE and KAE Provider!"); + } + } +} \ No newline at end of file diff --git a/test/jdk/org/openeuler/security/openssl/DigestTest.java b/test/jdk/org/openeuler/security/openssl/DigestTest.java new file mode 100644 index 000000000..120b6ac47 --- /dev/null +++ b/test/jdk/org/openeuler/security/openssl/DigestTest.java @@ -0,0 +1,61 @@ +import org.openeuler.security.openssl.KAEProvider; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.Security; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * @test + * @summary Basic test for MD5 SHA256 SHA384 + * @modules jdk.crypto.kaeprovider/org.openeuler.security.openssl + * @requires os.arch=="aarch64" + * @run main/othervm DigestTest + */ +public class DigestTest { + private static String PLAIN_TEXT = "hello world"; + + private static Map alg = new HashMap(); + + static { + alg.put("MD5", new byte[] {94, -74, 59, -69, -32, 30, -18, -48, -109, -53, 34, -69, -113, 90, -51, -61}); + alg.put( + "SHA-256", + new byte[] { + -71, 77, 39, -71, -109, 77, 62, 8, -91, 46, 82, -41, -38, 125, -85, -6, + -60, -124, -17, -29, 122, 83, -128, -18, -112, -120, -9, -84, -30, -17, -51, -23 + }); + alg.put( + "SHA-384", + new byte[] { + -3, -67, -114, 117, -90, 127, 41, -9, 1, -92, -32, 64, 56, 94, 46, 35, + -104, 99, 3, -22, 16, 35, -110, 17, -81, -112, 127, -53, -72, 53, 120, -77, + -28, 23, -53, 113, -50, 100, 110, -3, 8, 25, -35, -116, 8, -115, -31, -67 + }); + alg.put( + "SM3", + new byte[] { + 68, -16, 6, 30, 105, -6, 111, -33, -62, -112, -60, -108, 101, 74, 5, + -36, 12, 5, 61, -89, -27, -59, 43, -124, -17, -109, -87, -42, 125, 63, + -1, -120 + }); + } + + public static void main(String[] args) throws Exception { + Security.insertProviderAt(new KAEProvider(), 1); + for (String key : alg.keySet()) { + test(PLAIN_TEXT, key, alg.get(key)); + } + } + + public static void test(String plainText, String algo, byte[] expectRes) throws Exception { + MessageDigest md = MessageDigest.getInstance(algo); + md.update(plainText.getBytes(StandardCharsets.UTF_8)); + byte[] res = md.digest(); + if (!Arrays.equals(res, expectRes)) { + throw new RuntimeException(algo + " failed"); + } + } +} diff --git a/test/jdk/org/openeuler/security/openssl/ECDHTest.java b/test/jdk/org/openeuler/security/openssl/ECDHTest.java new file mode 100644 index 000000000..791fec252 --- /dev/null +++ b/test/jdk/org/openeuler/security/openssl/ECDHTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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 org.openeuler.security.openssl.KAEProvider; +import sun.security.ec.ECPrivateKeyImpl; +import sun.security.ec.ECPublicKeyImpl; + +import javax.crypto.KeyAgreement; +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.Provider; +import java.security.Security; +import java.security.spec.ECFieldFp; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.EllipticCurve; +import java.util.Arrays; +import java.nio.charset.StandardCharsets; +import java.security.spec.*; +import java.security.KeyFactory; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; + +/** + * @test + * @summary Basic test for ECDH + * @modules jdk.crypto.kaeprovider/org.openeuler.security.openssl + * @modules jdk.crypto.ec/sun.security.ec + * @requires os.arch=="aarch64" + * @run main ECDHTest + */ + +public class ECDHTest { + + private static KeyPairGenerator keyPairGenerator; + private static String algorithm = "EC"; + private static int[] keyArr = {224, 256, 384, 521}; + + public static void main(String[] args) throws Exception { + Security.insertProviderAt(new KAEProvider(), 1); + + BigInteger a = new BigInteger("26959946667150639794667015087019630673557916260026308143510066298878"); + BigInteger b = new BigInteger("18958286285566608000408668544493926415504680968679321075787234672564"); + BigInteger p = new BigInteger("26959946667150639794667015087019630673557916260026308143510066298881"); + BigInteger x = new BigInteger("19277929113566293071110308034699488026831934219452440156649784352033"); + BigInteger y = new BigInteger("19926808758034470970197974370888749184205991990603949537637343198772"); + EllipticCurve CURVE = new EllipticCurve(new ECFieldFp(p), a, b); + ECPoint POINT = new ECPoint(x, y); + BigInteger ORDER = new BigInteger("26959946667150639794667015087019625940457807714424391721682722368061"); + int COFACTOR = 1; + ECParameterSpec PARAMS = new ECParameterSpec(CURVE, POINT, ORDER, COFACTOR); + + testKeyPairByParam(PARAMS); + for (int keySize : keyArr) { + testKeyPairByKeySize(keySize); + } + + KeyFactory keyFactory = KeyFactory.getInstance("EC"); + ECPrivateKeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger("20135071615800221517902437867016717688420688735490569283842831828983"), PARAMS); + ECPrivateKeyImpl ecPrivKey = (ECPrivateKeyImpl)keyFactory.generatePrivate(privateKeySpec); + + ECPoint ecPoint = new ECPoint(new BigInteger("9490267631555585552004372465967099662885480699902812460349461311384"), new BigInteger("1974573604976093871117393045089050409882519645527397292712281520811")); + ECPublicKeySpec publicKeySpec = new ECPublicKeySpec(ecPoint,PARAMS); + ECPublicKeyImpl ecPublicKey = (ECPublicKeyImpl)keyFactory.generatePublic(publicKeySpec); + testKeyAgreement(ecPrivKey, ecPublicKey, new byte[]{-88, -65, 43, -84, 26, 43, 46, 106, 20, 39, -76, 30, -71, 72, -102, 120, 108, -92, -86, -14, -96, -42, 93, -40, -43, -25, 15, -62}); + } + + public static void testKeyPairByParam(ECParameterSpec PARAMS) throws Exception { + keyPairGenerator = KeyPairGenerator.getInstance(algorithm); + keyPairGenerator.initialize(PARAMS); + KeyPair keyPair = keyPairGenerator.generateKeyPair(); + ECPrivateKeyImpl ecPrivKey = (ECPrivateKeyImpl) keyPair.getPrivate(); + ECPublicKeyImpl ecPublicKey = (ECPublicKeyImpl) keyPair.getPublic(); + } + + public static void testKeyPairByKeySize(int keySize) throws Exception { + keyPairGenerator = KeyPairGenerator.getInstance(algorithm); + keyPairGenerator.initialize(keySize); + KeyPair keyPair = keyPairGenerator.generateKeyPair(); + ECPrivateKeyImpl ecPrivKey = (ECPrivateKeyImpl) keyPair.getPrivate(); + ECPublicKeyImpl ecPublicKey = (ECPublicKeyImpl) keyPair.getPublic(); + } + + public static void testKeyAgreement(ECPrivateKeyImpl ecPrivKey, ECPublicKeyImpl ecPublicKey, byte[] expectRes) throws Exception { + KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH"); + keyAgreement.init(ecPrivKey); + keyAgreement.doPhase(ecPublicKey, true); + byte[] res = keyAgreement.generateSecret(); + if (!Arrays.equals(res, expectRes)) { + throw new RuntimeException("keyagreement failed"); + } + } +} \ No newline at end of file diff --git a/test/jdk/org/openeuler/security/openssl/HmacTest.java b/test/jdk/org/openeuler/security/openssl/HmacTest.java new file mode 100644 index 000000000..bddc37b4c --- /dev/null +++ b/test/jdk/org/openeuler/security/openssl/HmacTest.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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 org.openeuler.security.openssl.KAEProvider; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.security.Key; +import java.security.Security; +import java.util.Arrays; + +/** + * @test + * @summary test for Hmac + * @modules jdk.crypto.kaeprovider/org.openeuler.security.openssl + * @requires os.arch=="aarch64" + * @run main/othervm HmacTest + */ +public class HmacTest { + private static final byte[] PLAIN_BYTES = "hello world".getBytes(); + private static final String[] ALGORITHMS = new String[]{ + "HmacMD5", + "HmacSHA1", + "HmacSHA224", + "HmacSHA256", + "HmacSHA384", + "HmacSHA512", + }; + private static final byte[][] EXPECTED_BYTES = { + {-40, 63, -96, 13, 107, -33, -1, -53, -116, 117, 75, -6, 85, -88, -112, -90}, + {-68, 104, 112, -36, 123, 123, -92, 104, 89, -90, 63, 56, 84, 45, 12, -7, 41, 103, -105, -27}, + {-31, 0, 103, 51, -119, -61, 2, -76, -83, -113, 95, 86, 8, 46, 91, 20, + -15, -23, -71, 62, -50, 86, -54, 71, -94, -47, -103, 43}, + {-69, -83, -3, 7, 61, 38, -122, -59, 7, -53, 106, 114, 58, 102, 65, -118, + 54, -50, 116, -56, 110, 54, -71, 36, 60, 84, 14, 97, 78, 18, -119, -24}, + {100, -58, 106, 64, -96, 91, 99, -33, 36, -78, -53, -50, -78, 116, -110, 85, + 84, -5, -63, 17, 51, -69, -39, -122, 65, 8, -122, -43, 39, 13, -41, -52, + 45, -38, -59, 70, 17, -87, -63, -126, 4, 120, -77, 71, 119, 96, -2, -68}, + {-89, 47, -98, -12, 110, -88, 23, 2, 28, 26, -71, 53, -108, 54, -52, 1, + -121, -121, 87, 6, -78, 123, -14, -86, 127, 114, 124, -73, -98, 79, -122, 69, + -32, 50, 48, -79, -110, 66, 38, 70, -3, -76, 95, 55, 74, 48, 57, -121, + 22, 60, -83, -109, 59, 79, 0, -49, 107, 88, -82, -35, 87, -36, 49, -54} + }; + private static final Key key = new SecretKeySpec("mac".getBytes(), ""); + + public static void main(String[] args) throws Exception { + Security.insertProviderAt(new KAEProvider(), 1); + for (int i = 0; i < ALGORITHMS.length; i++) { + test(ALGORITHMS[i], key, PLAIN_BYTES, EXPECTED_BYTES[i]); + } + } + + private static void test(String algorithm, Key key, byte[] inputBytes, byte[] expectedBytes) throws Exception { + Mac mac = Mac.getInstance(algorithm); + mac.init(key); + mac.update(inputBytes); + byte[] bytes = mac.doFinal(); + if (!(mac.getProvider() instanceof KAEProvider)) { + throw new RuntimeException(algorithm + " failed," + + "provider=" + mac.getProvider().getClass() + "," + + "expectedProvider=" + KAEProvider.class); + } + if (!Arrays.equals(bytes, expectedBytes)) { + throw new RuntimeException(algorithm + " failed," + + "bytes=" + Arrays.toString(bytes) + "," + + "expectedBytes=" + Arrays.toString(expectedBytes)); + } + } +} diff --git a/test/jdk/org/openeuler/security/openssl/KAEConfTest.java b/test/jdk/org/openeuler/security/openssl/KAEConfTest.java new file mode 100644 index 000000000..4076aa2fc --- /dev/null +++ b/test/jdk/org/openeuler/security/openssl/KAEConfTest.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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 org.openeuler.security.openssl.KAEConfig; +import org.openeuler.security.openssl.KAEProvider; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; + +/* + * @test + * @summary Test KAE Conf + * @modules jdk.crypto.kaeprovider/org.openeuler.security.openssl + * @requires os.arch=="aarch64" + * @run main/othervm KAEConfTest DEFAULT + * @run main/othervm KAEConfTest SPECIFY + */ +public class KAEConfTest { + private static final String DEFAULT_CONF = System.getProperty("java.home") + + File.separator + "conf" + File.separator + "kaeprovider.conf"; + + private static final String SPECIFY_CONF = System.getProperty("user.dir") + + File.separator + "kaeprovider.conf"; + + private static final String SPECIFY_LOG_PATH = System.getProperty("user.dir") + File.separator + "kae.log"; + private static final List files = new ArrayList<>(); + + enum Mode { + DEFAULT, + SPECIFY + } + + public static void main(String[] args) throws IOException { + Mode mode = getMode(args); + try { + init(mode); + new KAEProvider(); + test(mode); + } finally { + KAETestHelper.cleanUp(files); + } + } + + private static Mode getMode(String[] args) { + if (args.length <= 0) { + return Mode.DEFAULT; + } + return Mode.valueOf(args[0]); + } + + private static void init(Mode mode) throws IOException { + if (Mode.SPECIFY.equals(mode)) { + System.setProperty("kae.conf", SPECIFY_CONF); + File file = new File(SPECIFY_CONF); + if (!file.exists()) { + Files.createFile(file.toPath()); + } + files.add(file); + try (FileWriter fileWriter = new FileWriter(file)) { + fileWriter.write("kae.log=true"); + fileWriter.flush(); + } + } + } + + private static void testDefault() { + File file = new File(DEFAULT_CONF); + if (!file.exists()) { + throw new RuntimeException("test failed"); + } + } + + private static void testSpecify() { + String value = KAEConfig.privilegedGetOverridable("kae.log"); + if (!"true".equals(value)) { + throw new RuntimeException("test failed : kae.log=" + value); + } + File file = new File(SPECIFY_LOG_PATH); + if (!file.exists()) { + throw new RuntimeException(SPECIFY_LOG_PATH + "does not exist"); + } + // kae log file + files.add(file); + } + + private static void test(Mode mode) { + switch (mode) { + case DEFAULT: + testDefault(); + break; + case SPECIFY: + testSpecify(); + break; + default: + throw new IllegalArgumentException("invalid mode"); + } + } +} diff --git a/test/jdk/org/openeuler/security/openssl/KAEDisabledAlgorithmsTest.java b/test/jdk/org/openeuler/security/openssl/KAEDisabledAlgorithmsTest.java new file mode 100644 index 000000000..e8d767f0c --- /dev/null +++ b/test/jdk/org/openeuler/security/openssl/KAEDisabledAlgorithmsTest.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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 org.openeuler.security.openssl.KAEConfig; +import org.openeuler.security.openssl.KAEProvider; + +import java.util.HashSet; +import java.util.Set; + +/* + * @test + * @summary Test property kae.engine.disableAlgorithms + * @modules jdk.crypto.kaeprovider/org.openeuler.security.openssl + * @requires os.arch=="aarch64" + * @run main/othervm -Dkae.engine.disabledAlgorithms=md5 KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.disabledAlgorithms=sha256 KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.disabledAlgorithms=sha384 KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.disabledAlgorithms=sm3 KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-128-ecb KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-128-cbc KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-128-ctr KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-128-gcm KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-192-ecb KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-192-cbc KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-192-ctr KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-192-gcm KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-256-ecb KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-256-cbc KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-256-ctr KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-256-gcm KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.disabledAlgorithms=sm4-ecb KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.disabledAlgorithms=sm4-cbc KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.disabledAlgorithms=sm4-ctr KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.disabledAlgorithms=sm4-ofb KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.disabledAlgorithms=hmac-md5 KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.disabledAlgorithms=hmac-sha1 KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.disabledAlgorithms=hmac-sha224 KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.disabledAlgorithms=hmac-sha256 KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.disabledAlgorithms=hmac-sha384 KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.disabledAlgorithms=hmac-sha512 KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.disabledAlgorithms=rsa KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.disabledAlgorithms=dh KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.disabledAlgorithms=ec KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.disabledAlgorithms=aes-128-gcm,aes-192-gcm,aes-256-gcm KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.disabledAlgorithms=md5,aes-128-ecb,sm4-ecb,hmac-sha1,rsa,dh,ec KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=md5 KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=sha256 KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=sha384 KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=sm3 KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-128-ecb KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-128-cbc KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-128-ctr KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-128-gcm KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-192-ecb KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-192-cbc KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-192-ctr KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-192-gcm KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-256-ecb KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-256-cbc KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-256-ctr KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-256-gcm KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=sm4-ecb KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=sm4-cbc KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=sm4-ctr KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=sm4-ofb KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=hmac-md5 KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=hmac-sha1 KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=hmac-sha224 KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=hmac-sha256 KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=hmac-sha384 KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=hmac-sha512 KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=rsa KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=dh KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=ec KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=aes-128-gcm,aes-192-gcm,aes-256-gcm KAEDisabledAlgorithmsTest + * @run main/othervm -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.engine.disabledAlgorithms=md5,aes-128-ecb,sm4-ecb,hmac-sha1,rsa,dh,ec KAEDisabledAlgorithmsTest + */ +public class KAEDisabledAlgorithmsTest { + + public static void main(String[] args) { + KAETestHelper.Engine engine = KAETestHelper.getEngine(); + if (!engine.isValid()) { + System.out.println("Skip test, engine " + engine.getEngineId() + " does not exist."); + return; + } + String[] disabledAlgorithms = getDisabledAlgorithms(); + init(); + new KAEProvider(); + test(disabledAlgorithms); + } + + private static final String[] PROPERTY_NAMES = new String[]{ + "kae.digest.useKaeEngine", + "kae.aes.useKaeEngine", + "kae.sm4.useKaeEngine", + "kae.hmac.useKaeEngine", + "kae.rsa.useKaeEngine", + "kae.dh.useKaeEngine", + "kae.ec.useKaeEngine" + }; + + private static String[] getDisabledAlgorithms() { + String value = System.getProperty("kae.engine.disabledAlgorithms"); + if (value == null) { + return new String[0]; + } + return value.split(","); + } + + private static void init() { + for (String propertyName : PROPERTY_NAMES) { + System.setProperty(propertyName, "true"); + } + } + + private static void test(String[] disabledAlgorithms) { + boolean[] useKaeEngineFlags = KAEConfig.getUseKaeEngineFlags(); + Set disabledAlgorithmIndexSet = new HashSet<>(); + + // test disabled algorithms + for (String disabledAlgorithm : disabledAlgorithms) { + Integer index = KAETestHelper.getAlgorithmIndex(disabledAlgorithm); + if (index == null || index < 0 || index >= useKaeEngineFlags.length) { + continue; + } + if (useKaeEngineFlags[index]) { + throw new RuntimeException("test failed"); + } + disabledAlgorithmIndexSet.add(index); + } + + // test other algorithms that are not disabled (except ec) + for (int i = 0; i < useKaeEngineFlags.length - 1; i++) { + if (!disabledAlgorithmIndexSet.contains(i) && !useKaeEngineFlags[i]) { + throw new RuntimeException(KAETestHelper.getAlgorithmName(i) + " algorithm is not disabled"); + } + } + + // test whether the ec algorithm is disabled by default + if (useKaeEngineFlags[useKaeEngineFlags.length - 1]) { + throw new RuntimeException(KAETestHelper.getAlgorithmName(useKaeEngineFlags.length - 1) + + " algorithm is disabled by default"); + } + } +} diff --git a/test/jdk/org/openeuler/security/openssl/KAEEngineIdTest.java b/test/jdk/org/openeuler/security/openssl/KAEEngineIdTest.java new file mode 100644 index 000000000..75248a432 --- /dev/null +++ b/test/jdk/org/openeuler/security/openssl/KAEEngineIdTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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 org.openeuler.security.openssl.KAEProvider; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/* + * @test + * @summary Test KAE property kae.engine.id and kae.libcrypto.useGlobalMode + * @modules jdk.crypto.kaeprovider/org.openeuler.security.openssl + * @requires os.arch=="aarch64" + * @run main/othervm -Dkae.log=true KAEEngineIdTest + * @run main/othervm -Dkae.log=true -Dkae.engine.id=kae KAEEngineIdTest + * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true KAEEngineIdTest + */ +public class KAEEngineIdTest { + + private static final String LOG_PATH = System.getProperty("user.dir") + + File.separator + "kae.log"; + + private static final List files = new ArrayList<>(); + + public static void main(String[] args) throws IOException { + KAETestHelper.Engine engine = KAETestHelper.getEngine(); + if (!engine.isValid()) { + System.out.println("Skip test, engine " + engine.getEngineId() + " does not exist."); + return; + } + + try { + new KAEProvider(); + test(engine); + } finally { + KAETestHelper.cleanUp(files); + } + } + + private static void test(KAETestHelper.Engine engine) throws IOException { + File file = new File(LOG_PATH); + if (!file.exists()) { + throw new RuntimeException(LOG_PATH + " does not exist"); + } + files.add(file); + try (BufferedReader bufferedReader = new BufferedReader(new FileReader(file))) { + String s = bufferedReader.readLine(); + if (!s.contains(engine.getEngineId() + " engine was found")) { + throw new RuntimeException("test failed"); + } + } + } +} diff --git a/test/jdk/org/openeuler/security/openssl/KAELogTest.java b/test/jdk/org/openeuler/security/openssl/KAELogTest.java new file mode 100644 index 000000000..f635d96f3 --- /dev/null +++ b/test/jdk/org/openeuler/security/openssl/KAELogTest.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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 org.openeuler.security.openssl.KAEProvider; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/* + * @test + * @summary Test KAE log + * @modules jdk.crypto.kaeprovider/org.openeuler.security.openssl + * @requires os.arch=="aarch64" + * @run main/othervm KAELogTest + * @run main/othervm -Dkae.log=false KAELogTest + * @run main/othervm -Dkae.log=true KAELogTest + * @run main/othervm -Dkae.log=true -Dkae.log.file=./KAELogTest/kae.log KAELogTest + */ +public class KAELogTest { + private static final String DEFAULT_LOG_PATH = System.getProperty("user.dir") + + File.separator + "kae.log"; + + private static final String SPECIFY_LOG_PATH = System.getProperty("user.dir") + + File.separator + "KAELogTest" + File.separator + "kae.log"; + + private static final List files = new ArrayList<>(); + + enum Mode { + DEFAULT, + DISABLE, + ENABLE, + SPECIFY + } + + public static void main(String[] args) { + Mode mode = getMode(); + try { + new KAEProvider(); + test(mode); + } finally { + KAETestHelper.cleanUp(files); + } + } + + private static Mode getMode() { + String enableKaeLog = System.getProperty("kae.log"); + if (enableKaeLog == null) { + return Mode.DEFAULT; + } else if ("false".equals(enableKaeLog)) { + return Mode.DISABLE; + } else { + String logPath = System.getProperty("kae.log.file"); + if (logPath == null) { + return Mode.ENABLE; + } + return Mode.SPECIFY; + } + } + + private static void testDefault() { + testDisable(); + } + + private static void testDisable() { + File file = new File(DEFAULT_LOG_PATH); + if (file.exists()) { + throw new RuntimeException("test failed"); + } + } + + private static void testEnable() { + File file = new File(DEFAULT_LOG_PATH); + if (!file.exists()) { + throw new RuntimeException("test failed"); + } + files.add(file); + } + + private static void testSpecify() { + File file = new File(KAELogTest.SPECIFY_LOG_PATH); + if (!file.exists()) { + throw new RuntimeException("test failed"); + } + files.add(file); + files.add(file.getParentFile()); + } + + private static void test(Mode mode) { + switch (mode) { + case DEFAULT: + testDefault(); + break; + case DISABLE: + testDisable(); + break; + case ENABLE: + testEnable(); + break; + case SPECIFY: + testSpecify(); + break; + default: + throw new IllegalArgumentException("invalid mode"); + } + } +} diff --git a/test/jdk/org/openeuler/security/openssl/KAETestHelper.java b/test/jdk/org/openeuler/security/openssl/KAETestHelper.java new file mode 100644 index 000000000..59ad91ddc --- /dev/null +++ b/test/jdk/org/openeuler/security/openssl/KAETestHelper.java @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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 java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +class KAETestHelper { + private static final String KAE_ENGINE_ID = "kae"; + private static final String UADK_ENGINE_ID = "uadk_engine"; + private static boolean hasKaeEngine; + private static boolean hasUadkEngine; + + private static String engineRootPath; + + // algorithm names + private static final String[] ALGORITHM_NAMES = new String[]{ + "md5", + "sha256", + "sha384", + "sm3", + "aes-128-ecb", + "aes-128-cbc", + "aes-128-ctr", + "aes-128-gcm", + "aes-192-ecb", + "aes-192-cbc", + "aes-192-ctr", + "aes-192-gcm", + "aes-256-ecb", + "aes-256-cbc", + "aes-256-ctr", + "aes-256-gcm", + "sm4-ecb", + "sm4-cbc", + "sm4-ctr", + "sm4-ofb", + "hmac-md5", + "hmac-sha1", + "hmac-sha224", + "hmac-sha256", + "hmac-sha384", + "hmac-sha512", + "rsa", + "dh", + "ec" + }; + private static final Map ALGORITHM_NAME_MAP = new HashMap<>(); + + private static final String PROVIDER_NAME = "KAEProvider"; + private static final String USE_OPENSSL_MSG = "Use openssl soft calculation"; + private static final String USE_KAE_HARDWARE_MSG = "enable KAE hardware acceleration"; + private static final Map ALGORITHM_MSG_MAP = new HashMap<>(); + + static { + init(); + } + + enum Engine { + default_engine(hasKaeEngine, KAE_ENGINE_ID), + kae(hasKaeEngine, KAE_ENGINE_ID), + uadk_engine(hasUadkEngine, UADK_ENGINE_ID); + private final boolean isValid; + private final String engineId; + + Engine(boolean isValid, String engineId) { + this.isValid = isValid; + this.engineId = engineId; + } + + public boolean isValid() { + return isValid; + } + + public String getEngineId() { + return engineId; + } + } + + private static void init() { + engineRootPath = System.getenv("OPENSSL_ENGINES"); + if (engineRootPath == null || engineRootPath.equals("")) { + System.out.println("Environment variable OPENSSL_ENGINES is not configured"); + } + hasKaeEngine = hasEngine(KAE_ENGINE_ID); + hasUadkEngine = hasEngine(UADK_ENGINE_ID); + + for (int i = 0; i < ALGORITHM_NAMES.length; i++) { + ALGORITHM_NAME_MAP.put(ALGORITHM_NAMES[i], i); + } + + ALGORITHM_MSG_MAP.put(USE_OPENSSL_MSG, false); + ALGORITHM_MSG_MAP.put(USE_KAE_HARDWARE_MSG, true); + } + + static Integer getAlgorithmIndex(String algorithmName) { + return ALGORITHM_NAME_MAP.get(algorithmName); + } + + static String getAlgorithmName(Integer algorithmIndex) { + return ALGORITHM_NAMES[algorithmIndex]; + } + + private static boolean hasEngine(String engineId) { + String filePath = engineRootPath + File.separator + engineId + ".so"; + File file = new File(filePath); + return file.exists(); + } + + static boolean hasKaeEngine() { + return hasKaeEngine; + } + + static boolean hasUadkEngine() { + return hasUadkEngine; + } + + static void cleanUp(List files) { + for (File file : files) { + System.out.println("delete file : " + file); + file.delete(); + } + } + + static boolean[] parseLog(Engine engine, File file) throws IOException { + boolean[] kaeUseEngineFlags; + String expectedEngineMsg = engine.getEngineId() + " engine was found"; + try (BufferedReader reader = new BufferedReader(new FileReader(file))) { + // load engine message + String engineMsg = reader.readLine(); + if (engineMsg == null || !engineMsg.contains(expectedEngineMsg)) { + throw new RuntimeException("test failed : actual message :" + engineMsg); + } + + // summary message + String summaryMessage = reader.readLine(); + if (summaryMessage == null) { + throw new RuntimeException("test failed : summary message is null"); + } + + kaeUseEngineFlags = new boolean[ALGORITHM_NAMES.length]; + // strategy of each algorithm + String strategy; + while ((strategy = reader.readLine()) != null) { + String[] splitArray = strategy.split("=>"); + if (splitArray.length < 2) { + throw new RuntimeException("test failed : strategy = " + strategy); + } + + // algorithm Index + String algorithm = splitArray[0].replace(" ", ""); + Integer algorithmIndex = ALGORITHM_NAME_MAP.get(algorithm); + if (algorithmIndex == null) { + throw new RuntimeException("test failed : illegal algorithm " + algorithm); + } + + // provider and algorithm value + String detail = splitArray[1]; + String[] detailArray = detail.split(":"); + if (detailArray.length < 2) { + throw new RuntimeException("test failed : detail=" + strategy); + } + String provider = detailArray[0].replace(" ", ""); + if (!PROVIDER_NAME.equals(provider)) { + throw new RuntimeException("test failed : provider= " + provider); + } + String algorithmMsg = detailArray[1].trim(); + Boolean algorithmValue = ALGORITHM_MSG_MAP.get(algorithmMsg); + if (algorithmValue == null) { + throw new RuntimeException("test failed : algorithmMsg= " + algorithmMsg); + } + kaeUseEngineFlags[algorithmIndex] = algorithmValue; + } + } + return kaeUseEngineFlags; + } + + static KAETestHelper.Engine getEngine() { + String engineId = System.getProperty("kae.engine.id"); + if (engineId == null) { + return KAETestHelper.Engine.default_engine; + } + return KAETestHelper.Engine.valueOf(engineId); + } +} diff --git a/test/jdk/org/openeuler/security/openssl/KAEUseEngineTest.java b/test/jdk/org/openeuler/security/openssl/KAEUseEngineTest.java new file mode 100644 index 000000000..a5b9b5386 --- /dev/null +++ b/test/jdk/org/openeuler/security/openssl/KAEUseEngineTest.java @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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 org.openeuler.security.openssl.KAEProvider; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/* + * @test + * @summary Test KAE property kae..useKaeEngine + * @modules jdk.crypto.kaeprovider/org.openeuler.security.openssl + * @requires os.arch=="aarch64" + * @run main/othervm -Dkae.log=true -Dall.test=default KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dkae.digest.useKaeEngine=true KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dkae.aes.useKaeEngine=true KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dkae.sm4.useKaeEngine=true KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dkae.hmac.useKaeEngine=true KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dkae.rsa.useKaeEngine=true KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dkae.dh.useKaeEngine=true KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dkae.ec.useKaeEngine=true KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dall.test=enable -Dkae.digest.useKaeEngine=true -Dkae.aes.useKaeEngine=true -Dkae.sm4.useKaeEngine=true -Dkae.hmac.useKaeEngine=true -Dkae.rsa.useKaeEngine=true -Dkae.dh.useKaeEngine=true -Dkae.ec.useKaeEngine=true KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dkae.digest.useKaeEngine=false KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dkae.aes.useKaeEngine=true KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dkae.sm4.useKaeEngine=true KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dkae.hmac.useKaeEngine=true KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dkae.rsa.useKaeEngine=true KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dkae.dh.useKaeEngine=true KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dkae.ec.useKaeEngine=true KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dall.test=disable -Dkae.digest.useKaeEngine=false -Dkae.aes.useKaeEngine=false -Dkae.sm4.useKaeEngine=false -Dkae.hmac.useKaeEngine=false -Dkae.rsa.useKaeEngine=false -Dkae.dh.useKaeEngine=false -Dkae.ec.useKaeEngine=false KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dall.test=default -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.digest.useKaeEngine=true KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.aes.useKaeEngine=true KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.sm4.useKaeEngine=true KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.hmac.useKaeEngine=true KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.rsa.useKaeEngine=true KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.dh.useKaeEngine=true KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.ec.useKaeEngine=true KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dall.test=enable -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.digest.useKaeEngine=true -Dkae.aes.useKaeEngine=true -Dkae.sm4.useKaeEngine=true -Dkae.hmac.useKaeEngine=true -Dkae.rsa.useKaeEngine=true -Dkae.dh.useKaeEngine=true -Dkae.ec.useKaeEngine=true KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.digest.useKaeEngine=false KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.aes.useKaeEngine=true KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.sm4.useKaeEngine=true KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.hmac.useKaeEngine=true KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.rsa.useKaeEngine=true KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.dh.useKaeEngine=true KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.ec.useKaeEngine=true KAEUseEngineTest + * @run main/othervm -Dkae.log=true -Dall.test=disable -Dkae.engine.id=uadk_engine -Dkae.libcrypto.useGlobalMode=true -Dkae.digest.useKaeEngine=false -Dkae.aes.useKaeEngine=false -Dkae.sm4.useKaeEngine=false -Dkae.hmac.useKaeEngine=false -Dkae.rsa.useKaeEngine=false -Dkae.dh.useKaeEngine=false -Dkae.ec.useKaeEngine=false KAEUseEngineTest + */ +public class KAEUseEngineTest { + enum Mode { + DEFAULT(new boolean[]{ + true, false, false, true, false, false, false, false, false, false, + false, false, false, false, false, false, true, true, true, true, + false, false, false, false, false, false, true, true, false + }), + DIGEST_ENABLE(new boolean[]{ + true, false, false, true, false, false, false, false, false, false, + false, false, false, false, false, false, true, true, true, true, + false, false, false, false, false, false, true, true, false + }, 0, true), + AES_ENABLE(new boolean[]{ + true, false, false, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, + false, false, false, false, false, false, true, true, false + }, 1, true), + SM4_ENABLE(new boolean[]{ + true, false, false, true, false, false, false, false, false, false, + false, false, false, false, false, false, true, true, true, true, + false, false, false, false, false, false, true, true, false + }, 2, true), + HMAC_ENABLE(new boolean[]{ + true, false, false, true, false, false, false, false, false, false, + false, false, false, false, false, false, true, true, true, true, + true, true, true, true, true, true, true, true, false + }, 3, true), + RSA_ENABLE(new boolean[]{ + true, false, false, true, false, false, false, false, false, false, + false, false, false, false, false, false, true, true, true, true, + false, false, false, false, false, false, true, true, false + }, 4, true), + DH_ENABLE(new boolean[]{ + true, false, false, true, false, false, false, false, false, false, + false, false, false, false, false, false, true, true, true, true, + false, false, false, false, false, false, true, true, false + }, 5, true), + EC_ENABLE(new boolean[]{ + true, false, false, true, false, false, false, false, false, false, + false, false, false, false, false, false, true, true, true, true, + false, false, false, false, false, false, true, true, false + }, 6, true), + ALL_ENABLE(new boolean[]{ + true, false, false, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, false + }, true), + DIGEST_DISABLE(new boolean[]{ + false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, true, true, true, true, + false, false, false, false, false, false, true, true, false + }, 0, false), + AES_DISABLE(new boolean[]{ + true, false, false, true, false, false, false, false, false, false, + false, false, false, false, false, false, true, true, true, true, + false, false, false, false, false, false, true, true, false + }, 1, false), + SM4_DISABLE(new boolean[]{ + true, false, false, true, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, true, true, false + }, 2, false), + HMAC_DISABLE(new boolean[]{ + true, false, false, true, false, false, false, false, false, false, + false, false, false, false, false, false, true, true, true, true, + false, false, false, false, false, false, true, true, false + }, 3, false), + RSA_DISABLE(new boolean[]{ + true, false, false, true, false, false, false, false, false, false, + false, false, false, false, false, false, true, true, true, true, + false, false, false, false, false, false, false, true, false + }, 4, false), + DH_DISABLE(new boolean[]{ + true, false, false, true, false, false, false, false, false, false, + false, false, false, false, false, false, true, true, true, true, + false, false, false, false, false, false, true, false, false + }, 5, false), + EC_DISABLE(new boolean[]{ + true, false, false, true, false, false, false, false, false, false, + false, false, false, false, false, false, true, true, true, true, + false, false, false, false, false, false, true, true, false + }, 6, false), + ALL_DISABLE(new boolean[]{ + false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, false + }, false); + private final boolean[] expectedResult; + private final Integer propertyNameIndex; + private final boolean enable; + private static final Map modeMap = new HashMap<>(); + + static { + Mode[] modes = Mode.values(); + for (Mode mode : modes) { + if (mode.propertyNameIndex != null) { + modeMap.put(PROPERTY_NAMES[mode.propertyNameIndex] + ":" + mode.enable, mode); + } + } + modeMap.put("default", DEFAULT); + modeMap.put("disable", ALL_DISABLE); + modeMap.put("enable", ALL_ENABLE); + } + + Mode(boolean[] expectedResult) { + this(expectedResult, false); + } + + Mode(boolean[] expectedResult, boolean enable) { + this(expectedResult, null, enable); + } + + Mode(boolean[] expectedResult, Integer propertyNameIndex, boolean enable) { + this.expectedResult = expectedResult; + this.propertyNameIndex = propertyNameIndex; + this.enable = enable; + } + + static Mode getMode(String name, Boolean enable) { + return modeMap.get(name + ":" + enable); + } + + static Mode getMode(String key) { + return modeMap.get(key); + } + } + + private static final String KAE_LOG_PATH = System.getProperty("user.dir") + + File.separator + "kae.log"; + + private static final String[] PROPERTY_NAMES = new String[]{ + "kae.digest.useKaeEngine", + "kae.aes.useKaeEngine", + "kae.sm4.useKaeEngine", + "kae.hmac.useKaeEngine", + "kae.rsa.useKaeEngine", + "kae.dh.useKaeEngine", + "kae.ec.useKaeEngine" + }; + + private static final List files = new ArrayList<>(); + + public static void main(String[] args) throws IOException { + KAETestHelper.Engine engine = KAETestHelper.getEngine(); + if (!engine.isValid()) { + System.out.println("Skip test, engine " + engine.getEngineId() + " does not exist."); + return; + } + Mode mode = getMode(); + if (mode == null) { + throw new RuntimeException("test failed: mode is null"); + } + + try { + new KAEProvider(); + test(mode, engine); + } finally { + KAETestHelper.cleanUp(files); + } + } + + private static Mode getMode() { + String value = System.getProperty("all.test"); + if (value != null) { + return Mode.getMode(value); + } + for (String propertyName : PROPERTY_NAMES) { + String property = System.getProperty(propertyName); + Boolean enable = null; + if (property != null) { + enable = Boolean.valueOf(property); + } + Mode mode = Mode.getMode(propertyName, enable); + if (mode != null) { + return mode; + } + } + return null; + } + + private static void test(Mode mode, KAETestHelper.Engine engine) throws IOException { + File file = new File(KAE_LOG_PATH); + files.add(file); + boolean[] kaeUseEngineFlags = KAETestHelper.parseLog(engine, file); + if (!Arrays.equals(mode.expectedResult, kaeUseEngineFlags)) { + throw new RuntimeException("test failed : expected : " + Arrays.toString(mode.expectedResult) + "," + + "actual:" + Arrays.toString(kaeUseEngineFlags)); + } + } +} \ No newline at end of file diff --git a/test/jdk/org/openeuler/security/openssl/KaeDebugLogTest.java b/test/jdk/org/openeuler/security/openssl/KaeDebugLogTest.java new file mode 100644 index 000000000..7bffbd6ec --- /dev/null +++ b/test/jdk/org/openeuler/security/openssl/KaeDebugLogTest.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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 org.openeuler.security.openssl.KAEProvider; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import java.io.PrintStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.Security; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @test + * @summary test for KaeDebugLogTest + * @modules jdk.crypto.kaeprovider/org.openeuler.security.openssl + * @requires os.arch=="aarch64" + * @run main/othervm -Djava.security.debug=kae -Dkae.sm4.maxChunkSize=65536 KaeDebugLogTest + * @run main/othervm -Djava.security.debug=kae KaeDebugLogTest + * @run main/othervm -Djava.security.auth.debug=kae KaeDebugLogTest + * @run main/othervm KaeDebugLogTest + */ + +public class KaeDebugLogTest { + + private static final PrintStream err = System.err; + + public static void main(String[] args) throws Exception { + PrintStream printStream = new PrintStream("kaetest.out"); + System.setErr(printStream); + testDebugLog(); + System.setErr(printStream); + testSm4ChunkSize(); + } + + public static void testDebugLog() throws Exception { + new KAEProvider(); + Stream lines = Files.lines(Paths.get("kaetest.out")); + System.setErr(err); + String content = lines.collect(Collectors.joining(System.lineSeparator())); + if(("kae".equals(System.getProperty("java.security.debug")) + || "kae".equals(System.getProperty("java.security..auth.debug"))) + && !content.contains("reading kae properties file:")){ + throw new RuntimeException("KaeDebugLogTest Failed! Failed to set the debug log."); + } + lines.close(); + } + + public static void testSm4ChunkSize() throws Exception { + Security.insertProviderAt(new KAEProvider(), 1); + Cipher cipher = Cipher.getInstance("SM4"); + cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec("sm4EncryptionKey".getBytes(StandardCharsets.UTF_8), "SM4")); + Stream lines = Files.lines(Paths.get("kaetest.out")); + System.setErr(err); + String content = lines.collect(Collectors.joining(System.lineSeparator())); + String log = "The configured chunk size is " + System.getProperty("kae.sm4.maxChunkSize"); + if(("kae".equals(System.getProperty("java.security.debug")) + || "kae".equals(System.getProperty("java.security..auth.debug"))) + && Objects.nonNull(System.getProperty("kae.sm4.maxChunkSize")) &&!content.contains(log)){ + throw new RuntimeException("KaeDebugLogTest Failed! Failed to set the kae.sm4.maxChunkSize = " + System.getProperty("kae.sm4.maxChunkSize")); + } + lines.close(); + } + +} diff --git a/test/jdk/org/openeuler/security/openssl/KaeProviderTest.java b/test/jdk/org/openeuler/security/openssl/KaeProviderTest.java new file mode 100644 index 000000000..0f4425b6d --- /dev/null +++ b/test/jdk/org/openeuler/security/openssl/KaeProviderTest.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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 org.openeuler.security.openssl.KAEProvider; + +import javax.crypto.Cipher; +import javax.crypto.Mac; +import javax.crypto.NoSuchPaddingException; +import java.security.KeyPairGenerator; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.Security; + +/** + * @test + * @requires os.arch=="aarch64" + * @modules jdk.crypto.kaeprovider/org.openeuler.security.openssl + * @summary test for KaeProviderTest + * @run main/othervm KaeProviderTest + * @run main/othervm KaeProviderTest true + * @run main/othervm KaeProviderTest false + * @run main/othervm KaeProviderTest wrong + */ + +public class KaeProviderTest { + + private static final String[] algorithmKaeProviderPropertyNames = new String[]{ + "kae.md5", + "kae.sha256", + "kae.sha384", + "kae.sm3", + "kae.aes", + "kae.sm4", + "kae.hmac", + "kae.rsa", + "kae.dh", + "kae.ec" + }; + + private static final String KAE = "KAEProvider"; + + public static void main(String[] args) throws Exception { + initProperty(args); + Security.insertProviderAt(new KAEProvider(), 1); + testALL(); + } + + private static void initProperty(String[] args) { + if (args.length <= 0) { + return; + } + String value = args[0]; + for (String name : algorithmKaeProviderPropertyNames){ + System.setProperty(name,value); + } + } + + public static void testALL() throws Exception { + testMd5(); + testSha256(); + testSha384(); + testSm3(); + testAes(); + testSm4(); + testHmac(); + testRsa(); + testDh(); + testEc(); + } + + public static void testMd5() throws NoSuchAlgorithmException { + MessageDigest messageDigest = MessageDigest.getInstance("MD5"); + judge("kae.md5",messageDigest.getProvider().getName()); + + } + + public static void testSha256() throws NoSuchAlgorithmException { + MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); + judge("kae.sha256",messageDigest.getProvider().getName()); + } + + public static void testSha384() throws NoSuchAlgorithmException { + MessageDigest messageDigest = MessageDigest.getInstance("SHA-384"); + judge("kae.sha384",messageDigest.getProvider().getName()); + } + + public static void testSm3() throws NoSuchAlgorithmException { + try{ + MessageDigest messageDigest = MessageDigest.getInstance("SM3"); + judge("kae.sm3",messageDigest.getProvider().getName()); + }catch (NoSuchAlgorithmException e){ + if(Boolean.parseBoolean(System.getProperty("kae.sm3"))){ + throw e; + } + } + } + + public static void testAes() throws NoSuchAlgorithmException, NoSuchPaddingException { + Cipher cipher = Cipher.getInstance("AES"); + judge("kae.aes",cipher.getProvider().getName()); + } + + public static void testSm4() throws NoSuchAlgorithmException, NoSuchPaddingException { + try{ + Cipher cipher = Cipher.getInstance("SM4"); + judge("kae.sm4",cipher.getProvider().getName()); + }catch (NoSuchAlgorithmException e){ + if(Boolean.parseBoolean(System.getProperty("kae.sm4"))){ + throw e; + } + } + } + + public static void testHmac() throws NoSuchAlgorithmException { + Mac mac = Mac.getInstance("HmacMD5"); + judge("kae.hmac",mac.getProvider().getName()); + } + + public static void testRsa() throws NoSuchAlgorithmException, NoSuchPaddingException { + Cipher cipher = Cipher.getInstance("RSA"); + judge("kae.rsa",cipher.getProvider().getName()); + } + + public static void testDh() throws NoSuchAlgorithmException { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DH"); + judge("kae.dh",keyPairGenerator.getProvider().getName()); + } + + public static void testEc() throws NoSuchAlgorithmException { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC"); + judge("kae.ec",keyPairGenerator.getProvider().getName()); + } + + private static void judge(String algorithm , String providerName){ + String value = System.getProperty(algorithm); + if (value == null) { + if (!KAE.equals(providerName)) { + throw new RuntimeException("KaeProviderTest Failed! default Provider.name is not right!"); + } + } else { + if (Boolean.parseBoolean(value) && !KAE.equals(providerName)) { + throw new RuntimeException("KaeProviderTest Failed! " + algorithm + " is " + value + "," + + " Provider.name is not right!"); + } + if (!Boolean.parseBoolean(value) && KAE.equals(providerName)) { + throw new RuntimeException("KaeProviderTest Failed! " + algorithm + " is " + value + ", " + + " Provider.name is not right!"); + } + } + } +} \ No newline at end of file diff --git a/test/jdk/org/openeuler/security/openssl/RSATest.java b/test/jdk/org/openeuler/security/openssl/RSATest.java new file mode 100644 index 000000000..8a787e1f7 --- /dev/null +++ b/test/jdk/org/openeuler/security/openssl/RSATest.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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 org.openeuler.security.openssl.KAEProvider; + +import java.nio.charset.StandardCharsets; +import java.security.Security; +import java.security.spec.AlgorithmParameterSpec; +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; + +/** + * @test + * @summary Basic test for AES + * @modules jdk.crypto.kaeprovider/org.openeuler.security.openssl + * @requires os.arch=="aarch64" + * @run main AESTest + */ + +public class AESTest { + private static final String[] ALGORITHM = {"AES", "AES_128", "AES_192", "AES_256"}; + private static final String[] MODES = {"ECB", "CBC", "CTR", "GCM"}; + private static final String[] PADDING = {"NoPadding", "PKCS5Padding"}; + private static final int AES_128_KEY_LENGTH = 128; + private static final int AES_192_KEY_LENGTH = 192; + private static final int AES_256_KEY_LENGTH = 256; + private static String plainText = "helloworldhellow"; // 16bytes for NoPadding + private static String shortPlainText = "helloworld"; // 5 bytes for padding + + public static void main(String[] args) throws Exception { + Security.insertProviderAt(new KAEProvider(), 1); + for (String algo : ALGORITHM) { + for (String mode : MODES) { + int padKinds = 2; + if (mode.equalsIgnoreCase("CTR")) { + padKinds = 1; + } + for (int k = 0; k < padKinds; k++) { + test(algo, mode, PADDING[k]); + } + } + } + } + + public static void test(String algo, String mo, String pad) throws Exception { + AlgorithmParameterSpec aps = null; + + Cipher cipher = Cipher.getInstance(algo + "/" + mo + "/" + pad); + + KeyGenerator kg = KeyGenerator.getInstance("AES"); + if (algo.equalsIgnoreCase("AES_192")) { + kg.init(AES_192_KEY_LENGTH); + } else if (algo.equalsIgnoreCase("AES_256")) { + kg.init(AES_256_KEY_LENGTH); + } else { + kg.init(AES_128_KEY_LENGTH); + } + + SecretKey key = kg.generateKey(); + + // encrypt + if (!mo.equalsIgnoreCase("GCM")) { + cipher.init(Cipher.ENCRYPT_MODE, key, aps); + } else { + cipher.init(Cipher.ENCRYPT_MODE, key); + } + + String cipherString = null; + if (!pad.equalsIgnoreCase("NoPadding")) { + cipherString = shortPlainText; + } else { + cipherString = plainText; + } + byte[] cipherText = cipher.doFinal(cipherString.getBytes(StandardCharsets.UTF_8)); + if (!mo.equalsIgnoreCase("ECB")) { + aps = new IvParameterSpec(cipher.getIV()); + } else { + aps = null; + } + + if (!mo.equalsIgnoreCase("GCM")) { + cipher.init(Cipher.DECRYPT_MODE, key, aps); + } else { + cipher.init(Cipher.DECRYPT_MODE, key, cipher.getParameters()); + } + + String decryptPlainText = new String(cipher.doFinal(cipherText)); + + if (!cipherString.equals(decryptPlainText)) { + throw new RuntimeException("aes decryption failed, algo = " + algo + ", mo = " + mo + ", pad = " + pad); + } + } +} \ No newline at end of file diff --git a/test/jdk/org/openeuler/security/openssl/SM3Test.java b/test/jdk/org/openeuler/security/openssl/SM3Test.java new file mode 100644 index 000000000..e1c5346c9 --- /dev/null +++ b/test/jdk/org/openeuler/security/openssl/SM3Test.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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 org.openeuler.security.openssl.KAEProvider; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.security.MessageDigest; +import java.security.Security; + +/** + * @test + * @summary Basic test for sm3 + * @modules jdk.crypto.kaeprovider/org.openeuler.security.openssl + * @run main SM3Test + */ + +public class SM3Test { + + private static String plainText = "helloworldhellow"; + + public static void main(String[] args) throws Exception { + Security.insertProviderAt(new KAEProvider(), 1); + test(plainText, "SM3", new byte[]{40, -103, -71, 4, -80, -49, 94, 112, 11, -75, -66, 121, 63, 80, 62, -14, -45, -75, -34, 66, -77, -34, -26, 26, 33, -23, 45, 52, -74, 67, -18, 118}); + } + + public static void test(String plainText, String algo, byte[] expectRes) throws Exception { + MessageDigest md = MessageDigest.getInstance(algo); + md.update(plainText.getBytes(StandardCharsets.UTF_8)); + byte[] res = md.digest(); + if (!Arrays.equals(res, expectRes)) { + throw new RuntimeException("sm3 failed"); + } + } + +} diff --git a/test/jdk/org/openeuler/security/openssl/SM4Test.java b/test/jdk/org/openeuler/security/openssl/SM4Test.java new file mode 100644 index 000000000..ac81831ba --- /dev/null +++ b/test/jdk/org/openeuler/security/openssl/SM4Test.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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 org.openeuler.security.openssl.KAEProvider; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.security.Security; +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +/** + * @test + * @summary Basic test for sm4 + * @modules jdk.crypto.kaeprovider/org.openeuler.security.openssl + * @run main SM4Test + */ + +public class SM4Test { + + private static SecretKeySpec ks = new SecretKeySpec("sm4EncryptionKey".getBytes(StandardCharsets.UTF_8), "SM4"); // key has 16 bytes + private static IvParameterSpec iv = new IvParameterSpec("abcdefghabcdefgh".getBytes(StandardCharsets.UTF_8)); // iv has 16 bytes + private static IvParameterSpec shortIv = new IvParameterSpec("abcdefgh".getBytes(StandardCharsets.UTF_8)); // CTR support >= 8bytes iv + private static String plainText = "helloworldhellow"; // 16bytes for NoPadding + private static String shortPlainText = "helloworld"; // 5 bytes for padding + + public static void main(String[] args) throws Exception { + Security.insertProviderAt(new KAEProvider(), 1); + test(plainText, "SM4/CBC/NOPADDING", new byte[]{86, 69, 47, -115, -63, 54, 35, 24, -2, 114, 113, 102, 82, 20, 69, 59}); + test(shortPlainText, "SM4/CBC/PKCS5Padding", new byte[]{10, 105, 75, -80, -85, -68, 13, -53, 42, 91, -64, 99, 104, 35, -85, 8}); + test(plainText, "SM4/ECB/NOPADDING", new byte[]{103, 36, -31, -53, -109, -12, -71, -79, -54, 106, 10, -3, -35, -22, -122, -67}); + test(shortPlainText, "SM4/ECB/PKCS5Padding", new byte[]{-10, 99, -9, 90, 58, -36, -109, 54, -55, -52, 7, -49, 110, -88, 72, 40}); + test(plainText, "SM4/CTR/NOPADDING", new byte[]{32, 108, 35, 108, -16, 119, -111, 114, 94, 110, -100, -113, -46, -29, -11, 71}); + test(plainText, "SM4/OFB/NOPADDING", new byte[]{32, 108, 35, 108, -16, 119, -111, 114, 94, 110, -100, -113, -46, -29, -11, 71}); + test(shortPlainText, "SM4/OFB/PKCS5Padding", new byte[]{32, 108, 35, 108, -16, 119, -111, 114, 94, 110}); + + testCtrShortIv(plainText, "SM4/CTR/NOPADDING", new byte[]{-13, 73, 40, -36, -64, -67, 75, -72, 90, 58, 73, -4, -36, 115, 126, -48}); + + testByteBuffer(plainText, "SM4/CBC/NOPADDING", new byte[]{86, 69, 47, -115, -63, 54, 35, 24, -2, 114, 113, 102, 82, 20, 69, 59}); + testByteBuffer(shortPlainText, "SM4/CBC/PKCS5Padding", new byte[]{10, 105, 75, -80, -85, -68, 13, -53, 42, 91, -64, 99, 104, 35, -85, 8}); + testByteBuffer(plainText, "SM4/ECB/NOPADDING", new byte[]{103, 36, -31, -53, -109, -12, -71, -79, -54, 106, 10, -3, -35, -22, -122, -67}); + testByteBuffer(shortPlainText, "SM4/ECB/PKCS5Padding", new byte[]{-10, 99, -9, 90, 58, -36, -109, 54, -55, -52, 7, -49, 110, -88, 72, 40}); + testByteBuffer(plainText, "SM4/CTR/NOPADDING", new byte[]{32, 108, 35, 108, -16, 119, -111, 114, 94, 110, -100, -113, -46, -29, -11, 71}); + testByteBuffer(plainText, "SM4/OFB/NOPADDING", new byte[]{32, 108, 35, 108, -16, 119, -111, 114, 94, 110, -100, -113, -46, -29, -11, 71}); + testByteBuffer(shortPlainText, "SM4/OFB/PKCS5Padding", new byte[]{32, 108, 35, 108, -16, 119, -111, 114, 94, 110}); + + System.setProperty("kae.sm4.maxChunkSize", "65536"); + testByteBuffer(plainText, "SM4/CBC/NOPADDING", new byte[]{86, 69, 47, -115, -63, 54, 35, 24, -2, 114, 113, 102, 82, 20, 69, 59}); + testByteBuffer(shortPlainText, "SM4/CBC/PKCS5Padding", new byte[]{10, 105, 75, -80, -85, -68, 13, -53, 42, 91, -64, 99, 104, 35, -85, 8}); + testByteBuffer(plainText, "SM4/ECB/NOPADDING", new byte[]{103, 36, -31, -53, -109, -12, -71, -79, -54, 106, 10, -3, -35, -22, -122, -67}); + testByteBuffer(shortPlainText, "SM4/ECB/PKCS5Padding", new byte[]{-10, 99, -9, 90, 58, -36, -109, 54, -55, -52, 7, -49, 110, -88, 72, 40}); + testByteBuffer(plainText, "SM4/CTR/NOPADDING", new byte[]{32, 108, 35, 108, -16, 119, -111, 114, 94, 110, -100, -113, -46, -29, -11, 71}); + testByteBuffer(plainText, "SM4/OFB/NOPADDING", new byte[]{32, 108, 35, 108, -16, 119, -111, 114, 94, 110, -100, -113, -46, -29, -11, 71}); + testByteBuffer(shortPlainText, "SM4/OFB/PKCS5Padding", new byte[]{32, 108, 35, 108, -16, 119, -111, 114, 94, 110}); + } + + public static void test(String plainText, String algo, byte[] expectRes) throws Exception { + Cipher encryptCipher = Cipher.getInstance(algo); + if (algo.contains("ECB")) { + encryptCipher.init(Cipher.ENCRYPT_MODE, ks); + } else { + encryptCipher.init(Cipher.ENCRYPT_MODE, ks, iv); + } + byte[] cipherText = encryptCipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8)); + if (!Arrays.equals(cipherText, expectRes)) { + throw new RuntimeException("sm4 encryption failed, algo = " + algo); + } + + Cipher decryptCipher = Cipher.getInstance(algo); + decryptCipher.init(Cipher.DECRYPT_MODE, ks, encryptCipher.getParameters()); + String decryptPlainText = new String(decryptCipher.doFinal(cipherText)); + if (!plainText.equals(decryptPlainText)) { + throw new RuntimeException("sm4 decryption failed, algo = " + algo); + } + } + + public static void testCtrShortIv(String plainText, String algo, byte[] expectRes) throws Exception { + Cipher encryptCipher = Cipher.getInstance(algo); + encryptCipher.init(Cipher.ENCRYPT_MODE, ks, shortIv); + byte[] cipherText = encryptCipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8)); + if (!Arrays.equals(cipherText, expectRes)) { + throw new RuntimeException("sm4 encryption failed, algo = " + algo); + } + + Cipher decryptCipher = Cipher.getInstance(algo); + decryptCipher.init(Cipher.DECRYPT_MODE, ks, encryptCipher.getParameters()); + String decryptPlainText = new String(decryptCipher.doFinal(cipherText)); + if (!plainText.equals(decryptPlainText)) { + throw new RuntimeException("sm4 decryption failed, algo = " + algo); + } + } + + public static void testByteBuffer(String plainText, String algo, byte[] expectRes) throws Exception { + // encrypt + Cipher encryptCipher = Cipher.getInstance(algo); + if (algo.contains("ECB")) { + encryptCipher.init(Cipher.ENCRYPT_MODE, ks); + } else { + encryptCipher.init(Cipher.ENCRYPT_MODE, ks, iv); + } + int inputLen = plainText.length(); + ByteBuffer sourceByteBuffer = ByteBuffer.allocateDirect(inputLen); + sourceByteBuffer.put(plainText.getBytes()); + sourceByteBuffer.flip(); + int outputLen = encryptCipher.getOutputSize(inputLen); + ByteBuffer encryptedByteBuffer = ByteBuffer.allocate(outputLen); + encryptCipher.doFinal(sourceByteBuffer,encryptedByteBuffer); + encryptedByteBuffer.flip(); + byte[] encryptedBytes = new byte[encryptedByteBuffer.limit()]; + encryptedByteBuffer.get(encryptedBytes); + if (!Arrays.equals(encryptedBytes, expectRes)) { + throw new RuntimeException("sm4 encryption failed, algo = " + algo); + } + sourceByteBuffer.clear(); + encryptedByteBuffer.flip(); + + // decrypt + Cipher decryptCipher = Cipher.getInstance(algo); + decryptCipher.init(Cipher.DECRYPT_MODE, ks, encryptCipher.getParameters()); + outputLen = decryptCipher.getOutputSize(encryptedBytes.length); + ByteBuffer decryptedByteBuffer = ByteBuffer.allocate(outputLen); + decryptCipher.doFinal(encryptedByteBuffer, decryptedByteBuffer); + decryptedByteBuffer.flip(); + byte[] decryptedBytes = new byte[decryptedByteBuffer.limit()]; + decryptedByteBuffer.get(decryptedBytes); + if (!Arrays.equals(plainText.getBytes(), decryptedBytes)) { + throw new RuntimeException("sm4 decryption failed, algo = " + algo); + } + encryptedByteBuffer.clear(); + decryptedByteBuffer.clear(); + } +} \ No newline at end of file diff --git a/test/jdk/sun/security/jca/PreferredProviderNegativeTest.java b/test/jdk/sun/security/jca/PreferredProviderNegativeTest.java index 58bcbe911..bfc38df35 100644 --- a/test/jdk/sun/security/jca/PreferredProviderNegativeTest.java +++ b/test/jdk/sun/security/jca/PreferredProviderNegativeTest.java @@ -107,7 +107,12 @@ public class PreferredProviderNegativeTest { String expected; String value = args[1]; - expected = System.getProperty("test.provider.name", "SunJCE"); + + if (Security.getProperty("security.provider.1").equals("KAEProvider")) { + expected = "KAEProvider"; + } else { + expected = "SunJCE"; + } if (args.length >= 2) { switch (args[0]) { diff --git a/test/jdk/sun/security/krb5/auto/BasicProc.java b/test/jdk/sun/security/krb5/auto/BasicProc.java index 0ba0b907b..c67efa446 100644 --- a/test/jdk/sun/security/krb5/auto/BasicProc.java +++ b/test/jdk/sun/security/krb5/auto/BasicProc.java @@ -298,7 +298,9 @@ public class BasicProc { Proc p = Proc.create("BasicProc") .inheritProp("jdk.net.hosts.file") .prop("java.security.manager", "") - .perm(new javax.security.auth.AuthPermission("doAs")); + .perm(new javax.security.auth.AuthPermission("doAs")) + .perm(new java.util.PropertyPermission( + "kae.disableKaeDispose", "read")); if (lib != null) { p.env("KRB5_CONFIG", CONF) .env("KRB5_TRACE", Platform.isWindows() ? "CON" : "/dev/stderr") diff --git a/test/jdk/sun/security/pkcs11/Secmod/policy b/test/jdk/sun/security/pkcs11/Secmod/policy index e4c95ca6d..60488fd06 100644 --- a/test/jdk/sun/security/pkcs11/Secmod/policy +++ b/test/jdk/sun/security/pkcs11/Secmod/policy @@ -3,4 +3,5 @@ grant { permission java.io.FilePermission "${test.src}/-", "read"; permission java.io.FilePermission "${pkcs11test.nss.db}/-", "read"; permission java.io.FilePermission "${pkcs11test.nss.libdir}/-", "read"; + permission java.util.PropertyPermission "kae.disableKaeDispose", "read"; }; \ No newline at end of file diff --git a/test/jdk/sun/security/pkcs11/policy b/test/jdk/sun/security/pkcs11/policy index 54281a781..4d887e239 100644 --- a/test/jdk/sun/security/pkcs11/policy +++ b/test/jdk/sun/security/pkcs11/policy @@ -1,4 +1,5 @@ grant { permission java.lang.RuntimePermission "setSecurityManager"; permission java.util.PropertyPermission "test.provider.name", "read"; + permission java.util.PropertyPermission "kae.disableKaeDispose", "read"; }; diff --git a/test/jdk/sun/security/ssl/CipherSuite/DisabledCurve.java b/test/jdk/sun/security/ssl/CipherSuite/DisabledCurve.java index 26304c5df..ca618ccfe 100644 --- a/test/jdk/sun/security/ssl/CipherSuite/DisabledCurve.java +++ b/test/jdk/sun/security/ssl/CipherSuite/DisabledCurve.java @@ -91,6 +91,10 @@ public class DisabledCurve extends SSLSocketTemplate { } public static void main(String[] args) throws Exception { + // KAEProvider does not support sect283r1 + if (Security.getProperty("security.provider.1").equals("KAEProvider")) { + return; + } String expected = args[1]; String disabledName = ("DISABLE_NONE".equals(args[0]) ? "" : args[0]); boolean disabled = false; diff --git a/test/jdk/sun/security/ssl/SSLSocketImpl/NotifyHandshakeTest.policy b/test/jdk/sun/security/ssl/SSLSocketImpl/NotifyHandshakeTest.policy index b426b173f..6f65e48a3 100644 --- a/test/jdk/sun/security/ssl/SSLSocketImpl/NotifyHandshakeTest.policy +++ b/test/jdk/sun/security/ssl/SSLSocketImpl/NotifyHandshakeTest.policy @@ -33,5 +33,7 @@ grant codeBase "file:com.jar" { "javax.net.ssl.trustStore", "write"; permission java.util.PropertyPermission "javax.net.ssl.trustStorePassword", "write"; + permission java.util.PropertyPermission + "kae.disableKaeDispose", "read"; }; diff --git a/test/micro/org/openeuler/bench/security/openssl/AESBenchmark.java b/test/micro/org/openeuler/bench/security/openssl/AESBenchmark.java new file mode 100644 index 000000000..8a6e5658d --- /dev/null +++ b/test/micro/org/openeuler/bench/security/openssl/AESBenchmark.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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. + */ +package org.openeuler.bench.security.openssl; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.Warmup; + +import java.security.InvalidKeyException; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.Security; +import java.util.concurrent.TimeUnit; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.SecretKeySpec; + +@Warmup(iterations = 10, time = 2, timeUnit = TimeUnit.SECONDS) +public class AESBenchmark extends BenchmarkBase { + + @Param({"AES/ECB/PKCS5Padding", "AES/ECB/NoPadding", "AES/CBC/NoPadding", "AES/CBC/PKCS5Padding", "AES/CTR/NoPadding"}) + private String algorithm; + + @Param({"128", "192", "256"}) + private int keyLength; + + @Param({"" + 1024, "" + 10 * 1024, "" + 100 * 1024, "" + 1024 * 1024}) + private int dataSize; + + private byte[][] encryptedData; + private Cipher encryptCipher; + private Cipher decryptCipher; + + @Setup + public void setup() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, + InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { + setupProvider(); + + byte[] keystring = fillSecureRandom(new byte[keyLength / 8]); + SecretKeySpec ks = new SecretKeySpec(keystring, "AES"); + + encryptCipher = (prov == null) ? Cipher.getInstance(algorithm) : Cipher.getInstance(algorithm, prov); + encryptCipher.init(Cipher.ENCRYPT_MODE, ks); + decryptCipher = (prov == null) ? Cipher.getInstance(algorithm) : Cipher.getInstance(algorithm, prov); + decryptCipher.init(Cipher.DECRYPT_MODE, ks, encryptCipher.getParameters()); + + data = fillRandom(new byte[SET_SIZE][dataSize]); + encryptedData = fillEncrypted(data, encryptCipher); + } + + @Benchmark + public byte[] encrypt() throws IllegalBlockSizeException, BadPaddingException { + byte[] d = data[index]; + index = (index + 1) % SET_SIZE; + return encryptCipher.doFinal(d); + } + + @Benchmark + @Fork(jvmArgsPrepend = {"-Xms100G", "-Xmx100G", "-XX:+AlwaysPreTouch", "-Dkae.disableKaeDispose=true"}, value = 5) + public byte[] encryptDispose() throws IllegalBlockSizeException, BadPaddingException { + byte[] d = data[index]; + index = (index + 1) % SET_SIZE; + return encryptCipher.doFinal(d); + } + + @Benchmark + public byte[] decrypt() throws IllegalBlockSizeException, BadPaddingException { + byte[] e = encryptedData[index]; + index = (index + 1) % SET_SIZE; + return decryptCipher.doFinal(e); + } + + @Benchmark + @Fork(jvmArgsPrepend = {"-Xms100G", "-Xmx100G", "-XX:+AlwaysPreTouch", "-Dkae.disableKaeDispose=true"}, value = 5) + public byte[] decryptDispose() throws IllegalBlockSizeException, BadPaddingException { + byte[] e = encryptedData[index]; + index = (index + 1) % SET_SIZE; + return decryptCipher.doFinal(e); + } +} + diff --git a/test/micro/org/openeuler/bench/security/openssl/AESGCMBenchmark.java b/test/micro/org/openeuler/bench/security/openssl/AESGCMBenchmark.java new file mode 100644 index 000000000..222405235 --- /dev/null +++ b/test/micro/org/openeuler/bench/security/openssl/AESGCMBenchmark.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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 org.openeuler.bench.security.openssl; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.Fork; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidParameterSpecException; + +import java.security.Provider; + + +public class AESGCMBenchmark extends BenchmarkBase{ + + @Param({"AES/GCM/NoPadding","AES/GCM/PKCS5Padding"}) + private String algorithm; + + @Param({"128", "192", "256"}) + private int keyLength; + + @Param({""+1024, "" + 10 * 1024, "" + 100 * 1024, "" + 1024 * 1024}) + private int dataSize; + + byte[] data; + byte[] encryptedData; + private Cipher encryptCipher; + private Cipher decryptCipher; + SecretKeySpec ks; + GCMParameterSpec gcm_spec; + byte[] aad; + byte[] iv; + + public static final int IV_BUFFER_SIZE = 32; + public static final int IV_MODULO = IV_BUFFER_SIZE - 16; + int iv_index = 0; + + private int next_iv_index() { + int r = iv_index; + iv_index = (iv_index + 1) % IV_MODULO; + return r; + } + + @Setup + public void setup() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException, InvalidParameterSpecException { + setupProvider(); + assert algorithm.split("/")[1].compareToIgnoreCase("GCM") == 0; + + byte[] keystring = fillSecureRandom(new byte[keyLength / 8]); + ks = new SecretKeySpec(keystring, "AES"); + iv = fillSecureRandom(new byte[IV_BUFFER_SIZE]); + gcm_spec = new GCMParameterSpec(96, iv, next_iv_index(), 16); + aad = fillSecureRandom(new byte[5]); + encryptCipher = makeCipher(prov, algorithm); + encryptCipher.init(Cipher.ENCRYPT_MODE, ks, gcm_spec); + encryptCipher.updateAAD(aad); + decryptCipher = makeCipher(prov, algorithm); + decryptCipher.init(Cipher.DECRYPT_MODE, ks, encryptCipher.getParameters().getParameterSpec(GCMParameterSpec.class)); + decryptCipher.updateAAD(aad); + data = fillRandom(new byte[dataSize]); + encryptedData = encryptCipher.doFinal(data); + } + + @Benchmark + public byte[] encrypt() throws BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException, InvalidKeyException { + gcm_spec = new GCMParameterSpec(96, iv, next_iv_index(), 16); + encryptCipher.init(Cipher.ENCRYPT_MODE, ks, gcm_spec); + encryptCipher.updateAAD(aad); + return encryptCipher.doFinal(data); + } + + @Benchmark + @Fork(jvmArgsPrepend = {"-Xms100G", "-Xmx100G", "-XX:+AlwaysPreTouch", "-Dkae.disableKaeDispose=true"}, value = 5) + public byte[] encrypt_arg() throws BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException, InvalidKeyException { + gcm_spec = new GCMParameterSpec(96, iv, next_iv_index(), 16); + encryptCipher.init(Cipher.ENCRYPT_MODE, ks, gcm_spec); + encryptCipher.updateAAD(aad); + return encryptCipher.doFinal(data); + } + + @Benchmark + public byte[] decrypt() throws BadPaddingException, IllegalBlockSizeException, InvalidParameterSpecException, InvalidAlgorithmParameterException, InvalidKeyException { + decryptCipher.init(Cipher.DECRYPT_MODE, ks, gcm_spec); + decryptCipher.updateAAD(aad); + return decryptCipher.doFinal(encryptedData); + } + + @Benchmark + @Fork(jvmArgsPrepend = {"-Xms100G", "-Xmx100G", "-XX:+AlwaysPreTouch", "-Dkae.disableKaeDispose=true"}, value = 5) + public byte[] decrypt_arg() throws BadPaddingException, IllegalBlockSizeException, InvalidParameterSpecException, InvalidAlgorithmParameterException, InvalidKeyException { + decryptCipher.init(Cipher.DECRYPT_MODE, ks, gcm_spec); + decryptCipher.updateAAD(aad); + return decryptCipher.doFinal(encryptedData); + } + + public static Cipher makeCipher(Provider prov, String algorithm) throws NoSuchPaddingException, NoSuchAlgorithmException { + return (prov == null) ? Cipher.getInstance(algorithm) : Cipher.getInstance(algorithm, prov); + } +} + diff --git a/test/micro/org/openeuler/bench/security/openssl/BenchmarkBase.java b/test/micro/org/openeuler/bench/security/openssl/BenchmarkBase.java new file mode 100644 index 000000000..4e3e7f817 --- /dev/null +++ b/test/micro/org/openeuler/bench/security/openssl/BenchmarkBase.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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. + */ +package org.openeuler.bench.security.openssl; + +import org.openeuler.security.openssl.KAEProvider; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import java.security.Provider; +import java.security.SecureRandom; +import java.security.Security; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.SECONDS) +@Warmup(iterations = 3, time = 3, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 8, time = 2, timeUnit = TimeUnit.SECONDS) +@Fork(jvmArgsPrepend = {"-Xms100G", "-Xmx100G", "-XX:+AlwaysPreTouch"}, value = 5) +@Threads(1) +@State(Scope.Thread) +public class BenchmarkBase { + public static final int SET_SIZE = 128; + + byte[][] data; + int index = 0; + + @Param({"", "KAEProvider"}) + private String provider; + + public Provider prov = null; + + @Setup + public void setupProvider() { + Security.addProvider(new KAEProvider()); + if (provider != null && !provider.isEmpty()) { + prov = Security.getProvider(provider); + if (prov == null) { + throw new RuntimeException("Can't find provider \"" + provider + "\""); + } + } + } + + public static byte[][] fillRandom(byte[][] data) { + Random rnd = new Random(); + for (byte[] d : data) { + rnd.nextBytes(d); + } + return data; + } + + public static byte[] fillRandom(byte[] data) { + Random rnd = new Random(); + rnd.nextBytes(data); + return data; + } + + public static byte[] fillSecureRandom(byte[] data) { + SecureRandom rnd = new SecureRandom(); + rnd.nextBytes(data); + return data; + } + + public static byte[][] fillEncrypted(byte[][] data, Cipher encryptCipher) + throws IllegalBlockSizeException, BadPaddingException { + byte[][] encryptedData = new byte[data.length][]; + for (int i = 0; i < encryptedData.length; i++) { + encryptedData[i] = encryptCipher.doFinal(data[i]); + } + return encryptedData; + } + +} diff --git a/test/micro/org/openeuler/bench/security/openssl/DHKeyAgreementBenchMark.java b/test/micro/org/openeuler/bench/security/openssl/DHKeyAgreementBenchMark.java new file mode 100644 index 000000000..eb5ece804 --- /dev/null +++ b/test/micro/org/openeuler/bench/security/openssl/DHKeyAgreementBenchMark.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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. + */ + +package org.openeuler.bench.security.openssl; + +import java.math.BigInteger; +import java.security.*; +import javax.crypto.*; +import javax.crypto.spec.*; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Setup; +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.Warmup; + +import java.security.KeyPairGenerator; + +public class DHKeyAgreementBenchMark extends BenchmarkBase { + @Param({"DH"}) + private String algorithm; + + @Param({"512", "1024", "2048", "3072", "4096"}) + private int keySize; + + private KeyPairGenerator aliceKpairGen; + private KeyPairGenerator bobKpairGen; + private KeyPairGenerator carolKpairGen; + + private KeyPair aliceKpair; + private KeyPair bobKpair; + private KeyPair carolKpair; + + private DHParameterSpec dhSkipParamSpec; + + @Setup + public void setUp() throws Exception { + setupProvider(); + aliceKpairGen = createKeyPairGenerator(); + bobKpairGen = createKeyPairGenerator(); + carolKpairGen = createKeyPairGenerator(); + + // Alice creates her own DH key pair + aliceKpairGen.initialize(keySize); + aliceKpair = aliceKpairGen.generateKeyPair(); + // Bob creates his own DH key pair + bobKpairGen.initialize(keySize); + bobKpair = bobKpairGen.generateKeyPair(); + // Carol creates her own DH key pair + carolKpairGen.initialize(keySize); + carolKpair = carolKpairGen.generateKeyPair(); + } + + @Benchmark + public void KeyAgreement() throws Exception { + + // Alice initialize + KeyAgreement aliceKeyAgree = (prov == null ? KeyAgreement.getInstance("DH") : KeyAgreement.getInstance("DH", prov)); + aliceKeyAgree.init(aliceKpair.getPrivate()); + // Bob initialize + KeyAgreement bobKeyAgree = (prov == null ? KeyAgreement.getInstance("DH") : KeyAgreement.getInstance("DH", prov)); + bobKeyAgree.init(bobKpair.getPrivate()); + // Carol initialize + KeyAgreement carolKeyAgree = (prov == null ? KeyAgreement.getInstance("DH") : KeyAgreement.getInstance("DH", prov)); + carolKeyAgree.init(carolKpair.getPrivate()); + // Alice uses Carol's public key + Key ac = aliceKeyAgree.doPhase(carolKpair.getPublic(), false); + // Bob uses Alice's public key + Key ba = bobKeyAgree.doPhase(aliceKpair.getPublic(), false); + // Carol uses Bob's public key + Key cb = carolKeyAgree.doPhase(bobKpair.getPublic(), false); + // Alice uses Carol's result from above + aliceKeyAgree.doPhase(cb, true); + // Bob uses Alice's result from above + bobKeyAgree.doPhase(ac, true); + // Carol uses Bob's result from above + carolKeyAgree.doPhase(ba, true); + + // Alice, Bob and Carol compute their secrets + byte[] aliceSharedSecret = aliceKeyAgree.generateSecret(); + int aliceLen = aliceSharedSecret.length; + + byte[] bobSharedSecret = bobKeyAgree.generateSecret(); + int bobLen = bobSharedSecret.length; + + byte[] carolSharedSecret = carolKeyAgree.generateSecret(); + int carolLen = carolSharedSecret.length; + + // Compare Alice and Bob + if (aliceLen != bobLen) { + throw new Exception("Alice and Bob have different lengths"); + } + for (int i=0; i pssParameterSpecMap; + + static { + initPSSParameterSpecMap(); + } + + @Param({"SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512"}) + private String algorithm; + + @Param({"2048", "3072", "4096"}) + private int keySize; + + @Param({"" + 1024, "" + 10 * 1024, "" + 100 * 1024, "" + 256 * 1024, "" + 1024 * 1024, "" + 10 * 1024 * 1024}) + private int dataSize; + + private KeyPair keyPair; + + private byte[][] sigData; + + + private static void initPSSParameterSpecMap() { + pssParameterSpecMap = new HashMap<>(); + pssParameterSpecMap.put("SHA-1", new PSSParameterSpec("SHA-1", + "MGF1", MGF1ParameterSpec.SHA1, 20, PSSParameterSpec.TRAILER_FIELD_BC)); + pssParameterSpecMap.put("SHA-224", new PSSParameterSpec("SHA-224", + "MGF1", MGF1ParameterSpec.SHA224, 28, PSSParameterSpec.TRAILER_FIELD_BC)); + pssParameterSpecMap.put("SHA-256", new PSSParameterSpec("SHA-256", + "MGF1", MGF1ParameterSpec.SHA256, 32, PSSParameterSpec.TRAILER_FIELD_BC)); + pssParameterSpecMap.put("SHA-384", new PSSParameterSpec("SHA-384", + "MGF1", MGF1ParameterSpec.SHA384, 48, PSSParameterSpec.TRAILER_FIELD_BC)); + pssParameterSpecMap.put("SHA-512", new PSSParameterSpec("SHA-512", + "MGF1", MGF1ParameterSpec.SHA512, 64, PSSParameterSpec.TRAILER_FIELD_BC)); + } + + @Setup + public void setup() throws Exception { + setupProvider(); + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(keySize); + keyPair = keyPairGenerator.generateKeyPair(); + data = new byte[SET_SIZE][dataSize]; + sigData = getSigBytes(data); + } + + private byte[][] getSigBytes(byte[][] data) throws Exception { + byte[][] sigBytes = new byte[data.length][]; + Signature signature = prov != null ? Signature.getInstance("RSASSA-PSS", prov) : + Signature.getInstance("RSASSA-PSS"); + signature.setParameter(pssParameterSpecMap.get(algorithm)); + signature.initSign(keyPair.getPrivate()); + for (int i = 0; i < sigBytes.length; i++) { + signature.update(data[i]); + sigBytes[i] = signature.sign(); + } + return sigBytes; + } + + @Benchmark + public void sign() throws Exception { + Signature signature = prov != null ? Signature.getInstance("RSASSA-PSS", prov) : + Signature.getInstance("RSASSA-PSS"); + signature.setParameter(pssParameterSpecMap.get(algorithm)); + signature.initSign(keyPair.getPrivate()); + signature.update(data[index]); + signature.sign(); + index = (index + 1) % SET_SIZE; + } + + @Benchmark + public void verify() throws Exception { + Signature signature = prov != null ? Signature.getInstance("RSASSA-PSS", prov) : + Signature.getInstance("RSASSA-PSS"); + signature.setParameter(pssParameterSpecMap.get(algorithm)); + signature.initVerify(keyPair.getPublic()); + signature.update(data[index]); + signature.verify(sigData[index]); + index = (index + 1) % SET_SIZE; + } +} diff --git a/test/micro/org/openeuler/bench/security/openssl/RSASignatureBenchmark.java b/test/micro/org/openeuler/bench/security/openssl/RSASignatureBenchmark.java new file mode 100644 index 000000000..31112ea57 --- /dev/null +++ b/test/micro/org/openeuler/bench/security/openssl/RSASignatureBenchmark.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2023, Huawei Technologies Co., Ltd. All rights reserved. + * Copyright (c) 2015, 2018, 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. + */ +package org.openeuler.bench.security.openssl; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Setup; + +import java.security.*; + +/** + * RSA Signature Benchmark + */ +public class RSASignatureBenchmark extends BenchmarkBase { + @Param({"MD5withRSA", "SHA1withRSA", "SHA224withRSA", "SHA384withRSA", "SHA256withRSA", "SHA512withRSA"}) + private String algorithm; + + @Param({"2048", "3072", "4096"}) + private int keySize; + + @Param({"" + 1024, "" + 10 * 1024, "" + 100 * 1024, "" + 256 * 1024, "" + 1024 * 1024, "" + 10 * 1024 * 1024}) + private int dataSize; + + private KeyPair keyPair; + + private byte[][] sigData; + + @Setup + public void setup() throws Exception { + setupProvider(); + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(keySize); + keyPair = keyPairGenerator.generateKeyPair(); + data = new byte[SET_SIZE][dataSize]; + sigData = getSigBytes(data); + } + + private byte[][] getSigBytes(byte[][] data) throws Exception { + byte[][] sigBytes = new byte[data.length][]; + Signature signature = prov != null ? Signature.getInstance(algorithm, prov) : + Signature.getInstance(algorithm); + signature.initSign(keyPair.getPrivate()); + for (int i = 0; i < sigBytes.length; i++) { + signature.update(data[i]); + sigBytes[i] = signature.sign(); + } + return sigBytes; + } + + @Benchmark + public void sign() throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { + Signature signature = prov != null ? Signature.getInstance(algorithm, prov) : + Signature.getInstance(algorithm); + signature.initSign(keyPair.getPrivate()); + signature.update(data[index]); + signature.sign(); + index = (index + 1) % SET_SIZE; + } + + @Benchmark + public void verify() throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { + Signature signature = prov != null ? Signature.getInstance(algorithm, prov) : + Signature.getInstance(algorithm); + signature.initVerify(keyPair.getPublic()); + signature.update(data[index]); + signature.verify(sigData[index]); + index = (index + 1) % SET_SIZE; + } +} diff --git a/test/micro/org/openeuler/bench/security/openssl/SM3Benchmark.java b/test/micro/org/openeuler/bench/security/openssl/SM3Benchmark.java new file mode 100644 index 000000000..eea830dbe --- /dev/null +++ b/test/micro/org/openeuler/bench/security/openssl/SM3Benchmark.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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. + */ +package org.openeuler.bench.security.openssl; + +import org.openeuler.security.openssl.KAEProvider; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.Security; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.SECONDS) +@Warmup(iterations = 3, time = 3, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 8, time = 2, timeUnit = TimeUnit.SECONDS) +@Fork(jvmArgsPrepend = {"-Xms100G", "-Xmx100G", "-XX:+AlwaysPreTouch"}, value = 5) +@Threads(1) +@State(Scope.Thread) +public class SM3Benchmark { + public static final int SET_SIZE = 128; + byte[][] data; + int index = 0; + + @Param({"SM3"}) + private String algorithm; + + @Param({"" + 1024, "" + 10 * 1024, "" + 100 * 1024, "" + 1024 * 1024}) + int dataSize; + + MessageDigest md; + + @Setup + public void setup() throws NoSuchAlgorithmException { + Security.addProvider(new KAEProvider()); + Provider prov = Security.getProvider("KAEProvider"); + data = fillRandom(new byte[SET_SIZE][dataSize]); + md = (prov == null) ? MessageDigest.getInstance(algorithm) : MessageDigest.getInstance(algorithm, prov); + } + + @Benchmark + public byte[] digest() { + byte[] d = data[index]; + index = (index + 1) % SET_SIZE; + return md.digest(d); + } + + @Benchmark + @Fork(jvmArgsPrepend = {"-Xms100G", "-Xmx100G", "-XX:+AlwaysPreTouch", "-Dkae.disableKaeDispose=true"}, value = 5) + public byte[] digestDispose() { + byte[] d = data[index]; + index = (index + 1) % SET_SIZE; + return md.digest(d); + } + + public static byte[][] fillRandom(byte[][] data) { + Random rnd = new Random(); + for (byte[] d : data) { + rnd.nextBytes(d); + } + return data; + } +} + diff --git a/test/micro/org/openeuler/bench/security/openssl/SM4Benchmark.java b/test/micro/org/openeuler/bench/security/openssl/SM4Benchmark.java new file mode 100644 index 000000000..32de2b235 --- /dev/null +++ b/test/micro/org/openeuler/bench/security/openssl/SM4Benchmark.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Huawei Technologies Co., Ltd. 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. + */ +package org.openeuler.bench.security.openssl; + +import org.openeuler.security.openssl.KAEProvider; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Threads; +import org.openjdk.jmh.annotations.Warmup; + +import java.security.InvalidKeyException; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.SecureRandom; +import java.security.Security; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.SecretKeySpec; + +@BenchmarkMode(Mode.Throughput) +@OutputTimeUnit(TimeUnit.SECONDS) +@Warmup(iterations = 3, time = 3, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 8, time = 2, timeUnit = TimeUnit.SECONDS) +@Fork(jvmArgsPrepend = {"-Xms100G", "-Xmx100G", "-XX:+AlwaysPreTouch"}, value = 5) +@Threads(1) +@State(Scope.Thread) +public class SM4Benchmark { + public static final int SET_SIZE = 128; + byte[][] data; + int index = 0; + + @Param({"SM4/ECB/NoPadding", "SM4/ECB/PKCS5Padding", "SM4/CBC/NoPadding", "SM4/CBC/PKCS5Padding", "SM4/CTR/NoPadding", "SM4/OFB/NoPadding", "SM4/OFB/PKCS5Padding"}) + private String algorithm; + + @Param({"128"}) + private int keyLength; + + @Param({"" + 1024, "" + 10 * 1024, "" + 100 * 1024, "" + 1024 * 1024}) + private int dataSize; + + private byte[][] encryptedData; + private Cipher encryptCipher; + private Cipher decryptCipher; + + @Setup + public void setup() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, + InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { + Security.addProvider(new KAEProvider()); + Provider prov = Security.getProvider("KAEProvider"); + + byte[] keystring = fillSecureRandom(new byte[keyLength / 8]); + SecretKeySpec ks = new SecretKeySpec(keystring, "SM4"); + + encryptCipher = (prov == null) ? Cipher.getInstance(algorithm) : Cipher.getInstance(algorithm, prov); + encryptCipher.init(Cipher.ENCRYPT_MODE, ks); + decryptCipher = (prov == null) ? Cipher.getInstance(algorithm) : Cipher.getInstance(algorithm, prov); + decryptCipher.init(Cipher.DECRYPT_MODE, ks, encryptCipher.getParameters()); + + data = fillRandom(new byte[SET_SIZE][dataSize]); + encryptedData = fillEncrypted(data, encryptCipher); + } + + @Benchmark + public byte[] encrypt() throws IllegalBlockSizeException, BadPaddingException { + byte[] d = data[index]; + index = (index + 1) % SET_SIZE; + return encryptCipher.doFinal(d); + } + + @Benchmark + @Fork(jvmArgsPrepend = {"-Xms100G", "-Xmx100G", "-XX:+AlwaysPreTouch", "-Dkae.disableKaeDispose=true"}, value = 5) + public byte[] encryptDispose() throws IllegalBlockSizeException, BadPaddingException { + byte[] d = data[index]; + index = (index + 1) % SET_SIZE; + return encryptCipher.doFinal(d); + } + + @Benchmark + public byte[] decrypt() throws IllegalBlockSizeException, BadPaddingException { + byte[] e = encryptedData[index]; + index = (index + 1) % SET_SIZE; + return decryptCipher.doFinal(e); + } + + @Benchmark + @Fork(jvmArgsPrepend = {"-Xms100G", "-Xmx100G", "-XX:+AlwaysPreTouch", "-Dkae.disableKaeDispose=true"}, value = 5) + public byte[] decryptDispose() throws IllegalBlockSizeException, BadPaddingException { + byte[] e = encryptedData[index]; + index = (index + 1) % SET_SIZE; + return decryptCipher.doFinal(e); + } + + public static byte[][] fillRandom(byte[][] data) { + Random rnd = new Random(); + for (byte[] d : data) { + rnd.nextBytes(d); + } + return data; + } + + public static byte[] fillRandom(byte[] data) { + Random rnd = new Random(); + rnd.nextBytes(data); + return data; + } + + public static byte[] fillSecureRandom(byte[] data) { + SecureRandom rnd = new SecureRandom(); + rnd.nextBytes(data); + return data; + } + + public static byte[][] fillEncrypted(byte[][] data, Cipher encryptCipher) + throws IllegalBlockSizeException, BadPaddingException { + byte[][] encryptedData = new byte[data.length][]; + for (int i = 0; i < encryptedData.length; i++) { + encryptedData[i] = encryptCipher.doFinal(data[i]); + } + return encryptedData; + } +} + -- 2.44.0