1use bitwarden_encoding::B64;
2
3use super::{AsymmetricCryptoKey, PublicKeyEncryptionAlgorithm};
4use crate::{
5 error::Result, CryptoError, EncString, KeyDecryptable, KeyEncryptable, Pkcs8PrivateKeyBytes,
6 SymmetricCryptoKey, UnsignedSharedKey,
7};
8
9#[derive(Debug)]
14pub struct DeviceKey(SymmetricCryptoKey);
15
16#[allow(missing_docs)]
17#[derive(Debug)]
18#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
19pub struct TrustDeviceResponse {
20 pub device_key: B64,
22 pub protected_user_key: UnsignedSharedKey,
24 pub protected_device_private_key: EncString,
26 pub protected_device_public_key: EncString,
28}
29
30impl DeviceKey {
31 pub fn trust_device(user_key: &SymmetricCryptoKey) -> Result<TrustDeviceResponse> {
36 let device_key = DeviceKey(SymmetricCryptoKey::make_aes256_cbc_hmac_key());
37
38 let device_private_key =
39 AsymmetricCryptoKey::make(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
40
41 let protected_user_key = UnsignedSharedKey::encapsulate_key_unsigned(
42 user_key,
43 &device_private_key.to_public_key(),
44 )?;
45
46 let protected_device_public_key = device_private_key
47 .to_public_key()
48 .to_der()?
49 .encrypt_with_key(user_key)?;
50
51 let protected_device_private_key = device_private_key
52 .to_der()?
53 .encrypt_with_key(&device_key.0)?;
54
55 Ok(TrustDeviceResponse {
56 device_key: device_key.to_base64(),
57 protected_user_key,
58 protected_device_private_key,
59 protected_device_public_key,
60 })
61 }
62
63 pub fn decrypt_user_key(
65 &self,
66 protected_device_private_key: EncString,
67 protected_user_key: UnsignedSharedKey,
68 ) -> Result<SymmetricCryptoKey> {
69 let device_private_key: Vec<u8> = protected_device_private_key.decrypt_with_key(&self.0)?;
70 let device_private_key = Pkcs8PrivateKeyBytes::from(device_private_key);
71 let device_private_key = AsymmetricCryptoKey::from_der(&device_private_key)?;
72
73 let user_key: SymmetricCryptoKey =
74 protected_user_key.decapsulate_key_unsigned(&device_private_key)?;
75 Ok(user_key)
76 }
77
78 fn to_base64(&self) -> B64 {
79 self.0.to_base64()
80 }
81}
82
83impl TryFrom<String> for DeviceKey {
84 type Error = CryptoError;
85
86 fn try_from(value: String) -> Result<Self, Self::Error> {
87 SymmetricCryptoKey::try_from(value).map(DeviceKey)
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94 use crate::{derive_symmetric_key, BitwardenLegacyKeyBytes};
95
96 #[test]
97 fn test_trust_device() {
98 let key = SymmetricCryptoKey::Aes256CbcHmacKey(derive_symmetric_key("test"));
99
100 let result = DeviceKey::trust_device(&key).unwrap();
101
102 let device_key = DeviceKey::try_from(result.device_key.to_string()).unwrap();
103 let decrypted = device_key
104 .decrypt_user_key(
105 result.protected_device_private_key,
106 result.protected_user_key,
107 )
108 .unwrap();
109
110 assert_eq!(key, decrypted);
111 assert_eq!(key, decrypted);
112 }
113
114 #[test]
115 fn test_decrypt_user_key() {
116 let user_key: &[u8] = &[
118 109, 128, 172, 147, 206, 123, 134, 95, 16, 36, 155, 113, 201, 18, 186, 230, 216, 212,
119 173, 188, 74, 11, 134, 131, 137, 242, 105, 178, 105, 126, 52, 139, 248, 91, 215, 21,
120 128, 91, 226, 222, 165, 67, 251, 34, 83, 81, 77, 147, 225, 76, 13, 41, 102, 45, 183,
121 218, 106, 89, 254, 208, 251, 101, 130, 10,
122 ];
123 let user_key =
124 SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(user_key)).unwrap();
125
126 let key_data: &[u8] = &[
127 114, 235, 60, 115, 172, 156, 203, 145, 195, 130, 215, 250, 88, 146, 215, 230, 12, 109,
128 245, 222, 54, 217, 255, 211, 221, 105, 230, 236, 65, 52, 209, 133, 76, 208, 113, 254,
129 194, 216, 156, 19, 230, 62, 32, 93, 87, 7, 144, 156, 117, 142, 250, 32, 182, 118, 187,
130 8, 247, 7, 203, 201, 65, 147, 206, 247,
131 ];
132 let device_key = DeviceKey(
133 SymmetricCryptoKey::try_from(&BitwardenLegacyKeyBytes::from(key_data)).unwrap(),
134 );
135
136 let protected_user_key: UnsignedSharedKey = "4.f+VbbacRhO2q4MOUSdt1AIjQ2FuLAvg4aDxJMXAh3VxvbmUADj8Ct/R7XEpPUqApmbRS566jS0eRVy8Sk08ogoCdj1IFN9VsIky2i2X1WHK1fUnr3UBmXE3tl2NPBbx56U+h73S2jNTSyet2W18Jg2q7/w8KIhR3J41QrG9aGoOTN93to3hb5W4z6rdrSI0e7GkizbwcIA0NH7Z1JyAhrjPm9+tjRjg060YbEbGaWTAOkZWfgbLjr8bY455DteO2xxG139cOx7EBo66N+YhjsLi0ozkeUyPQkoWBdKMcQllS7jCfB4fDyJA05ALTbk74syKkvqFxqwmQbg+aVn+dcw==".parse().unwrap();
137
138 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();
139
140 let decrypted = device_key
141 .decrypt_user_key(protected_device_private_key, protected_user_key)
142 .unwrap();
143
144 assert_eq!(decrypted, user_key);
145 }
146}