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.
313 lines
9.9 KiB
313 lines
9.9 KiB
//! Serialisation helpers & traits
|
|
use super::*;
|
|
use tokio::io::{AsyncWrite,AsyncBufRead,AsyncRead,};
|
|
use std::{
|
|
marker::Unpin,
|
|
fmt,
|
|
};
|
|
use crypto::{
|
|
password::{
|
|
Salt,
|
|
Password,
|
|
},
|
|
aes::{self, AesKey,},
|
|
};
|
|
use once_cell::sync::OnceCell;
|
|
|
|
/// Used to lazily generate a salt
|
|
pub struct SaltGen
|
|
{
|
|
salt: OnceCell<Salt>,
|
|
}
|
|
|
|
impl SaltGen
|
|
{
|
|
/// Create a new salt generator
|
|
pub fn new() -> Self
|
|
{
|
|
Self {salt:OnceCell::new()}
|
|
}
|
|
/// Get or generate the salt
|
|
#[inline] pub fn get(&self) -> &Salt
|
|
{
|
|
self.salt.get_or_init(|| Salt::random().expect("Failed to generate random salt")) //is panicking here desired? I think so
|
|
}
|
|
|
|
/// Is this instancce initialised?
|
|
#[inline] pub fn is_set(&self) -> bool
|
|
{
|
|
self.salt.get().is_some()
|
|
}
|
|
|
|
/// Get value if it is inisialised
|
|
#[inline] pub fn try_get(&self) -> Option<&Salt>
|
|
{
|
|
self.salt.get()
|
|
}
|
|
|
|
/// Set the salt to a specific value. Will panic if already set.
|
|
pub fn set(&self, with: Salt) -> &Salt
|
|
{
|
|
self.salt.set(with).unwrap_or_else(|e| panic!("Tried to set `SaltGen` salt multiple times. New value was {:?}", e));
|
|
unsafe {
|
|
self.salt.get_unchecked() // is initialised at this point
|
|
}
|
|
}
|
|
|
|
/// Consume into an instance of `Salt` if it has been initialised yet
|
|
#[inline] pub fn into_inner(self) -> Option<Salt>
|
|
{
|
|
self.salt.into_inner()
|
|
}
|
|
}
|
|
|
|
impl AsRef<Salt> for SaltGen
|
|
{
|
|
#[inline] fn as_ref(&self) -> &Salt
|
|
{
|
|
self.get()
|
|
}
|
|
}
|
|
|
|
impl std::ops::Deref for SaltGen
|
|
{
|
|
type Target = Salt;
|
|
#[inline] fn deref(&self) -> &Self::Target {
|
|
self.get()
|
|
}
|
|
}
|
|
|
|
///// The callback to get a derrived password
|
|
//pub trait PasswordFn: for<'r> FnOnce(&'r Salt) -> Option<Password>{}
|
|
//impl<T: for<'r> FnOnce(&'r Salt) -> Option<Password>> PasswordFn for T{}
|
|
// ^ -- super dumb awful unhelpful error messages trying to do this, so fuck it copy & paste it is
|
|
|
|
pub trait TextSerialiseable: Sized
|
|
{
|
|
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>>;
|
|
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;
|
|
#[inline] fn size_hint(&self) -> (usize, Option<usize>)
|
|
{
|
|
(0,None)
|
|
}
|
|
}
|
|
|
|
|
|
pub trait BinarySerialisable: Sized
|
|
{
|
|
fn serialise_bytes<'a, 'b, 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>>
|
|
where 'b: 'a;
|
|
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;
|
|
|
|
#[inline] fn size_hint(&self) -> (usize, Option<usize>)
|
|
{
|
|
(0,None)
|
|
}
|
|
}
|
|
|
|
pub trait Serialisable: BinarySerialisable + TextSerialiseable
|
|
{
|
|
/// Attempt to immediately serialise this object into memory.
|
|
///
|
|
/// Returns a newly allocated `Vec<u8>` containing the bytes.
|
|
/// Attempts to use the appropriate `size_hint` to allocate vector capacity if possible.
|
|
///
|
|
/// # Notes
|
|
/// This function will fail if any `await`ed future within the `serialise_*` method cannot complete immediately.
|
|
#[instrument(skip(self, passwd), fields(ty = ?std::any::type_name::<Self>()))]
|
|
fn into_memory<F: FnOnce(&SaltGen) -> Option<Password>>(&self, mode: Mode, passwd: F) -> Result<Vec<u8>, eyre::Report>
|
|
{
|
|
let mut output = match mode.size_hint(self)
|
|
{
|
|
(0, None) | (_, Some(0)) => Vec::new(),
|
|
(_, Some(x)) | (x, None) => Vec::with_capacity(x),
|
|
};
|
|
trace!("Cap for membuf is {} bytes", output.capacity());
|
|
let note = || format!("While serialising {} into memory", std::any::type_name::<Self>());
|
|
let written = match mode {
|
|
Mode::Text => self.serialise_text(&mut output, passwd).now_or_never(),
|
|
Mode::Binary => self.serialise_bytes(&mut output, passwd).now_or_never(),
|
|
}
|
|
.ok_or_else(|| eyre::eyre!("Failed to complete {} write syncronously. A future returned `Poll::Pending`.", mode))
|
|
.with_note(note.clone())?
|
|
.wrap_err_with(|| eyre::eyre!("Failed to write {} into memory", mode))
|
|
.with_note(note)?;
|
|
if written != output.len()
|
|
{
|
|
error!("Function reported {} bytes written, but actually wrote {}", written, output.len());
|
|
}
|
|
Ok(output)
|
|
}
|
|
|
|
/// Attempt to immediately deserialise this object from memory
|
|
///
|
|
/// Returns the object and the number of bytes read from `buf`.
|
|
///
|
|
/// # Notes
|
|
/// This function will fail if any `await`ed future within the `deserialise_*` method cannot complete immediately.
|
|
#[instrument(skip(buf, passwd),fields(ty = ?std::any::type_name::<Self>(), buf_len = ?buf.as_ref().len()))]
|
|
fn from_memory<T: AsRef<[u8]>, F: FnOnce(&Salt) -> Option<Password>>(buf: T, mode: Mode, passwd: F) -> Result<(Self, usize), eyre::Report>
|
|
{
|
|
let buf = buf.as_ref();
|
|
let mut reader = &buf[..];
|
|
|
|
trace!("Attempting read of up to {} bytes in {} mode to instantiate type {}", buf.len(), mode, std::any::type_name::<Self>());
|
|
let note = || format!("While deserialising {} from memory", std::any::type_name::<Self>());
|
|
let bytes = || format!("{}", buf.fmt_view()).header("Binary buffer was");
|
|
let text = || String::from_utf8_lossy(buf).into_owned().header("Text buffer was");
|
|
let value = match mode {
|
|
Mode::Binary => Self::deserialise_bytes(&mut reader, passwd).now_or_never(),
|
|
Mode::Text => Self::deserialise_text(&mut reader, passwd).now_or_never(),
|
|
}
|
|
.ok_or_else(|| eyre::eyre!("Failed to complete {} read syncronously. A future returned `Poll::Pending`.", mode))
|
|
.with_note(note.clone())?
|
|
.wrap_err_with(|| eyre::eyre!("Failed to read {} from memory", mode))
|
|
.with_note(note)
|
|
.with_section(bytes)
|
|
.with_section(text)?;
|
|
|
|
let diff = (&reader[0] as *const u8 as usize) - (&buf[0] as *const u8 as usize);
|
|
|
|
Ok((value, diff))
|
|
}
|
|
}
|
|
|
|
impl<T> Serialisable for T
|
|
where T: BinarySerialisable + TextSerialiseable{}
|
|
|
|
/// Serialisation mode for `Serialisable` types
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub enum Mode
|
|
{
|
|
/// Serialise as text
|
|
Text,
|
|
/// Serialise as binary
|
|
Binary,
|
|
}
|
|
|
|
impl fmt::Display for Mode
|
|
{
|
|
#[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
|
{
|
|
match self {
|
|
Self::Text => write!(f, "text"),
|
|
Self::Binary => write!(f, "binary"),
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
impl Mode
|
|
{
|
|
#[inline(always)] fn size_hint<T: Serialisable>(&self, other: &T) -> (usize, Option<usize>)
|
|
{
|
|
match self {
|
|
Self::Text => TextSerialiseable::size_hint(other),
|
|
Self::Binary => BinarySerialisable::size_hint(other),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Serialise this object into base64 text
|
|
///
|
|
/// # TODO
|
|
/// Line wrapping
|
|
#[instrument(err, skip(value), fields(value_type=?std::any::type_name::<T>()))]
|
|
pub fn into_text<T: Serialize>(value: &T) -> Result<String, Error>
|
|
{
|
|
let bytes = serde_cbor::to_vec(value).map_err(Error::Serialise)?;
|
|
Ok(base64::encode(bytes))
|
|
}
|
|
|
|
|
|
/// Serialise this object into encrypted base64 text
|
|
///
|
|
/// # TODO
|
|
/// Line wrapping
|
|
#[instrument(err, skip(value), fields(value_type=?std::any::type_name::<T>()))]
|
|
pub async fn into_text_with_key_async<T: Serialize>(value: &T, k: &AesKey) -> Result<String, Error>
|
|
{
|
|
let bytes = serde_cbor::to_vec(value).map_err(Error::Serialise)?;
|
|
let mut output = Vec::with_capacity(bytes.len());
|
|
trace!("Encrypting stream");
|
|
let b = aes::encrypt_stream(k, &mut &bytes[..], &mut output).await.map_err(Error::Encryption)?;
|
|
trace!("Encrypted {} bytes (from {} -> {})", b, bytes.len(), output.len());
|
|
Ok(base64::encode(output))
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum Error
|
|
{
|
|
Base64(base64::DecodeError),
|
|
Serialise(serde_cbor::Error),
|
|
Deserialise(serde_cbor::Error),
|
|
Encryption(aes::Error),
|
|
Decryption(aes::Error),
|
|
}
|
|
|
|
impl std::error::Error for Error
|
|
{
|
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
|
match &self {
|
|
Self::Base64(b) => Some(b),
|
|
Self::Serialise(b) => Some(b),
|
|
Self::Deserialise(b) => Some(b),
|
|
Self::Encryption(b) => Some(b),
|
|
Self::Decryption(b) => Some(b),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Display for Error
|
|
{
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
|
|
{
|
|
match self {
|
|
Self::Base64(_) => write!(f, "base64 decode failed"),
|
|
Self::Serialise(_) => write!(f, "serialisation failed"),
|
|
Self::Deserialise(_) => write!(f, "deserialisation failed"),
|
|
Self::Encryption(_) => write!(f, "encryption failed"),
|
|
Self::Decryption(_) => write!(f, "decryption failed"),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Try to deserialise this object from text.
|
|
#[instrument(err, skip(string), fields(string = ?string.as_ref()))]
|
|
pub fn from_text<T: serde::de::DeserializeOwned>(string: impl AsRef<[u8]>) -> Result<T, Error>
|
|
{
|
|
let bytes = base64::decode(string).map_err(Error::Base64)?;
|
|
serde_cbor::from_reader(&bytes[..]).map_err(Error::Deserialise)
|
|
}
|
|
|
|
|
|
/// Try to deserialise this encrypted object from text.
|
|
#[instrument(err, skip(string), fields(string = ?string.as_ref()))]
|
|
pub async fn from_text_with_key_async<T: serde::de::DeserializeOwned>(string: impl AsRef<[u8]>, k: &AesKey) -> Result<T, Error>
|
|
{
|
|
let bytes = base64::decode(string).map_err(Error::Base64)?;
|
|
let mut output = Vec::with_capacity(bytes.len());
|
|
trace!("Decrypting stream");
|
|
let b = aes::decrypt_stream(k, &mut &bytes[..], &mut output).await.map_err(Error::Decryption)?;
|
|
trace!("Decrypted {} bytes (from {} -> {})", b, bytes.len(), output.len());
|
|
serde_cbor::from_reader(&output[..]).map_err(Error::Deserialise)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests
|
|
{
|
|
use crate::*;
|
|
#[test]
|
|
fn test_ser() -> Result<(), eyre::Report>
|
|
{
|
|
let object = "hello world";
|
|
let enc = super::into_text(&String::from(object)).wrap_err(eyre::eyre!("failed to encode"))?;
|
|
println!("Enc: {:?}", enc);
|
|
let dec: String = super::from_text(enc).wrap_err(eyre::eyre!("failed to decode"))?;
|
|
assert_eq!(object,dec);
|
|
Ok(())
|
|
}
|
|
}
|