Skip to content

Commit

Permalink
feat(frost-core): add (de)serialization for `VerifiableSecretSharingS…
Browse files Browse the repository at this point in the history
…haringSharingCommitment` (#878)

* feat(frost-core): add (de)serialization for `VerifiableSecretSharingSharingSharingCommitment`

* Update frost-core/src/keys.rs

Co-authored-by: Conrado Gouvea <conradoplg@gmail.com>

* Update frost-core/src/keys.rs

Co-authored-by: Conrado Gouvea <conradoplg@gmail.com>

* add tests

* fix doc

---------

Co-authored-by: Conrado Gouvea <conradoplg@gmail.com>
  • Loading branch information
StackOverflowExcept1on and conradoplg authored Feb 24, 2025
1 parent fc87f59 commit aed1ea8
Show file tree
Hide file tree
Showing 8 changed files with 293 additions and 1 deletion.
24 changes: 24 additions & 0 deletions frost-core/src/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,11 @@ where
.collect::<Result<_, Error<C>>>()
}

/// Serialize the whole commitment vector as a single byte vector.
pub fn serialize_whole(&self) -> Result<Vec<u8>, Error<C>> {
self.serialize().map(|v| v.concat())
}

/// Returns VerifiableSecretSharingCommitment from an iterator of serialized
/// CoefficientCommitments (e.g. a [`Vec<Vec<u8>>`]).
pub fn deserialize<I, V>(serialized_coefficient_commitments: I) -> Result<Self, Error<C>>
Expand All @@ -342,6 +347,25 @@ where
Ok(Self::new(coefficient_commitments))
}

/// Deserialize a whole commitment vector from a single byte vector as returned by
/// [`VerifiableSecretSharingCommitment::serialize_whole()`].
pub fn deserialize_whole(bytes: &[u8]) -> Result<Self, Error<C>> {
// Get size from the size of the generator
let generator = <C::Group>::generator();
let len = <C::Group>::serialize(&generator)
.expect("serializing the generator always works")
.as_ref()
.len();

let serialized_coefficient_commitments = bytes.chunks_exact(len);

if !serialized_coefficient_commitments.remainder().is_empty() {
return Err(Error::InvalidCoefficient);
}

Self::deserialize(serialized_coefficient_commitments)
}

/// Get the VerifyingKey matching this commitment vector (which is the first
/// element in the vector), or an error if the vector is empty.
pub(crate) fn verifying_key(&self) -> Result<VerifyingKey<C>, Error<C>> {
Expand Down
124 changes: 123 additions & 1 deletion frost-core/src/tests/vss_commitment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use crate::{
keys::{CoefficientCommitment, VerifiableSecretSharingCommitment},
tests::helpers::generate_element,
Group,
Error, Group,
};
use debugless_unwrap::DebuglessUnwrap;
use rand_core::{CryptoRng, RngCore};
Expand Down Expand Up @@ -46,6 +46,40 @@ pub fn check_serialize_vss_commitment<C: Ciphersuite, R: RngCore + CryptoRng>(mu
.all(|(e, c)| e.as_ref() == c));
}

/// Test serialize_whole VerifiableSecretSharingCommitment
pub fn check_serialize_whole_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>::new(input_1),
CoefficientCommitment::new(input_2),
CoefficientCommitment::new(input_3),
];

// ---

let expected = [
<C::Group>::serialize(&input_1).unwrap(),
<C::Group>::serialize(&input_2).unwrap(),
<C::Group>::serialize(&input_3).unwrap(),
]
.into_iter()
.map(|element| element.as_ref().to_vec())
.collect::<Vec<_>>()
.concat();

let vss_commitment = VerifiableSecretSharingCommitment(coeff_comms)
.serialize_whole()
.unwrap();

assert!(expected == vss_commitment);
}

/// Test deserialize VerifiableSecretSharingCommitment
pub fn check_deserialize_vss_commitment<C: Ciphersuite, R: RngCore + CryptoRng>(mut rng: R) {
// Generate test CoefficientCommitments
Expand Down Expand Up @@ -76,6 +110,40 @@ pub fn check_deserialize_vss_commitment<C: Ciphersuite, R: RngCore + CryptoRng>(
assert!(expected == vss_value.unwrap());
}

/// Test deserialize_whole VerifiableSecretSharingCommitment
pub fn check_deserialize_whole_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>::new(input_1),
CoefficientCommitment::new(input_2),
CoefficientCommitment::new(input_3),
];
// ---

