2024-08-18 01:27:04 +02:00
|
|
|
package fr.acinq.secp256k1
|
|
|
|
|
|
|
|
import kotlinx.serialization.json.*
|
|
|
|
|
|
|
|
import kotlin.test.*
|
|
|
|
|
|
|
|
class FrostTest: BaseTest() {
|
2024-08-20 22:29:29 +02:00
|
|
|
val msg32 = "this_could_be_the_hash_of_a_msg!".encodeToByteArray()
|
2024-08-18 01:27:04 +02:00
|
|
|
|
|
|
|
@Test
|
2024-08-20 02:19:43 +02:00
|
|
|
fun `frost share generation test cases`() {
|
2024-08-18 01:27:04 +02:00
|
|
|
val tests = readData("frost/share_gen_vectors.json")
|
|
|
|
val pubkeys = tests.jsonObject["pubkeys"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
|
|
|
|
2024-08-19 21:40:56 +02:00
|
|
|
tests.jsonObject["valid_share_gen_test_cases"]!!.jsonArray.forEach { validTestCases ->
|
|
|
|
|
2024-08-18 01:27:04 +02:00
|
|
|
val keyIndices = validTestCases.jsonObject["key_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
|
|
|
|
2024-08-19 21:40:56 +02:00
|
|
|
val seed32 = Hex.decode(validTestCases.jsonObject["seed"]!!.jsonPrimitive.content)
|
2024-08-18 01:27:04 +02:00
|
|
|
val nParticipants = keyIndices.size
|
|
|
|
val threshold = validTestCases.jsonObject["threshold"]!!.jsonPrimitive.int
|
|
|
|
val ids33 = keyIndices.map { pubkeys[it] }.toTypedArray()
|
|
|
|
|
|
|
|
val result = Secp256k1.frostSharesGen(
|
|
|
|
seed32,
|
|
|
|
threshold,
|
|
|
|
nParticipants,
|
|
|
|
ids33
|
|
|
|
)
|
|
|
|
|
|
|
|
val expected = validTestCases.jsonObject["expected"]!!;
|
|
|
|
|
|
|
|
val expectedShare = expected.jsonObject["frost_share"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
|
|
|
val expectedVSSCommitment = expected.jsonObject["vss_commitment"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
|
|
|
val expectedPoK64 = Hex.decode(expected.jsonObject["pok64"]!!.jsonPrimitive.content)
|
|
|
|
|
2024-08-20 22:29:29 +02:00
|
|
|
|
2024-08-18 01:27:04 +02:00
|
|
|
result.first.forEachIndexed { index, share ->
|
2024-08-21 17:28:07 +02:00
|
|
|
assertEquals(
|
|
|
|
expected = Hex.encode(expectedShare[index]),
|
|
|
|
actual = Hex.encode(share),
|
|
|
|
"Unexpected $index:share for $keyIndices test case"
|
2024-08-18 01:27:04 +02:00
|
|
|
)
|
|
|
|
}
|
|
|
|
result.second.forEachIndexed { index, vssCommitment ->
|
2024-08-21 17:28:07 +02:00
|
|
|
assertEquals(
|
|
|
|
expected = Hex.encode(expectedVSSCommitment[index]),
|
|
|
|
actual = Hex.encode(vssCommitment),
|
|
|
|
"Unexpected $index:vss_commitment for the $keyIndices test case"
|
2024-08-18 01:27:04 +02:00
|
|
|
)
|
|
|
|
}
|
|
|
|
assertEquals(
|
|
|
|
expected = Hex.encode(expectedPoK64),
|
|
|
|
actual = Hex.encode(result.third),
|
|
|
|
message = "Unexpected pok64 for $keyIndices test case"
|
|
|
|
)
|
|
|
|
}
|
2024-08-20 02:19:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
fun `frost share generation signers`() {
|
|
|
|
val tests = readData("frost/share_gen_signers_vectors.json")
|
|
|
|
val pubkeys = tests.jsonObject["pubkeys"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
|
|
|
|
2024-08-19 21:40:56 +02:00
|
|
|
val signerShareGenTestCase = tests.jsonObject["valid_signers_share_gen_test_case"]!!
|
|
|
|
|
|
|
|
val keyIndices = signerShareGenTestCase.jsonObject["key_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
|
|
|
|
|
|
|
val nParticipants = keyIndices.size
|
|
|
|
val threshold = signerShareGenTestCase.jsonObject["threshold"]!!.jsonPrimitive.int
|
|
|
|
val ids33 = keyIndices.map { pubkeys[it] }.toTypedArray()
|
|
|
|
|
|
|
|
signerShareGenTestCase.jsonObject["signers"]!!.jsonArray.forEachIndexed { signerIndex, signer ->
|
|
|
|
val seed32 = Hex.decode(signer.jsonObject["seed"]!!.jsonPrimitive.content)
|
2024-08-20 02:19:43 +02:00
|
|
|
println("Testing ${signer.jsonObject["seed"]!!.jsonPrimitive.content}")
|
|
|
|
// There seems to be a bug that causes a crash if we call frost_share_gen too often
|
2024-08-19 21:40:56 +02:00
|
|
|
|
|
|
|
val result = Secp256k1.frostSharesGen(
|
|
|
|
seed32,
|
|
|
|
threshold,
|
|
|
|
nParticipants,
|
|
|
|
ids33
|
|
|
|
)
|
|
|
|
|
|
|
|
val expected = signer.jsonObject["expected"]!!;
|
|
|
|
|
|
|
|
val expectedShare = expected.jsonObject["frost_share"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
|
|
|
val expectedVSSCommitment = expected.jsonObject["vss_commitment"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
|
|
|
val expectedPoK64 = Hex.decode(expected.jsonObject["pok64"]!!.jsonPrimitive.content)
|
|
|
|
|
|
|
|
result.first.forEachIndexed { index, share ->
|
|
|
|
assertEquals(
|
|
|
|
expected = Hex.encode(expectedShare[index]),
|
|
|
|
actual = Hex.encode(share),
|
|
|
|
"Unexpected $signerIndex:signer $index:share for $keyIndices test case"
|
|
|
|
)
|
|
|
|
}
|
|
|
|
result.second.forEachIndexed { index, vssCommitment ->
|
|
|
|
assertEquals(
|
|
|
|
expected = Hex.encode(expectedVSSCommitment[index]),
|
|
|
|
actual = Hex.encode(vssCommitment),
|
|
|
|
"Unexpected $signerIndex:signer $index:vss_commitment for the $keyIndices test case"
|
|
|
|
)
|
|
|
|
}
|
|
|
|
assertEquals(
|
|
|
|
expected = Hex.encode(expectedPoK64),
|
|
|
|
actual = Hex.encode(result.third),
|
|
|
|
message = "Unexpected $signerIndex:signer pok64 for $keyIndices test case"
|
|
|
|
)
|
|
|
|
}
|
2024-08-18 01:27:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
fun `frost share aggregation`() {
|
2024-08-19 21:40:56 +02:00
|
|
|
val shareGenTests = readData("frost/share_gen_vectors.json")
|
|
|
|
val tests = readData("frost/share_agg_vectors.json")
|
|
|
|
|
2024-08-20 22:29:29 +02:00
|
|
|
val expectedAggregatePublicKey = tests.jsonObject["aggregate_public_key"]!!.jsonPrimitive.content
|
2024-08-19 21:40:56 +02:00
|
|
|
val publicKeys = shareGenTests.jsonObject["pubkeys"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
|
|
|
|
|
|
|
val signerShareGenTestCase = shareGenTests.jsonObject["valid_signers_share_gen_test_case"]!!;
|
|
|
|
|
|
|
|
val keyIndices = signerShareGenTestCase.jsonObject["key_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
|
|
|
val nParticipants = keyIndices.size
|
|
|
|
val threshold = signerShareGenTestCase.jsonObject["threshold"]!!.jsonPrimitive.int
|
|
|
|
val ids33 = keyIndices.map { publicKeys[it] }.toTypedArray()
|
|
|
|
|
|
|
|
val vssCommitments = signerShareGenTestCase.jsonObject["signers"]!!.jsonArray.map { signer ->
|
|
|
|
signer.jsonObject["expected"]!!.jsonObject["vss_commitment"]!!.jsonArray.map {
|
|
|
|
Hex.decode(it.jsonPrimitive.content)
|
|
|
|
}.toTypedArray()
|
|
|
|
}
|
|
|
|
|
2024-08-20 01:05:29 +02:00
|
|
|
signerShareGenTestCase.jsonObject["signers"]!!.jsonArray.forEachIndexed { index, _ ->
|
2024-08-19 21:40:56 +02:00
|
|
|
val assignedShares = signerShareGenTestCase.jsonObject["signers"]!!.jsonArray.map {
|
|
|
|
Hex.decode(
|
|
|
|
it.jsonObject["expected"]!!.jsonObject["frost_share"]!!.jsonArray[index].jsonPrimitive.content
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
val result = Secp256k1.frostShareAggregate(
|
|
|
|
assignedShares.toTypedArray(),
|
|
|
|
vssCommitments.toTypedArray(),
|
|
|
|
nParticipants,
|
|
|
|
threshold,
|
|
|
|
ids33[index]
|
|
|
|
)
|
|
|
|
|
|
|
|
val expected = tests.jsonObject["expected"]!!.jsonArray[index];
|
|
|
|
|
|
|
|
val expectedAggregateShare = expected.jsonObject["aggregate_share"]!!.jsonPrimitive.content
|
2024-08-20 01:05:29 +02:00
|
|
|
val expectedPublicShare = expected.jsonObject["public_share"]!!.jsonPrimitive.content
|
2024-08-19 21:40:56 +02:00
|
|
|
|
2024-08-20 00:09:56 +02:00
|
|
|
assertEquals(
|
|
|
|
expected = expectedAggregateShare,
|
|
|
|
actual = Hex.encode(result.first),
|
|
|
|
"Unexpected $index:aggregate_share"
|
2024-08-19 21:40:56 +02:00
|
|
|
)
|
2024-08-20 00:09:56 +02:00
|
|
|
assertEquals(
|
2024-08-20 22:29:29 +02:00
|
|
|
expected = expectedAggregatePublicKey,
|
2024-08-20 00:09:56 +02:00
|
|
|
actual = Hex.encode(result.second),
|
|
|
|
"Unexpected $index:aggregate_public_key"
|
2024-08-19 21:40:56 +02:00
|
|
|
)
|
|
|
|
|
2024-08-20 01:05:29 +02:00
|
|
|
assertEquals(
|
|
|
|
expected = 1,
|
|
|
|
actual = Secp256k1.frostShareVerify(
|
|
|
|
threshold,
|
|
|
|
ids33[index],
|
|
|
|
assignedShares[index],
|
|
|
|
vssCommitments[index]
|
|
|
|
),
|
|
|
|
message = "Couldn't verify share from $index signer"
|
|
|
|
)
|
2024-08-19 21:40:56 +02:00
|
|
|
|
2024-08-20 01:05:29 +02:00
|
|
|
assertEquals(
|
|
|
|
expected = expectedPublicShare,
|
|
|
|
actual = Hex.encode(
|
|
|
|
Secp256k1.frostComputePublicShare(
|
|
|
|
threshold,
|
|
|
|
ids33[index],
|
|
|
|
vssCommitments.toTypedArray(),
|
|
|
|
nParticipants
|
|
|
|
)
|
|
|
|
),
|
|
|
|
message = "Couldn't verify share from $index signer"
|
|
|
|
)
|
|
|
|
}
|
2024-08-18 01:27:04 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
fun `frost share verify`() {
|
2024-08-20 01:05:29 +02:00
|
|
|
val shareGenTests = readData("frost/share_gen_vectors.json")
|
|
|
|
val tests = readData("frost/share_agg_vectors.json")
|
|
|
|
|
2024-08-20 22:29:29 +02:00
|
|
|
val expectedAggregatePublicKey = tests.jsonObject["aggregate_public_key"]!!.jsonPrimitive.content
|
2024-08-20 01:05:29 +02:00
|
|
|
val publicKeys = shareGenTests.jsonObject["pubkeys"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
|
|
|
|
|
|
|
val signerShareGenTestCase = shareGenTests.jsonObject["valid_signers_share_gen_test_case"]!!;
|
|
|
|
|
|
|
|
val keyIndices = signerShareGenTestCase.jsonObject["key_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
|
|
|
val nParticipants = keyIndices.size
|
|
|
|
val threshold = signerShareGenTestCase.jsonObject["threshold"]!!.jsonPrimitive.int
|
|
|
|
val ids33 = keyIndices.map { publicKeys[it] }.toTypedArray()
|
|
|
|
|
|
|
|
val vssCommitments = signerShareGenTestCase.jsonObject["signers"]!!.jsonArray.map { signer ->
|
|
|
|
signer.jsonObject["expected"]!!.jsonObject["vss_commitment"]!!.jsonArray.map {
|
|
|
|
Hex.decode(it.jsonPrimitive.content)
|
|
|
|
}.toTypedArray()
|
|
|
|
}
|
|
|
|
|
|
|
|
signerShareGenTestCase.jsonObject["signers"]!!.jsonArray.forEachIndexed { index, _ ->
|
|
|
|
val assignedShares = signerShareGenTestCase.jsonObject["signers"]!!.jsonArray.map {
|
|
|
|
Hex.decode(
|
|
|
|
it.jsonObject["expected"]!!.jsonObject["frost_share"]!!.jsonArray[index].jsonPrimitive.content
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
val result = Secp256k1.frostShareAggregate(
|
|
|
|
assignedShares.toTypedArray(),
|
|
|
|
vssCommitments.toTypedArray(),
|
|
|
|
nParticipants,
|
|
|
|
threshold,
|
|
|
|
ids33[index]
|
|
|
|
)
|
|
|
|
|
|
|
|
val expected = tests.jsonObject["expected"]!!.jsonArray[index];
|
|
|
|
|
|
|
|
val expectedAggregateShare = expected.jsonObject["aggregate_share"]!!.jsonPrimitive.content
|
|
|
|
val expectedPublicShare = expected.jsonObject["public_share"]!!.jsonPrimitive.content
|
2024-08-18 01:27:04 +02:00
|
|
|
|
2024-08-20 01:05:29 +02:00
|
|
|
assertEquals(
|
|
|
|
expected = expectedAggregateShare,
|
|
|
|
actual = Hex.encode(result.first),
|
|
|
|
"Unexpected $index:aggregate_share"
|
|
|
|
)
|
|
|
|
assertEquals(
|
2024-08-20 22:29:29 +02:00
|
|
|
expected = expectedAggregatePublicKey,
|
2024-08-20 01:05:29 +02:00
|
|
|
actual = Hex.encode(result.second),
|
|
|
|
"Unexpected $index:aggregate_public_key"
|
|
|
|
)
|
|
|
|
|
|
|
|
assertEquals(
|
|
|
|
expected = 1,
|
|
|
|
actual = Secp256k1.frostShareVerify(
|
|
|
|
threshold,
|
|
|
|
ids33[index],
|
|
|
|
assignedShares[index],
|
|
|
|
vssCommitments[index]
|
|
|
|
),
|
|
|
|
message = "Couldn't verify share from $index signer"
|
|
|
|
)
|
|
|
|
|
|
|
|
assertEquals(
|
|
|
|
expected = expectedPublicShare,
|
|
|
|
actual = Hex.encode(
|
|
|
|
Secp256k1.frostComputePublicShare(
|
|
|
|
threshold,
|
|
|
|
ids33[index],
|
|
|
|
vssCommitments.toTypedArray(),
|
|
|
|
nParticipants
|
|
|
|
)
|
|
|
|
),
|
|
|
|
message = "Couldn't verify share from $index signer"
|
|
|
|
)
|
|
|
|
}
|
2024-08-18 01:27:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
fun `frost compute pubshare`() {
|
2024-08-20 01:05:29 +02:00
|
|
|
val shareGenTests = readData("frost/share_gen_vectors.json")
|
|
|
|
val tests = readData("frost/share_agg_vectors.json")
|
|
|
|
|
|
|
|
val publicKeys = shareGenTests.jsonObject["pubkeys"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
2024-08-18 01:27:04 +02:00
|
|
|
|
2024-08-20 01:05:29 +02:00
|
|
|
val signerShareGenTestCase = shareGenTests.jsonObject["valid_signers_share_gen_test_case"]!!;
|
|
|
|
|
|
|
|
val keyIndices = signerShareGenTestCase.jsonObject["key_indices"]!!.jsonArray.map { it.jsonPrimitive.int }
|
|
|
|
val nParticipants = keyIndices.size
|
|
|
|
val threshold = signerShareGenTestCase.jsonObject["threshold"]!!.jsonPrimitive.int
|
|
|
|
val ids33 = keyIndices.map { publicKeys[it] }.toTypedArray()
|
|
|
|
|
|
|
|
val vssCommitments = signerShareGenTestCase.jsonObject["signers"]!!.jsonArray.map { signer ->
|
|
|
|
signer.jsonObject["expected"]!!.jsonObject["vss_commitment"]!!.jsonArray.map {
|
|
|
|
Hex.decode(it.jsonPrimitive.content)
|
|
|
|
}.toTypedArray()
|
|
|
|
}
|
|
|
|
|
|
|
|
signerShareGenTestCase.jsonObject["signers"]!!.jsonArray.forEachIndexed { index, _ ->
|
|
|
|
|
|
|
|
val expected = tests.jsonObject["expected"]!!.jsonArray[index];
|
|
|
|
|
|
|
|
val expectedPublicShare = expected.jsonObject["public_share"]!!.jsonPrimitive.content
|
|
|
|
|
|
|
|
assertEquals(
|
|
|
|
expected = expectedPublicShare,
|
|
|
|
actual = Hex.encode(
|
|
|
|
Secp256k1.frostComputePublicShare(
|
|
|
|
threshold,
|
|
|
|
ids33[index],
|
|
|
|
vssCommitments.toTypedArray(),
|
|
|
|
nParticipants
|
|
|
|
)
|
|
|
|
),
|
|
|
|
message = "Couldn't verify share from $index signer"
|
|
|
|
)
|
|
|
|
}
|
2024-08-18 01:27:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Frost Tweak
|
|
|
|
@Test
|
|
|
|
fun `frost pubkey tweak`() {
|
2024-08-20 22:29:29 +02:00
|
|
|
val tests = readData("frost/share_agg_vectors.json")
|
|
|
|
|
|
|
|
val expectedAggregatePublicKey = Hex.decode(
|
|
|
|
tests.jsonObject["aggregate_public_key"]!!.jsonPrimitive.content
|
|
|
|
)
|
|
|
|
|
|
|
|
val expectedTweakCache = tests.jsonObject["tweak_cache"]!!.jsonPrimitive.content
|
2024-08-18 01:27:04 +02:00
|
|
|
|
2024-08-20 22:29:29 +02:00
|
|
|
assertEquals(
|
|
|
|
expected = expectedTweakCache,
|
|
|
|
actual = Hex.encode(
|
|
|
|
Secp256k1.frostPublicKeyTweak(
|
|
|
|
expectedAggregatePublicKey
|
|
|
|
)
|
|
|
|
),
|
|
|
|
message = "Tweak cache incorrect."
|
|
|
|
)
|
2024-08-18 01:27:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
fun `frost pubkey ec tweak add`() {
|
2024-08-20 22:29:29 +02:00
|
|
|
val tests = readData("frost/frost_tweak_vectors.json")
|
|
|
|
|
|
|
|
val tweakCache = Hex.decode(
|
|
|
|
tests.jsonObject["tweak_cache"]!!.jsonPrimitive.content
|
|
|
|
)
|
|
|
|
val ordinaryTweak = "this could be a BIP32 tweak.....".encodeToByteArray()
|
|
|
|
|
|
|
|
assertEquals(
|
|
|
|
expected = tests.jsonObject["ec_tweak_add"]!!.jsonPrimitive.content,
|
|
|
|
actual = Secp256k1.frostPublicKeyEcTweakAdd(
|
|
|
|
tweakCache,
|
|
|
|
ordinaryTweak
|
|
|
|
)?.let {
|
|
|
|
Hex.encode(
|
|
|
|
it
|
|
|
|
)
|
|
|
|
},
|
|
|
|
message = "EC Tweak Add incorrect."
|
|
|
|
)
|
2024-08-18 01:27:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
fun `frost pubkey xonly tweak add`() {
|
2024-08-20 22:29:29 +02:00
|
|
|
val tests = readData("frost/frost_tweak_vectors.json")
|
|
|
|
|
|
|
|
val tweakCache = Hex.decode(
|
|
|
|
tests.jsonObject["tweak_cache"]!!.jsonPrimitive.content
|
|
|
|
)
|
|
|
|
val ordinaryTweak = "this could be a BIP32 tweak.....".encodeToByteArray()
|
|
|
|
|
|
|
|
assertEquals(
|
|
|
|
expected = tests.jsonObject["ec_tweak_add"]!!.jsonPrimitive.content,
|
|
|
|
// TODO: Return public key
|
|
|
|
actual = Secp256k1.frostPublicKeyXonlyTweakAdd(
|
|
|
|
tweakCache,
|
|
|
|
ordinaryTweak
|
|
|
|
)?.let {
|
|
|
|
Hex.encode(
|
|
|
|
it
|
|
|
|
)
|
|
|
|
},
|
|
|
|
message = "EC Tweak Add incorrect."
|
|
|
|
)
|
2024-08-18 01:27:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Frost Sign functionality
|
|
|
|
@Test
|
|
|
|
fun `frost nonce gen`() {
|
2024-08-20 22:29:29 +02:00
|
|
|
val tests = readData("frost/frost_nonce_vectors.json")
|
|
|
|
|
|
|
|
val sessionId = Hex.decode(
|
|
|
|
tests.jsonObject["session_id"]!!.jsonPrimitive.content
|
|
|
|
)
|
|
|
|
val aggregatePublicKey = Hex.decode(
|
|
|
|
tests.jsonObject["aggregate_public_key"]!!.jsonPrimitive.content
|
|
|
|
)
|
|
|
|
|
|
|
|
tests.jsonObject["signers"]!!.jsonArray.forEachIndexed { index, signers ->
|
|
|
|
val aggregateShare = Hex.decode(
|
|
|
|
signers.jsonObject["aggregate_share"]!!.jsonPrimitive.content
|
|
|
|
)
|
|
|
|
|
|
|
|
val (secNonce, pubNonce) = Secp256k1.frostNonceGen(
|
|
|
|
sessionId,
|
|
|
|
aggregateShare,
|
|
|
|
msg32,
|
|
|
|
aggregatePublicKey,
|
|
|
|
null
|
|
|
|
)
|
|
|
|
|
|
|
|
val expectedSecNonce = signers.jsonObject["secnonce"]!!.jsonPrimitive.content
|
|
|
|
val expectedPubNonce = signers.jsonObject["pubnonce"]!!.jsonPrimitive.content
|
|
|
|
|
|
|
|
assertEquals(
|
|
|
|
expected = expectedSecNonce,
|
|
|
|
actual = Hex.encode(secNonce),
|
|
|
|
message = "Invalid $index:secnonce"
|
|
|
|
)
|
|
|
|
|
|
|
|
assertEquals(
|
|
|
|
expected = expectedPubNonce,
|
|
|
|
actual = Hex.encode(pubNonce),
|
|
|
|
message = "Invalid $index:pubnonce"
|
|
|
|
)
|
|
|
|
}
|
2024-08-18 01:27:04 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
fun `frost nonce process`() {
|
2024-08-20 22:29:29 +02:00
|
|
|
val tests = readData("frost/frost_nonce_vectors.json")
|
|
|
|
|
|
|
|
val pubnonces = tests.jsonObject["signers"]!!.jsonArray.map {
|
|
|
|
Hex.decode(it.jsonObject["pubnonce"]!!.jsonPrimitive.content)
|
|
|
|
}
|
|
|
|
|
|
|
|
val aggregatePublicKey = Hex.decode(
|
|
|
|
tests.jsonObject["aggregate_public_key"]!!.jsonPrimitive.content
|
|
|
|
)
|
|
|
|
|
|
|
|
val pubkeys = tests.jsonObject["pubkeys"]!!.jsonArray.map { Hex.decode(it.jsonPrimitive.content) }
|
|
|
|
|
|
|
|
val tweakCache = Hex.decode(
|
|
|
|
tests.jsonObject["tweak_cache"]!!.jsonPrimitive.content
|
|
|
|
)
|
|
|
|
|
|
|
|
tests.jsonObject["signers"]!!.jsonArray.forEachIndexed { signerIndex, signer ->
|
|
|
|
|
|
|
|
val session = Secp256k1.frostNonceProcess(
|
|
|
|
pubnonces.toTypedArray(),
|
|
|
|
msg32,
|
|
|
|
aggregatePublicKey,
|
|
|
|
pubkeys[signerIndex],
|
|
|
|
pubkeys.toTypedArray(),
|
|
|
|
tweakCache,
|
|
|
|
null
|
|
|
|
)
|
|
|
|
|
2024-08-21 17:28:07 +02:00
|
|
|
assertEquals(
|
|
|
|
expected = signer.jsonObject["session"]!!.jsonPrimitive.content,
|
|
|
|
actual = Hex.encode(session),
|
|
|
|
message = "Invalid $signerIndex:session"
|
|
|
|
)
|
2024-08-20 22:29:29 +02:00
|
|
|
}
|
2024-08-18 01:27:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
2024-08-21 17:28:07 +02:00
|
|
|
fun `frost partial sign`() {
|
|
|
|
val tests = readData("frost/frost_nonce_vectors.json")
|
2024-08-18 01:27:04 +02:00
|
|
|
|
2024-08-21 17:28:07 +02:00
|
|
|
|
|
|
|
val tweakCache = Hex.decode(
|
|
|
|
tests.jsonObject["tweak_cache"]!!.jsonPrimitive.content
|
|
|
|
)
|
|
|
|
|
|
|
|
tests.jsonObject["signers"]!!.jsonArray.forEachIndexed { signerIndex, signer ->
|
|
|
|
|
|
|
|
val secNonce = Hex.decode(
|
|
|
|
signer.jsonObject["secnonce"]!!.jsonPrimitive.content
|
|
|
|
)
|
|
|
|
|
|
|
|
val aggregateShare = Hex.decode(
|
|
|
|
signer.jsonObject["aggregate_share"]!!.jsonPrimitive.content
|
|
|
|
)
|
|
|
|
|
|
|
|
val session = Hex.decode(
|
|
|
|
signer.jsonObject["session"]!!.jsonPrimitive.content
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
val partialSignature = Secp256k1.frostPartialSign(
|
|
|
|
secNonce,
|
|
|
|
aggregateShare,
|
|
|
|
session,
|
|
|
|
tweakCache
|
|
|
|
)
|
|
|
|
|
|
|
|
assertEquals(
|
|
|
|
expected = signer.jsonObject["partial_signature"]!!.jsonPrimitive.content,
|
|
|
|
actual = Hex.encode(partialSignature),
|
|
|
|
message = "Invalid $signerIndex:partial signature"
|
|
|
|
)
|
|
|
|
}
|
2024-08-18 01:27:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
fun `frost partial signature verify`() {
|
2024-08-21 17:28:07 +02:00
|
|
|
val tests = readData("frost/frost_nonce_vectors.json")
|
|
|
|
|
|
|
|
val tweakCache = Hex.decode(
|
|
|
|
tests.jsonObject["tweak_cache"]!!.jsonPrimitive.content
|
|
|
|
)
|
|
|
|
|
|
|
|
tests.jsonObject["signers"]!!.jsonArray.forEach { signer ->
|
|
|
|
val partialSignature = Hex.decode(
|
|
|
|
signer.jsonObject["partial_signature"]!!.jsonPrimitive.content
|
|
|
|
)
|
|
|
|
|
|
|
|
val pubNonce = Hex.decode(
|
|
|
|
signer.jsonObject["pubnonce"]!!.jsonPrimitive.content
|
|
|
|
)
|
|
|
|
|
|
|
|
val publicShare = Hex.decode(
|
|
|
|
signer.jsonObject["public_share"]!!.jsonPrimitive.content
|
|
|
|
)
|
|
|
|
|
|
|
|
val session = Hex.decode(
|
|
|
|
signer.jsonObject["session"]!!.jsonPrimitive.content
|
|
|
|
)
|
2024-08-18 01:27:04 +02:00
|
|
|
|
2024-08-21 17:28:07 +02:00
|
|
|
assertEquals(
|
|
|
|
expected = 1,
|
|
|
|
actual = Secp256k1.frostPartialSignatureVerify(
|
|
|
|
partialSignature,
|
|
|
|
pubNonce,
|
|
|
|
publicShare,
|
|
|
|
session,
|
|
|
|
tweakCache
|
|
|
|
),
|
|
|
|
message = "Failed to verify partial signature"
|
|
|
|
)
|
|
|
|
}
|
2024-08-18 01:27:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
fun `frost partial signature aggregation`() {
|
2024-08-21 17:28:07 +02:00
|
|
|
val tests = readData("frost/frost_nonce_vectors.json")
|
|
|
|
|
|
|
|
val partialSignatures = tests.jsonObject["signers"]!!.jsonArray.map {
|
|
|
|
Hex.decode(it.jsonObject["partial_signature"]!!.jsonPrimitive.content)
|
|
|
|
}
|
2024-08-18 01:27:04 +02:00
|
|
|
|
2024-08-21 17:28:07 +02:00
|
|
|
tests.jsonObject["signers"]!!.jsonArray.forEach { signer ->
|
|
|
|
val session = Hex.decode(
|
|
|
|
signer.jsonObject["session"]!!.jsonPrimitive.content
|
|
|
|
)
|
|
|
|
|
|
|
|
val aggregatedSignature = Secp256k1.frostPartialSignatureAggregate(
|
|
|
|
session,
|
|
|
|
partialSignatures.toTypedArray(),
|
|
|
|
)
|
|
|
|
|
|
|
|
assertEquals(
|
|
|
|
expected = tests.jsonObject["aggregate_signature"]!!.jsonPrimitive.content,
|
|
|
|
actual = Hex.encode(aggregatedSignature),
|
|
|
|
message = "Invalid aggregate partial signature"
|
|
|
|
)
|
|
|
|
}
|
2024-08-18 01:27:04 +02:00
|
|
|
}
|
|
|
|
}
|