aes body ser

master
Avril 4 years ago
parent f324132f18
commit 439663539b
Signed by: flanchan
GPG Key ID: 284488987C31F630

20
Cargo.lock generated

@ -206,12 +206,12 @@ dependencies = [
[[package]]
name = "cryptohelpers"
version = "1.3.0"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bc52622422e80c9a71f9664e3b90a77465b3ea4f05f06591987395eda0ff0f2"
checksum = "f58a1678cb9178e7af87eeba23f1524507e6fcf76a2f4807179a02dde722ccc7"
dependencies = [
"crc",
"getrandom",
"getrandom 0.1.15",
"hex-literal",
"hmac",
"libc",
@ -395,6 +395,17 @@ dependencies = [
"wasi 0.9.0+wasi-snapshot-preview1",
]
[[package]]
name = "getrandom"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee8025cf36f917e6a52cce185b7c7177689b838b7ec138364e50cc2277a56cf4"
dependencies = [
"cfg-if",
"libc",
"wasi 0.9.0+wasi-snapshot-preview1",
]
[[package]]
name = "gimli"
version = "0.22.0"
@ -771,6 +782,7 @@ dependencies = [
"color-eyre",
"cryptohelpers",
"futures",
"getrandom 0.2.0",
"hex",
"hex-literal",
"lazy_static",
@ -816,7 +828,7 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
dependencies = [
"getrandom",
"getrandom 0.1.15",
]
[[package]]

@ -15,7 +15,7 @@ local-time = []
[dependencies]
color-eyre = "0.5.3"
lazy_static = "1.4.0"
crypto = {package= "cryptohelpers", version = "1.3", features=["full", "async", "serialise"]}
crypto = {package= "cryptohelpers", version = "1.4.1", features=["full", "async", "serialise"]}
cfg-if = "0.1.10"
tokio = {version = "0.2", features=["full"]}
serde = {version ="1.0.116", features=["derive"]}
@ -36,6 +36,7 @@ hex = "0.4.2"
async-trait = "0.1.40"
hex-literal = "0.3.1"
once_cell = "1.4.1"
getrandom = "0.2.0"
#serde_json = "1.0.57" # serde not suitable for our text formatting :/ maybe just use `cbor` -> base64 with text header? would be a PEM-like format. sounds good imo
[build-dependencies]

@ -4,6 +4,9 @@ use std::{
error,
pin::Pin,
task::{Poll,Context,},
ops::{
Range,
},
};
use tokio::{
io::AsyncRead,
@ -434,3 +437,4 @@ mod tests
}
}
}

@ -0,0 +1,177 @@
//! AES key serialisation
use super::*;
use crypto::{
aes::{
self,
AesKey,
},
consts::{AES_IVSIZE, AES_KEYSIZE},
};
use eyre::eyre;
const PADDING_SZ: usize = AES_KEYSIZE + AES_IVSIZE;
fn new_padding() -> [u8; PADDING_SZ]
{
let mut buf = [0u8; PADDING_SZ];
getrandom::getrandom(&mut buf[..]).expect("Not enough entropy");
buf
}
/// Contains the body of an AES key
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct AesBody
{
key: AesKey,
garbage: [u8; PADDING_SZ],
}
impl AesBody
{
/// Compute the sha256 hash of this body instance
#[inline] pub fn compute_hash(&self) -> Sha256Hash
{
debug_assert_ne!(self.garbage, [0u8; PADDING_SZ], "Empty padding is unwise");
let mut cbuf = [0u8; PADDING_SZ + AES_KEYSIZE+ AES_IVSIZE];
bytes::copy_slice(&mut cbuf[..AES_KEYSIZE+ AES_IVSIZE], self.key.as_ref());
bytes::copy_slice(&mut cbuf[AES_KEYSIZE+AES_IVSIZE..], &self.garbage[..]);
crypto::sha256::compute_slice(cbuf)
}
/// Create a new body container from this key
pub fn new_key(key: AesKey) -> Self
{
Self { key, garbage: new_padding() }
}
/// Create an empty, keyless body
///
/// # Important
/// This creates an instance with all 0 padding. Use `with_padding` or `give_padding` pls.
pub const fn empty() -> Self
{
Self { key: AesKey::empty(), garbage: [0u8; PADDING_SZ] }
}
/// Create a new instance with new padding
pub fn with_padding(self) -> Self
{
Self {
garbage: new_padding(),
..self
}
}
/// Give this body new padding
pub fn give_padding(&mut self)
{
bytes::copy_slice(&mut self.garbage[..], &new_padding()[..]);
}
/// Consume into the internal aes key
pub fn into_key(self) -> AesKey
{
self.key
}
/// Get the internal body key
pub fn key(&self) -> &AesKey
{
&self.key
}
/// Get the internal body key
pub fn key_mut(&mut self) -> &mut AesKey
{
&mut self.key
}
/// Write an aes body to a binary stream
#[instrument(skip(out), err)]
pub async fn write_bytes<T: AsyncWrite+?Sized+Unpin>(&self, out: &mut T, ses_enc: &AesKey) -> Result<usize, eyre::Report>
{
if self.garbage == [0u8; PADDING_SZ] {
warn!("Writing empty-padded body");
}
let mut buf = [0u8; AES_KEYSIZE +AES_IVSIZE + PADDING_SZ];
bytes::copy_slice(&mut buf[..AES_KEYSIZE], self.key.k());
bytes::copy_slice(&mut buf[AES_KEYSIZE..], self.key.i());
bytes::copy_slice(&mut buf[AES_KEYSIZE+ AES_IVSIZE..], &self.garbage[..]);
let mut tbuf = Vec::with_capacity(buf.len()); //eh, i need to do calculating instead of these dumb temp buffers...
aes::encrypt_stream(ses_enc, &mut &buf[..], &mut tbuf).await
.wrap_err_with(|| eyre!("Failed to encrypt body stream with header key"))
.with_section(|| format!("{}", buf.fmt_view()).header("Body stream was"))
.with_section(|| ses_enc.to_string().header("Header key was"))?;
out.write_u64(try_usize!(<- tbuf.len())?).await?;
out.write_all(&tbuf[..]).await?;
Ok(8 + tbuf.len())
}
/// Read an aes body from a binary stream
#[instrument(skip(input), err)]
pub async fn read_bytes<T: AsyncRead+?Sized+Unpin>(input: &mut T, ses_enc: &AesKey) -> Result<Self, eyre::Report>
{
let sz = try_usize!(-> input.read_u64().await?)?;
let mut tbuf = vec![0u8; sz];
input.read_exact(&mut tbuf[..]).await?;
let mut buf = [0u8; AES_KEYSIZE + AES_IVSIZE + PADDING_SZ];
let mut cbuf = std::io::Cursor::new(&mut buf[..]);
let written = aes::decrypt_stream(ses_enc, &mut &tbuf[..], &mut cbuf).await
.wrap_err_with(|| eyre!("Failed to decrypt body stream with header key"))
.with_section(|| format!("{}", tbuf.fmt_view()).header("Body stream was"))
.with_section(|| ses_enc.to_string().header("Header key was"))?;
if written != buf.len() {
return Err(eyre::eyre!("Decrypted buffer length {} is not equal to expected AES key size {}", written, buf.len()))
.with_section(|| format!("{}", (&buf[..written]).fmt_view()).header("Decrypted buffer was"))
.with_note(|| "This likeley indicates bad decrypted data");
}
let mut key = AesKey::default();
bytes::copy_slice(key.k_mut(), &buf[..AES_KEYSIZE]);
bytes::copy_slice(key.i_mut(), &buf[AES_KEYSIZE..]);
let mut garbage = [0u8; PADDING_SZ];
bytes::copy_slice(&mut garbage[..], &buf[AES_KEYSIZE + AES_IVSIZE..]);
if garbage == [0u8; PADDING_SZ] {
warn!("Read empty-padded body");
}
Ok(Self{
key,
garbage,
})
}
/// Write an aes body to a text stream
#[instrument(err, skip(out))]
pub async fn write_text<T: AsyncWrite+Unpin+?Sized>(&self, out: &mut T, ses_enc: &AesKey) -> Result<usize, eyre::Report>
{
let text = serialise::into_text_with_key_async(self, ses_enc).await
.wrap_err_with(|| eyre!("Failed to serialise body to text"))
.with_section(|| self.key.to_string().header("Body was"))
.with_section(|| ses_enc.to_string().header("Header key was"))?;
let mut written = 0;
trace!("Writing whole {:?}", 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;
}
Ok(written)
}
/// Read an AES body from a text stream
#[instrument(err, skip(input))]
pub async fn read_text<T: AsyncBufRead+Unpin+?Sized>(input: &mut T, ses_enc: &AesKey) -> Result<Self, eyre::Report>
{
let mut whole = String::new();
let mut buf = String::with_capacity(32);
loop {
match input.read_line(&mut buf).await? {
0 => break,
_ => {
whole.push_str(buf.trim());
buf.clear();
},
}
}
trace!("Read whole {:?}", whole);
Ok(serialise::from_text_with_key_async(&whole[..], ses_enc).await
.wrap_err_with(|| eyre!("Failed to deserialise body from text"))
.with_section(move|| whole.header("Body was"))
.with_section(|| ses_enc.to_string().header("Header key was"))?)
}
}

@ -15,6 +15,7 @@ use crypto::{
Password,
SALTSIZE
},
aes::AesKey,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord,PartialOrd, Hash, Serialize, Deserialize)]
@ -39,6 +40,25 @@ impl fmt::Display for ParsingError
}
}
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"))
}
}
}
impl TryFrom<u8> for KeyHeaderKind
{
@ -70,11 +90,23 @@ impl TryFrom<u8> for KeyHeaderKind
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;
@ -115,22 +147,29 @@ fn encode_salt(salt: &Salt) -> Result<[u8; SALTSIZE*2], eyre::Report>
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, hash: Sha256Hash, timestamp: i64) -> Self
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,hash,0)
..Self::new(kind,info, crypto::aes::AesKey::generate().expect("Not enough entropy") ,hash,0)
}
}
@ -141,11 +180,8 @@ impl KeyHeader
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 = 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?;
@ -166,13 +202,14 @@ impl KeyHeader
}.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) {
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"---").await?;
Ok(written + 4)
out.write_all(b"---\n>BEGIN\nBODY<\n").await?; // We can add a comment after > as long as it doesn't have any newlines.
// this is because of the retarded way i did read_text with mpsc.
Ok(written + 8 + 9)
}
/// Read a superheader as text bytes from this stream
#[instrument(err, skip(input, passwd))]
@ -226,7 +263,7 @@ impl KeyHeader
}
let line_sender = async move { //this is actually kinda overkill for this...
let line_sender = async move { //this causes the >\n<\n dumbness in the encoding. Rewrite this to not use mpsc pls
let mut buffer = String::new();
while input.read_line(&mut buffer).await? != 0 {
tx.send(buffer.clone()).await?;
@ -291,6 +328,7 @@ impl KeyHeader
if !had_delim {
warn!("Buffer contained no end-of-entry delimiter");
}
trace!("Done reading lines");
match password {
Some(salt) => {
@ -303,12 +341,12 @@ impl KeyHeader
.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?")?)
.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_note(|| "This key must be loaded with `-p` or `-P`"),
.with_suggestion(|| "Specify the password to load this key with"),
}
}
None => {
@ -323,35 +361,70 @@ impl KeyHeader
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")
Err(_) => {
trace!("We completed without reading whole file");
Ok(rres?)
},
_ => {
warn!("Header reading completed to EOF, there is no body in this stream.");
Ok(rres?)
},
_ => 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 buf = serde_cbor::to_vec(&self.info)
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"))?;
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()
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>() + 8 + desc)
Ok(1 + std::mem::size_of::<Sha256Hash>() + desc + 8)
}
/// Read a key header as bytes from this stream
@ -363,18 +436,58 @@ impl KeyHeader
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 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();
@ -385,7 +498,8 @@ impl KeyHeader
Ok(Self {
kind,
info,
info: info.info,
content_enc: info.content_enc,
hash,
timestamp
})
@ -509,8 +623,7 @@ mod tests
#[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);
@ -536,3 +649,5 @@ mod tests
Ok(())
}
}
pub mod aes;

