1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
pub mod frame;
pub mod injector;
pub mod meta;
pub mod replicator;
pub mod rpc;
pub mod snapshot;

mod error;

use libsql_sys::Cipher;

pub const LIBSQL_PAGE_SIZE: usize = 4096;

#[derive(Debug, Clone)]
pub struct FrameEncryptor {
    enc: cbc::Encryptor<aes::Aes256>,
    dec: cbc::Decryptor<aes::Aes256>,
}

impl FrameEncryptor {
    pub fn new(encryption_config: libsql_sys::EncryptionConfig) -> Self {
        #[cfg(feature = "encryption")]
        const SEED: u32 = 911;
        #[cfg(not(feature = "encryption"))]
        let _ = encryption_config;

        use aes::cipher::KeyIvInit;

        // TODO: make cipher configurable
        assert!(matches!(encryption_config.cipher, Cipher::Aes256Cbc));

        #[allow(unused_mut)]
        let mut iv: [u8; 16] = [0; 16];
        #[allow(unused_mut)]
        let mut digest: [u8; 32] = [0; 32];
        #[cfg(feature = "encryption")]
        libsql_sys::connection::generate_initial_vector(SEED, &mut iv);
        #[cfg(feature = "encryption")]
        libsql_sys::connection::generate_aes256_key(&encryption_config.encryption_key, &mut digest);

        let enc = cbc::Encryptor::new((&digest).into(), (&iv).into());
        let dec = cbc::Decryptor::new((&digest).into(), (&iv).into());
        Self { enc, dec }
    }

    pub fn encrypt(&self, data: &mut [u8]) -> Result<(), rusqlite::ffi::Error> {
        use aes::cipher::{block_padding::NoPadding, BlockEncryptMut};
        // NOTICE: We don't want to return padding errors, it will make the code
        // prone to CBC padding oracle attacks.
        self.enc
            .clone()
            .encrypt_padded_mut::<NoPadding>(data, data.len())
            .map_err(|_| rusqlite::ffi::Error::new(libsql_sys::ffi::SQLITE_IOERR_WRITE))?;
        Ok(())
    }

    pub fn decrypt(&self, data: &mut [u8]) -> Result<(), rusqlite::ffi::Error> {
        use aes::cipher::{block_padding::NoPadding, BlockDecryptMut};
        // NOTICE: We don't want to return padding errors, it will make the code
        // prone to CBC padding oracle attacks.
        self.dec
            .clone()
            .decrypt_padded_mut::<NoPadding>(data)
            .map_err(|_| rusqlite::ffi::Error::new(libsql_sys::ffi::SQLITE_IOERR_READ))?;
        Ok(())
    }
}