Skip to content

Commit

Permalink
Merge pull request #5616 from oasisprotocol/peternose/feature/verific…
Browse files Browse the repository at this point in the history
…ation-vector

secret-sharing/src/vss: Implement verification vector
  • Loading branch information
peternose authored Mar 29, 2024
2 parents 17332da + cb8660d commit f1753d4
Show file tree
Hide file tree
Showing 13 changed files with 457 additions and 96 deletions.
1 change: 1 addition & 0 deletions .changelog/5616.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
secret-sharing/src/vss: Implement verification vector
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 13 additions & 18 deletions keymanager/src/churp/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,29 +142,30 @@ impl Churp {
return Err(Error::ApplicationsSubmitted.into());
}

let dealing_phase = status.committee.is_empty();

// For now, support only one group.
match status.group_id {
GroupID::NistP384 => self.do_init::<NistP384>(
req.id,
status.active_handoff,
req.handoff,
status.next_handoff,
status.threshold,
dealing_phase,
),
}
}

fn do_init<D>(
&self,
churp_id: u8,
active_handoff: EpochTime,
handoff: EpochTime,
threshold: u8,
dealing_phase: bool,
) -> Result<SignedApplicationRequest>
where
D: DealerParams + 'static,
{
let dealer =
self.get_or_create_dealer::<D>(churp_id, active_handoff, handoff, threshold)?;
let dealer = self.get_or_create_dealer::<D>(churp_id, handoff, threshold, dealing_phase)?;

// Fetch verification matrix and compute its checksum.
let matrix = dealer.verification_matrix();
Expand Down Expand Up @@ -192,15 +193,15 @@ impl Churp {
fn get_or_create_dealer<D>(
&self,
churp_id: u8,
active_handoff: EpochTime,
handoff: EpochTime,
epoch: EpochTime,
threshold: u8,
dealing_phase: bool,
) -> Result<Arc<Dealer<D>>>
where
D: DealerParams + 'static,
{
// Check the memory first.
let key = (churp_id, handoff);
let key = (churp_id, epoch);
let mut dealers = self.dealers.write().unwrap();

if let Some(dealer) = dealers.get(&key) {
Expand All @@ -219,7 +220,7 @@ impl Churp {
// host has cleared the storage.
let polynomial = self
.storage
.load_bivariate_polynomial::<D::PrimeField>(churp_id, handoff);
.load_bivariate_polynomial::<D::PrimeField>(churp_id, epoch);
let polynomial = match polynomial {
Ok(polynomial) => Ok(polynomial),
Err(err) => match err.downcast_ref::<Error>() {
Expand All @@ -234,7 +235,7 @@ impl Churp {
// tampering, while consensus ensures that the group ID remains
// unchanged and that polynomial dimensions remain consistent
// for any given pair of churp ID and handoff.
Dealer::new(bp)
Dealer::from(bp)
}
None => {
// The local storage is either empty or contains a polynomial
Expand All @@ -244,18 +245,12 @@ impl Churp {
// will detect the polynomial change because the checksum
// of the verification matrix in the submitted application
// will also change.
let dx = threshold.saturating_sub(1);
let dy = 2 * dx;

let dealer = match active_handoff {
0 => Dealer::random(dx, dy, &mut OsRng),
_ => Dealer::zero_hole(dx, dy, &mut OsRng),
};
let dealer = Dealer::new(threshold, dealing_phase, &mut OsRng);

// Encrypt and store the polynomial in case of a restart.
let polynomial = dealer.bivariate_polynomial();
self.storage
.store_bivariate_polynomial(polynomial, churp_id, handoff)?;
.store_bivariate_polynomial(polynomial, churp_id, epoch)?;

dealer
}
Expand Down
1 change: 1 addition & 0 deletions secret-sharing/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ anyhow = "1.0"
group = "0.13.0"
p384 = { version = "0.13.0" }
rand_core = "0.6.4"
thiserror = "1.0"

# Fuzzing.
honggfuzz = "0.5.55"
Expand Down
105 changes: 82 additions & 23 deletions secret-sharing/src/churp/dealer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ use crate::vss::{
polynomial::{BivariatePolynomial, Polynomial},
};

use super::Error;

/// Shareholder identifier.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct Shareholder(pub [u8; 32]);

/// Dealer parameters.
pub trait DealerParams {
/// A prime field used for constructing the bivariate polynomial.
Expand All @@ -21,8 +27,8 @@ pub trait DealerParams {
/// A group used for constructing the verification matrix.
type Group: Group<Scalar = Self::PrimeField> + GroupEncoding;

// Maps input data to an element of the prime field.
fn encode(data: [u8; 32]) -> Option<Self::PrimeField>;
/// Maps given shareholder ID to a non-zero element of the prime field.
fn encode_shareholder(id: Shareholder) -> Result<Self::PrimeField>;
}

/// Dealer is responsible for generating a secret bivariate polynomial,
Expand All @@ -46,23 +52,28 @@ impl<D> Dealer<D>
where
D: DealerParams,
{
/// Creates a new dealer from the given bivariate polynomial.
pub fn new(bp: BivariatePolynomial<D::PrimeField>) -> Self {
let vm = VerificationMatrix::new(&bp);
Self { bp, vm }
/// Creates a new dealer.
pub fn new(threshold: u8, dealing_phase: bool, rng: &mut impl RngCore) -> Self {
let dx = threshold.saturating_sub(1); // Handle threshold 0 as 1.
let dy = 2 * dx;

match dealing_phase {
true => Dealer::random(dx, dy, rng),
false => Dealer::zero_hole(dx, dy, rng),
}
}

/// Creates a new dealer with a random bivariate polynomial.
pub fn random(dx: u8, dy: u8, rng: &mut impl RngCore) -> Self {
fn random(dx: u8, dy: u8, rng: &mut impl RngCore) -> Self {
let bp = BivariatePolynomial::random(dx, dy, rng);
Self::new(bp)
bp.into()
}

/// Creates a new dealer with a random zero-hole bivariate polynomial.
pub fn zero_hole(dx: u8, dy: u8, rng: &mut impl RngCore) -> Self {
fn zero_hole(dx: u8, dy: u8, rng: &mut impl RngCore) -> Self {
let mut bp = BivariatePolynomial::random(dx, dy, rng);
bp.to_zero_hole();
Self::new(bp)
bp.into()
}

/// Returns the secret bivariate polynomial.
Expand All @@ -84,6 +95,17 @@ where
}
}

impl<D> From<BivariatePolynomial<D::PrimeField>> for Dealer<D>
where
D: DealerParams,
{
/// Creates a new dealer from the given bivariate polynomial.
fn from(bp: BivariatePolynomial<D::PrimeField>) -> Self {
let vm = VerificationMatrix::from(&bp);
Self { bp, vm }
}
}

/// Dealer for NIST P-384's elliptic curve group.
pub type NistP384Dealer = Dealer<NistP384>;

Expand All @@ -94,23 +116,54 @@ impl DealerParams for NistP384 {
type PrimeField = p384::Scalar;
type Group = p384::ProjectivePoint;

fn encode(data: [u8; 32]) -> Option<Self::PrimeField> {
fn encode_shareholder(id: Shareholder) -> Result<Self::PrimeField> {
let mut bytes = [0u8; 48];
bytes[16..].copy_from_slice(&data);
p384::Scalar::from_slice(&bytes).ok()
bytes[16..].copy_from_slice(&id.0);

let s = p384::Scalar::from_slice(&bytes).or(Err(Error::ShareholderEncodingFailed))?;
if s.is_zero().into() {
return Err(Error::ZeroValueShareholder.into());
}

Ok(s)
}
}

#[cfg(test)]
mod tests {
use rand::{rngs::StdRng, SeedableRng};

use super::{BivariatePolynomial, DealerParams, NistP384, NistP384Dealer};
use super::{BivariatePolynomial, DealerParams, Error, NistP384, NistP384Dealer, Shareholder};

#[test]
fn test_new() {
let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]);

let threshold = 3;
for dealing_phase in vec![true, false] {
let dealer = NistP384Dealer::new(threshold, dealing_phase, &mut rng);
assert_eq!(dealer.verification_matrix().is_zero_hole(), !dealing_phase);
assert_eq!(dealer.bivariate_polynomial().deg_x, 2);
assert_eq!(dealer.bivariate_polynomial().deg_y, 4);
assert_eq!(dealer.verification_matrix().rows, 3);
assert_eq!(dealer.verification_matrix().cols, 5);
}

let threshold = 0;
for dealing_phase in vec![true, false] {
let dealer = NistP384Dealer::new(threshold, dealing_phase, &mut rng);
assert_eq!(dealer.verification_matrix().is_zero_hole(), !dealing_phase);
assert_eq!(dealer.bivariate_polynomial().deg_x, 0);
assert_eq!(dealer.bivariate_polynomial().deg_y, 0);
assert_eq!(dealer.verification_matrix().rows, 1);
assert_eq!(dealer.verification_matrix().cols, 1);
}
}

#[test]
fn test_from() {
let bp = BivariatePolynomial::zero(2, 3);
let _ = NistP384Dealer::new(bp);
let _ = NistP384Dealer::from(bp);
}

#[test]
Expand All @@ -129,13 +182,19 @@ mod tests {

#[test]
fn test_encode() {
let zero = NistP384::encode([0; 32]);
assert_eq!(zero, Some(p384::Scalar::ZERO));

let mut data = [0; 32];
data[30] = 3;
data[31] = 232;
let thousand = NistP384::encode(data);
assert_eq!(thousand, Some(p384::Scalar::from_u64(1000)));
let id = [0; 32];
let zero = NistP384::encode_shareholder(Shareholder(id));
assert!(zero.is_err());
assert_eq!(
zero.unwrap_err().to_string(),
Error::ZeroValueShareholder.to_string()
);

let mut id = [0; 32];
id[30] = 3;
id[31] = 232;
let thousand = NistP384::encode_shareholder(Shareholder(id));
assert!(thousand.is_ok());
assert_eq!(thousand.unwrap(), p384::Scalar::from_u64(1000));
}
}
7 changes: 7 additions & 0 deletions secret-sharing/src/churp/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("shareholder encoding failed")]
ShareholderEncodingFailed,
#[error("zero value shareholder")]
ZeroValueShareholder,
}
3 changes: 2 additions & 1 deletion secret-sharing/src/churp/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! CHUrn-Robust Proactive secret sharing.
mod dealer;
mod errors;

// Re-exports.
pub use self::dealer::*;
pub use self::{dealer::*, errors::*};
2 changes: 1 addition & 1 deletion secret-sharing/src/vss/fuzz/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ fn fuzz_verification_matrix_from_seed(data: &[u8]) {
}

let bp = random_bivariate_polynomial(data);
let vm = VerificationMatrix::<p384::ProjectivePoint>::new(&bp); // Slow.
let vm = VerificationMatrix::<p384::ProjectivePoint>::from(&bp); // Slow.
let restored = VerificationMatrix::<p384::ProjectivePoint>::from_bytes(vm.to_bytes())
.expect("deserialization should succeed");
assert_eq!(vm, restored)
Expand Down
Loading

0 comments on commit f1753d4

Please sign in to comment.