Skip to content

Commit

Permalink
added VerifiedDoc type
Browse files Browse the repository at this point in the history
  • Loading branch information
themighty1 committed Jan 30, 2023
1 parent 3094a49 commit 785d267
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 119 deletions.
20 changes: 10 additions & 10 deletions verifier/src/checks.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/// Methods performing various validation checks on the [crate::verifier_doc::VerifierDocUnchecked]
use super::verifier_doc::VerifierDocUnchecked;
use super::doc::UncheckedDoc;
use super::{commitment::Range, Error};

pub fn perform_checks(unchecked: &VerifierDocUnchecked) -> Result<(), Error> {
pub fn perform_checks(unchecked: &UncheckedDoc) -> Result<(), Error> {
// Performs the following validation checks:
//
// - at least one commitment is present
Expand Down Expand Up @@ -35,7 +35,7 @@ pub fn perform_checks(unchecked: &VerifierDocUnchecked) -> Result<(), Error> {
}

/// Condition checked: at least one commitment is present
fn check_at_least_one_commitment_present(unchecked: &VerifierDocUnchecked) -> Result<(), Error> {
fn check_at_least_one_commitment_present(unchecked: &UncheckedDoc) -> Result<(), Error> {
if unchecked.commitments().is_empty() {
return Err(Error::SanityCheckError(
"check_at_least_one_commitment_present".to_string(),
Expand All @@ -45,7 +45,7 @@ fn check_at_least_one_commitment_present(unchecked: &VerifierDocUnchecked) -> Re
}

/// Condition checked: commitments and openings have their ids incremental and ascending
fn check_commitment_and_opening_ids(unchecked: &VerifierDocUnchecked) -> Result<(), Error> {
fn check_commitment_and_opening_ids(unchecked: &UncheckedDoc) -> Result<(), Error> {
for i in 0..unchecked.commitments().len() {
if !(unchecked.commitments()[i].id() == i && unchecked.commitment_openings()[i].id() == i) {
return Err(Error::SanityCheckError(
Expand All @@ -57,7 +57,7 @@ fn check_commitment_and_opening_ids(unchecked: &VerifierDocUnchecked) -> Result<
}

/// Condition checked: commitment count equals opening count
fn check_commitment_and_opening_count_equal(unchecked: &VerifierDocUnchecked) -> Result<(), Error> {
fn check_commitment_and_opening_count_equal(unchecked: &UncheckedDoc) -> Result<(), Error> {
if unchecked.commitments().len() != unchecked.commitment_openings().len() {
return Err(Error::SanityCheckError(
"check_commitment_and_opening_count_equal".to_string(),
Expand All @@ -67,7 +67,7 @@ fn check_commitment_and_opening_count_equal(unchecked: &VerifierDocUnchecked) ->
}

/// Condition checked: ranges inside one commitment are non-empty, valid, ascending, non-overlapping, non-overflowing
fn check_ranges_inside_each_commitment(unchecked: &VerifierDocUnchecked) -> Result<(), Error> {
fn check_ranges_inside_each_commitment(unchecked: &UncheckedDoc) -> Result<(), Error> {
for c in unchecked.commitments() {
let len = c.ranges().len();
// at least one range is expected
Expand Down Expand Up @@ -110,7 +110,7 @@ fn check_ranges_inside_each_commitment(unchecked: &VerifierDocUnchecked) -> Resu
/// corresponding commitment
/// Condition checked: the total amount of committed data is less than 1GB to prevent DoS
/// (this will cause the verifier to hash up to a max of 1GB * 128 = 128GB of labels)
fn check_commitment_sizes(unchecked: &VerifierDocUnchecked) -> Result<(), Error> {
fn check_commitment_sizes(unchecked: &UncheckedDoc) -> Result<(), Error> {
let mut total_committed = 0usize;

for i in 0..unchecked.commitment_openings().len() {
Expand All @@ -137,7 +137,7 @@ fn check_commitment_sizes(unchecked: &VerifierDocUnchecked) -> Result<(), Error>
/// Condition checked: the amount of commitments is less that 1000
/// (searching for overlapping commitments in the naive way which we implemeted has quadratic cost,
/// hence this number shouldn't be too high to prevent DoS)
fn check_commitment_count(unchecked: &VerifierDocUnchecked) -> Result<(), Error> {
fn check_commitment_count(unchecked: &UncheckedDoc) -> Result<(), Error> {
if unchecked.commitments().len() >= 1000 {
return Err(Error::SanityCheckError(
"check_commitment_count".to_string(),
Expand All @@ -147,7 +147,7 @@ fn check_commitment_count(unchecked: &VerifierDocUnchecked) -> Result<(), Error>
}

/// Condition checked: each Merkle tree index is both unique and also ascending between commitments
fn check_merkle_tree_indices(unchecked: &VerifierDocUnchecked) -> Result<(), Error> {
fn check_merkle_tree_indices(unchecked: &UncheckedDoc) -> Result<(), Error> {
let indices: Vec<usize> = unchecked
.commitments()
.iter()
Expand All @@ -166,7 +166,7 @@ fn check_merkle_tree_indices(unchecked: &VerifierDocUnchecked) -> Result<(), Err
/// Makes sure that if two or more commitments contain overlapping ranges, the openings
/// corresponding to those ranges match exactly. Otherwise, if the openings don't match,
/// returns an error.
fn check_overlapping_openings(unchecked: &VerifierDocUnchecked) -> Result<(), Error> {
fn check_overlapping_openings(unchecked: &UncheckedDoc) -> Result<(), Error> {
// Note: using an existing lib to find multi-range overlap would incur the need to audit
// that lib for correctness. Instead, since checking two range overlap is cheap, we are using
// a naive way where we compare each range to all other ranges.
Expand Down
142 changes: 76 additions & 66 deletions verifier/src/verifier_doc.rs → verifier/src/doc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ use serde::{ser::Serializer, Serialize};
use std::{any::Any, collections::HashMap};

#[derive(Serialize)]
/// A validated notarization document received from the User
pub struct VerifierDoc {
/// A validated and verified notarization document
pub struct VerifiedDoc {
version: u8,
tls_doc: TLSDoc,
/// Notary's signature over the [Signed] portion of this doc
Expand Down Expand Up @@ -44,10 +44,48 @@ pub struct VerifierDoc {
commitment_openings: Vec<CommitmentOpening>,
}

impl VerifierDoc {
/// Creates a new document. This method is called only by the User.
/// [VerifierDoc] is never passed directly to the Verifier. Instead, the User must convert
/// it into [VerifierDocUnchecked]
impl VerifiedDoc {
/// Creates a new [VerifiedDoc] from [ValidatedDoc]
pub(crate) fn from_validated(validated: ValidatedDoc) -> Self {
Self {
version: validated.version,
tls_doc: validated.tls_doc,
signature: validated.signature,
label_seed: validated.label_seed,
merkle_root: validated.merkle_root,
merkle_tree_leaf_count: validated.merkle_tree_leaf_count,
merkle_multi_proof: validated.merkle_multi_proof,
commitments: validated.commitments,
commitment_openings: validated.commitment_openings,
}
}

pub fn commitments(&self) -> &Vec<Commitment> {
&self.commitments
}

pub fn commitment_openings(&self) -> &Vec<CommitmentOpening> {
&self.commitment_openings
}
}

/// Notarization document in its unchecked form. This is the form in which the document is received
/// by the Verifier from the User.
pub struct UncheckedDoc {
/// All fields are exactly as in [VerifiedDoc]
version: u8,
tls_doc: TLSDoc,
signature: Option<Vec<u8>>,
label_seed: LabelSeed,
merkle_root: [u8; 32],
merkle_tree_leaf_count: usize,
merkle_multi_proof: MerkleProof<algorithms::Sha256>,
commitments: Vec<Commitment>,
commitment_openings: Vec<CommitmentOpening>,
}

impl UncheckedDoc {
/// Creates a new unchecked document. This method is called only by the User.
pub fn new(
version: u8,
tls_doc: TLSDoc,
Expand All @@ -72,13 +110,36 @@ impl VerifierDoc {
}
}

/// Returns a new [VerifierDoc] after performing all validation checks. This is the only way
/// for the Verifier (who was NOT acting as the Notary) to derive [VerifierDoc].
pub fn from_unchecked(unchecked: VerifierDocUnchecked) -> Result<Self, Error> {
pub fn commitments(&self) -> &Vec<Commitment> {
&self.commitments
}

pub fn commitment_openings(&self) -> &Vec<CommitmentOpening> {
&self.commitment_openings
}
}

// Notarization document in its validated form (not yet verified)
pub(crate) struct ValidatedDoc {
/// All fields are exactly as in [VerifiedDoc]
version: u8,
tls_doc: TLSDoc,
signature: Option<Vec<u8>>,
label_seed: LabelSeed,
merkle_root: [u8; 32],
merkle_tree_leaf_count: usize,
merkle_multi_proof: MerkleProof<algorithms::Sha256>,
commitments: Vec<Commitment>,
commitment_openings: Vec<CommitmentOpening>,
}

impl ValidatedDoc {
/// Returns a new [ValidatedDoc] after performing all validation checks
pub(crate) fn from_unchecked(unchecked: UncheckedDoc) -> Result<Self, Error> {
checks::perform_checks(&unchecked)?;

// Make sure the Notary's signature is present.
// (If the Verifier IS also the Notary then the signature is NOT needed. `VerifierDoc`
// (If the Verifier IS also the Notary then the signature is NOT needed. `VerifiedDoc`
// should be created with `from_unchecked_with_signed_data()` instead.)

if unchecked.signature.is_none() {
Expand All @@ -98,18 +159,17 @@ impl VerifierDoc {
})
}

/// Returns a new VerifierDoc after performing all validation checks and adding the signed data.
/// This is the only way for the Verifier who acted as the Notary to derive [VerifierDoc].
/// Returns a new [ValidatedDoc] after performing all validation checks and adding the signed data.
/// `signed_data` (despite its name) is not actually signed because it was generated locally by
/// the calling Verifier.
pub fn from_unchecked_with_signed_data(
unchecked: VerifierDocUnchecked,
pub(crate) fn from_unchecked_with_signed_data(
unchecked: UncheckedDoc,
signed_data: Signed,
) -> Result<Self, Error> {
checks::perform_checks(&unchecked)?;

// Make sure the Notary's signature is NOT present.
// (If the Verifier is NOT the Notary then the Notary's signature IS needed. `VerifierDoc`
// (If the Verifier is NOT the Notary then the Notary's signature IS needed. `ValidatedDoc`
// should be created with `from_unchecked()` instead.)

if unchecked.signature.is_some() {
Expand Down Expand Up @@ -142,7 +202,7 @@ impl VerifierDoc {
/// - the TLS document
/// - the inclusion of commitments in the Merkle tree
/// - each commitment
pub fn verify(&self, dns_name: String) -> Result<(), Error> {
pub(crate) fn verify(&self, dns_name: String) -> Result<(), Error> {
self.tls_doc.verify(dns_name)?;

self.verify_merkle_proofs()?;
Expand Down Expand Up @@ -235,56 +295,6 @@ impl VerifierDoc {
pub fn tls_doc(&self) -> &TLSDoc {
&self.tls_doc
}

pub fn commitments(&self) -> &Vec<Commitment> {
&self.commitments
}

pub fn commitment_openings(&self) -> &Vec<CommitmentOpening> {
&self.commitment_openings
}
}

/// This is the [VerifierDoc] in its unchecked form. This is the form in which the doc is received
/// by the Verifier from the User.
pub struct VerifierDocUnchecked {
/// All fields are exactly as in [VerifierDoc]
version: u8,
tls_doc: TLSDoc,
signature: Option<Vec<u8>>,
label_seed: LabelSeed,
merkle_root: [u8; 32],
merkle_tree_leaf_count: usize,
merkle_multi_proof: MerkleProof<algorithms::Sha256>,
commitments: Vec<Commitment>,
commitment_openings: Vec<CommitmentOpening>,
}

impl VerifierDocUnchecked {
pub fn commitments(&self) -> &Vec<Commitment> {
&self.commitments
}

pub fn commitment_openings(&self) -> &Vec<CommitmentOpening> {
&self.commitment_openings
}
}

/// Converts VerifierDoc into an unchecked type with will be passed to the Verifier
impl std::convert::From<VerifierDoc> for VerifierDocUnchecked {
fn from(doc: VerifierDoc) -> Self {
Self {
version: doc.version,
tls_doc: doc.tls_doc,
signature: doc.signature,
label_seed: doc.label_seed,
merkle_root: doc.merkle_root,
merkle_tree_leaf_count: doc.merkle_tree_leaf_count,
merkle_multi_proof: doc.merkle_multi_proof,
commitments: doc.commitments,
commitment_openings: doc.commitment_openings,
}
}
}

/// Serialize the [MerkleProof] type using its native `serialize` method
Expand Down
Loading

0 comments on commit 785d267

Please sign in to comment.