You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

539 lines
16 KiB

//! Keyiles format
use super::*;
use crypto::sha256::Sha256Hash;
use futures::{
future::FutureExt,
};
use std::{
fmt,
error,
convert::{TryFrom, TryInto,},
};
use crypto::{
password::{
Salt,
Password,
SALTSIZE
},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord,PartialOrd, Hash, Serialize, Deserialize)]
#[repr(u8)]
pub enum KeyHeaderKind
{
Aes,
RsaPrivate,
RsaPublic,
}
#[derive(Debug)]
pub struct ParsingError;
impl error::Error for ParsingError{}
impl fmt::Display for ParsingError
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "failed to parse key header kind from byte")
}
}
impl TryFrom<u8> for KeyHeaderKind
{
type Error = ParsingError;
fn try_from(from: u8) -> Result<Self, Self::Error>
{
macro_rules! branches {
($($num:path),*) => {
match from {
$(
x if x == $num as u8 => $num,
)*
_ => return Err(ParsingError),
}
}
}
Ok(branches! {
Self::Aes,
Self::RsaPrivate,
Self::RsaPublic
})
}
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
pub struct KeyHeader
{
kind: KeyHeaderKind,
info: config::op::KeyDescription,
hash: Sha256Hash,
timestamp: i64,
}
impl Header for KeyHeader
{
const CHECK: u16 = CHECK_KEY;
const NAME: &'static str = "KEY";
fn hash(&self) -> Sha256Hash
{
let mut output = Vec::new();
self.write_bytes(&mut output, |_| None).now_or_never().unwrap().expect("Failed to write bytes to in-memory buffer");
crypto::sha256::compute_slice(output)
}
}
#[instrument]
fn decode_salt(p: &str) -> Result<Salt, eyre::Report>
{
trace!("Decoding salt");
let mut salt = Salt::none();
salt.repl_with_hex(p).wrap_err_with(|| eyre::eyre!("Failed to construct password salt from bytes"))?;
Ok(salt)
}
#[instrument]
#[inline] fn encode_salt_to_string(salt: &Salt) -> String
{
salt.to_hex_string()
}
#[instrument]
fn encode_salt(salt: &Salt) -> Result<[u8; SALTSIZE*2], eyre::Report>
{
let mut output = [0u8; SALTSIZE*2];
hex::encode_to_slice(salt.as_ref(), &mut output[..])
.wrap_err_with(|| eyre::eyre!("Failed to encode salt to {} hex char bytes", SALTSIZE *2))
.with_section(|| salt.to_hex_string().header("Salt was"))?;
Ok(output)
}
impl KeyHeader
{
/// Create a new key header from these values
pub const fn new(kind: KeyHeaderKind, info: config::op::KeyDescription, hash: Sha256Hash, timestamp: i64) -> Self
{
Self {
kind,
info,
hash,
timestamp,
}
}
/// Create a new key header from these values with the current timestamp
pub fn new_now(kind: KeyHeaderKind, info: config::op::KeyDescription, hash: Sha256Hash) -> Self
{
Self {
timestamp: timestamp::now(),
..Self::new(kind,info,hash,0)
}
}
/// Write this superheader as text bytes to this stream
#[instrument(err, skip(out, passwd))]
pub async fn write_text<T: AsyncWrite+Unpin+?Sized, F: FnOnce(&SaltGen) -> Option<Password>>(&self, out: &mut T, passwd: F) -> Result<usize, eyre::Report>
{
let salt = SaltGen::new();
let (mut written, passwd)= match passwd(&salt) {
Some(passwd) => {
let mut salt = salt.into_inner().ok_or_else(|| eyre::eyre!("Password function returned a password not bound to the provided salt generator"))
.with_warning(|| "This is a bug, and should never happen")
.with_section(|| passwd.to_string().header("Unbound returned password was"))?;
salt.as_mut()[0] |= 1u8; // make sure no conflict with NOPASS
let salt = encode_salt_to_string(&salt);
out.write_all(salt.as_bytes()).await?;
out.write_u8(b'\n').await?;
debug_assert_eq!(salt.len(), TEXT_NOPASS.len());
(salt.len() + 1, Some(passwd))
},
None => {
out.write_all(TEXT_NOPASS).await?;
out.write_u8(b'\n').await?;
(TEXT_NOPASS.len() + 1, None)
},
};
let text = if let Some(passwd) = passwd {
let aes = passwd.create_aes();
serialise::into_text_with_key_async(self, &aes).await
} else {
serialise::into_text(self)
}.wrap_err_with(|| eyre::eyre!("Failed to serialise header to text"))
.with_section(|| format!("{:?}", self).header("Header was"))?;
for bytes in text.as_bytes().chunks(16) {
out.write_all(bytes).await?;
out.write_u8(b'\n').await?;
written += bytes.len() + 1;
}
out.write_all(b"---").await?;
Ok(written + 4)
}
/// Read a superheader as text bytes from this stream
#[instrument(err, skip(input, passwd))]
pub async fn read_text<T: AsyncBufRead+Unpin+?Sized, F: FnOnce(&Salt) -> Option<Password>>(input: &mut T, passwd: F) -> Result<Self, eyre::Report>
{
let (mut tx, mut rx) = mpsc::channel(1);
#[derive(Debug)]
enum SendError
{
SendError,
IO(std::io::Error),
}
impl std::error::Error for SendError
{
fn source(&self) -> Option<&(dyn std::error::Error + 'static)>
{
match &self {
Self::IO(io) => Some(io),
_ => None,
}
}
}
impl fmt::Display for SendError
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "line reading failed: ")?;
match self {
Self::SendError => write!(f, "channel closed"),
Self::IO(_) => write!(f, "io error"),
}
}
}
impl From<std::io::Error> for SendError
{
#[inline] fn from(from: std::io::Error) -> Self
{
Self::IO(from)
}
}
impl<T> From<tokio::sync::mpsc::error::SendError<T>> for SendError
{
#[inline] fn from(_: tokio::sync::mpsc::error::SendError<T>) -> Self
{
Self::SendError
}
}
let line_sender = async move { //this is actually kinda overkill for this...
let mut buffer = String::new();
while input.read_line(&mut buffer).await? != 0 {
tx.send(buffer.clone()).await?;
buffer.clear();
}
Ok::<(), SendError>(())
};
let line_reader = async move {
macro_rules! take_one {
($msg:literal $($tt:tt)*) => {
loop {
if let Some(mut line) = rx.recv().await {
if line.trim().len() == 0 {
continue;
}
if {
let bytes = line.as_bytes();
if bytes.len() > 0 && bytes[bytes.len()-1] == b'\n' {
true
} else {
false
}
} {
line.truncate(line.len()-1);
}
break line;
} else {
return Err(eyre::eyre!(format!($msg $($tt)*))).wrap_err(eyre::eyre!("Failed to deserialise string"));
}
}
}
}
trace!("Reading password");
let password = {
let pass_part = take_one!("Failed to read password part");
trace!("Read password {}", pass_part);
if pass_part.as_bytes() == TEXT_NOPASS {
None
} else {
Some(decode_salt(&pass_part[..])
.with_section(move || pass_part.header("Password salt string part was"))?)
}
};
trace!("Decoded hex");
let mut enc = String::new();
let mut had_delim =false;
while let Some(line) = rx.recv().await {
let line = line.trim();
if line.len() == 0 {
continue;
}
if line == "---" {
had_delim=true;
break;
} else if line.len()>0 {
enc.push_str(line)
}
}
if !had_delim {
warn!("Buffer contained no end-of-entry delimiter");
}
match password {
Some(salt) => {
match passwd(&salt) {
Some(passwd) => {
let aes = passwd.create_aes();
Ok::<Self, eyre::Report>(serialise::from_text_with_key_async(&enc[..], &aes).await
.wrap_err_with(|| eyre::eyre!("Failed to deserialise encrypted string"))
.with_section(|| aes.to_string().header("Derived key was"))
.with_section(|| salt.to_hex_string().header("Embedded salt was"))
.with_section(|| enc.header("Read string was"))
.with_note(|| "Did you enter the wrong password?")?)
},
None => return Err(eyre::eyre!("Container is password encrypted but no password was provided"))
.with_section(|| enc.header("Read (encrypted) string was"))
.with_section(|| salt.to_hex_string().header("Embedded salt was"))
.with_note(|| "This key must be loaded with `-p` or `-P`"),
}
}
None => {
Ok::<Self, eyre::Report>(serialise::from_text(&enc[..])
.wrap_err_with(|| eyre::eyre!("Failed to deserialise string"))
.with_section(|| enc.header("Read string was"))?)
}
}
};
tokio::pin!(line_sender);
tokio::pin!(line_reader);
let (sres, rres) = tokio::join!(line_sender, line_reader);
match sres {
Err(x @ SendError::IO(_)) => Err(x).with_note(|| "In line reader"),
Err(s @ SendError::SendError) => {
rres
.with_error(move || s)?;
warn!("Unreachable code entered");
Err(SendError::SendError)
.with_note(|| "In line reader")
.with_warning(|| "`sres` failed with `SendError` but `rres` completed successfully. This should not happen")
},
_ => Ok(rres?),
}
}
/// Write this key header as bytes to this stream
#[instrument(err, skip(out, passwd))]
pub async fn write_bytes<T: AsyncWrite+Unpin+?Sized, F: FnOnce(&SaltGen) -> Option<Password>>(&self, out: &mut T, passwd: F) -> Result<usize, eyre::Report>
{
out.write_u8(self.kind as u8).await?;
let desc = {
let buf = serde_cbor::to_vec(&self.info)
.wrap_err_with(|| eyre::eyre!("Failed to serialise key info into bytes"))
.with_section(|| format!("{:?}", self.info).header("Key info was"))?;
out.write_u64(buf.len().try_into()
.wrap_err_with(|| eyre::eyre!("Failed to represent key info size as 64 bit integer. Something is very wrong"))
.with_section(|| buf.len().to_string().header("Buffer size was"))?).await?;
out.write_all(&buf[..]).await?;
buf.len()
};
out.write_all(self.hash.as_ref()).await?;
out.write_i64(self.timestamp).await?;
Ok(1 + std::mem::size_of::<Sha256Hash>() + 8 + desc)
}
/// Read a key header as bytes from this stream
#[instrument(err, skip(input, passwd))]
pub async fn read_bytes<T: AsyncRead+Unpin+?Sized, F: FnOnce(&Salt) -> Option<Password>>(input: &mut T, passwd: F) -> Result<Self, eyre::Report>
{
let kind = {
let byte = input.read_u8().await?;
byte.try_into().
with_section(|| [byte].to_hex_string().header("Invalid byte was"))?
};
let info = {
let read = input.read_u64().await?;
let read = usize::try_from(read)
.wrap_err_with(|| eyre::eyre!("Read buffer size does not fit into usize (u64 -> u{}).", std::mem::size_of::<usize>()*8))
.with_section(|| read.to_string().header("Read size was"))
.with_note(|| "This is a likely indication of file corruption")?;
let mut buffer = vec![0u8; read];
input.read_exact(&mut buffer[..]).await?;
serde_cbor::from_reader(&buffer[..])
.wrap_err_with(|| eyre::eyre!("Failed to deserialise key info from byte buffer"))
.with_section(|| read.to_string().header("Byte buffer's valid read size was"))
.with_section(|| format!("{}", buffer.fmt_view()).header("Byte buffer was"))?
};
let hash = {
let mut hash = Sha256Hash::empty();
input.read_exact(hash.as_mut()).await?;
hash
};
let timestamp = input.read_i64().await?;
Ok(Self {
kind,
info,
hash,
timestamp
})
}
}
impl serialise::BinarySerialisable for KeyHeader
{
#[inline(always)] fn serialise_bytes<'a, 'b: 'a, T: AsyncWrite+Unpin+?Sized,F: for<'r> FnOnce(&'r SaltGen) -> Option<Password>+ 'b>(&'a self, out: &'b mut T, passwd: F) -> LocalBoxFuture<'a, Result<usize, eyre::Report>>
{
self.write_bytes(out, passwd).boxed_local()
}
#[inline(always)] fn deserialise_bytes<'b , T: AsyncRead+Unpin+?Sized, F: for<'r> FnOnce(&'r Salt) -> Option<Password>+ 'b>(input: &'b mut T, passwd: F) -> LocalBoxFuture<'b, Result<Self, eyre::Report>>
where Self: 'b
{
Self::read_bytes(input, passwd).boxed_local()
}
}
impl serialise::TextSerialiseable for KeyHeader
{
#[inline(always)] fn serialise_text<'a, 'b: 'a, T: AsyncWrite+Unpin+?Sized, F: for<'r> FnOnce(&'r SaltGen) -> Option<Password> + 'b>(&'a self, out: &'b mut T, passwd: F) -> LocalBoxFuture<'a, Result<usize, eyre::Report>>
{
self.write_text(out, passwd).boxed_local()
}
#[inline(always)] fn deserialise_text<'a, T: AsyncBufRead+Unpin+?Sized, F: for<'r> FnOnce(&'r Salt) -> Option<Password> + 'a>(input: &'a mut T, passwd: F) -> LocalBoxFuture<'a, Result<Self, eyre::Report>>
where Self: 'a
{
Self::read_text(input, passwd).boxed_local()
}
}
#[cfg(test)]
mod tests
{
use super::*;
#[test]
fn now_or_never()
{
color_eyre::install().unwrap();
let mut vec = Vec::new();
use tokio::{
prelude::*,
io::AsyncWrite,
};
use futures::future::FutureExt;
async {
vec.write_all(&[0u8,1,2,3]).await.unwrap();
vec.write_all(&[4u8,5,6,7]).await.unwrap();
}.now_or_never().unwrap();
assert_eq!(&vec[..], &[0,1,2,3,4,5,6,7]);
}
#[tokio::test]
async fn serde_with_super() -> Result<(), eyre::Report>
{
//color_eyre::install()?;
let header = KeyHeader::new_now(KeyHeaderKind::Aes, Default::default(), Default::default());
let mut ser = Vec::new();
const PASSWORD: &str = "hello world";
fn derive(salt: &Salt) -> Option<Password>
{
Some(Password::derive(PASSWORD, salt))
}
let superheader = SuperHeader::<KeyHeader>::new_for(&header);
println!("Writing: {:?} + {:?}", superheader, header);
let written = superheader.serialise_bytes(&mut ser, |s| derive(s.get())).await? +
header.write_bytes(&mut ser, |s| derive(s.get())).await?;
println!("Wrote {} bytes", written);
println!("{}\n", ser.fmt_view());
let mut read = &ser[..];
let reads = SuperHeader::deserialise_bytes(&mut read, |s| derive(s)).await?;
println!("Read super: {:?}", reads);
let readheader = KeyHeader::deserialise_bytes(&mut read, |s| derive(s)).await?;
println!("Read real: {:?}", readheader);
reads.verify_for(&header)?;
reads.verify_for(&readheader)?;
assert_eq!(readheader, header);
assert_eq!(reads, superheader);
Ok(())
}
#[tokio::test]
async fn serde() -> Result<(), eyre::Report>
{
let header = KeyHeader::new_now(KeyHeaderKind::Aes, Default::default(), Default::default());
let mut ser = Vec::new();
println!("Writing {:?}", header);
let val = header.write_bytes(&mut ser, |_| None).await?;
println!("Wrote {} bytes:", val);
println!("{}\n", ser.fmt_view());
let reader = KeyHeader::read_bytes(&mut &ser[..], |_| None).await?;
println!("Read: {:?}", reader);
assert_eq!(reader, header);
Ok(())
}
#[tokio::test]
async fn serde_text() -> Result<(), eyre::Report>
{
let header = KeyHeader::new_now(KeyHeaderKind::Aes, Default::default(), Default::default());
let mut ser = Vec::new();
println!("Writing {:?}", header);
let val = header.write_text(&mut ser, |_| None).await?;
println!("Wrote {} bytes:", val);
println!("{}\n", ser.fmt_view());
let reader = KeyHeader::read_text(&mut &ser[..], |_| None).await?;
println!("Read: {:?}", reader);
assert_eq!(reader, header);
Ok(())
}
#[tokio::test]
async fn serde_text_with_super() -> Result<(), eyre::Report>
{
let header = KeyHeader::new_now(KeyHeaderKind::Aes, Default::default(), Default::default());
let mut ser = Vec::new();
let superheader = SuperHeader::<KeyHeader>::new_for(&header);
println!("Writing: {:?} + {:?}", superheader, header);
let written = superheader.write_text(&mut ser, |_| None).await?;
ser.extend(header.into_memory(serialise::Mode::Text, |_| None)?); //header.write_text(&mut ser).await?;
println!("Wrote {} bytes", written);
println!("{}\n", ser.fmt_view());
let mut read = &ser[..];
let (reads, readn) = SuperHeader::from_memory(&mut read, serialise::Mode::Text, |_| None)?; // SuperHeader::read_text(read).await?;
let mut read = &read[readn..];
println!("Read super: {:?}", reads);
let readheader = KeyHeader::read_text(&mut read, |_| None).await?;
println!("Read real: {:?}", readheader);
reads.verify_for(&header)?;
reads.verify_for(&readheader)?;
assert_eq!(readheader, header);
assert_eq!(reads, superheader);
//panic!("\n{}\n",std::str::from_utf8(&ser[..]).unwrap());
Ok(())
}
}