diff --git a/crypto/src/batch_transcript.rs b/crypto/src/batch_transcript.rs index 93acb58..5f2842b 100644 --- a/crypto/src/batch_transcript.rs +++ b/crypto/src/batch_transcript.rs @@ -1,6 +1,6 @@ use crate::{ signature::{identity::Identity, ContributionTypedData, EcdsaSignature}, - BatchContribution, CeremoniesError, Engine, Transcript, + BatchContribution, CeremoniesError, CeremonyError, Engine, Transcript, }; use rayon::prelude::*; use serde::{Deserialize, Serialize}; @@ -10,6 +10,7 @@ use tracing::instrument; #[serde(deny_unknown_fields, rename_all = "camelCase")] pub struct BatchTranscript { pub transcripts: Vec, + // TODO: Turn into one vector of structs pub participant_ids: Vec, pub participant_ecdsa_signatures: Vec, } @@ -35,6 +36,32 @@ impl BatchTranscript { self.participant_ids.len() - 1 } + /// Verify that the batch transcript is valid. + pub fn validate(&self) -> Result<(), CeremoniesError> { + if self.participant_ids.len() != self.participant_ecdsa_signatures.len() { + return Err(CeremoniesError::InconsistentNumParticipants( + self.participant_ids.len(), + self.participant_ecdsa_signatures.len(), + )); + } + for (i, transcript) in self.transcripts.iter().enumerate() { + transcript + .validate() + .map_err(|e| CeremoniesError::InvalidCeremony(i, e))?; + if transcript.num_participants() != self.num_participants() { + return Err(CeremoniesError::InvalidCeremony( + i, + CeremonyError::UnexpectedNumParticipants( + self.num_participants(), + transcript.num_participants(), + ), + )); + } + } + // TODO: Verify signatures + Ok(()) + } + /// Creates the start of a new batch contribution. #[must_use] pub fn contribution(&self) -> BatchContribution { diff --git a/crypto/src/error.rs b/crypto/src/error.rs index e293fe1..829ba00 100644 --- a/crypto/src/error.rs +++ b/crypto/src/error.rs @@ -9,6 +9,8 @@ pub trait ErrorCode { pub enum CeremoniesError { #[error("Unexpected number of contributions: expected {0}, got {1}")] UnexpectedNumContributions(usize, usize), + #[error("Inconsistent number of participants: {0} identities and {1} signatures")] + InconsistentNumParticipants(usize, usize), #[error("Error in contribution {0}: {1}")] InvalidCeremony(usize, #[source] CeremonyError), } @@ -25,6 +27,8 @@ impl ErrorCode for CeremoniesError { #[derive(Clone, Copy, PartialEq, Eq, Debug, Error, IntoStaticStr)] pub enum CeremonyError { + #[error("Unexpected number of participants: expected {0}, got {1}")] + UnexpectedNumParticipants(usize, usize), #[error("Unsupported number of G1 powers: {0}")] UnsupportedNumG1Powers(usize), #[error("Unsupported number of G2 powers: {0}")] @@ -80,7 +84,9 @@ pub enum CeremonyError { #[error("Contribution contains no entropy: pubkey equals generator")] ContributionNoEntropy, #[error("Mismatch in witness length: {0} products and {1} pubkeys")] - WitnessLengthMismatch(usize, usize), + WitnessPubkeyLengthMismatch(usize, usize), + #[error("Mismatch in witness length: {0} products and {1} signatures")] + WitnessSignatureLengthMismatch(usize, usize), } impl ErrorCode for CeremonyError { diff --git a/crypto/src/transcript.rs b/crypto/src/transcript.rs index fa3b2c7..7f42e40 100644 --- a/crypto/src/transcript.rs +++ b/crypto/src/transcript.rs @@ -57,6 +57,25 @@ impl Transcript { self.num_participants() > 0 } + /// Verify that the transcript is valid. + pub fn validate(&self) -> Result<(), CeremonyError> { + if self.witness.products.len() != self.witness.pubkeys.len() { + return Err(CeremonyError::WitnessPubkeyLengthMismatch( + self.witness.products.len(), + self.witness.pubkeys.len(), + )); + } + if self.witness.products.len() != self.witness.signatures.len() { + return Err(CeremonyError::WitnessSignatureLengthMismatch( + self.witness.products.len(), + self.witness.signatures.len(), + )); + } + // TODO: Verify pairing checks. + // TODO: Verify signature. + Ok(()) + } + /// Creates the start of a new contribution. #[must_use] pub fn contribution(&self) -> Contribution { diff --git a/src/io.rs b/src/io.rs index 053791f..9180c08 100644 --- a/src/io.rs +++ b/src/io.rs @@ -97,6 +97,7 @@ pub async fn read_or_create_transcript( info!(?path, "Opening transcript file"); let transcript = read_json_file::(path).await; ceremony_sizes.validate_batch_transcript(&transcript)?; + transcript.validate()?; Ok(Arc::new(RwLock::new(transcript))) } else { warn!(?path, "No transcript found, creating new transcript file");