Skip to main content

lox_library/proto/
check_blockage.rs

1/*! A module for the protocol for the user to check for the availability
2of a migration credential they can use in order to move to a new bucket
3if theirs has been blocked.
4
5The user presents their current Lox credential:
6- id: revealed
7- bucket: blinded
8- trust_level: revealed to be 3 or above
9- level_since: blinded
10- invites_remaining: blinded
11- blockages: blinded
12
13They are allowed to to this as long as they are level 3 or above.  If
14they have too many blockages (but are level 3 or above), they will be
15allowed to perform this migration, but will not be able to advance to
16level 3 in their new bucket, so this will be their last allowed
17migration without rejoining the system either with a new invitation or
18an open invitation.
19
20They will receive in return the encrypted MAC (Pk, EncQk) for their
21implicit Migration Key credential with attributes id and bucket,
22along with a HashMap of encrypted Migration credentials.  For each
23(from_i, to_i) in the BA's migration list, there will be an entry in
24the HashMap with key H1(id, from_attr_i, Qk_i) and value
25Enc_{H2(id, from_attr_i, Qk_i)}(to_attr_i, P_i, Q_i).  Here H1 and H2
26are the first 16 bytes and the second 16 bytes respectively of the
27SHA256 hash of the input, P_i and Q_i are a MAC on the Migration
28credential with attributes id, from_attr_i, and to_attr_i. Qk_i is the
29value EncQk would decrypt to if bucket were equal to from_attr_i. */
30
31use curve25519_dalek::ristretto::RistrettoBasepointTable;
32use curve25519_dalek::ristretto::RistrettoPoint;
33use curve25519_dalek::scalar::Scalar;
34use curve25519_dalek::traits::IsIdentity;
35
36use lox_zkp::CompactProof;
37use lox_zkp::ProofError;
38use lox_zkp::Transcript;
39
40use serde::{Deserialize, Serialize};
41use serde_with::serde_as;
42
43use std::collections::HashMap;
44
45use super::super::cred;
46#[cfg(feature = "bridgeauth")]
47use super::super::dup_filter::SeenType;
48use super::super::migration_table;
49use super::super::scalar_u32;
50#[cfg(feature = "bridgeauth")]
51use super::super::BridgeAuth;
52use super::super::IssuerPubKey;
53use super::super::{CMZ_A, CMZ_A_TABLE, CMZ_B, CMZ_B_TABLE};
54
55use super::errors::CredentialError;
56
57/// The minimum trust level a Lox credential must have to be allowed to
58/// perform this protocol.
59pub const MIN_TRUST_LEVEL: u32 = 3;
60
61#[derive(Serialize, Deserialize)]
62pub struct Request {
63    // Fields for blind showing the Lox credential
64    P: RistrettoPoint,
65    id: Scalar,
66    CBucket: RistrettoPoint,
67    level: Scalar,
68    CSince: RistrettoPoint,
69    CInvRemain: RistrettoPoint,
70    CBlockages: RistrettoPoint,
71    CQ: RistrettoPoint,
72
73    // Fields for user blinding of the Migration Key credential
74    D: RistrettoPoint,
75    EncBucket: (RistrettoPoint, RistrettoPoint),
76
77    // The combined lox_zkp
78    piUser: CompactProof,
79}
80
81#[derive(Debug, Serialize, Deserialize)]
82pub struct State {
83    d: Scalar,
84    D: RistrettoPoint,
85    EncBucket: (RistrettoPoint, RistrettoPoint),
86    id: Scalar,
87    bucket: Scalar,
88}
89
90#[serde_as]
91#[derive(Serialize, Deserialize, Debug)]
92pub struct Response {
93    // The encrypted MAC for the Migration Key credential
94    Pk: RistrettoPoint,
95    EncQk: (RistrettoPoint, RistrettoPoint),
96
97    // A table of encrypted Migration credentials; the encryption keys
98    // are formed from the possible values of Qk (the decrypted form of
99    // EncQk)
100    #[serde_as(as = "Vec<(_,[_; migration_table::ENC_MIGRATION_BYTES])>")]
101    enc_migration_table: HashMap<[u8; 16], [u8; migration_table::ENC_MIGRATION_BYTES]>,
102}
103
104define_proof! {
105    requestproof,
106    "Check Blockage Request",
107    (bucket, since, invremain, blockages, zbucket, zsince, zinvremain,
108     zblockages, negzQ,
109     d, ebucket),
110    (P, CBucket, CSince, CInvRemain, CBlockages, V, Xbucket, Xsince,
111     Xinvremain, Xblockages,
112     D, EncBucket0, EncBucket1),
113    (A, B):
114    // Blind showing of the Lox credential
115    CBucket = (bucket*P + zbucket*A),
116    CSince = (since*P + zsince*A),
117    CInvRemain = (invremain*P + zinvremain*A),
118    CBlockages = (blockages*P + zblockages*A),
119    V = (zbucket*Xbucket + zsince*Xsince + zinvremain*Xinvremain
120        + zblockages*Xblockages + negzQ*A),
121    // User blinding of the Migration Key credential
122    D = (d*B),
123    EncBucket0 = (ebucket*B),
124    EncBucket1 = (bucket*B + ebucket*D)
125}
126
127pub fn request(
128    lox_cred: &cred::Lox,
129    lox_pub: &IssuerPubKey,
130) -> Result<(Request, State), CredentialError> {
131    let A: &RistrettoPoint = &CMZ_A;
132    let B: &RistrettoPoint = &CMZ_B;
133    let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE;
134    let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
135
136    // Ensure the credential can be correctly shown: it must be the case
137    // that trust_level >= MIN_TRUST_LEVEL
138    let level: u32 = match scalar_u32(&lox_cred.trust_level) {
139        Some(v) => v,
140        None => {
141            return Err(CredentialError::InvalidField(
142                String::from("trust_level"),
143                String::from("could not be converted to u32"),
144            ))
145        }
146    };
147    if level < MIN_TRUST_LEVEL {
148        return Err(CredentialError::InvalidField(
149            String::from("trust_level"),
150            format!("level {:?} not in range", level),
151        ));
152    }
153
154    // Blind showing the Lox credential
155
156    // Reblind P and Q
157    let mut rng = rand::thread_rng();
158    let t = Scalar::random(&mut rng);
159    let P = t * lox_cred.P;
160    let Q = t * lox_cred.Q;
161
162    // Form Pedersen commitments to the blinded attributes
163    let zbucket = Scalar::random(&mut rng);
164    let zsince = Scalar::random(&mut rng);
165    let zinvremain = Scalar::random(&mut rng);
166    let zblockages = Scalar::random(&mut rng);
167    let CBucket = lox_cred.bucket * P + &zbucket * Atable;
168    let CSince = lox_cred.level_since * P + &zsince * Atable;
169    let CInvRemain = lox_cred.invites_remaining * P + &zinvremain * Atable;
170    let CBlockages = lox_cred.blockages * P + &zblockages * Atable;
171
172    // Form a Pedersen commitment to the MAC Q
173    // We flip the sign of zQ from that of the Hyphae paper so that
174    // the lox_zkp has a "+" instead of a "-", as that's what the lox_zkp
175    // macro supports.
176    let negzQ = Scalar::random(&mut rng);
177    let CQ = Q - &negzQ * Atable;
178
179    // Compute the "error factor"
180    let V = zbucket * lox_pub.X[2]
181        + zsince * lox_pub.X[4]
182        + zinvremain * lox_pub.X[5]
183        + zblockages * lox_pub.X[6]
184        + &negzQ * Atable;
185
186    // User blinding the Migration Key credential
187
188    // Pick an ElGamal keypair
189    let d = Scalar::random(&mut rng);
190    let D = &d * Btable;
191
192    // Encrypt the attributes to be blinded (each times the
193    // basepoint B) to the public key we just created
194    let ebucket = Scalar::random(&mut rng);
195    let EncBucket = (&ebucket * Btable, &lox_cred.bucket * Btable + ebucket * D);
196
197    // Construct the proof
198    let mut transcript = Transcript::new(b"check blockage request");
199    let piUser = requestproof::prove_compact(
200        &mut transcript,
201        requestproof::ProveAssignments {
202            A,
203            B,
204            P: &P,
205            CBucket: &CBucket,
206            CSince: &CSince,
207            CInvRemain: &CInvRemain,
208            CBlockages: &CBlockages,
209            V: &V,
210            Xbucket: &lox_pub.X[2],
211            Xsince: &lox_pub.X[4],
212            Xinvremain: &lox_pub.X[5],
213            Xblockages: &lox_pub.X[6],
214            D: &D,
215            EncBucket0: &EncBucket.0,
216            EncBucket1: &EncBucket.1,
217            bucket: &lox_cred.bucket,
218            since: &lox_cred.level_since,
219            invremain: &lox_cred.invites_remaining,
220            blockages: &lox_cred.blockages,
221            zbucket: &zbucket,
222            zsince: &zsince,
223            zinvremain: &zinvremain,
224            zblockages: &zblockages,
225            negzQ: &negzQ,
226            d: &d,
227            ebucket: &ebucket,
228        },
229    )
230    .0;
231
232    Ok((
233        Request {
234            P,
235            id: lox_cred.id,
236            CBucket,
237            level: lox_cred.trust_level,
238            CSince,
239            CInvRemain,
240            CBlockages,
241            CQ,
242            D,
243            EncBucket,
244            piUser,
245        },
246        State {
247            d,
248            D,
249            EncBucket,
250            id: lox_cred.id,
251            bucket: lox_cred.bucket,
252        },
253    ))
254}
255
256#[cfg(feature = "bridgeauth")]
257impl BridgeAuth {
258    /// Receive a check blockage request
259    pub fn handle_check_blockage(&mut self, req: Request) -> Result<Response, ProofError> {
260        let A: &RistrettoPoint = &CMZ_A;
261        let B: &RistrettoPoint = &CMZ_B;
262        let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
263
264        let level: u32 = match scalar_u32(&req.level) {
265            Some(v) => v,
266            None => return Err(ProofError::VerificationFailure),
267        };
268
269        if req.P.is_identity() || level < MIN_TRUST_LEVEL {
270            return Err(ProofError::VerificationFailure);
271        }
272
273        // Recompute the "error factor" using knowledge of our own
274        // (the issuer's) private key instead of knowledge of the
275        // hidden attributes
276        let Vprime =
277            (self.lox_priv.x[0] + self.lox_priv.x[1] * req.id + self.lox_priv.x[3] * req.level)
278                * req.P
279                + self.lox_priv.x[2] * req.CBucket
280                + self.lox_priv.x[4] * req.CSince
281                + self.lox_priv.x[5] * req.CInvRemain
282                + self.lox_priv.x[6] * req.CBlockages
283                - req.CQ;
284
285        // Verify the zkp
286        let mut transcript = Transcript::new(b"check blockage request");
287        requestproof::verify_compact(
288            &req.piUser,
289            &mut transcript,
290            requestproof::VerifyAssignments {
291                A: &A.compress(),
292                B: &B.compress(),
293                P: &req.P.compress(),
294                CBucket: &req.CBucket.compress(),
295                CSince: &req.CSince.compress(),
296                CInvRemain: &req.CInvRemain.compress(),
297                CBlockages: &req.CBlockages.compress(),
298                V: &Vprime.compress(),
299                Xbucket: &self.lox_pub.X[2].compress(),
300                Xsince: &self.lox_pub.X[4].compress(),
301                Xinvremain: &self.lox_pub.X[5].compress(),
302                Xblockages: &self.lox_pub.X[6].compress(),
303                D: &req.D.compress(),
304                EncBucket0: &req.EncBucket.0.compress(),
305                EncBucket1: &req.EncBucket.1.compress(),
306            },
307        )?;
308
309        // Ensure the id has not been seen before in the general id
310        // filter, but do not add it, so that the user can potentially
311        // run this protocol multiple times.
312        if self.id_filter.check(&req.id) == SeenType::Seen {
313            return Err(ProofError::VerificationFailure);
314        }
315
316        // Compute the encrypted MAC (Pk, EncQk) for the Migration Key
317        // credential.
318
319        // Compute the MAC on the visible attributes
320        let mut rng = rand::thread_rng();
321        let b = Scalar::random(&mut rng);
322        let Pk = &b * Btable;
323        let Pktable = RistrettoBasepointTable::create(&Pk);
324        let Qid = &(self.migrationkey_priv.x[0] + self.migrationkey_priv.x[1] * req.id) * &Pktable;
325
326        // El Gamal encrypt it to the public key req.D
327        let s = Scalar::random(&mut rng);
328        let EncQkid = (&s * Btable, Qid + s * req.D);
329
330        // Homomorphically compute the part of the MAC corresponding to
331        // the blinded attributes
332        let tbucket = self.migrationkey_priv.x[2] * b;
333        let EncQkBucket = (tbucket * req.EncBucket.0, tbucket * req.EncBucket.1);
334
335        let EncQk = (EncQkid.0 + EncQkBucket.0, EncQkid.1 + EncQkBucket.1);
336
337        Ok(Response {
338            Pk,
339            EncQk,
340            enc_migration_table: self.blockage_migration_table.encrypt_table(
341                &req.id,
342                &self.bridge_table,
343                &Pktable,
344                &self.migration_priv,
345                &self.migrationkey_priv,
346            ),
347        })
348    }
349}
350
351/// Handle the response to the request, producing a Migration credential
352/// if successful.
353///
354/// The Migration credential can then be used in the migration protocol
355/// to actually change buckets
356pub fn handle_response(state: State, resp: Response) -> Result<cred::Migration, ProofError> {
357    if resp.Pk.is_identity() {
358        return Err(ProofError::VerificationFailure);
359    }
360
361    // Decrypt the MAC on the Migration Key credential
362    let Qk = resp.EncQk.1 - (state.d * resp.EncQk.0);
363
364    // Use Qk to locate and decrypt the Migration credential
365    match migration_table::decrypt_cred(
366        &Qk,
367        &state.id,
368        &state.bucket,
369        migration_table::MigrationType::Blockage,
370        &resp.enc_migration_table,
371    ) {
372        Some(m) => Ok(m),
373        None => Err(ProofError::VerificationFailure),
374    }
375}