diff --git a/src/backup/crypt_config.rs b/src/backup/crypt_config.rs index b8697d01..2fec19bc 100644 --- a/src/backup/crypt_config.rs +++ b/src/backup/crypt_config.rs @@ -51,6 +51,11 @@ impl CryptConfig { Ok(Self { id_key, id_pkey, enc_key, cipher: Cipher::aes_256_gcm() }) } + /// Expose Cipher + pub fn cipher(&self) -> &Cipher { + &self.cipher + } + /// Compute a chunk digest using a secret name space. /// /// Computes an SHA256 checksum over some secret data (derived diff --git a/src/backup/data_blob.rs b/src/backup/data_blob.rs index af41b841..c76c01de 100644 --- a/src/backup/data_blob.rs +++ b/src/backup/data_blob.rs @@ -309,6 +309,58 @@ impl DataBlob { use std::io::{Read, BufRead, BufReader, Write, Seek, SeekFrom}; +struct CryptReader { + reader: R, + block_size: usize, + crypter: openssl::symm::Crypter, + finalized: bool, +} + +impl CryptReader { + + fn new(reader: R, iv: [u8; 16], tag: [u8; 16], config: &CryptConfig) -> Result { + let block_size = config.cipher().block_size(); + if block_size.count_ones() != 1 || block_size > 1024 { + bail!("unexpected Cipher block size {}", block_size); + } + let mut crypter = config.data_crypter(&iv, openssl::symm::Mode::Decrypt)?; + crypter.set_tag(&tag)?; + Ok(Self { reader, crypter, block_size, finalized: false }) + } + + fn finish(self) -> Result { + if !self.finalized { + bail!("CryptReader not successfully finalized."); + } + Ok(self.reader) + } +} + +impl Read for CryptReader { + + fn read(&mut self, buf: &mut [u8]) -> Result { + if buf.len() <= 2*self.block_size { + panic!("CryptReader read buffer too small!"); + } + + let data = self.reader.fill_buf()?; + + if data.len() == 0 { // EOF + let rest = self.crypter.finalize(buf)?; + self.finalized = true; + Ok(rest) + } else { + let mut read_size = buf.len() - self.block_size; + if read_size > data.len() { + read_size = data.len(); + } + let count = self.crypter.update(&data[..read_size], buf)?; + self.reader.consume(read_size); + Ok(count) + } + } +} + struct CryptWriter { writer: W, encr_buf: [u8; 64*1024], @@ -697,6 +749,7 @@ enum BlobReaderState<'a, R: Read> { Compressed { expected_crc: u32, decompr: zstd::stream::read::Decoder>> }, Signed { expected_crc: u32, expected_hmac: [u8; 32], csum_reader: ChecksumReader<'a, R> }, SignedCompressed { expected_crc: u32, expected_hmac: [u8; 32], decompr: zstd::stream::read::Decoder>> }, + Encrypted { expected_crc: u32, decrypt_reader: CryptReader>> }, } /// Read data blobs @@ -740,6 +793,16 @@ impl <'a, R: Read> DataBlobReader<'a, R> { let decompr = zstd::stream::read::Decoder::new(csum_reader)?; Ok(Self { state: BlobReaderState::SignedCompressed { expected_crc, expected_hmac, decompr }}) } + ENCRYPTED_BLOB_MAGIC_1_0 => { + let expected_crc = u32::from_le_bytes(head.crc); + let mut iv = [0u8; 16]; + let mut expected_tag = [0u8; 16]; + reader.read_exact(&mut iv)?; + reader.read_exact(&mut expected_tag)?; + let csum_reader = ChecksumReader::new(reader, None); + let decrypt_reader = CryptReader::new(BufReader::with_capacity(64*1024,csum_reader), iv, expected_tag, config.unwrap())?; + Ok(Self { state: BlobReaderState::Encrypted { expected_crc, decrypt_reader }}) + } _ => bail!("got wrong magic number {:?}", head.magic) } } @@ -786,6 +849,14 @@ impl <'a, R: Read> DataBlobReader<'a, R> { } Ok(reader) } + BlobReaderState::Encrypted { expected_crc, decrypt_reader } => { + let csum_reader = decrypt_reader.finish()?.into_inner(); + let (reader, crc, _) = csum_reader.finish()?; + if crc != expected_crc { + bail!("blob crc check failed"); + } + Ok(reader) + } } } } @@ -806,6 +877,9 @@ impl <'a, R: BufRead> Read for DataBlobReader<'a, R> { BlobReaderState::SignedCompressed { decompr, .. } => { decompr.read(buf) } + BlobReaderState::Encrypted { decrypt_reader, .. } => { + decrypt_reader.read(buf) + } } } }