src/backup/manifest.rs: use serde_json::from_value() to deserialize data

Also modified from_data compute signature ditectly from json.
This commit is contained in:
Dietmar Maurer 2020-07-09 09:48:30 +02:00
parent 512d50a455
commit 3dacedce71
2 changed files with 14 additions and 53 deletions

View File

@ -168,8 +168,12 @@ impl BackupManifest {
/// By generating a HMAC SHA256 over the canonical json /// By generating a HMAC SHA256 over the canonical json
/// representation, The 'unpreotected' property is excluded. /// representation, The 'unpreotected' property is excluded.
pub fn signature(&self, crypt_config: &CryptConfig) -> Result<[u8; 32], Error> { pub fn signature(&self, crypt_config: &CryptConfig) -> Result<[u8; 32], Error> {
Self::json_signature(&serde_json::to_value(&self)?, crypt_config)
}
let mut signed_data = serde_json::to_value(&self)?; fn json_signature(data: &Value, crypt_config: &CryptConfig) -> Result<[u8; 32], Error> {
let mut signed_data = data.clone();
signed_data.as_object_mut().unwrap().remove("unprotected"); // exclude signed_data.as_object_mut().unwrap().remove("unprotected"); // exclude
@ -199,11 +203,10 @@ impl BackupManifest {
pub fn from_data(data: &[u8], crypt_config: Option<&CryptConfig>) -> Result<BackupManifest, Error> { pub fn from_data(data: &[u8], crypt_config: Option<&CryptConfig>) -> Result<BackupManifest, Error> {
let json: Value = serde_json::from_slice(data)?; let json: Value = serde_json::from_slice(data)?;
let signature = json["signature"].as_str().map(String::from); let signature = json["signature"].as_str().map(String::from);
let manifest = BackupManifest::try_from(json)?;
if let Some(ref crypt_config) = crypt_config { if let Some(ref crypt_config) = crypt_config {
if let Some(signature) = signature { if let Some(signature) = signature {
let expected_signature = proxmox::tools::digest_to_hex(&manifest.signature(crypt_config)?); let expected_signature = proxmox::tools::digest_to_hex(&Self::json_signature(&json, crypt_config)?);
if signature != expected_signature { if signature != expected_signature {
bail!("wrong signature in manifest"); bail!("wrong signature in manifest");
} }
@ -211,10 +214,13 @@ impl BackupManifest {
// not signed: warn/fail? // not signed: warn/fail?
} }
} }
let manifest: BackupManifest = serde_json::from_value(json)?;
Ok(manifest) Ok(manifest)
} }
} }
impl TryFrom<super::DataBlob> for BackupManifest { impl TryFrom<super::DataBlob> for BackupManifest {
type Error = Error; type Error = Error;
@ -223,54 +229,11 @@ impl TryFrom<super::DataBlob> for BackupManifest {
.map_err(|err| format_err!("decode backup manifest blob failed - {}", err))?; .map_err(|err| format_err!("decode backup manifest blob failed - {}", err))?;
let json: Value = serde_json::from_slice(&data[..]) let json: Value = serde_json::from_slice(&data[..])
.map_err(|err| format_err!("unable to parse backup manifest json - {}", err))?; .map_err(|err| format_err!("unable to parse backup manifest json - {}", err))?;
BackupManifest::try_from(json) let manifest: BackupManifest = serde_json::from_value(json)?;
}
}
impl TryFrom<Value> for BackupManifest {
type Error = Error;
fn try_from(data: Value) -> Result<Self, Error> {
use crate::tools::{required_string_property, required_integer_property, required_array_property};
proxmox::try_block!({
let backup_type = required_string_property(&data, "backup-type")?;
let backup_id = required_string_property(&data, "backup-id")?;
let backup_time = required_integer_property(&data, "backup-time")?;
let snapshot = BackupDir::new(backup_type, backup_id, backup_time);
let mut manifest = BackupManifest::new(snapshot);
for item in required_array_property(&data, "files")?.iter() {
let filename = required_string_property(item, "filename")?.to_owned();
let csum = required_string_property(item, "csum")?;
let csum = proxmox::tools::hex_to_digest(csum)?;
let size = required_integer_property(item, "size")? as u64;
let mut crypt_mode = CryptMode::None;
if let Some(true) = item["encrypted"].as_bool() { // compatible to < 0.8.0
crypt_mode = CryptMode::Encrypt;
}
if let Some(mode) = item.get("crypt-mode") {
crypt_mode = serde_json::from_value(mode.clone())?;
}
manifest.add_file(filename, size, csum, crypt_mode)?;
}
if manifest.files().is_empty() {
bail!("manifest does not list any files.");
}
Ok(manifest) Ok(manifest)
}).map_err(|err: Error| format_err!("unable to parse backup manifest - {}", err)) }
}
}
}
#[test] #[test]
fn test_manifest_signature() -> Result<(), Error> { fn test_manifest_signature() -> Result<(), Error> {
@ -306,7 +269,7 @@ fn test_manifest_signature() -> Result<(), Error> {
assert_eq!(signature, "d7b446fb7db081662081d4b40fedd858a1d6307a5aff4ecff7d5bf4fd35679e9"); assert_eq!(signature, "d7b446fb7db081662081d4b40fedd858a1d6307a5aff4ecff7d5bf4fd35679e9");
let manifest = BackupManifest::try_from(manifest)?; let manifest: BackupManifest = serde_json::from_value(manifest)?;
let expected_signature = proxmox::tools::digest_to_hex(&manifest.signature(&crypt_config)?); let expected_signature = proxmox::tools::digest_to_hex(&manifest.signature(&crypt_config)?);
assert_eq!(signature, expected_signature); assert_eq!(signature, expected_signature);

View File

@ -453,8 +453,6 @@ impl BackupWriter {
/// Download backup manifest (index.json) of last backup /// Download backup manifest (index.json) of last backup
pub async fn download_previous_manifest(&self) -> Result<BackupManifest, Error> { pub async fn download_previous_manifest(&self) -> Result<BackupManifest, Error> {
use std::convert::TryFrom;
let mut raw_data = Vec::with_capacity(64 * 1024); let mut raw_data = Vec::with_capacity(64 * 1024);
let param = json!({ "archive-name": MANIFEST_BLOB_NAME }); let param = json!({ "archive-name": MANIFEST_BLOB_NAME });
@ -463,8 +461,8 @@ impl BackupWriter {
let blob = DataBlob::from_raw(raw_data)?; let blob = DataBlob::from_raw(raw_data)?;
blob.verify_crc()?; blob.verify_crc()?;
let data = blob.decode(self.crypt_config.as_ref().map(Arc::as_ref))?; let data = blob.decode(self.crypt_config.as_ref().map(Arc::as_ref))?;
let json: Value = serde_json::from_slice(&data[..])?;
let manifest = BackupManifest::try_from(json)?; let manifest = BackupManifest::from_data(&data[..], self.crypt_config.as_ref().map(Arc::as_ref))?;
Ok(manifest) Ok(manifest)
} }