Skip to content

Commit

Permalink
use random generator for nonce generator
Browse files Browse the repository at this point in the history
  • Loading branch information
radumarias committed May 4, 2024
1 parent 3ac8f73 commit 8d3b89b
Show file tree
Hide file tree
Showing 10 changed files with 250 additions and 150 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ retainer = "0.3.0"
num-format = "0.4.4"
ring = "0.17.8"
hex = "0.4.3"
rand_chacha = "0.3.1"

[package.metadata.aur]
depends = ["fuse3"]
6 changes: 3 additions & 3 deletions examples/ring_crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,15 @@ fn main() {
let key = SecretVec::new(key);
let key_store = KeyStore::new(key);
println!("hash {:?}", key_store.hash);
let mut writer = crypto::create_crypto_writer(OpenOptions::new().read(true).write(true).create(true).open(path.clone()).unwrap(),
&cipher, &derived_key);
let mut writer = crypto::create_writer(OpenOptions::new().read(true).write(true).create(true).open(path.clone()).unwrap(),
&cipher, &derived_key, 42_u64);
bincode::serialize_into(&mut writer, &key_store).unwrap();
writer.flush().unwrap();
writer.finish().unwrap();

// read key

let reader = crypto::create_crypto_reader(File::open(path).unwrap(), &cipher, &derived_key);
let reader = crypto::create_reader(File::open(path).unwrap(), &cipher, &derived_key);
let key_store: KeyStore = bincode::deserialize_from(reader).map_err(|_| FsError::InvalidPassword).unwrap();
println!("key {:?}", key_store.key.expose_secret());
println!("hash {:?}", key_store.hash);
Expand Down
102 changes: 70 additions & 32 deletions src/crypto.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
use std::fs::OpenOptions;
use std::io::{Read, Write};
use std::io;
use std::num::ParseIntError;
use std::path::PathBuf;

use argon2::Argon2;
use argon2::password_hash::rand_core::RngCore;
use base64::DecodeError;
use hex::FromHexError;
use num_format::{Locale, ToFormattedString};
use rand::thread_rng;
use ring::aead::{AES_256_GCM, CHACHA20_POLY1305};
use secrecy::{ExposeSecret, SecretString, SecretVec};
use serde::{Deserialize, Serialize};
Expand All @@ -29,6 +33,8 @@ pub enum Cipher {
Aes256Gcm,
}

pub const ENCRYPT_FILENAME_OVERHEAD_CHARS: usize = 8;

#[derive(Debug, Error)]
pub enum Error {
// #[error("cryptostream error: {source}")]
Expand All @@ -43,37 +49,50 @@ pub enum Error {
source: io::Error,
// backtrace: Backtrace,
},
#[error("hex error: {source}")]
Hex {
#[error("from hex error: {source}")]
FromHexError {
#[from]
source: FromHexError,
// backtrace: Backtrace,
},
#[error("crypto error: {0}")]
Generic(String),
#[error("hex decode: {source}")]
DecodeError {
#[from]
source: DecodeError,
// backtrace: Backtrace,
},
#[error("parse int: {source}")]
ParseIntError {
#[from]
source: ParseIntError,
// backtrace: Backtrace,
},
#[error("generic error: {0}")]
Generic(&'static str),
#[error("generic error: {0}")]
GenericString(String),
}

pub type Result<T> = std::result::Result<T, Error>;

pub fn create_crypto_writer<W: Write + Send + Sync>(writer: W, cipher: &Cipher, key: &SecretVec<u8>) -> impl CryptoWriter<W> {
// create_cryptostream_crypto_writer(file, cipher, key)
create_ring_crypto_writer(writer, cipher, key)
pub fn create_writer<W: Write + Send + Sync>(writer: W, cipher: &Cipher, key: &SecretVec<u8>, nonce_seed: u64) -> impl CryptoWriter<W> {
create_ring_writer(writer, cipher, key, nonce_seed)
}

fn create_ring_crypto_writer<W: Write + Send + Sync>(writer: W, cipher: &Cipher, key: &SecretVec<u8>) -> RingCryptoWriter<W> {
fn create_ring_writer<W: Write + Send + Sync>(writer: W, cipher: &Cipher, key: &SecretVec<u8>, nonce_seed: u64) -> RingCryptoWriter<W> {
let algorithm = match cipher {
Cipher::ChaCha20 => &CHACHA20_POLY1305,
Cipher::Aes256Gcm => &AES_256_GCM,
};
RingCryptoWriter::new(writer, algorithm, &key.expose_secret())
RingCryptoWriter::new(writer, algorithm, &key.expose_secret(), nonce_seed)
}

fn create_ring_crypto_reader<R: Read + Send + Sync>(reader: R, cipher: &Cipher, key: &SecretVec<u8>) -> RingCryptoReader<R> {
fn create_ring_reader<R: Read + Send + Sync>(reader: R, cipher: &Cipher, key: &SecretVec<u8>, nonce_seed: u64) -> RingCryptoReader<R> {
let algorithm = match cipher {
Cipher::ChaCha20 => &CHACHA20_POLY1305,
Cipher::Aes256Gcm => &AES_256_GCM,
};
RingCryptoReader::new(reader, algorithm, &key.expose_secret())
RingCryptoReader::new(reader, algorithm, &key.expose_secret(), nonce_seed)
}

// fn _create_cryptostream_crypto_writer(mut file: File, cipher: &Cipher, key: &SecretVec<u8>) -> impl CryptoWriter<File> {
Expand All @@ -94,9 +113,8 @@ fn create_ring_crypto_reader<R: Read + Send + Sync>(reader: R, cipher: &Cipher,
// }

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

// fn _create_cryptostream_crypto_reader(mut file: File, cipher: &Cipher, key: &SecretVec<u8>) -> CryptostreamCryptoReader<File> {
Expand All @@ -122,28 +140,35 @@ pub fn create_crypto_reader<R: Read + Send + Sync>(reader: R, cipher: &Cipher, k
// CryptostreamCryptoReader::new(file, get_cipher(cipher), &key.expose_secret(), &iv).unwrap()
// }

pub fn encrypt_string(s: &SecretString, cipher: &Cipher, key: &SecretVec<u8>) -> Result<String> {
pub fn encrypt_string_with_nonce_seed(s: &SecretString, cipher: &Cipher, key: &SecretVec<u8>, nonce_seed: u64) -> Result<String> {
let mut cursor = io::Cursor::new(vec![]);

let mut writer = create_crypto_writer(cursor, cipher, key);
writer.write_all(s.expose_secret().as_bytes()).unwrap();
writer.flush().unwrap();
let mut writer = create_writer(cursor, cipher, key, nonce_seed);
writer.write_all(s.expose_secret().as_bytes())?;
writer.flush()?;
cursor = writer.finish()?.unwrap();
Ok(hex::encode(&cursor.into_inner()))
let v = cursor.into_inner();
Ok(format!("{}.{}", base64::encode(v), nonce_seed))
}

pub fn decrypt_string(s: &str, cipher: &Cipher, key: &SecretVec<u8>) -> Result<SecretString> {
let vec = hex::decode(s)?;
// extract nonce seed
if !s.contains(".") {
return Err(Error::Generic("nonce seed is missing"));
}
let nonce_seed = s.split('.').last().unwrap().parse::<u64>()?;
let s = s.split('.').next().unwrap();

let vec = base64::decode(s)?;
let cursor = io::Cursor::new(vec);

let mut reader = create_crypto_reader(cursor, cipher, key);
let mut reader = create_reader(cursor, cipher, key, nonce_seed);
let mut decrypted = String::new();
reader.read_to_string(&mut decrypted)?;
Ok(SecretString::new(decrypted))
}

pub fn decrypt_and_unnormalize_end_file_name(name: &str, cipher: &Cipher, key: &SecretVec<u8>) -> Result<SecretString> {
// let name = String::from(name).replace("|", "/");
pub fn decrypt_file_name(name: &str, cipher: &Cipher, key: &SecretVec<u8>) -> Result<SecretString> {
let name = String::from(name).replace("|", "/");
decrypt_string(&name, cipher, key)
}

Expand All @@ -156,18 +181,22 @@ pub fn derive_key(password: &SecretString, cipher: &Cipher, salt: SecretVec<u8>)
};
dk.resize(key_len, 0);
Argon2::default().hash_password_into(password.expose_secret().as_bytes(), salt.expose_secret(), &mut dk)
.map_err(|err| Error::Generic(err.to_string()))?;
.map_err(|err| Error::GenericString(err.to_string()))?;
Ok(SecretVec::new(dk))
}

pub fn encrypt_file_name(name: &SecretString, cipher: &Cipher, key: &SecretVec<u8>) -> FsResult<String> {
pub fn encrypt_file_name_with_nonce_seed(name: &SecretString, cipher: &Cipher, key: &SecretVec<u8>, nonce_seed: u64) -> FsResult<String> {
if name.expose_secret() != "$." && name.expose_secret() != "$.." {
let normalized_name = SecretString::new(name.expose_secret().replace("/", " ").replace("\\", " "));
let encrypted = encrypt_string(&normalized_name, cipher, key)?;
// encrypted = encrypted.replace("/", "|");
return Ok(encrypted);
let mut encrypted = encrypt_string_with_nonce_seed(&normalized_name, cipher, key, nonce_seed)?;
encrypted = encrypted.replace("/", "|");
Ok(encrypted)
} else {
// add nonce seed
let mut name = name.expose_secret().clone();
name.push_str(&nonce_seed.to_string());
Ok(name)
}
Ok(name.expose_secret().to_owned())
}

pub fn hash(data: &[u8]) -> [u8; 32] {
Expand All @@ -182,14 +211,14 @@ pub fn hash_secret(data: &SecretString) -> SecretVec<u8> {

/// Copy from `pos` position in file `len` bytes
#[instrument(skip(w, key), fields(pos = pos.to_formatted_string(& Locale::en), len = len.to_formatted_string(& Locale::en)))]
pub fn copy_from_file_exact(w: &mut impl Write, pos: u64, len: u64, cipher: &Cipher, key: &SecretVec<u8>, file: PathBuf) -> io::Result<()> {
pub fn copy_from_file_exact(w: &mut impl Write, pos: u64, len: u64, cipher: &Cipher, key: &SecretVec<u8>, nonce_seed: u64, file: PathBuf) -> io::Result<()> {
debug!("");
if len == 0 {
// no-op
return Ok(());
}
// create a new reader by reading from the beginning of the file
let mut reader = create_crypto_reader(OpenOptions::new().read(true).open(file)?, cipher, key);
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)?;

Expand All @@ -198,3 +227,12 @@ pub fn copy_from_file_exact(w: &mut impl Write, pos: u64, len: u64, cipher: &Cip
reader.finish();
Ok(())
}


pub fn extract_nonce_from_encrypted_string(name: &str) -> Result<u64> {
if !name.contains(".") {
return Err(Error::Generic("nonce seed is missing"));
}
let nonce_seed = name.split('.').last().unwrap().parse::<u64>()?;
Ok(nonce_seed)
}
14 changes: 10 additions & 4 deletions src/crypto/decryptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::io;
use std::io::{Read, Seek, SeekFrom};

use ring::aead::{Aad, Algorithm, BoundKey, OpeningKey, UnboundKey};
use tracing::{error, instrument};

use crate::crypto::buf_mut::BufMut;
use crate::crypto::encryptor::{BUF_SIZE, CounterNonceSequence};
Expand Down Expand Up @@ -51,9 +52,9 @@ pub struct RingCryptoReader<R: Read> {
}

impl<R: Read> RingCryptoReader<R> {
pub fn new<'a: 'static>(r: R, algorithm: &'a Algorithm, key: &[u8]) -> Self {
pub fn new<'a: 'static>(r: R, algorithm: &'a Algorithm, key: &[u8], nonce_seed: u64) -> Self {
let unbound_key = UnboundKey::new(algorithm, &key).unwrap();
let nonce_sequence = CounterNonceSequence(1);
let nonce_sequence = CounterNonceSequence::new(nonce_seed);
let opening_key = OpeningKey::new(unbound_key, nonce_sequence);
let buf = BufMut::new(vec![0; BUF_SIZE + algorithm.tag_len()]);
Self {
Expand All @@ -65,6 +66,7 @@ impl<R: Read> RingCryptoReader<R> {
}

impl<R: Read> 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)?;
Expand Down Expand Up @@ -94,7 +96,11 @@ impl<R: Read> Read for RingCryptoReader<R> {
return Ok(0);
}
let mut data = &mut buffer[..len];
let plaintext = self.opening_key.open_within(Aad::empty(), &mut data, 0..).unwrap();
let res = self.opening_key.open_within(Aad::empty(), &mut data, 0..);
if res.is_err() {
error!("error opening in place: {:?}", res);
}
let plaintext = res.unwrap();
plaintext.len()
};
self.buf.seek(SeekFrom::Start(pos as u64)).unwrap();
Expand All @@ -104,7 +110,7 @@ impl<R: Read> Read for RingCryptoReader<R> {
}

impl<R: Read + Send + Sync> Seek for RingCryptoReader<R> {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
fn seek(&mut self, _pos: SeekFrom) -> io::Result<u64> {
todo!()
}
}
Expand Down
23 changes: 17 additions & 6 deletions src/crypto/encryptor.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use std::io;
use std::io::{BufWriter, Write};
use rand_chacha::ChaCha8Rng;
use rand_chacha::rand_core::{RngCore, SeedableRng};

use ring::aead::{Aad, Algorithm, BoundKey, Nonce, NONCE_LEN, NonceSequence, SealingKey, UnboundKey};
use ring::error::Unspecified;
Expand Down Expand Up @@ -55,10 +57,10 @@ pub struct RingCryptoWriter<W: Write> {
}

impl<W: Write> RingCryptoWriter<W> {
pub fn new<'a: 'static>(w: W, algorithm: &'a Algorithm, key: &[u8]) -> Self {
pub fn new<'a: 'static>(w: W, algorithm: &'a Algorithm, key: &[u8], nonce_seed: u64) -> Self {
// todo: param for start nonce sequence
let unbound_key = UnboundKey::new(&algorithm, &key).unwrap();
let nonce_sequence = CounterNonceSequence(1);
let nonce_sequence = CounterNonceSequence::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 @@ -118,18 +120,27 @@ impl<W: Write + Send + Sync> CryptoWriter<W> for RingCryptoWriter<W> {
}
}

pub(crate) struct CounterNonceSequence(pub(crate) u32);
pub(crate) struct CounterNonceSequence{
rng: ChaCha8Rng,
}

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

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

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

self.0 += 1; // advance the counter
Nonce::try_assume_unique_for_key(&nonce_bytes)
}
}
Loading

0 comments on commit 8d3b89b

Please sign in to comment.