From 40ec0fea381a6538f5498b9fb75706ef9fa79f59 Mon Sep 17 00:00:00 2001 From: J08nY Date: Tue, 16 Oct 2018 00:57:24 +0200 Subject: Add support for BoringSSL. --- src/cz/crcs/ectester/standalone/ECTesterStandalone.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/cz/crcs/ectester/standalone/ECTesterStandalone.java') diff --git a/src/cz/crcs/ectester/standalone/ECTesterStandalone.java b/src/cz/crcs/ectester/standalone/ECTesterStandalone.java index 1d7b821..cb6728b 100644 --- a/src/cz/crcs/ectester/standalone/ECTesterStandalone.java +++ b/src/cz/crcs/ectester/standalone/ECTesterStandalone.java @@ -66,7 +66,7 @@ import java.util.stream.Collectors; * @version v0.3.0 */ public class ECTesterStandalone { - private ProviderECLibrary[] libs = new ProviderECLibrary[]{new SunECLib(), new BouncyCastleLib(), new TomcryptLib(), new BotanLib(), new CryptoppLib(), new OpensslLib(), new MscngLib()}; + private ProviderECLibrary[] libs = new ProviderECLibrary[]{new SunECLib(), new BouncyCastleLib(), new TomcryptLib(), new BotanLib(), new CryptoppLib(), new OpensslLib(), new BoringsslLib(), new MscngLib()}; private Config cfg; private Options opts = new Options(); -- cgit v1.2.3-70-g09d2 From 030629076791f517622f7fc8fcfcf51e10f6e063 Mon Sep 17 00:00:00 2001 From: J08nY Date: Wed, 17 Oct 2018 00:23:07 +0200 Subject: Add libgcrypt support, only keygen first. --- build-standalone.xml | 5 + .../ectester/standalone/ECTesterStandalone.java | 2 +- .../crcs/ectester/standalone/libs/GcryptLib.java | 20 ++ src/cz/crcs/ectester/standalone/libs/jni/Makefile | 71 ++++-- .../standalone/libs/jni/NativeECPrivateKey.java | 6 + .../standalone/libs/jni/NativeECPublicKey.java | 6 + .../libs/jni/NativeKeyPairGeneratorSpi.java | 18 ++ .../standalone/libs/jni/NativeProvider.java | 10 + .../crcs/ectester/standalone/libs/jni/boringssl.c | 2 - src/cz/crcs/ectester/standalone/libs/jni/gcrypt.c | 284 +++++++++++++++++++++ src/cz/crcs/ectester/standalone/libs/jni/native.h | 105 ++++++++ src/cz/crcs/ectester/standalone/libs/jni/openssl.c | 2 - 12 files changed, 501 insertions(+), 30 deletions(-) create mode 100644 src/cz/crcs/ectester/standalone/libs/GcryptLib.java create mode 100644 src/cz/crcs/ectester/standalone/libs/jni/gcrypt.c (limited to 'src/cz/crcs/ectester/standalone/ECTesterStandalone.java') diff --git a/build-standalone.xml b/build-standalone.xml index c141509..84f4595 100644 --- a/build-standalone.xml +++ b/build-standalone.xml @@ -164,6 +164,11 @@ + + + + + diff --git a/src/cz/crcs/ectester/standalone/ECTesterStandalone.java b/src/cz/crcs/ectester/standalone/ECTesterStandalone.java index cb6728b..cc0e465 100644 --- a/src/cz/crcs/ectester/standalone/ECTesterStandalone.java +++ b/src/cz/crcs/ectester/standalone/ECTesterStandalone.java @@ -66,7 +66,7 @@ import java.util.stream.Collectors; * @version v0.3.0 */ public class ECTesterStandalone { - private ProviderECLibrary[] libs = new ProviderECLibrary[]{new SunECLib(), new BouncyCastleLib(), new TomcryptLib(), new BotanLib(), new CryptoppLib(), new OpensslLib(), new BoringsslLib(), new MscngLib()}; + private ProviderECLibrary[] libs = new ProviderECLibrary[]{new SunECLib(), new BouncyCastleLib(), new TomcryptLib(), new BotanLib(), new CryptoppLib(), new OpensslLib(), new BoringsslLib(), new GcryptLib(), new MscngLib()}; private Config cfg; private Options opts = new Options(); diff --git a/src/cz/crcs/ectester/standalone/libs/GcryptLib.java b/src/cz/crcs/ectester/standalone/libs/GcryptLib.java new file mode 100644 index 0000000..a0a7fc8 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/libs/GcryptLib.java @@ -0,0 +1,20 @@ +package cz.crcs.ectester.standalone.libs; + +import java.security.Provider; +import java.util.Set; + +/** + * @author Jan Jancar johny@neuromancer.sk + */ +public class GcryptLib extends NativeECLibrary { + + public GcryptLib() { + super("gcrypt_provider", "gcrypt", "gpg-error"); + } + + @Override + native Provider createProvider(); + + @Override + public native Set getCurves(); +} diff --git a/src/cz/crcs/ectester/standalone/libs/jni/Makefile b/src/cz/crcs/ectester/standalone/libs/jni/Makefile index 189a6dd..2232e3d 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/Makefile +++ b/src/cz/crcs/ectester/standalone/libs/jni/Makefile @@ -1,3 +1,28 @@ +############################################################################### +## General CC setup. + +CC?=gcc +CXX?=g++ + +LFLAGS+=-fPIC -shared +CFLAGS+=-fPIC -I"$(JNI_INCLUDEDIR)" -I"$(JNI_PLATFORMINCLUDEDIR)" -I. +CXXFLAGS+=-fPIC -I"$(JNI_INCLUDEDIR)" -I"$(JNI_PLATFORMINCLUDEDIR)" -I. + +DEBUG ?= 0 + +ifeq ($(DEBUG), 1) + CFLAGS+=-g -Wall + LFLAGS+=-g + CXXFLAGS+=-g -Wall +else + CFLAGS+=-O2 + LFLAGS+=-O2 + CXXFLAGS+=-O2 +endif + +############################################################################### +## Java JNI setup. + ifeq ($(JAVA_HOME),) ifeq ($(OS),Windows_NT) which = $(shell where $1) @@ -24,37 +49,16 @@ ifeq ($(JNI_PLATFORM),) else ifeq ($(findstring linux,$(TARGETTRIPLET)),linux) JNI_PLATFORM:= linux - # add more checks here endif endif endif JNI_PLATFORMINCLUDEDIR ?= $(JNI_INCLUDEDIR)/$(JNI_PLATFORM) -LOCAL_INCLUDES = /usr/local/include -LOCAL_LIBS = /usr/local/lib +############################################################################### +## Targets. -CC?=gcc -CXX?=g++ -STRIP?=strip - -LFLAGS+=-fPIC -shared -CFLAGS+=-fPIC -I"$(JNI_INCLUDEDIR)" -I"$(JNI_PLATFORMINCLUDEDIR)" -I. -CXXFLAGS+=-fPIC -I"$(JNI_INCLUDEDIR)" -I"$(JNI_PLATFORMINCLUDEDIR)" -I. - -DEBUG ?= 0 - -ifeq ($(DEBUG), 1) - CFLAGS+=-g - LFLAGS+=-g - CXXFLAGS+=-g -else - CFLAGS+=-O2 - LFLAGS+=-O2 - CXXFLAGS+=-O2 -endif - -all: tomcrypt_provider.so botan_provider.so cryptopp_provider.so openssl_provider.so boringssl_provider.so +all: tomcrypt_provider.so botan_provider.so cryptopp_provider.so openssl_provider.so boringssl_provider.so gcrypt_provider.so # Common utils c_utils.o: c_utils.c @@ -81,6 +85,14 @@ boringssl.o: boringssl.c $(CC) -I../../../../../../../ext/boringssl/include/ $(CFLAGS) -c $< +# libgcrypt shim +gcrypt_provider.so: gcrypt.o c_utils.o + $(CC) $(LFLAGS) -o $@ $^ -L. $(shell libgcrypt-config --libs) + +gcrypt.o: gcrypt.c + $(CC) $(shell libgcrypt-config --cflags) $(CFLAGS) -c $< + + # Libtomcrypt shim tomcrypt_provider.so: tomcrypt.o c_utils.o $(CC) $(LFLAGS) -o $@ $^ -L. -ltommath $(shell pkg-config --libs libtomcrypt) @@ -104,9 +116,18 @@ cryptopp_provider.so: cryptopp.o cpp_utils.o cryptopp.o: cryptopp.cpp $(CXX) $(shell pkg-config --cflags libcrypto++) $(CXXFLAGS) -c $< +help: + @echo "# This makefile builds the JNI shims necessary to test native libraries." + @echo "# Targets:" + @echo " - openssl_provider.so" + @echo " - boringssl_provider.so" + @echo " - gcrypt_provider.so" + @echo " - tomcrypt_provider.so" + @echo " - botan_provider.so" + @echo " - cryptopp_provider.so" clean: rm -rf *.o rm -rf *.so -.PHONY: all clean \ No newline at end of file +.PHONY: all help clean \ No newline at end of file diff --git a/src/cz/crcs/ectester/standalone/libs/jni/NativeECPrivateKey.java b/src/cz/crcs/ectester/standalone/libs/jni/NativeECPrivateKey.java index a74c155..6d8101f 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/NativeECPrivateKey.java +++ b/src/cz/crcs/ectester/standalone/libs/jni/NativeECPrivateKey.java @@ -91,6 +91,12 @@ public abstract class NativeECPrivateKey implements ECPrivateKey { } } + public static class Gcrypt extends Raw { + public Gcrypt(byte[] keyData, ECParameterSpec params) { + super(keyData, params); + } + } + public static class Mscng extends Raw { // 0 -> implicit (meta = curveName UTF16, header = full); // 1 -> explicit (meta = null, header = full); diff --git a/src/cz/crcs/ectester/standalone/libs/jni/NativeECPublicKey.java b/src/cz/crcs/ectester/standalone/libs/jni/NativeECPublicKey.java index 7c17646..dcb8b3f 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/NativeECPublicKey.java +++ b/src/cz/crcs/ectester/standalone/libs/jni/NativeECPublicKey.java @@ -92,6 +92,12 @@ public abstract class NativeECPublicKey implements ECPublicKey { } } + public static class Gcrypt extends ANSIX962 { + public Gcrypt(byte[] keyData, ECParameterSpec params) { + super(keyData, params); + } + } + public static class Mscng extends ANSIX962 { // 0 -> implicit (meta = curveName UTF16, header = full); // 1 -> explicit (meta = null, header = full); diff --git a/src/cz/crcs/ectester/standalone/libs/jni/NativeKeyPairGeneratorSpi.java b/src/cz/crcs/ectester/standalone/libs/jni/NativeKeyPairGeneratorSpi.java index 77f0d18..78656fc 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/NativeKeyPairGeneratorSpi.java +++ b/src/cz/crcs/ectester/standalone/libs/jni/NativeKeyPairGeneratorSpi.java @@ -198,6 +198,24 @@ public abstract class NativeKeyPairGeneratorSpi extends KeyPairGeneratorSpi { native KeyPair generate(AlgorithmParameterSpec params, SecureRandom random); } + public static class Gcrypt extends NativeKeyPairGeneratorSpi { + + public Gcrypt() { + } + + @Override + native boolean keysizeSupported(int keysize); + + @Override + native boolean paramsSupported(AlgorithmParameterSpec params); + + @Override + native KeyPair generate(int keysize, SecureRandom random); + + @Override + native KeyPair generate(AlgorithmParameterSpec params, SecureRandom random); + } + public static abstract class Mscng extends NativeKeyPairGeneratorSpi { private String type; diff --git a/src/cz/crcs/ectester/standalone/libs/jni/NativeProvider.java b/src/cz/crcs/ectester/standalone/libs/jni/NativeProvider.java index 1399500..99baa97 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/NativeProvider.java +++ b/src/cz/crcs/ectester/standalone/libs/jni/NativeProvider.java @@ -74,6 +74,16 @@ public abstract class NativeProvider extends Provider { native void setup(); } + public static class Gcrypt extends NativeProvider { + + public Gcrypt(String name, double version, String info) { + super(name, version, info); + } + + @Override + native void setup(); + } + public static class Mscng extends NativeProvider { public Mscng(String name, double version, String info) { diff --git a/src/cz/crcs/ectester/standalone/libs/jni/boringssl.c b/src/cz/crcs/ectester/standalone/libs/jni/boringssl.c index 0353741..965e396 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/boringssl.c +++ b/src/cz/crcs/ectester/standalone/libs/jni/boringssl.c @@ -1,6 +1,5 @@ #include "native.h" #include -#include #include #include @@ -256,7 +255,6 @@ static jobject create_ec_param_spec(JNIEnv *env, const EC_GROUP *curve) { jmethodID elliptic_curve_init = (*env)->GetMethodID(env, elliptic_curve_class, "", "(Ljava/security/spec/ECField;Ljava/math/BigInteger;Ljava/math/BigInteger;)V"); jobject elliptic_curve = (*env)->NewObject(env, elliptic_curve_class, elliptic_curve_init, field, a_int, b_int); - fflush(stderr); BN_free(a); BN_free(b); diff --git a/src/cz/crcs/ectester/standalone/libs/jni/gcrypt.c b/src/cz/crcs/ectester/standalone/libs/jni/gcrypt.c new file mode 100644 index 0000000..0705df6 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/libs/jni/gcrypt.c @@ -0,0 +1,284 @@ +#include "native.h" +#include +#include +#include "c_utils.h" + +static jclass provider_class; + + +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_GcryptLib_createProvider(JNIEnv *env, jobject this){ + /* Create the custom provider. */ + jclass local_provider_class = (*env)->FindClass(env, "cz/crcs/ectester/standalone/libs/jni/NativeProvider$Gcrypt"); + provider_class = (*env)->NewGlobalRef(env, local_provider_class); + + jmethodID init = (*env)->GetMethodID(env, local_provider_class, "", "(Ljava/lang/String;DLjava/lang/String;)V"); + + const char *built_with = GCRYPT_VERSION; + const char *running_with = gcry_check_version(GCRYPT_VERSION); + if (!running_with) { + return NULL; + } + char full_name[strlen("libgcrypt ") + strlen(running_with) + 1]; + strcpy(full_name, "libgcrypt "); + strcat(full_name, running_with); + jstring name = (*env)->NewStringUTF(env, full_name); + double version = strtod(running_with, NULL); + + return (*env)->NewObject(env, provider_class, init, name, version, name); +} + +JNIEXPORT void JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeProvider_00024Gcrypt_setup(JNIEnv *env, jobject this) { + gcry_control(GCRYCTL_DISABLE_SECMEM, 0); + //gcry_control(GCRYCTL_SET_DEBUG_FLAGS, 1); + gcry_control(GCRYCTL_ENABLE_QUICK_RANDOM, 0); + gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); + + INIT_PROVIDER(env, provider_class); + + ADD_KPG(env, this, "EC", "Gcrypt"); + //ADD_KA(env, self, "ECDH", "OpensslECDH"); + //ADD_SIG(env, self, "NONEwithECDSA", "OpensslECDSAwithNONE"); + + init_classes(env, "Gcrypt"); +} + +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_GcryptLib_getCurves(JNIEnv *env, jobject this) { + jclass hash_set_class = (*env)->FindClass(env, "java/util/TreeSet"); + + jmethodID hash_set_ctr = (*env)->GetMethodID(env, hash_set_class, "", "()V"); + jmethodID hash_set_add = (*env)->GetMethodID(env, hash_set_class, "add", "(Ljava/lang/Object;)Z"); + + jobject result = (*env)->NewObject(env, hash_set_class, hash_set_ctr); + + const char *name; + unsigned int nbits; + + for (size_t i = 0; (name = gcry_pk_get_curve(NULL, i, &nbits)); i++){ + jstring curve_name = (*env)->NewStringUTF(env, name); + (*env)->CallBooleanMethod(env, result, hash_set_add, curve_name); + } + + return result; +} + +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Gcrypt_keysizeSupported(JNIEnv *env, jobject this, jint keysize) { + const char *name; + unsigned int nbits; + + for (size_t i = 0; (name = gcry_pk_get_curve(NULL, i, &nbits)); i++){ + if (nbits == keysize) { + return JNI_TRUE; + } + } + + return JNI_FALSE; +} + +static void print_sexp(gcry_sexp_t sexp) { + size_t len = gcry_sexp_sprint(sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0); + char string[len]; + gcry_sexp_sprint(sexp, GCRYSEXP_FMT_ADVANCED, string, len); + printf("%s\n", string); + fflush(stdout); +} + +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Gcrypt_paramsSupported(JNIEnv *env, jobject this, jobject params) { + if (params == NULL) { + return JNI_FALSE; + } + + if ((*env)->IsInstanceOf(env, params, ec_parameter_spec_class)) { + return JNI_FALSE; + } else if ((*env)->IsInstanceOf(env, params, ecgen_parameter_spec_class)) { + jmethodID get_name = (*env)->GetMethodID(env, ecgen_parameter_spec_class, "getName", "()Ljava/lang/String;"); + jstring name = (*env)->CallObjectMethod(env, params, get_name); + const char *utf_name = (*env)->GetStringUTFChars(env, name, NULL); + gcry_sexp_t curve_sexp; + gcry_sexp_build(&curve_sexp, NULL, "(public-key (ecc (curve %s)))", utf_name); + unsigned int nbits; + const char *ret_name = gcry_pk_get_curve(curve_sexp, 0, &nbits); + (*env)->ReleaseStringUTFChars(env, name, utf_name); + gcry_sexp_release(curve_sexp); + return ret_name ? JNI_TRUE : JNI_FALSE; + } else { + return JNI_FALSE; + } +} + +static jobject mpi_to_biginteger(JNIEnv *env, gcry_mpi_t mpi) { + jmethodID biginteger_init = (*env)->GetMethodID(env, biginteger_class, "", "(I[B)V"); + size_t len = 0; + gcry_mpi_print(GCRYMPI_FMT_USG, NULL, 0, &len, mpi); + jbyteArray bytes = (*env)->NewByteArray(env, len); + jbyte *data = (*env)->GetByteArrayElements(env, bytes, NULL); + gcry_mpi_print(GCRYMPI_FMT_USG, data, len, &len, mpi); + (*env)->ReleaseByteArrayElements(env, bytes, data, 0); + jobject result = (*env)->NewObject(env, biginteger_class, biginteger_init, 1, bytes); + return result; +} + +static gcry_mpi_t biginteger_to_mpi(JNIEnv *env, jobject bigint) { + jmethodID to_byte_array = (*env)->GetMethodID(env, biginteger_class, "toByteArray", "()[B"); + + jbyteArray byte_array = (jbyteArray) (*env)->CallObjectMethod(env, bigint, to_byte_array); + jsize byte_length = (*env)->GetArrayLength(env, byte_array); + jbyte *byte_data = (*env)->GetByteArrayElements(env, byte_array, NULL); + gcry_mpi_t result; + gcry_mpi_scan(&result, GCRYMPI_FMT_USG, byte_data, byte_length, NULL); + (*env)->ReleaseByteArrayElements(env, byte_array, byte_data, JNI_ABORT); + return result; +} + +static jint mpi_to_jint(gcry_mpi_t mpi) { + jint result = 0; + unsigned long nbits = gcry_mpi_get_nbits(mpi); + int max_bits = sizeof(jint) * 8; + for (size_t i = 0; i < nbits && i < max_bits; ++i) { + if (gcry_mpi_test_bit(mpi, nbits - i - 1)) { + result = ((result << 1) | 1); + } else { + result = (result << 1); + } + } + return result; +} + +static jobject buff_to_ecpoint(JNIEnv *env, gcry_buffer_t buff) { + jint coord_size = (buff.size - 1) / 2; + jmethodID biginteger_init = (*env)->GetMethodID(env, biginteger_class, "", "(I[B)V"); + + jbyteArray x_bytes = (*env)->NewByteArray(env, coord_size); + jbyte *x_data = (*env)->GetByteArrayElements(env, x_bytes, NULL); + memcpy(x_data, ((char *) buff.data) + 1, coord_size); + (*env)->ReleaseByteArrayElements(env, x_bytes, x_data, 0); + jobject xi = (*env)->NewObject(env, biginteger_class, biginteger_init, 1, x_bytes); + + jbyteArray y_bytes = (*env)->NewByteArray(env, coord_size); + jbyte *y_data = (*env)->GetByteArrayElements(env, y_bytes, NULL); + memcpy(y_data, ((char *) buff.data) + 1 + coord_size, coord_size); + (*env)->ReleaseByteArrayElements(env, y_bytes, y_data, 0); + jobject yi = (*env)->NewObject(env, biginteger_class, biginteger_init, 1, y_bytes); + + jmethodID point_init = (*env)->GetMethodID(env, point_class, "", "(Ljava/math/BigInteger;Ljava/math/BigInteger;)V"); + return (*env)->NewObject(env, point_class, point_init, xi, yi); +} + +static jobject create_ec_param_spec(JNIEnv *env, gcry_sexp_t key) { + jobject result = NULL; + gcry_mpi_t p, a, b, n, h; + gcry_buffer_t g = {0}; + gcry_error_t err = gcry_sexp_extract_param(key, "ecc", "pab&g+nh", &p, &a, &b, &g, &n, &h, NULL); + if (gcry_err_code(err) != GPG_ERR_NO_ERROR) { + throw_new_var(env, "java/security/GeneralSecurityException", "Error exporting domain parameters. Error: %ui", gcry_err_code(err)); + goto end; + } + + jobject pi = mpi_to_biginteger(env, p); + jmethodID fp_field_init = (*env)->GetMethodID(env, fp_field_class, "", "(Ljava/math/BigInteger;)V"); + jobject field = (*env)->NewObject(env, fp_field_class, fp_field_init, pi); + + jobject ai = mpi_to_biginteger(env, a); + jobject bi = mpi_to_biginteger(env, b); + + jmethodID elliptic_curve_init = (*env)->GetMethodID(env, elliptic_curve_class, "", "(Ljava/security/spec/ECField;Ljava/math/BigInteger;Ljava/math/BigInteger;)V"); + jobject elliptic_curve = (*env)->NewObject(env, elliptic_curve_class, elliptic_curve_init, field, ai, bi); + + jobject gen = buff_to_ecpoint(env, g); + + jobject order = mpi_to_biginteger(env, n); + jint cofactor = mpi_to_jint(h); + + jmethodID ec_parameter_spec_init = (*env)->GetMethodID(env, ec_parameter_spec_class, "", "(Ljava/security/spec/EllipticCurve;Ljava/security/spec/ECPoint;Ljava/math/BigInteger;I)V"); + result = (*env)->NewObject(env, ec_parameter_spec_class, ec_parameter_spec_init, elliptic_curve, gen, order, cofactor); + +end: + gcry_mpi_release(p); + gcry_mpi_release(a); + gcry_mpi_release(b); + gcry_free(g.data); + gcry_mpi_release(n); + gcry_mpi_release(h); + return result; +} + +static jobject generate_from_sexp(JNIEnv *env, gcry_sexp_t gen_sexp) { + jobject result = NULL; + gcry_sexp_t key_sexp; + gcry_error_t err = gcry_pk_genkey(&key_sexp, gen_sexp); + if (gcry_err_code(err) != GPG_ERR_NO_ERROR) { + throw_new_var(env, "java/security/GeneralSecurityException", "Error generating key. Error: %ui", gcry_err_code(err)); + goto release_sexp; + } + gcry_sexp_t pkey = gcry_sexp_find_token(key_sexp, "public-key", 0); + gcry_sexp_t skey = gcry_sexp_find_token(key_sexp, "private-key", 0); + + jobject ec_param_spec = create_ec_param_spec(env, skey); + if (!ec_param_spec) { + goto release_keypair; + } + + gcry_buffer_t q = {0}; + gcry_mpi_t d; + err = gcry_sexp_extract_param(skey, "ecc", "&q+d", &q, &d, NULL); + + jbyteArray pub_bytes = (*env)->NewByteArray(env, q.size); + jbyte *key_pub = (*env)->GetByteArrayElements(env, pub_bytes, NULL); + memcpy(key_pub, q.data, q.size); + (*env)->ReleaseByteArrayElements(env, pub_bytes, key_pub, 0); + + size_t priv_len = 0; + gcry_mpi_print(GCRYMPI_FMT_USG, NULL, 0, &priv_len, d); + jbyteArray priv_bytes = (*env)->NewByteArray(env, priv_len); + jbyte *key_priv = (*env)->GetByteArrayElements(env, priv_bytes, NULL); + gcry_mpi_print(GCRYMPI_FMT_USG, key_priv, priv_len, NULL, d); + (*env)->ReleaseByteArrayElements(env, priv_bytes, key_priv, 0); + + jobject ec_pub_param_spec = (*env)->NewLocalRef(env, ec_param_spec); + jmethodID ec_pub_init = (*env)->GetMethodID(env, pubkey_class, "", "([BLjava/security/spec/ECParameterSpec;)V"); + jobject pubkey = (*env)->NewObject(env, pubkey_class, ec_pub_init, pub_bytes, ec_param_spec); + + jobject ec_priv_param_spec = (*env)->NewLocalRef(env, ec_param_spec); + jmethodID ec_priv_init = (*env)->GetMethodID(env, privkey_class, "", "([BLjava/security/spec/ECParameterSpec;)V"); + jobject privkey = (*env)->NewObject(env, privkey_class, ec_priv_init, priv_bytes, ec_priv_param_spec); + + jmethodID keypair_init = (*env)->GetMethodID(env, keypair_class, "", "(Ljava/security/PublicKey;Ljava/security/PrivateKey;)V"); + result = (*env)->NewObject(env, keypair_class, keypair_init, pubkey, privkey); + + gcry_mpi_release(d); + gcry_free(q.data); + +release_keypair: + gcry_sexp_release(pkey); + gcry_sexp_release(skey); +release_sexp: + gcry_sexp_release(key_sexp); + return result; +} + +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Gcrypt_generate__ILjava_security_SecureRandom_2(JNIEnv *env, jobject this, jint keysize, jobject random) { + gcry_sexp_t gen_sexp; + gcry_sexp_build(&gen_sexp, NULL, "(genkey (ecc (flags no-keytest param) (nbits %d)))", keysize); + + jobject result = generate_from_sexp(env, gen_sexp); + gcry_sexp_release(gen_sexp); + return result; +} + +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Gcrypt_generate__Ljava_security_spec_AlgorithmParameterSpec_2Ljava_security_SecureRandom_2(JNIEnv *env, jobject this, jobject params, jobject random) { + + if ((*env)->IsInstanceOf(env, params, ec_parameter_spec_class)) { + return NULL; + } else if ((*env)->IsInstanceOf(env, params, ecgen_parameter_spec_class)) { + jmethodID get_name = (*env)->GetMethodID(env, ecgen_parameter_spec_class, "getName", "()Ljava/lang/String;"); + jstring name = (*env)->CallObjectMethod(env, params, get_name); + const char *utf_name = (*env)->GetStringUTFChars(env, name, NULL); + gcry_sexp_t gen_sexp; + gcry_sexp_build(&gen_sexp, NULL, "(genkey (ecc (flags no-keytest param) (curve %s)))", utf_name); + (*env)->ReleaseStringUTFChars(env, name, utf_name); + jobject result = generate_from_sexp(env, gen_sexp); + gcry_sexp_release(gen_sexp); + return result; + } else { + return NULL; + } +} \ No newline at end of file diff --git a/src/cz/crcs/ectester/standalone/libs/jni/native.h b/src/cz/crcs/ectester/standalone/libs/jni/native.h index eb64b53..e86a847 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/native.h +++ b/src/cz/crcs/ectester/standalone/libs/jni/native.h @@ -1086,3 +1086,108 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSigna } #endif #endif +/* Header for class cz_crcs_ectester_standalone_libs_GcryptLib */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_GcryptLib +#define _Included_cz_crcs_ectester_standalone_libs_GcryptLib +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: cz_crcs_ectester_standalone_libs_GcryptLib + * Method: createProvider + * Signature: ()Ljava/security/Provider; + */ +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_GcryptLib_createProvider + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_GcryptLib + * Method: getCurves + * Signature: ()Ljava/util/Set; + */ +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_GcryptLib_getCurves + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif +/* Header for class cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt +#define _Included_cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt +#ifdef __cplusplus +extern "C" { +#endif +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt_serialVersionUID +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt_serialVersionUID 1421746759512286392LL +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt_MAX_ARRAY_SIZE +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt_MAX_ARRAY_SIZE 2147483639L +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt_KEYS +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt_KEYS 0L +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt_VALUES +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt_VALUES 1L +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt_ENTRIES +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt_ENTRIES 2L +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt_serialVersionUID +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt_serialVersionUID 4112578634029874840LL +#undef cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt_serialVersionUID +#define cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt_serialVersionUID -4298000515446427739LL +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeProvider_Gcrypt + * Method: setup + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeProvider_00024Gcrypt_setup + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif +/* Header for class cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Gcrypt */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Gcrypt +#define _Included_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Gcrypt +#ifdef __cplusplus +extern "C" { +#endif +#undef cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Gcrypt_DEFAULT_KEYSIZE +#define cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Gcrypt_DEFAULT_KEYSIZE 256L +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Gcrypt + * Method: keysizeSupported + * Signature: (I)Z + */ +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Gcrypt_keysizeSupported + (JNIEnv *, jobject, jint); + +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Gcrypt + * Method: paramsSupported + * Signature: (Ljava/security/spec/AlgorithmParameterSpec;)Z + */ +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Gcrypt_paramsSupported + (JNIEnv *, jobject, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Gcrypt + * Method: generate + * Signature: (ILjava/security/SecureRandom;)Ljava/security/KeyPair; + */ +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Gcrypt_generate__ILjava_security_SecureRandom_2 + (JNIEnv *, jobject, jint, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_Gcrypt + * Method: generate + * Signature: (Ljava/security/spec/AlgorithmParameterSpec;Ljava/security/SecureRandom;)Ljava/security/KeyPair; + */ +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Gcrypt_generate__Ljava_security_spec_AlgorithmParameterSpec_2Ljava_security_SecureRandom_2 + (JNIEnv *, jobject, jobject, jobject); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/cz/crcs/ectester/standalone/libs/jni/openssl.c b/src/cz/crcs/ectester/standalone/libs/jni/openssl.c index 255834a..4ea5f6c 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/openssl.c +++ b/src/cz/crcs/ectester/standalone/libs/jni/openssl.c @@ -1,6 +1,5 @@ #include "native.h" #include -#include #include #include @@ -327,7 +326,6 @@ static jobject create_ec_param_spec(JNIEnv *env, const EC_GROUP *curve) { jmethodID elliptic_curve_init = (*env)->GetMethodID(env, elliptic_curve_class, "", "(Ljava/security/spec/ECField;Ljava/math/BigInteger;Ljava/math/BigInteger;)V"); jobject elliptic_curve = (*env)->NewObject(env, elliptic_curve_class, elliptic_curve_init, field, a_int, b_int); - fflush(stderr); BN_free(a); BN_free(b); -- cgit v1.2.3-70-g09d2 From ba5cfcc98d7bf5107e6aac3a4e5c7416ab7b76a7 Mon Sep 17 00:00:00 2001 From: J08nY Date: Wed, 17 Oct 2018 22:41:19 +0200 Subject: Add support for libgcrypt ecdh and ecdsa. --- build-standalone.xml | 2 + .../ectester/standalone/ECTesterStandalone.java | 2 +- .../ectester/standalone/consts/SignatureIdent.java | 2 +- .../standalone/libs/jni/NativeKeyAgreementSpi.java | 23 +- .../standalone/libs/jni/NativeSignatureSpi.java | 91 ++++++ src/cz/crcs/ectester/standalone/libs/jni/c_utils.c | 101 +++++- src/cz/crcs/ectester/standalone/libs/jni/c_utils.h | 11 + src/cz/crcs/ectester/standalone/libs/jni/gcrypt.c | 360 +++++++++++++++++++-- src/cz/crcs/ectester/standalone/libs/jni/native.h | 76 +++++ 9 files changed, 645 insertions(+), 23 deletions(-) (limited to 'src/cz/crcs/ectester/standalone/ECTesterStandalone.java') diff --git a/build-standalone.xml b/build-standalone.xml index 84f4595..9a47642 100644 --- a/build-standalone.xml +++ b/build-standalone.xml @@ -169,6 +169,8 @@ + + diff --git a/src/cz/crcs/ectester/standalone/ECTesterStandalone.java b/src/cz/crcs/ectester/standalone/ECTesterStandalone.java index cc0e465..7480215 100644 --- a/src/cz/crcs/ectester/standalone/ECTesterStandalone.java +++ b/src/cz/crcs/ectester/standalone/ECTesterStandalone.java @@ -325,7 +325,7 @@ public class ECTesterStandalone { ECPublicKey pubkey = (ECPublicKey) other.getPublic(); long elapsed = -System.nanoTime(); - if (spec != null) { + if (spec instanceof ECParameterSpec) { ka.init(privkey, spec); } else { ka.init(privkey); diff --git a/src/cz/crcs/ectester/standalone/consts/SignatureIdent.java b/src/cz/crcs/ectester/standalone/consts/SignatureIdent.java index dea8abe..93b7f99 100644 --- a/src/cz/crcs/ectester/standalone/consts/SignatureIdent.java +++ b/src/cz/crcs/ectester/standalone/consts/SignatureIdent.java @@ -52,7 +52,7 @@ public class SignatureIdent extends Ident { ALL.add(new SignatureIdent("ECGOST3410-2012-512", "GOST-3410-2012-512")); ALL.add(new SignatureIdent("GOST3411-2012-512withECGOST3410-2012-512", "GOST3411-2012-512/ECGOST3410-2012-5120", "1.2.643.7.1.1.3.3")); ALL.add(new SignatureIdent("SM3withSM2")); - // ECDDSA + // ECDDSA (rfc6979?) ALL.add(new SignatureIdent("ECDDSA", "DETECDSA", "ECDETDSA")); ALL.add(new SignatureIdent("SHA1withECDDSA", "SHA1withDETECDSA")); ALL.add(new SignatureIdent("SHA224withECDDSA", "SHA224withDETECDSA")); diff --git a/src/cz/crcs/ectester/standalone/libs/jni/NativeKeyAgreementSpi.java b/src/cz/crcs/ectester/standalone/libs/jni/NativeKeyAgreementSpi.java index 5fca448..7b531f5 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/NativeKeyAgreementSpi.java +++ b/src/cz/crcs/ectester/standalone/libs/jni/NativeKeyAgreementSpi.java @@ -65,7 +65,7 @@ public abstract class NativeKeyAgreementSpi extends KeyAgreementSpi { @Override protected void engineInit(Key key, AlgorithmParameterSpec params, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { if (!(params instanceof ECParameterSpec)) { - throw new InvalidAlgorithmParameterException(); + throw new InvalidAlgorithmParameterException(params.toString()); } engineInit(key, random); this.params = params; @@ -254,6 +254,27 @@ public abstract class NativeKeyAgreementSpi extends KeyAgreementSpi { } } + public abstract static class Gcrypt extends SimpleKeyAgreementSpi { + private String type; + + public Gcrypt(String type) { + this.type = type; + } + + @Override + native byte[] generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params); + + @Override + native SecretKey generateSecret(byte[] pubkey, byte[] privkey, ECParameterSpec params, String algorithm); + } + + public static class GcryptECDH extends Gcrypt { + public GcryptECDH() { + super("ECDH"); + } + } + + public abstract static class Mscng extends ExtendedKeyAgreementSpi { private String type; diff --git a/src/cz/crcs/ectester/standalone/libs/jni/NativeSignatureSpi.java b/src/cz/crcs/ectester/standalone/libs/jni/NativeSignatureSpi.java index 3f99771..f2b1ab9 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/NativeSignatureSpi.java +++ b/src/cz/crcs/ectester/standalone/libs/jni/NativeSignatureSpi.java @@ -349,6 +349,97 @@ public abstract class NativeSignatureSpi extends SignatureSpi { } } + public abstract static class Gcrypt extends SimpleSignatureSpi { + private String type; + + public Gcrypt(String type) { + this.type = type; + } + + @Override + native byte[] sign(byte[] data, byte[] privkey, ECParameterSpec params); + + @Override + native boolean verify(byte[] signature, byte[] data, byte[] pubkey, ECParameterSpec params); + } + + public static class GcryptECDSAwithNONE extends Gcrypt { + + public GcryptECDSAwithNONE() { + super("NONEwithECDSA"); + } + } + + public static class GcryptECDSAwithSHA1 extends Gcrypt { + + public GcryptECDSAwithSHA1() { + super("SHA1withECDSA"); + } + } + + public static class GcryptECDSAwithSHA224 extends Gcrypt { + + public GcryptECDSAwithSHA224() { + super("SHA224withECDSA"); + } + } + + public static class GcryptECDSAwithSHA256 extends Gcrypt { + + public GcryptECDSAwithSHA256() { + super("SHA256withECDSA"); + } + } + + public static class GcryptECDSAwithSHA384 extends Gcrypt { + + public GcryptECDSAwithSHA384() { + super("SHA384withECDSA"); + } + } + + public static class GcryptECDSAwithSHA512 extends Gcrypt { + + public GcryptECDSAwithSHA512() { + super("SHA512withECDSA"); + } + } + + public static class GcryptECDDSAwithSHA1 extends Gcrypt { + + public GcryptECDDSAwithSHA1() { + super("SHA1withECDDSA"); + } + } + + public static class GcryptECDDSAwithSHA224 extends Gcrypt { + + public GcryptECDDSAwithSHA224() { + super("SHA224withECDDSA"); + } + } + + public static class GcryptECDDSAwithSHA256 extends Gcrypt { + + public GcryptECDDSAwithSHA256() { + super("SHA256withECDDSA"); + } + } + + public static class GcryptECDDSAwithSHA384 extends Gcrypt { + + public GcryptECDDSAwithSHA384() { + super("SHA384withECDDSA"); + } + } + + public static class GcryptECDDSAwithSHA512 extends Gcrypt { + + public GcryptECDDSAwithSHA512() { + super("SHA512withECDDSA"); + } + } + public abstract static class Mscng extends ExtendedSignatureSpi { private String type; diff --git a/src/cz/crcs/ectester/standalone/libs/jni/c_utils.c b/src/cz/crcs/ectester/standalone/libs/jni/c_utils.c index 49cab44..c36d3c9 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/c_utils.c +++ b/src/cz/crcs/ectester/standalone/libs/jni/c_utils.c @@ -75,7 +75,7 @@ void throw_new_var(JNIEnv *env, const char *class, const char *format, ...) { char buffer[2048]; va_list args; va_start(args, format); - int res = vsnprintf(buffer, 2048, format, args); + vsnprintf(buffer, 2048, format, args); va_end(args); throw_new(env, class, buffer); } @@ -105,4 +105,103 @@ jint get_kdf_bits(JNIEnv *env, jstring algorithm) { } (*env)->ReleaseStringUTFChars(env, algorithm, algo_data); return result; +} + +jbyteArray asn1_der_encode(JNIEnv *env, const jbyte *r, size_t r_len, const jbyte *s, size_t s_len) { + jbyte r_length = (jbyte) r_len + (r[0] & 0x80 ? 1 : 0); + jbyte s_length = (jbyte) s_len + (s[0] & 0x80 ? 1 : 0); + + // R and S are < 128 bytes, so 1 byte tag + 1 byte len + len bytes value + size_t seq_value_len = 2 + r_length + 2 + s_length; + size_t whole_len = seq_value_len; + + // The SEQUENCE length might be >= 128, so more bytes of length + size_t seq_len_len = 0; + if (seq_value_len >= 128) { + size_t s = seq_value_len; + while ((s = s >> 8)) { + seq_len_len++; + } + } + // seq_len_len bytes for length and one for length of length + whole_len += seq_len_len + 1; + + // 1 byte tag for SEQUENCE + whole_len += 1; + + jbyteArray result = (jbyteArray) (*env)->NewByteArray(env, whole_len); + jbyte *data = (*env)->GetByteArrayElements(env, result, NULL); + size_t i = 0; + data[i++] = 0x30; // SEQUENCE + if (seq_value_len < 128) { + data[i++] = (jbyte) seq_value_len; + } else { + data[i++] = (jbyte) (seq_len_len | (1 << 7)); + for (size_t j = 0; j < seq_len_len; ++j) { + data[i++] = (jbyte) (seq_value_len & (0xff << (seq_len_len - j))); + } + } + data[i++] = 0x02; //INTEGER + data[i++] = r_length; + if (r[0] & 0x80) { + data[i++] = 0; + } + memcpy(data + i, r, r_len); + i += r_len; + data[i++] = 0x02; //INTEGER + data[i++] = s_length; + if (s[0] & 0x80) { + data[i++] = 0; + } + memcpy(data + i, s, s_len); + i += s_len; + (*env)->ReleaseByteArrayElements(env, result, data, 0); + + return result; +} + +bool asn1_der_decode(JNIEnv *env, jbyteArray sig, jbyte **r_data, size_t *r_len, jbyte **s_data, size_t *s_len) { + size_t sig_len = (*env)->GetArrayLength(env, sig); + jbyte *data = (*env)->GetByteArrayElements(env, sig, NULL); + size_t i = 0; + if (data[i++] != 0x30) {//SEQUENCE + (*env)->ReleaseByteArrayElements(env, sig, data, JNI_ABORT); + return false; + } + size_t seq_value_len = 0; + if (!(data[i] & 0x80)) { + seq_value_len = data[i++]; + } else { + size_t seq_len_len = data[i++] & 0x7f; + while (seq_len_len > 0) { + seq_value_len |= (data[i++] << (seq_len_len - 1)); + seq_len_len--; + } + } + + if (data[i++] != 0x02) {//INTEGER + (*env)->ReleaseByteArrayElements(env, sig, data, JNI_ABORT); + return false; + } + size_t r_length = data[i++]; + jbyte *r_out = malloc(r_length); + memcpy(r_out, data + i, r_length); + i += r_length; + + if (data[i++] != 0x02) {//INTEGER + free(r_out); + (*env)->ReleaseByteArrayElements(env, sig, data, JNI_ABORT); + return false; + } + size_t s_length = data[i++]; + jbyte *s_out = malloc(s_length); + memcpy(s_out, data + i, s_length); + i += s_length; + + *r_len = r_length; + *r_data = r_out; + *s_len = s_length; + *s_data = s_out; + (*env)->ReleaseByteArrayElements(env, sig, data, JNI_ABORT); + return true; } \ No newline at end of file diff --git a/src/cz/crcs/ectester/standalone/libs/jni/c_utils.h b/src/cz/crcs/ectester/standalone/libs/jni/c_utils.h index b767b61..82c3538 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/c_utils.h +++ b/src/cz/crcs/ectester/standalone/libs/jni/c_utils.h @@ -1,6 +1,7 @@ #pragma once #include "native.h" +#include /** * Classes that are accessed alot are cached here, manually. @@ -38,6 +39,16 @@ void throw_new_var(JNIEnv *env, const char *class, const char *format, ...); */ jint get_kdf_bits(JNIEnv *env, jstring algorithm); +/** + * DER encode the r and s values. + */ +jbyteArray asn1_der_encode(JNIEnv *env, const jbyte *r, size_t r_len, const jbyte *s, size_t s_len); + +/** + * DER decode a signature into r and s values. + */ +bool asn1_der_decode(JNIEnv *env, jbyteArray sig, jbyte **r_data, size_t *r_len, jbyte **s_data, size_t *s_len); + /** * Some useful defines to init the provider. */ diff --git a/src/cz/crcs/ectester/standalone/libs/jni/gcrypt.c b/src/cz/crcs/ectester/standalone/libs/jni/gcrypt.c index 0705df6..0ba94c6 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/gcrypt.c +++ b/src/cz/crcs/ectester/standalone/libs/jni/gcrypt.c @@ -1,5 +1,7 @@ #include "native.h" #include +#include +#include #include #include "c_utils.h" @@ -13,7 +15,6 @@ JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_GcryptLib_create jmethodID init = (*env)->GetMethodID(env, local_provider_class, "", "(Ljava/lang/String;DLjava/lang/String;)V"); - const char *built_with = GCRYPT_VERSION; const char *running_with = gcry_check_version(GCRYPT_VERSION); if (!running_with) { return NULL; @@ -36,8 +37,18 @@ JNIEXPORT void JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeProvider_ INIT_PROVIDER(env, provider_class); ADD_KPG(env, this, "EC", "Gcrypt"); - //ADD_KA(env, self, "ECDH", "OpensslECDH"); - //ADD_SIG(env, self, "NONEwithECDSA", "OpensslECDSAwithNONE"); + ADD_KA(env, this, "ECDH", "GcryptECDH"); + ADD_SIG(env, this, "NONEwithECDSA", "GcryptECDSAwithNONE"); + ADD_SIG(env, this, "SHA1withECDSA", "GcryptECDSAwithSHA1"); + ADD_SIG(env, this, "SHA224withECDSA", "GcryptECDSAwithSHA224"); + ADD_SIG(env, this, "SHA256withECDSA", "GcryptECDSAwithSHA256"); + ADD_SIG(env, this, "SHA384withECDSA", "GcryptECDSAwithSHA384"); + ADD_SIG(env, this, "SHA512withECDSA", "GcryptECDSAwithSHA512"); + ADD_SIG(env, this, "SHA1withECDDSA", "GcryptECDDSAwithSHA1"); + ADD_SIG(env, this, "SHA224withECDDSA", "GcryptECDDSAwithSHA224"); + ADD_SIG(env, this, "SHA256withECDDSA", "GcryptECDDSAwithSHA256"); + ADD_SIG(env, this, "SHA384withECDDSA", "GcryptECDDSAwithSHA384"); + ADD_SIG(env, this, "SHA512withECDDSA", "GcryptECDDSAwithSHA512"); init_classes(env, "Gcrypt"); } @@ -82,6 +93,13 @@ static void print_sexp(gcry_sexp_t sexp) { fflush(stdout); } +static void print_chrray(unsigned char *arr, size_t len) { + for (size_t i = 0; i < len; ++i) { + printf("%02x,", ((unsigned char) arr[i] & 0xff)); + } + printf("\n"); +} + JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Gcrypt_paramsSupported(JNIEnv *env, jobject this, jobject params) { if (params == NULL) { return JNI_FALSE; @@ -105,28 +123,66 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPa } } -static jobject mpi_to_biginteger(JNIEnv *env, gcry_mpi_t mpi) { - jmethodID biginteger_init = (*env)->GetMethodID(env, biginteger_class, "", "(I[B)V"); - size_t len = 0; - gcry_mpi_print(GCRYMPI_FMT_USG, NULL, 0, &len, mpi); - jbyteArray bytes = (*env)->NewByteArray(env, len); +static gcry_mpi_t bytearray_to_mpi(JNIEnv *env, jbyteArray array) { + if (!array) { + return NULL; + } + + gcry_mpi_t result; + + size_t length = (*env)->GetArrayLength(env, array); + char data[length + 1]; + data[0] = 0; + (*env)->GetByteArrayRegion(env, array, 0, length, data + 1); + gcry_mpi_scan(&result, GCRYMPI_FMT_USG, data, length + 1, NULL); + return result; +} + +static jbyteArray mpi_to_bytearray0(JNIEnv *env, gcry_mpi_t mpi, size_t start, size_t len) { + if (!mpi) { + return NULL; + } + + size_t mpi_len = 0; + gcry_mpi_print(GCRYMPI_FMT_USG, NULL, 0, &mpi_len, mpi); + if (start >= mpi_len) { + return NULL; + } + if (start + len > mpi_len || len == 0) { + len = mpi_len - start; + } + unsigned char buff[mpi_len]; + gcry_mpi_print(GCRYMPI_FMT_USG, buff, mpi_len, NULL, mpi); + jbyteArray bytes = (*env)->NewByteArray(env, len); jbyte *data = (*env)->GetByteArrayElements(env, bytes, NULL); - gcry_mpi_print(GCRYMPI_FMT_USG, data, len, &len, mpi); + memcpy(data, buff + start, len); (*env)->ReleaseByteArrayElements(env, bytes, data, 0); + return bytes; +} + +static jbyteArray mpi_to_bytearray(JNIEnv *env, gcry_mpi_t mpi) { + return mpi_to_bytearray0(env, mpi, 0, 0); +} + +static jobject mpi_to_biginteger(JNIEnv *env, gcry_mpi_t mpi) { + if (!mpi) { + return NULL; + } + + jmethodID biginteger_init = (*env)->GetMethodID(env, biginteger_class, "", "(I[B)V"); + jbyteArray bytes = mpi_to_bytearray(env, mpi); jobject result = (*env)->NewObject(env, biginteger_class, biginteger_init, 1, bytes); return result; } static gcry_mpi_t biginteger_to_mpi(JNIEnv *env, jobject bigint) { - jmethodID to_byte_array = (*env)->GetMethodID(env, biginteger_class, "toByteArray", "()[B"); + if (!bigint) { + return NULL; + } + jmethodID to_byte_array = (*env)->GetMethodID(env, biginteger_class, "toByteArray", "()[B"); jbyteArray byte_array = (jbyteArray) (*env)->CallObjectMethod(env, bigint, to_byte_array); - jsize byte_length = (*env)->GetArrayLength(env, byte_array); - jbyte *byte_data = (*env)->GetByteArrayElements(env, byte_array, NULL); - gcry_mpi_t result; - gcry_mpi_scan(&result, GCRYMPI_FMT_USG, byte_data, byte_length, NULL); - (*env)->ReleaseByteArrayElements(env, byte_array, byte_data, JNI_ABORT); - return result; + return bytearray_to_mpi(env, byte_array); } static jint mpi_to_jint(gcry_mpi_t mpi) { @@ -144,7 +200,7 @@ static jint mpi_to_jint(gcry_mpi_t mpi) { } static jobject buff_to_ecpoint(JNIEnv *env, gcry_buffer_t buff) { - jint coord_size = (buff.size - 1) / 2; + jint coord_size = (buff.len - 1) / 2; jmethodID biginteger_init = (*env)->GetMethodID(env, biginteger_class, "", "(I[B)V"); jbyteArray x_bytes = (*env)->NewByteArray(env, coord_size); @@ -235,7 +291,7 @@ static jobject generate_from_sexp(JNIEnv *env, gcry_sexp_t gen_sexp) { jobject ec_pub_param_spec = (*env)->NewLocalRef(env, ec_param_spec); jmethodID ec_pub_init = (*env)->GetMethodID(env, pubkey_class, "", "([BLjava/security/spec/ECParameterSpec;)V"); - jobject pubkey = (*env)->NewObject(env, pubkey_class, ec_pub_init, pub_bytes, ec_param_spec); + jobject pubkey = (*env)->NewObject(env, pubkey_class, ec_pub_init, pub_bytes, ec_pub_param_spec); jobject ec_priv_param_spec = (*env)->NewLocalRef(env, ec_param_spec); jmethodID ec_priv_init = (*env)->GetMethodID(env, privkey_class, "", "([BLjava/security/spec/ECParameterSpec;)V"); @@ -265,7 +321,6 @@ JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPai } JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPairGeneratorSpi_00024Gcrypt_generate__Ljava_security_spec_AlgorithmParameterSpec_2Ljava_security_SecureRandom_2(JNIEnv *env, jobject this, jobject params, jobject random) { - if ((*env)->IsInstanceOf(env, params, ec_parameter_spec_class)) { return NULL; } else if ((*env)->IsInstanceOf(env, params, ecgen_parameter_spec_class)) { @@ -281,4 +336,271 @@ JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPai } else { return NULL; } +} + +static gcry_sexp_t create_key(JNIEnv *env, jobject ec_param_spec, const char *key_fmt, gcry_mpi_t q, gcry_mpi_t d) { + gcry_sexp_t inner; + gcry_mpi_t p, a, b, g, n, h; + + jmethodID get_curve = (*env)->GetMethodID(env, ec_parameter_spec_class, "getCurve", "()Ljava/security/spec/EllipticCurve;"); + jobject elliptic_curve = (*env)->CallObjectMethod(env, ec_param_spec, get_curve); + + jmethodID get_field = (*env)->GetMethodID(env, elliptic_curve_class, "getField", "()Ljava/security/spec/ECField;"); + jobject field = (*env)->CallObjectMethod(env, elliptic_curve, get_field); + + jmethodID get_bits = (*env)->GetMethodID(env, fp_field_class, "getFieldSize", "()I"); + jint bits = (*env)->CallIntMethod(env, field, get_bits); + jint bytes = (bits + 7) / 8; + + jmethodID get_a = (*env)->GetMethodID(env, elliptic_curve_class, "getA", "()Ljava/math/BigInteger;"); + jobject big_a = (*env)->CallObjectMethod(env, elliptic_curve, get_a); + a = biginteger_to_mpi(env, big_a); + + jmethodID get_b = (*env)->GetMethodID(env, elliptic_curve_class, "getB", "()Ljava/math/BigInteger;"); + jobject big_b = (*env)->CallObjectMethod(env, elliptic_curve, get_b); + b = biginteger_to_mpi(env, big_b); + + jmethodID get_p = (*env)->GetMethodID(env, fp_field_class, "getP", "()Ljava/math/BigInteger;"); + jobject big_p = (*env)->CallObjectMethod(env, field, get_p); + p = biginteger_to_mpi(env, big_p); + + jmethodID get_g = (*env)->GetMethodID(env, ec_parameter_spec_class, "getGenerator", "()Ljava/security/spec/ECPoint;"); + jobject g_point = (*env)->CallObjectMethod(env, ec_param_spec, get_g); + + jmethodID get_x = (*env)->GetMethodID(env, point_class, "getAffineX", "()Ljava/math/BigInteger;"); + jobject gx = (*env)->CallObjectMethod(env, g_point, get_x); + + jmethodID get_y = (*env)->GetMethodID(env, point_class, "getAffineY", "()Ljava/math/BigInteger;"); + jobject gy = (*env)->CallObjectMethod(env, g_point, get_y); + + jmethodID to_byte_array = (*env)->GetMethodID(env, biginteger_class, "toByteArray", "()[B"); + + jbyteArray gx_bytes = (jbyteArray) (*env)->CallObjectMethod(env, gx, to_byte_array); + size_t gx_len = (*env)->GetArrayLength(env, gx_bytes); + jbyteArray gy_bytes = (jbyteArray) (*env)->CallObjectMethod(env, gy, to_byte_array); + size_t gy_len = (*env)->GetArrayLength(env, gy_bytes); + unsigned char g_data[1 + 2 * bytes]; + g_data[0] = 0x04; + jbyte *gx_data = (*env)->GetByteArrayElements(env, gx_bytes, NULL); + memcpy(g_data + 1, gx_data + (gx_len - bytes), bytes); + (*env)->ReleaseByteArrayElements(env, gx_bytes, gx_data, JNI_ABORT); + jbyte *gy_data = (*env)->GetByteArrayElements(env, gy_bytes, NULL); + memcpy(g_data + 1 + bytes, gy_data + (gy_len - bytes), bytes); + (*env)->ReleaseByteArrayElements(env, gy_bytes, gy_data, JNI_ABORT); + + gcry_mpi_scan(&g, GCRYMPI_FMT_USG, g_data, 1 + 2 * bytes, NULL); + + jmethodID get_n = (*env)->GetMethodID(env, ec_parameter_spec_class, "getOrder", "()Ljava/math/BigInteger;"); + jobject big_n = (*env)->CallObjectMethod(env, ec_param_spec, get_n); + n = biginteger_to_mpi(env, big_n); + + jmethodID get_h = (*env)->GetMethodID(env, ec_parameter_spec_class, "getCofactor", "()I"); + jint jh = (*env)->CallIntMethod(env, ec_param_spec, get_h); + h = gcry_mpi_set_ui(NULL, jh); + + if (q && d) { + gcry_sexp_build(&inner, NULL, "(ecc (flags param) (p %M) (a %M) (b %M) (g %M) (n %M) (h %M) (q %M) (d %M))", p, a, b, g, n, h, q, d, NULL); + } else if (q && !d) { + gcry_sexp_build(&inner, NULL, "(ecc (flags param) (p %M) (a %M) (b %M) (g %M) (n %M) (h %M) (q %M))", p, a, b, g, n, h, q, NULL); + } else if (!q && d) { + gcry_sexp_build(&inner, NULL, "(ecc (flags param) (p %M) (a %M) (b %M) (g %M) (n %M) (h %M) (d %M))", p, a, b, g, n, h, d, NULL); + } + gcry_sexp_t result; + gcry_sexp_build(&result, NULL, key_fmt, inner, NULL); + gcry_sexp_release(inner); + return result; +} + +static gcry_sexp_t create_pubkey(JNIEnv *env, jobject ec_param_spec, jbyteArray pubkey) { + gcry_mpi_t q = bytearray_to_mpi(env, pubkey); + gcry_sexp_t result = create_key(env, ec_param_spec, "(public-key %S)", q, NULL); + gcry_mpi_release(q); + return result; +} + +static gcry_sexp_t create_privkey(JNIEnv *env, jobject ec_param_spec, jbyteArray pubkey, jbyteArray privkey) { + gcry_mpi_t q = bytearray_to_mpi(env, pubkey); + gcry_mpi_t d = bytearray_to_mpi(env, privkey); + gcry_sexp_t result = create_key(env, ec_param_spec, "(private-key %S)", q, d); + gcry_mpi_release(q); + gcry_mpi_release(d); + return result; +} + +JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_00024Gcrypt_generateSecret___3B_3BLjava_security_spec_ECParameterSpec_2(JNIEnv *env, jobject this, jbyteArray pubkey, jbyteArray privkey, jobject params) { + jbyteArray result = NULL; + gcry_sexp_t pub = create_pubkey(env, params, pubkey); + gcry_mpi_t priv = bytearray_to_mpi(env, privkey); + + gcry_sexp_t enc_sexp; + gcry_sexp_build(&enc_sexp, NULL, "(data (flags raw) (value %M))", priv, NULL); + gcry_sexp_t res_sexp; + // TODO: figure out why ecc_encrypt_raw takes signed representation.. Nobody uses that., everybody uses unsigned reduced mod p. + gcry_error_t err = gcry_pk_encrypt(&res_sexp, enc_sexp, pub); + if (gcry_err_code(err) != GPG_ERR_NO_ERROR) { + throw_new_var(env, "java/security/GeneralSecurityException", "Error performing ECDH. Error: %ui", gcry_err_code(err)); + goto end; + } + + gcry_mpi_t derived; + err = gcry_sexp_extract_param(res_sexp, NULL, "s", &derived, NULL); + + size_t derived_bytes; + gcry_mpi_print(GCRYMPI_FMT_USG, NULL, 0, &derived_bytes, derived); + size_t coord_bytes = (derived_bytes - 1) / 2; + result = mpi_to_bytearray0(env, derived, 1, coord_bytes); + + gcry_mpi_release(derived); +end: + gcry_sexp_release(enc_sexp); + gcry_sexp_release(res_sexp); + gcry_sexp_release(pub); + gcry_mpi_release(priv); + return result; +} + +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_00024Gcrypt_generateSecret___3B_3BLjava_security_spec_ECParameterSpec_2Ljava_lang_String_2(JNIEnv *env, jobject this, jbyteArray pubkey, jbyteArray privkey, jobject params, jstring algorithm) { + throw_new(env, "java/lang/UnsupportedOperationException", "Not supported."); + return NULL; +} + +static int starts_with(const char *whole, const char *prefix) { + return !strncmp(whole, prefix, strlen(prefix)); +} + +static int get_hash_algo(const char *sig_type) { + if (starts_with(sig_type, "SHA1")) { + return GCRY_MD_SHA1; + } else if (starts_with(sig_type, "SHA224")) { + return GCRY_MD_SHA224; + } else if (starts_with(sig_type, "SHA256")) { + return GCRY_MD_SHA256; + } else if (starts_with(sig_type, "SHA384")) { + return GCRY_MD_SHA384; + } else if (starts_with(sig_type, "SHA512")) { + return GCRY_MD_SHA512; + } else { + return GCRY_MD_NONE; + } +} + +static const char *get_sig_algo(const char *sig_type) { + const char *start = strstr(sig_type, "with") + strlen("with"); + if (starts_with(start, "ECDSA")) { + return NULL; + } else if (starts_with(start, "ECDDSA")) { + return "rfc6979"; + } else { + return NULL; + } +} + +static void get_sign_data_sexp(JNIEnv *env, gcry_sexp_t *result, jobject this, jbyteArray data) { + jclass sig_class = (*env)->FindClass(env, "cz/crcs/ectester/standalone/libs/jni/NativeSignatureSpi$Gcrypt"); + jfieldID type_id = (*env)->GetFieldID(env, sig_class, "type", "Ljava/lang/String;"); + jstring type = (jstring)(*env)->GetObjectField(env, this, type_id); + const char* type_data = (*env)->GetStringUTFChars(env, type, NULL); + int hash_algo = get_hash_algo(type_data); + const char *sig_algo = get_sig_algo(type_data); + const char *with = strstr(type_data, "with"); + char hash_name[with - type_data + 1]; + memcpy(hash_name, type_data, with - type_data); + for (size_t i = 0; i < with - type_data; ++i) { + hash_name[i] = tolower(hash_name[i]); + } + hash_name[with - type_data] = 0; + (*env)->ReleaseStringUTFChars(env, type, type_data); + + if (hash_algo == GCRY_MD_NONE) { + gcry_mpi_t data_mpi = bytearray_to_mpi(env, data); + gcry_sexp_build(result, NULL, "(data (flags raw param) (value %M))", data_mpi); + gcry_mpi_release(data_mpi); + } else { + unsigned int hash_len = gcry_md_get_algo_dlen(hash_algo); + size_t data_len = (*env)->GetArrayLength(env, data); + jbyte *data_bytes = (*env)->GetByteArrayElements(env, data, NULL); + unsigned char out_hash[hash_len]; + gcry_md_hash_buffer(hash_algo, out_hash, data_bytes, data_len); + (*env)->ReleaseByteArrayElements(env, data, data_bytes, JNI_ABORT); + gcry_mpi_t hash_mpi; + gcry_mpi_scan(&hash_mpi, GCRYMPI_FMT_USG, out_hash, hash_len, NULL); + if (!sig_algo) { + gcry_sexp_build(result, NULL, "(data (flags raw param) (value %M))", hash_mpi); + } else { + gcry_sexp_build(result, NULL, "(data (flags %s param) (hash %s %M))", sig_algo, hash_name, hash_mpi); + } + gcry_mpi_release(hash_mpi); + } +} + +JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_00024Gcrypt_sign(JNIEnv *env, jobject this, jbyteArray data, jbyteArray privkey, jobject params) { + jbyteArray result = NULL; + gcry_sexp_t priv_sexp = create_privkey(env, params, NULL, privkey); + + gcry_sexp_t data_sexp; + get_sign_data_sexp(env, &data_sexp, this, data); + + gcry_sexp_t res_sexp; + gcry_error_t err = gcry_pk_sign(&res_sexp, data_sexp, priv_sexp); + if (gcry_err_code(err) != GPG_ERR_NO_ERROR) { + throw_new_var(env, "java/security/GeneralSecurityException", "Error performing ECDSA. Error: %ui", gcry_err_code(err)); + goto release_init; + } + + gcry_buffer_t r_buf = {0}; + gcry_buffer_t s_buf = {0}; + err = gcry_sexp_extract_param(res_sexp, "ecdsa", "&rs", &r_buf, &s_buf, NULL); + if (gcry_err_code(err) != GPG_ERR_NO_ERROR) { + throw_new_var(env, "java/security/GeneralSecurityException", "Error extracting ECDSA output. Error: %ui", gcry_err_code(err)); + goto release_res; + } + result = asn1_der_encode(env, r_buf.data, r_buf.len, s_buf.data, s_buf.len); + + gcry_free(r_buf.data); + gcry_free(s_buf.data); +release_res: + gcry_sexp_release(res_sexp); +release_init: + gcry_sexp_release(priv_sexp); + gcry_sexp_release(data_sexp); + return result; +} + +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_00024Gcrypt_verify(JNIEnv *env, jobject this, jbyteArray sig, jbyteArray data, jbyteArray pubkey, jobject params) { + jboolean result = JNI_FALSE; + gcry_sexp_t pub_sexp = create_pubkey(env, params, pubkey); + + gcry_sexp_t data_sexp; + get_sign_data_sexp(env, &data_sexp, this, data); + + size_t r_len, s_len; + jbyte *r_data, *s_data; + bool decode = asn1_der_decode(env, sig, &r_data, &r_len, &s_data, &s_len); + if (!decode) { + throw_new(env, "java/security/GeneralSecurityException", "Error decoding sig."); + goto release_init; + } + + gcry_mpi_t r_mpi, s_mpi; + gcry_mpi_scan(&r_mpi, GCRYMPI_FMT_USG, r_data, r_len, NULL); + gcry_mpi_scan(&s_mpi, GCRYMPI_FMT_USG, s_data, s_len, NULL); + free(r_data); + free(s_data); + + gcry_sexp_t sig_sexp; + gcry_sexp_build(&sig_sexp, NULL, "(sig-val (ecdsa (r %M) (s %M)))", r_mpi, s_mpi); + gcry_error_t err = gcry_pk_verify(sig_sexp, data_sexp, pub_sexp); + if (gcry_err_code(err) != GPG_ERR_NO_ERROR) { + if (gcry_err_code(err) != GPG_ERR_BAD_SIGNATURE) { + throw_new(env, "java/security/GeneralSecurityException", "Error verif sig."); + goto release_init; + } + } else { + result = JNI_TRUE; + } + +release_init: + gcry_sexp_release(pub_sexp); + gcry_sexp_release(data_sexp); + return result; } \ No newline at end of file diff --git a/src/cz/crcs/ectester/standalone/libs/jni/native.h b/src/cz/crcs/ectester/standalone/libs/jni/native.h index e86a847..47031e4 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/native.h +++ b/src/cz/crcs/ectester/standalone/libs/jni/native.h @@ -1191,3 +1191,79 @@ JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyPai } #endif #endif +/* Header for class cz_crcs_ectester_standalone_libs_jni_NativeECPublicKey_Gcrypt */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_jni_NativeECPublicKey_Gcrypt +#define _Included_cz_crcs_ectester_standalone_libs_jni_NativeECPublicKey_Gcrypt +#ifdef __cplusplus +extern "C" { +#endif +#ifdef __cplusplus +} +#endif +#endif +/* Header for class cz_crcs_ectester_standalone_libs_jni_NativeECPrivateKey_Gcrypt */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_jni_NativeECPrivateKey_Gcrypt +#define _Included_cz_crcs_ectester_standalone_libs_jni_NativeECPrivateKey_Gcrypt +#ifdef __cplusplus +extern "C" { +#endif +#ifdef __cplusplus +} +#endif +#endif +/* Header for class cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_Gcrypt */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_Gcrypt +#define _Included_cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_Gcrypt +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_Gcrypt + * Method: generateSecret + * Signature: ([B[BLjava/security/spec/ECParameterSpec;)[B + */ +JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_00024Gcrypt_generateSecret___3B_3BLjava_security_spec_ECParameterSpec_2 + (JNIEnv *, jobject, jbyteArray, jbyteArray, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_Gcrypt + * Method: generateSecret + * Signature: ([B[BLjava/security/spec/ECParameterSpec;Ljava/lang/String;)Ljavax/crypto/SecretKey; + */ +JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKeyAgreementSpi_00024Gcrypt_generateSecret___3B_3BLjava_security_spec_ECParameterSpec_2Ljava_lang_String_2 + (JNIEnv *, jobject, jbyteArray, jbyteArray, jobject, jstring); + +#ifdef __cplusplus +} +#endif +#endif +/* Header for class cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_Gcrypt */ + +#ifndef _Included_cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_Gcrypt +#define _Included_cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_Gcrypt +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_Gcrypt + * Method: sign + * Signature: ([B[BLjava/security/spec/ECParameterSpec;)[B + */ +JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_00024Gcrypt_sign + (JNIEnv *, jobject, jbyteArray, jbyteArray, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_Gcrypt + * Method: verify + * Signature: ([B[B[BLjava/security/spec/ECParameterSpec;)Z + */ +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSignatureSpi_00024Gcrypt_verify + (JNIEnv *, jobject, jbyteArray, jbyteArray, jbyteArray, jobject); + +#ifdef __cplusplus +} +#endif +#endif -- cgit v1.2.3-70-g09d2 From f59aaf972a83330206e8b0d2e1819f45da38f74e Mon Sep 17 00:00:00 2001 From: J08nY Date: Sun, 11 Nov 2018 17:51:53 +0100 Subject: Add WolfCrypt provider to testing. --- README.md | 1 + docs/LIBS.md | 2 ++ lib/wolfcrypt-jni.jar | Bin 0 -> 83453 bytes nbproject/standalone/manifest.mf | 2 +- nbproject/standalone/project.properties | 1 + .../crcs/ectester/standalone/ECTesterStandalone.java | 13 ++++++++++++- .../crcs/ectester/standalone/libs/WolfCryptLib.java | 18 ++++++++++++++++++ 7 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 lib/wolfcrypt-jni.jar create mode 100644 src/cz/crcs/ectester/standalone/libs/WolfCryptLib.java (limited to 'src/cz/crcs/ectester/standalone/ECTesterStandalone.java') diff --git a/README.md b/README.md index d70ae9b..d23922d 100644 --- a/README.md +++ b/README.md @@ -270,6 +270,7 @@ Currently supported libraries include: - [Sun EC](https://docs.oracle.com/javase/7/docs/technotes/guides/security/SunProviders.html#SunEC) - [OpenSSL](https://www.openssl.org/) - [BoringSSL](https://boringssl.googlesource.com/boringssl) + - [wolfSSL](https://www.wolfssl.com/) - [Crypto++](https://cryptopp.com/) - [libtomcrypt](http://www.libtom.net/LibTomCrypt/) - [libgcrypt](https://www.gnupg.org/related_software/libgcrypt/) diff --git a/docs/LIBS.md b/docs/LIBS.md index faab84d..d41f98a 100644 --- a/docs/LIBS.md +++ b/docs/LIBS.md @@ -55,6 +55,8 @@ Libraries that ECTester can test. - Uses Lopez-Dahab (Montgomery) ladder, XZ coordinates (ec2_mont.c): Fast multiplication on elliptic curves over GF(2^m) without precomputation (Algorithm 2P) - Contains an implementation of IEEE P1363 algorithm A.10.3 using affine coordinates (ec2_aff.c) - Has some custom arithmetic for some of the NIST primes. + - [WolfCrypt](https://www.wolfssl.com) + - C + Java - [OpenSSL](https://www.openssl.org/) - C - For prime field curves: diff --git a/lib/wolfcrypt-jni.jar b/lib/wolfcrypt-jni.jar new file mode 100644 index 0000000..890ae14 Binary files /dev/null and b/lib/wolfcrypt-jni.jar differ diff --git a/nbproject/standalone/manifest.mf b/nbproject/standalone/manifest.mf index 5e8f5d9..ad7aacb 100644 --- a/nbproject/standalone/manifest.mf +++ b/nbproject/standalone/manifest.mf @@ -1,4 +1,4 @@ Manifest-Version: 1.0 -Class-Path: lib/bcprov-jdk15on-1.58.jar lib/jcardsim-3.0.4-SNAPSHOT.jar lib/commons-cli-1.4.jar lib/snakeyaml-1.19.jar +Class-Path: lib/bcprov-jdk15on-1.58.jar lib/wolfcrypt-jni.jar lib/jcardsim-3.0.4-SNAPSHOT.jar lib/commons-cli-1.4.jar lib/snakeyaml-1.19.jar Main-Class: cz.crcs.ectester.standalone.ECTesterStandalone diff --git a/nbproject/standalone/project.properties b/nbproject/standalone/project.properties index 6b6d440..29ad3cc 100644 --- a/nbproject/standalone/project.properties +++ b/nbproject/standalone/project.properties @@ -34,6 +34,7 @@ includes=**/common/**,**/standalone/**,**/data/**,**/applet/* jar.compress=true javac.classpath=\ lib/bcprov-jdk15on-1.58.jar:\ + lib/wolfcrypt-jni.jar:\ lib/jcardsim-3.0.4-SNAPSHOT.jar:\ lib/commons-cli-1.4.jar:\ lib/snakeyaml-1.19.jar diff --git a/src/cz/crcs/ectester/standalone/ECTesterStandalone.java b/src/cz/crcs/ectester/standalone/ECTesterStandalone.java index 7480215..b6f5478 100644 --- a/src/cz/crcs/ectester/standalone/ECTesterStandalone.java +++ b/src/cz/crcs/ectester/standalone/ECTesterStandalone.java @@ -49,6 +49,7 @@ import javax.xml.parsers.ParserConfigurationException; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.lang.reflect.Field; import java.nio.file.Files; import java.security.*; import java.security.interfaces.ECPrivateKey; @@ -66,7 +67,17 @@ import java.util.stream.Collectors; * @version v0.3.0 */ public class ECTesterStandalone { - private ProviderECLibrary[] libs = new ProviderECLibrary[]{new SunECLib(), new BouncyCastleLib(), new TomcryptLib(), new BotanLib(), new CryptoppLib(), new OpensslLib(), new BoringsslLib(), new GcryptLib(), new MscngLib()}; + private ProviderECLibrary[] libs = new ProviderECLibrary[]{ + new SunECLib(), + new BouncyCastleLib(), + new TomcryptLib(), + new BotanLib(), + new CryptoppLib(), + new OpensslLib(), + new BoringsslLib(), + new GcryptLib(), + new MscngLib(), + new WolfCryptLib()}; private Config cfg; private Options opts = new Options(); diff --git a/src/cz/crcs/ectester/standalone/libs/WolfCryptLib.java b/src/cz/crcs/ectester/standalone/libs/WolfCryptLib.java new file mode 100644 index 0000000..b58eb91 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/libs/WolfCryptLib.java @@ -0,0 +1,18 @@ +package cz.crcs.ectester.standalone.libs; + +import com.wolfssl.provider.jce.WolfCryptProvider; + +import java.util.HashSet; +import java.util.Set; + +public class WolfCryptLib extends ProviderECLibrary { + + public WolfCryptLib() { + super(new WolfCryptProvider()); + } + + @Override + public Set getCurves() { + return new HashSet<>(); + } +} -- cgit v1.2.3-70-g09d2 From 105228a6e842df46ba8523fc5214b3a8833cbef0 Mon Sep 17 00:00:00 2001 From: J08nY Date: Sun, 18 Nov 2018 19:37:59 +0100 Subject: Output duration of keygen and export as well. --- src/cz/crcs/ectester/reader/ECTesterReader.java | 5 +- .../ectester/standalone/ECTesterStandalone.java | 6 +- util/plot_gen.py | 151 +++++++++++++-------- 3 files changed, 97 insertions(+), 65 deletions(-) (limited to 'src/cz/crcs/ectester/standalone/ECTesterStandalone.java') diff --git a/src/cz/crcs/ectester/reader/ECTesterReader.java b/src/cz/crcs/ectester/reader/ECTesterReader.java index 377e2ff..abc7264 100644 --- a/src/cz/crcs/ectester/reader/ECTesterReader.java +++ b/src/cz/crcs/ectester/reader/ECTesterReader.java @@ -439,7 +439,7 @@ public class ECTesterReader { respWriter.outputResponse(allocate); OutputStreamWriter keysFile = FileUtil.openFiles(cfg.outputs); - keysFile.write("index;time;pubW;privS\n"); + keysFile.write("index;genTime;exportTime;pubW;privS\n"); int generated = 0; int retry = 0; @@ -451,7 +451,6 @@ public class ECTesterReader { Command.Generate generate = new Command.Generate(cardManager, ECTesterApplet.KEYPAIR_LOCAL); Response.Generate response = generate.send(); - long elapsed = response.getDuration(); respWriter.outputResponse(response); Response.Export export = new Command.Export(cardManager, ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.KEY_BOTH, EC_Consts.PARAMETERS_KEYPAIR).send(); @@ -469,7 +468,7 @@ public class ECTesterReader { String pub = ByteUtil.bytesToHex(export.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_W), false); String priv = ByteUtil.bytesToHex(export.getParameter(ECTesterApplet.KEYPAIR_LOCAL, EC_Consts.PARAMETER_S), false); - String line = String.format("%d;%d;%s;%s\n", generated, elapsed / 1000000, pub, priv); + String line = String.format("%d;%d;%d;%s;%s\n", generated, response.getDuration() / 1000000, export.getDuration() / 1000000, pub, priv); keysFile.write(line); keysFile.flush(); generated++; diff --git a/src/cz/crcs/ectester/standalone/ECTesterStandalone.java b/src/cz/crcs/ectester/standalone/ECTesterStandalone.java index b6f5478..e75274d 100644 --- a/src/cz/crcs/ectester/standalone/ECTesterStandalone.java +++ b/src/cz/crcs/ectester/standalone/ECTesterStandalone.java @@ -325,7 +325,7 @@ public class ECTesterStandalone { kpg.initialize(spec); } - System.out.println("index;nanotime;pubW;privS;secret"); + System.out.println("index;time[nano];pubW;privS;secret"); int amount = Integer.parseInt(cli.getOptionValue("ecdh.amount", "1")); for (int i = 0; i < amount; ++i) { @@ -435,7 +435,7 @@ public class ECTesterStandalone { kpg.initialize(new ECGenParameterSpec(curveName)); } - System.out.println("index;data;signtime;verifytime;pubW;privS;signature;verified"); + System.out.println("index;data;signTime[nano];verifyTime[nano];pubW;privS;signature;verified"); int amount = Integer.parseInt(cli.getOptionValue("ecdsa.amount", "1")); for (int i = 0; i < amount; ++i) { @@ -499,7 +499,7 @@ public class ECTesterStandalone { String curveName = cli.getOptionValue("generate.curve-name"); kpg.initialize(new ECGenParameterSpec(curveName)); } - System.out.println("index;nanotime;pubW;privS"); + System.out.println("index;time[nano];pubW;privS"); int amount = Integer.parseInt(cli.getOptionValue("generate.amount", "1")); for (int i = 0; i < amount; ++i) { diff --git a/util/plot_gen.py b/util/plot_gen.py index 98d8261..f6cd8e4 100755 --- a/util/plot_gen.py +++ b/util/plot_gen.py @@ -13,19 +13,24 @@ import numpy as np import matplotlib.pyplot as plt -import matplotlib.ticker as ticker -import matplotlib.colors as colors -from operator import itemgetter +from matplotlib import ticker, colors from copy import deepcopy import argparse +def hw(i): + res = 0 + while i: + res += 1 + i &= i - 1 + return res + if __name__ == "__main__": parser = argparse.ArgumentParser(description="Plot results of ECTester key generation timing.") parser.add_argument("-o", "--output", dest="output", type=argparse.FileType("wb"), help="Write image to [file], do not display.", metavar="file") - parser.add_argument("--pub", dest="pub", action="store_true", help="Show public key scatter plot.") - parser.add_argument("--priv", dest="priv", action="store_true", help="Show private key scatter plot.") - parser.add_argument("--hist", dest="hist", action="store_true", help="Show histogram.") - parser.add_argument("--hw-hist", dest="hw_hist", action="store_true", help="Show Hamming weight 2D histogram (private key Hamming weight and generation time).") + parser.add_argument("--priv", dest="priv", action="store_true", help="Show private key MSB heatmap plot.") + parser.add_argument("--hist", dest="hist", action="store_true", help="Show keygen time histogram.") + parser.add_argument("--export-hist", dest="export_hist", action="store_true", help="Show export time histogram.") + parser.add_argument("--hw-hist", dest="hw_hist", action="store_true", help="Show Hamming weight heatmap (private key Hamming weight and keygen time).") parser.add_argument("--skip-first", dest="skip_first", action="store_true", help="Skip first entry, as it's usually a large outlier.") parser.add_argument("-t", "--title", dest="title", type=str, nargs="?", default="", help="What title to give the figure.") parser.add_argument("file", type=str, help="The file to plot(csv).") @@ -35,99 +40,127 @@ if __name__ == "__main__": with open(opts.file, "r") as f: header = f.readline() header_names = header.split(";") + if len(header_names) not in (4, 5): + print("Bad data?") + exit(1) - plots = [opts.priv, opts.pub, opts.hist, opts.hw_hist] + plots = [opts.priv, opts.hist, opts.export_hist, opts.hw_hist] n_plots = sum(plots) if n_plots == 0: - n_plots = 4 - plots = [True, True, True, True] + if len(header_names) == 4: + n_plots = 3 + else: + n_plots = 4 + plots = [True for _ in range(n_plots)] + + if plots[2] and len(header_names) != 5: + n_plots = n_plots - 1 + if n_plots == 0: + print("Nothing to plot.") + exit(1) + plots[2] = False hx = lambda x: int(x, 16) - data = np.genfromtxt(opts.file, delimiter=";", skip_header=1, converters={2: hx, 3: hx}, dtype=np.dtype([("index","u4"), ("time","u4"), ("pub", "O"), ("priv", "O")])) + if len(header_names) == 4: + data = np.genfromtxt(opts.file, delimiter=";", skip_header=1, converters={2: hx, 3: hx}, dtype=np.dtype([("index", "u4"), ("gen_time", "u4"), ("pub", "O"), ("priv", "O")])) + else: + data = np.genfromtxt(opts.file, delimiter=";", skip_header=1, converters={3: hx, 4: hx}, dtype=np.dtype([("index", "u4"), ("gen_time", "u4"), ("export_time", "u4"), ("pub", "O"), ("priv", "O")])) + if opts.skip_first: data = data[1:] - if "nano" in header_names[1]: - unit = r"$\mu s$" - time_data = map(lambda x: x[1]//1000, data) - else: - unit = r"ms" - time_data = map(itemgetter(1), data) - time_data = list(time_data) - priv_data = list(map(itemgetter(2), data)) - pub_data = list(map(itemgetter(3), data)) + gen_time_data = data["gen_time"] + export_time_data = None + if "export_time" in data.dtype.names: + export_time_data = data["export_time"] + pub_data = data["pub"] + priv_data = data["priv"] + + + gen_unit = "ms" + if header_names[1].endswith("[nano]"): + gen_unit = r"$\mu s$" + gen_time_data = list(map(lambda x: x[1]//1000, gen_time_data)) + export_unit = "ms" + if len(header_names) == 5 and header_names[2].endswith("[nano]"): + export_unit = r"$\mu s$" + export_time_data = list(map(lambda x: x[1]//1000, export_time_data)) plt.style.use("ggplot") fig = plt.figure() layout_kwargs = {} if opts.title is None: fig.suptitle(opts.file) - layout_kwargs["rect"] = [0, 0.02, 1, 0.98] + #layout_kwargs["rect"] = [0, 0.02, 1, 0.98] elif opts.title: fig.suptitle(opts.title) - layout_kwargs["rect"] = [0, 0.02, 1, 0.98] - fig.tight_layout(**layout_kwargs) + #layout_kwargs["rect"] = [0, 0.02, 1, 0.98] + fig.tight_layout(**layout_kwargs) + + max_gen_time = max(gen_time_data) + min_gen_time = min(gen_time_data) + bit_size = len(bin(max(priv_data))) - 2 + + cmap = deepcopy(plt.cm.plasma) + cmap.set_bad("black") plot_i = 1 if plots[0]: axe_private = fig.add_subplot(n_plots, 1, plot_i) - axe_private.scatter(time_data, priv_data, marker="x", s=10) - axe_private.set_ylabel("private key value\n(big endian)") - axe_private.set_xlabel("time ({})".format(unit)) + priv_msb = np.array(list(map(lambda x: x >> (bit_size - 8), priv_data)), dtype=np.dtype("u1")) + heatmap, xedges, yedges = np.histogram2d(priv_msb, gen_time_data, bins=[128, max_gen_time - min_gen_time]) + extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]] + axe_private.imshow(heatmap.T, extent=extent, aspect="auto", cmap=cmap, origin="low", interpolation="nearest", norm=colors.LogNorm()) + axe_private.set_xlabel("private key MSB value\n(big endian)") + axe_private.set_ylabel("time ({})".format(gen_unit)) plot_i += 1 if plots[1]: - axe_public = fig.add_subplot(n_plots, 1, plot_i) - axe_public.scatter(time_data, pub_data, marker="x", s=10) - axe_public.set_ylabel("public key value\n(big endian)") - axe_public.set_xlabel("time ({})".format(unit)) + axe_hist = fig.add_subplot(n_plots, 1, plot_i) + time_max = max(gen_time_data) + time_min = min(gen_time_data) + time_avg = np.average(gen_time_data) + time_median = np.median(gen_time_data) + axe_hist.hist(gen_time_data, bins=int((time_max - time_min)/1.2), log=True) + axe_hist.axvline(x=time_avg, alpha=0.7, linestyle="dotted", color="blue", label="avg = {}".format(time_avg)) + axe_hist.axvline(x=time_median, alpha=0.7, linestyle="dotted", color="green", label="median = {}".format(time_median)) + axe_hist.set_ylabel("count\n(log)") + axe_hist.set_xlabel("keygen time ({})".format(gen_unit)) + axe_hist.xaxis.set_major_locator(ticker.MultipleLocator()) + axe_hist.legend(loc="best") plot_i += 1 if plots[2]: axe_hist = fig.add_subplot(n_plots, 1, plot_i) - time_max = max(time_data) - time_avg = np.average(time_data) - time_median = np.median(time_data) - axe_hist.hist(time_data, bins=time_max//3, log=True) - axe_hist.axvline(x=time_avg, alpha=0.7, linestyle="dotted", color="red", label="avg = {}".format(time_avg)) + time_max = max(export_time_data) + time_min = min(export_time_data) + time_avg = np.average(export_time_data) + time_median = np.median(export_time_data) + axe_hist.hist(export_time_data, bins=int((time_max - time_min)/1.2), log=True) + axe_hist.axvline(x=time_avg, alpha=0.7, linestyle="dotted", color="blue", label="avg = {}".format(time_avg)) axe_hist.axvline(x=time_median, alpha=0.7, linestyle="dotted", color="green", label="median = {}".format(time_median)) axe_hist.set_ylabel("count\n(log)") - axe_hist.set_xlabel("time ({})".format(unit)) - axe_hist.xaxis.set_major_locator(ticker.MaxNLocator()) + axe_hist.set_xlabel("export time ({})".format(export_unit)) + axe_hist.xaxis.set_major_locator(ticker.MultipleLocator()) axe_hist.legend(loc="best") plot_i += 1 if plots[3]: - priv_bit_bins = {} - for i in range(len(data)): - skey = priv_data[i] - time = time_data[i] - skey_hw = 0 - while skey: - skey_hw += 1 - skey &= skey - 1 - if skey_hw in priv_bit_bins: - priv_bit_bins[skey_hw].append(time) - else: - priv_bit_bins[skey_hw] = [time] - priv_bit_x = [] - priv_bit_y = [] - for k,v in priv_bit_bins.items(): - priv_bit_x.extend([k] * len(v)) - priv_bit_y.extend(v) axe_priv_hist = fig.add_subplot(n_plots, 1, plot_i) - h, xe, ye = np.histogram2d(priv_bit_x, priv_bit_y, bins=[max(priv_bit_bins) - min(priv_bit_bins), (max(time_data) - min(time_data))//5]) - cmap = deepcopy(plt.cm.plasma) - cmap.set_bad("black") + priv_hw = np.array(list(map(hw, priv_data)), dtype=np.dtype("u2")) + h, xe, ye = np.histogram2d(priv_hw, gen_time_data, bins=[max(priv_hw) - min(priv_hw), max_gen_time - min_gen_time]) im = axe_priv_hist.imshow(h.T, origin="low", cmap=cmap, aspect="auto", extent=[xe[0], xe[-1], ye[0], ye[-1]], norm=colors.LogNorm()) + axe_priv_hist.axvline(x=bit_size//2, alpha=0.7, linestyle="dotted", color="white", label=str(bit_size//2) + " bits") axe_priv_hist.set_xlabel("private key Hamming weight") - axe_priv_hist.set_ylabel("time ({})".format(unit)) + axe_priv_hist.set_ylabel("time ({})".format(gen_unit)) + axe_priv_hist.legend(loc="best") fig.colorbar(im, ax=axe_priv_hist) if plot_i > 2: - fig.text(0.01, 0.02, "Data size: {}".format(len(time_data)), size="small") + fig.text(0.01, 0.02, "Data size: {}".format(len(gen_time_data)), size="small") if opts.output is None: + plt.tight_layout() plt.show() else: fig.set_size_inches(12, 10) -- cgit v1.2.3-70-g09d2 From 7e524b3ac2898c4373be9a3c81733ba725f9772d Mon Sep 17 00:00:00 2001 From: J08nY Date: Sun, 18 Nov 2018 20:01:23 +0100 Subject: Fix keygen plotting. --- .../ectester/standalone/ECTesterStandalone.java | 2 +- util/plot_gen.py | 75 ++++++++++++++-------- 2 files changed, 50 insertions(+), 27 deletions(-) (limited to 'src/cz/crcs/ectester/standalone/ECTesterStandalone.java') diff --git a/src/cz/crcs/ectester/standalone/ECTesterStandalone.java b/src/cz/crcs/ectester/standalone/ECTesterStandalone.java index e75274d..d740579 100644 --- a/src/cz/crcs/ectester/standalone/ECTesterStandalone.java +++ b/src/cz/crcs/ectester/standalone/ECTesterStandalone.java @@ -502,7 +502,7 @@ public class ECTesterStandalone { System.out.println("index;time[nano];pubW;privS"); int amount = Integer.parseInt(cli.getOptionValue("generate.amount", "1")); - for (int i = 0; i < amount; ++i) { + for (int i = 0; i < amount || amount == 0; ++i) { long elapsed = -System.nanoTime(); KeyPair kp = kpg.genKeyPair(); elapsed += System.nanoTime(); diff --git a/util/plot_gen.py b/util/plot_gen.py index f6cd8e4..c07fc91 100755 --- a/util/plot_gen.py +++ b/util/plot_gen.py @@ -24,14 +24,21 @@ def hw(i): i &= i - 1 return res +def moving_average(a, n) : + ret = np.cumsum(a, dtype=float) + ret[n:] = ret[n:] - ret[:-n] + return ret[n - 1:] / n + if __name__ == "__main__": parser = argparse.ArgumentParser(description="Plot results of ECTester key generation timing.") parser.add_argument("-o", "--output", dest="output", type=argparse.FileType("wb"), help="Write image to [file], do not display.", metavar="file") parser.add_argument("--priv", dest="priv", action="store_true", help="Show private key MSB heatmap plot.") parser.add_argument("--hist", dest="hist", action="store_true", help="Show keygen time histogram.") parser.add_argument("--export-hist", dest="export_hist", action="store_true", help="Show export time histogram.") + parser.add_argument("--avg", dest="avg", action="store_true", help="Show moving average of keygen time.") parser.add_argument("--hw-hist", dest="hw_hist", action="store_true", help="Show Hamming weight heatmap (private key Hamming weight and keygen time).") - parser.add_argument("--skip-first", dest="skip_first", action="store_true", help="Skip first entry, as it's usually a large outlier.") + parser.add_argument("--log", dest="log", action="store_true", help="Use logarithmic scale.") + parser.add_argument("--skip-first", dest="skip_first", nargs="?", const=1, type=int, help="Skip first entry, as it's usually a large outlier.") parser.add_argument("-t", "--title", dest="title", type=str, nargs="?", default="", help="What title to give the figure.") parser.add_argument("file", type=str, help="The file to plot(csv).") @@ -44,14 +51,16 @@ if __name__ == "__main__": print("Bad data?") exit(1) - plots = [opts.priv, opts.hist, opts.export_hist, opts.hw_hist] + plots = [opts.priv, opts.hist, opts.export_hist, opts.avg, opts.hw_hist] n_plots = sum(plots) if n_plots == 0: + plots = [True for _ in range(5)] if len(header_names) == 4: - n_plots = 3 - else: n_plots = 4 - plots = [True for _ in range(n_plots)] + plots[2] = False + else: + n_plots = 5 + if plots[2] and len(header_names) != 5: n_plots = n_plots - 1 @@ -67,7 +76,7 @@ if __name__ == "__main__": data = np.genfromtxt(opts.file, delimiter=";", skip_header=1, converters={3: hx, 4: hx}, dtype=np.dtype([("index", "u4"), ("gen_time", "u4"), ("export_time", "u4"), ("pub", "O"), ("priv", "O")])) if opts.skip_first: - data = data[1:] + data = data[opts.skip_first:] gen_time_data = data["gen_time"] export_time_data = None @@ -80,21 +89,21 @@ if __name__ == "__main__": gen_unit = "ms" if header_names[1].endswith("[nano]"): gen_unit = r"$\mu s$" - gen_time_data = list(map(lambda x: x[1]//1000, gen_time_data)) + gen_time_data = list(map(lambda x: x//1000, gen_time_data)) export_unit = "ms" if len(header_names) == 5 and header_names[2].endswith("[nano]"): export_unit = r"$\mu s$" - export_time_data = list(map(lambda x: x[1]//1000, export_time_data)) + export_time_data = list(map(lambda x: x//1000, export_time_data)) plt.style.use("ggplot") fig = plt.figure() layout_kwargs = {} if opts.title is None: fig.suptitle(opts.file) - #layout_kwargs["rect"] = [0, 0.02, 1, 0.98] + layout_kwargs["rect"] = [0, 0.02, 1, 0.98] elif opts.title: fig.suptitle(opts.title) - #layout_kwargs["rect"] = [0, 0.02, 1, 0.98] + layout_kwargs["rect"] = [0, 0.02, 1, 0.98] fig.tight_layout(**layout_kwargs) max_gen_time = max(gen_time_data) @@ -104,29 +113,31 @@ if __name__ == "__main__": cmap = deepcopy(plt.cm.plasma) cmap.set_bad("black") + norm = colors.Normalize() + if opts.log: + norm = colors.LogNorm() + plot_i = 1 if plots[0]: axe_private = fig.add_subplot(n_plots, 1, plot_i) priv_msb = np.array(list(map(lambda x: x >> (bit_size - 8), priv_data)), dtype=np.dtype("u1")) - heatmap, xedges, yedges = np.histogram2d(priv_msb, gen_time_data, bins=[128, max_gen_time - min_gen_time]) + heatmap, xedges, yedges = np.histogram2d(priv_msb, gen_time_data, bins=[256, max_gen_time - min_gen_time]) extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]] - axe_private.imshow(heatmap.T, extent=extent, aspect="auto", cmap=cmap, origin="low", interpolation="nearest", norm=colors.LogNorm()) + axe_private.imshow(heatmap.T, extent=extent, aspect="auto", cmap=cmap, origin="low", interpolation="nearest", norm=norm) axe_private.set_xlabel("private key MSB value\n(big endian)") - axe_private.set_ylabel("time ({})".format(gen_unit)) + axe_private.set_ylabel("keygen time ({})".format(gen_unit)) plot_i += 1 if plots[1]: axe_hist = fig.add_subplot(n_plots, 1, plot_i) - time_max = max(gen_time_data) - time_min = min(gen_time_data) time_avg = np.average(gen_time_data) time_median = np.median(gen_time_data) - axe_hist.hist(gen_time_data, bins=int((time_max - time_min)/1.2), log=True) + axe_hist.hist(gen_time_data, bins=max_gen_time - min_gen_time, log=opts.log) axe_hist.axvline(x=time_avg, alpha=0.7, linestyle="dotted", color="blue", label="avg = {}".format(time_avg)) axe_hist.axvline(x=time_median, alpha=0.7, linestyle="dotted", color="green", label="median = {}".format(time_median)) - axe_hist.set_ylabel("count\n(log)") + axe_hist.set_ylabel("count" + ("\n(log)" if opts.log else "")) axe_hist.set_xlabel("keygen time ({})".format(gen_unit)) - axe_hist.xaxis.set_major_locator(ticker.MultipleLocator()) + axe_hist.xaxis.set_major_locator(ticker.MaxNLocator()) axe_hist.legend(loc="best") plot_i += 1 @@ -136,31 +147,43 @@ if __name__ == "__main__": time_min = min(export_time_data) time_avg = np.average(export_time_data) time_median = np.median(export_time_data) - axe_hist.hist(export_time_data, bins=int((time_max - time_min)/1.2), log=True) + axe_hist.hist(export_time_data, bins=time_max - time_min, log=opts.log) axe_hist.axvline(x=time_avg, alpha=0.7, linestyle="dotted", color="blue", label="avg = {}".format(time_avg)) axe_hist.axvline(x=time_median, alpha=0.7, linestyle="dotted", color="green", label="median = {}".format(time_median)) - axe_hist.set_ylabel("count\n(log)") + axe_hist.set_ylabel("count" + ("\n(log)" if opts.log else "")) axe_hist.set_xlabel("export time ({})".format(export_unit)) - axe_hist.xaxis.set_major_locator(ticker.MultipleLocator()) + axe_hist.xaxis.set_major_locator(ticker.MaxNLocator()) axe_hist.legend(loc="best") plot_i += 1 if plots[3]: + axe_avg = fig.add_subplot(n_plots, 1, plot_i) + #if len(header_names) == 5: + # axe_other = axe_avg.twinx() + # axe_other.plot(moving_average(export_time_data, 100), color="green", alpha=0.6, label="export, window = 100") + # axe_other.plot(moving_average(export_time_data, 1000), color="yellow", alpha=0.6, label="export, window = 1000") + # axe_other.legend(loc="lower right") + axe_avg.plot(moving_average(gen_time_data, 100), label="window = 100") + axe_avg.plot(moving_average(gen_time_data, 1000), label="window = 1000") + axe_avg.set_ylabel("keygen time ({})".format(gen_unit)) + axe_avg.set_xlabel("index") + axe_avg.legend(loc="best") + plot_i += 1 + + if plots[4]: axe_priv_hist = fig.add_subplot(n_plots, 1, plot_i) priv_hw = np.array(list(map(hw, priv_data)), dtype=np.dtype("u2")) h, xe, ye = np.histogram2d(priv_hw, gen_time_data, bins=[max(priv_hw) - min(priv_hw), max_gen_time - min_gen_time]) - im = axe_priv_hist.imshow(h.T, origin="low", cmap=cmap, aspect="auto", extent=[xe[0], xe[-1], ye[0], ye[-1]], norm=colors.LogNorm()) + im = axe_priv_hist.imshow(h.T, origin="low", cmap=cmap, aspect="auto", extent=[xe[0], xe[-1], ye[0], ye[-1]], norm=norm) axe_priv_hist.axvline(x=bit_size//2, alpha=0.7, linestyle="dotted", color="white", label=str(bit_size//2) + " bits") axe_priv_hist.set_xlabel("private key Hamming weight") - axe_priv_hist.set_ylabel("time ({})".format(gen_unit)) + axe_priv_hist.set_ylabel("keygen time ({})".format(gen_unit)) axe_priv_hist.legend(loc="best") fig.colorbar(im, ax=axe_priv_hist) - if plot_i > 2: - fig.text(0.01, 0.02, "Data size: {}".format(len(gen_time_data)), size="small") + fig.text(0.01, 0.02, "Data size: {}".format(len(gen_time_data)), size="small") if opts.output is None: - plt.tight_layout() plt.show() else: fig.set_size_inches(12, 10) -- cgit v1.2.3-70-g09d2 From 644ebed6714df70df0a78bbeb04c9941eb7f69f8 Mon Sep 17 00:00:00 2001 From: J08nY Date: Sun, 18 Nov 2018 22:30:50 +0100 Subject: Add more keygen/ecdh/ecdsa options to standalone. --- .../ectester/standalone/ECTesterStandalone.java | 294 ++++++++++++--------- 1 file changed, 173 insertions(+), 121 deletions(-) (limited to 'src/cz/crcs/ectester/standalone/ECTesterStandalone.java') diff --git a/src/cz/crcs/ectester/standalone/ECTesterStandalone.java b/src/cz/crcs/ectester/standalone/ECTesterStandalone.java index d740579..46c60e0 100644 --- a/src/cz/crcs/ectester/standalone/ECTesterStandalone.java +++ b/src/cz/crcs/ectester/standalone/ECTesterStandalone.java @@ -28,6 +28,7 @@ import cz.crcs.ectester.common.output.TestWriter; import cz.crcs.ectester.common.test.TestException; import cz.crcs.ectester.common.util.ByteUtil; import cz.crcs.ectester.common.util.ECUtil; +import cz.crcs.ectester.common.util.FileUtil; import cz.crcs.ectester.data.EC_Store; import cz.crcs.ectester.standalone.consts.KeyAgreementIdent; import cz.crcs.ectester.standalone.consts.KeyPairGeneratorIdent; @@ -49,7 +50,7 @@ import javax.xml.parsers.ParserConfigurationException; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; -import java.lang.reflect.Field; +import java.io.PrintStream; import java.nio.file.Files; import java.security.*; import java.security.interfaces.ECPrivateKey; @@ -151,6 +152,7 @@ public class ECTesterStandalone { Option namedCurve = Option.builder("nc").longOpt("named-curve").desc("Use a named curve, from CurveDB: ").hasArg().argName("cat/id").optionalArg(false).build(); Option curveName = Option.builder("cn").longOpt("curve-name").desc("Use a named curve, search from curves supported by the library: ").hasArg().argName("name").optionalArg(false).build(); Option bits = Option.builder("b").longOpt("bits").hasArg().argName("n").optionalArg(false).desc("What size of curve to use.").build(); + Option output = Option.builder("o").longOpt("output").desc("Output into file .").hasArgs().argName("output_file").optionalArg(false).build(); Options testOpts = new Options(); testOpts.addOption(bits); @@ -170,9 +172,12 @@ public class ECTesterStandalone { ecdhOpts.addOption(bits); ecdhOpts.addOption(namedCurve); ecdhOpts.addOption(curveName); + ecdhOpts.addOption(output); ecdhOpts.addOption(Option.builder("t").longOpt("type").desc("Set KeyAgreement object [type].").hasArg().argName("type").optionalArg(false).build()); ecdhOpts.addOption(Option.builder().longOpt("key-type").desc("Set the key [algorithm] for which the key should be derived in KeyAgreements with KDF. Default is \"AES\".").hasArg().argName("algorithm").optionalArg(false).build()); ecdhOpts.addOption(Option.builder("n").longOpt("amount").hasArg().argName("amount").optionalArg(false).desc("Do ECDH [amount] times.").build()); + ecdhOpts.addOption(Option.builder().longOpt("fixed-private").desc("Perform ECDH with fixed private key.").build()); + ecdhOpts.addOption(Option.builder().longOpt("fixed-public").desc("Perform ECDH with fixed public key.").build()); ParserOptions ecdh = new ParserOptions(new DefaultParser(), ecdhOpts, "Perform EC based KeyAgreement."); actions.put("ecdh", ecdh); @@ -180,6 +185,7 @@ public class ECTesterStandalone { ecdsaOpts.addOption(bits); ecdsaOpts.addOption(namedCurve); ecdsaOpts.addOption(curveName); + ecdhOpts.addOption(output); ecdsaOpts.addOption(Option.builder("t").longOpt("type").desc("Set Signature object [type].").hasArg().argName("type").optionalArg(false).build()); ecdsaOpts.addOption(Option.builder("n").longOpt("amount").hasArg().argName("amount").optionalArg(false).desc("Do ECDSA [amount] times.").build()); ecdsaOpts.addOption(Option.builder("f").longOpt("file").hasArg().argName("file").optionalArg(false).desc("Input [file] to sign.").build()); @@ -190,6 +196,7 @@ public class ECTesterStandalone { generateOpts.addOption(bits); generateOpts.addOption(namedCurve); generateOpts.addOption(curveName); + ecdhOpts.addOption(output); generateOpts.addOption(Option.builder("n").longOpt("amount").hasArg().argName("amount").optionalArg(false).desc("Generate [amount] of EC keys.").build()); generateOpts.addOption(Option.builder("t").longOpt("type").hasArg().argName("type").optionalArg(false).desc("Set KeyPairGenerator object [type].").build()); ParserOptions generate = new ParserOptions(new DefaultParser(), generateOpts, "Generate EC keypairs."); @@ -197,6 +204,7 @@ public class ECTesterStandalone { Options exportOpts = new Options(); exportOpts.addOption(bits); + ecdhOpts.addOption(output); exportOpts.addOption(Option.builder("t").longOpt("type").hasArg().argName("type").optionalArg(false).desc("Set KeyPair object [type].").build()); ParserOptions export = new ParserOptions(new DefaultParser(), exportOpts, "Export default curve parameters."); actions.put("export", export); @@ -270,7 +278,7 @@ public class ECTesterStandalone { /** * */ - private void ecdh() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException { + private void ecdh() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, FileNotFoundException { ProviderECLibrary lib = cfg.selected; String algo = cli.getOptionValue("ecdh.type", "ECDH"); @@ -303,63 +311,87 @@ public class ECTesterStandalone { if (kaIdent == null || kpIdent == null) { throw new NoSuchAlgorithmException(algo); - } else { - KeyAgreement ka = kaIdent.getInstance(lib.getProvider()); - KeyPairGenerator kpg = kpIdent.getInstance(lib.getProvider()); - AlgorithmParameterSpec spec = null; - if (cli.hasOption("ecdh.bits")) { - int bits = Integer.parseInt(cli.getOptionValue("ecdh.bits")); - kpg.initialize(bits); - } else if (cli.hasOption("ecdh.named-curve")) { - String curveName = cli.getOptionValue("ecdh.named-curve"); - EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, curveName); - if (curve == null) { - System.err.println("Curve not found: " + curveName); - return; - } - spec = curve.toSpec(); - kpg.initialize(spec); - } else if (cli.hasOption("ecdh.curve-name")) { - String curveName = cli.getOptionValue("ecdh.curve-name"); - spec = new ECGenParameterSpec(curveName); - kpg.initialize(spec); + } + + KeyAgreement ka = kaIdent.getInstance(lib.getProvider()); + KeyPairGenerator kpg = kpIdent.getInstance(lib.getProvider()); + AlgorithmParameterSpec spec = null; + if (cli.hasOption("ecdh.bits")) { + int bits = Integer.parseInt(cli.getOptionValue("ecdh.bits")); + kpg.initialize(bits); + } else if (cli.hasOption("ecdh.named-curve")) { + String curveName = cli.getOptionValue("ecdh.named-curve"); + EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, curveName); + if (curve == null) { + System.err.println("Curve not found: " + curveName); + return; } + spec = curve.toSpec(); + kpg.initialize(spec); + } else if (cli.hasOption("ecdh.curve-name")) { + String curveName = cli.getOptionValue("ecdh.curve-name"); + spec = new ECGenParameterSpec(curveName); + kpg.initialize(spec); + } - System.out.println("index;time[nano];pubW;privS;secret"); + PrintStream out; + if (cli.hasOption("ecdh.output")) { + out = new PrintStream(FileUtil.openStream(cli.getOptionValues("ecdh.output"))); + } else { + out = System.out; + } - int amount = Integer.parseInt(cli.getOptionValue("ecdh.amount", "1")); - for (int i = 0; i < amount; ++i) { - KeyPair one = kpg.genKeyPair(); - KeyPair other = kpg.genKeyPair(); + out.println("index;time[nano];pubW;privS;secret"); - ECPrivateKey privkey = (ECPrivateKey) one.getPrivate(); - ECPublicKey pubkey = (ECPublicKey) other.getPublic(); + KeyPair one = null; + if (cli.hasOption("ecdh.fixed-private")) { + one = kpg.genKeyPair(); + } + KeyPair other = null; + if (cli.hasOption("ecdh.fixed-public")) { + other = kpg.genKeyPair(); + } - long elapsed = -System.nanoTime(); - if (spec instanceof ECParameterSpec) { - ka.init(privkey, spec); - } else { - ka.init(privkey); - } - ka.doPhase(pubkey, true); - elapsed += System.nanoTime(); - SecretKey derived; - byte[] result; - elapsed -= System.nanoTime(); - if (kaIdent.requiresKeyAlgo()) { - derived = ka.generateSecret(keyAlgo); - result = derived.getEncoded(); - } else { - result = ka.generateSecret(); - } - elapsed += System.nanoTime(); - ka = kaIdent.getInstance(lib.getProvider()); + int amount = Integer.parseInt(cli.getOptionValue("ecdh.amount", "1")); + for (int i = 0; i < amount; ++i) { + if (!cli.hasOption("ecdh.fixed-private")) { + one = kpg.genKeyPair(); + } + if (!cli.hasOption("ecdh.fixed-public")) { + other = kpg.genKeyPair(); + } - String pub = ByteUtil.bytesToHex(ECUtil.toX962Uncompressed(pubkey.getW(), pubkey.getParams()), false); - String priv = ByteUtil.bytesToHex(privkey.getS().toByteArray(), false); - String dh = ByteUtil.bytesToHex(result, false); - System.out.println(String.format("%d;%d;%s;%s;%s", i, elapsed, pub, priv, dh)); + ECPrivateKey privkey = (ECPrivateKey) one.getPrivate(); + ECPublicKey pubkey = (ECPublicKey) other.getPublic(); + + long elapsed = -System.nanoTime(); + if (spec instanceof ECParameterSpec) { + ka.init(privkey, spec); + } else { + ka.init(privkey); + } + ka.doPhase(pubkey, true); + elapsed += System.nanoTime(); + SecretKey derived; + byte[] result; + elapsed -= System.nanoTime(); + if (kaIdent.requiresKeyAlgo()) { + derived = ka.generateSecret(keyAlgo); + result = derived.getEncoded(); + } else { + result = ka.generateSecret(); } + elapsed += System.nanoTime(); + ka = kaIdent.getInstance(lib.getProvider()); + + String pub = ByteUtil.bytesToHex(ECUtil.toX962Uncompressed(pubkey.getW(), pubkey.getParams()), false); + String priv = ByteUtil.bytesToHex(privkey.getS().toByteArray(), false); + String dh = ByteUtil.bytesToHex(result, false); + out.println(String.format("%d;%d;%s;%s;%s", i, elapsed, pub, priv, dh)); + } + + if (cli.hasOption("ecdh.output")) { + out.close(); } } @@ -416,61 +448,70 @@ public class ECTesterStandalone { if (sigIdent == null || kpIdent == null) { throw new NoSuchAlgorithmException(algo); - } else { - Signature sig = sigIdent.getInstance(lib.getProvider()); - KeyPairGenerator kpg = kpIdent.getInstance(lib.getProvider()); - if (cli.hasOption("ecdsa.bits")) { - int bits = Integer.parseInt(cli.getOptionValue("ecdsa.bits")); - kpg.initialize(bits); - } else if (cli.hasOption("ecdsa.named-curve")) { - String curveName = cli.getOptionValue("ecdsa.named-curve"); - EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, curveName); - if (curve == null) { - System.err.println("Curve not found: " + curveName); - return; - } - kpg.initialize(curve.toSpec()); - } else if (cli.hasOption("ecdsa.curve-name")) { - String curveName = cli.getOptionValue("ecdsa.curve-name"); - kpg.initialize(new ECGenParameterSpec(curveName)); + } + Signature sig = sigIdent.getInstance(lib.getProvider()); + KeyPairGenerator kpg = kpIdent.getInstance(lib.getProvider()); + if (cli.hasOption("ecdsa.bits")) { + int bits = Integer.parseInt(cli.getOptionValue("ecdsa.bits")); + kpg.initialize(bits); + } else if (cli.hasOption("ecdsa.named-curve")) { + String curveName = cli.getOptionValue("ecdsa.named-curve"); + EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, curveName); + if (curve == null) { + System.err.println("Curve not found: " + curveName); + return; } + kpg.initialize(curve.toSpec()); + } else if (cli.hasOption("ecdsa.curve-name")) { + String curveName = cli.getOptionValue("ecdsa.curve-name"); + kpg.initialize(new ECGenParameterSpec(curveName)); + } - System.out.println("index;data;signTime[nano];verifyTime[nano];pubW;privS;signature;verified"); + PrintStream out; + if (cli.hasOption("ecdsa.output")) { + out = new PrintStream(FileUtil.openStream(cli.getOptionValues("ecdh.output"))); + } else { + out = System.out; + } - int amount = Integer.parseInt(cli.getOptionValue("ecdsa.amount", "1")); - for (int i = 0; i < amount; ++i) { - KeyPair one = kpg.genKeyPair(); + out.println("index;data;signTime[nano];verifyTime[nano];pubW;privS;signature;verified"); - ECPrivateKey privkey = (ECPrivateKey) one.getPrivate(); - ECPublicKey pubkey = (ECPublicKey) one.getPublic(); + int amount = Integer.parseInt(cli.getOptionValue("ecdsa.amount", "1")); + for (int i = 0; i < amount; ++i) { + KeyPair one = kpg.genKeyPair(); - sig.initSign(privkey); - sig.update(data); + ECPrivateKey privkey = (ECPrivateKey) one.getPrivate(); + ECPublicKey pubkey = (ECPublicKey) one.getPublic(); - long signTime = -System.nanoTime(); - byte[] signature = sig.sign(); - signTime += System.nanoTime(); + sig.initSign(privkey); + sig.update(data); - sig.initVerify(pubkey); - sig.update(data); + long signTime = -System.nanoTime(); + byte[] signature = sig.sign(); + signTime += System.nanoTime(); - long verifyTime = -System.nanoTime(); - boolean verified = sig.verify(signature); - verifyTime += System.nanoTime(); + sig.initVerify(pubkey); + sig.update(data); + long verifyTime = -System.nanoTime(); + boolean verified = sig.verify(signature); + verifyTime += System.nanoTime(); - String pub = ByteUtil.bytesToHex(ECUtil.toX962Uncompressed(pubkey.getW(), pubkey.getParams()), false); - String priv = ByteUtil.bytesToHex(privkey.getS().toByteArray(), false); - String sign = ByteUtil.bytesToHex(signature, false); - System.out.println(String.format("%d;%s;%d;%d;%s;%s;%s;%d", i, dataString, signTime, verifyTime, pub, priv, sign, verified ? 1 : 0)); - } + String pub = ByteUtil.bytesToHex(ECUtil.toX962Uncompressed(pubkey.getW(), pubkey.getParams()), false); + String priv = ByteUtil.bytesToHex(privkey.getS().toByteArray(), false); + String sign = ByteUtil.bytesToHex(signature, false); + out.println(String.format("%d;%s;%d;%d;%s;%s;%s;%d", i, dataString, signTime, verifyTime, pub, priv, sign, verified ? 1 : 0)); + } + + if (cli.hasOption("ecdsa.output")) { + out.close(); } } /** * */ - private void generate() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { + private void generate() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, FileNotFoundException { ProviderECLibrary lib = cfg.selected; KeyPairGeneratorIdent ident = null; String algo = cli.getOptionValue("generate.type", "EC"); @@ -482,37 +523,48 @@ public class ECTesterStandalone { } if (ident == null) { throw new NoSuchAlgorithmException(algo); - } else { - KeyPairGenerator kpg = ident.getInstance(lib.getProvider()); - if (cli.hasOption("generate.bits")) { - int bits = Integer.parseInt(cli.getOptionValue("generate.bits")); - kpg.initialize(bits); - } else if (cli.hasOption("generate.named-curve")) { - String curveName = cli.getOptionValue("generate.named-curve"); - EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, curveName); - if (curve == null) { - System.err.println("Curve not found: " + curveName); - return; - } - kpg.initialize(curve.toSpec()); - } else if (cli.hasOption("generate.curve-name")) { - String curveName = cli.getOptionValue("generate.curve-name"); - kpg.initialize(new ECGenParameterSpec(curveName)); - } - System.out.println("index;time[nano];pubW;privS"); - - int amount = Integer.parseInt(cli.getOptionValue("generate.amount", "1")); - for (int i = 0; i < amount || amount == 0; ++i) { - long elapsed = -System.nanoTime(); - KeyPair kp = kpg.genKeyPair(); - elapsed += System.nanoTime(); - ECPublicKey publicKey = (ECPublicKey) kp.getPublic(); - ECPrivateKey privateKey = (ECPrivateKey) kp.getPrivate(); - - String pub = ByteUtil.bytesToHex(ECUtil.toX962Uncompressed(publicKey.getW(), publicKey.getParams()), false); - String priv = ByteUtil.bytesToHex(privateKey.getS().toByteArray(), false); - System.out.println(String.format("%d;%d;%s;%s", i, elapsed, pub, priv)); + } + KeyPairGenerator kpg = ident.getInstance(lib.getProvider()); + if (cli.hasOption("generate.bits")) { + int bits = Integer.parseInt(cli.getOptionValue("generate.bits")); + kpg.initialize(bits); + } else if (cli.hasOption("generate.named-curve")) { + String curveName = cli.getOptionValue("generate.named-curve"); + EC_Curve curve = EC_Store.getInstance().getObject(EC_Curve.class, curveName); + if (curve == null) { + System.err.println("Curve not found: " + curveName); + return; } + kpg.initialize(curve.toSpec()); + } else if (cli.hasOption("generate.curve-name")) { + String curveName = cli.getOptionValue("generate.curve-name"); + kpg.initialize(new ECGenParameterSpec(curveName)); + } + + PrintStream out; + if (cli.hasOption("generate.output")) { + out = new PrintStream(FileUtil.openStream(cli.getOptionValues("ecdh.output"))); + } else { + out = System.out; + } + + out.println("index;time[nano];pubW;privS"); + + int amount = Integer.parseInt(cli.getOptionValue("generate.amount", "1")); + for (int i = 0; i < amount || amount == 0; ++i) { + long elapsed = -System.nanoTime(); + KeyPair kp = kpg.genKeyPair(); + elapsed += System.nanoTime(); + ECPublicKey publicKey = (ECPublicKey) kp.getPublic(); + ECPrivateKey privateKey = (ECPrivateKey) kp.getPrivate(); + + String pub = ByteUtil.bytesToHex(ECUtil.toX962Uncompressed(publicKey.getW(), publicKey.getParams()), false); + String priv = ByteUtil.bytesToHex(privateKey.getS().toByteArray(), false); + out.println(String.format("%d;%d;%s;%s", i, elapsed, pub, priv)); + } + + if (cli.hasOption("generate.output")) { + out.close(); } } -- cgit v1.2.3-70-g09d2 From 977bc58d83195e804769f837bd206c3467354ffe Mon Sep 17 00:00:00 2001 From: J08nY Date: Fri, 23 Nov 2018 15:25:22 +0100 Subject: Improve native lib timing. --- .../ectester/standalone/ECTesterStandalone.java | 4 + .../ectester/standalone/libs/BoringsslLib.java | 9 ++ src/cz/crcs/ectester/standalone/libs/BotanLib.java | 9 ++ .../crcs/ectester/standalone/libs/CryptoppLib.java | 9 ++ .../crcs/ectester/standalone/libs/GcryptLib.java | 9 ++ src/cz/crcs/ectester/standalone/libs/MscngLib.java | 9 ++ .../ectester/standalone/libs/NativeECLibrary.java | 9 ++ .../crcs/ectester/standalone/libs/OpensslLib.java | 9 ++ .../standalone/libs/ProviderECLibrary.java | 12 ++ .../crcs/ectester/standalone/libs/TomcryptLib.java | 10 ++ src/cz/crcs/ectester/standalone/libs/jni/Makefile | 15 +- .../crcs/ectester/standalone/libs/jni/boringssl.c | 35 ++++- src/cz/crcs/ectester/standalone/libs/jni/botan.cpp | 21 +++ .../crcs/ectester/standalone/libs/jni/c_timing.c | 57 +++++++ .../crcs/ectester/standalone/libs/jni/c_timing.h | 38 +++++ .../crcs/ectester/standalone/libs/jni/cryptopp.cpp | 23 +++ src/cz/crcs/ectester/standalone/libs/jni/gcrypt.c | 27 ++++ src/cz/crcs/ectester/standalone/libs/jni/mscng.c | 12 ++ src/cz/crcs/ectester/standalone/libs/jni/native.h | 168 +++++++++++++++++++++ src/cz/crcs/ectester/standalone/libs/jni/openssl.c | 36 ++++- .../crcs/ectester/standalone/libs/jni/tomcrypt.c | 45 ++++-- util/plot_dh.py | 76 +++++----- util/plot_gen.py | 43 +----- 23 files changed, 593 insertions(+), 92 deletions(-) create mode 100644 src/cz/crcs/ectester/standalone/libs/jni/c_timing.c create mode 100644 src/cz/crcs/ectester/standalone/libs/jni/c_timing.h (limited to 'src/cz/crcs/ectester/standalone/ECTesterStandalone.java') diff --git a/src/cz/crcs/ectester/standalone/ECTesterStandalone.java b/src/cz/crcs/ectester/standalone/ECTesterStandalone.java index 46c60e0..037e8b2 100644 --- a/src/cz/crcs/ectester/standalone/ECTesterStandalone.java +++ b/src/cz/crcs/ectester/standalone/ECTesterStandalone.java @@ -241,6 +241,7 @@ public class ECTesterStandalone { for (ProviderECLibrary lib : libs) { if (lib.isInitialized() && (cfg.selected == null || lib == cfg.selected)) { System.out.println("\t- " + Colors.bold(lib.name())); + System.out.println(Colors.bold("\t\t- Supports native timing: ") + lib.supportsNativeTiming()); Set kpgs = lib.getKPGs(); if (!kpgs.isEmpty()) { System.out.println(Colors.bold("\t\t- KeyPairGenerators: ") + String.join(", ", kpgs.stream().map(KeyPairGeneratorIdent::getName).collect(Collectors.toList()))); @@ -555,6 +556,9 @@ public class ECTesterStandalone { long elapsed = -System.nanoTime(); KeyPair kp = kpg.genKeyPair(); elapsed += System.nanoTime(); + if (lib.supportsNativeTiming()) { + elapsed = lib.getLastNativeTiming(); + } ECPublicKey publicKey = (ECPublicKey) kp.getPublic(); ECPrivateKey privateKey = (ECPrivateKey) kp.getPrivate(); diff --git a/src/cz/crcs/ectester/standalone/libs/BoringsslLib.java b/src/cz/crcs/ectester/standalone/libs/BoringsslLib.java index 60ca5d9..35a48a8 100644 --- a/src/cz/crcs/ectester/standalone/libs/BoringsslLib.java +++ b/src/cz/crcs/ectester/standalone/libs/BoringsslLib.java @@ -11,6 +11,15 @@ public class BoringsslLib extends NativeECLibrary { super("boringssl_provider", "lib_boringssl.so"); } + @Override + public native boolean supportsNativeTiming(); + + @Override + public native long getNativeTimingResolution(); + + @Override + public native long getLastNativeTiming(); + @Override native Provider createProvider(); diff --git a/src/cz/crcs/ectester/standalone/libs/BotanLib.java b/src/cz/crcs/ectester/standalone/libs/BotanLib.java index cd28791..34fb178 100644 --- a/src/cz/crcs/ectester/standalone/libs/BotanLib.java +++ b/src/cz/crcs/ectester/standalone/libs/BotanLib.java @@ -12,6 +12,15 @@ public class BotanLib extends NativeECLibrary { super("botan_provider", "botan-2"); } + @Override + public native boolean supportsNativeTiming(); + + @Override + public native long getNativeTimingResolution(); + + @Override + public native long getLastNativeTiming(); + @Override native Provider createProvider(); diff --git a/src/cz/crcs/ectester/standalone/libs/CryptoppLib.java b/src/cz/crcs/ectester/standalone/libs/CryptoppLib.java index 5112d7d..5153df5 100644 --- a/src/cz/crcs/ectester/standalone/libs/CryptoppLib.java +++ b/src/cz/crcs/ectester/standalone/libs/CryptoppLib.java @@ -12,6 +12,15 @@ public class CryptoppLib extends NativeECLibrary { super("cryptopp_provider", "cryptopp"); } + @Override + public native boolean supportsNativeTiming(); + + @Override + public native long getNativeTimingResolution(); + + @Override + public native long getLastNativeTiming(); + @Override native Provider createProvider(); diff --git a/src/cz/crcs/ectester/standalone/libs/GcryptLib.java b/src/cz/crcs/ectester/standalone/libs/GcryptLib.java index a0a7fc8..ef20f97 100644 --- a/src/cz/crcs/ectester/standalone/libs/GcryptLib.java +++ b/src/cz/crcs/ectester/standalone/libs/GcryptLib.java @@ -12,6 +12,15 @@ public class GcryptLib extends NativeECLibrary { super("gcrypt_provider", "gcrypt", "gpg-error"); } + @Override + public native boolean supportsNativeTiming(); + + @Override + public native long getNativeTimingResolution(); + + @Override + public native long getLastNativeTiming(); + @Override native Provider createProvider(); diff --git a/src/cz/crcs/ectester/standalone/libs/MscngLib.java b/src/cz/crcs/ectester/standalone/libs/MscngLib.java index 527a65b..354199a 100644 --- a/src/cz/crcs/ectester/standalone/libs/MscngLib.java +++ b/src/cz/crcs/ectester/standalone/libs/MscngLib.java @@ -12,6 +12,15 @@ public class MscngLib extends NativeECLibrary { super("mscng_provider", "bcrypt"); } + @Override + public native boolean supportsNativeTiming(); + + @Override + public native long getNativeTimingResolution(); + + @Override + public native long getLastNativeTiming(); + @Override native Provider createProvider(); diff --git a/src/cz/crcs/ectester/standalone/libs/NativeECLibrary.java b/src/cz/crcs/ectester/standalone/libs/NativeECLibrary.java index ff23fd9..7870377 100644 --- a/src/cz/crcs/ectester/standalone/libs/NativeECLibrary.java +++ b/src/cz/crcs/ectester/standalone/libs/NativeECLibrary.java @@ -130,5 +130,14 @@ public abstract class NativeECLibrary extends ProviderECLibrary { return true; } + @Override + public abstract boolean supportsNativeTiming(); + + @Override + public abstract long getNativeTimingResolution(); + + @Override + public abstract long getLastNativeTiming(); + abstract Provider createProvider(); } diff --git a/src/cz/crcs/ectester/standalone/libs/OpensslLib.java b/src/cz/crcs/ectester/standalone/libs/OpensslLib.java index e558336..4f44a2a 100644 --- a/src/cz/crcs/ectester/standalone/libs/OpensslLib.java +++ b/src/cz/crcs/ectester/standalone/libs/OpensslLib.java @@ -11,6 +11,15 @@ public class OpensslLib extends NativeECLibrary { super("openssl_provider", "crypto"); } + @Override + public native boolean supportsNativeTiming(); + + @Override + public native long getNativeTimingResolution(); + + @Override + public native long getLastNativeTiming(); + @Override native Provider createProvider(); diff --git a/src/cz/crcs/ectester/standalone/libs/ProviderECLibrary.java b/src/cz/crcs/ectester/standalone/libs/ProviderECLibrary.java index 9108eaf..83a9dc9 100644 --- a/src/cz/crcs/ectester/standalone/libs/ProviderECLibrary.java +++ b/src/cz/crcs/ectester/standalone/libs/ProviderECLibrary.java @@ -62,6 +62,18 @@ public abstract class ProviderECLibrary implements ECLibrary { return results; } + public boolean supportsNativeTiming() { + return false; + } + + public long getNativeTimingResolution() { + return 0; + } + + public long getLastNativeTiming() { + return 0; + } + @Override public Set getKAs() { return getIdents("KeyAgreement", KeyAgreementIdent::get); diff --git a/src/cz/crcs/ectester/standalone/libs/TomcryptLib.java b/src/cz/crcs/ectester/standalone/libs/TomcryptLib.java index 78db00e..6ac74c9 100644 --- a/src/cz/crcs/ectester/standalone/libs/TomcryptLib.java +++ b/src/cz/crcs/ectester/standalone/libs/TomcryptLib.java @@ -12,6 +12,16 @@ public class TomcryptLib extends NativeECLibrary { super("tomcrypt_provider", "tommath", "tomcrypt"); } + @Override + public native boolean supportsNativeTiming(); + + @Override + public native long getNativeTimingResolution(); + + @Override + public native long getLastNativeTiming(); + + @Override native Provider createProvider(); diff --git a/src/cz/crcs/ectester/standalone/libs/jni/Makefile b/src/cz/crcs/ectester/standalone/libs/jni/Makefile index 2232e3d..c8ab47b 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/Makefile +++ b/src/cz/crcs/ectester/standalone/libs/jni/Makefile @@ -64,12 +64,15 @@ all: tomcrypt_provider.so botan_provider.so cryptopp_provider.so openssl_provide c_utils.o: c_utils.c $(CC) $(CFLAGS) -c $< +c_timing.o: c_timing.c + $(CC) $(CFLAGS) -c $< + cpp_utils.o: cpp_utils.cpp $(CXX) $(CXXFLAGS) -c $< # OpenSSL shim -openssl_provider.so: openssl.o c_utils.o +openssl_provider.so: openssl.o c_utils.o c_timing.o $(CC) $(LFLAGS) -o $@ $^ -L. $(shell pkg-config --libs openssl) openssl.o: openssl.c @@ -77,7 +80,7 @@ openssl.o: openssl.c # BoringSSL shim -boringssl_provider.so: boringssl.o c_utils.o +boringssl_provider.so: boringssl.o c_utils.o c_timing.o $(CC) $(LFLAGS) -o $@ $^ -L. ../../../../../../../ext/boringssl/build/crypto/libcrypto.so cp ../../../../../../../ext/boringssl/build/crypto/libcrypto.so lib_boringssl.so @@ -86,7 +89,7 @@ boringssl.o: boringssl.c # libgcrypt shim -gcrypt_provider.so: gcrypt.o c_utils.o +gcrypt_provider.so: gcrypt.o c_utils.o c_timing.o $(CC) $(LFLAGS) -o $@ $^ -L. $(shell libgcrypt-config --libs) gcrypt.o: gcrypt.c @@ -94,7 +97,7 @@ gcrypt.o: gcrypt.c # Libtomcrypt shim -tomcrypt_provider.so: tomcrypt.o c_utils.o +tomcrypt_provider.so: tomcrypt.o c_utils.o c_timing.o $(CC) $(LFLAGS) -o $@ $^ -L. -ltommath $(shell pkg-config --libs libtomcrypt) tomcrypt.o: tomcrypt.c @@ -102,7 +105,7 @@ tomcrypt.o: tomcrypt.c # Botan-2 shim -botan_provider.so: botan.o cpp_utils.o +botan_provider.so: botan.o cpp_utils.o c_timing.o $(CXX) $(LFLAGS) -o $@ $^ -L. $(shell pkg-config --libs botan-2) botan.o: botan.cpp @@ -110,7 +113,7 @@ botan.o: botan.cpp # Crypto++ shim -cryptopp_provider.so: cryptopp.o cpp_utils.o +cryptopp_provider.so: cryptopp.o cpp_utils.o c_timing.o $(CXX) $(LFLAGS) -o $@ $^ -L. $(shell pkg-config --libs libcrypto++) cryptopp.o: cryptopp.cpp diff --git a/src/cz/crcs/ectester/standalone/libs/jni/boringssl.c b/src/cz/crcs/ectester/standalone/libs/jni/boringssl.c index cb1ea77..0484d28 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/boringssl.c +++ b/src/cz/crcs/ectester/standalone/libs/jni/boringssl.c @@ -13,6 +13,7 @@ #include #include "c_utils.h" +#include "c_timing.h" static jclass provider_class; @@ -279,7 +280,12 @@ static jobject generate_from_curve(JNIEnv *env, const EC_GROUP *curve) { EC_KEY *key = EC_KEY_new(); EC_KEY_set_group(key, curve); - if (!EC_KEY_generate_key(key)) { + + native_timing_start(); + int err = EC_KEY_generate_key(key); + native_timing_stop(); + + if (!err) { throw_new(env, "java/security/GeneralSecurityException", "Error generating key, EC_KEY_generate_key."); EC_KEY_free(key); return NULL; @@ -412,7 +418,12 @@ JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKey // probably using the ECDH_KDF_X9_62 by wrapping it and dynamically choosing the EVP_MD. from the type string. jbyteArray result = (*env)->NewByteArray(env, secret_len); jbyte *result_data = (*env)->GetByteArrayElements(env, result, NULL); - if (ECDH_compute_key(result_data, secret_len, EC_KEY_get0_public_key(pub), priv, NULL) <= 0) { + + native_timing_start(); + int err = ECDH_compute_key(result_data, secret_len, EC_KEY_get0_public_key(pub), priv, NULL); + native_timing_stop(); + + if (err <= 0) { throw_new(env, "java/security/GeneralSecurityException", "Error computing ECDH, ECDH_compute_key."); EC_KEY_free(pub); EC_KEY_free(priv); EC_GROUP_free(curve); (*env)->ReleaseByteArrayElements(env, result, result_data, JNI_ABORT); @@ -443,7 +454,11 @@ JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSig jsize data_size = (*env)->GetArrayLength(env, data); jbyte *data_data = (*env)->GetByteArrayElements(env, data, NULL); // TODO: Do more Signatures here, maybe use the EVP interface to get to the hashes easier and not hash manually? + + native_timing_start(); ECDSA_SIG *signature = ECDSA_do_sign((unsigned char *) data_data, data_size, priv); + native_timing_stop(); + (*env)->ReleaseByteArrayElements(env, data, data_data, JNI_ABORT); if (!signature) { throw_new(env, "java/security/GeneralSecurityException", "Error signing, ECDSA_do_sign."); @@ -481,7 +496,11 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSigna jsize data_size = (*env)->GetArrayLength(env, data); jbyte *data_data = (*env)->GetByteArrayElements(env, data, NULL); + + native_timing_start(); int result = ECDSA_do_verify((unsigned char *) data_data, data_size, sig_obj, pub); + native_timing_stop(); + if (result < 0) { throw_new(env, "java/security/GeneralSecurityException", "Error verifying, ECDSA_do_verify."); EC_KEY_free(pub); EC_GROUP_free(curve); ECDSA_SIG_free(sig_obj); @@ -495,3 +514,15 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSigna EC_GROUP_free(curve); return (result == 1) ? JNI_TRUE : JNI_FALSE; } + +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_BoringsslLib_supportsNativeTiming(JNIEnv *env, jobject this) { + return native_timing_supported(); +} + +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_BoringsslLib_getNativeTimingResolution(JNIEnv *env, jobject this) { + return native_timing_resolution(); +} + +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_BoringsslLib_getLastNativeTiming(JNIEnv *env, jobject this) { + return native_timing_last(); +} \ No newline at end of file diff --git a/src/cz/crcs/ectester/standalone/libs/jni/botan.cpp b/src/cz/crcs/ectester/standalone/libs/jni/botan.cpp index b7940df..813b9f8 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/botan.cpp +++ b/src/cz/crcs/ectester/standalone/libs/jni/botan.cpp @@ -17,6 +17,7 @@ #include #include #include "cpp_utils.hpp" +#include "c_timing.h" static jclass provider_class; static Botan::AutoSeeded_RNG rng; @@ -244,6 +245,7 @@ static jobject generate_from_group(JNIEnv* env, jobject self, Botan::EC_Group gr std::unique_ptr skey; try { + native_timing_start(); if (type_str == "ECDH") { skey = std::make_unique(rng, group); } else if (type_str == "ECDSA") { @@ -253,6 +255,7 @@ static jobject generate_from_group(JNIEnv* env, jobject self, Botan::EC_Group gr } else if (type_str == "ECGDSA") { skey = std::make_unique(rng, group); } + native_timing_stop(); } catch (Botan::Exception & ex) { throw_new(env, "java/security/GeneralSecurityException", ex.what()); return NULL; @@ -371,7 +374,9 @@ jbyteArray generate_secret(JNIEnv *env, jobject self, jbyteArray pubkey, jbyteAr std::vector derived; try { + native_timing_start(); derived = Botan::unlock(ka.derive_key(key_len, pkey.public_value()).bits_of()); + native_timing_stop(); } catch (Botan::Exception & ex) { throw_new(env, "java/security/GeneralSecurityException", ex.what()); return NULL; @@ -442,7 +447,9 @@ JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSig jbyte *data_bytes = env->GetByteArrayElements(data, NULL); std::vector sig; try { + native_timing_start(); sig = signer.sign_message((uint8_t*) data_bytes, data_length, rng); + native_timing_stop(); } catch (Botan::Exception & ex) { throw_new(env, "java/security/GeneralSecurityException", ex.what()); env->ReleaseByteArrayElements(data, data_bytes, JNI_ABORT); @@ -506,7 +513,9 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSigna bool result; try { + native_timing_start(); result = verifier.verify_message((uint8_t*)data_bytes, data_length, (uint8_t*)sig_bytes, sig_length); + native_timing_stop(); } catch (Botan::Exception & ex) { throw_new(env, "java/security/GeneralSecurityException", ex.what()); env->ReleaseByteArrayElements(data, data_bytes, JNI_ABORT); @@ -519,4 +528,16 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSigna return JNI_TRUE; } return JNI_FALSE; +} + +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_BotanLib_supportsNativeTiming(JNIEnv *env, jobject self) { + return native_timing_supported(); +} + +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_BotanLib_getNativeTimingResolution(JNIEnv *env, jobject self) { + return native_timing_resolution(); +} + +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_BotanLib_getLastNativeTiming(JNIEnv *env, jobject self) { + return native_timing_last(); } \ No newline at end of file diff --git a/src/cz/crcs/ectester/standalone/libs/jni/c_timing.c b/src/cz/crcs/ectester/standalone/libs/jni/c_timing.c new file mode 100644 index 0000000..be46398 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/libs/jni/c_timing.c @@ -0,0 +1,57 @@ +#include "c_timing.h" +#include + +#if _POSIX_TIMERS > 0 + +struct timespec start = {0}; +struct timespec end = {0}; + +jboolean native_timing_supported() { + return JNI_TRUE; +} + +jlong native_timing_resolution() { + struct timespec timeval; + clock_getres(CLOCK_MONOTONIC, &timeval); + return timeval.tv_nsec; +} + + +void native_timing_start() { + clock_gettime(CLOCK_MONOTONIC, &start); +} + + +void native_timing_stop() { + clock_gettime(CLOCK_MONOTONIC, &end); +} + + +jlong native_timing_last() { + jlong res = (end.tv_sec - start.tv_sec) * 1000000000 + (end.tv_nsec - start.tv_nsec); + if (res < 0) { + return 0; + } else { + return res; + } +} + +#else + +jboolean native_timing_supported() { + return JNI_FALSE; +} + +jlong native_timing_resolution() { + return 0; +} + +void native_timing_start() {} + +void native_timing_stop() {} + +jlong native_timing_last() { + return 0; +} + +#endif \ No newline at end of file diff --git a/src/cz/crcs/ectester/standalone/libs/jni/c_timing.h b/src/cz/crcs/ectester/standalone/libs/jni/c_timing.h new file mode 100644 index 0000000..bce2a19 --- /dev/null +++ b/src/cz/crcs/ectester/standalone/libs/jni/c_timing.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * + */ +jboolean native_timing_supported(); + +/** + * + */ +jlong native_timing_resolution(); + +/** + * + */ +void native_timing_start(); + +/** + * + */ +void native_timing_stop(); + +/** + * + */ +jlong native_timing_last(); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/src/cz/crcs/ectester/standalone/libs/jni/cryptopp.cpp b/src/cz/crcs/ectester/standalone/libs/jni/cryptopp.cpp index f14aa97..089724e 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/cryptopp.cpp +++ b/src/cz/crcs/ectester/standalone/libs/jni/cryptopp.cpp @@ -70,6 +70,7 @@ using CryptoPP::Integer; #include "cpp_utils.hpp" +#include "c_timing.h" static jclass provider_class; static AutoSeededRandomPool rng; @@ -498,7 +499,9 @@ template jobject generate_from_group(JNIEnv *env, DL_GroupParameters_ SecByteBlock priv(ec_domain.PrivateKeyLength()), pub(ec_domain.PublicKeyLength()); try { + native_timing_start(); ec_domain.GenerateKeyPair(rng, priv, pub); + native_timing_stop(); } catch (Exception & ex) { throw_new(env, "java/security/GeneralSecurityException", ex.what()); return NULL; @@ -579,7 +582,9 @@ JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKey try { secret = std::make_unique(dh_agreement.AgreedValueLength()); + native_timing_start(); success = dh_agreement.Agree(*secret, private_key, public_key); + native_timing_stop(); } catch (Exception & ex) { throw_new(env, "java/security/GeneralSecurityException", ex.what()); return NULL; @@ -589,7 +594,9 @@ JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKey try { secret = std::make_unique(dh_agreement.AgreedValueLength()); + native_timing_start(); success = dh_agreement.Agree(*secret, private_key, public_key); + native_timing_stop(); } catch (Exception & ex) { throw_new(env, "java/security/GeneralSecurityException", ex.what()); return NULL; @@ -624,7 +631,9 @@ jbyteArray sign_message(JNIEnv *env, DL_GroupParameters_EC group, jbyteArray jsize data_length = env->GetArrayLength(data); jbyte *data_bytes = env->GetByteArrayElements(data, NULL); + native_timing_start(); size_t len = signer.SignMessage(rng, (byte *)data_bytes, data_length, (byte *)signature.c_str()); + native_timing_stop(); env->ReleaseByteArrayElements(data, data_bytes, JNI_ABORT); signature.resize(len); @@ -709,7 +718,9 @@ jboolean verify_message(JNIEnv *env, DL_GroupParameters_EC group, jbyteArray jsize data_length = env->GetArrayLength(data); jbyte *data_bytes = env->GetByteArrayElements(data, NULL); + native_timing_start(); bool result = verifier.VerifyMessage((byte *)data_bytes, data_length, sig, sig_len); + native_timing_stop(); env->ReleaseByteArrayElements(data, data_bytes, JNI_ABORT); return result; @@ -754,3 +765,15 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSigna // unreachable return JNI_FALSE; } + +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_CryptoppLib_supportsNativeTiming(JNIEnv *env, jobject self) { + return native_timing_supported(); +} + +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_CryptoppLib_getNativeTimingResolution(JNIEnv *env, jobject self) { + return native_timing_resolution(); +} + +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_CryptoppLib_getLastNativeTiming(JNIEnv *env, jobject self) { + return native_timing_last(); +} \ No newline at end of file diff --git a/src/cz/crcs/ectester/standalone/libs/jni/gcrypt.c b/src/cz/crcs/ectester/standalone/libs/jni/gcrypt.c index 9590d0a..359d0f4 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/gcrypt.c +++ b/src/cz/crcs/ectester/standalone/libs/jni/gcrypt.c @@ -4,6 +4,7 @@ #include #include #include "c_utils.h" +#include "c_timing.h" static jclass provider_class; @@ -262,7 +263,11 @@ end: static jobject generate_from_sexp(JNIEnv *env, gcry_sexp_t gen_sexp) { jobject result = NULL; gcry_sexp_t key_sexp; + + native_timing_start(); gcry_error_t err = gcry_pk_genkey(&key_sexp, gen_sexp); + native_timing_stop(); + if (gcry_err_code(err) != GPG_ERR_NO_ERROR) { throw_new_var(env, "java/security/GeneralSecurityException", "Error generating key. Error: %ui", gcry_err_code(err)); goto release_sexp; @@ -438,7 +443,11 @@ JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKey gcry_sexp_build(&enc_sexp, NULL, "(data (flags raw) (value %M))", priv, NULL); gcry_sexp_t res_sexp; // TODO: figure out why ecc_encrypt_raw takes signed representation.. Nobody uses that., everybody uses unsigned reduced mod p. + + native_timing_start(); gcry_error_t err = gcry_pk_encrypt(&res_sexp, enc_sexp, pub); + native_timing_stop(); + if (gcry_err_code(err) != GPG_ERR_NO_ERROR) { throw_new_var(env, "java/security/GeneralSecurityException", "Error performing ECDH. Error: %ui", gcry_err_code(err)); goto end; @@ -543,7 +552,9 @@ JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSig get_sign_data_sexp(env, &data_sexp, this, data); gcry_sexp_t res_sexp; + native_timing_start(); gcry_error_t err = gcry_pk_sign(&res_sexp, data_sexp, priv_sexp); + native_timing_stop(); if (gcry_err_code(err) != GPG_ERR_NO_ERROR) { throw_new_var(env, "java/security/GeneralSecurityException", "Error performing ECDSA. Error: %ui", gcry_err_code(err)); goto release_init; @@ -591,7 +602,11 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSigna gcry_sexp_t sig_sexp; gcry_sexp_build(&sig_sexp, NULL, "(sig-val (ecdsa (r %M) (s %M)))", r_mpi, s_mpi); + + native_timing_start(); gcry_error_t err = gcry_pk_verify(sig_sexp, data_sexp, pub_sexp); + native_timing_stop(); + if (gcry_err_code(err) != GPG_ERR_NO_ERROR) { if (gcry_err_code(err) != GPG_ERR_BAD_SIGNATURE) { throw_new(env, "java/security/GeneralSecurityException", "Error verif sig."); @@ -605,4 +620,16 @@ release_init: gcry_sexp_release(pub_sexp); gcry_sexp_release(data_sexp); return result; +} + +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_GcryptLib_supportsNativeTiming(JNIEnv *env, jobject this) { + return native_timing_supported(); +} + +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_GcryptLib_getNativeTimingResolution(JNIEnv *env, jobject this) { + return native_timing_resolution(); +} + +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_GcryptLib_getLastNativeTiming(JNIEnv *env, jobject this) { + return native_timing_last(); } \ No newline at end of file diff --git a/src/cz/crcs/ectester/standalone/libs/jni/mscng.c b/src/cz/crcs/ectester/standalone/libs/jni/mscng.c index 5820afd..568e924 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/mscng.c +++ b/src/cz/crcs/ectester/standalone/libs/jni/mscng.c @@ -1211,4 +1211,16 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSigna throw_new_var(env, "java/security/GeneralSecurityException", "Error 0x%x returned by BCryptVerifySignature\n", status); return JNI_FALSE; } +} + +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_MscngLib_supportsNativeTiming(JNIEnv *env, jobject self) { + return JNI_FALSE; +} + +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_MscngLib_getNativeTimingResolution(JNIEnv *env, jobject self) { + return 0; +} + +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_MscngLib_getLastNativeTiming(JNIEnv *env, jobject self) { + return 0; } \ No newline at end of file diff --git a/src/cz/crcs/ectester/standalone/libs/jni/native.h b/src/cz/crcs/ectester/standalone/libs/jni/native.h index 47031e4..e410204 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/native.h +++ b/src/cz/crcs/ectester/standalone/libs/jni/native.h @@ -7,6 +7,30 @@ #ifdef __cplusplus extern "C" { #endif +/* + * Class: cz_crcs_ectester_standalone_libs_TomcryptLib + * Method: supportsNativeTiming + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_TomcryptLib_supportsNativeTiming + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_TomcryptLib + * Method: getNativeTimingResolution + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_TomcryptLib_getNativeTimingResolution + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_TomcryptLib + * Method: getLastNativeTiming + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_TomcryptLib_getLastNativeTiming + (JNIEnv *, jobject); + /* * Class: cz_crcs_ectester_standalone_libs_TomcryptLib * Method: createProvider @@ -188,6 +212,30 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSigna #ifdef __cplusplus extern "C" { #endif +/* + * Class: cz_crcs_ectester_standalone_libs_BotanLib + * Method: supportsNativeTiming + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_BotanLib_supportsNativeTiming + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_BotanLib + * Method: getNativeTimingResolution + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_BotanLib_getNativeTimingResolution + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_BotanLib + * Method: getLastNativeTiming + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_BotanLib_getLastNativeTiming + (JNIEnv *, jobject); + /* * Class: cz_crcs_ectester_standalone_libs_BotanLib * Method: createProvider @@ -369,6 +417,30 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSigna #ifdef __cplusplus extern "C" { #endif +/* + * Class: cz_crcs_ectester_standalone_libs_CryptoppLib + * Method: supportsNativeTiming + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_CryptoppLib_supportsNativeTiming + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_CryptoppLib + * Method: getNativeTimingResolution + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_CryptoppLib_getNativeTimingResolution + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_CryptoppLib + * Method: getLastNativeTiming + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_CryptoppLib_getLastNativeTiming + (JNIEnv *, jobject); + /* * Class: cz_crcs_ectester_standalone_libs_CryptoppLib * Method: createProvider @@ -550,6 +622,30 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSigna #ifdef __cplusplus extern "C" { #endif +/* + * Class: cz_crcs_ectester_standalone_libs_OpensslLib + * Method: supportsNativeTiming + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_OpensslLib_supportsNativeTiming + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_OpensslLib + * Method: getNativeTimingResolution + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_OpensslLib_getNativeTimingResolution + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_OpensslLib + * Method: getLastNativeTiming + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_OpensslLib_getLastNativeTiming + (JNIEnv *, jobject); + /* * Class: cz_crcs_ectester_standalone_libs_OpensslLib * Method: createProvider @@ -731,6 +827,30 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSigna #ifdef __cplusplus extern "C" { #endif +/* + * Class: cz_crcs_ectester_standalone_libs_MscngLib + * Method: supportsNativeTiming + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_MscngLib_supportsNativeTiming + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_MscngLib + * Method: getNativeTimingResolution + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_MscngLib_getNativeTimingResolution + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_MscngLib + * Method: getLastNativeTiming + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_MscngLib_getLastNativeTiming + (JNIEnv *, jobject); + /* * Class: cz_crcs_ectester_standalone_libs_MscngLib * Method: createProvider @@ -912,6 +1032,30 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSigna #ifdef __cplusplus extern "C" { #endif +/* + * Class: cz_crcs_ectester_standalone_libs_BoringsslLib + * Method: supportsNativeTiming + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_BoringsslLib_supportsNativeTiming + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_BoringsslLib + * Method: getNativeTimingResolution + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_BoringsslLib_getNativeTimingResolution + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_BoringsslLib + * Method: getLastNativeTiming + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_BoringsslLib_getLastNativeTiming + (JNIEnv *, jobject); + /* * Class: cz_crcs_ectester_standalone_libs_BoringsslLib * Method: createProvider @@ -1093,6 +1237,30 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSigna #ifdef __cplusplus extern "C" { #endif +/* + * Class: cz_crcs_ectester_standalone_libs_GcryptLib + * Method: supportsNativeTiming + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_GcryptLib_supportsNativeTiming + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_GcryptLib + * Method: getNativeTimingResolution + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_GcryptLib_getNativeTimingResolution + (JNIEnv *, jobject); + +/* + * Class: cz_crcs_ectester_standalone_libs_GcryptLib + * Method: getLastNativeTiming + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_GcryptLib_getLastNativeTiming + (JNIEnv *, jobject); + /* * Class: cz_crcs_ectester_standalone_libs_GcryptLib * Method: createProvider diff --git a/src/cz/crcs/ectester/standalone/libs/jni/openssl.c b/src/cz/crcs/ectester/standalone/libs/jni/openssl.c index 639503a..a63c2fb 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/openssl.c +++ b/src/cz/crcs/ectester/standalone/libs/jni/openssl.c @@ -12,6 +12,8 @@ #include #include "c_utils.h" +#include "c_timing.h" + static jclass provider_class; @@ -348,7 +350,12 @@ static jobject generate_from_curve(JNIEnv *env, const EC_GROUP *curve) { EC_KEY *key = EC_KEY_new(); EC_KEY_set_group(key, curve); - if (!EC_KEY_generate_key(key)) { + + native_timing_start(); + int result = EC_KEY_generate_key(key); + native_timing_stop(); + + if (!result) { throw_new(env, "java/security/GeneralSecurityException", "Error generating key, EC_KEY_generate_key."); EC_KEY_free(key); return NULL; @@ -481,7 +488,12 @@ JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKey // probably using the ECDH_KDF_X9_62 by wrapping it and dynamically choosing the EVP_MD. from the type string. jbyteArray result = (*env)->NewByteArray(env, secret_len); jbyte *result_data = (*env)->GetByteArrayElements(env, result, NULL); - if (ECDH_compute_key(result_data, secret_len, EC_KEY_get0_public_key(pub), priv, NULL) <= 0) { + + native_timing_start(); + int err = ECDH_compute_key(result_data, secret_len, EC_KEY_get0_public_key(pub), priv, NULL); + native_timing_stop(); + + if (err <= 0) { throw_new(env, "java/security/GeneralSecurityException", "Error computing ECDH, ECDH_compute_key."); EC_KEY_free(pub); EC_KEY_free(priv); EC_GROUP_free(curve); (*env)->ReleaseByteArrayElements(env, result, result_data, JNI_ABORT); @@ -512,7 +524,11 @@ JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSig jsize data_size = (*env)->GetArrayLength(env, data); jbyte *data_data = (*env)->GetByteArrayElements(env, data, NULL); // TODO: Do more Signatures here, maybe use the EVP interface to get to the hashes easier and not hash manually? + + native_timing_start(); ECDSA_SIG *signature = ECDSA_do_sign((unsigned char *) data_data, data_size, priv); + native_timing_stop(); + (*env)->ReleaseByteArrayElements(env, data, data_data, JNI_ABORT); if (!signature) { throw_new(env, "java/security/GeneralSecurityException", "Error signing, ECDSA_do_sign."); @@ -550,7 +566,11 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSigna jsize data_size = (*env)->GetArrayLength(env, data); jbyte *data_data = (*env)->GetByteArrayElements(env, data, NULL); + + native_timing_start(); int result = ECDSA_do_verify((unsigned char *) data_data, data_size, sig_obj, pub); + native_timing_stop(); + if (result < 0) { throw_new(env, "java/security/GeneralSecurityException", "Error verifying, ECDSA_do_verify."); EC_KEY_free(pub); EC_GROUP_free(curve); ECDSA_SIG_free(sig_obj); @@ -564,3 +584,15 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSigna EC_GROUP_free(curve); return (result == 1) ? JNI_TRUE : JNI_FALSE; } + +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_OpensslLib_supportsNativeTiming(JNIEnv *env, jobject this) { + return native_timing_supported(); +} + +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_OpensslLib_getNativeTimingResolution(JNIEnv *env, jobject this) { + return native_timing_resolution(); +} + +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_OpensslLib_getLastNativeTiming(JNIEnv *env, jobject this) { + return native_timing_last(); +} \ No newline at end of file diff --git a/src/cz/crcs/ectester/standalone/libs/jni/tomcrypt.c b/src/cz/crcs/ectester/standalone/libs/jni/tomcrypt.c index 49e997a..471190e 100644 --- a/src/cz/crcs/ectester/standalone/libs/jni/tomcrypt.c +++ b/src/cz/crcs/ectester/standalone/libs/jni/tomcrypt.c @@ -3,6 +3,7 @@ #include #include #include "c_utils.h" +#include "c_timing.h" static prng_state ltc_prng; static jclass provider_class; @@ -20,7 +21,6 @@ JNIEXPORT jobject JNICALL Java_cz_crcs_ectester_standalone_libs_TomcryptLib_crea return (*env)->NewObject(env, provider_class, init, name, version, name); } - JNIEXPORT void JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeProvider_00024TomCrypt_setup(JNIEnv *env, jobject this) { /* Initialize libtommath as the math lib. */ ltc_mp = ltm_desc; @@ -243,8 +243,12 @@ static void free_curve(ltc_ecc_set_type *curve) { static jobject generate_from_curve(JNIEnv *env, const ltc_ecc_set_type *curve) { ecc_key key; - int err; - if ((err = ecc_make_key_ex(<c_prng, find_prng("yarrow"), &key, curve)) != CRYPT_OK) { + + native_timing_start(); + int err = ecc_make_key_ex(<c_prng, find_prng("yarrow"), &key, curve); + native_timing_stop(); + + if (err != CRYPT_OK) { throw_new(env, "java/security/GeneralSecurityException", error_to_string(err)); return NULL; } @@ -380,8 +384,12 @@ JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeKey unsigned char result[curve->size]; unsigned long output_len = curve->size; - int err; - if ((err = ecc_shared_secret(&priv, &pub, result, &output_len)) != CRYPT_OK) { + + native_timing_start(); + int err = ecc_shared_secret(&priv, &pub, result, &output_len); + native_timing_stop(); + + if (err != CRYPT_OK) { throw_new(env, "java/security/GeneralSecurityException", error_to_string(err)); free_curve(curve); return NULL; @@ -416,8 +424,12 @@ JNIEXPORT jbyteArray JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSig unsigned char result[curve->size*4]; unsigned long output_len = curve->size*4; - int err; - if ((err = ecc_sign_hash((unsigned char *) data_data, data_size, result, &output_len, <c_prng, find_prng("yarrow"), &priv)) != CRYPT_OK) { + + native_timing_start(); + int err = ecc_sign_hash((unsigned char *) data_data, data_size, result, &output_len, <c_prng, find_prng("yarrow"), &priv); + native_timing_stop(); + + if (err != CRYPT_OK) { throw_new(env, "java/security/GeneralSecurityException", error_to_string(err)); free_curve(curve); (*env)->ReleaseByteArrayElements(env, data, data_data, JNI_ABORT); @@ -450,9 +462,12 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSigna jsize sig_size = (*env)->GetArrayLength(env, signature); jbyte *sig_data = (*env)->GetByteArrayElements(env, signature, NULL); - int err; int result; - if ((err = ecc_verify_hash((unsigned char *) sig_data, sig_size, (unsigned char *) data_data, data_size, &result, &pub)) != CRYPT_OK) { + native_timing_start(); + int err = ecc_verify_hash((unsigned char *) sig_data, sig_size, (unsigned char *) data_data, data_size, &result, &pub); + native_timing_stop(); + + if (err != CRYPT_OK) { throw_new(env, "java/security/GeneralSecurityException", error_to_string(err)); free_curve(curve); (*env)->ReleaseByteArrayElements(env, data, data_data, JNI_ABORT); @@ -464,4 +479,16 @@ JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_jni_NativeSigna (*env)->ReleaseByteArrayElements(env, signature, sig_data, JNI_ABORT); free_curve(curve); return result; +} + +JNIEXPORT jboolean JNICALL Java_cz_crcs_ectester_standalone_libs_TomcryptLib_supportsNativeTiming(JNIEnv *env, jobject this) { + return native_timing_supported(); +} + +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_TomcryptLib_getNativeTimingResolution(JNIEnv *env, jobject this) { + return native_timing_resolution(); +} + +JNIEXPORT jlong JNICALL Java_cz_crcs_ectester_standalone_libs_TomcryptLib_getLastNativeTiming(JNIEnv *env, jobject this) { + return native_timing_last(); } \ No newline at end of file diff --git a/util/plot_dh.py b/util/plot_dh.py index 468e73a..60e20ae 100755 --- a/util/plot_dh.py +++ b/util/plot_dh.py @@ -18,10 +18,17 @@ import argparse from copy import deepcopy from operator import itemgetter +from utils import hw, moving_average, plot_hist + if __name__ == "__main__": parser = argparse.ArgumentParser(description="Plot ECTester ECDH timing.") parser.add_argument("-o", "--output", dest="output", type=argparse.FileType("wb"), help="Write image to [file], do not display.", metavar="file") - parser.add_argument("--skip-first", dest="skip_first", action="store_true", help="Skip first entry, as it's usually a large outlier.") + parser.add_argument("--priv", dest="priv", action="store_true", help="Show private key MSB heatmap plot.") + parser.add_argument("--hist", dest="hist", action="store_true", help="Show time histogram.") + parser.add_argument("--hw-hist", dest="hw_hist", action="store_true", help="Show Hamming weight heatmap (private key Hamming weight and time).") + parser.add_argument("--avg", dest="avg", action="store_true", help="Show moving average of time.") + parser.add_argument("--log", dest="log", action="store_true", help="Use logarithmic scale.") + parser.add_argument("--skip-first", dest="skip_first", nargs="?", const=1, type=int, help="Skip first entry, as it's usually a large outlier.") parser.add_argument("-t", "--title", dest="title", nargs="?", default="", type=str, help="What title to give the figure.") parser.add_argument("file", type=str, help="The file to plot(csv).") @@ -34,18 +41,17 @@ if __name__ == "__main__": hx = lambda x: int(x, 16) data = np.genfromtxt(opts.file, delimiter=";", skip_header=1, converters={2: hx, 3: hx, 4: hx}, dtype=np.dtype([("index","u4"), ("time","u4"), ("pub", "O"), ("priv", "O"), ("secret","O")])) if opts.skip_first: - data = data[1:] + data = data[opts.skip_first:] + time_data = data["time"] if "nano" in header_names[1]: unit = r"$\mu s$" - time_data = map(lambda x: x[1]//1000, data) + time_data = np.array(list(map(lambda x: x//1000, time_data))) else: unit = r"ms" - time_data = map(itemgetter(1), data) - time_data = list(time_data) - priv_data = list(map(itemgetter(2), data)) - pub_data = list(map(itemgetter(3), data)) - secret_data = list(map(itemgetter(4), data)) + priv_data = data["priv"] + pub_data = data["pub"] + secret_data = data["secret"] plt.style.use("ggplot") fig = plt.figure() @@ -58,43 +64,37 @@ if __name__ == "__main__": layout_kwargs["rect"] = [0, 0.02, 1, 0.98] fig.tight_layout(**layout_kwargs) - axe_hist = fig.add_subplot(2,1,1) time_max = max(time_data) - time_avg = np.average(time_data) - time_median = np.median(time_data) - axe_hist.hist(time_data, bins=time_max//3, log=True) - axe_hist.axvline(x=time_avg, alpha=0.7, linestyle="dotted", color="red", label="avg = {}".format(time_avg)) - axe_hist.axvline(x=time_median, alpha=0.7, linestyle="dotted", color="green", label="median = {}".format(time_median)) - axe_hist.set_ylabel("count\n(log)") - axe_hist.set_xlabel("time ({})".format(unit)) - axe_hist.xaxis.set_major_locator(ticker.MaxNLocator()) - axe_hist.legend(loc="best") + time_min = min(time_data) + bit_size = len(bin(max(priv_data))) - 2 - priv_bit_bins = {} - for i in range(len(data)): - skey = priv_data[i] - time = time_data[i] - skey_hw = 0 - while skey: - skey_hw += 1 - skey &= skey - 1 - if skey_hw in priv_bit_bins: - priv_bit_bins[skey_hw].append(time) - else: - priv_bit_bins[skey_hw] = [time] - priv_bit_x = [] - priv_bit_y = [] - for k,v in priv_bit_bins.items(): - priv_bit_x.extend([k] * len(v)) - priv_bit_y.extend(v) - - axe_priv_hist = fig.add_subplot(2,1,2) - h, xe, ye = np.histogram2d(priv_bit_x, priv_bit_y, bins=[max(priv_bit_bins) - min(priv_bit_bins), (time_max - min(time_data))//5]) cmap = deepcopy(plt.cm.plasma) cmap.set_bad("black") + + norm = colors.Normalize() + if opts.log: + norm = colors.LogNorm() + + axe_private = fig.add_subplot(3,1,1) + priv_msb = np.array(list(map(lambda x: x >> (bit_size - 8), priv_data)), dtype=np.dtype("u1")) + heatmap, xedges, yedges = np.histogram2d(priv_msb, time_data, bins=[128, time_max - time_min]) + extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]] + axe_private.imshow(heatmap.T, extent=extent, aspect="auto", cmap=cmap, origin="low", interpolation="nearest", norm=norm) + axe_private.set_xlabel("private key MSB value") + axe_private.set_ylabel("ECDH time ({})".format(unit)) + + axe_hist = fig.add_subplot(3,1,2) + plot_hist(axe_hist, time_data, "ECDH time ({})".format(unit), opts.log) + axe_hist.legend(loc="best") + + axe_priv_hist = fig.add_subplot(3,1,3) + priv_hw = np.array(list(map(hw, priv_data)), dtype=np.dtype("u2")) + h, xe, ye = np.histogram2d(priv_hw, time_data, bins=[max(priv_hw) - min(priv_hw), time_max - time_min]) im = axe_priv_hist.imshow(h.T, origin="low", cmap=cmap, aspect="auto", extent=[xe[0], xe[-1], ye[0], ye[-1]], norm=colors.LogNorm()) + axe_priv_hist.axvline(x=bit_size//2, alpha=0.7, linestyle="dotted", color="white", label=str(bit_size//2) + " bits") axe_priv_hist.set_xlabel("private key Hamming weight") axe_priv_hist.set_ylabel("time ({})".format(unit)) + axe_priv_hist.legend(loc="best") fig.colorbar(im, ax=axe_priv_hist) fig.text(0.01, 0.02, "Data size: {}".format(len(time_data)), size="small") diff --git a/util/plot_gen.py b/util/plot_gen.py index c07fc91..9d4863f 100755 --- a/util/plot_gen.py +++ b/util/plot_gen.py @@ -17,17 +17,7 @@ from matplotlib import ticker, colors from copy import deepcopy import argparse -def hw(i): - res = 0 - while i: - res += 1 - i &= i - 1 - return res - -def moving_average(a, n) : - ret = np.cumsum(a, dtype=float) - ret[n:] = ret[n:] - ret[:-n] - return ret[n - 1:] / n +from utils import hw, moving_average, plot_hist if __name__ == "__main__": parser = argparse.ArgumentParser(description="Plot results of ECTester key generation timing.") @@ -85,7 +75,6 @@ if __name__ == "__main__": pub_data = data["pub"] priv_data = data["priv"] - gen_unit = "ms" if header_names[1].endswith("[nano]"): gen_unit = r"$\mu s$" @@ -121,39 +110,23 @@ if __name__ == "__main__": if plots[0]: axe_private = fig.add_subplot(n_plots, 1, plot_i) priv_msb = np.array(list(map(lambda x: x >> (bit_size - 8), priv_data)), dtype=np.dtype("u1")) - heatmap, xedges, yedges = np.histogram2d(priv_msb, gen_time_data, bins=[256, max_gen_time - min_gen_time]) - extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]] + max_msb = max(priv_msb) + min_msb = min(priv_msb) + heatmap, xedges, yedges = np.histogram2d(priv_msb, gen_time_data, bins=[max_msb - min_msb, max_gen_time - min_gen_time]) + extent = [min_msb, max_msb, yedges[0], yedges[-1]] axe_private.imshow(heatmap.T, extent=extent, aspect="auto", cmap=cmap, origin="low", interpolation="nearest", norm=norm) - axe_private.set_xlabel("private key MSB value\n(big endian)") + axe_private.set_xlabel("private key MSB value") axe_private.set_ylabel("keygen time ({})".format(gen_unit)) plot_i += 1 if plots[1]: axe_hist = fig.add_subplot(n_plots, 1, plot_i) - time_avg = np.average(gen_time_data) - time_median = np.median(gen_time_data) - axe_hist.hist(gen_time_data, bins=max_gen_time - min_gen_time, log=opts.log) - axe_hist.axvline(x=time_avg, alpha=0.7, linestyle="dotted", color="blue", label="avg = {}".format(time_avg)) - axe_hist.axvline(x=time_median, alpha=0.7, linestyle="dotted", color="green", label="median = {}".format(time_median)) - axe_hist.set_ylabel("count" + ("\n(log)" if opts.log else "")) - axe_hist.set_xlabel("keygen time ({})".format(gen_unit)) - axe_hist.xaxis.set_major_locator(ticker.MaxNLocator()) - axe_hist.legend(loc="best") + plot_hist(axe_hist, gen_time_data, "keygen time ({})".format(gen_unit), opts.log) plot_i += 1 if plots[2]: axe_hist = fig.add_subplot(n_plots, 1, plot_i) - time_max = max(export_time_data) - time_min = min(export_time_data) - time_avg = np.average(export_time_data) - time_median = np.median(export_time_data) - axe_hist.hist(export_time_data, bins=time_max - time_min, log=opts.log) - axe_hist.axvline(x=time_avg, alpha=0.7, linestyle="dotted", color="blue", label="avg = {}".format(time_avg)) - axe_hist.axvline(x=time_median, alpha=0.7, linestyle="dotted", color="green", label="median = {}".format(time_median)) - axe_hist.set_ylabel("count" + ("\n(log)" if opts.log else "")) - axe_hist.set_xlabel("export time ({})".format(export_unit)) - axe_hist.xaxis.set_major_locator(ticker.MaxNLocator()) - axe_hist.legend(loc="best") + plot_hist(axe_hist, export_time_data, "export time ({})".format(export_unit), opts.log) plot_i += 1 if plots[3]: -- cgit v1.2.3-70-g09d2 From f4c37b69a15a01bb3b0ead8b00b01536234dd7dd Mon Sep 17 00:00:00 2001 From: J08nY Date: Fri, 23 Nov 2018 18:13:14 +0100 Subject: Use native timing when available. --- .../ectester/standalone/ECTesterStandalone.java | 32 ++++++++++++++-------- 1 file changed, 20 insertions(+), 12 deletions(-) (limited to 'src/cz/crcs/ectester/standalone/ECTesterStandalone.java') diff --git a/src/cz/crcs/ectester/standalone/ECTesterStandalone.java b/src/cz/crcs/ectester/standalone/ECTesterStandalone.java index 037e8b2..e19377d 100644 --- a/src/cz/crcs/ectester/standalone/ECTesterStandalone.java +++ b/src/cz/crcs/ectester/standalone/ECTesterStandalone.java @@ -383,6 +383,9 @@ public class ECTesterStandalone { result = ka.generateSecret(); } elapsed += System.nanoTime(); + if (lib.supportsNativeTiming()) { + elapsed = lib.getLastNativeTiming(); + } ka = kaIdent.getInstance(lib.getProvider()); String pub = ByteUtil.bytesToHex(ECUtil.toX962Uncompressed(pubkey.getW(), pubkey.getParams()), false); @@ -490,6 +493,9 @@ public class ECTesterStandalone { long signTime = -System.nanoTime(); byte[] signature = sig.sign(); signTime += System.nanoTime(); + if (lib.supportsNativeTiming()) { + signTime = lib.getLastNativeTiming(); + } sig.initVerify(pubkey); sig.update(data); @@ -497,6 +503,9 @@ public class ECTesterStandalone { long verifyTime = -System.nanoTime(); boolean verified = sig.verify(signature); verifyTime += System.nanoTime(); + if (lib.supportsNativeTiming()) { + verifyTime = lib.getLastNativeTiming(); + } String pub = ByteUtil.bytesToHex(ECUtil.toX962Uncompressed(pubkey.getW(), pubkey.getParams()), false); String priv = ByteUtil.bytesToHex(privkey.getS().toByteArray(), false); @@ -612,19 +621,18 @@ public class ECTesterStandalone { } if (ident == null) { throw new NoSuchAlgorithmException(algo); - } else { - KeyPairGenerator kpg = ident.getInstance(lib.getProvider()); - if (cli.hasOption("export.bits")) { - int bits = Integer.parseInt(cli.getOptionValue("export.bits")); - kpg.initialize(bits); - } - KeyPair kp = kpg.genKeyPair(); - ECPrivateKey privateKey = (ECPrivateKey) kp.getPrivate(); - ECParameterSpec params = privateKey.getParams(); - System.out.println(params); - EC_Curve curve = EC_Curve.fromSpec(params); - curve.writeCSV(System.out); } + KeyPairGenerator kpg = ident.getInstance(lib.getProvider()); + if (cli.hasOption("export.bits")) { + int bits = Integer.parseInt(cli.getOptionValue("export.bits")); + kpg.initialize(bits); + } + KeyPair kp = kpg.genKeyPair(); + ECPrivateKey privateKey = (ECPrivateKey) kp.getPrivate(); + ECParameterSpec params = privateKey.getParams(); + System.out.println(params); + EC_Curve curve = EC_Curve.fromSpec(params); + curve.writeCSV(System.out); } public static void main(String[] args) { -- cgit v1.2.3-70-g09d2