diff --git a/crates/lox-library/src/bridge_table.rs b/crates/lox-library/src/bridge_table.rs index 65e7ac8..3abc640 100644 --- a/crates/lox-library/src/bridge_table.rs +++ b/crates/lox-library/src/bridge_table.rs @@ -29,7 +29,7 @@ pub const BUCKET_BYTES: usize = BRIDGE_BYTES * MAX_BRIDGES_PER_BUCKET; pub const ENC_BUCKET_BYTES: usize = BUCKET_BYTES + 12 + 16; /// A bridge information line -#[derive(Debug)] +#[derive(Copy, Clone, Debug)] pub struct BridgeLine { /// IPv4 or IPv6 address pub addr: [u8; 16], diff --git a/crates/lox-library/src/cred.rs b/crates/lox-library/src/cred.rs index 241b03f..871f6c6 100644 --- a/crates/lox-library/src/cred.rs +++ b/crates/lox-library/src/cred.rs @@ -41,3 +41,19 @@ pub struct Lox { pub P_noopmigration: RistrettoPoint, pub Q_noopmigration: RistrettoPoint, } + +// The migration key credential is never actually instantiated. It is +// an implicit credential with the following attributes: +// - lox_id: Scalar, +// - from_bucket: Scalar +// Plus the usual (P,Q) MAC. This credential type does have an +// associated private and public key, however. The idea is that if a +// user proves (in zero knowledge) that their Lox credential entitles +// them to migrate from one bucket to another, the BA will issue a +// (blinded, so the BA will not know the values of the attributes or of +// Q) MAC on this implicit credential. The Q value will then be used +// (actually, a hash of lox_id, from_bucket, and Q) to encrypt the +// to_bucket, P, and Q fields of a Migration credential. That way, +// people entitled to migrate buckets can receive a Migration credential +// with their new bucket, without the BA learning either their old or +// new buckets. diff --git a/crates/lox-library/src/lib.rs b/crates/lox-library/src/lib.rs index 1972ea2..52159df 100644 --- a/crates/lox-library/src/lib.rs +++ b/crates/lox-library/src/lib.rs @@ -20,6 +20,8 @@ extern crate zkp; pub mod bridge_table; pub mod cred; pub mod dup_filter; +pub mod migration_table; +pub mod trust_promotion; use sha2::Sha512; @@ -178,6 +180,10 @@ pub struct BridgeAuth { migration_priv: IssuerPrivKey, /// The public key for migration credentials pub migration_pub: IssuerPubKey, + /// The private key for migration key credentials + migrationkey_priv: IssuerPrivKey, + /// The public key for migration key credentials + pub migrationkey_pub: IssuerPubKey, /// The public key of the BridgeDb issuing open invitations pub bridgedb_pub: PublicKey, @@ -185,10 +191,16 @@ pub struct BridgeAuth { /// The bridge table bridge_table: bridge_table::BridgeTable, + /// The migration table + migration_table: migration_table::MigrationTable, + /// Duplicate filter for open invitations openinv_filter: dup_filter::DupFilter, /// Duplicate filter for credential ids id_filter: dup_filter::DupFilter, + /// Duplicate filter for trust promotions (from untrusted level 0 to + /// trusted level 1) + trust_promotion_filter: dup_filter::DupFilter, /// For testing only: offset of the true time to the simulated time time_offset: time::Duration, @@ -196,19 +208,27 @@ pub struct BridgeAuth { impl BridgeAuth { pub fn new(bridgedb_pub: PublicKey) -> Self { + // Create the private and public keys for each of the types of + // credential, each with the appropriate number of attributes let lox_priv = IssuerPrivKey::new(6); let lox_pub = IssuerPubKey::new(&lox_priv); let migration_priv = IssuerPrivKey::new(3); let migration_pub = IssuerPubKey::new(&migration_priv); + let migrationkey_priv = IssuerPrivKey::new(2); + let migrationkey_pub = IssuerPubKey::new(&migrationkey_priv); Self { lox_priv, lox_pub, migration_priv, migration_pub, + migrationkey_priv, + migrationkey_pub, bridgedb_pub, bridge_table: Default::default(), + migration_table: Default::default(), openinv_filter: Default::default(), id_filter: Default::default(), + trust_promotion_filter: Default::default(), time_offset: time::Duration::zero(), } } @@ -284,4 +304,48 @@ mod tests { println!("cred = {:?}", cred); println!("bucket = {:?}", bucket); } + + #[test] + fn test_trust_promotion() { + // Create a BridegDb + let bdb = BridgeDb::new(15); + // Create a BridgeAuth + let mut ba = BridgeAuth::new(bdb.pubkey); + + // Make 15 buckets with one random bridge each + for _ in 0..15 { + let bucket: [BridgeLine; 3] = + [BridgeLine::random(), Default::default(), Default::default()]; + ba.bridge_table.new_bucket(bucket); + } + // Make 5 more buckets, each containing 3 of the previously + // created bridges + for i in 0u32..5 { + let iusize = i as usize; + let bucket: [BridgeLine; 3] = [ + ba.bridge_table.buckets[3 * iusize][0], + ba.bridge_table.buckets[3 * iusize + 1][0], + ba.bridge_table.buckets[3 * iusize + 2][0], + ]; + ba.bridge_table.new_bucket(bucket); + // Add the allowed migrations to the migration table + ba.migration_table.table.push((3 * i, 15 + i)); + ba.migration_table.table.push((3 * i + 1, 15 + i)); + ba.migration_table.table.push((3 * i + 2, 15 + i)); + } + // Create the encrypted bridge table + ba.bridge_table.encrypt_table(); + + // Issue an open invitation + let inv = bdb.invite(); + + // Use it to get a Lox credential + let (req, state) = open_invite::request(&inv); + let resp = ba.handle_open_invite(req).unwrap(); + let cred = + open_invite::handle_response(state, resp, &ba.lox_pub, &ba.migration_pub).unwrap(); + + // Time passes + ba.advance_days(40); + } } diff --git a/crates/lox-library/src/migration_table.rs b/crates/lox-library/src/migration_table.rs new file mode 100644 index 0000000..0937c76 --- /dev/null +++ b/crates/lox-library/src/migration_table.rs @@ -0,0 +1,15 @@ +/*! The migration table. + +This is a table listing pairs of (from_bucket_id, to_bucket_id). A pair +in this table indicates that a user with a Lox credential containing +from_bucket_id (and possibly meeting other conditions as well) is +entitled to exchange their credential for one with to_bucket_id. (Note +that the credentials contain the bucket attributes, which include both +the id and the bucket decrytpion key, but the table just contains the +bucket ids.) */ + +/// The migration table +#[derive(Default, Debug)] +pub struct MigrationTable { + pub table: Vec<(u32, u32)>, +} diff --git a/crates/lox-library/src/trust_promotion.rs b/crates/lox-library/src/trust_promotion.rs new file mode 100644 index 0000000..e090346 --- /dev/null +++ b/crates/lox-library/src/trust_promotion.rs @@ -0,0 +1,50 @@ +/*! A module for the protocol for the user to get promoted from +untrusted (trust level 0) to trusted (trust level 1). + +They are allowed to do this as long as UNTRUSTED_INTERVAL days have +passed since they obtained their level 0 Lox credential, and their +bridge (level 0 users get put in a one-bridge bucket) has not been +blocked. (Blocked bridges in one-bridge buckets will have their entries +removed from the bridge authority's migration table.) + +The user presents their current Lox credential: +- id: revealed +- bucket: blinded +- trust_level: revealed to be 0 +- level_since: blinded, but proved in ZK that it's at least + UNTRUSTED_INTERVAL days ago +- invites_remaining: revealed to be 0 +- invites_issued: revealed to be 0 + +They will receive in return the encrypted MAC (Pk, EncQk) for their +implicit Migration Key credential with attributes id and bucket, +along with a HashMap of encrypted Migration credentials. For each +(from_i, to_i) in the BA's migration list, there will be an entry in +the HashMap with key H1(id, from_attr_i, Qk_i) and value +Enc_{H2(id, from_attr_i, Qk_i)}(to_attr_i, P_i, Q_i). Here H1 and H2 +are the first 16 bytes and the second 16 bytes respectively of the +SHA256 hash of the input, P_i and Q_i are a MAC on the Migration +credential with attributes id, from_attr_i, and to_attr_i. Qk_i is the +value EncQk would decrypt to if bucket were equal to from_attr_i. */ + +use curve25519_dalek::ristretto::RistrettoBasepointTable; +use curve25519_dalek::ristretto::RistrettoPoint; +use curve25519_dalek::scalar::Scalar; +use curve25519_dalek::traits::IsIdentity; + +use zkp::CompactProof; +use zkp::ProofError; +use zkp::Transcript; + +/// 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. +/// +/// The implementation also puts an upper bound of UNTRUSTED_INTERVAL + +/// 511 days, which is not unreasonable; we want users to be engaging +/// with the system in order to move up trust levels. +pub const UNTRUSTED_INTERVAL: u64 = 30; + +pub struct Request { + id: Scalar, +}