//! Data structures for the in-memory map. use super::*; use std::{ collections::HashMap, borrow::Cow, error, fmt, }; use generational_arena::{ Arena, Index, }; use bitflags::bitflags; use cryptohelpers::{ rsa::{ RsaPublicKey, RsaPrivateKey, Signature, }, aes::{self, AesKey,}, sha256::Sha256Hash, }; use tokio::io::{ AsyncRead, AsyncWrite, }; /// An absolute (nested) index #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] pub struct AbsoluteIndex(Vec); /// Possible value types of the data map #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum Data { Byte(u8), Char(char), Bool(bool), /// Signed integer SI(i64), /// Unsigned integer UI(u64), /// Floating-point integer FI(f64), /// A UTF-8 text string Text(String), /// A binary blob Binary(Vec), /// A reference index to an item within the same `Datamap` as this one. RelativeRef(Index), /// A reference to an item N deep within nested `Map` elements. /// /// The first `Index` specifies the `Map` data item at the root `Datamap` that contains the next, et cetera. The pointed to value is the last index. AbsoluteRef(AbsoluteIndex), /// A list of atoms List(Vec), /// Another datamap Map(Datamap), /// An AES key AesKey(AesKey), /// An RSA keypair RsaKeypair(RsaPrivateKey, RsaPublicKey), /// An RSA private key RsaPrivate(RsaPrivateKey), /// An RSA public key RsaPublic(RsaPublicKey), /// A SHA256 hash Hash(Sha256Hash), /// A unique ID Uuid(uuid::Uuid), /// An RSA signature Signature(Signature), /// Nothing Null, } /// An entry that may or may not be encrypted #[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)] struct Tags: u16 { /// Default const NONE = 0; /// This value should be cloned on write unless specified elsewhere. const COW = 1<<0; /// This should not show up in searches. const HIDDEN = 1<<1; /// Versioning is enabled for this value. /// /// When it is deleted, it should instead be moved away into a sperate entry. const VERSIONED = 1<<2; } } /// Information about a map entry. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct Info { alt_name: Option<(String, String)>, created: u64, modified: u64, tags: Tags, /// Events to be emitted to `State` when this element does something. /// /// Can be used for monitoring or logging and such. hooks: event::Hooks, owner: Option>, //starts as the user that created (i.e. same as `created_by`), or `None` if ownership is disabled signed: Option>, perms: user::Permissions, created_by: user::UserID, log: Vec, } /// The root data containing map #[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. /// /// # Note /// `Atom` boxes most of itself. It's not needed to box `Atom` itself. // Box these first two together, since they are hueg. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct Atom(Box<(MaybeEncrypted, Info)>, Vec); /// An identifier for an item in a `Datamap`, or an item nested within many `Datamap`s. #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Serialize, Deserialize)] pub struct Identifier(String); #[cfg(test)] mod tests { #[test] fn data_me_sync() { let data = super::Data::SI(-120); let aes = super::aes::AesKey::generate().unwrap(); println!("Data: {:?}", data); let enc = super::MaybeEncrypted::new_encrypted(data.clone(), &aes).expect("Couldn't encrypt"); println!("M/Enc: {:?}", enc); let dec = enc.clone().into_unencrypted(Some(&aes)).expect("Couldn't decrypt"); println!("M/Dec: {:?}", dec); let out = dec.into_data(Some(&aes)).unwrap(); assert_eq!(data, out); } }