zkgroup/api/profiles/
profile_key.rs

1//
2// Copyright 2020 Signal Messenger, LLC.
3// SPDX-License-Identifier: AGPL-3.0-only
4//
5
6use partial_default::PartialDefault;
7use serde::{Deserialize, Serialize};
8use signal_crypto::Aes256GcmEncryption;
9use subtle::ConstantTimeEq;
10
11use crate::common::constants::*;
12use crate::common::sho::*;
13use crate::common::simple_types::*;
14use crate::{api, crypto};
15
16#[derive(Copy, Clone, Serialize, Deserialize, PartialDefault)]
17pub struct ProfileKey {
18    pub bytes: ProfileKeyBytes,
19}
20
21impl std::fmt::Debug for ProfileKey {
22    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23        f.debug_struct("ProfileKey")
24            .field("bytes", &zkcredential::PrintAsHex(self.bytes.as_slice()))
25            .finish()
26    }
27}
28
29impl PartialEq for ProfileKey {
30    fn eq(&self, other: &Self) -> bool {
31        self.bytes.as_slice().ct_eq(other.bytes.as_slice()).into()
32    }
33}
34
35impl ProfileKey {
36    pub fn generate(randomness: RandomnessBytes) -> Self {
37        let mut sho = Sho::new(
38            b"Signal_ZKGroup_20200424_Random_ProfileKey_Generate",
39            &randomness,
40        );
41        let mut bytes = [0u8; PROFILE_KEY_LEN];
42        bytes.copy_from_slice(&sho.squeeze(PROFILE_KEY_LEN)[..]);
43        Self { bytes }
44    }
45
46    pub fn create(bytes: ProfileKeyBytes) -> Self {
47        Self { bytes }
48    }
49
50    pub fn get_bytes(&self) -> ProfileKeyBytes {
51        self.bytes
52    }
53
54    pub fn get_commitment(
55        &self,
56        user_id: libsignal_core::Aci,
57    ) -> api::profiles::ProfileKeyCommitment {
58        let uid_bytes = uuid::Uuid::from(user_id).into_bytes();
59        let profile_key = crypto::profile_key_struct::ProfileKeyStruct::new(self.bytes, uid_bytes);
60        let commitment =
61            crypto::profile_key_commitment::CommitmentWithSecretNonce::new(profile_key, uid_bytes);
62        api::profiles::ProfileKeyCommitment {
63            reserved: Default::default(),
64            commitment: commitment.get_profile_key_commitment(),
65        }
66    }
67
68    pub fn get_profile_key_version(
69        &self,
70        user_id: libsignal_core::Aci,
71    ) -> api::profiles::ProfileKeyVersion {
72        let uid_bytes = uuid::Uuid::from(user_id).into_bytes();
73        let mut combined_array = [0u8; PROFILE_KEY_LEN + UUID_LEN];
74        combined_array[..PROFILE_KEY_LEN].copy_from_slice(&self.bytes);
75        combined_array[PROFILE_KEY_LEN..].copy_from_slice(&uid_bytes);
76        let mut sho = Sho::new(
77            b"Signal_ZKGroup_20200424_ProfileKeyAndUid_ProfileKey_GetProfileKeyVersion",
78            &combined_array,
79        );
80
81        let pkv_hex_string = hex::encode(&sho.squeeze(PROFILE_KEY_VERSION_LEN)[..]);
82        let mut pkv_hex_array: [u8; PROFILE_KEY_VERSION_ENCODED_LEN] =
83            [0u8; PROFILE_KEY_VERSION_ENCODED_LEN];
84        pkv_hex_array.copy_from_slice(pkv_hex_string.as_bytes());
85        api::profiles::ProfileKeyVersion {
86            bytes: pkv_hex_array,
87        }
88    }
89
90    pub fn derive_access_key(&self) -> [u8; ACCESS_KEY_LEN] {
91        let nonce = &[0u8; AESGCM_NONCE_LEN];
92        let mut cipher = Aes256GcmEncryption::new(&self.bytes, nonce, &[]).unwrap();
93        let mut buf = [0u8; ACCESS_KEY_LEN];
94        cipher.encrypt(&mut buf[..]);
95        buf
96    }
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102
103    #[test]
104    fn access_key_kat() {
105        // Pairs of profile keys and derived access keys
106        let kat = [
107            (
108                [
109                    0xb9, 0x50, 0x42, 0xa2, 0xc2, 0xd9, 0xe5, 0xb3, 0xbb, 0x9, 0x30, 0xe, 0xe4,
110                    0x8, 0xa1, 0x72, 0xfa, 0xcd, 0x96, 0xe9, 0x1b, 0x50, 0x4e, 0x4, 0x3a, 0x5a,
111                    0x2, 0x3d, 0xc4, 0xcf, 0xf3, 0x59,
112                ],
113                [
114                    0x24, 0xfb, 0x96, 0xd4, 0xa5, 0xe3, 0x33, 0xe9, 0xd4, 0x45, 0x12, 0x5, 0xb9,
115                    0xe2, 0xfa, 0xed,
116                ],
117            ),
118            (
119                [
120                    0x26, 0x19, 0x7b, 0x17, 0xe5, 0xa2, 0xc3, 0x6d, 0x8c, 0x95, 0x18, 0xc3, 0x53,
121                    0x58, 0xf1, 0x23, 0xc4, 0x76, 0x0, 0xd, 0xb6, 0xda, 0x75, 0x65, 0xc0, 0xd4,
122                    0x1f, 0x66, 0x74, 0x46, 0x2c, 0x4d,
123                ],
124                [
125                    0xe8, 0x95, 0xc3, 0xc, 0xf7, 0x80, 0x75, 0x7d, 0x22, 0xf7, 0xa1, 0x79, 0x70,
126                    0x8b, 0x14, 0xa1,
127                ],
128            ),
129        ];
130
131        for (pk, ak) in kat {
132            let profile_key = ProfileKey::create(pk);
133            let access_key = profile_key.derive_access_key();
134            assert_eq!(ak, access_key);
135        }
136    }
137}