Skip to main content

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