diff --git a/jni/c/src/fr_acinq_secp256k1_Secp256k1CFunctions.c b/jni/c/src/fr_acinq_secp256k1_Secp256k1CFunctions.c index e989d33..0e03ff4 100644 --- a/jni/c/src/fr_acinq_secp256k1_Secp256k1CFunctions.c +++ b/jni/c/src/fr_acinq_secp256k1_Secp256k1CFunctions.c @@ -1390,7 +1390,7 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256 if (jids33 == NULL) return NULL; - CHECKRESULT(jthreshold <= 0, "threshold can't be 0"); + CHECKRESULT(jthreshold <= 1, "threshold can't be less then 1"); CHECKRESULT(jn_participants <= 0, "n_participants can't be 0"); CHECKRESULT(jthreshold > jn_participants, "threshold can't be greater then n_participants"); @@ -1512,8 +1512,8 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256 if (jid33 == NULL) return NULL; - CHECKRESULT(jthreshold <= 0, "threshold can't be 0"); - CHECKRESULT(jtotalShareCount <= 0, "totalShareCount can't be 0"); + CHECKRESULT(jthreshold <= 1, "threshold can't be less then 1"); + CHECKRESULT(jtotalShareCount <= 1, "totalShareCount can't be 0"); CHECKRESULT(jthreshold > jtotalShareCount, "threshold can't be greater then totalShareCount"); count = (*penv)->GetArrayLength(penv, jshares); @@ -1547,7 +1547,6 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256 for (int j = 0; j < jthreshold; j++) { - // vss_commitment[i] = calloc(1, sizeof(secp256k1_pubkey)); jvss_commitment_bytes = (jbyteArray)(*penv)->GetObjectArrayElement(penv, jvss_commitment, j); size = (*penv)->GetArrayLength(penv, jvss_commitment_bytes); CHECKRESULT1(size != 65, "invalid vss commitment size", free_pubkeys(vss_commitments, count)); @@ -1600,6 +1599,8 @@ JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1fr jbyte *id33; secp256k1_pubkey *vss_commitment; + jbyteArray jvss_commitment_bytes; + jbyteArray jpubkey; jbyte *pub; @@ -1619,7 +1620,7 @@ JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1fr int result = 0; - CHECKRESULT(jthreshold <= 0, "threshold can't be 0"); + CHECKRESULT(jthreshold <= 1, "threshold can't be less then 1"); size = (*penv)->GetArrayLength(penv, jshare); @@ -1632,18 +1633,18 @@ JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1fr id33 = (*penv)->GetByteArrayElements(penv, jid33, 0); count = (*penv)->GetArrayLength(penv, jvss_commitment); - vss_commitment = calloc(count, sizeof(secp256k1_pubkey *)); + CHECKRESULT(count != jthreshold, "vss_commitment needs to be the same as the threshold"); + vss_commitment = calloc(jthreshold, sizeof(secp256k1_pubkey)); for (i = 0; i < count; i++) { -// vss_commitment[i] = calloc(1, sizeof(secp256k1_pubkey)); - jpubkey = (jbyteArray)(*penv)->GetObjectArrayElement(penv, jvss_commitment, i); - size = (*penv)->GetArrayLength(penv, jpubkey); - CHECKRESULT1((size != 33) && (size != 65), "invalid public key size", free(vss_commitment)); - pub = (*penv)->GetByteArrayElements(penv, jpubkey, 0); - result = secp256k1_ec_pubkey_parse(ctx, &vss_commitment[i], (unsigned char *)pub, size); - (*penv)->ReleaseByteArrayElements(penv, jpubkey, pub, 0); - CHECKRESULT1(!result, "secp256k1_ec_pubkey_parse failed", free(vss_commitment)); + jvss_commitment_bytes = (jbyteArray)(*penv)->GetObjectArrayElement(penv, jvss_commitment, i); + size = (*penv)->GetArrayLength(penv, jvss_commitment_bytes); + CHECKRESULT1(size != 65, "invalid vss commitment size", free(vss_commitment)); + pub = (*penv)->GetByteArrayElements(penv, jvss_commitment_bytes, 0); + result = secp256k1_ec_pubkey_parse(ctx, &vss_commitment[i], (unsigned char *)pub, size); + (*penv)->ReleaseByteArrayElements(penv, jvss_commitment_bytes, pub, 0); + CHECKRESULT1(!result, "secp256k1_ec_pubkey_parse failed", free(vss_commitment)); } result = secp256k1_frost_share_verify( @@ -1651,7 +1652,7 @@ JNIEXPORT jint JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256k1_1fr jthreshold, (unsigned char *)id33, &share, - (const secp256k1_pubkey *const *)vss_commitment + &vss_commitment ); return result; @@ -1690,9 +1691,9 @@ JNIEXPORT jbyteArray JNICALL Java_fr_acinq_secp256k1_Secp256k1CFunctions_secp256 int result = 0; - CHECKRESULT(jthreshold <= 0, "threshold can't be 0"); - CHECKRESULT(jtotalSignersCount <= 0, "totalSignersCount can't be 0"); - CHECKRESULT(jthreshold > jtotalSignersCount, "totalSignersCount can't be greater then n_participants"); + CHECKRESULT(jthreshold <= 1, "threshold can't be less then 1"); + CHECKRESULT(jtotalSignersCount <= 1, "totalSignersCount can't be less then 1"); + CHECKRESULT(jthreshold > jtotalSignersCount, "threshold can't be greater then n_participants"); id33 = (*penv)->GetByteArrayElements(penv, jid33, 0); diff --git a/tests/src/commonTest/kotlin/fr/acinq/secp256k1/FrostTest.kt b/tests/src/commonTest/kotlin/fr/acinq/secp256k1/FrostTest.kt index a97b9a2..5d804e2 100644 --- a/tests/src/commonTest/kotlin/fr/acinq/secp256k1/FrostTest.kt +++ b/tests/src/commonTest/kotlin/fr/acinq/secp256k1/FrostTest.kt @@ -120,21 +120,13 @@ class FrostTest: BaseTest() { }.toTypedArray() } - signerShareGenTestCase.jsonObject["signers"]!!.jsonArray.forEachIndexed { index, _signer -> + 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 ) } - println("Indexs: $index") -// println(assignedShares.map { Hex.encode(it) }) -// println(vssCommitments.map { vssCommitment -> -// vssCommitment.map { Hex.encode(it) } -// }) -// println( -// Hex.encode(ids33[index]) -// ) val result = Secp256k1.frostShareAggregate( assignedShares.toTypedArray(), vssCommitments.toTypedArray(), @@ -147,6 +139,7 @@ class FrostTest: BaseTest() { val expectedAggregateShare = expected.jsonObject["aggregate_share"]!!.jsonPrimitive.content val expectedPublicKey = expected.jsonObject["aggregate_public_key"]!!.jsonPrimitive.content + val expectedPublicShare = expected.jsonObject["public_share"]!!.jsonPrimitive.content assertEquals( expected = expectedAggregateShare, @@ -158,34 +151,154 @@ class FrostTest: BaseTest() { 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 = 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" + ) } - - } @Test fun `frost share verify`() { + 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) } + + 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 expectedPublicKey = expected.jsonObject["aggregate_public_key"]!!.jsonPrimitive.content + val expectedPublicShare = expected.jsonObject["public_share"]!!.jsonPrimitive.content + + assertEquals( + expected = expectedAggregateShare, + actual = Hex.encode(result.first), + "Unexpected $index:aggregate_share" + ) + assertEquals( + expected = expectedPublicKey, + 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" + ) + } } @Test fun `frost compute pubshare`() { + 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) } + + 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" + ) + } } // Frost Tweak - @Test fun `frost pubkey tweak`() { diff --git a/tests/src/commonTest/resources/frost/share_agg_vectors.json b/tests/src/commonTest/resources/frost/share_agg_vectors.json index d68330f..1791074 100644 --- a/tests/src/commonTest/resources/frost/share_agg_vectors.json +++ b/tests/src/commonTest/resources/frost/share_agg_vectors.json @@ -2,23 +2,28 @@ "expected": [ { "aggregate_share": "1cfa28492e84e945343f1167401cdce061202a59e47e050c0c2f7f0c56e8e148", - "aggregate_public_key": "bd5561ef6dbff52d3f73b8cb0c065328988b71d3386d23890744a0dd6ad27c15" + "aggregate_public_key": "bd5561ef6dbff52d3f73b8cb0c065328988b71d3386d23890744a0dd6ad27c15", + "public_share": "0493effba7e50d3885bb0c4665149abd4fd13622047412f1da4c0e3754ecb1a9183aaadfdf0f2f82e24641e6ed7a0f7ee22a4a8a47c6d2df66daad37a4880fffe2" }, { "aggregate_share": "dd82fcc1806f1a968228c794a7001c18d209871fb3441bae80fd8229f6a9b0dd", - "aggregate_public_key": "bd5561ef6dbff52d3f73b8cb0c065328988b71d3386d23890744a0dd6ad27c15" + "aggregate_public_key": "bd5561ef6dbff52d3f73b8cb0c065328988b71d3386d23890744a0dd6ad27c15", + "public_share": "048482e27b533879d4f3d68bdb2038bf9480d4ce4cc614d7133238e55179c65a175c684afb7f983e60139542b80f0f12815f3194082f07c93e1f87f3cd1b1c0d8b" }, { "aggregate_share": "5fe629d5f34fdb3ea2f6e545fc3d2cf1f5ce23a504b144e6ebe928793cc85cb4", - "aggregate_public_key": "bd5561ef6dbff52d3f73b8cb0c065328988b71d3386d23890744a0dd6ad27c15" + "aggregate_public_key": "bd5561ef6dbff52d3f73b8cb0c065328988b71d3386d23890744a0dd6ad27c15", + "public_share": "04d71784b58d8958141f8f405d56026f214a736e73f9c1f70776fc2e49f4e90fc0a9396bcc7471a83caf4076a18cb6ab4264aa37174ca19e142259aa5f6bb7fea3" }, { "aggregate_share": "71181e5b46742333f12672d85d0e1472770a082f0a62d3d204c9e191fb45ef91", - "aggregate_public_key": "bd5561ef6dbff52d3f73b8cb0c065328988b71d3386d23890744a0dd6ad27c15" + "aggregate_public_key": "bd5561ef6dbff52d3f73b8cb0c065328988b71d3386d23890744a0dd6ad27c15", + "public_share": "04ec0fb2b4c1ac2d9b761f32cb2972e6d6fb74ed4195d872aeaaf4306bb64eb465580d03102849363ec49c3d1eecdd239337d0a66cdfc4d74c29c824c0f941832a" }, { "aggregate_share": "15bc5e3eeb4ec318a718b3015b78e8496cc5ede81c05727936ade625532dce55", - "aggregate_public_key": "bd5561ef6dbff52d3f73b8cb0c065328988b71d3386d23890744a0dd6ad27c15" + "aggregate_public_key": "bd5561ef6dbff52d3f73b8cb0c065328988b71d3386d23890744a0dd6ad27c15", + "public_share": "04b3ad3909e919f1a27faff7a3aec8f04a9ca54a065d9774ae37c3b9903ad4a19b71f11e148b549ef168465d065279f773175b254f64573e7ce30f4aba0954be19" } ] } \ No newline at end of file