let expected = VerifiableSecretSharingCommitment(coeff_comms);

let data = vec![
<C::Group>::serialize(&input_1).unwrap(),
<C::Group>::serialize(&input_2).unwrap(),
<C::Group>::serialize(&input_3).unwrap(),
]
.into_iter()
.map(|element| element.as_ref().to_vec())
.collect::<Vec<_>>()
.concat();

let vss_value = VerifiableSecretSharingCommitment::deserialize_whole(&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,
Expand Down Expand Up @@ -109,6 +177,60 @@ pub fn check_deserialize_vss_commitment_error<C: Ciphersuite, R: RngCore + Crypt
assert!(vss_value.is_err());
}

/// Test deserialize_whole VerifiableSecretSharingCommitment error
pub fn check_deserialize_whole_vss_commitment_error<C: Ciphersuite, R: RngCore + CryptoRng>(
mut rng: R,
commitment_helpers: &Value,
) {
// Generate test CoefficientCommitments

// ---
let values = &commitment_helpers["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).unwrap(),
<C::Group>::serialize(&input_2).unwrap(),
<C::Group>::serialize(&input_3).unwrap(),
serialized,
]
.into_iter()
.map(|element| element.as_ref().to_vec())
.collect::<Vec<_>>()
.concat();

let vss_value = VerifiableSecretSharingCommitment::<C>::deserialize_whole(&data);

assert!(vss_value.is_err());

// Generate test CoefficientCommitments with invalid length

let mut data = vec![
<C::Group>::serialize(&input_1).unwrap(),
<C::Group>::serialize(&input_2).unwrap(),
<C::Group>::serialize(&input_3).unwrap(),
]
.into_iter()
.map(|element| element.as_ref().to_vec())
.collect::<Vec<_>>()
.concat();
data.append(&mut vec![0x00]);

let vss_value = VerifiableSecretSharingCommitment::<C>::deserialize_whole(&data);

assert_eq!(vss_value, Err(Error::InvalidCoefficient));
}

