diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/utils/Secp256k1Instance.android.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/utils/Secp256k1Instance.android.kt new file mode 100644 index 0000000000..789fe2404a --- /dev/null +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/utils/Secp256k1Instance.android.kt @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.utils + +import fr.acinq.secp256k1.Secp256k1 + +actual object Secp256k1Instance { + private val h02 = Hex.decode("02") + private val secp256k1 = Secp256k1.get() + + actual fun compressedPubKeyFor(privKey: ByteArray) = secp256k1.pubKeyCompress(secp256k1.pubkeyCreate(privKey)) + + actual fun isPrivateKeyValid(il: ByteArray): Boolean = secp256k1.secKeyVerify(il) + + actual fun signSchnorr( + data: ByteArray, + privKey: ByteArray, + nonce: ByteArray?, + ): ByteArray = secp256k1.signSchnorr(data, privKey, nonce) + + actual fun signSchnorr( + data: ByteArray, + privKey: ByteArray, + ): ByteArray = secp256k1.signSchnorr(data, privKey, null) + + actual fun verifySchnorr( + signature: ByteArray, + hash: ByteArray, + pubKey: ByteArray, + ): Boolean = secp256k1.verifySchnorr(signature, hash, pubKey) + + actual fun privateKeyAdd( + first: ByteArray, + second: ByteArray, + ): ByteArray = secp256k1.privKeyTweakAdd(first, second) + + actual fun pubKeyTweakMulCompact( + pubKey: ByteArray, + privateKey: ByteArray, + ): ByteArray = secp256k1.pubKeyTweakMul(h02 + pubKey, privateKey).copyOfRange(1, 33) +} diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/Secp256k1Instance.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/Secp256k1Instance.kt index c99652f366..594b025f94 100644 --- a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/Secp256k1Instance.kt +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/Secp256k1Instance.kt @@ -20,47 +20,35 @@ */ package com.vitorpamplona.quartz.utils -import com.vitorpamplona.quartz.utils.secp256k1.Secp256k1 +expect object Secp256k1Instance { + fun compressedPubKeyFor(privKey: ByteArray): ByteArray -object Secp256k1Instance { - private val h02 = Hex.decode("02") - - fun compressedPubKeyFor(privKey: ByteArray) = Secp256k1.pubKeyCompress(Secp256k1.pubkeyCreate(privKey)) - - fun isPrivateKeyValid(il: ByteArray): Boolean = Secp256k1.secKeyVerify(il) + fun isPrivateKeyValid(il: ByteArray): Boolean fun signSchnorr( data: ByteArray, privKey: ByteArray, nonce: ByteArray? = RandomInstance.bytes(32), - ): ByteArray = Secp256k1.signSchnorr(data, privKey, nonce) + ): ByteArray fun signSchnorr( data: ByteArray, privKey: ByteArray, - ): ByteArray = Secp256k1.signSchnorr(data, privKey, null) - - /** Fast signing with pre-computed compressed public key (skips G multiplication). */ - fun signSchnorrWithPubKey( - data: ByteArray, - privKey: ByteArray, - compressedPubKey: ByteArray, - nonce: ByteArray? = RandomInstance.bytes(32), - ): ByteArray = Secp256k1.signSchnorrWithPubKey(data, privKey, compressedPubKey, nonce) + ): ByteArray fun verifySchnorr( signature: ByteArray, hash: ByteArray, pubKey: ByteArray, - ): Boolean = Secp256k1.verifySchnorr(signature, hash, pubKey) + ): Boolean fun privateKeyAdd( first: ByteArray, second: ByteArray, - ): ByteArray = Secp256k1.privKeyTweakAdd(first, second) + ): ByteArray fun pubKeyTweakMulCompact( pubKey: ByteArray, privateKey: ByteArray, - ): ByteArray = Secp256k1.ecdhXOnly(pubKey, privateKey) + ): ByteArray } diff --git a/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/Secp256k1InstanceOurs.kt b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/Secp256k1InstanceOurs.kt new file mode 100644 index 0000000000..145de46756 --- /dev/null +++ b/quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/Secp256k1InstanceOurs.kt @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.utils + +import com.vitorpamplona.quartz.utils.secp256k1.Secp256k1 + +object Secp256k1InstanceOurs { + private val h02 = Hex.decode("02") + + fun compressedPubKeyFor(privKey: ByteArray) = Secp256k1.pubKeyCompress(Secp256k1.pubkeyCreate(privKey)) + + fun isPrivateKeyValid(il: ByteArray): Boolean = Secp256k1.secKeyVerify(il) + + fun signSchnorr( + data: ByteArray, + privKey: ByteArray, + nonce: ByteArray? = RandomInstance.bytes(32), + ): ByteArray = Secp256k1.signSchnorr(data, privKey, nonce) + + fun signSchnorr( + data: ByteArray, + privKey: ByteArray, + ): ByteArray = Secp256k1.signSchnorr(data, privKey, null) + + /** Fast signing with pre-computed compressed public key (skips G multiplication). */ + fun signSchnorrWithPubKey( + data: ByteArray, + privKey: ByteArray, + compressedPubKey: ByteArray, + nonce: ByteArray? = RandomInstance.bytes(32), + ): ByteArray = Secp256k1.signSchnorrWithPubKey(data, privKey, compressedPubKey, nonce) + + fun verifySchnorr( + signature: ByteArray, + hash: ByteArray, + pubKey: ByteArray, + ): Boolean = Secp256k1.verifySchnorr(signature, hash, pubKey) + + fun privateKeyAdd( + first: ByteArray, + second: ByteArray, + ): ByteArray = Secp256k1.privKeyTweakAdd(first, second) + + fun pubKeyTweakMulCompact( + pubKey: ByteArray, + privateKey: ByteArray, + ): ByteArray = Secp256k1.ecdhXOnly(pubKey, privateKey) +} diff --git a/quartz/src/jvmMain/kotlin/com/vitorpamplona/quartz/utils/Secp256k1Instance.jvm.kt b/quartz/src/jvmMain/kotlin/com/vitorpamplona/quartz/utils/Secp256k1Instance.jvm.kt new file mode 100644 index 0000000000..789fe2404a --- /dev/null +++ b/quartz/src/jvmMain/kotlin/com/vitorpamplona/quartz/utils/Secp256k1Instance.jvm.kt @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.utils + +import fr.acinq.secp256k1.Secp256k1 + +actual object Secp256k1Instance { + private val h02 = Hex.decode("02") + private val secp256k1 = Secp256k1.get() + + actual fun compressedPubKeyFor(privKey: ByteArray) = secp256k1.pubKeyCompress(secp256k1.pubkeyCreate(privKey)) + + actual fun isPrivateKeyValid(il: ByteArray): Boolean = secp256k1.secKeyVerify(il) + + actual fun signSchnorr( + data: ByteArray, + privKey: ByteArray, + nonce: ByteArray?, + ): ByteArray = secp256k1.signSchnorr(data, privKey, nonce) + + actual fun signSchnorr( + data: ByteArray, + privKey: ByteArray, + ): ByteArray = secp256k1.signSchnorr(data, privKey, null) + + actual fun verifySchnorr( + signature: ByteArray, + hash: ByteArray, + pubKey: ByteArray, + ): Boolean = secp256k1.verifySchnorr(signature, hash, pubKey) + + actual fun privateKeyAdd( + first: ByteArray, + second: ByteArray, + ): ByteArray = secp256k1.privKeyTweakAdd(first, second) + + actual fun pubKeyTweakMulCompact( + pubKey: ByteArray, + privateKey: ByteArray, + ): ByteArray = secp256k1.pubKeyTweakMul(h02 + pubKey, privateKey).copyOfRange(1, 33) +} diff --git a/quartz/src/nativeMain/kotlin/com/vitorpamplona/quartz/utils/Secp256k1Instance.native.kt b/quartz/src/nativeMain/kotlin/com/vitorpamplona/quartz/utils/Secp256k1Instance.native.kt new file mode 100644 index 0000000000..771c359161 --- /dev/null +++ b/quartz/src/nativeMain/kotlin/com/vitorpamplona/quartz/utils/Secp256k1Instance.native.kt @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.quartz.utils + +import fr.acinq.secp256k1.Secp256k1 + +actual object Secp256k1Instance { + private val h02 = Hex.decode("02") + private val secp256k1Ref = Secp256k1.get() + + actual fun compressedPubKeyFor(privKey: ByteArray): ByteArray = secp256k1Ref.pubKeyCompress(secp256k1Ref.pubkeyCreate(privKey)) + + actual fun isPrivateKeyValid(il: ByteArray): Boolean = secp256k1Ref.secKeyVerify(il) + + actual fun signSchnorr( + data: ByteArray, + privKey: ByteArray, + nonce: ByteArray?, + ): ByteArray = secp256k1Ref.signSchnorr(data, privKey, nonce) + + actual fun signSchnorr( + data: ByteArray, + privKey: ByteArray, + ): ByteArray = secp256k1Ref.signSchnorr(data, privKey, null) + + actual fun verifySchnorr( + signature: ByteArray, + hash: ByteArray, + pubKey: ByteArray, + ): Boolean = secp256k1Ref.verifySchnorr(signature, hash, pubKey) + + actual fun privateKeyAdd( + first: ByteArray, + second: ByteArray, + ): ByteArray = secp256k1Ref.privKeyTweakAdd(first, second) + + actual fun pubKeyTweakMulCompact( + pubKey: ByteArray, + privateKey: ByteArray, + ): ByteArray = secp256k1Ref.pubKeyTweakMul(h02 + pubKey, privateKey).copyOfRange(1, 33) +}