From 69ecd8d5b027af81ebeacafb997fd9f8b2b569f0 Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Fri, 2 Aug 2019 09:56:01 +0200 Subject: [PATCH] src/backup/data_blob.rs: implement signed blobs --- src/backup/data_blob.rs | 70 ++++++++++++++++++++++++++++++++++++++ src/backup/file_formats.rs | 19 +++++++++++ 2 files changed, 89 insertions(+) diff --git a/src/backup/data_blob.rs b/src/backup/data_blob.rs index e0378b6c..c50af748 100644 --- a/src/backup/data_blob.rs +++ b/src/backup/data_blob.rs @@ -62,6 +62,7 @@ impl DataBlob { Ok(()) } + /// Create a DataBlob, optionally compressed and/or encrypted pub fn encode( data: &[u8], config: Option<&CryptConfig>, @@ -174,11 +175,72 @@ impl DataBlob { } else { bail!("unable to decrypt blob - missing CryptConfig"); } + } else if magic == &AUTH_COMPR_BLOB_MAGIC_1_0 || magic == &AUTHENTICATED_BLOB_MAGIC_1_0 { + let header_len = std::mem::size_of::(); + let head = unsafe { + (&self.raw_data[..header_len]).read_le_value::()? + }; + + let data_start = std::mem::size_of::(); + + // Note: only verify if we have a crypt config + if let Some(config) = config { + let signature = config.compute_auth_tag(&self.raw_data[data_start..]); + if signature != head.tag { + bail!("verifying blob signature failed"); + } + } + + if magic == &AUTH_COMPR_BLOB_MAGIC_1_0 { + let data = zstd::block::decompress(&self.raw_data[data_start..], 16*1024*1024)?; + return Ok(data); + } else { + return Ok(self.raw_data[data_start..].to_vec()); + } } else { bail!("Invalid blob magic number."); } } + /// Create a signed DataBlob, optionally compressed + pub fn create_signed( + data: &[u8], + config: &CryptConfig, + compress: bool, + ) -> Result { + + if data.len() > 16*1024*1024 { + bail!("data blob too large ({} bytes).", data.len()); + } + + let compr_data; + let (_compress, data, magic) = if compress { + compr_data = zstd::block::compress(data, 1)?; + // Note: We only use compression if result is shorter + if compr_data.len() < data.len() { + (true, &compr_data[..], AUTH_COMPR_BLOB_MAGIC_1_0) + } else { + (false, data, AUTHENTICATED_BLOB_MAGIC_1_0) + } + } else { + (false, data, AUTHENTICATED_BLOB_MAGIC_1_0) + }; + + let header_len = std::mem::size_of::(); + let mut raw_data = Vec::with_capacity(data.len() + header_len); + + let head = AuthenticatedDataBlobHeader { + head: DataBlobHeader { magic, crc: [0; 4] }, + tag: config.compute_auth_tag(data), + }; + unsafe { + raw_data.write_le_value(head)?; + } + raw_data.extend_from_slice(data); + + return Ok(DataBlob { raw_data }); + } + /// Create Instance from raw data pub fn from_raw(data: Vec) -> Result { @@ -201,6 +263,14 @@ impl DataBlob { let blob = DataBlob { raw_data: data }; + Ok(blob) + } else if magic == AUTH_COMPR_BLOB_MAGIC_1_0 || magic == AUTHENTICATED_BLOB_MAGIC_1_0 { + if data.len() < std::mem::size_of::() { + bail!("authenticated blob too small ({} bytes).", data.len()); + } + + let blob = DataBlob { raw_data: data }; + Ok(blob) } else { bail!("unable to parse raw blob - wrong magic"); diff --git a/src/backup/file_formats.rs b/src/backup/file_formats.rs index 025e2498..ae7a5024 100644 --- a/src/backup/file_formats.rs +++ b/src/backup/file_formats.rs @@ -26,6 +26,12 @@ pub static ENCRYPTED_BLOB_MAGIC_1_0: [u8; 8] = [123, 103, 133, 190, 34, 45, 76, // openssl::sha::sha256(b"Proxmox Backup zstd compressed encrypted blob v1.0")[0..8] pub static ENCR_COMPR_BLOB_MAGIC_1_0: [u8; 8] = [230, 89, 27, 191, 11, 191, 216, 11]; +//openssl::sha::sha256(b"Proxmox Backup authenticated blob v1.0")[0..8] +pub static AUTHENTICATED_BLOB_MAGIC_1_0: [u8; 8] = [31, 135, 238, 226, 145, 206, 5, 2]; + +//openssl::sha::sha256(b"Proxmox Backup zstd compressed authenticated blob v1.0")[0..8] +pub static AUTH_COMPR_BLOB_MAGIC_1_0: [u8; 8] = [126, 166, 15, 190, 145, 31, 169, 96]; + // openssl::sha::sha256(b"Proxmox Backup fixed sized chunk index v1.0")[0..8] pub static FIXED_SIZED_CHUNK_INDEX_1_0: [u8; 8] = [47, 127, 65, 237, 145, 253, 15, 205]; @@ -53,6 +59,19 @@ pub struct DataBlobHeader { pub crc: [u8; 4], } +/// Authenticated data blob binary storage format +/// +/// The ``DataBlobHeader`` for authenticated blobs additionally contains +/// a 16 byte HMAC tag, followed by the data: +/// +/// (MAGIC || CRC32 || TAG || Data). +#[derive(Endian)] +#[repr(C,packed)] +pub struct AuthenticatedDataBlobHeader { + pub head: DataBlobHeader, + pub tag: [u8; 32], +} + /// Encrypted data blob binary storage format /// /// The ``DataBlobHeader`` for encrypted blobs additionally contains