1use aes_gcm_siv::aead::Aead;
7use aes_gcm_siv::aead::generic_array::GenericArray;
8use aes_gcm_siv::{Aes256GcmSiv, KeyInit};
9use partial_default::PartialDefault;
10use serde::{Deserialize, Serialize};
11
12use crate::common::constants::*;
13use crate::common::errors::*;
14use crate::common::serialization::ReservedByte;
15use crate::common::sho::*;
16use crate::common::simple_types::*;
17use crate::crypto::uid_encryption;
18use crate::{api, crypto};
19
20#[derive(Copy, Clone, Serialize, Deserialize, Default)]
21pub struct GroupMasterKey {
22 pub(crate) bytes: [u8; GROUP_MASTER_KEY_LEN],
23}
24
25#[derive(Copy, Clone, Serialize, Deserialize, PartialDefault)]
26pub struct GroupSecretParams {
27 reserved: ReservedByte,
28 master_key: GroupMasterKey,
29 group_id: GroupIdentifierBytes,
30 blob_key: AesKeyBytes,
31 pub(crate) uid_enc_key_pair: crypto::uid_encryption::KeyPair,
32 pub(crate) profile_key_enc_key_pair: crypto::profile_key_encryption::KeyPair,
33}
34
35impl AsRef<uid_encryption::KeyPair> for GroupSecretParams {
36 fn as_ref(&self) -> &uid_encryption::KeyPair {
37 &self.uid_enc_key_pair
38 }
39}
40
41#[derive(Copy, Clone, Serialize, Deserialize, PartialDefault)]
42pub struct GroupPublicParams {
43 reserved: ReservedByte,
44 group_id: GroupIdentifierBytes,
45 pub(crate) uid_enc_public_key: crypto::uid_encryption::PublicKey,
46 pub(crate) profile_key_enc_public_key: crypto::profile_key_encryption::PublicKey,
47}
48
49impl GroupMasterKey {
50 pub fn new(bytes: [u8; GROUP_MASTER_KEY_LEN]) -> Self {
51 GroupMasterKey { bytes }
52 }
53}
54
55const ENCRYPTED_BLOB_PADDING_LENGTH_SIZE: usize = std::mem::size_of::<u32>();
56
57impl GroupSecretParams {
58 pub fn generate(randomness: RandomnessBytes) -> Self {
59 let mut sho = Sho::new(
60 b"Signal_ZKGroup_20200424_Random_GroupSecretParams_Generate",
61 &randomness,
62 );
63 let master_key = GroupMasterKey::new(sho.squeeze_as_array());
64 GroupSecretParams::derive_from_master_key(master_key)
65 }
66
67 pub fn derive_from_master_key(master_key: GroupMasterKey) -> Self {
68 let mut sho = Sho::new(
69 b"Signal_ZKGroup_20200424_GroupMasterKey_GroupSecretParams_DeriveFromMasterKey",
70 &master_key.bytes,
71 );
72 let group_id: GroupIdentifierBytes = sho.squeeze_as_array();
73 let blob_key: AesKeyBytes = sho.squeeze_as_array();
74 let uid_enc_key_pair = crypto::uid_encryption::KeyPair::derive_from(sho.as_mut());
75 let profile_key_enc_key_pair =
76 crypto::profile_key_encryption::KeyPair::derive_from(sho.as_mut());
77
78 Self {
79 reserved: Default::default(),
80 master_key,
81 group_id,
82 blob_key,
83 uid_enc_key_pair,
84 profile_key_enc_key_pair,
85 }
86 }
87
88 pub fn get_master_key(&self) -> GroupMasterKey {
89 self.master_key
90 }
91
92 pub fn get_group_identifier(&self) -> GroupIdentifierBytes {
93 self.group_id
94 }
95
96 pub fn get_public_params(&self) -> GroupPublicParams {
97 GroupPublicParams {
98 reserved: Default::default(),
99 uid_enc_public_key: self.uid_enc_key_pair.public_key,
100 profile_key_enc_public_key: self.profile_key_enc_key_pair.public_key,
101 group_id: self.group_id,
102 }
103 }
104
105 pub fn encrypt_service_id(
106 &self,
107 service_id: libsignal_core::ServiceId,
108 ) -> api::groups::UuidCiphertext {
109 let uid = crypto::uid_struct::UidStruct::from_service_id(service_id);
110 self.encrypt_uid_struct(uid)
111 }
112
113 pub fn encrypt_uid_struct(
114 &self,
115 uid: crypto::uid_struct::UidStruct,
116 ) -> api::groups::UuidCiphertext {
117 let ciphertext = self.uid_enc_key_pair.encrypt(&uid);
118 api::groups::UuidCiphertext {
119 reserved: Default::default(),
120 ciphertext,
121 }
122 }
123
124 pub fn decrypt_service_id(
125 &self,
126 ciphertext: api::groups::UuidCiphertext,
127 ) -> Result<libsignal_core::ServiceId, ZkGroupVerificationFailure> {
128 crypto::uid_encryption::UidEncryptionDomain::decrypt(
129 &self.uid_enc_key_pair,
130 &ciphertext.ciphertext,
131 )
132 }
133
134 pub fn encrypt_profile_key(
135 &self,
136 profile_key: api::profiles::ProfileKey,
137 user_id: libsignal_core::Aci,
138 ) -> api::groups::ProfileKeyCiphertext {
139 self.encrypt_profile_key_bytes(profile_key.bytes, user_id)
140 }
141
142 pub fn encrypt_profile_key_bytes(
143 &self,
144 profile_key_bytes: ProfileKeyBytes,
145 user_id: libsignal_core::Aci,
146 ) -> api::groups::ProfileKeyCiphertext {
147 let profile_key = crypto::profile_key_struct::ProfileKeyStruct::new(
148 profile_key_bytes,
149 uuid::Uuid::from(user_id).into_bytes(),
150 );
151 let ciphertext = self.profile_key_enc_key_pair.encrypt(&profile_key);
152 api::groups::ProfileKeyCiphertext {
153 reserved: Default::default(),
154 ciphertext,
155 }
156 }
157
158 pub fn decrypt_profile_key(
159 &self,
160 ciphertext: api::groups::ProfileKeyCiphertext,
161 user_id: libsignal_core::Aci,
162 ) -> Result<api::profiles::ProfileKey, ZkGroupVerificationFailure> {
163 let profile_key_struct =
164 crypto::profile_key_encryption::ProfileKeyEncryptionDomain::decrypt(
165 &self.profile_key_enc_key_pair,
166 &ciphertext.ciphertext,
167 uuid::Uuid::from(user_id).into_bytes(),
168 )?;
169 Ok(api::profiles::ProfileKey {
170 bytes: profile_key_struct.bytes,
171 })
172 }
173
174 pub fn encrypt_blob(&self, randomness: RandomnessBytes, plaintext: &[u8]) -> Vec<u8> {
175 let mut sho = Sho::new(
176 b"Signal_ZKGroup_20200424_Random_GroupSecretParams_EncryptBlob",
177 &randomness,
178 );
179 let nonce_vec = sho.squeeze_as_array::<AESGCM_NONCE_LEN>();
180 let mut ciphertext_vec = self.encrypt_blob_aesgcmsiv(&self.blob_key, &nonce_vec, plaintext);
181 ciphertext_vec.extend(nonce_vec);
182 ciphertext_vec.extend([0u8]); ciphertext_vec
184 }
185
186 pub fn encrypt_blob_with_padding(
187 &self,
188 randomness: RandomnessBytes,
189 plaintext: &[u8],
190 padding_len: u32,
191 ) -> Vec<u8> {
192 let full_length =
193 ENCRYPTED_BLOB_PADDING_LENGTH_SIZE + plaintext.len() + padding_len as usize;
194 let mut padded_plaintext = Vec::with_capacity(full_length);
195 padded_plaintext.extend_from_slice(&padding_len.to_be_bytes());
196 padded_plaintext.extend_from_slice(plaintext);
197 padded_plaintext.resize(full_length, 0);
198 self.encrypt_blob(randomness, &padded_plaintext)
199 }
200
201 pub fn decrypt_blob(&self, ciphertext: &[u8]) -> Result<Vec<u8>, ZkGroupVerificationFailure> {
202 if ciphertext.len() < AESGCM_NONCE_LEN + 1 {
203 return Err(ZkGroupVerificationFailure);
205 }
206 let unreserved_len = ciphertext.len() - 1;
207 let (ciphertext, nonce) = ciphertext[..unreserved_len]
208 .split_last_chunk::<AESGCM_NONCE_LEN>()
209 .expect("checked length already");
210 self.decrypt_blob_aesgcmsiv(&self.blob_key, nonce, ciphertext)
211 }
212
213 pub fn decrypt_blob_with_padding(
214 &self,
215 ciphertext: &[u8],
216 ) -> Result<Vec<u8>, ZkGroupVerificationFailure> {
217 let mut decrypted = self.decrypt_blob(ciphertext)?;
218
219 let (padding_len_bytes, plaintext_plus_padding) = decrypted
220 .split_first_chunk::<ENCRYPTED_BLOB_PADDING_LENGTH_SIZE>()
221 .ok_or(ZkGroupVerificationFailure)?;
222
223 let padding_len = u32::from_be_bytes(*padding_len_bytes);
224 if plaintext_plus_padding.len() < padding_len as usize {
225 return Err(ZkGroupVerificationFailure);
226 }
227
228 decrypted.truncate(decrypted.len() - padding_len as usize);
229 decrypted.drain(..ENCRYPTED_BLOB_PADDING_LENGTH_SIZE);
230 Ok(decrypted)
231 }
232
233 fn encrypt_blob_aesgcmsiv(&self, key: &[u8], nonce: &[u8], plaintext: &[u8]) -> Vec<u8> {
234 let key = GenericArray::from_slice(key);
235 let aead_cipher = Aes256GcmSiv::new(key);
236 let nonce = GenericArray::from_slice(nonce);
237 aead_cipher
238 .encrypt(nonce, plaintext)
239 .expect("aead encrypt failure")
240 }
241
242 fn decrypt_blob_aesgcmsiv(
243 &self,
244 key: &[u8],
245 nonce: &[u8],
246 ciphertext: &[u8],
247 ) -> Result<Vec<u8>, ZkGroupVerificationFailure> {
248 if ciphertext.len() < AESGCM_TAG_LEN {
249 return Err(ZkGroupVerificationFailure);
251 }
252 let key = GenericArray::from_slice(key);
253 let aead_cipher = Aes256GcmSiv::new(key);
254 let nonce = GenericArray::from_slice(nonce);
255 match aead_cipher.decrypt(nonce, ciphertext) {
256 Ok(plaintext_vec) => Ok(plaintext_vec),
257 Err(_) => Err(ZkGroupVerificationFailure),
258 }
259 }
260}
261
262impl GroupPublicParams {
263 pub fn get_group_identifier(&self) -> GroupIdentifierBytes {
264 self.group_id
265 }
266}
267
268#[cfg(test)]
269mod tests {
270 use super::*;
271
272 #[test]
273 fn test_aesgcmsiv_vec1() {
274 let group_secret_params = GroupSecretParams::generate([0u8; RANDOMNESS_LEN]);
277
278 let plaintext_vec = vec![
279 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
280 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
281 0x00, 0x00, 0x00, 0x00,
282 ];
283
284 let key = [
285 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
286 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
287 0x00, 0x00, 0x00, 0x00,
288 ];
289
290 let nonce = [
291 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
292 ];
293
294 let ciphertext = [
295 0x4a, 0x6a, 0x9d, 0xb4, 0xc8, 0xc6, 0x54, 0x92, 0x01, 0xb9, 0xed, 0xb5, 0x30, 0x06,
296 0xcb, 0xa8, 0x21, 0xec, 0x9c, 0xf8, 0x50, 0x94, 0x8a, 0x7c, 0x86, 0xc6, 0x8a, 0xc7,
297 0x53, 0x9d, 0x02, 0x7f, 0xe8, 0x19, 0xe6, 0x3a, 0xbc, 0xd0, 0x20, 0xb0, 0x06, 0xa9,
298 0x76, 0x39, 0x76, 0x32, 0xeb, 0x5d,
299 ];
300
301 let calc_ciphertext =
302 group_secret_params.encrypt_blob_aesgcmsiv(&key, &nonce, &plaintext_vec);
303
304 assert!(calc_ciphertext[..ciphertext.len()] == ciphertext[..]);
305
306 let calc_plaintext = group_secret_params
307 .decrypt_blob_aesgcmsiv(&key, &nonce, &calc_ciphertext)
308 .unwrap();
309 assert!(calc_plaintext[..] == plaintext_vec[..]);
310 }
311
312 #[test]
313 fn test_aesgcmsiv_vec2() {
314 let group_secret_params = GroupSecretParams::generate([0u8; RANDOMNESS_LEN]);
317
318 let plaintext_vec = vec![
319 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
320 0x00, 0x00, 0x4d, 0xb9, 0x23, 0xdc, 0x79, 0x3e, 0xe6, 0x49, 0x7c, 0x76, 0xdc, 0xc0,
321 0x3a, 0x98, 0xe1, 0x08,
322 ];
323
324 let key = [
325 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
326 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
327 0x00, 0x00, 0x00, 0x00,
328 ];
329
330 let nonce = [
331 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
332 ];
333
334 let ciphertext = [
335 0xf3, 0xf8, 0x0f, 0x2c, 0xf0, 0xcb, 0x2d, 0xd9, 0xc5, 0x98, 0x4f, 0xcd, 0xa9, 0x08,
336 0x45, 0x6c, 0xc5, 0x37, 0x70, 0x3b, 0x5b, 0xa7, 0x03, 0x24, 0xa6, 0x79, 0x3a, 0x7b,
337 0xf2, 0x18, 0xd3, 0xea, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
338 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
339 ];
340
341 let calc_ciphertext =
342 group_secret_params.encrypt_blob_aesgcmsiv(&key, &nonce, &plaintext_vec);
343
344 assert!(calc_ciphertext[..ciphertext.len()] == ciphertext[..]);
345
346 let calc_plaintext = group_secret_params
347 .decrypt_blob_aesgcmsiv(&key, &nonce, &calc_ciphertext)
348 .unwrap();
349 assert!(calc_plaintext[..] == plaintext_vec[..]);
350 }
351
352 #[test]
353 fn test_encrypt_with_padding() {
354 let group_secret_params = GroupSecretParams::generate([0u8; RANDOMNESS_LEN]);
355 let plaintext = b"secret team";
356
357 {
358 let expected_ciphertext_hex = "3798afe9c65ffb35a63b2c048b16f19dd50ee9acc33cc925667a9abad4d4c6f86675fa8e32243e0831203700";
359
360 let calc_ciphertext =
361 group_secret_params.encrypt_blob_with_padding([0u8; RANDOMNESS_LEN], plaintext, 0);
362 assert_eq!(hex::encode(&calc_ciphertext), expected_ciphertext_hex);
363
364 let calc_plaintext = group_secret_params
365 .decrypt_blob_with_padding(&calc_ciphertext)
366 .unwrap();
367 assert_eq!(calc_plaintext[..], plaintext[..]);
368 }
369
370 {
371 let expected_ciphertext_hex = "880a70e071b33f81e1219842c8514f34901abb734c191292ac325455d898da000484080099c620f86675fa8e32243e0831203700";
372
373 let calc_ciphertext =
374 group_secret_params.encrypt_blob_with_padding([0u8; RANDOMNESS_LEN], plaintext, 8);
375 assert_eq!(hex::encode(&calc_ciphertext), expected_ciphertext_hex);
376
377 let calc_plaintext = group_secret_params
378 .decrypt_blob_with_padding(&calc_ciphertext)
379 .unwrap();
380 assert_eq!(calc_plaintext[..], plaintext[..]);
381 }
382 }
383}