data entry encryption

master
Avril 3 years ago
parent 070c180303
commit 2b5c02af17
Signed by: flanchan
GPG Key ID: 284488987C31F630

18
Cargo.lock generated

@ -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"

@ -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"]}

@ -15,6 +15,7 @@ use color_eyre::{
WrapErr as _,
},
SectionExt as _,
Help as _,
};
use futures::{
prelude::*,

@ -0,0 +1 @@
avril@eientei.39076:1605647320

@ -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<Index>);
/// 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<u8>),
/// 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<EncryptedEntryErrorKind>);
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::<std::convert::Infallible, _>(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<EncryptedEntryErrorKind> for EncryptedEntryError
{
#[inline] fn from(from: EncryptedEntryErrorKind) -> Self
{
Self(Box::new(from))
}
}
/// Encrypt a `Data` entry into this buffer
async fn encrypt_data_entry<T>(output: &mut T, data: &Data, key: &AesKey) -> Result<usize, EncryptedEntryError>
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<T: ?Sized>(input: &mut T, key: &AesKey, init_buf: Option<Vec<u8>>) -> Result<Data, EncryptedEntryError>
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<Self, EncryptedEntryError>
{
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<Self, EncryptedEntryError>
{
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<Self, EncryptedEntryError>
{
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<Cow<'a, Data>, 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<Data, EncryptedEntryError>
{
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<Atom>,
ident: HashMap<Identifier, Index>,
}
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<Atom>);
/// An identifier for an item in a `Datamap`, or an item nested within many `Datamap`s.

@ -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<user::UserID>,
@ -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,

@ -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;

@ -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,

Loading…
Cancel
Save