XChaCha20Poly1305 should utilise a 24 byte nonce, but it requires a 19 byte nonce when used as AEAD stream. The same situation with Aes256Gcm which requires a 7 byte nonce instead of declared 12 bytes. I have very limited knowledge of cryptography. I want to figure out: do I missunderstand crypto nonce and AEAD stream or do I missuse Rust libs?
[dependencies]
aead = { version = "0.5.1", features = ["getrandom"] }
chacha20poly1305 = { version = "0.10.1", features = ["stream"] }
aes-gcm = "0.10.1"
use std::{ fs::File, io::{ Write, Read, Error as ErrorIo } };
use aead::{ stream, KeyInit, OsRng, Error as ErrorAead, rand_core::RngCore };
use chacha20poly1305::XChaCha20Poly1305;
// use aes_gcm::Aes256Gcm;
const BUFFER_LEN_ENC: usize = 500;
const BUFFER_LEN_DEC: usize = BUFFER_LEN_ENC + 16;
fn main() -> Result<(), Error> {
let mut key = [0u8; 32];
let mut nonce = [0u8; 24]; // no error with [0u8; 19]
OsRng.fill_bytes(&mut key);
OsRng.fill_bytes(&mut nonce);
let path_raw = "src.txt";
let path_enc = "enc";
let path_dec = "dec.txt";
encrypt(&key, &nonce, path_raw, path_enc)?;
decrypt(&key, &nonce, path_enc, path_dec)?;
Ok(())
}
fn encrypt(key: &[u8], nonce: &[u8], path_src: &str, path_dst: &str) -> Result<(), Error> {
let aead = XChaCha20Poly1305::new(key[..32].as_ref().into());
let mut stream_encryptor = stream::EncryptorBE32::from_aead(aead, nonce.as_ref().into());
let mut src = File::open(path_src).map_err(Error::make_io)?;
let mut dst = File::create(path_dst).map_err(Error::make_io)?;
let mut buffer = [0u8; BUFFER_LEN_ENC];
loop {
let read_count = src.read(&mut buffer).map_err(Error::make_io)?;
if read_count == BUFFER_LEN_ENC {
let ciphertext = stream_encryptor
.encrypt_next(buffer.as_slice())
.map_err(Error::make_aead)?;
dst.write(&ciphertext).map_err(Error::make_io)?;
} else {
let ciphertext = stream_encryptor
.encrypt_last(&buffer[..read_count])
.map_err(Error::make_aead)?;
dst.write(&ciphertext).map_err(Error::make_io)?;
break;
}
}
Ok(())
}
fn decrypt(key: &[u8], nonce: &[u8], path_src: &str, path_dst: &str) -> Result<(), Error> {
let aead = XChaCha20Poly1305::new(key[..32].as_ref().into());
let mut stream_decryptor = stream::DecryptorBE32::from_aead(aead, nonce.as_ref().into());
let mut src = File::open(path_src).map_err(Error::make_io)?;
let mut dst = File::create(path_dst).map_err(Error::make_io)?;
let mut buffer = [0u8; BUFFER_LEN_DEC];
loop {
let read_count = src.read(&mut buffer).map_err(Error::make_io)?;
if read_count == BUFFER_LEN_DEC {
let plaintext = stream_decryptor
.decrypt_next(buffer.as_slice())
.map_err(Error::make_aead)?;
dst.write(&plaintext).map_err(Error::make_io)?;
} else if read_count == 0 {
break;
} else {
let plaintext = stream_decryptor
.decrypt_last(&buffer[..read_count])
.map_err(Error::make_aead)?;
dst.write(&plaintext).map_err(Error::make_io)?;
break;
}
}
Ok(())
}
#[derive(Debug)]
enum Error {
Io(ErrorIo),
Aead(ErrorAead),
}
impl Error {
fn make_io(err: ErrorIo) -> Self { Self::Io(err) }
fn make_aead(err: ErrorAead) -> Self { Self::Aead(err) }
}
thread 'main' panicked at 'assertion failed: `(left == right)`
left: `24`,
right: `19`', /home/lex/.cargo/registry/src/github.com-1ecc6299db9ec823/generic-array-0.14.6/src/lib.rs:565:9
stack backtrace:
0: rust_begin_unwind
at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/std/src/panicking.rs:584:5
1: core::panicking::panic_fmt
at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/core/src/panicking.rs:142:14
2: core::panicking::assert_failed_inner
3: core::panicking::assert_failed
at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/core/src/panicking.rs:181:5
4: <&generic_array::GenericArray<T,N> as core::convert::From<&[T]>>::from
at /home/lex/.cargo/registry/src/github.com-1ecc6299db9ec823/generic-array-0.14.6/src/lib.rs:565:9
5: <T as core::convert::Into<U>>::into
at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/core/src/convert/mod.rs:550:9
6: crydec::encrypt
at ./src/main.rs:28:71
7: crydec::main
at ./src/main.rs:20:5
8: core::ops::function::FnOnce::call_once
at /rustc/a55dd71d5fb0ec5a6a3a9e8c27b2127ba491ce52/library/core/src/ops/function.rs:248:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
The last 5 bytes of the nonce are used for a 32 bits counter, and a 1-byte "last block" flag, so we only need to generate 19 (or in general nonce_size - 5
) bytes of random data.
https://docs.rs/aead/latest/aead/stream/struct.StreamBE32.html