zkgroup/api/
server_params.rs

1//
2// Copyright 2020-2022 Signal Messenger, LLC.
3// SPDX-License-Identifier: AGPL-3.0-only
4//
5
6use partial_default::PartialDefault;
7use serde::{Deserialize, Serialize};
8
9use crate::common::constants::*;
10use crate::common::errors::*;
11use crate::common::serialization::{ReservedByte, VersionByte};
12use crate::common::sho::*;
13use crate::common::simple_types::*;
14use crate::{api, crypto};
15
16#[derive(Clone, Serialize, Deserialize, PartialDefault)]
17pub struct ServerSecretParams {
18    reserved: ReservedByte,
19    // Now unused
20    auth_credentials_key_pair: crypto::credentials::KeyPair<crypto::credentials::AuthCredential>,
21
22    // Now unused
23    pub(crate) profile_key_credentials_key_pair:
24        crypto::credentials::KeyPair<crypto::credentials::ProfileKeyCredential>,
25
26    sig_key_pair: crypto::signature::KeyPair,
27    receipt_credentials_key_pair:
28        crypto::credentials::KeyPair<crypto::credentials::ReceiptCredential>,
29
30    // Now unused
31    pni_credentials_key_pair: crypto::credentials::KeyPair<crypto::credentials::PniCredential>,
32
33    expiring_profile_key_credentials_key_pair:
34        crypto::credentials::KeyPair<crypto::credentials::ExpiringProfileKeyCredential>,
35    auth_credentials_with_pni_key_pair:
36        crypto::credentials::KeyPair<crypto::credentials::AuthCredentialWithPni>,
37
38    pub(crate) generic_credential_key_pair: zkcredential::credentials::CredentialKeyPair,
39    pub(crate) endorsement_key_pair: zkcredential::endorsements::ServerRootKeyPair,
40}
41
42#[derive(Clone, Serialize, Deserialize, PartialDefault)]
43pub struct ServerPublicParams {
44    reserved: ReservedByte,
45    // Now unused
46    auth_credentials_public_key: crypto::credentials::PublicKey,
47
48    // Now unused
49    pub(crate) profile_key_credentials_public_key: crypto::credentials::PublicKey,
50
51    sig_public_key: crypto::signature::PublicKey,
52    receipt_credentials_public_key: crypto::credentials::PublicKey,
53
54    // Now unused
55    pni_credentials_public_key: crypto::credentials::PublicKey,
56
57    expiring_profile_key_credentials_public_key: crypto::credentials::PublicKey,
58    auth_credentials_with_pni_public_key: crypto::credentials::PublicKey,
59
60    pub(crate) generic_credential_public_key: zkcredential::credentials::CredentialPublicKey,
61    pub(crate) endorsement_public_key: zkcredential::endorsements::ServerRootPublicKey,
62}
63
64impl ServerSecretParams {
65    pub fn generate(randomness: RandomnessBytes) -> Self {
66        let mut sho = Sho::new(
67            b"Signal_ZKGroup_20200424_Random_ServerSecretParams_Generate",
68            &randomness,
69        );
70
71        let auth_credentials_key_pair = crypto::credentials::KeyPair::generate(&mut sho);
72        let profile_key_credentials_key_pair = crypto::credentials::KeyPair::generate(&mut sho);
73        let sig_key_pair = crypto::signature::KeyPair::generate(&mut sho);
74        let receipt_credentials_key_pair = crypto::credentials::KeyPair::generate(&mut sho);
75        let pni_credentials_key_pair = crypto::credentials::KeyPair::generate(&mut sho);
76        let expiring_profile_key_credentials_key_pair =
77            crypto::credentials::KeyPair::generate(&mut sho);
78        let auth_credentials_with_pni_key_pair = crypto::credentials::KeyPair::generate(&mut sho);
79        let generic_credential_key_pair =
80            zkcredential::credentials::CredentialKeyPair::generate(randomness);
81        let endorsement_key_pair =
82            zkcredential::endorsements::ServerRootKeyPair::generate(randomness);
83
84        Self {
85            reserved: Default::default(),
86            auth_credentials_key_pair,
87            profile_key_credentials_key_pair,
88            sig_key_pair,
89            receipt_credentials_key_pair,
90            pni_credentials_key_pair,
91            expiring_profile_key_credentials_key_pair,
92            auth_credentials_with_pni_key_pair,
93            generic_credential_key_pair,
94            endorsement_key_pair,
95        }
96    }
97
98    pub fn get_public_params(&self) -> ServerPublicParams {
99        ServerPublicParams {
100            reserved: Default::default(),
101            auth_credentials_public_key: self.auth_credentials_key_pair.get_public_key(),
102            profile_key_credentials_public_key: self
103                .profile_key_credentials_key_pair
104                .get_public_key(),
105            sig_public_key: self.sig_key_pair.get_public_key(),
106            receipt_credentials_public_key: self.receipt_credentials_key_pair.get_public_key(),
107            pni_credentials_public_key: self.pni_credentials_key_pair.get_public_key(),
108            expiring_profile_key_credentials_public_key: self
109                .expiring_profile_key_credentials_key_pair
110                .get_public_key(),
111            auth_credentials_with_pni_public_key: self
112                .auth_credentials_with_pni_key_pair
113                .get_public_key(),
114            generic_credential_public_key: self.generic_credential_key_pair.public_key().clone(),
115            endorsement_public_key: self.endorsement_key_pair.public_key().clone(),
116        }
117    }
118
119    pub fn sign(&self, randomness: RandomnessBytes, message: &[u8]) -> NotarySignatureBytes {
120        let mut sho = Sho::new(
121            b"Signal_ZKGroup_20200424_Random_ServerSecretParams_Sign",
122            &randomness,
123        );
124        self.sig_key_pair.sign(message, &mut sho)
125    }
126
127    pub fn issue_auth_credential_with_pni_as_service_id(
128        &self,
129        randomness: RandomnessBytes,
130        aci: libsignal_core::Aci,
131        pni: libsignal_core::Pni,
132        redemption_time: Timestamp,
133    ) -> api::auth::AuthCredentialWithPniResponse {
134        let mut sho = Sho::new(
135            b"Signal_ZKGroup_20220617_Random_ServerSecretParams_IssueAuthCredentialWithPni",
136            &randomness,
137        );
138
139        let aci_struct = crypto::uid_struct::UidStruct::from_service_id(aci.into());
140        let pni_struct = crypto::uid_struct::UidStruct::from_service_id(pni.into());
141        let credential = self
142            .auth_credentials_with_pni_key_pair
143            .create_auth_credential_with_pni(aci_struct, pni_struct, redemption_time, &mut sho);
144        let proof = crypto::proofs::AuthCredentialWithPniIssuanceProof::new(
145            self.auth_credentials_with_pni_key_pair,
146            credential,
147            aci_struct,
148            pni_struct,
149            redemption_time,
150            &mut sho,
151        );
152        api::auth::AuthCredentialWithPniV0Response {
153            version: VersionByte,
154            credential,
155            proof,
156        }
157        .into()
158    }
159
160    /// Checks that `current_time` is within the validity window defined by
161    /// `redemption_time`.
162    ///
163    /// All times are relative to SystemTime::UNIX_EPOCH,
164    /// but we don't actually use SystemTime because it's too small on 32-bit Linux.
165    pub(crate) fn check_auth_credential_redemption_time(
166        redemption_time: Timestamp,
167        current_time: Timestamp,
168    ) -> Result<(), ZkGroupVerificationFailure> {
169        let acceptable_start_time = redemption_time
170            .checked_sub_seconds(SECONDS_PER_DAY)
171            .ok_or(ZkGroupVerificationFailure)?;
172        let acceptable_end_time = redemption_time
173            .checked_add_seconds(2 * SECONDS_PER_DAY)
174            .ok_or(ZkGroupVerificationFailure)?;
175
176        if !(acceptable_start_time..=acceptable_end_time).contains(&current_time) {
177            return Err(ZkGroupVerificationFailure);
178        }
179
180        Ok(())
181    }
182
183    pub fn verify_auth_credential_presentation(
184        &self,
185        group_public_params: api::groups::GroupPublicParams,
186        presentation: &api::auth::AnyAuthCredentialPresentation,
187        current_time: Timestamp,
188    ) -> Result<(), ZkGroupVerificationFailure> {
189        Self::check_auth_credential_redemption_time(
190            presentation.get_redemption_time(),
191            current_time,
192        )?;
193
194        match presentation {
195            api::auth::AnyAuthCredentialPresentation::V3(presentation) => {
196                presentation.proof.verify(
197                    self.auth_credentials_with_pni_key_pair,
198                    group_public_params.uid_enc_public_key,
199                    presentation.aci_ciphertext,
200                    presentation.pni_ciphertext,
201                    presentation.redemption_time,
202                )
203            }
204
205            api::auth::AnyAuthCredentialPresentation::V4(presentation) => {
206                presentation.verify(self, &group_public_params, presentation.redemption_time())
207            }
208        }
209    }
210
211    pub fn verify_auth_credential_with_pni_presentation(
212        &self,
213        group_public_params: api::groups::GroupPublicParams,
214        presentation: &api::auth::AuthCredentialWithPniPresentation,
215        current_time: Timestamp,
216    ) -> Result<(), ZkGroupVerificationFailure> {
217        Self::check_auth_credential_redemption_time(
218            presentation.get_redemption_time(),
219            current_time,
220        )?;
221        presentation.proof.verify(
222            self.auth_credentials_with_pni_key_pair,
223            group_public_params.uid_enc_public_key,
224            presentation.aci_ciphertext,
225            presentation.pni_ciphertext,
226            presentation.redemption_time,
227        )
228    }
229
230    pub fn verify_profile_key_credential_presentation(
231        &self,
232        group_public_params: api::groups::GroupPublicParams,
233        presentation: &api::profiles::AnyProfileKeyCredentialPresentation,
234        current_time: Timestamp,
235    ) -> Result<(), ZkGroupVerificationFailure> {
236        match presentation {
237            api::profiles::AnyProfileKeyCredentialPresentation::V1(_) => {
238                Err(ZkGroupVerificationFailure)
239            }
240
241            api::profiles::AnyProfileKeyCredentialPresentation::V2(_) => {
242                Err(ZkGroupVerificationFailure)
243            }
244
245            api::profiles::AnyProfileKeyCredentialPresentation::V3(presentation) => self
246                .verify_expiring_profile_key_credential_presentation(
247                    group_public_params,
248                    presentation,
249                    current_time,
250                ),
251        }
252    }
253
254    pub fn verify_expiring_profile_key_credential_presentation(
255        &self,
256        group_public_params: api::groups::GroupPublicParams,
257        presentation: &api::profiles::ExpiringProfileKeyCredentialPresentation,
258        current_time: Timestamp,
259    ) -> Result<(), ZkGroupVerificationFailure> {
260        let credentials_key_pair = self.expiring_profile_key_credentials_key_pair;
261        let uid_enc_public_key = group_public_params.uid_enc_public_key;
262        let profile_key_enc_public_key = group_public_params.profile_key_enc_public_key;
263
264        presentation.proof.verify(
265            credentials_key_pair,
266            presentation.uid_enc_ciphertext,
267            uid_enc_public_key,
268            presentation.profile_key_enc_ciphertext,
269            profile_key_enc_public_key,
270            presentation.credential_expiration_time,
271        )?;
272
273        if presentation.credential_expiration_time <= current_time {
274            return Err(ZkGroupVerificationFailure);
275        }
276
277        Ok(())
278    }
279
280    pub fn issue_expiring_profile_key_credential(
281        &self,
282        randomness: RandomnessBytes,
283        request: &api::profiles::ProfileKeyCredentialRequest,
284        aci: libsignal_core::Aci,
285        commitment: api::profiles::ProfileKeyCommitment,
286        credential_expiration_time: Timestamp,
287    ) -> Result<api::profiles::ExpiringProfileKeyCredentialResponse, ZkGroupVerificationFailure>
288    {
289        let mut sho = Sho::new(
290            b"Signal_ZKGroup_20220508_Random_ServerSecretParams_IssueExpiringProfileKeyCredential",
291            &randomness,
292        );
293
294        request.proof.verify(
295            request.public_key,
296            request.ciphertext,
297            commitment.commitment,
298        )?;
299
300        let uid = crypto::uid_struct::UidStruct::from_service_id(aci.into());
301        let blinded_credential_with_secret_nonce = self
302            .expiring_profile_key_credentials_key_pair
303            .create_blinded_expiring_profile_key_credential(
304                uid,
305                request.public_key,
306                request.ciphertext,
307                credential_expiration_time,
308                &mut sho,
309            );
310
311        let proof = crypto::proofs::ExpiringProfileKeyCredentialIssuanceProof::new(
312            self.expiring_profile_key_credentials_key_pair,
313            request.public_key,
314            request.ciphertext,
315            blinded_credential_with_secret_nonce,
316            uid,
317            credential_expiration_time,
318            &mut sho,
319        );
320
321        Ok(api::profiles::ExpiringProfileKeyCredentialResponse {
322            reserved: Default::default(),
323            blinded_credential: blinded_credential_with_secret_nonce
324                .get_blinded_expiring_profile_key_credential(),
325            credential_expiration_time,
326            proof,
327        })
328    }
329
330    pub fn issue_receipt_credential(
331        &self,
332        randomness: RandomnessBytes,
333        request: &api::receipts::ReceiptCredentialRequest,
334        receipt_expiration_time: Timestamp,
335        receipt_level: ReceiptLevel,
336    ) -> api::receipts::ReceiptCredentialResponse {
337        let mut sho = Sho::new(
338            b"Signal_ZKGroup_20210919_Random_ServerSecretParams_IssueReceiptCredential",
339            &randomness,
340        );
341
342        let blinded_credential_with_secret_nonce = self
343            .receipt_credentials_key_pair
344            .create_blinded_receipt_credential(
345                request.public_key,
346                request.ciphertext,
347                receipt_expiration_time,
348                receipt_level,
349                &mut sho,
350            );
351
352        let proof = crypto::proofs::ReceiptCredentialIssuanceProof::new(
353            self.receipt_credentials_key_pair,
354            request.public_key,
355            request.ciphertext,
356            blinded_credential_with_secret_nonce,
357            receipt_expiration_time,
358            receipt_level,
359            &mut sho,
360        );
361
362        api::receipts::ReceiptCredentialResponse {
363            reserved: Default::default(),
364            receipt_expiration_time,
365            receipt_level,
366            blinded_credential: blinded_credential_with_secret_nonce
367                .get_blinded_receipt_credential(),
368            proof,
369        }
370    }
371
372    pub fn verify_receipt_credential_presentation(
373        &self,
374        presentation: &api::receipts::ReceiptCredentialPresentation,
375    ) -> Result<(), ZkGroupVerificationFailure> {
376        presentation.proof.verify(
377            self.receipt_credentials_key_pair,
378            presentation.get_receipt_struct(),
379        )
380    }
381}
382
383impl ServerPublicParams {
384    pub fn verify_signature(
385        &self,
386        message: &[u8],
387        signature: NotarySignatureBytes,
388    ) -> Result<(), ZkGroupVerificationFailure> {
389        self.sig_public_key.verify(message, signature)
390    }
391
392    pub fn receive_auth_credential_with_pni_as_service_id(
393        &self,
394        aci: libsignal_core::Aci,
395        pni: libsignal_core::Pni,
396        redemption_time: Timestamp,
397        response: api::auth::AuthCredentialWithPniResponse,
398    ) -> Result<api::auth::AuthCredentialWithPni, ZkGroupVerificationFailure> {
399        let response = match response {
400            api::auth::AuthCredentialWithPniResponse::Zkc(response) => {
401                return response
402                    .receive(aci, pni, redemption_time, self)
403                    .map(Into::into)
404            }
405            api::auth::AuthCredentialWithPniResponse::V0(response) => response,
406        };
407        let aci_struct = crypto::uid_struct::UidStruct::from_service_id(aci.into());
408        let pni_struct = crypto::uid_struct::UidStruct::from_service_id(pni.into());
409        response.proof.verify(
410            self.auth_credentials_with_pni_public_key,
411            response.credential,
412            aci_struct,
413            pni_struct,
414            redemption_time,
415        )?;
416
417        Ok(api::auth::AuthCredentialWithPniV0 {
418            version: VersionByte,
419            credential: response.credential,
420            aci: aci_struct,
421            pni: pni_struct,
422            redemption_time,
423        }
424        .into())
425    }
426
427    pub fn create_auth_credential_with_pni_presentation(
428        &self,
429        randomness: RandomnessBytes,
430        group_secret_params: api::groups::GroupSecretParams,
431        auth_credential: api::auth::AuthCredentialWithPni,
432    ) -> api::auth::AnyAuthCredentialPresentation {
433        let auth_credential = match auth_credential {
434            api::auth::AuthCredentialWithPni::Zkc(auth_credential) => {
435                return auth_credential
436                    .present(self, &group_secret_params, randomness)
437                    .into()
438            }
439            api::auth::AuthCredentialWithPni::V0(auth_credential) => auth_credential,
440        };
441        let mut sho = Sho::new(
442            b"Signal_ZKGroup_20220617_Random_ServerPublicParams_CreateAuthCredentialWithPniPresentation",
443            &randomness,
444        );
445
446        let aci_ciphertext = group_secret_params.encrypt_uid_struct(auth_credential.aci);
447        let pni_ciphertext = group_secret_params.encrypt_uid_struct(auth_credential.pni);
448
449        let proof = crypto::proofs::AuthCredentialWithPniPresentationProof::new(
450            self.auth_credentials_with_pni_public_key,
451            group_secret_params.uid_enc_key_pair,
452            auth_credential.credential,
453            auth_credential.aci,
454            aci_ciphertext.ciphertext,
455            auth_credential.pni,
456            pni_ciphertext.ciphertext,
457            auth_credential.redemption_time,
458            &mut sho,
459        );
460
461        api::auth::AuthCredentialWithPniPresentation {
462            version: VersionByte,
463            proof,
464            aci_ciphertext: aci_ciphertext.ciphertext,
465            pni_ciphertext: pni_ciphertext.ciphertext,
466            redemption_time: auth_credential.redemption_time,
467        }
468        .into()
469    }
470
471    pub fn create_profile_key_credential_request_context(
472        &self,
473        randomness: RandomnessBytes,
474        aci: libsignal_core::Aci,
475        profile_key: api::profiles::ProfileKey,
476    ) -> api::profiles::ProfileKeyCredentialRequestContext {
477        let mut sho = Sho::new(
478            b"Signal_ZKGroup_20200424_Random_ServerPublicParams_CreateProfileKeyCredentialRequestContext",
479            &randomness,
480        );
481        let uid_bytes = uuid::Uuid::from(aci).into_bytes();
482        let profile_key_struct =
483            crypto::profile_key_struct::ProfileKeyStruct::new(profile_key.bytes, uid_bytes);
484
485        let commitment_with_secret_nonce =
486            crypto::profile_key_commitment::CommitmentWithSecretNonce::new(
487                profile_key_struct,
488                uid_bytes,
489            );
490
491        let key_pair = crypto::profile_key_credential_request::KeyPair::generate(&mut sho);
492        let ciphertext_with_secret_nonce = key_pair.encrypt(profile_key_struct, &mut sho);
493
494        let proof = crypto::proofs::ProfileKeyCredentialRequestProof::new(
495            key_pair,
496            ciphertext_with_secret_nonce,
497            commitment_with_secret_nonce,
498            &mut sho,
499        );
500
501        api::profiles::ProfileKeyCredentialRequestContext {
502            reserved: Default::default(),
503            aci_bytes: uid_bytes,
504            profile_key_bytes: profile_key_struct.bytes,
505            key_pair,
506            ciphertext_with_secret_nonce,
507            proof,
508        }
509    }
510
511    pub fn receive_expiring_profile_key_credential(
512        &self,
513        context: &api::profiles::ProfileKeyCredentialRequestContext,
514        response: &api::profiles::ExpiringProfileKeyCredentialResponse,
515        current_time: Timestamp,
516    ) -> Result<api::profiles::ExpiringProfileKeyCredential, ZkGroupVerificationFailure> {
517        response.proof.verify(
518            self.expiring_profile_key_credentials_public_key,
519            context.key_pair.get_public_key(),
520            context.aci_bytes,
521            context.ciphertext_with_secret_nonce.get_ciphertext(),
522            response.blinded_credential,
523            response.credential_expiration_time,
524        )?;
525
526        if !response.credential_expiration_time.is_day_aligned() {
527            return Err(ZkGroupVerificationFailure);
528        }
529        let days_remaining = response
530            .credential_expiration_time
531            .saturating_seconds_since(current_time)
532            / SECONDS_PER_DAY;
533        if days_remaining == 0 || days_remaining > 7 {
534            return Err(ZkGroupVerificationFailure);
535        }
536
537        let credential = context
538            .key_pair
539            .decrypt_blinded_expiring_profile_key_credential(response.blinded_credential);
540
541        Ok(api::profiles::ExpiringProfileKeyCredential {
542            reserved: Default::default(),
543            credential,
544            aci_bytes: context.aci_bytes,
545            profile_key_bytes: context.profile_key_bytes,
546            credential_expiration_time: response.credential_expiration_time,
547        })
548    }
549
550    pub fn create_expiring_profile_key_credential_presentation(
551        &self,
552        randomness: RandomnessBytes,
553        group_secret_params: api::groups::GroupSecretParams,
554        expiring_profile_key_credential: api::profiles::ExpiringProfileKeyCredential,
555    ) -> api::profiles::ExpiringProfileKeyCredentialPresentation {
556        let mut sho = Sho::new(
557            b"Signal_ZKGroup_20220508_Random_ServerPublicParams_CreateExpiringProfileKeyCredentialPresentation",
558            &randomness,
559        );
560
561        let uid_enc_key_pair = group_secret_params.uid_enc_key_pair;
562        let profile_key_enc_key_pair = group_secret_params.profile_key_enc_key_pair;
563        let credentials_public_key = self.expiring_profile_key_credentials_public_key;
564
565        let uid = expiring_profile_key_credential.aci();
566        let uuid_ciphertext = group_secret_params.encrypt_service_id(uid.into());
567        let profile_key_ciphertext = group_secret_params
568            .encrypt_profile_key_bytes(expiring_profile_key_credential.profile_key_bytes, uid);
569
570        let proof = crypto::proofs::ExpiringProfileKeyCredentialPresentationProof::new(
571            uid_enc_key_pair,
572            profile_key_enc_key_pair,
573            credentials_public_key,
574            expiring_profile_key_credential.credential,
575            uuid_ciphertext.ciphertext,
576            profile_key_ciphertext.ciphertext,
577            expiring_profile_key_credential.aci_bytes,
578            expiring_profile_key_credential.profile_key_bytes,
579            &mut sho,
580        );
581
582        api::profiles::ExpiringProfileKeyCredentialPresentation {
583            version: VersionByte,
584            proof,
585            uid_enc_ciphertext: uuid_ciphertext.ciphertext,
586            profile_key_enc_ciphertext: profile_key_ciphertext.ciphertext,
587            credential_expiration_time: expiring_profile_key_credential.credential_expiration_time,
588        }
589    }
590
591    pub fn create_receipt_credential_request_context(
592        &self,
593        randomness: RandomnessBytes,
594        receipt_serial_bytes: ReceiptSerialBytes,
595    ) -> api::receipts::ReceiptCredentialRequestContext {
596        let mut sho = Sho::new(
597            b"Signal_ZKGroup_20210919_Random_ServerPublicParams_CreateReceiptCredentialRequestContext",
598            &randomness,
599        );
600
601        let key_pair = crypto::receipt_credential_request::KeyPair::generate(&mut sho);
602        let ciphertext_with_secret_nonce = key_pair.encrypt(receipt_serial_bytes, &mut sho);
603
604        api::receipts::ReceiptCredentialRequestContext {
605            reserved: Default::default(),
606            receipt_serial_bytes,
607            key_pair,
608            ciphertext_with_secret_nonce,
609        }
610    }
611
612    pub fn receive_receipt_credential(
613        &self,
614        context: &api::receipts::ReceiptCredentialRequestContext,
615        response: &api::receipts::ReceiptCredentialResponse,
616    ) -> Result<api::receipts::ReceiptCredential, ZkGroupVerificationFailure> {
617        let receipt_struct = crypto::receipt_struct::ReceiptStruct::new(
618            context.receipt_serial_bytes,
619            response.receipt_expiration_time,
620            response.receipt_level,
621        );
622        response.proof.verify(
623            self.receipt_credentials_public_key,
624            context.key_pair.get_public_key(),
625            context.ciphertext_with_secret_nonce.get_ciphertext(),
626            response.blinded_credential,
627            receipt_struct,
628        )?;
629        let credential = context
630            .key_pair
631            .decrypt_blinded_receipt_credential(response.blinded_credential);
632        Ok(api::receipts::ReceiptCredential {
633            reserved: Default::default(),
634            credential,
635            receipt_expiration_time: response.receipt_expiration_time,
636            receipt_level: response.receipt_level,
637            receipt_serial_bytes: context.receipt_serial_bytes,
638        })
639    }
640
641    pub fn create_receipt_credential_presentation(
642        &self,
643        randomness: RandomnessBytes,
644        receipt_credential: &api::receipts::ReceiptCredential,
645    ) -> api::receipts::ReceiptCredentialPresentation {
646        let mut sho = Sho::new(
647            b"Signal_ZKGroup_20210919_Random_ServerPublicParams_CreateReceiptCredentialPresentation",
648            &randomness,
649        );
650        let proof = crypto::proofs::ReceiptCredentialPresentationProof::new(
651            self.receipt_credentials_public_key,
652            receipt_credential.credential,
653            &mut sho,
654        );
655        api::receipts::ReceiptCredentialPresentation {
656            reserved: Default::default(),
657            proof,
658            receipt_expiration_time: receipt_credential.receipt_expiration_time,
659            receipt_level: receipt_credential.receipt_level,
660            receipt_serial_bytes: receipt_credential.receipt_serial_bytes,
661        }
662    }
663}