2 use std
::convert
::TryInto
;
4 use proxmox
::tools
::io
::{ReadExt, WriteExt}
;
8 /// Data blob binary storage format
10 /// Data blobs store arbitrary binary data (< 16MB), and can be
11 /// compressed and encrypted. A simply binary format is used to store
12 /// them on disk or transfer them over the network. Please use index
13 /// files to store large data files (".fidx" of ".didx").
16 raw_data
: Vec
<u8>, // tagged, compressed, encryped data
21 /// accessor to raw_data field
22 pub fn raw_data(&self) -> &[u8] {
26 /// Consume self and returns raw_data
27 pub fn into_inner(self) -> Vec
<u8> {
31 /// accessor to chunk type (magic number)
32 pub fn magic(&self) -> &[u8; 8] {
33 self.raw_data
[0..8].try_into().unwrap()
36 /// accessor to crc32 checksum
37 pub fn crc(&self) -> u32 {
38 let crc_o
= proxmox
::tools
::offsetof!(DataBlobHeader
, crc
);
39 u32::from_le_bytes(self.raw_data
[crc_o
..crc_o
+4].try_into().unwrap())
42 // set the CRC checksum field
43 pub fn set_crc(&mut self, crc
: u32) {
44 let crc_o
= proxmox
::tools
::offsetof!(DataBlobHeader
, crc
);
45 self.raw_data
[crc_o
..crc_o
+4].copy_from_slice(&crc
.to_le_bytes());
48 /// compute the CRC32 checksum
49 pub fn compute_crc(&self) -> u32 {
50 let mut hasher
= crc32fast
::Hasher
::new();
51 let start
= std
::mem
::size_of
::<DataBlobHeader
>(); // start after HEAD
52 hasher
.update(&self.raw_data
[start
..]);
56 /// verify the CRC32 checksum
57 pub fn verify_crc(&self) -> Result
<(), Error
> {
58 let expected_crc
= self.compute_crc();
59 if expected_crc
!= self.crc() {
60 bail
!("Data blob has wrong CRC checksum.");
65 /// Create a DataBlob, optionally compressed and/or encrypted
68 config
: Option
<&CryptConfig
>,
70 ) -> Result
<Self, Error
> {
72 if data
.len() > 16*1024*1024 {
73 bail
!("data blob too large ({} bytes).", data
.len());
76 let mut blob
= if let Some(config
) = config
{
79 let (_compress
, data
, magic
) = if compress
{
80 compr_data
= zstd
::block
::compress(data
, 1)?
;
81 // Note: We only use compression if result is shorter
82 if compr_data
.len() < data
.len() {
83 (true, &compr_data
[..], ENCR_COMPR_BLOB_MAGIC_1_0
)
85 (false, data
, ENCRYPTED_BLOB_MAGIC_1_0
)
88 (false, data
, ENCRYPTED_BLOB_MAGIC_1_0
)
91 let header_len
= std
::mem
::size_of
::<EncryptedDataBlobHeader
>();
92 let mut raw_data
= Vec
::with_capacity(data
.len() + header_len
);
94 let dummy_head
= EncryptedDataBlobHeader
{
95 head
: DataBlobHeader { magic: [0u8; 8], crc: [0; 4] }
,
100 raw_data
.write_le_value(dummy_head
)?
;
103 let (iv
, tag
) = config
.encrypt_to(data
, &mut raw_data
)?
;
105 let head
= EncryptedDataBlobHeader
{
106 head
: DataBlobHeader { magic, crc: [0; 4] }
, iv
, tag
,
110 (&mut raw_data
[0..header_len
]).write_le_value(head
)?
;
113 DataBlob { raw_data }
116 let max_data_len
= data
.len() + std
::mem
::size_of
::<DataBlobHeader
>();
118 let mut comp_data
= Vec
::with_capacity(max_data_len
);
120 let head
= DataBlobHeader
{
121 magic
: COMPRESSED_BLOB_MAGIC_1_0
,
125 comp_data
.write_le_value(head
)?
;
128 zstd
::stream
::copy_encode(data
, &mut comp_data
, 1)?
;
130 if comp_data
.len() < max_data_len
{
131 return Ok(DataBlob { raw_data: comp_data }
);
135 let mut raw_data
= Vec
::with_capacity(max_data_len
);
137 let head
= DataBlobHeader
{
138 magic
: UNCOMPRESSED_BLOB_MAGIC_1_0
,
142 raw_data
.write_le_value(head
)?
;
144 raw_data
.extend_from_slice(data
);
146 DataBlob { raw_data }
149 blob
.set_crc(blob
.compute_crc());
155 pub fn decode(self, config
: Option
<&CryptConfig
>) -> Result
<Vec
<u8>, Error
> {
157 let magic
= self.magic();
159 if magic
== &UNCOMPRESSED_BLOB_MAGIC_1_0
{
160 let data_start
= std
::mem
::size_of
::<DataBlobHeader
>();
161 return Ok(self.raw_data
[data_start
..].to_vec());
162 } else if magic
== &COMPRESSED_BLOB_MAGIC_1_0
{
163 let data_start
= std
::mem
::size_of
::<DataBlobHeader
>();
164 let data
= zstd
::block
::decompress(&self.raw_data
[data_start
..], 16*1024*1024)?
;
166 } else if magic
== &ENCR_COMPR_BLOB_MAGIC_1_0
|| magic
== &ENCRYPTED_BLOB_MAGIC_1_0
{
167 let header_len
= std
::mem
::size_of
::<EncryptedDataBlobHeader
>();
169 (&self.raw_data
[..header_len
]).read_le_value
::<EncryptedDataBlobHeader
>()?
172 if let Some(config
) = config
{
173 let data
= if magic
== &ENCR_COMPR_BLOB_MAGIC_1_0
{
174 config
.decode_compressed_chunk(&self.raw_data
[header_len
..], &head
.iv
, &head
.tag
)?
176 config
.decode_uncompressed_chunk(&self.raw_data
[header_len
..], &head
.iv
, &head
.tag
)?
180 bail
!("unable to decrypt blob - missing CryptConfig");
182 } else if magic
== &AUTH_COMPR_BLOB_MAGIC_1_0
|| magic
== &AUTHENTICATED_BLOB_MAGIC_1_0
{
183 let header_len
= std
::mem
::size_of
::<AuthenticatedDataBlobHeader
>();
185 (&self.raw_data
[..header_len
]).read_le_value
::<AuthenticatedDataBlobHeader
>()?
188 let data_start
= std
::mem
::size_of
::<AuthenticatedDataBlobHeader
>();
190 // Note: only verify if we have a crypt config
191 if let Some(config
) = config
{
192 let signature
= config
.compute_auth_tag(&self.raw_data
[data_start
..]);
193 if signature
!= head
.tag
{
194 bail
!("verifying blob signature failed");
198 if magic
== &AUTH_COMPR_BLOB_MAGIC_1_0
{
199 let data
= zstd
::block
::decompress(&self.raw_data
[data_start
..], 16*1024*1024)?
;
202 return Ok(self.raw_data
[data_start
..].to_vec());
205 bail
!("Invalid blob magic number.");
209 /// Create a signed DataBlob, optionally compressed
210 pub fn create_signed(
212 config
: &CryptConfig
,
214 ) -> Result
<Self, Error
> {
216 if data
.len() > 16*1024*1024 {
217 bail
!("data blob too large ({} bytes).", data
.len());
221 let (_compress
, data
, magic
) = if compress
{
222 compr_data
= zstd
::block
::compress(data
, 1)?
;
223 // Note: We only use compression if result is shorter
224 if compr_data
.len() < data
.len() {
225 (true, &compr_data
[..], AUTH_COMPR_BLOB_MAGIC_1_0
)
227 (false, data
, AUTHENTICATED_BLOB_MAGIC_1_0
)
230 (false, data
, AUTHENTICATED_BLOB_MAGIC_1_0
)
233 let header_len
= std
::mem
::size_of
::<AuthenticatedDataBlobHeader
>();
234 let mut raw_data
= Vec
::with_capacity(data
.len() + header_len
);
236 let head
= AuthenticatedDataBlobHeader
{
237 head
: DataBlobHeader { magic, crc: [0; 4] }
,
238 tag
: config
.compute_auth_tag(data
),
241 raw_data
.write_le_value(head
)?
;
243 raw_data
.extend_from_slice(data
);
245 let mut blob
= DataBlob { raw_data }
;
246 blob
.set_crc(blob
.compute_crc());
251 /// Create Instance from raw data
252 pub fn from_raw(data
: Vec
<u8>) -> Result
<Self, Error
> {
254 if data
.len() < std
::mem
::size_of
::<DataBlobHeader
>() {
255 bail
!("blob too small ({} bytes).", data
.len());
258 let magic
= &data
[0..8];
260 if magic
== ENCR_COMPR_BLOB_MAGIC_1_0
|| magic
== ENCRYPTED_BLOB_MAGIC_1_0
{
262 if data
.len() < std
::mem
::size_of
::<EncryptedDataBlobHeader
>() {
263 bail
!("encrypted blob too small ({} bytes).", data
.len());
266 let blob
= DataBlob { raw_data: data }
;
269 } else if magic
== COMPRESSED_BLOB_MAGIC_1_0
|| magic
== UNCOMPRESSED_BLOB_MAGIC_1_0
{
271 let blob
= DataBlob { raw_data: data }
;
274 } else if magic
== AUTH_COMPR_BLOB_MAGIC_1_0
|| magic
== AUTHENTICATED_BLOB_MAGIC_1_0
{
275 if data
.len() < std
::mem
::size_of
::<AuthenticatedDataBlobHeader
>() {
276 bail
!("authenticated blob too small ({} bytes).", data
.len());
279 let blob
= DataBlob { raw_data: data }
;
283 bail
!("unable to parse raw blob - wrong magic");