123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 |
- /*! A module for the protocol for the user to check for the availability
- of a migration credential they can use in order to move to a new bucket
- if theirs has been blocked.
- The user presents their current Lox credential:
- - id: revealed
- - bucket: blinded
- - trust_level: revealed to be 3 or above
- - level_since: blinded
- - invites_remaining: blinded
- - blockages: blinded
- They are allowed to to this as long as they are level 3 or above. If
- they have too many blockages (but are level 3 or above), they will be
- allowed to perform this migration, but will not be able to advance to
- level 3 in their new bucket, so this will be their last allowed
- migration without rejoining the system either with a new invitation or
- an open invitation.
- 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;
- use serde::{Deserialize, Serialize};
- use serde_with::serde_as;
- use std::collections::HashMap;
- use super::super::cred;
- use super::super::dup_filter::SeenType;
- use super::super::migration_table;
- use super::super::scalar_u32;
- use super::super::{BridgeAuth, IssuerPubKey};
- use super::super::{CMZ_A, CMZ_A_TABLE, CMZ_B, CMZ_B_TABLE};
- /// The minimum trust level a Lox credential must have to be allowed to
- /// perform this protocol.
- pub const MIN_TRUST_LEVEL: u32 = 3;
- #[derive(Serialize, Deserialize)]
- pub struct Request {
- // Fields for blind showing the Lox credential
- P: RistrettoPoint,
- id: Scalar,
- CBucket: RistrettoPoint,
- level: Scalar,
- CSince: RistrettoPoint,
- CInvRemain: RistrettoPoint,
- CBlockages: RistrettoPoint,
- CQ: RistrettoPoint,
- // Fields for user blinding of the Migration Key credential
- D: RistrettoPoint,
- EncBucket: (RistrettoPoint, RistrettoPoint),
- // The combined ZKP
- piUser: CompactProof,
- }
- #[derive(Debug)]
- pub struct State {
- d: Scalar,
- D: RistrettoPoint,
- EncBucket: (RistrettoPoint, RistrettoPoint),
- id: Scalar,
- bucket: Scalar,
- }
- #[serde_as]
- #[derive(Serialize, Deserialize, Debug)]
- pub struct Response {
- // The encrypted MAC for the Migration Key credential
- Pk: RistrettoPoint,
- EncQk: (RistrettoPoint, RistrettoPoint),
- // A table of encrypted Migration credentials; the encryption keys
- // are formed from the possible values of Qk (the decrypted form of
- // EncQk)
- #[serde_as(as = "Vec<(_,[_; migration_table::ENC_MIGRATION_BYTES])>")]
- enc_migration_table: HashMap<[u8; 16], [u8; migration_table::ENC_MIGRATION_BYTES]>,
- }
- define_proof! {
- requestproof,
- "Check Blockage Request",
- (bucket, since, invremain, blockages, zbucket, zsince, zinvremain,
- zblockages, negzQ,
- d, ebucket),
- (P, CBucket, CSince, CInvRemain, CBlockages, V, Xbucket, Xsince,
- Xinvremain, Xblockages,
- D, EncBucket0, EncBucket1),
- (A, B):
- // Blind showing of the Lox credential
- CBucket = (bucket*P + zbucket*A),
- CSince = (since*P + zsince*A),
- CInvRemain = (invremain*P + zinvremain*A),
- CBlockages = (blockages*P + zblockages*A),
- V = (zbucket*Xbucket + zsince*Xsince + zinvremain*Xinvremain
- + zblockages*Xblockages + negzQ*A),
- // User blinding of the Migration Key credential
- D = (d*B),
- EncBucket0 = (ebucket*B),
- EncBucket1 = (bucket*B + ebucket*D)
- }
- pub fn request(
- lox_cred: &cred::Lox,
- lox_pub: &IssuerPubKey,
- ) -> 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 trust_level >= MIN_TRUST_LEVEL
- let level: u32 = match scalar_u32(&lox_cred.trust_level) {
- Some(v) => v,
- None => return Err(ProofError::VerificationFailure),
- };
- if level < MIN_TRUST_LEVEL {
- 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 zblockages = 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 CBlockages = lox_cred.blockages * P + &zblockages * 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]
- + zblockages * lox_pub.X[6]
- + &negzQ * Atable;
- // User blinding the Migration Key credential
- // Pick an ElGamal keypair
- let d = Scalar::random(&mut rng);
- let D = &d * Btable;
- // Encrypt the attributes to be blinded (each times the
- // basepoint B) to the public key we just created
- let ebucket = Scalar::random(&mut rng);
- let EncBucket = (&ebucket * Btable, &lox_cred.bucket * Btable + ebucket * D);
- // Construct the proof
- let mut transcript = Transcript::new(b"check blockage request");
- let piUser = requestproof::prove_compact(
- &mut transcript,
- requestproof::ProveAssignments {
- A,
- B,
- P: &P,
- CBucket: &CBucket,
- CSince: &CSince,
- CInvRemain: &CInvRemain,
- CBlockages: &CBlockages,
- V: &V,
- Xbucket: &lox_pub.X[2],
- Xsince: &lox_pub.X[4],
- Xinvremain: &lox_pub.X[5],
- Xblockages: &lox_pub.X[6],
- D: &D,
- EncBucket0: &EncBucket.0,
- EncBucket1: &EncBucket.1,
- bucket: &lox_cred.bucket,
- since: &lox_cred.level_since,
- invremain: &lox_cred.invites_remaining,
- blockages: &lox_cred.blockages,
- zbucket: &zbucket,
- zsince: &zsince,
- zinvremain: &zinvremain,
- zblockages: &zblockages,
- negzQ: &negzQ,
- d: &d,
- ebucket: &ebucket,
- },
- )
- .0;
- Ok((
- Request {
- P,
- id: lox_cred.id,
- CBucket,
- level: lox_cred.trust_level,
- CSince,
- CInvRemain,
- CBlockages,
- CQ,
- D,
- EncBucket,
- piUser,
- },
- State {
- d,
- D,
- EncBucket,
- id: lox_cred.id,
- bucket: lox_cred.bucket,
- },
- ))
- }
- impl BridgeAuth {
- /// Receive a check blockage request
- pub fn handle_check_blockage(&mut self, req: Request) -> Result<Response, ProofError> {
- let A: &RistrettoPoint = &CMZ_A;
- let B: &RistrettoPoint = &CMZ_B;
- let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
- let level: u32 = match scalar_u32(&req.level) {
- Some(v) => v,
- None => return Err(ProofError::VerificationFailure),
- };
- if req.P.is_identity() || level < MIN_TRUST_LEVEL {
- return Err(ProofError::VerificationFailure);
- }
- // Recompute the "error factor" using knowledge of our own
- // (the issuer's) private key instead of knowledge of the
- // hidden attributes
- let Vprime =
- (self.lox_priv.x[0] + self.lox_priv.x[1] * req.id + self.lox_priv.x[3] * req.level)
- * req.P
- + self.lox_priv.x[2] * req.CBucket
- + self.lox_priv.x[4] * req.CSince
- + self.lox_priv.x[5] * req.CInvRemain
- + self.lox_priv.x[6] * req.CBlockages
- - req.CQ;
- // Verify the ZKP
- let mut transcript = Transcript::new(b"check blockage request");
- requestproof::verify_compact(
- &req.piUser,
- &mut transcript,
- requestproof::VerifyAssignments {
- A: &A.compress(),
- B: &B.compress(),
- P: &req.P.compress(),
- CBucket: &req.CBucket.compress(),
- CSince: &req.CSince.compress(),
- CInvRemain: &req.CInvRemain.compress(),
- CBlockages: &req.CBlockages.compress(),
- V: &Vprime.compress(),
- Xbucket: &self.lox_pub.X[2].compress(),
- Xsince: &self.lox_pub.X[4].compress(),
- Xinvremain: &self.lox_pub.X[5].compress(),
- Xblockages: &self.lox_pub.X[6].compress(),
- D: &req.D.compress(),
- EncBucket0: &req.EncBucket.0.compress(),
- EncBucket1: &req.EncBucket.1.compress(),
- },
- )?;
- // Ensure the id has not been seen before in the general id
- // filter, but do not add it, so that the user can potentially
- // run this protocol multiple times.
- if self.id_filter.check(&req.id) == SeenType::Seen {
- return Err(ProofError::VerificationFailure);
- }
- // Compute the encrypted MAC (Pk, EncQk) for the Migration Key
- // credential.
- // Compute the MAC on the visible attributes
- let mut rng = rand::thread_rng();
- let b = Scalar::random(&mut rng);
- let Pk = &b * Btable;
- let Pktable = RistrettoBasepointTable::create(&Pk);
- let Qid = &(self.migrationkey_priv.x[0] + self.migrationkey_priv.x[1] * req.id) * &Pktable;
- // El Gamal encrypt it to the public key req.D
- let s = Scalar::random(&mut rng);
- let EncQkid = (&s * Btable, Qid + s * req.D);
- // Homomorphically compute the part of the MAC corresponding to
- // the blinded attributes
- let tbucket = self.migrationkey_priv.x[2] * b;
- let EncQkBucket = (tbucket * req.EncBucket.0, tbucket * req.EncBucket.1);
- let EncQk = (EncQkid.0 + EncQkBucket.0, EncQkid.1 + EncQkBucket.1);
- Ok(Response {
- Pk,
- EncQk,
- enc_migration_table: self.blockage_migration_table.encrypt_table(
- &req.id,
- &self.bridge_table,
- &Pktable,
- &self.migration_priv,
- &self.migrationkey_priv,
- ),
- })
- }
- }
- /// Handle the response to the request, producing a Migration credential
- /// if successful.
- ///
- /// The Migration credential can then be used in the migration protocol
- /// to actually change buckets
- pub fn handle_response(state: State, resp: Response) -> Result<cred::Migration, ProofError> {
- if resp.Pk.is_identity() {
- return Err(ProofError::VerificationFailure);
- }
- // Decrypt the MAC on the Migration Key credential
- let Qk = resp.EncQk.1 - (state.d * resp.EncQk.0);
- // Use Qk to locate and decrypt the Migration credential
- match migration_table::decrypt_cred(
- &Qk,
- &state.id,
- &state.bucket,
- migration_table::MigrationType::Blockage,
- &resp.enc_migration_table,
- ) {
- Some(m) => Ok(m),
- None => Err(ProofError::VerificationFailure),
- }
- }
|