}
/// Decode blob data
- pub fn decode(&self, config: Option<&CryptConfig>) -> Result<Vec<u8>, Error> {
+ pub fn decode(&self, config: Option<&CryptConfig>, digest: Option<&[u8; 32]>) -> Result<Vec<u8>, Error> {
let magic = self.magic();
if magic == &UNCOMPRESSED_BLOB_MAGIC_1_0 {
let data_start = std::mem::size_of::<DataBlobHeader>();
- Ok(self.raw_data[data_start..].to_vec())
+ let data = self.raw_data[data_start..].to_vec();
+ if let Some(digest) = digest {
+ Self::verify_digest(&data, None, digest)?;
+ }
+ Ok(data)
} else if magic == &COMPRESSED_BLOB_MAGIC_1_0 {
let data_start = std::mem::size_of::<DataBlobHeader>();
let data = zstd::block::decompress(&self.raw_data[data_start..], MAX_BLOB_SIZE)?;
+ if let Some(digest) = digest {
+ Self::verify_digest(&data, None, digest)?;
+ }
Ok(data)
} else if magic == &ENCR_COMPR_BLOB_MAGIC_1_0 || magic == &ENCRYPTED_BLOB_MAGIC_1_0 {
let header_len = std::mem::size_of::<EncryptedDataBlobHeader>();
} else {
config.decode_uncompressed_chunk(&self.raw_data[header_len..], &head.iv, &head.tag)?
};
+ if let Some(digest) = digest {
+ Self::verify_digest(&data, Some(config), digest)?;
+ }
Ok(data)
} else {
bail!("unable to decrypt blob - missing CryptConfig");
return Ok(());
}
- let data = self.decode(None)?;
+ // verifies digest!
+ let data = self.decode(None, Some(expected_digest))?;
if expected_chunk_size != data.len() {
bail!("detected chunk with wrong length ({} != {})", expected_chunk_size, data.len());
}
- let digest = openssl::sha::sha256(&data);
+
+ Ok(())
+ }
+
+ fn verify_digest(
+ data: &[u8],
+ config: Option<&CryptConfig>,
+ expected_digest: &[u8; 32],
+ ) -> Result<(), Error> {
+
+ let digest = match config {
+ Some(config) => config.compute_digest(data),
+ None => openssl::sha::sha256(&data),
+ };
if &digest != expected_digest {
bail!("detected chunk with wrong digest.");
}
backup_dir: &BackupDir,
) -> Result<Value, Error> {
let blob = self.load_blob(backup_dir, MANIFEST_BLOB_NAME)?;
- let manifest_data = blob.decode(None)?;
+ // no expected digest available
+ let manifest_data = blob.decode(None, None)?;
let manifest: Value = serde_json::from_slice(&manifest_data[..])?;
Ok(manifest)
}
type Error = Error;
fn try_from(blob: super::DataBlob) -> Result<Self, Error> {
- let data = blob.decode(None)
+ // no expected digest available
+ let data = blob.decode(None, None)
.map_err(|err| format_err!("decode backup manifest blob failed - {}", err))?;
let json: Value = serde_json::from_slice(&data[..])
.map_err(|err| format_err!("unable to parse backup manifest json - {}", err))?;
fn read_chunk(&self, digest: &[u8; 32]) -> Result<Vec<u8>, Error> {
let chunk = ReadChunk::read_raw_chunk(self, digest)?;
- let raw_data = chunk.decode(self.crypt_config.as_ref().map(Arc::as_ref))?;
-
- // fixme: verify digest?
+ let raw_data = chunk.decode(self.crypt_config.as_ref().map(Arc::as_ref), Some(digest))?;
Ok(raw_data)
}
Box::pin(async move {
let chunk = AsyncReadChunk::read_raw_chunk(self, digest).await?;
- let raw_data = chunk.decode(self.crypt_config.as_ref().map(Arc::as_ref))?;
+ let raw_data = chunk.decode(self.crypt_config.as_ref().map(Arc::as_ref), Some(digest))?;
// fixme: verify digest?
use super::{
DataStore, BackupGroup, BackupDir, BackupInfo, IndexFile,
- ENCR_COMPR_BLOB_MAGIC_1_0, ENCRYPTED_BLOB_MAGIC_1_0,
+ CryptMode,
FileInfo, ArchiveType, archive_type,
};
bail!("wrong index checksum");
}
- let magic = blob.magic();
-
- if magic == &ENCR_COMPR_BLOB_MAGIC_1_0 || magic == &ENCRYPTED_BLOB_MAGIC_1_0 {
- return Ok(());
+ match blob.crypt_mode()? {
+ CryptMode::Encrypt => Ok(()),
+ CryptMode::None => {
+ // digest already verified above
+ blob.decode(None, None)?;
+ Ok(())
+ },
+ CryptMode::SignOnly => bail!("Invalid CryptMode for blob"),
}
-
- blob.decode(None)?;
-
- Ok(())
}
fn verify_index_chunks(
let mut raw_data = Vec::with_capacity(64 * 1024);
self.download(MANIFEST_BLOB_NAME, &mut raw_data).await?;
let blob = DataBlob::load_from_reader(&mut &raw_data[..])?;
- let data = blob.decode(None)?;
+ // no expected digest available
+ let data = blob.decode(None, None)?;
let manifest = BackupManifest::from_data(&data[..], self.crypt_config.as_ref().map(Arc::as_ref))?;
self.h2.download("previous", Some(param), &mut raw_data).await?;
let blob = DataBlob::load_from_reader(&mut &raw_data[..])?;
- let data = blob.decode(self.crypt_config.as_ref().map(Arc::as_ref))?;
+ // no expected digest available
+ let data = blob.decode(self.crypt_config.as_ref().map(Arc::as_ref), None)?;
let manifest = BackupManifest::from_data(&data[..], self.crypt_config.as_ref().map(Arc::as_ref))?;
let chunk = ReadChunk::read_raw_chunk(self, digest)?;
- let raw_data = chunk.decode(self.crypt_config.as_ref().map(Arc::as_ref))?;
-
- // fixme: verify digest?
+ let raw_data = chunk.decode(self.crypt_config.as_ref().map(Arc::as_ref), Some(digest))?;
let use_cache = self.cache_hint.contains_key(digest);
if use_cache {
let chunk = Self::read_raw_chunk(self, digest).await?;
- let raw_data = chunk.decode(self.crypt_config.as_ref().map(Arc::as_ref))?;
-
- // fixme: verify digest?
+ let raw_data = chunk.decode(self.crypt_config.as_ref().map(Arc::as_ref), Some(digest))?;
let use_cache = self.cache_hint.contains_key(digest);
if use_cache {
let key = [1u8; 32];
Arc::new(CryptConfig::new(key).unwrap())
};
+
+ static ref TEST_DIGEST_PLAIN: [u8; 32] = [83, 154, 96, 195, 167, 204, 38, 142, 204, 224, 130, 201, 24, 71, 2, 188, 130, 155, 177, 6, 162, 100, 61, 238, 38, 219, 63, 240, 191, 132, 87, 238];
+
+ static ref TEST_DIGEST_ENC: [u8; 32] = [50, 162, 191, 93, 255, 132, 9, 14, 127, 23, 92, 39, 246, 102, 245, 204, 130, 104, 4, 106, 182, 239, 218, 14, 80, 17, 150, 188, 239, 253, 198, 117];
}
-fn verify_test_blob(mut cursor: Cursor<Vec<u8>>) -> Result<(), Error> {
+fn verify_test_blob(mut cursor: Cursor<Vec<u8>>, digest: &[u8; 32]) -> Result<(), Error> {
// run read tests with different buffer sizes
for size in [1, 3, 64*1024].iter() {
let blob = DataBlob::load_from_reader(&mut &raw_data[..])?;
- let data = blob.decode(Some(&CRYPT_CONFIG))?;
+ let data = blob.decode(Some(&CRYPT_CONFIG), Some(digest))?;
if data != *TEST_DATA {
bail!("blob data is wrong (decode)");
}
let mut blob_writer = DataBlobWriter::new_uncompressed(tmp)?;
blob_writer.write_all(&TEST_DATA)?;
- verify_test_blob(blob_writer.finish()?)
+ verify_test_blob(blob_writer.finish()?, &*TEST_DIGEST_PLAIN)
}
#[test]
let mut blob_writer = DataBlobWriter::new_compressed(tmp)?;
blob_writer.write_all(&TEST_DATA)?;
- verify_test_blob(blob_writer.finish()?)
+ verify_test_blob(blob_writer.finish()?, &*TEST_DIGEST_PLAIN)
}
#[test]
let mut blob_writer = DataBlobWriter::new_encrypted(tmp, CRYPT_CONFIG.clone())?;
blob_writer.write_all(&TEST_DATA)?;
- verify_test_blob(blob_writer.finish()?)
+ verify_test_blob(blob_writer.finish()?, &*TEST_DIGEST_ENC)
}
#[test]
let mut blob_writer = DataBlobWriter::new_encrypted_compressed(tmp, CRYPT_CONFIG.clone())?;
blob_writer.write_all(&TEST_DATA)?;
- verify_test_blob(blob_writer.finish()?)
+ verify_test_blob(blob_writer.finish()?, &*TEST_DIGEST_ENC)
}