zkgroup/api/call_links/
auth_credential.rs

1//
2// Copyright 2023 Signal Messenger, LLC.
3// SPDX-License-Identifier: AGPL-3.0-only
4//
5
6//! Provides CallLinkAuthCredential and related types.
7//!
8//! CreateCallLinkCredential is a MAC over:
9//! - the user's ACI (provided by the chat server at issuance, passed encrypted to the calling server for verification)
10//! - a "redemption time", truncated to day granularity (chosen by the chat server at issuance based on parameters from the client, passed publicly to the calling server for verification)
11
12use partial_default::PartialDefault;
13use serde::{Deserialize, Serialize};
14
15use super::{CallLinkPublicParams, CallLinkSecretParams};
16use crate::common::serialization::ReservedByte;
17use crate::common::simple_types::*;
18use crate::crypto::uid_encryption;
19use crate::crypto::uid_struct::UidStruct;
20use crate::generic_server_params::{GenericServerPublicParams, GenericServerSecretParams};
21use crate::groups::UuidCiphertext;
22use crate::ZkGroupVerificationFailure;
23
24const CREDENTIAL_LABEL: &[u8] = b"20230421_Signal_CallLinkAuthCredential";
25
26#[derive(Serialize, Deserialize, PartialDefault)]
27pub struct CallLinkAuthCredentialResponse {
28    reserved: ReservedByte,
29    proof: zkcredential::issuance::IssuanceProof,
30    // Does not include the user ID because the client already knows that.
31    // Does not include the redemption time because that is passed externally.
32}
33
34impl CallLinkAuthCredentialResponse {
35    pub fn issue_credential(
36        user_id: libsignal_core::Aci,
37        redemption_time: Timestamp,
38        params: &GenericServerSecretParams,
39        randomness: RandomnessBytes,
40    ) -> CallLinkAuthCredentialResponse {
41        let proof = zkcredential::issuance::IssuanceProofBuilder::new(CREDENTIAL_LABEL)
42            .add_attribute(&UidStruct::from_service_id(user_id.into()))
43            .add_public_attribute(&redemption_time)
44            .issue(&params.credential_key, randomness);
45        Self {
46            reserved: Default::default(),
47            proof,
48        }
49    }
50
51    pub fn receive(
52        self,
53        user_id: libsignal_core::Aci,
54        redemption_time: Timestamp,
55        params: &GenericServerPublicParams,
56    ) -> Result<CallLinkAuthCredential, ZkGroupVerificationFailure> {
57        if !redemption_time.is_day_aligned() {
58            return Err(ZkGroupVerificationFailure);
59        }
60
61        let raw_credential = zkcredential::issuance::IssuanceProofBuilder::new(CREDENTIAL_LABEL)
62            .add_attribute(&UidStruct::from_service_id(user_id.into()))
63            .add_public_attribute(&redemption_time)
64            .verify(&params.credential_key, self.proof)
65            .map_err(|_| ZkGroupVerificationFailure)?;
66        Ok(CallLinkAuthCredential {
67            reserved: Default::default(),
68            credential: raw_credential,
69        })
70    }
71}
72
73#[derive(Serialize, Deserialize, PartialDefault)]
74pub struct CallLinkAuthCredential {
75    reserved: ReservedByte,
76    credential: zkcredential::credentials::Credential,
77    // Does not include the user ID because the client already knows that.
78    // Does not include the redemption time because that's used as a key to lookup up this credential.
79}
80
81impl CallLinkAuthCredential {
82    pub fn present(
83        &self,
84        user_id: libsignal_core::Aci,
85        redemption_time: Timestamp,
86        server_params: &GenericServerPublicParams,
87        call_link_params: &CallLinkSecretParams,
88        randomness: RandomnessBytes,
89    ) -> CallLinkAuthCredentialPresentation {
90        let uid_attr = UidStruct::from_service_id(user_id.into());
91        let proof = zkcredential::presentation::PresentationProofBuilder::new(CREDENTIAL_LABEL)
92            .add_attribute(&uid_attr, &call_link_params.uid_enc_key_pair)
93            .present(&server_params.credential_key, &self.credential, randomness);
94        CallLinkAuthCredentialPresentation {
95            reserved: Default::default(),
96            proof,
97            ciphertext: call_link_params.uid_enc_key_pair.encrypt(&uid_attr),
98            redemption_time,
99        }
100    }
101}
102
103#[derive(Serialize, Deserialize, PartialDefault)]
104pub struct CallLinkAuthCredentialPresentation {
105    reserved: ReservedByte,
106    pub(crate) proof: zkcredential::presentation::PresentationProof,
107    pub(crate) ciphertext: uid_encryption::Ciphertext,
108    pub(crate) redemption_time: Timestamp,
109}
110
111impl CallLinkAuthCredentialPresentation {
112    pub fn verify(
113        &self,
114        current_time: Timestamp,
115        server_params: &GenericServerSecretParams,
116        call_link_params: &CallLinkPublicParams,
117    ) -> Result<(), ZkGroupVerificationFailure> {
118        crate::ServerSecretParams::check_auth_credential_redemption_time(
119            self.redemption_time,
120            current_time,
121        )?;
122
123        zkcredential::presentation::PresentationProofVerifier::new(CREDENTIAL_LABEL)
124            .add_attribute(&self.ciphertext, &call_link_params.uid_enc_public_key)
125            .add_public_attribute(&self.redemption_time)
126            .verify(&server_params.credential_key, &self.proof)
127            .map_err(|_| ZkGroupVerificationFailure)
128    }
129
130    pub fn get_user_id(&self) -> UuidCiphertext {
131        UuidCiphertext {
132            reserved: Default::default(),
133            ciphertext: self.ciphertext,
134        }
135    }
136}