use crate::*;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::io::{Seek, SeekFrom, Write};
pub const TLS_CERT_ENTRY: &[u8] = &[0, 0, 0, 0, 0, 0, 0, 0x10];
pub const SIGN_ED25519_ENTRY: &[u8] = &[0, 0, 0, 0, 0, 0, 0, 0x20];
#[derive(Debug, PartialEq, Eq)]
pub enum EntryType {
TlsCert,
SignEd25519,
}
pub struct CodecReader<'lt>(std::io::Cursor<&'lt [u8]>);
impl<'lt> CodecReader<'lt> {
pub fn new(data: &'lt [u8]) -> Self {
Self(std::io::Cursor::new(data))
}
pub fn read_pre_padding(&mut self) -> LairResult<()> {
seek_cur(&mut self.0, 4)?;
let rem_len = read_u32(&mut self.0)? - 8;
seek_cur(&mut self.0, rem_len as i64)?;
Ok(())
}
pub fn read_entry_type(&mut self) -> LairResult<EntryType> {
match self.read_bytes(8)? {
TLS_CERT_ENTRY => Ok(EntryType::TlsCert),
SIGN_ED25519_ENTRY => Ok(EntryType::SignEd25519),
_ => Err("invalid entry type bytes".into()),
}
}
pub fn read_u32(&mut self) -> LairResult<u32> {
read_u32(&mut self.0)
}
pub fn read_u64(&mut self) -> LairResult<u64> {
read_u64(&mut self.0)
}
pub fn read_bytes(&mut self, size: u64) -> LairResult<&[u8]> {
let start = self.0.position() as usize;
let end = (self.0.position() + size) as usize;
let slice = &self.0.get_ref()[start..end];
seek_cur(&mut self.0, size as i64)?;
Ok(slice)
}
}
pub struct CodecWriter(std::io::Cursor<Vec<u8>>);
impl CodecWriter {
pub fn new_zeroed(size: usize) -> LairResult<Self> {
let data = vec![0; size];
Ok(Self(std::io::Cursor::new(data)))
}
pub fn new(size: usize) -> LairResult<Self> {
let mut data = vec![0; size];
let sys_rand = ring::rand::SystemRandom::new();
ring::rand::SecureRandom::fill(&sys_rand, &mut data)
.map_err(|e| format!("{:?}", e))?;
Ok(Self(std::io::Cursor::new(data)))
}
pub fn into_vec(self) -> Vec<u8> {
self.0.into_inner()
}
pub fn write_pre_padding(&mut self, size: u32) -> LairResult<()> {
if self.0.position() + size as u64 > self.0.get_ref().len() as u64 {
panic!("pre padding would write beyond end of buffer");
}
seek_cur(&mut self.0, 4)?;
write_u32(&mut self.0, size)?;
seek_cur(&mut self.0, size as i64 - 8)?;
Ok(())
}
pub fn write_entry_type(
&mut self,
entry_type: EntryType,
) -> LairResult<()> {
match entry_type {
EntryType::TlsCert => self.0.write_all(TLS_CERT_ENTRY),
EntryType::SignEd25519 => self.0.write_all(SIGN_ED25519_ENTRY),
}
.map_err(LairError::other)?;
Ok(())
}
pub fn write_u32(&mut self, val: u32) -> LairResult<()> {
write_u32(&mut self.0, val)?;
Ok(())
}
pub fn write_u64(&mut self, val: u64) -> LairResult<()> {
write_u64(&mut self.0, val)?;
Ok(())
}
pub fn write_bytes(&mut self, val: &[u8]) -> LairResult<()> {
self.0.write_all(val).map_err(LairError::other)?;
Ok(())
}
}
fn seek_cur<T>(cur: &mut std::io::Cursor<T>, amnt: i64) -> LairResult<()>
where
T: std::convert::AsRef<[u8]>,
{
cur.seek(SeekFrom::Current(amnt))
.map_err(LairError::other)?;
Ok(())
}
fn write_u32<W>(mut writer: W, val: u32) -> LairResult<()>
where
W: std::io::Write,
{
writer
.write_u32::<LittleEndian>(val)
.map_err(LairError::other)?;
Ok(())
}
fn read_u32<R>(mut reader: R) -> LairResult<u32>
where
R: std::io::Read,
{
reader.read_u32::<LittleEndian>().map_err(LairError::other)
}
fn write_u64<W>(mut writer: W, val: u64) -> LairResult<()>
where
W: std::io::Write,
{
writer
.write_u64::<LittleEndian>(val)
.map_err(LairError::other)?;
Ok(())
}
fn read_u64<R>(mut reader: R) -> LairResult<u64>
where
R: std::io::Read,
{
reader.read_u64::<LittleEndian>().map_err(LairError::other)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_codec_encode_and_decode() {
let mut writer = CodecWriter::new(1024).unwrap();
writer.write_pre_padding(16).unwrap();
writer.write_pre_padding(16).unwrap();
writer.write_entry_type(EntryType::TlsCert).unwrap();
writer.write_entry_type(EntryType::SignEd25519).unwrap();
writer.write_u64(4).unwrap();
writer.write_u64(4).unwrap();
writer.write_bytes(&[42, 42, 42, 42]).unwrap();
writer.write_bytes(&[43, 43, 43, 43]).unwrap();
writer.write_pre_padding(16).unwrap();
writer.write_bytes(&[44, 44, 44, 44]).unwrap();
let raw = writer.into_vec();
let mut reader = CodecReader::new(&raw);
reader.read_pre_padding().unwrap();
reader.read_pre_padding().unwrap();
assert_eq!(EntryType::TlsCert, reader.read_entry_type().unwrap());
assert_eq!(EntryType::SignEd25519, reader.read_entry_type().unwrap());
assert_eq!(4, reader.read_u64().unwrap());
assert_eq!(4, reader.read_u64().unwrap());
assert_eq!(&[42, 42, 42, 42], reader.read_bytes(4).unwrap());
assert_eq!(&[43, 43, 43, 43], reader.read_bytes(4).unwrap());
reader.read_pre_padding().unwrap();
assert_eq!(&[44, 44, 44, 44], reader.read_bytes(4).unwrap());
}
}