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}