package fr.acinq.secp256k1 import kotlinx.cinterop.* import kotlinx.cinterop.ptr import platform.posix.memcpy import platform.posix.size_tVar import secp256k1.* @OptIn(ExperimentalUnsignedTypes::class, ExperimentalForeignApi::class) 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 secp256k1 context") } private fun Int.requireSuccess(message: String): Int = if (this != 1) throw Secp256k1Exception(message) else this private fun MemScope.allocSignature(input: ByteArray): secp256k1_ecdsa_signature { val sig = alloc() 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()) } result.requireSuccess("cannot parse signature (size = ${input.size} sig = ${Hex.encode(input)}") return sig } private fun MemScope.serializeSignature(signature: secp256k1_ecdsa_signature): ByteArray { val natOutput = allocArray(64) secp256k1_ecdsa_signature_serialize_compact(ctx, natOutput, signature.ptr).requireSuccess("secp256k1_ecdsa_signature_serialize_compact() failed") return natOutput.readBytes(64) } private fun MemScope.allocPublicKey(pubkey: ByteArray): secp256k1_pubkey { val natPub = toNat(pubkey) val pub = alloc() secp256k1_ec_pubkey_parse(ctx, pub.ptr, natPub, pubkey.size.convert()).requireSuccess("secp256k1_ec_pubkey_parse() failed") return pub } private fun MemScope.allocXonlyPublicKey(pubkey: ByteArray): secp256k1_xonly_pubkey { val natPub = toNat(pubkey) val pub = alloc() 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_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_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(65) val outputLen = alloc() outputLen.value = 65.convert() secp256k1_ec_pubkey_serialize(ctx, serialized, outputLen.ptr, pubkey.ptr, SECP256K1_EC_UNCOMPRESSED.convert()).requireSuccess("secp256k1_ec_pubkey_serialize() failed") return serialized.readBytes(outputLen.value.convert()) } private fun MemScope.serializeXonlyPubkey(pubkey: secp256k1_xonly_pubkey): ByteArray { val serialized = allocArray(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(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(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 { 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) memScoped { val nPubkey = allocPublicKey(pubkey) val nMessage = toNat(message) val nSig = allocSignature(signature) return secp256k1_ecdsa_verify(ctx, nSig.ptr, nMessage, nPubkey.ptr) == 1 } } public override fun sign(message: ByteArray, privkey: ByteArray): ByteArray { require(privkey.size == 32) require(message.size == 32) memScoped { val nPrivkey = toNat(privkey) val nMessage = toNat(message) val nSig = alloc() secp256k1_ecdsa_sign(ctx, nSig.ptr, nMessage, nPrivkey, null, null).requireSuccess("secp256k1_ecdsa_sign() failed") return serializeSignature(nSig) } } public override fun signatureNormalize(sig: ByteArray): Pair { require(sig.size >= 64) { "invalid signature ${Hex.encode(sig)}" } memScoped { val nSig = allocSignature(sig) val isHighS = secp256k1_ecdsa_signature_normalize(ctx, nSig.ptr, nSig.ptr) return Pair(serializeSignature(nSig), isHighS == 1) } } public override fun secKeyVerify(privkey: ByteArray): Boolean { if (privkey.size != 32) return false memScoped { val nPrivkey = toNat(privkey) return secp256k1_ec_seckey_verify(ctx, nPrivkey) == 1 } } public override fun pubkeyCreate(privkey: ByteArray): ByteArray { require(privkey.size == 32) memScoped { val nPrivkey = toNat(privkey) val nPubkey = alloc() secp256k1_ec_pubkey_create(ctx, nPubkey.ptr, nPrivkey).requireSuccess("secp256k1_ec_pubkey_create() failed") return serializePubkey(nPubkey) } } public override fun pubkeyParse(pubkey: ByteArray): ByteArray { require(pubkey.size == 33 || pubkey.size == 65) memScoped { val nPubkey = allocPublicKey(pubkey) return serializePubkey(nPubkey) } } public override fun privKeyNegate(privkey: ByteArray): ByteArray { 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") return negated } } public override fun privKeyTweakAdd(privkey: ByteArray, tweak: ByteArray): ByteArray { require(privkey.size == 32) require(tweak.size == 32) memScoped { val added = privkey.copyOf() val natAdd = toNat(added) val natTweak = toNat(tweak) secp256k1_ec_seckey_tweak_add(ctx, natAdd, natTweak).requireSuccess("secp256k1_ec_seckey_tweak_add() failed") return added } } public override fun privKeyTweakMul(privkey: ByteArray, tweak: ByteArray): ByteArray { require(privkey.size == 32) require(tweak.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 { val nPubkey = allocPublicKey(pubkey) secp256k1_ec_pubkey_negate(ctx, nPubkey.ptr).requireSuccess("secp256k1_ec_pubkey_negate() failed") return serializePubkey(nPubkey) } } public override fun pubKeyTweakAdd(pubkey: ByteArray, tweak: ByteArray): ByteArray { require(pubkey.size == 33 || pubkey.size == 65) require(tweak.size == 32) 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) } } public override fun pubKeyTweakMul(pubkey: ByteArray, tweak: ByteArray): ByteArray { require(pubkey.size == 33 || pubkey.size == 65) require(tweak.size == 32) 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) } } public override fun pubKeyCombine(pubkeys: Array): ByteArray { require(pubkeys.isNotEmpty()) pubkeys.forEach { require(it.size == 33 || it.size == 65) } memScoped { val nPubkeys = pubkeys.map { allocPublicKey(it).ptr } val combined = alloc() 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(privkey: ByteArray, pubkey: ByteArray): ByteArray { require(privkey.size == 32) require(pubkey.size == 33 || pubkey.size == 65) memScoped { val nPubkey = allocPublicKey(pubkey) val nPrivkey = toNat(privkey) val output = allocArray(32) secp256k1_ecdh(ctx, output, nPubkey.ptr, nPrivkey, null, null).requireSuccess("secp256k1_ecdh() failed") return output.readBytes(32) } } public override fun ecdsaRecover(sig: ByteArray, message: ByteArray, recid: Int): ByteArray { require(sig.size == 64) require(message.size == 32) require(recid in 0..3) memScoped { val nSig = toNat(sig) val rSig = alloc() secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, rSig.ptr, nSig, recid).requireSuccess("secp256k1_ecdsa_recoverable_signature_parse_compact() failed") val nMessage = toNat(message) val pubkey = alloc() secp256k1_ecdsa_recover(ctx, pubkey.ptr, rSig.ptr, nMessage).requireSuccess("secp256k1_ecdsa_recover() failed") return serializePubkey(pubkey) } } public override fun compact2der(sig: ByteArray): ByteArray { require(sig.size == 64) memScoped { val nSig = allocSignature(sig) val natOutput = allocArray(73) val len = alloc() len.value = 73.convert() secp256k1_ecdsa_signature_serialize_der(ctx, natOutput, len.ptr, nSig.ptr).requireSuccess("secp256k1_ecdsa_signature_serialize_der() failed") return natOutput.readBytes(len.value.toInt()) } } 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_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, 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(64) val keypair = alloc() 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() val pubnonce = alloc() val nPubkey = allocPublicKey(aggpubkey) val nKeyAggCache = keyaggCache?.let { val n = alloc() 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(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 { 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_nonce_agg(ctx, combined.ptr, nPubnonces.toCValues(), pubnonces.size.convert()).requireSuccess("secp256k1_musig_nonce_agg() failed") return serializeAggnonce(combined) } } override fun musigPubkeyAgg(pubkeys: Array, 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() val nKeyAggCache = keyaggCache?.let { val n = alloc() 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() memcpy(nKeyAggCache.ptr, toNat(keyaggCache), Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong()) val nPubkey = alloc() 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() memcpy(nKeyAggCache.ptr, toNat(keyaggCache), Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong()) val nPubkey = alloc() 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() memcpy(nKeyAggCache.ptr, toNat(keyaggCache), Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong()) val nSession = alloc() val nAggnonce = alloc() 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() memcpy(nSecnonce.ptr, toNat(secnonce), Secp256k1.MUSIG2_SECRET_NONCE_SIZE.toULong()) val nKeypair = alloc() secp256k1_keypair_create(ctx, nKeypair.ptr, toNat(privkey)) val nPsig = alloc() val nKeyAggCache = alloc() memcpy(nKeyAggCache.ptr, toNat(keyaggCache), Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong()) val nSession = alloc() 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() memcpy(nKeyAggCache.ptr, toNat(keyaggCache), Secp256k1.MUSIG2_PUBLIC_KEYAGG_CACHE_SIZE.toULong()) val nSession = alloc() 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 { require(session.size == Secp256k1.MUSIG2_PUBLIC_SESSION_SIZE) require(psigs.isNotEmpty()) psigs.forEach { require(it.size == 32) } memScoped { val nSession = alloc() 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 } } private fun MemScope.allocFrostShare(share: ByteArray): secp256k1_frost_share { val nat = toNat(share) val nFrostShare = alloc() 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_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_parse(ctx, nPublicNonce.ptr, nat).requireSuccess("secp256k1_frost_pubnonce_parse() failed") return nPublicNonce } override fun frostSharesGen( seed32: ByteArray, threshold: Int, totalSigners: Int, ids33: Array ): Triple, Array, 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" }) } memScoped { val nShares = allocArray(ids33.size) val nVssCommitment = allocArray(threshold) val pok64 = ByteArray(64) val nIds33s = ids33.map { toNat(it) } secp256k1_frost_shares_gen( ctx = ctx, shares = nShares, vss_commitment = nVssCommitment, pok64 = toNat(pok64), seed32 = toNat(seed32), threshold = threshold.convert(), n_participants = ids33.size.convert(), ids33 = nIds33s.toCValues() ) return Triple( ids33.indices.map { serializeFrostShare(nShares[it]) }.toTypedArray(), (0 until threshold).map { serializePubkey(nVssCommitment[it]) }.toTypedArray(), pok64 ) } } private fun MemScope.serializeFrostShare(nFrostShare: secp256k1_frost_share): ByteArray { val natOutput = allocArray(Secp256k1.FROST_SHARE_SIZE) 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, vssCommitments: Array>, totalShareCount: Int, threshold: Int, id33: ByteArray ): Pair { require(totalShares.size == totalShareCount) totalShares.forEach { require(it.size == 33) } require(vssCommitments.size == totalShareCount) vssCommitments.forEach { vssCommitment -> require(vssCommitment.size == threshold) vssCommitment.forEach { publicKey -> require(publicKey.size == 33 || publicKey.size == 65) } } require(threshold > 1) require(threshold <= totalShareCount) require(id33.size == 33) memScoped { val nAggregateShare = alloc() val nAggregatePublicKey = alloc() val nTotalShares = totalShares.map { allocFrostShare(it).ptr } val nVssCommitments = allocArray>(vssCommitments.size) vssCommitments.forEachIndexed { index, vssCommitment -> nVssCommitments[index] = allocArrayOf( vssCommitment.map { bytes -> allocPublicKey(bytes).ptr } ).reinterpret() } secp256k1_frost_share_agg( ctx = ctx, agg_share = nAggregateShare.ptr, agg_pk = nAggregatePublicKey.ptr, shares = nTotalShares.toCValues(), vss_commitments = nVssCommitments, n_shares = totalShareCount.convert(), threshold = threshold.convert(), id33 = toNat(id33) ) return Pair( serializeFrostShare(nAggregateShare), serializeXonlyPubkey(nAggregatePublicKey) ) } } override fun frostShareVerify( threshold: Int, id33: ByteArray, share: ByteArray, vssCommitment: Array ): Int { require(threshold > 1) require(id33.size == 33) require(share.size == Secp256k1.FROST_SHARE_SIZE) require(vssCommitment.size == threshold) vssCommitment.forEach { publicKey -> require(publicKey.size == 33 || publicKey.size == 65) } memScoped { val nId33 = toNat(id33); val nFrostShare = allocFrostShare(share) val nVssCommitment = vssCommitment.map { allocPublicKey(it).ptr } return secp256k1_frost_share_verify( ctx = ctx, threshold = threshold.convert(), id33 = nId33, share = nFrostShare.ptr, vss_commitment = nVssCommitment.toCValues() ) } } override fun frostComputePublicShare( threshold: Int, id33: ByteArray, vssCommitments: Array>, totalSignersCount: Int ): ByteArray { require(threshold > 1) 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) } } memScoped { val nPublicShare = alloc() val nVssCommitments = allocArray>(vssCommitments.size) vssCommitments.forEachIndexed { index, vssCommitment -> nVssCommitments[index] = allocArrayOf( vssCommitment.map { bytes -> allocPublicKey(bytes).ptr } ).reinterpret() } secp256k1_frost_compute_pubshare( ctx = ctx, pubshare = nPublicShare.ptr, threshold = threshold.convert(), id33 = toNat(id33), vss_commitments = nVssCommitments, n_participants = totalSignersCount.convert() ) return serializePubkey(nPublicShare) } } override fun frostPublicKeyTweak(xOnlyPublicKey: ByteArray): ByteArray { require(xOnlyPublicKey.size == Secp256k1.X_ONLY_PUBKEY_SIZE) memScoped { val nTweakCache = alloc() val nPublicKey = allocXonlyPublicKey(xOnlyPublicKey) 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 { require(tweakCache.size == Secp256k1.FROST_TWEAK_CACHE_SIZE) require(tweak32.size == 32) memScoped { val nTweakCache = alloc() memcpy(nTweakCache.ptr, toNat(tweakCache), Secp256k1.FROST_TWEAK_CACHE_SIZE.toULong()) val nPublicKey = alloc() 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? { require(tweakCache.size == Secp256k1.FROST_TWEAK_CACHE_SIZE) require(tweak32.size == 32) memScoped { val nPublicKey = alloc() val nTweakCache = alloc() 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) } } 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(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?, extraInput32: ByteArray? ): Pair { require(sessionId32.size == 32) share?.let { require(share.size == Secp256k1.FROST_SHARE_SIZE) } msg32?.let { require(msg32.size == 33) } publicKey?.let { require(publicKey.size == 33 || publicKey.size == 65) } extraInput32?.let { require(it.size == 33) } memScoped { val nFrostSecnonce = alloc() val nPublicNonce = alloc() val nShare = share?.let { allocFrostShare(it) } val nPublicKey = publicKey?.let { allocXonlyPublicKey(it) } val nExtraInput32 = extraInput32?.let { toNat(it) } secp256k1_frost_nonce_gen( ctx = ctx, secnonce = nFrostSecnonce.ptr, pubnonce = nPublicNonce.ptr, session_id32 = toNat(sessionId32), agg_share = nShare?.ptr, msg32 = msg32?.let { toNat(it) }, agg_pk = nPublicKey?.ptr, extra_input32 = nExtraInput32 ) return Pair( serializeFrostSecnonce(nFrostSecnonce), serializeFrostPubnonce(nPublicNonce) ) } } 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, msg32: ByteArray, publicKey: ByteArray, id33: ByteArray, ids33: Array, tweakCache: ByteArray?, adaptor: ByteArray? ): ByteArray { publicNonces.forEach { publicNonce -> require(publicNonce.size == Secp256k1.FROST_PUBNONCE_SIZE) } require(msg32.size == 32) require(publicKey.size == 33 || publicKey.size == 65) ids33.forEach { require(it.size == 33) } tweakCache?.let { require(tweakCache.size == Secp256k1.FROST_TWEAK_CACHE_SIZE) } adaptor?.let { require(it.size == 33 || it.size == 65) } memScoped { val nSession = alloc(); val nPublicNonces = publicNonces.map { allocFrostPublicNonce(it).ptr } val nPublicKey = allocXonlyPublicKey(publicKey) val nIds33 = ids33.map { toNat(it) } val nTweakCache = tweakCache?.let { alloc() }?.also { nTweakCache -> memcpy(nTweakCache.ptr, toNat(tweakCache) , Secp256k1.FROST_TWEAK_CACHE_SIZE.toULong()) } val nAdaptor = adaptor?.let { allocPublicKey(it).ptr } 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, adaptor = nAdaptor ) return serializeFrostSession( nSession = nSession ) } } private fun MemScope.serializeFrostPartialSignature(nPartialSignature: secp256k1_frost_partial_sig): ByteArray { val natOutput = allocArray(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? ): ByteArray { require(secnonce.size == Secp256k1.FROST_SECNONCE_SIZE) require(share.size == Secp256k1.FROST_SHARE_SIZE) require(session.size == Secp256k1.FROST_SESSION_SIZE) tweakCache?.let { require(tweakCache.size == Secp256k1.FROST_TWEAK_CACHE_SIZE) } memScoped { val nPartialSignature = alloc(); val nSecnonce = alloc() memcpy(nSecnonce.ptr, toNat(secnonce), Secp256k1.FROST_SECNONCE_SIZE.toULong()) val nShare = allocFrostShare(share) val nSession = alloc() memcpy(nSession.ptr, toNat(session), Secp256k1.FROST_SESSION_SIZE.toULong()) val nTweakCache = tweakCache?.let { alloc() }?.also { nTweakCache -> memcpy(nTweakCache.ptr, toNat(tweakCache) , Secp256k1.FROST_TWEAK_CACHE_SIZE.toULong()) } secp256k1_frost_partial_sign( ctx, nPartialSignature.ptr, nSecnonce.ptr, nShare.ptr, nSession.ptr, nTweakCache?.ptr ) return serializeFrostPartialSignature(nPartialSignature) } } override fun frostPartialSignatureVerify( partialSig: ByteArray, publicNonce: ByteArray, publicShare: ByteArray, session: ByteArray, tweakCache: ByteArray? ): Int { require(partialSig.size == 32) require(publicNonce.size == Secp256k1.MUSIG2_PUBLIC_NONCE_SIZE) require(publicShare.size == 33 || publicShare.size == 65) require(session.size == Secp256k1.FROST_SESSION_SIZE) tweakCache?.let { require(tweakCache.size == Secp256k1.FROST_TWEAK_CACHE_SIZE) } memScoped { val nPartialSignature = allocFrostPartialSignature(partialSig) val nPublicNonce = allocFrostPublicNonce(publicNonce) val nPublicShare = allocPublicKey(publicShare) val nSession = alloc() memcpy(nSession.ptr, toNat(session), Secp256k1.FROST_SESSION_SIZE.toULong()) val nTweakCache = tweakCache?.let { alloc() }?.also { nTweakCache -> memcpy(nTweakCache.ptr, toNat(tweakCache) , Secp256k1.FROST_TWEAK_CACHE_SIZE.toULong()) } return secp256k1_frost_partial_sig_verify( ctx, nPartialSignature.ptr, nPublicNonce.ptr, nPublicShare.ptr, nSession.ptr, nTweakCache?.ptr ) } } override fun frostPartialSignatureAggregate(session: ByteArray, partialSignatures: Array): ByteArray { require(session.size == Secp256k1.FROST_SESSION_SIZE) partialSignatures.forEach { partialSig -> require(partialSig.size == 32) } memScoped { val sig64 = ByteArray(64) val nSession = alloc(); 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(), partialSignatures.size.convert() ) return sig64 } } public override fun cleanup() { secp256k1_context_destroy(ctx) } } internal actual fun getSecpk256k1(): Secp256k1 = Secp256k1Native