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 bytes = sho.squeeze_as_array();
42        Self { bytes }
43    }
44
45    pub fn create(bytes: ProfileKeyBytes) -> Self {
46        Self { bytes }
47    }
48
49    pub fn get_bytes(&self) -> ProfileKeyBytes {
50        self.bytes
51    }
52
53    pub fn get_commitment(
54        &self,
55        user_id: libsignal_core::Aci,
56    ) -> api::profiles::ProfileKeyCommitment {
57        let uid_bytes = uuid::Uuid::from(user_id).into_bytes();
58        let profile_key = crypto::profile_key_struct::ProfileKeyStruct::new(self.bytes, uid_bytes);
59        let commitment =
60            crypto::profile_key_commitment::CommitmentWithSecretNonce::new(profile_key, uid_bytes);
61        api::profiles::ProfileKeyCommitment {
62            reserved: Default::default(),
63            commitment: commitment.get_profile_key_commitment(),
64        }
65    }
66
67    pub fn derive_access_key(&self) -> [u8; ACCESS_KEY_LEN] {
68        let nonce = &[0u8; AESGCM_NONCE_LEN];
69        let mut cipher = Aes256GcmEncryption::new(&self.bytes, nonce, &[]).unwrap();
70        let mut buf = [0u8; ACCESS_KEY_LEN];
71        cipher.encrypt(&mut buf[..]);
72        buf
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    #[test]
81    fn access_key_kat() {
82        // Pairs of profile keys and derived access keys
83        let kat = [
84            (
85                [
86                    0xb9, 0x50, 0x42, 0xa2, 0xc2, 0xd9, 0xe5, 0xb3, 0xbb, 0x9, 0x30, 0xe, 0xe4,
87                    0x8, 0xa1, 0x72, 0xfa, 0xcd, 0x96, 0xe9, 0x1b, 0x50, 0x4e, 0x4, 0x3a, 0x5a,
88                    0x2, 0x3d, 0xc4, 0xcf, 0xf3, 0x59,
89                ],
90                [
91                    0x24, 0xfb, 0x96, 0xd4, 0xa5, 0xe3, 0x33, 0xe9, 0xd4, 0x45, 0x12, 0x5, 0xb9,
92                    0xe2, 0xfa, 0xed,
93                ],
94            ),
95            (
96                [
97                    0x26, 0x19, 0x7b, 0x17, 0xe5, 0xa2, 0xc3, 0x6d, 0x8c, 0x95, 0x18, 0xc3, 0x53,
98                    0x58, 0xf1, 0x23, 0xc4, 0x76, 0x0, 0xd, 0xb6, 0xda, 0x75, 0x65, 0xc0, 0xd4,
99                    0x1f, 0x66, 0x74, 0x46, 0x2c, 0x4d,
100                ],
101                [
102                    0xe8, 0x95, 0xc3, 0xc, 0xf7, 0x80, 0x75, 0x7d, 0x22, 0xf7, 0xa1, 0x79, 0x70,
103                    0x8b, 0x14, 0xa1,
104                ],
105            ),
106        ];
107
108        for (pk, ak) in kat {
109            let profile_key = ProfileKey::create(pk);
110            let access_key = profile_key.derive_access_key();
111            assert_eq!(ak, access_key);
112        }
113    }
114}