1 use anyhow
::{bail, Error}
;
2 use std
::convert
::TryInto
;
4 use proxmox
::tools
::io
::{ReadExt, WriteExt}
;
6 use super::file_formats
::*;
7 use super::{CryptConfig, CryptMode}
;
9 const MAX_BLOB_SIZE
: usize = 128*1024*1024;
11 /// Encoded data chunk with digest and positional information
12 pub struct ChunkInfo
{
19 /// Data blob binary storage format
21 /// Data blobs store arbitrary binary data (< 128MB), and can be
22 /// compressed and encrypted (or just signed). A simply binary format
23 /// is used to store them on disk or transfer them over the network.
25 /// Please use index files to store large data files (".fidx" of
29 raw_data
: Vec
<u8>, // tagged, compressed, encryped data
34 /// accessor to raw_data field
35 pub fn raw_data(&self) -> &[u8] {
39 /// Consume self and returns raw_data
40 pub fn into_inner(self) -> Vec
<u8> {
44 /// accessor to chunk type (magic number)
45 pub fn magic(&self) -> &[u8; 8] {
46 self.raw_data
[0..8].try_into().unwrap()
49 /// accessor to crc32 checksum
50 pub fn crc(&self) -> u32 {
51 let crc_o
= proxmox
::offsetof!(DataBlobHeader
, crc
);
52 u32::from_le_bytes(self.raw_data
[crc_o
..crc_o
+4].try_into().unwrap())
55 // set the CRC checksum field
56 pub fn set_crc(&mut self, crc
: u32) {
57 let crc_o
= proxmox
::offsetof!(DataBlobHeader
, crc
);
58 self.raw_data
[crc_o
..crc_o
+4].copy_from_slice(&crc
.to_le_bytes());
61 /// compute the CRC32 checksum
62 pub fn compute_crc(&self) -> u32 {
63 let mut hasher
= crc32fast
::Hasher
::new();
64 let start
= header_size(self.magic()); // start after HEAD
65 hasher
.update(&self.raw_data
[start
..]);
69 /// verify the CRC32 checksum
70 pub fn verify_crc(&self) -> Result
<(), Error
> {
71 let expected_crc
= self.compute_crc();
72 if expected_crc
!= self.crc() {
73 bail
!("Data blob has wrong CRC checksum.");
78 /// Create a DataBlob, optionally compressed and/or encrypted
81 config
: Option
<&CryptConfig
>,
83 ) -> Result
<Self, Error
> {
85 if data
.len() > MAX_BLOB_SIZE
{
86 bail
!("data blob too large ({} bytes).", data
.len());
89 let mut blob
= if let Some(config
) = config
{
92 let (_compress
, data
, magic
) = if compress
{
93 compr_data
= zstd
::block
::compress(data
, 1)?
;
94 // Note: We only use compression if result is shorter
95 if compr_data
.len() < data
.len() {
96 (true, &compr_data
[..], ENCR_COMPR_BLOB_MAGIC_1_0
)
98 (false, data
, ENCRYPTED_BLOB_MAGIC_1_0
)
101 (false, data
, ENCRYPTED_BLOB_MAGIC_1_0
)
104 let header_len
= std
::mem
::size_of
::<EncryptedDataBlobHeader
>();
105 let mut raw_data
= Vec
::with_capacity(data
.len() + header_len
);
107 let dummy_head
= EncryptedDataBlobHeader
{
108 head
: DataBlobHeader { magic: [0u8; 8], crc: [0; 4] }
,
113 raw_data
.write_le_value(dummy_head
)?
;
116 let (iv
, tag
) = config
.encrypt_to(data
, &mut raw_data
)?
;
118 let head
= EncryptedDataBlobHeader
{
119 head
: DataBlobHeader { magic, crc: [0; 4] }
, iv
, tag
,
123 (&mut raw_data
[0..header_len
]).write_le_value(head
)?
;
126 DataBlob { raw_data }
129 let max_data_len
= data
.len() + std
::mem
::size_of
::<DataBlobHeader
>();
131 let mut comp_data
= Vec
::with_capacity(max_data_len
);
133 let head
= DataBlobHeader
{
134 magic
: COMPRESSED_BLOB_MAGIC_1_0
,
138 comp_data
.write_le_value(head
)?
;
141 zstd
::stream
::copy_encode(data
, &mut comp_data
, 1)?
;
143 if comp_data
.len() < max_data_len
{
144 let mut blob
= DataBlob { raw_data: comp_data }
;
145 blob
.set_crc(blob
.compute_crc());
150 let mut raw_data
= Vec
::with_capacity(max_data_len
);
152 let head
= DataBlobHeader
{
153 magic
: UNCOMPRESSED_BLOB_MAGIC_1_0
,
157 raw_data
.write_le_value(head
)?
;
159 raw_data
.extend_from_slice(data
);
161 DataBlob { raw_data }
164 blob
.set_crc(blob
.compute_crc());
169 /// Get the encryption mode for this blob.
170 pub fn crypt_mode(&self) -> Result
<CryptMode
, Error
> {
171 let magic
= self.magic();
173 Ok(if magic
== &UNCOMPRESSED_BLOB_MAGIC_1_0
|| magic
== &COMPRESSED_BLOB_MAGIC_1_0
{
175 } else if magic
== &ENCR_COMPR_BLOB_MAGIC_1_0
|| magic
== &ENCRYPTED_BLOB_MAGIC_1_0
{
178 bail
!("Invalid blob magic number.");
183 pub fn decode(&self, config
: Option
<&CryptConfig
>) -> Result
<Vec
<u8>, Error
> {
185 let magic
= self.magic();
187 if magic
== &UNCOMPRESSED_BLOB_MAGIC_1_0
{
188 let data_start
= std
::mem
::size_of
::<DataBlobHeader
>();
189 Ok(self.raw_data
[data_start
..].to_vec())
190 } else if magic
== &COMPRESSED_BLOB_MAGIC_1_0
{
191 let data_start
= std
::mem
::size_of
::<DataBlobHeader
>();
192 let data
= zstd
::block
::decompress(&self.raw_data
[data_start
..], MAX_BLOB_SIZE
)?
;
194 } else if magic
== &ENCR_COMPR_BLOB_MAGIC_1_0
|| magic
== &ENCRYPTED_BLOB_MAGIC_1_0
{
195 let header_len
= std
::mem
::size_of
::<EncryptedDataBlobHeader
>();
197 (&self.raw_data
[..header_len
]).read_le_value
::<EncryptedDataBlobHeader
>()?
200 if let Some(config
) = config
{
201 let data
= if magic
== &ENCR_COMPR_BLOB_MAGIC_1_0
{
202 config
.decode_compressed_chunk(&self.raw_data
[header_len
..], &head
.iv
, &head
.tag
)?
204 config
.decode_uncompressed_chunk(&self.raw_data
[header_len
..], &head
.iv
, &head
.tag
)?
208 bail
!("unable to decrypt blob - missing CryptConfig");
211 bail
!("Invalid blob magic number.");
215 /// Load blob from ``reader``
216 pub fn load(reader
: &mut dyn std
::io
::Read
) -> Result
<Self, Error
> {
218 let mut data
= Vec
::with_capacity(1024*1024);
219 reader
.read_to_end(&mut data
)?
;
224 /// Create Instance from raw data
225 pub fn from_raw(data
: Vec
<u8>) -> Result
<Self, Error
> {
227 if data
.len() < std
::mem
::size_of
::<DataBlobHeader
>() {
228 bail
!("blob too small ({} bytes).", data
.len());
231 let magic
= &data
[0..8];
233 if magic
== ENCR_COMPR_BLOB_MAGIC_1_0
|| magic
== ENCRYPTED_BLOB_MAGIC_1_0
{
235 if data
.len() < std
::mem
::size_of
::<EncryptedDataBlobHeader
>() {
236 bail
!("encrypted blob too small ({} bytes).", data
.len());
239 let blob
= DataBlob { raw_data: data }
;
242 } else if magic
== COMPRESSED_BLOB_MAGIC_1_0
|| magic
== UNCOMPRESSED_BLOB_MAGIC_1_0
{
244 let blob
= DataBlob { raw_data: data }
;
248 bail
!("unable to parse raw blob - wrong magic");
252 /// Verify digest and data length for unencrypted chunks.
254 /// To do that, we need to decompress data first. Please note that
255 /// this is not possible for encrypted chunks. This function simply return Ok
256 /// for encrypted chunks.
257 /// Note: This does not call verify_crc
258 pub fn verify_unencrypted(
260 expected_chunk_size
: usize,
261 expected_digest
: &[u8; 32],
262 ) -> Result
<(), Error
> {
264 let magic
= self.magic();
266 if magic
== &ENCR_COMPR_BLOB_MAGIC_1_0
|| magic
== &ENCRYPTED_BLOB_MAGIC_1_0
{
270 let data
= self.decode(None
)?
;
272 if expected_chunk_size
!= data
.len() {
273 bail
!("detected chunk with wrong length ({} != {})", expected_chunk_size
, data
.len());
275 let digest
= openssl
::sha
::sha256(&data
);
276 if &digest
!= expected_digest
{
277 bail
!("detected chunk with wrong digest.");
284 /// Builder for chunk DataBlobs
286 /// Main purpose is to centralize digest computation. Digest
287 /// computation differ for encryped chunk, and this interface ensures that
288 /// we always compute the correct one.
289 pub struct DataChunkBuilder
<'a
, 'b
> {
290 config
: Option
<&'b CryptConfig
>,
292 digest_computed
: bool
,
297 impl <'a
, 'b
> DataChunkBuilder
<'a
, 'b
> {
299 /// Create a new builder instance.
300 pub fn new(orig_data
: &'a
[u8]) -> Self {
304 digest_computed
: false,
310 /// Set compression flag.
312 /// If true, chunk data is compressed using zstd (level 1).
313 pub fn compress(mut self, value
: bool
) -> Self {
314 self.compress
= value
;
318 /// Set encryption Configuration
320 /// If set, chunks are encrypted
321 pub fn crypt_config(mut self, value
: &'b CryptConfig
) -> Self {
322 if self.digest_computed
{
323 panic
!("unable to set crypt_config after compute_digest().");
325 self.config
= Some(value
);
329 fn compute_digest(&mut self) {
330 if !self.digest_computed
{
331 if let Some(ref config
) = self.config
{
332 self.digest
= config
.compute_digest(self.orig_data
);
334 self.digest
= openssl
::sha
::sha256(self.orig_data
);
336 self.digest_computed
= true;
340 /// Returns the chunk Digest
342 /// Note: For encrypted chunks, this needs to be called after
343 /// ``crypt_config``.
344 pub fn digest(&mut self) -> &[u8; 32] {
345 if !self.digest_computed
{
346 self.compute_digest();
351 /// Consume self and build the ``DataBlob``.
353 /// Returns the blob and the computet digest.
354 pub fn build(mut self) -> Result
<(DataBlob
, [u8; 32]), Error
> {
355 if !self.digest_computed
{
356 self.compute_digest();
359 let chunk
= DataBlob
::encode(self.orig_data
, self.config
, self.compress
)?
;
360 Ok((chunk
, self.digest
))
363 /// Create a chunk filled with zeroes
364 pub fn build_zero_chunk(
365 crypt_config
: Option
<&CryptConfig
>,
368 ) -> Result
<(DataBlob
, [u8; 32]), Error
> {
370 let mut zero_bytes
= Vec
::with_capacity(chunk_size
);
371 zero_bytes
.resize(chunk_size
, 0u8);
372 let mut chunk_builder
= DataChunkBuilder
::new(&zero_bytes
).compress(compress
);
373 if let Some(ref crypt_config
) = crypt_config
{
374 chunk_builder
= chunk_builder
.crypt_config(crypt_config
);
377 chunk_builder
.build()