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::generic_array::GenericArray;
7use aes_gcm_siv::aead::Aead;
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 nonce = &ciphertext[unreserved_len - AESGCM_NONCE_LEN..unreserved_len];
208        let ciphertext = &ciphertext[..unreserved_len - AESGCM_NONCE_LEN];
209        self.decrypt_blob_aesgcmsiv(&self.blob_key, nonce, ciphertext)
210    }
211
212    pub fn decrypt_blob_with_padding(
213        &self,
214        ciphertext: &[u8],
215    ) -> Result<Vec<u8>, ZkGroupVerificationFailure> {
216        let mut decrypted = self.decrypt_blob(ciphertext)?;
217
218        if decrypted.len() < ENCRYPTED_BLOB_PADDING_LENGTH_SIZE {
219            return Err(ZkGroupVerificationFailure);
220        }
221        let (padding_len_bytes, plaintext_plus_padding) =
222            decrypted.split_at(ENCRYPTED_BLOB_PADDING_LENGTH_SIZE);
223
224        let padding_len = u32::from_be_bytes(padding_len_bytes.try_into().expect("correct size"));
225        if plaintext_plus_padding.len() < padding_len as usize {
226            return Err(ZkGroupVerificationFailure);
227        }
228
229        decrypted.truncate(decrypted.len() - padding_len as usize);
230        decrypted.drain(..ENCRYPTED_BLOB_PADDING_LENGTH_SIZE);
231        Ok(decrypted)
232    }
233
234    fn encrypt_blob_aesgcmsiv(&self, key: &[u8], nonce: &[u8], plaintext: &[u8]) -> Vec<u8> {
235        let key = GenericArray::from_slice(key);
236        let aead_cipher = Aes256GcmSiv::new(key);
237        let nonce = GenericArray::from_slice(nonce);
238        aead_cipher
239            .encrypt(nonce, plaintext)
240            .expect("aead encrypt failure")
241    }
242
243    fn decrypt_blob_aesgcmsiv(
244        &self,
245        key: &[u8],
246        nonce: &[u8],
247        ciphertext: &[u8],
248    ) -> Result<Vec<u8>, ZkGroupVerificationFailure> {
249        if ciphertext.len() < AESGCM_TAG_LEN {
250            // AESGCM_TAG_LEN = 16 bytes for tag
251            return Err(ZkGroupVerificationFailure);
252        }
253        let key = GenericArray::from_slice(key);
254        let aead_cipher = Aes256GcmSiv::new(key);
255        let nonce = GenericArray::from_slice(nonce);
256        match aead_cipher.decrypt(nonce, ciphertext) {
257            Ok(plaintext_vec) => Ok(plaintext_vec),
258            Err(_) => Err(ZkGroupVerificationFailure),
259        }
260    }
261}
262
263impl GroupPublicParams {
264    pub fn get_group_identifier(&self) -> GroupIdentifierBytes {
265        self.group_id
266    }
267}
268
269#[cfg(test)]
270mod tests {
271    use super::*;
272
273    #[test]
274    fn test_aesgcmsiv_vec1() {
275        // https://tools.ietf.org/html/rfc8452#appendix-C
276
277        let group_secret_params = GroupSecretParams::generate([0u8; RANDOMNESS_LEN]);
278
279        let plaintext_vec = vec![
280            0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
281            0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
282            0x00, 0x00, 0x00, 0x00,
283        ];
284
285        let key = [
286            0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
287            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
288            0x00, 0x00, 0x00, 0x00,
289        ];
290
291        let nonce = [
292            0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
293        ];
294
295        let ciphertext = [
296            0x4a, 0x6a, 0x9d, 0xb4, 0xc8, 0xc6, 0x54, 0x92, 0x01, 0xb9, 0xed, 0xb5, 0x30, 0x06,
297            0xcb, 0xa8, 0x21, 0xec, 0x9c, 0xf8, 0x50, 0x94, 0x8a, 0x7c, 0x86, 0xc6, 0x8a, 0xc7,
298            0x53, 0x9d, 0x02, 0x7f, 0xe8, 0x19, 0xe6, 0x3a, 0xbc, 0xd0, 0x20, 0xb0, 0x06, 0xa9,
299            0x76, 0x39, 0x76, 0x32, 0xeb, 0x5d,
300        ];
301
302        let calc_ciphertext =
303            group_secret_params.encrypt_blob_aesgcmsiv(&key, &nonce, &plaintext_vec);
304
305        assert!(calc_ciphertext[..ciphertext.len()] == ciphertext[..]);
306
307        let calc_plaintext = group_secret_params
308            .decrypt_blob_aesgcmsiv(&key, &nonce, &calc_ciphertext)
309            .unwrap();
310        assert!(calc_plaintext[..] == plaintext_vec[..]);
311    }
312
313    #[test]
314    fn test_aesgcmsiv_vec2() {
315        // https://tools.ietf.org/html/rfc8452#appendix-C
316
317        let group_secret_params = GroupSecretParams::generate([0u8; RANDOMNESS_LEN]);
318
319        let plaintext_vec = vec![
320            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
321            0x00, 0x00, 0x4d, 0xb9, 0x23, 0xdc, 0x79, 0x3e, 0xe6, 0x49, 0x7c, 0x76, 0xdc, 0xc0,
322            0x3a, 0x98, 0xe1, 0x08,
323        ];
324
325        let key = [
326            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
327            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
328            0x00, 0x00, 0x00, 0x00,
329        ];
330
331        let nonce = [
332            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
333        ];
334
335        let ciphertext = [
336            0xf3, 0xf8, 0x0f, 0x2c, 0xf0, 0xcb, 0x2d, 0xd9, 0xc5, 0x98, 0x4f, 0xcd, 0xa9, 0x08,
337            0x45, 0x6c, 0xc5, 0x37, 0x70, 0x3b, 0x5b, 0xa7, 0x03, 0x24, 0xa6, 0x79, 0x3a, 0x7b,
338            0xf2, 0x18, 0xd3, 0xea, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
339            0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
340        ];
341
342        let calc_ciphertext =
343            group_secret_params.encrypt_blob_aesgcmsiv(&key, &nonce, &plaintext_vec);
344
345        assert!(calc_ciphertext[..ciphertext.len()] == ciphertext[..]);
346
347        let calc_plaintext = group_secret_params
348            .decrypt_blob_aesgcmsiv(&key, &nonce, &calc_ciphertext)
349            .unwrap();
350        assert!(calc_plaintext[..] == plaintext_vec[..]);
351    }
352
353    #[test]
354    fn test_encrypt_with_padding() {
355        let group_secret_params = GroupSecretParams::generate([0u8; RANDOMNESS_LEN]);
356        let plaintext = b"secret team";
357
358        {
359            let expected_ciphertext_hex = "3798afe9c65ffb35a63b2c048b16f19dd50ee9acc33cc925667a9abad4d4c6f86675fa8e32243e0831203700";
360
361            let calc_ciphertext =
362                group_secret_params.encrypt_blob_with_padding([0u8; RANDOMNESS_LEN], plaintext, 0);
363            assert_eq!(hex::encode(&calc_ciphertext), expected_ciphertext_hex);
364
365            let calc_plaintext = group_secret_params
366                .decrypt_blob_with_padding(&calc_ciphertext)
367                .unwrap();
368            assert_eq!(calc_plaintext[..], plaintext[..]);
369        }
370
371        {
372            let expected_ciphertext_hex = "880a70e071b33f81e1219842c8514f34901abb734c191292ac325455d898da000484080099c620f86675fa8e32243e0831203700";
373
374            let calc_ciphertext =
375                group_secret_params.encrypt_blob_with_padding([0u8; RANDOMNESS_LEN], plaintext, 8);
376            assert_eq!(hex::encode(&calc_ciphertext), expected_ciphertext_hex);
377
378            let calc_plaintext = group_secret_params
379                .decrypt_blob_with_padding(&calc_ciphertext)
380                .unwrap();
381            assert_eq!(calc_plaintext[..], plaintext[..]);
382        }
383    }
384}