use crate::error::{JournalError, Result};
use crate::file::ObjectType;
use crate::fss::{
RECOMMENDED_SECPAR, RECOMMENDED_SEEDLEN, evolve, gen_mk, gen_state0, get_epoch, get_key,
};
use hmac::{Hmac, Mac};
use sha2::Sha256;
pub const TAG_LENGTH: usize = 256 / 8;
type HmacSha256 = Hmac<Sha256>;
#[derive(Debug, Clone)]
pub struct SealOptions {
pub seed: [u8; RECOMMENDED_SEEDLEN],
pub interval_usec: u64,
pub start_usec: u64,
}
impl SealOptions {
pub fn new(seed: [u8; RECOMMENDED_SEEDLEN], interval_usec: u64, start_usec: u64) -> Self {
Self {
seed,
interval_usec,
start_usec,
}
}
}
pub struct SealState {
fsprg_state: Vec<u8>,
#[allow(dead_code)]
msk: Vec<u8>,
#[allow(dead_code)]
seed: [u8; RECOMMENDED_SEEDLEN],
interval: u64,
start: u64,
hmac: Option<HmacSha256>,
hmac_running: bool,
}
impl SealState {
pub fn new(opts: &SealOptions) -> Result<Self> {
if opts.interval_usec == 0 || opts.start_usec < opts.interval_usec {
return Err(JournalError::FssVerificationError);
}
let start = (opts.start_usec / opts.interval_usec) * opts.interval_usec;
let (msk, mpk) = gen_mk(&opts.seed, RECOMMENDED_SECPAR);
let state0 = gen_state0(&mpk, &opts.seed);
Ok(Self {
fsprg_state: state0,
msk,
seed: opts.seed,
interval: opts.interval_usec,
start,
hmac: None,
hmac_running: false,
})
}
pub fn epoch(&self) -> u64 {
get_epoch(&self.fsprg_state)
}
pub fn goal_epoch(&self, realtime: u64) -> Result<u64> {
if self.start == 0 || self.interval == 0 {
return Err(JournalError::FssVerificationError);
}
if realtime < self.start {
return Err(JournalError::FssVerificationError);
}
Ok((realtime - self.start) / self.interval)
}
pub fn need_evolve(&self, realtime: u64) -> Result<bool> {
let goal = self.goal_epoch(realtime)?;
let epoch = self.epoch();
if epoch > goal {
return Err(JournalError::FssVerificationError);
}
Ok(epoch != goal)
}
pub fn hmac_start(&mut self) {
if self.hmac_running {
return;
}
let key = get_key(&self.fsprg_state, TAG_LENGTH, 0);
self.hmac = Some(HmacSha256::new_from_slice(&key).expect("HMAC accepts any key length"));
self.hmac_running = true;
}
pub fn hmac_write(&mut self, data: &[u8]) {
self.hmac_start();
if let Some(ref mut hmac) = self.hmac {
hmac.update(data);
}
}
pub fn hmac_reset(&mut self) {
self.hmac_running = false;
self.hmac = None;
}
pub fn hmac_finalize(&mut self) -> [u8; TAG_LENGTH] {
let result = self
.hmac
.take()
.expect("hmac_finalize called without active HMAC")
.finalize()
.into_bytes();
self.hmac_running = false;
let mut out = [0u8; TAG_LENGTH];
out.copy_from_slice(&result);
out
}
pub fn hmac_put_header_ranges(&mut self, header_bytes: &[u8]) {
self.hmac_start();
self.hmac_write(&header_bytes[0..16]);
self.hmac_write(&header_bytes[24..56]);
self.hmac_write(&header_bytes[72..96]);
self.hmac_write(&header_bytes[104..136]);
}
pub fn hmac_put_object_bytes(
&mut self,
object_bytes: &[u8],
typ: ObjectType,
object_size: u64,
is_compact: bool,
) {
self.hmac_start();
let object_header_size: u64 = 16;
self.hmac_write(&object_bytes[..object_header_size as usize]);
match typ {
ObjectType::Data => {
let hash_offset = object_header_size as usize;
self.hmac_write(&object_bytes[hash_offset..hash_offset + 8]);
let payload_offset: u64 = if is_compact { 72 } else { 64 };
if object_size > payload_offset {
let payload_size = (object_size - payload_offset) as usize;
let start = payload_offset as usize;
if start + payload_size <= object_bytes.len() {
self.hmac_write(&object_bytes[start..start + payload_size]);
}
}
}
ObjectType::Field => {
let hash_offset = object_header_size as usize;
self.hmac_write(&object_bytes[hash_offset..hash_offset + 8]);
let payload_offset: u64 = 40;
if object_size > payload_offset {
let payload_size = (object_size - payload_offset) as usize;
let start = payload_offset as usize;
if start + payload_size <= object_bytes.len() {
self.hmac_write(&object_bytes[start..start + payload_size]);
}
}
}
ObjectType::Entry => {
if object_size > object_header_size {
let rest_size = (object_size - object_header_size) as usize;
let start = object_header_size as usize;
if start + rest_size <= object_bytes.len() {
self.hmac_write(&object_bytes[start..start + rest_size]);
}
}
}
ObjectType::DataHashTable | ObjectType::FieldHashTable | ObjectType::EntryArray => {
}
ObjectType::Tag => {
let seqnum_offset = object_header_size as usize;
self.hmac_write(&object_bytes[seqnum_offset..seqnum_offset + 8]);
self.hmac_write(&object_bytes[seqnum_offset + 8..seqnum_offset + 16]);
}
ObjectType::Unused => {}
}
}
pub fn evolve_state(&mut self) {
self.fsprg_state = evolve(&self.fsprg_state);
}
}