From 5e09b079e001fc9512596d550f35afee3d9264fa Mon Sep 17 00:00:00 2001 From: Peter Nose Date: Sat, 2 Nov 2024 23:56:03 +0100 Subject: [PATCH 01/21] secret-sharing/src/poly/lagrange: Add debug assertions --- secret-sharing/src/poly/lagrange/naive.rs | 1 + secret-sharing/src/poly/lagrange/optimized.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/secret-sharing/src/poly/lagrange/naive.rs b/secret-sharing/src/poly/lagrange/naive.rs index e1936bd520f..e286962a905 100644 --- a/secret-sharing/src/poly/lagrange/naive.rs +++ b/secret-sharing/src/poly/lagrange/naive.rs @@ -13,6 +13,7 @@ use crate::poly::Polynomial; /// ``` /// where `L_i(x)` represents the i-th Lagrange basis polynomial. pub fn lagrange_naive(xs: &[F], ys: &[F]) -> Polynomial { + debug_assert!(xs.len() == ys.len()); let ls = basis_polynomials_naive(xs); zip(ls, ys).map(|(li, &yi)| li * yi).sum() } diff --git a/secret-sharing/src/poly/lagrange/optimized.rs b/secret-sharing/src/poly/lagrange/optimized.rs index 89f24cf310e..56e72d11adb 100644 --- a/secret-sharing/src/poly/lagrange/optimized.rs +++ b/secret-sharing/src/poly/lagrange/optimized.rs @@ -14,6 +14,7 @@ use super::multiplier::Multiplier; /// ``` /// where `L_i(x)` represents the i-th Lagrange basis polynomial. pub fn lagrange(xs: &[F], ys: &[F]) -> Polynomial { + debug_assert!(xs.len() == ys.len()); let ls = basis_polynomials(xs); zip(ls, ys).map(|(li, &yi)| li * yi).sum() } From 16d589a7a9bdd87e73cb1ef69a2f51f78feea269 Mon Sep 17 00:00:00 2001 From: Peter Nose Date: Sun, 3 Nov 2024 00:01:36 +0100 Subject: [PATCH 02/21] secret-sharing/src/poly/lagrange: Update comments --- secret-sharing/src/poly/lagrange/naive.rs | 38 ++++++++++++++----- secret-sharing/src/poly/lagrange/optimized.rs | 36 ++++++++++++++---- 2 files changed, 57 insertions(+), 17 deletions(-) diff --git a/secret-sharing/src/poly/lagrange/naive.rs b/secret-sharing/src/poly/lagrange/naive.rs index e286962a905..a969dd76439 100644 --- a/secret-sharing/src/poly/lagrange/naive.rs +++ b/secret-sharing/src/poly/lagrange/naive.rs @@ -9,35 +9,47 @@ use crate::poly::Polynomial; /// /// The Lagrange polynomial is defined as: /// ```text -/// L(x) = \sum_{i=0}^n y_i * L_i(x) +/// L(x) = \sum_{i=0}^n y_i * L_i(x) /// ``` /// where `L_i(x)` represents the i-th Lagrange basis polynomial. +/// +/// # Panics +/// +/// Panics if the x-coordinates are not unique. pub fn lagrange_naive(xs: &[F], ys: &[F]) -> Polynomial { debug_assert!(xs.len() == ys.len()); let ls = basis_polynomials_naive(xs); zip(ls, ys).map(|(li, &yi)| li * yi).sum() } -/// Returns Lagrange basis polynomials for the given set of x values. +/// Returns Lagrange basis polynomials for the given set of x-coordinates. /// /// The i-th Lagrange basis polynomial is defined as: /// ```text -/// L_i(x) = \prod_{j=0,j≠i}^n (x - x_j) / (x_i - x_j) +/// L_i(x) = \prod_{j=0,j≠i}^n (x - x_j) / (x_i - x_j) /// ``` /// i.e. it holds `L_i(x_i)` = 1 and `L_i(x_j) = 0` for all `j ≠ i`. +/// +/// # Panics +/// +/// Panics if the x-coordinates are not unique. fn basis_polynomials_naive(xs: &[F]) -> Vec> { (0..xs.len()) .map(|i| basis_polynomial_naive(xs, i)) .collect() } -/// Returns i-th Lagrange basis polynomial for the given set of x values. +/// Returns i-th Lagrange basis polynomial for the given set of x-coordinates. /// /// The i-th Lagrange basis polynomial is defined as: /// ```text -/// L_i(x) = \prod_{j=0,j≠i}^n (x - x_j) / (x_i - x_j) +/// L_i(x) = \prod_{j=0,j≠i}^n (x - x_j) / (x_i - x_j) /// ``` /// i.e. it holds `L_i(x_i)` = 1 and `L_i(x_j) = 0` for all `j ≠ i`. +/// +/// # Panics +/// +/// Panics if the x-coordinates are not unique. fn basis_polynomial_naive(xs: &[F], i: usize) -> Polynomial { let mut nom = Polynomial::with_coefficients(vec![F::ONE]); let mut denom = F::ONE; @@ -54,22 +66,30 @@ fn basis_polynomial_naive(xs: &[F], i: usize) -> Polynomial { nom } -/// Returns Lagrange coefficients for the given set of x values. +/// Returns Lagrange coefficients for the given set of x-coordinates. /// /// The i-th Lagrange coefficient is defined as: /// ```text -/// L_i(0) = \prod_{j=0,j≠i}^n x_j / (x_j - x_i) +/// L_i(0) = \prod_{j=0,j≠i}^n x_j / (x_j - x_i) /// ``` +/// +/// # Panics +/// +/// Panics if the x-coordinates are not unique. pub fn coefficients_naive(xs: &[F]) -> Vec { (0..xs.len()).map(|i| coefficient_naive(xs, i)).collect() } -/// Returns i-th Lagrange coefficient for the given set of x values. +/// Returns i-th Lagrange coefficient for the given set of x-coordinates. /// /// The i-th Lagrange coefficient is defined as: /// ```text -/// L_i(0) = \prod_{j=0,j≠i}^n x_j / (x_j - x_i) +/// L_i(0) = \prod_{j=0,j≠i}^n x_j / (x_j - x_i) /// ``` +/// +/// # Panics +/// +/// Panics if the x-coordinates are not unique. fn coefficient_naive(xs: &[F], i: usize) -> F { let mut nom = F::ONE; let mut denom = F::ONE; diff --git a/secret-sharing/src/poly/lagrange/optimized.rs b/secret-sharing/src/poly/lagrange/optimized.rs index 56e72d11adb..cda8afe3f48 100644 --- a/secret-sharing/src/poly/lagrange/optimized.rs +++ b/secret-sharing/src/poly/lagrange/optimized.rs @@ -13,31 +13,43 @@ use super::multiplier::Multiplier; /// L(x) = \sum_{i=0}^n y_i * L_i(x) /// ``` /// where `L_i(x)` represents the i-th Lagrange basis polynomial. +/// +/// # Panics +/// +/// Panics if the x-coordinates are not unique. pub fn lagrange(xs: &[F], ys: &[F]) -> Polynomial { debug_assert!(xs.len() == ys.len()); let ls = basis_polynomials(xs); zip(ls, ys).map(|(li, &yi)| li * yi).sum() } -/// Returns Lagrange basis polynomials for the given set of x values. +/// Returns Lagrange basis polynomials for the given set of x-coordinates. /// /// The i-th Lagrange basis polynomial is defined as: /// ```text -/// L_i(x) = \prod_{j=0,j≠i}^n (x - x_j) / (x_i - x_j) +/// L_i(x) = \prod_{j=0,j≠i}^n (x - x_j) / (x_i - x_j) /// ``` /// i.e. it holds `L_i(x_i)` = 1 and `L_i(x_j) = 0` for all `j ≠ i`. +/// +/// # Panics +/// +/// Panics if the x-coordinates are not unique. fn basis_polynomials(xs: &[F]) -> Vec> { let m = multiplier_for_basis_polynomials(xs); (0..xs.len()).map(|i| basis_polynomial(xs, i, &m)).collect() } -/// Returns i-th Lagrange basis polynomial for the given set of x values. +/// Returns i-th Lagrange basis polynomial for the given set of x-coordinates. /// /// The i-th Lagrange basis polynomial is defined as: /// ```text -/// L_i(x) = \prod_{j=0,j≠i}^n (x - x_j) / (x_i - x_j) +/// L_i(x) = \prod_{j=0,j≠i}^n (x - x_j) / (x_i - x_j) /// ``` /// i.e. it holds `L_i(x_i)` = 1 and `L_i(x_j) = 0` for all `j ≠ i`. +/// +/// # Panics +/// +/// Panics if the x-coordinates are not unique. fn basis_polynomial( xs: &[F], i: usize, @@ -59,23 +71,31 @@ fn basis_polynomial( nom } -/// Returns Lagrange coefficients for the given set of x values. +/// Returns Lagrange coefficients for the given set of x-coordinates. /// /// The i-th Lagrange coefficient is defined as: /// ```text -/// L_i(0) = \prod_{j=0,j≠i}^n x_j / (x_j - x_i) +/// L_i(0) = \prod_{j=0,j≠i}^n x_j / (x_j - x_i) /// ``` +/// +/// # Panics +/// +/// Panics if the x-coordinates are not unique. pub fn coefficients(xs: &[F]) -> Vec { let m = multiplier_for_coefficients(xs); (0..xs.len()).map(|i| coefficient(xs, i, &m)).collect() } -/// Returns i-th Lagrange coefficient for the given set of x values. +/// Returns i-th Lagrange coefficient for the given set of x-coordinates. /// /// The i-th Lagrange coefficient is defined as: /// ```text -/// L_i(0) = \prod_{j=0,j≠i}^n x_j / (x_j - x_i) +/// L_i(0) = \prod_{j=0,j≠i}^n x_j / (x_j - x_i) /// ``` +/// +/// # Panics +/// +/// Panics if the x-coordinates are not unique. fn coefficient(xs: &[F], i: usize, multiplier: &Multiplier) -> F { let mut nom = multiplier.get_product(i).unwrap_or(F::ONE); let mut denom = F::ONE; From d5a9a7a04e0c5beec5129330f8dc8fa7f705f2d1 Mon Sep 17 00:00:00 2001 From: Peter Nose Date: Mon, 4 Nov 2024 09:21:53 +0100 Subject: [PATCH 03/21] secret-sharing/src/poly/univariate: Fix vec capacity --- secret-sharing/src/poly/univariate.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/secret-sharing/src/poly/univariate.rs b/secret-sharing/src/poly/univariate.rs index 3fda53619e1..7372fa4dd89 100644 --- a/secret-sharing/src/poly/univariate.rs +++ b/secret-sharing/src/poly/univariate.rs @@ -416,7 +416,7 @@ where F: PrimeField, { fn mul_assign(&mut self, rhs: &Polynomial) { - let mut a = Vec::with_capacity(self.a.len() + rhs.a.len() - 2); + let mut a = Vec::with_capacity(self.a.len() + rhs.a.len() - 1); for i in 0..self.a.len() { for j in 0..rhs.a.len() { let aij = self.a[i] * rhs.a[j]; From 6696dd5d3635f24c726fb8f09f5fd0f8713b03c8 Mon Sep 17 00:00:00 2001 From: Peter Nose Date: Mon, 4 Nov 2024 10:20:45 +0100 Subject: [PATCH 04/21] secret-sharing: Remove redundant GroupEncoding constraint --- keymanager/src/churp/handler.rs | 4 ++-- secret-sharing/src/churp/dealer.rs | 8 +++---- secret-sharing/src/churp/handoff.rs | 22 +++++++++---------- secret-sharing/src/churp/shareholder.rs | 14 ++++++------- secret-sharing/src/churp/switch.rs | 28 +++++++------------------ secret-sharing/src/vss/matrix.rs | 20 ++++++++++-------- 6 files changed, 43 insertions(+), 53 deletions(-) diff --git a/keymanager/src/churp/handler.rs b/keymanager/src/churp/handler.rs index 1321947445f..a8e8ef615a9 100644 --- a/keymanager/src/churp/handler.rs +++ b/keymanager/src/churp/handler.rs @@ -105,7 +105,7 @@ const CHURP_CONTEXT_SEPARATOR: &[u8] = b" for churp "; const ALLOWED_BLOCKS_BEHIND: u64 = 5; /// Represents information about a dealer. -struct DealerInfo { +struct DealerInfo { /// The epoch during which this dealer is active. epoch: EpochTime, /// The dealer associated with this information. @@ -113,7 +113,7 @@ struct DealerInfo { } /// Represents information about a handoff. -struct HandoffInfo { +struct HandoffInfo { /// The handoff epoch. epoch: EpochTime, /// The handoff associated with this information. diff --git a/secret-sharing/src/churp/dealer.rs b/secret-sharing/src/churp/dealer.rs index abe188b841a..94107b15fbe 100644 --- a/secret-sharing/src/churp/dealer.rs +++ b/secret-sharing/src/churp/dealer.rs @@ -1,7 +1,7 @@ //! CHURP dealer. use anyhow::Result; -use group::{ff::Field, Group, GroupEncoding}; +use group::{ff::Field, Group}; use rand_core::RngCore; use crate::{poly::BivariatePolynomial, vss::VerificationMatrix}; @@ -15,7 +15,7 @@ use super::{Error, HandoffKind, SecretShare}; /// Shares must always be distributed over a secure channel and verified /// against the matrix. Recovering the secret bivariate polynomial requires /// obtaining more than a threshold number of shares from distinct participants. -pub struct Dealer { +pub struct Dealer { /// Secret bivariate polynomial. bp: BivariatePolynomial, @@ -25,7 +25,7 @@ pub struct Dealer { impl Dealer where - G: Group + GroupEncoding, + G: Group, { /// Creates a new dealer of secret bivariate shares, which can be used /// to recover a randomly selected shared secret. @@ -161,7 +161,7 @@ where impl From> for Dealer where - G: Group + GroupEncoding, + G: Group, { /// Creates a new dealer from the given bivariate polynomial. fn from(bp: BivariatePolynomial) -> Self { diff --git a/secret-sharing/src/churp/handoff.rs b/secret-sharing/src/churp/handoff.rs index c919dfe1298..d2511548df8 100644 --- a/secret-sharing/src/churp/handoff.rs +++ b/secret-sharing/src/churp/handoff.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use anyhow::Result; -use group::{Group, GroupEncoding}; +use group::Group; use crate::vss::VerificationMatrix; @@ -82,7 +82,7 @@ impl HandoffKind { /// shares among committee members, or proactivizes an existing secret by /// randomizing the shares while transferring the secret from an old committee /// to a new, possibly intersecting one. -pub trait Handoff: Send + Sync { +pub trait Handoff: Send + Sync { /// Checks if the handoff needs the verification matrix from the previous /// handoff. fn needs_verification_matrix(&self) -> Result { @@ -157,14 +157,14 @@ pub trait Handoff: Send + Sync { /// A handoff where the committee collaboratively generates a random secret /// and secret shares. -pub struct DealingPhase { +pub struct DealingPhase { /// The share distribution phase of the handoff. share_distribution: DimensionSwitch, } impl DealingPhase where - G: Group + GroupEncoding, + G: Group, { /// Creates a new handoff where the given shareholders will generate /// a random secret and receive corresponding secret shares. @@ -190,7 +190,7 @@ where impl Handoff for DealingPhase where - G: Group + GroupEncoding, + G: Group, { fn needs_bivariate_share(&self, x: &G::Scalar) -> Result { self.share_distribution.needs_bivariate_share(x) @@ -213,14 +213,14 @@ where /// A handoff where the committee remains the same. During this handoff, /// committee members randomize their secret shares without altering /// the shared secret. -pub struct CommitteeUnchanged { +pub struct CommitteeUnchanged { /// The share distribution phase of the handoff. share_distribution: DimensionSwitch, } impl CommitteeUnchanged where - G: Group + GroupEncoding, + G: Group, { /// Creates a new handoff where the secret shares of the given shareholders /// will be randomized. @@ -241,7 +241,7 @@ where impl Handoff for CommitteeUnchanged where - G: Group + GroupEncoding, + G: Group, { fn needs_shareholder(&self) -> Result { Ok(self.share_distribution.is_waiting_for_shareholder()) @@ -271,7 +271,7 @@ where /// A handoff where the committee changes. During this handoff, committee /// members transfer the shared secret to the new committee. -pub struct CommitteeChanged { +pub struct CommitteeChanged { /// The share reduction phase of the handoff. share_reduction: DimensionSwitch, @@ -281,7 +281,7 @@ pub struct CommitteeChanged { impl CommitteeChanged where - G: Group + GroupEncoding, + G: Group, { /// Creates a new handoff where the shared secret will be transferred /// to a new committee composed of the given shareholders. @@ -305,7 +305,7 @@ where impl Handoff for CommitteeChanged where - G: Group + GroupEncoding, + G: Group, { fn needs_verification_matrix(&self) -> Result { Ok(self.share_reduction.is_waiting_for_verification_matrix()) diff --git a/secret-sharing/src/churp/shareholder.rs b/secret-sharing/src/churp/shareholder.rs index 7dd1d685136..fe2eabad45d 100644 --- a/secret-sharing/src/churp/shareholder.rs +++ b/secret-sharing/src/churp/shareholder.rs @@ -3,7 +3,7 @@ use anyhow::Result; use group::{ ff::{Field, PrimeField}, - Group, GroupEncoding, + Group, }; use crate::{ @@ -26,14 +26,14 @@ pub fn encode_shareholder(id: &[u8], dst: &[u8]) -> Result { +pub struct Shareholder { /// Verifiable secret (full or reduced) share of the shared secret. verifiable_share: VerifiableSecretShare, } impl Shareholder where - G: Group + GroupEncoding, + G: Group, { /// Creates a new shareholder. pub fn new(share: SecretShare, vm: VerificationMatrix) -> Self { @@ -78,7 +78,7 @@ where impl From> for Shareholder where - G: Group + GroupEncoding, + G: Group, { fn from(verifiable_share: VerifiableSecretShare) -> Shareholder { Shareholder { verifiable_share } @@ -87,7 +87,7 @@ where impl PointShareholder for Shareholder where - G: Group + GroupEncoding, + G: Group, { fn coordinate_x(&self) -> &G::Scalar { self.verifiable_share.share.coordinate_x() @@ -143,7 +143,7 @@ where } /// Verifiable secret share of the shared secret. -pub struct VerifiableSecretShare { +pub struct VerifiableSecretShare { /// Secret (full or reduced) share of the shared secret. pub(crate) share: SecretShare, @@ -155,7 +155,7 @@ pub struct VerifiableSecretShare { impl VerifiableSecretShare where - G: Group + GroupEncoding, + G: Group, { /// Creates a new verifiable secret share. pub fn new(share: SecretShare, vm: VerificationMatrix) -> Self { diff --git a/secret-sharing/src/churp/switch.rs b/secret-sharing/src/churp/switch.rs index 52691631cf4..446cd865867 100644 --- a/secret-sharing/src/churp/switch.rs +++ b/secret-sharing/src/churp/switch.rs @@ -1,7 +1,7 @@ use std::sync::{Arc, Mutex}; use anyhow::Result; -use group::{Group, GroupEncoding}; +use group::Group; use crate::{ poly::{lagrange::lagrange, Polynomial}, @@ -11,10 +11,7 @@ use crate::{ use super::{Error, SecretShare, Shareholder, VerifiableSecretShare}; /// Dimension switch state. -enum DimensionSwitchState -where - G: Group + GroupEncoding, -{ +enum DimensionSwitchState { /// Represents the state where the dimension switch is waiting for /// the verification matrix from the previous switch, which is needed /// to verify switch points. Once the matrix is received, the state @@ -45,10 +42,7 @@ where } /// A dimension switch based on a share resharing technique. -pub struct DimensionSwitch -where - G: Group + GroupEncoding, -{ +pub struct DimensionSwitch { /// The degree of the secret-sharing polynomial. threshold: u8, @@ -72,7 +66,7 @@ where impl DimensionSwitch where - G: Group + GroupEncoding, + G: Group, { /// Creates a new share reduction dimension switch. /// @@ -282,10 +276,7 @@ where /// An accumulator for switch points. #[derive(Debug)] -pub struct SwitchPoints -where - G: Group + GroupEncoding, -{ +pub struct SwitchPoints { /// The minimum number of distinct points required to reconstruct /// the polynomial. n: usize, @@ -319,7 +310,7 @@ where impl SwitchPoints where - G: Group + GroupEncoding, + G: Group, { /// Creates a new accumulator for switch points. fn new( @@ -425,10 +416,7 @@ where } /// An accumulator for bivariate shares. -struct BivariateShares -where - G: Group + GroupEncoding, -{ +struct BivariateShares { /// The degree of the secret-sharing polynomial. threshold: u8, @@ -460,7 +448,7 @@ where impl BivariateShares where - G: Group + GroupEncoding, + G: Group, { /// Creates a new accumulator for bivariate shares. fn new( diff --git a/secret-sharing/src/vss/matrix.rs b/secret-sharing/src/vss/matrix.rs index 8f153e3c1ff..5d9f315b159 100644 --- a/secret-sharing/src/vss/matrix.rs +++ b/secret-sharing/src/vss/matrix.rs @@ -23,10 +23,7 @@ use super::VerificationVector; /// B(x,y) = \sum_{i=0}^{deg_x} \sum_{j=0}^{deg_y} b_{i,j} x^i y^j /// ``` #[derive(Debug, Clone, PartialEq, Eq)] -pub struct VerificationMatrix -where - G: Group + GroupEncoding, -{ +pub struct VerificationMatrix { /// The number of rows in the verification matrix, determined by /// the degree of the bivariate polynomial in the `x` variable from /// which the matrix was constructed. @@ -42,7 +39,7 @@ where impl VerificationMatrix where - G: Group + GroupEncoding, + G: Group, { /// Returns the dimensions (number of rows and columns) of the verification /// matrix. @@ -202,7 +199,12 @@ where verified.into() } +} +impl VerificationMatrix +where + G: Group + GroupEncoding, +{ /// Returns the byte representation of the verification matrix. pub fn to_bytes(&self) -> Vec { let cap = Self::byte_size(self.rows, self.cols); @@ -275,7 +277,7 @@ where impl From<&BivariatePolynomial> for VerificationMatrix where - G: Group + GroupEncoding, + G: Group, { /// Constructs a new verification matrix from the given bivariate /// polynomial. @@ -297,7 +299,7 @@ where impl From> for VerificationMatrix where - G: Group + GroupEncoding, + G: Group, { /// Constructs a new verification matrix from the given bivariate /// polynomial. @@ -308,7 +310,7 @@ where impl Add for VerificationMatrix where - G: Group + GroupEncoding, + G: Group, { type Output = Self; @@ -319,7 +321,7 @@ where impl Add for &VerificationMatrix where - G: Group + GroupEncoding, + G: Group, { type Output = VerificationMatrix; From 454802deb4cd94aa23a572503aebc7756e862b79 Mon Sep 17 00:00:00 2001 From: Peter Nose Date: Mon, 4 Nov 2024 16:36:24 +0100 Subject: [PATCH 05/21] secret-sharing/src/vss/matrix: Implement AddAssign for matrix --- secret-sharing/src/vss/matrix.rs | 145 +++++++++++++++++++++++++------ 1 file changed, 119 insertions(+), 26 deletions(-) diff --git a/secret-sharing/src/vss/matrix.rs b/secret-sharing/src/vss/matrix.rs index 5d9f315b159..0470fea0711 100644 --- a/secret-sharing/src/vss/matrix.rs +++ b/secret-sharing/src/vss/matrix.rs @@ -1,4 +1,7 @@ -use std::{cmp::max, ops::Add}; +use std::{ + cmp::max, + ops::{Add, AddAssign}, +}; use group::{Group, GroupEncoding}; use subtle::Choice; @@ -312,10 +315,35 @@ impl Add for VerificationMatrix where G: Group, { - type Output = Self; + type Output = VerificationMatrix; + + #[inline] + fn add(self, rhs: Self) -> VerificationMatrix { + &self + &rhs + } +} + +impl Add<&VerificationMatrix> for VerificationMatrix +where + G: Group, +{ + type Output = VerificationMatrix; + + #[inline] + fn add(self, rhs: &VerificationMatrix) -> VerificationMatrix { + &self + rhs + } +} + +impl Add> for &VerificationMatrix +where + G: Group, +{ + type Output = VerificationMatrix; - fn add(self, other: Self) -> Self { - &self + &other + #[inline] + fn add(self, rhs: VerificationMatrix) -> VerificationMatrix { + self + &rhs } } @@ -354,6 +382,34 @@ where } } +impl AddAssign for VerificationMatrix +where + G: Group, +{ + #[inline] + fn add_assign(&mut self, rhs: VerificationMatrix) { + *self += &rhs + } +} + +impl AddAssign<&VerificationMatrix> for VerificationMatrix +where + G: Group, +{ + fn add_assign(&mut self, rhs: &VerificationMatrix) { + if self.rows < rhs.rows || self.cols < rhs.cols { + *self = &*self + rhs; + return; + } + + for i in 0..rhs.rows { + for j in 0..rhs.cols { + self.m[i][j] += rhs.m[i][j]; + } + } + } +} + #[cfg(test)] mod tests { use group::Group as _; @@ -538,30 +594,67 @@ mod tests { } #[test] - fn test_add() { - let c1 = vec![scalars(&[1, 2, 3, 4]), scalars(&[5, 6, 7, 8])]; - let c2 = vec![scalars(&[1, 2]), scalars(&[3, 4]), scalars(&[5, 6])]; - let bp1 = BivariatePolynomial::with_coefficients(c1); - let bp2 = BivariatePolynomial::with_coefficients(c2); - let vm1 = VerificationMatrix::from(&bp1); - let vm2 = VerificationMatrix::from(&bp2); - - let c = vec![ - scalars(&[1 + 1, 2 + 2, 3, 4]), - scalars(&[5 + 3, 6 + 4, 7, 8]), - scalars(&[5, 6, 0, 0]), + pub fn test_add() { + let test_cases = vec![ + // Same size. + ( + vec![scalars(&[0, 1, 2]), scalars(&[3, 4, 5])], + vec![scalars(&[1, 3, 5]), scalars(&[0, 2, 4])], + vec![scalars(&[1, 4, 7]), scalars(&[3, 6, 9])], + ), + // LHS smaller. + ( + vec![scalars(&[0, 1]), scalars(&[3, 4])], + vec![scalars(&[1, 3, 5]), scalars(&[0, 2, 4])], + vec![scalars(&[1, 4, 5]), scalars(&[3, 6, 4])], + ), + // RHS smaller. + ( + vec![scalars(&[0, 1, 2]), scalars(&[3, 4, 5])], + vec![scalars(&[1, 3]), scalars(&[0, 2])], + vec![scalars(&[1, 4, 2]), scalars(&[3, 6, 5])], + ), + // Mixed size. + ( + vec![scalars(&[1, 2, 3, 4]), scalars(&[5, 6, 7, 8])], + vec![scalars(&[1, 2]), scalars(&[3, 4]), scalars(&[5, 6])], + vec![ + scalars(&[2, 4, 3, 4]), + scalars(&[8, 10, 7, 8]), + scalars(&[5, 6, 0, 0]), + ], + ), ]; - let bp = BivariatePolynomial::with_coefficients(c); - let vm = VerificationMatrix::from(&bp); - let sum = &vm1 + &vm2; - assert_eq!(sum.rows, 3); - assert_eq!(sum.cols, 4); - assert_eq!(sum, vm); + for (c1, c2, c3) in test_cases { + let bp1 = BivariatePolynomial::with_coefficients(c1); + let bp2 = BivariatePolynomial::with_coefficients(c2); + let bp3 = BivariatePolynomial::with_coefficients(c3); + let vm1 = VerificationMatrix::from(&bp1); + let vm2 = VerificationMatrix::from(&bp2); + let vm3 = VerificationMatrix::from(&bp3); + + // Test add. + let sum = vm1.clone() + vm2.clone(); + assert_eq!(sum, vm3); - let sum = vm1 + vm2; - assert_eq!(sum.rows, 3); - assert_eq!(sum.cols, 4); - assert_eq!(sum, vm); + let sum = vm1.clone() + &vm2.clone(); + assert_eq!(sum, vm3); + + let sum = &vm1.clone() + vm2.clone(); + assert_eq!(sum, vm3); + + let sum = &vm1.clone() + &vm2.clone(); + assert_eq!(sum, vm3); + + // Test add assign. + let mut sum = vm1.clone(); + sum += vm2.clone(); + assert_eq!(sum, vm3); + + let mut sum = vm1.clone(); + sum += &vm2.clone(); + assert_eq!(sum, vm3); + } } } From ffde5b8b48f89922354d8f3204f6db600c55fe30 Mon Sep 17 00:00:00 2001 From: Peter Nose Date: Mon, 4 Nov 2024 16:37:44 +0100 Subject: [PATCH 06/21] secret-sharing/src/churp/switch: Add bivariate shares using AddAssign --- secret-sharing/src/churp/switch.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/secret-sharing/src/churp/switch.rs b/secret-sharing/src/churp/switch.rs index 446cd865867..2f837b006c9 100644 --- a/secret-sharing/src/churp/switch.rs +++ b/secret-sharing/src/churp/switch.rs @@ -509,17 +509,17 @@ where } verifiable_share.verify(self.threshold, self.zero_hole, self.full_share)?; - let p = match self.p.take() { - Some(p) => p + verifiable_share.share.p, - None => verifiable_share.share.p, - }; - self.p = Some(p); + if let Some(ref mut p) = self.p { + *p += &verifiable_share.share.p; + } else { + self.p = Some(verifiable_share.share.p) + } - let vm = match self.vm.take() { - Some(vm) => vm + verifiable_share.vm, - None => verifiable_share.vm, - }; - self.vm = Some(vm); + if let Some(ref mut vm) = self.vm { + *vm += &verifiable_share.vm; + } else { + self.vm = Some(verifiable_share.vm); + } let index = self .pending_shareholders From a2f76b60e1a7a330610a9d3789df6c57348b015a Mon Sep 17 00:00:00 2001 From: Peter Nose Date: Mon, 4 Nov 2024 21:09:19 +0100 Subject: [PATCH 07/21] secret-sharing/src/churp/switch: Implement AddAssign for shares --- secret-sharing/src/churp/player.rs | 18 ++++----- secret-sharing/src/churp/shareholder.rs | 50 ++++++++++++++++++++++--- secret-sharing/src/churp/switch.rs | 44 +++++++--------------- 3 files changed, 67 insertions(+), 45 deletions(-) diff --git a/secret-sharing/src/churp/player.rs b/secret-sharing/src/churp/player.rs index 8dda962da6b..e8afea4250d 100644 --- a/secret-sharing/src/churp/player.rs +++ b/secret-sharing/src/churp/player.rs @@ -73,7 +73,7 @@ mod tests { use rand_core::OsRng; use crate::{ - churp::{self, HandoffKind, Shareholder}, + churp::{self, HandoffKind, Shareholder, VerifiableSecretShare}, kdc::{KeyRecoverer, KeySharer}, suites::{self, p384, GroupDigest}, }; @@ -161,9 +161,9 @@ mod tests { let xs = (1..=n).map(PrimeField::from_u64).collect(); let shares = dealer.make_shares(xs, kind); let vm = dealer.verification_matrix(); - let shareholders: Vec<_> = shares + let shareholders: Vec> = shares .into_iter() - .map(|share| Shareholder::new(share, vm.clone())) + .map(|share| VerifiableSecretShare::new(share, vm.clone()).into()) .collect(); let key_shares: Vec<_> = shareholders .iter() @@ -180,9 +180,9 @@ mod tests { .collect(); let shares = dealer.make_shares(xs, kind); let vm = dealer.verification_matrix(); - let shareholders: Vec<_> = shares + let shareholders: Vec> = shares .into_iter() - .map(|share| Shareholder::new(share, vm.clone())) + .map(|share| VerifiableSecretShare::new(share, vm.clone()).into()) .collect(); let key_shares: Vec<_> = shareholders .iter() @@ -197,9 +197,9 @@ mod tests { let xs = (1..=n).map(PrimeField::from_u64).collect(); let shares = dealer.make_shares(xs, kind); let vm = dealer.verification_matrix(); - let shareholders: Vec<_> = shares + let shareholders: Vec> = shares .into_iter() - .map(|share| Shareholder::new(share, vm.clone())) + .map(|share| VerifiableSecretShare::new(share, vm.clone()).into()) .collect(); let key_shares: Vec<_> = shareholders .iter() @@ -213,9 +213,9 @@ mod tests { let xs = (1..=n).map(PrimeField::from_u64).collect(); let shares = dealer.make_shares(xs, kind); let vm = dealer.verification_matrix(); - let shareholders: Vec<_> = shares + let shareholders: Vec> = shares .into_iter() - .map(|share| Shareholder::new(share, vm.clone())) + .map(|share| VerifiableSecretShare::new(share, vm.clone()).into()) .collect(); let key_shares: Vec<_> = shareholders .iter() diff --git a/secret-sharing/src/churp/shareholder.rs b/secret-sharing/src/churp/shareholder.rs index fe2eabad45d..df8a345069b 100644 --- a/secret-sharing/src/churp/shareholder.rs +++ b/secret-sharing/src/churp/shareholder.rs @@ -1,5 +1,7 @@ //! CHURP shareholder. +use std::ops::AddAssign; + use anyhow::Result; use group::{ ff::{Field, PrimeField}, @@ -35,11 +37,6 @@ impl Shareholder where G: Group, { - /// Creates a new shareholder. - pub fn new(share: SecretShare, vm: VerificationMatrix) -> Self { - VerifiableSecretShare::new(share, vm).into() - } - /// Returns the verifiable secret share. pub fn verifiable_share(&self) -> &VerifiableSecretShare { &self.verifiable_share @@ -70,7 +67,8 @@ where let p = p + &self.verifiable_share.share.p; let vm = vm + &self.verifiable_share.vm; let share = SecretShare::new(x, p); - let shareholder = Shareholder::new(share, vm); + let verifiable_share = VerifiableSecretShare::new(share, vm); + let shareholder = verifiable_share.into(); Ok(shareholder) } @@ -142,6 +140,26 @@ where } } +impl AddAssign for SecretShare +where + F: PrimeField, +{ + #[inline] + fn add_assign(&mut self, rhs: SecretShare) { + *self += &rhs + } +} + +impl AddAssign<&SecretShare> for SecretShare +where + F: PrimeField, +{ + fn add_assign(&mut self, rhs: &SecretShare) { + debug_assert!(self.x == rhs.x); + self.p += &rhs.p; + } +} + /// Verifiable secret share of the shared secret. pub struct VerifiableSecretShare { /// Secret (full or reduced) share of the shared secret. @@ -248,3 +266,23 @@ where (rows, cols) } } + +impl AddAssign for VerifiableSecretShare +where + G: Group, +{ + #[inline] + fn add_assign(&mut self, rhs: VerifiableSecretShare) { + *self += &rhs + } +} + +impl AddAssign<&VerifiableSecretShare> for VerifiableSecretShare +where + G: Group, +{ + fn add_assign(&mut self, rhs: &VerifiableSecretShare) { + self.share += &rhs.share; + self.vm += &rhs.vm; + } +} diff --git a/secret-sharing/src/churp/switch.rs b/secret-sharing/src/churp/switch.rs index 2f837b006c9..0a9ef80c388 100644 --- a/secret-sharing/src/churp/switch.rs +++ b/secret-sharing/src/churp/switch.rs @@ -4,7 +4,7 @@ use anyhow::Result; use group::Group; use crate::{ - poly::{lagrange::lagrange, Polynomial}, + poly::lagrange::lagrange, vss::{VerificationMatrix, VerificationVector}, }; @@ -408,8 +408,9 @@ where let x = self.me.take().ok_or(Error::ShareholderIdentityRequired)?; let vm = self.vm.take().ok_or(Error::VerificationMatrixRequired)?; - let share = SecretShare::new(x, p); - let shareholder = Shareholder::new(share, vm); + let share: SecretShare<::Scalar> = SecretShare::new(x, p); + let verifiable_share = VerifiableSecretShare::new(share, vm); + let shareholder = verifiable_share.into(); Ok(shareholder) } @@ -439,11 +440,8 @@ struct BivariateShares { /// The shareholder to be proactivized with bivariate shares. shareholder: Option>>, - /// The sum of the received bivariate shares. - p: Option>, - - /// The sum of the verification matrices of the received bivariate shares. - vm: Option>, + /// The sum of the received verifiable bivariate shares. + verifiable_share: Option>, } impl BivariateShares @@ -473,8 +471,7 @@ where shareholders, pending_shareholders, shareholder, - p: None, - vm: None, + verifiable_share: None, }) } @@ -509,16 +506,10 @@ where } verifiable_share.verify(self.threshold, self.zero_hole, self.full_share)?; - if let Some(ref mut p) = self.p { - *p += &verifiable_share.share.p; - } else { - self.p = Some(verifiable_share.share.p) - } - - if let Some(ref mut vm) = self.vm { - *vm += &verifiable_share.vm; + if let Some(ref mut vs) = self.verifiable_share { + *vs += &verifiable_share; } else { - self.vm = Some(verifiable_share.vm); + self.verifiable_share = Some(verifiable_share); } let index = self @@ -540,21 +531,14 @@ where return Err(Error::NotEnoughBivariateShares.into()); } - let p = self - .p - .take() - .ok_or(Error::ShareholderProactivizationCompleted)?; - let vm = self - .vm + let vs = self + .verifiable_share .take() .ok_or(Error::ShareholderProactivizationCompleted)?; let shareholder = match &self.shareholder { - Some(shareholder) => shareholder.proactivize(&p, &vm)?, - None => { - let share = SecretShare::new(self.me, p); - Shareholder::new(share, vm) - } + Some(shareholder) => shareholder.proactivize(&vs.share.p, &vs.vm)?, + None => vs.into(), }; // Ensure that the combined bivariate polynomial satisfies From 8b5ad487c18b8e30c7335f16a9ed2679c552c090 Mon Sep 17 00:00:00 2001 From: Peter Nose Date: Fri, 30 Aug 2024 15:14:04 +0200 Subject: [PATCH 08/21] secret-sharing/src/poly: Support zeroize --- Cargo.lock | 1 + secret-sharing/Cargo.toml | 1 + secret-sharing/src/poly/bivariate.rs | 14 ++++++++++++++ secret-sharing/src/poly/point.rs | 22 ++++++++++++++++++++++ secret-sharing/src/poly/univariate.rs | 14 +++++++++++++- secret-sharing/src/shamir/dealer.rs | 2 +- 6 files changed, 52 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 74781d81350..32f6852fbb8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2630,6 +2630,7 @@ dependencies = [ "sha3", "subtle", "thiserror", + "zeroize", ] [[package]] diff --git a/secret-sharing/Cargo.toml b/secret-sharing/Cargo.toml index a8fab6f8e69..ae6e24183c6 100644 --- a/secret-sharing/Cargo.toml +++ b/secret-sharing/Cargo.toml @@ -17,6 +17,7 @@ rand_core = { version = "0.6" } sha3 = { version = "0.10" } subtle = { version = "2.6", default-features = false } thiserror = { version = "1.0" } +zeroize = { version = "1.7" } [[bin]] name = "fuzz-vss" diff --git a/secret-sharing/src/poly/bivariate.rs b/secret-sharing/src/poly/bivariate.rs index aca8ad5b463..1983cd6730f 100644 --- a/secret-sharing/src/poly/bivariate.rs +++ b/secret-sharing/src/poly/bivariate.rs @@ -1,6 +1,7 @@ use group::ff::PrimeField; use rand_core::RngCore; use subtle::{Choice, CtOption}; +use zeroize::Zeroize; use crate::poly::powers; @@ -244,6 +245,19 @@ where } } +impl Zeroize for BivariatePolynomial +where + F: PrimeField + Zeroize, +{ + fn zeroize(&mut self) { + for bi in self.b.iter_mut() { + for bij in bi.iter_mut() { + bij.zeroize(); + } + } + } +} + #[cfg(test)] mod tests { use std::panic; diff --git a/secret-sharing/src/poly/point.rs b/secret-sharing/src/poly/point.rs index d7d97b3b2c1..8e022f74e60 100644 --- a/secret-sharing/src/poly/point.rs +++ b/secret-sharing/src/poly/point.rs @@ -1,4 +1,5 @@ use group::{ff::PrimeField, Group}; +use zeroize::Zeroize; /// A point (x,y) on a univariate polynomial f(x), where y = f(x). #[derive(Clone)] @@ -19,6 +20,16 @@ where } } +impl Zeroize for Point +where + F: PrimeField + Zeroize, +{ + fn zeroize(&mut self) { + self.x.zeroize(); + self.y.zeroize(); + } +} + /// A point (x,y) on a univariate polynomial f(x), where y = f(x), /// with an encrypted y-coordinate. /// @@ -48,3 +59,14 @@ impl EncryptedPoint { &self.z } } + +impl Zeroize for EncryptedPoint +where + G: Group + Zeroize, + G::Scalar: Zeroize, +{ + fn zeroize(&mut self) { + self.x.zeroize(); + self.z.zeroize(); + } +} diff --git a/secret-sharing/src/poly/univariate.rs b/secret-sharing/src/poly/univariate.rs index 7372fa4dd89..307caa7499e 100644 --- a/secret-sharing/src/poly/univariate.rs +++ b/secret-sharing/src/poly/univariate.rs @@ -7,6 +7,7 @@ use std::{ use group::ff::PrimeField; use rand_core::RngCore; use subtle::{Choice, CtOption}; +use zeroize::Zeroize; use crate::poly::powers; @@ -23,7 +24,7 @@ use crate::poly::powers; /// degree are consistently represented by vectors of the same size, resulting /// in encodings of equal length. #[derive(Clone, PartialEq, Eq)] -pub struct Polynomial { +pub struct Polynomial { pub(crate) a: Vec, } @@ -529,6 +530,17 @@ where } } +impl Zeroize for Polynomial +where + F: PrimeField + Zeroize, +{ + fn zeroize(&mut self) { + for ai in self.a.iter_mut() { + ai.zeroize(); + } + } +} + #[cfg(test)] mod tests { use rand::{rngs::StdRng, SeedableRng}; diff --git a/secret-sharing/src/shamir/dealer.rs b/secret-sharing/src/shamir/dealer.rs index 955deb5e84f..59dda248296 100644 --- a/secret-sharing/src/shamir/dealer.rs +++ b/secret-sharing/src/shamir/dealer.rs @@ -5,7 +5,7 @@ use crate::poly::{Point, Polynomial}; /// A holder of the secret-sharing polynomial responsible for generating /// secret shares. -pub struct Dealer { +pub struct Dealer { /// The secret-sharing polynomial where the coefficient of the constant /// term represents the shared secret. poly: Polynomial, From d74f0f5dd9044d2aa8967f0cc3c44cf43b476eb6 Mon Sep 17 00:00:00 2001 From: Peter Nose Date: Mon, 4 Nov 2024 21:45:26 +0100 Subject: [PATCH 09/21] secret-sharing/src/churp/shareholder: Zeroize share on drop --- keymanager/src/churp/storage.rs | 49 +++++++++++++++++++------ keymanager/src/churp/types.rs | 6 ++- secret-sharing/src/churp/dealer.rs | 2 + secret-sharing/src/churp/handoff.rs | 31 ++++++++++++++-- secret-sharing/src/churp/player.rs | 5 ++- secret-sharing/src/churp/shareholder.rs | 39 +++++++++++++++++--- secret-sharing/src/churp/switch.rs | 22 +++++++++-- secret-sharing/src/suites/mod.rs | 4 +- 8 files changed, 128 insertions(+), 30 deletions(-) diff --git a/keymanager/src/churp/storage.rs b/keymanager/src/churp/storage.rs index 99fe49e10b7..99924111fed 100644 --- a/keymanager/src/churp/storage.rs +++ b/keymanager/src/churp/storage.rs @@ -14,6 +14,7 @@ use oasis_core_runtime::{ consensus::beacon::EpochTime, storage::KeyValue, }; +use zeroize::Zeroize; use super::{EncodedVerifiableSecretShare, Error}; @@ -74,11 +75,15 @@ impl Storage { /// Loads and decrypts a secret share, consisting of a polynomial /// and its associated verification matrix. - pub fn load_secret_share( + pub fn load_secret_share( &self, churp_id: u8, epoch: EpochTime, - ) -> Result>> { + ) -> Result>> + where + G: Group + GroupEncoding, + G::Scalar: Zeroize, + { let key = Self::create_secret_share_storage_key(churp_id); let mut ciphertext = self.storage.get(key)?; if ciphertext.is_empty() { @@ -91,12 +96,16 @@ impl Storage { /// Encrypts and stores the provided secret share, consisting of /// a polynomial and its associated verification matrix. - pub fn store_secret_share( + pub fn store_secret_share( &self, share: &VerifiableSecretShare, churp_id: u8, epoch: EpochTime, - ) -> Result<()> { + ) -> Result<()> + where + G: Group + GroupEncoding, + G::Scalar: Zeroize, + { let key = Self::create_secret_share_storage_key(churp_id); let ciphertext = Self::encrypt_secret_share(share, churp_id, epoch); self.storage.insert(key, ciphertext)?; @@ -106,11 +115,15 @@ impl Storage { /// Loads and decrypts the next secret share, consisting of a polynomial /// and its associated verification matrix. - pub fn load_next_secret_share( + pub fn load_next_secret_share( &self, churp_id: u8, epoch: EpochTime, - ) -> Result>> { + ) -> Result>> + where + G: Group + GroupEncoding, + G::Scalar: Zeroize, + { let key = Self::create_next_secret_share_storage_key(churp_id); let mut ciphertext = self.storage.get(key)?; if ciphertext.is_empty() { @@ -123,12 +136,16 @@ impl Storage { /// Encrypts and stores the provided next secret share, consisting of /// a polynomial and its associated verification matrix. - pub fn store_next_secret_share( + pub fn store_next_secret_share( &self, share: &VerifiableSecretShare, churp_id: u8, epoch: EpochTime, - ) -> Result<()> { + ) -> Result<()> + where + G: Group + GroupEncoding, + G::Scalar: Zeroize, + { let key = Self::create_next_secret_share_storage_key(churp_id); let ciphertext = Self::encrypt_secret_share(share, churp_id, epoch); self.storage.insert(key, ciphertext)?; @@ -172,11 +189,15 @@ impl Storage { /// Encrypts and authenticates the given polynomial and verification matrix /// using the provided ID and handoff as additional data. - fn encrypt_secret_share( + fn encrypt_secret_share( verifiable_share: &VerifiableSecretShare, churp_id: u8, epoch: EpochTime, - ) -> Vec { + ) -> Vec + where + G: Group + GroupEncoding, + G::Scalar: Zeroize, + { let share: EncodedVerifiableSecretShare = verifiable_share.into(); let nonce: Nonce = Nonce::generate(); let plaintext = cbor::to_vec(share); @@ -189,11 +210,15 @@ impl Storage { /// Decrypts and authenticates encrypted polynomial and verification matrix /// using the provided ID and handoff as additional data. - fn decrypt_secret_share( + fn decrypt_secret_share( ciphertext: &mut Vec, churp_id: u8, epoch: EpochTime, - ) -> Result> { + ) -> Result> + where + G: Group + GroupEncoding, + G::Scalar: Zeroize, + { let (ciphertext, nonce) = Self::unpack_ciphertext_with_nonce(ciphertext)?; let additional_data = Self::pack_churp_id_epoch(churp_id, epoch); let d2 = new_deoxysii(Keypolicy::MRENCLAVE, SECRET_SHARE_SEAL_CONTEXT); diff --git a/keymanager/src/churp/types.rs b/keymanager/src/churp/types.rs index 02727e0f0a4..e083d4b1db9 100644 --- a/keymanager/src/churp/types.rs +++ b/keymanager/src/churp/types.rs @@ -170,6 +170,7 @@ pub struct EncodedVerifiableSecretShare { impl From<&VerifiableSecretShare> for EncodedVerifiableSecretShare where G: Group + GroupEncoding, + G::Scalar: Zeroize, { fn from(verifiable_share: &VerifiableSecretShare) -> Self { Self { @@ -182,6 +183,7 @@ where impl TryFrom for VerifiableSecretShare where G: Group + GroupEncoding, + G::Scalar: Zeroize, { type Error = Error; @@ -206,7 +208,7 @@ pub struct EncodedSecretShare { impl From<&SecretShare> for EncodedSecretShare where - F: PrimeField, + F: PrimeField + Zeroize, { fn from(share: &SecretShare) -> Self { Self { @@ -218,7 +220,7 @@ where impl TryFrom for SecretShare where - F: PrimeField, + F: PrimeField + Zeroize, { type Error = Error; diff --git a/secret-sharing/src/churp/dealer.rs b/secret-sharing/src/churp/dealer.rs index 94107b15fbe..ffb0281d60c 100644 --- a/secret-sharing/src/churp/dealer.rs +++ b/secret-sharing/src/churp/dealer.rs @@ -3,6 +3,7 @@ use anyhow::Result; use group::{ff::Field, Group}; use rand_core::RngCore; +use zeroize::Zeroize; use crate::{poly::BivariatePolynomial, vss::VerificationMatrix}; @@ -26,6 +27,7 @@ pub struct Dealer { impl Dealer where G: Group, + G::Scalar: Zeroize, { /// Creates a new dealer of secret bivariate shares, which can be used /// to recover a randomly selected shared secret. diff --git a/secret-sharing/src/churp/handoff.rs b/secret-sharing/src/churp/handoff.rs index d2511548df8..13a6fe69dcc 100644 --- a/secret-sharing/src/churp/handoff.rs +++ b/secret-sharing/src/churp/handoff.rs @@ -2,6 +2,7 @@ use std::sync::Arc; use anyhow::Result; use group::Group; +use zeroize::Zeroize; use crate::vss::VerificationMatrix; @@ -82,7 +83,11 @@ impl HandoffKind { /// shares among committee members, or proactivizes an existing secret by /// randomizing the shares while transferring the secret from an old committee /// to a new, possibly intersecting one. -pub trait Handoff: Send + Sync { +pub trait Handoff: Send + Sync +where + G: Group, + G::Scalar: Zeroize, +{ /// Checks if the handoff needs the verification matrix from the previous /// handoff. fn needs_verification_matrix(&self) -> Result { @@ -157,7 +162,11 @@ pub trait Handoff: Send + Sync { /// A handoff where the committee collaboratively generates a random secret /// and secret shares. -pub struct DealingPhase { +pub struct DealingPhase +where + G: Group, + G::Scalar: Zeroize, +{ /// The share distribution phase of the handoff. share_distribution: DimensionSwitch, } @@ -165,6 +174,7 @@ pub struct DealingPhase { impl DealingPhase where G: Group, + G::Scalar: Zeroize, { /// Creates a new handoff where the given shareholders will generate /// a random secret and receive corresponding secret shares. @@ -191,6 +201,7 @@ where impl Handoff for DealingPhase where G: Group, + G::Scalar: Zeroize, { fn needs_bivariate_share(&self, x: &G::Scalar) -> Result { self.share_distribution.needs_bivariate_share(x) @@ -213,7 +224,11 @@ where /// A handoff where the committee remains the same. During this handoff, /// committee members randomize their secret shares without altering /// the shared secret. -pub struct CommitteeUnchanged { +pub struct CommitteeUnchanged +where + G: Group, + G::Scalar: Zeroize, +{ /// The share distribution phase of the handoff. share_distribution: DimensionSwitch, } @@ -221,6 +236,7 @@ pub struct CommitteeUnchanged { impl CommitteeUnchanged where G: Group, + G::Scalar: Zeroize, { /// Creates a new handoff where the secret shares of the given shareholders /// will be randomized. @@ -242,6 +258,7 @@ where impl Handoff for CommitteeUnchanged where G: Group, + G::Scalar: Zeroize, { fn needs_shareholder(&self) -> Result { Ok(self.share_distribution.is_waiting_for_shareholder()) @@ -271,7 +288,11 @@ where /// A handoff where the committee changes. During this handoff, committee /// members transfer the shared secret to the new committee. -pub struct CommitteeChanged { +pub struct CommitteeChanged +where + G: Group, + G::Scalar: Zeroize, +{ /// The share reduction phase of the handoff. share_reduction: DimensionSwitch, @@ -282,6 +303,7 @@ pub struct CommitteeChanged { impl CommitteeChanged where G: Group, + G::Scalar: Zeroize, { /// Creates a new handoff where the shared secret will be transferred /// to a new committee composed of the given shareholders. @@ -306,6 +328,7 @@ where impl Handoff for CommitteeChanged where G: Group, + G::Scalar: Zeroize, { fn needs_verification_matrix(&self) -> Result { Ok(self.share_reduction.is_waiting_for_verification_matrix()) diff --git a/secret-sharing/src/churp/player.rs b/secret-sharing/src/churp/player.rs index e8afea4250d..6a45a1462aa 100644 --- a/secret-sharing/src/churp/player.rs +++ b/secret-sharing/src/churp/player.rs @@ -2,6 +2,7 @@ use std::iter::zip; use anyhow::{bail, Result}; use group::ff::PrimeField; +use zeroize::Zeroize; use crate::{kdc::KeyRecoverer, poly::lagrange}; @@ -20,7 +21,7 @@ impl Player { } /// Recovers the secret from the provided shares. - pub fn recover_secret(&self, shares: &[SecretShare]) -> Result { + pub fn recover_secret(&self, shares: &[SecretShare]) -> Result { if shares.len() < self.min_shares() { bail!("not enough shares"); } @@ -48,7 +49,7 @@ impl Player { } /// Returns true if shares are from distinct shareholders. - fn distinct_shares(shares: &[SecretShare]) -> bool { + fn distinct_shares(shares: &[SecretShare]) -> bool { // For a small number of shareholders, a brute-force approach should // suffice, and it doesn't require the prime field to be hashable. for i in 0..shares.len() { diff --git a/secret-sharing/src/churp/shareholder.rs b/secret-sharing/src/churp/shareholder.rs index df8a345069b..2782aff4e49 100644 --- a/secret-sharing/src/churp/shareholder.rs +++ b/secret-sharing/src/churp/shareholder.rs @@ -7,6 +7,7 @@ use group::{ ff::{Field, PrimeField}, Group, }; +use zeroize::Zeroize; use crate::{ kdc::PointShareholder, poly::Polynomial, suites::FieldDigest, vss::VerificationMatrix, @@ -28,7 +29,11 @@ pub fn encode_shareholder(id: &[u8], dst: &[u8]) -> Result { +pub struct Shareholder +where + G: Group, + G::Scalar: Zeroize, +{ /// Verifiable secret (full or reduced) share of the shared secret. verifiable_share: VerifiableSecretShare, } @@ -36,6 +41,7 @@ pub struct Shareholder { impl Shareholder where G: Group, + G::Scalar: Zeroize, { /// Returns the verifiable secret share. pub fn verifiable_share(&self) -> &VerifiableSecretShare { @@ -77,6 +83,7 @@ where impl From> for Shareholder where G: Group, + G::Scalar: Zeroize, { fn from(verifiable_share: VerifiableSecretShare) -> Shareholder { Shareholder { verifiable_share } @@ -86,6 +93,7 @@ where impl PointShareholder for Shareholder where G: Group, + G::Scalar: Zeroize, { fn coordinate_x(&self) -> &G::Scalar { self.verifiable_share.share.coordinate_x() @@ -97,7 +105,10 @@ where } /// Secret share of the shared secret. -pub struct SecretShare { +pub struct SecretShare +where + F: PrimeField + Zeroize, +{ /// The encoded identity of the shareholder. /// /// The identity is the x-coordinate of a point on the secret-sharing @@ -113,7 +124,7 @@ pub struct SecretShare { impl SecretShare where - F: PrimeField, + F: PrimeField + Zeroize, { /// Creates a new secret share. pub fn new(x: F, p: Polynomial) -> Self { @@ -142,7 +153,7 @@ where impl AddAssign for SecretShare where - F: PrimeField, + F: PrimeField + Zeroize, { #[inline] fn add_assign(&mut self, rhs: SecretShare) { @@ -152,7 +163,7 @@ where impl AddAssign<&SecretShare> for SecretShare where - F: PrimeField, + F: PrimeField + Zeroize, { fn add_assign(&mut self, rhs: &SecretShare) { debug_assert!(self.x == rhs.x); @@ -160,8 +171,21 @@ where } } +impl Drop for SecretShare +where + F: PrimeField + Zeroize, +{ + fn drop(&mut self) { + self.p.zeroize(); + } +} + /// Verifiable secret share of the shared secret. -pub struct VerifiableSecretShare { +pub struct VerifiableSecretShare +where + G: Group, + G::Scalar: Zeroize, +{ /// Secret (full or reduced) share of the shared secret. pub(crate) share: SecretShare, @@ -174,6 +198,7 @@ pub struct VerifiableSecretShare { impl VerifiableSecretShare where G: Group, + G::Scalar: Zeroize, { /// Creates a new verifiable secret share. pub fn new(share: SecretShare, vm: VerificationMatrix) -> Self { @@ -270,6 +295,7 @@ where impl AddAssign for VerifiableSecretShare where G: Group, + G::Scalar: Zeroize, { #[inline] fn add_assign(&mut self, rhs: VerifiableSecretShare) { @@ -280,6 +306,7 @@ where impl AddAssign<&VerifiableSecretShare> for VerifiableSecretShare where G: Group, + G::Scalar: Zeroize, { fn add_assign(&mut self, rhs: &VerifiableSecretShare) { self.share += &rhs.share; diff --git a/secret-sharing/src/churp/switch.rs b/secret-sharing/src/churp/switch.rs index 0a9ef80c388..49f28e342f7 100644 --- a/secret-sharing/src/churp/switch.rs +++ b/secret-sharing/src/churp/switch.rs @@ -2,6 +2,7 @@ use std::sync::{Arc, Mutex}; use anyhow::Result; use group::Group; +use zeroize::Zeroize; use crate::{ poly::lagrange::lagrange, @@ -11,7 +12,11 @@ use crate::{ use super::{Error, SecretShare, Shareholder, VerifiableSecretShare}; /// Dimension switch state. -enum DimensionSwitchState { +enum DimensionSwitchState +where + G: Group, + G::Scalar: Zeroize, +{ /// Represents the state where the dimension switch is waiting for /// the verification matrix from the previous switch, which is needed /// to verify switch points. Once the matrix is received, the state @@ -42,7 +47,11 @@ enum DimensionSwitchState { } /// A dimension switch based on a share resharing technique. -pub struct DimensionSwitch { +pub struct DimensionSwitch +where + G: Group, + G::Scalar: Zeroize, +{ /// The degree of the secret-sharing polynomial. threshold: u8, @@ -67,6 +76,7 @@ pub struct DimensionSwitch { impl DimensionSwitch where G: Group, + G::Scalar: Zeroize, { /// Creates a new share reduction dimension switch. /// @@ -311,6 +321,7 @@ pub struct SwitchPoints { impl SwitchPoints where G: Group, + G::Scalar: Zeroize, { /// Creates a new accumulator for switch points. fn new( @@ -417,7 +428,11 @@ where } /// An accumulator for bivariate shares. -struct BivariateShares { +struct BivariateShares +where + G: Group, + G::Scalar: Zeroize, +{ /// The degree of the secret-sharing polynomial. threshold: u8, @@ -447,6 +462,7 @@ struct BivariateShares { impl BivariateShares where G: Group, + G::Scalar: Zeroize, { /// Creates a new accumulator for bivariate shares. fn new( diff --git a/secret-sharing/src/suites/mod.rs b/secret-sharing/src/suites/mod.rs index a90aa4d6130..f8689289d18 100644 --- a/secret-sharing/src/suites/mod.rs +++ b/secret-sharing/src/suites/mod.rs @@ -1,6 +1,7 @@ use anyhow::Result; use group::{ff::PrimeField, Group, GroupEncoding}; +use zeroize::Zeroize; pub mod p384; @@ -30,7 +31,7 @@ pub trait Suite: FieldDigest + GroupDigest { /// The type representing an element modulo the order of the group. - type PrimeField: PrimeField; + type PrimeField: PrimeField + Zeroize; /// The type representing an element of a cryptographic group. type Group: Group + GroupEncoding; @@ -40,6 +41,7 @@ impl Suite for S where S: FieldDigest + GroupDigest, ::Output: Group::Output> + GroupEncoding, + ::Output: Zeroize, { type PrimeField = ::Output; type Group = ::Output; From cf2fe4385a8388713db71400db0c2457a9d906b0 Mon Sep 17 00:00:00 2001 From: Peter Nose Date: Tue, 5 Nov 2024 04:10:08 +0100 Subject: [PATCH 10/21] secret-sharing/src/poly/lagrange: Zeroize product of li and yi --- secret-sharing/src/poly/lagrange/naive.rs | 17 ++++++++++++++--- secret-sharing/src/poly/lagrange/optimized.rs | 18 ++++++++++++++---- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/secret-sharing/src/poly/lagrange/naive.rs b/secret-sharing/src/poly/lagrange/naive.rs index a969dd76439..3f33e995078 100644 --- a/secret-sharing/src/poly/lagrange/naive.rs +++ b/secret-sharing/src/poly/lagrange/naive.rs @@ -1,7 +1,7 @@ // Lagrange Polynomials interpolation / reconstruction -use std::iter::zip; use group::ff::PrimeField; +use zeroize::Zeroize; use crate::poly::Polynomial; @@ -16,10 +16,21 @@ use crate::poly::Polynomial; /// # Panics /// /// Panics if the x-coordinates are not unique. -pub fn lagrange_naive(xs: &[F], ys: &[F]) -> Polynomial { +pub fn lagrange_naive(xs: &[F], ys: &[F]) -> Polynomial +where + F: PrimeField + Zeroize, +{ debug_assert!(xs.len() == ys.len()); + let ls = basis_polynomials_naive(xs); - zip(ls, ys).map(|(li, &yi)| li * yi).sum() + let mut l = Polynomial::default(); + for (mut li, yi) in ls.into_iter().zip(ys) { + li *= yi; + l += &li; + li.zeroize(); + } + + l } /// Returns Lagrange basis polynomials for the given set of x-coordinates. diff --git a/secret-sharing/src/poly/lagrange/optimized.rs b/secret-sharing/src/poly/lagrange/optimized.rs index cda8afe3f48..e3671260f9f 100644 --- a/secret-sharing/src/poly/lagrange/optimized.rs +++ b/secret-sharing/src/poly/lagrange/optimized.rs @@ -1,6 +1,5 @@ -use std::iter::zip; - use group::ff::PrimeField; +use zeroize::Zeroize; use crate::poly::Polynomial; @@ -17,10 +16,21 @@ use super::multiplier::Multiplier; /// # Panics /// /// Panics if the x-coordinates are not unique. -pub fn lagrange(xs: &[F], ys: &[F]) -> Polynomial { +pub fn lagrange(xs: &[F], ys: &[F]) -> Polynomial +where + F: PrimeField + Zeroize, +{ debug_assert!(xs.len() == ys.len()); + let ls = basis_polynomials(xs); - zip(ls, ys).map(|(li, &yi)| li * yi).sum() + let mut l = Polynomial::default(); + for (mut li, yi) in ls.into_iter().zip(ys) { + li *= yi; + l += &li; + li.zeroize(); + } + + l } /// Returns Lagrange basis polynomials for the given set of x-coordinates. From be45e1b62df52bb59de60afcd0249819689af6d5 Mon Sep 17 00:00:00 2001 From: Peter Nose Date: Tue, 5 Nov 2024 10:17:25 +0100 Subject: [PATCH 11/21] secret-sharing/src/churp/switch: Flatten code when adding switch point --- secret-sharing/src/churp/switch.rs | 37 +++++++++++++++--------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/secret-sharing/src/churp/switch.rs b/secret-sharing/src/churp/switch.rs index 49f28e342f7..e5e24eac9f1 100644 --- a/secret-sharing/src/churp/switch.rs +++ b/secret-sharing/src/churp/switch.rs @@ -184,27 +184,28 @@ where _ => return Err(Error::InvalidState.into()), }; - let done = sp.add_point(x, bij)?; - if done { - let shareholder = sp.reconstruct_shareholder()?; - let shareholder = Arc::new(shareholder); + if !sp.add_point(x, bij)? { + return Ok(false); + } - if self.shareholders.is_empty() { - *state = DimensionSwitchState::Serving(shareholder); - } else { - let bs = BivariateShares::new( - self.threshold, - self.zero_hole, - self.full_share, - self.me, - self.shareholders.clone(), - Some(shareholder), - )?; - *state = DimensionSwitchState::Merging(bs); - } + let shareholder = sp.reconstruct_shareholder()?; + let shareholder = Arc::new(shareholder); + + if self.shareholders.is_empty() { + *state = DimensionSwitchState::Serving(shareholder); + } else { + let bs = BivariateShares::new( + self.threshold, + self.zero_hole, + self.full_share, + self.me, + self.shareholders.clone(), + Some(shareholder), + )?; + *state = DimensionSwitchState::Merging(bs); } - Ok(done) + Ok(true) } /// Checks if the switch is waiting for a shareholder. From 3a03273f01a74e960caa37e3c72c4842e6158d2b Mon Sep 17 00:00:00 2001 From: Peter Nose Date: Tue, 5 Nov 2024 11:27:37 +0100 Subject: [PATCH 12/21] secret-sharing/src/poly: Add accessors for point coordinates --- secret-sharing/src/poly/point.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/secret-sharing/src/poly/point.rs b/secret-sharing/src/poly/point.rs index 8e022f74e60..f405eb59ca7 100644 --- a/secret-sharing/src/poly/point.rs +++ b/secret-sharing/src/poly/point.rs @@ -18,6 +18,16 @@ where pub fn new(x: F, y: F) -> Self { Self { x, y } } + + /// Returns the x-coordinate of the point. + pub fn x(&self) -> &F { + &self.x + } + + /// Returns the y-coordinate of the point. + pub fn y(&self) -> &F { + &self.y + } } impl Zeroize for Point From 8325ebda3691b3f9754c05149052df5356cff50e Mon Sep 17 00:00:00 2001 From: Peter Nose Date: Tue, 5 Nov 2024 11:17:46 +0100 Subject: [PATCH 13/21] secret-sharing/src/churp/switch: Store points instead of coordinates PrimeField implements the Copy trait, which means values are copied when passed as parameters. To prevent sensitive data from being replicated, the switch point struct should accumulate points rather than coordinates. --- keymanager/src/churp/handler.rs | 32 ++++--- secret-sharing/src/churp/handoff.rs | 31 +++---- secret-sharing/src/churp/switch.rs | 88 ++++++++++--------- secret-sharing/src/poly/lagrange/naive.rs | 47 +++++----- secret-sharing/src/poly/lagrange/optimized.rs | 48 +++++----- 5 files changed, 132 insertions(+), 114 deletions(-) diff --git a/keymanager/src/churp/handler.rs b/keymanager/src/churp/handler.rs index a8e8ef615a9..d32dfd9cc48 100644 --- a/keymanager/src/churp/handler.rs +++ b/keymanager/src/churp/handler.rs @@ -38,7 +38,7 @@ use secret_sharing::{ HandoffKind, Shareholder, VerifiableSecretShare, }, kdc::KeySharer, - poly::{scalar_from_bytes, scalar_to_bytes}, + poly::{scalar_from_bytes, scalar_to_bytes, Point}, suites::{p384, Suite}, vss::VerificationMatrix, }; @@ -608,7 +608,8 @@ impl Instance { // Fetch from the host node. if node_id == self.node_id { let shareholder = self.get_shareholder(status.handoff)?; - let point = shareholder.switch_point(&x); + let y = shareholder.switch_point(&x); + let point = Point::new(x, y); if handoff.needs_verification_matrix()? { // Local verification matrix is trusted. @@ -616,7 +617,7 @@ impl Instance { handoff.set_verification_matrix(vm)?; } - return handoff.add_share_reduction_switch_point(x, point); + return handoff.add_share_reduction_switch_point(point); } // Fetch from the remote node. @@ -638,15 +639,16 @@ impl Instance { handoff.set_verification_matrix(vm)?; } - let point = block_on(client.churp_share_reduction_point( + let y = block_on(client.churp_share_reduction_point( self.churp_id, status.next_handoff, self.node_id, vec![node_id], ))?; - let point = scalar_from_bytes(&point).ok_or(Error::PointDecodingFailed)?; + let y = scalar_from_bytes(&y).ok_or(Error::PointDecodingFailed)?; + let point = Point::new(x, y); - handoff.add_share_reduction_switch_point(x, point) + handoff.add_share_reduction_switch_point(point) } /// Tries to fetch switch point for share reduction from the given node. @@ -666,21 +668,23 @@ impl Instance { // Fetch from the host node. if node_id == self.node_id { let shareholder = handoff.get_reduced_shareholder()?; - let point = shareholder.switch_point(&x); + let y = shareholder.switch_point(&x); + let point = Point::new(x, y); - return handoff.add_full_share_distribution_switch_point(x, point); + return handoff.add_full_share_distribution_switch_point(point); } // Fetch from the remote node. - let point = block_on(client.churp_share_distribution_point( + let bytes = block_on(client.churp_share_distribution_point( self.churp_id, status.next_handoff, self.node_id, vec![node_id], ))?; - let point = scalar_from_bytes(&point).ok_or(Error::PointDecodingFailed)?; + let y = scalar_from_bytes(&bytes).ok_or(Error::PointDecodingFailed)?; + let point = Point::new(x, y); - handoff.add_full_share_distribution_switch_point(x, point) + handoff.add_full_share_distribution_switch_point(point) } /// Tries to fetch proactive bivariate share from the given node. @@ -1237,10 +1241,10 @@ impl Handler for Instance { let x = encode_shareholder::(&node_id.0, &self.shareholder_dst)?; let shareholder = self.get_shareholder(status.handoff)?; - let point = shareholder.switch_point(&x); - let point = scalar_to_bytes(&point); + let y = shareholder.switch_point(&x); + let bytes = scalar_to_bytes(&y); - Ok(point) + Ok(bytes) } fn share_distribution_switch_point( diff --git a/secret-sharing/src/churp/handoff.rs b/secret-sharing/src/churp/handoff.rs index 13a6fe69dcc..abf882b1810 100644 --- a/secret-sharing/src/churp/handoff.rs +++ b/secret-sharing/src/churp/handoff.rs @@ -4,7 +4,7 @@ use anyhow::Result; use group::Group; use zeroize::Zeroize; -use crate::vss::VerificationMatrix; +use crate::{poly::Point, vss::VerificationMatrix}; use super::{DimensionSwitch, Error, Shareholder, VerifiableSecretShare}; @@ -116,7 +116,7 @@ where } /// Adds the given switch point to share reduction. - fn add_share_reduction_switch_point(&self, _x: G::Scalar, _bij: G::Scalar) -> Result { + fn add_share_reduction_switch_point(&self, _point: Point) -> Result { Err(Error::InvalidKind.into()) } @@ -127,11 +127,7 @@ where } /// Adds the given switch point to full share distribution. - fn add_full_share_distribution_switch_point( - &self, - _x: G::Scalar, - _bij: G::Scalar, - ) -> Result { + fn add_full_share_distribution_switch_point(&self, _point: Point) -> Result { Err(Error::InvalidKind.into()) } @@ -342,20 +338,16 @@ where self.share_reduction.needs_switch_point(x) } - fn add_share_reduction_switch_point(&self, x: G::Scalar, bij: G::Scalar) -> Result { - self.share_reduction.add_switch_point(x, bij) + fn add_share_reduction_switch_point(&self, point: Point) -> Result { + self.share_reduction.add_switch_point(point) } fn needs_full_share_distribution_switch_point(&self, x: &G::Scalar) -> Result { self.share_distribution.needs_switch_point(x) } - fn add_full_share_distribution_switch_point( - &self, - x: G::Scalar, - bij: G::Scalar, - ) -> Result { - self.share_distribution.add_switch_point(x, bij) + fn add_full_share_distribution_switch_point(&self, point: Point) -> Result { + self.share_distribution.add_switch_point(point) } fn needs_bivariate_share(&self, x: &G::Scalar) -> Result { @@ -403,6 +395,7 @@ mod tests { use crate::{ churp::{self, Handoff, HandoffKind, VerifiableSecretShare}, + poly::Point, suites::{self, p384}, }; @@ -582,9 +575,8 @@ mod tests { assert!(handoff.needs_share_reduction_switch_point(&bob).unwrap()); let bij = shareholder.switch_point(alice); - let done = handoff - .add_share_reduction_switch_point(bob.clone(), bij) - .unwrap(); + let point = Point::new(bob.clone(), bij); + let done = handoff.add_share_reduction_switch_point(point).unwrap(); if j + 1 < num_points { // Accumulation still in progress. @@ -643,8 +635,9 @@ mod tests { .needs_full_share_distribution_switch_point(&bob) .unwrap()); let bij = shareholder.switch_point(&alice); + let point = Point::new(bob.clone(), bij); let done = handoff - .add_full_share_distribution_switch_point(bob.clone(), bij) + .add_full_share_distribution_switch_point(point) .unwrap(); if j + 1 < num_points { diff --git a/secret-sharing/src/churp/switch.rs b/secret-sharing/src/churp/switch.rs index e5e24eac9f1..d9cbc1cb225 100644 --- a/secret-sharing/src/churp/switch.rs +++ b/secret-sharing/src/churp/switch.rs @@ -5,7 +5,7 @@ use group::Group; use zeroize::Zeroize; use crate::{ - poly::lagrange::lagrange, + poly::{lagrange::lagrange, Point}, vss::{VerificationMatrix, VerificationVector}, }; @@ -177,14 +177,16 @@ where /// /// Returns true if enough points have been received and the switch /// transitioned to the next state. - pub(crate) fn add_switch_point(&self, x: G::Scalar, bij: G::Scalar) -> Result { + pub(crate) fn add_switch_point(&self, point: Point) -> Result { let mut state = self.state.lock().unwrap(); let sp = match &mut *state { DimensionSwitchState::Accumulating(sp) => sp, _ => return Err(Error::InvalidState.into()), }; - if !sp.add_point(x, bij)? { + sp.add_point(point)?; + + if sp.needs_points() { return Ok(false); } @@ -286,8 +288,11 @@ where } /// An accumulator for switch points. -#[derive(Debug)] -pub struct SwitchPoints { +pub struct SwitchPoints +where + G: Group, + G::Scalar: Zeroize, +{ /// The minimum number of distinct points required to reconstruct /// the polynomial. n: usize, @@ -311,12 +316,8 @@ pub struct SwitchPoints { /// distribution phase. vv: VerificationVector, - /// A list of encoded shareholders' identities whose points have been - /// received. - xs: Vec, - /// A list of received switch points. - bijs: Vec, + points: Vec>, } impl SwitchPoints @@ -352,36 +353,41 @@ where let vm = Some(vm); // We need at least n points to reconstruct the polynomial share. - let xs = Vec::with_capacity(n); - let bijs = Vec::with_capacity(n); + let points = Vec::with_capacity(n); Ok(Self { n, me, vm, vv, - xs, - bijs, + points, }) } + /// Checks if a switch point has already been received from the given shareholder. + fn has_point(&self, x: &G::Scalar) -> bool { + self.points.iter().any(|p| &p.x == x) + } + /// Checks if a switch point is required from the given shareholder. fn needs_point(&self, x: &G::Scalar) -> bool { - if self.xs.len() >= self.n { - return false; - } - !self.xs.contains(x) + self.needs_points() && !self.has_point(x) + } + + /// Checks if additional switch points are needed. + fn needs_points(&self) -> bool { + self.points.len() < self.n } /// Verifies and adds the given switch point. /// /// Returns true if enough points have been received; otherwise, /// it returns false. - fn add_point(&mut self, x: G::Scalar, bij: G::Scalar) -> Result { - if self.xs.len() >= self.n { + fn add_point(&mut self, point: Point) -> Result<()> { + if self.points.len() >= self.n { return Err(Error::TooManySwitchPoints.into()); } - if self.xs.contains(&x) { + if self.has_point(&point.x) { return Err(Error::DuplicateShareholder.into()); } @@ -389,16 +395,13 @@ where // If the point is valid, it doesn't matter if it came from a stranger. // However, since verification is costly, one could check if the point // came from a legitimate shareholder. - if !self.vv.verify(&x, &bij) { + if !self.vv.verify(&point.x, &point.y) { return Err(Error::InvalidSwitchPoint.into()); } - self.xs.push(x); - self.bijs.push(bij); + self.points.push(point); - let done = self.xs.len() >= self.n; - - Ok(done) + Ok(()) } /// Reconstructs the shareholder from the received switch points. @@ -406,23 +409,27 @@ where /// The shareholder can be reconstructed only once, which avoids copying /// the verification matrix. fn reconstruct_shareholder(&mut self) -> Result> { - if self.xs.len() < self.n { + if self.points.len() < self.n { return Err(Error::NotEnoughSwitchPoints.into()); } - let xs = &self.xs[0..self.n]; - let ys = &self.bijs[0..self.n]; - let p = lagrange(xs, ys); - - if p.size() != self.n { - return Err(Error::PolynomialDegreeMismatch.into()); - } - + let points = &self.points[0..self.n]; + let p = lagrange(points); let x = self.me.take().ok_or(Error::ShareholderIdentityRequired)?; let vm = self.vm.take().ok_or(Error::VerificationMatrixRequired)?; let share: SecretShare<::Scalar> = SecretShare::new(x, p); let verifiable_share = VerifiableSecretShare::new(share, vm); - let shareholder = verifiable_share.into(); + let shareholder: Shareholder = verifiable_share.into(); + + if shareholder + .verifiable_share() + .secret_share() + .polynomial() + .size() + != self.n + { + return Err(Error::PolynomialDegreeMismatch.into()); + } Ok(shareholder) } @@ -575,7 +582,7 @@ mod tests { use crate::{ churp::{SecretShare, VerifiableSecretShare}, - poly, + poly::{self, Point}, suites::{self, p384}, vss, }; @@ -609,8 +616,9 @@ mod tests { false => bp.eval(&x, &y), true => bp.eval(&y, &x), }; - let res = sp.add_point(x, bij); - res + let point = Point::new(x, bij); + sp.add_point(point)?; + Ok(!sp.needs_points()) } #[test] diff --git a/secret-sharing/src/poly/lagrange/naive.rs b/secret-sharing/src/poly/lagrange/naive.rs index 3f33e995078..b02fbbc7236 100644 --- a/secret-sharing/src/poly/lagrange/naive.rs +++ b/secret-sharing/src/poly/lagrange/naive.rs @@ -3,7 +3,7 @@ use group::ff::PrimeField; use zeroize::Zeroize; -use crate::poly::Polynomial; +use crate::poly::{Point, Polynomial}; /// Returns the Lagrange interpolation polynomial for the given set of points. /// @@ -16,16 +16,15 @@ use crate::poly::Polynomial; /// # Panics /// /// Panics if the x-coordinates are not unique. -pub fn lagrange_naive(xs: &[F], ys: &[F]) -> Polynomial +pub fn lagrange_naive(points: &[Point]) -> Polynomial where F: PrimeField + Zeroize, { - debug_assert!(xs.len() == ys.len()); - - let ls = basis_polynomials_naive(xs); + let xs: Vec<_> = points.iter().map(|p| p.x).collect(); + let ls = basis_polynomials_naive(&xs); let mut l = Polynomial::default(); - for (mut li, yi) in ls.into_iter().zip(ys) { - li *= yi; + for (mut li, point) in ls.into_iter().zip(points) { + li *= &point.y; l += &li; li.zeroize(); } @@ -123,11 +122,11 @@ mod tests { use self::test::Bencher; - use std::iter::zip; - use group::ff::Field; use rand::{rngs::StdRng, RngCore, SeedableRng}; + use crate::poly::Point; + use super::{ basis_polynomial_naive, basis_polynomials_naive, coefficient_naive, coefficients_naive, lagrange_naive, @@ -153,22 +152,31 @@ mod tests { (0..n).map(|_| PrimeField::random(&mut rng)).collect() } + fn random_points(n: usize, mut rng: &mut impl RngCore) -> Vec> { + let mut points = Vec::with_capacity(n); + for _ in 0..n { + let x = PrimeField::random(&mut rng); + let y = PrimeField::random(&mut rng); + let point = Point::new(x, y); + points.push(point); + } + points + } + #[test] fn test_lagrange_naive() { - // Prepare points (x, 2**x + 1). + // Prepare random points. let n = 10; - let xs: Vec<_> = (1..=n as i64).collect(); - let ys: Vec<_> = (1..=n as u32).map(|x| 1 + 2_i64.pow(x)).collect(); + let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let points = random_points(n, &mut rng); // Test polynomials of different degrees. for size in 1..=n { - let xs = scalars(&xs[..size]); - let ys = scalars(&ys[..size]); - let p = lagrange_naive(&xs, &ys); + let p = lagrange_naive(&points[..size]); // Verify zeros. - for (x, y) in zip(xs, ys) { - assert_eq!(p.eval(&x), y); + for point in &points[..size] { + assert_eq!(p.eval(&point.x), point.y); } // Verify degree. @@ -223,11 +231,10 @@ mod tests { fn bench_lagrange_naive(b: &mut Bencher, n: usize) { let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); - let xs = random_scalars(n, &mut rng); - let ys = random_scalars(n, &mut rng); + let points = random_points(n, &mut rng); b.iter(|| { - let _p = lagrange_naive(&xs, &ys); + let _p = lagrange_naive(&points); }); } diff --git a/secret-sharing/src/poly/lagrange/optimized.rs b/secret-sharing/src/poly/lagrange/optimized.rs index e3671260f9f..1d7eae4ef42 100644 --- a/secret-sharing/src/poly/lagrange/optimized.rs +++ b/secret-sharing/src/poly/lagrange/optimized.rs @@ -1,7 +1,7 @@ use group::ff::PrimeField; use zeroize::Zeroize; -use crate::poly::Polynomial; +use crate::poly::{Point, Polynomial}; use super::multiplier::Multiplier; @@ -16,16 +16,15 @@ use super::multiplier::Multiplier; /// # Panics /// /// Panics if the x-coordinates are not unique. -pub fn lagrange(xs: &[F], ys: &[F]) -> Polynomial +pub fn lagrange(points: &[Point]) -> Polynomial where F: PrimeField + Zeroize, { - debug_assert!(xs.len() == ys.len()); - - let ls = basis_polynomials(xs); + let xs: Vec<_> = points.iter().map(|p| p.x).collect(); + let ls = basis_polynomials(&xs); let mut l = Polynomial::default(); - for (mut li, yi) in ls.into_iter().zip(ys) { - li *= yi; + for (mut li, point) in ls.into_iter().zip(points) { + li *= &point.y; l += &li; li.zeroize(); } @@ -141,11 +140,11 @@ mod tests { use self::test::Bencher; - use std::iter::zip; - use group::ff::Field; use rand::{rngs::StdRng, RngCore, SeedableRng}; + use crate::poly::Point; + use super::{ basis_polynomial, basis_polynomials, coefficient, coefficients, lagrange, multiplier_for_basis_polynomials, multiplier_for_coefficients, @@ -171,22 +170,31 @@ mod tests { (0..n).map(|_| PrimeField::random(&mut rng)).collect() } + fn random_points(n: usize, mut rng: &mut impl RngCore) -> Vec> { + let mut points = Vec::with_capacity(n); + for _ in 0..n { + let x = PrimeField::random(&mut rng); + let y = PrimeField::random(&mut rng); + let point = Point::new(x, y); + points.push(point); + } + points + } + #[test] fn test_lagrange() { - // Prepare points (x, 2**x + 1). + // Prepare random points. let n = 10; - let xs: Vec<_> = (1..=n as i64).collect(); - let ys: Vec<_> = (1..=n as u32).map(|x| 1 + 2_i64.pow(x)).collect(); + let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); + let points = random_points(n, &mut rng); // Test polynomials of different degrees. for size in 1..=n { - let xs = scalars(&xs[..size]); - let ys = scalars(&ys[..size]); - let p = lagrange(&xs, &ys); + let p = lagrange(&points[..size]); // Verify zeros. - for (x, y) in zip(xs, ys) { - assert_eq!(p.eval(&x), y); + for point in &points[..size] { + assert_eq!(p.eval(&point.x), point.y); } // Verify degree. @@ -245,12 +253,10 @@ mod tests { fn bench_lagrange(b: &mut Bencher, n: usize) { let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); - - let xs = random_scalars(n, &mut rng); - let ys = random_scalars(n, &mut rng); + let points = random_points(n, &mut rng); b.iter(|| { - let _p = lagrange(&xs, &ys); + let _p = lagrange(&points); }); } From a717221c7c632f94a4584a3d7604b6019cdb287f Mon Sep 17 00:00:00 2001 From: Peter Nose Date: Tue, 5 Nov 2024 11:25:54 +0100 Subject: [PATCH 14/21] secret-sharing/src/churp/switch: Zeroize switch points on drop --- keymanager/src/churp/handler.rs | 12 ++-- secret-sharing/src/churp/handoff.rs | 25 ++++--- secret-sharing/src/churp/switch.rs | 67 ++++++++++++++++--- secret-sharing/src/poly/lagrange/naive.rs | 4 +- secret-sharing/src/poly/lagrange/optimized.rs | 4 +- 5 files changed, 84 insertions(+), 28 deletions(-) diff --git a/keymanager/src/churp/handler.rs b/keymanager/src/churp/handler.rs index d32dfd9cc48..3ddd3cfd90c 100644 --- a/keymanager/src/churp/handler.rs +++ b/keymanager/src/churp/handler.rs @@ -35,10 +35,10 @@ use oasis_core_runtime::{ use secret_sharing::{ churp::{ encode_shareholder, CommitteeChanged, CommitteeUnchanged, Dealer, DealingPhase, Handoff, - HandoffKind, Shareholder, VerifiableSecretShare, + HandoffKind, Shareholder, SwitchPoint, VerifiableSecretShare, }, kdc::KeySharer, - poly::{scalar_from_bytes, scalar_to_bytes, Point}, + poly::{scalar_from_bytes, scalar_to_bytes}, suites::{p384, Suite}, vss::VerificationMatrix, }; @@ -609,7 +609,7 @@ impl Instance { if node_id == self.node_id { let shareholder = self.get_shareholder(status.handoff)?; let y = shareholder.switch_point(&x); - let point = Point::new(x, y); + let point = SwitchPoint::new(x, y); if handoff.needs_verification_matrix()? { // Local verification matrix is trusted. @@ -646,7 +646,7 @@ impl Instance { vec![node_id], ))?; let y = scalar_from_bytes(&y).ok_or(Error::PointDecodingFailed)?; - let point = Point::new(x, y); + let point = SwitchPoint::new(x, y); handoff.add_share_reduction_switch_point(point) } @@ -669,7 +669,7 @@ impl Instance { if node_id == self.node_id { let shareholder = handoff.get_reduced_shareholder()?; let y = shareholder.switch_point(&x); - let point = Point::new(x, y); + let point = SwitchPoint::new(x, y); return handoff.add_full_share_distribution_switch_point(point); } @@ -682,7 +682,7 @@ impl Instance { vec![node_id], ))?; let y = scalar_from_bytes(&bytes).ok_or(Error::PointDecodingFailed)?; - let point = Point::new(x, y); + let point = SwitchPoint::new(x, y); handoff.add_full_share_distribution_switch_point(point) } diff --git a/secret-sharing/src/churp/handoff.rs b/secret-sharing/src/churp/handoff.rs index abf882b1810..d8a4930c95b 100644 --- a/secret-sharing/src/churp/handoff.rs +++ b/secret-sharing/src/churp/handoff.rs @@ -4,9 +4,9 @@ use anyhow::Result; use group::Group; use zeroize::Zeroize; -use crate::{poly::Point, vss::VerificationMatrix}; +use crate::vss::VerificationMatrix; -use super::{DimensionSwitch, Error, Shareholder, VerifiableSecretShare}; +use super::{DimensionSwitch, Error, Shareholder, SwitchPoint, VerifiableSecretShare}; /// Handoff kind. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -116,7 +116,7 @@ where } /// Adds the given switch point to share reduction. - fn add_share_reduction_switch_point(&self, _point: Point) -> Result { + fn add_share_reduction_switch_point(&self, _point: SwitchPoint) -> Result { Err(Error::InvalidKind.into()) } @@ -127,7 +127,10 @@ where } /// Adds the given switch point to full share distribution. - fn add_full_share_distribution_switch_point(&self, _point: Point) -> Result { + fn add_full_share_distribution_switch_point( + &self, + _point: SwitchPoint, + ) -> Result { Err(Error::InvalidKind.into()) } @@ -338,7 +341,7 @@ where self.share_reduction.needs_switch_point(x) } - fn add_share_reduction_switch_point(&self, point: Point) -> Result { + fn add_share_reduction_switch_point(&self, point: SwitchPoint) -> Result { self.share_reduction.add_switch_point(point) } @@ -346,7 +349,10 @@ where self.share_distribution.needs_switch_point(x) } - fn add_full_share_distribution_switch_point(&self, point: Point) -> Result { + fn add_full_share_distribution_switch_point( + &self, + point: SwitchPoint, + ) -> Result { self.share_distribution.add_switch_point(point) } @@ -394,8 +400,7 @@ mod tests { use rand::{rngs::StdRng, RngCore, SeedableRng}; use crate::{ - churp::{self, Handoff, HandoffKind, VerifiableSecretShare}, - poly::Point, + churp::{self, Handoff, HandoffKind, SwitchPoint, VerifiableSecretShare}, suites::{self, p384}, }; @@ -575,7 +580,7 @@ mod tests { assert!(handoff.needs_share_reduction_switch_point(&bob).unwrap()); let bij = shareholder.switch_point(alice); - let point = Point::new(bob.clone(), bij); + let point = SwitchPoint::new(bob.clone(), bij); let done = handoff.add_share_reduction_switch_point(point).unwrap(); if j + 1 < num_points { @@ -635,7 +640,7 @@ mod tests { .needs_full_share_distribution_switch_point(&bob) .unwrap()); let bij = shareholder.switch_point(&alice); - let point = Point::new(bob.clone(), bij); + let point = SwitchPoint::new(bob.clone(), bij); let done = handoff .add_full_share_distribution_switch_point(point) .unwrap(); diff --git a/secret-sharing/src/churp/switch.rs b/secret-sharing/src/churp/switch.rs index d9cbc1cb225..d572805b1e4 100644 --- a/secret-sharing/src/churp/switch.rs +++ b/secret-sharing/src/churp/switch.rs @@ -1,7 +1,10 @@ -use std::sync::{Arc, Mutex}; +use std::{ + ops::Deref, + sync::{Arc, Mutex}, +}; use anyhow::Result; -use group::Group; +use group::{ff::PrimeField, Group}; use zeroize::Zeroize; use crate::{ @@ -11,6 +14,50 @@ use crate::{ use super::{Error, SecretShare, Shareholder, VerifiableSecretShare}; +/// A simple wrapper around point that is zeroized when dropped. +pub struct SwitchPoint(Point) +where + F: PrimeField + Zeroize; + +impl SwitchPoint +where + F: PrimeField + Zeroize, +{ + /// Creates a new switch point. + pub fn new(x: F, y: F) -> Self { + Self(Point::new(x, y)) + } +} + +impl Deref for SwitchPoint +where + F: PrimeField + Zeroize, +{ + type Target = Point; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Zeroize for SwitchPoint +where + F: PrimeField + Zeroize, +{ + fn zeroize(&mut self) { + self.0.zeroize(); + } +} + +impl Drop for SwitchPoint +where + F: PrimeField + Zeroize, +{ + fn drop(&mut self) { + self.zeroize(); + } +} + /// Dimension switch state. enum DimensionSwitchState where @@ -177,7 +224,7 @@ where /// /// Returns true if enough points have been received and the switch /// transitioned to the next state. - pub(crate) fn add_switch_point(&self, point: Point) -> Result { + pub(crate) fn add_switch_point(&self, point: SwitchPoint) -> Result { let mut state = self.state.lock().unwrap(); let sp = match &mut *state { DimensionSwitchState::Accumulating(sp) => sp, @@ -317,7 +364,7 @@ where vv: VerificationVector, /// A list of received switch points. - points: Vec>, + points: Vec>, } impl SwitchPoints @@ -383,7 +430,7 @@ where /// /// Returns true if enough points have been received; otherwise, /// it returns false. - fn add_point(&mut self, point: Point) -> Result<()> { + fn add_point(&mut self, point: SwitchPoint) -> Result<()> { if self.points.len() >= self.n { return Err(Error::TooManySwitchPoints.into()); } @@ -413,8 +460,8 @@ where return Err(Error::NotEnoughSwitchPoints.into()); } - let points = &self.points[0..self.n]; - let p = lagrange(points); + let points: Vec<_> = self.points[0..self.n].iter().map(|p| &p.0).collect(); + let p = lagrange(&points); let x = self.me.take().ok_or(Error::ShareholderIdentityRequired)?; let vm = self.vm.take().ok_or(Error::VerificationMatrixRequired)?; let share: SecretShare<::Scalar> = SecretShare::new(x, p); @@ -582,12 +629,12 @@ mod tests { use crate::{ churp::{SecretShare, VerifiableSecretShare}, - poly::{self, Point}, + poly::{self}, suites::{self, p384}, vss, }; - use super::{BivariateShares, Error, SwitchPoints}; + use super::{BivariateShares, Error, SwitchPoint, SwitchPoints}; type Suite = p384::Sha3_384; type Group = ::Group; @@ -616,7 +663,7 @@ mod tests { false => bp.eval(&x, &y), true => bp.eval(&y, &x), }; - let point = Point::new(x, bij); + let point = SwitchPoint::new(x, bij); sp.add_point(point)?; Ok(!sp.needs_points()) } diff --git a/secret-sharing/src/poly/lagrange/naive.rs b/secret-sharing/src/poly/lagrange/naive.rs index b02fbbc7236..b6ef4c9a9c4 100644 --- a/secret-sharing/src/poly/lagrange/naive.rs +++ b/secret-sharing/src/poly/lagrange/naive.rs @@ -16,7 +16,7 @@ use crate::poly::{Point, Polynomial}; /// # Panics /// /// Panics if the x-coordinates are not unique. -pub fn lagrange_naive(points: &[Point]) -> Polynomial +pub fn lagrange_naive(points: &[&Point]) -> Polynomial where F: PrimeField + Zeroize, { @@ -169,6 +169,7 @@ mod tests { let n = 10; let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); let points = random_points(n, &mut rng); + let points: Vec<_> = points.iter().collect(); // Test polynomials of different degrees. for size in 1..=n { @@ -232,6 +233,7 @@ mod tests { fn bench_lagrange_naive(b: &mut Bencher, n: usize) { let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); let points = random_points(n, &mut rng); + let points: Vec<_> = points.iter().collect(); b.iter(|| { let _p = lagrange_naive(&points); diff --git a/secret-sharing/src/poly/lagrange/optimized.rs b/secret-sharing/src/poly/lagrange/optimized.rs index 1d7eae4ef42..41ba5f1458e 100644 --- a/secret-sharing/src/poly/lagrange/optimized.rs +++ b/secret-sharing/src/poly/lagrange/optimized.rs @@ -16,7 +16,7 @@ use super::multiplier::Multiplier; /// # Panics /// /// Panics if the x-coordinates are not unique. -pub fn lagrange(points: &[Point]) -> Polynomial +pub fn lagrange(points: &[&Point]) -> Polynomial where F: PrimeField + Zeroize, { @@ -187,6 +187,7 @@ mod tests { let n = 10; let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); let points = random_points(n, &mut rng); + let points: Vec<_> = points.iter().collect(); // Test polynomials of different degrees. for size in 1..=n { @@ -254,6 +255,7 @@ mod tests { fn bench_lagrange(b: &mut Bencher, n: usize) { let mut rng: StdRng = SeedableRng::from_seed([1u8; 32]); let points = random_points(n, &mut rng); + let points: Vec<_> = points.iter().collect(); b.iter(|| { let _p = lagrange(&points); From 1cf9f7ac56412def1c91357853cf85fade9d4886 Mon Sep 17 00:00:00 2001 From: Peter Nose Date: Tue, 5 Nov 2024 12:27:32 +0100 Subject: [PATCH 15/21] secret-sharing/src/churp/shareholder: Deref verifiable secret share --- keymanager/src/churp/handler.rs | 2 +- keymanager/src/churp/types.rs | 2 +- secret-sharing/src/churp/handoff.rs | 4 +-- secret-sharing/src/churp/player.rs | 7 ++--- secret-sharing/src/churp/shareholder.rs | 36 ++++++++++++++++--------- secret-sharing/src/churp/switch.rs | 30 +++++++++------------ 6 files changed, 43 insertions(+), 38 deletions(-) diff --git a/keymanager/src/churp/handler.rs b/keymanager/src/churp/handler.rs index 3ddd3cfd90c..ed3235bd1bf 100644 --- a/keymanager/src/churp/handler.rs +++ b/keymanager/src/churp/handler.rs @@ -817,7 +817,7 @@ impl Instance { // Verify that the host hasn't changed. let me = encode_shareholder::(&self.node_id.0, &self.shareholder_dst)?; - if share.secret_share().coordinate_x() != &me { + if share.x() != &me { return Err(Error::InvalidHost.into()); } diff --git a/keymanager/src/churp/types.rs b/keymanager/src/churp/types.rs index e083d4b1db9..9c86104ab90 100644 --- a/keymanager/src/churp/types.rs +++ b/keymanager/src/churp/types.rs @@ -212,7 +212,7 @@ where { fn from(share: &SecretShare) -> Self { Self { - x: scalar_to_bytes(share.coordinate_x()), + x: scalar_to_bytes(share.x()), polynomial: share.polynomial().to_bytes(), } } diff --git a/secret-sharing/src/churp/handoff.rs b/secret-sharing/src/churp/handoff.rs index d8a4930c95b..62a86820acb 100644 --- a/secret-sharing/src/churp/handoff.rs +++ b/secret-sharing/src/churp/handoff.rs @@ -576,7 +576,7 @@ mod tests { // Share reduction. let num_points = threshold as usize + 1; for (j, shareholder) in shareholders.iter().take(num_points).enumerate() { - let bob = shareholder.verifiable_share().share.x; + let bob = shareholder.verifiable_share().x; assert!(handoff.needs_share_reduction_switch_point(&bob).unwrap()); let bij = shareholder.switch_point(alice); @@ -634,7 +634,7 @@ mod tests { // Share distribution. let num_points = 2 * threshold as usize + 1; for (j, shareholder) in shareholders.iter().take(num_points).enumerate() { - let bob = shareholder.verifiable_share().share.x; + let bob = shareholder.verifiable_share().x; assert!(handoff .needs_full_share_distribution_switch_point(&bob) diff --git a/secret-sharing/src/churp/player.rs b/secret-sharing/src/churp/player.rs index 6a45a1462aa..906982ca067 100644 --- a/secret-sharing/src/churp/player.rs +++ b/secret-sharing/src/churp/player.rs @@ -29,10 +29,7 @@ impl Player { bail!("not distinct shares"); } - let (xs, ys): (Vec, Vec<&F>) = shares - .iter() - .map(|s| (s.coordinate_x(), s.coordinate_y())) - .unzip(); + let (xs, ys): (Vec, Vec<&F>) = shares.iter().map(|s| (s.x(), s.y())).unzip(); let cs = lagrange::coefficients(&xs); let secret = zip(cs, ys).map(|(c, y)| c * y).sum(); @@ -54,7 +51,7 @@ impl Player { // suffice, and it doesn't require the prime field to be hashable. for i in 0..shares.len() { for j in (i + 1)..shares.len() { - if shares[i].coordinate_x() == shares[j].coordinate_x() { + if shares[i].x() == shares[j].x() { return false; } } diff --git a/secret-sharing/src/churp/shareholder.rs b/secret-sharing/src/churp/shareholder.rs index 2782aff4e49..4c422930165 100644 --- a/secret-sharing/src/churp/shareholder.rs +++ b/secret-sharing/src/churp/shareholder.rs @@ -1,6 +1,6 @@ //! CHURP shareholder. -use std::ops::AddAssign; +use std::ops::{AddAssign, Deref}; use anyhow::Result; use group::{ @@ -50,7 +50,7 @@ where /// Computes switch point for the given shareholder. pub fn switch_point(&self, x: &G::Scalar) -> G::Scalar { - self.verifiable_share.share.p.eval(x) + self.verifiable_share.p.eval(x) } /// Creates a new shareholder with a proactivized secret polynomial. @@ -59,7 +59,7 @@ where p: &Polynomial, vm: &VerificationMatrix, ) -> Result> { - if p.size() != self.verifiable_share.share.p.size() { + if p.size() != self.verifiable_share.p.size() { return Err(Error::PolynomialDegreeMismatch.into()); } if !vm.is_zero_hole() { @@ -69,8 +69,8 @@ where return Err(Error::VerificationMatrixDimensionMismatch.into()); } - let x = self.verifiable_share.share.x; - let p = p + &self.verifiable_share.share.p; + let x = self.verifiable_share.x; + let p = p + &self.verifiable_share.p; let vm = vm + &self.verifiable_share.vm; let share = SecretShare::new(x, p); let verifiable_share = VerifiableSecretShare::new(share, vm); @@ -96,11 +96,11 @@ where G::Scalar: Zeroize, { fn coordinate_x(&self) -> &G::Scalar { - self.verifiable_share.share.coordinate_x() + self.verifiable_share.x() } fn coordinate_y(&self) -> &G::Scalar { - self.verifiable_share.share.coordinate_y() + self.verifiable_share.y() } } @@ -138,13 +138,13 @@ where /// Returns the x-coordinate of a point on the secret-sharing /// univariate polynomial B(x,0) or B(0,y). - pub fn coordinate_x(&self) -> &F { + pub fn x(&self) -> &F { &self.x } /// Returns the y-coordinate of a point on the secret-sharing /// univariate polynomial B(x,0) or B(0,y). - pub fn coordinate_y(&self) -> &F { + pub fn y(&self) -> &F { self.p .coefficient(0) .expect("polynomial has at least one term") @@ -268,14 +268,14 @@ where if self.share.p.size() != cols { return Err(Error::PolynomialDegreeMismatch.into()); } - if !self.vm.verify_x(&self.share.x, &self.share.p) { + if !self.vm.verify_x(&self.x, &self.p) { return Err(Error::InvalidPolynomial.into()); } } else { - if self.share.p.size() != rows { + if self.p.size() != rows { return Err(Error::PolynomialDegreeMismatch.into()); } - if !self.vm.verify_y(&self.share.x, &self.share.p) { + if !self.vm.verify_y(&self.x, &self.p) { return Err(Error::InvalidPolynomial.into()); } } @@ -292,6 +292,18 @@ where } } +impl Deref for VerifiableSecretShare +where + G: Group, + G::Scalar: Zeroize, +{ + type Target = SecretShare; + + fn deref(&self) -> &Self::Target { + &self.share + } +} + impl AddAssign for VerifiableSecretShare where G: Group, diff --git a/secret-sharing/src/churp/switch.rs b/secret-sharing/src/churp/switch.rs index d572805b1e4..6fb1beb6907 100644 --- a/secret-sharing/src/churp/switch.rs +++ b/secret-sharing/src/churp/switch.rs @@ -468,13 +468,7 @@ where let verifiable_share = VerifiableSecretShare::new(share, vm); let shareholder: Shareholder = verifiable_share.into(); - if shareholder - .verifiable_share() - .secret_share() - .polynomial() - .size() - != self.n - { + if shareholder.verifiable_share().polynomial().size() != self.n { return Err(Error::PolynomialDegreeMismatch.into()); } @@ -511,7 +505,7 @@ where shareholder: Option>>, /// The sum of the received verifiable bivariate shares. - verifiable_share: Option>, + combined_share: Option>, } impl BivariateShares @@ -542,7 +536,7 @@ where shareholders, pending_shareholders, shareholder, - verifiable_share: None, + combined_share: None, }) } @@ -572,15 +566,15 @@ where return Err(Error::DuplicateShareholder.into()); } - if verifiable_share.share.x != self.me { + if verifiable_share.x() != &self.me { return Err(Error::ShareholderIdentityMismatch.into()); } verifiable_share.verify(self.threshold, self.zero_hole, self.full_share)?; - if let Some(ref mut vs) = self.verifiable_share { - *vs += &verifiable_share; + if let Some(ref mut cs) = self.combined_share { + *cs += &verifiable_share; } else { - self.verifiable_share = Some(verifiable_share); + self.combined_share = Some(verifiable_share); } let index = self @@ -602,14 +596,16 @@ where return Err(Error::NotEnoughBivariateShares.into()); } - let vs = self - .verifiable_share + let verifiable_share = self + .combined_share .take() .ok_or(Error::ShareholderProactivizationCompleted)?; let shareholder = match &self.shareholder { - Some(shareholder) => shareholder.proactivize(&vs.share.p, &vs.vm)?, - None => vs.into(), + Some(shareholder) => { + shareholder.proactivize(&verifiable_share.p, &verifiable_share.vm)? + } + None => verifiable_share.into(), }; // Ensure that the combined bivariate polynomial satisfies From 0191ae3113fdf53592c5e2ba9d4daa73f57529cc Mon Sep 17 00:00:00 2001 From: Peter Nose Date: Tue, 5 Nov 2024 12:49:00 +0100 Subject: [PATCH 16/21] ZeroizePolyFromLagrangie on error --- secret-sharing/src/churp/shareholder.rs | 3 +-- secret-sharing/src/churp/switch.rs | 13 +++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/secret-sharing/src/churp/shareholder.rs b/secret-sharing/src/churp/shareholder.rs index 4c422930165..c912710d303 100644 --- a/secret-sharing/src/churp/shareholder.rs +++ b/secret-sharing/src/churp/shareholder.rs @@ -74,9 +74,8 @@ where let vm = vm + &self.verifiable_share.vm; let share = SecretShare::new(x, p); let verifiable_share = VerifiableSecretShare::new(share, vm); - let shareholder = verifiable_share.into(); - Ok(shareholder) + Ok(verifiable_share.into()) } } diff --git a/secret-sharing/src/churp/switch.rs b/secret-sharing/src/churp/switch.rs index 6fb1beb6907..6e4ab9911fe 100644 --- a/secret-sharing/src/churp/switch.rs +++ b/secret-sharing/src/churp/switch.rs @@ -460,19 +460,20 @@ where return Err(Error::NotEnoughSwitchPoints.into()); } - let points: Vec<_> = self.points[0..self.n].iter().map(|p| &p.0).collect(); - let p = lagrange(&points); let x = self.me.take().ok_or(Error::ShareholderIdentityRequired)?; let vm = self.vm.take().ok_or(Error::VerificationMatrixRequired)?; - let share: SecretShare<::Scalar> = SecretShare::new(x, p); + let points: Vec<_> = self.points[0..self.n].iter().map(|p| &p.0).collect(); + let p = lagrange(&points); + let share = SecretShare::new(x, p); let verifiable_share = VerifiableSecretShare::new(share, vm); - let shareholder: Shareholder = verifiable_share.into(); - if shareholder.verifiable_share().polynomial().size() != self.n { + // Intentionally verifying the size of the polynomial at the end + // to ensure that it is zeroized in case of an error. + if verifiable_share.polynomial().size() != self.n { return Err(Error::PolynomialDegreeMismatch.into()); } - Ok(shareholder) + Ok(verifiable_share.into()) } } From 6c812b449d46e6cce232d44ebd562bb8c076e2e8 Mon Sep 17 00:00:00 2001 From: Peter Nose Date: Tue, 5 Nov 2024 13:15:57 +0100 Subject: [PATCH 17/21] secret-sharing/src/churp/player: Zeroize intermediate values --- secret-sharing/src/churp/player.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/secret-sharing/src/churp/player.rs b/secret-sharing/src/churp/player.rs index 906982ca067..7872df24cfa 100644 --- a/secret-sharing/src/churp/player.rs +++ b/secret-sharing/src/churp/player.rs @@ -1,5 +1,3 @@ -use std::iter::zip; - use anyhow::{bail, Result}; use group::ff::PrimeField; use zeroize::Zeroize; @@ -29,9 +27,14 @@ impl Player { bail!("not distinct shares"); } - let (xs, ys): (Vec, Vec<&F>) = shares.iter().map(|s| (s.x(), s.y())).unzip(); + let xs = shares.iter().map(|s| *s.x()).collect::>(); let cs = lagrange::coefficients(&xs); - let secret = zip(cs, ys).map(|(c, y)| c * y).sum(); + let mut secret = F::ZERO; + for (mut ci, share) in cs.into_iter().zip(shares) { + ci *= share.y(); + secret += &ci; + ci.zeroize(); + } Ok(secret) } From 2e0805f5426c32541233fe8165a8c32044d8b051 Mon Sep 17 00:00:00 2001 From: Peter Nose Date: Tue, 5 Nov 2024 13:32:46 +0100 Subject: [PATCH 18/21] secret-sharing/src/kdc: Zeroize intermediate values --- secret-sharing/src/kdc/mod.rs | 19 ++++++++++++++----- secret-sharing/src/suites/mod.rs | 3 ++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/secret-sharing/src/kdc/mod.rs b/secret-sharing/src/kdc/mod.rs index adeac3e4095..55e647d1e8d 100644 --- a/secret-sharing/src/kdc/mod.rs +++ b/secret-sharing/src/kdc/mod.rs @@ -1,9 +1,8 @@ //! Key derivation center. -use std::iter::zip; - use anyhow::{bail, Result}; use group::{ff::PrimeField, Group}; +use zeroize::Zeroize; use crate::{ poly::{lagrange, EncryptedPoint}, @@ -57,7 +56,10 @@ pub trait KeyRecoverer { fn min_shares(&self) -> usize; /// Recovers the secret key from the provided key shares. - fn recover_key(&self, shares: &[EncryptedPoint]) -> Result { + fn recover_key(&self, shares: &[EncryptedPoint]) -> Result + where + G: Group + Zeroize, + { if shares.len() < self.min_shares() { bail!("not enough shares"); } @@ -65,9 +67,16 @@ pub trait KeyRecoverer { bail!("not distinct shares"); } - let (xs, zs): (Vec<_>, Vec<_>) = shares.iter().map(|p| (p.x, p.z)).unzip(); + let xs = shares.iter().map(|s| *s.x()).collect::>(); let cs = lagrange::coefficients(&xs); - let key = zip(cs, zs).map(|(c, z)| z * c).sum(); + let mut key = G::identity(); + + for (ci, share) in cs.into_iter().zip(shares) { + let mut zi = *share.z(); + zi *= ci; + key += &zi; + zi.zeroize(); + } Ok(key) } diff --git a/secret-sharing/src/suites/mod.rs b/secret-sharing/src/suites/mod.rs index f8689289d18..d8e8f28cc6d 100644 --- a/secret-sharing/src/suites/mod.rs +++ b/secret-sharing/src/suites/mod.rs @@ -34,13 +34,14 @@ pub trait Suite: type PrimeField: PrimeField + Zeroize; /// The type representing an element of a cryptographic group. - type Group: Group + GroupEncoding; + type Group: Group + GroupEncoding + Zeroize; } impl Suite for S where S: FieldDigest + GroupDigest, ::Output: Group::Output> + GroupEncoding, + ::Output: Zeroize, ::Output: Zeroize, { type PrimeField = ::Output; From ec0556b3a329aed9f835fb1115663278af61ada3 Mon Sep 17 00:00:00 2001 From: Peter Nose Date: Tue, 5 Nov 2024 13:46:48 +0100 Subject: [PATCH 19/21] secret-sharing/src/churp/dealer: Zeroize bivariate polynomial on drop --- keymanager/src/churp/handler.rs | 7 ++++++- secret-sharing/src/churp/dealer.rs | 17 ++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/keymanager/src/churp/handler.rs b/keymanager/src/churp/handler.rs index ed3235bd1bf..4f69f95c42d 100644 --- a/keymanager/src/churp/handler.rs +++ b/keymanager/src/churp/handler.rs @@ -42,6 +42,7 @@ use secret_sharing::{ suites::{p384, Suite}, vss::VerificationMatrix, }; +use zeroize::Zeroize; use crate::{ beacon::State as BeaconState, @@ -105,7 +106,11 @@ const CHURP_CONTEXT_SEPARATOR: &[u8] = b" for churp "; const ALLOWED_BLOCKS_BEHIND: u64 = 5; /// Represents information about a dealer. -struct DealerInfo { +struct DealerInfo +where + G: Group, + G::Scalar: Zeroize, +{ /// The epoch during which this dealer is active. epoch: EpochTime, /// The dealer associated with this information. diff --git a/secret-sharing/src/churp/dealer.rs b/secret-sharing/src/churp/dealer.rs index ffb0281d60c..371db457ac0 100644 --- a/secret-sharing/src/churp/dealer.rs +++ b/secret-sharing/src/churp/dealer.rs @@ -16,7 +16,11 @@ use super::{Error, HandoffKind, SecretShare}; /// Shares must always be distributed over a secure channel and verified /// against the matrix. Recovering the secret bivariate polynomial requires /// obtaining more than a threshold number of shares from distinct participants. -pub struct Dealer { +pub struct Dealer +where + G: Group, + G::Scalar: Zeroize, +{ /// Secret bivariate polynomial. bp: BivariatePolynomial, @@ -164,6 +168,7 @@ where impl From> for Dealer where G: Group, + G::Scalar: Zeroize, { /// Creates a new dealer from the given bivariate polynomial. fn from(bp: BivariatePolynomial) -> Self { @@ -172,6 +177,16 @@ where } } +impl Drop for Dealer +where + G: Group, + G::Scalar: Zeroize, +{ + fn drop(&mut self) { + self.bp.zeroize(); + } +} + #[cfg(test)] mod tests { use rand::{rngs::StdRng, Error, RngCore, SeedableRng}; From d98b3184077ad7b63413cb2e3535e5e7234e1bb0 Mon Sep 17 00:00:00 2001 From: Peter Nose Date: Tue, 5 Nov 2024 14:00:37 +0100 Subject: [PATCH 20/21] keymanager/src/churp: Zeroize sensitive data --- keymanager/src/churp/handler.rs | 24 ++++++---- keymanager/src/churp/storage.rs | 84 ++++++++++++++++++++++++++++----- keymanager/src/churp/types.rs | 22 +++++++-- 3 files changed, 103 insertions(+), 27 deletions(-) diff --git a/keymanager/src/churp/handler.rs b/keymanager/src/churp/handler.rs index 4f69f95c42d..b7c6beaa054 100644 --- a/keymanager/src/churp/handler.rs +++ b/keymanager/src/churp/handler.rs @@ -644,13 +644,15 @@ impl Instance { handoff.set_verification_matrix(vm)?; } - let y = block_on(client.churp_share_reduction_point( + let mut bytes = block_on(client.churp_share_reduction_point( self.churp_id, status.next_handoff, self.node_id, vec![node_id], ))?; - let y = scalar_from_bytes(&y).ok_or(Error::PointDecodingFailed)?; + let maybe_y = scalar_from_bytes(&bytes); + bytes.zeroize(); + let y = maybe_y.ok_or(Error::PointDecodingFailed)?; let point = SwitchPoint::new(x, y); handoff.add_share_reduction_switch_point(point) @@ -680,13 +682,15 @@ impl Instance { } // Fetch from the remote node. - let bytes = block_on(client.churp_share_distribution_point( + let mut bytes = block_on(client.churp_share_distribution_point( self.churp_id, status.next_handoff, self.node_id, vec![node_id], ))?; - let y = scalar_from_bytes(&bytes).ok_or(Error::PointDecodingFailed)?; + let maybe_y = scalar_from_bytes(&bytes); + bytes.zeroize(); + let y = maybe_y.ok_or(Error::PointDecodingFailed)?; let point = SwitchPoint::new(x, y); handoff.add_full_share_distribution_switch_point(point) @@ -737,7 +741,7 @@ impl Instance { return Err(Error::InvalidVerificationMatrixChecksum.into()); } - let verifiable_share: VerifiableSecretShare = share.try_into()?; + let verifiable_share: VerifiableSecretShare = (&share).try_into()?; handoff.add_bivariate_share(&x, verifiable_share) } @@ -787,7 +791,7 @@ impl Instance { .load_next_secret_share(self.churp_id, epoch) .or_else(|err| ignore_error(err, Error::InvalidSecretShare))?; // Ignore previous shares. - // // Back up the secret share, if it is valid. + // Back up the secret share, if it is valid. if let Some(share) = share.as_ref() { self.storage .store_secret_share(share, self.churp_id, epoch)?; @@ -1246,8 +1250,9 @@ impl Handler for Instance { let x = encode_shareholder::(&node_id.0, &self.shareholder_dst)?; let shareholder = self.get_shareholder(status.handoff)?; - let y = shareholder.switch_point(&x); + let mut y = shareholder.switch_point(&x); let bytes = scalar_to_bytes(&y); + y.zeroize(); Ok(bytes) } @@ -1278,8 +1283,9 @@ impl Handler for Instance { let x = encode_shareholder::(&node_id.0, &self.shareholder_dst)?; let handoff = self.get_handoff(status.next_handoff)?; let shareholder = handoff.get_reduced_shareholder()?; - let point = shareholder.switch_point(&x); - let point = scalar_to_bytes(&point); + let mut y = shareholder.switch_point(&x); + let point = scalar_to_bytes(&y); + y.zeroize(); Ok(point) } diff --git a/keymanager/src/churp/storage.rs b/keymanager/src/churp/storage.rs index 99924111fed..4b51c666360 100644 --- a/keymanager/src/churp/storage.rs +++ b/keymanager/src/churp/storage.rs @@ -8,7 +8,10 @@ use sgx_isa::Keypolicy; use oasis_core_runtime::{ common::{ - crypto::mrae::nonce::{Nonce, NONCE_SIZE}, + crypto::mrae::{ + deoxysii::TAG_SIZE, + nonce::{Nonce, NONCE_SIZE}, + }, sgx::seal::new_deoxysii, }, consensus::beacon::EpochTime, @@ -155,17 +158,33 @@ impl Storage { /// Encrypts and authenticates the given bivariate polynomial /// using the provided ID and handoff epoch as additional data. + #[allow(clippy::uninit_vec)] fn encrypt_bivariate_polynomial( polynomial: &BivariatePolynomial, churp_id: u8, epoch: EpochTime, ) -> Vec { + // Prepare data for encryption. let nonce = Nonce::generate(); - let plaintext = polynomial.to_bytes(); + let mut plaintext = polynomial.to_bytes(); let additional_data = Self::pack_churp_id_epoch(churp_id, epoch); + + // Encrypt data using `seal_into` so that we can zeroize the plaintext. + // The unsafe ciphertext buffer initialization was taken from the `seal` + // method to speed up encryption. + let mut ciphertext = Vec::with_capacity(plaintext.len() + TAG_SIZE + NONCE_SIZE); + unsafe { ciphertext.set_len(plaintext.len() + TAG_SIZE) } + let d2 = new_deoxysii(Keypolicy::MRENCLAVE, BIVARIATE_POLYNOMIAL_SEAL_CONTEXT); - let mut ciphertext = d2.seal(&nonce, plaintext, additional_data); + d2.seal_into(&nonce, &plaintext, &additional_data, &mut ciphertext) + .unwrap(); + + // Zeroize sensitive data. + plaintext.zeroize(); + + // Append nonce to the ciphertext. ciphertext.extend_from_slice(&nonce.to_vec()); + ciphertext } @@ -176,19 +195,28 @@ impl Storage { churp_id: u8, epoch: EpochTime, ) -> Result> { + // Prepare data for decryption. let (ciphertext, nonce) = Self::unpack_ciphertext_with_nonce(ciphertext)?; let additional_data = Self::pack_churp_id_epoch(churp_id, epoch); + + // Decrypt data. let d2 = new_deoxysii(Keypolicy::MRENCLAVE, BIVARIATE_POLYNOMIAL_SEAL_CONTEXT); - let plaintext = d2 + let mut plaintext = d2 .open(nonce, ciphertext, additional_data) .map_err(|_| Error::InvalidBivariatePolynomial)?; - BivariatePolynomial::from_bytes(&plaintext) - .ok_or(Error::BivariatePolynomialDecodingFailed.into()) + // Decode bivariate polynomial. + let maybe_bp = BivariatePolynomial::from_bytes(&plaintext); + + // Zeroize sensitive data on failure. + plaintext.zeroize(); + + maybe_bp.ok_or(Error::BivariatePolynomialDecodingFailed.into()) } /// Encrypts and authenticates the given polynomial and verification matrix /// using the provided ID and handoff as additional data. + #[allow(clippy::uninit_vec)] fn encrypt_secret_share( verifiable_share: &VerifiableSecretShare, churp_id: u8, @@ -198,13 +226,28 @@ impl Storage { G: Group + GroupEncoding, G::Scalar: Zeroize, { + // Prepare data for encryption. let share: EncodedVerifiableSecretShare = verifiable_share.into(); let nonce: Nonce = Nonce::generate(); - let plaintext = cbor::to_vec(share); + let mut plaintext = cbor::to_vec(share); let additional_data = Self::pack_churp_id_epoch(churp_id, epoch); + + // Encrypt data using `seal_into` so that we can zeroize the plaintext. + // The unsafe ciphertext buffer initialization was taken from the `seal` + // method to speed up encryption. + let mut ciphertext = Vec::with_capacity(plaintext.len() + TAG_SIZE + NONCE_SIZE); + unsafe { ciphertext.set_len(plaintext.len() + TAG_SIZE) } + let d2 = new_deoxysii(Keypolicy::MRENCLAVE, SECRET_SHARE_SEAL_CONTEXT); - let mut ciphertext = d2.seal(&nonce, plaintext, additional_data); + d2.seal_into(&nonce, &plaintext, &additional_data, &mut ciphertext) + .unwrap(); + + // Zeroize sensitive data. + plaintext.zeroize(); + + // Append nonce to the ciphertext. ciphertext.extend_from_slice(&nonce.to_vec()); + ciphertext } @@ -219,18 +262,33 @@ impl Storage { G: Group + GroupEncoding, G::Scalar: Zeroize, { + // Prepare data for decryption. let (ciphertext, nonce) = Self::unpack_ciphertext_with_nonce(ciphertext)?; let additional_data = Self::pack_churp_id_epoch(churp_id, epoch); + + // Decrypt data. let d2 = new_deoxysii(Keypolicy::MRENCLAVE, SECRET_SHARE_SEAL_CONTEXT); - let plaintext = d2 + let mut plaintext = d2 .open(nonce, ciphertext, additional_data) .map_err(|_| Error::InvalidSecretShare)?; - let encoded: EncodedVerifiableSecretShare = - cbor::from_slice(&plaintext).map_err(|_| Error::InvalidSecretShare)?; - let verifiable_share = encoded.try_into()?; + // Decode encoded share. + let maybe_encoded_share: Result = + cbor::from_slice(&plaintext).map_err(|_| Error::InvalidSecretShare.into()); + + // Zeroize sensitive data on failure. + plaintext.zeroize(); + + // Decode verifiable share. + let mut encoded_share = maybe_encoded_share?; + let maybe_verifiable_share = (&encoded_share) + .try_into() + .map_err(|_| Error::InvalidSecretShare.into()); + + // Zeroize sensitive data on failure. + encoded_share.zeroize(); - Ok(verifiable_share) + maybe_verifiable_share } /// Creates storage key for the bivariate polynomial. diff --git a/keymanager/src/churp/types.rs b/keymanager/src/churp/types.rs index 9c86104ab90..166907aefed 100644 --- a/keymanager/src/churp/types.rs +++ b/keymanager/src/churp/types.rs @@ -167,6 +167,12 @@ pub struct EncodedVerifiableSecretShare { pub verification_matrix: Vec, } +impl Zeroize for EncodedVerifiableSecretShare { + fn zeroize(&mut self) { + self.share.zeroize(); + } +} + impl From<&VerifiableSecretShare> for EncodedVerifiableSecretShare where G: Group + GroupEncoding, @@ -180,15 +186,15 @@ where } } -impl TryFrom for VerifiableSecretShare +impl TryFrom<&EncodedVerifiableSecretShare> for VerifiableSecretShare where G: Group + GroupEncoding, G::Scalar: Zeroize, { type Error = Error; - fn try_from(encoded: EncodedVerifiableSecretShare) -> Result { - let share = encoded.share.try_into()?; + fn try_from(encoded: &EncodedVerifiableSecretShare) -> Result { + let share = (&encoded.share).try_into()?; let vm = VerificationMatrix::from_bytes(&encoded.verification_matrix) .ok_or(Error::VerificationMatrixDecodingFailed)?; let verifiable_share = VerifiableSecretShare::new(share, vm); @@ -206,6 +212,12 @@ pub struct EncodedSecretShare { pub polynomial: Vec, } +impl Zeroize for EncodedSecretShare { + fn zeroize(&mut self) { + self.polynomial.zeroize(); + } +} + impl From<&SecretShare> for EncodedSecretShare where F: PrimeField + Zeroize, @@ -218,13 +230,13 @@ where } } -impl TryFrom for SecretShare +impl TryFrom<&EncodedSecretShare> for SecretShare where F: PrimeField + Zeroize, { type Error = Error; - fn try_from(encoded: EncodedSecretShare) -> Result { + fn try_from(encoded: &EncodedSecretShare) -> Result { let x = scalar_from_bytes(&encoded.x).ok_or(Error::IdentityDecodingFailed)?; let p = Polynomial::from_bytes(&encoded.polynomial).ok_or(Error::PolynomialDecodingFailed)?; From 0d475b1d00a7d67b68e6a1502fdb3363f5932b95 Mon Sep 17 00:00:00 2001 From: Peter Nose Date: Mon, 4 Nov 2024 09:38:34 +0100 Subject: [PATCH 21/21] secret-sharing/src/poly: Restrict add/sub/mul assign std ops The AddAssign, SubAssign, and MulAssign functions can now be used only when the prime field supports zeroization. This ensures that any leftover data from heap reallocation, when the right-hand-side polynomial has more coefficients than the left-hand-side, is zeroized. An alternative solution is to remove these functions, but this could lead to performance drawbacks. --- .changelog/5928.trivial.md | 0 secret-sharing/src/poly/univariate.rs | 26 ++++++++++++++++++++------ 2 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 .changelog/5928.trivial.md diff --git a/.changelog/5928.trivial.md b/.changelog/5928.trivial.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/secret-sharing/src/poly/univariate.rs b/secret-sharing/src/poly/univariate.rs index 307caa7499e..7bd5ab3bab1 100644 --- a/secret-sharing/src/poly/univariate.rs +++ b/secret-sharing/src/poly/univariate.rs @@ -238,7 +238,7 @@ where impl AddAssign for Polynomial where - F: PrimeField, + F: PrimeField + Zeroize, { #[inline] fn add_assign(&mut self, rhs: Polynomial) { @@ -248,9 +248,16 @@ where impl AddAssign<&Polynomial> for Polynomial where - F: PrimeField, + F: PrimeField + Zeroize, { fn add_assign(&mut self, rhs: &Polynomial) { + if self.a.capacity() < rhs.a.len() { + let mut a = Vec::with_capacity(rhs.a.len()); + a.extend_from_slice(&self.a); + self.a.zeroize(); + self.a = a; + } + let min_len = min(self.a.len(), rhs.a.len()); for i in 0..min_len { @@ -321,7 +328,7 @@ where impl SubAssign for Polynomial where - F: PrimeField, + F: PrimeField + Zeroize, { #[inline] fn sub_assign(&mut self, rhs: Polynomial) { @@ -331,9 +338,16 @@ where impl SubAssign<&Polynomial> for Polynomial where - F: PrimeField, + F: PrimeField + Zeroize, { fn sub_assign(&mut self, rhs: &Polynomial) { + if self.a.capacity() < rhs.a.len() { + let mut a = Vec::with_capacity(rhs.a.len()); + a.extend_from_slice(&self.a); + self.a.zeroize(); + self.a = a; + } + let min_len = min(self.a.len(), rhs.a.len()); for i in 0..min_len { @@ -510,7 +524,7 @@ where impl Sum for Polynomial where - F: PrimeField, + F: PrimeField + Zeroize, { fn sum>>(iter: I) -> Polynomial { let mut sum = Polynomial::zero(0); @@ -521,7 +535,7 @@ where impl<'a, F> Sum<&'a Polynomial> for Polynomial where - F: PrimeField, + F: PrimeField + Zeroize, { fn sum>>(iter: I) -> Polynomial { let mut sum = Polynomial::zero(0);