use crate::repository::{ChunkID, Key, HMAC};
use chrono::prelude::*;
use rand::prelude::*;
use rmp_serde as rmps;
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize, Hash)]
pub struct ManifestID([u8; 32]);
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
pub struct ManifestTransaction {
previous_heads: Vec<ManifestID>,
pointer: ChunkID,
timestamp: DateTime<FixedOffset>,
name: String,
nonce: [u8; 16],
hmac: HMAC,
tag: ManifestID,
}
impl ManifestTransaction {
pub fn new(
previous_heads: &[ManifestID],
pointer: ChunkID,
timestamp: DateTime<FixedOffset>,
name: &str,
hmac: HMAC,
key: &Key,
) -> ManifestTransaction {
let mut nonce = [0_u8; 16];
rand::thread_rng().fill_bytes(&mut nonce);
let mut tx = ManifestTransaction {
previous_heads: previous_heads.to_vec(),
pointer,
timestamp,
name: name.to_string(),
nonce,
hmac,
tag: ManifestID([0_u8; 32]),
};
tx.update_tag(key);
tx
}
fn update_tag(&mut self, key: &Key) {
self.tag.0 = [0_u8; 32];
let bytes = rmps::encode::to_vec(self).expect("Serialization in hmac failed");
let tag = self.hmac.mac(&bytes[..], key);
self.tag.0.copy_from_slice(&tag[..32]);
}
pub fn previous_heads(&self) -> &[ManifestID] {
&self.previous_heads[..]
}
pub fn pointer(&self) -> ChunkID {
self.pointer
}
pub fn name(&self) -> &str {
&self.name
}
pub fn timestamp(&self) -> DateTime<FixedOffset> {
self.timestamp
}
pub fn tag(&self) -> ManifestID {
self.tag
}
pub fn verify(&self, key: &Key) -> bool {
let tag = &self.tag;
let mut copy = self.clone();
copy.update_tag(key);
tag == ©.tag
}
}
#[cfg(test)]
mod tests {
use super::*;
fn create_tx(name: &str, key: &Key) -> ManifestTransaction {
let hmac = HMAC::Blake2b;
let pointer = ChunkID::new(&[1_u8; 32]);
let timestamp = Local::now().with_timezone(Local::now().offset());
ManifestTransaction::new(&[], pointer, timestamp, name, hmac, key)
}
#[test]
fn create_and_verify() {
let key = Key::random(32);
let tx = create_tx("test", &key);
assert!(tx.verify(&key));
}
#[test]
#[should_panic]
fn modify_verify() {
let key = Key::random(32);
let mut tx = create_tx("test", &key);
tx.previous_heads = vec![ManifestID([2_u8; 32])];
assert!(tx.verify(&key));
}
#[test]
#[should_panic]
fn verify_wrong_key() {
let key = Key::random(32);
let tx = create_tx("test", &key);
let bad_key = Key::random(32);
assert!(tx.verify(&bad_key));
}
#[test]
fn serialize_deserialize() {
let key = Key::random(32);
let tx = create_tx("test", &key);
let bytes = rmps::encode::to_vec(&tx).unwrap();
let output_tx: ManifestTransaction = rmps::decode::from_slice(&bytes[..]).unwrap();
assert!(output_tx.verify(&key));
}
}