@ -23,6 +23,8 @@ use version::Version;
use crypto::password::{Password,Salt};
use serialise::SaltGen;
const TEXT_CHUNK_SIZE: usize = 32;
/// Trait RAE headers implement
pub trait Header: fmt::Debug
{
@ -45,6 +47,46 @@ pub trait Header: fmt::Debug
}
}
macro_rules! extract_salt {
($salt:expr, $passwd:expr) => {
$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"))
}
}
macro_rules! encrypt_buffer {
($aes:expr, $buf:expr, $ebuf:expr, $passwd:expr, $salt:expr; $msg:literal $($tt:tt)*) => {
{
let buf = $buf;
let ebuf = $ebuf;
crypto::aes::encrypt_stream($aes, buf, ebuf).await //TODO: Remove this 2nd temporary, calculate the size beforehand and encrypt directly to output stream
.wrap_err_with(|| eyre::eyre!($msg $($tt)*))
.map(|ew| debug!("Encrypted in-memory {} bytes (from {} -> {})", ew, buf.len(), ebuf.len()))
.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"))
}
};
}
macro_rules! decrypt_buffer {
($aes:expr, $buf:expr, $ebuf:expr, $passwd:expr, $salt:expr; $msg:literal $($tt:tt)*) => {
{
let buffer = $buf;
let ebuffer= $ebuf;
crypto::aes::decrypt_stream($aes, buffer, ebuffer).await
.map(|r| debug!("Decrypted in-memory {} (from {} -> {})", r, buffer.len(), ebuffer.len()))
.wrap_err_with(|| eyre::eyre!($msg $($tt)*))
.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?")
}
};
}
pub const RAE_HEADER_BIT: [u8; 4] = *b"RAE0";
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
@ -73,8 +115,8 @@ const MAX_TEXT_SZ: Option<usize> = Some(1024 * 1024);
impl<H: Header +?Sized> SuperHeader<H>
{
/// 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>
#[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 string = format!(r#"--- {} v{}-{:x} ({}) {} ---
"#, std::str::from_utf8(&self.head[..])
@ -89,8 +131,8 @@ impl<H: Header +?Sized> SuperHeader<H>
Ok(string.len())
}
/// 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>
#[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 line = String::new();
input.read_line(&mut line).await?;
@ -198,17 +240,37 @@ impl<H: Header +?Sized> SuperHeader<H>
}
/// Write this superheader 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>
#[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>
{
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>()}) //this is what gets encrypted by password
{
// for now we won't bother encrypting here
/*
let salt= SaltGen::new();
if let Some(passwd) = passwd(&salt) {
let salt = extract_salt!(salt, passwd);
let aes = passwd.create_aes();
let hash_bytes = self.header_hash.as_ref();
let mut output = Vec::with_capacity(hash_bytes.len());
encrypt_buffer!(&aes, &mut &hash_bytes[..], &mut output, passwd, salt; "Failed to encrypt hash part")
.with_section(|| self.header_hash.to_string().header("Hash was"))?;
out.write_u8(1).await?;
out.write_all(&output[..]).await?;
output.len()
} else {
out.write_u8(0).await?;*/
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, passwd))]
pub async fn read_bytes<T: AsyncRead+Unpin+?Sized, F: FnOnce(&Salt) -> Option<Password>>(input: &mut T, passwd: F) -> Result<Self, eyre::Report>
#[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 mut new = Self::new();
input.read_exact(&mut new.head[..]).await?;
@ -349,6 +411,6 @@ impl<H: Header+?Sized> serialise::TextSerialiseable for SuperHeader<H>
}
/// Nopassword text constant
const TEXT_NOPASS: &[u8; 16] = b"0000000000000000";
const TEXT_NOPASS: &[u8; 64] = b"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
pub mod key;
const CHECK_KEY: u16 = 0x0001;

