Skip to main content

lox_library/proto/
update_cred.rs

1/*! A module for the protocol for a user to request the issuing of an updated credential after a key rotation has occurred
2
3
4They are allowed to do this as long as their current Lox credential is valid
5
6The user presents their current Lox credential:
7- id: revealed
8- bucket: blinded
9- trust_level: blinded
10- level_since: blinded
11- invites_remaining: blinded
12- blockages: blinded
13
14and a new Lox credential to be issued:
15- id: jointly chosen by the user and BA
16- bucket: blinded, but proved in ZK that it's the same as in the Lox
17  credential above
18- trust_level: blinded, but proved in ZK that it's the same as in the
19  Lox credential above
20- level_since: blinded, but proved in ZK that it's the same as in the
21  Lox credential above
22- invites_remaining: blinded, but proved in ZK that it's the same as in the Lox credential above
23- blockages: blinded, but proved in ZK that it's the same as in the
24  Lox credential above
25
26*/
27
28use curve25519_dalek::ristretto::RistrettoBasepointTable;
29use curve25519_dalek::ristretto::RistrettoPoint;
30use curve25519_dalek::scalar::Scalar;
31use curve25519_dalek::traits::IsIdentity;
32
33use lox_zkp::CompactProof;
34use lox_zkp::ProofError;
35use lox_zkp::Transcript;
36
37use serde::{Deserialize, Serialize};
38
39use super::super::cred;
40#[cfg(feature = "bridgeauth")]
41use super::super::dup_filter::SeenType;
42#[cfg(feature = "bridgeauth")]
43use super::super::BridgeAuth;
44use super::super::IssuerPubKey;
45use super::super::{CMZ_A, CMZ_A_TABLE, CMZ_B, CMZ_B_TABLE};
46
47use super::errors::CredentialError;
48
49#[derive(Serialize, Deserialize)]
50pub struct Request {
51    // Fields for blind showing the Lox credential
52    OldPubKey: IssuerPubKey,
53    P: RistrettoPoint,
54    id: Scalar,
55    CBucket: RistrettoPoint,
56    CLevel: RistrettoPoint,
57    CSince: RistrettoPoint,
58    CInvRemain: RistrettoPoint,
59    CBlockages: RistrettoPoint,
60    CQ: RistrettoPoint,
61
62    // Fields for user blinding of the Lox credential to be issued
63    D: RistrettoPoint,
64    EncIdClient: (RistrettoPoint, RistrettoPoint),
65    EncBucket: (RistrettoPoint, RistrettoPoint),
66    EncLevel: (RistrettoPoint, RistrettoPoint),
67    EncSince: (RistrettoPoint, RistrettoPoint),
68    EncInvRemain: (RistrettoPoint, RistrettoPoint),
69    EncBlockages: (RistrettoPoint, RistrettoPoint),
70
71    // The combined ZKP
72    piUser: CompactProof,
73}
74
75#[derive(Debug, Serialize, Deserialize)]
76pub struct State {
77    d: Scalar,
78    D: RistrettoPoint,
79    EncIdClient: (RistrettoPoint, RistrettoPoint),
80    EncBucket: (RistrettoPoint, RistrettoPoint),
81    EncLevel: (RistrettoPoint, RistrettoPoint),
82    EncSince: (RistrettoPoint, RistrettoPoint),
83    EncInvRemain: (RistrettoPoint, RistrettoPoint),
84    EncBlockages: (RistrettoPoint, RistrettoPoint),
85    id_client: Scalar,
86    bucket: Scalar,
87    level: Scalar,
88    since: Scalar,
89    invremain: Scalar,
90    blockages: Scalar,
91}
92
93#[derive(Serialize, Deserialize)]
94pub struct Response {
95    // The fields for the new Lox credential; the new invites_remaining
96    // is one less than the old value, so we don't have to include it
97    // here explicitly
98    P: RistrettoPoint,
99    EncQ: (RistrettoPoint, RistrettoPoint),
100    id_server: Scalar,
101    TId: RistrettoPoint,
102    TBucket: RistrettoPoint,
103    TLevel: RistrettoPoint,
104    TSince: RistrettoPoint,
105    TInvRemain: RistrettoPoint,
106    TBlockages: RistrettoPoint,
107
108    // The ZKP
109    piBlindIssue: CompactProof,
110}
111
112define_proof! {
113    requestproof,
114    "Update Credential Key Request",
115    (bucket, level, since, invremain, blockages, zbucket, zlevel,
116     zsince, zinvremain, zblockages, negzQ,
117     d, eid_client, ebucket, elevel, esince, einvremain, eblockages, id_client
118    ),
119    (P, CBucket, CLevel, CSince, CInvRemain, CBlockages, V, Xbucket,
120     Xlevel, Xsince, Xinvremain, Xblockages,
121     D, EncIdClient0, EncIdClient1, EncBucket0, EncBucket1,
122     EncLevel0, EncLevel1, EncSince0, EncSince1,
123     EncInvRemain0, EncInvRemain1, EncBlockages0, EncBlockages1
124     ),
125    (A, B):
126    // Blind showing of the Lox credential
127    CBucket = (bucket*P + zbucket*A),
128    CLevel = (level*P + zlevel*A),
129    CSince = (since*P + zsince*A),
130    CInvRemain = (invremain*P + zinvremain*A),
131    CBlockages = (blockages*P + zblockages*A),
132    // User blinding of the Lox credential to be issued
133    D = (d*B),
134    EncIdClient0 = (eid_client*B),
135    EncIdClient1 = (id_client*B + eid_client*D),
136    EncBucket0 = (ebucket*B),
137    EncBucket1 = (bucket*B + ebucket*D),
138    EncLevel0 = (elevel*B),
139    EncLevel1 = (level*B + elevel*D),
140    EncSince0 = (esince*B),
141    EncSince1 = (since*B + esince*D),
142    EncInvRemain0 = (einvremain*B),
143    EncInvRemain1 = (invremain*B + einvremain*D),
144    EncBlockages0 = (eblockages*B),
145    EncBlockages1 = (blockages*B + eblockages*D)
146}
147
148define_proof! {
149    blindissue,
150    "Issue updated cred",
151    (x0, x0tilde, xid, xbucket, xlevel, xsince, xinvremain, xblockages,
152     s, b, tid, tbucket, tlevel, tsince, tinvremain, tblockages),
153    (P, EncQ0, EncQ1, X0, Xid, Xbucket, Xlevel, Xsince, Xinvremain,
154     Xblockages, TId, TBucket, TLevel, TSince, TInvRemain, TBlockages,
155     D, EncId0, EncId1, EncBucket0, EncBucket1, EncLevel0, EncLevel1,
156     EncSince0, EncSince1, EncInvRemain0, EncInvRemain1,
157     EncBlockages0, EncBlockages1
158    ),
159    (A, B):
160    Xid = (xid*A),
161    Xbucket = (xbucket*A),
162    Xlevel = (xlevel*A),
163    Xsince = (xsince*A),
164    Xinvremain = (xinvremain*A),
165    Xblockages = (xblockages*A),
166    X0 = (x0*B + x0tilde*A),
167    P = (b*B),
168    TId = (b*Xid),
169    TId = (tid*A),
170    TBucket = (b*Xbucket),
171    TBucket = (tbucket*A),
172    TLevel = (b*Xlevel),
173    TLevel = (tlevel*A),
174    TSince = (b*Xsince),
175    TSince = (tsince*A),
176    TInvRemain = (b*Xinvremain),
177    TInvRemain = (tinvremain*A),
178    TBlockages = (b*Xblockages),
179    TBlockages = (tblockages*A),
180    EncQ0 = (s*B + tid*EncId0 + tbucket*EncBucket0 + tlevel*EncLevel0
181        + tsince*EncSince0 + tinvremain*EncInvRemain0 + tblockages*EncBlockages0),
182    EncQ1 = (s*D + tid*EncId1 + tbucket*EncBucket1 + tlevel*EncLevel1
183        + tsince*EncSince1 + tinvremain*EncInvRemain1 + tblockages*EncBlockages1
184        + x0*P)
185}
186
187pub fn request(
188    lox_cred: &cred::Lox,
189    lox_pub: &IssuerPubKey,
190) -> Result<(Request, State), CredentialError> {
191    let A: &RistrettoPoint = &CMZ_A;
192    let B: &RistrettoPoint = &CMZ_B;
193    let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE;
194    let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
195
196    // Blind showing the Lox credential
197
198    // Reblind P and Q
199    let mut rng = rand::thread_rng();
200    let t = Scalar::random(&mut rng);
201    let P = t * lox_cred.P;
202    let Q = t * lox_cred.Q;
203
204    // Form Pedersen commitments to the blinded attributes
205    let zbucket = Scalar::random(&mut rng);
206    let zlevel = Scalar::random(&mut rng);
207    let zsince = Scalar::random(&mut rng);
208    let zinvremain = Scalar::random(&mut rng);
209    let zblockages = Scalar::random(&mut rng);
210    let CBucket = lox_cred.bucket * P + &zbucket * Atable;
211    let CLevel = lox_cred.trust_level * P + &zlevel * Atable;
212    let CSince = lox_cred.level_since * P + &zsince * Atable;
213    let CInvRemain = lox_cred.invites_remaining * P + &zinvremain * Atable;
214    let CBlockages = lox_cred.blockages * P + &zblockages * Atable;
215
216    // Form a Pedersen commitment to the MAC Q
217    // We flip the sign of zQ from that of the Hyphae paper so that
218    // the ZKP has a "+" instead of a "-", as that's what the zkp
219    // macro supports.
220    let negzQ = Scalar::random(&mut rng);
221    let CQ = Q - &negzQ * Atable;
222
223    // Compute the "error factor"
224    let V = zbucket * lox_pub.X[2]
225        + zlevel * lox_pub.X[3]
226        + zsince * lox_pub.X[4]
227        + zinvremain * lox_pub.X[5]
228        + zblockages * lox_pub.X[6]
229        + &negzQ * Atable;
230
231    // User blinding for the Lox certificate to be issued
232
233    // Pick an ElGamal keypair
234    let d = Scalar::random(&mut rng);
235    let D = &d * Btable;
236
237    // Pick a random client component of the id
238    let id_client = Scalar::random(&mut rng);
239
240    // Encrypt it (times the basepoint B) to the ElGamal public key D we
241    // just created
242    let eid_client = Scalar::random(&mut rng);
243    let EncIdClient = (&eid_client * Btable, &id_client * Btable + eid_client * D);
244
245    // Encrypt the other blinded fields (times B) to D as well
246    let ebucket = Scalar::random(&mut rng);
247    let EncBucket = (&ebucket * Btable, &lox_cred.bucket * Btable + ebucket * D);
248    let elevel = Scalar::random(&mut rng);
249    let EncLevel = (
250        &elevel * Btable,
251        &lox_cred.trust_level * Btable + elevel * D,
252    );
253    let esince = Scalar::random(&mut rng);
254    let EncSince = (
255        &esince * Btable,
256        &lox_cred.level_since * Btable + esince * D,
257    );
258    let einvremain = Scalar::random(&mut rng);
259    let EncInvRemain = (
260        &einvremain * Btable,
261        &lox_cred.invites_remaining * Btable + einvremain * D,
262    );
263    let eblockages = Scalar::random(&mut rng);
264    let EncBlockages = (
265        &eblockages * Btable,
266        &lox_cred.blockages * Btable + eblockages * D,
267    );
268
269    // Construct the proof
270    let mut transcript = Transcript::new(b"update credential key request");
271    let piUser = requestproof::prove_compact(
272        &mut transcript,
273        requestproof::ProveAssignments {
274            A,
275            B,
276            P: &P,
277            CBucket: &CBucket,
278            CLevel: &CLevel,
279            CSince: &CSince,
280            CInvRemain: &CInvRemain,
281            CBlockages: &CBlockages,
282            V: &V,
283            Xbucket: &lox_pub.X[2],
284            Xlevel: &lox_pub.X[3],
285            Xsince: &lox_pub.X[4],
286            Xinvremain: &lox_pub.X[5],
287            Xblockages: &lox_pub.X[6],
288            D: &D,
289            EncIdClient0: &EncIdClient.0,
290            EncIdClient1: &EncIdClient.1,
291            EncBucket0: &EncBucket.0,
292            EncBucket1: &EncBucket.1,
293            EncLevel0: &EncLevel.0,
294            EncLevel1: &EncLevel.1,
295            EncSince0: &EncSince.0,
296            EncSince1: &EncSince.1,
297            EncInvRemain0: &EncInvRemain.0,
298            EncInvRemain1: &EncInvRemain.1,
299            EncBlockages0: &EncBlockages.0,
300            EncBlockages1: &EncBlockages.1,
301            bucket: &lox_cred.bucket,
302            level: &lox_cred.trust_level,
303            since: &lox_cred.level_since,
304            invremain: &lox_cred.invites_remaining,
305            blockages: &lox_cred.blockages,
306            zbucket: &zbucket,
307            zlevel: &zlevel,
308            zsince: &zsince,
309            zinvremain: &zinvremain,
310            zblockages: &zblockages,
311            negzQ: &negzQ,
312            d: &d,
313            eid_client: &eid_client,
314            ebucket: &ebucket,
315            elevel: &elevel,
316            esince: &esince,
317            einvremain: &einvremain,
318            eblockages: &eblockages,
319            id_client: &id_client,
320        },
321    )
322    .0;
323
324    Ok((
325        Request {
326            OldPubKey: lox_pub.clone(),
327            P,
328            id: lox_cred.id,
329            CBucket,
330            CLevel,
331            CSince,
332            CInvRemain,
333            CBlockages,
334            CQ,
335            D,
336            EncIdClient,
337            EncBucket,
338            EncLevel,
339            EncSince,
340            EncInvRemain,
341            EncBlockages,
342            piUser,
343        },
344        State {
345            d,
346            D,
347            EncIdClient,
348            EncBucket,
349            EncLevel,
350            EncSince,
351            EncInvRemain,
352            EncBlockages,
353            id_client,
354            bucket: lox_cred.bucket,
355            level: lox_cred.trust_level,
356            since: lox_cred.level_since,
357            invremain: lox_cred.invites_remaining,
358            blockages: lox_cred.blockages,
359        },
360    ))
361}
362
363#[cfg(feature = "bridgeauth")]
364impl BridgeAuth {
365    /// Receive an issue invite request
366    pub fn handle_update_cred(&mut self, req: Request) -> Result<Response, ProofError> {
367        let A: &RistrettoPoint = &CMZ_A;
368        let B: &RistrettoPoint = &CMZ_B;
369        let Atable: &RistrettoBasepointTable = &CMZ_A_TABLE;
370        let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
371
372        if req.P.is_identity() {
373            return Err(ProofError::VerificationFailure);
374        }
375
376        // Both of these must be true and should be true after rotate_lox_keys is called
377        if self.old_keys.lox_keys.is_empty() || self.old_filters.lox_filter.is_empty() {
378            return Err(ProofError::VerificationFailure);
379        }
380
381        // calling this function will automatically use the most recent old private key for
382        // verification and the new private key for issuing.
383
384        // Recompute the "error factors" using knowledge of our own
385        // (the issuer's) outdated private key instead of knowledge of the
386        // hidden attributes
387        let old_keys = match self
388            .old_keys
389            .lox_keys
390            .iter()
391            .find(|x| x.pub_key == req.OldPubKey)
392        {
393            Some(old_keys) => old_keys,
394            None => return Err(ProofError::VerificationFailure),
395        };
396        let index = self
397            .old_keys
398            .lox_keys
399            .iter()
400            .position(|x| x.pub_key == old_keys.pub_key)
401            .unwrap();
402
403        let old_priv_key = old_keys.priv_key.clone();
404        let old_pub_key = old_keys.pub_key.clone();
405        let Vprime = (old_priv_key.x[0] + old_priv_key.x[1] * req.id) * req.P
406            + old_priv_key.x[2] * req.CBucket
407            + old_priv_key.x[3] * req.CLevel
408            + old_priv_key.x[4] * req.CSince
409            + old_priv_key.x[5] * req.CInvRemain
410            + old_priv_key.x[6] * req.CBlockages
411            - req.CQ;
412
413        // Verify the ZKP
414        let mut transcript = Transcript::new(b"update credential key request");
415        requestproof::verify_compact(
416            &req.piUser,
417            &mut transcript,
418            requestproof::VerifyAssignments {
419                A: &A.compress(),
420                B: &B.compress(),
421                P: &req.P.compress(),
422                CBucket: &req.CBucket.compress(),
423                CLevel: &req.CLevel.compress(),
424                CSince: &req.CSince.compress(),
425                CInvRemain: &req.CInvRemain.compress(),
426                CBlockages: &req.CBlockages.compress(),
427                V: &Vprime.compress(),
428                Xbucket: &old_pub_key.X[2].compress(),
429                Xlevel: &old_pub_key.X[3].compress(),
430                Xsince: &old_pub_key.X[4].compress(),
431                Xinvremain: &old_pub_key.X[5].compress(),
432                Xblockages: &old_pub_key.X[6].compress(),
433                D: &req.D.compress(),
434                EncIdClient0: &req.EncIdClient.0.compress(),
435                EncIdClient1: &req.EncIdClient.1.compress(),
436                EncBucket0: &req.EncBucket.0.compress(),
437                EncBucket1: &req.EncBucket.1.compress(),
438                EncLevel0: &req.EncLevel.0.compress(),
439                EncLevel1: &req.EncLevel.1.compress(),
440                EncSince0: &req.EncSince.0.compress(),
441                EncSince1: &req.EncSince.1.compress(),
442                EncInvRemain0: &req.EncInvRemain.0.compress(),
443                EncInvRemain1: &req.EncInvRemain.1.compress(),
444                EncBlockages0: &req.EncBlockages.0.compress(),
445                EncBlockages1: &req.EncBlockages.1.compress(),
446            },
447        )?;
448
449        // Check the old_lox_id_filter for the id.
450        // Ensure the id has not been seen before, and add it to the
451        // seen list.
452        if self
453            .old_filters
454            .lox_filter
455            .get_mut(index)
456            .unwrap()
457            .filter(&req.id)
458            == SeenType::Seen
459        {
460            return Err(ProofError::VerificationFailure);
461        }
462
463        // Blind issuing of the new Lox credential using the new key
464
465        // Choose a random server id component to add to the client's
466        // (blinded) id component
467        let mut rng = rand::thread_rng();
468        let id_server = Scalar::random(&mut rng);
469        let EncId = (req.EncIdClient.0, req.EncIdClient.1 + &id_server * Btable);
470
471        // Compute the MAC on the visible attributes (none here)
472        let b = Scalar::random(&mut rng);
473        let P = &b * Btable;
474        let QHc = self.lox_priv.x[0] * P;
475
476        // El Gamal encrypt it to the public key req.D
477        let s = Scalar::random(&mut rng);
478        let EncQHc = (&s * Btable, QHc + s * req.D);
479
480        // Homomorphically compute the part of the MAC corresponding to
481        // the blinded attributes
482        let tid = self.lox_priv.x[1] * b;
483        let TId = &tid * Atable;
484        let EncQId = (tid * EncId.0, tid * EncId.1);
485        let tbucket = self.lox_priv.x[2] * b;
486        let TBucket = &tbucket * Atable;
487        let EncQBucket = (tbucket * req.EncBucket.0, tbucket * req.EncBucket.1);
488        let tlevel = self.lox_priv.x[3] * b;
489        let TLevel = &tlevel * Atable;
490        let EncQLevel = (tlevel * req.EncLevel.0, tlevel * req.EncLevel.1);
491        let tsince = self.lox_priv.x[4] * b;
492        let TSince = &tsince * Atable;
493        let EncQSince = (tsince * req.EncSince.0, tsince * req.EncSince.1);
494        let tinvremain = self.lox_priv.x[5] * b;
495        let TInvRemain = &tinvremain * Atable;
496        let EncQInvRemain = (
497            tinvremain * req.EncInvRemain.0,
498            tinvremain * req.EncInvRemain.1,
499        );
500        let tblockages = self.lox_priv.x[6] * b;
501        let TBlockages = &tblockages * Atable;
502        let EncQBlockages = (
503            tblockages * req.EncBlockages.0,
504            tblockages * req.EncBlockages.1,
505        );
506
507        let EncQ = (
508            EncQHc.0
509                + EncQId.0
510                + EncQBucket.0
511                + EncQLevel.0
512                + EncQSince.0
513                + EncQInvRemain.0
514                + EncQBlockages.0,
515            EncQHc.1
516                + EncQId.1
517                + EncQBucket.1
518                + EncQLevel.1
519                + EncQSince.1
520                + EncQInvRemain.1
521                + EncQBlockages.1,
522        );
523
524        let mut transcript = Transcript::new(b"issue updated cred");
525        let piBlindIssue = blindissue::prove_compact(
526            &mut transcript,
527            blindissue::ProveAssignments {
528                A,
529                B,
530                P: &P,
531                EncQ0: &EncQ.0,
532                EncQ1: &EncQ.1,
533                X0: &self.lox_pub.X[0],
534                Xid: &self.lox_pub.X[1],
535                Xbucket: &self.lox_pub.X[2],
536                Xlevel: &self.lox_pub.X[3],
537                Xsince: &self.lox_pub.X[4],
538                Xinvremain: &self.lox_pub.X[5],
539                Xblockages: &self.lox_pub.X[6],
540                TId: &TId,
541                TBucket: &TBucket,
542                TLevel: &TLevel,
543                TSince: &TSince,
544                TInvRemain: &TInvRemain,
545                TBlockages: &TBlockages,
546                D: &req.D,
547                EncId0: &EncId.0,
548                EncId1: &EncId.1,
549                EncBucket0: &req.EncBucket.0,
550                EncBucket1: &req.EncBucket.1,
551                EncLevel0: &req.EncLevel.0,
552                EncLevel1: &req.EncLevel.1,
553                EncSince0: &req.EncSince.0,
554                EncSince1: &req.EncSince.1,
555                EncInvRemain0: &req.EncInvRemain.0,
556                EncInvRemain1: &req.EncInvRemain.1,
557                EncBlockages0: &req.EncBlockages.0,
558                EncBlockages1: &req.EncBlockages.1,
559                x0: &self.lox_priv.x[0],
560                x0tilde: &self.lox_priv.x0tilde,
561                xid: &self.lox_priv.x[1],
562                xbucket: &self.lox_priv.x[2],
563                xlevel: &self.lox_priv.x[3],
564                xsince: &self.lox_priv.x[4],
565                xinvremain: &self.lox_priv.x[5],
566                xblockages: &self.lox_priv.x[6],
567                s: &s,
568                b: &b,
569                tid: &tid,
570                tbucket: &tbucket,
571                tlevel: &tlevel,
572                tsince: &tsince,
573                tinvremain: &tinvremain,
574                tblockages: &tblockages,
575            },
576        )
577        .0;
578
579        Ok(Response {
580            P,
581            EncQ,
582            id_server,
583            TId,
584            TBucket,
585            TLevel,
586            TSince,
587            TInvRemain,
588            TBlockages,
589            piBlindIssue,
590        })
591    }
592}
593
594/// Handle the response to the request, producing the new Lox credential
595/// and Invitation credential if successful.
596pub fn handle_response(
597    state: State,
598    resp: Response,
599    lox_pub: &IssuerPubKey,
600) -> Result<cred::Lox, ProofError> {
601    let A: &RistrettoPoint = &CMZ_A;
602    let B: &RistrettoPoint = &CMZ_B;
603    let Btable: &RistrettoBasepointTable = &CMZ_B_TABLE;
604
605    if resp.P.is_identity() {
606        return Err(ProofError::VerificationFailure);
607    }
608
609    // Add the server's contribution to the id to our own, both in plain
610    // and encrypted form and for both the Lox credential id and the
611    // Invitation credential id
612    let id = state.id_client + resp.id_server;
613    let EncId = (
614        state.EncIdClient.0,
615        state.EncIdClient.1 + &resp.id_server * Btable,
616    );
617
618    // Verify the proof
619    let mut transcript = Transcript::new(b"issue updated cred");
620    blindissue::verify_compact(
621        &resp.piBlindIssue,
622        &mut transcript,
623        blindissue::VerifyAssignments {
624            A: &A.compress(),
625            B: &B.compress(),
626            P: &resp.P.compress(),
627            EncQ0: &resp.EncQ.0.compress(),
628            EncQ1: &resp.EncQ.1.compress(),
629            X0: &lox_pub.X[0].compress(),
630            Xid: &lox_pub.X[1].compress(),
631            Xbucket: &lox_pub.X[2].compress(),
632            Xlevel: &lox_pub.X[3].compress(),
633            Xsince: &lox_pub.X[4].compress(),
634            Xinvremain: &lox_pub.X[5].compress(),
635            Xblockages: &lox_pub.X[6].compress(),
636            TId: &resp.TId.compress(),
637            TBucket: &resp.TBucket.compress(),
638            TLevel: &resp.TLevel.compress(),
639            TSince: &resp.TSince.compress(),
640            TInvRemain: &resp.TInvRemain.compress(),
641            TBlockages: &resp.TBlockages.compress(),
642            D: &state.D.compress(),
643            EncId0: &EncId.0.compress(),
644            EncId1: &EncId.1.compress(),
645            EncBucket0: &state.EncBucket.0.compress(),
646            EncBucket1: &state.EncBucket.1.compress(),
647            EncLevel0: &state.EncLevel.0.compress(),
648            EncLevel1: &state.EncLevel.1.compress(),
649            EncSince0: &state.EncSince.0.compress(),
650            EncSince1: &state.EncSince.1.compress(),
651            EncInvRemain0: &state.EncInvRemain.0.compress(),
652            EncInvRemain1: &state.EncInvRemain.1.compress(),
653            EncBlockages0: &state.EncBlockages.0.compress(),
654            EncBlockages1: &state.EncBlockages.1.compress(),
655        },
656    )?;
657
658    // Decrypt EncQ and EncQ_inv
659    let Q = resp.EncQ.1 - (state.d * resp.EncQ.0);
660
661    Ok(cred::Lox {
662        P: resp.P,
663        Q,
664        id,
665        bucket: state.bucket,
666        trust_level: state.level,
667        level_since: state.since,
668        invites_remaining: state.invremain,
669        blockages: state.blockages,
670    })
671}