1use byteorder::{LittleEndian, ReadBytesExt};
6use serde::{Deserialize, Serialize};
7use std::io::{Cursor, Read, Write};
8
9mod constants {
10 pub const CURRENT_BLOB_HEADER_VERSION: u32 = 1;
11}
12
13#[repr(C)]
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct HeaderPrefix {
25 pub magic: [u8; 4],
26 pub version: u32,
27 pub remaining_header_bytes: u32,
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct BlobHeader {
32 pub prefix: HeaderPrefix,
33 pub envelopes: Vec<KeyEnvelope>,
34 pub cipher_algorithm: CipherAlgorithm,
35 pub cipher_param_length: u32,
36 pub cipher_raw_params: Vec<u8>,
37 pub authentication_data_length: u16,
38 pub authentication_data: Vec<u8>,
39 pub cipher_length: u64,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct KeyEnvelope {
44 pub version: u32,
45 pub envelope_type: KeyEnvelopeType,
46 pub envelope_data: Vec<u8>,
47}
48
49impl KeyEnvelope {
50 const CURRENT_VERSION: u32 = 1;
51 pub fn new(envelope_type: KeyEnvelopeType, envelope_data: Vec<u8>) -> Self {
52 Self {
53 version: Self::CURRENT_VERSION,
54 envelope_type,
55 envelope_data,
56 }
57 }
58 pub fn envelope_data(&self) -> &Vec<u8> {
59 &self.envelope_data
60 }
61}
62
63#[repr(u8)]
64#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
65pub enum KeyEnvelopeType {
66 Invalid = 0,
67 Kdf = 1,
68 Pgp = 2,
69 Age = 3,
70}
71
72impl KeyEnvelopeType {
73 pub fn from_bytes(data: &[u8]) -> Result<(KeyEnvelopeType, usize), crate::error::Error> {
74 Ok((
75 bincode::deserialize(data)
76 .map_err(|e| crate::error::Error::DeserializationError(e.to_string()))?,
77 1,
78 ))
79 }
80}
81
82#[repr(u32)]
83#[derive(Debug, Clone, Serialize, Deserialize, Copy, PartialEq)]
84pub enum CipherAlgorithm {
85 Invalid = 0,
86 Aes256Gcm = 1,
87}
88
89impl CipherAlgorithm {
90 pub fn from_u32(value: u32) -> Option<Self> {
91 match value {
92 0 => Some(Self::Invalid),
93 1 => Some(Self::Aes256Gcm),
94 _ => None,
95 }
96 }
97
98 pub fn as_u32(self) -> u32 {
99 self as u32
100 }
101}
102
103#[repr(u16)]
104#[derive(Debug, Clone, Serialize, Deserialize, Copy, PartialEq)]
105pub enum KdfAlgorithm {
106 Invalid = 0,
107 Argon2id = 1,
108}
109
110impl KdfAlgorithm {
111 pub fn from_u16(value: u16) -> Option<Self> {
112 match value {
113 0 => Some(Self::Invalid),
114 1 => Some(Self::Argon2id),
115 _ => None,
116 }
117 }
118
119 pub fn as_u16(self) -> u16 {
120 self as u16
121 }
122
123 pub fn to_bytes(&self) -> [u8; 2] {
124 self.as_u16().to_le_bytes()
125 }
126
127 pub fn from_bytes(bytes: &[u8]) -> Result<(KdfAlgorithm, usize), crate::error::Error> {
128 if bytes.len() < 2 {
129 return Err(crate::error::Error::DeserializationError(
130 format!(
131 "Cannot read an u16 from a byte sequence of length {:?}",
132 bytes.len()
133 )
134 .to_string(),
135 ));
136 }
137 match Self::from_u16(u16::from_le_bytes(bytes[0..2].try_into().unwrap())) {
138 Some(kdf_algo) => Ok((kdf_algo, 2 as usize)),
139 None => Err(crate::error::Error::DeserializationError(
140 "Unexpected KDF algorithm tag value".to_string(),
141 )),
142 }
143 }
144}
145
146#[derive(Clone, Serialize, Deserialize, Debug)]
147pub struct Argon2idParams {
148 pub m_cost: u32,
149 pub t_cost: u32,
150 pub p_cost: u32,
151 pub salt: Vec<u8>,
152}
153
154#[derive(Clone, Serialize, Deserialize)]
155pub struct Aes256GcmParams {
156 pub nonce: Vec<u8>,
157}
158
159
160impl HeaderPrefix {
161 const HEADER_PREFIX_LENGTH: usize = size_of::<HeaderPrefix>();
162 const MAGIC: [u8; 4] = *b"SDCR";
163
164 pub fn new() -> Self {
165 HeaderPrefix {
166 magic: Self::MAGIC,
167 version: constants::CURRENT_BLOB_HEADER_VERSION,
168 remaining_header_bytes: 0,
169 }
170 }
171
172 pub fn length() -> usize {
173 HeaderPrefix::HEADER_PREFIX_LENGTH
174 }
175
176 pub fn write_all(&self, cursor: &mut Cursor<&mut [u8]>) -> Result<(), crate::error::Error> {
177 cursor.write_all(&self.magic)?;
178 cursor.write_all(&self.version.to_le_bytes())?;
179 cursor.write_all(&self.remaining_header_bytes.to_le_bytes())?;
180 Ok(())
181 }
182
183 pub fn parse(buffer: &[u8]) -> Result<(Option<HeaderPrefix>, usize), crate::error::Error> {
184 log::debug!("About to parse buffer in HeaderPrefix");
185 if buffer.len() < Self::length() {
186 return Ok((None, 0));
187 }
188 let mut prefix = Self::new();
189 let mut cursor = Cursor::new(buffer);
190 cursor.read_exact(&mut prefix.magic)?;
191 if prefix.magic != Self::MAGIC {
192 return Err(crate::error::Error::DeserializationError(
193 format!(
194 "Unexpected magic '{:?}' while parsing blob header",
195 prefix.magic
196 )
197 .to_string(),
198 ));
199 }
200 prefix.version = cursor.read_u32::<LittleEndian>()?;
201 prefix.remaining_header_bytes = cursor.read_u32::<LittleEndian>()?;
202 Ok((Some(prefix), cursor.position() as usize))
203 }
204}
205
206impl BlobHeader {
207 pub fn serialized_size(&self) -> Result<usize, crate::error::Error> {
208 Ok(HeaderPrefix::length()
209 + self.size_of_envelopes()?
210 + size_of_val(&self.cipher_algorithm)
211 + size_of_val(&self.cipher_param_length)
212 + self.cipher_raw_params.len()
213 + size_of_val(&self.authentication_data_length)
214 + self.authentication_data.len()
215 + size_of_val(&self.cipher_length))
216 }
217
218 fn size_of_envelopes(&self) -> Result<usize, crate::error::Error> {
219 Ok(bincode::serialized_size(&self.envelopes)
220 .map_err(|e| crate::error::Error::SerializationError(e.to_string()))?
221 as usize)
222 }
223
224 pub fn get_cipher_length_pos(&self) -> Result<usize, crate::error::Error> {
225 Ok(self.serialized_size()? - size_of_val(&self.cipher_length))
226 }
227
228 pub fn get_cipher_length_length(&self) -> usize {
229 size_of_val(&self.cipher_length)
230 }
231
232 pub fn write_to_slice(&self, buffer: &mut [u8]) -> Result<(), crate::error::Error> {
233 let mut copied_prefix = self.prefix.clone();
234 let mut cursor = Cursor::new(&mut buffer[..]);
235 copied_prefix.write_all(&mut cursor)?;
238 let end_of_prefix_pos = cursor.position() as u32;
239 log::debug!("Writing key envelopes at offset {:?}", end_of_prefix_pos);
240 bincode::serialize_into(&mut cursor, &self.envelopes)
241 .map_err(|e| crate::error::Error::SerializationError(e.to_string()))?;
242 cursor.write_all(&self.cipher_algorithm.as_u32().to_le_bytes())?;
243 cursor.write_all(&self.cipher_param_length.to_le_bytes())?;
244 cursor.write_all(&self.cipher_raw_params)?;
245 cursor.write_all(&self.authentication_data_length.to_le_bytes())?;
246 cursor.write_all(&self.authentication_data)?;
247 cursor.write_all(&self.cipher_length.to_le_bytes())?;
248 let end_of_header_pos = cursor.position() as u32;
249 copied_prefix.remaining_header_bytes = end_of_header_pos - end_of_prefix_pos;
250 let mut cursor2 = Cursor::new(&mut buffer[..]);
252 copied_prefix.write_all(&mut cursor2)?;
253 log::debug!("Blob header written to buffer: {:02x?}", buffer);
254 Ok(())
255 }
256
257 pub fn to_bytes(&self) -> Result<Vec<u8>, crate::error::Error> {
258 let mut buffer = Vec::with_capacity(self.serialized_size()?);
259 self.write_to_slice(&mut buffer)?;
260 Ok(buffer)
261 }
262
263 pub fn new() -> BlobHeader {
264 Self {
265 prefix: HeaderPrefix::new(),
266 envelopes: vec![],
267 cipher_algorithm: CipherAlgorithm::Invalid,
268 cipher_param_length: 0,
269 cipher_raw_params: vec![0u8; 0],
270 authentication_data_length: 0,
271 authentication_data: vec![0u8; 0],
272 cipher_length: 0,
273 }
274 }
275
276 pub fn parse(buffer: &[u8]) -> Result<(Self, u64), crate::error::Error> {
277 log::debug!(
278 "Parsing buffer form {:02x?} [truncated to 256 bytes]",
279 &buffer[..buffer.len().min(256)]
280 );
281 let mut file_header = BlobHeader::new();
282 let (prefix, header_prefix_length) = HeaderPrefix::parse(buffer)?;
284 if prefix.is_none()
285 || (header_prefix_length + prefix.as_ref().unwrap().remaining_header_bytes as usize
286 > buffer.len())
287 {
288 return Err(crate::error::Error::BlobParsingError(
290 "Cannot read blob header, not enough data".to_string(),
291 ));
292 }
293 file_header.prefix = prefix.unwrap();
294 let mut cursor = Cursor::new(&buffer[header_prefix_length..]);
295 log::debug!("Reading key envelopes at offset {:?}", header_prefix_length);
296 file_header.envelopes = bincode::deserialize_from(&mut cursor)
297 .map_err(|e| crate::error::Error::DeserializationError(e.to_string()))?;
298 log::debug!("Read {:?} key envelope", file_header.envelopes.len());
299 log::debug!("Envelope: {:?}", file_header.envelopes.first());
300
301 file_header.cipher_algorithm =
302 CipherAlgorithm::from_u32(cursor.read_u32::<LittleEndian>()?)
303 .expect("Invalid cipher algorithm id");
304 file_header.cipher_param_length = cursor.read_u32::<LittleEndian>()?;
305 log::debug!(
306 "Cipher parameters length: {}",
307 file_header.cipher_param_length
308 );
309 file_header.cipher_raw_params = vec![0u8; file_header.cipher_param_length as usize];
310 cursor.read_exact(&mut file_header.cipher_raw_params)?;
311 file_header.authentication_data_length = cursor.read_u16::<LittleEndian>()?;
312 file_header.authentication_data =
313 vec![0u8; file_header.authentication_data_length as usize];
314 cursor.read_exact(&mut file_header.authentication_data)?;
315 file_header.cipher_length = cursor.read_u64::<LittleEndian>()?;
316
317 if file_header.prefix.remaining_header_bytes != cursor.position() as u32 {
318 log::debug!(
319 "** EPIC FAIL: {:?} != {:?}",
320 file_header.prefix.remaining_header_bytes,
321 cursor.position()
322 );
323 return Err(crate::error::Error::BlobParsingError(
324 "Corrupt blob encountered, unexpected header length".to_string(),
325 ));
326 }
327
328 Ok((file_header, header_prefix_length as u64 + cursor.position()))
329 }
330}
331
332impl Argon2idParams {
333 pub fn to_bytes(&self) -> Result<Vec<u8>, crate::error::Error> {
334 bincode::serialize(self).map_err(|e| crate::error::Error::BincodeError(e.to_string()))
335 }
336 pub fn from_bytes(bytes: &[u8]) -> Result<(Self, u64), crate::error::Error> {
337 log::debug!(
338 "Reading Argon2idParams from bytes, buffer size: {}",
339 bytes.len()
340 );
341 let mut cursor = Cursor::new(bytes);
342 Ok((
343 bincode::deserialize_from(&mut cursor)
344 .map_err(|e| crate::error::Error::BincodeError(e.to_string()))?,
345 cursor.position(),
346 ))
347 }
348}
349
350impl Aes256GcmParams {
351 pub fn to_bytes(&self) -> Result<Vec<u8>, crate::error::Error> {
352 bincode::serialize(self).map_err(|e| crate::error::Error::BincodeError(e.to_string()))
353 }
354 pub fn from_bytes(bytes: &[u8]) -> Result<Self, crate::error::Error> {
355 log::debug!(
356 "Reading Aes256GcmParams from buffer of size {}",
357 bytes.len()
358 );
359 bincode::deserialize(bytes).map_err(|e| crate::error::Error::BincodeError(e.to_string()))
360 }
361}
362