diff --git a/crates/lox-library/src/lib.rs b/crates/lox-library/src/lib.rs index 043084c..37338a8 100644 --- a/crates/lox-library/src/lib.rs +++ b/crates/lox-library/src/lib.rs @@ -376,6 +376,7 @@ pub fn pt_dbl(P: &RistrettoPoint) -> RistrettoPoint { /// Response. It also adds a handle_* function to the BridgeAuth struct /// that consumes a Request and produces a Result. pub mod proto { + pub mod level_up; pub mod migration; pub mod open_invite; pub mod trust_promotion; diff --git a/crates/lox-library/src/proto/level_up.rs b/crates/lox-library/src/proto/level_up.rs new file mode 100644 index 0000000..4edd7d8 --- /dev/null +++ b/crates/lox-library/src/proto/level_up.rs @@ -0,0 +1,579 @@ +/*! A module for the protocol for the user to increase their trust level +(from a level at least 1; use the trust promotion protocol to go from +untrusted (level 0) to minimally trusted (level 1). + +They are allowed to do this as long as some amount of time (depending on +their current level) has elapsed since their last level change, and they +have a Bucket Reachability credential for their current bucket and +today's date. (Such credentials are placed daily in the encrypted +bridge table.) + +The user presents their current Lox credential: +- id: revealed +- bucket: blinded +- trust_level: revealed, and must be at least 1 +- level_since: blinded, but proved in ZK that it's at least the + appropriate number of days ago +- invites_remaining: blinded +- invites_issued: blinded + +and a Bucket Reachability credential: +- date: revealed to be today +- bucket: blinded, but proved in ZK that it's the same as in the Lox + credential above + +and a new Lox credential to be issued: + +- id: jointly chosen by the user and BA +- bucket: blinded, but proved in ZK that it's the same as in the Lox + credential above +- trust_level: revealed to be one more than the trust level above +- level_since: today +- invites_remaining: blinded, but proved in ZK that it's the same as in + the Lox credential above, _plus_ a per-level constant +- invites_issued: blinded, but proved in ZK that it's the same as in the + Lox credential above + +*/ + +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; + +use super::super::cred; +use super::super::dup_filter::SeenType; +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}; + +/// 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. The large +/// last entry makes it impossible to advance past the top level. Note +/// that the 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 LEVEL_INTERVAL: [u32; 5] = [0, 14, 28, 56, u32::MAX]; + +/// LEVEL_INVITATIONS\[i\] for i >= 1 is the number of additional +/// 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 LEVEL_INTERVAL. Also the last entry is 0 because +/// users cannot advance above the highest level. +pub const LEVEL_INVITATIONS: [u32; 5] = [0, 2, 4, 6, 0]; + +pub struct Request { + // Fields for blind showing the Lox credential + P: RistrettoPoint, + id: Scalar, + CBucket: RistrettoPoint, + level: Scalar, + CSince: RistrettoPoint, + CInvRemain: RistrettoPoint, + CInvIssued: RistrettoPoint, + CQ: RistrettoPoint, + + // Fields for blind showing the Bucket Reachability credential + P_reach: RistrettoPoint, + CBucket_reach: RistrettoPoint, + CQ_reach: RistrettoPoint, + + // Fields for the inequality proof (level_since + + // LEVEL_INTERVAL[level] <= today) + CG1: RistrettoPoint, + CG2: RistrettoPoint, + CG3: RistrettoPoint, + CG4: RistrettoPoint, + CG5: RistrettoPoint, + CG6: RistrettoPoint, + CG7: RistrettoPoint, + CG8: RistrettoPoint, + CG0sq: RistrettoPoint, + CG1sq: RistrettoPoint, + CG2sq: RistrettoPoint, + CG3sq: RistrettoPoint, + CG4sq: RistrettoPoint, + CG5sq: RistrettoPoint, + CG6sq: RistrettoPoint, + CG7sq: RistrettoPoint, + CG8sq: RistrettoPoint, + + // Fields for user blinding of the Lox credential to be issued + D: RistrettoPoint, + EncIdClient: (RistrettoPoint, RistrettoPoint), + EncBucket: (RistrettoPoint, RistrettoPoint), + EncInvRemain: (RistrettoPoint, RistrettoPoint), + EncInvIssued: (RistrettoPoint, RistrettoPoint), + + // The combined ZKP + piUser: CompactProof, +} + +#[derive(Debug)] +pub struct State { + d: Scalar, + D: RistrettoPoint, + EncIdClient: (RistrettoPoint, RistrettoPoint), + EncBucket: (RistrettoPoint, RistrettoPoint), + EncInvRemain: (RistrettoPoint, RistrettoPoint), + EncInvIssued: (RistrettoPoint, RistrettoPoint), + id_client: Scalar, + bucket: Scalar, + level: Scalar, + invremain: Scalar, + invissued: Scalar, +} + +pub struct Response { + // The fields for the new Lox credential; the new trust level is one + // more than the old trust level, so we don't have to include it + // here explicitly + P: RistrettoPoint, + EncQ: (RistrettoPoint, RistrettoPoint), + id_server: Scalar, + level_since: Scalar, + TId: RistrettoPoint, + TBucket: RistrettoPoint, + TInvRemain: RistrettoPoint, + TInvIssued: RistrettoPoint, + + // The fields for the implicit noop migration ("nm") credential + P_nm: RistrettoPoint, + EncQ_nm: (RistrettoPoint, RistrettoPoint), + TId_nm: RistrettoPoint, + TBucket_nm: RistrettoPoint, + + // The ZKP + piBlindIssue: CompactProof, +} + +define_proof! { + requestproof, + "Level Upgrade Request", + (bucket, since, invremain, invissued, zbucket, zsince, zinvremain, + zinvissued, negzQ, + zbucket_reach, negzQ_reach, + d, eid_client, ebucket, einvremain, einvissued, id_client, + g0, g1, g2, g3, g4, g5, g6, g7, g8, + zg0, zg1, zg2, zg3, zg4, zg5, zg6, zg7, zg8, + wg0, wg1, wg2, wg3, wg4, wg5, wg6, wg7, wg8, + yg0, yg1, yg2, yg3, yg4, yg5, yg6, yg7, yg8), + (P, CBucket, CSince, CInvRemain, CInvIssued, V, Xbucket, Xsince, + Xinvremain, Xinvissued, + P_reach, CBucket_reach, V_reach, Xbucket_reach, + D, EncIdClient0, EncIdClient1, EncBucket0, EncBucket1, + EncInvRemain0, EncInvRemain1_minus_LEVELINV_B, + EncInvIssued0, EncInvIssued1, + CG0, CG1, CG2, CG3, CG4, CG5, CG6, CG7, CG8, + CG0sq, CG1sq, CG2sq, CG3sq, CG4sq, CG5sq, CG6sq, CG7sq, CG8sq), + (A, B) : + // Blind showing of the Lox credential + CBucket = (bucket*P + zbucket*A), + CSince = (since*P + zsince*A), + CInvRemain = (invremain*P + zinvremain*A), + CInvIssued = (invissued*P + zinvissued*A), + // Blind showing of the Bucket Reachability credential; note the + // same bucket is used in the proof + CBucket_reach = (bucket*P_reach + zbucket_reach*A), + // User blinding of the Lox credential to be issued + D = (d*B), + EncIdClient0 = (eid_client*B), + EncIdClient1 = (id_client*B + eid_client*D), + EncBucket0 = (ebucket*B), + EncBucket1 = (bucket*B + ebucket*D), + EncInvRemain0 = (einvremain*B), + EncInvRemain1_minus_LEVELINV_B = (invremain*B + einvremain*D), + EncInvIssued0 = (einvissued*B), + EncInvIssued1 = (invissued*B + einvissued*D), + // Prove CSince encodes a value at least LEVEL_INTERVAL + // days ago (at technically at most LEVEL_INTERVAL+511 days + // ago): first prove each of g0, ..., g8 is a bit by proving that + // gi = gi^2 + CG0 = (g0*P + zg0*A), CG0sq = (g0*CG0 + wg0*A), CG0sq = (g0*P + yg0*A), + CG1 = (g1*P + zg1*A), CG1sq = (g1*CG1 + wg1*A), CG1sq = (g1*P + yg1*A), + CG2 = (g2*P + zg2*A), CG2sq = (g2*CG2 + wg2*A), CG2sq = (g2*P + yg2*A), + CG3 = (g3*P + zg3*A), CG3sq = (g3*CG3 + wg3*A), CG3sq = (g3*P + yg3*A), + CG4 = (g4*P + zg4*A), CG4sq = (g4*CG4 + wg4*A), CG4sq = (g4*P + yg4*A), + CG5 = (g5*P + zg5*A), CG5sq = (g5*CG5 + wg5*A), CG5sq = (g5*P + yg5*A), + CG6 = (g6*P + zg6*A), CG6sq = (g6*CG6 + wg6*A), CG6sq = (g6*P + yg6*A), + CG7 = (g7*P + zg7*A), CG7sq = (g7*CG7 + wg7*A), CG7sq = (g7*P + yg7*A), + CG8 = (g8*P + zg8*A), CG8sq = (g8*CG8 + wg8*A), CG8sq = (g8*P + yg8*A) + // Then we'll check that CSince + LEVEL_INTERVAL*P + CG0 + 2*CG1 + // + 4*CG2 + 8*CG3 + ... + 256*CG8 = today*P by having the verifier + // plug in today*P - (CSince + LEVEL_INTERVAL*P + 2*CG1 + 4*CG2 + // + ... + 256*CG8) as its value of CG0. +} + +pub fn request( + lox_cred: &cred::Lox, + reach_cred: &cred::BucketReachability, + lox_pub: &IssuerPubKey, + reach_pub: &IssuerPubKey, + today: u32, +) -> Result<(Request, State), ProofError> { + let A: &RistrettoPoint = &CMZ_A; + let B: &RistrettoPoint = &CMZ_B; + let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE; + let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE; + + // 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(&lox_cred.level_since) { + Some(v) => v, + None => return Err(ProofError::VerificationFailure), + }; + // 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), + }; + if trust_level < 1 { + return Err(ProofError::VerificationFailure); + } + // 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), + }; + if level_since + level_interval > today { + return Err(ProofError::VerificationFailure); + } + // The credential can't be _too_ old + let diffdays = today - (level_since + level_interval); + if diffdays > 511 { + return Err(ProofError::VerificationFailure); + } + // The buckets in the Lox and Bucket Reachability credentials have + // to match + if lox_cred.bucket != reach_cred.bucket { + return Err(ProofError::VerificationFailure); + } + // 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), + }; + if reach_date != today { + return Err(ProofError::VerificationFailure); + } + + // Blind showing the Lox credential + + // Reblind P and Q + let mut rng = rand::thread_rng(); + let t = Scalar::random(&mut rng); + let P = t * lox_cred.P; + let Q = t * lox_cred.Q; + + // Form Pedersen commitments to the blinded attributes + let zbucket = Scalar::random(&mut rng); + let zsince = Scalar::random(&mut rng); + let zinvremain = Scalar::random(&mut rng); + let zinvissued = Scalar::random(&mut rng); + let CBucket = lox_cred.bucket * P + &zbucket * Atable; + let CSince = lox_cred.level_since * P + &zsince * Atable; + let CInvRemain = lox_cred.invites_remaining * P + &zinvremain * Atable; + let CInvIssued = lox_cred.invites_issued * P + &zinvissued * Atable; + + // Form a Pedersen commitment to the MAC Q + // We flip the sign of zQ from that of the Hyphae paper so that + // the ZKP has a "+" instead of a "-", as that's what the zkp + // macro supports. + let negzQ = Scalar::random(&mut rng); + let CQ = Q - &negzQ * Atable; + + // Compute the "error factor" + let V = zbucket * lox_pub.X[2] + + zsince * lox_pub.X[4] + + zinvremain * lox_pub.X[5] + + zinvissued * lox_pub.X[6] + + &negzQ * Atable; + + // Blind showing the Bucket Reachability credential + + // Reblind P and Q + let t_reach = Scalar::random(&mut rng); + let P_reach = t_reach * reach_cred.P; + let Q_reach = t_reach * reach_cred.Q; + + // Form Pedersen commitments to the blinded attributes + let zbucket_reach = Scalar::random(&mut rng); + let CBucket_reach = reach_cred.bucket * P_reach + &zbucket_reach * Atable; + + // Form a Pedersen commitment to the MAC Q + // We flip the sign of zQ from that of the Hyphae paper so that + // the ZKP has a "+" instead of a "-", as that's what the zkp + // macro supports. + let negzQ_reach = Scalar::random(&mut rng); + let CQ_reach = Q_reach - &negzQ_reach * Atable; + + // Compute the "error factor" + let V_reach = zbucket_reach * reach_pub.X[2] + &negzQ_reach * Atable; + + // User blinding for the Lox certificate to be issued + + // Pick an ElGamal keypair + let d = Scalar::random(&mut rng); + let D = &d * Btable; + + // Pick a random client component of the id + let id_client = Scalar::random(&mut rng); + + // Encrypt it (times the basepoint B) to the ElGamal public key D we + // just created + let eid_client = Scalar::random(&mut rng); + let EncIdClient = (&eid_client * Btable, &id_client * Btable + eid_client * D); + + // Encrypt the other blinded fields (times B) to D as well + let ebucket = Scalar::random(&mut rng); + let EncBucket = (&ebucket * Btable, &lox_cred.bucket * Btable + ebucket * D); + let newinvites: Scalar = LEVEL_INVITATIONS[trust_level as usize].into(); + let einvremain = Scalar::random(&mut rng); + let EncInvRemain = ( + &einvremain * Btable, + &(lox_cred.invites_remaining + newinvites) * Btable + einvremain * D, + ); + let einvissued = Scalar::random(&mut rng); + let EncInvIssued = ( + &einvissued * Btable, + &lox_cred.invites_issued * Btable + einvissued * D, + ); + + // The range proof that 0 <= diffdays <= 511 + + // Extract the 9 bits from diffdays + let g0: Scalar = (diffdays & 1).into(); + let g1: Scalar = ((diffdays >> 1) & 1).into(); + let g2: Scalar = ((diffdays >> 2) & 1).into(); + let g3: Scalar = ((diffdays >> 3) & 1).into(); + let g4: Scalar = ((diffdays >> 4) & 1).into(); + let g5: Scalar = ((diffdays >> 5) & 1).into(); + let g6: Scalar = ((diffdays >> 6) & 1).into(); + let g7: Scalar = ((diffdays >> 7) & 1).into(); + let g8: Scalar = ((diffdays >> 8) & 1).into(); + + // Pick random factors for the Pedersen commitments + let wg0 = Scalar::random(&mut rng); + let zg1 = Scalar::random(&mut rng); + let wg1 = Scalar::random(&mut rng); + let zg2 = Scalar::random(&mut rng); + let wg2 = Scalar::random(&mut rng); + let zg3 = Scalar::random(&mut rng); + let wg3 = Scalar::random(&mut rng); + let zg4 = Scalar::random(&mut rng); + let wg4 = Scalar::random(&mut rng); + let zg5 = Scalar::random(&mut rng); + let wg5 = Scalar::random(&mut rng); + let zg6 = Scalar::random(&mut rng); + let wg6 = Scalar::random(&mut rng); + let zg7 = Scalar::random(&mut rng); + let wg7 = Scalar::random(&mut rng); + let zg8 = Scalar::random(&mut rng); + let wg8 = Scalar::random(&mut rng); + + // Compute zg0 to cancel things out as + // zg0 = -(zsince + 2*zg1 + 4*zg2 + 8*zg3 + 16*zg4 + 32*zg5 + 64*zg6 + 128*zg7 + 256*zg8) + // but use Horner's method + let zg0 = -(scalar_dbl( + &(scalar_dbl( + &(scalar_dbl( + &(scalar_dbl( + &(scalar_dbl( + &(scalar_dbl(&(scalar_dbl(&(scalar_dbl(&zg8) + zg7)) + zg6)) + zg5), + ) + zg4), + ) + zg3), + ) + zg2), + ) + zg1), + ) + zsince); + + let yg0 = wg0 + g0 * zg0; + let yg1 = wg1 + g1 * zg1; + let yg2 = wg2 + g2 * zg2; + let yg3 = wg3 + g3 * zg3; + let yg4 = wg4 + g4 * zg4; + let yg5 = wg5 + g5 * zg5; + let yg6 = wg6 + g6 * zg6; + let yg7 = wg7 + g7 * zg7; + let yg8 = wg8 + g8 * zg8; + + let CG0 = g0 * P + &zg0 * Atable; + let CG1 = g1 * P + &zg1 * Atable; + let CG2 = g2 * P + &zg2 * Atable; + let CG3 = g3 * P + &zg3 * Atable; + let CG4 = g4 * P + &zg4 * Atable; + let CG5 = g5 * P + &zg5 * Atable; + let CG6 = g6 * P + &zg6 * Atable; + let CG7 = g7 * P + &zg7 * Atable; + let CG8 = g8 * P + &zg8 * Atable; + + let CG0sq = g0 * P + &yg0 * Atable; + let CG1sq = g1 * P + &yg1 * Atable; + let CG2sq = g2 * P + &yg2 * Atable; + let CG3sq = g3 * P + &yg3 * Atable; + let CG4sq = g4 * P + &yg4 * Atable; + let CG5sq = g5 * P + &yg5 * Atable; + let CG6sq = g6 * P + &yg6 * Atable; + let CG7sq = g7 * P + &yg7 * Atable; + let CG8sq = g8 * P + &yg8 * Atable; + + // Construct the proof + let mut transcript = Transcript::new(b"level upgrade request"); + let piUser = requestproof::prove_compact( + &mut transcript, + requestproof::ProveAssignments { + A: &A, + B: &B, + P: &P, + CBucket: &CBucket, + CSince: &CSince, + CInvRemain: &CInvRemain, + CInvIssued: &CInvIssued, + V: &V, + Xbucket: &lox_pub.X[2], + Xsince: &lox_pub.X[4], + Xinvremain: &lox_pub.X[5], + Xinvissued: &lox_pub.X[6], + P_reach: &P_reach, + CBucket_reach: &CBucket_reach, + V_reach: &V_reach, + Xbucket_reach: &reach_pub.X[2], + D: &D, + EncIdClient0: &EncIdClient.0, + EncIdClient1: &EncIdClient.1, + EncBucket0: &EncBucket.0, + EncBucket1: &EncBucket.1, + EncInvRemain0: &EncInvRemain.0, + EncInvRemain1_minus_LEVELINV_B: &(EncInvRemain.1 - &newinvites * Btable), + EncInvIssued0: &EncInvIssued.0, + EncInvIssued1: &EncInvIssued.1, + CG0: &CG0, + CG1: &CG1, + CG2: &CG2, + CG3: &CG3, + CG4: &CG4, + CG5: &CG5, + CG6: &CG6, + CG7: &CG7, + CG8: &CG8, + CG0sq: &CG0sq, + CG1sq: &CG1sq, + CG2sq: &CG2sq, + CG3sq: &CG3sq, + CG4sq: &CG4sq, + CG5sq: &CG5sq, + CG6sq: &CG6sq, + CG7sq: &CG7sq, + CG8sq: &CG8sq, + bucket: &lox_cred.bucket, + since: &lox_cred.level_since, + invremain: &lox_cred.invites_remaining, + invissued: &lox_cred.invites_issued, + zbucket: &zbucket, + zsince: &zsince, + zinvremain: &zinvremain, + zinvissued: &zinvissued, + negzQ: &negzQ, + zbucket_reach: &zbucket_reach, + negzQ_reach: &negzQ_reach, + d: &d, + eid_client: &eid_client, + ebucket: &ebucket, + einvremain: &einvremain, + einvissued: &einvissued, + id_client: &id_client, + g0: &g0, + g1: &g1, + g2: &g2, + g3: &g3, + g4: &g4, + g5: &g5, + g6: &g6, + g7: &g7, + g8: &g8, + zg0: &zg0, + zg1: &zg1, + zg2: &zg2, + zg3: &zg3, + zg4: &zg4, + zg5: &zg5, + zg6: &zg6, + zg7: &zg7, + zg8: &zg8, + wg0: &wg0, + wg1: &wg1, + wg2: &wg2, + wg3: &wg3, + wg4: &wg4, + wg5: &wg5, + wg6: &wg6, + wg7: &wg7, + wg8: &wg8, + yg0: &yg0, + yg1: &yg1, + yg2: &yg2, + yg3: &yg3, + yg4: &yg4, + yg5: &yg5, + yg6: &yg6, + yg7: &yg7, + yg8: &yg8, + }, + ) + .0; + + Ok(( + Request { + P, + id: lox_cred.id, + CBucket, + level: lox_cred.trust_level, + CSince, + CInvRemain, + CInvIssued, + CQ, + P_reach, + CBucket_reach, + CQ_reach, + D, + EncIdClient, + EncBucket, + EncInvRemain, + EncInvIssued, + CG1, + CG2, + CG3, + CG4, + CG5, + CG6, + CG7, + CG8, + CG0sq, + CG1sq, + CG2sq, + CG3sq, + CG4sq, + CG5sq, + CG6sq, + CG7sq, + CG8sq, + piUser, + }, + State { + d, + D, + EncIdClient, + EncBucket, + EncInvRemain, + EncInvIssued, + id_client, + bucket: lox_cred.bucket, + level: lox_cred.trust_level + Scalar::one(), + invremain: lox_cred.invites_remaining + newinvites, + invissued: lox_cred.invites_issued, + }, + )) +} diff --git a/crates/lox-library/src/tests.rs b/crates/lox-library/src/tests.rs index eeef6d3..bb40cda 100644 --- a/crates/lox-library/src/tests.rs +++ b/crates/lox-library/src/tests.rs @@ -119,17 +119,23 @@ fn test_trust_promotion() { assert!(ba.verify_reachability(&bucket.1.unwrap())); } -#[test] -fn test_level0_migration() { - let (bdb, mut ba) = setup(); - - let (loxcred, migcred) = trust_promotion(&bdb, &mut ba); +fn level0_migration(bdb: &BridgeDb, ba: &mut BridgeAuth) -> cred::Lox { + let (loxcred, migcred) = trust_promotion(bdb, ba); let (migreq, migstate) = migration::request(&loxcred, &migcred, &ba.lox_pub, &ba.migration_pub).unwrap(); let migresp = ba.handle_migration(migreq).unwrap(); let newloxcred = migration::handle_response(migstate, migresp, &ba.lox_pub, &ba.migration_pub).unwrap(); + newloxcred +} + +#[test] +fn test_level0_migration() { + let (bdb, mut ba) = setup(); + + let newloxcred = level0_migration(&bdb, &mut ba); + assert!(ba.verify_lox(&newloxcred)); println!("newloxcred = {:?}", newloxcred); // Check that we can use the credenital to read a bucket @@ -140,3 +146,26 @@ fn test_level0_migration() { println!("bucket = {:?}", bucket); assert!(ba.verify_reachability(&bucket.1.unwrap())); } + +#[test] +fn test_level_up() { + let (bdb, mut ba) = setup(); + let loxcred = level0_migration(&bdb, &mut ba); + + // Time passes + ba.advance_days(20); + + let (id, key) = bridge_table::from_scalar(loxcred.bucket).unwrap(); + let encbuckets = ba.enc_bridge_table(); + let bucket = + bridge_table::BridgeTable::decrypt_bucket(id, &key, &encbuckets[id as usize]).unwrap(); + let reachcred = bucket.1.unwrap(); + let (req, state) = level_up::request( + &loxcred, + &reachcred, + &ba.lox_pub, + &ba.reachability_pub, + ba.today(), + ) + .unwrap(); +}