bitwarden_crypto/keys/
asymmetric_crypto_key.rs1use std::pin::Pin;
2
3use rsa::{pkcs8::DecodePublicKey, RsaPrivateKey, RsaPublicKey};
4use serde_repr::{Deserialize_repr, Serialize_repr};
5
6use super::key_encryptable::CryptoKey;
7use crate::error::{CryptoError, Result};
8
9#[derive(Serialize_repr, Deserialize_repr)]
11#[repr(u8)]
12pub enum PublicKeyEncryptionAlgorithm {
13 RsaOaepSha1 = 0,
15}
16
17#[derive(Clone)]
18pub(crate) enum RawPublicKey {
19 RsaOaepSha1(RsaPublicKey),
20}
21
22#[derive(Clone)]
25pub struct AsymmetricPublicCryptoKey {
26 inner: RawPublicKey,
27}
28
29impl AsymmetricPublicCryptoKey {
30 pub(crate) fn inner(&self) -> &RawPublicKey {
31 &self.inner
32 }
33
34 pub fn from_der(der: &[u8]) -> Result<Self> {
36 Ok(AsymmetricPublicCryptoKey {
37 inner: RawPublicKey::RsaOaepSha1(
38 RsaPublicKey::from_public_key_der(der).map_err(|_| CryptoError::InvalidKey)?,
39 ),
40 })
41 }
42
43 pub fn to_der(&self) -> Result<Vec<u8>> {
45 use rsa::pkcs8::EncodePublicKey;
46 match &self.inner {
47 RawPublicKey::RsaOaepSha1(public_key) => Ok(public_key
48 .to_public_key_der()
49 .map_err(|_| CryptoError::InvalidKey)?
50 .as_bytes()
51 .to_owned()),
52 }
53 }
54}
55
56#[derive(Clone)]
57pub(crate) enum RawPrivateKey {
58 RsaOaepSha1(Pin<Box<RsaPrivateKey>>),
63}
64
65#[derive(Clone)]
68pub struct AsymmetricCryptoKey {
69 inner: RawPrivateKey,
70}
71
72const _: () = {
75 fn assert_zeroize_on_drop<T: zeroize::ZeroizeOnDrop>() {}
76 fn assert_all() {
77 assert_zeroize_on_drop::<RsaPrivateKey>();
78 }
79};
80impl zeroize::ZeroizeOnDrop for AsymmetricCryptoKey {}
81impl CryptoKey for AsymmetricCryptoKey {}
82
83impl AsymmetricCryptoKey {
84 pub fn make(algorithm: PublicKeyEncryptionAlgorithm) -> Self {
86 Self::make_internal(algorithm, &mut rand::thread_rng())
87 }
88
89 fn make_internal<R: rand::CryptoRng + rand::RngCore>(
90 algorithm: PublicKeyEncryptionAlgorithm,
91 rng: &mut R,
92 ) -> Self {
93 match algorithm {
94 PublicKeyEncryptionAlgorithm::RsaOaepSha1 => Self {
95 inner: RawPrivateKey::RsaOaepSha1(Box::pin(
96 RsaPrivateKey::new(rng, 2048).expect("failed to generate a key"),
97 )),
98 },
99 }
100 }
101
102 #[allow(missing_docs)]
103 pub fn from_pem(pem: &str) -> Result<Self> {
104 use rsa::pkcs8::DecodePrivateKey;
105 Ok(Self {
106 inner: RawPrivateKey::RsaOaepSha1(Box::pin(
107 RsaPrivateKey::from_pkcs8_pem(pem).map_err(|_| CryptoError::InvalidKey)?,
108 )),
109 })
110 }
111
112 #[allow(missing_docs)]
113 pub fn from_der(der: &[u8]) -> Result<Self> {
114 use rsa::pkcs8::DecodePrivateKey;
115 Ok(Self {
116 inner: RawPrivateKey::RsaOaepSha1(Box::pin(
117 RsaPrivateKey::from_pkcs8_der(der).map_err(|_| CryptoError::InvalidKey)?,
118 )),
119 })
120 }
121
122 #[allow(missing_docs)]
123 pub fn to_der(&self) -> Result<Vec<u8>> {
124 match &self.inner {
125 RawPrivateKey::RsaOaepSha1(private_key) => {
126 use rsa::pkcs8::EncodePrivateKey;
127 Ok(private_key
128 .to_pkcs8_der()
129 .map_err(|_| CryptoError::InvalidKey)?
130 .as_bytes()
131 .to_owned())
132 }
133 }
134 }
135
136 pub fn to_public_key(&self) -> AsymmetricPublicCryptoKey {
139 match &self.inner {
140 RawPrivateKey::RsaOaepSha1(private_key) => AsymmetricPublicCryptoKey {
141 inner: RawPublicKey::RsaOaepSha1(private_key.to_public_key()),
142 },
143 }
144 }
145
146 pub(crate) fn inner(&self) -> &RawPrivateKey {
147 &self.inner
148 }
149}
150
151impl std::fmt::Debug for AsymmetricCryptoKey {
153 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154 f.debug_struct("AsymmetricCryptoKey").finish()
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use base64::{engine::general_purpose::STANDARD, Engine};
161
162 use crate::{
163 AsymmetricCryptoKey, AsymmetricPublicCryptoKey, SymmetricCryptoKey, UnsignedSharedKey,
164 };
165
166 #[test]
167 fn test_asymmetric_crypto_key() {
168 let pem_key_str = "-----BEGIN PRIVATE KEY-----
169MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDiTQVuzhdygFz5
170qv14i+XFDGTnDravzUQT1hPKPGUZOUSZ1gwdNgkWqOIaOnR65BHEnL0sp4bnuiYc
171afeK2JAW5Sc8Z7IxBNSuAwhQmuKx3RochMIiuCkI2/p+JvUQoJu6FBNm8OoJ4Cwm
172qqHGZESMfnpQDCuDrB3JdJEdXhtmnl0C48sGjOk3WaBMcgGqn8LbJDUlyu1zdqyv
173b0waJf0iV4PJm2fkUl7+57D/2TkpbCqURVnZK1FFIEg8mr6FzSN1F2pOfktkNYZw
174P7MSNR7o81CkRSCMr7EkIVa+MZYMBx106BMK7FXgWB7nbSpsWKxBk7ZDHkID2fam
175rEcVtrzDAgMBAAECggEBAKwq9OssGGKgjhvUnyrLJHAZ0dqIMyzk+dotkLjX4gKi
176szJmyqiep6N5sStLNbsZMPtoU/RZMCW0VbJgXFhiEp2YkZU/Py5UAoqw++53J+kx
1770d/IkPphKbb3xUec0+1mg5O6GljDCQuiZXS1dIa/WfeZcezclW6Dz9WovY6ePjJ+
1788vEBR1icbNKzyeINd6MtPtpcgQPHtDwHvhPyUDbKDYGbLvjh9nui8h4+ZUlXKuVR
179jB0ChxiKV1xJRjkrEVoulOOicd5r597WfB2ghax3pvRZ4MdXemCXm3gQYqPVKach
180vGU+1cPQR/MBJZpxT+EZA97xwtFS3gqwbxJaNFcoE8ECgYEA9OaeYZhQPDo485tI
1811u/Z7L/3PNape9hBQIXoW7+MgcQ5NiWqYh8Jnj43EIYa0wM/ECQINr1Za8Q5e6KR
182J30FcU+kfyjuQ0jeXdNELGU/fx5XXNg/vV8GevHwxRlwzqZTCg6UExUZzbYEQqd7
183l+wPyETGeua5xCEywA1nX/D101kCgYEA7I6aMFjhEjO71RmzNhqjKJt6DOghoOfQ
184TjhaaanNEhLYSbenFz1mlb21mW67ulmz162saKdIYLxQNJIP8ZPmxh4ummOJI8w9
185ClHfo8WuCI2hCjJ19xbQJocSbTA5aJg6lA1IDVZMDbQwsnAByPRGpaLHBT/Q9Bye
186KvCMB+9amXsCgYEAx65yXSkP4sumPBrVHUub6MntERIGRxBgw/drKcPZEMWp0FiN
187wEuGUBxyUWrG3F69QK/gcqGZE6F/LSu0JvptQaKqgXQiMYJsrRvhbkFvsHpQyUcZ
188UZL1ebFjm5HOxPAgrQaN/bEqxOwwNRjSUWEMzUImg3c06JIZCzbinvudtKECgYEA
189kY3JF/iIPI/yglP27lKDlCfeeHSYxI3+oTKRhzSAxx8rUGidenJAXeDGDauR/T7W
190pt3pGNfddBBK9Z3uC4Iq3DqUCFE4f/taj7ADAJ1Q0Vh7/28/IJM77ojr8J1cpZwN
191Zy2o6PPxhfkagaDjqEeN9Lrs5LD4nEvDkr5CG1vOjmMCgYEAvIBFKRm31NyF8jLi
192CVuPwC5PzrW5iThDmsWTaXFpB3esUsbICO2pEz872oeQS+Em4GO5vXUlpbbFPzup
193PFhA8iMJ8TAvemhvc7oM0OZqpU6p3K4seHf6BkwLxumoA3vDJfovu9RuXVcJVOnf
194DnqOsltgPomWZ7xVfMkm9niL2OA=
195-----END PRIVATE KEY-----";
196
197 let der_key_vec = STANDARD.decode("MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDiTQVuzhdygFz5qv14i+XFDGTnDravzUQT1hPKPGUZOUSZ1gwdNgkWqOIaOnR65BHEnL0sp4bnuiYcafeK2JAW5Sc8Z7IxBNSuAwhQmuKx3RochMIiuCkI2/p+JvUQoJu6FBNm8OoJ4CwmqqHGZESMfnpQDCuDrB3JdJEdXhtmnl0C48sGjOk3WaBMcgGqn8LbJDUlyu1zdqyvb0waJf0iV4PJm2fkUl7+57D/2TkpbCqURVnZK1FFIEg8mr6FzSN1F2pOfktkNYZwP7MSNR7o81CkRSCMr7EkIVa+MZYMBx106BMK7FXgWB7nbSpsWKxBk7ZDHkID2famrEcVtrzDAgMBAAECggEBAKwq9OssGGKgjhvUnyrLJHAZ0dqIMyzk+dotkLjX4gKiszJmyqiep6N5sStLNbsZMPtoU/RZMCW0VbJgXFhiEp2YkZU/Py5UAoqw++53J+kx0d/IkPphKbb3xUec0+1mg5O6GljDCQuiZXS1dIa/WfeZcezclW6Dz9WovY6ePjJ+8vEBR1icbNKzyeINd6MtPtpcgQPHtDwHvhPyUDbKDYGbLvjh9nui8h4+ZUlXKuVRjB0ChxiKV1xJRjkrEVoulOOicd5r597WfB2ghax3pvRZ4MdXemCXm3gQYqPVKachvGU+1cPQR/MBJZpxT+EZA97xwtFS3gqwbxJaNFcoE8ECgYEA9OaeYZhQPDo485tI1u/Z7L/3PNape9hBQIXoW7+MgcQ5NiWqYh8Jnj43EIYa0wM/ECQINr1Za8Q5e6KRJ30FcU+kfyjuQ0jeXdNELGU/fx5XXNg/vV8GevHwxRlwzqZTCg6UExUZzbYEQqd7l+wPyETGeua5xCEywA1nX/D101kCgYEA7I6aMFjhEjO71RmzNhqjKJt6DOghoOfQTjhaaanNEhLYSbenFz1mlb21mW67ulmz162saKdIYLxQNJIP8ZPmxh4ummOJI8w9ClHfo8WuCI2hCjJ19xbQJocSbTA5aJg6lA1IDVZMDbQwsnAByPRGpaLHBT/Q9ByeKvCMB+9amXsCgYEAx65yXSkP4sumPBrVHUub6MntERIGRxBgw/drKcPZEMWp0FiNwEuGUBxyUWrG3F69QK/gcqGZE6F/LSu0JvptQaKqgXQiMYJsrRvhbkFvsHpQyUcZUZL1ebFjm5HOxPAgrQaN/bEqxOwwNRjSUWEMzUImg3c06JIZCzbinvudtKECgYEAkY3JF/iIPI/yglP27lKDlCfeeHSYxI3+oTKRhzSAxx8rUGidenJAXeDGDauR/T7Wpt3pGNfddBBK9Z3uC4Iq3DqUCFE4f/taj7ADAJ1Q0Vh7/28/IJM77ojr8J1cpZwNZy2o6PPxhfkagaDjqEeN9Lrs5LD4nEvDkr5CG1vOjmMCgYEAvIBFKRm31NyF8jLiCVuPwC5PzrW5iThDmsWTaXFpB3esUsbICO2pEz872oeQS+Em4GO5vXUlpbbFPzupPFhA8iMJ8TAvemhvc7oM0OZqpU6p3K4seHf6BkwLxumoA3vDJfovu9RuXVcJVOnfDnqOsltgPomWZ7xVfMkm9niL2OA=").unwrap();
198
199 let pem_key = AsymmetricCryptoKey::from_pem(pem_key_str).unwrap();
201 let der_key = AsymmetricCryptoKey::from_der(&der_key_vec).unwrap();
202 assert_eq!(pem_key.to_der().unwrap(), der_key.to_der().unwrap());
203
204 assert_eq!(der_key.to_der().unwrap(), der_key_vec);
206 assert_eq!(pem_key.to_der().unwrap(), der_key_vec);
207 }
208
209 #[test]
210 fn test_encrypt_public_decrypt_private() {
211 let private_key = STANDARD
212 .decode(concat!(
213 "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCu9xd+vmkIPoqH",
214 "NejsFZzkd1xuCn1TqGTT7ANhAEnbI/yaVt3caI30kwUC2WIToFpNgu7Ej0x2TteY",
215 "OgrLrdcC4jy1SifmKYv/v3ZZxrd/eqttmH2k588panseRwHK3LVk7xA+URhQ/bjL",
216 "gPM59V0uR1l+z1fmooeJPFz5WSXNObc9Jqnh45FND+U/UYHXTLSomTn7jgZFxJBK",
217 "veS7q6Lat7wAnYZCF2dnPmhZoJv+SKPltA8HAGsgQGWBF1p5qxV1HrAUk8kBBnG2",
218 "paj0w8p5UM6RpDdCuvKH7j1LiuWffn3b9Z4dgzmE7jsMmvzoQtypzIKaSxhqzvFO",
219 "od9V8dJdAgMBAAECggEAGGIYjOIB1rOKkDHP4ljXutI0mCRPl3FMDemiBeppoIfZ",
220 "G/Q3qpAKmndDt0Quwh/yfcNdvZhf1kwCCTWri/uPz5fSUIyDV3TaTRu0ZWoHaBVj",
221 "Hxylg+4HRZUQj+Vi50/PWr/jQmAAVMcrMfcoTl82q2ynmP/R1vM3EsXOCjTliv5B",
222 "XlMPRjj/9PDBH0dnnVcAPDOpflzOTL2f4HTFEMlmg9/tZBnd96J/cmfhjAv9XpFL",
223 "FBAFZzs5pz0rwCNSR8QZNonnK7pngVUlGDLORK58y84tGmxZhGdne3CtCWey/sJ4",
224 "7QF0Pe8YqWBU56926IY6DcSVBuQGZ6vMCNlU7J8D2QKBgQDXyh3t2TicM/n1QBLk",
225 "zLoGmVUmxUGziHgl2dnJiGDtyOAU3+yCorPgFaCie29s5qm4b0YEGxUxPIrRrEro",
226 "h0FfKn9xmr8CdmTPTcjJW1+M7bxxq7oBoU/QzKXgIHlpeCjjnvPJt0PcNkNTjCXv",
227 "shsrINh2rENoe/x79eEfM/N5eQKBgQDPkYSmYyALoNq8zq0A4BdR+F5lb5Fj5jBH",
228 "Jk68l6Uti+0hRbJ2d1tQTLkU+eCPQLGBl6fuc1i4K5FV7v14jWtRPdD7wxrkRi3j",
229 "ilqQwLBOU6Bj3FK4DvlLF+iYTuBWj2/KcxflXECmsjitKHLK6H7kFEiuJql+NAHU",
230 "U9EFXepLBQKBgQDQ+HCnZ1bFHiiP8m7Zl9EGlvK5SwlnPV9s+F1KJ4IGhCNM09UM",
231 "ZVfgR9F5yCONyIrPiyK40ylgtwqQJlOcf281I8irUXpsfg7+Gou5Q31y0r9NLUpC",
232 "Td8niyePtqMdGjouxD2+OHXFCd+FRxFt4IMi7vnxYr0csAVAXkqWlw7PsQKBgH/G",
233 "/PnQm7GM3BrOwAGB8dksJDAddkshMScblezTDYP0V43b8firkTLliCo5iNum357/",
234 "VQmdSEhXyag07yR/Kklg3H2fpbZQ3X7tdMMXW3FcWagfwWw9C4oGtdDM/Z1Lv23J",
235 "XDR9je8QV4OBGul+Jl8RfYx3kG94ZIfo8Qt0vP5hAoGARjAzdCGYz42NwaUk8n94",
236 "W2RuKHtTV9vtjaAbfPFbZoGkT7sXNJVlrA0C+9f+H9rOTM3mX59KrjmLVzde4Vhs",
237 "avWMShuK4vpAiDQLU7GyABvi5CR6Ld+AT+LSzxHhVe0ASOQPNCA2SOz3RQvgPi7R",
238 "GDgRMUB6cL3IRVzcR0dC6cY=",
239 ))
240 .unwrap();
241
242 let public_key = STANDARD
243 .decode(concat!(
244 "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArvcXfr5pCD6KhzXo7BWc",
245 "5Hdcbgp9U6hk0+wDYQBJ2yP8mlbd3GiN9JMFAtliE6BaTYLuxI9Mdk7XmDoKy63X",
246 "AuI8tUon5imL/792Wca3f3qrbZh9pOfPKWp7HkcByty1ZO8QPlEYUP24y4DzOfVd",
247 "LkdZfs9X5qKHiTxc+VklzTm3PSap4eORTQ/lP1GB10y0qJk5+44GRcSQSr3ku6ui",
248 "2re8AJ2GQhdnZz5oWaCb/kij5bQPBwBrIEBlgRdaeasVdR6wFJPJAQZxtqWo9MPK",
249 "eVDOkaQ3Qrryh+49S4rln3592/WeHYM5hO47DJr86ELcqcyCmksYas7xTqHfVfHS",
250 "XQIDAQAB",
251 ))
252 .unwrap();
253
254 let private_key = AsymmetricCryptoKey::from_der(&private_key).unwrap();
255 let public_key = AsymmetricPublicCryptoKey::from_der(&public_key).unwrap();
256
257 let raw_key = SymmetricCryptoKey::make_aes256_cbc_hmac_key();
258 let encrypted = UnsignedSharedKey::encapsulate_key_unsigned(&raw_key, &public_key).unwrap();
259 let decrypted = encrypted.decapsulate_key_unsigned(&private_key).unwrap();
260
261 assert_eq!(raw_key, decrypted);
262 }
263}