Make CoefficientCommitment and VerifiableSecretSharingCommitment accessible (#361)

* Add funvtion to create new CoefficientCommitment (#323)

* Update function to create new Coefficient Commitment and add error case (#323)

* Add function for CoefficientCommitment to return associated Element (#323)

* Add serialize function for VerifiableSecretSharingCommitment (#323)

* Rename test for the serialize function of vss_commitment (#323)

* Add deserialize function for VerifiableSecretSharingCommitment (#323)

* Rename new to deserialize for CoefficientCommitment (#323)

* Refactor CoefficientCommitment::deserialize (#323)

Update README with developer information

* Improve deserialize function for vss_commitment (#323)

* Handle errors for deserialization function for vss_commitment (#323)

* Update changelog (#323)

Add v0.4.0 to changelog
Remove deprecated changelog file

* Refactor error in deserialize function for CoefficientCommitment

Co-authored-by: Conrado Gouvea <conrado@zfnd.org>

* Update vss commitment serialization and deserialization functions (#323)

* Refactor tests for serialization and deserialization of commitments (#323)

* Refactor test to remove PartialEq trait from Group Serialization (#323)

Co-authored-by: Conrado Gouvea <conrado@zfnd.org>

* Add serialize function for CoefficientCommitment (#323)

* Fix formatting (#323)

* Add back in error test for vss commitment deserialize (#323)

* Clean up commitment tests (#323)

---------

Co-authored-by: Conrado Gouvea <conrado@zfnd.org>
This commit is contained in:
natalie 2023-05-30 14:17:21 +01:00 committed by GitHub
parent 334447df1b
commit 8f216f3c81
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 500 additions and 66 deletions

View File

@ -1,59 +0,0 @@
# Changelog
Entries are listed in reverse chronological order.
## Unreleased
* Fixed a bug where small-order verification keys (including the identity) were
handled inconsistently: the `VerificationKey` parsing logic rejected them, but
the identity `VerificationKey` could be produced from the zero `SigningKey`.
The behaviour is now to consistently accept all small-order verification keys,
matching the RedDSA specification.
* Downstream users who currently rely on the inconsistent behaviour (for e.g.
consensus compatibility, either explicitly wanting to reject small-order
verification keys, or on the belief that this crate implemented the RedDSA
specification) should continue to use previous versions of this crate, until
they can either move the checks into their own code, or migrate their
consensus rules to match the RedDSA specification.
## 0.4.0
* Upgrade `rand` to 0.8, `rand_core` to 0.6, and `rand_chacha` to 0.3, together
(#55)
* Migrate to `jubjub 0.6` (#59)
* Derive `Debug, PartialEq` (#67)
* Restrict the maximum number of FROST participants to 255 by using `u8` (#66)
## 0.3.0
* Initial support for FROST (Flexible Round-Optimized Schnorr Threshold)
signatures.
## 0.2.2
* Make `batch::Item: Clone + Debug` and add `batch::Item::verify_single`
for fallback verification when batch verification fails.
## 0.2.1
* Update `Cargo.toml` metadata.
## 0.2.0
* Change terminology to "signing key" and "verification key" from "secret key"
and "public key".
* Adds a batch verification implementation which can process both binding and
spend authorization signatures in the same batch.
## 0.1.1
* Explicitly document the consensus checks performed by
`impl TryFrom<PublicKeyBytes<T>> for PublicKey<T>`.
* Add a test that small-order public keys are rejected.
* Add `html_root_url` to ensure cross-rendering docs works correctly (thanks
@QuietMisdreavus).
## 0.1.0
* Initial release.

View File

@ -30,13 +30,15 @@ scratch. End-users should not use `frost-core` if they want to sign and verify s
should use the crate specific to their ciphersuite/curve parameters that uses `frost-core` as a
dependency.
## Pre-commit checks
## Developer Information
### Pre-commit checks
1. Run tests `cargo test`
2. Run formatter `cargo fmt`
3. Check linter `cargo clippy` and if you want to automatically fix then run `cargo clippy --fix`
3. Check linter `cargo clippy --all-features --all-targets -- -D warnings` and if you want to automatically fix then run `cargo clippy --fix`
## Coverage
### Coverage
Test coverage checks are performed in the pipeline. This is cofigured here: `.github/workflows/coverage.yaml`
To run these locally:

View File

@ -4,6 +4,13 @@ Entries are listed in reverse chronological order.
## Unreleased
## 0.4.0
* add serialize and deserialize functions for VerifiableSecretSharingCommitment
* add value and deserialize functions for CoefficientCommitment
## Released
## 0.3.0
* add multiscalar support to speed up signing and aggregating

View File

@ -25,6 +25,7 @@ rand_core = "0.6"
thiserror = "1.0"
visibility = "0.0.1"
zeroize = { version = "1.5.4", default-features = false, features = ["derive"] }
itertools = "0.10.5"
# Test dependencies used with the test-impl feature
proptest = { version = "1.0", optional = true }

View File

@ -77,6 +77,9 @@ pub enum Error<C: Ciphersuite> {
/// Error in elliptic curve Group.
#[error("Error in elliptic curve Group.")]
GroupError(#[from] GroupError),
/// Error in coefficient commitment deserialization.
#[error("Invalid coefficient")]
InvalidCoefficient,
}
/// An error related to a scalar Field.

View File

@ -142,7 +142,29 @@ where
/// This is a (public) commitment to one coefficient of a secret polynomial used for performing
/// verifiable secret sharing for a Shamir secret share.
#[derive(Clone, Copy, PartialEq)]
pub(super) struct CoefficientCommitment<C: Ciphersuite>(pub(super) Element<C>);
pub struct CoefficientCommitment<C: Ciphersuite>(pub(crate) Element<C>);
impl<C> CoefficientCommitment<C>
where
C: Ciphersuite,
{
/// returns serialized element
pub fn serialize(&self) -> <C::Group as Group>::Serialization {
<C::Group>::serialize(&self.0)
}
/// Creates a new commitment from a coefficient input
pub fn deserialize(
coefficient: <C::Group as Group>::Serialization,
) -> Result<CoefficientCommitment<C>, Error<C>> {
Ok(Self(<C::Group as Group>::deserialize(&coefficient)?))
}
/// Returns inner element value
pub fn value(&self) -> Element<C> {
self.0
}
}
/// Contains the commitments to the coefficients for our secret polynomial _f_,
/// used to generate participants' key shares.
@ -156,11 +178,36 @@ pub(super) struct CoefficientCommitment<C: Ciphersuite>(pub(super) Element<C>);
/// [`VerifiableSecretSharingCommitment`], either by performing pairwise comparison, or by using
/// some agreed-upon public location for publication, where each participant can
/// ensure that they received the correct (and same) value.
#[derive(Clone)]
#[derive(Clone, PartialEq)]
pub struct VerifiableSecretSharingCommitment<C: Ciphersuite>(
pub(super) Vec<CoefficientCommitment<C>>,
pub(crate) Vec<CoefficientCommitment<C>>,
);
impl<C> VerifiableSecretSharingCommitment<C>
where
C: Ciphersuite,
{
/// Returns serialized coefficent commitments
pub fn serialize(&self) -> Vec<<C::Group as Group>::Serialization> {
self.0
.iter()
.map(|cc| <<C as Ciphersuite>::Group as Group>::serialize(&cc.0))
.collect()
}
/// Returns VerifiableSecretSharingCommitment from a vector of serialized CoefficientCommitments
pub fn deserialize(
serialized_coefficient_commitments: Vec<<C::Group as Group>::Serialization>,
) -> Result<Self, Error<C>> {
let mut coefficient_commitments = Vec::new();
for cc in serialized_coefficient_commitments {
coefficient_commitments.push(CoefficientCommitment::<C>::deserialize(cc)?);
}
Ok(Self(coefficient_commitments))
}
}
/// A secret share generated by performing a (t-out-of-n) secret sharing scheme,
/// generated by a dealer performing [`generate_with_dealer`].
///

View File

@ -1,9 +1,17 @@
//! Ciphersuite-generic test functions.
use std::{collections::HashMap, convert::TryFrom};
use crate::{frost, Error, Signature, VerifyingKey};
use crate::{
frost::{
self,
keys::{CoefficientCommitment, VerifiableSecretSharingCommitment},
},
Error, Field, Group, Signature, VerifyingKey,
};
use debugless_unwrap::DebuglessUnwrap;
use debugless_unwrap::DebuglessUnwrapErr;
use rand_core::{CryptoRng, RngCore};
use serde_json::Value;
use crate::Ciphersuite;
@ -325,3 +333,160 @@ where
// Proceed with the signing test.
check_sign(min_signers, key_packages, rng, pubkeys)
}
fn generate_element<C: Ciphersuite, R: RngCore + CryptoRng>(
rng: &mut R,
) -> <<C as Ciphersuite>::Group as Group>::Element {
let scalar = <<C::Group as Group>::Field>::random(rng);
<C::Group>::generator() * scalar
}
/// Test retrieving Element from CoefficientCommitment
pub fn check_serialization_of_coefficient_commitment<C: Ciphersuite, R: RngCore + CryptoRng>(
mut rng: R,
) {
let element = generate_element::<C, R>(&mut rng);
let expected = <C::Group>::serialize(&element);
let data = frost::keys::CoefficientCommitment::<C>(element).serialize();
assert!(expected.as_ref() == data.as_ref());
}
/// Test create a CoefficientCommitment.
pub fn check_create_coefficient_commitment<C: Ciphersuite, R: RngCore + CryptoRng>(mut rng: R) {
let element = generate_element::<C, R>(&mut rng);
let expected = CoefficientCommitment::<C>(element);
let serialized_element = <C::Group>::serialize(&element);
let coeff_commitment =
frost::keys::CoefficientCommitment::<C>::deserialize(serialized_element).unwrap();
assert!(expected == coeff_commitment);
}
/// Test error handling for creation of a coefficient commitment
pub fn check_create_coefficient_commitment_error<C: Ciphersuite + PartialEq>(
commitment_helper_functions: &Value,
) {
let values = &commitment_helper_functions["elements"];
let serialized: <C::Group as Group>::Serialization =
<C::Group as Group>::Serialization::try_from(
hex::decode(values["invalid_element"].as_str().unwrap()).unwrap(),
)
.debugless_unwrap();
let coeff_commitment = frost::keys::CoefficientCommitment::<C>::deserialize(serialized);
assert!(coeff_commitment.is_err());
}
/// Test retrieve Element from CoefficientCommitment
pub fn check_get_value_of_coefficient_commitment<C: Ciphersuite, R: RngCore + CryptoRng>(
mut rng: R,
) {
let element = generate_element::<C, R>(&mut rng);
let coeff_commitment = frost::keys::CoefficientCommitment::<C>(element);
let value = coeff_commitment.value();
assert!(value == element)
}
/// Test serialize VerifiableSecretSharingCommitment
pub fn check_serialize_vss_commitment<C: Ciphersuite, R: RngCore + CryptoRng>(mut rng: R) {
// Generate test CoefficientCommitments
// ---
let input_1 = generate_element::<C, R>(&mut rng);
let input_2 = generate_element::<C, R>(&mut rng);
let input_3 = generate_element::<C, R>(&mut rng);
let coeff_comms = vec![
CoefficientCommitment::<C>(input_1),
CoefficientCommitment(input_2),
CoefficientCommitment(input_3),
];
// ---
let expected = vec![
<C::Group>::serialize(&input_1),
<C::Group>::serialize(&input_2),
<C::Group>::serialize(&input_3),
];
let vss_commitment = VerifiableSecretSharingCommitment(coeff_comms).serialize();
assert!(expected.len() == vss_commitment.len());
assert!(expected
.iter()
.zip(vss_commitment.iter())
.all(|(e, c)| e.as_ref() == c.as_ref()));
}
/// Test deserialize VerifiableSecretSharingCommitment
pub fn check_deserialize_vss_commitment<C: Ciphersuite, R: RngCore + CryptoRng>(mut rng: R) {
// Generate test CoefficientCommitments
// ---
let input_1 = generate_element::<C, R>(&mut rng);
let input_2 = generate_element::<C, R>(&mut rng);
let input_3 = generate_element::<C, R>(&mut rng);
let coeff_comms = vec![
CoefficientCommitment::<C>(input_1),
CoefficientCommitment(input_2),
CoefficientCommitment(input_3),
];
// ---
let expected = VerifiableSecretSharingCommitment(coeff_comms);
let data = vec![
<C::Group>::serialize(&input_1),
<C::Group>::serialize(&input_2),
<C::Group>::serialize(&input_3),
];
let vss_value = VerifiableSecretSharingCommitment::deserialize(data);
assert!(vss_value.is_ok());
assert!(expected == vss_value.unwrap());
}
/// Test deserialize VerifiableSecretSharingCommitment error
pub fn check_deserialize_vss_commitment_error<C: Ciphersuite, R: RngCore + CryptoRng>(
mut rng: R,
commitment_helper_functions: &Value,
) {
// Generate test CoefficientCommitments
// ---
let values = &commitment_helper_functions["elements"];
let input_1 = generate_element::<C, R>(&mut rng);
let input_2 = generate_element::<C, R>(&mut rng);
let input_3 = generate_element::<C, R>(&mut rng);
let serialized: <C::Group as Group>::Serialization =
<C::Group as Group>::Serialization::try_from(
hex::decode(values["invalid_element"].as_str().unwrap()).unwrap(),
)
.debugless_unwrap();
// ---
let data = vec![
<C::Group>::serialize(&input_1),
<C::Group>::serialize(&input_2),
<C::Group>::serialize(&input_3),
serialized,
];
let vss_value = VerifiableSecretSharingCommitment::<C>::deserialize(data);
assert!(vss_value.is_err());
}

View File

@ -0,0 +1,5 @@
{
"elements": {
"invalid_element": "123456f9e9d319cad973b84cc4c835c8ee73281f5e2638d2d2b352c09edccbfb"
}
}

View File

@ -119,3 +119,51 @@ fn check_rts() {
frost_core::tests::repairable::check_rts::<Ed25519Sha512, _>(rng);
}
/// Tests for serialization and deserialization of CoefficientCommitment and VerifiableSecretSharingCommitment
#[test]
fn check_serialization_of_coefficient_commitment() {
let rng = thread_rng();
frost_core::tests::check_serialization_of_coefficient_commitment::<Ed25519Sha512, _>(rng);
}
#[test]
fn check_create_coefficient_commitment() {
let rng = thread_rng();
frost_core::tests::check_create_coefficient_commitment::<Ed25519Sha512, _>(rng);
}
#[test]
fn check_create_coefficient_commitment_error() {
frost_core::tests::check_create_coefficient_commitment_error::<Ed25519Sha512>(&ELEMENTS);
}
#[test]
fn check_get_value_of_coefficient_commitment() {
let rng = thread_rng();
frost_core::tests::check_get_value_of_coefficient_commitment::<Ed25519Sha512, _>(rng);
}
lazy_static! {
pub static ref ELEMENTS: Value =
serde_json::from_str(include_str!("elements.json").trim()).unwrap();
}
#[test]
fn check_serialize_vss_commitment() {
let rng = thread_rng();
frost_core::tests::check_serialize_vss_commitment::<Ed25519Sha512, _>(rng);
}
#[test]
fn check_deserialize_vss_commitment() {
let rng = thread_rng();
frost_core::tests::check_deserialize_vss_commitment::<Ed25519Sha512, _>(rng);
}
#[test]
fn check_deserialize_vss_commitment_error() {
let rng = thread_rng();
frost_core::tests::check_deserialize_vss_commitment_error::<Ed25519Sha512, _>(rng, &ELEMENTS);
}

View File

@ -0,0 +1,5 @@
{
"elements": {
"invalid_element": "1234562a8ca666c6ee884b6f5a79481cec55a9d7f474918956bf2faedd01ef86be2588aa7526893e67e787db3fd7f2a40ab7c5c76fd9229100"
}
}

View File

@ -108,3 +108,51 @@ fn check_rts() {
frost_core::tests::repairable::check_rts::<Ed448Shake256, _>(rng);
}
/// Tests for serialization and deserialization of CoefficientCommitment and VerifiableSecretSharingCommitment
#[test]
fn check_serialization_of_coefficient_commitment() {
let rng = thread_rng();
frost_core::tests::check_serialization_of_coefficient_commitment::<Ed448Shake256, _>(rng);
}
#[test]
fn check_create_coefficient_commitment() {
let rng = thread_rng();
frost_core::tests::check_create_coefficient_commitment::<Ed448Shake256, _>(rng);
}
#[test]
fn check_create_coefficient_commitment_error() {
frost_core::tests::check_create_coefficient_commitment_error::<Ed448Shake256>(&ELEMENTS);
}
#[test]
fn check_get_value_of_coefficient_commitment() {
let rng = thread_rng();
frost_core::tests::check_get_value_of_coefficient_commitment::<Ed448Shake256, _>(rng);
}
lazy_static! {
pub static ref ELEMENTS: Value =
serde_json::from_str(include_str!("elements.json").trim()).unwrap();
}
#[test]
fn check_serialize_vss_commitment() {
let rng = thread_rng();
frost_core::tests::check_serialize_vss_commitment::<Ed448Shake256, _>(rng);
}
#[test]
fn check_deserialize_vss_commitment() {
let rng = thread_rng();
frost_core::tests::check_deserialize_vss_commitment::<Ed448Shake256, _>(rng);
}
#[test]
fn check_deserialize_vss_commitment_error() {
let rng = thread_rng();
frost_core::tests::check_deserialize_vss_commitment_error::<Ed448Shake256, _>(rng, &ELEMENTS);
}

View File

@ -0,0 +1,5 @@
{
"elements": {
"invalid_element": "1234561ff7e5eee9158f10f7af6deea45e93b0c5a30b01701107b29cf8d4bae5b1"
}
}

View File

@ -96,3 +96,51 @@ fn check_rts() {
frost_core::tests::repairable::check_rts::<P256Sha256, _>(rng);
}
/// Tests for serialization and deserialization of CoefficientCommitment and VerifiableSecretSharingCommitment
#[test]
fn check_serialization_of_coefficient_commitment() {
let rng = thread_rng();
frost_core::tests::check_serialization_of_coefficient_commitment::<P256Sha256, _>(rng);
}
#[test]
fn check_create_coefficient_commitment() {
let rng = thread_rng();
frost_core::tests::check_create_coefficient_commitment::<P256Sha256, _>(rng);
}
#[test]
fn check_create_coefficient_commitment_error() {
frost_core::tests::check_create_coefficient_commitment_error::<P256Sha256>(&ELEMENTS);
}
#[test]
fn check_get_value_of_coefficient_commitment() {
let rng = thread_rng();
frost_core::tests::check_get_value_of_coefficient_commitment::<P256Sha256, _>(rng);
}
lazy_static! {
pub static ref ELEMENTS: Value =
serde_json::from_str(include_str!("elements.json").trim()).unwrap();
}
#[test]
fn check_serialize_vss_commitment() {
let rng = thread_rng();
frost_core::tests::check_serialize_vss_commitment::<P256Sha256, _>(rng);
}
#[test]
fn check_deserialize_vss_commitment() {
let rng = thread_rng();
frost_core::tests::check_deserialize_vss_commitment::<P256Sha256, _>(rng);
}
#[test]
fn check_deserialize_vss_commitment_error() {
let rng = thread_rng();
frost_core::tests::check_deserialize_vss_commitment_error::<P256Sha256, _>(rng, &ELEMENTS);
}

View File

@ -0,0 +1,5 @@
{
"elements": {
"invalid_element": "abcdef7de8baf62d57fe0452581b147b152f776e830c346d1119cee0bc954a59"
}
}

View File

@ -1,4 +1,5 @@
use curve25519_dalek::{ristretto::RistrettoPoint, traits::Identity};
use frost_core::{Ciphersuite, Group, GroupError};
use frost_ristretto255::*;
use lazy_static::lazy_static;
use rand::thread_rng;
@ -72,3 +73,53 @@ fn check_rts() {
frost_core::tests::repairable::check_rts::<Ristretto255Sha512, _>(rng);
}
/// Tests for serialization and deserialization of CoefficientCommitment and VerifiableSecretSharingCommitment
#[test]
fn check_serialization_of_coefficient_commitment() {
let rng = thread_rng();
frost_core::tests::check_serialization_of_coefficient_commitment::<Ristretto255Sha512, _>(rng);
}
#[test]
fn check_create_coefficient_commitment() {
let rng = thread_rng();
frost_core::tests::check_create_coefficient_commitment::<Ristretto255Sha512, _>(rng);
}
#[test]
fn check_create_coefficient_commitment_error() {
frost_core::tests::check_create_coefficient_commitment_error::<Ristretto255Sha512>(&ELEMENTS);
}
#[test]
fn check_get_value_of_coefficient_commitment() {
let rng = thread_rng();
frost_core::tests::check_get_value_of_coefficient_commitment::<Ristretto255Sha512, _>(rng);
}
lazy_static! {
pub static ref ELEMENTS: Value =
serde_json::from_str(include_str!("elements.json").trim()).unwrap();
}
#[test]
fn check_serialize_vss_commitment() {
let rng = thread_rng();
frost_core::tests::check_serialize_vss_commitment::<Ristretto255Sha512, _>(rng);
}
#[test]
fn check_deserialize_vss_commitment() {
let rng = thread_rng();
frost_core::tests::check_deserialize_vss_commitment::<Ristretto255Sha512, _>(rng);
}
#[test]
fn check_deserialize_vss_commitment_error() {
let rng = thread_rng();
frost_core::tests::check_deserialize_vss_commitment_error::<Ristretto255Sha512, _>(
rng, &ELEMENTS,
);
}

View File

@ -0,0 +1,5 @@
{
"elements": {
"invalid_element": "123456afdf4a7f88885ab26b20d18edb7d4d9589812a6cf1a5a1a09d3808dae5d8"
}
}

View File

@ -99,3 +99,51 @@ fn check_rts() {
frost_core::tests::repairable::check_rts::<Secp256K1Sha256, _>(rng);
}
/// Tests for serialization and deserialization of CoefficientCommitment and VerifiableSecretSharingCommitment
#[test]
fn check_serialization_of_coefficient_commitment() {
let rng = thread_rng();
frost_core::tests::check_serialization_of_coefficient_commitment::<Secp256K1Sha256, _>(rng);
}
#[test]
fn check_create_coefficient_commitment() {
let rng = thread_rng();
frost_core::tests::check_create_coefficient_commitment::<Secp256K1Sha256, _>(rng);
}
#[test]
fn check_create_coefficient_commitment_error() {
frost_core::tests::check_create_coefficient_commitment_error::<Secp256K1Sha256>(&ELEMENTS);
}
#[test]
fn check_get_value_of_coefficient_commitment() {
let rng = thread_rng();
frost_core::tests::check_get_value_of_coefficient_commitment::<Secp256K1Sha256, _>(rng);
}
lazy_static! {
pub static ref ELEMENTS: Value =
serde_json::from_str(include_str!("elements.json").trim()).unwrap();
}
#[test]
fn check_serialize_vss_commitment() {
let rng = thread_rng();
frost_core::tests::check_serialize_vss_commitment::<Secp256K1Sha256, _>(rng);
}
#[test]
fn check_deserialize_vss_commitment() {
let rng = thread_rng();
frost_core::tests::check_deserialize_vss_commitment::<Secp256K1Sha256, _>(rng);
}
#[test]
fn check_deserialize_vss_commitment_error() {
let rng = thread_rng();
frost_core::tests::check_deserialize_vss_commitment_error::<Secp256K1Sha256, _>(rng, &ELEMENTS);
}