parent
f324132f18
commit
439663539b
@ -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"))?)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue