1use 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 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}