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::{api, crypto};
18
19#[derive(Copy, Clone, Serialize, Deserialize, Default)]
20pub struct GroupMasterKey {
21    pub(crate) bytes: [u8; GROUP_MASTER_KEY_LEN],
22}
23
24#[derive(Copy, Clone, Serialize, Deserialize, PartialDefault)]
25pub struct GroupSecretParams {
26    reserved: ReservedByte,
27    master_key: GroupMasterKey,
28    group_id: GroupIdentifierBytes,
29    blob_key: AesKeyBytes,
30    pub(crate) uid_enc_key_pair: crypto::uid_encryption::KeyPair,
31    pub(crate) profile_key_enc_key_pair: crypto::profile_key_encryption::KeyPair,
32}
33
34#[derive(Copy, Clone, Serialize, Deserialize, PartialDefault)]
35pub struct GroupPublicParams {
36    reserved: ReservedByte,
37    group_id: GroupIdentifierBytes,
38    pub(crate) uid_enc_public_key: crypto::uid_encryption::PublicKey,
39    pub(crate) profile_key_enc_public_key: crypto::profile_key_encryption::PublicKey,
40}
41
42impl GroupMasterKey {
43    pub fn new(bytes: [u8; GROUP_MASTER_KEY_LEN]) -> Self {
44        GroupMasterKey { bytes }
45    }
46}
47
48const ENCRYPTED_BLOB_PADDING_LENGTH_SIZE: usize = std::mem::size_of::<u32>();
49
50impl GroupSecretParams {
51    pub fn generate(randomness: RandomnessBytes) -> Self {
52        let mut sho = Sho::new(
53            b"Signal_ZKGroup_20200424_Random_GroupSecretParams_Generate",
54            &randomness,
55        );
56        let mut master_key: GroupMasterKey = Default::default();
57        master_key
58            .bytes
59            .copy_from_slice(&sho.squeeze(GROUP_MASTER_KEY_LEN)[..]);
60        GroupSecretParams::derive_from_master_key(master_key)
61    }
62
63    pub fn derive_from_master_key(master_key: GroupMasterKey) -> Self {
64        let mut sho = Sho::new(
65            b"Signal_ZKGroup_20200424_GroupMasterKey_GroupSecretParams_DeriveFromMasterKey",
66            &master_key.bytes,
67        );
68        let mut group_id: GroupIdentifierBytes = Default::default();
69        let mut blob_key: AesKeyBytes = Default::default();
70        group_id.copy_from_slice(&sho.squeeze(GROUP_IDENTIFIER_LEN)[..]);
71        blob_key.copy_from_slice(&sho.squeeze(AES_KEY_LEN)[..]);
72        let uid_enc_key_pair = crypto::uid_encryption::KeyPair::derive_from(sho.as_mut());
73        let profile_key_enc_key_pair =
74            crypto::profile_key_encryption::KeyPair::derive_from(sho.as_mut());
75
76        Self {
77            reserved: Default::default(),
78            master_key,
79            group_id,
80            blob_key,
81            uid_enc_key_pair,
82            profile_key_enc_key_pair,
83        }
84    }
85
86    pub fn get_master_key(&self) -> GroupMasterKey {
87        self.master_key
88    }
89
90    pub fn get_group_identifier(&self) -> GroupIdentifierBytes {
91        self.group_id
92    }
93
94    pub fn get_public_params(&self) -> GroupPublicParams {
95        GroupPublicParams {
96            reserved: Default::default(),
97            uid_enc_public_key: self.uid_enc_key_pair.public_key,
98            profile_key_enc_public_key: self.profile_key_enc_key_pair.public_key,
99            group_id: self.group_id,
100        }
101    }
102
103    pub fn encrypt_service_id(
104        &self,
105        service_id: libsignal_core::ServiceId,
106    ) -> api::groups::UuidCiphertext {
107        let uid = crypto::uid_struct::UidStruct::from_service_id(service_id);
108        self.encrypt_uid_struct(uid)
109    }
110
111    pub fn encrypt_uid_struct(
112        &self,
113        uid: crypto::uid_struct::UidStruct,
114    ) -> api::groups::UuidCiphertext {
115        let ciphertext = self.uid_enc_key_pair.encrypt(&uid);
116        api::groups::UuidCiphertext {
117            reserved: Default::default(),
118            ciphertext,
119        }
120    }
121
122    pub fn decrypt_service_id(
123        &self,
124        ciphertext: api::groups::UuidCiphertext,
125    ) -> Result<libsignal_core::ServiceId, ZkGroupVerificationFailure> {
126        crypto::uid_encryption::UidEncryptionDomain::decrypt(
127            &self.uid_enc_key_pair,
128            &ciphertext.ciphertext,
129        )
130    }
131
132    pub fn encrypt_profile_key(
133        &self,
134        profile_key: api::profiles::ProfileKey,
135        user_id: libsignal_core::Aci,
136    ) -> api::groups::ProfileKeyCiphertext {
137        self.encrypt_profile_key_bytes(profile_key.bytes, user_id)
138    }
139
140    pub fn encrypt_profile_key_bytes(
141        &self,
142        profile_key_bytes: ProfileKeyBytes,
143        user_id: libsignal_core::Aci,
144    ) -> api::groups::ProfileKeyCiphertext {
145        let profile_key = crypto::profile_key_struct::ProfileKeyStruct::new(
146            profile_key_bytes,
147            uuid::Uuid::from(user_id).into_bytes(),
148        );
149        let ciphertext = self.profile_key_enc_key_pair.encrypt(&profile_key);
150        api::groups::ProfileKeyCiphertext {
151            reserved: Default::default(),
152            ciphertext,
153        }
154    }
155
156    pub fn decrypt_profile_key(
157        &self,
158        ciphertext: api::groups::ProfileKeyCiphertext,
159        user_id: libsignal_core::Aci,
160    ) -> Result<api::profiles::ProfileKey, ZkGroupVerificationFailure> {
161        let profile_key_struct =
162            crypto::profile_key_encryption::ProfileKeyEncryptionDomain::decrypt(
163                &self.profile_key_enc_key_pair,
164                &ciphertext.ciphertext,
165                uuid::Uuid::from(user_id).into_bytes(),
166            )?;
167        Ok(api::profiles::ProfileKey {
168            bytes: profile_key_struct.bytes,
169        })
170    }
171
172    pub fn encrypt_blob(&self, randomness: RandomnessBytes, plaintext: &[u8]) -> Vec<u8> {
173        let mut sho = Sho::new(
174            b"Signal_ZKGroup_20200424_Random_GroupSecretParams_EncryptBlob",
175            &randomness,
176        );
177        let nonce_vec = sho.squeeze(AESGCM_NONCE_LEN);
178        let mut ciphertext_vec =
179            self.encrypt_blob_aesgcmsiv(&self.blob_key, &nonce_vec[..], plaintext);
180        ciphertext_vec.extend(nonce_vec);
181        ciphertext_vec.extend([0u8]); // reserved byte
182        ciphertext_vec
183    }
184
185    pub fn encrypt_blob_with_padding(
186        &self,
187        randomness: RandomnessBytes,
188        plaintext: &[u8],
189        padding_len: u32,
190    ) -> Vec<u8> {
191        let full_length =
192            ENCRYPTED_BLOB_PADDING_LENGTH_SIZE + plaintext.len() + padding_len as usize;
193        let mut padded_plaintext = Vec::with_capacity(full_length);
194        padded_plaintext.extend_from_slice(&padding_len.to_be_bytes());
195        padded_plaintext.extend_from_slice(plaintext);
196        padded_plaintext.resize(full_length, 0);
197        self.encrypt_blob(randomness, &padded_plaintext)
198    }
199
200    pub fn decrypt_blob(&self, ciphertext: &[u8]) -> Result<Vec<u8>, ZkGroupVerificationFailure> {
201        if ciphertext.len() < AESGCM_NONCE_LEN + 1 {
202            // AESGCM_NONCE_LEN = 12 bytes for IV
203            return Err(ZkGroupVerificationFailure);
204        }
205        let unreserved_len = ciphertext.len() - 1;
206        let nonce = &ciphertext[unreserved_len - AESGCM_NONCE_LEN..unreserved_len];
207        let ciphertext = &ciphertext[..unreserved_len - AESGCM_NONCE_LEN];
208        self.decrypt_blob_aesgcmsiv(&self.blob_key, nonce, ciphertext)
209    }
210
211    pub fn decrypt_blob_with_padding(
212        &self,
213        ciphertext: &[u8],
214    ) -> Result<Vec<u8>, ZkGroupVerificationFailure> {
215        let mut decrypted = self.decrypt_blob(ciphertext)?;
216
217        if decrypted.len() < ENCRYPTED_BLOB_PADDING_LENGTH_SIZE {
218            return Err(ZkGroupVerificationFailure);
219        }
220        let (padding_len_bytes, plaintext_plus_padding) =
221            decrypted.split_at(ENCRYPTED_BLOB_PADDING_LENGTH_SIZE);
222
223        let padding_len = u32::from_be_bytes(padding_len_bytes.try_into().expect("correct size"));
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}