I am using the chacha20poly1305
crate, which uses a GenericArray
v0.14.7 for its Key
.
I now need to convert that key from a Vec<u8>
. However GenericArray::from::<&[u8]>()
is implemented to panic upon conversion errors (and thus also will try_from()
). I would, however, like to convert from Vec<u8>
to Key
without panicking and handling errors gracefully.
Is there a way to do this without going the long way and converting into [u8; SIZE]
fist? If not, is it possible to get the key's size from Key
somehow without using a magic number in my code?
My current attempt is:
use clap::Parser;
use clap_stdin::FileOrStdin;
use ios_log_decrypt::EncryptedLog;
use log::error;
use rpassword::prompt_password;
use std::io::{stdout, Write};
use std::process::exit;
const KEY_SIZE: usize = 32;
#[derive(Debug, Parser)]
struct Args {
#[arg(index = 1, help = "path to the encrypted log file")]
logfile: FileOrStdin,
#[arg(long, short, help = "hexadecimal decryption key")]
key: Option<String>,
}
fn main() {
env_logger::init();
let args = Args::parse();
let encrypted_log = EncryptedLog::new(args.logfile.to_string());
let key: [u8; KEY_SIZE] = hex::decode(args.key.unwrap_or_else(|| {
prompt_password("Decryption key: ").unwrap_or_else(|error| {
error!("{error}");
exit(1)
})
}))
.unwrap_or_else(|error| {
error!("{error}");
exit(2);
})
.try_into()
.unwrap_or_else(|vec: Vec<u8>| {
error!("Invalid key size: {}", vec.len());
exit(3);
});
for block in encrypted_log.decrypt(&key.into()) {
match block {
Ok(ref bytes) => stdout().write_all(bytes).expect("could not write bytes"),
Err(error) => error!("{error}"),
}
}
}
Which is pretty ugly, since I need to hard-code the key size of 32
in my code and thus it is not directly tied to the size of Key
.
While the size of Key
is unlikely to change in the future, possible discrepancies between Key
's size and KEY_SIZE
will lead the program to panic again.
You can create a conversion function:
use generic_array::{ArrayLength, GenericArray};
fn generic_array_try_from_slice<T, N>(data: &[T]) -> Option<&GenericArray<T, N>>
where
N: ArrayLength<T>,
{
if data.len() == N::to_usize() {
Some(data.into())
} else {
None
}
}