use crate::error::{Error, Result};
use crate::{
compress::CompressType,
de::FogDeserializer,
document::Document,
element::{serialize_elem, Element},
ser::FogSerializer,
MAX_ENTRY_SIZE,
};
use byteorder::{LittleEndian, ReadBytesExt};
use fog_crypto::{
hash::{Hash, HashState},
identity::{Identity, IdentityKey},
};
use serde::{Deserialize, Serialize};
use std::convert::TryFrom;
pub(crate) const ENTRY_PREFIX_LEN: usize = 3;
pub(crate) struct SplitEntry<'a> {
pub compress_raw: u8,
pub data: &'a [u8],
pub signature_raw: &'a [u8],
}
impl<'a> SplitEntry<'a> {
pub(crate) fn split(buf: &'a [u8]) -> Result<SplitEntry> {
let (&compress_raw, mut buf) = buf.split_first().ok_or(Error::LengthTooShort {
step: "get compress type",
actual: 0,
expected: 1,
})?;
let data_len = buf
.read_u16::<LittleEndian>()
.map_err(|_| Error::LengthTooShort {
step: "get data length",
actual: buf.len(),
expected: 2,
})? as usize;
if data_len > buf.len() {
return Err(Error::LengthTooShort {
step: "get document data",
actual: buf.len(),
expected: data_len,
});
}
let (data, signature_raw) = buf.split_at(data_len);
Ok(Self {
compress_raw,
data,
signature_raw,
})
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct EntryRef {
pub parent: Hash,
pub key: String,
pub hash: Hash,
}
impl std::fmt::Display for EntryRef {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}-{}-{}", self.parent, self.key, self.hash)
}
}
#[derive(Clone, Debug)]
struct EntryInner {
buf: Vec<u8>,
hash_state: Option<HashState>,
id: EntryRef,
schema_hash: Hash,
signer: Option<Identity>,
set_compress: Option<Option<u8>>,
}
impl EntryInner {
fn data(&self) -> &[u8] {
SplitEntry::split(&self.buf).unwrap().data
}
fn parent(&self) -> &Hash {
&self.id.parent
}
fn schema_hash(&self) -> &Hash {
&self.schema_hash
}
fn key(&self) -> &str {
&self.id.key
}
fn signer(&self) -> Option<&Identity> {
self.signer.as_ref()
}
fn hash(&self) -> &Hash {
&self.id.hash
}
fn reference(&self) -> &EntryRef {
&self.id
}
fn deserialize<'de, D: Deserialize<'de>>(&'de self) -> Result<D> {
let buf = self.data();
let mut de = FogDeserializer::new(buf);
D::deserialize(&mut de)
}
fn compression(&mut self, setting: Option<u8>) -> &mut Self {
self.set_compress = Some(setting);
self
}
fn setup_hash_state(parent_hash: Hash, key: &str, data: &[u8]) -> HashState {
let mut hash_state = HashState::new();
let mut prefix = Vec::new();
serialize_elem(&mut prefix, Element::Hash(parent_hash));
serialize_elem(&mut prefix, Element::Str(key));
hash_state.update(&prefix);
hash_state.update(data);
hash_state
}
fn sign(mut self, key: &IdentityKey) -> Result<Self> {
let pre_sign_len = if self.signer.is_some() {
let split = SplitEntry::split(&self.buf).unwrap();
let new_len = split.data.len() + ENTRY_PREFIX_LEN;
self.hash_state = Some(Self::setup_hash_state(
self.id.parent.clone(),
&self.id.key,
split.data,
));
new_len
} else {
self.buf.len()
};
if self.hash_state.is_none() {
let split = SplitEntry::split(&self.buf).unwrap();
let state = Self::setup_hash_state(self.id.parent.clone(), &self.id.key, split.data);
self.hash_state = Some(state);
}
let hash_state = self.hash_state.as_mut().unwrap();
let entry_hash = hash_state.hash();
let signature = key.sign(&entry_hash);
let new_len = pre_sign_len + signature.size();
if new_len > MAX_ENTRY_SIZE {
return Err(Error::LengthTooLong {
max: MAX_ENTRY_SIZE,
actual: self.buf.len(),
});
}
self.buf.resize(pre_sign_len, 0);
signature.encode_vec(&mut self.buf);
hash_state.update(&self.buf[pre_sign_len..]);
self.id.hash = hash_state.hash();
self.signer = Some(key.id().clone());
Ok(self)
}
fn complete(self) -> (EntryRef, Vec<u8>, Option<Option<u8>>) {
(self.id, self.buf, self.set_compress)
}
}
#[derive(Clone, Debug)]
pub struct NewEntry(EntryInner);
impl NewEntry {
fn new_from<F>(key: &str, parent: &Document, encoder: F) -> Result<Self>
where
F: FnOnce(Vec<u8>) -> Result<Vec<u8>>,
{
let buf: Vec<u8> = vec![CompressType::None.into(), 0u8, 0u8];
let mut buf = encoder(buf)?;
if buf.len() > MAX_ENTRY_SIZE {
return Err(Error::LengthTooLong {
max: MAX_ENTRY_SIZE,
actual: buf.len(),
});
}
let data_len = (buf.len() - ENTRY_PREFIX_LEN).to_le_bytes();
buf[1] = data_len[0];
buf[2] = data_len[1];
let hash_state =
EntryInner::setup_hash_state(parent.hash().clone(), key, &buf[ENTRY_PREFIX_LEN..]);
let this_hash = hash_state.hash();
let schema_hash = match parent.schema_hash() {
Some(h) => h.clone(),
None => {
return Err(Error::FailValidate(
"Entries can only be created for documents that use a schema.".into(),
))
}
};
Ok(Self(EntryInner {
buf,
hash_state: Some(hash_state),
id: EntryRef {
parent: parent.hash().clone(),
key: key.to_owned(),
hash: this_hash,
},
schema_hash,
signer: None,
set_compress: None,
}))
}
pub fn new<S: Serialize>(key: &str, parent: &Document, data: S) -> Result<Self> {
Self::new_from(key, parent, |buf| {
let mut ser = FogSerializer::from_vec(buf, false);
data.serialize(&mut ser)?;
Ok(ser.finish())
})
}
pub fn new_ordered<S: Serialize>(data: S, key: &str, parent: &Document) -> Result<Self> {
Self::new_from(key, parent, |buf| {
let mut ser = FogSerializer::from_vec(buf, true);
data.serialize(&mut ser)?;
Ok(ser.finish())
})
}
pub fn compression(mut self, setting: Option<u8>) -> Self {
self.0.compression(setting);
self
}
pub fn sign(self, key: &IdentityKey) -> Result<Self> {
Ok(Self(self.0.sign(key)?))
}
pub fn hash(&self) -> &Hash {
self.0.hash()
}
pub(crate) fn data(&self) -> &[u8] {
self.0.data()
}
pub fn parent(&self) -> &Hash {
self.0.parent()
}
pub fn schema_hash(&self) -> &Hash {
self.0.schema_hash()
}
pub fn key(&self) -> &str {
self.0.key()
}
pub fn reference(&self) -> &EntryRef {
self.0.reference()
}
}
#[derive(Clone, Debug)]
pub struct Entry(EntryInner);
impl Entry {
pub(crate) fn from_new(entry: NewEntry) -> Entry {
Self(entry.0)
}
pub(crate) fn trusted_new(
buf: Vec<u8>,
key: &str,
parent: &Document,
entry: &Hash,
) -> Result<Self> {
if buf.len() > MAX_ENTRY_SIZE {
return Err(Error::LengthTooLong {
max: MAX_ENTRY_SIZE,
actual: buf.len(),
});
}
let split = SplitEntry::split(&buf)?;
let signer = if !split.signature_raw.is_empty() {
let unverified =
fog_crypto::identity::UnverifiedSignature::try_from(split.signature_raw)?;
Some(unverified.signer().clone())
} else {
None
};
let schema_hash = match parent.schema_hash() {
Some(h) => h.clone(),
None => {
return Err(Error::FailValidate(
"Entries can only be created for documents that use a schema.".into(),
))
}
};
Ok(Self(EntryInner {
buf,
hash_state: None,
id: EntryRef {
parent: parent.hash().to_owned(),
key: key.to_owned(),
hash: entry.to_owned(),
},
schema_hash,
signer,
set_compress: None,
}))
}
pub(crate) fn new(buf: Vec<u8>, key: &str, parent: &Document) -> Result<Self> {
if buf.len() > MAX_ENTRY_SIZE {
return Err(Error::LengthTooLong {
max: MAX_ENTRY_SIZE,
actual: buf.len(),
});
}
let split = SplitEntry::split(&buf)?;
let mut hash_state = EntryInner::setup_hash_state(parent.hash().clone(), key, split.data);
let entry_hash = hash_state.hash();
if !split.signature_raw.is_empty() {
hash_state.update(split.signature_raw);
}
let this_hash = hash_state.hash();
let signer = if !split.signature_raw.is_empty() {
let unverified =
fog_crypto::identity::UnverifiedSignature::try_from(split.signature_raw)?;
let verified = unverified.verify(&entry_hash)?;
Some(verified.signer().clone())
} else {
None
};
let schema_hash = match parent.schema_hash() {
Some(h) => h.clone(),
None => {
return Err(Error::FailValidate(
"Entries can only be created for documents that use a schema.".into(),
))
}
};
Ok(Self(EntryInner {
buf,
hash_state: Some(hash_state),
id: EntryRef {
parent: parent.hash().to_owned(),
key: key.to_owned(),
hash: this_hash,
},
schema_hash,
signer,
set_compress: None,
}))
}
pub(crate) fn data(&self) -> &[u8] {
self.0.data()
}
pub fn find_hashes(&self) -> Vec<Hash> {
crate::find_hashes(self.data())
}
pub fn parent(&self) -> &Hash {
self.0.parent()
}
pub fn schema_hash(&self) -> &Hash {
self.0.schema_hash()
}
pub fn key(&self) -> &str {
self.0.key()
}
pub fn reference(&self) -> &EntryRef {
self.0.reference()
}
pub fn signer(&self) -> Option<&Identity> {
self.0.signer()
}
pub fn hash(&self) -> &Hash {
self.0.hash()
}
pub fn deserialize<'de, D: Deserialize<'de>>(&'de self) -> Result<D> {
self.0.deserialize()
}
pub fn compression(mut self, setting: Option<u8>) -> Self {
self.0.compression(setting);
self
}
pub fn sign(self, key: &IdentityKey) -> Result<Self> {
Ok(Self(self.0.sign(key)?))
}
pub(crate) fn complete(self) -> (EntryRef, Vec<u8>, Option<Option<u8>>) {
self.0.complete()
}
}