diff --git a/jni/c/headers/java/fr_acinq_secp256k1_Secp256k1CFunctions.h b/jni/c/headers/java/fr_acinq_secp256k1_Secp256k1CFunctions.h index 08e764c..f44a66c 100644 --- a/jni/c/headers/java/fr_acinq_secp256k1_Secp256k1CFunctions.h +++ b/jni/c/headers/java/fr_acinq_secp256k1_Secp256k1CFunctions.h @@ -119,14 +119,6 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256 JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1tweak_1mul (JNIEnv *, jclass, jlong, jbyteArray, jbyteArray); -/* - * Class: fr_acinq_secp256k1_Secp256k1CFunctions - * Method: secp256k1_ec_pubkey_add - * Signature: (J[B[B)[B - */ -JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1add - (JNIEnv *, jclass, jlong, jbyteArray, jbyteArray); - /* * Class: fr_acinq_secp256k1_Secp256k1CFunctions * Method: secp256k1_ec_pubkey_combine diff --git a/jni/c/src/fr_acinq_secp256k1_Secp256k1CFunctions.c b/jni/c/src/fr_acinq_secp256k1_Secp256k1CFunctions.c index 8c7479d..7d0fd76 100644 --- a/jni/c/src/fr_acinq_secp256k1_Secp256k1CFunctions.c +++ b/jni/c/src/fr_acinq_secp256k1_Secp256k1CFunctions.c @@ -491,51 +491,6 @@ void free_pubkeys(secp256k1_pubkey **pubkeys, size_t count) free(pubkeys); } -/* - * Class: fr_acinq_bitcoin_Secp256k1Bindings - * Method: secp256k1_ec_pubkey_add - * Signature: (J[B[B)[B - */ -JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1ec_1pubkey_1add - (JNIEnv *penv, jclass clazz, jlong jctx, jbyteArray jpubkey1, jbyteArray jpubkey2) -{ - secp256k1_context* ctx = (secp256k1_context *)jctx; - jbyte *pub1, *pub2; - secp256k1_pubkey pubkey1, pubkey2, combined; - secp256k1_pubkey const *pubkeys[2] = { &pubkey1, &pubkey2 }; - size_t size = 0; - int result = 0; - - if (jctx == 0) return NULL; - if (jpubkey1 == NULL) return NULL; - if (jpubkey2 == NULL) return NULL; - - size = (*penv)->GetArrayLength(penv, jpubkey1); - CHECKRESULT((size != 33) && (size != 65), "invalid public key size"); - pub1 = (*penv)->GetByteArrayElements(penv, jpubkey1, 0); - result = secp256k1_ec_pubkey_parse(ctx, &pubkey1, (unsigned char*)pub1, size); - (*penv)->ReleaseByteArrayElements(penv, jpubkey1, pub1, 0); - CHECKRESULT(!result, "secp256k1_ec_pubkey_parse failed"); - - size = (*penv)->GetArrayLength(penv, jpubkey2); - CHECKRESULT((size != 33) && (size != 65), "invalid public key size"); - pub2 = (*penv)->GetByteArrayElements(penv, jpubkey2, 0); - result = secp256k1_ec_pubkey_parse(ctx, &pubkey2, (unsigned char*)pub2, size); - (*penv)->ReleaseByteArrayElements(penv, jpubkey2, pub2, 0); - CHECKRESULT(!result, "secp256k1_ec_pubkey_parse failed"); - - result = secp256k1_ec_pubkey_combine(ctx, &combined, pubkeys, 2); - CHECKRESULT(!result, "secp256k1_ec_pubkey_combine failed"); - - size = 65; - jpubkey1 = (*penv)->NewByteArray(penv, 65); - pub1 = (*penv)->GetByteArrayElements(penv, jpubkey1, 0); - result = secp256k1_ec_pubkey_serialize(ctx, (unsigned char*)pub1, &size, &combined, SECP256K1_EC_UNCOMPRESSED); - (*penv)->ReleaseByteArrayElements(penv, jpubkey1, pub1, 0); - CHECKRESULT(!result, "secp256k1_ec_pubkey_serialize failed"); - return jpubkey1; -} - /* * Class: fr_acinq_bitcoin_Secp256k1Bindings * Method: secp256k1_ec_pubkey_combine diff --git a/jni/src/main/java/fr/acinq/secp256k1/Secp256k1CFunctions.java b/jni/src/main/java/fr/acinq/secp256k1/Secp256k1CFunctions.java index cd7ae60..a87357f 100644 --- a/jni/src/main/java/fr/acinq/secp256k1/Secp256k1CFunctions.java +++ b/jni/src/main/java/fr/acinq/secp256k1/Secp256k1CFunctions.java @@ -57,8 +57,6 @@ public class Secp256k1CFunctions { public static native byte[] secp256k1_ec_pubkey_tweak_mul(long ctx, byte[] pubkey, byte[] tweak); - public static native byte[] secp256k1_ec_pubkey_add(long ctx, byte[] pubkey1, byte[] pubkey2); - public static native byte[] secp256k1_ec_pubkey_combine(long ctx, byte[][] pubkeys); public static native byte[] secp256k1_ecdh(long ctx, byte[] seckey, byte[] pubkey); diff --git a/jni/src/main/kotlin/fr/acinq/secp256k1/NativeSecp256k1.kt b/jni/src/main/kotlin/fr/acinq/secp256k1/NativeSecp256k1.kt index 3c777c9..6ca9783 100644 --- a/jni/src/main/kotlin/fr/acinq/secp256k1/NativeSecp256k1.kt +++ b/jni/src/main/kotlin/fr/acinq/secp256k1/NativeSecp256k1.kt @@ -14,15 +14,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package fr.acinq.secp256k1 public object NativeSecp256k1 : Secp256k1 { - override fun verify(signature: ByteArray, data: ByteArray, pub: ByteArray): Boolean { - return Secp256k1CFunctions.secp256k1_ecdsa_verify(Secp256k1Context.getContext(), signature, data, pub) == 1 + override fun verify(signature: ByteArray, message: ByteArray, pubkey: ByteArray): Boolean { + return Secp256k1CFunctions.secp256k1_ecdsa_verify(Secp256k1Context.getContext(), signature, message, pubkey) == 1 } - override fun sign(data: ByteArray, sec: ByteArray): ByteArray { - return Secp256k1CFunctions.secp256k1_ecdsa_sign(Secp256k1Context.getContext(), data, sec) + override fun sign(message: ByteArray, privkey: ByteArray): ByteArray { + return Secp256k1CFunctions.secp256k1_ecdsa_sign(Secp256k1Context.getContext(), message, privkey) } override fun signatureNormalize(sig: ByteArray): Pair { @@ -31,35 +32,30 @@ public object NativeSecp256k1 : Secp256k1 { return Pair(sigout, result == 1) } - override fun secKeyVerify(seckey: ByteArray): Boolean { - val result = Secp256k1CFunctions.secp256k1_ec_seckey_verify(Secp256k1Context.getContext(), seckey); - return result == 1; + override fun secKeyVerify(privkey: ByteArray): Boolean { + return Secp256k1CFunctions.secp256k1_ec_seckey_verify(Secp256k1Context.getContext(), privkey) == 1 } - override fun pubkeyCreate(seckey: ByteArray): ByteArray { - return Secp256k1CFunctions.secp256k1_ec_pubkey_create(Secp256k1Context.getContext(), seckey) + override fun pubkeyCreate(privkey: ByteArray): ByteArray { + return Secp256k1CFunctions.secp256k1_ec_pubkey_create(Secp256k1Context.getContext(), privkey) } override fun pubkeyParse(pubkey: ByteArray): ByteArray { return Secp256k1CFunctions.secp256k1_ec_pubkey_parse(Secp256k1Context.getContext(), pubkey) } - override fun cleanup() { - return Secp256k1CFunctions.secp256k1_context_destroy(Secp256k1Context.getContext()) - } - override fun privKeyNegate(privkey: ByteArray): ByteArray { return Secp256k1CFunctions.secp256k1_ec_privkey_negate(Secp256k1Context.getContext(), privkey) } - override fun privKeyTweakMul(privkey: ByteArray, tweak: ByteArray): ByteArray { - return Secp256k1CFunctions.secp256k1_ec_privkey_tweak_mul(Secp256k1Context.getContext(), privkey, tweak) - } - override fun privKeyTweakAdd(privkey: ByteArray, tweak: ByteArray): ByteArray { return Secp256k1CFunctions.secp256k1_ec_privkey_tweak_add(Secp256k1Context.getContext(), privkey, tweak) } + override fun privKeyTweakMul(privkey: ByteArray, tweak: ByteArray): ByteArray { + return Secp256k1CFunctions.secp256k1_ec_privkey_tweak_mul(Secp256k1Context.getContext(), privkey, tweak) + } + override fun pubKeyNegate(pubkey: ByteArray): ByteArray { return Secp256k1CFunctions.secp256k1_ec_pubkey_negate(Secp256k1Context.getContext(), pubkey) } @@ -72,12 +68,12 @@ public object NativeSecp256k1 : Secp256k1 { return Secp256k1CFunctions.secp256k1_ec_pubkey_tweak_mul(Secp256k1Context.getContext(), pubkey, tweak) } - override fun pubKeyAdd(pubkey1: ByteArray, pubkey2: ByteArray): ByteArray { - return Secp256k1CFunctions.secp256k1_ec_pubkey_add(Secp256k1Context.getContext(), pubkey1, pubkey2) + override fun pubKeyCombine(pubkeys: Array): ByteArray { + return Secp256k1CFunctions.secp256k1_ec_pubkey_combine(Secp256k1Context.getContext(), pubkeys) } - override fun ecdh(seckey: ByteArray, pubkey: ByteArray): ByteArray { - return Secp256k1CFunctions.secp256k1_ecdh(Secp256k1Context.getContext(), seckey, pubkey) + override fun ecdh(privkey: ByteArray, pubkey: ByteArray): ByteArray { + return Secp256k1CFunctions.secp256k1_ecdh(Secp256k1Context.getContext(), privkey, pubkey) } override fun ecdsaRecover(sig: ByteArray, message: ByteArray, recid: Int): ByteArray { @@ -87,4 +83,8 @@ public object NativeSecp256k1 : Secp256k1 { override fun compact2der(sig: ByteArray): ByteArray { return Secp256k1CFunctions.secp256k1_compact_to_der(Secp256k1Context.getContext(), sig) } + + override fun cleanup() { + return Secp256k1CFunctions.secp256k1_context_destroy(Secp256k1Context.getContext()) + } } diff --git a/src/commonMain/kotlin/fr/acinq/secp256k1/Secp256k1.kt b/src/commonMain/kotlin/fr/acinq/secp256k1/Secp256k1.kt index 89f5695..755f93a 100644 --- a/src/commonMain/kotlin/fr/acinq/secp256k1/Secp256k1.kt +++ b/src/commonMain/kotlin/fr/acinq/secp256k1/Secp256k1.kt @@ -20,41 +20,104 @@ import kotlin.jvm.JvmStatic public interface Secp256k1 { - public fun verify(signature: ByteArray, data: ByteArray, pub: ByteArray): Boolean + /** + * Verify an ECDSA signature. + * + * @param signature signature using either compact encoding (64 bytes) or der-encoding. + * @param message message signed. + * @param pubkey signer's public key. + */ + public fun verify(signature: ByteArray, message: ByteArray, pubkey: ByteArray): Boolean - public fun sign(data: ByteArray, sec: ByteArray): ByteArray + /** + * Create a normalized ECDSA signature. + * + * @param message message to sign. + * @param privkey signer's private key. + */ + public fun sign(message: ByteArray, privkey: ByteArray): ByteArray + /** + * Convert an ECDSA signature to a normalized lower-S form (bitcoin standardness rule). + * Returns the normalized signature and a boolean set to true if the input signature was not normalized. + * + * @param sig signature that should be normalized. + */ public fun signatureNormalize(sig: ByteArray): Pair - public fun secKeyVerify(seckey: ByteArray): Boolean + /** + * Verify the validity of a private key. + */ + public fun secKeyVerify(privkey: ByteArray): Boolean - public fun pubkeyCreate(seckey: ByteArray): ByteArray + /** + * Get the public key corresponding to the given private key. + */ + public fun pubkeyCreate(privkey: ByteArray): ByteArray + /** + * Parse a serialized public key. + */ public fun pubkeyParse(pubkey: ByteArray): ByteArray - public fun cleanup() - + /** + * Negate the given private key. + */ public fun privKeyNegate(privkey: ByteArray): ByteArray - public fun privKeyTweakMul(privkey: ByteArray, tweak: ByteArray): ByteArray - + /** + * Tweak a private key by adding tweak to it. + */ public fun privKeyTweakAdd(privkey: ByteArray, tweak: ByteArray): ByteArray + /** + * Tweak a private key by multiplying it by a tweak. + */ + public fun privKeyTweakMul(privkey: ByteArray, tweak: ByteArray): ByteArray + + /** + * Negate the given public key. + */ public fun pubKeyNegate(pubkey: ByteArray): ByteArray + /** + * Tweak a public key by adding tweak times the generator to it. + */ public fun pubKeyTweakAdd(pubkey: ByteArray, tweak: ByteArray): ByteArray + /** + * Tweak a public key by multiplying it by a tweak value. + */ public fun pubKeyTweakMul(pubkey: ByteArray, tweak: ByteArray): ByteArray - public fun pubKeyAdd(pubkey1: ByteArray, pubkey2: ByteArray): ByteArray + /** + * Add a number of public keys together. + */ + public fun pubKeyCombine(pubkeys: Array): ByteArray - public fun ecdh(seckey: ByteArray, pubkey: ByteArray): ByteArray + /** + * Compute an elliptic curve Diffie-Hellman secret. + */ + public fun ecdh(privkey: ByteArray, pubkey: ByteArray): ByteArray + /** + * Recover a public key from an ECDSA signature. + * + * @param sig ecdsa compact signature (64 bytes). + * @param message message signed. + * @param recid recoveryId (should have been provided with the signature to allow recovery). + */ public fun ecdsaRecover(sig: ByteArray, message: ByteArray, recid: Int): ByteArray + /** + * Convert a compact ECDSA signature (64 bytes) to a der-encoded ECDSA signature. + */ public fun compact2der(sig: ByteArray): ByteArray - public fun pubKeyCompress(pubkey: ByteArray) : ByteArray { + /** + * Serialize a public key to compact form (33 bytes). + */ + public fun pubKeyCompress(pubkey: ByteArray): ByteArray { return when { pubkey.size == 33 && (pubkey[0] == 2.toByte() || pubkey[0] == 3.toByte()) -> pubkey pubkey.size == 65 && pubkey[0] == 4.toByte() -> { @@ -66,14 +129,20 @@ public interface Secp256k1 { } } + /** + * Delete the secp256k1 context from dynamic memory. + */ + public fun cleanup() + public companion object : Secp256k1 by getSecpk256k1() { - @JvmStatic public fun get(): Secp256k1 = this + @JvmStatic + public fun get(): Secp256k1 = this } } internal expect fun getSecpk256k1(): Secp256k1 public class Secp256k1Exception : RuntimeException { - public constructor() : super() {} - public constructor(message: String?) : super(message) {} + public constructor() : super() + public constructor(message: String?) : super(message) } \ No newline at end of file diff --git a/src/nativeMain/kotlin/fr/acinq/secp256k1/Secp256k1Native.kt b/src/nativeMain/kotlin/fr/acinq/secp256k1/Secp256k1Native.kt index d6a9427..49af5b6 100644 --- a/src/nativeMain/kotlin/fr/acinq/secp256k1/Secp256k1Native.kt +++ b/src/nativeMain/kotlin/fr/acinq/secp256k1/Secp256k1Native.kt @@ -9,7 +9,7 @@ public object Secp256k1Native : Secp256k1 { private val ctx: CPointer by lazy { secp256k1_context_create((SECP256K1_FLAGS_TYPE_CONTEXT or SECP256K1_FLAGS_BIT_CONTEXT_SIGN or SECP256K1_FLAGS_BIT_CONTEXT_VERIFY).toUInt()) - ?: error("Could not create segp256k1 context") + ?: error("Could not create secp256k1 context") } private fun Int.requireSuccess(message: String): Int = if (this != 1) throw Secp256k1Exception(message) else this @@ -55,25 +55,25 @@ public object Secp256k1Native : Secp256k1 { return pinned.addressOf(0) } - public override fun verify(signature: ByteArray, data: ByteArray, pub: ByteArray): Boolean { - require(data.size == 32) - require(pub.size == 33 || pub.size == 65) + public override fun verify(signature: ByteArray, message: ByteArray, pubkey: ByteArray): Boolean { + require(message.size == 32) + require(pubkey.size == 33 || pubkey.size == 65) memScoped { - val nPubkey = allocPublicKey(pub) - val nData = toNat(data) + val nPubkey = allocPublicKey(pubkey) + val nMessage = toNat(message) val nSig = allocSignature(signature) - return secp256k1_ecdsa_verify(ctx, nSig.ptr, nData, nPubkey.ptr) == 1 + return secp256k1_ecdsa_verify(ctx, nSig.ptr, nMessage, nPubkey.ptr) == 1 } } - public override fun sign(data: ByteArray, sec: ByteArray): ByteArray { - require(sec.size == 32) - require(data.size == 32) + public override fun sign(message: ByteArray, privkey: ByteArray): ByteArray { + require(privkey.size == 32) + require(message.size == 32) memScoped { - val nSec = toNat(sec) - val nData = toNat(data) + val nPrivkey = toNat(privkey) + val nMessage = toNat(message) val nSig = alloc() - secp256k1_ecdsa_sign(ctx, nSig.ptr, nData, nSec, null, null).requireSuccess("secp256k1_ecdsa_sign() failed") + secp256k1_ecdsa_sign(ctx, nSig.ptr, nMessage, nPrivkey, null, null).requireSuccess("secp256k1_ecdsa_sign() failed") return serializeSignature(nSig) } } @@ -87,20 +87,20 @@ public object Secp256k1Native : Secp256k1 { } } - public override fun secKeyVerify(seckey: ByteArray): Boolean { - require(seckey.size == 32) + public override fun secKeyVerify(privkey: ByteArray): Boolean { + require(privkey.size == 32) memScoped { - val nSec = toNat(seckey) - return secp256k1_ec_seckey_verify(ctx, nSec) == 1 + val nPrivkey = toNat(privkey) + return secp256k1_ec_seckey_verify(ctx, nPrivkey) == 1 } } - public override fun pubkeyCreate(seckey: ByteArray): ByteArray { - require(seckey.size == 32) + public override fun pubkeyCreate(privkey: ByteArray): ByteArray { + require(privkey.size == 32) memScoped { - val nSec = toNat(seckey) + val nPrivkey = toNat(privkey) val nPubkey = alloc() - secp256k1_ec_pubkey_create(ctx, nPubkey.ptr, nSec).requireSuccess("secp256k1_ec_pubkey_create() failed") + secp256k1_ec_pubkey_create(ctx, nPubkey.ptr, nPrivkey).requireSuccess("secp256k1_ec_pubkey_create() failed") return serializePubkey(nPubkey) } } @@ -113,10 +113,6 @@ public object Secp256k1Native : Secp256k1 { } } - public override fun cleanup() { - secp256k1_context_destroy(ctx) - } - public override fun privKeyNegate(privkey: ByteArray): ByteArray { require(privkey.size == 32) memScoped { @@ -127,17 +123,6 @@ public object Secp256k1Native : Secp256k1 { } } - public override fun privKeyTweakMul(privkey: ByteArray, tweak: ByteArray): ByteArray { - require(privkey.size == 32) - memScoped { - val multiplied = privkey.copyOf() - val natMul = toNat(multiplied) - val natTweak = toNat(tweak) - secp256k1_ec_privkey_tweak_mul(ctx, natMul, natTweak).requireSuccess("secp256k1_ec_privkey_tweak_mul() failed") - return multiplied - } - } - public override fun privKeyTweakAdd(privkey: ByteArray, tweak: ByteArray): ByteArray { require(privkey.size == 32) memScoped { @@ -149,6 +134,17 @@ public object Secp256k1Native : Secp256k1 { } } + public override fun privKeyTweakMul(privkey: ByteArray, tweak: ByteArray): ByteArray { + require(privkey.size == 32) + memScoped { + val multiplied = privkey.copyOf() + val natMul = toNat(multiplied) + val natTweak = toNat(tweak) + secp256k1_ec_privkey_tweak_mul(ctx, natMul, natTweak).requireSuccess("secp256k1_ec_privkey_tweak_mul() failed") + return multiplied + } + } + public override fun pubKeyNegate(pubkey: ByteArray): ByteArray { require(pubkey.size == 33 || pubkey.size == 65) memScoped { @@ -178,26 +174,24 @@ public object Secp256k1Native : Secp256k1 { } } - public override fun pubKeyAdd(pubkey1: ByteArray, pubkey2: ByteArray): ByteArray { - require(pubkey1.size == 33 || pubkey1.size == 65) - require(pubkey2.size == 33 || pubkey2.size == 65) + public override fun pubKeyCombine(pubkeys: Array): ByteArray { + pubkeys.forEach { require(it.size == 33 || it.size == 65) } memScoped { - val nPubkey1 = allocPublicKey(pubkey1) - val nPubkey2 = allocPublicKey(pubkey2) + val nPubkeys = pubkeys.map { allocPublicKey(it).ptr } val combined = alloc() - secp256k1_ec_pubkey_combine(ctx, combined.ptr, cValuesOf(nPubkey1.ptr, nPubkey2.ptr), 2.convert()).requireSuccess("secp256k1_ec_pubkey_combine() failed") + secp256k1_ec_pubkey_combine(ctx, combined.ptr, nPubkeys.toCValues(), pubkeys.size.convert()).requireSuccess("secp256k1_ec_pubkey_combine() failed") return serializePubkey(combined) } } - public override fun ecdh(seckey: ByteArray, pubkey: ByteArray): ByteArray { - require(seckey.size == 32) + public override fun ecdh(privkey: ByteArray, pubkey: ByteArray): ByteArray { + require(privkey.size == 32) require(pubkey.size == 33 || pubkey.size == 65) memScoped { val nPubkey = allocPublicKey(pubkey) - val nSeckey = toNat(seckey) + val nPrivkey = toNat(privkey) val output = allocArray(32) - secp256k1_ecdh(ctx, output, nPubkey.ptr, nSeckey, null, null).requireSuccess("secp256k1_ecdh() failed") + secp256k1_ecdh(ctx, output, nPubkey.ptr, nPrivkey, null, null).requireSuccess("secp256k1_ecdh() failed") return output.readBytes(32) } } @@ -227,6 +221,10 @@ public object Secp256k1Native : Secp256k1 { return natOutput.readBytes(len.value.toInt()) } } + + public override fun cleanup() { + secp256k1_context_destroy(ctx) + } } internal actual fun getSecpk256k1(): Secp256k1 = Secp256k1Native diff --git a/tests/build.gradle.kts b/tests/build.gradle.kts index 9f4b948..a50d3e5 100644 --- a/tests/build.gradle.kts +++ b/tests/build.gradle.kts @@ -18,6 +18,7 @@ kotlin { } val commonTest by sourceSets.getting { dependencies { + implementation(kotlin("test-common")) implementation(kotlin("test-annotations-common")) } } diff --git a/tests/src/commonTest/kotlin/fr/acinq/secp256k1/Secp256k1Test.kt b/tests/src/commonTest/kotlin/fr/acinq/secp256k1/Secp256k1Test.kt index e5dbf85..74e68f0 100644 --- a/tests/src/commonTest/kotlin/fr/acinq/secp256k1/Secp256k1Test.kt +++ b/tests/src/commonTest/kotlin/fr/acinq/secp256k1/Secp256k1Test.kt @@ -3,264 +3,244 @@ package fr.acinq.secp256k1 import kotlin.random.Random import kotlin.test.* - -/** - * This class holds test cases defined for testing this library. - */ class Secp256k1Test { - //TODO improve comments/add more tests @Test - fun testVerifyPos() { - var result: Boolean - val data: ByteArray = Hex.decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".lowercase()) //sha256hash of "testing" - val sig: ByteArray = Hex.decode("3044022079BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980220294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".lowercase()) - val pub: ByteArray = Hex.decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".lowercase()) - result = Secp256k1.verify(sig, data, pub) - assertTrue(result, "testVerifyPos") - val sigCompact: ByteArray = Hex.decode("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".lowercase()) - result = Secp256k1.verify(sigCompact, data, pub) - assertTrue(result, "testVerifyPos") + fun verifyValidPrivateKey() { + val priv = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".lowercase()) + assertTrue(Secp256k1.secKeyVerify(priv)) } @Test - fun testVerifyNeg() { - var result: Boolean - val data: ByteArray = Hex.decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A91".lowercase()) //sha256hash of "testing" - val sig: ByteArray = Hex.decode("3044022079BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980220294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".lowercase()) - val pub: ByteArray = Hex.decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".lowercase()) - result = Secp256k1.verify(sig, data, pub) - assertFalse(result, "testVerifyNeg") + fun verifyInvalidPrivateKey() { + val greaterThanCurveOrder = Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".lowercase()) + assertFalse(Secp256k1.secKeyVerify(greaterThanCurveOrder)) + val zero = Hex.decode("0000000000000000000000000000000000000000000000000000000000000000".lowercase()) + assertFalse(Secp256k1.secKeyVerify(zero)) } @Test - fun testSecKeyVerifyPos() { - var result: Boolean - val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".lowercase()) - result = Secp256k1.secKeyVerify(sec) - assertTrue(result, "testSecKeyVerifyPos") - } - - @Test - fun testSecKeyVerifyNeg() { - var result: Boolean - val sec: ByteArray = Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".lowercase()) - result = Secp256k1.secKeyVerify(sec) - assertFalse(result, "testSecKeyVerifyNeg") - } - - @Test - fun testPubKeyCreatePos() { - val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".lowercase()) - val resultArr: ByteArray = Secp256k1.pubkeyCreate(sec) - val pubkeyString: String = Hex.encode(resultArr).uppercase() + fun createValidPublicKey() { + val priv = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".lowercase()) + val pub = Secp256k1.pubkeyCreate(priv) assertEquals( "04C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D2103ED494718C697AC9AEBCFD19612E224DB46661011863ED2FC54E71861E2A6", - pubkeyString, - "testPubKeyCreatePos" + Hex.encode(pub).uppercase(), ) } @Test - fun testPubKeyCreateNeg() { - val sec: ByteArray = Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".lowercase()) + fun createInvalidPublicKey() { + val priv = Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".lowercase()) assertFailsWith { - Secp256k1.pubkeyCreate(sec) + Secp256k1.pubkeyCreate(priv) } } @Test - fun testPubkeyCompress() { + fun compressPublicKey() { val pub = Hex.decode("04C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D2103ED494718C697AC9AEBCFD19612E224DB46661011863ED2FC54E71861E2A6") val compressed = Secp256k1.pubKeyCompress(pub) assertEquals("02C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D", Hex.encode(compressed).uppercase()) } @Test - fun testPubKeyNegatePos() { - val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".lowercase()) - val pubkey: ByteArray = Secp256k1.pubkeyCreate(sec) - val pubkeyString: String = Hex.encode(pubkey).uppercase() + fun negatePublicKey() { + val priv = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".lowercase()) + val pub = Secp256k1.pubkeyCreate(priv) assertEquals( "04C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D2103ED494718C697AC9AEBCFD19612E224DB46661011863ED2FC54E71861E2A6", - pubkeyString, - "testPubKeyCreatePos" + Hex.encode(pub).uppercase(), ) - val pubkey1: ByteArray = Secp256k1.pubKeyNegate(pubkey) - val pubkeyString1: String = Hex.encode(pubkey1).uppercase() + val npub = Secp256k1.pubKeyNegate(pub) assertEquals( "04C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2DDEFC12B6B8E73968536514302E69ED1DDB24B999EFEE79C12D03AB17E79E1989", - pubkeyString1, - "testPubKeyNegatePos" + Hex.encode(npub).uppercase(), ) } @Test - fun testPubKeyParse() { - val pub: ByteArray = Hex.decode("02C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D".lowercase()) - val resultArr: ByteArray = Secp256k1.pubkeyParse(pub) - val pubkeyString: String = Hex.encode(resultArr).uppercase() + fun parsePublicKey() { + val pub = Hex.decode("02C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D".lowercase()) + val parsed = Secp256k1.pubkeyParse(pub) assertEquals( "04C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D2103ED494718C697AC9AEBCFD19612E224DB46661011863ED2FC54E71861E2A6", - pubkeyString, - "testPubKeyAdd" + Hex.encode(parsed).uppercase(), ) } @Test - fun testPubKeyAdd() { - val pub1: ByteArray = Hex.decode("041b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f70beaf8f588b541507fed6a642c5ab42dfdf8120a7f639de5122d47a69a8e8d1".lowercase()) - val pub2: ByteArray = Hex.decode("044d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d07662a3eada2d0fe208b6d257ceb0f064284662e857f57b66b54c198bd310ded36d0".lowercase()) - val pub3: ByteArray = Secp256k1.pubKeyAdd(pub1, pub2) - val pubkeyString: String = Hex.encode(pub3).uppercase() - assertEquals( - "04531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE3379E92C265E71E481BA82A84675A47AC705A200FCD524E92D93B0E7386F26A5458", - pubkeyString, - "testPubKeyAdd" - ) - } - - @Test - fun testSignPos() { - val data: ByteArray = Hex.decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".lowercase()) //sha256hash of "testing" - val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".lowercase()) - val resultArr: ByteArray = Secp256k1.sign(data, sec) - val sigString: String = Hex.encode(resultArr).uppercase() - assertEquals( - "182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A21C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9", - sigString, - "testSignPos" - ) - } - - @Test - fun testSignatureNormalize() { - val data: ByteArray = Hex.decode("30440220182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A202201C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9".lowercase()) - val (resultArr, isHighS) = Secp256k1.signatureNormalize(data) - val sigString: String = Hex.encode(resultArr).uppercase() - assertEquals( - "182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A21C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9", - sigString, - "testSignPos" - ) - assertFalse(isHighS, "isHighS") - } - - @Test - fun testSignNeg() { - val data: ByteArray = Hex.decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".lowercase()) //sha256hash of "testing" - val sec: ByteArray = Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".lowercase()) + fun parseInvalidPublicKey() { + val pub = Hex.decode("02FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".lowercase()) assertFailsWith { - Secp256k1.sign(data, sec) + Secp256k1.pubkeyParse(pub) } } @Test - fun testSignCompactPos() { - val data: ByteArray = Hex.decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".lowercase()) //sha256hash of "testing" - val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".lowercase()) - val resultArr: ByteArray = Secp256k1.sign(data, sec) - val sigString: String = Hex.encode(resultArr).uppercase() + fun combinePublicKeys() { + val pub1 = Hex.decode("041b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f70beaf8f588b541507fed6a642c5ab42dfdf8120a7f639de5122d47a69a8e8d1".lowercase()) + val pub2 = Hex.decode("044d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d07662a3eada2d0fe208b6d257ceb0f064284662e857f57b66b54c198bd310ded36d0".lowercase()) + val pub3 = Secp256k1.pubKeyCombine(arrayOf(pub1, pub2)) + assertEquals( + "04531FE6068134503D2723133227C867AC8FA6C83C537E9A44C3C5BDBDCB1FE3379E92C265E71E481BA82A84675A47AC705A200FCD524E92D93B0E7386F26A5458", + Hex.encode(pub3).uppercase(), + ) + } + + @Test + fun createEcdsaSignature() { + val message = Hex.decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".lowercase()) //sha256hash of "testing" + val priv = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".lowercase()) + val sig = Secp256k1.sign(message, priv) assertEquals( "182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A21C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9", - sigString, - "testSignCompactPos" + Hex.encode(sig).uppercase(), ) } @Test - fun testPrivKeyTweakNegate() { - val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".lowercase()) - val sec1: ByteArray = Secp256k1.privKeyNegate(sec) + fun normalizeEcdsaSignature() { + val sig = Hex.decode("30440220182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A202201C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9".lowercase()) + val (normalized, wasNotNormalized) = Secp256k1.signatureNormalize(sig) + assertFalse(wasNotNormalized) + assertEquals( + "182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A21C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9", + Hex.encode(normalized).uppercase(), + ) + } + + @Test + fun failToCreateEcdsaSignature() { + val message = Hex.decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".lowercase()) //sha256hash of "testing" + val priv = Hex.decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".lowercase()) + assertFailsWith { + Secp256k1.sign(message, priv) + } + } + + @Test + fun createCompactEcdsaSignature() { + val message = Hex.decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".lowercase()) //sha256hash of "testing" + val priv = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".lowercase()) + val sig = Secp256k1.sign(message, priv) + assertEquals( + "182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A21C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9", + Hex.encode(sig).uppercase(), + ) + } + + @Test + fun verifyValidEcdsaSignatures() { + val message = Hex.decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".lowercase()) //sha256hash of "testing" + val sig = Hex.decode("3044022079BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980220294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".lowercase()) + val pub = Hex.decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".lowercase()) + assertTrue(Secp256k1.verify(sig, message, pub)) + val sigCompact = Hex.decode("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".lowercase()) + assertTrue(Secp256k1.verify(sigCompact, message, pub)) + } + + @Test + fun verifyInvalidEcdsaSignatures() { + val message = Hex.decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A91".lowercase()) //sha256hash of "testing" + val sig = Hex.decode("3044022079BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980220294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".lowercase()) + val pub = Hex.decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".lowercase()) + assertFalse(Secp256k1.verify(sig, message, pub)) + } + + @Test + fun negatePrivateKey() { + val priv = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".lowercase()) + val npriv = Secp256k1.privKeyNegate(priv) assertEquals( "981A9A7DD677A622518DA068D66D5F824E5F22F084B8A0E2F195B5662F300C11", - Hex.encode(sec1).uppercase(), - "testPrivKeyNegate" + Hex.encode(npriv).uppercase(), ) - val sec2: ByteArray = Secp256k1.privKeyNegate(sec1) - assertTrue(sec.contentEquals(sec2)) + val nnpriv: ByteArray = Secp256k1.privKeyNegate(npriv) + assertContentEquals(priv, nnpriv) } @Test - fun testPrivKeyTweakAdd_1() { - val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".lowercase()) - val data: ByteArray = Hex.decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".lowercase()) //sha256hash of "tweak" - val resultArr: ByteArray = Secp256k1.privKeyTweakAdd(sec, data) - val sigString: String = Hex.encode(resultArr).uppercase() + fun addTweakToPrivateKey() { + val priv = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".lowercase()) + val tweak = Hex.decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".lowercase()) + val tweakedPriv = Secp256k1.privKeyTweakAdd(priv, tweak) assertEquals( "A168571E189E6F9A7E2D657A4B53AE99B909F7E712D1C23CED28093CD57C88F3", - sigString, - "testPrivKeyAdd_1" + Hex.encode(tweakedPriv).uppercase(), ) } @Test - fun testPrivKeyTweakMul_1() { - val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".lowercase()) - val data: ByteArray = Hex.decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".lowercase()) //sha256hash of "tweak" - val resultArr: ByteArray = Secp256k1.privKeyTweakMul(sec, data) - val sigString: String = Hex.encode(resultArr).uppercase() + fun multiplyPrivateKeyWithTweak() { + val priv = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".lowercase()) + val tweak = Hex.decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".lowercase()) + val tweakedPriv = Secp256k1.privKeyTweakMul(priv, tweak) assertEquals( "97F8184235F101550F3C71C927507651BD3F1CDB4A5A33B8986ACF0DEE20FFFC", - sigString, - "testPrivKeyMul_1" + Hex.encode(tweakedPriv).uppercase(), ) } @Test - fun testPrivKeyTweakAdd_2() { - val pub: ByteArray = Hex.decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".lowercase()) - val data: ByteArray = Hex.decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".lowercase()) //sha256hash of "tweak" - val resultArr: ByteArray = Secp256k1.pubKeyTweakAdd(pub, data) - val sigString: String = Hex.encode(resultArr).uppercase() + fun addTweakToPublicKey() { + val pub = Hex.decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".lowercase()) + val tweak = Hex.decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".lowercase()) + val tweakedPub = Secp256k1.pubKeyTweakAdd(pub, tweak) assertEquals( "0411C6790F4B663CCE607BAAE08C43557EDC1A4D11D88DFCB3D841D0C6A941AF525A268E2A863C148555C48FB5FBA368E88718A46E205FABC3DBA2CCFFAB0796EF", - sigString, - "testPrivKeyAdd_2" + Hex.encode(tweakedPub).uppercase(), ) } @Test - fun testPrivKeyTweakMul_2() { - val pub: ByteArray = Hex.decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".lowercase()) - val data: ByteArray = Hex.decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".lowercase()) //sha256hash of "tweak" - val resultArr: ByteArray = Secp256k1.pubKeyTweakMul(pub, data) - val sigString: String = Hex.encode(resultArr).uppercase() + fun multiplyPublicKeyWithTweak() { + val pub = Hex.decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".lowercase()) + val tweak = Hex.decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".lowercase()) + val tweakedPub = Secp256k1.pubKeyTweakMul(pub, tweak) assertEquals( "04E0FE6FE55EBCA626B98A807F6CAF654139E14E5E3698F01A9A658E21DC1D2791EC060D4F412A794D5370F672BC94B722640B5F76914151CFCA6E712CA48CC589", - sigString, - "testPrivKeyMul_2" + Hex.encode(tweakedPub).uppercase(), ) } @Test - fun testCreateECDHSecret() { - val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".lowercase()) - val pub: ByteArray = Hex.decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".lowercase()) - val resultArr: ByteArray = Secp256k1.ecdh(sec, pub) - val ecdhString: String = Hex.encode(resultArr).uppercase() + fun createEcdhSecret() { + val priv = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".lowercase()) + val pub = Hex.decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".lowercase()) + val secret = Secp256k1.ecdh(priv, pub) assertEquals( "2A2A67007A926E6594AF3EB564FC74005B37A9C8AEF2033C4552051B5C87F043", - ecdhString, - "testCreateECDHSecret" + Hex.encode(secret).uppercase(), ) } @Test - fun testEcdsaRecover() { - val data: ByteArray = Hex.decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".lowercase()) //sha256hash of "testing" - val sec: ByteArray = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".lowercase()) - val pub: ByteArray = Secp256k1.pubkeyCreate(sec) - val sig: ByteArray = Secp256k1.sign(data, sec) - val pub0: ByteArray = Secp256k1.ecdsaRecover(sig, data, 0) - val pub1: ByteArray = Secp256k1.ecdsaRecover(sig, data, 1) - assertTrue(pub.contentEquals(pub0) || pub.contentEquals(pub1), "testEcdsaRecover") + fun createSymmetricEcdhSecret() { + val priv1 = Hex.decode("3580a881ac24eb00530a51235c42bcb65424ba121e2e7d910a70fa531a578d21") + val pub1 = Secp256k1.pubkeyCreate(priv1) + val priv2 = Hex.decode("f6a353f7a5de654501c3495acde7450293f74d09086c2b7c9a4e524248d0daac") + val pub2 = Secp256k1.pubkeyCreate(priv2) + val secret1 = Secp256k1.ecdh(priv1, pub2) + val secret2 = Secp256k1.ecdh(priv2, pub1) + assertContentEquals(secret1, secret2) } @Test - fun testCompactToDER() { - val sig: ByteArray = Hex.decode("182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A21C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9".lowercase()) //sha256hash of "testing" - val der: ByteArray = Secp256k1.compact2der(sig) + fun recoverPublicKeyFromEcdsaSignature() { + val message = Hex.decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".lowercase()) //sha256hash of "testing" + val priv = Hex.decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".lowercase()) + val pub = Secp256k1.pubkeyCreate(priv) + val sig = Secp256k1.sign(message, priv) + val pub0 = Secp256k1.ecdsaRecover(sig, message, 0) + val pub1 = Secp256k1.ecdsaRecover(sig, message, 1) + assertTrue(pub.contentEquals(pub0) || pub.contentEquals(pub1)) + } + + @Test + fun convertCompactEcdsaSignatureToDer() { + val compact = Hex.decode("182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A21C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9".lowercase()) //sha256hash of "testing" + val der = Secp256k1.compact2der(compact) assertEquals( "30440220182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A202201C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9", Hex.encode(der).uppercase(), @@ -268,7 +248,7 @@ class Secp256k1Test { } @Test - fun testFormatConversion() { + fun fuzzEcdsaSignVerify() { val random = Random.Default fun randomBytes(length: Int): ByteArray { @@ -279,11 +259,14 @@ class Secp256k1Test { repeat(200) { val priv = randomBytes(32) + assertTrue(Secp256k1.secKeyVerify(priv)) val pub = Secp256k1.pubkeyCreate(priv) - val data = randomBytes(32) - val sig = Secp256k1.sign(data, priv) + val message = randomBytes(32) + val sig = Secp256k1.sign(message, priv) + assertTrue(Secp256k1.verify(sig, message, pub)) val der = Secp256k1.compact2der(sig) - Secp256k1.verify(der, data, pub) + assertTrue(Secp256k1.verify(der, message, pub)) } } + }