From f324132f18c3d5022586850919e3d298db1bd9da Mon Sep 17 00:00:00 2001 From: Avril Date: Tue, 29 Sep 2020 17:03:20 +0100 Subject: [PATCH] dumb shit --- Cargo.lock | 1 + Cargo.toml | 1 + src/format/key/mod.rs | 104 +++++++++++++++++++++++++++------------- src/format/mod.rs | 17 +++---- src/main.rs | 4 +- src/serialise.rs | 108 ++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 188 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 86a8401..af93669 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -775,6 +775,7 @@ dependencies = [ "hex-literal", "lazy_static", "libc", + "once_cell", "pin-project", "recolored", "rustc_version", diff --git a/Cargo.toml b/Cargo.toml index 3100722..c0e00ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ base64 = "0.12.3" hex = "0.4.2" async-trait = "0.1.40" hex-literal = "0.3.1" +once_cell = "1.4.1" #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] diff --git a/src/format/key/mod.rs b/src/format/key/mod.rs index a36e289..beaf27e 100644 --- a/src/format/key/mod.rs +++ b/src/format/key/mod.rs @@ -82,7 +82,7 @@ impl Header for KeyHeader 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"); + self.write_bytes(&mut output, |_| None).now_or_never().unwrap().expect("Failed to write bytes to in-memory buffer"); crypto::sha256::compute_slice(output) } } @@ -135,19 +135,37 @@ impl KeyHeader } /// Write this superheader as text bytes to this stream - #[instrument(err, skip(out))] - pub async fn write_text(&self, out: &mut T, passwd: Option<&Password>) -> Result + #[instrument(err, skip(out, passwd))] + pub async fn write_text Option>(&self, out: &mut T, passwd: F) -> Result { - let text = serialise::into_text(self) - .wrap_err_with(|| eyre::eyre!("Failed to serialise header to text")) + 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"))?; - - let mut written={ - out.write_all(TEXT_NOPASS).await?; - out.write_u8(b'\n').await?; - TEXT_NOPASS.len() + 1 - }; for bytes in text.as_bytes().chunks(16) { out.write_all(bytes).await?; out.write_u8(b'\n').await?; @@ -252,7 +270,7 @@ impl KeyHeader None } else { Some(decode_salt(&pass_part[..]) - .with_section(move || pass_part.header("Password string part was"))?) + .with_section(move || pass_part.header("Password salt string part was"))?) } }; trace!("Decoded hex"); @@ -274,14 +292,30 @@ impl KeyHeader warn!("Buffer contained no end-of-entry delimiter"); } - //let = take_one!("Expected header line"); - - if let Some(salt) = password { - todo!() - } else { - Ok::(serialise::from_text(&enc[..]) - .wrap_err_with(|| eyre::eyre!("Failed to deserialise string")) - .with_section(|| enc.header("Read string was"))?) + match password { + Some(salt) => { + match passwd(&salt) { + Some(passwd) => { + let aes = passwd.create_aes(); + + Ok::(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::(serialise::from_text(&enc[..]) + .wrap_err_with(|| eyre::eyre!("Failed to deserialise string")) + .with_section(|| enc.header("Read string was"))?) + } } }; tokio::pin!(line_sender); @@ -301,8 +335,8 @@ impl KeyHeader } } /// Write this key header as bytes to this stream - #[instrument(err, skip(out))] - pub async fn write_bytes(&self, out: &mut T, passwd: Option<&Password>) -> Result + #[instrument(err, skip(out, passwd))] + pub async fn write_bytes Option>(&self, out: &mut T, passwd: F) -> Result { out.write_u8(self.kind as u8).await?; let desc = { @@ -361,7 +395,7 @@ impl KeyHeader impl serialise::BinarySerialisable for KeyHeader { - #[inline(always)] fn serialise_bytes<'a, 'b: 'a, T: AsyncWrite+Unpin+?Sized>(&'a self, out: &'b mut T, passwd: Option<&'b Password>) -> LocalBoxFuture<'a, Result> + #[inline(always)] fn serialise_bytes<'a, 'b: 'a, T: AsyncWrite+Unpin+?Sized,F: for<'r> FnOnce(&'r SaltGen) -> Option+ 'b>(&'a self, out: &'b mut T, passwd: F) -> LocalBoxFuture<'a, Result> { self.write_bytes(out, passwd).boxed_local() } @@ -374,7 +408,7 @@ impl serialise::BinarySerialisable for KeyHeader impl serialise::TextSerialiseable for KeyHeader { - #[inline(always)] fn serialise_text<'a, 'b: 'a, T: AsyncWrite+Unpin+?Sized>(&'a self, out: &'b mut T, passwd: Option<&'b Password>) -> LocalBoxFuture<'a, Result> + #[inline(always)] fn serialise_text<'a, 'b: 'a, T: AsyncWrite+Unpin+?Sized, F: for<'r> FnOnce(&'r SaltGen) -> Option + 'b>(&'a self, out: &'b mut T, passwd: F) -> LocalBoxFuture<'a, Result> { self.write_text(out, passwd).boxed_local() } @@ -414,18 +448,22 @@ mod tests let header = KeyHeader::new_now(KeyHeaderKind::Aes, Default::default(), Default::default()); let mut ser = Vec::new(); - let password = Password::derive("hello world", &Default::default()); + const PASSWORD: &str = "hello world"; + fn derive(salt: &Salt) -> Option + { + Some(Password::derive(PASSWORD, salt)) + } let superheader = SuperHeader::::new_for(&header); println!("Writing: {:?} + {:?}", superheader, header); - let written = superheader.serialise_bytes(&mut ser, Some(&password)).await? + - header.write_bytes(&mut ser,Some(&password)).await?; + 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, |_| Some(password.clone())).await?; + let reads = SuperHeader::deserialise_bytes(&mut read, |s| derive(s)).await?; println!("Read super: {:?}", reads); - let readheader = KeyHeader::deserialise_bytes(&mut read, |_| Some(password.clone())).await?; + let readheader = KeyHeader::deserialise_bytes(&mut read, |s| derive(s)).await?; println!("Read real: {:?}", readheader); reads.verify_for(&header)?; @@ -441,7 +479,7 @@ mod tests 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?; + let val = header.write_bytes(&mut ser, |_| None).await?; println!("Wrote {} bytes:", val); println!("{}\n", ser.fmt_view()); @@ -458,7 +496,7 @@ mod tests 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?; + let val = header.write_text(&mut ser, |_| None).await?; println!("Wrote {} bytes:", val); println!("{}\n", ser.fmt_view()); @@ -477,8 +515,8 @@ mod tests let mut ser = Vec::new(); let superheader = SuperHeader::::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?; + 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()); diff --git a/src/format/mod.rs b/src/format/mod.rs index f3fe7e9..445ec74 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -21,6 +21,7 @@ use tokio::{ }; use version::Version; use crypto::password::{Password,Salt}; +use serialise::SaltGen; /// Trait RAE headers implement pub trait Header: fmt::Debug @@ -72,8 +73,8 @@ const MAX_TEXT_SZ: Option = Some(1024 * 1024); impl SuperHeader { /// Write this superheader as text bytes to this stream - #[instrument(err, skip(out))] - pub async fn write_text(&self, out: &mut T, passwd: Option<&Password>) -> Result + #[instrument(err, skip(out, passwd))] + pub async fn write_text Option>(&self, out: &mut T, passwd: F) -> Result { let string = format!(r#"--- {} v{}-{:x} ({}) {} --- "#, std::str::from_utf8(&self.head[..]) @@ -82,7 +83,7 @@ impl SuperHeader .with_section(|| self.head.fmt_view().to_string().header("Head bytes were"))?, self.vers, self.vers.to_u32(), - self.header_hash.to_hex_string(), + self.header_hash.to_hex_string(), // this is what gets encrypted by password H::NAME); out.write_all(string.as_bytes()).await?; Ok(string.len()) @@ -197,13 +198,13 @@ impl SuperHeader } /// Write this superheader as bytes to this stream - #[instrument(err, skip(out))] - pub async fn write_bytes(&self, out: &mut T, passwd: Option<&Password>) -> Result + #[instrument(err, skip(out, passwd))] + pub async fn write_bytes Option>(&self, out: &mut T, passwd: F) -> Result { Ok({out.write_all(&self.head[..]).await?; self.head.len()} + {out.write_all(self.vers.as_bytes()).await?; std::mem::size_of::()} + {out.write_u16(self.chk).await?; 2} + - {out.write_all(self.header_hash.as_ref()).await?; std::mem::size_of::()}) + {out.write_all(self.header_hash.as_ref()).await?; std::mem::size_of::()}) //this is what gets encrypted by password } /// Read a superheader as bytes from this stream #[instrument(err, skip(input, passwd))] @@ -323,7 +324,7 @@ impl fmt::Display for VerificationError impl serialise::BinarySerialisable for SuperHeader { - #[inline(always)] fn serialise_bytes<'a, 'b: 'a, T: AsyncWrite+Unpin+?Sized>(&'a self, out: &'b mut T, passwd: Option<&'b Password>) -> LocalBoxFuture<'a, Result> + #[inline(always)] fn serialise_bytes<'a, 'b: 'a, T: AsyncWrite+Unpin+?Sized, F: for<'r> FnOnce(&'r SaltGen) -> Option + 'b>(&'a self, out: &'b mut T, passwd: F) -> LocalBoxFuture<'a, Result> { self.write_bytes(out, passwd).boxed_local() } @@ -336,7 +337,7 @@ impl serialise::BinarySerialisable for SuperHeader impl serialise::TextSerialiseable for SuperHeader { - #[inline(always)] fn serialise_text<'a, 'b: 'a, T: AsyncWrite+Unpin+?Sized>(&'a self, out: &'b mut T, passwd: Option<&'b Password>) -> LocalBoxFuture<'a, Result> + #[inline(always)] fn serialise_text<'a, 'b: 'a, T: AsyncWrite+Unpin+?Sized, F: for<'r> FnOnce(&'r SaltGen) -> Option + 'b>(&'a self, out: &'b mut T, passwd: F) -> LocalBoxFuture<'a, Result> { self.write_text(out, passwd).boxed_local() } diff --git a/src/main.rs b/src/main.rs index 7d70849..601cf1d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -148,8 +148,8 @@ async fn fuck() -> eyre::Result<()> let mut ser = Vec::new(); let superheader = SuperHeader::::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?; + 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()); diff --git a/src/serialise.rs b/src/serialise.rs index 1e737ee..be17691 100644 --- a/src/serialise.rs +++ b/src/serialise.rs @@ -10,7 +10,72 @@ use crypto::{ Salt, Password, }, + aes::{self, AesKey,}, }; +use once_cell::sync::OnceCell; + +/// Used to lazily generate a salt +pub struct SaltGen +{ + salt: OnceCell, +} + +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 + { + self.salt.into_inner() + } +} + +impl AsRef 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{} @@ -19,7 +84,7 @@ use crypto::{ pub trait TextSerialiseable: Sized { - fn serialise_text<'a, 'b: 'a, T: AsyncWrite+Unpin+?Sized>(&'a self, out: &'b mut T, passwd: Option<&'b Password>) -> LocalBoxFuture<'a, Result>; + fn serialise_text<'a, 'b: 'a, T: AsyncWrite+Unpin+?Sized, F: for<'r> FnOnce(&'r SaltGen) -> Option + 'b>(&'a self, out: &'b mut T, passwd: F) -> LocalBoxFuture<'a, Result>; fn deserialise_text<'a, T: AsyncBufRead+Unpin+?Sized, F: for<'r> FnOnce(&'r Salt) -> Option + 'a>(input: &'a mut T, passwd: F) -> LocalBoxFuture<'a, Result> where Self: 'a; #[inline] fn size_hint(&self) -> (usize, Option) @@ -31,7 +96,7 @@ pub trait TextSerialiseable: Sized pub trait BinarySerialisable: Sized { - fn serialise_bytes<'a, 'b, T: AsyncWrite+Unpin+?Sized>(&'a self, out: &'b mut T, passwd: Option<&'b Password>) -> LocalBoxFuture<'a, Result> + fn serialise_bytes<'a, 'b, T: AsyncWrite+Unpin+?Sized,F: for<'r> FnOnce(&'r SaltGen) -> Option + 'b>(&'a self, out: &'b mut T, passwd: F) -> LocalBoxFuture<'a, Result> where 'b: 'a; fn deserialise_bytes<'b , T: AsyncRead+Unpin+?Sized, F: for<'r> FnOnce(&'r Salt) -> Option + 'b>(input: &'b mut T, passwd: F) -> LocalBoxFuture<'b, Result> where Self: 'b; @@ -51,8 +116,8 @@ 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))] - fn into_memory(&self, mode: Mode, passwd: Option<&Password>) -> Result, eyre::Report> + #[instrument(skip(self, passwd))] + fn into_memory Option>(&self, mode: Mode, passwd: F) -> Result, eyre::Report> { let mut output = match mode.size_hint(self) { @@ -156,12 +221,30 @@ pub fn into_text(value: &T) -> Result 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::()))] +pub async fn into_text_with_key_async(value: &T, k: &AesKey) -> Result +{ + 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 @@ -171,6 +254,8 @@ impl std::error::Error for Error 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), } } } @@ -183,6 +268,8 @@ impl std::fmt::Display for Error 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"), } } } @@ -195,6 +282,19 @@ pub fn from_text(string: impl AsRef<[u8]>) -> Re 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(string: impl AsRef<[u8]>, k: &AesKey) -> Result +{ + 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 {