use std::io::{Cursor, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use crate::Error;
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) struct Opaque<T> {
body: T,
}
impl<T> Opaque<T>
where
T: AsRef<[u8]>,
{
pub(crate) fn from_user_payload(body: T) -> Self {
Self { body }
}
pub(crate) fn into_inner(self) -> T {
self.body
}
pub(crate) fn len(&self) -> usize {
self.body.as_ref().len()
}
pub(crate) fn serialise_into<W: Write>(&self, buf: &mut W) -> Result<(), std::io::Error> {
let len = self.len() as u32;
buf.write_u32::<BigEndian>(len)?;
buf.write_all(self.body.as_ref())?;
let fill_bytes = pad_length(len) as usize;
const PADDING: [u8; 3] = [0; 3];
if fill_bytes > 0 {
buf.write_all(&PADDING[..fill_bytes])?;
}
Ok(())
}
pub(crate) fn serialised_len(&self) -> u32 {
let payload_len: u32 = self.as_ref().len() as u32;
4 + payload_len + pad_length(payload_len)
}
}
impl Opaque<&[u8]> {
pub(crate) fn from_wire<'a>(
c: &mut Cursor<&'a [u8]>,
max_len: usize,
) -> Result<Opaque<&'a [u8]>, Error> {
let payload_len = c.read_u32::<BigEndian>()?;
if payload_len as usize > max_len {
return Err(Error::InvalidLength);
}
let data = *c.get_ref();
let start = c.position() as usize;
let end = start + payload_len as usize;
let end_plus_padding = end + pad_length(payload_len) as usize;
if end_plus_padding > data.len() {
return Err(Error::InvalidLength);
}
let body = &data[start..end];
c.set_position(end_plus_padding as u64);
Ok(Opaque { body })
}
}
impl<T> AsRef<[u8]> for Opaque<T>
where
T: AsRef<[u8]>,
{
fn as_ref(&self) -> &[u8] {
self.body.as_ref()
}
}
#[inline]
pub(crate) fn pad_length(l: u32) -> u32 {
if l.is_multiple_of(4) {
return 0;
}
4 - (l % 4)
}
#[cfg(test)]
mod tests {
use std::io::Cursor;
use hex_literal::hex;
use proptest::prelude::*;
use super::Opaque;
#[test]
fn test_one_padded_opaque() {
let raw = hex!("0000000f4c4150544f502d315151425044474d00").as_slice();
let payload: [u8; 15] = [76, 65, 80, 84, 79, 80, 45, 49, 81, 81, 66, 80, 68, 71, 77];
let mut cursor = Cursor::new(raw);
let data = Opaque::from_wire(&mut cursor, 100).unwrap();
assert_eq!(raw.len(), 20);
assert_eq!(data.as_ref().len(), 15);
assert!(data
.as_ref()
.iter()
.zip(payload.iter())
.all(|(a, b)| a == b));
let mydata = Vec::from(data.body);
let myopaque = Opaque { body: mydata };
let mut buf: Cursor<Vec<u8>> = Cursor::new(Vec::<u8>::new());
myopaque.serialise_into(&mut buf).unwrap();
assert_eq!(buf.get_ref().len(), 20);
assert!(buf.get_ref().iter().zip(raw.iter()).all(|(a, b)| a == b));
}
#[test]
fn test_no_padded_opaque() {
let raw = hex!("0000000c4c4150544f5151425044474d").as_slice();
let payload: [u8; 12] = [76, 65, 80, 84, 79, 81, 81, 66, 80, 68, 71, 77];
let mut cursor = Cursor::new(raw);
let data = Opaque::from_wire(&mut cursor, 100).unwrap();
assert_eq!(raw.len(), 16);
assert_eq!(data.as_ref().len(), 12);
assert!(data
.as_ref()
.iter()
.zip(payload.iter())
.all(|(a, b)| a == b));
let mydata = Vec::from(data.body);
let myopaque = Opaque { body: mydata };
let mut buf: Cursor<Vec<u8>> = Cursor::new(Vec::<u8>::new());
myopaque.serialise_into(&mut buf).unwrap();
assert_eq!(buf.get_ref().len(), 16);
assert!(buf.get_ref().iter().zip(raw.iter()).all(|(a, b)| a == b));
}
#[test]
fn test_max_bytes() {
let payload: [u8; 12] = [255, 65, 80, 84, 79, 81, 81, 66, 80, 68, 71, 77];
let mut cursor = Cursor::new(payload.as_slice());
Opaque::from_wire(&mut cursor, 100).expect_err("should hit max size");
}
proptest! {
#[test]
fn prop_round_trip(
data in prop::collection::vec(any::<u8>(), 0..256),
) {
let mut buf = Vec::new();
Opaque::from_user_payload(data.clone()).serialise_into(&mut buf).unwrap();
let mut c = Cursor::new(buf.as_slice());
let got = Opaque::from_wire(&mut c, data.len() + 1).unwrap().into_inner();
assert_eq!(data, got);
}
}
}