@ -3,7 +3,7 @@
#![cfg_attr(nightly, feature(const_fn_transmute))]
#![cfg_attr(nightly, feature(never_type))]
#![cfg_attr(nightly, feature(test))]
#![cfg_attr(nightly, feature(untagged_unions))]
#![allow(dead_code)]
#![allow(unused_macros)]
@ -140,31 +140,45 @@ fn install_tracing() {
.with(ErrorLayer::default())
.init();
}
#[instrument]
async fn fuck() -> eyre::Result<()>
{
use format::*;
use format::key::*;
let header = KeyHeader::new_now(KeyHeaderKind::Aes, Default::default(), Default::default());
use crypto::password::Password;
let header = KeyHeader::new_now(KeyHeaderKind::Aes, config::op::KeyDescription{
name: "Name!".to_owned(),
description: "Some description".to_owned(),
..Default::default()
}, Default::default());
let body = key::aes::AesBody::new_key(crypto::aes::AesKey::generate().unwrap());
let mut ser = Vec::new();
let password = "hello world. I am a password";
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!("Writing: {:?}\n\t{:?}\n\t{:?}", superheader, header, body);
let mut written = superheader.write_bytes(&mut ser, |salt| Some(Password::derive(password, salt.as_ref()))).await?;
ser.extend(header.into_memory(serialise::Mode::Binary, |salt| Some(Password::derive(password, salt.as_ref())))?); //header.write_text(&mut ser).await?;
written += body.write_bytes(&mut ser, header.body_key()).await?;
println!("Wrote {} bytes", written);
println!("{}\n", ser.fmt_view());
println!("As text:\n{}\n", String::from_utf8_lossy(&ser[..]));
let mut read = &ser[..];
let (reads, readn) = SuperHeader::from_memory(&mut read, serialise::Mode::Text, |_| None)?; // SuperHeader::read_text(read).await?;
let (reads, readn) = SuperHeader::from_memory(&mut read, serialise::Mode::Binary, |salt| Some(Password::derive(password, salt)))?; // SuperHeader::read_text(read).await?;
let mut read = &read[readn..];
println!("Read super: {:?}", reads);
let readheader = KeyHeader::read_text(&mut read, |_| None).await?;
let readheader = KeyHeader::read_bytes(&mut read, |salt| Some(Password::derive(password, salt))).await?;
println!("Read real: {:?}", readheader);
let readbody = key::aes::AesBody::read_bytes(&mut read, readheader.body_key()).await?;
println!("Read body: {:?}", readbody);
reads.verify_for(&header)?;
reads.verify_for(&readheader)?;
assert_eq!(readheader, header);
assert_eq!(reads, superheader);
assert_eq!(readbody, body);
info!("All okay!");
Ok(())
}

@ -116,7 +116,7 @@ pub trait Serialisable: BinarySerialisable + TextSerialiseable
///
/// # Notes
/// This function will fail if any `await`ed future within the `serialise_*` method cannot complete immediately.
#[instrument(skip(self, passwd))]
#[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)
@ -147,7 +147,7 @@ pub trait Serialisable: BinarySerialisable + TextSerialiseable
///
/// # Notes
/// This function will fail if any `await`ed future within the `deserialise_*` method cannot complete immediately.
#[instrument(skip(buf, passwd),fields(buf = ?buf.as_ref()))]
#[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();

Loading…
Cancel
Save