diff --git a/Cargo.lock b/Cargo.lock index d5919b2..0bca956 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -221,6 +221,7 @@ dependencies = [ "serde", "serde_derive", "sha2", + "tokio", ] [[package]] @@ -242,6 +243,7 @@ dependencies = [ "rand 0.7.3", "regex", "serde", + "serde_cbor", "smallmap", "stack-vec", "tokio", @@ -533,6 +535,12 @@ dependencies = [ "tracing-futures", ] +[[package]] +name = "half" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d36fab90f82edc3c747f9d438e06cf0a491055896f2a279638bb5beed6c40177" + [[package]] name = "hashbrown" version = "0.9.1" @@ -1352,6 +1360,16 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_cbor" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e18acfa2f90e8b735b2836ab8d538de304cbb6729a7360729ea5a895d15a622" +dependencies = [ + "half", + "serde", +] + [[package]] name = "serde_derive" version = "1.0.117" diff --git a/Cargo.toml b/Cargo.toml index 09aa88e..980475e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ base64 = "0.13.0" bitflags = "1.2.1" chrono = "0.4.19" color-eyre = {version = "0.5", default-features=false} -cryptohelpers = { version = "1.6", default-features=false, features = ["sha256", "rsa", "serialise", "aes"] } +cryptohelpers = { version = "1.6", default-features=false, features = ["async", "sha256", "rsa", "serialise", "aes"] } futures = "0.3.8" generational-arena = {version = "0.2.8", features= ["serde"]} getrandom = "0.2.0" @@ -35,6 +35,7 @@ pretty_env_logger = "0.4.0" rand = "0.7.3" regex = "1.4.2" serde = {version = "1.0", features = ["derive"]} +serde_cbor = "0.11.1" smallmap = {version = "1.2", features= ["serde"]} stack-vec = "0.1.0" tokio = {version = "0.2", features = ["full"]} diff --git a/src/main.rs b/src/main.rs index 986853a..f43acc3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,6 +15,7 @@ use color_eyre::{ WrapErr as _, }, SectionExt as _, + Help as _, }; use futures::{ prelude::*, diff --git a/src/server/.#mod.rs b/src/server/.#mod.rs new file mode 120000 index 0000000..87d8e7d --- /dev/null +++ b/src/server/.#mod.rs @@ -0,0 +1 @@ +avril@eientei.39076:1605647320 \ No newline at end of file diff --git a/src/server/data.rs b/src/server/data.rs index a37caaf..98c659b 100644 --- a/src/server/data.rs +++ b/src/server/data.rs @@ -1,8 +1,10 @@ //! Data structures for the in-memory map. use super::*; - use std::{ collections::HashMap, + borrow::Cow, + error, + fmt, }; use generational_arena::{ Arena, Index, @@ -13,16 +15,19 @@ use cryptohelpers::{ RsaPublicKey, RsaPrivateKey, Signature, }, - aes::AesKey, + aes::{self, AesKey,}, sha256::Sha256Hash, }; +use tokio::io::{ + AsyncRead, AsyncWrite, +}; /// An absolute (nested) index -#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] pub struct AbsoluteIndex(Vec); /// Possible value types of the data map -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum Data { Byte(u8), @@ -76,13 +81,196 @@ pub enum Data } /// An entry that may or may not be encrypted -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum MaybeEncrypted { + /// This is an encrypted serialized `Data`. Encrypted(Vec), + /// An unencrypted `Data` Unencrypted(Data), } +#[non_exhaustive] +#[derive(Debug)] +enum EncryptedEntryErrorKind +{ + KeyNeeded, + Decrypt(aes::Error), + Deserialize(serde_cbor::Error), + + Encrypt(aes::Error), + Serialize(serde_cbor::Error), +} + +/// Error type returned when performing encryption/decryption operations on `Data` through `MaybeEncrypted`. +#[non_exhaustive] +#[derive(Debug)] +pub struct EncryptedEntryError(Box); + +impl EncryptedEntryError +{ + /// Consume into a nicely readable `eyre::Report` + pub fn report(self) -> eyre::Report + { + let (whe, sug) = match self.0.as_ref() { + EncryptedEntryErrorKind::Serialize(_) => ("Object serialisation", "Bad data?"), + EncryptedEntryErrorKind::Deserialize(_) => ("Object deserialisation", "Corrupted data?"), + EncryptedEntryErrorKind::Decrypt(_) => ("Data decryption", "Bad key?"), + EncryptedEntryErrorKind::Encrypt(_) => ("Data encryption" ,"Bad key?"), + EncryptedEntryErrorKind::KeyNeeded => return eyre::Report::from(self) + .with_suggestion(|| "Try providing a key"), + }; + + Err::(self) + .with_section(|| whe.header("In operation")) + .with_warning(|| sug) + .unwrap_err() + } +} + +impl error::Error for EncryptedEntryError +{ + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + Some(match self.0.as_ref() { + EncryptedEntryErrorKind::Decrypt(d) => d, + EncryptedEntryErrorKind::Deserialize(d) => d, + EncryptedEntryErrorKind::Encrypt(d) => d, + EncryptedEntryErrorKind::Serialize(d) => d, + _ => return None, + }) + } +} + +impl fmt::Display for EncryptedEntryError +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + match self.0.as_ref() { + EncryptedEntryErrorKind::KeyNeeded => write!(f, "this entry needed a decryption key but none was provided"), + EncryptedEntryErrorKind::Decrypt(_) => write!(f, "decryption failed"), + EncryptedEntryErrorKind::Deserialize(_) => write!(f, "deserialisation failed"), + EncryptedEntryErrorKind::Encrypt(_) => write!(f, "encryption failed"), + EncryptedEntryErrorKind::Serialize(_) => write!(f, "serialisation failed"), + } + } +} + +impl From for EncryptedEntryError +{ + #[inline] fn from(from: EncryptedEntryErrorKind) -> Self + { + Self(Box::new(from)) + } +} + + +/// Encrypt a `Data` entry into this buffer +async fn encrypt_data_entry(output: &mut T, data: &Data, key: &AesKey) -> Result +where T: ?Sized + AsyncWrite + Unpin +{ + let bytes = serde_cbor::to_vec(data).map_err(EncryptedEntryErrorKind::Serialize)?; + Ok(aes::encrypt_stream(key, &mut &bytes[..], output).await.map_err(EncryptedEntryErrorKind::Encrypt)?) +} + +/// Decrypt an encrypted `Data` entry +async fn decrypt_data_entry(input: &mut T, key: &AesKey, init_buf: Option>) -> Result +where T: AsyncRead + Unpin +{ + let mut output = init_buf.unwrap_or_default(); + aes::decrypt_stream(key, input, &mut output).await.map_err(EncryptedEntryErrorKind::Decrypt)?; + Ok(serde_cbor::from_slice(&output[..]).map_err(EncryptedEntryErrorKind::Deserialize)?) +} + +impl MaybeEncrypted +{ + /// Create a new non-encrypted entry + #[inline(always)] pub const fn new_raw(data: Data) -> Self + { + Self::Unencrypted(data) + } + /// Create a new encrypted entry + #[inline] pub fn new_encrypted(data: Data, encrypt: &AesKey) -> Result + { + let mut buffer = Vec::new(); + encrypt_data_entry(&mut buffer, &data, encrypt).now_or_never().unwrap()?; + Ok(Self::Encrypted(buffer)) + } + + /// Is this data entry encrypted + #[inline] pub fn is_encrypted(&self) -> bool + { + if let MaybeEncrypted::Encrypted(_) = &self { + true + } else { + false + } + } + + /// Consume into a decrypted data instance + #[inline] pub fn into_unencrypted(self, key: Option<&AesKey>) -> Result + { + Ok(Self::Unencrypted(self.into_data(key)?)) + } + + + /// Make this entry unencrypted + pub fn make_unencrypted(&mut self, key: Option<&AesKey>) -> Result<(), EncryptedEntryError> + { + match self { + MaybeEncrypted::Encrypted(bytes) => { + *self = MaybeEncrypted::Unencrypted(decrypt_data_entry(&mut &bytes[..], key.ok_or(EncryptedEntryErrorKind::KeyNeeded)?, Some(Vec::with_capacity(bytes.len()))).now_or_never().unwrap()?); + }, + _ => (), + } + Ok(()) + } + + /// Make this entry encrypted + pub fn make_encrypted(&mut self, key: Option<&AesKey>) -> Result<(), EncryptedEntryError> + { + match self { + MaybeEncrypted::Unencrypted(data) => { + let mut buffer = Vec::new(); + encrypt_data_entry(&mut buffer, data, key.ok_or(EncryptedEntryErrorKind::KeyNeeded)?).now_or_never().unwrap()?; + *self = Self::Encrypted(buffer); + }, + _ => (), + } + Ok(()) + } + /// Consume into an encrypted data instance + pub fn into_encrypted(self, key: Option<&AesKey>) -> Result + { + match self { + MaybeEncrypted::Unencrypted(data) => Self::new_encrypted(data, key.ok_or(EncryptedEntryErrorKind::KeyNeeded)?), + _ => Ok(self), + } + } + + /// Attempt to get the `Data`, decrypting it if needed. + pub fn get_data<'a>(&'a self, key: Option<&AesKey>) -> Result, EncryptedEntryError> + { + Ok(match self { + Self::Unencrypted(data) => Cow::Borrowed(&data), + Self::Encrypted(bytes) => { + // decrypt + Cow::Owned(decrypt_data_entry(&mut &bytes[..], key.ok_or(EncryptedEntryErrorKind::KeyNeeded)?, Some(Vec::with_capacity(bytes.len()))).now_or_never().unwrap()?) + }, + }) + } + + /// Consume into the data object, decrypting it if needed. + pub fn into_data(self, key: Option<&AesKey>) -> Result + { + Ok(match self { + Self::Encrypted(bytes) => { + decrypt_data_entry(&mut &bytes[..], key.ok_or(EncryptedEntryErrorKind::KeyNeeded)?, Some(Vec::with_capacity(bytes.len()))).now_or_never().unwrap()? + }, + Self::Unencrypted(data) => data, + }) + } +} + bitflags! { /// And additional metadata for values #[derive(Serialize, Deserialize)] @@ -102,7 +290,7 @@ bitflags! { } /// Information about a map entry. -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct Info { alt_name: Option<(String, String)>, @@ -127,17 +315,33 @@ pub struct Info } /// The root data containing map -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Datamap { data: Arena, ident: HashMap, } +impl PartialEq for Datamap +{ + fn eq(&self, other: &Self) -> bool + { + self.ident == other.ident && { + for (v1, v2) in self.ident.iter().map(|(_, v)| (self.data.get(*v), other.data.get(*v))) + { + if v1 != v2 { + return false; + } + } + true + } + } +} + /// A value in a datamap, contains the information about the value and the value itself. /// /// May also contain previous versions of this atom -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct Atom(MaybeEncrypted, Info, Vec); /// An identifier for an item in a `Datamap`, or an item nested within many `Datamap`s. diff --git a/src/server/event.rs b/src/server/event.rs index 13b8fbe..d966d5d 100644 --- a/src/server/event.rs +++ b/src/server/event.rs @@ -3,7 +3,7 @@ use super::*; use bitflags::bitflags; /// What happened in the in-band event -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub enum InBandEventKind { Created, @@ -21,7 +21,7 @@ pub enum InBandEventKind /// /// # Note /// Not to be confused with `HookEvent`s -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct InBandEvent { who: Option, @@ -71,7 +71,7 @@ impl Default for HookMask id_type!(pub HookID: "The ID of a hook, passed with the event when the hook is fired"); /// Fire events when something happens to this value. -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] pub struct Hooks { filter: HookMask, diff --git a/src/server/mod.rs b/src/server/mod.rs index bdb8213..0368c13 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,5 +1,6 @@ use super::*; +/// Config used to create `Settings` #[derive(Debug, PartialEq, Eq, Clone)] pub struct Config { @@ -11,7 +12,5 @@ mod user; mod data; mod state; - #[cfg(feature="server-http")] pub mod web; #[cfg(feature="server-tcp")] pub mod tcp; - diff --git a/src/server/user.rs b/src/server/user.rs index 8801fae..78ec710 100644 --- a/src/server/user.rs +++ b/src/server/user.rs @@ -347,7 +347,7 @@ impl Default for Permission /// A set of permissions informations for users and/or groups -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Permissions { blanket: Permission,