|
|
@@ -38,10 +38,14 @@ and a new Lox credential to be issued:
|
|
|
|
|
|
*/
|
|
|
|
|
|
-use phf::{Map, phf_map};
|
|
|
-use super::super::cmz::{IssueType, ShowType};
|
|
|
-use super::super::lox_creds;
|
|
|
+use super::super::scalar_u32;
|
|
|
+use super::errors::CredentialError;
|
|
|
+use crate::lox_creds::{BucketReachability, Lox};
|
|
|
+use cmz::*;
|
|
|
use curve25519_dalek::ristretto::RistrettoPoint as G;
|
|
|
+use ff::PrimeField;
|
|
|
+use rand_core::RngCore;
|
|
|
+use sha2::Sha512;
|
|
|
|
|
|
/// 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
|
|
|
@@ -49,105 +53,152 @@ use curve25519_dalek::ristretto::RistrettoPoint as G;
|
|
|
/// field to today's date, but will remain in the max level.
|
|
|
pub const MAX_LEVEL: usize = 4;
|
|
|
|
|
|
-/// DAYS_AGO\[i\] for i >= 1 is the minimum number of days a user
|
|
|
+/// LEVEL_INTERVAL\[i\] for i >= 1 is the minimum number of days a user
|
|
|
/// must be at trust level i before advancing to level i+1 (or as above,
|
|
|
/// remain at level i if i == MAX_LEVEL). Note that the
|
|
|
-/// DAYS_AGO\[0\] entry is a dummy; the trust_promotion protocol
|
|
|
+/// LEVEL_INTERVAL\[0\] entry is a dummy; the trust_promotion protocol
|
|
|
/// is used instead of this one to move from level 0 to level 1.
|
|
|
-pub const DAYS_AGO: [u32; MAX_LEVEL + 1] = [0, 14, 28, 56, 84];
|
|
|
+pub const LEVEL_INTERVAL: [u32; MAX_LEVEL + 1] = [0, 14, 28, 56, 84];
|
|
|
|
|
|
/// LEVEL_INVITATIONS\[i\] for i >= 1 is the number of invitations a
|
|
|
/// user will be eligible to issue upon advancing from level i to level
|
|
|
/// i+1. Again the LEVEL_INVITATIONS\[0\] entry is a dummy, as for
|
|
|
-/// DAYS_AGO.
|
|
|
+/// LEVEL_INTERVAL.
|
|
|
pub const LEVEL_INVITATIONS: [u32; MAX_LEVEL + 1] = [0, 2, 4, 6, 8];
|
|
|
|
|
|
/// MAX_BLOCKAGES\[i\] for i >= 1 is the maximum number of bucket
|
|
|
/// blockages this credential is allowed to have recorded in order to
|
|
|
/// advance from level i to level i+1. Again the LEVEL_INVITATIONS\[0\]
|
|
|
-/// entry is a dummy, as for DAYS_AGO.
|
|
|
+/// entry is a dummy, as for LEVEL_INTERVAL.
|
|
|
// If you change this to have a number greater than 7, you need to add
|
|
|
// one or more bits to the ZKP.
|
|
|
pub const MAX_BLOCKAGES: [u32; MAX_LEVEL + 1] = [0, 4, 3, 2, 2];
|
|
|
|
|
|
+CMZProtocol! { level_up<credential_expiry, eligibility_max_age, invitations, max_blockage>,
|
|
|
+ [ L: Lox { id: R, bucket: H, trust_level: R, level_since: H, invites_remaining: H, blockages: H },
|
|
|
+ B: BucketReachability { date: R, bucket: H } ],
|
|
|
+ N: Lox {id: J, bucket: H, trust_level: R, level_since: S, invites_remaining: I, blockages: H },
|
|
|
+ credential_expiry <= L.level_since,
|
|
|
+ L.level_since <= eligibility_max_age,
|
|
|
+ 0 <= L.blockages,
|
|
|
+ L.blockages <= max_blockage,
|
|
|
+ B.bucket = L.bucket,
|
|
|
+ N.bucket = L.bucket,
|
|
|
+ N.trust_level = L.trust_level+1,
|
|
|
+ N.invites_remaining = invitations,
|
|
|
+ N.blockages = L.blockages,
|
|
|
+}
|
|
|
|
|
|
-static LOX_LEVEL_UP_SHOW: Map<&str, ShowType> = phf_map! {
|
|
|
- "id" => ShowType::Reveal,
|
|
|
- "bucket" => ShowType::Hide,
|
|
|
- "trust_level" => ShowType::Reveal,
|
|
|
- "level_since" => ShowType::Hide,
|
|
|
- "invites_remaining" => ShowType::Hide,
|
|
|
- "blockages" => ShowType::Hide,
|
|
|
-};
|
|
|
-
|
|
|
-static REACHABILITY_SHOW: Map<&str, ShowType> = phf_map! {
|
|
|
- "date" => ShowType::Reveal,
|
|
|
- "bucket" => ShowType::Hide,
|
|
|
-};
|
|
|
-
|
|
|
-static LOX_LEVEL_UP_ISSUE: Map<&str, IssueType> = phf_map! {
|
|
|
- "id" => IssueType::Joint,
|
|
|
- "bucket" => IssueType::Hide,
|
|
|
- "trust_level" => IssueType::Reveal,
|
|
|
- "level_since" => IssueType::Server,
|
|
|
- "invites_remaining" => IssueType::Implicit, // determined by trust_level
|
|
|
- "blockages" => IssueType::Hide,
|
|
|
-};
|
|
|
-
|
|
|
-let level_up = CMZ.Protocol(
|
|
|
- vec![("L", lox_creds::Lox::attrs(), LOX_LEVEL_UP_SHOW),
|
|
|
- ("BR", Reachability::attrs(), REACHABILITY_SHOW)],
|
|
|
- vec![("NEW", Lox::attrs(), LOX_LEVEL_UP_ISSUE)],
|
|
|
- statement,
|
|
|
- request_message
|
|
|
-);
|
|
|
-
|
|
|
-let statement = Statement! {
|
|
|
- today-(DAYS_AGO[level]+511) <= L.level_since <= today-DAYS_AGO[level],
|
|
|
- 0 <= L.blockages <= MAX_BLOCKAGES[level+1],
|
|
|
- BR.bucket = L.bucket,
|
|
|
- NEW.bucket = L.bucket,
|
|
|
- NEW.blockages = L.blockages,
|
|
|
-};
|
|
|
-
|
|
|
-pub fn request(lox_credential: lox_creds::Lox, reachability_credential: lox_cred::Bucket,
|
|
|
-pubkeys: IssuerPubKeys) -> Result<(Vec<u8>, State), Error> {
|
|
|
-
|
|
|
- // TODO: Check everything required for the lox_credential to run
|
|
|
- // this protocol
|
|
|
- let new_lox_credential = lox_creds::Lox::default();
|
|
|
+pub fn request(
|
|
|
+ L: Lox,
|
|
|
+ B: BucketReachability,
|
|
|
+ pubkeys: CMZPubkey<G>,
|
|
|
+ today: u32,
|
|
|
+) -> Result<(level_up::Request, level_up::ClientState), CredentialError> {
|
|
|
let mut rng = rand::thread_rng();
|
|
|
- new_lox_credential.id = Some(<G as Group>::Scalar::random(&mut rng));
|
|
|
- new_lox_credential.bucket = Some(lox_credential.bucket);
|
|
|
- new_lox_credential.trust_level = Some((lox_credential.trust_level as usize + 1).into());
|
|
|
- new_lox_credential.invites_remaining = Some(LEVEL_INVITATIONS[trust_level as usize].into())
|
|
|
- new_lox_credential.blockages = Some(lox_credential.blockages);
|
|
|
-
|
|
|
- // TODO: Allocate pubkeys somehow
|
|
|
- match level_up.show_issue(
|
|
|
- vec![&lox_credential, &reachability_credential],
|
|
|
- vec![&mut new_lox_credential]) {
|
|
|
- Ok(request_message, state) => Ok(request_message, state),
|
|
|
- Err(e) => Error(e),
|
|
|
+ cmz_group_init(G::hash_from_bytes::<Sha512>(b"CMZ Generator A"));
|
|
|
+
|
|
|
+ // Ensure the credential can be correctly shown: it must be the case
|
|
|
+ // that level_since + LEVEL_INTERVAL[level] <= today.
|
|
|
+ let level_since: u32 = match scalar_u32(&L.level_since.unwrap()) {
|
|
|
+ Some(v) => v,
|
|
|
+ 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(&L.trust_level.unwrap()) {
|
|
|
+ Some(v) => v,
|
|
|
+ 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(CredentialError::InvalidField(
|
|
|
+ String::from("trust_level"),
|
|
|
+ format!("level {:?} not in range", trust_level),
|
|
|
+ ));
|
|
|
}
|
|
|
-}
|
|
|
-
|
|
|
-impl BridgeAuth {
|
|
|
-
|
|
|
- pub fn handle_level_up(&mut self, req: Request) -> Result<Response,
|
|
|
-ProofError> {
|
|
|
-
|
|
|
- let public_verify = TODO;
|
|
|
- level_up.handle(
|
|
|
- vec![&mut req.lox_credential, &mut req.reachability_credential],
|
|
|
- vec![&mut req.new_lox_credential],
|
|
|
- public_verify)
|
|
|
+ // 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(CredentialError::InvalidField(
|
|
|
+ String::from("trust_level"),
|
|
|
+ format!("level {:?} not in range", trust_level),
|
|
|
+ ))
|
|
|
+ }
|
|
|
+ };
|
|
|
+ if level_since + level_interval > today {
|
|
|
+ return Err(CredentialError::TimeThresholdNotMet(
|
|
|
+ level_since + level_interval - today,
|
|
|
+ ));
|
|
|
}
|
|
|
-}
|
|
|
-
|
|
|
-pub fn handle_response(state: State, resp: Response) -> Result<Vec<Credential>, ProofError> {
|
|
|
- match cmz_finalize(client_state, response_message) {
|
|
|
- Ok(Vec<Credential>) => Ok(Vec<Credential>),
|
|
|
- Err(e) => ProofError(e),
|
|
|
+ // The credential can't be _too_ old
|
|
|
+ let diffdays = today - (level_since + level_interval);
|
|
|
+ if diffdays > 511 {
|
|
|
+ return Err(CredentialError::CredentialExpired);
|
|
|
+ }
|
|
|
+ // The current number of blockages
|
|
|
+ let blockages = match scalar_u32(&L.blockages.unwrap()) {
|
|
|
+ Some(v) => v,
|
|
|
+ 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(CredentialError::ExceededBlockagesThreshold);
|
|
|
+ }
|
|
|
+ // The buckets in the Lox and Bucket Reachability credentials have
|
|
|
+ // to match
|
|
|
+ if L.bucket != B.bucket {
|
|
|
+ return Err(CredentialError::CredentialMismatch);
|
|
|
+ }
|
|
|
+ // The Bucket Reachability credential has to be dated today
|
|
|
+ let reach_date: u32 = match scalar_u32(&B.date.unwrap()) {
|
|
|
+ Some(v) => v,
|
|
|
+ None => {
|
|
|
+ return Err(CredentialError::InvalidField(
|
|
|
+ String::from("date"),
|
|
|
+ String::from("could not be converted to u32"),
|
|
|
+ ))
|
|
|
}
|
|
|
+ };
|
|
|
+ if reach_date != today {
|
|
|
+ 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 {
|
|
|
+ trust_level + 1
|
|
|
+ } else {
|
|
|
+ trust_level
|
|
|
+ };
|
|
|
+
|
|
|
+ let credential_expiry = today - LEVEL_INTERVAL[trust_level as usize] + 511;
|
|
|
+ let eligibility_max_age = today - LEVEL_INTERVAL[trust_level as usize];
|
|
|
+
|
|
|
+ let mut N = Lox::using_pubkey(&pubkeys);
|
|
|
+ let params = level_up::Params {
|
|
|
+ credential_expiry: level_up::Scalar::from_u128(credential_expiry.into()),
|
|
|
+ eligibility_max_age: level_up::Scalar::from_u128(eligibility_max_age.into()),
|
|
|
+ invitations: level_up::Scalar::from_u128(LEVEL_INVITATIONS[new_level as usize].into()),
|
|
|
+ max_blockage: level_up::Scalar::from_u128(MAX_BLOCKAGES[new_level as usize].into()),
|
|
|
+ };
|
|
|
+
|
|
|
+ match level_up::prepare(&mut rng, &L, &B, N, ¶ms) {
|
|
|
+ Ok(req_state) => Ok(req_state),
|
|
|
+ Err(e) => Err(CredentialError::CMZError(e)),
|
|
|
+ }
|
|
|
}
|