Avoid allocation in decrypt_block
This commit is contained in:
parent
fdcdc83648
commit
58ee7769d8
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue