]> git.proxmox.com Git - proxmox-backup.git/blob - src/backup/data_blob.rs
src/backup/data_blob.rs - DataBlobReader: start serious impl.
[proxmox-backup.git] / src / backup / data_blob.rs
1 use failure::*;
2 use std::convert::TryInto;
3
4 use proxmox::tools::io::{ReadExt, WriteExt};
5
6 const MAX_BLOB_SIZE: usize = 128*1024*1024;
7
8 use super::*;
9
10 /// Data blob binary storage format
11 ///
12 /// Data blobs store arbitrary binary data (< 128MB), and can be
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 ///
17 pub struct DataBlob {
18 raw_data: Vec<u8>, // tagged, compressed, encryped data
19 }
20
21 impl DataBlob {
22
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
40 /// accessor to raw_data field
41 pub fn raw_data(&self) -> &[u8] {
42 &self.raw_data
43 }
44
45 /// Consume self and returns raw_data
46 pub fn into_inner(self) -> Vec<u8> {
47 self.raw_data
48 }
49
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
55 /// accessor to crc32 checksum
56 pub fn crc(&self) -> u32 {
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())
59 }
60
61 // set the CRC checksum field
62 pub fn set_crc(&mut self, crc: u32) {
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());
65 }
66
67 /// compute the CRC32 checksum
68 pub fn compute_crc(&self) -> u32 {
69 let mut hasher = crc32fast::Hasher::new();
70 let start = Self::header_size(self.magic()); // start after HEAD
71 hasher.update(&self.raw_data[start..]);
72 hasher.finalize()
73 }
74
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
84 /// Create a DataBlob, optionally compressed and/or encrypted
85 pub fn encode(
86 data: &[u8],
87 config: Option<&CryptConfig>,
88 compress: bool,
89 ) -> Result<Self, Error> {
90
91 if data.len() > MAX_BLOB_SIZE {
92 bail!("data blob too large ({} bytes).", data.len());
93 }
94
95 let mut blob = if let Some(config) = config {
96
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 };
118 unsafe {
119 raw_data.write_le_value(dummy_head)?;
120 }
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
128 unsafe {
129 (&mut raw_data[0..header_len]).write_le_value(head)?;
130 }
131
132 DataBlob { raw_data }
133 } else {
134
135 let max_data_len = data.len() + std::mem::size_of::<DataBlobHeader>();
136 if compress {
137 let mut comp_data = Vec::with_capacity(max_data_len);
138
139 let head = DataBlobHeader {
140 magic: COMPRESSED_BLOB_MAGIC_1_0,
141 crc: [0; 4],
142 };
143 unsafe {
144 comp_data.write_le_value(head)?;
145 }
146
147 zstd::stream::copy_encode(data, &mut comp_data, 1)?;
148
149 if comp_data.len() < max_data_len {
150 let mut blob = DataBlob { raw_data: comp_data };
151 blob.set_crc(blob.compute_crc());
152 return Ok(blob);
153 }
154 }
155
156 let mut raw_data = Vec::with_capacity(max_data_len);
157
158 let head = DataBlobHeader {
159 magic: UNCOMPRESSED_BLOB_MAGIC_1_0,
160 crc: [0; 4],
161 };
162 unsafe {
163 raw_data.write_le_value(head)?;
164 }
165 raw_data.extend_from_slice(data);
166
167 DataBlob { raw_data }
168 };
169
170 blob.set_crc(blob.compute_crc());
171
172 Ok(blob)
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 {
181 let data_start = std::mem::size_of::<DataBlobHeader>();
182 return Ok(self.raw_data[data_start..].to_vec());
183 } else if magic == &COMPRESSED_BLOB_MAGIC_1_0 {
184 let data_start = std::mem::size_of::<DataBlobHeader>();
185 let data = zstd::block::decompress(&self.raw_data[data_start..], MAX_BLOB_SIZE)?;
186 return Ok(data);
187 } else if magic == &ENCR_COMPR_BLOB_MAGIC_1_0 || magic == &ENCRYPTED_BLOB_MAGIC_1_0 {
188 let header_len = std::mem::size_of::<EncryptedDataBlobHeader>();
189 let head = unsafe {
190 (&self.raw_data[..header_len]).read_le_value::<EncryptedDataBlobHeader>()?
191 };
192
193 if let Some(config) = config {
194 let data = if magic == &ENCR_COMPR_BLOB_MAGIC_1_0 {
195 config.decode_compressed_chunk(&self.raw_data[header_len..], &head.iv, &head.tag)?
196 } else {
197 config.decode_uncompressed_chunk(&self.raw_data[header_len..], &head.iv, &head.tag)?
198 };
199 return Ok(data);
200 } else {
201 bail!("unable to decrypt blob - missing CryptConfig");
202 }
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 }
225 } else {
226 bail!("Invalid blob magic number.");
227 }
228 }
229
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
237 if data.len() > MAX_BLOB_SIZE {
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
266 let mut blob = DataBlob { raw_data };
267 blob.set_crc(blob.compute_crc());
268
269 return Ok(blob);
270 }
271
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
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
302 Ok(blob)
303 } else {
304 bail!("unable to parse raw blob - wrong magic");
305 }
306 }
307
308 }
309
310 use std::io::{Read, BufRead, BufReader, Write, Seek, SeekFrom};
311
312 struct CryptWriter<W> {
313 writer: W,
314 encr_buf: [u8; 64*1024],
315 iv: [u8; 16],
316 crypter: openssl::symm::Crypter,
317 }
318
319 impl <W: Write> CryptWriter<W> {
320
321 fn new(writer: W, config: &CryptConfig) -> Result<Self, Error> {
322 let mut iv = [0u8; 16];
323 proxmox::sys::linux::fill_with_random_data(&mut iv)?;
324
325 let crypter = config.data_crypter(&iv, openssl::symm::Mode::Encrypt)?;
326
327 Ok(Self { writer, iv, crypter, encr_buf: [0u8; 64*1024] })
328 }
329
330 fn finish(mut self) -> Result<(W, [u8; 16], [u8; 16]), Error> {
331 let rest = self.crypter.finalize(&mut self.encr_buf)?;
332 if rest > 0 {
333 self.writer.write_all(&self.encr_buf[..rest])?;
334 }
335
336 self.writer.flush()?;
337
338 let mut tag = [0u8; 16];
339 self.crypter.get_tag(&mut tag)?;
340
341 Ok((self.writer, self.iv, tag))
342 }
343 }
344
345 impl <W: Write> Write for CryptWriter<W> {
346
347 fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
348 let count = self.crypter.update(buf, &mut self.encr_buf)
349 .map_err(|err| {
350 std::io::Error::new(
351 std::io::ErrorKind::Other,
352 format!("crypter update failed - {}", err))
353 })?;
354
355 self.writer.write_all(&self.encr_buf[..count])?;
356
357 Ok(count)
358 }
359
360 fn flush(&mut self) -> Result<(), std::io::Error> {
361 Ok(())
362 }
363 }
364
365 struct ChecksumWriter<'a, W> {
366 writer: W,
367 hasher: crc32fast::Hasher,
368 signer: Option<openssl::sign::Signer<'a>>,
369 }
370
371 impl <'a, W: Write> ChecksumWriter<'a, W> {
372
373 fn new(writer: W, signer: Option<openssl::sign::Signer<'a>>) -> Self {
374 let hasher = crc32fast::Hasher::new();
375 Self { writer, hasher, signer }
376 }
377
378 pub fn finish(mut self) -> Result<(W, u32, Option<[u8; 32]>), Error> {
379 let crc = self.hasher.finalize();
380
381 if let Some(ref mut signer) = self.signer {
382 let mut tag = [0u8; 32];
383 signer.sign(&mut tag)?;
384 Ok((self.writer, crc, Some(tag)))
385 } else {
386 Ok((self.writer, crc, None))
387 }
388 }
389 }
390
391 impl <'a, W: Write> Write for ChecksumWriter<'a, W> {
392
393 fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
394 self.hasher.update(buf);
395 if let Some(ref mut signer) = self.signer {
396 signer.update(buf)
397 .map_err(|err| {
398 std::io::Error::new(
399 std::io::ErrorKind::Other,
400 format!("hmac update failed - {}", err))
401 })?;
402 }
403 self.writer.write(buf)
404 }
405
406 fn flush(&mut self) -> Result<(), std::io::Error> {
407 self.writer.flush()
408 }
409 }
410
411 enum BlobWriterState<'a, W: Write> {
412 Uncompressed { csum_writer: ChecksumWriter<'a, W> },
413 Compressed { compr: zstd::stream::write::Encoder<ChecksumWriter<'a, W>> },
414 Signed { csum_writer: ChecksumWriter<'a, W> },
415 SignedCompressed { compr: zstd::stream::write::Encoder<ChecksumWriter<'a, W>> },
416 Encrypted { crypt_writer: CryptWriter<ChecksumWriter<'a, W>> },
417 EncryptedCompressed { compr: zstd::stream::write::Encoder<CryptWriter<ChecksumWriter<'a, W>>> },
418 }
419
420 /// Write compressed data blobs
421 pub struct DataBlobWriter<'a, W: Write> {
422 state: BlobWriterState<'a, W>,
423 }
424
425 impl <'a, W: Write + Seek> DataBlobWriter<'a, W> {
426
427 pub fn new_uncompressed(mut writer: W) -> Result<Self, Error> {
428 writer.seek(SeekFrom::Start(0))?;
429 let head = DataBlobHeader { magic: UNCOMPRESSED_BLOB_MAGIC_1_0, crc: [0; 4] };
430 unsafe {
431 writer.write_le_value(head)?;
432 }
433 let csum_writer = ChecksumWriter::new(writer, None);
434 Ok(Self { state: BlobWriterState::Uncompressed { csum_writer }})
435 }
436
437 pub fn new_compressed(mut writer: W) -> Result<Self, Error> {
438 writer.seek(SeekFrom::Start(0))?;
439 let head = DataBlobHeader { magic: COMPRESSED_BLOB_MAGIC_1_0, crc: [0; 4] };
440 unsafe {
441 writer.write_le_value(head)?;
442 }
443 let csum_writer = ChecksumWriter::new(writer, None);
444 let compr = zstd::stream::write::Encoder::new(csum_writer, 1)?;
445 Ok(Self { state: BlobWriterState::Compressed { compr }})
446 }
447
448 pub fn new_signed(mut writer: W, config: &'a CryptConfig) -> Result<Self, Error> {
449 writer.seek(SeekFrom::Start(0))?;
450 let head = AuthenticatedDataBlobHeader {
451 head: DataBlobHeader { magic: AUTHENTICATED_BLOB_MAGIC_1_0, crc: [0; 4] },
452 tag: [0u8; 32],
453 };
454 unsafe {
455 writer.write_le_value(head)?;
456 }
457 let signer = config.data_signer();
458 let csum_writer = ChecksumWriter::new(writer, Some(signer));
459 Ok(Self { state: BlobWriterState::Signed { csum_writer }})
460 }
461
462 pub fn new_signed_compressed(mut writer: W, config: &'a CryptConfig) -> Result<Self, Error> {
463 writer.seek(SeekFrom::Start(0))?;
464 let head = AuthenticatedDataBlobHeader {
465 head: DataBlobHeader { magic: AUTH_COMPR_BLOB_MAGIC_1_0, crc: [0; 4] },
466 tag: [0u8; 32],
467 };
468 unsafe {
469 writer.write_le_value(head)?;
470 }
471 let signer = config.data_signer();
472 let csum_writer = ChecksumWriter::new(writer, Some(signer));
473 let compr = zstd::stream::write::Encoder::new(csum_writer, 1)?;
474 Ok(Self { state: BlobWriterState::SignedCompressed { compr }})
475 }
476
477 pub fn new_encrypted(mut writer: W, config: &'a CryptConfig) -> Result<Self, Error> {
478 writer.seek(SeekFrom::Start(0))?;
479 let head = EncryptedDataBlobHeader {
480 head: DataBlobHeader { magic: ENCRYPTED_BLOB_MAGIC_1_0, crc: [0; 4] },
481 iv: [0u8; 16],
482 tag: [0u8; 16],
483 };
484 unsafe {
485 writer.write_le_value(head)?;
486 }
487
488 let csum_writer = ChecksumWriter::new(writer, None);
489 let crypt_writer = CryptWriter::new(csum_writer, config)?;
490 Ok(Self { state: BlobWriterState::Encrypted { crypt_writer }})
491 }
492
493 pub fn new_encrypted_compressed(mut writer: W, config: &'a CryptConfig) -> Result<Self, Error> {
494 writer.seek(SeekFrom::Start(0))?;
495 let head = EncryptedDataBlobHeader {
496 head: DataBlobHeader { magic: ENCR_COMPR_BLOB_MAGIC_1_0, crc: [0; 4] },
497 iv: [0u8; 16],
498 tag: [0u8; 16],
499 };
500 unsafe {
501 writer.write_le_value(head)?;
502 }
503
504 let csum_writer = ChecksumWriter::new(writer, None);
505 let crypt_writer = CryptWriter::new(csum_writer, config)?;
506 let compr = zstd::stream::write::Encoder::new(crypt_writer, 1)?;
507 Ok(Self { state: BlobWriterState::EncryptedCompressed { compr }})
508 }
509
510 pub fn finish(self) -> Result<W, Error> {
511 match self.state {
512 BlobWriterState::Uncompressed { csum_writer } => {
513 // write CRC
514 let (mut writer, crc, _) = csum_writer.finish()?;
515 let head = DataBlobHeader { magic: UNCOMPRESSED_BLOB_MAGIC_1_0, crc: crc.to_le_bytes() };
516
517 writer.seek(SeekFrom::Start(0))?;
518 unsafe {
519 writer.write_le_value(head)?;
520 }
521
522 return Ok(writer)
523 }
524 BlobWriterState::Compressed { compr } => {
525 let csum_writer = compr.finish()?;
526 let (mut writer, crc, _) = csum_writer.finish()?;
527
528 let head = DataBlobHeader { magic: COMPRESSED_BLOB_MAGIC_1_0, crc: crc.to_le_bytes() };
529
530 writer.seek(SeekFrom::Start(0))?;
531 unsafe {
532 writer.write_le_value(head)?;
533 }
534
535 return Ok(writer)
536 }
537 BlobWriterState::Signed { csum_writer } => {
538 let (mut writer, crc, tag) = csum_writer.finish()?;
539
540 let head = AuthenticatedDataBlobHeader {
541 head: DataBlobHeader { magic: AUTHENTICATED_BLOB_MAGIC_1_0, crc: crc.to_le_bytes() },
542 tag: tag.unwrap(),
543 };
544
545 writer.seek(SeekFrom::Start(0))?;
546 unsafe {
547 writer.write_le_value(head)?;
548 }
549
550 return Ok(writer)
551 }
552 BlobWriterState::SignedCompressed { compr } => {
553 let csum_writer = compr.finish()?;
554 let (mut writer, crc, tag) = csum_writer.finish()?;
555
556 let head = AuthenticatedDataBlobHeader {
557 head: DataBlobHeader { magic: AUTH_COMPR_BLOB_MAGIC_1_0, crc: crc.to_le_bytes() },
558 tag: tag.unwrap(),
559 };
560
561 writer.seek(SeekFrom::Start(0))?;
562 unsafe {
563 writer.write_le_value(head)?;
564 }
565
566 return Ok(writer)
567 }
568 BlobWriterState::Encrypted { crypt_writer } => {
569 let (csum_writer, iv, tag) = crypt_writer.finish()?;
570 let (mut writer, crc, _) = csum_writer.finish()?;
571
572 let head = EncryptedDataBlobHeader {
573 head: DataBlobHeader { magic: ENCRYPTED_BLOB_MAGIC_1_0, crc: crc.to_le_bytes() },
574 iv, tag,
575 };
576 writer.seek(SeekFrom::Start(0))?;
577 unsafe {
578 writer.write_le_value(head)?;
579 }
580 return Ok(writer)
581 }
582 BlobWriterState::EncryptedCompressed { compr } => {
583 let crypt_writer = compr.finish()?;
584 let (csum_writer, iv, tag) = crypt_writer.finish()?;
585 let (mut writer, crc, _) = csum_writer.finish()?;
586
587 let head = EncryptedDataBlobHeader {
588 head: DataBlobHeader { magic: ENCR_COMPR_BLOB_MAGIC_1_0, crc: crc.to_le_bytes() },
589 iv, tag,
590 };
591 writer.seek(SeekFrom::Start(0))?;
592 unsafe {
593 writer.write_le_value(head)?;
594 }
595 return Ok(writer)
596 }
597 }
598 }
599 }
600
601 impl <'a, W: Write + Seek> Write for DataBlobWriter<'a, W> {
602
603 fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
604 match self.state {
605 BlobWriterState::Uncompressed { ref mut csum_writer } => {
606 csum_writer.write(buf)
607 }
608 BlobWriterState::Compressed { ref mut compr } => {
609 compr.write(buf)
610 }
611 BlobWriterState::Signed { ref mut csum_writer } => {
612 csum_writer.write(buf)
613 }
614 BlobWriterState::SignedCompressed { ref mut compr } => {
615 compr.write(buf)
616 }
617 BlobWriterState::Encrypted { ref mut crypt_writer } => {
618 crypt_writer.write(buf)
619 }
620 BlobWriterState::EncryptedCompressed { ref mut compr } => {
621 compr.write(buf)
622 }
623 }
624 }
625
626 fn flush(&mut self) -> Result<(), std::io::Error> {
627 match self.state {
628 BlobWriterState::Uncompressed { ref mut csum_writer } => {
629 csum_writer.flush()
630 }
631 BlobWriterState::Compressed { ref mut compr } => {
632 compr.flush()
633 }
634 BlobWriterState::Signed { ref mut csum_writer } => {
635 csum_writer.flush()
636 }
637 BlobWriterState::SignedCompressed { ref mut compr } => {
638 compr.flush()
639 }
640 BlobWriterState::Encrypted { ref mut crypt_writer } => {
641 crypt_writer.flush()
642 }
643 BlobWriterState::EncryptedCompressed { ref mut compr } => {
644 compr.flush()
645 }
646 }
647 }
648 }
649
650 struct ChecksumReader<'a, R> {
651 reader: R,
652 hasher: crc32fast::Hasher,
653 signer: Option<openssl::sign::Signer<'a>>,
654 }
655
656 impl <'a, R: Read> ChecksumReader<'a, R> {
657
658 fn new(reader: R, signer: Option<openssl::sign::Signer<'a>>) -> Self {
659 let hasher = crc32fast::Hasher::new();
660 Self { reader, hasher, signer }
661 }
662
663 pub fn finish(mut self) -> Result<(R, u32, Option<[u8; 32]>), Error> {
664 let crc = self.hasher.finalize();
665
666 if let Some(ref mut signer) = self.signer {
667 let mut tag = [0u8; 32];
668 signer.sign(&mut tag)?;
669 Ok((self.reader, crc, Some(tag)))
670 } else {
671 Ok((self.reader, crc, None))
672 }
673 }
674 }
675
676 impl <'a, R: Read> Read for ChecksumReader<'a, R> {
677
678 fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
679 let count = self.reader.read(buf)?;
680 if count > 0 {
681 self.hasher.update(&buf[..count]);
682 if let Some(ref mut signer) = self.signer {
683 signer.update(&buf[..count])
684 .map_err(|err| {
685 std::io::Error::new(
686 std::io::ErrorKind::Other,
687 format!("hmac update failed - {}", err))
688 })?;
689 }
690 }
691 Ok(count)
692 }
693 }
694
695 enum BlobReaderState<'a, R: Read> {
696 Uncompressed { expected_crc: u32, csum_reader: ChecksumReader<'a, R> },
697 Compressed { expected_crc: u32, decompr: zstd::stream::read::Decoder<BufReader<ChecksumReader<'a, R>>> },
698 }
699
700 /// Read data blobs
701 pub struct DataBlobReader<'a, R: Read> {
702 state: BlobReaderState<'a, R>,
703 }
704
705 impl <'a, R: Read> DataBlobReader<'a, R> {
706
707 pub fn new(mut reader: R) -> Result<Self, Error> {
708
709 let head: DataBlobHeader = unsafe { reader.read_le_value()? };
710 match head.magic {
711 UNCOMPRESSED_BLOB_MAGIC_1_0 => {
712 let expected_crc = u32::from_le_bytes(head.crc);
713 let csum_reader = ChecksumReader::new(reader, None);
714 Ok(Self { state: BlobReaderState::Uncompressed { expected_crc, csum_reader }})
715 }
716 COMPRESSED_BLOB_MAGIC_1_0 => {
717 let expected_crc = u32::from_le_bytes(head.crc);
718 let csum_reader = ChecksumReader::new(reader, None);
719
720 let decompr = zstd::stream::read::Decoder::new(csum_reader)?;
721 Ok(Self { state: BlobReaderState::Compressed { expected_crc, decompr }})
722 }
723 _ => bail!("got wrong magic number {:?}", head.magic)
724 }
725 }
726
727 pub fn finish(self) -> Result<R, Error> {
728 match self.state {
729 BlobReaderState::Uncompressed { csum_reader, expected_crc } => {
730 let (reader, crc, _) = csum_reader.finish()?;
731 if crc != expected_crc {
732 bail!("blob crc check failed");
733 }
734 Ok(reader)
735 }
736 BlobReaderState::Compressed { expected_crc, decompr } => {
737 let csum_reader = decompr.finish().into_inner();
738 let (reader, crc, _) = csum_reader.finish()?;
739 if crc != expected_crc {
740 bail!("blob crc check failed");
741 }
742 Ok(reader)
743 }
744 }
745 }
746 }
747
748 impl <'a, R: BufRead> Read for DataBlobReader<'a, R> {
749
750 fn read(&mut self, buf: &mut [u8]) -> Result<usize, std::io::Error> {
751 match &mut self.state {
752 BlobReaderState::Uncompressed { csum_reader, .. } => {
753 csum_reader.read(buf)
754 }
755 BlobReaderState::Compressed { decompr, .. } => {
756 decompr.read(buf)
757 }
758 }
759 }
760 }