From 34e11500b80df0699ac8e533d52e06d30f7367d4 Mon Sep 17 00:00:00 2001 From: John Jiang Date: Tue, 14 Jan 2025 18:37:45 +0800 Subject: [PATCH] TKSS-1044: Provide one-shot native crypto implementations --- .github/workflows/build-pr.yml | 7 +- .../src/main/kotlin/kona-common.gradle.kts | 70 ++++-- .../com/tencent/kona/crypto/CryptoInsts.java | 25 +- .../com/tencent/kona/crypto/CryptoUtils.java | 11 +- .../KonaCryptoNativeOneShotProvider.java | 100 ++++++++ .../provider/nativeImpl/NativeCrypto.java | 38 +-- .../provider/nativeImpl/SM2KeyAgreement.java | 4 + .../provider/nativeImpl/SM2OneShotCipher.java | 237 ++++++++++++++++++ .../nativeImpl/SM2OneShotKeyAgreement.java | 167 ++++++++++++ .../SM2OneShotKeyPairGenerator.java | 74 ++++++ .../nativeImpl/SM2OneShotSignature.java | 176 +++++++++++++ .../provider/nativeImpl/SM3OneShotHMac.java | 101 ++++++++ .../nativeImpl/SM3OneShotMessageDigest.java | 76 ++++++ .../src/main/jni/include/kona/kona_jni.h | 64 +++++ kona-crypto/src/main/jni/kona_sm2_cipher.c | 204 +++++++++++++++ .../src/main/jni/kona_sm2_keyagreement.c | 190 +++++++++++++- kona-crypto/src/main/jni/kona_sm2_keypair.c | 30 +++ kona-crypto/src/main/jni/kona_sm2_signature.c | 218 ++++++++++++++++ kona-crypto/src/main/jni/kona_sm3.c | 132 +++++++++- .../resources/libKonaCrypto-linux-aarch64.so | Bin 82608 -> 83480 bytes .../resources/libKonaCrypto-linux-x86_64.so | Bin 61224 -> 70288 bytes .../com/tencent/kona/crypto/TestUtils.java | 10 +- .../nativeImpl/NativeRefProfTest.java | 112 ++++++++- .../provider/nativeImpl/NativeSM2Test.java | 105 ++++++++ .../nativeImpl/NativeSM3HMacTest.java | 6 + .../provider/nativeImpl/NativeSM3Test.java | 11 + .../java/com/tencent/kona/KonaProvider.java | 20 +- .../main/java/com/tencent/kona/KonaUtils.java | 112 +++++++++ .../kona/ssl/interop/JdkProcClient.java | 3 +- .../kona/ssl/interop/JdkProcServer.java | 3 +- .../kona/ssl/interop/JdkProcUtils.java | 2 +- 31 files changed, 2233 insertions(+), 75 deletions(-) create mode 100644 kona-crypto/src/main/java/com/tencent/kona/crypto/KonaCryptoNativeOneShotProvider.java create mode 100644 kona-crypto/src/main/java/com/tencent/kona/crypto/provider/nativeImpl/SM2OneShotCipher.java create mode 100644 kona-crypto/src/main/java/com/tencent/kona/crypto/provider/nativeImpl/SM2OneShotKeyAgreement.java create mode 100644 kona-crypto/src/main/java/com/tencent/kona/crypto/provider/nativeImpl/SM2OneShotKeyPairGenerator.java create mode 100644 kona-crypto/src/main/java/com/tencent/kona/crypto/provider/nativeImpl/SM2OneShotSignature.java create mode 100644 kona-crypto/src/main/java/com/tencent/kona/crypto/provider/nativeImpl/SM3OneShotHMac.java create mode 100644 kona-crypto/src/main/java/com/tencent/kona/crypto/provider/nativeImpl/SM3OneShotMessageDigest.java create mode 100644 kona-provider/src/main/java/com/tencent/kona/KonaUtils.java diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index 08612ecc..90d00bf5 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -1,5 +1,5 @@ # -# Copyright (C) 2023, 2024, THL A29 Limited, a Tencent company. All rights reserved. +# Copyright (C) 2023, 2025, THL A29 Limited, a Tencent company. 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 @@ -57,7 +57,10 @@ jobs: uses: gradle/actions/setup-gradle@v3 - name: Execute tests with the pure Java crypto - run: ./gradlew clean test + run: ./gradlew clean testJavaOnCurrent - name: Execute tests with the native crypto run: ./gradlew clean testNativeOnCurrent + + - name: Execute tests with the native OneShot crypto + run: ./gradlew clean testNativeOneShotOnCurrent diff --git a/buildSrc/src/main/kotlin/kona-common.gradle.kts b/buildSrc/src/main/kotlin/kona-common.gradle.kts index 02cc61a5..373d91cb 100644 --- a/buildSrc/src/main/kotlin/kona-common.gradle.kts +++ b/buildSrc/src/main/kotlin/kona-common.gradle.kts @@ -59,8 +59,8 @@ tasks { } } - val testOnCurrent = register("testOnCurrent", CommonTest::class) { - jvmArgs("-Dcom.tencent.kona.useNativeCrypto=false") + val testJavaOnCurrent = register("testJavaOnCurrent", CommonTest::class) { + jvmArgs("-Dcom.tencent.kona.defaultCrypto=Java") systemProperty("test.classpath", classpath.joinToString(separator = ":")) @@ -70,7 +70,7 @@ tasks { } register("testNativeOnCurrent", CommonTest::class) { - jvmArgs("-Dcom.tencent.kona.useNativeCrypto=true") + jvmArgs("-Dcom.tencent.kona.defaultCrypto=Native") systemProperty("test.classpath", classpath.joinToString(separator = ":")) @@ -79,8 +79,18 @@ tasks { } } - register("testOnAdop8", CommonTest::class) { - jvmArgs("-Dcom.tencent.kona.useNativeCrypto=false") + register("testNativeOneShotOnCurrent", CommonTest::class) { + jvmArgs("-Dcom.tencent.kona.defaultCrypto=NativeOneShot") + + systemProperty("test.classpath", classpath.joinToString(separator = ":")) + + doFirst { + println("Testing JDK: " + javaLauncher.get().metadata.installationPath) + } + } + + register("testJavaOnAdop8", CommonTest::class) { + jvmArgs("-Dcom.tencent.kona.defaultCrypto=Java") systemProperty("test.classpath", classpath.joinToString(separator = ":")) @@ -94,8 +104,8 @@ tasks { } } - register("testOnAdop11", CommonTest::class) { - jvmArgs("-Dcom.tencent.kona.useNativeCrypto=false", + register("testJavaOnAdop11", CommonTest::class) { + jvmArgs("-Dcom.tencent.kona.defaultCrypto=Java", "--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED") systemProperty("test.classpath", classpath.joinToString(separator = ":")) @@ -110,8 +120,8 @@ tasks { } } - register("testOnAdop17", CommonTest::class) { - jvmArgs("-Dcom.tencent.kona.useNativeCrypto=false", + register("testJavaOnAdop17", CommonTest::class) { + jvmArgs("-Dcom.tencent.kona.defaultCrypto=Java", "--add-exports", "java.base/jdk.internal.access=ALL-UNNAMED") systemProperty("test.classpath", classpath.joinToString(separator = ":")) @@ -126,8 +136,8 @@ tasks { } } - register("testOnAdop21", CommonTest::class) { - jvmArgs("-Dcom.tencent.kona.useNativeCrypto=false", + register("testJavaOnAdop21", CommonTest::class) { + jvmArgs("-Dcom.tencent.kona.defaultCrypto=Java", "--add-exports", "java.base/jdk.internal.access=ALL-UNNAMED") systemProperty("test.classpath", classpath.joinToString(separator = ":")) @@ -142,8 +152,8 @@ tasks { } } - register("testOnKona8", CommonTest::class) { - jvmArgs("-Dcom.tencent.kona.useNativeCrypto=false"); + register("testJavaOnKona8", CommonTest::class) { + jvmArgs("-Dcom.tencent.kona.defaultCrypto=Java"); systemProperty("test.classpath", classpath.joinToString(separator = ":")) @@ -157,8 +167,8 @@ tasks { } } - register("testOnKona11", CommonTest::class) { - jvmArgs("-Dcom.tencent.kona.useNativeCrypto=false", + register("testJavaOnKona11", CommonTest::class) { + jvmArgs("-Dcom.tencent.kona.defaultCrypto=Java", "--add-exports", "java.base/jdk.internal.misc=ALL-UNNAMED") systemProperty("test.classpath", classpath.joinToString(separator = ":")) @@ -173,8 +183,8 @@ tasks { } } - register("testOnKona17", CommonTest::class) { - jvmArgs("-Dcom.tencent.kona.useNativeCrypto=false", + register("testJavaOnKona17", CommonTest::class) { + jvmArgs("-Dcom.tencent.kona.defaultCrypto=Java", "--add-exports", "java.base/jdk.internal.access=ALL-UNNAMED") systemProperty("test.classpath", classpath.joinToString(separator = ":")) @@ -189,8 +199,8 @@ tasks { } } - register("testOnKona21", CommonTest::class) { - jvmArgs("-Dcom.tencent.kona.useNativeCrypto=false", + register("testJavaOnKona21", CommonTest::class) { + jvmArgs("-Dcom.tencent.kona.defaultCrypto=Java", "--add-exports", "java.base/jdk.internal.access=ALL-UNNAMED") systemProperty("test.classpath", classpath.joinToString(separator = ":")) @@ -205,8 +215,8 @@ tasks { } } - register("testOnGraal17", CommonTest::class) { - jvmArgs("-Dcom.tencent.kona.useNativeCrypto=false", + register("testJavaOnGraal17", CommonTest::class) { + jvmArgs("-Dcom.tencent.kona.defaultCrypto=Java", "--add-exports", "java.base/jdk.internal.access=ALL-UNNAMED") systemProperty("test.classpath", classpath.joinToString(separator = ":")) @@ -221,8 +231,8 @@ tasks { } } - register("testOnGraal21", CommonTest::class) { - jvmArgs("-Dcom.tencent.kona.useNativeCrypto=false", + register("testJavaOnGraal21", CommonTest::class) { + jvmArgs("-Dcom.tencent.kona.defaultCrypto=Java", "--add-exports", "java.base/jdk.internal.access=ALL-UNNAMED") systemProperty("test.classpath", classpath.joinToString(separator = ":")) @@ -238,7 +248,7 @@ tasks { } test { - dependsOn(testOnCurrent) + dependsOn(testJavaOnCurrent) } javadoc { @@ -246,16 +256,22 @@ tasks { isFailOnError = false } - register("jmh", type=JavaExec::class) { + register("jmhJava", type=JavaExec::class) { mainClass.set("org.openjdk.jmh.Main") classpath(sourceSets["jmh"].runtimeClasspath) - jvmArgs("-Dcom.tencent.kona.useNativeCrypto=false") + jvmArgs("-Dcom.tencent.kona.defaultCrypto=Java") } register("jmhNative", type=JavaExec::class) { mainClass.set("org.openjdk.jmh.Main") classpath(sourceSets["jmh"].runtimeClasspath) - jvmArgs("-Dcom.tencent.kona.useNativeCrypto=true") + jvmArgs("-Dcom.tencent.kona.defaultCrypto=Native") + } + + register("jmhNativeOneShot", type=JavaExec::class) { + mainClass.set("org.openjdk.jmh.Main") + classpath(sourceSets["jmh"].runtimeClasspath) + jvmArgs("-Dcom.tencent.kona.defaultCrypto=NativeOneShot") } } diff --git a/kona-crypto/src/main/java/com/tencent/kona/crypto/CryptoInsts.java b/kona-crypto/src/main/java/com/tencent/kona/crypto/CryptoInsts.java index 2128c96a..d33969f0 100644 --- a/kona-crypto/src/main/java/com/tencent/kona/crypto/CryptoInsts.java +++ b/kona-crypto/src/main/java/com/tencent/kona/crypto/CryptoInsts.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022, 2024, THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, 2025, THL A29 Limited, a Tencent company. 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 @@ -33,9 +33,18 @@ public class CryptoInsts { - public static final Provider PROV = CryptoUtils.useNativeCrypto() - ? KonaCryptoNativeProvider.instance() - : KonaCryptoProvider.instance(); + public static final Provider PROV = provider(); + + private static Provider provider() { + String provName = CryptoUtils.defaultCrypto(); + if ("Native".equalsIgnoreCase(provName)) { + return KonaCryptoNativeProvider.instance(); + } else if ("NativeOneShot".equalsIgnoreCase(provName)) { + return KonaCryptoNativeOneShotProvider.instance(); + } else { + return KonaCryptoProvider.instance(); + } + } private static final Set ALGO_PARAMS_ALGOS = new HashSet<>(Arrays.asList("EC", "SM4", "PBES2")); @@ -114,7 +123,13 @@ public static Cipher getCipher(String algorithm, Provider prov) public static Cipher getCipher(String algorithm) throws NoSuchPaddingException, NoSuchAlgorithmException { - return getCipher(algorithm, PROV); + if ("SM4".equalsIgnoreCase(algorithm)) { + return getCipher(algorithm, + // use pure Java-based SM4 for TLCP/TLS protocol + KonaCryptoProvider.instance()); + } else { + return getCipher(algorithm, PROV); + } } private static final Set MESSAGE_DIGEST_ALGOS diff --git a/kona-crypto/src/main/java/com/tencent/kona/crypto/CryptoUtils.java b/kona-crypto/src/main/java/com/tencent/kona/crypto/CryptoUtils.java index 54939aba..9f254b3a 100644 --- a/kona-crypto/src/main/java/com/tencent/kona/crypto/CryptoUtils.java +++ b/kona-crypto/src/main/java/com/tencent/kona/crypto/CryptoUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022, 2024, THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, 2025, THL A29 Limited, a Tencent company. 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 @@ -44,8 +44,9 @@ public final class CryptoUtils { private static final String JDK_VENDOR = privilegedGetProperty( "java.specification.vendor"); - private static final boolean USE_NATIVE_CRYPTO = privilegedGetBoolProperty( - "com.tencent.kona.useNativeCrypto", "false"); + // Java, Native or NativeOneShot + private static final String DEFAULT_CRYPTO = privilegedGetProperty( + "com.tencent.kona.defaultCrypto", "Java"); public static String privilegedGetProperty(String key, String def) { return AccessController.doPrivileged( @@ -86,8 +87,8 @@ public static boolean isAndroid() { return JDK_VENDOR.contains("Android"); } - public static boolean useNativeCrypto() { - return USE_NATIVE_CRYPTO && isLinux() && !isAndroid(); + public static String defaultCrypto() { + return isLinux() && !isAndroid() ? DEFAULT_CRYPTO : "Java"; } public static boolean isLinux() { diff --git a/kona-crypto/src/main/java/com/tencent/kona/crypto/KonaCryptoNativeOneShotProvider.java b/kona-crypto/src/main/java/com/tencent/kona/crypto/KonaCryptoNativeOneShotProvider.java new file mode 100644 index 00000000..b6636bba --- /dev/null +++ b/kona-crypto/src/main/java/com/tencent/kona/crypto/KonaCryptoNativeOneShotProvider.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2024, 2025, THL A29 Limited, a Tencent company. 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 as + * published by the Free Software Foundation. THL A29 Limited designates + * this particular file as subject to the "Classpath" exception as provided + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.tencent.kona.crypto; + +import com.tencent.kona.sun.security.rsa.SunRsaSignEntries; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.Provider; + +/** + * The Kona Crypto Provider based on OpenSSL. + */ +public class KonaCryptoNativeOneShotProvider extends Provider { + + static final String NAME = "KonaCrypto-NativeOneShot"; + + private static final double VERSION_NUM = 1.0D; + + private static final String INFO = "Kona crypto provider based on OpenSSL " + + "(implements SM2, SM3 and SM4 algorithms)"; + + private static volatile KonaCryptoNativeOneShotProvider instance = null; + + public KonaCryptoNativeOneShotProvider() { + super(NAME, VERSION_NUM, INFO); + + AccessController.doPrivileged( + (PrivilegedAction) () -> { + putEntries(this); + + return null; + }); + } + + private static void putEntries(Provider provider) { + putSMEntries(provider); + KonaCryptoProvider.putSMPBES2Entries(provider); + KonaCryptoProvider.putECEntries(provider); + SunRsaSignEntries.putEntries(provider); + } + + private static void putSMEntries(Provider provider) { + provider.put("Cipher.SM4 SupportedPaddings", "NOPADDING|PKCS7PADDING"); + provider.put("Cipher.SM4 SupportedModes", "CBC|CTR|ECB|GCM"); + provider.put("Cipher.SM4", + "com.tencent.kona.crypto.provider.nativeImpl.SM4Cipher");; + provider.put("AlgorithmParameters.SM4", + "com.tencent.kona.crypto.provider.SM4Parameters"); + provider.put("AlgorithmParameterGenerator.SM4", + "com.tencent.kona.crypto.provider.SM4ParameterGenerator"); + provider.put("KeyGenerator.SM4", + "com.tencent.kona.crypto.provider.SM4KeyGenerator"); + + provider.put("Alg.Alias.MessageDigest.OID.1.2.156.10197.1.401", "SM3"); + provider.put("MessageDigest.SM3", + "com.tencent.kona.crypto.provider.nativeImpl.SM3OneShotMessageDigest"); + provider.put("Mac.HmacSM3", + "com.tencent.kona.crypto.provider.nativeImpl.SM3OneShotHMac"); + provider.put("Alg.Alias.Mac.SM3HMac", "HmacSM3"); + provider.put("KeyGenerator.HmacSM3", + "com.tencent.kona.crypto.provider.SM3HMacKeyGenerator"); + provider.put("Alg.Alias.KeyGenerator.SM3HMac", "HmacSM3"); + + provider.put("Alg.Alias.Cipher.OID.1.2.156.10197.1.301", "SM2"); + provider.put("Alg.Alias.Signature.OID.1.2.156.10197.1.501", "SM2"); + provider.put("KeyPairGenerator.SM2", + "com.tencent.kona.crypto.provider.nativeImpl.SM2OneShotKeyPairGenerator"); + provider.put("Cipher.SM2", "com.tencent.kona.crypto.provider.nativeImpl.SM2OneShotCipher"); + provider.put("Signature.SM2", "com.tencent.kona.crypto.provider.nativeImpl.SM2OneShotSignature"); + provider.put("Alg.Alias.Signature.SM3withSM2", "SM2"); + provider.put("KeyAgreement.SM2", "com.tencent.kona.crypto.provider.nativeImpl.SM2OneShotKeyAgreement"); + provider.put("KeyFactory.SM2", "com.tencent.kona.crypto.provider.SM2KeyFactory"); + } + + public static KonaCryptoNativeOneShotProvider instance() { + if (instance == null) { + instance = new KonaCryptoNativeOneShotProvider(); + } + return instance; + } +} diff --git a/kona-crypto/src/main/java/com/tencent/kona/crypto/provider/nativeImpl/NativeCrypto.java b/kona-crypto/src/main/java/com/tencent/kona/crypto/provider/nativeImpl/NativeCrypto.java index 067840e5..1656f528 100644 --- a/kona-crypto/src/main/java/com/tencent/kona/crypto/provider/nativeImpl/NativeCrypto.java +++ b/kona-crypto/src/main/java/com/tencent/kona/crypto/provider/nativeImpl/NativeCrypto.java @@ -32,7 +32,7 @@ import static com.tencent.kona.crypto.CryptoUtils.*; /** - * The internal APIs for underlying native crypto library from OpenSSL 3.4.0. + * The internal APIs for underlying native crypto library from OpenSSL. */ final class NativeCrypto { @@ -41,19 +41,8 @@ final class NativeCrypto { private static final String KONA_CRYPTO_LIB = privilegedGetProperty( "com.tencent.kona.crypto.lib.path"); - private NativeCrypto() {} - - private static class InstanceHolder { - - static { - loadLibs(); - } - - private static final NativeCrypto INSTANCE = new NativeCrypto(); - } - - static NativeCrypto nativeCrypto() { - return InstanceHolder.INSTANCE; + static { + loadLibs(); } private static void loadLibs() { @@ -161,6 +150,16 @@ private static void copyNativeLib(String libName, Path libPath) } } + private static class InstanceHolder { + private static final NativeCrypto INSTANCE = new NativeCrypto(); + } + + static NativeCrypto nativeCrypto() { + return InstanceHolder.INSTANCE; + } + + private NativeCrypto() {} + static final int OPENSSL_SUCCESS = 1; static final int OPENSSL_FAILURE = 0; @@ -171,6 +170,7 @@ private static void copyNativeLib(String libName, Path libPath) native byte[] sm3Final(long pointer); native int sm3Reset(long pointer); native long sm3Clone(long pointer); + static native byte[] sm3OneShotDigest(byte[] data); /* ***** SM3HMAC ***** */ native long sm3hmacCreateCtx(byte[] key); @@ -179,6 +179,7 @@ private static void copyNativeLib(String libName, Path libPath) native byte[] sm3hmacFinal(long pointer); native int sm3hmacReset(long pointer); native long sm3hmacClone(long pointer); + static native byte[] sm3hmacOneShotMac(byte[] key, byte[] data); /* ***** SM4 ***** */ native long sm4CreateCtx(boolean encrypt, String mode, boolean padding, byte[] key, byte[] iv); @@ -197,16 +198,21 @@ private static void copyNativeLib(String libName, Path libPath) native long sm2KeyPairGenCreateCtx(); native void sm2KeyPairGenFreeCtx(long pointer); native byte[] sm2KeyPairGenGenKeyPair(long pointer); + static native byte[] sm2OneShotKeyPairGenGenKeyPair(); native long sm2CipherCreateCtx(byte[] key); native void sm2CipherFreeCtx(long pointer); native byte[] sm2CipherEncrypt(long pointer, byte[] plaintext); native byte[] sm2CipherDecrypt(long pointer, byte[] ciphertext); + static native byte[] sm2OneShotCipherEncrypt(byte[] key, byte[] plaintext); + static native byte[] sm2OneShotCipherDecrypt(byte[] key, byte[] ciphertext); native long sm2SignatureCreateCtx(byte[] key, byte[] id, boolean isSign); native void sm2SignatureFreeCtx(long pointer); native byte[] sm2SignatureSign(long pointer, byte[] message); native int sm2SignatureVerify(long pointer, byte[] message, byte[] signature); + static native byte[] sm2OneShotSignatureSign(byte[] key, byte[] id, byte[] message); + static native int sm2OneShotSignatureVerify(byte[] key, byte[] id, byte[] message, byte[] signature); native long sm2KeyExCreateCtx(); native void sm2KeyExFreeCtx(long pointer); @@ -214,4 +220,8 @@ native byte[] sm2DeriveKey(long pointer, byte[] priKey, byte[] pubKey, byte[] ePrivKey, byte[] id, byte[] peerPubKey, byte[] peerEPubKey, byte[] peerId, boolean isInitiator, int sharedKeyLength); + static native byte[] sm2OneShotDeriveKey( + byte[] priKey, byte[] pubKey, byte[] ePrivKey, byte[] id, + byte[] peerPubKey, byte[] peerEPubKey, byte[] peerId, + boolean isInitiator, int sharedKeyLength); } diff --git a/kona-crypto/src/main/java/com/tencent/kona/crypto/provider/nativeImpl/SM2KeyAgreement.java b/kona-crypto/src/main/java/com/tencent/kona/crypto/provider/nativeImpl/SM2KeyAgreement.java index 85aad264..848588c0 100644 --- a/kona-crypto/src/main/java/com/tencent/kona/crypto/provider/nativeImpl/SM2KeyAgreement.java +++ b/kona-crypto/src/main/java/com/tencent/kona/crypto/provider/nativeImpl/SM2KeyAgreement.java @@ -71,6 +71,10 @@ protected void engineInit(Key key, SecureRandom random) { protected void engineInit(Key key, AlgorithmParameterSpec params, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { + ephemeralPrivateKey = null; + paramSpec = null; + peerEphemeralPublicKey = null; + if (!(key instanceof ECPrivateKey)) { throw new InvalidKeyException("Only accept ECPrivateKey"); } diff --git a/kona-crypto/src/main/java/com/tencent/kona/crypto/provider/nativeImpl/SM2OneShotCipher.java b/kona-crypto/src/main/java/com/tencent/kona/crypto/provider/nativeImpl/SM2OneShotCipher.java new file mode 100644 index 00000000..8132609e --- /dev/null +++ b/kona-crypto/src/main/java/com/tencent/kona/crypto/provider/nativeImpl/SM2OneShotCipher.java @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2022, 2024, THL A29 Limited, a Tencent company. 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 as + * published by the Free Software Foundation. THL A29 Limited designates + * this particular file as subject to the "Classpath" exception as provided + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.tencent.kona.crypto.provider.nativeImpl; + +import com.tencent.kona.crypto.provider.SM2PrivateKey; +import com.tencent.kona.crypto.provider.SM2PublicKey; +import com.tencent.kona.crypto.util.Sweeper; + +import javax.crypto.*; +import java.math.BigInteger; +import java.security.*; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Arrays; + +import static com.tencent.kona.crypto.spec.SM2ParameterSpec.ORDER; +import static com.tencent.kona.crypto.util.Constants.SM2_CURVE_FIELD_SIZE; +import static com.tencent.kona.crypto.util.Constants.SM3_DIGEST_LEN; +import static java.math.BigInteger.ZERO; + +public final class SM2OneShotCipher extends CipherSpi { + + private static final Sweeper SWEEPER = Sweeper.instance(); + + private static final byte[] B0 = new byte[0]; + + private byte[] key = null; + private final ByteArrayWriter buffer = new ByteArrayWriter(); + + private boolean encrypted = true; + + @Override + public void engineSetMode(String mode) throws NoSuchAlgorithmException { + if (!mode.equalsIgnoreCase("none")) { + throw new NoSuchAlgorithmException("Mode must be none"); + } + } + + @Override + public void engineSetPadding(String paddingName) + throws NoSuchPaddingException { + if (!paddingName.equalsIgnoreCase("NoPadding")) { + throw new NoSuchPaddingException("Padding must be NoPadding"); + } + } + + @Override + public void engineInit(int opmode, Key key, SecureRandom random) + throws InvalidKeyException { + this.key = null; + buffer.reset(); + + if (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE) { + encrypted = true; + + if (key instanceof ECPublicKey) { + SM2PublicKey publicKey = new SM2PublicKey((ECPublicKey) key); + this.key = publicKey.getEncoded(); + } else { + throw new InvalidKeyException( + "Only accept ECPublicKey for encryption"); + } + } else if (opmode == Cipher.DECRYPT_MODE || opmode == Cipher.UNWRAP_MODE) { + encrypted = false; + + if (key instanceof ECPrivateKey) { + SM2PrivateKey privateKey = new SM2PrivateKey((ECPrivateKey) key); + + BigInteger s = privateKey.getS(); + if (s.compareTo(ZERO) <= 0 || s.compareTo(ORDER) >= 0) { + throw new InvalidKeyException("The private key must be " + + "within the range [1, n - 1]"); + } + + this.key = privateKey.getEncoded(); + } else { + throw new InvalidKeyException( + "Only accept ECPrivateKey for decryption"); + } + } + } + + @Override + public void engineInit(int opmode, Key key, + AlgorithmParameterSpec params, SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException { + engineInit(opmode, key, random); + } + + @Override + public void engineInit(int opmode, Key key, + AlgorithmParameters params, SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException { + if (params != null) { + throw new InvalidAlgorithmParameterException( + "Not need AlgorithmParameters"); + } + + engineInit(opmode, key, random); + } + + @Override + public byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { + buffer.write(input, inputOffset, inputLen); + return B0; + } + + @Override + public int engineUpdate(byte[] input, int inputOffset, int inputLen, + byte[] output, int outputOffset) { + buffer.write(input, inputOffset, inputLen); + return 0; + } + + @Override + public byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) + throws IllegalBlockSizeException, BadPaddingException { + update(input, inputOffset, inputLen); + return doFinal(); + } + + @Override + public int engineDoFinal(byte[] input, int inputOffset, int inputLen, + byte[] output, int outputOffset) + throws ShortBufferException, IllegalBlockSizeException, + BadPaddingException { + int outputSize = engineGetOutputSize(buffer.size()); + if (outputSize > output.length - outputOffset) { + throw new ShortBufferException( + "Need " + outputSize + " bytes for output"); + } + + update(input, inputOffset, inputLen); + byte[] result = doFinal(); + int n = result.length; + System.arraycopy(result, 0, output, outputOffset, n); + Arrays.fill(result, (byte)0); + return result.length; + } + + @Override + public byte[] engineWrap(Key key) throws InvalidKeyException, + IllegalBlockSizeException { + byte[] encoded = key.getEncoded(); + if (encoded == null || encoded.length == 0) { + throw new InvalidKeyException("No encoded key"); + } + + try { + return engineDoFinal(encoded, 0, encoded.length); + } catch (BadPaddingException e) { + throw new InvalidKeyException("Wrap key failed", e); + } finally { + Arrays.fill(encoded, (byte)0); + } + } + + @Override + public Key engineUnwrap(byte[] wrappedKey, String algorithm, + int type) throws InvalidKeyException, NoSuchAlgorithmException { + if (wrappedKey == null || wrappedKey.length == 0) { + throw new InvalidKeyException("No wrapped key"); + } + + byte[] encoded; + try { + encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length); + } catch (IllegalBlockSizeException | BadPaddingException e) { + throw new InvalidKeyException("Unwrap key failed", e); + } + + return ConstructKeys.constructKey(encoded, algorithm, type); + } + + private void update(byte[] input, int inputOffset, int inputLen) { + if (input == null || inputLen == 0) { + return; + } + + buffer.write(input, inputOffset, inputLen); + } + + private byte[] doFinal() throws BadPaddingException, IllegalBlockSizeException { + try (NativeSM2Cipher sm2 = new NativeSM2Cipher(key)) { + byte[] input = buffer.toByteArray(); + return encrypted ? sm2.encrypt(input) : sm2.decrypt(input); + } finally { + buffer.reset(); + } + } + + @Override + public AlgorithmParameters engineGetParameters() { + return null; + } + + @Override + public byte[] engineGetIV() { + return null; + } + + @Override + public int engineGetBlockSize() { + return 0; + } + + @Override + public int engineGetOutputSize(int inputLen) { + int offset = 1 + 2 * SM2_CURVE_FIELD_SIZE + SM3_DIGEST_LEN; + return encrypted ? inputLen + offset + : Math.max(0, inputLen - offset); + } + + @Override + public int engineGetKeySize(Key key) { + return SM2_CURVE_FIELD_SIZE << 3; + } +} diff --git a/kona-crypto/src/main/java/com/tencent/kona/crypto/provider/nativeImpl/SM2OneShotKeyAgreement.java b/kona-crypto/src/main/java/com/tencent/kona/crypto/provider/nativeImpl/SM2OneShotKeyAgreement.java new file mode 100644 index 00000000..ac9c1e71 --- /dev/null +++ b/kona-crypto/src/main/java/com/tencent/kona/crypto/provider/nativeImpl/SM2OneShotKeyAgreement.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2024, 2025, THL A29 Limited, a Tencent company. 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 as + * published by the Free Software Foundation. THL A29 Limited designates + * this particular file as subject to the "Classpath" exception as provided + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.tencent.kona.crypto.provider.nativeImpl; + +import com.tencent.kona.crypto.provider.SM2PrivateKey; +import com.tencent.kona.crypto.provider.SM2PublicKey; +import com.tencent.kona.crypto.spec.SM2KeyAgreementParamSpec; + +import javax.crypto.KeyAgreementSpi; +import javax.crypto.SecretKey; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.SecretKeySpec; +import java.math.BigInteger; +import java.security.*; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.AlgorithmParameterSpec; + +import static com.tencent.kona.crypto.provider.nativeImpl.NativeCrypto.OPENSSL_SUCCESS; +import static com.tencent.kona.crypto.spec.SM2ParameterSpec.ORDER; +import static java.math.BigInteger.ZERO; + +/** + * SM2 key agreement in compliance with GB/T 32918.3-2016. + */ +public final class SM2OneShotKeyAgreement extends KeyAgreementSpi { + + private SM2PrivateKey ephemeralPrivateKey; + private SM2KeyAgreementParamSpec paramSpec; + private SM2PublicKey peerEphemeralPublicKey; + + @Override + protected void engineInit(Key key, SecureRandom random) { + throw new UnsupportedOperationException( + "Use init(Key, AlgorithmParameterSpec, SecureRandom) instead"); + } + + @Override + protected void engineInit(Key key, AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException { + ephemeralPrivateKey = null; + paramSpec = null; + peerEphemeralPublicKey = null; + + if (!(key instanceof ECPrivateKey)) { + throw new InvalidKeyException("Only accept ECPrivateKey"); + } + + if (!(params instanceof SM2KeyAgreementParamSpec)) { + throw new InvalidAlgorithmParameterException( + "Only accept SM2KeyAgreementParamSpec"); + } + + ECPrivateKey ecPrivateKey = (ECPrivateKey) key; + BigInteger s = ecPrivateKey.getS(); + if (s.compareTo(ZERO) <= 0 || s.compareTo(ORDER) >= 0) { + throw new InvalidKeyException("The private key must be " + + "within the range [1, n - 1]"); + } + + ephemeralPrivateKey = new SM2PrivateKey((ECPrivateKey) key); + paramSpec = (SM2KeyAgreementParamSpec) params; + peerEphemeralPublicKey = null; + } + + @Override + protected Key engineDoPhase(Key key, boolean lastPhase) + throws InvalidKeyException, IllegalStateException { + if (ephemeralPrivateKey == null || paramSpec == null) { + throw new IllegalStateException("Not initialized"); + } + + if (peerEphemeralPublicKey != 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("Only accept ECPublicKey"); + } + + SM2PublicKey sm2PublicKey = new SM2PublicKey((ECPublicKey) key); + if (NativeCrypto.sm2ValidatePoint(sm2PublicKey.getEncoded()) + != OPENSSL_SUCCESS) { + throw new InvalidKeyException("Public key is invalid"); + } + + peerEphemeralPublicKey = sm2PublicKey; + + return null; + } + + @Override + protected byte[] engineGenerateSecret() throws IllegalStateException { + if (ephemeralPrivateKey == null || (peerEphemeralPublicKey == null)) { + throw new IllegalStateException("Not initialized correctly"); + } + + byte[] result; + try { + result = deriveKeyImpl(); + } catch (Exception e) { + throw new IllegalStateException(e); + } + peerEphemeralPublicKey = null; + return result; + } + + private byte[] deriveKeyImpl() { + return NativeCrypto.sm2OneShotDeriveKey( + new SM2PrivateKey(paramSpec.privateKey()).getEncoded(), + new SM2PublicKey(paramSpec.publicKey()).getEncoded(), + ephemeralPrivateKey.getEncoded(), + paramSpec.id(), + new SM2PublicKey(paramSpec.peerPublicKey()).getEncoded(), + peerEphemeralPublicKey.getEncoded(), + paramSpec.peerId(), + paramSpec.isInitiator(), + paramSpec.sharedKeyLength()); + } + + @Override + protected int engineGenerateSecret(byte[] sharedSecret, int offset) + throws IllegalStateException, ShortBufferException { + if (offset + paramSpec.sharedKeyLength() > sharedSecret.length) { + throw new ShortBufferException("Need " + paramSpec.sharedKeyLength() + + " 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 { + if (algorithm == null) { + throw new NoSuchAlgorithmException("Algorithm must not be null"); + } + return new SecretKeySpec(engineGenerateSecret(), algorithm); + } +} diff --git a/kona-crypto/src/main/java/com/tencent/kona/crypto/provider/nativeImpl/SM2OneShotKeyPairGenerator.java b/kona-crypto/src/main/java/com/tencent/kona/crypto/provider/nativeImpl/SM2OneShotKeyPairGenerator.java new file mode 100644 index 00000000..e1bfba6e --- /dev/null +++ b/kona-crypto/src/main/java/com/tencent/kona/crypto/provider/nativeImpl/SM2OneShotKeyPairGenerator.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2024, 2025, THL A29 Limited, a Tencent company. 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 as + * published by the Free Software Foundation. THL A29 Limited designates + * this particular file as subject to the "Classpath" exception as provided + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.tencent.kona.crypto.provider.nativeImpl; + +import com.tencent.kona.crypto.CryptoUtils; +import com.tencent.kona.crypto.provider.SM2PrivateKey; +import com.tencent.kona.crypto.provider.SM2PublicKey; +import com.tencent.kona.crypto.spec.SM2ParameterSpec; +import com.tencent.kona.sun.security.util.KnownOIDs; +import com.tencent.kona.sun.security.util.NamedCurve; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.SecureRandom; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.AlgorithmParameterSpec; + +import static com.tencent.kona.crypto.util.Constants.SM2_PRIKEY_LEN; +import static com.tencent.kona.crypto.util.Constants.SM2_PUBKEY_LEN; + +public final class SM2OneShotKeyPairGenerator extends KeyPairGenerator { + + public SM2OneShotKeyPairGenerator() { + super("SM2"); + } + + @Override + public void initialize(int keySize, SecureRandom random) { + if (keySize != SM2_PRIKEY_LEN << 3) { + throw new IllegalArgumentException( + "keySize must be 256-bit: " + keySize); + } + } + + @Override + public void initialize(AlgorithmParameterSpec params, SecureRandom random) { + if (params == null || !(params instanceof SM2ParameterSpec) + && !KnownOIDs.curveSM2.value().equals( + ((NamedCurve) params).getObjectId())) { + throw new IllegalArgumentException( + "params must be SM2ParameterSpec or NamedCurve (curveSM2)"); + } + } + + @Override + public KeyPair generateKeyPair() { + byte[] keyPair = NativeCrypto.sm2OneShotKeyPairGenGenKeyPair(); + ECPrivateKey priKey = new SM2PrivateKey( + CryptoUtils.copy(keyPair, 0, SM2_PRIKEY_LEN)); + ECPublicKey pubKey = new SM2PublicKey( + CryptoUtils.copy(keyPair, SM2_PRIKEY_LEN, SM2_PUBKEY_LEN)); + + return new KeyPair(pubKey, priKey); + } +} diff --git a/kona-crypto/src/main/java/com/tencent/kona/crypto/provider/nativeImpl/SM2OneShotSignature.java b/kona-crypto/src/main/java/com/tencent/kona/crypto/provider/nativeImpl/SM2OneShotSignature.java new file mode 100644 index 00000000..28aedcf4 --- /dev/null +++ b/kona-crypto/src/main/java/com/tencent/kona/crypto/provider/nativeImpl/SM2OneShotSignature.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2022, 2025, THL A29 Limited, a Tencent company. 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 as + * published by the Free Software Foundation. THL A29 Limited designates + * this particular file as subject to the "Classpath" exception as provided + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.tencent.kona.crypto.provider.nativeImpl; + +import com.tencent.kona.crypto.provider.SM2PrivateKey; +import com.tencent.kona.crypto.provider.SM2PublicKey; +import com.tencent.kona.crypto.spec.SM2SignatureParameterSpec; + +import javax.crypto.BadPaddingException; +import java.math.BigInteger; +import java.security.*; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.AlgorithmParameterSpec; + +import static com.tencent.kona.crypto.spec.SM2ParameterSpec.ORDER; +import static java.math.BigInteger.ONE; +import static java.math.BigInteger.ZERO; + +public final class SM2OneShotSignature extends SignatureSpi { + + // The default ID 1234567812345678 + private static final byte[] DEFAULT_ID = new byte[] { + 49, 50, 51, 52, 53, 54, 55, 56, + 49, 50, 51, 52, 53, 54, 55, 56}; + + private SM2PrivateKey privateKey; + private SM2PublicKey publicKey; + private byte[] id; + + private final ByteArrayWriter buffer = new ByteArrayWriter(); + + @Override + protected void engineInitSign(PrivateKey privateKey, SecureRandom random) + throws InvalidKeyException { + this.privateKey = null; + buffer.reset(); + + if (!(privateKey instanceof ECPrivateKey)) { + throw new InvalidKeyException("Only ECPrivateKey accepted!"); + } + + ECPrivateKey ecPrivateKey = (ECPrivateKey) privateKey; + + BigInteger s = ecPrivateKey.getS(); + if (s.compareTo(ZERO) <= 0 || s.compareTo(ORDER.subtract(ONE)) >= 0) { + throw new InvalidKeyException("The private key must be " + + "within the range [1, n - 2]"); + } + + this.privateKey = new SM2PrivateKey(ecPrivateKey); + } + + @Override + protected void engineInitSign(PrivateKey privateKey) + throws InvalidKeyException { + engineInitSign(privateKey, null); + } + + @Override + protected void engineInitVerify(PublicKey publicKey) + throws InvalidKeyException { + this.privateKey = null; + this.publicKey = null; + buffer.reset(); + + if (!(publicKey instanceof ECPublicKey)) { + throw new InvalidKeyException("Only ECPublicKey accepted!"); + } + + this.publicKey = new SM2PublicKey((ECPublicKey) publicKey); + } + + @Override + protected void engineSetParameter(AlgorithmParameterSpec params) + throws InvalidAlgorithmParameterException { + privateKey = null; + publicKey = null; + id = null; + + if (!(params instanceof SM2SignatureParameterSpec)) { + throw new InvalidAlgorithmParameterException( + "Only accept SM2SignatureParameterSpec"); + } + + SM2SignatureParameterSpec paramSpec = (SM2SignatureParameterSpec) params; + publicKey = new SM2PublicKey(paramSpec.getPublicKey()); + id = paramSpec.getId(); + } + + @Override + protected void engineSetParameter(String param, Object value) + throws InvalidParameterException { + throw new UnsupportedOperationException( + "Use setParameter(AlgorithmParameterSpec params) instead"); + } + + @Override + protected Object engineGetParameter(String param) + throws InvalidParameterException { + throw new UnsupportedOperationException( + "getParameter(String param) not supported"); + } + + @Override + protected void engineUpdate(byte b) throws SignatureException { + byte[] buf = new byte[] {b}; + buffer.write(buf, 0, 1); + } + + @Override + protected void engineUpdate(byte[] b, int off, int len) + throws SignatureException { + buffer.write(b, off, len); + } + + @Override + protected byte[] engineSign() throws SignatureException { + if (privateKey == null) { + throw new SignatureException("Private key not initialized"); + } + + if (id == null) { + id = DEFAULT_ID.clone(); + } + + try (NativeSM2Signature sm2 = new NativeSM2Signature( + privateKey.getEncoded(), + publicKey != null ? publicKey.getEncoded() : null, + id, true)) { + return sm2.sign(buffer.toByteArray()); + } catch (BadPaddingException e) { + throw new SignatureException(e); + } finally { + buffer.reset(); + } + } + + @Override + protected boolean engineVerify(byte[] sigBytes) throws SignatureException { + if (publicKey == null) { + throw new SignatureException("Public key not initialized"); + } + + if (id == null) { + id = DEFAULT_ID.clone(); + } + + try (NativeSM2Signature sm2 = new NativeSM2Signature( + null, publicKey.getEncoded(), id, false)) { + return sm2.verify(buffer.toByteArray(), sigBytes); + } catch (BadPaddingException e) { + throw new SignatureException(e); + } finally { + buffer.reset(); + } + } +} diff --git a/kona-crypto/src/main/java/com/tencent/kona/crypto/provider/nativeImpl/SM3OneShotHMac.java b/kona-crypto/src/main/java/com/tencent/kona/crypto/provider/nativeImpl/SM3OneShotHMac.java new file mode 100644 index 00000000..822a9b6b --- /dev/null +++ b/kona-crypto/src/main/java/com/tencent/kona/crypto/provider/nativeImpl/SM3OneShotHMac.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2024, 2025, THL A29 Limited, a Tencent company. 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 as + * published by the Free Software Foundation. THL A29 Limited designates + * this particular file as subject to the "Classpath" exception as provided + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.tencent.kona.crypto.provider.nativeImpl; + +import com.tencent.kona.crypto.util.Constants; +import com.tencent.kona.jdk.internal.util.Preconditions; + +import javax.crypto.MacSpi; +import javax.crypto.SecretKey; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.spec.AlgorithmParameterSpec; + +public final class SM3OneShotHMac extends MacSpi implements Cloneable { + + private final ByteArrayWriter buffer = new ByteArrayWriter(); + + private byte[] key; + + @Override + protected int engineGetMacLength() { + return Constants.SM3_HMAC_LEN; + } + + @Override + protected void engineInit(Key key, AlgorithmParameterSpec params) + throws InvalidKeyException, InvalidAlgorithmParameterException { + this.key = null; + buffer.reset(); + + if (params != null) { + throw new InvalidAlgorithmParameterException("No need parameters"); + } + + if (!(key instanceof SecretKey)) { + throw new InvalidKeyException("SecretKey is expected"); + } + + byte[] secret = key.getEncoded(); + if (secret == null) { + throw new InvalidKeyException("No key data"); + } + + this.key = secret; + } + + @Override + protected void engineUpdate(byte input) { + buffer.write(new byte[] { input }); + } + + @Override + protected void engineUpdate(byte[] input, int offset, int len) { + if (len == 0) { + return; + } + Preconditions.checkFromIndexSize( + offset, len, input.length, Preconditions.AIOOBE_FORMATTER); + + buffer.write(input, offset, len); + } + + @Override + protected byte[] engineDoFinal() { + byte[] mac = NativeCrypto.sm3hmacOneShotMac(key, buffer.toByteArray()); + buffer.reset(); + return mac; + } + + @Override + protected void engineReset() { + buffer.reset(); + } + + @Override + public SM3OneShotHMac clone() throws CloneNotSupportedException { + SM3OneShotHMac clone = new SM3OneShotHMac(); + clone.key = key == null ? null : key.clone(); + clone.buffer.write(buffer.toByteArray()); + return clone; + } +} diff --git a/kona-crypto/src/main/java/com/tencent/kona/crypto/provider/nativeImpl/SM3OneShotMessageDigest.java b/kona-crypto/src/main/java/com/tencent/kona/crypto/provider/nativeImpl/SM3OneShotMessageDigest.java new file mode 100644 index 00000000..c60575cf --- /dev/null +++ b/kona-crypto/src/main/java/com/tencent/kona/crypto/provider/nativeImpl/SM3OneShotMessageDigest.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2024, 2025, THL A29 Limited, a Tencent company. 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 as + * published by the Free Software Foundation. THL A29 Limited designates + * this particular file as subject to the "Classpath" exception as provided + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.tencent.kona.crypto.provider.nativeImpl; + +import com.tencent.kona.jdk.internal.util.Preconditions; + +import java.security.MessageDigest; + +import static com.tencent.kona.crypto.util.Constants.SM3_DIGEST_LEN; + +public final class SM3OneShotMessageDigest extends MessageDigest implements Cloneable { + + private final ByteArrayWriter buffer = new ByteArrayWriter(); + + public SM3OneShotMessageDigest() { + super("SM3"); + } + + @Override + protected void engineUpdate(byte input) { + buffer.write(new byte[] { input }); + } + + @Override + protected int engineGetDigestLength() { + return SM3_DIGEST_LEN; + } + + @Override + protected void engineUpdate(byte[] input, int offset, int len) { + if (len == 0) { + return; + } + Preconditions.checkFromIndexSize( + offset, len, input.length, Preconditions.AIOOBE_FORMATTER); + + buffer.write(input, offset, len); + } + + @Override + protected byte[] engineDigest() { + byte[] digest = NativeCrypto.sm3OneShotDigest(buffer.toByteArray()); + buffer.reset(); + return digest; + } + + @Override + protected void engineReset() { + buffer.reset(); + } + + @Override + public SM3OneShotMessageDigest clone() throws CloneNotSupportedException { + SM3OneShotMessageDigest clone = new SM3OneShotMessageDigest(); + clone.buffer.write(buffer.toByteArray()); + return clone; + } +} diff --git a/kona-crypto/src/main/jni/include/kona/kona_jni.h b/kona-crypto/src/main/jni/include/kona/kona_jni.h index 97252ce4..fd2765e5 100644 --- a/kona-crypto/src/main/jni/include/kona/kona_jni.h +++ b/kona-crypto/src/main/jni/include/kona/kona_jni.h @@ -59,6 +59,14 @@ JNIEXPORT jint JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_NativeCr JNIEXPORT jlong JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto_sm3Clone (JNIEnv *, jobject, jlong); +/* + * Class: com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto + * Method: sm3OneShotDigest + * Signature: ([B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto_sm3OneShotDigest + (JNIEnv *, jclass, jbyteArray); + /* * Class: com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto * Method: sm3hmacCreateCtx @@ -107,6 +115,14 @@ JNIEXPORT jint JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_NativeCr JNIEXPORT jlong JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto_sm3hmacClone (JNIEnv *, jobject, jlong); +/* + * Class: com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto + * Method: sm3hmacOneShotMac + * Signature: ([B[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto_sm3hmacOneShotMac + (JNIEnv *, jclass, jbyteArray, jbyteArray); + /* * Class: com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto * Method: sm4CreateCtx @@ -211,6 +227,14 @@ JNIEXPORT void JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_NativeCr JNIEXPORT jbyteArray JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto_sm2KeyPairGenGenKeyPair (JNIEnv *, jobject, jlong); +/* + * Class: com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto + * Method: sm2OneShotKeyPairGenGenKeyPair + * Signature: ()[B + */ +JNIEXPORT jbyteArray JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto_sm2OneShotKeyPairGenGenKeyPair + (JNIEnv *, jclass); + /* * Class: com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto * Method: sm2CipherCreateCtx @@ -243,6 +267,22 @@ JNIEXPORT jbyteArray JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_Na JNIEXPORT jbyteArray JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto_sm2CipherDecrypt (JNIEnv *, jobject, jlong, jbyteArray); +/* + * Class: com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto + * Method: sm2OneShotCipherEncrypt + * Signature: ([B[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto_sm2OneShotCipherEncrypt + (JNIEnv *, jclass, jbyteArray, jbyteArray); + +/* + * Class: com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto + * Method: sm2OneShotCipherDecrypt + * Signature: ([B[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto_sm2OneShotCipherDecrypt + (JNIEnv *, jclass, jbyteArray, jbyteArray); + /* * Class: com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto * Method: sm2SignatureCreateCtx @@ -275,6 +315,22 @@ JNIEXPORT jbyteArray JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_Na JNIEXPORT jint JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto_sm2SignatureVerify (JNIEnv *, jobject, jlong, jbyteArray, jbyteArray); +/* + * Class: com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto + * Method: sm2OneShotSignatureSign + * Signature: ([B[B[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto_sm2OneShotSignatureSign + (JNIEnv *, jclass, jbyteArray, jbyteArray, jbyteArray); + +/* + * Class: com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto + * Method: sm2OneShotSignatureVerify + * Signature: ([B[B[B[B)I + */ +JNIEXPORT jint JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto_sm2OneShotSignatureVerify + (JNIEnv *, jclass, jbyteArray, jbyteArray, jbyteArray, jbyteArray); + /* * Class: com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto * Method: sm2KeyExCreateCtx @@ -299,6 +355,14 @@ JNIEXPORT void JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_NativeCr JNIEXPORT jbyteArray JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto_sm2DeriveKey (JNIEnv *, jobject, jlong, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jboolean, jint); +/* + * Class: com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto + * Method: sm2OneShotDeriveKey + * Signature: ([B[B[B[B[B[B[BZI)[B + */ +JNIEXPORT jbyteArray JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto_sm2OneShotDeriveKey + (JNIEnv *, jclass, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jboolean, jint); + #ifdef __cplusplus } #endif diff --git a/kona-crypto/src/main/jni/kona_sm2_cipher.c b/kona-crypto/src/main/jni/kona_sm2_cipher.c index 7082e5a5..0a41bee2 100644 --- a/kona-crypto/src/main/jni/kona_sm2_cipher.c +++ b/kona-crypto/src/main/jni/kona_sm2_cipher.c @@ -278,3 +278,207 @@ JNIEXPORT jbyteArray JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_Na return cleartext_bytes; } + +JNIEXPORT jbyteArray JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto_sm2OneShotCipherEncrypt + (JNIEnv* env, jclass classObj, jbyteArray key, jbyteArray plaintext) { + int key_len = (*env)->GetArrayLength(env, key); + if (key_len < SM2_PRI_KEY_LEN) { + return NULL; + } + jbyte* key_bytes = (*env)->GetByteArrayElements(env, key, NULL); + if (key_bytes == NULL) { + return NULL; + } + + EVP_PKEY* pkey = NULL; + if (key_len == SM2_PRI_KEY_LEN) { + uint8_t* pub_key_buf = OPENSSL_malloc(SM2_PUB_KEY_LEN); + if (!pub_key_buf) { + (*env)->ReleaseByteArrayElements(env, key, key_bytes, JNI_ABORT); + return NULL; + } + + if (!sm2_gen_pub_key((const uint8_t*)key_bytes, pub_key_buf)) { + OPENSSL_free(pub_key_buf); + (*env)->ReleaseByteArrayElements(env, key, key_bytes, JNI_ABORT); + return NULL; + } + + pkey = sm2_load_key_pair((const uint8_t*)key_bytes, pub_key_buf); + OPENSSL_free(pub_key_buf); + } else if (key_len == SM2_PUB_KEY_LEN) { + pkey = sm2_load_pub_key((const uint8_t*)key_bytes, key_len); + } else if (key_len == (SM2_PRI_KEY_LEN + SM2_PUB_KEY_LEN)) { + uint8_t* pri_key_buf = OPENSSL_malloc(SM2_PRI_KEY_LEN); + if (!pri_key_buf) { + (*env)->ReleaseByteArrayElements(env, key, key_bytes, JNI_ABORT); + return NULL; + } + memcpy(pri_key_buf, (const uint8_t*)key_bytes, SM2_PRI_KEY_LEN); + + uint8_t* pub_key_buf = OPENSSL_malloc(SM2_PUB_KEY_LEN); + if (!pub_key_buf) { + OPENSSL_free(pri_key_buf); + (*env)->ReleaseByteArrayElements(env, key, key_bytes, JNI_ABORT); + return NULL; + } + memcpy(pub_key_buf, (const uint8_t*)key_bytes + SM2_PRI_KEY_LEN, SM2_PUB_KEY_LEN); + + pkey = sm2_load_key_pair((const uint8_t*)pri_key_buf, pub_key_buf); + OPENSSL_free(pri_key_buf); + OPENSSL_free(pub_key_buf); + } + + (*env)->ReleaseByteArrayElements(env, key, key_bytes, JNI_ABORT); + + if (pkey == NULL) { + return NULL; + } + + EVP_PKEY_CTX* pctx = sm2_create_pkey_ctx(pkey); + if (pctx == NULL) { + EVP_PKEY_free(pkey); + return NULL; + } + + jsize plaintext_len = (*env)->GetArrayLength(env, plaintext); + if (plaintext_len == 0) { + EVP_PKEY_CTX_free(pctx); + EVP_PKEY_free(pkey); + return NULL; + } + jbyte* plaintext_bytes = (*env)->GetByteArrayElements(env, plaintext, NULL); + if (plaintext_bytes == NULL) { + EVP_PKEY_CTX_free(pctx); + EVP_PKEY_free(pkey); + return NULL; + } + + size_t ciphertext_len; + uint8_t* ciphertext_buf = sm2_encrypt(pctx, (const uint8_t*)plaintext_bytes, plaintext_len, &ciphertext_len); + if (ciphertext_buf == NULL) { + (*env)->ReleaseByteArrayElements(env, plaintext, plaintext_bytes, JNI_ABORT); + EVP_PKEY_CTX_free(pctx); + EVP_PKEY_free(pkey); + return NULL; + } + + jbyteArray ciphertext_bytes = (*env)->NewByteArray(env, ciphertext_len); + if (ciphertext_bytes == NULL) { + OPENSSL_free(ciphertext_buf); + (*env)->ReleaseByteArrayElements(env, plaintext, plaintext_bytes, JNI_ABORT); + EVP_PKEY_CTX_free(pctx); + EVP_PKEY_free(pkey); + return NULL; + } + (*env)->SetByteArrayRegion(env, ciphertext_bytes, 0, ciphertext_len, (jbyte*)ciphertext_buf); + + OPENSSL_free(ciphertext_buf); + (*env)->ReleaseByteArrayElements(env, plaintext, plaintext_bytes, JNI_ABORT); + EVP_PKEY_CTX_free(pctx); + EVP_PKEY_free(pkey); + + return ciphertext_bytes; +} + +JNIEXPORT jbyteArray JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto_sm2OneShotCipherDecrypt + (JNIEnv* env, jclass classObj, jbyteArray key, jbyteArray ciphertext) { + int key_len = (*env)->GetArrayLength(env, key); + if (key_len < SM2_PRI_KEY_LEN) { + return NULL; + } + jbyte* key_bytes = (*env)->GetByteArrayElements(env, key, NULL); + if (key_bytes == NULL) { + return NULL; + } + + EVP_PKEY* pkey = NULL; + if (key_len == SM2_PRI_KEY_LEN) { + uint8_t* pub_key_buf = OPENSSL_malloc(SM2_PUB_KEY_LEN); + if (!pub_key_buf) { + (*env)->ReleaseByteArrayElements(env, key, key_bytes, JNI_ABORT); + return NULL; + } + + if (!sm2_gen_pub_key((const uint8_t*)key_bytes, pub_key_buf)) { + OPENSSL_free(pub_key_buf); + (*env)->ReleaseByteArrayElements(env, key, key_bytes, JNI_ABORT); + return NULL; + } + + pkey = sm2_load_key_pair((const uint8_t*)key_bytes, pub_key_buf); + OPENSSL_free(pub_key_buf); + } else if (key_len == SM2_PUB_KEY_LEN) { + pkey = sm2_load_pub_key((const uint8_t*)key_bytes, key_len); + } else if (key_len == (SM2_PRI_KEY_LEN + SM2_PUB_KEY_LEN)) { + uint8_t* pri_key_buf = OPENSSL_malloc(SM2_PRI_KEY_LEN); + if (!pri_key_buf) { + (*env)->ReleaseByteArrayElements(env, key, key_bytes, JNI_ABORT); + return NULL; + } + memcpy(pri_key_buf, (const uint8_t*)key_bytes, SM2_PRI_KEY_LEN); + + uint8_t* pub_key_buf = OPENSSL_malloc(SM2_PUB_KEY_LEN); + if (!pub_key_buf) { + OPENSSL_free(pri_key_buf); + (*env)->ReleaseByteArrayElements(env, key, key_bytes, JNI_ABORT); + return NULL; + } + memcpy(pub_key_buf, (const uint8_t*)key_bytes + SM2_PRI_KEY_LEN, SM2_PUB_KEY_LEN); + + pkey = sm2_load_key_pair((const uint8_t*)pri_key_buf, pub_key_buf); + OPENSSL_free(pri_key_buf); + OPENSSL_free(pub_key_buf); + } + + (*env)->ReleaseByteArrayElements(env, key, key_bytes, JNI_ABORT); + + if (pkey == NULL) { + return NULL; + } + + EVP_PKEY_CTX* pctx = sm2_create_pkey_ctx(pkey); + if (pctx == NULL) { + EVP_PKEY_free(pkey); + return NULL; + } + + jsize ciphertext_len = (*env)->GetArrayLength(env, ciphertext); + if (ciphertext_len == 0) { + EVP_PKEY_CTX_free(pctx); + EVP_PKEY_free(pkey); + return NULL; + } + jbyte* ciphertext_bytes = (*env)->GetByteArrayElements(env, ciphertext, NULL); + if (ciphertext_bytes == NULL) { + EVP_PKEY_CTX_free(pctx); + EVP_PKEY_free(pkey); + return NULL; + } + + size_t cleartext_len = 0; + uint8_t* cleartext_buf = sm2_decrypt(pctx, (const uint8_t*)ciphertext_bytes, ciphertext_len, &cleartext_len); + if (cleartext_buf == NULL) { + (*env)->ReleaseByteArrayElements(env, ciphertext, ciphertext_bytes, JNI_ABORT); + EVP_PKEY_CTX_free(pctx); + EVP_PKEY_free(pkey); + return NULL; + } + + jbyteArray cleartext_bytes = (*env)->NewByteArray(env, cleartext_len); + if (cleartext_bytes == NULL) { + OPENSSL_free(cleartext_buf); + (*env)->ReleaseByteArrayElements(env, ciphertext, ciphertext_bytes, JNI_ABORT); + EVP_PKEY_CTX_free(pctx); + EVP_PKEY_free(pkey); + return NULL; + } + (*env)->SetByteArrayRegion(env, cleartext_bytes, 0, cleartext_len, (jbyte*)cleartext_buf); + + OPENSSL_free(cleartext_buf); + (*env)->ReleaseByteArrayElements(env, ciphertext, ciphertext_bytes, JNI_ABORT); + EVP_PKEY_CTX_free(pctx); + EVP_PKEY_free(pkey); + + return cleartext_bytes; +} diff --git a/kona-crypto/src/main/jni/kona_sm2_keyagreement.c b/kona-crypto/src/main/jni/kona_sm2_keyagreement.c index 1718534c..7af50ab9 100644 --- a/kona-crypto/src/main/jni/kona_sm2_keyagreement.c +++ b/kona-crypto/src/main/jni/kona_sm2_keyagreement.c @@ -410,9 +410,9 @@ JNIEXPORT void JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_NativeCr JNIEXPORT jbyteArray JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto_sm2DeriveKey (JNIEnv* env, jobject thisObj, jlong pointer, - jbyteArray priKey, jbyteArray pubKey, jbyteArray ePriKey, jbyteArray id, - jbyteArray peerPubKey, jbyteArray peerEPubKey, jbyteArray peerId, - jboolean isInitiator, jint sharedKeyLength) { + jbyteArray priKey, jbyteArray pubKey, jbyteArray ePriKey, jbyteArray id, + jbyteArray peerPubKey, jbyteArray peerEPubKey, jbyteArray peerId, + jboolean isInitiator, jint sharedKeyLength) { SM2_KEYEX_CTX* ctx = (SM2_KEYEX_CTX*)pointer; if (ctx == NULL) { return NULL; @@ -590,3 +590,187 @@ JNIEXPORT jbyteArray JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_Na return shared_key_bytes; } + +JNIEXPORT jbyteArray JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto_sm2OneShotDeriveKey + (JNIEnv* env, jclass classObj, + jbyteArray priKey, jbyteArray pubKey, jbyteArray ePriKey, jbyteArray id, + jbyteArray peerPubKey, jbyteArray peerEPubKey, jbyteArray peerId, + jboolean isInitiator, jint sharedKeyLength) { + SM2_KEYEX_CTX* ctx = sm2_create_keyex_ctx(); + if (ctx == NULL) { + return NULL; + } + + jbyte* pri_key_bytes = NULL; + jbyte* pub_key_bytes = NULL; + jbyte* e_pri_key_bytes = NULL; + jbyte* id_bytes = NULL; + jbyte* peer_pub_key_bytes = NULL; + jbyte* peer_e_pub_key_bytes = NULL; + jbyte* peer_id_bytes = NULL; + BIGNUM* pri_key = NULL; + EC_POINT* pub_key = NULL; + BIGNUM* e_pri_key = NULL; + EC_POINT* peer_pub_key = NULL; + EC_POINT* peer_e_pub_key = NULL; + SM2_KEYEX_PARAMS* params = NULL; + uint8_t* shared_key_buf = NULL; + jbyteArray shared_key_bytes = NULL; + + int pri_key_len = (*env)->GetArrayLength(env, priKey); + if (pri_key_len != SM2_PRI_KEY_LEN) { + goto cleanup; + } + pri_key_bytes = (*env)->GetByteArrayElements(env, priKey, NULL); + if (pri_key_bytes == NULL) { + goto cleanup; + } + + int pub_key_len = (*env)->GetArrayLength(env, pubKey); + if (pub_key_len != SM2_PUB_KEY_LEN) { + goto cleanup; + } + pub_key_bytes = (*env)->GetByteArrayElements(env, pubKey, NULL); + if (pub_key_bytes == NULL) { + goto cleanup; + } + + int e_pri_key_len = (*env)->GetArrayLength(env, ePriKey); + if (e_pri_key_len != SM2_PRI_KEY_LEN) { + goto cleanup; + } + e_pri_key_bytes = (*env)->GetByteArrayElements(env, ePriKey, NULL); + if (e_pri_key_bytes == NULL) { + goto cleanup; + } + + int id_len = (*env)->GetArrayLength(env, id); + if (id_len <= 0) { + goto cleanup; + } + id_bytes = (*env)->GetByteArrayElements(env, id, NULL); + if (id_bytes == NULL) { + goto cleanup; + } + + int peer_pub_key_len = (*env)->GetArrayLength(env, peerPubKey); + if (peer_pub_key_len != SM2_PUB_KEY_LEN) { + goto cleanup; + } + peer_pub_key_bytes = (*env)->GetByteArrayElements(env, peerPubKey, NULL); + if (peer_pub_key_bytes == NULL) { + goto cleanup; + } + + int peer_e_pub_key_len = (*env)->GetArrayLength(env, peerEPubKey); + if (peer_e_pub_key_len != SM2_PUB_KEY_LEN) { + goto cleanup; + } + peer_e_pub_key_bytes = (*env)->GetByteArrayElements(env, peerEPubKey, NULL); + if (peer_e_pub_key_bytes == NULL) { + goto cleanup; + } + + int peer_id_len = (*env)->GetArrayLength(env, peerId); + if (peer_id_len <= 0) { + goto cleanup; + } + peer_id_bytes = (*env)->GetByteArrayElements(env, peerId, NULL); + if (peer_id_bytes == NULL) { + goto cleanup; + } + + bool is_initiator = (bool)isInitiator; + + int shared_key_len = (int)sharedKeyLength; + if (shared_key_len <= 0) { + goto cleanup; + } + + pri_key = sm2_pri_key((const uint8_t *)pri_key_bytes); + if (pri_key == NULL) { + goto cleanup; + } + + pub_key = sm2_pub_key((const uint8_t *)pub_key_bytes, pub_key_len); + if (pub_key == NULL) { + goto cleanup; + } + + e_pri_key = sm2_pri_key((const uint8_t *)e_pri_key_bytes); + if (e_pri_key == NULL) { + goto cleanup; + } + + peer_pub_key = sm2_pub_key((const uint8_t *)peer_pub_key_bytes, peer_pub_key_len); + if (peer_pub_key == NULL) { + goto cleanup; + } + + peer_e_pub_key = sm2_pub_key((const uint8_t *)peer_e_pub_key_bytes, peer_e_pub_key_len); + if (peer_e_pub_key == NULL) { + goto cleanup; + } + + params = OPENSSL_malloc(sizeof(SM2_KEYEX_PARAMS)); + if (params == NULL) { + goto cleanup; + } + params->pri_key = pri_key; + params->pub_key = pub_key; + params->e_pri_key = e_pri_key; + params->id = (uint8_t*)id_bytes; + params->id_len = id_len; + params->peer_pub_key = peer_pub_key; + params->peer_e_pub_key = peer_e_pub_key; + params->peer_id = (uint8_t*)peer_id_bytes; + params->peer_id_len = peer_id_len; + + shared_key_buf = OPENSSL_malloc(shared_key_len); + if (shared_key_buf == NULL) { + goto cleanup; + } + + if (!sm2_derive_key(shared_key_buf, shared_key_len, ctx, params, is_initiator)) { + goto cleanup; + } + + shared_key_bytes = (*env)->NewByteArray(env, shared_key_len); + if (shared_key_bytes == NULL) { + goto cleanup; + } + (*env)->SetByteArrayRegion(env, shared_key_bytes, 0, shared_key_len, (jbyte*)shared_key_buf); + +cleanup: + if (pri_key_bytes != NULL) { + (*env)->ReleaseByteArrayElements(env, priKey, pri_key_bytes, JNI_ABORT); + } + if (pub_key_bytes != NULL) { + (*env)->ReleaseByteArrayElements(env, pubKey, pub_key_bytes, JNI_ABORT); + } + if (e_pri_key_bytes != NULL) { + (*env)->ReleaseByteArrayElements(env, ePriKey, e_pri_key_bytes, JNI_ABORT); + } + if (id_bytes != NULL) { + (*env)->ReleaseByteArrayElements(env, id, id_bytes, JNI_ABORT); + } + if (peer_pub_key_bytes != NULL) { + (*env)->ReleaseByteArrayElements(env, peerPubKey, peer_pub_key_bytes, JNI_ABORT); + } + if (peer_e_pub_key_bytes != NULL) { + (*env)->ReleaseByteArrayElements(env, peerEPubKey, peer_e_pub_key_bytes, JNI_ABORT); + } + if (peer_id_bytes != NULL) { + (*env)->ReleaseByteArrayElements(env, peerId, peer_id_bytes, JNI_ABORT); + } + BN_free(pri_key); + EC_POINT_free(pub_key); + BN_free(e_pri_key); + EC_POINT_free(peer_pub_key); + EC_POINT_free(peer_e_pub_key); + OPENSSL_free(params); + OPENSSL_free(shared_key_buf); + sm2_free_keyex_ctx(ctx); + + return shared_key_bytes; +} diff --git a/kona-crypto/src/main/jni/kona_sm2_keypair.c b/kona-crypto/src/main/jni/kona_sm2_keypair.c index abc912ff..bb040aff 100644 --- a/kona-crypto/src/main/jni/kona_sm2_keypair.c +++ b/kona-crypto/src/main/jni/kona_sm2_keypair.c @@ -258,3 +258,33 @@ JNIEXPORT jbyteArray JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_Na return result; } + +JNIEXPORT jbyteArray JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto_sm2OneShotKeyPairGenGenKeyPair + (JNIEnv* env, jclass classObj) { + EVP_PKEY_CTX* ctx = sm2_create_pkey_ctx(NULL); + if (ctx == NULL) { + return NULL; + } + + size_t key_pair_len = SM2_PRI_KEY_LEN + SM2_PUB_KEY_LEN; + uint8_t* key_pair_buf = OPENSSL_malloc(key_pair_len); + if (key_pair_buf == NULL) { + return NULL; + } + + if (!sm2_gen_key_pair(ctx, key_pair_buf, &key_pair_len)) { + OPENSSL_free(key_pair_buf); + EVP_PKEY_CTX_free(ctx); + return NULL; + } + + jbyteArray result = (*env)->NewByteArray(env, key_pair_len); + if (result) { + (*env)->SetByteArrayRegion(env, result, 0, key_pair_len, (jbyte*)key_pair_buf); + } + + OPENSSL_free(key_pair_buf); + EVP_PKEY_CTX_free(ctx); + + return result; +} diff --git a/kona-crypto/src/main/jni/kona_sm2_signature.c b/kona-crypto/src/main/jni/kona_sm2_signature.c index 40b35876..c0335ba4 100644 --- a/kona-crypto/src/main/jni/kona_sm2_signature.c +++ b/kona-crypto/src/main/jni/kona_sm2_signature.c @@ -335,3 +335,221 @@ JNIEXPORT jint JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_NativeCr return verified; } + +JNIEXPORT jbyteArray JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto_sm2OneShotSignatureSign + (JNIEnv* env, jclass classObj, jbyteArray key, jbyteArray id, jbyteArray message) { + int key_len = (*env)->GetArrayLength(env, key); + if (key_len < SM2_PRI_KEY_LEN) { + return NULL; + } + jbyte* key_bytes = (*env)->GetByteArrayElements(env, key, NULL); + if (key_bytes == NULL) { + return NULL; + } + + int id_len = (*env)->GetArrayLength(env, id); + if (id_len <= 0) { + (*env)->ReleaseByteArrayElements(env, key, key_bytes, JNI_ABORT); + return NULL; + } + jbyte* id_bytes = (*env)->GetByteArrayElements(env, id, NULL); + if (id_bytes == NULL) { + (*env)->ReleaseByteArrayElements(env, key, key_bytes, JNI_ABORT); + return NULL; + } + + EVP_PKEY* pkey = NULL; + if (key_len == SM2_PRI_KEY_LEN) { + uint8_t* pub_key_buf = OPENSSL_malloc(SM2_PUB_KEY_LEN); + if (!pub_key_buf) { + (*env)->ReleaseByteArrayElements(env, key, key_bytes, JNI_ABORT); + (*env)->ReleaseByteArrayElements(env, id, id_bytes, JNI_ABORT); + return NULL; + } + + if (!sm2_gen_pub_key((const uint8_t*)key_bytes, pub_key_buf)) { + OPENSSL_free(pub_key_buf); + (*env)->ReleaseByteArrayElements(env, key, key_bytes, JNI_ABORT); + (*env)->ReleaseByteArrayElements(env, id, id_bytes, JNI_ABORT); + return NULL; + } + + pkey = sm2_load_key_pair((const uint8_t*)key_bytes, pub_key_buf); + OPENSSL_free(pub_key_buf); + } else if (key_len == SM2_PUB_KEY_LEN) { + pkey = sm2_load_pub_key((const uint8_t*)key_bytes, key_len); + } else if (key_len == (SM2_PRI_KEY_LEN + SM2_PUB_KEY_LEN)) { + uint8_t* pri_key_buf = OPENSSL_malloc(SM2_PRI_KEY_LEN); + uint8_t* pub_key_buf = OPENSSL_malloc(SM2_PUB_KEY_LEN); + if (!pri_key_buf || !pub_key_buf) { + OPENSSL_free(pri_key_buf); + OPENSSL_free(pub_key_buf); + (*env)->ReleaseByteArrayElements(env, key, key_bytes, JNI_ABORT); + (*env)->ReleaseByteArrayElements(env, id, id_bytes, JNI_ABORT); + return NULL; + } + + memcpy(pri_key_buf, (const uint8_t*)key_bytes, SM2_PRI_KEY_LEN); + memcpy(pub_key_buf, (const uint8_t*)key_bytes + SM2_PRI_KEY_LEN, SM2_PUB_KEY_LEN); + + pkey = sm2_load_key_pair((const uint8_t*)pri_key_buf, pub_key_buf); + OPENSSL_free(pri_key_buf); + OPENSSL_free(pub_key_buf); + } + + if (pkey == NULL) { + (*env)->ReleaseByteArrayElements(env, key, key_bytes, JNI_ABORT); + (*env)->ReleaseByteArrayElements(env, id, id_bytes, JNI_ABORT); + return NULL; + } + + SM2_SIGNATURE_CTX* ctx = sm2_create_md_ctx(pkey, (const uint8_t*)id_bytes, id_len, true); + if (ctx == NULL) { + (*env)->ReleaseByteArrayElements(env, key, key_bytes, JNI_ABORT); + (*env)->ReleaseByteArrayElements(env, id, id_bytes, JNI_ABORT); + EVP_PKEY_free(pkey); + return NULL; + } + + (*env)->ReleaseByteArrayElements(env, key, key_bytes, JNI_ABORT); + (*env)->ReleaseByteArrayElements(env, id, id_bytes, JNI_ABORT); + + jsize msg_len = (*env)->GetArrayLength(env, message); + if (msg_len < 0) { + sm2_signature_ctx_free(ctx); + return NULL; + } + jbyte* msg_bytes = (*env)->GetByteArrayElements(env, message, NULL); + if (msg_bytes == NULL) { + sm2_signature_ctx_free(ctx); + return NULL; + } + + size_t sig_len = 0; + uint8_t* sig_buf = sm2_sign(ctx->mctx, (uint8_t*)msg_bytes, msg_len, &sig_len); + + (*env)->ReleaseByteArrayElements(env, message, msg_bytes, JNI_ABORT); + + if (sig_buf == NULL) { + sm2_signature_ctx_free(ctx); + return NULL; + } + + jbyteArray sig_bytes = (*env)->NewByteArray(env, sig_len); + if (sig_bytes == NULL) { + OPENSSL_free(sig_buf); + sm2_signature_ctx_free(ctx); + return NULL; + } + + (*env)->SetByteArrayRegion(env, sig_bytes, 0, sig_len, (jbyte*)sig_buf); + + OPENSSL_free(sig_buf); + sm2_signature_ctx_free(ctx); + + return sig_bytes; +} + +JNIEXPORT jint JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto_sm2OneShotSignatureVerify + (JNIEnv* env, jclass classObj, jbyteArray key, jbyteArray id, jbyteArray message, jbyteArray signature) { + int key_len = (*env)->GetArrayLength(env, key); + if (key_len < SM2_PRI_KEY_LEN) { + return OPENSSL_FAILURE; + } + jbyte* key_bytes = (*env)->GetByteArrayElements(env, key, NULL); + if (key_bytes == NULL) { + return OPENSSL_FAILURE; + } + + int id_len = (*env)->GetArrayLength(env, id); + if (id_len <= 0) { + (*env)->ReleaseByteArrayElements(env, key, key_bytes, JNI_ABORT); + return OPENSSL_FAILURE; + } + jbyte* id_bytes = (*env)->GetByteArrayElements(env, id, NULL); + if (id_bytes == NULL) { + (*env)->ReleaseByteArrayElements(env, key, key_bytes, JNI_ABORT); + return OPENSSL_FAILURE; + } + + EVP_PKEY* pkey = NULL; + if (key_len == SM2_PRI_KEY_LEN) { + uint8_t* pub_key_buf = OPENSSL_malloc(SM2_PUB_KEY_LEN); + if (!pub_key_buf) { + (*env)->ReleaseByteArrayElements(env, key, key_bytes, JNI_ABORT); + (*env)->ReleaseByteArrayElements(env, id, id_bytes, JNI_ABORT); + return OPENSSL_FAILURE; + } + + if (!sm2_gen_pub_key((const uint8_t*)key_bytes, pub_key_buf)) { + OPENSSL_free(pub_key_buf); + (*env)->ReleaseByteArrayElements(env, key, key_bytes, JNI_ABORT); + (*env)->ReleaseByteArrayElements(env, id, id_bytes, JNI_ABORT); + return OPENSSL_FAILURE; + } + + pkey = sm2_load_key_pair((const uint8_t*)key_bytes, pub_key_buf); + OPENSSL_free(pub_key_buf); + } else if (key_len == SM2_PUB_KEY_LEN) { + pkey = sm2_load_pub_key((const uint8_t*)key_bytes, key_len); + } else if (key_len == (SM2_PRI_KEY_LEN + SM2_PUB_KEY_LEN)) { + uint8_t* pri_key_buf = OPENSSL_malloc(SM2_PRI_KEY_LEN); + uint8_t* pub_key_buf = OPENSSL_malloc(SM2_PUB_KEY_LEN); + if (!pri_key_buf || !pub_key_buf) { + OPENSSL_free(pri_key_buf); + OPENSSL_free(pub_key_buf); + (*env)->ReleaseByteArrayElements(env, key, key_bytes, JNI_ABORT); + (*env)->ReleaseByteArrayElements(env, id, id_bytes, JNI_ABORT); + return OPENSSL_FAILURE; + } + + memcpy(pri_key_buf, (const uint8_t*)key_bytes, SM2_PRI_KEY_LEN); + memcpy(pub_key_buf, (const uint8_t*)key_bytes + SM2_PRI_KEY_LEN, SM2_PUB_KEY_LEN); + + pkey = sm2_load_key_pair((const uint8_t*)pri_key_buf, pub_key_buf); + OPENSSL_free(pri_key_buf); + OPENSSL_free(pub_key_buf); + } + + if (pkey == NULL) { + (*env)->ReleaseByteArrayElements(env, key, key_bytes, JNI_ABORT); + (*env)->ReleaseByteArrayElements(env, id, id_bytes, JNI_ABORT); + return OPENSSL_FAILURE; + } + + SM2_SIGNATURE_CTX* ctx = sm2_create_md_ctx(pkey, (const uint8_t*)id_bytes, id_len, false); + if (ctx == NULL) { + (*env)->ReleaseByteArrayElements(env, key, key_bytes, JNI_ABORT); + (*env)->ReleaseByteArrayElements(env, id, id_bytes, JNI_ABORT); + EVP_PKEY_free(pkey); + return OPENSSL_FAILURE; + } + + (*env)->ReleaseByteArrayElements(env, key, key_bytes, JNI_ABORT); + (*env)->ReleaseByteArrayElements(env, id, id_bytes, JNI_ABORT); + + jsize msg_len = (*env)->GetArrayLength(env, message); + jbyte* msg_bytes = (*env)->GetByteArrayElements(env, message, NULL); + if (msg_bytes == NULL) { + sm2_signature_ctx_free(ctx); + return OPENSSL_FAILURE; + } + + jsize sig_len = (*env)->GetArrayLength(env, signature); + jbyte* sig_bytes = (*env)->GetByteArrayElements(env, signature, NULL); + if (sig_bytes == NULL) { + (*env)->ReleaseByteArrayElements(env, message, msg_bytes, JNI_ABORT); + sm2_signature_ctx_free(ctx); + return OPENSSL_FAILURE; + } + + int verified = sm2_verify(ctx->mctx, (uint8_t*)msg_bytes, msg_len, (uint8_t*)sig_bytes, sig_len) + ? OPENSSL_SUCCESS : OPENSSL_FAILURE; + + (*env)->ReleaseByteArrayElements(env, message, msg_bytes, JNI_ABORT); + (*env)->ReleaseByteArrayElements(env, signature, sig_bytes, JNI_ABORT); + + sm2_signature_ctx_free(ctx); + + return verified; +} diff --git a/kona-crypto/src/main/jni/kona_sm3.c b/kona-crypto/src/main/jni/kona_sm3.c index 9233153d..702aa30d 100644 --- a/kona-crypto/src/main/jni/kona_sm3.c +++ b/kona-crypto/src/main/jni/kona_sm3.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2024, THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2024, 2025, THL A29 Limited, a Tencent company. 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 @@ -192,6 +192,62 @@ JNIEXPORT jlong JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_NativeC return (jlong)new_ctx; } +JNIEXPORT jbyteArray JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto_sm3OneShotDigest + (JNIEnv* env, jclass classObj, jbyteArray data) { + EVP_MD_CTX* ctx = sm3_create_ctx(); + if (ctx == NULL) { + return NULL; + } + + if (data == NULL) { + EVP_MD_CTX_free(ctx); + return NULL; + } + + jsize data_len = (*env)->GetArrayLength(env, data); + if (data_len < 0) { + EVP_MD_CTX_free(ctx); + return NULL; + } + + jbyte* data_bytes = (*env)->GetByteArrayElements(env, data, NULL); + if (data_bytes == NULL) { + EVP_MD_CTX_free(ctx); + return NULL; + } + + if (!EVP_DigestUpdate(ctx, data_bytes, data_len)) { + (*env)->ReleaseByteArrayElements(env, data, data_bytes, JNI_ABORT); + EVP_MD_CTX_free(ctx); + return NULL; + } + + (*env)->ReleaseByteArrayElements(env, data, data_bytes, JNI_ABORT); + + uint8_t digest[SM3_DIGEST_LEN]; + unsigned int digest_len = 0; + + if (!EVP_DigestFinal_ex(ctx, digest, &digest_len)) { + EVP_MD_CTX_free(ctx); + return NULL; + } + + EVP_MD_CTX_free(ctx); + + if (digest_len <= 0) { + return NULL; + } + + jbyteArray digest_bytes = (*env)->NewByteArray(env, digest_len); + if (digest_bytes == NULL) { + return NULL; + } + + (*env)->SetByteArrayRegion(env, digest_bytes, 0, digest_len, (jbyte*)digest); + + return digest_bytes; +} + EVP_MAC_CTX* sm3hmac_create_ctx(EVP_MAC* mac, const uint8_t* key, size_t key_len) { if (mac == NULL) { return OPENSSL_FAILURE; @@ -357,3 +413,77 @@ JNIEXPORT jlong JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_NativeC return (jlong)new_ctx; } + +JNIEXPORT jbyteArray JNICALL Java_com_tencent_kona_crypto_provider_nativeImpl_NativeCrypto_sm3hmacOneShotMac + (JNIEnv* env, jclass classObj, jbyteArray key, jbyteArray data) { + EVP_MAC* mac = hmac(); + if (mac == NULL) { + return NULL; + } + + if (key == NULL) { + return NULL; + } + + const int key_len = (*env)->GetArrayLength(env, key); + if (key_len <= 0) { + return NULL; + } + jbyte* key_bytes = (*env)->GetByteArrayElements(env, key, NULL); + if (key_bytes == NULL) { + return NULL; + } + + EVP_MAC_CTX* ctx = sm3hmac_create_ctx(mac, (const uint8_t*)key_bytes, key_len); + + (*env)->ReleaseByteArrayElements(env, key, key_bytes, JNI_ABORT); + + if (ctx == NULL) { + return NULL; + } + + if (data == NULL) { + EVP_MAC_CTX_free(ctx); + return NULL; + } + + const int data_len = (*env)->GetArrayLength(env, data); + jbyte* data_bytes = (*env)->GetByteArrayElements(env, data, NULL); + if (data_bytes == NULL) { + EVP_MAC_CTX_free(ctx); + return NULL; + } + + if (!EVP_MAC_update(ctx, (const uint8_t*)data_bytes, data_len)) { + (*env)->ReleaseByteArrayElements(env, data, data_bytes, JNI_ABORT); + EVP_MAC_CTX_free(ctx); + return NULL; + } + + (*env)->ReleaseByteArrayElements(env, data, data_bytes, JNI_ABORT); + + uint8_t mac_buf[SM3_MAC_LEN]; + size_t mac_len = 0; + + if (!EVP_MAC_final(ctx, mac_buf, &mac_len, sizeof(mac_buf))) { + EVP_MAC_CTX_free(ctx); + return NULL; + } + + if (mac_len <= 0) { + EVP_MAC_CTX_free(ctx); + return NULL; + } + + jbyteArray result = (*env)->NewByteArray(env, mac_len); + if (result == NULL) { + EVP_MAC_CTX_free(ctx); + return NULL; + } + + (*env)->SetByteArrayRegion(env, result, 0, mac_len, (jbyte*)mac_buf); + + EVP_MAC_CTX_free(ctx); + + return result; +} diff --git a/kona-crypto/src/main/resources/libKonaCrypto-linux-aarch64.so b/kona-crypto/src/main/resources/libKonaCrypto-linux-aarch64.so index 69c97a0f7fd4aa1b0d6fc09a5c2643938498418e..08ff4c7c04aed86610091de59a9b2bbbaef60d91 100755 GIT binary patch delta 26752 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 b9W5-7b0|r8)aXP+k0C*?Z0U&YCrAX4bq8 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 P>6~v?LY;rNDS`hF&IXAb diff --git a/kona-crypto/src/main/resources/libKonaCrypto-linux-x86_64.so b/kona-crypto/src/main/resources/libKonaCrypto-linux-x86_64.so index 451f5d61b65e5055398fd3ae3ae6be0a54f215a8..c2640ebe3b456757dc2b55837e1cb89b048b14a3 100755 GIT binary patch literal 70288 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 z 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

