From 08d1692932573cf5ec418d4803c3bf0617e8bdd7 Mon Sep 17 00:00:00 2001 From: sstone Date: Thu, 2 Jul 2020 21:39:33 +0200 Subject: [PATCH] Add compact2der() method --- .../fr_acinq_secp256k1_Secp256k1CFunctions.h | 8 +++++ .../fr_acinq_secp256k1_Secp256k1CFunctions.c | 35 +++++++++++++++++++ .../acinq/secp256k1/Secp256k1CFunctions.java | 2 ++ .../fr/acinq/secp256k1/NativeSecp256k1.kt | 4 +++ .../kotlin/fr/acinq/secp256k1/Secp256k1.kt | 2 ++ .../fr/acinq/secp256k1/Secp256k1Native.kt | 12 +++++++ .../fr/acinq/secp256k1/Secp256k1Test.kt | 10 ++++++ 7 files changed, 73 insertions(+) diff --git a/jni/c/headers/java/fr_acinq_secp256k1_Secp256k1CFunctions.h b/jni/c/headers/java/fr_acinq_secp256k1_Secp256k1CFunctions.h index c1eb8c3..08e764c 100644 --- a/jni/c/headers/java/fr_acinq_secp256k1_Secp256k1CFunctions.h +++ b/jni/c/headers/java/fr_acinq_secp256k1_Secp256k1CFunctions.h @@ -151,6 +151,14 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256 JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ecdsa_1recover (JNIEnv *, jclass, jlong, jbyteArray, jbyteArray, jint); +/* + * Class: fr_acinq_secp256k1_Secp256k1CFunctions + * Method: secp256k1_compact_to_der + * Signature: (J[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1compact_1to_1der + (JNIEnv *, jclass, jlong, jbyteArray); + #ifdef __cplusplus } #endif diff --git a/jni/c/src/fr_acinq_secp256k1_Secp256k1CFunctions.c b/jni/c/src/fr_acinq_secp256k1_Secp256k1CFunctions.c index 3c4856f..c9f950c 100644 --- a/jni/c/src/fr_acinq_secp256k1_Secp256k1CFunctions.c +++ b/jni/c/src/fr_acinq_secp256k1_Secp256k1CFunctions.c @@ -687,3 +687,38 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256 CHECKRESULT(!result, "secp256k1_ec_pubkey_serialize failed"); return jpubkey; } + +/* + * Class: fr_acinq_secp256k1_Secp256k1CFunctions + * Method: secp256k1_compact_to_der + * Signature: (J[B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1compact_1to_1der + (JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jsig) +{ + secp256k1_context* ctx = (secp256k1_context *)jctx; + jbyte *sig; + secp256k1_ecdsa_signature signature;; + unsigned char der[73]; + size_t size; + int result = 0; + + if (jctx == 0) return 0; + if (jsig == NULL) return 0; + CHECKRESULT((*penv)->GetArrayLength(penv, jsig) != 64, "invalid signature size"); + + size = (*penv)->GetArrayLength(penv, jsig); + sig = (*penv)->GetByteArrayElements(penv, jsig, 0); + result = secp256k1_ecdsa_signature_parse_compact(ctx, &signature, (unsigned char*)sig); + (*penv)->ReleaseByteArrayElements(penv, jsig, sig, 0); + CHECKRESULT(!result, "secp256k1_ecdsa_signature_parse_compact failed"); + + size = 73; + result = secp256k1_ecdsa_signature_serialize_der(ctx, der, &size, &signature); + CHECKRESULT(!result, "secp256k1_ecdsa_signature_serialize_der failed"); + jsig = (*penv)->NewByteArray(penv, size); + sig = (*penv)->GetByteArrayElements(penv, jsig, 0); + memcpy(sig, der, size); + (*penv)->ReleaseByteArrayElements(penv, jsig, sig, 0); + return jsig; +} diff --git a/jni/src/main/java/fr/acinq/secp256k1/Secp256k1CFunctions.java b/jni/src/main/java/fr/acinq/secp256k1/Secp256k1CFunctions.java index 34e1ce5..cd7ae60 100644 --- a/jni/src/main/java/fr/acinq/secp256k1/Secp256k1CFunctions.java +++ b/jni/src/main/java/fr/acinq/secp256k1/Secp256k1CFunctions.java @@ -64,4 +64,6 @@ public class Secp256k1CFunctions { public static native byte[] secp256k1_ecdh(long ctx, byte[] seckey, byte[] pubkey); public static native byte[] secp256k1_ecdsa_recover(long ctx, byte[] sig, byte[] msg32, int recid); + + public static native byte[] secp256k1_compact_to_der(long ctx, byte[] sig); } diff --git a/jni/src/main/kotlin/fr/acinq/secp256k1/NativeSecp256k1.kt b/jni/src/main/kotlin/fr/acinq/secp256k1/NativeSecp256k1.kt index b604090..3c777c9 100644 --- a/jni/src/main/kotlin/fr/acinq/secp256k1/NativeSecp256k1.kt +++ b/jni/src/main/kotlin/fr/acinq/secp256k1/NativeSecp256k1.kt @@ -83,4 +83,8 @@ public object NativeSecp256k1 : Secp256k1 { override fun ecdsaRecover(sig: ByteArray, message: ByteArray, recid: Int): ByteArray { return Secp256k1CFunctions.secp256k1_ecdsa_recover(Secp256k1Context.getContext(), sig, message, recid) } + + override fun compact2der(sig: ByteArray): ByteArray { + return Secp256k1CFunctions.secp256k1_compact_to_der(Secp256k1Context.getContext(), sig) + } } diff --git a/src/commonMain/kotlin/fr/acinq/secp256k1/Secp256k1.kt b/src/commonMain/kotlin/fr/acinq/secp256k1/Secp256k1.kt index 19df290..89f5695 100644 --- a/src/commonMain/kotlin/fr/acinq/secp256k1/Secp256k1.kt +++ b/src/commonMain/kotlin/fr/acinq/secp256k1/Secp256k1.kt @@ -52,6 +52,8 @@ public interface Secp256k1 { public fun ecdsaRecover(sig: ByteArray, message: ByteArray, recid: Int): ByteArray + public fun compact2der(sig: ByteArray): ByteArray + public fun pubKeyCompress(pubkey: ByteArray) : ByteArray { return when { pubkey.size == 33 && (pubkey[0] == 2.toByte() || pubkey[0] == 3.toByte()) -> pubkey diff --git a/src/nativeMain/kotlin/fr/acinq/secp256k1/Secp256k1Native.kt b/src/nativeMain/kotlin/fr/acinq/secp256k1/Secp256k1Native.kt index 952068d..95bc3cc 100644 --- a/src/nativeMain/kotlin/fr/acinq/secp256k1/Secp256k1Native.kt +++ b/src/nativeMain/kotlin/fr/acinq/secp256k1/Secp256k1Native.kt @@ -215,6 +215,18 @@ public object Secp256k1Native : Secp256k1 { return serializePubkey(pubkey) } } + + public override fun compact2der(sig: ByteArray): ByteArray { + require(sig.size == 64) + memScoped { + val nSig = allocSignature(sig) + val natOutput = allocArray(73) + val len = alloc() + len.value = 73.convert() + secp256k1_ecdsa_signature_serialize_der(ctx, natOutput, len.ptr, nSig.ptr).requireSuccess("secp256k1_ecdsa_signature_serialize_der() failed") + return natOutput.readBytes(len.value.toInt()) + } + } } internal actual fun getSecpk256k1(): Secp256k1 = Secp256k1Native diff --git a/tests/src/commonTest/kotlin/fr/acinq/secp256k1/Secp256k1Test.kt b/tests/src/commonTest/kotlin/fr/acinq/secp256k1/Secp256k1Test.kt index 034fb0c..884c7ab 100644 --- a/tests/src/commonTest/kotlin/fr/acinq/secp256k1/Secp256k1Test.kt +++ b/tests/src/commonTest/kotlin/fr/acinq/secp256k1/Secp256k1Test.kt @@ -255,4 +255,14 @@ class Secp256k1Test { val pub1: ByteArray = Secp256k1.ecdsaRecover(sig, data, 1) assertTrue(pub.contentEquals(pub0) || pub.contentEquals(pub1), "testEcdsaRecover") } + + @Test + fun testCompactToDER() { + val sig: ByteArray = Hex.decode("182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A21C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9".toLowerCase()) //sha256hash of "testing" + val der: ByteArray = Secp256k1.compact2der(sig) + assertEquals( + "30440220182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A202201C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9", + Hex.encode(der).toUpperCase(), + ) + } }