/// Test computing the public key package from a list of commitments.
pub fn check_compute_public_key_package<C: Ciphersuite, R: RngCore + CryptoRng>(mut rng: R) {
let max_signers = 3;
Expand Down
25 changes: 25 additions & 0 deletions frost-ed25519/src/tests/vss_commitment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,28 @@ fn check_serialize_vss_commitment() {
frost_core::tests::vss_commitment::check_serialize_vss_commitment::<Ed25519Sha512, _>(rng);
}

#[test]
fn check_serialize_whole_vss_commitment() {
let rng = rand::rngs::OsRng;
frost_core::tests::vss_commitment::check_serialize_whole_vss_commitment::<Ed25519Sha512, _>(
rng,
);
}

#[test]
fn check_deserialize_vss_commitment() {
let rng = rand::rngs::OsRng;
frost_core::tests::vss_commitment::check_deserialize_vss_commitment::<Ed25519Sha512, _>(rng);
}

#[test]
fn check_deserialize_whole_vss_commitment() {
let rng = rand::rngs::OsRng;
frost_core::tests::vss_commitment::check_deserialize_whole_vss_commitment::<Ed25519Sha512, _>(
rng,
);
}

#[test]
fn check_deserialize_vss_commitment_error() {
let rng = rand::rngs::OsRng;
Expand All @@ -30,6 +46,15 @@ fn check_deserialize_vss_commitment_error() {
);
}

#[test]
fn check_deserialize_whole_vss_commitment_error() {
let rng = rand::rngs::OsRng;
frost_core::tests::vss_commitment::check_deserialize_whole_vss_commitment_error::<
Ed25519Sha512,
_,
>(rng, &ELEMENTS);
}

#[test]
fn check_compute_public_key_package() {
let rng = rand::rngs::OsRng;
Expand Down
25 changes: 25 additions & 0 deletions frost-ed448/src/tests/vss_commitment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,28 @@ fn check_serialize_vss_commitment() {
frost_core::tests::vss_commitment::check_serialize_vss_commitment::<Ed448Shake256, _>(rng);
}

#[test]
fn check_serialize_whole_vss_commitment() {
let rng = rand::rngs::OsRng;
frost_core::tests::vss_commitment::check_serialize_whole_vss_commitment::<Ed448Shake256, _>(
rng,
);
}

#[test]
fn check_deserialize_vss_commitment() {
let rng = rand::rngs::OsRng;
frost_core::tests::vss_commitment::check_deserialize_vss_commitment::<Ed448Shake256, _>(rng);
}

#[test]
fn check_deserialize_whole_vss_commitment() {
let rng = rand::rngs::OsRng;
frost_core::tests::vss_commitment::check_deserialize_whole_vss_commitment::<Ed448Shake256, _>(
rng,
);
}

#[test]
fn check_deserialize_vss_commitment_error() {
let rng = rand::rngs::OsRng;
Expand All @@ -30,6 +46,15 @@ fn check_deserialize_vss_commitment_error() {
);
}

#[test]
fn check_deserialize_whole_vss_commitment_error() {
let rng = rand::rngs::OsRng;
frost_core::tests::vss_commitment::check_deserialize_whole_vss_commitment_error::<
Ed448Shake256,
_,
>(rng, &ELEMENTS);
}

#[test]
fn check_compute_public_key_package() {
let rng = rand::rngs::OsRng;
Expand Down
20 changes: 20 additions & 0 deletions frost-p256/src/tests/vss_commitment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,24 @@ fn check_serialize_vss_commitment() {
frost_core::tests::vss_commitment::check_serialize_vss_commitment::<P256Sha256, _>(rng);
}

#[test]
fn check_serialize_whole_vss_commitment() {
let rng = rand::rngs::OsRng;
frost_core::tests::vss_commitment::check_serialize_whole_vss_commitment::<P256Sha256, _>(rng);
}

#[test]
fn check_deserialize_vss_commitment() {
let rng = rand::rngs::OsRng;
frost_core::tests::vss_commitment::check_deserialize_vss_commitment::<P256Sha256, _>(rng);
}

#[test]
fn check_deserialize_whole_vss_commitment() {
let rng = rand::rngs::OsRng;
frost_core::tests::vss_commitment::check_deserialize_whole_vss_commitment::<P256Sha256, _>(rng);
}

#[test]
fn check_deserialize_vss_commitment_error() {
let rng = rand::rngs::OsRng;
Expand All @@ -30,6 +42,14 @@ fn check_deserialize_vss_commitment_error() {
);
}

#[test]
fn check_deserialize_whole_vss_commitment_error() {
let rng = rand::rngs::OsRng;
frost_core::tests::vss_commitment::check_deserialize_whole_vss_commitment_error::<P256Sha256, _>(
rng, &ELEMENTS,
);
}

#[test]
fn check_compute_public_key_package() {
let rng = rand::rngs::OsRng;
Expand Down
26 changes: 26 additions & 0 deletions frost-ristretto255/src/tests/vss_commitment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ fn check_serialize_vss_commitment() {
frost_core::tests::vss_commitment::check_serialize_vss_commitment::<Ristretto255Sha512, _>(rng);
}

#[test]
fn check_serialize_whole_vss_commitment() {
let rng = rand::rngs::OsRng;
frost_core::tests::vss_commitment::check_serialize_whole_vss_commitment::<Ristretto255Sha512, _>(
rng,
);
}

#[test]
fn check_deserialize_vss_commitment() {
let rng = rand::rngs::OsRng;
Expand All @@ -24,6 +32,15 @@ fn check_deserialize_vss_commitment() {
);
}

#[test]
fn check_deserialize_whole_vss_commitment() {
let rng = rand::rngs::OsRng;
frost_core::tests::vss_commitment::check_deserialize_whole_vss_commitment::<
Ristretto255Sha512,
_,
>(rng);
}

#[test]
fn check_deserialize_vss_commitment_error() {
let rng = rand::rngs::OsRng;
Expand All @@ -33,6 +50,15 @@ fn check_deserialize_vss_commitment_error() {
>(rng, &ELEMENTS);
}

#[test]
fn check_deserialize_whole_vss_commitment_error() {
let rng = rand::rngs::OsRng;
frost_core::tests::vss_commitment::check_deserialize_whole_vss_commitment_error::<
Ristretto255Sha512,
_,
>(rng, &ELEMENTS);
}

#[test]
fn check_compute_public_key_package() {
let rng = rand::rngs::OsRng;
Expand Down
Loading

0 comments on commit aed1ea8

Please sign in to comment.