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.
708 lines
22 KiB
708 lines
22 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
|
|
},
|
|
aes::AesKey,
|
|
};
|
|
|
|
#[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")
|
|
}
|
|
}
|
|
|
|
macro_rules! try_usize {
|
|
(-> $read:expr) => {
|
|
{
|
|
let read = $read;
|
|
usize::try_from(read)
|
|
.wrap_err_with(|| eyre::eyre!("Size does not fit into usize (u{} -> u{}).", std::mem::size_of_val(&read)*8, 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")
|
|
}
|
|
};
|
|
(<- $read:expr) => {
|
|
{
|
|
let read = $read;
|
|
read.try_into()
|
|
.wrap_err_with(|| eyre::eyre!("Failed to represent size (u{}) as 64 bit integer. Something is very wrong", std::mem::size_of_val(&read)*8))
|
|
.with_section(|| read.to_string().header("Buffer size was"))
|
|
}
|
|
}
|
|
}
|
|
|
|
macro_rules! write_chunked_text
|
|
{
|
|
($out:expr, $text:expr) => {
|
|
{
|
|
let mut written =0;
|
|
let out = $out;
|
|
let text = $text;
|
|
for bytes in text.as_bytes().chunks(TEXT_CHUNK_SIZE) {
|
|
out.write_all(bytes).await?;
|
|
out.write_u8(b'\n').await?;
|
|
written += bytes.len() + 1;
|
|
}
|
|
written
|
|
}
|
|
}
|
|
}
|
|
|
|
macro_rules! read_chunked_text {
|
|
($input:expr) => {
|
|
{
|
|
let input = $input;
|
|
let mut whole = String::new();
|
|
let mut buf = String::with_capacity(TEXT_CHUNK_SIZE+1);
|
|
loop {
|
|
match input.read_line(&mut buf).await? {
|
|
0 => break,
|
|
_ => {
|
|
whole.push_str(buf.trim());
|
|
buf.clear();
|
|
},
|
|
}
|
|
}
|
|
whole
|
|
}
|
|
};
|
|
}
|
|
|
|
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,
|
|
/// Key info
|
|
info: config::op::KeyDescription,
|
|
/// Used to encrypt the key body
|
|
content_enc: crypto::aes::AesKey,
|
|
/// Hash of encrypted key body
|
|
hash: Sha256Hash,
|
|
timestamp: i64,
|
|
}
|
|
|
|
/// Parts of the key that are encrypted
|
|
#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
|
|
struct InternalKeyInfo
|
|
{
|
|
content_enc: crypto::aes::AesKey,
|
|
info: config::op::KeyDescription
|
|
}
|
|
|
|
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
|
|
{
|
|
/// Get the key used to encrypt the real key
|
|
pub fn body_key(&self) -> &AesKey
|
|
{
|
|
&self.content_enc
|
|
}
|
|
/// Create a new key header from these values
|
|
pub const fn new(kind: KeyHeaderKind, info: config::op::KeyDescription, content_enc: crypto::aes::AesKey, hash: Sha256Hash, timestamp: i64) -> Self
|
|
{
|
|
Self {
|
|
kind,
|
|
info,
|
|
hash,
|
|
timestamp,
|
|
content_enc,
|
|
}
|
|
}
|
|
/// Create a new key header from these values with the current timestamp
|
|
#[instrument]
|
|
pub fn new_now(kind: KeyHeaderKind, info: config::op::KeyDescription, hash: Sha256Hash) -> Self
|
|
{
|
|
Self {
|
|
timestamp: timestamp::now(),
|
|
..Self::new(kind,info, crypto::aes::AesKey::generate().expect("Not enough entropy") ,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 salt = extract_salt!(salt, passwd)?;
|
|
trace!("Using password {} with salt {}", passwd.to_hex_string(), salt.to_hex_string());
|
|
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(TEXT_CHUNK_SIZE) {
|
|
out.write_all(bytes).await?;
|
|
out.write_u8(b'\n').await?;
|
|
written += bytes.len() + 1;
|
|
}
|
|
out.write_all(b"---\n").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 {
|
|
let mut buffer = String::new();
|
|
while input.read_line(&mut buffer).await? != 0 {
|
|
let bounds = {
|
|
let trimmed = buffer.trim();
|
|
if trimmed.len() == 0 {
|
|
continue;
|
|
} else if trimmed == "---" {
|
|
return Ok(true)
|
|
}
|
|
buffer.slice_bounds(trimmed)
|
|
};
|
|
buffer.drain_inverse(bounds);
|
|
if {
|
|
let bytes = buffer.as_bytes();
|
|
|
|
if bytes.len() > 0 && bytes[bytes.len()-1] == b'\n' {
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
} {
|
|
buffer.truncate(buffer.len()-1);
|
|
}
|
|
tx.send(buffer.clone()).await?;
|
|
buffer.clear();
|
|
}
|
|
warn!("Buffer contained no end-of-entry delimiter");
|
|
Ok::<bool, SendError>(false)
|
|
};
|
|
let line_reader = async move {
|
|
macro_rules! take_one {
|
|
($msg:literal $($tt:tt)*) => {
|
|
if let Some(line) = rx.recv().await {
|
|
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();
|
|
while let Some(line) = rx.recv().await {
|
|
debug_assert_eq!(&line[..], line.trim());
|
|
enc.push_str(&line[..]);
|
|
}
|
|
trace!("Done reading lines");
|
|
|
|
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_suggestion(|| "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_suggestion(|| "Specify the password to load this key with"),
|
|
}
|
|
}
|
|
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(_) | Ok(true) => {
|
|
trace!("We completed without reading whole file");
|
|
Ok(rres?)
|
|
},
|
|
_ => {
|
|
warn!("Header reading completed to EOF, there is no body in this stream.");
|
|
|
|
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>
|
|
{
|
|
//TODO: Replace with just serialising whole object with cbor.
|
|
let salt= SaltGen::new();
|
|
out.write_u8(self.kind as u8).await?;
|
|
|
|
let desc = {
|
|
let info = InternalKeyInfo {
|
|
content_enc: self.content_enc.clone(),
|
|
info: self.info.clone(),
|
|
};
|
|
let buf = serde_cbor::to_vec(&info)
|
|
.wrap_err_with(|| eyre::eyre!("Failed to serialise key info into bytes"))
|
|
.with_section(|| format!("{:?}", self.info).header("Key info was"))?;
|
|
1 + if let Some(passwd) = passwd(&salt) {
|
|
|
|
let 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"))?;
|
|
|
|
let buf = {
|
|
let mut ebuf = Vec::with_capacity(buf.len());
|
|
let aes = passwd.create_aes();
|
|
let ew =crypto::aes::encrypt_stream(&aes, &mut &buf[..], &mut ebuf).await //TODO: Remove this 2nd temporary, calculate the size beforehand and encrypt directly to output stream
|
|
.wrap_err_with(|| eyre::eyre!("Failed to encrypt serialised object to buffer"))
|
|
.with_section(|| format!("{}", buf.fmt_view()).header("Buffer was"))
|
|
.with_section(|| passwd.to_hex_string().header("Password hash was"))
|
|
.with_section(|| salt.to_hex_string().header("Salt was"))?;
|
|
debug!("Encrypted in-memory {} bytes (from {} -> {})", ew, buf.len(), ebuf.len());
|
|
ebuf
|
|
};
|
|
out.write_i8(1).await?;
|
|
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("Encrypted buffer size was"))?).await?;
|
|
out.write_all(salt.as_ref()).await?;
|
|
out.write_all(&buf[..]).await?;
|
|
buf.len() + 8 + salt.as_ref().len()
|
|
}
|
|
else
|
|
{
|
|
out.write_u8(0).await?;
|
|
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() + 8
|
|
}
|
|
};
|
|
out.write_all(self.hash.as_ref()).await?;
|
|
out.write_i64(self.timestamp).await?;
|
|
Ok(1 + std::mem::size_of::<Sha256Hash>() + desc + 8)
|
|
}
|
|
|
|
/// 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 encrypted = input.read_u8().await?;
|
|
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 info: InternalKeyInfo = match encrypted {
|
|
1 => { // encrypted
|
|
let mut salt = Salt::none();
|
|
input.read_exact(salt.as_mut()).await?;
|
|
if let Some(passwd) = passwd(&salt) {
|
|
let mut buffer = vec![0u8; read];
|
|
input.read_exact(&mut buffer[..]).await?;
|
|
let mut ebuffer = Vec::with_capacity(buffer.len());
|
|
|
|
let aes = passwd.create_aes();
|
|
let r = crypto::aes::decrypt_stream(&aes, &mut &buffer[..], &mut ebuffer).await
|
|
.wrap_err_with(|| eyre::eyre!("Failed to decrypt key data"))
|
|
.with_section(|| aes.to_string().header("Derived key was"))
|
|
.with_section(|| salt.to_hex_string().header("Embedded salt was"))
|
|
.with_section(|| format!("{}", buffer.fmt_view()).header("Read data was"))
|
|
.with_suggestion(|| "Did you enter the wrong password?")?;
|
|
debug!("Decrypted {} (from {} -> {})", r, buffer.len(), ebuffer.len());
|
|
|
|
serde_cbor::from_reader(&ebuffer[..])
|
|
.wrap_err_with(|| eyre::eyre!("Failed to deserialise key info from decrypted byte buffer"))
|
|
.with_section(|| read.to_string().header("Encrypted byte buffer's valid read size was"))
|
|
.with_section(|| ebuffer.len().to_string().header("Decrypted byte buffer's valid read size was"))
|
|
.with_section(|| r.to_string().header("Processed stream size was"))
|
|
.with_section(|| aes.to_string().header("Derived key was"))
|
|
.with_section(|| salt.to_hex_string().header("Embedded salt was"))
|
|
.with_section(|| format!("{}", ebuffer.fmt_view()).header("Decrypted byte buffer was"))
|
|
.with_section(|| format!("{}", buffer.fmt_view()).header("Encrypted read data was"))?
|
|
}
|
|
else {
|
|
return Err(eyre::eyre!("Container is password encrypted but no password was provided"))
|
|
.with_section(|| salt.to_hex_string().header("Embedded salt was"))
|
|
.with_suggestion(|| "Specify the password to load this key with");
|
|
}
|
|
},
|
|
0 => { // not encrypted
|
|
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"))?
|
|
},
|
|
invalid => return Err(eyre::eyre!("Invalid encryption specifier"))
|
|
.with_note(|| "This suggests very likely corrupted data")
|
|
.with_section(|| format!("{:#x}", invalid).header("Invalid bit 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: info.info,
|
|
content_enc: info.content_enc,
|
|
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()
|
|
}
|
|
}
|
|
|
|
/// Trait for serialising key bodies
|
|
///
|
|
/// # Notes
|
|
/// Using this requires heap allocations, use each key's `write_` methods instead.
|
|
pub trait KeyBodySerialisable
|
|
{
|
|
fn serialise_text<'a, 'b: 'a, T: AsyncWrite+Unpin+?Sized>(&'a self, out: &'b mut T, ses_enc: &'b AesKey) -> LocalBoxFuture<'a, Result<usize, eyre::Report>>;
|
|
fn serialise_bytes<'a, 'b: 'a, T: AsyncWrite+Unpin+?Sized>(&'a self, out: &'b mut T, ses_enc: &'b AesKey) -> LocalBoxFuture<'a, Result<usize, eyre::Report>>;
|
|
}
|
|
|
|
/// Trait for deserialising key bodies
|
|
///
|
|
/// # Notes
|
|
/// Using this requires heap allocations, use each key's `read_` methods instead.
|
|
pub trait KeyBodyDeserialisable: Sized
|
|
{
|
|
fn deserialise_text<'a, T: AsyncBufRead+Unpin+?Sized>(input: &'a mut T, ses_enc: &'a AesKey) -> LocalBoxFuture<'a, Result<Self, eyre::Report>>
|
|
where Self: 'a;
|
|
fn deserialise_bytes<'a, T: AsyncBufRead+Unpin+?Sized>(input: &'a mut T, ses_enc: &'a AesKey) -> LocalBoxFuture<'a, Result<Self, eyre::Report>>
|
|
where Self: 'a;
|
|
}
|
|
|
|
#[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(())
|
|
}
|
|
}
|
|
|
|
pub mod aes;
|
|
pub mod rsa;
|