1#![allow(const_evaluatable_unchecked)]
53
54use crate::error::*;
55use std::sync::Arc;
56
57#[cfg(feature = "keydb")]
58use futures::executor::block_on;
59
60#[cfg(feature = "keydb")]
61use async_trait::async_trait;
62
63#[cfg(feature = "keydb")]
64use parking_lot::RwLock;
65
66#[cfg(feature = "keydb")]
67use nss_as::assert_initialized as assert_nss_initialized;
68#[cfg(feature = "keydb")]
69use nss_as::pk11::sym_key::{
70 authenticate_with_primary_password, authentication_with_primary_password_is_needed,
71 get_or_create_aes256_key,
72};
73
74#[uniffi::trait_interface]
82pub trait EncryptorDecryptor: Send + Sync {
83 fn encrypt(&self, cleartext: Vec<u8>) -> ApiResult<Vec<u8>>;
84 fn decrypt(&self, ciphertext: Vec<u8>) -> ApiResult<Vec<u8>>;
85}
86
87impl<T: EncryptorDecryptor> EncryptorDecryptor for Arc<T> {
88 fn encrypt(&self, clearbytes: Vec<u8>) -> ApiResult<Vec<u8>> {
89 (**self).encrypt(clearbytes)
90 }
91
92 fn decrypt(&self, cipherbytes: Vec<u8>) -> ApiResult<Vec<u8>> {
93 (**self).decrypt(cipherbytes)
94 }
95}
96
97pub struct ManagedEncryptorDecryptor {
100 key_manager: Arc<dyn KeyManager>,
101}
102
103impl ManagedEncryptorDecryptor {
104 #[uniffi::constructor()]
105 pub fn new(key_manager: Arc<dyn KeyManager>) -> Self {
106 Self { key_manager }
107 }
108}
109
110impl EncryptorDecryptor for ManagedEncryptorDecryptor {
111 fn encrypt(&self, clearbytes: Vec<u8>) -> ApiResult<Vec<u8>> {
112 let keybytes = self
113 .key_manager
114 .get_key()
115 .map_err(|_| LoginsApiError::MissingKey)?;
116 let key = std::str::from_utf8(&keybytes).map_err(|_| LoginsApiError::InvalidKey)?;
117
118 let encdec = jwcrypto::EncryptorDecryptor::new(key)
119 .map_err(|_: jwcrypto::JwCryptoError| LoginsApiError::InvalidKey)?;
120
121 let cleartext =
122 std::str::from_utf8(&clearbytes).map_err(|e| LoginsApiError::EncryptionFailed {
123 reason: e.to_string(),
124 })?;
125 encdec
126 .encrypt(cleartext)
127 .map_err(
128 |e: jwcrypto::JwCryptoError| LoginsApiError::EncryptionFailed {
129 reason: e.to_string(),
130 },
131 )
132 .map(|text| text.into())
133 }
134
135 fn decrypt(&self, cipherbytes: Vec<u8>) -> ApiResult<Vec<u8>> {
136 let keybytes = self
137 .key_manager
138 .get_key()
139 .map_err(|_| LoginsApiError::MissingKey)?;
140 let key = std::str::from_utf8(&keybytes).map_err(|_| LoginsApiError::InvalidKey)?;
141
142 let encdec = jwcrypto::EncryptorDecryptor::new(key)
143 .map_err(|_: jwcrypto::JwCryptoError| LoginsApiError::InvalidKey)?;
144
145 let ciphertext =
146 std::str::from_utf8(&cipherbytes).map_err(|e| LoginsApiError::DecryptionFailed {
147 reason: e.to_string(),
148 })?;
149 encdec
150 .decrypt(ciphertext)
151 .map_err(
152 |e: jwcrypto::JwCryptoError| LoginsApiError::DecryptionFailed {
153 reason: e.to_string(),
154 },
155 )
156 .map(|text| text.into())
157 }
158}
159
160#[uniffi::trait_interface]
163pub trait KeyManager: Send + Sync {
164 fn get_key(&self) -> ApiResult<Vec<u8>>;
165}
166
167pub struct StaticKeyManager {
170 key: String,
171}
172
173impl StaticKeyManager {
174 pub fn new(key: String) -> Self {
175 Self { key }
176 }
177}
178
179impl KeyManager for StaticKeyManager {
180 #[handle_error(Error)]
181 fn get_key(&self) -> ApiResult<Vec<u8>> {
182 Ok(self.key.as_bytes().into())
183 }
184}
185
186#[cfg(feature = "keydb")]
189#[uniffi::export(with_foreign)]
190#[async_trait]
191pub trait PrimaryPasswordAuthenticator: Send + Sync {
192 async fn get_primary_password(&self) -> ApiResult<String>;
195 async fn on_authentication_success(&self) -> ApiResult<()>;
196 async fn on_authentication_failure(&self) -> ApiResult<()>;
197}
198
199#[cfg(feature = "keydb")]
243#[derive(uniffi::Object)]
244pub struct NSSKeyManager {
245 primary_password_authenticator: Arc<dyn PrimaryPasswordAuthenticator>,
246 cached_key: RwLock<Option<Vec<u8>>>,
247}
248
249#[cfg(feature = "keydb")]
250#[uniffi::export]
251impl NSSKeyManager {
252 #[uniffi::constructor()]
256 pub fn new(primary_password_authenticator: Arc<dyn PrimaryPasswordAuthenticator>) -> Self {
257 assert_nss_initialized();
258 Self {
259 primary_password_authenticator,
260 cached_key: RwLock::new(None),
261 }
262 }
263
264 pub fn into_dyn_key_manager(self: Arc<Self>) -> Arc<dyn KeyManager> {
265 self
266 }
267}
268
269#[cfg(feature = "keydb")]
271static KEY_NAME: &str = "as-logins-key";
272
273#[cfg(feature = "keydb")]
275fn api_authentication_with_primary_password_is_needed() -> ApiResult<bool> {
276 authentication_with_primary_password_is_needed().map_err(|e: nss_as::Error| {
277 LoginsApiError::NSSAuthenticationError {
278 reason: e.to_string(),
279 }
280 })
281}
282
283#[cfg(feature = "keydb")]
285fn api_authenticate_with_primary_password(primary_password: &str) -> ApiResult<bool> {
286 authenticate_with_primary_password(primary_password).map_err(|e: nss_as::Error| {
287 LoginsApiError::NSSAuthenticationError {
288 reason: e.to_string(),
289 }
290 })
291}
292
293#[cfg(feature = "keydb")]
294impl KeyManager for NSSKeyManager {
295 fn get_key(&self) -> ApiResult<Vec<u8>> {
296 if api_authentication_with_primary_password_is_needed()? {
297 *self.cached_key.write() = None;
299
300 let primary_password =
301 block_on(self.primary_password_authenticator.get_primary_password())?;
302 let mut result = api_authenticate_with_primary_password(&primary_password)?;
303
304 if result {
305 block_on(
306 self.primary_password_authenticator
307 .on_authentication_success(),
308 )?;
309 } else {
310 while !result {
311 block_on(
312 self.primary_password_authenticator
313 .on_authentication_failure(),
314 )?;
315
316 let primary_password =
317 block_on(self.primary_password_authenticator.get_primary_password())?;
318 result = api_authenticate_with_primary_password(&primary_password)?;
319 }
320 block_on(
321 self.primary_password_authenticator
322 .on_authentication_success(),
323 )?;
324 }
325 }
326
327 let cached = self.cached_key.read().clone();
328 if let Some(bytes) = cached {
329 return Ok(bytes);
330 }
331
332 let key = get_or_create_aes256_key(KEY_NAME).map_err(|_| LoginsApiError::MissingKey)?;
333 let mut bytes: Vec<u8> = Vec::new();
334 serde_json::to_writer(
335 &mut bytes,
336 &jwcrypto::Jwk::new_direct_from_bytes(None, &key),
337 )
338 .unwrap();
339 *self.cached_key.write() = Some(bytes.clone());
340 Ok(bytes)
341 }
342}
343
344#[handle_error(Error)]
345pub fn create_canary(text: &str, key: &str) -> ApiResult<String> {
346 Ok(jwcrypto::EncryptorDecryptor::new(key)?.create_canary(text)?)
347}
348
349pub fn check_canary(canary: &str, text: &str, key: &str) -> ApiResult<bool> {
350 let encdec = jwcrypto::EncryptorDecryptor::new(key)
351 .map_err(|_: jwcrypto::JwCryptoError| LoginsApiError::InvalidKey)?;
352 Ok(encdec.check_canary(canary, text).unwrap_or(false))
353}
354
355#[handle_error(Error)]
356pub fn create_key() -> ApiResult<String> {
357 Ok(jwcrypto::EncryptorDecryptor::create_key()?)
358}
359
360#[cfg(test)]
361pub mod test_utils {
362 use super::*;
363 use serde::{de::DeserializeOwned, Serialize};
364
365 lazy_static::lazy_static! {
366 pub static ref TEST_ENCRYPTION_KEY: String = serde_json::to_string(&jwcrypto::Jwk::new_direct_key(Some("test-key".to_string())).unwrap()).unwrap();
367 pub static ref TEST_ENCDEC: Arc<ManagedEncryptorDecryptor> = Arc::new(ManagedEncryptorDecryptor::new(Arc::new(StaticKeyManager { key: TEST_ENCRYPTION_KEY.clone() })));
368 }
369
370 pub fn encrypt_struct<T: Serialize>(fields: &T) -> String {
371 let string = serde_json::to_string(fields).unwrap();
372 let cipherbytes = TEST_ENCDEC.encrypt(string.as_bytes().into()).unwrap();
373 std::str::from_utf8(&cipherbytes).unwrap().to_owned()
374 }
375 pub fn decrypt_struct<T: DeserializeOwned>(ciphertext: String) -> T {
376 let jsonbytes = TEST_ENCDEC.decrypt(ciphertext.as_bytes().into()).unwrap();
377 serde_json::from_str(std::str::from_utf8(&jsonbytes).unwrap()).unwrap()
378 }
379}
380
381#[cfg(not(feature = "keydb"))]
382#[cfg(test)]
383mod tests {
384 use super::*;
385 use nss_as::ensure_initialized;
386
387 #[test]
388 fn test_static_key_manager() {
389 ensure_initialized();
390 let key = create_key().unwrap();
391 let key_manager = StaticKeyManager { key: key.clone() };
392 assert_eq!(key.as_bytes(), key_manager.get_key().unwrap());
393 }
394
395 #[test]
396 fn test_managed_encdec_with_invalid_key() {
397 ensure_initialized();
398 let key_manager = Arc::new(StaticKeyManager {
399 key: "bad_key".to_owned(),
400 });
401 let encdec = ManagedEncryptorDecryptor { key_manager };
402 assert!(matches!(
403 encdec.encrypt("secret".as_bytes().into()).err().unwrap(),
404 LoginsApiError::InvalidKey
405 ));
406 }
407
408 #[test]
409 fn test_managed_encdec_with_missing_key() {
410 ensure_initialized();
411 struct MyKeyManager {}
412 impl KeyManager for MyKeyManager {
413 fn get_key(&self) -> ApiResult<Vec<u8>> {
414 Err(LoginsApiError::MissingKey)
415 }
416 }
417 let key_manager = Arc::new(MyKeyManager {});
418 let encdec = ManagedEncryptorDecryptor { key_manager };
419 assert!(matches!(
420 encdec.encrypt("secret".as_bytes().into()).err().unwrap(),
421 LoginsApiError::MissingKey
422 ));
423 }
424
425 #[test]
426 fn test_managed_encdec() {
427 ensure_initialized();
428 let key = create_key().unwrap();
429 let key_manager = Arc::new(StaticKeyManager { key });
430 let encdec = ManagedEncryptorDecryptor { key_manager };
431 let cleartext = "secret";
432 let ciphertext = encdec.encrypt(cleartext.as_bytes().into()).unwrap();
433 assert_eq!(
434 encdec.decrypt(ciphertext.clone()).unwrap(),
435 cleartext.as_bytes()
436 );
437 let other_encdec = ManagedEncryptorDecryptor {
438 key_manager: Arc::new(StaticKeyManager {
439 key: create_key().unwrap(),
440 }),
441 };
442
443 assert_eq!(
444 other_encdec.decrypt(ciphertext).err().unwrap().to_string(),
445 "decryption failed: Crypto error: NSS error: NSS error: -8190 "
446 );
447 }
448
449 #[test]
450 fn test_key_error() {
451 let storage_err = jwcrypto::EncryptorDecryptor::new("bad-key").err().unwrap();
452 println!("{storage_err:?}");
453 assert!(matches!(storage_err, jwcrypto::JwCryptoError::InvalidKey));
454 }
455
456 #[test]
457 fn test_canary_functionality() {
458 ensure_initialized();
459 const CANARY_TEXT: &str = "Arbitrary sequence of text";
460 let key = create_key().unwrap();
461 let canary = create_canary(CANARY_TEXT, &key).unwrap();
462 assert!(check_canary(&canary, CANARY_TEXT, &key).unwrap());
463
464 let different_key = create_key().unwrap();
465 assert!(!check_canary(&canary, CANARY_TEXT, &different_key).unwrap());
466
467 let bad_key = "bad_key".to_owned();
468 assert!(matches!(
469 check_canary(&canary, CANARY_TEXT, &bad_key).err().unwrap(),
470 LoginsApiError::InvalidKey
471 ));
472 }
473}
474
475#[cfg(feature = "keydb")]
476#[cfg(test)]
477mod tests_keydb {
478 use super::*;
479 use nss_as::ensure_initialized_with_profile_dir;
480 use std::path::PathBuf;
481
482 struct MockPrimaryPasswordAuthenticator {
483 password: String,
484 }
485
486 #[async_trait]
487 impl PrimaryPasswordAuthenticator for MockPrimaryPasswordAuthenticator {
488 async fn get_primary_password(&self) -> ApiResult<String> {
489 Ok(self.password.clone())
490 }
491 async fn on_authentication_success(&self) -> ApiResult<()> {
492 Ok(())
493 }
494 async fn on_authentication_failure(&self) -> ApiResult<()> {
495 Ok(())
496 }
497 }
498
499 fn profile_path() -> PathBuf {
500 std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
501 .join("../support/rc_crypto/nss/fixtures/profile")
502 }
503
504 #[test]
505 fn test_ensure_initialized_with_profile_dir() {
506 ensure_initialized_with_profile_dir(profile_path());
507 }
508
509 #[test]
510 fn test_create_key() {
511 ensure_initialized_with_profile_dir(profile_path());
512 let key = create_key().unwrap();
513 assert_eq!(key.len(), 63)
514 }
515
516 #[test]
517 fn test_nss_key_manager() {
518 ensure_initialized_with_profile_dir(profile_path());
519 let mock_primary_password_authenticator = MockPrimaryPasswordAuthenticator {
521 password: "password".to_string(),
522 };
523 let nss_key_manager = NSSKeyManager::new(Arc::new(mock_primary_password_authenticator));
524 let expected = [
526 123, 34, 107, 116, 121, 34, 58, 34, 111, 99, 116, 34, 44, 34, 107, 34, 58, 34, 66, 74,
527 104, 84, 108, 103, 51, 118, 56, 49, 65, 66, 51, 118, 87, 50, 71, 122, 54, 104, 69, 54,
528 84, 116, 75, 83, 112, 85, 102, 84, 86, 75, 73, 83, 99, 74, 45, 77, 78, 83, 67, 117, 99,
529 34, 125,
530 ]
531 .to_vec();
532 assert_eq!(nss_key_manager.get_key().unwrap(), expected);
533 }
534
535 #[test]
536 fn test_nss_key_manager_caching() {
537 ensure_initialized_with_profile_dir(profile_path());
538 let nss_key_manager = NSSKeyManager::new(Arc::new(MockPrimaryPasswordAuthenticator {
540 password: "password".to_string(),
541 }));
542
543 let key = nss_key_manager.get_key().unwrap();
544 assert_eq!(*nss_key_manager.cached_key.read(), Some(key.clone()));
545
546 let sentinel = b"sentinel".to_vec();
548 *nss_key_manager.cached_key.write() = Some(sentinel.clone());
549 assert_eq!(nss_key_manager.get_key().unwrap(), sentinel);
550
551 assert!(!authenticate_with_primary_password("wrong password").unwrap());
554 assert_eq!(nss_key_manager.get_key().unwrap(), key);
555 assert_eq!(*nss_key_manager.cached_key.read(), Some(key));
556 }
557
558 #[test]
559 fn test_primary_password_authentication() {
560 ensure_initialized_with_profile_dir(profile_path());
561 assert!(authenticate_with_primary_password("password").unwrap());
562 }
563}