1#![forbid(unsafe_code, missing_debug_implementations, missing_docs)]
2#![cfg_attr(test, deny(warnings))]
3
4mod file_type;
20mod hash_type;
21mod protocol_version;
22
23use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
24use failure::{bail, ensure, format_err, Error};
25use std::io::Cursor;
26
27pub use crate::file_type::FileType;
28pub use crate::hash_type::HashType;
29pub use crate::protocol_version::ProtocolVersion;
30
31const HEADER_LENGTH: usize = 32;
32const MAX_ALGORITHM_NAME_LENGTH: usize = HEADER_LENGTH - 8;
33
34#[derive(Debug)]
36pub struct Header {
37 pub file_type: FileType,
39 pub protocol_version: ProtocolVersion,
41 pub entry_size: u16,
43 pub hash_type: HashType,
45}
46
47impl Header {
48 #[inline]
50 pub fn new(
51 file_type: FileType,
52 entry_size: u16,
53 hash_type: HashType,
54 ) -> Self {
55 Header {
56 file_type,
57 entry_size,
58 hash_type,
59 protocol_version: ProtocolVersion::V0,
60 }
61 }
62
63 #[inline]
65 pub fn from_vec(buffer: &[u8]) -> Result<Header, Error> {
66 ensure!(buffer.len() == 32, "buffer should be 32 bytes");
67
68 let mut rdr = Cursor::new(buffer);
69 let byte = rdr.read_u8().unwrap();
70 ensure!(
71 byte == 5,
72 "The first byte of a SLEEP header should be '5', found {}",
73 byte
74 );
75
76 let byte = rdr.read_u8().unwrap();
77 ensure!(
78 byte == 2,
79 "The second byte of a SLEEP header should be '2', found {}",
80 byte
81 );
82
83 let byte = rdr.read_u8().unwrap();
84 ensure!(
85 byte == 87,
86 "The third byte of a SLEEP header should be '87', found {}",
87 byte
88 );
89
90 let file_type = match rdr.read_u8().unwrap() {
91 0 => FileType::BitField,
92 1 => FileType::Signatures,
93 2 => FileType::Tree,
94 num => bail!(
95 "The fourth byte '{}' does not belong to any known SLEEP file type",
96 num
97 ),
98 };
99
100 let protocol_version = match rdr.read_u8().unwrap() {
101 0 => ProtocolVersion::V0,
102 num => bail!(
103 "The fifth byte '{}' does not belong to any known SLEEP protocol protocol_version",
104 num
105 ),
106 };
107
108 let entry_size = rdr.read_u16::<BigEndian>().unwrap();
110
111 let hash_name_len = rdr.read_u8().unwrap() as usize;
114 let current = rdr.position() as usize;
115
116 ensure!(
117 hash_name_len <= MAX_ALGORITHM_NAME_LENGTH,
118 "Algorithm name is too long: {} (max: {})",
119 hash_name_len,
120 MAX_ALGORITHM_NAME_LENGTH
121 );
122
123 let hash_name_upper = current + hash_name_len;
124 ensure!(
125 buffer.len() >= hash_name_upper,
126 "Broken parser: algorithm name is out of bounds: {} {}",
127 hash_name_upper,
128 buffer.len()
129 );
130
131 let buf_slice = &buffer[current..hash_name_upper];
132 rdr.set_position(hash_name_upper as u64 + 1);
133 let algo = ::std::str::from_utf8(buf_slice).map_err(|e| {
134 format_err!("The algorithm string was invalid utf8 encoded: {:?}", e)
135 })?;
136
137 let hash_type = match algo {
138 "BLAKE2b" => HashType::BLAKE2b,
139 "Ed25519" => HashType::Ed25519,
140 "" => HashType::None,
141 name => bail!("Unexpected algorithm name: {}", name),
142 };
143
144 Ok(Header {
145 protocol_version,
146 entry_size,
147 file_type,
148 hash_type,
149 })
150 }
151
152 #[inline]
155 pub fn to_vec(&self) -> Vec<u8> {
156 let mut wtr = Vec::with_capacity(32);
157
158 wtr.extend_from_slice(&[5u8, 2, 87]);
159
160 let file_type = match self.file_type {
161 FileType::BitField => 0,
162 FileType::Signatures => 1,
163 FileType::Tree => 2,
164 };
165 wtr.write_u8(file_type).unwrap();
166
167 let protocol_version = match self.protocol_version {
168 ProtocolVersion::V0 => 0,
169 };
170 wtr.write_u8(protocol_version).unwrap();
171
172 wtr.write_u16::<BigEndian>(self.entry_size).unwrap();
173
174 let hash_type = match self.hash_type {
175 HashType::BLAKE2b => "BLAKE2b",
176 HashType::Ed25519 => "Ed25519",
177 HashType::None => "",
178 };
179 let hash_type = hash_type.as_bytes();
180 wtr.write_u8(hash_type.len() as u8).unwrap();
181 wtr.extend_from_slice(hash_type);
182
183 for _ in wtr.len()..wtr.capacity() {
184 wtr.write_u8(0).unwrap();
185 }
186 wtr
187 }
188
189 #[inline]
191 pub fn is_bitfield(&self) -> bool {
192 self.entry_size == 3328
193 && self.file_type.is_bitfield()
194 && self.hash_type.is_none()
195 }
196
197 #[inline]
199 pub fn is_signatures(&self) -> bool {
200 self.entry_size == 64
201 && self.file_type.is_signatures()
202 && self.hash_type.is_ed25519()
203 }
204
205 #[inline]
207 pub fn is_tree(&self) -> bool {
208 self.entry_size == 40
209 && self.file_type.is_tree()
210 && self.hash_type.is_blake2b()
211 }
212}
213
214#[inline]
216pub fn create_bitfield() -> Header {
217 Header::new(FileType::BitField, 3328, HashType::None)
218}
219
220#[inline]
222pub fn create_signatures() -> Header {
223 Header::new(FileType::Signatures, 64, HashType::Ed25519)
224}
225
226#[inline]
228pub fn create_tree() -> Header {
229 Header::new(FileType::Tree, 40, HashType::BLAKE2b)
230}