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