fogtix/crates/fogtix-bytecode/src/pointer.rs
2022-09-28 14:23:05 +02:00

151 lines
4.1 KiB
Rust

use siphasher::sip::SipHasher24 as SipHasher;
/// A signed pointer, the `payload` should never by dereferenced
/// without verifying the pointer first
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C, align(16))]
pub struct Pointer(pub [u8; 16]);
impl From<u128> for Pointer {
#[inline]
fn from(x: u128) -> Self {
Self(x.to_be_bytes())
}
}
impl From<(u64, u64)> for Pointer {
#[inline]
fn from((a, b): (u64, u64)) -> Self {
let mut this = Self([0; 16]);
{
let (a_, b_) = this.0.split_at_mut(8);
a_.copy_from_slice(&a.to_be_bytes());
b_.copy_from_slice(&b.to_be_bytes());
}
this
}
}
impl From<Pointer> for u128 {
#[inline]
fn from(Pointer(x): Pointer) -> u128 {
u128::from_be_bytes(x)
}
}
impl From<Pointer> for (u64, u64) {
#[inline]
fn from(Pointer(x): Pointer) -> (u64, u64) {
let (a, b) = x.split_at(8);
(
u64::from_be_bytes(a.try_into().unwrap()),
u64::from_be_bytes(b.try_into().unwrap()),
)
}
}
impl Pointer {
fn calculate_hmac(payload: u64, key: &u128) -> u64 {
use core::hash::Hasher;
let mut h = SipHasher::new_with_key(&key.to_be_bytes());
h.write_u64(payload & ((1 << 48) - 1));
h.finish()
}
/// SECURITY NOTE: the upper two bytes of `payload` (`origin`) aren't taken
/// into account when calculating the HMAC because they're node-specific
pub fn new_with_key(payload: u64, key: &u128) -> Pointer {
let hmac = Self::calculate_hmac(payload, key);
Self::from((hmac, payload))
}
#[inline]
pub fn verify(&self, key: &u128) -> Option<u64> {
let (hmac, payload) = (*self).into();
if hmac == Self::calculate_hmac(payload, key) {
Some(payload)
} else {
None
}
}
#[inline]
pub fn set_origin(&mut self, origin: u16) {
self.0[8..10].copy_from_slice(&origin.to_be_bytes());
}
#[inline]
pub fn origin(&self) -> u16 {
u16::from_be_bytes(self.0[8..10].try_into().unwrap())
}
}
impl crate::Parse<'_> for Pointer {
type Err = ();
fn parse(inp: &[u8]) -> Result<(&[u8], Self), ()> {
if inp.len() < 16 {
Err(())
} else {
let (this, next) = inp.split_at(16);
Ok((next, Self(this.try_into().unwrap())))
}
}
}
#[cfg(any(test, feature = "std"))]
impl Pointer {
#[inline]
pub fn write_to<W: std::io::Write>(&self, mut writer: W) -> std::io::Result<()> {
writer.write_all(&self.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
proptest::proptest! {
#[test]
fn u128_roundtrip(x in 0..u128::MAX) {
let ptr = Pointer::from(x);
assert_eq!(u128::from(ptr), x);
}
}
#[test]
fn pointer_repr() {
use core::mem;
assert_eq!(mem::size_of::<Pointer>(), 16);
assert_eq!(mem::align_of::<Pointer>(), 16);
}
#[test]
fn pointer_usage() {
let k: u128 = 0x000000000000dead000000000000beef;
let p = Pointer::new_with_key(0xfefe, &k);
// verify that this is the same value on all systems
assert_eq!(p.0[0..8], [42, 115, 131, 215, 127, 235, 147, 241]);
assert_eq!(p.verify(&k), Some(0xfefe));
assert_eq!(p.origin(), 0);
}
#[test]
fn pointer_usage2() {
let k: u128 = 0x000000000000dead000000000000beef;
let p = Pointer::new_with_key(0x0508deadbeeffefe, &k);
// verify that this is the same value on all systems
assert_eq!(p.0[0..8], [191, 23, 107, 0, 61, 74, 249, 219]);
assert_eq!(p.verify(&k), Some(0x0508deadbeeffefe));
assert_eq!(p.origin(), 0x0508);
}
#[test]
fn pointer_usage3() {
let k: u128 = 0x000000000000dead000000000000beef;
let p = Pointer::new_with_key(0xf7d8deadbeeffefe, &k);
// verify that this is the same value on all systems
assert_eq!(p.0[0..8], [191, 23, 107, 0, 61, 74, 249, 219]);
assert_eq!(p.verify(&k), Some(0xf7d8deadbeeffefe));
assert_eq!(p.origin(), 0xf7d8);
}
}