1use aes::cipher::{BlockEncrypt as _, KeyInit as _};
7use partial_default::PartialDefault;
8use serde::{Deserialize, Serialize};
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 static_assertions::const_assert_eq!(ACCESS_KEY_LEN, {
74 type BlockSize = <::aes::Aes256Enc as ::aes::cipher::BlockSizeUser>::BlockSize;
75 <BlockSize as ::aes::cipher::Unsigned>::USIZE
76 });
77 let aes = ::aes::Aes256Enc::new((&self.bytes).into());
78 let mut buf = [0u8; ACCESS_KEY_LEN];
79 buf[ACCESS_KEY_LEN - 1] = 2;
80 aes.encrypt_block((&mut buf).into());
81 buf
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88
89 #[test]
90 fn access_key_kat() {
91 let kat = [
93 (
94 [
95 0xb9, 0x50, 0x42, 0xa2, 0xc2, 0xd9, 0xe5, 0xb3, 0xbb, 0x9, 0x30, 0xe, 0xe4,
96 0x8, 0xa1, 0x72, 0xfa, 0xcd, 0x96, 0xe9, 0x1b, 0x50, 0x4e, 0x4, 0x3a, 0x5a,
97 0x2, 0x3d, 0xc4, 0xcf, 0xf3, 0x59,
98 ],
99 [
100 0x24, 0xfb, 0x96, 0xd4, 0xa5, 0xe3, 0x33, 0xe9, 0xd4, 0x45, 0x12, 0x5, 0xb9,
101 0xe2, 0xfa, 0xed,
102 ],
103 ),
104 (
105 [
106 0x26, 0x19, 0x7b, 0x17, 0xe5, 0xa2, 0xc3, 0x6d, 0x8c, 0x95, 0x18, 0xc3, 0x53,
107 0x58, 0xf1, 0x23, 0xc4, 0x76, 0x0, 0xd, 0xb6, 0xda, 0x75, 0x65, 0xc0, 0xd4,
108 0x1f, 0x66, 0x74, 0x46, 0x2c, 0x4d,
109 ],
110 [
111 0xe8, 0x95, 0xc3, 0xc, 0xf7, 0x80, 0x75, 0x7d, 0x22, 0xf7, 0xa1, 0x79, 0x70,
112 0x8b, 0x14, 0xa1,
113 ],
114 ),
115 ];
116
117 for (pk, ak) in kat {
118 let profile_key = ProfileKey::create(pk);
119 let access_key = profile_key.derive_access_key();
120 assert_eq!(ak, access_key);
121 }
122 }
123}