autofill/encryption.rs
1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 */
5
6// This is the *local* encryption support - it has nothing to do with the
7// encryption used by sync.
8
9// For context, what "local encryption" means in this context is:
10// * We use regular sqlite, but want to ensure the credit-card numbers are
11// encrypted in the DB - so we store the number encrypted, and the key
12// is managed by the app.
13// * The credit-card API always just accepts and returns the encrypted string,
14// so we also expose encryption and decryption public functions that take
15// the key and text. The core storage API never knows the unencrypted number.
16//
17// This makes life tricky for Sync - sync has its own encryption and its own
18// management of sync keys. The entire records are encrypted on the server -
19// so the record on the server has the plain-text number (which is then
20// encrypted as part of the entire record), so:
21// * When transforming a record from the DB into a Sync record, we need to
22// *decrypt* the field.
23// * When transforming a record from Sync into a DB record, we need to *encrypt*
24// the field.
25//
26// So Sync needs to know the key etc, and that needs to get passed down
27// multiple layers, from the app saying "sync now" all the way down to the
28// low level sync code.
29// To make life a little easier, we do that via a struct.
30
31use crate::error::*;
32use error_support::handle_error;
33pub use jwcrypto::EncryptorDecryptor;
34
35// public functions we expose over the FFI (which is why they take `String`
36// rather than the `&str` you'd otherwise expect)
37#[handle_error(Error)]
38pub fn encrypt_string(key: String, cleartext: String) -> ApiResult<String> {
39 // It would be nice to have more detailed error messages, but that would require the consumer
40 // to pass them in. Let's not change the API yet.
41 Ok(EncryptorDecryptor::new(&key)?.encrypt(&cleartext)?)
42}
43
44#[handle_error(Error)]
45pub fn decrypt_string(key: String, ciphertext: String) -> ApiResult<String> {
46 // It would be nice to have more detailed error messages, but that would require the consumer
47 // to pass them in. Let's not change the API yet.
48 Ok(EncryptorDecryptor::new(&key)?.decrypt(&ciphertext)?)
49}
50
51#[handle_error(Error)]
52pub fn create_autofill_key() -> ApiResult<String> {
53 Ok(EncryptorDecryptor::create_key()?)
54}
55
56#[cfg(test)]
57mod test {
58 use super::*;
59 use nss::ensure_initialized;
60
61 #[test]
62 fn test_encrypt() {
63 ensure_initialized();
64 let ed = EncryptorDecryptor::new(&create_autofill_key().unwrap()).unwrap();
65 let cleartext = "secret";
66 let ciphertext = ed.encrypt(cleartext).unwrap();
67 assert_eq!(ed.decrypt(&ciphertext).unwrap(), cleartext);
68 let ed2 = EncryptorDecryptor::new(&create_autofill_key().unwrap()).unwrap();
69 assert!(matches!(
70 ed2.decrypt(&ciphertext).map_err(Error::from),
71 Err(Error::CryptoError(_))
72 ));
73 }
74
75 #[test]
76 fn test_decryption_errors() {
77 ensure_initialized();
78 let ed = EncryptorDecryptor::new(&create_autofill_key().unwrap()).unwrap();
79 assert!(matches!(
80 ed.decrypt("invalid-ciphertext").map_err(Error::from),
81 Err(Error::CryptoError(_)),
82 ));
83 assert!(matches!(
84 ed.decrypt("").unwrap_err(),
85 jwcrypto::JwCryptoError::EmptyCyphertext,
86 ));
87 }
88}