H0pbO; 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 delta 21127 zcmZu(30xIb7oQmx5fL6Lf|&YHP(&jjHF7C%!6%~O8o4A|nwcps^^r>t6_poK=&dl@ zu*K9gQAmkOQFF=q3jNI8L^GYIR%m5r%lrQ4-aB}GHU4JK{r}Io+qvsJnOpp>@7iyD ziX)mnZ4heb*WjOORILyifb|d?;n1fKCU^tqDML3 zIv2h0xs(8{Q5S7c$BB-1M!>E+G%e6iE6@vkeVYaK)dsfJqZ{=}WIY><)noSf^leJH zJ&JoG?#Z|x$2|r26S(P`Duk8;IYWkLK|UqJB>p4A59J~-2lrgu^Kj3{ZCy?o$b(#n zdy&NXQZ9yED#I?wLfl2T$xq8mb_VNBqY%;>N=bN>nzl#Y36!_HsB_= ze}H=v?lRn4aBstXZ~j-=V}gB-yx3<fU`G>b} zSe`w-blcpJv0blqXZb#v?3hn-^zV^}Us;v8t-SdMO?+QH7~nFhUKrS?-#1rAFD+=a z!6)*qr5m0&!%}=>*;LK z*m%9E?#H!`#u54fUi$3Q{_NStak`tAx*EJcrO)abCpa%RK`gD2mhc=p0j`5WXyu`T z&s!phfi#+NJ%gLP!r;79fCL|?aKj)&+IFBnZNd#Y&6LUyyS~y&9qs9I4=1Yk+Dq+*HXM(T_l^VOg7m@Y5%tD6Lqq}n9xM; zdrFo_%M+G6WrX&!X|x9g;VWyFDqX707W^hT{w@L1L{wWOc)JkVT~^!6rkO4659!FC zTvXa$SX$%qy7WZZ1i{ac2`z&p`;Bm<%-Q3{ zY2`J|-}W!BY4sny{O2|!jPmPk8yn>#V%&OpQHR9_i@O$7J}vG`U#Fq16C}UV z>7`gHvG>YyoEivM@g_T$$d&JIIlyD3!lT6XSE<6!sb@89t>m4QPzyy-rbLKV_)3NE zpzzfSPwxTNwO`?_)r)jB3XgFsh6yQ-DT=3wkXNn3e`t}Arozjam22x1euH9vL*cXQ zdE_5aX6Fz+oxea>I=jNl;mK983U64(kO>Np#i{;ER(RifR?||5r}|UInDgr|hZ2FB z$GS2Vp5jBH&*y! zg)bAlX#bUp!g{+TeW}6+D+#Stcr04=SDC_Zs%JH=QsJ#-nbg%~PCGXvcZTwick1#B z@L>vXQ~2iKh5r$VP!!=x1iQjpb1XZnb75;|8->LB07CT3jPQ1bgDExB@Z>#5#zxAd;`iOdk*bi87 z>)VsqkGC`z(d;s_@wg-%a72 zlNH51iXunhlNCN!;qO)WJcaMB@C6FrL*a`B@8rYJ+MSYqr4pf+5}{P#t=%c9*DCz| zihY^Frzm`-!l%~z9|qNmLT=A^wEYS%cgLKsQTWYD2FDb>ufo?V{5p%L_BR#93`+r7 zr||Nvm}_q+e41jfmD@R@QP5xE0~CIe!rQ>}{znlUiXuXZP^j>Bg&&~su?nBA@CgdP zK;e^b@lI`^qDZ-=&@vR>q40wgK2zZbD}0v1KcMj03ZKb&IsPUqiYJr^ISM~h;d2#! zh{ES7{7{82Q21dAU+h#A!xhC!gXlp4Nr|@(Psg>>$Mc{bnX4LUGv2B#WSQ$3POY@lgeG| z!4=b;uuiy!<0QiLX5g;oIF7In;WCcf5~eo+cPYotNFwMBz+KFVCWL9{@6O{mkTBK9 zox`y~IDl|A$2U>_$@JFe&gA$S;XuME9A6^bh;RbO=Lpk=-EAlAj5$F>V2sa^I&GCA|O$nEA{1)M6giC>)o|sic zgb`894OS4Q{i!>TBtrahy)L4dJ@K$OF9z+X>fl+?_D({@gVjClRJi zoV%LiIKu4+mvP*da5Ul4zmUHtCXxu+#kq^QK@-BXdvoV;97wnW;T(<)!m)(2Ileg? zxFg|Aj;|4>&6Ycb<4c4)5l-Ov9AVm9x$RC)oFIZWRc;%{hX~VV%B^vHfH3Wq+;x9a zfp-y3AY99F72!m}H5_jtoJ6>q^EiHv z@I8ccI9^DYwkGauj^_}jEr~mmgZ2P!J10&MK?|(g#_=J-w6MB0jt>x~1=U@5llK9_w2-=MIj$m1>!G`b<1K`T z60YWWJz-k^++`fUMR+*!hX**Ziii<}i#c9FnAR>oSevohu#|sH(5zgj#4q;lh z+?gEDBuvYdJB8yZglU;_CvZH0u#?s&x1AHCh`_o84{$t`FfB)JjpKB};|SON&ier2 zY{Io1cPC8Ck-LWDB*L@|xvM#jBm4;AGLG8fg5jGsM`` zF{xwS6~B2rv}Hm09-1!7=Zj)#jzw1js%UN5VZ*~w)b$TUF^^nFzDFIyN zvN>FSttc+-i$dDu!yYjT{q%pNM}uTXHqB;8@O?^fiAZp%Owdj-kziq1kzgVOlSx#% zpkgIu2i0Ft$&yL{)mKmsNu_`~`#3LdmZUNrEnI$+rpW?NQc65v8k;yE%4k2WykNjJ z-5A%8%^TRO9sPeM%G-*B>nHOwG;oH?o#8s?D4G|Y3{As+?9{*q+onE-Bny+x)o`i9 zbtdto`5;U$9}H=O2^{{2dW?<8_}hq@#6}Fd&p11gy)-C(!sdx~tzY5~6fYRQa8E^p zszq9UOaJ4qJY6&GD(w2gt{92VfSw{BgfauA`-qf%pp=a|WMX;D;CRD`dZc_r=3w18 zIG(*bq?_@|cy@Y7nlWU2dCbtCb>p30EOK~`@la2;YcedMw6_jEah*8)#I*4yM zl}d#T8Ovfu78v0@*rt(7jpw_w)b4E5gZHonSq0;;BJZF@((HcG;~C)EOB?Tx z9Im~TXom|P&*JKk{1;G^4A*s{DqvMbi!R!t=3b(IbGY`H8$n-gioU#^(sH==nT042 zG%RIpICDPK*vP)Xo_{F35u)&3ZoYAWl|7Wov7NFXdV&41K!k(_{G1vxeS|F} zG}C6by1)`g^>aRrqGY%{RkRo(>la{0`A!F2MGF$Rx!_P7b2K>jc5D;j2I>tcbpWWu zYIh>Y;wsv+z&t~mcP8_{v8S>;4f|u?i{>vyB@plTzs4gQT8I%ZjK}kszrx175Ahs} zJ+lW6bGZI+6y8taAafN(-$$<)=r3~vh-_?*AcySh6Tn&5nJSvWklbDrXS93m_iW_o z*2d)T*wdq9jr%UJ($QfpFMdbvpFMCuVbAEQmBM@=%*|)dvl>``@Exlg-7@GQ3^viL zx0x(_Oy}-VAgG@%FsT+lCH_#gT12kI_RW0tJeukSrHlAoeNfmBG1>evdB&!5%rj=7 ze^=Ndhs?2al6oTw3i5E`^+B#WIUZeS%*$wW(EWM&z7turp=d+Tu|LN~I=2da(rAav z#}@mkUA42YK zEjd-0qQIfZd`AQ&cIFHcqx|;bFX=viEZI5wACW)jZT=KZH!lwpHM$Ne!*$X80Ifjj zPeS^M)tA>!;&YNTIw*}?WVGlrTVnO-dY8v5({8G?UrjkDRr5Di_^N&-35y~FSKsIW zM`3q!o=mli%=*gN@-36w>m9Bnph|YCQq^gUjADfNB!d2Y5GsmqGv7STJdaQ5@;_KO z3NxZ3s@{YdEYd9thwE!|55+GPibSsX$=rOJy*{O-GYC*jO(#xK&G&HEEy5U<)6?#g zh`SJR$)(#U4cCt}M-8Mj3q_jy@QQ@)BTCep7X@XzY_p~+vTsRkOlfpL+5;Xrl2JH2 zIv|nV^MuXl`YjvqME}-Zcq(6;6(~p*6Diw8%4X>)_Q4Y|cYcl@f#Gd~QW^9=Q_|L; zFYFHB~gcK(gv+U8$4D%H0Hnq#dx4@wL3{Jf6a9yHyUyV|O=;bJ! zuT`BCnRi8GGXV9s`X=p{V9z~s(VQltba%LpQ8(DlyUNwU7;z_QOg(UheLXe0`%Vx9 zqo`ZbB3gA`q+W^lPO}G6zx<{;#Fk6@tKYIapM2IxJjpgbIkrz#XBrh3&F&}Qyu$gm zeu>o>4~G)3n*~S#1#d?E5mP{Yhm_0Zj6ov%gQwY`Y3%}s%XAJOXLF{tX?C}8LkC*> zT|ZL4A98|ioYp$nonhC|zk;ZL_B+jvPupR1Im%W|PY=7n^Lr{BV`tAmk#@`}_WSgn zsr4OvQt^ny>S5S$MVP~o0a|_bV7Sm+a+F*^J#6-URY}4NRdMKl)ShPSQMTlnI|D;y zqBBmiPoIhDa^N`Pc+c`-@IrlqW0uFpi~4+fq&3>@ILeyONeQ}6o^tI;tTw+q%qGl9 z>@?sQcT{r+dPvdi=r&cDjx_Ub&OX%v)v=|E>g1eY+vh|-b`zyVN%svX92q;H@M$~Z znHL0C4lWrSwr4%eLxOt~oC916aoYt~2rdg;CUNUI7m_~RowbyK0!#X=#8#52F3}+u_Dpr z`Ne)LIB(LhSX4IEwU3+~gHo90*W{ZEn8C2{n9tqPHh`AqwW9}V5A;*@wGdlaqiFp3 zWQeWR<#r_Fz&+%5v>n@*7iE0bj$O*@FnBbQgGJxySUz9wy%y5BX3-_xy!$KBz@vSU zXEzw}Sr)s#UFI=lK*dRc%7NYIWVkM)FxO_@f0&J15No{q6>}|UXJ`l62Md}T0bjGd z3)&bLzhoB|v^KVV%^EC>GAh1gaSJ=dkvAx%aXh7Y(Y6fNHRNMHA~Ww#Jm!FQ|6_9( zCK=BEvP}!y#aGE#BcUBo_#l?7+0hYXw))F@S2dI|-#D~lQJnGC7p(K5j$uckr|cY1 z^8RtgjQ$^+vZ!O*7N{U;0x4SIylA_56h)vAHD#>%`B&`2MGK6VzhKGv_w*S1g|Pm? z(6lQ0i$o0d&26l;u?=$Jt7KI_p?VoAk&N$`Y<+&RF{g%|&+lk|K-QxcW>p!&wkai( zht6s)_=4TJxNDC&G8AA2%8<>}WGKSCzF*kc%_}nGFwe$O{jWIRCN!rCfF+a*&b#UAgCD+ zA7IZdiHd1)pQy+)XbJa4K8N`Ef$Nv(5$jnt6fBfAXCE&PX1^?H61=^awbb2VsUBYH z&k~k42rYuDKBD_1DphD4~Qv;NCIUfRRid>;z$J?V#17pY(6 z=i0=E|MFhWT04RWVp~+r6IHz45_N7t&fV9C_J(Mwxv?VNZWIb^tYi}eyOFc(se_JMr z1H6t7OQBeEk>u>AOL7sWQ-*9+8R+h4rX`Um$2xKd;sjI;5^N5!Y#)1mMR@r+SBOuW zxu3xTg9jDCz$(FO5!t8kH$`*sZe}ZLo}4Ca-~!lu0~@Z!8=u*7w}@`%stdPNKbhKY z_Ha=|(DvpSDh^c2?7W*TENX8oYtGge^*27=#eOcj%b2peJo@>mK2EHo3+}P=|5>tr zc8N$lgnG1Vigm(cZm32^SXM*u8%q`$F8VBj|2$I|_InZ_s=znJ!>K?Rhyj<6&})X= zI!`JdW=8IyqSDL4?B3>+yF_x72VSdP_nk5S00mtG+Lnnu*=i&#`ml%0IbWY76__%{ zf_HWoYxQco#MeXu5&X9SUcTE~89%@zA_hvzw?{?0*o;@38^7*iuMBHx9IIw)UL9p@ z*ujj~1_TaB6e+LU$ws`^BUq-KMZ+#-2YchS5rL}{goVD7{qf;&uRdlU_=8D?STp!_K@B)%QzM%Xh3i zG~)o*_lci7io!k$(Wskkg`kP+LrM}q1I3!15W#v4AG5x1-W50GE>YsmydCIQ zp>SAU-eLVJlqmJfcd*yrY~6mr$GkZj-|{A(IK!^S-*Vxg9qjCzc4KZ8^C@*0S3hNg zN*^?Ce8Sd1c(yZ7Y2UyFcZyukLb-ZC@DXH*MjlF@y!jD(XY~Vt2RjRk#80^=b3bD3 z-)ehzK6#S&H|l7&dJ-COJVmhj+b3iv`Wx%?PTI&eK^l~Mv4i}1O-M=g9a=ty4E@da zM_9AquwMQ!z}D=RcJU3a=<04n8zOY|fKO@nvX76bAjC8u{Dkd$XPmx`b$E9{*BgP} z@ehgkC!uhE|F<=_pntB86E)L6W~bk6owS?`-iHBg@#zKTC-cW`&hfgjTWtu1Usc zQ~vLbZh>U0=!a`WPid%Wy^DG#I3Dqs=PTHp_d4q3?49@e2f6SQr}**YVkP_Sy{Nz! z@dFJ_J69@L+qILzDv`!ZB8?W%I3BrS?ks1ouWhGqW;@rW1-{#W%zrcgDrceV`bVI$ z^sXB~??0)oo%p$geDLiSmb31WxYh0X(nI6<@2#jogs6d>kTKYy=YxGK`)gfV7)%g( zkjqT`97*vvs_UCs*7|{g2V;a`;#RhCeV$&%;@=+;cr{v(Lq23h?`P{DvLD}1L?!JT z9>6naLy!2++uiEcF%|OX!e%IgcpnQ}r|A7E*zpY=SdMPnvhhzJCw&%~PF`5&@#G>L zu*Kskg`5mYUj^)kyh41r$Adr5){a4DLuMhHm5^&8%Lsqy@$82zfUJc)2H6a~HW_uz zhKz;(sv##s){=z#@9?AFu&X?tJnV-fFc!XqoD69~IxyIhvE7Z>;ql}{7C=@)u7s?C ztb{xdnTv@t6HQfsxqBsK1lHz1A#))k(5?lLogr6ZH7ka!#9C=WuEh$S+|Y>{VtZ1I zPLzv9@K2K1+hn6p(k6T!_E z`~ew5W(;c;10r3HcdhGJfP9 zWy2;W3xR0JfNQ7`WF=$;G4lc4}-gpGnzIA@*N77c|5x{ z*19U3jjiZoD6CFv#1&+HUP`^etx(Z=GONSFl}M(~Z7cS@5<MB&-!|JC3lh{rV34wh9e*v_A%vMx(hd=QL=D2%e zP?%WPiVYUOEu8iGsL#EnpqgQv3?Z8|D)Ic?$b)C{33OS|0rD#w&l+&U!TGXQRjt^O zkM8sDf)S+cV3Acr1D7GVAA{;FIsOi|qAIT8DNvKKHn)a-6uVFr7f40zg3*<*lWqBU zl7A_Jvv;x9pFG^nm-5Blay%?VS&U&+7^<)xx<{e2%0e~l0+79jZTY0P|2zaE_OhFw z4D~-uCi~dXPw#6Ofw9;VgSHDBvL~8-`f1BRYQRUqChTV`w#T+@jE6&(x2)~#W0@_3%V#l8sf+B&bA}ozYM`&zGUC-NcYG0Gg|aF zEWWyV(j5qz-*`NzD!-^gH2%+nqteoqfx9obT5y{J*s4#XSV?uOI8sp~zX(;zw;sx9iUK z4oo{6!0@5aH`t!V3p|?o7XSN2`V*!vYgxD5X{{>|wBrYND}73EKe*1|R?8CYj`NSe z#I)=zyRf_aeOUPgF{XkGz=SvuVe-%rWH^v~fQ#C58T84}Z#CfDcI@-d+O#?b zY6E_~wlXHOE8r@@4di{Mkn7(SF3t97PkHpA=Uwl472u|kL zO8iF|UGNdVhA$Jcg%CbCurB8dx9UYF9J_j-6h%Jx!rlQK=pky1{Q@@XlTNcpmqYoy#N zEBCwK+5(~c9Swq%8|Xwo78kOjN{8#lf&tO*4Y}>sqBNpk%3k} z``}&Si%ov-zXPJjgdWe{4(^Uwk-dfE>5dkih_%$kLIgc z-S>9hPfOu*qsr39fBsror`tI~J{tOS=YQh^G}+_Pn#$#)HPo!yD>M=c)T(7wZs!|m zmOpOigEY$%xAXXlQu;vfGJhXT0@k@Bw?_!kj;&A`5%$6AXnn}2YJKpDl=UIw?R*o> z`fl+yexlY?vyMTzO@U34^&#W!Jf@X0yGrq}559V~K8RG6KKMj8qJphDdKXT!IB+!H z>Fq9>{0uCbb*XAJO^cGeHJMu4dl1lhqIOPMlW7{~|5^Jd!BhHja+Xsp$Y*#2awB{0 zSpUQdJ1{16{?xQRkd#fV7ypgq?G?;8{sQW>;&_^o`8GRoJl^TufX|?824-*Xp5O$p zYyb4)OPv4b*W;@>|IY@b@ACCq?RRScY=gr1m#+LNc=5$C+j1h!u#aLlPsB&kr`U8c zA+(RNTP3U7`KCDDn8Qt1Lq^yd{S1`>8$(d_(<>5LWW_5BX!vVw(vC8tO;z( z>9kbqFXpY!wwqxpAggRS;;hAgwB&F9G+)!k(di}nTzU>zE^WLIGN2cIO4IxJkIjT8Aj!DfAt>PH^q7QtN%J2R~aT zF${YQN79xeHrJi*IVOG~1&Ve|*+={FHJqE=4MuBKXm0enxJe9_D9*#2+`$8(0_Zb5Y@S(~C1{d;cn6$rjWQX9_NdC6d zRG|97vdr?sjReHAgR9bqUN%0l8`H`OEbr5Hz zE6LXl6@+y_puGrN$V_>S}jl1bf@&d85W(vp56I#G=CP*v&H*Ze*f+GkTTLH9)gz2Swp`!;F5Eoc3kqA69sP_uXK{9{1<<= zWi!lheK0*+Ou_#`Q99lS(L6)i#Pw3^eWpq^_>br}KWSi2rtWO&WOKX0t_-)_Et(CGUM!)+>^) zS#WzvzL&iBX(90~cx{{fdZ7wn<~B+czk8 z{8Fj=_ZyixZcyKn>ApA$uIJ5w^7+@l^I@BAwq(!#(uj5XV-EZ6kHqraKf465%p1NX zZXa~LKRPwuX7|(g`EKhKsF&;dhLWp|^t+l^uRNAa2`xzq(&L+0uRNB_ZdNiAe5m!x zW67M7%|ZHIfz}&OS*MaqL3+2q`Zt}bzwq9&j-dnPsu=PSyeQ(Jn z;3n42+>$S*mm~%2@qyO%b@;N9DZz+my};xxD_IX5YHfQhIi}=lFluSNwpenWtt2T# zzpLR!J9m1)#*!%^`aKpb-dM6;fh#wbTvA}^#*#Q2j5mq6#hXf|5G)f=D=S$~utGpv zMad-vMpTrMy>UJP#l diff --git a/kona-crypto/src/test/java/com/tencent/kona/crypto/TestUtils.java b/kona-crypto/src/test/java/com/tencent/kona/crypto/TestUtils.java index 9a6922e1..4eb543ac 100644 --- a/kona-crypto/src/test/java/com/tencent/kona/crypto/TestUtils.java +++ b/kona-crypto/src/test/java/com/tencent/kona/crypto/TestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022, 2024, THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, 2025, THL A29 Limited, a Tencent company. 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 @@ -45,13 +45,7 @@ public class TestUtils { public static final byte[] EMPTY = new byte[0]; public static void addProviders() { - if (PROVIDER instanceof KonaCryptoProvider) { - Security.addProvider(KonaCryptoProvider.instance()); - Security.addProvider(KonaCryptoNativeProvider.instance()); - } else { - Security.addProvider(KonaCryptoNativeProvider.instance()); - Security.addProvider(KonaCryptoProvider.instance()); - } + Security.addProvider(PROVIDER); } public static void repeatTaskParallelly(Callable task, int count) diff --git a/kona-crypto/src/test/java/com/tencent/kona/crypto/provider/nativeImpl/NativeRefProfTest.java b/kona-crypto/src/test/java/com/tencent/kona/crypto/provider/nativeImpl/NativeRefProfTest.java index 054b8402..d15aead1 100644 --- a/kona-crypto/src/test/java/com/tencent/kona/crypto/provider/nativeImpl/NativeRefProfTest.java +++ b/kona-crypto/src/test/java/com/tencent/kona/crypto/provider/nativeImpl/NativeRefProfTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2024, THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2024, 2025, THL A29 Limited, a Tencent company. 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 @@ -42,7 +42,7 @@ public class NativeRefProfTest { private static final int ITERATIONS = 1_000_000_000; - private static void testSM3() { + private static void testSM3Digest() { for (int i = 0; i < ITERATIONS; i++) { NativeSM3 sm3 = new NativeSM3(); sm3.update(DATA); @@ -51,6 +51,12 @@ private static void testSM3() { } } + private static void testSM3OneShotDigest() { + for (int i = 0; i < ITERATIONS; i++) { + NativeCrypto.sm3OneShotDigest(DATA); + } + } + private static void testSM3HMac() { for (int i = 0; i < ITERATIONS; i++) { NativeSM3HMac sm3HMac = new NativeSM3HMac(KEY); @@ -60,6 +66,15 @@ private static void testSM3HMac() { } } + private static void testSM3OneShotHMac() { + for (int i = 0; i < ITERATIONS; i++) { + NativeSM3HMac sm3HMac = new NativeSM3HMac(KEY); + sm3HMac.update(DATA); + sm3HMac.doFinal(DATA); + sm3HMac.close(); + } + } + private static void testSM4CBCEncrypter() { for (int i = 0; i < ITERATIONS; i++) { NativeSM4 sm4 = new NativeSM4.SM4CBC(true, false, KEY, IV); @@ -143,6 +158,12 @@ private static void testSM2KeyPairGen() { } } + private static void testSM2OneShotKeyPairGen() { + for (int i = 0; i < ITERATIONS; i++) { + NativeCrypto.sm2OneShotKeyPairGenGenKeyPair(); + } + } + private static void testSM2CipherEncrypter() throws Exception { NativeSM2KeyPairGen sm2KeyPairGen = new NativeSM2KeyPairGen(); byte[] keyPair = sm2KeyPairGen.genKeyPair(); @@ -155,6 +176,16 @@ private static void testSM2CipherEncrypter() throws Exception { } } + private static void testSM2OneShotCipherEncrypter() throws Exception { + NativeSM2KeyPairGen sm2KeyPairGen = new NativeSM2KeyPairGen(); + byte[] keyPair = sm2KeyPairGen.genKeyPair(); + sm2KeyPairGen.close(); + + for (int i = 0; i < ITERATIONS; i++) { + NativeCrypto.sm2OneShotCipherEncrypt(keyPair, DATA); + } + } + private static void testSM2CipherDecrypter() throws Exception { NativeSM2KeyPairGen sm2KeyPairGen = new NativeSM2KeyPairGen(); byte[] keyPair = sm2KeyPairGen.genKeyPair(); @@ -171,6 +202,20 @@ private static void testSM2CipherDecrypter() throws Exception { } } + private static void testSM2OneShotCipherDecrypter() throws Exception { + NativeSM2KeyPairGen sm2KeyPairGen = new NativeSM2KeyPairGen(); + byte[] keyPair = sm2KeyPairGen.genKeyPair(); + sm2KeyPairGen.close(); + + NativeSM2Cipher sm2Encrypter = new NativeSM2Cipher(keyPair); + byte[] ciphertext = sm2Encrypter.encrypt(DATA); + sm2Encrypter.close(); + + for (int i = 0; i < ITERATIONS; i++) { + NativeCrypto.sm2OneShotCipherDecrypt(keyPair, ciphertext); + } + } + private static void testSM2SignatureSign() throws Exception { NativeSM2KeyPairGen sm2KeyPairGen = new NativeSM2KeyPairGen(); byte[] keyPair = sm2KeyPairGen.genKeyPair(); @@ -186,6 +231,18 @@ private static void testSM2SignatureSign() throws Exception { } } + private static void testSM2OneShotSignatureSign() throws Exception { + NativeSM2KeyPairGen sm2KeyPairGen = new NativeSM2KeyPairGen(); + byte[] keyPair = sm2KeyPairGen.genKeyPair(); + byte[] priKey = copy(keyPair, 0, SM2_PRIKEY_LEN); + byte[] pubKey = copy(keyPair, SM2_PRIKEY_LEN, SM2_PUBKEY_LEN); + sm2KeyPairGen.close(); + + for (int i = 0; i < ITERATIONS; i++) { + NativeCrypto.sm2OneShotSignatureSign(keyPair, priKey, pubKey); + } + } + private static void testSM2SignatureVerify() throws Exception { NativeSM2KeyPairGen sm2KeyPairGen = new NativeSM2KeyPairGen(); byte[] keyPair = sm2KeyPairGen.genKeyPair(); @@ -205,6 +262,22 @@ private static void testSM2SignatureVerify() throws Exception { } } + private static void testSM2OneShotSignatureVerify() throws Exception { + NativeSM2KeyPairGen sm2KeyPairGen = new NativeSM2KeyPairGen(); + byte[] keyPair = sm2KeyPairGen.genKeyPair(); + sm2KeyPairGen.close(); + byte[] priKey = copy(keyPair, 0, SM2_PRIKEY_LEN); + byte[] pubKey = copy(keyPair, SM2_PRIKEY_LEN, SM2_PUBKEY_LEN); + + NativeSM2Signature sm2Signer + = new NativeSM2Signature(priKey, pubKey, ID, true); + byte[] signature = sm2Signer.sign(DATA); + + for (int i = 0; i < ITERATIONS; i++) { + NativeCrypto.sm2OneShotSignatureVerify(keyPair, priKey, pubKey, signature); + } + } + private static void testSM2KeyAgreement() throws Exception { NativeSM2KeyPairGen sm2KeyPairGen = new NativeSM2KeyPairGen(); byte[] keyPair = sm2KeyPairGen.genKeyPair(); @@ -231,11 +304,38 @@ private static void testSM2KeyAgreement() throws Exception { } } + private static void testSM2OneShotKeyAgreement() throws Exception { + NativeSM2KeyPairGen sm2KeyPairGen = new NativeSM2KeyPairGen(); + byte[] keyPair = sm2KeyPairGen.genKeyPair(); + byte[] priKey = copy(keyPair, 0, SM2_PRIKEY_LEN); + byte[] pubKey = copy(keyPair, SM2_PRIKEY_LEN, SM2_PUBKEY_LEN); + + byte[] eKeyPair = sm2KeyPairGen.genKeyPair(); + byte[] ePriKey = copy(eKeyPair, 0, SM2_PRIKEY_LEN); + + byte[] peerKeyPair = sm2KeyPairGen.genKeyPair(); + byte[] peerPubKey = copy(peerKeyPair, SM2_PRIKEY_LEN, SM2_PUBKEY_LEN); + + byte[] peerEKeyPair = sm2KeyPairGen.genKeyPair(); + byte[] peerEPubKey = copy(peerEKeyPair, SM2_PRIKEY_LEN, SM2_PUBKEY_LEN); + sm2KeyPairGen.close(); + + for (int i = 0; i < ITERATIONS; i++) { + NativeCrypto.sm2OneShotDeriveKey( + priKey, pubKey, ePriKey, ID, + peerPubKey, peerEPubKey, ID, + true, 32); + } + } + public static void main(String[] args) throws Exception { List> tasks = new ArrayList<>(); - tasks.add(()-> {testSM3(); return null;}); + tasks.add(()-> {testSM3Digest(); return null;}); + tasks.add(()-> {testSM3OneShotDigest(); return null;}); + tasks.add(()-> {testSM3HMac(); return null;}); + tasks.add(()-> {testSM3OneShotHMac(); return null;}); tasks.add(()-> {testSM4CBCEncrypter(); return null;}); tasks.add(()-> {testSM4CBCDecrypter(); return null;}); @@ -250,14 +350,20 @@ public static void main(String[] args) throws Exception { tasks.add(()-> {testSM4GCMDecrypter(); return null;}); tasks.add(()-> {testSM2KeyPairGen(); return null;}); + tasks.add(()-> {testSM2OneShotKeyPairGen(); return null;}); tasks.add(()-> {testSM2CipherEncrypter(); return null;}); tasks.add(()-> {testSM2CipherDecrypter(); return null;}); + tasks.add(()-> {testSM2OneShotCipherEncrypter(); return null;}); + tasks.add(()-> {testSM2OneShotCipherDecrypter(); return null;}); tasks.add(()-> {testSM2SignatureSign(); return null;}); tasks.add(()-> {testSM2SignatureVerify(); return null;}); + tasks.add(()-> {testSM2OneShotSignatureSign(); return null;}); + tasks.add(()-> {testSM2OneShotSignatureVerify(); return null;}); tasks.add(()-> {testSM2KeyAgreement(); return null;}); + tasks.add(()-> {testSM2OneShotKeyAgreement(); return null;}); execTasksParallelly(tasks); } diff --git a/kona-crypto/src/test/java/com/tencent/kona/crypto/provider/nativeImpl/NativeSM2Test.java b/kona-crypto/src/test/java/com/tencent/kona/crypto/provider/nativeImpl/NativeSM2Test.java index 8ff09319..b66b2dae 100644 --- a/kona-crypto/src/test/java/com/tencent/kona/crypto/provider/nativeImpl/NativeSM2Test.java +++ b/kona-crypto/src/test/java/com/tencent/kona/crypto/provider/nativeImpl/NativeSM2Test.java @@ -29,6 +29,7 @@ import static com.tencent.kona.crypto.CryptoUtils.*; import static com.tencent.kona.crypto.CryptoUtils.toBytes; +import static com.tencent.kona.crypto.provider.nativeImpl.NativeCrypto.OPENSSL_SUCCESS; import static com.tencent.kona.crypto.util.Constants.*; /** @@ -134,6 +135,12 @@ public void testSM2KeyPairGenGenKeyPair() { } } + @Test + public void testSM2OneShotKeyPairGenGenKeyPair() { + byte[] keyPair = NativeCrypto.sm2OneShotKeyPairGenGenKeyPair(); + Assertions.assertEquals(SM2_PRIKEY_LEN + SM2_PUBKEY_LEN, keyPair.length); + } + @Test public void testSM2KeyPairGenGenKeyPairParallelly() throws Exception { TestUtils.repeatTaskParallelly(() -> { @@ -142,6 +149,14 @@ public void testSM2KeyPairGenGenKeyPairParallelly() throws Exception { }); } + @Test + public void testSM2OneShotKeyPairGenGenKeyPairParallelly() throws Exception { + TestUtils.repeatTaskParallelly(() -> { + testSM2OneShotKeyPairGenGenKeyPair(); + return null; + }); + } + @Test public void testSM2KeyPairGenGenKeyPairSerially() throws Exception { TestUtils.repeatTaskSerially(() -> { @@ -150,6 +165,14 @@ public void testSM2KeyPairGenGenKeyPairSerially() throws Exception { }); } + @Test + public void testSM2OneShotKeyPairGenGenKeyPairSerially() throws Exception { + TestUtils.repeatTaskSerially(() -> { + testSM2OneShotKeyPairGenGenKeyPair(); + return null; + }); + } + @Test public void testSM2KeyPairGenUseClosedRef() { NativeSM2KeyPairGen sm2KeyPairGen = new NativeSM2KeyPairGen(); @@ -188,6 +211,17 @@ public void testSM2Cipher() throws Exception { } } + @Test + public void testSM2OneShotCipher() throws Exception { + byte[] keyPair = NativeCrypto.sm2OneShotKeyPairGenGenKeyPair(); + byte[] priKey = copy(keyPair, 0, SM2_PRIKEY_LEN); + byte[] pubKey = copy(keyPair, SM2_PRIKEY_LEN, SM2_PUBKEY_LEN); + + byte[] ciphertext = NativeCrypto.sm2OneShotCipherEncrypt(pubKey, MESSAGE); + byte[] cleartext = NativeCrypto.sm2OneShotCipherDecrypt(priKey, ciphertext); + Assertions.assertArrayEquals(MESSAGE, cleartext); + } + @Test public void testSM2CipherWithKeyPair() throws Exception { try (NativeSM2KeyPairGen sm2KeyPairGen = new NativeSM2KeyPairGen()) { @@ -213,6 +247,14 @@ public void testSM2CipherParallelly() throws Exception { }); } + @Test + public void testSM2OneShotCipherParallelly() throws Exception { + TestUtils.repeatTaskParallelly(() -> { + testSM2OneShotCipher(); + return null; + }); + } + @Test public void testSM2CipherSerially() throws Exception { TestUtils.repeatTaskSerially(() -> { @@ -221,6 +263,14 @@ public void testSM2CipherSerially() throws Exception { }); } + @Test + public void testSM2OneShotCipherSerially() throws Exception { + TestUtils.repeatTaskSerially(() -> { + testSM2OneShotCipher(); + return null; + }); + } + @Test public void testSM2CipherEmptyInput() { try (NativeSM2KeyPairGen sm2KeyPairGen = new NativeSM2KeyPairGen()) { @@ -310,6 +360,19 @@ public void testSM2Signature() throws Exception { } } + @Test + public void testSM2OneShotSignature() { + byte[] keyPair = NativeCrypto.sm2OneShotKeyPairGenGenKeyPair(); + byte[] priKey = copy(keyPair, 0, SM2_PRIKEY_LEN); + byte[] pubKey = copy(keyPair, SM2_PRIKEY_LEN, SM2_PUBKEY_LEN); + + byte[] signature = NativeCrypto.sm2OneShotSignatureSign( + priKey, ID, MESSAGE); + int verified = NativeCrypto.sm2OneShotSignatureVerify( + pubKey, ID, MESSAGE, signature); + Assertions.assertEquals(OPENSSL_SUCCESS, verified); + } + @Test public void testSM2SignatureWithKeyPair() throws Exception { try (NativeSM2KeyPairGen sm2KeyPairGen = new NativeSM2KeyPairGen()) { @@ -339,6 +402,14 @@ public void testSM2SignatureParallelly() throws Exception { }); } + @Test + public void testSM2OneShotSignatureParallelly() throws Exception { + TestUtils.repeatTaskParallelly(() -> { + testSM2OneShotSignature(); + return null; + }); + } + @Test public void testSM2SignatureSerially() throws Exception { TestUtils.repeatTaskSerially(() -> { @@ -347,6 +418,14 @@ public void testSM2SignatureSerially() throws Exception { }); } + @Test + public void testSM2OneShotSignatureSerially() throws Exception { + TestUtils.repeatTaskSerially(() -> { + testSM2OneShotSignature(); + return null; + }); + } + @Test public void testSM2SignatureEmptyInput() throws Exception { try (NativeSM2KeyPairGen sm2KeyPairGen = new NativeSM2KeyPairGen()) { @@ -471,6 +550,16 @@ public void testSM2KeyAgreement() { } } + @Test + public void testSM2OneShotKeyAgreement() { + byte[] sharedKey = NativeCrypto.sm2OneShotDeriveKey( + PRI_KEY, PUB_KEY, E_PRI_KEY, ID, + PEER_PUB_KEY, PEER_E_PUB_KEY, PEER_ID, + true, 16); + + Assertions.assertArrayEquals(SHARED_KEY, sharedKey); + } + @Test public void testSM2KeyAgreementWithKeyPair() { try (NativeSM2KeyPairGen sm2KeyPairGen = new NativeSM2KeyPairGen()) { @@ -507,6 +596,14 @@ public void testSM2KeyAgreementParallelly() throws Exception { }); } + @Test + public void testSM2OneShotKeyAgreementParallelly() throws Exception { + TestUtils.repeatTaskParallelly(() -> { + testSM2OneShotKeyAgreement(); + return null; + }); + } + @Test public void testSM2KeyAgreementSerially() throws Exception { TestUtils.repeatTaskSerially(() -> { @@ -515,6 +612,14 @@ public void testSM2KeyAgreementSerially() throws Exception { }); } + @Test + public void testSM2OneShotKeyAgreementSerially() throws Exception { + TestUtils.repeatTaskSerially(() -> { + testSM2OneShotKeyAgreement(); + return null; + }); + } + @Test public void testSM2KeyAgreementUseClosedRef() { try (NativeSM2KeyPairGen sm2KeyPairGen = new NativeSM2KeyPairGen()) { diff --git a/kona-crypto/src/test/java/com/tencent/kona/crypto/provider/nativeImpl/NativeSM3HMacTest.java b/kona-crypto/src/test/java/com/tencent/kona/crypto/provider/nativeImpl/NativeSM3HMacTest.java index 5243de80..11313d5c 100644 --- a/kona-crypto/src/test/java/com/tencent/kona/crypto/provider/nativeImpl/NativeSM3HMacTest.java +++ b/kona-crypto/src/test/java/com/tencent/kona/crypto/provider/nativeImpl/NativeSM3HMacTest.java @@ -48,6 +48,12 @@ public void testMac() { } } + @Test + public void testOneShotMac() { + byte[] mac = NativeCrypto.sm3hmacOneShotMac(KEY, MESSAGE); + Assertions.assertArrayEquals(MAC, mac); + } + @Test public void testUpdate() { try(NativeSM3HMac sm3hmac = new NativeSM3HMac(KEY)) { diff --git a/kona-crypto/src/test/java/com/tencent/kona/crypto/provider/nativeImpl/NativeSM3Test.java b/kona-crypto/src/test/java/com/tencent/kona/crypto/provider/nativeImpl/NativeSM3Test.java index 0820cd6d..ac51881d 100644 --- a/kona-crypto/src/test/java/com/tencent/kona/crypto/provider/nativeImpl/NativeSM3Test.java +++ b/kona-crypto/src/test/java/com/tencent/kona/crypto/provider/nativeImpl/NativeSM3Test.java @@ -58,6 +58,17 @@ private static void checkDigest(byte[] message, byte[] expectedDigest) { } } + @Test + public void testOneShotKAT() { + checkOneShotDigest(MESSAGE_SHORT, DIGEST_SHORT); + checkOneShotDigest(MESSAGE_LONG, DIGEST_LONG); + } + + private static void checkOneShotDigest(byte[] message, byte[] expectedDigest) { + byte[] digest = NativeCrypto.sm3OneShotDigest(message); + Assertions.assertArrayEquals(expectedDigest, digest); + } + @Test public void testUpdate() { try(NativeSM3 sm3 = new NativeSM3()) { diff --git a/kona-provider/src/main/java/com/tencent/kona/KonaProvider.java b/kona-provider/src/main/java/com/tencent/kona/KonaProvider.java index a63ef02e..8eaf6577 100644 --- a/kona-provider/src/main/java/com/tencent/kona/KonaProvider.java +++ b/kona-provider/src/main/java/com/tencent/kona/KonaProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022, 2024, THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2022, 2025, THL A29 Limited, a Tencent company. 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 @@ -60,8 +60,19 @@ public KonaProvider() { } private static void putEntries(Provider provider) { + String defaultCrypto = KonaUtils.defaultCrypto(); + + String defaultCryptoProvider; + if ("Native".equalsIgnoreCase(defaultCrypto)) { + defaultCryptoProvider = "com.tencent.kona.crypto.KonaCryptoNativeProvider"; + } else if ("NativeOneShot".equalsIgnoreCase(defaultCrypto)) { + defaultCryptoProvider = "com.tencent.kona.crypto.KonaCryptoNativeOneShotProvider"; + } else { + defaultCryptoProvider = "com.tencent.kona.crypto.KonaCryptoProvider"; + } + try { - putEntries("com.tencent.kona.crypto.KonaCryptoProvider", provider); + putEntries(defaultCryptoProvider, provider); putEntries("com.tencent.kona.pkix.KonaPKIXProvider", provider); putEntries("com.tencent.kona.ssl.KonaSSLProvider", provider); } catch (Exception e) { @@ -86,6 +97,11 @@ private static void putEntries(String providerClass, Provider provider) } } + public static String privilegedGetProperty(String key, String def) { + return AccessController.doPrivileged( + (PrivilegedAction) () -> System.getProperty(key, def)); + } + private static String privilegedSetProperty(String key, String value) { return AccessController.doPrivileged( (PrivilegedAction) () -> System.setProperty(key, value)); diff --git a/kona-provider/src/main/java/com/tencent/kona/KonaUtils.java b/kona-provider/src/main/java/com/tencent/kona/KonaUtils.java new file mode 100644 index 00000000..8477350c --- /dev/null +++ b/kona-provider/src/main/java/com/tencent/kona/KonaUtils.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2025, THL A29 Limited, a Tencent company. 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 as + * published by the Free Software Foundation. THL A29 Limited designates + * this particular file as subject to the "Classpath" exception as provided + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package com.tencent.kona; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Locale; +import java.util.regex.Pattern; + +public class KonaUtils { + + private static final String OS = privilegedGetProperty("os.name"); + private static final String ARCH = privilegedGetProperty("os.arch"); + + private static final String JDK_VERSION = privilegedGetProperty( + "java.specification.version"); + + private static final String JDK_VENDOR = privilegedGetProperty( + "java.specification.vendor"); + + // Java, Native or NativeOneShot + private static final String DEFAULT_CRYPTO = privilegedGetProperty( + "com.tencent.kona.defaultCrypto", "Java"); + + public static String privilegedGetProperty(String key, String def) { + return AccessController.doPrivileged( + (PrivilegedAction) () -> System.getProperty(key, def)); + } + + public static String privilegedGetProperty(String key) { + return privilegedGetProperty(key, null); + } + + public static Boolean privilegedGetBoolProperty(String key, String def) { + return AccessController.doPrivileged( + (PrivilegedAction) () -> Boolean.parseBoolean( + System.getProperty(key, def))); + } + + public static Boolean privilegedGetBoolProperty(String key) { + return privilegedGetBoolProperty(key, "false"); + } + + public static boolean isJdk8() { + return JDK_VERSION.equals("1.8"); + } + + public static boolean isJdk11() { + return JDK_VERSION.equals("11"); + } + + public static boolean isJdk17() { + return JDK_VERSION.equals("17"); + } + + public static boolean isJdk21() { + return JDK_VERSION.equals("21"); + } + + public static boolean isAndroid() { + return JDK_VENDOR.contains("Android"); + } + + public static String defaultCrypto() { + return isLinux() && !isAndroid() ? DEFAULT_CRYPTO : "Java"; + } + + public static boolean isLinux() { + return isOs("linux"); + } + + public static boolean isMac() { + return isOs("mac"); + } + + private static boolean isOs(String osname) { + return OS.toLowerCase(Locale.ENGLISH).startsWith( + osname.toLowerCase(Locale.ENGLISH)); + } + + public static boolean isX64() { + return isArch("(amd64)|(x86_64)"); + } + + public static boolean isArm64() { + return isArch("aarch64"); + } + + private static boolean isArch(String arch) { + return Pattern.compile(arch, Pattern.CASE_INSENSITIVE) + .matcher(ARCH) + .matches(); + } +} diff --git a/kona-ssl/src/test/java/com/tencent/kona/ssl/interop/JdkProcClient.java b/kona-ssl/src/test/java/com/tencent/kona/ssl/interop/JdkProcClient.java index 5c25ccbc..f9cb793c 100644 --- a/kona-ssl/src/test/java/com/tencent/kona/ssl/interop/JdkProcClient.java +++ b/kona-ssl/src/test/java/com/tencent/kona/ssl/interop/JdkProcClient.java @@ -199,8 +199,7 @@ public Jdk getProduct() { public void connect(String host, int port) throws IOException { props.put(JdkProcUtils.PROP_HOST, host); props.put(JdkProcUtils.PROP_PORT, port + ""); - props.put(JdkProcUtils.PROP_USE_NATIVE_CRYPTO, - CryptoUtils.useNativeCrypto() + ""); + props.put(JdkProcUtils.PROP_DEFAULT_CRYPTO, CryptoUtils.defaultCrypto()); process = JdkProcUtils.java(jdk, Collections.emptyList(), getClass(), props, getLogPath()); diff --git a/kona-ssl/src/test/java/com/tencent/kona/ssl/interop/JdkProcServer.java b/kona-ssl/src/test/java/com/tencent/kona/ssl/interop/JdkProcServer.java index 19b0396b..9493d3be 100644 --- a/kona-ssl/src/test/java/com/tencent/kona/ssl/interop/JdkProcServer.java +++ b/kona-ssl/src/test/java/com/tencent/kona/ssl/interop/JdkProcServer.java @@ -214,8 +214,7 @@ public boolean isAlive() { @Override public void accept() throws IOException { - props.put(JdkProcUtils.PROP_USE_NATIVE_CRYPTO, - CryptoUtils.useNativeCrypto() + ""); + props.put(JdkProcUtils.PROP_DEFAULT_CRYPTO, CryptoUtils.defaultCrypto()); process = JdkProcUtils.java(jdk, Collections.emptyList(), getClass(), props, getLogPath()); diff --git a/kona-ssl/src/test/java/com/tencent/kona/ssl/interop/JdkProcUtils.java b/kona-ssl/src/test/java/com/tencent/kona/ssl/interop/JdkProcUtils.java index b704de6f..448b22f5 100644 --- a/kona-ssl/src/test/java/com/tencent/kona/ssl/interop/JdkProcUtils.java +++ b/kona-ssl/src/test/java/com/tencent/kona/ssl/interop/JdkProcUtils.java @@ -58,7 +58,7 @@ public class JdkProcUtils { public static final String PROP_MESSAGE = "test.message"; public static final String PROP_READ_RESPONSE = "test.read.response"; - public static final String PROP_USE_NATIVE_CRYPTO = "com.tencent.kona.useNativeCrypto"; + public static final String PROP_DEFAULT_CRYPTO = "com.tencent.kona.defaultCrypto"; /* * Converts a Cert instance to a string, which contains the field values of