diff --git a/crates/lox-library/src/lib.rs b/crates/lox-library/src/lib.rs index ea99c32..03b9c04 100644 --- a/crates/lox-library/src/lib.rs +++ b/crates/lox-library/src/lib.rs @@ -980,6 +980,7 @@ pub fn pt_dbl(P: &RistrettoPoint) -> RistrettoPoint { pub mod proto { pub mod blockage_migration; pub mod check_blockage; + pub mod errors; pub mod issue_invite; pub mod level_up; pub mod migration; diff --git a/crates/lox-library/src/proto/blockage_migration.rs b/crates/lox-library/src/proto/blockage_migration.rs index e2b5f58..043aec8 100644 --- a/crates/lox-library/src/proto/blockage_migration.rs +++ b/crates/lox-library/src/proto/blockage_migration.rs @@ -52,6 +52,8 @@ use super::super::{CMZ_A, CMZ_A_TABLE, CMZ_B, CMZ_B_TABLE}; use super::check_blockage::MIN_TRUST_LEVEL; use super::level_up::LEVEL_INVITATIONS; +use super::errors::CredentialError; + #[derive(Serialize, Deserialize)] pub struct Request { // Fields for blind showing the Lox credential @@ -182,7 +184,7 @@ pub fn request( migration_cred: &cred::Migration, lox_pub: &IssuerPubKey, migration_pub: &IssuerPubKey, -) -> Result<(Request, State), ProofError> { +) -> Result<(Request, State), CredentialError> { let A: &RistrettoPoint = &CMZ_A; let B: &RistrettoPoint = &CMZ_B; let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE; @@ -192,16 +194,24 @@ pub fn request( // ids match and the Lox credential bucket matches the Migration // credential from_bucket if lox_cred.id != migration_cred.lox_id || lox_cred.bucket != migration_cred.from_bucket { - return Err(ProofError::VerificationFailure); + return Err(CredentialError::CredentialMismatch); } // The trust level must be at least MIN_TRUST_LEVEL let level: u32 = match scalar_u32(&lox_cred.trust_level) { Some(v) => v, - None => return Err(ProofError::VerificationFailure), + None => { + return Err(CredentialError::InvalidField( + String::from("trust_level"), + String::from("could not be converted to u32"), + )) + } }; if level < MIN_TRUST_LEVEL { - return Err(ProofError::VerificationFailure); + return Err(CredentialError::InvalidField( + String::from("trust_level"), + format!("level {:?} not in range", level), + )); } // Blind showing the Lox credential diff --git a/crates/lox-library/src/proto/check_blockage.rs b/crates/lox-library/src/proto/check_blockage.rs index a2cc009..79de0d0 100644 --- a/crates/lox-library/src/proto/check_blockage.rs +++ b/crates/lox-library/src/proto/check_blockage.rs @@ -49,6 +49,8 @@ use super::super::scalar_u32; use super::super::{BridgeAuth, IssuerPubKey}; use super::super::{CMZ_A, CMZ_A_TABLE, CMZ_B, CMZ_B_TABLE}; +use super::errors::CredentialError; + /// The minimum trust level a Lox credential must have to be allowed to /// perform this protocol. pub const MIN_TRUST_LEVEL: u32 = 3; @@ -122,7 +124,7 @@ define_proof! { pub fn request( lox_cred: &cred::Lox, lox_pub: &IssuerPubKey, -) -> Result<(Request, State), ProofError> { +) -> Result<(Request, State), CredentialError> { let A: &RistrettoPoint = &CMZ_A; let B: &RistrettoPoint = &CMZ_B; let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE; @@ -132,10 +134,18 @@ pub fn request( // that trust_level >= MIN_TRUST_LEVEL let level: u32 = match scalar_u32(&lox_cred.trust_level) { Some(v) => v, - None => return Err(ProofError::VerificationFailure), + None => { + return Err(CredentialError::InvalidField( + String::from("trust_level"), + String::from("could not be converted to u32"), + )) + } }; if level < MIN_TRUST_LEVEL { - return Err(ProofError::VerificationFailure); + return Err(CredentialError::InvalidField( + String::from("trust_level"), + format!("level {:?} not in range", level), + )); } // Blind showing the Lox credential diff --git a/crates/lox-library/src/proto/errors.rs b/crates/lox-library/src/proto/errors.rs new file mode 100644 index 0000000..98a4915 --- /dev/null +++ b/crates/lox-library/src/proto/errors.rs @@ -0,0 +1,19 @@ +use thiserror::Error; + +/// This error is thrown if the number of buckets/keys in the bridge table +/// exceeds u32 MAX.It is unlikely this error will ever occur. +#[derive(Error, Debug)] +pub enum CredentialError { + #[error("time threshold for operation will not be met for {0} more days")] + TimeThresholdNotMet(u32), + #[error("credential has expired")] + CredentialExpired, + #[error("invalid field {0}: {1}")] + InvalidField(String, String), + #[error("exceeded blockages threshold")] + ExceededBlockagesThreshold, + #[error("credential has no available invitations")] + NoInvitationsRemaining, + #[error("supplied credentials do not match")] + CredentialMismatch, +} diff --git a/crates/lox-library/src/proto/issue_invite.rs b/crates/lox-library/src/proto/issue_invite.rs index 4efd1c9..b4de018 100644 --- a/crates/lox-library/src/proto/issue_invite.rs +++ b/crates/lox-library/src/proto/issue_invite.rs @@ -62,6 +62,8 @@ use super::super::scalar_u32; use super::super::{BridgeAuth, IssuerPubKey}; use super::super::{CMZ_A, CMZ_A_TABLE, CMZ_B, CMZ_B_TABLE}; +use super::errors::CredentialError; + #[derive(Serialize, Deserialize)] pub struct Request { // Fields for blind showing the Lox credential @@ -261,7 +263,7 @@ pub fn request( lox_pub: &IssuerPubKey, reach_pub: &IssuerPubKey, today: u32, -) -> Result<(Request, State), ProofError> { +) -> Result<(Request, State), CredentialError> { let A: &RistrettoPoint = &CMZ_A; let B: &RistrettoPoint = &CMZ_B; let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE; @@ -270,20 +272,28 @@ pub fn request( // Ensure the credential can be correctly shown: it must be the case // that invites_remaining not be 0 if lox_cred.invites_remaining == Scalar::ZERO { - return Err(ProofError::VerificationFailure); + return Err(CredentialError::NoInvitationsRemaining); } // The buckets in the Lox and Bucket Reachability credentials have // to match if lox_cred.bucket != reach_cred.bucket { - return Err(ProofError::VerificationFailure); + return Err(CredentialError::CredentialMismatch); } // The Bucket Reachability credential has to be dated today let reach_date: u32 = match scalar_u32(&reach_cred.date) { Some(v) => v, - None => return Err(ProofError::VerificationFailure), + None => { + return Err(CredentialError::InvalidField( + String::from("date"), + String::from("could not be converted to u32"), + )) + } }; if reach_date != today { - return Err(ProofError::VerificationFailure); + return Err(CredentialError::InvalidField( + String::from("date"), + String::from("reachability credential must be generated today"), + )); } // The new invites_remaining let new_invites_remaining = lox_cred.invites_remaining - Scalar::ONE; diff --git a/crates/lox-library/src/proto/level_up.rs b/crates/lox-library/src/proto/level_up.rs index c267825..b87d75a 100644 --- a/crates/lox-library/src/proto/level_up.rs +++ b/crates/lox-library/src/proto/level_up.rs @@ -55,6 +55,8 @@ use super::super::{pt_dbl, scalar_dbl, scalar_u32}; use super::super::{BridgeAuth, IssuerPubKey}; use super::super::{CMZ_A, CMZ_A_TABLE, CMZ_B, CMZ_B_TABLE}; +use super::errors::CredentialError; + /// The maximum trust level in the system. A user can run this level /// upgrade protocol when they're already at the max level; they will /// get a fresh invites_remaining batch, and reset their level_since @@ -272,7 +274,7 @@ pub fn request( lox_pub: &IssuerPubKey, reach_pub: &IssuerPubKey, today: u32, -) -> Result<(Request, State), ProofError> { +) -> Result<(Request, State), CredentialError> { let A: &RistrettoPoint = &CMZ_A; let B: &RistrettoPoint = &CMZ_B; let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE; @@ -282,50 +284,83 @@ pub fn request( // that level_since + LEVEL_INTERVAL[level] <= today. let level_since: u32 = match scalar_u32(&lox_cred.level_since) { Some(v) => v, - None => return Err(ProofError::VerificationFailure), + None => { + return Err(CredentialError::InvalidField( + String::from("level_since"), + String::from("could not be converted to u32"), + )) + } }; // The trust level has to be at least 1 let trust_level: u32 = match scalar_u32(&lox_cred.trust_level) { Some(v) => v, - None => return Err(ProofError::VerificationFailure), + None => { + return Err(CredentialError::InvalidField( + String::from("trust_level"), + String::from("could not be converted to u32"), + )) + } }; if trust_level < 1 || (trust_level as usize) > MAX_LEVEL { - return Err(ProofError::VerificationFailure); + return Err(CredentialError::InvalidField( + String::from("trust_level"), + format!("level {:?} not in range", trust_level), + )); } // The trust level has to be no higher than the highest level let level_interval: u32 = match LEVEL_INTERVAL.get(trust_level as usize) { Some(&v) => v, - None => return Err(ProofError::VerificationFailure), + None => { + return Err(CredentialError::InvalidField( + String::from("trust_level"), + format!("level {:?} not in range", trust_level), + )) + } }; if level_since + level_interval > today { - return Err(ProofError::VerificationFailure); + return Err(CredentialError::TimeThresholdNotMet( + level_since + level_interval - today, + )); } // The credential can't be _too_ old let diffdays = today - (level_since + level_interval); if diffdays > 511 { - return Err(ProofError::VerificationFailure); + return Err(CredentialError::CredentialExpired); } // The current number of blockages let blockages: u32 = match scalar_u32(&lox_cred.blockages) { Some(v) => v, - None => return Err(ProofError::VerificationFailure), + None => { + return Err(CredentialError::InvalidField( + String::from("blockages"), + String::from("could not be converted to u32"), + )) + } }; if blockages > MAX_BLOCKAGES[trust_level as usize] { - return Err(ProofError::VerificationFailure); + return Err(CredentialError::ExceededBlockagesThreshold); } let blockage_diff = MAX_BLOCKAGES[trust_level as usize] - blockages; // The buckets in the Lox and Bucket Reachability credentials have // to match if lox_cred.bucket != reach_cred.bucket { - return Err(ProofError::VerificationFailure); + return Err(CredentialError::CredentialMismatch); } // The Bucket Reachability credential has to be dated today let reach_date: u32 = match scalar_u32(&reach_cred.date) { Some(v) => v, - None => return Err(ProofError::VerificationFailure), + None => { + return Err(CredentialError::InvalidField( + String::from("date"), + String::from("could not be converted to u32"), + )) + } }; if reach_date != today { - return Err(ProofError::VerificationFailure); + return Err(CredentialError::InvalidField( + String::from("date"), + String::from("reachability credential must be generated today"), + )); } // The new trust level let new_level = if (trust_level as usize) < MAX_LEVEL { diff --git a/crates/lox-library/src/proto/migration.rs b/crates/lox-library/src/proto/migration.rs index df6a402..8354cb5 100644 --- a/crates/lox-library/src/proto/migration.rs +++ b/crates/lox-library/src/proto/migration.rs @@ -46,6 +46,8 @@ use super::super::dup_filter::SeenType; use super::super::{BridgeAuth, IssuerPubKey}; use super::super::{CMZ_A, CMZ_A_TABLE, CMZ_B, CMZ_B_TABLE}; +use super::errors::CredentialError; + #[derive(Serialize, Deserialize)] pub struct Request { // Fields for blind showing the Lox credential @@ -153,7 +155,7 @@ pub fn request( migration_cred: &cred::Migration, lox_pub: &IssuerPubKey, migration_pub: &IssuerPubKey, -) -> Result<(Request, State), ProofError> { +) -> Result<(Request, State), CredentialError> { let A: &RistrettoPoint = &CMZ_A; let B: &RistrettoPoint = &CMZ_B; let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE; @@ -163,13 +165,16 @@ pub fn request( // ids match and the Lox credential bucket matches the Migration // credential from_bucket if lox_cred.id != migration_cred.lox_id || lox_cred.bucket != migration_cred.from_bucket { - return Err(ProofError::VerificationFailure); + return Err(CredentialError::CredentialMismatch); } // This protocol only allows migrating from trust level 0 to trust // level 1 if lox_cred.trust_level != Scalar::ZERO { - return Err(ProofError::VerificationFailure); + return Err(CredentialError::InvalidField( + String::from("trust_level"), + String::from("must be zero"), + )); } // Blind showing the Lox credential diff --git a/crates/lox-library/src/proto/redeem_invite.rs b/crates/lox-library/src/proto/redeem_invite.rs index 230fe7c..c0ad9d9 100644 --- a/crates/lox-library/src/proto/redeem_invite.rs +++ b/crates/lox-library/src/proto/redeem_invite.rs @@ -38,6 +38,8 @@ use super::super::{pt_dbl, scalar_dbl, scalar_u32}; use super::super::{BridgeAuth, IssuerPubKey}; use super::super::{CMZ_A, CMZ_A_TABLE, CMZ_B, CMZ_B_TABLE}; +use super::errors::CredentialError; + /// Invitations must be used within this many days of being issued. /// Note that if you change this number to be larger than 15, you must /// also add bits to the zero knowledge proof. @@ -174,7 +176,7 @@ pub fn request( inv_cred: &cred::Invitation, invitation_pub: &IssuerPubKey, today: u32, -) -> Result<(Request, State), ProofError> { +) -> Result<(Request, State), CredentialError> { let A: &RistrettoPoint = &CMZ_A; let B: &RistrettoPoint = &CMZ_B; let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE; @@ -184,16 +186,24 @@ pub fn request( // that date + INVITATION_EXPIRY >= today. let date: u32 = match scalar_u32(&inv_cred.date) { Some(v) => v, - None => return Err(ProofError::VerificationFailure), + None => { + return Err(CredentialError::InvalidField( + String::from("date"), + String::from("could not be converted to u32"), + )) + } }; if date + INVITATION_EXPIRY < today { - return Err(ProofError::VerificationFailure); + return Err(CredentialError::CredentialExpired); } let diffdays = date + INVITATION_EXPIRY - today; // If diffdays > 15, then since INVITATION_EXPIRY <= 15, then date // must be in the future. Reject. if diffdays > 15 { - return Err(ProofError::VerificationFailure); + return Err(CredentialError::InvalidField( + String::from("date"), + String::from("credential was created in the future"), + )); } // Blind showing the Invitation credential diff --git a/crates/lox-library/src/proto/trust_promotion.rs b/crates/lox-library/src/proto/trust_promotion.rs index 2e2266f..1b370ce 100644 --- a/crates/lox-library/src/proto/trust_promotion.rs +++ b/crates/lox-library/src/proto/trust_promotion.rs @@ -48,6 +48,8 @@ use super::super::{pt_dbl, scalar_dbl, scalar_u32}; use super::super::{BridgeAuth, IssuerPubKey}; use super::super::{CMZ_A, CMZ_A_TABLE, CMZ_B, CMZ_B_TABLE}; +use super::errors::CredentialError; + /// The minimum number of days a user has to be at trust level 0 /// (untrusted) with their (single) bridge unblocked before they can /// move to level 1. @@ -164,7 +166,7 @@ pub fn request( lox_cred: &cred::Lox, lox_pub: &IssuerPubKey, today: u32, -) -> Result<(Request, State), ProofError> { +) -> Result<(Request, State), CredentialError> { let A: &RistrettoPoint = &CMZ_A; let B: &RistrettoPoint = &CMZ_B; let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE; @@ -174,14 +176,21 @@ pub fn request( // that level_since + UNTRUSTED_INTERVAL <= today. let level_since: u32 = match scalar_u32(&lox_cred.level_since) { Some(v) => v, - None => return Err(ProofError::VerificationFailure), + None => { + return Err(CredentialError::InvalidField( + String::from("level_since"), + String::from("could not be converted to u32"), + )) + } }; if level_since + UNTRUSTED_INTERVAL > today { - return Err(ProofError::VerificationFailure); + return Err(CredentialError::TimeThresholdNotMet( + level_since + UNTRUSTED_INTERVAL - today, + )); } let diffdays = today - (level_since + UNTRUSTED_INTERVAL); if diffdays > 511 { - return Err(ProofError::VerificationFailure); + return Err(CredentialError::CredentialExpired); } // Blind showing the Lox credential