1use std::{fmt::Display, pin::Pin, str::FromStr};
2
3use bitwarden_encoding::{B64, FromStrVisitor};
4use rsa::{RsaPrivateKey, RsaPublicKey, pkcs8::DecodePublicKey};
5use serde::{Deserialize, Serialize};
6use serde_repr::{Deserialize_repr, Serialize_repr};
7use tracing::instrument;
8#[cfg(feature = "wasm")]
9use wasm_bindgen::convert::FromWasmAbi;
10
11use super::key_encryptable::CryptoKey;
12use crate::{
13 Pkcs8PrivateKeyBytes, SpkiPublicKeyBytes,
14 error::{CryptoError, Result},
15};
16
17#[cfg(feature = "wasm")]
18#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
19const TS_CUSTOM_TYPES: &'static str = r#"
20export type PublicKey = Tagged<string, "PublicKey">;
21"#;
22
23#[cfg(feature = "wasm")]
24impl wasm_bindgen::describe::WasmDescribe for PublicKey {
25 fn describe() {
26 <String as wasm_bindgen::describe::WasmDescribe>::describe();
27 }
28}
29
30#[cfg(feature = "wasm")]
31impl FromWasmAbi for PublicKey {
32 type Abi = <String as FromWasmAbi>::Abi;
33
34 unsafe fn from_abi(abi: Self::Abi) -> Self {
35 use wasm_bindgen::UnwrapThrowExt;
36
37 let s = unsafe { String::from_abi(abi) };
38 let bytes: Vec<u8> = s.parse::<bitwarden_encoding::B64>().unwrap_throw().into();
39 PublicKey::from_der(&SpkiPublicKeyBytes::from(bytes)).unwrap_throw()
40 }
41}
42
43#[derive(Debug, Serialize_repr, Deserialize_repr)]
45#[repr(u8)]
46pub enum PublicKeyEncryptionAlgorithm {
47 RsaOaepSha1 = 0,
49}
50
51#[derive(Clone, PartialEq)]
52pub(crate) enum RawPublicKey {
53 RsaOaepSha1(RsaPublicKey),
54}
55
56#[derive(Clone, PartialEq)]
59pub struct PublicKey {
60 inner: RawPublicKey,
61}
62
63impl std::fmt::Debug for PublicKey {
64 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65 let key_suffix = match &self.inner {
66 RawPublicKey::RsaOaepSha1(_) => "RsaOaepSha1",
67 };
68 let mut debug_struct = f.debug_struct(format!("PublicKey::{}", key_suffix).as_str());
69 match &self.inner {
70 RawPublicKey::RsaOaepSha1(_) => {
71 if let Ok(der) = self.to_der() {
72 debug_struct.field("key", &hex::encode(der.as_ref()));
73 }
74 }
75 }
76 debug_struct.finish()
77 }
78}
79
80impl PublicKey {
81 pub(crate) fn inner(&self) -> &RawPublicKey {
82 &self.inner
83 }
84
85 #[instrument(skip_all, err)]
87 pub fn from_der(der: &SpkiPublicKeyBytes) -> Result<Self> {
88 Ok(PublicKey {
89 inner: RawPublicKey::RsaOaepSha1(
90 RsaPublicKey::from_public_key_der(der.as_ref())
91 .map_err(|_| CryptoError::InvalidKey)?,
92 ),
93 })
94 }
95
96 #[instrument(skip_all, err)]
98 pub fn to_der(&self) -> Result<SpkiPublicKeyBytes> {
99 use rsa::pkcs8::EncodePublicKey;
100 match &self.inner {
101 RawPublicKey::RsaOaepSha1(public_key) => Ok(public_key
102 .to_public_key_der()
103 .map_err(|_| CryptoError::InvalidKey)?
104 .as_bytes()
105 .to_owned()
106 .into()),
107 }
108 }
109}
110
111impl FromStr for PublicKey {
112 type Err = ();
113
114 fn from_str(s: &str) -> Result<Self, Self::Err> {
115 let bytes: Vec<u8> = s.parse::<B64>().map_err(|_| ())?.into();
116 Self::from_der(&SpkiPublicKeyBytes::from(bytes)).map_err(|_| ())
117 }
118}
119
120impl Display for PublicKey {
121 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122 match self.to_der() {
123 Ok(der) => write!(f, "{}", B64::from(der.as_ref())),
124 Err(_) => write!(f, "[INVALID PUBLIC KEY]"),
125 }
126 }
127}
128
129impl<'de> Deserialize<'de> for PublicKey {
130 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
131 where
132 D: serde::Deserializer<'de>,
133 {
134 deserializer.deserialize_str(FromStrVisitor::new())
135 }
136}
137
138impl Serialize for PublicKey {
139 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
140 where
141 S: serde::Serializer,
142 {
143 let der = self.to_der().map_err(serde::ser::Error::custom)?;
144 serializer.serialize_str(&B64::from(der.as_ref()).to_string())
145 }
146}
147
148#[derive(Clone)]
149pub(crate) enum RawPrivateKey {
150 RsaOaepSha1(Pin<Box<RsaPrivateKey>>),
155}
156
157#[derive(Clone)]
160pub struct PrivateKey {
161 inner: RawPrivateKey,
162}
163
164const _: fn() = || {
167 fn assert_zeroize_on_drop<T: zeroize::ZeroizeOnDrop>() {}
168 assert_zeroize_on_drop::<RsaPrivateKey>();
169};
170impl zeroize::ZeroizeOnDrop for PrivateKey {}
171impl CryptoKey for PrivateKey {}
172
173impl PrivateKey {
174 pub fn make(algorithm: PublicKeyEncryptionAlgorithm) -> Self {
176 Self::make_internal(algorithm, &mut rand::thread_rng())
177 }
178
179 fn make_internal<R: rand::CryptoRng + rand::RngCore>(
180 algorithm: PublicKeyEncryptionAlgorithm,
181 rng: &mut R,
182 ) -> Self {
183 match algorithm {
184 PublicKeyEncryptionAlgorithm::RsaOaepSha1 => Self {
185 inner: RawPrivateKey::RsaOaepSha1(Box::pin(
186 RsaPrivateKey::new(rng, 2048).expect("failed to generate a key"),
187 )),
188 },
189 }
190 }
191
192 #[allow(missing_docs)]
193 #[cfg_attr(feature = "dangerous-crypto-debug", instrument(err))]
194 #[cfg_attr(not(feature = "dangerous-crypto-debug"), instrument(skip_all, err))]
195 pub fn from_pem(pem: &str) -> Result<Self> {
196 use rsa::pkcs8::DecodePrivateKey;
197 Ok(Self {
198 inner: RawPrivateKey::RsaOaepSha1(Box::pin(
199 RsaPrivateKey::from_pkcs8_pem(pem).map_err(|_| CryptoError::InvalidKey)?,
200 )),
201 })
202 }
203
204 #[allow(missing_docs)]
205 #[cfg_attr(feature = "dangerous-crypto-debug", instrument(err))]
206 #[cfg_attr(not(feature = "dangerous-crypto-debug"), instrument(skip_all, err))]
207 pub fn from_der(der: &Pkcs8PrivateKeyBytes) -> Result<Self> {
208 use rsa::pkcs8::DecodePrivateKey;
209 Ok(Self {
210 inner: RawPrivateKey::RsaOaepSha1(Box::pin(
211 RsaPrivateKey::from_pkcs8_der(der.as_ref()).map_err(|_| CryptoError::InvalidKey)?,
212 )),
213 })
214 }
215
216 #[allow(missing_docs)]
217 #[cfg_attr(feature = "dangerous-crypto-debug", instrument(err))]
218 #[cfg_attr(not(feature = "dangerous-crypto-debug"), instrument(skip_all, err))]
219 pub fn to_der(&self) -> Result<Pkcs8PrivateKeyBytes> {
220 match &self.inner {
221 RawPrivateKey::RsaOaepSha1(private_key) => {
222 use rsa::pkcs8::EncodePrivateKey;
223 Ok(private_key
224 .to_pkcs8_der()
225 .map_err(|_| CryptoError::InvalidKey)?
226 .as_bytes()
227 .to_owned()
228 .into())
229 }
230 }
231 }
232
233 pub fn to_public_key(&self) -> PublicKey {
236 match &self.inner {
237 RawPrivateKey::RsaOaepSha1(private_key) => PublicKey {
238 inner: RawPublicKey::RsaOaepSha1(private_key.to_public_key()),
239 },
240 }
241 }
242
243 pub(crate) fn inner(&self) -> &RawPrivateKey {
244 &self.inner
245 }
246}
247
248impl std::fmt::Debug for PrivateKey {
250 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
251 let key_suffix = match &self.inner {
252 RawPrivateKey::RsaOaepSha1(_) => "RsaOaepSha1",
253 };
254 let mut debug_struct = f.debug_struct(format!("PrivateKey::{}", key_suffix).as_str());
255 #[cfg(feature = "dangerous-crypto-debug")]
256 match &self.inner {
257 RawPrivateKey::RsaOaepSha1(_) => {
258 if let Ok(der) = self.to_der() {
259 debug_struct.field("key", &hex::encode(der.as_ref()));
260 }
261 }
262 }
263 debug_struct.finish()
264 }
265}
266
267#[cfg(test)]
268mod tests {
269
270 use bitwarden_encoding::B64;
271
272 use crate::{
273 Pkcs8PrivateKeyBytes, PrivateKey, PublicKey, PublicKeyEncryptionAlgorithm,
274 SpkiPublicKeyBytes, SymmetricCryptoKey, UnsignedSharedKey,
275 content_format::{Bytes, Pkcs8PrivateKeyDerContentFormat},
276 };
277
278 #[test]
279 #[ignore = "Manual test to verify debug format"]
280 fn test_debug() {
281 let private_key = PrivateKey::make(PublicKeyEncryptionAlgorithm::RsaOaepSha1);
282 println!("{:?}", private_key);
283 let public_key = private_key.to_public_key();
284 println!("{:?}", public_key);
285 }
286
287 #[test]
288 fn test_asymmetric_crypto_key() {
289 let pem_key_str = "-----BEGIN PRIVATE KEY-----
290MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDiTQVuzhdygFz5
291qv14i+XFDGTnDravzUQT1hPKPGUZOUSZ1gwdNgkWqOIaOnR65BHEnL0sp4bnuiYc
292afeK2JAW5Sc8Z7IxBNSuAwhQmuKx3RochMIiuCkI2/p+JvUQoJu6FBNm8OoJ4Cwm
293qqHGZESMfnpQDCuDrB3JdJEdXhtmnl0C48sGjOk3WaBMcgGqn8LbJDUlyu1zdqyv
294b0waJf0iV4PJm2fkUl7+57D/2TkpbCqURVnZK1FFIEg8mr6FzSN1F2pOfktkNYZw
295P7MSNR7o81CkRSCMr7EkIVa+MZYMBx106BMK7FXgWB7nbSpsWKxBk7ZDHkID2fam
296rEcVtrzDAgMBAAECggEBAKwq9OssGGKgjhvUnyrLJHAZ0dqIMyzk+dotkLjX4gKi
297szJmyqiep6N5sStLNbsZMPtoU/RZMCW0VbJgXFhiEp2YkZU/Py5UAoqw++53J+kx
2980d/IkPphKbb3xUec0+1mg5O6GljDCQuiZXS1dIa/WfeZcezclW6Dz9WovY6ePjJ+
2998vEBR1icbNKzyeINd6MtPtpcgQPHtDwHvhPyUDbKDYGbLvjh9nui8h4+ZUlXKuVR
300jB0ChxiKV1xJRjkrEVoulOOicd5r597WfB2ghax3pvRZ4MdXemCXm3gQYqPVKach
301vGU+1cPQR/MBJZpxT+EZA97xwtFS3gqwbxJaNFcoE8ECgYEA9OaeYZhQPDo485tI
3021u/Z7L/3PNape9hBQIXoW7+MgcQ5NiWqYh8Jnj43EIYa0wM/ECQINr1Za8Q5e6KR
303J30FcU+kfyjuQ0jeXdNELGU/fx5XXNg/vV8GevHwxRlwzqZTCg6UExUZzbYEQqd7
304l+wPyETGeua5xCEywA1nX/D101kCgYEA7I6aMFjhEjO71RmzNhqjKJt6DOghoOfQ
305TjhaaanNEhLYSbenFz1mlb21mW67ulmz162saKdIYLxQNJIP8ZPmxh4ummOJI8w9
306ClHfo8WuCI2hCjJ19xbQJocSbTA5aJg6lA1IDVZMDbQwsnAByPRGpaLHBT/Q9Bye
307KvCMB+9amXsCgYEAx65yXSkP4sumPBrVHUub6MntERIGRxBgw/drKcPZEMWp0FiN
308wEuGUBxyUWrG3F69QK/gcqGZE6F/LSu0JvptQaKqgXQiMYJsrRvhbkFvsHpQyUcZ
309UZL1ebFjm5HOxPAgrQaN/bEqxOwwNRjSUWEMzUImg3c06JIZCzbinvudtKECgYEA
310kY3JF/iIPI/yglP27lKDlCfeeHSYxI3+oTKRhzSAxx8rUGidenJAXeDGDauR/T7W
311pt3pGNfddBBK9Z3uC4Iq3DqUCFE4f/taj7ADAJ1Q0Vh7/28/IJM77ojr8J1cpZwN
312Zy2o6PPxhfkagaDjqEeN9Lrs5LD4nEvDkr5CG1vOjmMCgYEAvIBFKRm31NyF8jLi
313CVuPwC5PzrW5iThDmsWTaXFpB3esUsbICO2pEz872oeQS+Em4GO5vXUlpbbFPzup
314PFhA8iMJ8TAvemhvc7oM0OZqpU6p3K4seHf6BkwLxumoA3vDJfovu9RuXVcJVOnf
315DnqOsltgPomWZ7xVfMkm9niL2OA=
316-----END PRIVATE KEY-----";
317
318 let der_key: B64 = "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=".parse().unwrap();
319 let der_key_vec: Vec<u8> = der_key.into();
320
321 let pem_key = PrivateKey::from_pem(pem_key_str).unwrap();
323 let der_key = PrivateKey::from_der(&Bytes::<Pkcs8PrivateKeyDerContentFormat>::from(
324 der_key_vec.clone(),
325 ))
326 .unwrap();
327 assert_eq!(pem_key.to_der().unwrap(), der_key.to_der().unwrap());
328
329 assert_eq!(der_key.to_der().unwrap().to_vec(), der_key_vec.clone());
332 assert_eq!(pem_key.to_der().unwrap().to_vec(), der_key_vec);
333 }
334
335 #[test]
336 fn test_encrypt_public_decrypt_private() {
337 let private_key: B64 = concat!(
338 "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCu9xd+vmkIPoqH",
339 "NejsFZzkd1xuCn1TqGTT7ANhAEnbI/yaVt3caI30kwUC2WIToFpNgu7Ej0x2TteY",
340 "OgrLrdcC4jy1SifmKYv/v3ZZxrd/eqttmH2k588panseRwHK3LVk7xA+URhQ/bjL",
341 "gPM59V0uR1l+z1fmooeJPFz5WSXNObc9Jqnh45FND+U/UYHXTLSomTn7jgZFxJBK",
342 "veS7q6Lat7wAnYZCF2dnPmhZoJv+SKPltA8HAGsgQGWBF1p5qxV1HrAUk8kBBnG2",
343 "paj0w8p5UM6RpDdCuvKH7j1LiuWffn3b9Z4dgzmE7jsMmvzoQtypzIKaSxhqzvFO",
344 "od9V8dJdAgMBAAECggEAGGIYjOIB1rOKkDHP4ljXutI0mCRPl3FMDemiBeppoIfZ",
345 "G/Q3qpAKmndDt0Quwh/yfcNdvZhf1kwCCTWri/uPz5fSUIyDV3TaTRu0ZWoHaBVj",
346 "Hxylg+4HRZUQj+Vi50/PWr/jQmAAVMcrMfcoTl82q2ynmP/R1vM3EsXOCjTliv5B",
347 "XlMPRjj/9PDBH0dnnVcAPDOpflzOTL2f4HTFEMlmg9/tZBnd96J/cmfhjAv9XpFL",
348 "FBAFZzs5pz0rwCNSR8QZNonnK7pngVUlGDLORK58y84tGmxZhGdne3CtCWey/sJ4",
349 "7QF0Pe8YqWBU56926IY6DcSVBuQGZ6vMCNlU7J8D2QKBgQDXyh3t2TicM/n1QBLk",
350 "zLoGmVUmxUGziHgl2dnJiGDtyOAU3+yCorPgFaCie29s5qm4b0YEGxUxPIrRrEro",
351 "h0FfKn9xmr8CdmTPTcjJW1+M7bxxq7oBoU/QzKXgIHlpeCjjnvPJt0PcNkNTjCXv",
352 "shsrINh2rENoe/x79eEfM/N5eQKBgQDPkYSmYyALoNq8zq0A4BdR+F5lb5Fj5jBH",
353 "Jk68l6Uti+0hRbJ2d1tQTLkU+eCPQLGBl6fuc1i4K5FV7v14jWtRPdD7wxrkRi3j",
354 "ilqQwLBOU6Bj3FK4DvlLF+iYTuBWj2/KcxflXECmsjitKHLK6H7kFEiuJql+NAHU",
355 "U9EFXepLBQKBgQDQ+HCnZ1bFHiiP8m7Zl9EGlvK5SwlnPV9s+F1KJ4IGhCNM09UM",
356 "ZVfgR9F5yCONyIrPiyK40ylgtwqQJlOcf281I8irUXpsfg7+Gou5Q31y0r9NLUpC",
357 "Td8niyePtqMdGjouxD2+OHXFCd+FRxFt4IMi7vnxYr0csAVAXkqWlw7PsQKBgH/G",
358 "/PnQm7GM3BrOwAGB8dksJDAddkshMScblezTDYP0V43b8firkTLliCo5iNum357/",
359 "VQmdSEhXyag07yR/Kklg3H2fpbZQ3X7tdMMXW3FcWagfwWw9C4oGtdDM/Z1Lv23J",
360 "XDR9je8QV4OBGul+Jl8RfYx3kG94ZIfo8Qt0vP5hAoGARjAzdCGYz42NwaUk8n94",
361 "W2RuKHtTV9vtjaAbfPFbZoGkT7sXNJVlrA0C+9f+H9rOTM3mX59KrjmLVzde4Vhs",
362 "avWMShuK4vpAiDQLU7GyABvi5CR6Ld+AT+LSzxHhVe0ASOQPNCA2SOz3RQvgPi7R",
363 "GDgRMUB6cL3IRVzcR0dC6cY=",
364 )
365 .parse()
366 .unwrap();
367
368 let public_key: B64 = concat!(
369 "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArvcXfr5pCD6KhzXo7BWc",
370 "5Hdcbgp9U6hk0+wDYQBJ2yP8mlbd3GiN9JMFAtliE6BaTYLuxI9Mdk7XmDoKy63X",
371 "AuI8tUon5imL/792Wca3f3qrbZh9pOfPKWp7HkcByty1ZO8QPlEYUP24y4DzOfVd",
372 "LkdZfs9X5qKHiTxc+VklzTm3PSap4eORTQ/lP1GB10y0qJk5+44GRcSQSr3ku6ui",
373 "2re8AJ2GQhdnZz5oWaCb/kij5bQPBwBrIEBlgRdaeasVdR6wFJPJAQZxtqWo9MPK",
374 "eVDOkaQ3Qrryh+49S4rln3592/WeHYM5hO47DJr86ELcqcyCmksYas7xTqHfVfHS",
375 "XQIDAQAB",
376 )
377 .parse()
378 .unwrap();
379
380 let private_key = Pkcs8PrivateKeyBytes::from(private_key.as_bytes());
381 let private_key = PrivateKey::from_der(&private_key).unwrap();
382 let public_key = PublicKey::from_der(&SpkiPublicKeyBytes::from(&public_key)).unwrap();
383
384 let raw_key = SymmetricCryptoKey::make_aes256_cbc_hmac_key();
385 #[expect(deprecated)]
386 let encrypted = UnsignedSharedKey::encapsulate_key_unsigned(&raw_key, &public_key).unwrap();
387 #[expect(deprecated)]
388 let decrypted = encrypted.decapsulate_key_unsigned(&private_key).unwrap();
389
390 assert_eq!(raw_key, decrypted);
391 }
392
393 #[test]
394 fn test_asymmetric_public_crypto_key_from_str() {
395 let public_key_b64 = concat!(
396 "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArvcXfr5pCD6KhzXo7BWc",
397 "5Hdcbgp9U6hk0+wDYQBJ2yP8mlbd3GiN9JMFAtliE6BaTYLuxI9Mdk7XmDoKy63X",
398 "AuI8tUon5imL/792Wca3f3qrbZh9pOfPKWp7HkcByty1ZO8QPlEYUP24y4DzOfVd",
399 "LkdZfs9X5qKHiTxc+VklzTm3PSap4eORTQ/lP1GB10y0qJk5+44GRcSQSr3ku6ui",
400 "2re8AJ2GQhdnZz5oWaCb/kij5bQPBwBrIEBlgRdaeasVdR6wFJPJAQZxtqWo9MPK",
401 "eVDOkaQ3Qrryh+49S4rln3592/WeHYM5hO47DJr86ELcqcyCmksYas7xTqHfVfHS",
402 "XQIDAQAB",
403 );
404
405 let parsed_key: PublicKey = public_key_b64.parse().expect("should parse");
407
408 let der = parsed_key.to_der().expect("should convert to DER");
410 let b64_str = B64::from(der.as_ref()).to_string();
411 assert_eq!(b64_str, public_key_b64);
412 }
413
414 #[test]
415 fn test_asymmetric_public_crypto_key_from_str_invalid() {
416 let result: Result<PublicKey, _> = "not-valid-base64!!!".parse();
418 assert!(result.is_err());
419
420 let result: Result<PublicKey, _> = "aGVsbG8gd29ybGQ=".parse();
422 assert!(result.is_err());
423 }
424
425 #[test]
426 fn test_asymmetric_public_crypto_key_serialize_deserialize() {
427 let public_key_b64 = concat!(
428 "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArvcXfr5pCD6KhzXo7BWc",
429 "5Hdcbgp9U6hk0+wDYQBJ2yP8mlbd3GiN9JMFAtliE6BaTYLuxI9Mdk7XmDoKy63X",
430 "AuI8tUon5imL/792Wca3f3qrbZh9pOfPKWp7HkcByty1ZO8QPlEYUP24y4DzOfVd",
431 "LkdZfs9X5qKHiTxc+VklzTm3PSap4eORTQ/lP1GB10y0qJk5+44GRcSQSr3ku6ui",
432 "2re8AJ2GQhdnZz5oWaCb/kij5bQPBwBrIEBlgRdaeasVdR6wFJPJAQZxtqWo9MPK",
433 "eVDOkaQ3Qrryh+49S4rln3592/WeHYM5hO47DJr86ELcqcyCmksYas7xTqHfVfHS",
434 "XQIDAQAB",
435 );
436
437 let key: PublicKey = public_key_b64.parse().expect("should parse");
439
440 let serialized = serde_json::to_string(&key).expect("should serialize");
442 assert_eq!(serialized, format!("\"{}\"", public_key_b64));
443
444 let deserialized: PublicKey =
446 serde_json::from_str(&serialized).expect("should deserialize");
447
448 assert_eq!(
450 key.to_der().expect("should convert to DER"),
451 deserialized.to_der().expect("should convert to DER")
452 );
453 }
454
455 #[test]
456 fn test_asymmetric_public_crypto_key_deserialize_invalid() {
457 let result: Result<PublicKey, _> = serde_json::from_str("\"not-valid-base64!!!\"");
459 assert!(result.is_err());
460
461 let result: Result<PublicKey, _> = serde_json::from_str("\"aGVsbG8gd29ybGQ=\"");
463 assert!(result.is_err());
464
465 let result: Result<PublicKey, _> = serde_json::from_str("123");
467 assert!(result.is_err());
468 }
469}