parent
af99394c2e
commit
6eff4d89ec
@ -0,0 +1,28 @@
|
||||
use libc::{
|
||||
c_void,
|
||||
};
|
||||
/// Copy slice of bytes only
|
||||
///
|
||||
/// # Notes
|
||||
/// `dst` and `src` must not overlap. See [move_slice].
|
||||
pub fn copy_slice(dst: &mut [u8], src: &[u8]) -> usize
|
||||
{
|
||||
let sz = std::cmp::min(dst.len(),src.len());
|
||||
unsafe {
|
||||
libc::memcpy(&mut dst[0] as *mut u8 as *mut c_void, &src[0] as *const u8 as *const c_void, sz);
|
||||
}
|
||||
sz
|
||||
}
|
||||
|
||||
/// Move slice of bytes only
|
||||
///
|
||||
/// # Notes
|
||||
/// `dst` and `src` can overlap.
|
||||
pub fn move_slice(dst: &mut [u8], src: &[u8]) -> usize
|
||||
{
|
||||
let sz = std::cmp::min(dst.len(),src.len());
|
||||
unsafe {
|
||||
libc::memmove(&mut dst[0] as *mut u8 as *mut c_void, &src[0] as *const u8 as *const c_void, sz);
|
||||
}
|
||||
sz
|
||||
}
|
@ -0,0 +1,295 @@
|
||||
//! Keyiles format
|
||||
use super::*;
|
||||
use crypto::sha256::Sha256Hash;
|
||||
use futures::{
|
||||
future::FutureExt,
|
||||
};
|
||||
use std::{
|
||||
fmt,
|
||||
error,
|
||||
convert::{TryFrom, TryInto,},
|
||||
};
|
||||
|
||||
#[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;
|
||||
fn hash(&self) -> Sha256Hash
|
||||
{
|
||||
let mut output = Vec::new();
|
||||
self.write_bytes(&mut output).now_or_never().unwrap().expect("Failed to write bytes to in-memory buffer");
|
||||
crypto::sha256::compute_slice(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))]
|
||||
pub async fn write_text<T: AsyncWrite+Unpin+?Sized>(&self, out: &mut T) -> Result<usize, eyre::Report>
|
||||
{
|
||||
let vec = serde_json::to_vec(self)
|
||||
.wrap_err_with(|| eyre::eyre!("Failed to serialise self to JSON"))
|
||||
.with_section(|| format!("{:?}", self).header("Self was"))?;
|
||||
out.write_all(&vec[..]).await?;
|
||||
Ok(vec.len())
|
||||
}
|
||||
/// Read a superheader as text bytes from this stream
|
||||
#[instrument(err, skip(input))]
|
||||
pub async fn read_text<T: AsyncRead+Unpin+?Sized>(input: &mut T) -> Result<Self, eyre::Report>
|
||||
{
|
||||
let whole = input.read_whole_stream(MAX_TEXT_SZ).await
|
||||
.wrap_err_with(|| eyre::eyre!("Failed to read text stream into memory"))?;
|
||||
|
||||
Ok(serde_json::from_slice(&whole[..])
|
||||
.wrap_err_with(|| eyre::eyre!("Failed to deserialise JSON to value"))
|
||||
.with_section(move || String::from_utf8_lossy(&whole[..]).into_owned().header("Read text was"))?)
|
||||
}
|
||||
/// Write this key header as bytes to this stream
|
||||
#[instrument(err, skip(out))]
|
||||
pub async fn write_bytes<T: AsyncWrite+Unpin+?Sized>(&self, out: &mut T) -> 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))]
|
||||
pub async fn read_bytes<T: AsyncRead+Unpin+?Sized>(input: &mut T) -> 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
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests
|
||||
{
|
||||
use super::*;
|
||||
#[test]
|
||||
fn now_or_never()
|
||||
{
|
||||
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();
|
||||
let superheader = SuperHeader::<KeyHeader>::new_for(&header);
|
||||
println!("Writing: {:?} + {:?}", superheader, header);
|
||||
let written = superheader.write_bytes(&mut ser).await? +
|
||||
header.write_bytes(&mut ser).await?;
|
||||
println!("Wrote {} bytes", written);
|
||||
println!("{}\n", ser.fmt_view());
|
||||
|
||||
let mut read = &ser[..];
|
||||
let reads = SuperHeader::read_bytes(&mut read).await?;
|
||||
println!("Read super: {:?}", reads);
|
||||
let readheader = KeyHeader::read_bytes(&mut read).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).await?;
|
||||
println!("Wrote {} bytes:", val);
|
||||
println!("{}\n", ser.fmt_view());
|
||||
|
||||
let reader = KeyHeader::read_bytes(&mut &ser[..]).await?;
|
||||
println!("Read: {:?}", reader);
|
||||
assert_eq!(reader, header);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn serde_text() -> Result<(), eyre::Report>
|
||||
{
|
||||
color_eyre::install()?;
|
||||
|
||||
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).await?;
|
||||
println!("Wrote {} bytes:", val);
|
||||
println!("{}\n", ser.fmt_view());
|
||||
|
||||
let reader = KeyHeader::read_text(&mut &ser[..]).await?;
|
||||
println!("Read: {:?}", reader);
|
||||
assert_eq!(reader, header);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn serde_text_with_super() -> Result<(), eyre::Report>
|
||||
{
|
||||
//color_eyre::install()?;
|
||||
|
||||
panic!("We're going to have to write our own text serialisation to get around the `read_whole_buffer` thingy...");
|
||||
|
||||
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).await? +
|
||||
header.write_text(&mut ser).await?;
|
||||
println!("Wrote {} bytes", written);
|
||||
println!("{}\n", ser.fmt_view());
|
||||
|
||||
let mut read = &ser[..];
|
||||
let reads = SuperHeader::read_text(&mut read).await?;
|
||||
println!("Read super: {:?}", reads);
|
||||
let readheader = KeyHeader::read_text(&mut read).await?;
|
||||
println!("Read real: {:?}", readheader);
|
||||
|
||||
reads.verify_for(&header)?;
|
||||
reads.verify_for(&readheader)?;
|
||||
assert_eq!(readheader, header);
|
||||
assert_eq!(reads, superheader);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -0,0 +1,199 @@
|
||||
//! Handles file formats
|
||||
use super::*;
|
||||
use std::{
|
||||
error,
|
||||
fmt,
|
||||
marker::{
|
||||
PhantomData,
|
||||
Unpin,
|
||||
},
|
||||
};
|
||||
use tokio::{
|
||||
prelude::*,
|
||||
io::{
|
||||
AsyncWrite,
|
||||
AsyncRead,
|
||||
},
|
||||
};
|
||||
use version::Version;
|
||||
|
||||
|
||||
pub trait Header: fmt::Debug
|
||||
{
|
||||
const CHECK: u16;
|
||||
fn hash(&self) -> crypto::sha256::Sha256Hash;
|
||||
|
||||
}
|
||||
|
||||
pub const RAE_HEADER_BIT: [u8; 4] = *b"RAE0";
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
/// The header before all RAE files.
|
||||
pub struct SuperHeader<H: Header>
|
||||
{
|
||||
head: [u8; 4],
|
||||
vers: Version,
|
||||
chk: u16,
|
||||
header_hash: crypto::sha256::Sha256Hash,
|
||||
|
||||
_header: PhantomData<H>,
|
||||
}
|
||||
|
||||
impl<H: Header> Default for SuperHeader<H>
|
||||
{
|
||||
#[inline]
|
||||
fn default() -> Self
|
||||
{
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_TEXT_SZ: Option<usize> = Some(1024 * 1024);
|
||||
|
||||
impl<H: Header> SuperHeader<H>
|
||||
{
|
||||
/// Write this superheader as text bytes to this stream
|
||||
#[instrument(err, skip(out))]
|
||||
pub async fn write_text<T: AsyncWrite+Unpin+?Sized>(&self, out: &mut T) -> Result<usize, eyre::Report>
|
||||
{
|
||||
let vec = serde_json::to_vec(self)
|
||||
.wrap_err_with(|| eyre::eyre!("Failed to serialise self to JSON"))
|
||||
.with_section(|| format!("{:?}", self).header("Self was"))?;
|
||||
out.write_all(&vec[..]).await?;
|
||||
Ok(vec.len())
|
||||
}
|
||||
/// Read a superheader as text bytes from this stream
|
||||
#[instrument(err, skip(input))]
|
||||
pub async fn read_text<T: AsyncRead+Unpin+?Sized>(input: &mut T) -> Result<Self, eyre::Report>
|
||||
{
|
||||
let whole = input.read_whole_stream(MAX_TEXT_SZ).await
|
||||
.wrap_err_with(|| eyre::eyre!("Failed to read text stream into memory"))?;
|
||||
|
||||
Ok(serde_json::from_slice(&whole[..])
|
||||
.wrap_err_with(|| eyre::eyre!("Failed to deserialise JSON to value"))
|
||||
.with_section(move || String::from_utf8_lossy(&whole[..]).into_owned().header("Read text was"))?)
|
||||
}
|
||||
|
||||
/// Write this superheader as bytes to this stream
|
||||
#[instrument(err, skip(out))]
|
||||
pub async fn write_bytes<T: AsyncWrite+Unpin+?Sized>(&self, out: &mut T) -> Result<usize, eyre::Report>
|
||||
{
|
||||
Ok({out.write_all(&self.head[..]).await?; self.head.len()} +
|
||||
{out.write_all(self.vers.as_bytes()).await?; std::mem::size_of::<Version>()} +
|
||||
{out.write_u16(self.chk).await?; 2} +
|
||||
{out.write_all(self.header_hash.as_ref()).await?; std::mem::size_of::<crypto::sha256::Sha256Hash>()})
|
||||
}
|
||||
/// Read a superheader as bytes from this stream
|
||||
#[instrument(err, skip(input))]
|
||||
pub async fn read_bytes<T: AsyncRead+Unpin+?Sized>(input: &mut T) -> Result<Self, eyre::Report>
|
||||
{
|
||||
let mut new = Self::new();
|
||||
input.read_exact(&mut new.head[..]).await?;
|
||||
new.vers = {
|
||||
let mut bytes = [0u8; std::mem::size_of::<Version>()];
|
||||
input.read_exact(&mut bytes[..]).await?;
|
||||
Version::try_from_bytes(bytes)
|
||||
.wrap_err_with(|| eyre::eyre!("Failed to decode version"))
|
||||
.with_section(|| bytes.to_broken_hex_string().header("Bytes (hex) were"))?
|
||||
};
|
||||
new.chk = input.read_u16().await?;
|
||||
new.header_hash = {
|
||||
let mut bytes= crypto::sha256::Sha256Hash::empty();
|
||||
input.read_exact(bytes.as_mut()).await?;
|
||||
bytes
|
||||
};
|
||||
if new.vers.should_warn(&CURRENT_VERSION) {
|
||||
warn!("Header ({:?}) is read to have deprecated version {}, we are on version {}", new, new.vers, CURRENT_VERSION)
|
||||
}
|
||||
Ok(new)
|
||||
}
|
||||
|
||||
/// Create a new empty superheader
|
||||
pub fn new() -> Self
|
||||
{
|
||||
Self{
|
||||
head: RAE_HEADER_BIT,
|
||||
vers: CURRENT_VERSION,
|
||||
chk: H::CHECK,
|
||||
header_hash: Default::default(),
|
||||
_header: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Consume into a new instance with hash for `header`.
|
||||
pub fn with_hash(self, header: &H) -> Self
|
||||
{
|
||||
|
||||
Self {
|
||||
header_hash: header.hash(),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new superheader for
|
||||
pub fn new_for(header: &H) -> Self
|
||||
{
|
||||
Self {
|
||||
header_hash: header.hash(),
|
||||
..Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// Verify this empty superheader
|
||||
pub fn verify(&self) -> Result<(), VerificationError>
|
||||
{
|
||||
macro_rules! check {
|
||||
($field:expr, $err:expr) => {
|
||||
if !$field {
|
||||
return Err($err);
|
||||
}
|
||||
}
|
||||
}
|
||||
check!(self.head == RAE_HEADER_BIT, VerificationError::BadHeaderBit);
|
||||
check!(self.vers.is_compat(&CURRENT_VERSION), VerificationError::IncompatableVersion(self.vers));
|
||||
check!(self.chk == H::CHECK, VerificationError::BadCheckBit);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Verify this not-empty header for `header`.
|
||||
///
|
||||
/// # Notes
|
||||
/// This also calls `self.verify()`.
|
||||
pub fn verify_for(&self, header: &H) -> Result<(), VerificationError>
|
||||
{
|
||||
self.verify()?;
|
||||
if self.header_hash == header.hash() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(VerificationError::BadHash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum VerificationError
|
||||
{
|
||||
BadHeaderBit,
|
||||
IncompatableVersion(Version),
|
||||
BadCheckBit,
|
||||
BadHash,
|
||||
}
|
||||
|
||||
impl error::Error for VerificationError{}
|
||||
impl fmt::Display for VerificationError
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
||||
{
|
||||
match self {
|
||||
Self::BadHeaderBit => write!(f, "bad header bit"),
|
||||
Self::IncompatableVersion(vers) => write!(f, "contained incompatable version {}, we are on version {}", vers, CURRENT_VERSION),
|
||||
Self::BadCheckBit => write!(f, "contained invalid check bit"),
|
||||
Self::BadHash => write!(f, "contained invalid header hash"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod key;
|
||||
const CHECK_KEY: u16 = 0x0001;
|
@ -0,0 +1,2 @@
|
||||
//! Generate aes key operations
|
||||
use super::*;
|
@ -0,0 +1,4 @@
|
||||
//! Generate key operations
|
||||
use super::*;
|
||||
|
||||
pub mod aes;
|
@ -0,0 +1,3 @@
|
||||
use super::*;
|
||||
|
||||
pub mod generate;
|
Loading…
Reference in new issue