263 lines
11 KiB
Kotlin
Raw Normal View History

2020-06-26 17:10:48 +02:00
package fr.acinq.secp256k1
import kotlinx.cinterop.*
import platform.posix.size_tVar
import secp256k1.*
@OptIn(ExperimentalUnsignedTypes::class)
public object Secp256k1Native : Secp256k1 {
2020-06-26 17:10:48 +02:00
private val ctx: CPointer<secp256k1_context> 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 secp256k1 context")
2020-06-26 17:10:48 +02:00
}
private fun Int.requireSuccess(message: String): Int = if (this != 1) throw Secp256k1Exception(message) else this
2020-06-26 17:10:48 +02:00
private fun MemScope.allocSignature(input: ByteArray): secp256k1_ecdsa_signature {
val sig = alloc<secp256k1_ecdsa_signature>()
val nativeBytes = toNat(input)
val result = when {
input.size == 64 -> secp256k1_ecdsa_signature_parse_compact(ctx, sig.ptr, nativeBytes)
input.size < 64 -> throw Secp256k1Exception("Unknown signature format")
else -> secp256k1_ecdsa_signature_parse_der(ctx, sig.ptr, nativeBytes, input.size.convert())
2020-06-26 17:10:48 +02:00
}
result.requireSuccess("cannot parse signature (size = ${input.size} sig = ${Hex.encode(input)}")
2020-06-26 17:10:48 +02:00
return sig
}
private fun MemScope.serializeSignature(signature: secp256k1_ecdsa_signature): ByteArray {
val natOutput = allocArray<UByteVar>(64)
secp256k1_ecdsa_signature_serialize_compact(ctx, natOutput, signature.ptr).requireSuccess("secp256k1_ecdsa_signature_serialize_compact() failed")
return natOutput.readBytes(64)
}
2020-06-26 17:10:48 +02:00
private fun MemScope.allocPublicKey(pubkey: ByteArray): secp256k1_pubkey {
val natPub = toNat(pubkey)
val pub = alloc<secp256k1_pubkey>()
secp256k1_ec_pubkey_parse(ctx, pub.ptr, natPub, pubkey.size.convert()).requireSuccess("secp256k1_ec_pubkey_parse() failed")
2020-06-26 17:10:48 +02:00
return pub
}
private fun MemScope.serializePubkey(pubkey: secp256k1_pubkey): ByteArray {
val serialized = allocArray<UByteVar>(65)
2020-06-26 17:10:48 +02:00
val outputLen = alloc<size_tVar>()
outputLen.value = 65.convert()
secp256k1_ec_pubkey_serialize(ctx, serialized, outputLen.ptr, pubkey.ptr, SECP256K1_EC_UNCOMPRESSED.convert()).requireSuccess("secp256k1_ec_pubkey_serialize() failed")
2020-06-26 17:10:48 +02:00
return serialized.readBytes(outputLen.value.convert())
}
private fun DeferScope.toNat(bytes: ByteArray): CPointer<UByteVar> {
val ubytes = bytes.asUByteArray()
val pinned = ubytes.pin()
this.defer { pinned.unpin() }
return pinned.addressOf(0)
}
public override fun verify(signature: ByteArray, message: ByteArray, pubkey: ByteArray): Boolean {
require(message.size == 32)
require(pubkey.size == 33 || pubkey.size == 65)
2020-06-26 17:10:48 +02:00
memScoped {
val nPubkey = allocPublicKey(pubkey)
val nMessage = toNat(message)
val nSig = allocSignature(signature)
return secp256k1_ecdsa_verify(ctx, nSig.ptr, nMessage, nPubkey.ptr) == 1
2020-06-26 17:10:48 +02:00
}
}
public override fun sign(message: ByteArray, privkey: ByteArray): ByteArray {
require(privkey.size == 32)
require(message.size == 32)
2020-06-26 17:10:48 +02:00
memScoped {
val nPrivkey = toNat(privkey)
val nMessage = toNat(message)
val nSig = alloc<secp256k1_ecdsa_signature>()
secp256k1_ecdsa_sign(ctx, nSig.ptr, nMessage, nPrivkey, null, null).requireSuccess("secp256k1_ecdsa_sign() failed")
return serializeSignature(nSig)
2020-06-26 17:10:48 +02:00
}
}
public override fun signatureNormalize(sig: ByteArray): Pair<ByteArray, Boolean> {
require(sig.size >= 64){ "invalid signature ${Hex.encode(sig)}" }
2020-06-26 17:10:48 +02:00
memScoped {
val nSig = allocSignature(sig)
val isHighS = secp256k1_ecdsa_signature_normalize(ctx, nSig.ptr, nSig.ptr)
return Pair(serializeSignature(nSig), isHighS == 1)
2020-06-26 17:10:48 +02:00
}
}
public override fun secKeyVerify(privkey: ByteArray): Boolean {
if (privkey.size != 32) return false
2020-06-26 17:10:48 +02:00
memScoped {
val nPrivkey = toNat(privkey)
return secp256k1_ec_seckey_verify(ctx, nPrivkey) == 1
2020-06-26 17:10:48 +02:00
}
}
public override fun pubkeyCreate(privkey: ByteArray): ByteArray {
require(privkey.size == 32)
2020-06-26 17:10:48 +02:00
memScoped {
val nPrivkey = toNat(privkey)
val nPubkey = alloc<secp256k1_pubkey>()
secp256k1_ec_pubkey_create(ctx, nPubkey.ptr, nPrivkey).requireSuccess("secp256k1_ec_pubkey_create() failed")
return serializePubkey(nPubkey)
2020-06-26 17:10:48 +02:00
}
}
public override fun pubkeyParse(pubkey: ByteArray): ByteArray {
2020-06-26 17:10:48 +02:00
require(pubkey.size == 33 || pubkey.size == 65)
memScoped {
val nPubkey = allocPublicKey(pubkey)
return serializePubkey(nPubkey)
2020-06-26 17:10:48 +02:00
}
}
public override fun privKeyNegate(privkey: ByteArray): ByteArray {
2020-06-26 17:10:48 +02:00
require(privkey.size == 32)
memScoped {
val negated = privkey.copyOf()
val negPriv = toNat(negated)
secp256k1_ec_privkey_negate(ctx, negPriv).requireSuccess("secp256k1_ec_privkey_negate() failed")
2020-06-26 17:10:48 +02:00
return negated
}
}
public override fun privKeyTweakAdd(privkey: ByteArray, tweak: ByteArray): ByteArray {
2020-06-26 17:10:48 +02:00
require(privkey.size == 32)
memScoped {
val added = privkey.copyOf()
val natAdd = toNat(added)
2020-06-26 17:10:48 +02:00
val natTweak = toNat(tweak)
secp256k1_ec_privkey_tweak_add(ctx, natAdd, natTweak).requireSuccess("secp256k1_ec_privkey_tweak_add() failed")
return added
2020-06-26 17:10:48 +02:00
}
}
public override fun privKeyTweakMul(privkey: ByteArray, tweak: ByteArray): ByteArray {
2020-06-26 17:10:48 +02:00
require(privkey.size == 32)
memScoped {
val multiplied = privkey.copyOf()
val natMul = toNat(multiplied)
2020-06-26 17:10:48 +02:00
val natTweak = toNat(tweak)
secp256k1_ec_privkey_tweak_mul(ctx, natMul, natTweak).requireSuccess("secp256k1_ec_privkey_tweak_mul() failed")
return multiplied
2020-06-26 17:10:48 +02:00
}
}
public override fun pubKeyNegate(pubkey: ByteArray): ByteArray {
2020-06-26 17:10:48 +02:00
require(pubkey.size == 33 || pubkey.size == 65)
memScoped {
val nPubkey = allocPublicKey(pubkey)
secp256k1_ec_pubkey_negate(ctx, nPubkey.ptr).requireSuccess("secp256k1_ec_pubkey_negate() failed")
return serializePubkey(nPubkey)
2020-06-26 17:10:48 +02:00
}
}
public override fun pubKeyTweakAdd(pubkey: ByteArray, tweak: ByteArray): ByteArray {
2020-06-26 17:10:48 +02:00
require(pubkey.size == 33 || pubkey.size == 65)
memScoped {
val nPubkey = allocPublicKey(pubkey)
val nTweak = toNat(tweak)
secp256k1_ec_pubkey_tweak_add(ctx, nPubkey.ptr, nTweak).requireSuccess("secp256k1_ec_pubkey_tweak_add() failed")
return serializePubkey(nPubkey)
2020-06-26 17:10:48 +02:00
}
}
public override fun pubKeyTweakMul(pubkey: ByteArray, tweak: ByteArray): ByteArray {
2020-06-26 17:10:48 +02:00
require(pubkey.size == 33 || pubkey.size == 65)
memScoped {
val nPubkey = allocPublicKey(pubkey)
val nTweak = toNat(tweak)
secp256k1_ec_pubkey_tweak_mul(ctx, nPubkey.ptr, nTweak).requireSuccess("secp256k1_ec_pubkey_tweak_mul() failed")
return serializePubkey(nPubkey)
2020-06-26 17:10:48 +02:00
}
}
public override fun pubKeyCombine(pubkeys: Array<ByteArray>): ByteArray {
pubkeys.forEach { require(it.size == 33 || it.size == 65) }
2020-06-26 17:10:48 +02:00
memScoped {
val nPubkeys = pubkeys.map { allocPublicKey(it).ptr }
val combined = alloc<secp256k1_pubkey>()
secp256k1_ec_pubkey_combine(ctx, combined.ptr, nPubkeys.toCValues(), pubkeys.size.convert()).requireSuccess("secp256k1_ec_pubkey_combine() failed")
return serializePubkey(combined)
2020-06-26 17:10:48 +02:00
}
}
public override fun ecdh(privkey: ByteArray, pubkey: ByteArray): ByteArray {
require(privkey.size == 32)
2020-06-26 17:10:48 +02:00
require(pubkey.size == 33 || pubkey.size == 65)
memScoped {
val nPubkey = allocPublicKey(pubkey)
val nPrivkey = toNat(privkey)
2020-06-26 17:10:48 +02:00
val output = allocArray<UByteVar>(32)
secp256k1_ecdh(ctx, output, nPubkey.ptr, nPrivkey, null, null).requireSuccess("secp256k1_ecdh() failed")
2020-06-26 17:10:48 +02:00
return output.readBytes(32)
}
}
public override fun ecdsaRecover(sig: ByteArray, message: ByteArray, recid: Int): ByteArray {
2020-06-26 17:10:48 +02:00
require(sig.size == 64)
require(message.size == 32)
memScoped {
val nSig = toNat(sig)
val rSig = alloc<secp256k1_ecdsa_recoverable_signature>()
secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, rSig.ptr, nSig, recid).requireSuccess("secp256k1_ecdsa_recoverable_signature_parse_compact() failed")
2020-06-26 17:10:48 +02:00
val nMessage = toNat(message)
val pubkey = alloc<secp256k1_pubkey>()
secp256k1_ecdsa_recover(ctx, pubkey.ptr, rSig.ptr, nMessage).requireSuccess("secp256k1_ecdsa_recover() failed")
return serializePubkey(pubkey)
2020-06-26 17:10:48 +02:00
}
}
2020-07-02 21:39:33 +02:00
public override fun compact2der(sig: ByteArray): ByteArray {
require(sig.size == 64)
memScoped {
val nSig = allocSignature(sig)
val natOutput = allocArray<UByteVar>(73)
val len = alloc<size_tVar>()
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())
}
}
override fun verifySchnorr(signature: ByteArray, data: ByteArray, pub: ByteArray): Boolean {
require(signature.size == 64)
require(data.size == 32)
require(pub.size == 32)
memScoped {
val nPub = toNat(pub)
val pubkey = alloc<secp256k1_xonly_pubkey>()
secp256k1_xonly_pubkey_parse(ctx, pubkey.ptr, nPub).requireSuccess("secp256k1_xonly_pubkey_parse() failed")
val nData = toNat(data)
val nSig = toNat(signature)
return secp256k1_schnorrsig_verify(ctx, nSig, nData, 32, pubkey.ptr) == 1
}
}
override fun signSchnorr(data: ByteArray, sec: ByteArray, auxrand32: ByteArray?): ByteArray {
require(sec.size == 32)
require(data.size == 32)
auxrand32?.let { require(it.size == 32) }
memScoped {
val nSec = toNat(sec)
val nData = toNat(data)
val nAuxrand32 = auxrand32?.let { toNat(it) }
val nSig = allocArray<UByteVar>(64)
val keypair = alloc<secp256k1_keypair>()
secp256k1_keypair_create(ctx, keypair.ptr, nSec).requireSuccess("secp256k1_keypair_create() failed")
secp256k1_schnorrsig_sign(ctx, nSig, nData, keypair.ptr, nAuxrand32).requireSuccess("secp256k1_ecdsa_sign() failed")
return nSig.readBytes(64)
}
}
public override fun cleanup() {
secp256k1_context_destroy(ctx)
}
2020-06-26 17:10:48 +02:00
}
internal actual fun getSecpk256k1(): Secp256k1 = Secp256k1Native