1003 lines
45 KiB
Kotlin
Raw Normal View History

2020-06-26 17:10:48 +02:00
package fr.acinq.secp256k1
2024-08-22 10:47:18 +02:00
import fr.acinq.secp256k1.Secp256k1Native.toNat
2020-06-26 17:10:48 +02:00
import kotlinx.cinterop.*
2024-08-04 23:53:28 +02:00
import kotlinx.cinterop.ptr
import platform.posix.memcpy
2020-06-26 17:10:48 +02:00
import platform.posix.size_tVar
import secp256k1.*
2024-01-23 15:44:06 +01:00
@OptIn(ExperimentalUnsignedTypes::class, ExperimentalForeignApi::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
}
2024-08-04 23:53:28 +02:00
private fun MemScope.allocXonlyPublicKey(pubkey: ByteArray): secp256k1_xonly_pubkey {
val natPub = toNat(pubkey)
val pub = alloc<secp256k1_xonly_pubkey>()
secp256k1_xonly_pubkey_parse(ctx, pub.ptr, natPub).requireSuccess("secp256k1_xonly_pubkey_parse() failed")
return pub
}
private fun MemScope.allocPublicNonce(pubnonce: ByteArray): secp256k1_musig_pubnonce {
val nat = toNat(pubnonce)
val nPubnonce = alloc<secp256k1_musig_pubnonce>()
secp256k1_musig_pubnonce_parse(ctx, nPubnonce.ptr, nat).requireSuccess("secp256k1_musig_pubnonce_parse() failed")
return nPubnonce
}
private fun MemScope.allocPartialSig(psig: ByteArray): secp256k1_musig_partial_sig {
val nat = toNat(psig)
val nPsig = alloc<secp256k1_musig_partial_sig>()
secp256k1_musig_partial_sig_parse(ctx, nPsig.ptr, nat).requireSuccess("secp256k1_musig_partial_sig_parse() failed")
return nPsig
}
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 MemScope.serializeXonlyPubkey(pubkey: secp256k1_xonly_pubkey): ByteArray {
val serialized = allocArray<UByteVar>(32)
secp256k1_xonly_pubkey_serialize(ctx, serialized, pubkey.ptr).requireSuccess("secp256k1_xonly_pubkey_serialize() failed")
return serialized.readBytes(32)
}
private fun MemScope.serializePubnonce(pubnonce: secp256k1_musig_pubnonce): ByteArray {
val serialized = allocArray<UByteVar>(Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
secp256k1_musig_pubnonce_serialize(ctx, serialized, pubnonce.ptr).requireSuccess("secp256k1_musig_pubnonce_serialize() failed")
return serialized.readBytes(Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
}
private fun MemScope.serializeAggnonce(aggnonce: secp256k1_musig_aggnonce): ByteArray {
val serialized = allocArray<UByteVar>(Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
secp256k1_musig_aggnonce_serialize(ctx, serialized, aggnonce.ptr).requireSuccess("secp256k1_musig_aggnonce_serialize() failed")
return serialized.readBytes(Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
}
private fun DeferScope.toNat(bytes: ByteArray): CPointer<UByteVar> {
2020-06-26 17:10:48 +02:00
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_seckey_negate(ctx, negPriv).requireSuccess("secp256k1_ec_seckey_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)
require(tweak.size == 32)
2020-06-26 17:10:48 +02:00
memScoped {
val added = privkey.copyOf()
val natAdd = toNat(added)
2020-06-26 17:10:48 +02:00
val natTweak = toNat(tweak)
secp256k1_ec_seckey_tweak_add(ctx, natAdd, natTweak).requireSuccess("secp256k1_ec_seckey_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)
require(tweak.size == 32)
2020-06-26 17:10:48 +02:00
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)
require(tweak.size == 32)
2020-06-26 17:10:48 +02:00
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)
require(tweak.size == 32)
2020-06-26 17:10:48 +02:00
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 {
require(pubkeys.isNotEmpty())
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)
require(recid in 0..3)
2020-06-26 17:10:48 +02:00
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)
2024-01-23 15:44:06 +01:00
return secp256k1_schnorrsig_verify(ctx, nSig, nData, 32u, 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_sign32(ctx, nSig, nData, keypair.ptr, nAuxrand32).requireSuccess("secp256k1_ecdsa_sign() failed")
return nSig.readBytes(64)
}
}
override fun musigNonceGen(sessionId32: ByteArray, privkey: ByteArray?, aggpubkey: ByteArray, msg32: ByteArray?, keyaggCache: ByteArray?, extraInput32: ByteArray?): ByteArray {
require(sessionId32.size == 32)
privkey?.let { require(it.size == 32) }
msg32?.let { require(it.size == 32) }
keyaggCache?.let { require(it.size == Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE) }
extraInput32?.let { require(it.size == 32) }
val nonce = memScoped {
val secnonce = alloc<secp256k1_musig_secnonce>()
val pubnonce = alloc<secp256k1_musig_pubnonce>()
val nPubkey = allocPublicKey(aggpubkey)
val nKeyAggCache = keyaggCache?.let {
val n = alloc<secp256k1_musig_keyagg_cache>()
memcpy(n.ptr, toNat(it), Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
n
}
secp256k1_musig_nonce_gen(
ctx,
secnonce.ptr,
pubnonce.ptr,
toNat(sessionId32),
privkey?.let { toNat(it) },
nPubkey.ptr,
msg32?.let { toNat(it) },
nKeyAggCache?.ptr,
extraInput32?.let { toNat(it) }).requireSuccess("secp256k1_musig_nonce_gen() failed")
val nPubnonce = allocArray<UByteVar>(Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
secp256k1_musig_pubnonce_serialize(ctx, nPubnonce, pubnonce.ptr).requireSuccess("secp256k1_musig_pubnonce_serialize failed")
secnonce.ptr.readBytes(Secp256k1.MUSIG2_SECRET_NONCE_SIZE) + nPubnonce.readBytes(Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
}
return nonce
}
override fun musigNonceAgg(pubnonces: Array<ByteArray>): ByteArray {
require(pubnonces.isNotEmpty())
pubnonces.forEach { require(it.size == Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE) }
memScoped {
val nPubnonces = pubnonces.map { allocPublicNonce(it).ptr }
val combined = alloc<secp256k1_musig_aggnonce>()
secp256k1_musig_nonce_agg(ctx, combined.ptr, nPubnonces.toCValues(), pubnonces.size.convert()).requireSuccess("secp256k1_musig_nonce_agg() failed")
return serializeAggnonce(combined)
}
}
override fun musigPubkeyAgg(pubkeys: Array<ByteArray>, keyaggCache: ByteArray?): ByteArray {
require(pubkeys.isNotEmpty())
pubkeys.forEach { require(it.size == 33 || it.size == 65) }
keyaggCache?.let { require(it.size == Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE) }
memScoped {
val nPubkeys = pubkeys.map { allocPublicKey(it).ptr }
val combined = alloc<secp256k1_xonly_pubkey>()
val nKeyAggCache = keyaggCache?.let {
val n = alloc<secp256k1_musig_keyagg_cache>()
memcpy(n.ptr, toNat(it), Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
n
}
secp256k1_musig_pubkey_agg(ctx, null, combined.ptr, nKeyAggCache?.ptr, nPubkeys.toCValues(), pubkeys.size.convert()).requireSuccess("secp256k1_musig_nonce_agg() failed")
val agg = serializeXonlyPubkey(combined)
keyaggCache?.let { blob -> nKeyAggCache?.let { memcpy(toNat(blob), it.ptr, Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong()) } }
return agg
}
}
override fun musigPubkeyTweakAdd(keyaggCache: ByteArray, tweak32: ByteArray): ByteArray {
require(keyaggCache.size == Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
require(tweak32.size == 32)
memScoped {
val nKeyAggCache = alloc<secp256k1_musig_keyagg_cache>()
memcpy(nKeyAggCache.ptr, toNat(keyaggCache), Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
val nPubkey = alloc<secp256k1_pubkey>()
secp256k1_musig_pubkey_ec_tweak_add(ctx, nPubkey.ptr, nKeyAggCache.ptr, toNat(tweak32)).requireSuccess("secp256k1_musig_pubkey_ec_tweak_add() failed")
memcpy(toNat(keyaggCache), nKeyAggCache.ptr, Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
return serializePubkey(nPubkey)
}
}
override fun musigPubkeyXonlyTweakAdd(keyaggCache: ByteArray, tweak32: ByteArray): ByteArray {
require(keyaggCache.size == Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
require(tweak32.size == 32)
memScoped {
val nKeyAggCache = alloc<secp256k1_musig_keyagg_cache>()
memcpy(nKeyAggCache.ptr, toNat(keyaggCache), Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
val nPubkey = alloc<secp256k1_pubkey>()
secp256k1_musig_pubkey_xonly_tweak_add(ctx, nPubkey.ptr, nKeyAggCache.ptr, toNat(tweak32)).requireSuccess("secp256k1_musig_pubkey_xonly_tweak_add() failed")
memcpy(toNat(keyaggCache), nKeyAggCache.ptr, Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
return serializePubkey(nPubkey)
}
}
override fun musigNonceProcess(aggnonce: ByteArray, msg32: ByteArray, keyaggCache: ByteArray): ByteArray {
require(aggnonce.size == Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
require(keyaggCache.size == Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
require(msg32.size == 32)
memScoped {
val nKeyAggCache = alloc<secp256k1_musig_keyagg_cache>()
memcpy(nKeyAggCache.ptr, toNat(keyaggCache), Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
val nSession = alloc<secp256k1_musig_session>()
val nAggnonce = alloc<secp256k1_musig_aggnonce>()
secp256k1_musig_aggnonce_parse(ctx, nAggnonce.ptr, toNat(aggnonce)).requireSuccess("secp256k1_musig_aggnonce_parse() failed")
secp256k1_musig_nonce_process(ctx, nSession.ptr, nAggnonce.ptr, toNat(msg32), nKeyAggCache.ptr, null).requireSuccess("secp256k1_musig_nonce_process() failed")
val session = ByteArray(Secp256k1.MUSIG2_PUBLIC_SESSION_SIZE)
memcpy(toNat(session), nSession.ptr, Secp256k1.MUSIG2_PUBLIC_SESSION_SIZE.toULong())
return session
}
}
override fun musigPartialSign(secnonce: ByteArray, privkey: ByteArray, keyaggCache: ByteArray, session: ByteArray): ByteArray {
require(secnonce.size == Secp256k1.MUSIG2_SECRET_NONCE_SIZE)
require(privkey.size == 32)
require(keyaggCache.size == Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
require(session.size == Secp256k1.MUSIG2_PUBLIC_SESSION_SIZE)
require(musigNonceValidate(secnonce, pubkeyCreate(privkey)))
memScoped {
val nSecnonce = alloc<secp256k1_musig_secnonce>()
memcpy(nSecnonce.ptr, toNat(secnonce), Secp256k1.MUSIG2_SECRET_NONCE_SIZE.toULong())
val nKeypair = alloc<secp256k1_keypair>()
secp256k1_keypair_create(ctx, nKeypair.ptr, toNat(privkey))
val nPsig = alloc<secp256k1_musig_partial_sig>()
val nKeyAggCache = alloc<secp256k1_musig_keyagg_cache>()
memcpy(nKeyAggCache.ptr, toNat(keyaggCache), Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
val nSession = alloc<secp256k1_musig_session>()
memcpy(nSession.ptr, toNat(session), Secp256k1.MUSIG2_PUBLIC_SESSION_SIZE.toULong())
secp256k1_musig_partial_sign(ctx, nPsig.ptr, nSecnonce.ptr, nKeypair.ptr, nKeyAggCache.ptr, nSession.ptr).requireSuccess("secp256k1_musig_partial_sign failed")
val psig = ByteArray(32)
secp256k1_musig_partial_sig_serialize(ctx, toNat(psig), nPsig.ptr).requireSuccess("secp256k1_musig_partial_sig_serialize() failed")
return psig
}
}
override fun musigPartialSigVerify(psig: ByteArray, pubnonce: ByteArray, pubkey: ByteArray, keyaggCache: ByteArray, session: ByteArray): Int {
require(psig.size == 32)
require(pubnonce.size == Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE)
require(pubkey.size == 33 || pubkey.size == 65)
require(keyaggCache.size == Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE)
require(session.size == Secp256k1.MUSIG2_PUBLIC_SESSION_SIZE)
memScoped {
val nPSig = allocPartialSig(psig)
val nPubnonce = allocPublicNonce(pubnonce)
val nPubkey = allocPublicKey(pubkey)
val nKeyAggCache = alloc<secp256k1_musig_keyagg_cache>()
memcpy(nKeyAggCache.ptr, toNat(keyaggCache), Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong())
val nSession = alloc<secp256k1_musig_session>()
memcpy(nSession.ptr, toNat(session), Secp256k1.MUSIG2_PUBLIC_SESSION_SIZE.toULong())
return secp256k1_musig_partial_sig_verify(ctx, nPSig.ptr, nPubnonce.ptr, nPubkey.ptr, nKeyAggCache.ptr, nSession.ptr)
}
}
override fun musigPartialSigAgg(session: ByteArray, psigs: Array<ByteArray>): ByteArray {
require(session.size == Secp256k1.MUSIG2_PUBLIC_SESSION_SIZE)
require(psigs.isNotEmpty())
psigs.forEach { require(it.size == 32) }
memScoped {
val nSession = alloc<secp256k1_musig_session>()
memcpy(nSession.ptr, toNat(session), Secp256k1.MUSIG2_PUBLIC_SESSION_SIZE.toULong())
val nPsigs = psigs.map { allocPartialSig(it).ptr }
val sig64 = ByteArray(64)
secp256k1_musig_partial_sig_agg(ctx, toNat(sig64), nSession.ptr, nPsigs.toCValues(), psigs.size.convert()).requireSuccess("secp256k1_musig_partial_sig_agg() failed")
return sig64
}
}
2024-08-04 23:53:28 +02:00
private fun MemScope.allocFrostShare(share: ByteArray): secp256k1_frost_share {
val nat = toNat(share)
val nFrostShare = alloc<secp256k1_frost_share>()
secp256k1_frost_share_parse(ctx, nFrostShare.ptr, nat).requireSuccess("secp256k1_frost_share_parse() failed")
return nFrostShare
}
private fun MemScope.allocFrostPartialSignature(partialSignature: ByteArray): secp256k1_frost_partial_sig {
val nat = toNat(partialSignature)
val nPartialSignature = alloc<secp256k1_frost_partial_sig>()
secp256k1_frost_partial_sig_parse(ctx, nPartialSignature.ptr, nat).requireSuccess("secp256k1_frost_partial_sig_parse() failed")
return nPartialSignature
}
private fun MemScope.allocFrostPublicNonce(pubnonce: ByteArray): secp256k1_frost_pubnonce {
val nat = toNat(pubnonce)
val nPublicNonce = alloc<secp256k1_frost_pubnonce>()
secp256k1_frost_pubnonce_parse(ctx, nPublicNonce.ptr, nat).requireSuccess("secp256k1_frost_pubnonce_parse() failed")
return nPublicNonce
}
override fun frostSharesGen(
seed32: ByteArray,
2024-08-05 22:34:45 +02:00
threshold: Int,
2024-08-04 23:53:28 +02:00
totalSigners: Int,
ids33: Array<ByteArray>
): Triple<Array<ByteArray>, Array<ByteArray>, ByteArray> {
require(seed32.size == 32) { "seed size should be 32" }
require(threshold > 1) { "threshold cannot be less then 1" }
require(threshold <= totalSigners) { "threshold($threshold) cannot be greater then totalSigners ($totalSigners)" }
require(ids33.size == totalSigners) { "ids33 size need to be the same as totalSigners size" }
ids33.forEach { require(it.size == 33) { "id33 size must be 33" } }
2024-08-04 23:53:28 +02:00
memScoped {
val nShares = allocArray<secp256k1_frost_share>(ids33.size)
2024-08-05 22:34:45 +02:00
val nVssCommitment = allocArray<secp256k1_pubkey>(threshold)
val pok64 = ByteArray(64)
2024-08-04 23:53:28 +02:00
val nIds33s = ids33.map { toNat(it) }
secp256k1_frost_shares_gen(
ctx = ctx,
shares = nShares,
2024-08-05 22:34:45 +02:00
vss_commitment = nVssCommitment,
2024-08-04 23:53:28 +02:00
pok64 = toNat(pok64),
seed32 = toNat(seed32),
threshold = threshold.convert(),
n_participants = ids33.size.convert(),
ids33 = nIds33s.toCValues()
)
return Triple(
2024-08-05 22:34:45 +02:00
ids33.indices.map { serializeFrostShare(nShares[it]) }.toTypedArray(),
(0 until threshold).map { serializePubkey(nVssCommitment[it]) }.toTypedArray(),
pok64
2024-08-05 22:34:45 +02:00
)
2024-08-04 23:53:28 +02:00
}
}
private fun MemScope.serializeFrostShare(nFrostShare: secp256k1_frost_share): ByteArray {
2024-08-21 17:28:07 +02:00
val natOutput = allocArray<UByteVar>(Secp256k1.FROST_SERIALIZED_SHARE_SIZE)
2024-08-04 23:53:28 +02:00
secp256k1_frost_share_serialize(ctx, natOutput, nFrostShare.ptr).requireSuccess("secp256k1_frost_share_serialize() failed")
return natOutput.readBytes(Secp256k1.FROST_SERIALIZED_SHARE_SIZE)
}
override fun frostShareAggregate(
totalShares: Array<ByteArray>,
vssCommitments: Array<Array<ByteArray>>,
2024-08-05 22:34:45 +02:00
totalShareCount: Int,
threshold: Int,
2024-08-04 23:53:28 +02:00
id33: ByteArray
): Pair<ByteArray, ByteArray> {
require(totalShares.size == totalShareCount) { "totalShare array size (${totalShares.size}) should be the same as total share count ($totalShareCount)" }
totalShares.forEach { require(it.size == 32) { "all shares should be of size 32" } }
require(vssCommitments.size == totalShareCount) { "vssCommitments array size ${vssCommitments.size} should be the same as total share count ($totalShareCount)" }
2024-08-06 00:19:55 +02:00
vssCommitments.forEach { vssCommitment ->
require(vssCommitment.size == threshold) { "all vss commitment array size (${vssCommitment.size}) should be the same as the threshold size ($threshold)" }
2024-08-06 00:19:55 +02:00
vssCommitment.forEach { publicKey ->
require(publicKey.size == 33 || publicKey.size == 65) { "vss commitment data size should be 33 or 65" }
2024-08-06 00:19:55 +02:00
}
}
require(threshold > 1) { "threshold should be greater then 1" }
require(threshold <= totalShareCount) { "Threshold can not be greater then the total share count" }
require(id33.size == 33) { "id size should be 33" }
2024-08-04 23:53:28 +02:00
memScoped {
2024-08-06 00:19:55 +02:00
val nAggregateShare = alloc<secp256k1_frost_share>()
val nAggregatePublicKey = alloc<secp256k1_xonly_pubkey>()
2024-08-04 23:53:28 +02:00
val nTotalShares = totalShares.map { allocFrostShare(it).ptr }
2024-08-05 22:34:45 +02:00
val nVssCommitments = allocArray<CPointerVar<secp256k1_pubkey>>(vssCommitments.size)
2024-08-22 10:47:18 +02:00
vssCommitments.forEachIndexed { index, commitments ->
val pubkeyArray = allocArray<secp256k1_pubkey>(commitments.size)
commitments.forEachIndexed { commitmentIndex, pubkeyData ->
pubkeyData.usePinned { pinned ->
if (secp256k1_ec_pubkey_parse(ctx, pubkeyArray[commitmentIndex].ptr, toNat(pinned.get()), pubkeyData.size.convert()) == 0) {
error("Failed to parse public key")
}
2024-08-05 22:34:45 +02:00
}
2024-08-22 10:47:18 +02:00
}
nVssCommitments[index] = pubkeyArray
2024-08-04 23:53:28 +02:00
}
val result = secp256k1_frost_share_agg(
2024-08-04 23:53:28 +02:00
ctx = ctx,
2024-08-06 00:19:55 +02:00
agg_share = nAggregateShare.ptr,
agg_pk = nAggregatePublicKey.ptr,
2024-08-04 23:53:28 +02:00
shares = nTotalShares.toCValues(),
2024-08-05 22:34:45 +02:00
vss_commitments = nVssCommitments,
2024-08-04 23:53:28 +02:00
n_shares = totalShareCount.convert(),
threshold = threshold.convert(),
id33 = toNat(id33)
)
println("Aggregate Result: $result")
2024-08-04 23:53:28 +02:00
return Pair(
2024-08-06 00:19:55 +02:00
serializeFrostShare(nAggregateShare),
serializeXonlyPubkey(nAggregatePublicKey)
2024-08-04 23:53:28 +02:00
)
}
}
override fun frostShareVerify(
threshold: Int,
id33: ByteArray,
share: ByteArray,
vssCommitment: Array<ByteArray>
): Int {
require(threshold > 1) { "threshold should be greater then 1" }
2024-08-21 17:28:07 +02:00
require(id33.size == 33) { "id size (${id33.size}) should be 33" }
require(share.size == Secp256k1.FROST_SERIALIZED_SHARE_SIZE) { "share size (${share.size}) should be of size ${Secp256k1.FROST_SERIALIZED_SHARE_SIZE}" }
2024-08-06 00:19:55 +02:00
2024-08-21 17:28:07 +02:00
require(vssCommitment.size == threshold) { "vss commitment array size (${vssCommitment.size}) should be the same as the threshold size ($threshold)" }
2024-08-06 00:19:55 +02:00
vssCommitment.forEach { publicKey ->
require(publicKey.size == 33 || publicKey.size == 65) { "vss commitment data size should be 33 or 65" }
2024-08-06 00:19:55 +02:00
}
2024-08-04 23:53:28 +02:00
memScoped {
val nFrostShare = allocFrostShare(share)
2024-08-22 10:47:18 +02:00
// val nVssCommitment = vssCommitment.map { allocPublicKey(it).ptr }.toCValues()
val nVssCommitment = allocArray<CPointerVar<secp256k1_pubkey>>(vssCommitment.size)
vssCommitment.forEachIndexed { index, pubkeyData ->
pubkeyData.usePinned { pinned ->
if (secp256k1_ec_pubkey_parse(ctx, nVssCommitment[index], toNat(pinned.get()), pubkeyData.size.convert()) == 0) {
error("Failed to parse public key")
}
}
}
2024-08-04 23:53:28 +02:00
return secp256k1_frost_share_verify(
ctx = ctx,
2024-08-22 10:47:18 +02:00
threshold = vssCommitment.size.convert(),
id33 = toNat(id33),
2024-08-04 23:53:28 +02:00
share = nFrostShare.ptr,
2024-08-22 10:47:18 +02:00
vss_commitment = nVssCommitment
2024-08-04 23:53:28 +02:00
)
}
}
2024-08-05 22:34:45 +02:00
override fun frostComputePublicShare(
threshold: Int,
id33: ByteArray,
vssCommitments: Array<Array<ByteArray>>,
totalSignersCount: Int
): ByteArray {
2024-08-18 01:27:04 +02:00
require(threshold > 1)
2024-08-06 00:19:55 +02:00
require(threshold <= totalSignersCount)
require(id33.size == 33)
require(vssCommitments.size == totalSignersCount)
vssCommitments.forEach { vssCommitment ->
require(vssCommitment.size == threshold)
vssCommitment.forEach { publicKey ->
require(publicKey.size == 33 || publicKey.size == 65)
}
}
2024-08-04 23:53:28 +02:00
memScoped {
2024-08-06 00:19:55 +02:00
val nPublicShare = alloc<secp256k1_pubkey>()
2024-08-05 22:34:45 +02:00
val nVssCommitments = allocArray<CPointerVar<secp256k1_pubkey>>(vssCommitments.size)
2024-08-22 10:47:18 +02:00
vssCommitments.forEachIndexed { index, commitments ->
val pubkeyArray = allocArray<secp256k1_pubkey>(commitments.size)
commitments.forEachIndexed { commitmentIndex, pubkeyData ->
pubkeyData.usePinned { pinned ->
if (secp256k1_ec_pubkey_parse(ctx, pubkeyArray[commitmentIndex].ptr, toNat(pinned.get()), pubkeyData.size.convert()) == 0) {
error("Failed to parse public key")
}
2024-08-05 22:34:45 +02:00
}
2024-08-22 10:47:18 +02:00
}
nVssCommitments[index] = pubkeyArray
2024-08-04 23:53:28 +02:00
}
2024-08-05 22:34:45 +02:00
2024-08-22 10:47:18 +02:00
val result = secp256k1_frost_compute_pubshare(
2024-08-04 23:53:28 +02:00
ctx = ctx,
2024-08-06 00:19:55 +02:00
pubshare = nPublicShare.ptr,
2024-08-04 23:53:28 +02:00
threshold = threshold.convert(),
id33 = toNat(id33),
vss_commitments = nVssCommitments,
2024-08-05 22:34:45 +02:00
n_participants = totalSignersCount.convert()
2024-08-04 23:53:28 +02:00
)
2024-08-22 10:47:18 +02:00
println("Compute pubshare result: $result")
2024-08-06 00:19:55 +02:00
return serializePubkey(nPublicShare)
2024-08-04 23:53:28 +02:00
}
}
2024-08-06 00:19:55 +02:00
override fun frostPublicKeyTweak(xOnlyPublicKey: ByteArray): ByteArray {
2024-08-21 17:28:07 +02:00
require(xOnlyPublicKey.size == Secp256k1.SERIALIZED_X_ONLY_PUBKEY_SIZE) { "pubkey size (${xOnlyPublicKey.size}) should be ${Secp256k1.SERIALIZED_X_ONLY_PUBKEY_SIZE}" }
2024-08-06 00:19:55 +02:00
2024-08-04 23:53:28 +02:00
memScoped {
val nTweakCache = alloc<secp256k1_frost_tweak_cache>()
2024-08-06 00:19:55 +02:00
val nPublicKey = allocXonlyPublicKey(xOnlyPublicKey)
2024-08-04 23:53:28 +02:00
secp256k1_frost_pubkey_tweak(
ctx = ctx,
tweak_cache = nTweakCache.ptr,
agg_pk = nPublicKey.ptr
)
return serializeFrostTweakCache(nTweakCache)
}
}
override fun frostPublicKeyEcTweakAdd(tweakCache: ByteArray, tweak32: ByteArray): ByteArray {
2024-08-06 00:19:55 +02:00
require(tweakCache.size == Secp256k1.FROST_TWEAK_CACHE_SIZE)
require(tweak32.size == 32)
2024-08-04 23:53:28 +02:00
memScoped {
val nTweakCache = alloc<secp256k1_frost_tweak_cache>()
memcpy(nTweakCache.ptr, toNat(tweakCache), Secp256k1.FROST_TWEAK_CACHE_SIZE.toULong())
val nPublicKey = alloc<secp256k1_pubkey>()
secp256k1_frost_pubkey_ec_tweak_add(
ctx = ctx,
output_pubkey = nPublicKey.ptr,
tweak_cache = nTweakCache.ptr,
tweak32 = toNat(tweak32)
)
return serializePubkey(nPublicKey)
}
}
private fun MemScope.serializeFrostTweakCache(nTweakCache: secp256k1_frost_tweak_cache): ByteArray {
val natOutput = ByteArray(Secp256k1.FROST_TWEAK_CACHE_SIZE)
memcpy(toNat(natOutput), nTweakCache.ptr, Secp256k1.FROST_TWEAK_CACHE_SIZE.toULong())
return natOutput
}
override fun frostPublicKeyXonlyTweakAdd(tweakCache: ByteArray, tweak32: ByteArray): ByteArray? {
2024-08-06 00:19:55 +02:00
require(tweakCache.size == Secp256k1.FROST_TWEAK_CACHE_SIZE)
require(tweak32.size == 32)
2024-08-04 23:53:28 +02:00
memScoped {
val nPublicKey = alloc<secp256k1_pubkey>()
val nTweakCache = alloc<secp256k1_frost_tweak_cache>()
memcpy(nTweakCache.ptr, toNat(tweakCache), Secp256k1.FROST_TWEAK_CACHE_SIZE.toULong())
secp256k1_frost_pubkey_xonly_tweak_add(
ctx = ctx,
output_pubkey = nPublicKey.ptr,
tweak_cache = nTweakCache.ptr,
tweak32 = toNat(tweak32)
)
return serializePubkey(nPublicKey)
2024-08-04 23:53:28 +02:00
}
}
private fun MemScope.serializeFrostSecnonce(nTweakCache: secp256k1_frost_secnonce): ByteArray {
val natOutput = ByteArray(Secp256k1.FROST_SECNONCE_SIZE)
memcpy(toNat(natOutput), nTweakCache.ptr, Secp256k1.FROST_SECNONCE_SIZE.toULong())
return natOutput
}
private fun MemScope.serializeFrostPubnonce(nPubnonce: secp256k1_frost_pubnonce): ByteArray {
val natOutput = allocArray<UByteVar>(Secp256k1.FROST_SERIALIZED_PUBNONCE_SIZE)
secp256k1_frost_pubnonce_serialize(ctx, natOutput, nPubnonce.ptr).requireSuccess("secp256k1_frost_pubnonce_serialize() failed")
return natOutput.readBytes(Secp256k1.FROST_SERIALIZED_PUBNONCE_SIZE)
}
override fun frostNonceGen(
sessionId32: ByteArray,
share: ByteArray?,
msg32: ByteArray?,
publicKey: ByteArray?,
2024-08-04 23:53:28 +02:00
extraInput32: ByteArray?
): Pair<ByteArray, ByteArray> {
2024-08-21 17:28:07 +02:00
require(sessionId32.size == 32) { "session id (${sessionId32.size}) size should be 32" }
share?.let {
2024-08-21 17:28:07 +02:00
require(share.size == Secp256k1.FROST_SERIALIZED_SHARE_SIZE) { "share size (${share.size}) should be ${Secp256k1.FROST_SERIALIZED_SHARE_SIZE}" }
}
msg32?.let {
2024-08-21 17:28:07 +02:00
require(msg32.size == 32) { "msg32 (${sessionId32.size}) size should be 32" }
}
publicKey?.let {
2024-08-21 17:28:07 +02:00
require(publicKey.size == 32) { "public key (${publicKey.size}) should be 32" }
}
2024-08-06 00:19:55 +02:00
extraInput32?.let {
2024-08-21 17:28:07 +02:00
require(it.size == 32) { "extraInput32 (${extraInput32.size}) size should be 32" }
2024-08-06 00:19:55 +02:00
}
2024-08-04 23:53:28 +02:00
memScoped {
2024-08-06 00:19:55 +02:00
val nFrostSecnonce = alloc<secp256k1_frost_secnonce>()
val nPublicNonce = alloc<secp256k1_frost_pubnonce>()
2024-08-04 23:53:28 +02:00
val nShare = share?.let { allocFrostShare(it) }
val nPublicKey = publicKey?.let { allocXonlyPublicKey(it) }
2024-08-04 23:53:28 +02:00
val nExtraInput32 = extraInput32?.let {
toNat(it)
}
secp256k1_frost_nonce_gen(
ctx = ctx,
2024-08-06 00:19:55 +02:00
secnonce = nFrostSecnonce.ptr,
pubnonce = nPublicNonce.ptr,
2024-08-04 23:53:28 +02:00
session_id32 = toNat(sessionId32),
agg_share = nShare?.ptr,
msg32 = msg32?.let { toNat(it) },
agg_pk = nPublicKey?.ptr,
2024-08-04 23:53:28 +02:00
extra_input32 = nExtraInput32
)
return Pair(
2024-08-06 00:19:55 +02:00
serializeFrostSecnonce(nFrostSecnonce),
serializeFrostPubnonce(nPublicNonce)
2024-08-04 23:53:28 +02:00
)
}
}
private fun MemScope.serializeFrostSession(nSession: secp256k1_frost_session): ByteArray {
val natOutput = ByteArray(Secp256k1.FROST_SESSION_SIZE)
memcpy(toNat(natOutput), nSession.ptr, Secp256k1.FROST_SESSION_SIZE.toULong())
return natOutput
}
override fun frostNonceProcess(
publicNonces: Array<ByteArray>,
2024-08-21 21:35:27 +02:00
threshold: Int,
2024-08-04 23:53:28 +02:00
msg32: ByteArray,
publicKey: ByteArray,
id33: ByteArray,
ids33: Array<ByteArray>,
tweakCache: ByteArray?,
2024-08-04 23:53:28 +02:00
adaptor: ByteArray?
): ByteArray {
2024-08-06 00:19:55 +02:00
publicNonces.forEach { publicNonce ->
2024-08-21 17:28:07 +02:00
require(publicNonce.size == Secp256k1.FROST_SERIALIZED_PUBNONCE_SIZE) { "pubnonce size (${publicNonce.size}) size should be ${Secp256k1.FROST_SERIALIZED_PUBNONCE_SIZE}" }
2024-08-06 00:19:55 +02:00
}
2024-08-21 17:28:07 +02:00
require(msg32.size == 32) { "msg32 (${msg32.size}) size should be 32" }
require(publicKey.size == Secp256k1.SERIALIZED_X_ONLY_PUBKEY_SIZE) { "publicKey size (${publicKey.size}) size should be ${Secp256k1.SERIALIZED_X_ONLY_PUBKEY_SIZE}" }
2024-08-21 21:35:27 +02:00
require(ids33.size == threshold) { "ids33 array size much match public nonces array size"}
2024-08-06 00:19:55 +02:00
ids33.forEach {
2024-08-21 17:28:07 +02:00
require(it.size == 33) { "id33 (${it.size}) size should be 33" }
2024-08-06 00:19:55 +02:00
}
tweakCache?.let {
2024-08-21 17:28:07 +02:00
require(tweakCache.size == Secp256k1.FROST_TWEAK_CACHE_SIZE) { "tweak cache size (${tweakCache.size}) size should be ${Secp256k1.FROST_TWEAK_CACHE_SIZE}" }
}
2024-08-06 00:19:55 +02:00
adaptor?.let {
2024-08-21 17:28:07 +02:00
require(it.size == 33 || it.size == 65) { "adaptor public key size (${it.size}) should be 33 or 65" }
2024-08-06 00:19:55 +02:00
}
2024-08-04 23:53:28 +02:00
memScoped {
val nSession = alloc<secp256k1_frost_session>();
val nPublicNonces = publicNonces.map { allocFrostPublicNonce(it).ptr }
val nPublicKey = allocXonlyPublicKey(publicKey)
val nIds33 = ids33.map { toNat(it) }
val nTweakCache = tweakCache?.let {
alloc<secp256k1_frost_tweak_cache>()
}?.also { nTweakCache ->
memcpy(nTweakCache.ptr, toNat(tweakCache) , Secp256k1.FROST_TWEAK_CACHE_SIZE.toULong())
}
2024-08-04 23:53:28 +02:00
val nAdaptor = adaptor?.let {
allocPublicKey(it).ptr
}
2024-08-21 22:28:30 +02:00
2024-08-04 23:53:28 +02:00
secp256k1_frost_nonce_process(
ctx = ctx,
session = nSession.ptr,
pubnonces = nPublicNonces.toCValues(),
n_pubnonces = publicNonces.size.convert(),
msg32 = toNat(msg32),
agg_pk = nPublicKey.ptr,
my_id33 = toNat(id33),
ids33 = nIds33.toCValues(),
tweak_cache = nTweakCache?.ptr,
2024-08-04 23:53:28 +02:00
adaptor = nAdaptor
)
return serializeFrostSession(
nSession = nSession
)
}
}
private fun MemScope.serializeFrostPartialSignature(nPartialSignature: secp256k1_frost_partial_sig): ByteArray {
val natOutput = allocArray<UByteVar>(Secp256k1.FROST_SERIALIZED_PARTIAL_SIGNATURE_SIZE)
secp256k1_frost_partial_sig_serialize(ctx, natOutput, nPartialSignature.ptr).requireSuccess("secp256k1_frost_partial_sig_serialize() failed")
return natOutput.readBytes(Secp256k1.FROST_SERIALIZED_PARTIAL_SIGNATURE_SIZE)
}
override fun frostPartialSign(
secnonce: ByteArray,
share: ByteArray,
session: ByteArray,
tweakCache: ByteArray?
2024-08-04 23:53:28 +02:00
): ByteArray {
2024-08-21 17:28:07 +02:00
require(secnonce.size == Secp256k1.FROST_SECNONCE_SIZE) { "secnonce size (${secnonce.size}) should be of size ${Secp256k1.FROST_SECNONCE_SIZE}" }
require(share.size == Secp256k1.FROST_SERIALIZED_SHARE_SIZE) { "share size (${share.size}) should be of size ${Secp256k1.FROST_SERIALIZED_SHARE_SIZE}" }
require(session.size == Secp256k1.FROST_SESSION_SIZE) { "session size (${share.size}) should be of size ${Secp256k1.FROST_SESSION_SIZE}" }
tweakCache?.let {
2024-08-21 17:28:07 +02:00
require(tweakCache.size == Secp256k1.FROST_TWEAK_CACHE_SIZE) { "tweak cache size (${tweakCache.size}) size should be ${Secp256k1.FROST_TWEAK_CACHE_SIZE}" }
}
2024-08-04 23:53:28 +02:00
memScoped {
val nPartialSignature = alloc<secp256k1_frost_partial_sig>();
val nSecnonce = alloc<secp256k1_frost_secnonce>()
memcpy(nSecnonce.ptr, toNat(secnonce), Secp256k1.FROST_SECNONCE_SIZE.toULong())
val nShare = allocFrostShare(share)
val nSession = alloc<secp256k1_frost_session>()
memcpy(nSession.ptr, toNat(session), Secp256k1.FROST_SESSION_SIZE.toULong())
val nTweakCache = tweakCache?.let {
alloc<secp256k1_frost_tweak_cache>()
}?.also { nTweakCache ->
memcpy(nTweakCache.ptr, toNat(tweakCache) , Secp256k1.FROST_TWEAK_CACHE_SIZE.toULong())
}
2024-08-04 23:53:28 +02:00
secp256k1_frost_partial_sign(
ctx,
nPartialSignature.ptr,
nSecnonce.ptr,
nShare.ptr,
nSession.ptr,
nTweakCache?.ptr
2024-08-04 23:53:28 +02:00
)
return serializeFrostPartialSignature(nPartialSignature)
}
}
override fun frostPartialSignatureVerify(
partialSig: ByteArray,
publicNonce: ByteArray,
publicShare: ByteArray,
session: ByteArray,
tweakCache: ByteArray?
2024-08-04 23:53:28 +02:00
): Int {
2024-08-21 17:28:07 +02:00
require(partialSig.size == 32) { "partialSig (${partialSig.size}) size should be 32" }
require(publicNonce.size == Secp256k1.FROST_SERIALIZED_PUBNONCE_SIZE) { "public nonce (${publicNonce.size}) size should be ${Secp256k1.FROST_SERIALIZED_PUBNONCE_SIZE}" }
require(publicShare.size == 33 || publicShare.size == 65) { "public share size (${partialSig.size}) should be 33 or 65" }
require(session.size == Secp256k1.FROST_SESSION_SIZE) { "session size (${session.size}) size should be ${Secp256k1.FROST_SESSION_SIZE}" }
tweakCache?.let {
2024-08-21 17:28:07 +02:00
require(tweakCache.size == Secp256k1.FROST_TWEAK_CACHE_SIZE) { "tweak cache size (${tweakCache.size}) size should be ${Secp256k1.FROST_TWEAK_CACHE_SIZE}" }
}
2024-08-04 23:53:28 +02:00
memScoped {
val nPartialSignature = allocFrostPartialSignature(partialSig)
val nPublicNonce = allocFrostPublicNonce(publicNonce)
val nPublicShare = allocPublicKey(publicShare)
val nSession = alloc<secp256k1_frost_session>()
memcpy(nSession.ptr, toNat(session), Secp256k1.FROST_SESSION_SIZE.toULong())
val nTweakCache = tweakCache?.let {
alloc<secp256k1_frost_tweak_cache>()
}?.also { nTweakCache ->
memcpy(nTweakCache.ptr, toNat(tweakCache) , Secp256k1.FROST_TWEAK_CACHE_SIZE.toULong())
}
2024-08-04 23:53:28 +02:00
return secp256k1_frost_partial_sig_verify(
ctx,
nPartialSignature.ptr,
nPublicNonce.ptr,
nPublicShare.ptr,
nSession.ptr,
nTweakCache?.ptr
2024-08-04 23:53:28 +02:00
)
}
}
2024-08-21 21:35:27 +02:00
override fun frostPartialSignatureAggregate(
session: ByteArray,
partialSignatures: Array<ByteArray>,
threshold: Int
): ByteArray {
require(threshold > 1) { "threshold must be greater then 1" }
require(session.size == Secp256k1.FROST_SESSION_SIZE) { "session size (${session.size}) should be ${Secp256k1.FROST_SESSION_SIZE}" }
require(partialSignatures.size == threshold) { "partialSignatures array size should match the threshold size" }
2024-08-06 00:19:55 +02:00
partialSignatures.forEach { partialSig ->
2024-08-21 21:35:27 +02:00
require(partialSig.size == 32) { "partialSignatures size (${partialSig.size}) should be 32" }
2024-08-06 00:19:55 +02:00
}
2024-08-04 23:53:28 +02:00
memScoped {
val sig64 = ByteArray(64)
val nSession = alloc<secp256k1_frost_session>();
memcpy(nSession.ptr, toNat(session), Secp256k1.FROST_SESSION_SIZE.toULong())
val nPartialSignatures = partialSignatures.map { allocFrostPartialSignature(it).ptr }
secp256k1_frost_partial_sig_agg(
ctx,
toNat(sig64),
nSession.ptr,
nPartialSignatures.toCValues(),
2024-08-21 21:35:27 +02:00
threshold.convert()
2024-08-04 23:53:28 +02:00
)
return sig64
}
}
public override fun cleanup() {
secp256k1_context_destroy(ctx)
}
2020-06-26 17:10:48 +02:00
}
internal actual fun getSecpk256k1(): Secp256k1 = Secp256k1Native