1use 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 auth_credentials_key_pair: crypto::credentials::KeyPair<crypto::credentials::AuthCredential>,
21
22 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 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 auth_credentials_public_key: crypto::credentials::PublicKey,
47
48 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 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 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(¤t_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}