1use super::{AsymmetricCryptoKey, PublicKeyEncryptionAlgorithm};
2use crate::{
3 error::Result, CryptoError, EncString, KeyDecryptable, KeyEncryptable, SymmetricCryptoKey,
4 UnsignedSharedKey,
5};
6
7#[derive(Debug)]
12pub struct DeviceKey(SymmetricCryptoKey);
13
14#[allow(missing_docs)]
15#[derive(Debug)]
16#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
17pub struct TrustDeviceResponse {
18 pub device_key: String,
20 pub protected_user_key: UnsignedSharedKey,
22 pub protected_device_private_key: EncString,
24 pub protected_device_public_key: EncString,
26}
27
28impl DeviceKey {
29 pub fn trust_device(user_key: &SymmetricCryptoKey) -> Result<TrustDeviceResponse> {
34 let device_key = DeviceKey(SymmetricCryptoKey::make_aes256_cbc_hmac_key());
35
36 let device_private_key =
37 AsymmetricCryptoKey::make(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
38
39 let protected_user_key = UnsignedSharedKey::encapsulate_key_unsigned(
40 user_key,
41 &device_private_key.to_public_key(),
42 )?;
43
44 let protected_device_public_key = device_private_key
45 .to_public_key()
46 .to_der()?
47 .encrypt_with_key(user_key)?;
48
49 let protected_device_private_key = device_private_key
50 .to_der()?
51 .encrypt_with_key(&device_key.0)?;
52
53 Ok(TrustDeviceResponse {
54 device_key: device_key.to_base64(),
55 protected_user_key,
56 protected_device_private_key,
57 protected_device_public_key,
58 })
59 }
60
61 pub fn decrypt_user_key(
63 &self,
64 protected_device_private_key: EncString,
65 protected_user_key: UnsignedSharedKey,
66 ) -> Result<SymmetricCryptoKey> {
67 let device_private_key: Vec<u8> = protected_device_private_key.decrypt_with_key(&self.0)?;
68 let device_private_key = AsymmetricCryptoKey::from_der(&device_private_key)?;
69
70 let user_key: SymmetricCryptoKey =
71 protected_user_key.decapsulate_key_unsigned(&device_private_key)?;
72 Ok(user_key)
73 }
74
75 fn to_base64(&self) -> String {
76 self.0.to_base64()
77 }
78}
79
80impl TryFrom<String> for DeviceKey {
81 type Error = CryptoError;
82
83 fn try_from(value: String) -> Result<Self, Self::Error> {
84 SymmetricCryptoKey::try_from(value).map(DeviceKey)
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91 use crate::derive_symmetric_key;
92
93 #[test]
94 fn test_trust_device() {
95 let key = SymmetricCryptoKey::Aes256CbcHmacKey(derive_symmetric_key("test"));
96
97 let result = DeviceKey::trust_device(&key).unwrap();
98
99 let device_key = DeviceKey::try_from(result.device_key).unwrap();
100 let decrypted = device_key
101 .decrypt_user_key(
102 result.protected_device_private_key,
103 result.protected_user_key,
104 )
105 .unwrap();
106
107 assert_eq!(key, decrypted);
108 assert_eq!(key, decrypted);
109 }
110
111 #[test]
112 fn test_decrypt_user_key() {
113 let user_key: &mut [u8] = &mut [
115 109, 128, 172, 147, 206, 123, 134, 95, 16, 36, 155, 113, 201, 18, 186, 230, 216, 212,
116 173, 188, 74, 11, 134, 131, 137, 242, 105, 178, 105, 126, 52, 139, 248, 91, 215, 21,
117 128, 91, 226, 222, 165, 67, 251, 34, 83, 81, 77, 147, 225, 76, 13, 41, 102, 45, 183,
118 218, 106, 89, 254, 208, 251, 101, 130, 10,
119 ];
120 let user_key = SymmetricCryptoKey::try_from(user_key).unwrap();
121
122 let key_data: &mut [u8] = &mut [
123 114, 235, 60, 115, 172, 156, 203, 145, 195, 130, 215, 250, 88, 146, 215, 230, 12, 109,
124 245, 222, 54, 217, 255, 211, 221, 105, 230, 236, 65, 52, 209, 133, 76, 208, 113, 254,
125 194, 216, 156, 19, 230, 62, 32, 93, 87, 7, 144, 156, 117, 142, 250, 32, 182, 118, 187,
126 8, 247, 7, 203, 201, 65, 147, 206, 247,
127 ];
128 let device_key = DeviceKey(key_data.try_into().unwrap());
129
130 let protected_user_key: UnsignedSharedKey = "4.f+VbbacRhO2q4MOUSdt1AIjQ2FuLAvg4aDxJMXAh3VxvbmUADj8Ct/R7XEpPUqApmbRS566jS0eRVy8Sk08ogoCdj1IFN9VsIky2i2X1WHK1fUnr3UBmXE3tl2NPBbx56U+h73S2jNTSyet2W18Jg2q7/w8KIhR3J41QrG9aGoOTN93to3hb5W4z6rdrSI0e7GkizbwcIA0NH7Z1JyAhrjPm9+tjRjg060YbEbGaWTAOkZWfgbLjr8bY455DteO2xxG139cOx7EBo66N+YhjsLi0ozkeUyPQkoWBdKMcQllS7jCfB4fDyJA05ALTbk74syKkvqFxqwmQbg+aVn+dcw==".parse().unwrap();
131
132 let protected_device_private_key: EncString = "2.GyQfUYWW6Byy4UV5icFLxg==|EMiU7OTF79N6tfv3+YUs5zJhBAgqv6sa5YCoPl6yAETh7Tfk+JmbeizxXFPj5Q1X/tcVpDZl/3fGcxtnIxg1YtvDFn7j8uPnoApOWhCKmwcvJSIkt+qvX3lELNBwZXozSiy7PbQ0JbCMe2d4MkimR5k8+lE9FB3208yYK7nOJhlrsUCnOekCYEU9/4NCMA8tz8SpITx/MN4JJ1TQ/KjPJYLt+3JNUxK47QlgREWQvyVzCRt7ZGtcgIJ/U1qycAWMpEg9NkuV8j5QRA1S7VBsA6qliJwys5+dmTuIOmOMwdKFZDc4ZvWoRkPp2TSJBu7L8sSAgU6mmDWac8iQ+9Ka/drdfwYLrH8GAZvURk79tSpRrT7+PAFe2QdUtliUIyiqkh8iJVjZube4hRnEsRuX9V9b+UdtAr6zAj7mugO/VAu5T9J38V79V2ohG3NtXysDeKLXpAlkhjllWXeq/wret2fD4WiwqEDj0G2A/PY3F3OziIgp0UKc00AfqrPq8OVK3A+aowwVqdYadgxyoVCKWJ8unJeAXG7MrMQ9tHpzF6COoaEy7Wwoc17qko33zazwLZbfAjB4oc8Ea26jRKnJZP56sVZAjOSQQMziAsA08MRaa/DQhgRea1+Ygba0gMft8Dww8anN2gQBveTZRBWyqXYgN3U0Ity5gNauT8RnFk9faqVFt2Qxnp0JgJ+PsqEt5Hn4avBRZQQ7o8VvPnxYLDKFe3I2m6HFYFWRhOGeDYxexIuaiF2iIAYFVUmnDuWpgnUiL4XJ3KHDsjkPzcV3z4D2Knr/El2VVXve8jhDjETfovmmN28+i2e29PXvKIymTskMFpFCQPc7wBY/Id7pmgb3SujKYNpkAS2sByDoRir0my49DDGfta0dENssJhFd3x+87fZbEj3cMiikg2pBwpTLgmfIUa5cVZU2s8JZ9wu7gaioYzvX+elHa3EHLcnEUoJTtSf9kjb+Nbq4ktMgYAO2wIC96t1LvmqK4Qn2cOdw5QNlRqALhqe5V31kyIcwRMK0AyIoOPhnSqtpYdFiR3LDTvZA8dU0vSsuchCwHNMeRUtKvdzN/tk+oeznyY/mpakUESN501lEKd/QFLtJZsDZTtNlcA8fU3kDtws4ZIMR0O5+PFmgQFSU8OMobf9ClUzy/wHTvYGyDuSwbOoPeS955QKkUKXCNMj33yrPr+ioHQ1BNwLX3VmMF4bNRBY/vr+CG0/EZi0Gwl0kyHGl0yWEtpQuu+/PaROJeOraWy5D1UoZZhY4n0zJZBt1eg3FZ2rhKv4gdUc50nZpeNWE8pIqZ6RQ7qPJuqfF1Z+G73iOSnLYCHDiiFmhD5ivf9IGkTAcWcBsQ/2wcSj9bFJr4DrKfsbQ4CkSWICWVn/W+InKkO6BTsBbYmvte5SvbaN+UOtiUSkHLBCCr8273VNgcB/hgtbUires3noxYZJxoczr+i7vdlEgQnWEKrpo0CifsFxGwYS3Yy2K79iwvDMaLPDf73zLSbuoUl6602F2Mzcjnals67f+gSpaDvWt7Kg9c/ZfGjq8oNxVaXJnX3gSDsO+fhwVAtnDApL+tL8cFfxGerW4KGi9/74woH+C3MMIViBtNnrpEuvxUW97Dg5nd40oGDeyi/q+8HdcxkneyFY=|JYdol19Yi+n1r7M+06EwK5JCi2s/CWqKui2Cy6hEb3k=".parse().unwrap();
133
134 let decrypted = device_key
135 .decrypt_user_key(protected_device_private_key, protected_user_key)
136 .unwrap();
137
138 assert_eq!(decrypted, user_key);
139 }
140}