]> git.proxmox.com Git - proxmox-backup.git/blame - src/backup/data_blob.rs
src/pxar/fuse.rs: enable libfuse debug output in verbose mode
[proxmox-backup.git] / src / backup / data_blob.rs
CommitLineData
3025b3a5
DM
1use failure::*;
2use std::convert::TryInto;
3025b3a5 3
5485b579 4use proxmox::tools::io::{ReadExt, WriteExt};
9f83e0f7 5
781ac11c
DM
6const MAX_BLOB_SIZE: usize = 128*1024*1024;
7
3025b3a5
DM
8use super::*;
9
10/// Data blob binary storage format
11///
863be2e6 12/// Data blobs store arbitrary binary data (< 128MB), and can be
3025b3a5
DM
13/// compressed and encrypted. A simply binary format is used to store
14/// them on disk or transfer them over the network. Please use index
15/// files to store large data files (".fidx" of ".didx").
16///
3025b3a5
DM
17pub struct DataBlob {
18 raw_data: Vec<u8>, // tagged, compressed, encryped data
19}
20
21impl DataBlob {
22
5d15cb49
DM
23 pub fn header_size(magic: &[u8; 8]) -> usize {
24 match magic {
25 &UNCOMPRESSED_CHUNK_MAGIC_1_0 => std::mem::size_of::<DataChunkHeader>(),
26 &COMPRESSED_CHUNK_MAGIC_1_0 => std::mem::size_of::<DataChunkHeader>(),
27 &ENCRYPTED_CHUNK_MAGIC_1_0 => std::mem::size_of::<EncryptedDataChunkHeader>(),
28 &ENCR_COMPR_CHUNK_MAGIC_1_0 => std::mem::size_of::<EncryptedDataChunkHeader>(),
29
30 &UNCOMPRESSED_BLOB_MAGIC_1_0 => std::mem::size_of::<DataBlobHeader>(),
31 &COMPRESSED_BLOB_MAGIC_1_0 => std::mem::size_of::<DataBlobHeader>(),
32 &ENCRYPTED_BLOB_MAGIC_1_0 => std::mem::size_of::<EncryptedDataBlobHeader>(),
33 &ENCR_COMPR_BLOB_MAGIC_1_0 => std::mem::size_of::<EncryptedDataBlobHeader>(),
34 &AUTHENTICATED_BLOB_MAGIC_1_0 => std::mem::size_of::<AuthenticatedDataBlobHeader>(),
35 &AUTH_COMPR_BLOB_MAGIC_1_0 => std::mem::size_of::<AuthenticatedDataBlobHeader>(),
36 _ => panic!("unknown blob magic"),
37 }
38 }
39
3025b3a5
DM
40 /// accessor to raw_data field
41 pub fn raw_data(&self) -> &[u8] {
42 &self.raw_data
43 }
44
cb08ac3e
DM
45 /// Consume self and returns raw_data
46 pub fn into_inner(self) -> Vec<u8> {
47 self.raw_data
48 }
49
3025b3a5
DM
50 /// accessor to chunk type (magic number)
51 pub fn magic(&self) -> &[u8; 8] {
52 self.raw_data[0..8].try_into().unwrap()
53 }
54
b7f4f27d
DM
55 /// accessor to crc32 checksum
56 pub fn crc(&self) -> u32 {
991abfa8
DM
57 let crc_o = proxmox::tools::offsetof!(DataBlobHeader, crc);
58 u32::from_le_bytes(self.raw_data[crc_o..crc_o+4].try_into().unwrap())
b7f4f27d
DM
59 }
60
61 // set the CRC checksum field
62 pub fn set_crc(&mut self, crc: u32) {
991abfa8
DM
63 let crc_o = proxmox::tools::offsetof!(DataBlobHeader, crc);
64 self.raw_data[crc_o..crc_o+4].copy_from_slice(&crc.to_le_bytes());
b7f4f27d
DM
65 }
66
67 /// compute the CRC32 checksum
cb08ac3e 68 pub fn compute_crc(&self) -> u32 {
b7f4f27d 69 let mut hasher = crc32fast::Hasher::new();
5d15cb49 70 let start = Self::header_size(self.magic()); // start after HEAD
991abfa8 71 hasher.update(&self.raw_data[start..]);
b7f4f27d
DM
72 hasher.finalize()
73 }
74
b208da83
DM
75 /// verify the CRC32 checksum
76 pub fn verify_crc(&self) -> Result<(), Error> {
77 let expected_crc = self.compute_crc();
78 if expected_crc != self.crc() {
79 bail!("Data blob has wrong CRC checksum.");
80 }
81 Ok(())
82 }
83
69ecd8d5 84 /// Create a DataBlob, optionally compressed and/or encrypted
3025b3a5
DM
85 pub fn encode(
86 data: &[u8],
87 config: Option<&CryptConfig>,
88 compress: bool,
89 ) -> Result<Self, Error> {
90
781ac11c 91 if data.len() > MAX_BLOB_SIZE {
3025b3a5
DM
92 bail!("data blob too large ({} bytes).", data.len());
93 }
94
f889b158 95 let mut blob = if let Some(config) = config {
3025b3a5 96
0066c6d9
DM
97 let compr_data;
98 let (_compress, data, magic) = if compress {
99 compr_data = zstd::block::compress(data, 1)?;
100 // Note: We only use compression if result is shorter
101 if compr_data.len() < data.len() {
102 (true, &compr_data[..], ENCR_COMPR_BLOB_MAGIC_1_0)
103 } else {
104 (false, data, ENCRYPTED_BLOB_MAGIC_1_0)
105 }
106 } else {
107 (false, data, ENCRYPTED_BLOB_MAGIC_1_0)
108 };
109
110 let header_len = std::mem::size_of::<EncryptedDataBlobHeader>();
111 let mut raw_data = Vec::with_capacity(data.len() + header_len);
112
113 let dummy_head = EncryptedDataBlobHeader {
114 head: DataBlobHeader { magic: [0u8; 8], crc: [0; 4] },
115 iv: [0u8; 16],
116 tag: [0u8; 16],
117 };
5485b579
WB
118 unsafe {
119 raw_data.write_le_value(dummy_head)?;
120 }
0066c6d9
DM
121
122 let (iv, tag) = config.encrypt_to(data, &mut raw_data)?;
123
124 let head = EncryptedDataBlobHeader {
125 head: DataBlobHeader { magic, crc: [0; 4] }, iv, tag,
126 };
127
5485b579
WB
128 unsafe {
129 (&mut raw_data[0..header_len]).write_le_value(head)?;
130 }
0066c6d9 131
f889b158 132 DataBlob { raw_data }
3025b3a5
DM
133 } else {
134
991abfa8 135 let max_data_len = data.len() + std::mem::size_of::<DataBlobHeader>();
3025b3a5 136 if compress {
991abfa8 137 let mut comp_data = Vec::with_capacity(max_data_len);
3025b3a5 138
991abfa8
DM
139 let head = DataBlobHeader {
140 magic: COMPRESSED_BLOB_MAGIC_1_0,
141 crc: [0; 4],
142 };
5485b579
WB
143 unsafe {
144 comp_data.write_le_value(head)?;
145 }
b7f4f27d 146
3025b3a5
DM
147 zstd::stream::copy_encode(data, &mut comp_data, 1)?;
148
991abfa8 149 if comp_data.len() < max_data_len {
eecb2356
DM
150 let mut blob = DataBlob { raw_data: comp_data };
151 blob.set_crc(blob.compute_crc());
152 return Ok(blob);
3025b3a5
DM
153 }
154 }
155
991abfa8 156 let mut raw_data = Vec::with_capacity(max_data_len);
3025b3a5 157
991abfa8
DM
158 let head = DataBlobHeader {
159 magic: UNCOMPRESSED_BLOB_MAGIC_1_0,
160 crc: [0; 4],
161 };
5485b579
WB
162 unsafe {
163 raw_data.write_le_value(head)?;
164 }
3025b3a5
DM
165 raw_data.extend_from_slice(data);
166
f889b158
DM
167 DataBlob { raw_data }
168 };
169
170 blob.set_crc(blob.compute_crc());
171
172 Ok(blob)
3025b3a5
DM
173 }
174
175 /// Decode blob data
176 pub fn decode(self, config: Option<&CryptConfig>) -> Result<Vec<u8>, Error> {
177
178 let magic = self.magic();
179
180 if magic == &UNCOMPRESSED_BLOB_MAGIC_1_0 {
991abfa8
DM
181 let data_start = std::mem::size_of::<DataBlobHeader>();
182 return Ok(self.raw_data[data_start..].to_vec());
3025b3a5 183 } else if magic == &COMPRESSED_BLOB_MAGIC_1_0 {
991abfa8 184 let data_start = std::mem::size_of::<DataBlobHeader>();
781ac11c 185 let data = zstd::block::decompress(&self.raw_data[data_start..], MAX_BLOB_SIZE)?;
3025b3a5 186 return Ok(data);
3025b3a5 187 } else if magic == &ENCR_COMPR_BLOB_MAGIC_1_0 || magic == &ENCRYPTED_BLOB_MAGIC_1_0 {
9f83e0f7 188 let header_len = std::mem::size_of::<EncryptedDataBlobHeader>();
ba01828d
DM
189 let head = unsafe {
190 (&self.raw_data[..header_len]).read_le_value::<EncryptedDataBlobHeader>()?
191 };
9f83e0f7 192
3025b3a5
DM
193 if let Some(config) = config {
194 let data = if magic == &ENCR_COMPR_BLOB_MAGIC_1_0 {
9f83e0f7 195 config.decode_compressed_chunk(&self.raw_data[header_len..], &head.iv, &head.tag)?
3025b3a5 196 } else {
9f83e0f7 197 config.decode_uncompressed_chunk(&self.raw_data[header_len..], &head.iv, &head.tag)?
3025b3a5
DM
198 };
199 return Ok(data);
200 } else {
201 bail!("unable to decrypt blob - missing CryptConfig");
202 }
69ecd8d5
DM
203 } else if magic == &AUTH_COMPR_BLOB_MAGIC_1_0 || magic == &AUTHENTICATED_BLOB_MAGIC_1_0 {
204 let header_len = std::mem::size_of::<AuthenticatedDataBlobHeader>();
205 let head = unsafe {
206 (&self.raw_data[..header_len]).read_le_value::<AuthenticatedDataBlobHeader>()?
207 };
208
209 let data_start = std::mem::size_of::<AuthenticatedDataBlobHeader>();
210
211 // Note: only verify if we have a crypt config
212 if let Some(config) = config {
213 let signature = config.compute_auth_tag(&self.raw_data[data_start..]);
214 if signature != head.tag {
215 bail!("verifying blob signature failed");
216 }
217 }
218
219 if magic == &AUTH_COMPR_BLOB_MAGIC_1_0 {
220 let data = zstd::block::decompress(&self.raw_data[data_start..], 16*1024*1024)?;
221 return Ok(data);
222 } else {
223 return Ok(self.raw_data[data_start..].to_vec());
224 }
3025b3a5
DM
225 } else {
226 bail!("Invalid blob magic number.");
227 }
228 }
a38c5d4d 229
69ecd8d5
DM
230 /// Create a signed DataBlob, optionally compressed
231 pub fn create_signed(
232 data: &[u8],
233 config: &CryptConfig,
234 compress: bool,
235 ) -> Result<Self, Error> {
236
781ac11c 237 if data.len() > MAX_BLOB_SIZE {
69ecd8d5
DM
238 bail!("data blob too large ({} bytes).", data.len());
239 }
240
241 let compr_data;
242 let (_compress, data, magic) = if compress {
243 compr_data = zstd::block::compress(data, 1)?;
244 // Note: We only use compression if result is shorter
245 if compr_data.len() < data.len() {
246 (true, &compr_data[..], AUTH_COMPR_BLOB_MAGIC_1_0)
247 } else {
248 (false, data, AUTHENTICATED_BLOB_MAGIC_1_0)
249 }
250 } else {
251 (false, data, AUTHENTICATED_BLOB_MAGIC_1_0)
252 };
253
254 let header_len = std::mem::size_of::<AuthenticatedDataBlobHeader>();
255 let mut raw_data = Vec::with_capacity(data.len() + header_len);
256
257 let head = AuthenticatedDataBlobHeader {
258 head: DataBlobHeader { magic, crc: [0; 4] },
259 tag: config.compute_auth_tag(data),
260 };
261 unsafe {
262 raw_data.write_le_value(head)?;
263 }
264 raw_data.extend_from_slice(data);
265
f889b158
DM
266 let mut blob = DataBlob { raw_data };
267 blob.set_crc(blob.compute_crc());
268
269 return Ok(blob);
69ecd8d5
DM
270 }
271
a38c5d4d
DM
272 /// Create Instance from raw data
273 pub fn from_raw(data: Vec<u8>) -> Result<Self, Error> {
274
275 if data.len() < std::mem::size_of::<DataBlobHeader>() {
276 bail!("blob too small ({} bytes).", data.len());
277 }
278
279 let magic = &data[0..8];
280
281 if magic == ENCR_COMPR_BLOB_MAGIC_1_0 || magic == ENCRYPTED_BLOB_MAGIC_1_0 {
282
283 if data.len() < std::mem::size_of::<EncryptedDataBlobHeader>() {
284 bail!("encrypted blob too small ({} bytes).", data.len());
285 }
286
287 let blob = DataBlob { raw_data: data };
288
289 Ok(blob)
290 } else if magic == COMPRESSED_BLOB_MAGIC_1_0 || magic == UNCOMPRESSED_BLOB_MAGIC_1_0 {
291
292 let blob = DataBlob { raw_data: data };
293
69ecd8d5
DM
294 Ok(blob)
295 } else if magic == AUTH_COMPR_BLOB_MAGIC_1_0 || magic == AUTHENTICATED_BLOB_MAGIC_1_0 {
296 if data.len() < std::mem::size_of::<AuthenticatedDataBlobHeader>() {
297 bail!("authenticated blob too small ({} bytes).", data.len());
298 }
299
300 let blob = DataBlob { raw_data: data };
301
a38c5d4d
DM
302 Ok(blob)
303 } else {
304 bail!("unable to parse raw blob - wrong magic");
305 }
306 }
307
3025b3a5 308}
1f26fdef 309
09785b27 310use std::io::{Read, BufRead, BufReader, Write, Seek, SeekFrom};
1f26fdef 311
2aa0bfff
DM
312struct CryptReader<R> {
313 reader: R,
60822163 314 small_read_buf: Vec<u8>,
2aa0bfff
DM
315 block_size: usize,
316 crypter: openssl::symm::Crypter,
317 finalized: bool,
318}
319
320impl <R: BufRead> CryptReader<R> {
321
322 fn new(reader: R, iv: [u8; 16], tag: [u8; 16], config: &CryptConfig) -> Result<Self, Error> {
60822163
DM
323 let block_size = config.cipher().block_size(); // Note: block size is normally 1 byte for stream ciphers
324 if block_size.count_ones() != 1 || block_size > 512 {
2aa0bfff
DM
325 bail!("unexpected Cipher block size {}", block_size);
326 }
327 let mut crypter = config.data_crypter(&iv, openssl::symm::Mode::Decrypt)?;
328 crypter.set_tag(&tag)?;
60822163
DM
329
330 Ok(Self { reader, crypter, block_size, finalized: false, small_read_buf: Vec::new() })
2aa0bfff
DM
331 }
332
333 fn finish(self) -> Result<R, Error> {
334 if !self.finalized {
335 bail!("CryptReader not successfully finalized.");
336 }
337 Ok(self.reader)
338 }
339}
340
341impl <R: BufRead> Read for CryptReader<R> {
342
343 fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
60822163
DM
344 if self.small_read_buf.len() > 0 {
345 let max = if self.small_read_buf.len() > buf.len() { buf.len() } else { self.small_read_buf.len() };
346 let rest = self.small_read_buf.split_off(max);
347 buf[..max].copy_from_slice(&self.small_read_buf);
348 self.small_read_buf = rest;
349 return Ok(max);
2aa0bfff
DM
350 }
351
352 let data = self.reader.fill_buf()?;
353
60822163
DM
354 // handle small read buffers
355 if buf.len() <= 2*self.block_size {
356 let mut outbuf = [0u8; 1024];
357
358 let count = if data.len() == 0 { // EOF
359 let written = self.crypter.finalize(&mut outbuf)?;
360 self.finalized = true;
361 written
362 } else {
363 let mut read_size = outbuf.len() - self.block_size;
364 if read_size > data.len() {
365 read_size = data.len();
366 }
367 let written = self.crypter.update(&data[..read_size], &mut outbuf)?;
368 self.reader.consume(read_size);
369 written
370 };
371
372 if count > buf.len() {
373 buf.copy_from_slice(&outbuf[..buf.len()]);
374 self.small_read_buf = outbuf[buf.len()..count].to_vec();
375 return Ok(buf.len());
376 } else {
377 buf[..count].copy_from_slice(&outbuf[..count]);
378 return Ok(count);
379 }
2aa0bfff 380 } else {
60822163
DM
381 if data.len() == 0 { // EOF
382 let rest = self.crypter.finalize(buf)?;
383 self.finalized = true;
384 return Ok(rest)
385 } else {
386 let mut read_size = buf.len() - self.block_size;
387 if read_size > data.len() {
388 read_size = data.len();
389 }
390 let count = self.crypter.update(&data[..read_size], buf)?;
391 self.reader.consume(read_size);
392 return Ok(count)
2aa0bfff 393 }
2aa0bfff
DM
394 }
395 }
396}
397
f4942e9f
DM
398struct CryptWriter<W> {
399 writer: W,
60822163 400 block_size: usize,
f4942e9f
DM
401 encr_buf: [u8; 64*1024],
402 iv: [u8; 16],
403 crypter: openssl::symm::Crypter,
404}
405
406impl <W: Write> CryptWriter<W> {
407
408 fn new(writer: W, config: &CryptConfig) -> Result<Self, Error> {
409 let mut iv = [0u8; 16];
410 proxmox::sys::linux::fill_with_random_data(&mut iv)?;
60822163 411 let block_size = config.cipher().block_size();
f4942e9f 412
a32bd8a5 413 let crypter = config.data_crypter(&iv, openssl::symm::Mode::Encrypt)?;
f4942e9f 414
60822163 415 Ok(Self { writer, iv, crypter, block_size, encr_buf: [0u8; 64*1024] })
f4942e9f
DM
416 }
417
418 fn finish(mut self) -> Result<(W, [u8; 16], [u8; 16]), Error> {
419 let rest = self.crypter.finalize(&mut self.encr_buf)?;
420 if rest > 0 {
421 self.writer.write_all(&self.encr_buf[..rest])?;
422 }
423
424 self.writer.flush()?;
425
426 let mut tag = [0u8; 16];
427 self.crypter.get_tag(&mut tag)?;
428
429 Ok((self.writer, self.iv, tag))
430 }
431}
432
433impl <W: Write> Write for CryptWriter<W> {
434
435 fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
60822163
DM
436 let mut write_size = buf.len();
437 if write_size > (self.encr_buf.len() - self.block_size) {
438 write_size = self.encr_buf.len() - self.block_size;
439 }
440 let count = self.crypter.update(&buf[..write_size], &mut self.encr_buf)
f4942e9f
DM
441 .map_err(|err| {
442 std::io::Error::new(
443 std::io::ErrorKind::Other,
444 format!("crypter update failed - {}", err))
445 })?;
446
447 self.writer.write_all(&self.encr_buf[..count])?;
448
60822163 449 Ok(write_size)
f4942e9f
DM
450 }
451
452 fn flush(&mut self) -> Result<(), std::io::Error> {
453 Ok(())
454 }
455}
456
706638f8
DM
457struct ChecksumWriter<'a, W> {
458 writer: W,
459 hasher: crc32fast::Hasher,
460 signer: Option<openssl::sign::Signer<'a>>,
461}
462
463impl <'a, W: Write> ChecksumWriter<'a, W> {
464
465 fn new(writer: W, signer: Option<openssl::sign::Signer<'a>>) -> Self {
466 let hasher = crc32fast::Hasher::new();
467 Self { writer, hasher, signer }
468 }
469
470 pub fn finish(mut self) -> Result<(W, u32, Option<[u8; 32]>), Error> {
471 let crc = self.hasher.finalize();
472
473 if let Some(ref mut signer) = self.signer {
474 let mut tag = [0u8; 32];
475 signer.sign(&mut tag)?;
476 Ok((self.writer, crc, Some(tag)))
477 } else {
478 Ok((self.writer, crc, None))
479 }
480 }
481}
482
483impl <'a, W: Write> Write for ChecksumWriter<'a, W> {
484
485 fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
486 self.hasher.update(buf);
487 if let Some(ref mut signer) = self.signer {
f4942e9f
DM
488 signer.update(buf)
489 .map_err(|err| {
706638f8
DM
490 std::io::Error::new(
491 std::io::ErrorKind::Other,
492 format!("hmac update failed - {}", err))
493 })?;
494 }
495 self.writer.write(buf)
496 }
497
498 fn flush(&mut self) -> Result<(), std::io::Error> {
499 self.writer.flush()
500 }
501}
502
f796351c 503enum BlobWriterState<'a, W: Write> {
706638f8
DM
504 Uncompressed { csum_writer: ChecksumWriter<'a, W> },
505 Compressed { compr: zstd::stream::write::Encoder<ChecksumWriter<'a, W>> },
506 Signed { csum_writer: ChecksumWriter<'a, W> },
7776becf 507 SignedCompressed { compr: zstd::stream::write::Encoder<ChecksumWriter<'a, W>> },
f4942e9f 508 Encrypted { crypt_writer: CryptWriter<ChecksumWriter<'a, W>> },
5622a3fc 509 EncryptedCompressed { compr: zstd::stream::write::Encoder<CryptWriter<ChecksumWriter<'a, W>>> },
a762ce54
DM
510}
511
1f26fdef 512/// Write compressed data blobs
f796351c
DM
513pub struct DataBlobWriter<'a, W: Write> {
514 state: BlobWriterState<'a, W>,
1f26fdef
DM
515}
516
f796351c 517impl <'a, W: Write + Seek> DataBlobWriter<'a, W> {
a762ce54
DM
518
519 pub fn new_uncompressed(mut writer: W) -> Result<Self, Error> {
a762ce54
DM
520 writer.seek(SeekFrom::Start(0))?;
521 let head = DataBlobHeader { magic: UNCOMPRESSED_BLOB_MAGIC_1_0, crc: [0; 4] };
522 unsafe {
523 writer.write_le_value(head)?;
524 }
706638f8
DM
525 let csum_writer = ChecksumWriter::new(writer, None);
526 Ok(Self { state: BlobWriterState::Uncompressed { csum_writer }})
a762ce54 527 }
1f26fdef 528
a762ce54 529 pub fn new_compressed(mut writer: W) -> Result<Self, Error> {
706638f8 530 writer.seek(SeekFrom::Start(0))?;
1f26fdef
DM
531 let head = DataBlobHeader { magic: COMPRESSED_BLOB_MAGIC_1_0, crc: [0; 4] };
532 unsafe {
a762ce54 533 writer.write_le_value(head)?;
1f26fdef 534 }
706638f8
DM
535 let csum_writer = ChecksumWriter::new(writer, None);
536 let compr = zstd::stream::write::Encoder::new(csum_writer, 1)?;
537 Ok(Self { state: BlobWriterState::Compressed { compr }})
1f26fdef
DM
538 }
539
f796351c 540 pub fn new_signed(mut writer: W, config: &'a CryptConfig) -> Result<Self, Error> {
f796351c
DM
541 writer.seek(SeekFrom::Start(0))?;
542 let head = AuthenticatedDataBlobHeader {
543 head: DataBlobHeader { magic: AUTHENTICATED_BLOB_MAGIC_1_0, crc: [0; 4] },
544 tag: [0u8; 32],
545 };
546 unsafe {
547 writer.write_le_value(head)?;
548 }
549 let signer = config.data_signer();
706638f8
DM
550 let csum_writer = ChecksumWriter::new(writer, Some(signer));
551 Ok(Self { state: BlobWriterState::Signed { csum_writer }})
f796351c
DM
552 }
553
7776becf
DM
554 pub fn new_signed_compressed(mut writer: W, config: &'a CryptConfig) -> Result<Self, Error> {
555 writer.seek(SeekFrom::Start(0))?;
556 let head = AuthenticatedDataBlobHeader {
557 head: DataBlobHeader { magic: AUTH_COMPR_BLOB_MAGIC_1_0, crc: [0; 4] },
558 tag: [0u8; 32],
559 };
560 unsafe {
561 writer.write_le_value(head)?;
562 }
563 let signer = config.data_signer();
564 let csum_writer = ChecksumWriter::new(writer, Some(signer));
565 let compr = zstd::stream::write::Encoder::new(csum_writer, 1)?;
566 Ok(Self { state: BlobWriterState::SignedCompressed { compr }})
567 }
568
f4942e9f
DM
569 pub fn new_encrypted(mut writer: W, config: &'a CryptConfig) -> Result<Self, Error> {
570 writer.seek(SeekFrom::Start(0))?;
571 let head = EncryptedDataBlobHeader {
572 head: DataBlobHeader { magic: ENCRYPTED_BLOB_MAGIC_1_0, crc: [0; 4] },
573 iv: [0u8; 16],
574 tag: [0u8; 16],
575 };
576 unsafe {
577 writer.write_le_value(head)?;
578 }
579
580 let csum_writer = ChecksumWriter::new(writer, None);
581 let crypt_writer = CryptWriter::new(csum_writer, config)?;
582 Ok(Self { state: BlobWriterState::Encrypted { crypt_writer }})
583 }
584
5622a3fc
DM
585 pub fn new_encrypted_compressed(mut writer: W, config: &'a CryptConfig) -> Result<Self, Error> {
586 writer.seek(SeekFrom::Start(0))?;
587 let head = EncryptedDataBlobHeader {
588 head: DataBlobHeader { magic: ENCR_COMPR_BLOB_MAGIC_1_0, crc: [0; 4] },
589 iv: [0u8; 16],
590 tag: [0u8; 16],
591 };
592 unsafe {
593 writer.write_le_value(head)?;
594 }
595
596 let csum_writer = ChecksumWriter::new(writer, None);
597 let crypt_writer = CryptWriter::new(csum_writer, config)?;
598 let compr = zstd::stream::write::Encoder::new(crypt_writer, 1)?;
599 Ok(Self { state: BlobWriterState::EncryptedCompressed { compr }})
600 }
601
a762ce54
DM
602 pub fn finish(self) -> Result<W, Error> {
603 match self.state {
706638f8 604 BlobWriterState::Uncompressed { csum_writer } => {
a762ce54 605 // write CRC
706638f8 606 let (mut writer, crc, _) = csum_writer.finish()?;
18be4ec2 607 let head = DataBlobHeader { magic: UNCOMPRESSED_BLOB_MAGIC_1_0, crc: crc.to_le_bytes() };
1f26fdef 608
a762ce54
DM
609 writer.seek(SeekFrom::Start(0))?;
610 unsafe {
611 writer.write_le_value(head)?;
612 }
1f26fdef 613
a762ce54
DM
614 return Ok(writer)
615 }
706638f8
DM
616 BlobWriterState::Compressed { compr } => {
617 let csum_writer = compr.finish()?;
618 let (mut writer, crc, _) = csum_writer.finish()?;
a762ce54 619
a762ce54 620 let head = DataBlobHeader { magic: COMPRESSED_BLOB_MAGIC_1_0, crc: crc.to_le_bytes() };
1f26fdef 621
a762ce54
DM
622 writer.seek(SeekFrom::Start(0))?;
623 unsafe {
624 writer.write_le_value(head)?;
625 }
626
f796351c
DM
627 return Ok(writer)
628 }
706638f8
DM
629 BlobWriterState::Signed { csum_writer } => {
630 let (mut writer, crc, tag) = csum_writer.finish()?;
f796351c 631
706638f8 632 let head = AuthenticatedDataBlobHeader {
f796351c 633 head: DataBlobHeader { magic: AUTHENTICATED_BLOB_MAGIC_1_0, crc: crc.to_le_bytes() },
706638f8 634 tag: tag.unwrap(),
f796351c 635 };
f796351c
DM
636
637 writer.seek(SeekFrom::Start(0))?;
638 unsafe {
639 writer.write_le_value(head)?;
640 }
641
7776becf
DM
642 return Ok(writer)
643 }
644 BlobWriterState::SignedCompressed { compr } => {
645 let csum_writer = compr.finish()?;
646 let (mut writer, crc, tag) = csum_writer.finish()?;
647
648 let head = AuthenticatedDataBlobHeader {
649 head: DataBlobHeader { magic: AUTH_COMPR_BLOB_MAGIC_1_0, crc: crc.to_le_bytes() },
650 tag: tag.unwrap(),
651 };
652
653 writer.seek(SeekFrom::Start(0))?;
654 unsafe {
655 writer.write_le_value(head)?;
656 }
657
a762ce54
DM
658 return Ok(writer)
659 }
f4942e9f
DM
660 BlobWriterState::Encrypted { crypt_writer } => {
661 let (csum_writer, iv, tag) = crypt_writer.finish()?;
662 let (mut writer, crc, _) = csum_writer.finish()?;
663
664 let head = EncryptedDataBlobHeader {
665 head: DataBlobHeader { magic: ENCRYPTED_BLOB_MAGIC_1_0, crc: crc.to_le_bytes() },
666 iv, tag,
667 };
a32bd8a5 668 writer.seek(SeekFrom::Start(0))?;
f4942e9f
DM
669 unsafe {
670 writer.write_le_value(head)?;
671 }
672 return Ok(writer)
673 }
5622a3fc
DM
674 BlobWriterState::EncryptedCompressed { compr } => {
675 let crypt_writer = compr.finish()?;
676 let (csum_writer, iv, tag) = crypt_writer.finish()?;
677 let (mut writer, crc, _) = csum_writer.finish()?;
678
679 let head = EncryptedDataBlobHeader {
680 head: DataBlobHeader { magic: ENCR_COMPR_BLOB_MAGIC_1_0, crc: crc.to_le_bytes() },
681 iv, tag,
682 };
a32bd8a5 683 writer.seek(SeekFrom::Start(0))?;
5622a3fc
DM
684 unsafe {
685 writer.write_le_value(head)?;
686 }
687 return Ok(writer)
688 }
a762ce54 689 }
1f26fdef
DM
690 }
691}
692
f796351c 693impl <'a, W: Write + Seek> Write for DataBlobWriter<'a, W> {
1f26fdef
DM
694
695 fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
a762ce54 696 match self.state {
706638f8
DM
697 BlobWriterState::Uncompressed { ref mut csum_writer } => {
698 csum_writer.write(buf)
a762ce54 699 }
706638f8 700 BlobWriterState::Compressed { ref mut compr } => {
a762ce54
DM
701 compr.write(buf)
702 }
706638f8
DM
703 BlobWriterState::Signed { ref mut csum_writer } => {
704 csum_writer.write(buf)
705 }
7776becf
DM
706 BlobWriterState::SignedCompressed { ref mut compr } => {
707 compr.write(buf)
708 }
f4942e9f
DM
709 BlobWriterState::Encrypted { ref mut crypt_writer } => {
710 crypt_writer.write(buf)
711 }
5622a3fc
DM
712 BlobWriterState::EncryptedCompressed { ref mut compr } => {
713 compr.write(buf)
714 }
a762ce54 715 }
1f26fdef
DM
716 }
717
718 fn flush(&mut self) -> Result<(), std::io::Error> {
a762ce54 719 match self.state {
706638f8
DM
720 BlobWriterState::Uncompressed { ref mut csum_writer } => {
721 csum_writer.flush()
a762ce54 722 }
706638f8 723 BlobWriterState::Compressed { ref mut compr } => {
a762ce54
DM
724 compr.flush()
725 }
706638f8
DM
726 BlobWriterState::Signed { ref mut csum_writer } => {
727 csum_writer.flush()
f796351c 728 }
7776becf
DM
729 BlobWriterState::SignedCompressed { ref mut compr } => {
730 compr.flush()
731 }
f4942e9f
DM
732 BlobWriterState::Encrypted { ref mut crypt_writer } => {
733 crypt_writer.flush()
734 }
5622a3fc
DM
735 BlobWriterState::EncryptedCompressed { ref mut compr } => {
736 compr.flush()
737 }
a762ce54 738 }
1f26fdef
DM
739 }
740}
741
09785b27
DM
742struct ChecksumReader<'a, R> {
743 reader: R,
744 hasher: crc32fast::Hasher,
745 signer: Option<openssl::sign::Signer<'a>>,
746}
747
748impl <'a, R: Read> ChecksumReader<'a, R> {
749
750 fn new(reader: R, signer: Option<openssl::sign::Signer<'a>>) -> Self {
751 let hasher = crc32fast::Hasher::new();
752 Self { reader, hasher, signer }
753 }
754
755 pub fn finish(mut self) -> Result<(R, u32, Option<[u8; 32]>), Error> {
756 let crc = self.hasher.finalize();
757
758 if let Some(ref mut signer) = self.signer {
759 let mut tag = [0u8; 32];
760 signer.sign(&mut tag)?;
761 Ok((self.reader, crc, Some(tag)))
762 } else {
763 Ok((self.reader, crc, None))
764 }
765 }
766}
767
768impl <'a, R: Read> Read for ChecksumReader<'a, R> {
769
770 fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
771 let count = self.reader.read(buf)?;
772 if count > 0 {
773 self.hasher.update(&buf[..count]);
774 if let Some(ref mut signer) = self.signer {
775 signer.update(&buf[..count])
776 .map_err(|err| {
777 std::io::Error::new(
778 std::io::ErrorKind::Other,
779 format!("hmac update failed - {}", err))
780 })?;
781 }
782 }
783 Ok(count)
784 }
785}
786
787enum BlobReaderState<'a, R: Read> {
788 Uncompressed { expected_crc: u32, csum_reader: ChecksumReader<'a, R> },
789 Compressed { expected_crc: u32, decompr: zstd::stream::read::Decoder<BufReader<ChecksumReader<'a, R>>> },
4bfa147e 790 Signed { expected_crc: u32, expected_hmac: [u8; 32], csum_reader: ChecksumReader<'a, R> },
e9a385a7 791 SignedCompressed { expected_crc: u32, expected_hmac: [u8; 32], decompr: zstd::stream::read::Decoder<BufReader<ChecksumReader<'a, R>>> },
2aa0bfff 792 Encrypted { expected_crc: u32, decrypt_reader: CryptReader<BufReader<ChecksumReader<'a, R>>> },
548c9489 793 EncryptedCompressed { expected_crc: u32, decompr: zstd::stream::read::Decoder<BufReader<CryptReader<BufReader<ChecksumReader<'a, R>>>>> },
09785b27
DM
794}
795
796/// Read data blobs
797pub struct DataBlobReader<'a, R: Read> {
798 state: BlobReaderState<'a, R>,
1f26fdef
DM
799}
800
09785b27 801impl <'a, R: Read> DataBlobReader<'a, R> {
1f26fdef 802
4bfa147e 803 pub fn new(mut reader: R, config: Option<&'a CryptConfig>) -> Result<Self, Error> {
1f26fdef
DM
804
805 let head: DataBlobHeader = unsafe { reader.read_le_value()? };
09785b27
DM
806 match head.magic {
807 UNCOMPRESSED_BLOB_MAGIC_1_0 => {
808 let expected_crc = u32::from_le_bytes(head.crc);
809 let csum_reader = ChecksumReader::new(reader, None);
810 Ok(Self { state: BlobReaderState::Uncompressed { expected_crc, csum_reader }})
811 }
812 COMPRESSED_BLOB_MAGIC_1_0 => {
813 let expected_crc = u32::from_le_bytes(head.crc);
814 let csum_reader = ChecksumReader::new(reader, None);
815
816 let decompr = zstd::stream::read::Decoder::new(csum_reader)?;
817 Ok(Self { state: BlobReaderState::Compressed { expected_crc, decompr }})
818 }
4bfa147e
DM
819 AUTHENTICATED_BLOB_MAGIC_1_0 => {
820 let expected_crc = u32::from_le_bytes(head.crc);
821 let mut expected_hmac = [0u8; 32];
822 reader.read_exact(&mut expected_hmac)?;
823 let signer = config.map(|c| c.data_signer());
824 let csum_reader = ChecksumReader::new(reader, signer);
825 Ok(Self { state: BlobReaderState::Signed { expected_crc, expected_hmac, csum_reader }})
826 }
e9a385a7
DM
827 AUTH_COMPR_BLOB_MAGIC_1_0 => {
828 let expected_crc = u32::from_le_bytes(head.crc);
829 let mut expected_hmac = [0u8; 32];
830 reader.read_exact(&mut expected_hmac)?;
831 let signer = config.map(|c| c.data_signer());
832 let csum_reader = ChecksumReader::new(reader, signer);
833
834 let decompr = zstd::stream::read::Decoder::new(csum_reader)?;
835 Ok(Self { state: BlobReaderState::SignedCompressed { expected_crc, expected_hmac, decompr }})
836 }
2aa0bfff
DM
837 ENCRYPTED_BLOB_MAGIC_1_0 => {
838 let expected_crc = u32::from_le_bytes(head.crc);
839 let mut iv = [0u8; 16];
840 let mut expected_tag = [0u8; 16];
841 reader.read_exact(&mut iv)?;
842 reader.read_exact(&mut expected_tag)?;
843 let csum_reader = ChecksumReader::new(reader, None);
60822163 844 let decrypt_reader = CryptReader::new(BufReader::with_capacity(64*1024, csum_reader), iv, expected_tag, config.unwrap())?;
2aa0bfff
DM
845 Ok(Self { state: BlobReaderState::Encrypted { expected_crc, decrypt_reader }})
846 }
548c9489
DM
847 ENCR_COMPR_BLOB_MAGIC_1_0 => {
848 let expected_crc = u32::from_le_bytes(head.crc);
849 let mut iv = [0u8; 16];
850 let mut expected_tag = [0u8; 16];
851 reader.read_exact(&mut iv)?;
852 reader.read_exact(&mut expected_tag)?;
853 let csum_reader = ChecksumReader::new(reader, None);
60822163 854 let decrypt_reader = CryptReader::new(BufReader::with_capacity(64*1024, csum_reader), iv, expected_tag, config.unwrap())?;
548c9489
DM
855 let decompr = zstd::stream::read::Decoder::new(decrypt_reader)?;
856 Ok(Self { state: BlobReaderState::EncryptedCompressed { expected_crc, decompr }})
857 }
09785b27 858 _ => bail!("got wrong magic number {:?}", head.magic)
1f26fdef 859 }
09785b27
DM
860 }
861
862 pub fn finish(self) -> Result<R, Error> {
4bfa147e
DM
863 match self.state {
864 BlobReaderState::Uncompressed { csum_reader, expected_crc } => {
865 let (reader, crc, _) = csum_reader.finish()?;
866 if crc != expected_crc {
867 bail!("blob crc check failed");
868 }
869 Ok(reader)
870 }
871 BlobReaderState::Compressed { expected_crc, decompr } => {
872 let csum_reader = decompr.finish().into_inner();
873 let (reader, crc, _) = csum_reader.finish()?;
874 if crc != expected_crc {
875 bail!("blob crc check failed");
876 }
877 Ok(reader)
878 }
879 BlobReaderState::Signed { csum_reader, expected_crc, expected_hmac } => {
880 let (reader, crc, hmac) = csum_reader.finish()?;
881 if crc != expected_crc {
882 bail!("blob crc check failed");
883 }
884 if let Some(hmac) = hmac {
885 if hmac != expected_hmac {
886 bail!("blob signature check failed");
887 }
888 }
889 Ok(reader)
890 }
e9a385a7
DM
891 BlobReaderState::SignedCompressed { expected_crc, expected_hmac, decompr } => {
892 let csum_reader = decompr.finish().into_inner();
893 let (reader, crc, hmac) = csum_reader.finish()?;
894 if crc != expected_crc {
895 bail!("blob crc check failed");
896 }
897 if let Some(hmac) = hmac {
898 if hmac != expected_hmac {
899 bail!("blob signature check failed");
900 }
901 }
902 Ok(reader)
903 }
2aa0bfff
DM
904 BlobReaderState::Encrypted { expected_crc, decrypt_reader } => {
905 let csum_reader = decrypt_reader.finish()?.into_inner();
906 let (reader, crc, _) = csum_reader.finish()?;
907 if crc != expected_crc {
908 bail!("blob crc check failed");
909 }
910 Ok(reader)
911 }
548c9489
DM
912 BlobReaderState::EncryptedCompressed { expected_crc, decompr } => {
913 let decrypt_reader = decompr.finish().into_inner();
914 let csum_reader = decrypt_reader.finish()?.into_inner();
915 let (reader, crc, _) = csum_reader.finish()?;
916 if crc != expected_crc {
917 bail!("blob crc check failed");
918 }
919 Ok(reader)
920 }
4bfa147e 921 }
1f26fdef
DM
922 }
923}
924
09785b27 925impl <'a, R: BufRead> Read for DataBlobReader<'a, R> {
1f26fdef
DM
926
927 fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
09785b27
DM
928 match &mut self.state {
929 BlobReaderState::Uncompressed { csum_reader, .. } => {
930 csum_reader.read(buf)
931 }
932 BlobReaderState::Compressed { decompr, .. } => {
933 decompr.read(buf)
1f26fdef 934 }
4bfa147e
DM
935 BlobReaderState::Signed { csum_reader, .. } => {
936 csum_reader.read(buf)
937 }
e9a385a7
DM
938 BlobReaderState::SignedCompressed { decompr, .. } => {
939 decompr.read(buf)
940 }
2aa0bfff
DM
941 BlobReaderState::Encrypted { decrypt_reader, .. } => {
942 decrypt_reader.read(buf)
943 }
548c9489
DM
944 BlobReaderState::EncryptedCompressed { decompr, .. } => {
945 decompr.read(buf)
946 }
1f26fdef 947 }
1f26fdef
DM
948 }
949}