Avoid allocation in decrypt_block

This commit is contained in:
oupson 2022-10-16 13:26:12 +02:00
parent fdcdc83648
commit 58ee7769d8
Signed by: oupson
GPG Key ID: 3BD88615552EFCB7
3 changed files with 37 additions and 42 deletions

View File

@ -240,7 +240,7 @@ impl EncryptedFs {
{ {
let n = file.read(&mut buf)?; let n = file.read(&mut buf)?;
let res = decoder.decrypt_block(&buf[..n], block_index, id)?; let res = decoder.decrypt_block(&mut buf[..n], block_index, id)?;
let seek = (offset as u64 - block_index * 4096) as usize; let seek = (offset as u64 - block_index * 4096) as usize;
buffer.extend_from_slice(&res[seek..]); buffer.extend_from_slice(&res[seek..]);
@ -257,7 +257,7 @@ impl EncryptedFs {
break; break;
} }
let res = decoder.decrypt_block(&buf[..n], block_index, id)?; let res = decoder.decrypt_block(&mut buf[..n], block_index, id)?;
let size = res.len().min(rem); let size = res.len().min(rem);

View File

@ -1,7 +1,7 @@
//! Utilities for file encryption. //! Utilities for file encryption.
use aes_gcm::{aead::generic_array::GenericArray, aes::Aes256, AeadInPlace, AesGcm, NewAead}; use aes_gcm::{aead::generic_array::GenericArray, aes::Aes256, AeadInPlace, AesGcm, NewAead};
use cipher::consts::{U16, U32}; use cipher::consts::U16;
use hkdf::Hkdf; use hkdf::Hkdf;
mod error; mod error;
@ -12,8 +12,8 @@ type Aes256Gcm = AesGcm<Aes256, U16>;
/// ContentEnc implement all methods related to file encryption. /// ContentEnc implement all methods related to file encryption.
pub struct ContentEnc { pub struct ContentEnc {
key: GenericArray<u8, U32>,
iv_len: usize, iv_len: usize,
cipher: Aes256Gcm,
} }
impl ContentEnc { impl ContentEnc {
@ -24,56 +24,47 @@ impl ContentEnc {
hdkf.expand(b"AES-GCM file content encryption", &mut key)?; hdkf.expand(b"AES-GCM file content encryption", &mut key)?;
Ok(Self { Ok(Self {
key: GenericArray::from(key),
iv_len: iv_len as usize, iv_len: iv_len as usize,
cipher: Aes256Gcm::new(&GenericArray::from(key)),
}) })
} }
/// Decrypt a encrypted block of len (iv_len + decrypted_block_size + iv_len), with the block number and the file id. /// Decrypt a encrypted block of len (iv_len + decrypted_block_size + iv_len), with the block number and the file id.
pub fn decrypt_block( /// The content of block is replaced with the plain text, in form of iv + plaintext + tag.
pub fn decrypt_block<'a>(
&self, &self,
block: &[u8], block: &'a mut [u8],
block_number: u64, block_number: u64,
file_id: Option<&[u8]>, file_id: Option<&[u8]>,
) -> Result<Vec<u8>, ContentCipherError> { ) -> Result<&'a [u8], ContentCipherError> {
// TODO NOT BOX
if block.is_empty() { if block.is_empty() {
return Ok(block.into()); return Ok(block);
} } else if block.iter().all(|f| *f == 0) {
return Ok(&block[0..block.len() - self.iv_len * 2]);
if block.iter().all(|f| *f == 0) { } else if block.len() < self.iv_len * 2 {
todo!("black hole")
}
if block.len() < self.iv_len {
return Err(ContentCipherError::BlockTooShort()); return Err(ContentCipherError::BlockTooShort());
} }
let nonce = &block[..self.iv_len]; let (nonce, other) = block.split_at_mut(self.iv_len);
let tag = &block[block.len() - self.iv_len..]; let (ciphertext, tag) = other.split_at_mut(other.len() - self.iv_len);
let ciphertext = &block[self.iv_len..block.len() - self.iv_len];
if nonce.iter().all(|f| *f == 0) { if nonce.iter().all(|f| *f == 0) {
return Err(ContentCipherError::AllZeroNonce()); return Err(ContentCipherError::AllZeroNonce());
} }
let mut buf = Vec::from(ciphertext);
let mut aad = Vec::from(block_number.to_be_bytes()); let mut aad = Vec::from(block_number.to_be_bytes());
if let Some(file_id) = file_id { if let Some(file_id) = file_id {
aad.extend(file_id); aad.extend(file_id);
} }
let aes = Aes256Gcm::new(&self.key); self.cipher.decrypt_in_place_detached(
aes.decrypt_in_place_detached(
GenericArray::from_slice(nonce), GenericArray::from_slice(nonce),
&aad, &aad,
&mut buf, ciphertext,
GenericArray::from_slice(tag), GenericArray::from_slice(tag),
)?; )?;
Ok(buf.to_vec()) Ok(ciphertext)
} }
/// Return the decrypted size of a file, based on the encrypted size. /// Return the decrypted size of a file, based on the encrypted size.
@ -93,7 +84,7 @@ impl ContentEnc {
mod test { mod test {
use std::io::{Cursor, Read}; use std::io::{Cursor, Read};
use sha2::{Sha256, Digest}; use sha2::{Digest, Sha256};
use super::ContentEnc; use super::ContentEnc;
@ -112,37 +103,41 @@ mod test {
#[test] #[test]
fn test_decrypt_empty_file() { fn test_decrypt_empty_file() {
let content = include_bytes!( let content = include_bytes!(concat!(
concat!(env!("CARGO_MANIFEST_DIR"), env!("CARGO_MANIFEST_DIR"),
"/../test-data/test.bin")); "/../test-data/test.bin"
));
let master_key = base64::decode("9gtUW9XiiefEgEXEkbONI6rnUsd2yh5UZZLG0V8Bxgk=").unwrap(); let master_key = base64::decode("9gtUW9XiiefEgEXEkbONI6rnUsd2yh5UZZLG0V8Bxgk=").unwrap();
let file_cipher = ContentEnc::new(&master_key, 16).unwrap(); let file_cipher = ContentEnc::new(&master_key, 16).unwrap();
let res = file_cipher.decrypt_block(&[], 1, None).expect("Failed to decrypt empty block"); let res = file_cipher
.decrypt_block(&mut [], 1, None)
.expect("Failed to decrypt empty block");
assert_eq!(res, Vec::<u8>::new()); assert_eq!(res, Vec::<u8>::new());
let mut reader= Cursor::new(content); let mut reader = Cursor::new(content);
let mut hasher = Sha256::new();
let mut hasher = Sha256::new();
let mut buf = [0u8; 18]; let mut buf = [0u8; 18];
let n = reader.read(&mut buf).unwrap(); let n = reader.read(&mut buf).unwrap();
let id = if n < 18 { None } else { Some(&buf[2..]) }; let id = if n < 18 { None } else { Some(&buf[2..]) };
let mut buf = [0u8; 4096 + 32]; let mut buf = [0u8; 4096 + 32];
let mut block_index = 0; let mut block_index = 0;
loop { loop {
let n = reader.read(&mut buf).unwrap(); let n = reader.read(&mut buf).unwrap();
let res = file_cipher.decrypt_block(&buf[..n], block_index, id).unwrap(); let res = file_cipher
.decrypt_block(&mut buf[..n], block_index, id)
.unwrap();
hasher.update(&res); hasher.update(&res);
if res.is_empty() { if res.is_empty() {
break; break;
} }
block_index += 1; block_index += 1;
} }

View File

@ -105,9 +105,9 @@ fn decrypt_file(c: &DecryptCommand) -> anyhow::Result<()> {
let mut block_index = 0; let mut block_index = 0;
loop { loop {
let n = file.read(&mut buf)?; let n = file.read(&mut buf)?;
let res = enc.decrypt_block(&buf[..n], block_index, id)?; let res = enc.decrypt_block(&mut buf[..n], block_index, id)?;
stdout.write_all(&res)?; stdout.write_all(res)?;
if res.is_empty() { if res.is_empty() {
break; break;