Skip to content

Commit

Permalink
add Seek to crypto reader
Browse files Browse the repository at this point in the history
  • Loading branch information
radumarias committed May 4, 2024
1 parent 95db2e7 commit d6ae144
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 58 deletions.
13 changes: 6 additions & 7 deletions src/crypto.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
use std::fs::OpenOptions;
use std::io::{Read, Write};
use std::io::{Read, Seek, Write};
use std::io;
use std::num::ParseIntError;
use std::path::PathBuf;
use std::sync::Arc;

use argon2::Argon2;
use argon2::password_hash::rand_core::RngCore;

use base64::DecodeError;
use hex::FromHexError;
use num_format::{Locale, ToFormattedString};
Expand Down Expand Up @@ -34,7 +33,7 @@ pub enum Cipher {
Aes256Gcm,
}

pub const ENCRYPT_FILENAME_OVERHEAD_CHARS: usize = 8;
pub const ENCRYPT_FILENAME_OVERHEAD_CHARS: usize = 22;

#[derive(Debug, Error)]
pub enum Error {
Expand Down Expand Up @@ -88,7 +87,7 @@ fn create_ring_writer<W: Write + Send + Sync>(writer: W, cipher: &Cipher, key: A
RingCryptoWriter::new(writer, algorithm, key, nonce_seed)
}

fn create_ring_reader<R: Read + Send + Sync>(reader: R, cipher: &Cipher, key: Arc<SecretVec<u8>>, nonce_seed: u64) -> RingCryptoReader<R> {
fn create_ring_reader<R: Read + Seek + Send + Sync>(reader: R, cipher: &Cipher, key: Arc<SecretVec<u8>>, nonce_seed: u64) -> RingCryptoReader<R> {
let algorithm = match cipher {
Cipher::ChaCha20 => &CHACHA20_POLY1305,
Cipher::Aes256Gcm => &AES_256_GCM,
Expand All @@ -114,7 +113,7 @@ fn create_ring_reader<R: Read + Send + Sync>(reader: R, cipher: &Cipher, key: Ar
// }

#[instrument(skip(reader, key))]
pub fn create_reader<R: Read + Send + Sync>(reader: R, cipher: &Cipher, key: Arc<SecretVec<u8>>, nonce_seed: u64) -> impl CryptoReader<R> {
pub fn create_reader<R: Read + Seek + Send + Sync>(reader: R, cipher: &Cipher, key: Arc<SecretVec<u8>>, nonce_seed: u64) -> impl CryptoReader<R> {
create_ring_reader(reader, cipher, key, nonce_seed)
}

Expand Down Expand Up @@ -221,7 +220,7 @@ pub fn copy_from_file_exact(w: &mut impl Write, pos: u64, len: u64, cipher: &Cip
// create a new reader by reading from the beginning of the file
let mut reader = create_reader(OpenOptions::new().read(true).open(file)?, cipher, key, nonce_seed);
// move read position to the write position
stream_util::read_seek_forward_exact(&mut reader, pos)?;
stream_util::seek_forward(&mut reader, pos)?;

// copy the rest of the file
stream_util::copy_exact(&mut reader, w, len)?;
Expand Down
53 changes: 27 additions & 26 deletions src/crypto/reader.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
use std::{fs, io};
use std::io;
use std::io::{Read, Seek, SeekFrom};
use std::sync::Arc;
use num_format::{Locale, ToFormattedString};

use num_format::{Locale, ToFormattedString};
use ring::aead::{Aad, Algorithm, BoundKey, OpeningKey, UnboundKey};
use secrecy::{ExposeSecret, SecretVec};
use tracing::{debug, error, instrument};
use tracing::{debug, error, info, instrument, warn};

use crate::crypto::buf_mut::BufMut;
use crate::crypto::writer::{BUF_SIZE, CounterNonceSequence};
use crate::encryptedfs::CONTENTS_DIR;
use crate::crypto::writer::{BUF_SIZE, RandomNonceSequence};
use crate::stream_util;

pub trait CryptoReader<R: Read>: Read + Seek + Send + Sync {
pub trait CryptoReader<R: Read + Seek>: Read + Seek + Send + Sync {
fn finish(&mut self) -> Option<R>;
}

Expand Down Expand Up @@ -50,23 +49,23 @@ pub trait CryptoReader<R: Read>: Read + Seek + Send + Sync {

/// ring
pub struct RingCryptoReader<R: Read> {
pub struct RingCryptoReader<R: Read + Seek> {
input: Option<R>,
opening_key: Option<OpeningKey<CounterNonceSequence>>,
opening_key: OpeningKey<RandomNonceSequence>,
buf: BufMut,
pos: u64,
algorithm: &'static Algorithm,
key: Arc<SecretVec<u8>>,
nonce_seed: u64,
}

impl<R: Read> RingCryptoReader<R> {
impl<R: Read + Seek> RingCryptoReader<R> {
pub fn new(r: R, algorithm: &'static Algorithm, key: Arc<SecretVec<u8>>, nonce_seed: u64) -> Self {
let opening_key = Self::create_opening_key(algorithm, key.clone(), nonce_seed);
let buf = BufMut::new(vec![0; BUF_SIZE + algorithm.tag_len()]);
Self {
input: Some(r),
opening_key: Some(opening_key),
opening_key,
buf,
pos: 0,
algorithm,
Expand All @@ -75,9 +74,9 @@ impl<R: Read> RingCryptoReader<R> {
}
}

fn create_opening_key(algorithm: &'static Algorithm, key: Arc<SecretVec<u8>>, nonce_seed: u64) -> OpeningKey<CounterNonceSequence> {
fn create_opening_key(algorithm: &'static Algorithm, key: Arc<SecretVec<u8>>, nonce_seed: u64) -> OpeningKey<RandomNonceSequence> {
let unbound_key = UnboundKey::new(algorithm, key.expose_secret()).unwrap();
let nonce_sequence = CounterNonceSequence::new(nonce_seed);
let nonce_sequence = RandomNonceSequence::new(nonce_seed);
OpeningKey::new(unbound_key, nonce_sequence)
}

Expand All @@ -89,27 +88,30 @@ impl<R: Read> RingCryptoReader<R> {
// if we need an offset before the current position, we can't seek back, we need
// to read from the beginning until the desired offset
debug!("seeking back, recreating decryptor");
self.opening_key.replace(Self::create_opening_key(self.algorithm, self.key.clone(), self.nonce_seed));
self.opening_key = Self::create_opening_key(self.algorithm, self.key.clone(), self.nonce_seed);
self.buf.clear();
self.pos = 0;
self.input.as_mut().unwrap().seek(SeekFrom::Start(0))?;
}
debug!(pos = self.pos.to_formatted_string(&Locale::en), offset = offset.to_formatted_string(&Locale::en), "seeking");
let len = offset - self.pos;
stream_util::read_seek_forward_exact(self, len)?;
self.pos += len;
stream_util::seek_forward(self, len)?;
}

Ok(self.pos)
}
}

impl<R: Read> Read for RingCryptoReader<R> {
impl<R: Read + Seek> Read for RingCryptoReader<R> {
#[instrument(name = "RingCryptoReader:read", skip(self, buf))]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
// first try to read remaining decrypted data
let len = self.buf.read(buf)?;
if len != 0 {
return Ok(len);
{
let len = self.buf.read(buf)?;
if len != 0 {
self.pos += len as u64;
return Ok(len);
}
}
// we read all the data from the buffer, so we need to read a new block and decrypt it
let pos = {
Expand All @@ -134,11 +136,10 @@ impl<R: Read> Read for RingCryptoReader<R> {
return Ok(0);
}
let mut data = &mut buffer[..len];
let res = self.opening_key.as_mut().unwrap().open_within(Aad::empty(), &mut data, 0..);
if res.is_err() {
error!("error opening in place: {:?}", res);
}
let plaintext = res.unwrap();
let plaintext = self.opening_key.open_within(Aad::empty(), &mut data, 0..).map_err(|err| {
error!("error opening within: {}", err);
io::Error::new(io::ErrorKind::Other, "error opening within")
})?;
plaintext.len()
};
self.buf.seek(SeekFrom::Start(pos as u64)).unwrap();
Expand All @@ -148,7 +149,7 @@ impl<R: Read> Read for RingCryptoReader<R> {
}
}

impl<R: Read + Send + Sync> Seek for RingCryptoReader<R> {
impl<R: Read + Seek + Send + Sync> Seek for RingCryptoReader<R> {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
match pos {
SeekFrom::Start(pos) => self.seek_from_start(pos),
Expand All @@ -164,7 +165,7 @@ impl<R: Read + Send + Sync> Seek for RingCryptoReader<R> {
}
}

impl<R: Read + Send + Sync> CryptoReader<R> for RingCryptoReader<R> {
impl<R: Read + Seek + Send + Sync> CryptoReader<R> for RingCryptoReader<R> {
fn finish(&mut self) -> Option<R> {
Some(self.input.take().unwrap())
}
Expand Down
26 changes: 13 additions & 13 deletions src/crypto/writer.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::io;
use std::io::{BufWriter, Write};
use std::sync::Arc;
use rand_chacha::ChaCha8Rng;
use rand_chacha::ChaCha20Rng;
use rand_chacha::rand_core::{RngCore, SeedableRng};

use ring::aead::{Aad, Algorithm, BoundKey, Nonce, NONCE_LEN, NonceSequence, SealingKey, UnboundKey};
Expand Down Expand Up @@ -54,15 +54,15 @@ pub(crate) const BUF_SIZE: usize = 1024 * 1024; // 1 MB buffer

pub struct RingCryptoWriter<W: Write> {
out: Option<BufWriter<W>>,
sealing_key: SealingKey<CounterNonceSequence>,
sealing_key: SealingKey<RandomNonceSequence>,
buf: BufMut,
}

impl<W: Write> RingCryptoWriter<W> {
pub fn new<'a: 'static>(w: W, algorithm: &'a Algorithm, key: Arc<SecretVec<u8>>, nonce_seed: u64) -> Self {
// todo: param for start nonce sequence
let unbound_key = UnboundKey::new(&algorithm, key.expose_secret()).unwrap();
let nonce_sequence = CounterNonceSequence::new(nonce_seed);
let nonce_sequence = RandomNonceSequence::new(nonce_seed);
let sealing_key = SealingKey::new(unbound_key, nonce_sequence);
let buf = BufMut::new(vec![0; BUF_SIZE]);
Self {
Expand Down Expand Up @@ -122,30 +122,30 @@ impl<W: Write + Send + Sync> CryptoWriter<W> for RingCryptoWriter<W> {
}
}

pub(crate) struct CounterNonceSequence {
// rng: ChaCha8Rng,
seed: u64,
pub(crate) struct RandomNonceSequence {
rng: ChaCha20Rng,
// seed: u64,
}

impl CounterNonceSequence {
impl RandomNonceSequence {
pub fn new(seed: u64) -> Self {
Self {
// rng: ChaCha8Rng::seed_from_u64(seed),
seed: 1,
rng: ChaCha20Rng::seed_from_u64(seed),
// seed: 1,
}
}
}

impl NonceSequence for CounterNonceSequence {
impl NonceSequence for RandomNonceSequence {
// called once for each seal operation
fn advance(&mut self) -> Result<Nonce, Unspecified> {
let mut nonce_bytes = vec![0; NONCE_LEN];

// let bytes = self.rng.next_u64().to_le_bytes();
let bytes = self.seed.to_le_bytes();
let bytes = self.rng.next_u64().to_le_bytes();
// let bytes = self.seed.to_le_bytes();
nonce_bytes[4..].copy_from_slice(&bytes);
// println!("nonce_bytes = {}", hex::encode(&nonce_bytes));
self.seed += 1;
// self.seed += 1;

Nonce::try_assume_unique_for_key(&nonce_bytes)
}
Expand Down
11 changes: 5 additions & 6 deletions src/encryptedfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ use argon2::password_hash::rand_core::RngCore;
use futures_util::TryStreamExt;
use num_format::{Locale, ToFormattedString};
use rand::thread_rng;
use rand_chacha::ChaCha20Rng;
use rand_chacha::rand_core::SeedableRng;
use ring::aead::{AES_256_GCM, CHACHA20_POLY1305};
use secrecy::{ExposeSecret, SecretString, SecretVec};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use thiserror::Error;
use tokio::sync::{Mutex, MutexGuard, RwLock};
use tokio_stream::wrappers::ReadDirStream;
use tracing::{debug, error, instrument, warn};
use tracing::{debug, error, info, instrument, warn};

use crate::{crypto, stream_util};
use crate::arc_hashmap::{ArcHashMap, Guard};
Expand Down Expand Up @@ -1036,10 +1038,6 @@ impl EncryptedFs {
error!(err = %err, pos = ctx.pos.to_formatted_string(&Locale::en), offset = offset.to_formatted_string(&Locale::en), file_size = ctx.attr.size.to_formatted_string(&Locale::en), "seeking");
err
})?;

if offset + buf.len() as u64 > ctx.attr.size {
buf = &mut buf[..(ctx.attr.size - offset) as usize];
}
let len = ctx.reader.as_mut().unwrap().read(&mut buf).map_err(|err| {
error!(err = %err, pos = ctx.pos.to_formatted_string(&Locale::en), offset = offset.to_formatted_string(&Locale::en), file_size = ctx.attr.size.to_formatted_string(&Locale::en), "reading from reader");
err
Expand Down Expand Up @@ -1855,7 +1853,8 @@ impl EncryptedFs {
Cipher::Aes256Gcm => AES_256_GCM.key_len(),
};
key.resize(key_len, 0);
thread_rng().fill_bytes(&mut key);
let mut rand = ChaCha20Rng::from_entropy();
rand.fill_bytes(&mut key);
let key = SecretVec::new(key);
let key_store = KeyStore::new(key);
let mut writer = crypto::create_writer(OpenOptions::new().read(true).write(true).create(true).open(path)?,
Expand Down
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ async fn main() -> Result<()> {
let matches = get_cli_args();

let log_level = if is_debug() {
Level::DEBUG
Level::INFO
} else {
let log_level_str = matches.get_one::<String>("log-level").unwrap().as_str();
let log_level = Level::from_str(log_level_str);
Expand Down
12 changes: 7 additions & 5 deletions src/stream_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::cmp::min;
use std::io;
use std::io::{Read, Write};
use num_format::{Locale, ToFormattedString};
use tracing::{debug, error, instrument};
use tracing::{debug, error, info, instrument, warn};
use crate::encryptedfs::EncryptedFs;

#[cfg(test)]
Expand All @@ -12,7 +12,7 @@ const BUF_SIZE: usize = 256 * 1024;
const BUF_SIZE: usize = 1024 * 1024; // 1 MB buffer

#[instrument(skip(r, len), fields(len = len.to_formatted_string( & Locale::en)))]
pub fn read_seek_forward_exact(r: &mut impl Read, len: u64) -> io::Result<()> {
pub fn seek_forward(r: &mut impl Read, len: u64) -> io::Result<()> {
debug!("");
if len == 0 {
return Ok(());
Expand All @@ -26,15 +26,17 @@ pub fn read_seek_forward_exact(r: &mut impl Read, len: u64) -> io::Result<()> {
} else {
buffer.len()
};
debug!(pos, read_len = read_len.to_formatted_string(&Locale::en), "reading");
// debug!(pos = pos.to_formatted_string(&Locale::en), read_len = read_len.to_formatted_string(&Locale::en), "reading");
if read_len > 0 {
r.read_exact(&mut buffer[..read_len]).map_err(|err| {
let read = r.read(&mut buffer[..read_len]).map_err(|err| {
error!("error reading from file pos {} len {}", pos.to_formatted_string(&Locale::en), read_len.to_formatted_string(&Locale::en));
err
})?;
pos += read_len as u64;
pos += read as u64;
if pos == len {
break;
} else if read == 0 {
Err(io::Error::new(io::ErrorKind::UnexpectedEof, "unexpected eof"))?;
}
} else {
break;
Expand Down

0 comments on commit d6ae144

Please sign in to comment.