1use crate::error::{Error, Result};
17use serde::de::DeserializeOwned;
18use serde::{Deserialize, Serialize};
19use serde_bytes::ByteBuf;
20
21pub const MAGIC: [u8; 4] = *b"TSR\x1A";
22pub const TRAILER_MAGIC: [u8; 4] = *b"RST\x1A";
23pub const VERSION: u16 = 1;
24pub const HEADER_LEN: usize = 32;
25pub const TRAILER_LEN: usize = 24;
26
27pub const FLAG_ENCRYPTED: u16 = 0x0001;
28pub const FLAG_SIGNED: u16 = 0x0002;
29
30pub mod section {
32 pub const RECIPIENTS: u8 = 1;
33 pub const DICTIONARY: u8 = 2;
34 pub const BLOBS: u8 = 3;
35 pub const CHUNK_INDEX: u8 = 4;
36 pub const MANIFEST: u8 = 5;
37 pub const SIGNATURES: u8 = 6;
38}
39
40#[derive(Clone, Debug, Serialize, Deserialize)]
41pub struct SectionEntry {
42 pub t: u8,
43 pub off: u64,
44 pub len: u64,
45 pub h: ByteBuf,
46}
47
48#[derive(Clone, Copy, Debug)]
49pub struct Header {
50 pub version: u16,
51 pub flags: u16,
52}
53
54pub fn encode_header(h: &Header) -> [u8; HEADER_LEN] {
55 let mut b = [0u8; HEADER_LEN];
56 b[0..4].copy_from_slice(&MAGIC);
57 b[4..6].copy_from_slice(&h.version.to_le_bytes());
58 b[6..8].copy_from_slice(&h.flags.to_le_bytes());
59 b[8..12].copy_from_slice(&(HEADER_LEN as u32).to_le_bytes());
60 b
61}
62
63pub fn decode_header(b: &[u8]) -> Result<Header> {
64 if b.len() < HEADER_LEN || b[0..4] != MAGIC {
65 return Err(Error::BadMagic);
66 }
67 let version = u16::from_le_bytes([b[4], b[5]]);
68 if version != VERSION {
69 return Err(Error::Version(version));
70 }
71 let flags = u16::from_le_bytes([b[6], b[7]]);
72 let hlen = u32::from_le_bytes([b[8], b[9], b[10], b[11]]) as usize;
73 if hlen != HEADER_LEN {
74 return Err(Error::Corrupt("header length field".into()));
75 }
76 if b[12..HEADER_LEN].iter().any(|&x| x != 0) {
77 return Err(Error::Corrupt("reserved header bytes must be zero".into()));
78 }
79 Ok(Header { version, flags })
80}
81
82pub fn encode_trailer(table_off: u64, table_len: u64, crc: u32) -> [u8; TRAILER_LEN] {
83 let mut b = [0u8; TRAILER_LEN];
84 b[0..8].copy_from_slice(&table_off.to_le_bytes());
85 b[8..16].copy_from_slice(&table_len.to_le_bytes());
86 b[16..20].copy_from_slice(&crc.to_le_bytes());
87 b[20..24].copy_from_slice(&TRAILER_MAGIC);
88 b
89}
90
91pub fn decode_trailer(b: &[u8]) -> Result<(u64, u64, u32)> {
93 if b.len() != TRAILER_LEN || b[20..24] != TRAILER_MAGIC {
94 return Err(Error::Corrupt("trailer magic".into()));
95 }
96 let off = u64::from_le_bytes(b[0..8].try_into().unwrap());
97 let len = u64::from_le_bytes(b[8..16].try_into().unwrap());
98 let crc = u32::from_le_bytes(b[16..20].try_into().unwrap());
99 Ok((off, len, crc))
100}
101
102pub fn cbor_encode<T: Serialize>(v: &T) -> Result<Vec<u8>> {
103 let mut out = Vec::new();
104 ciborium::into_writer(v, &mut out).map_err(|e| Error::Encoding(format!("cbor encode: {e}")))?;
105 Ok(out)
106}
107
108pub fn cbor_decode<T: DeserializeOwned>(b: &[u8]) -> Result<T> {
109 ciborium::from_reader(b).map_err(|e| Error::Encoding(format!("cbor decode: {e}")))
110}
111
112pub fn crc32(b: &[u8]) -> u32 {
113 let mut h = crc32fast::Hasher::new();
114 h.update(b);
115 h.finalize()
116}
117
118pub fn signing_message(header: &[u8], sections: &[SectionEntry]) -> [u8; 32] {
121 let mut h = blake3::Hasher::new();
122 h.update(b"tsaur-sig-v1");
123 h.update(header);
124 for s in sections {
125 if s.t == section::SIGNATURES {
126 continue;
127 }
128 h.update(&[s.t]);
129 h.update(&s.off.to_le_bytes());
130 h.update(&s.len.to_le_bytes());
131 h.update(&s.h);
132 }
133 *h.finalize().as_bytes()
134}
135
136#[derive(Clone, Debug, Serialize, Deserialize)]
137pub struct SignatureBlock {
138 pub alg: String,
139 pub key: ByteBuf,
140 pub sig: ByteBuf,
141}