1use super::{AsymmetricCryptoKey, PublicKeyEncryptionAlgorithm};
2use crate::{
3 error::Result, CryptoError, EncString, KeyDecryptable, KeyEncryptable, Pkcs8PrivateKeyBytes,
4 SymmetricCryptoKey, 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 = Pkcs8PrivateKeyBytes::from(device_private_key);
69 let device_private_key = AsymmetricCryptoKey::from_der(&device_private_key)?;
70
71 let user_key: SymmetricCryptoKey =
72 protected_user_key.decapsulate_key_unsigned(&device_private_key)?;
73 Ok(user_key)
74 }
75
76 fn to_base64(&self) -> String {
77 self.0.to_base64()
78 }
79}
80
81impl TryFrom<String> for DeviceKey {
82 type Error = CryptoError;
83
84 fn try_from(value: String) -> Result<Self, Self::Error> {
85 SymmetricCryptoKey::try_from(value).map(DeviceKey)
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92 use crate::{derive_symmetric_key, BitwardenLegacyKeyBytes};
93
94 #[test]
95 fn test_trust_device() {
96 let key = SymmetricCryptoKey::Aes256CbcHmacKey(derive_symmetric_key("test"));
97
98 let result = DeviceKey::trust_device(&key).unwrap();
99
100 let device_key = DeviceKey::try_from(result.device_key).unwrap();
101 let decrypted = device_key
102 .decrypt_user_key(
103 result.protected_device_private_key,
104 result.protected_user_key,
105 )
106 .unwrap();
107
108 assert_eq!(key, decrypted);
109 assert_eq!(key, decrypted);
110 }
111
112 #[test]
113 fn test_decrypt_user_key() {
114 let user_key: &[u8] = &[
116 109, 128, 172, 147, 206, 123, 134, 95, 16, 36, 155, 113, 201, 18, 186, 230, 216, 212,
117 173, 188, 74, 11, 134, 131, 137, 242, 105, 178, 105, 126, 52, 139, 248, 91, 215, 21,
118 128, 91, 226, 222, 165, 67, 251, 34, 83, 81, 77, 147, 225, 76, 13, 41, 102, 45, 183,
119 218, 106, 89, 254, 208, 251, 101, 130, 10,
120 ];
121 let user_key =
122 SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(user_key)).unwrap();
123
124 let key_data: &[u8] = &[
125 114, 235, 60, 115, 172, 156, 203, 145, 195, 130, 215, 250, 88, 146, 215, 230, 12, 109,
126 245, 222, 54, 217, 255, 211, 221, 105, 230, 236, 65, 52, 209, 133, 76, 208, 113, 254,
127 194, 216, 156, 19, 230, 62, 32, 93, 87, 7, 144, 156, 117, 142, 250, 32, 182, 118, 187,
128 8, 247, 7, 203, 201, 65, 147, 206, 247,
129 ];
130 let device_key = DeviceKey(
131 SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(key_data)).unwrap(),
132 );
133
134 let protected_user_key: UnsignedSharedKey = "4.f+VbbacRhO2q4MOUSdt1AIjQ2FuLAvg4aDxJMXAh3VxvbmUADj8Ct/R7XEpPUqApmbRS566jS0eRVy8Sk08ogoCdj1IFN9VsIky2i2X1WHK1fUnr3UBmXE3tl2NPBbx56U+h73S2jNTSyet2W18Jg2q7/w8KIhR3J41QrG9aGoOTN93to3hb5W4z6rdrSI0e7GkizbwcIA0NH7Z1JyAhrjPm9+tjRjg060YbEbGaWTAOkZWfgbLjr8bY455DteO2xxG139cOx7EBo66N+YhjsLi0ozkeUyPQkoWBdKMcQllS7jCfB4fDyJA05ALTbk74syKkvqFxqwmQbg+aVn+dcw==".parse().unwrap();
135
136 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();
137
138 let decrypted = device_key
139 .decrypt_user_key(protected_device_private_key, protected_user_key)
140 .unwrap();
141
142 assert_eq!(decrypted, user_key);
143 }
144}