#![allow(unsafe_op_in_unsafe_fn)]
use crate::error::{Error, check, check_ssize};
use crate::ffi;
use crate::types::{LOG_ENTRY_MAX_SIZE, MERKLE_PROOF_MAX_DEPTH, MerkleHash, NodeId};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MerkleEntryType {
KeyBinding = 1,
KeyRotation = 2,
Revocation = 3,
AnchorChange = 4,
}
impl From<ffi::nwep_merkle_entry_type> for MerkleEntryType {
fn from(t: ffi::nwep_merkle_entry_type) -> Self {
match t {
ffi::nwep_merkle_entry_type_NWEP_LOG_ENTRY_KEY_BINDING => MerkleEntryType::KeyBinding,
ffi::nwep_merkle_entry_type_NWEP_LOG_ENTRY_KEY_ROTATION => MerkleEntryType::KeyRotation,
ffi::nwep_merkle_entry_type_NWEP_LOG_ENTRY_REVOCATION => MerkleEntryType::Revocation,
ffi::nwep_merkle_entry_type_NWEP_LOG_ENTRY_ANCHOR_CHANGE => {
MerkleEntryType::AnchorChange
}
_ => MerkleEntryType::KeyBinding,
}
}
}
impl MerkleEntryType {
fn to_ffi(&self) -> ffi::nwep_merkle_entry_type {
match self {
MerkleEntryType::KeyBinding => ffi::nwep_merkle_entry_type_NWEP_LOG_ENTRY_KEY_BINDING,
MerkleEntryType::KeyRotation => ffi::nwep_merkle_entry_type_NWEP_LOG_ENTRY_KEY_ROTATION,
MerkleEntryType::Revocation => ffi::nwep_merkle_entry_type_NWEP_LOG_ENTRY_REVOCATION,
MerkleEntryType::AnchorChange => {
ffi::nwep_merkle_entry_type_NWEP_LOG_ENTRY_ANCHOR_CHANGE
}
}
}
}
#[derive(Clone, Debug)]
pub struct MerkleEntry {
pub entry_type: MerkleEntryType,
pub timestamp: crate::Tstamp,
pub node_id: NodeId,
pub pubkey: [u8; 32],
pub prev_pubkey: [u8; 32],
pub recovery_pubkey: [u8; 32],
pub signature: [u8; 64],
}
impl MerkleEntry {
pub(crate) fn to_ffi(&self) -> ffi::nwep_merkle_entry {
ffi::nwep_merkle_entry {
type_: self.entry_type.to_ffi(),
timestamp: self.timestamp,
nodeid: ffi::nwep_nodeid {
data: self.node_id.0,
},
pubkey: self.pubkey,
prev_pubkey: self.prev_pubkey,
recovery_pubkey: self.recovery_pubkey,
signature: self.signature,
}
}
pub(crate) fn from_ffi(e: &ffi::nwep_merkle_entry) -> Self {
MerkleEntry {
entry_type: MerkleEntryType::from(e.type_),
timestamp: e.timestamp,
node_id: NodeId(e.nodeid.data),
pubkey: e.pubkey,
prev_pubkey: e.prev_pubkey,
recovery_pubkey: e.recovery_pubkey,
signature: e.signature,
}
}
pub fn encode(&self) -> Result<Vec<u8>, Error> {
let mut buf = vec![0u8; LOG_ENTRY_MAX_SIZE];
let ffi_entry = self.to_ffi();
let n = check_ssize(unsafe {
ffi::nwep_merkle_entry_encode(buf.as_mut_ptr(), buf.len(), &ffi_entry)
})?;
buf.truncate(n);
Ok(buf)
}
pub fn decode(data: &[u8]) -> Result<Self, Error> {
let mut entry = unsafe { std::mem::zeroed::<ffi::nwep_merkle_entry>() };
check(unsafe { ffi::nwep_merkle_entry_decode(&mut entry, data.as_ptr(), data.len()) })?;
Ok(MerkleEntry::from_ffi(&entry))
}
pub fn leaf_hash(&self) -> Result<MerkleHash, Error> {
let mut hash = ffi::nwep_merkle_hash { data: [0u8; 32] };
let ffi_entry = self.to_ffi();
check(unsafe { ffi::nwep_merkle_leaf_hash(&mut hash, &ffi_entry) })?;
Ok(MerkleHash(hash.data))
}
}
pub fn merkle_node_hash(left: &MerkleHash, right: &MerkleHash) -> Result<MerkleHash, Error> {
let mut hash = ffi::nwep_merkle_hash { data: [0u8; 32] };
let l = ffi::nwep_merkle_hash { data: left.0 };
let r = ffi::nwep_merkle_hash { data: right.0 };
check(unsafe { ffi::nwep_merkle_node_hash(&mut hash, &l, &r) })?;
Ok(MerkleHash(hash.data))
}
#[derive(Clone, Debug)]
pub struct MerkleProof {
pub index: u64,
pub log_size: u64,
pub leaf_hash: MerkleHash,
pub siblings: Vec<MerkleHash>,
}
impl MerkleProof {
pub(crate) fn to_ffi(&self) -> ffi::nwep_merkle_proof {
let mut proof = unsafe { std::mem::zeroed::<ffi::nwep_merkle_proof>() };
proof.index = self.index;
proof.log_size = self.log_size;
proof.leaf_hash = ffi::nwep_merkle_hash {
data: self.leaf_hash.0,
};
proof.depth = self.siblings.len().min(MERKLE_PROOF_MAX_DEPTH);
for (i, s) in self
.siblings
.iter()
.take(MERKLE_PROOF_MAX_DEPTH)
.enumerate()
{
proof.siblings[i] = ffi::nwep_merkle_hash { data: s.0 };
}
proof
}
pub(crate) fn from_ffi(p: &ffi::nwep_merkle_proof) -> Self {
let siblings = (0..p.depth)
.map(|i| MerkleHash(p.siblings[i].data))
.collect();
MerkleProof {
index: p.index,
log_size: p.log_size,
leaf_hash: MerkleHash(p.leaf_hash.data),
siblings,
}
}
pub fn verify(&self, root: &MerkleHash) -> Result<(), Error> {
let ffi_proof = self.to_ffi();
let ffi_root = ffi::nwep_merkle_hash { data: root.0 };
check(unsafe { ffi::nwep_merkle_proof_verify(&ffi_proof, &ffi_root) })
}
pub fn encode(&self) -> Result<Vec<u8>, Error> {
let ffi_proof = self.to_ffi();
let max_size = 8 + 8 + 32 + 4 + MERKLE_PROOF_MAX_DEPTH * 32;
let mut buf = vec![0u8; max_size];
let n = check_ssize(unsafe {
ffi::nwep_merkle_proof_encode(buf.as_mut_ptr(), buf.len(), &ffi_proof)
})?;
buf.truncate(n);
Ok(buf)
}
pub fn decode(data: &[u8]) -> Result<Self, Error> {
let mut proof = unsafe { std::mem::zeroed::<ffi::nwep_merkle_proof>() };
check(unsafe { ffi::nwep_merkle_proof_decode(&mut proof, data.as_ptr(), data.len()) })?;
Ok(MerkleProof::from_ffi(&proof))
}
}
pub trait LogStorage: Send + 'static {
fn append(&mut self, index: u64, entry: &[u8]) -> Result<(), Error>;
fn get(&self, index: u64, buf: &mut [u8]) -> Result<usize, Error>;
fn size(&self) -> u64;
}
struct StorageCallbacks {
storage: Box<dyn LogStorage>,
}
unsafe extern "C" fn log_append_cb(
user_data: *mut std::ffi::c_void,
index: u64,
entry: *const u8,
entry_len: usize,
) -> std::ffi::c_int {
let cb = &mut *(user_data as *mut StorageCallbacks);
let data = std::slice::from_raw_parts(entry, entry_len);
match cb.storage.append(index, data) {
Ok(()) => 0,
Err(e) => e.code,
}
}
unsafe extern "C" fn log_get_cb(
user_data: *mut std::ffi::c_void,
index: u64,
buf: *mut u8,
buflen: usize,
) -> ffi::nwep_ssize {
let cb = &mut *(user_data as *mut StorageCallbacks);
let slice = std::slice::from_raw_parts_mut(buf, buflen);
match cb.storage.get(index, slice) {
Ok(n) => n as ffi::nwep_ssize,
Err(e) => e.code as ffi::nwep_ssize,
}
}
unsafe extern "C" fn log_size_cb(user_data: *mut std::ffi::c_void) -> u64 {
let cb = &*(user_data as *mut StorageCallbacks);
cb.storage.size()
}
pub struct MerkleLog {
ptr: *mut ffi::nwep_merkle_log,
_storage: Box<StorageCallbacks>,
}
unsafe impl Send for MerkleLog {}
impl MerkleLog {
pub fn new(storage: impl LogStorage) -> Result<Self, Error> {
let mut cb = Box::new(StorageCallbacks {
storage: Box::new(storage),
});
let ffi_storage = ffi::nwep_log_storage {
append: Some(log_append_cb),
get: Some(log_get_cb),
size: Some(log_size_cb),
user_data: cb.as_mut() as *mut _ as *mut std::ffi::c_void,
};
let mut ptr: *mut ffi::nwep_merkle_log = std::ptr::null_mut();
check(unsafe { ffi::nwep_merkle_log_new(&mut ptr, &ffi_storage) })?;
Ok(MerkleLog { ptr, _storage: cb })
}
pub fn append(&mut self, entry: &MerkleEntry) -> Result<u64, Error> {
let ffi_entry = entry.to_ffi();
let mut index = 0u64;
check(unsafe { ffi::nwep_merkle_log_append(self.ptr, &ffi_entry, &mut index) })?;
Ok(index)
}
pub fn get(&mut self, index: u64) -> Result<MerkleEntry, Error> {
let mut entry = unsafe { std::mem::zeroed::<ffi::nwep_merkle_entry>() };
check(unsafe { ffi::nwep_merkle_log_get(self.ptr, index, &mut entry) })?;
Ok(MerkleEntry::from_ffi(&entry))
}
pub fn size(&self) -> u64 {
unsafe { ffi::nwep_merkle_log_size(self.ptr) }
}
pub fn root(&mut self) -> Result<MerkleHash, Error> {
let mut hash = ffi::nwep_merkle_hash { data: [0u8; 32] };
check(unsafe { ffi::nwep_merkle_log_root(self.ptr, &mut hash) })?;
Ok(MerkleHash(hash.data))
}
pub fn prove(&mut self, index: u64) -> Result<MerkleProof, Error> {
let mut proof = unsafe { std::mem::zeroed::<ffi::nwep_merkle_proof>() };
check(unsafe { ffi::nwep_merkle_log_prove(self.ptr, index, &mut proof) })?;
Ok(MerkleProof::from_ffi(&proof))
}
pub(crate) fn as_ptr(&mut self) -> *mut ffi::nwep_merkle_log {
self.ptr
}
}
impl Drop for MerkleLog {
fn drop(&mut self) {
if !self.ptr.is_null() {
unsafe { ffi::nwep_merkle_log_free(self.ptr) }
}
}
}