zkgroup/api/groups/
group_params.rs

1//
2// Copyright 2020 Signal Messenger, LLC.
3// SPDX-License-Identifier: AGPL-3.0-only
4//
5
6use 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]); // reserved byte
183        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            // AESGCM_NONCE_LEN = 12 bytes for IV
204            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            // AESGCM_TAG_LEN = 16 bytes for tag
250            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        // https://tools.ietf.org/html/rfc8452#appendix-C
275
276        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        // https://tools.ietf.org/html/rfc8452#appendix-C
315
316        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}