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(presentation) => self
207                .verify_expiring_profile_key_credential_presentation(
208                    group_public_params,
209                    presentation,
210                    current_time,
211                ),
212        }
213    }
214
215    pub fn verify_expiring_profile_key_credential_presentation(
216        &self,
217        group_public_params: api::groups::GroupPublicParams,
218        presentation: &api::profiles::ExpiringProfileKeyCredentialPresentation,
219        current_time: Timestamp,
220    ) -> Result<(), ZkGroupVerificationFailure> {
221        let credentials_key_pair = self.expiring_profile_key_credentials_key_pair;
222        let uid_enc_public_key = group_public_params.uid_enc_public_key;
223        let profile_key_enc_public_key = group_public_params.profile_key_enc_public_key;
224
225        presentation.proof.verify(
226            credentials_key_pair,
227            presentation.uid_enc_ciphertext,
228            uid_enc_public_key,
229            presentation.profile_key_enc_ciphertext,
230            profile_key_enc_public_key,
231            presentation.credential_expiration_time,
232        )?;
233
234        if presentation.credential_expiration_time <= current_time {
235            return Err(ZkGroupVerificationFailure);
236        }
237
238        Ok(())
239    }
240
241    pub fn issue_expiring_profile_key_credential(
242        &self,
243        randomness: RandomnessBytes,
244        request: &api::profiles::ProfileKeyCredentialRequest,
245        aci: libsignal_core::Aci,
246        commitment: api::profiles::ProfileKeyCommitment,
247        credential_expiration_time: Timestamp,
248    ) -> Result<api::profiles::ExpiringProfileKeyCredentialResponse, ZkGroupVerificationFailure>
249    {
250        let mut sho = Sho::new(
251            b"Signal_ZKGroup_20220508_Random_ServerSecretParams_IssueExpiringProfileKeyCredential",
252            &randomness,
253        );
254
255        request.proof.verify(
256            request.public_key,
257            request.ciphertext,
258            commitment.commitment,
259        )?;
260
261        let uid = crypto::uid_struct::UidStruct::from_service_id(aci.into());
262        let blinded_credential_with_secret_nonce = self
263            .expiring_profile_key_credentials_key_pair
264            .create_blinded_expiring_profile_key_credential(
265                uid,
266                request.public_key,
267                request.ciphertext,
268                credential_expiration_time,
269                &mut sho,
270            );
271
272        let proof = crypto::proofs::ExpiringProfileKeyCredentialIssuanceProof::new(
273            self.expiring_profile_key_credentials_key_pair,
274            request.public_key,
275            request.ciphertext,
276            blinded_credential_with_secret_nonce,
277            uid,
278            credential_expiration_time,
279            &mut sho,
280        );
281
282        Ok(api::profiles::ExpiringProfileKeyCredentialResponse {
283            reserved: Default::default(),
284            blinded_credential: blinded_credential_with_secret_nonce
285                .get_blinded_expiring_profile_key_credential(),
286            credential_expiration_time,
287            proof,
288        })
289    }
290
291    pub fn issue_receipt_credential(
292        &self,
293        randomness: RandomnessBytes,
294        request: &api::receipts::ReceiptCredentialRequest,
295        receipt_expiration_time: Timestamp,
296        receipt_level: ReceiptLevel,
297    ) -> api::receipts::ReceiptCredentialResponse {
298        let mut sho = Sho::new(
299            b"Signal_ZKGroup_20210919_Random_ServerSecretParams_IssueReceiptCredential",
300            &randomness,
301        );
302
303        let blinded_credential_with_secret_nonce = self
304            .receipt_credentials_key_pair
305            .create_blinded_receipt_credential(
306                request.public_key,
307                request.ciphertext,
308                receipt_expiration_time,
309                receipt_level,
310                &mut sho,
311            );
312
313        let proof = crypto::proofs::ReceiptCredentialIssuanceProof::new(
314            self.receipt_credentials_key_pair,
315            request.public_key,
316            request.ciphertext,
317            blinded_credential_with_secret_nonce,
318            receipt_expiration_time,
319            receipt_level,
320            &mut sho,
321        );
322
323        api::receipts::ReceiptCredentialResponse {
324            reserved: Default::default(),
325            receipt_expiration_time,
326            receipt_level,
327            blinded_credential: blinded_credential_with_secret_nonce
328                .get_blinded_receipt_credential(),
329            proof,
330        }
331    }
332
333    pub fn verify_receipt_credential_presentation(
334        &self,
335        presentation: &api::receipts::ReceiptCredentialPresentation,
336    ) -> Result<(), ZkGroupVerificationFailure> {
337        presentation.proof.verify(
338            self.receipt_credentials_key_pair,
339            presentation.get_receipt_struct(),
340        )
341    }
342}
343
344impl ServerPublicParams {
345    pub fn get_endorsement_public_key(&self) -> EndorsementPublicKey {
346        EndorsementPublicKey {
347            reserved: Default::default(),
348            public_key: self.endorsement_public_key.clone(),
349        }
350    }
351
352    pub fn verify_signature(
353        &self,
354        message: &[u8],
355        signature: NotarySignatureBytes,
356    ) -> Result<(), ZkGroupVerificationFailure> {
357        self.sig_public_key.verify(message, signature)
358    }
359
360    pub fn create_profile_key_credential_request_context(
361        &self,
362        randomness: RandomnessBytes,
363        aci: libsignal_core::Aci,
364        profile_key: api::profiles::ProfileKey,
365    ) -> api::profiles::ProfileKeyCredentialRequestContext {
366        let mut sho = Sho::new(
367            b"Signal_ZKGroup_20200424_Random_ServerPublicParams_CreateProfileKeyCredentialRequestContext",
368            &randomness,
369        );
370        let uid_bytes = uuid::Uuid::from(aci).into_bytes();
371        let profile_key_struct =
372            crypto::profile_key_struct::ProfileKeyStruct::new(profile_key.bytes, uid_bytes);
373
374        let commitment_with_secret_nonce =
375            crypto::profile_key_commitment::CommitmentWithSecretNonce::new(
376                profile_key_struct,
377                uid_bytes,
378            );
379
380        let key_pair = crypto::profile_key_credential_request::KeyPair::generate(&mut sho);
381        let ciphertext_with_secret_nonce = key_pair.encrypt(profile_key_struct, &mut sho);
382
383        let proof = crypto::proofs::ProfileKeyCredentialRequestProof::new(
384            key_pair,
385            ciphertext_with_secret_nonce,
386            commitment_with_secret_nonce,
387            &mut sho,
388        );
389
390        api::profiles::ProfileKeyCredentialRequestContext {
391            reserved: Default::default(),
392            aci_bytes: uid_bytes,
393            profile_key_bytes: profile_key_struct.bytes,
394            key_pair,
395            ciphertext_with_secret_nonce,
396            proof,
397        }
398    }
399
400    pub fn receive_expiring_profile_key_credential(
401        &self,
402        context: &api::profiles::ProfileKeyCredentialRequestContext,
403        response: &api::profiles::ExpiringProfileKeyCredentialResponse,
404        current_time: Timestamp,
405    ) -> Result<api::profiles::ExpiringProfileKeyCredential, ZkGroupVerificationFailure> {
406        response.proof.verify(
407            self.expiring_profile_key_credentials_public_key,
408            context.key_pair.get_public_key(),
409            context.aci_bytes,
410            context.ciphertext_with_secret_nonce.get_ciphertext(),
411            response.blinded_credential,
412            response.credential_expiration_time,
413        )?;
414
415        if !response.credential_expiration_time.is_day_aligned() {
416            return Err(ZkGroupVerificationFailure);
417        }
418        let days_remaining = response
419            .credential_expiration_time
420            .saturating_seconds_since(current_time)
421            / SECONDS_PER_DAY;
422        if days_remaining == 0 || days_remaining > 7 {
423            return Err(ZkGroupVerificationFailure);
424        }
425
426        let credential = context
427            .key_pair
428            .decrypt_blinded_expiring_profile_key_credential(response.blinded_credential);
429
430        Ok(api::profiles::ExpiringProfileKeyCredential {
431            reserved: Default::default(),
432            credential,
433            aci_bytes: context.aci_bytes,
434            profile_key_bytes: context.profile_key_bytes,
435            credential_expiration_time: response.credential_expiration_time,
436        })
437    }
438
439    pub fn create_expiring_profile_key_credential_presentation(
440        &self,
441        randomness: RandomnessBytes,
442        group_secret_params: api::groups::GroupSecretParams,
443        expiring_profile_key_credential: api::profiles::ExpiringProfileKeyCredential,
444    ) -> api::profiles::ExpiringProfileKeyCredentialPresentation {
445        let mut sho = Sho::new(
446            b"Signal_ZKGroup_20220508_Random_ServerPublicParams_CreateExpiringProfileKeyCredentialPresentation",
447            &randomness,
448        );
449
450        let uid_enc_key_pair = group_secret_params.uid_enc_key_pair;
451        let profile_key_enc_key_pair = group_secret_params.profile_key_enc_key_pair;
452        let credentials_public_key = self.expiring_profile_key_credentials_public_key;
453
454        let uid = expiring_profile_key_credential.aci();
455        let uuid_ciphertext = group_secret_params.encrypt_service_id(uid.into());
456        let profile_key_ciphertext = group_secret_params
457            .encrypt_profile_key_bytes(expiring_profile_key_credential.profile_key_bytes, uid);
458
459        let proof = crypto::proofs::ExpiringProfileKeyCredentialPresentationProof::new(
460            uid_enc_key_pair,
461            profile_key_enc_key_pair,
462            credentials_public_key,
463            expiring_profile_key_credential.credential,
464            uuid_ciphertext.ciphertext,
465            profile_key_ciphertext.ciphertext,
466            expiring_profile_key_credential.aci_bytes,
467            expiring_profile_key_credential.profile_key_bytes,
468            &mut sho,
469        );
470
471        api::profiles::ExpiringProfileKeyCredentialPresentation {
472            version: VersionByte,
473            proof,
474            uid_enc_ciphertext: uuid_ciphertext.ciphertext,
475            profile_key_enc_ciphertext: profile_key_ciphertext.ciphertext,
476            credential_expiration_time: expiring_profile_key_credential.credential_expiration_time,
477        }
478    }
479
480    pub fn create_receipt_credential_request_context(
481        &self,
482        randomness: RandomnessBytes,
483        receipt_serial_bytes: ReceiptSerialBytes,
484    ) -> api::receipts::ReceiptCredentialRequestContext {
485        let mut sho = Sho::new(
486            b"Signal_ZKGroup_20210919_Random_ServerPublicParams_CreateReceiptCredentialRequestContext",
487            &randomness,
488        );
489
490        let key_pair = crypto::receipt_credential_request::KeyPair::generate(&mut sho);
491        let ciphertext_with_secret_nonce = key_pair.encrypt(receipt_serial_bytes, &mut sho);
492
493        api::receipts::ReceiptCredentialRequestContext {
494            reserved: Default::default(),
495            receipt_serial_bytes,
496            key_pair,
497            ciphertext_with_secret_nonce,
498        }
499    }
500
501    pub fn receive_receipt_credential(
502        &self,
503        context: &api::receipts::ReceiptCredentialRequestContext,
504        response: &api::receipts::ReceiptCredentialResponse,
505    ) -> Result<api::receipts::ReceiptCredential, ZkGroupVerificationFailure> {
506        let receipt_struct = crypto::receipt_struct::ReceiptStruct::new(
507            context.receipt_serial_bytes,
508            response.receipt_expiration_time,
509            response.receipt_level,
510        );
511        response.proof.verify(
512            self.receipt_credentials_public_key,
513            context.key_pair.get_public_key(),
514            context.ciphertext_with_secret_nonce.get_ciphertext(),
515            response.blinded_credential,
516            receipt_struct,
517        )?;
518        let credential = context
519            .key_pair
520            .decrypt_blinded_receipt_credential(response.blinded_credential);
521        Ok(api::receipts::ReceiptCredential {
522            reserved: Default::default(),
523            credential,
524            receipt_expiration_time: response.receipt_expiration_time,
525            receipt_level: response.receipt_level,
526            receipt_serial_bytes: context.receipt_serial_bytes,
527        })
528    }
529
530    pub fn create_receipt_credential_presentation(
531        &self,
532        randomness: RandomnessBytes,
533        receipt_credential: &api::receipts::ReceiptCredential,
534    ) -> api::receipts::ReceiptCredentialPresentation {
535        let mut sho = Sho::new(
536            b"Signal_ZKGroup_20210919_Random_ServerPublicParams_CreateReceiptCredentialPresentation",
537            &randomness,
538        );
539        let proof = crypto::proofs::ReceiptCredentialPresentationProof::new(
540            self.receipt_credentials_public_key,
541            receipt_credential.credential,
542            &mut sho,
543        );
544        api::receipts::ReceiptCredentialPresentation {
545            reserved: Default::default(),
546            proof,
547            receipt_expiration_time: receipt_credential.receipt_expiration_time,
548            receipt_level: receipt_credential.receipt_level,
549            receipt_serial_bytes: receipt_credential.receipt_serial_bytes,
550        }
551    }
552}
553
554#[derive(Clone, Serialize, Deserialize, PartialDefault)]
555pub struct EndorsementServerRootKeyPair {
556    reserved: ReservedByte,
557    pub(crate) key_pair: zkcredential::endorsements::ServerRootKeyPair,
558}
559
560impl AsRef<zkcredential::endorsements::ServerRootKeyPair> for EndorsementServerRootKeyPair {
561    fn as_ref(&self) -> &zkcredential::endorsements::ServerRootKeyPair {
562        &self.key_pair
563    }
564}
565
566impl EndorsementServerRootKeyPair {
567    pub fn public_key(&self) -> EndorsementPublicKey {
568        EndorsementPublicKey {
569            reserved: self.reserved,
570            public_key: self.key_pair.public_key().clone(),
571        }
572    }
573}
574
575#[derive(Clone, Serialize, Deserialize, PartialDefault)]
576pub struct EndorsementPublicKey {
577    reserved: ReservedByte,
578    public_key: zkcredential::endorsements::ServerRootPublicKey,
579}
580
581impl AsRef<zkcredential::endorsements::ServerRootPublicKey> for EndorsementPublicKey {
582    fn as_ref(&self) -> &zkcredential::endorsements::ServerRootPublicKey {
583        &self.public_key
584    }
585}