151 lines
4.1 KiB
Rust
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);
|
|
}
|
|
}
|