Use more descriptive error for lox proto functions

Request functions for protocols in the Lox library previously returned a
ProofError error type, which is not verbose enough to allow for accurate
debugging.

This commit introduces a new error enum, CredentialError, with several
types that allow for descriptive error messages.
This commit is contained in:
Cecylia Bocovich 2024-03-10 11:48:58 -04:00
parent bd58409762
commit c92a14d612
No known key found for this signature in database
GPG Key ID: 009DE379FD9B7B90
9 changed files with 144 additions and 35 deletions

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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,
}

View File

@ -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;

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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