zkgroup/api/call_links/
create_credential.rs1use curve25519_dalek_signal::ristretto::RistrettoPoint;
14use partial_default::PartialDefault;
15use poksho::ShoApi;
16use serde::{Deserialize, Serialize};
17
18use super::{CallLinkPublicParams, CallLinkSecretParams};
19use crate::common::serialization::ReservedByte;
20use crate::common::sho::Sho;
21use crate::common::simple_types::*;
22use crate::crypto::uid_encryption;
23use crate::crypto::uid_struct::UidStruct;
24use crate::generic_server_params::{GenericServerPublicParams, GenericServerSecretParams};
25use crate::groups::UuidCiphertext;
26use crate::ZkGroupVerificationFailure;
27
28#[derive(Serialize, Deserialize, Clone, Copy)]
29struct CallLinkRoomIdPoint(RistrettoPoint);
30
31impl CallLinkRoomIdPoint {
32 fn new(room_id: &[u8]) -> Self {
33 Self(Sho::new(b"20230413_Signal_CallLinkRoomId", room_id).get_point())
34 }
35}
36
37impl zkcredential::attributes::RevealedAttribute for CallLinkRoomIdPoint {
38 fn as_point(&self) -> RistrettoPoint {
39 self.0
40 }
41}
42
43const CREDENTIAL_LABEL: &[u8] = b"20230413_Signal_CreateCallLinkCredential";
44
45#[derive(Serialize, Deserialize, PartialDefault)]
46pub struct CreateCallLinkCredentialRequestContext {
47 reserved: ReservedByte,
48 blinded_room_id: zkcredential::issuance::blind::BlindedPoint,
49 key_pair: zkcredential::issuance::blind::BlindingKeyPair,
50}
51
52impl CreateCallLinkCredentialRequestContext {
53 pub fn new(room_id: &[u8], randomness: RandomnessBytes) -> Self {
54 let mut sho =
55 poksho::ShoHmacSha256::new(b"20230413_Signal_CreateCallLinkCredentialRequest");
56 sho.absorb_and_ratchet(&randomness);
57
58 let key_pair = zkcredential::issuance::blind::BlindingKeyPair::generate(&mut sho);
59 let blinded_room_id = key_pair
60 .blind(&CallLinkRoomIdPoint::new(room_id), &mut sho)
61 .into();
62
63 Self {
64 reserved: Default::default(),
65 blinded_room_id,
66 key_pair,
67 }
68 }
69
70 pub fn get_request(&self) -> CreateCallLinkCredentialRequest {
71 CreateCallLinkCredentialRequest {
72 reserved: Default::default(),
73 blinded_room_id: self.blinded_room_id,
74 public_key: *self.key_pair.public_key(),
75 }
76 }
77}
78
79#[derive(Serialize, Deserialize, PartialDefault)]
80pub struct CreateCallLinkCredentialRequest {
81 reserved: ReservedByte,
82 blinded_room_id: zkcredential::issuance::blind::BlindedPoint,
83 public_key: zkcredential::issuance::blind::BlindingPublicKey,
84 }
94
95impl CreateCallLinkCredentialRequest {
96 pub fn issue(
97 &self,
98 user_id: libsignal_core::Aci,
99 timestamp: Timestamp,
100 params: &GenericServerSecretParams,
101 randomness: RandomnessBytes,
102 ) -> CreateCallLinkCredentialResponse {
103 CreateCallLinkCredentialResponse {
104 reserved: Default::default(),
105 timestamp,
106 blinded_credential: zkcredential::issuance::IssuanceProofBuilder::new(CREDENTIAL_LABEL)
107 .add_public_attribute(×tamp)
108 .add_attribute(&UidStruct::from_service_id(user_id.into()))
109 .add_blinded_revealed_attribute(&self.blinded_room_id)
110 .issue(¶ms.credential_key, &self.public_key, randomness),
111 }
112 }
113}
114
115#[derive(Serialize, Deserialize, PartialDefault)]
116pub struct CreateCallLinkCredentialResponse {
117 reserved: ReservedByte,
118 timestamp: Timestamp,
120 blinded_credential: zkcredential::issuance::blind::BlindedIssuanceProof,
121}
122
123impl CreateCallLinkCredentialRequestContext {
124 pub fn receive(
125 self,
126 response: CreateCallLinkCredentialResponse,
127 user_id: libsignal_core::Aci,
128 params: &GenericServerPublicParams,
129 ) -> Result<CreateCallLinkCredential, ZkGroupVerificationFailure> {
130 if !response.timestamp.is_day_aligned() {
131 return Err(ZkGroupVerificationFailure);
132 }
133
134 Ok(CreateCallLinkCredential {
135 reserved: Default::default(),
136 timestamp: response.timestamp,
137 credential: zkcredential::issuance::IssuanceProofBuilder::new(CREDENTIAL_LABEL)
138 .add_public_attribute(&response.timestamp)
139 .add_attribute(&UidStruct::from_service_id(user_id.into()))
140 .add_blinded_revealed_attribute(&self.blinded_room_id)
141 .verify(
142 ¶ms.credential_key,
143 &self.key_pair,
144 response.blinded_credential,
145 )
146 .map_err(|_| ZkGroupVerificationFailure)?,
147 })
148 }
149}
150
151#[derive(Serialize, Deserialize, PartialDefault)]
152pub struct CreateCallLinkCredential {
153 reserved: ReservedByte,
154 timestamp: Timestamp,
158 credential: zkcredential::credentials::Credential,
159}
160
161impl CreateCallLinkCredential {
162 pub fn present(
163 &self,
164 room_id: &[u8],
165 user_id: libsignal_core::Aci,
166 server_params: &GenericServerPublicParams,
167 call_link_params: &CallLinkSecretParams,
168 randomness: RandomnessBytes,
169 ) -> CreateCallLinkCredentialPresentation {
170 let user_id = UidStruct::from_service_id(user_id.into());
171 let encrypted_user_id = call_link_params.uid_enc_key_pair.encrypt(&user_id);
172 CreateCallLinkCredentialPresentation {
173 reserved: Default::default(),
174 timestamp: self.timestamp,
175 user_id: encrypted_user_id,
176 proof: zkcredential::presentation::PresentationProofBuilder::new(CREDENTIAL_LABEL)
177 .add_attribute(&user_id, &call_link_params.uid_enc_key_pair)
178 .add_revealed_attribute(&CallLinkRoomIdPoint::new(room_id))
179 .present(&server_params.credential_key, &self.credential, randomness),
180 }
181 }
182}
183
184#[derive(Serialize, Deserialize, PartialDefault)]
185pub struct CreateCallLinkCredentialPresentation {
186 reserved: ReservedByte,
187 user_id: zkcredential::attributes::Ciphertext<uid_encryption::UidEncryptionDomain>,
189 timestamp: Timestamp,
190 proof: zkcredential::presentation::PresentationProof,
191}
192
193impl CreateCallLinkCredentialPresentation {
194 pub fn verify(
195 &self,
196 room_id: &[u8],
197 current_time: Timestamp,
198 server_params: &GenericServerSecretParams,
199 call_link_params: &CallLinkPublicParams,
200 ) -> Result<(), ZkGroupVerificationFailure> {
201 let expiration = self
202 .timestamp
203 .checked_add_seconds(30 * 60 * 60) .ok_or(ZkGroupVerificationFailure)?;
205
206 if !(self.timestamp..expiration).contains(¤t_time) {
207 return Err(ZkGroupVerificationFailure);
208 }
209
210 zkcredential::presentation::PresentationProofVerifier::new(CREDENTIAL_LABEL)
211 .add_public_attribute(&self.timestamp)
212 .add_attribute(&self.user_id, &call_link_params.uid_enc_public_key)
213 .add_revealed_attribute(&CallLinkRoomIdPoint::new(room_id))
214 .verify(&server_params.credential_key, &self.proof)
215 .map_err(|_| ZkGroupVerificationFailure)
216 }
217
218 pub fn get_user_id(&self) -> UuidCiphertext {
219 UuidCiphertext {
220 reserved: Default::default(),
221 ciphertext: self.user_id,
222 }
223 }
224}