This is more flexible, because we can choose what fileds we want to sign.
use serde_json::{json, Value};
-use crate::backup::{BackupDir, CryptMode};
+use crate::backup::{BackupDir, CryptMode, CryptConfig};
pub const MANIFEST_BLOB_NAME: &str = "index.json.blob";
pub const CLIENT_LOG_BLOB_NAME: &str = "client.log.blob";
Ok(())
}
- pub fn into_json(self) -> Value {
- json!({
+ pub fn signature(&self, crypt_config: &CryptConfig) -> [u8; 32] {
+
+ let mut data = String::new();
+
+ data.push_str(self.snapshot.group().backup_type());
+ data.push('\n');
+ data.push_str(self.snapshot.group().backup_id());
+ data.push('\n');
+ data.push_str(&format!("{}", self.snapshot.backup_time().timestamp()));
+ data.push('\n');
+ data.push('\n');
+
+ for info in self.files.iter() {
+ data.push_str(&info.filename);
+ data.push('\n');
+ data.push_str(match info.crypt_mode {
+ CryptMode::None => "None",
+ CryptMode::SignOnly => "SignOnly",
+ CryptMode::Encrypt => "Encrypt",
+ });
+ data.push('\n');
+ data.push_str(&format!("{}", info.size));
+ data.push('\n');
+ data.push_str(&proxmox::tools::digest_to_hex(&info.csum));
+ data.push('\n');
+
+ data.push('\n');
+ }
+
+ crypt_config.compute_auth_tag(data.as_bytes())
+ }
+
+ pub fn into_json(self, crypt_config: Option<&CryptConfig>) -> Value {
+
+ let mut manifest = json!({
"backup-type": self.snapshot.group().backup_type(),
"backup-id": self.snapshot.group().backup_id(),
"backup-time": self.snapshot.backup_time().timestamp(),
}));
acc
})
- })
+ });
+
+ if let Some(crypt_config) = crypt_config {
+ let sig = self.signature(crypt_config);
+ manifest["signature"] = proxmox::tools::digest_to_hex(&sig).into();
+ }
+
+ manifest
}
}
}
// create manifest (index.json)
- let manifest = manifest.into_json();
+ let manifest = manifest.into_json(crypt_config.as_ref().map(Arc::as_ref));
println!("Upload index.json to '{:?}'", repo);
let manifest = serde_json::to_string_pretty(&manifest)?.into();
true,
).await?;
- let manifest = client.download_manifest().await?;
+ let (manifest, backup_index_data) = client.download_manifest().await?;
let (archive_name, archive_type) = parse_archive_type(archive_name);
if archive_name == MANIFEST_BLOB_NAME {
- let backup_index_data = manifest.into_json().to_string();
if let Some(target) = target {
- replace_file(target, backup_index_data.as_bytes(), CreateOptions::new())?;
+ replace_file(target, &backup_index_data, CreateOptions::new())?;
} else {
let stdout = std::io::stdout();
let mut writer = stdout.lock();
- writer.write_all(backup_index_data.as_bytes())
+ writer.write_all(&backup_index_data)
.map_err(|err| format_err!("unable to pipe data - {}", err))?;
}
true,
).await?;
- let manifest = client.download_manifest().await?;
+ let (manifest, _) = client.download_manifest().await?;
let index = client.download_dynamic_index(&manifest, CATALOG_NAME).await?;
.custom_flags(libc::O_TMPFILE)
.open("/tmp")?;
- let manifest = client.download_manifest().await?;
+ let (manifest, _) = client.download_manifest().await?;
let index = client.download_dynamic_index(&manifest, &server_archive_name).await?;
let most_used = index.find_most_used_chunks(8);
true,
).await?;
- let manifest = client.download_manifest().await?;
+ let (manifest, _) = client.download_manifest().await?;
if server_archive_name.ends_with(".didx") {
let index = client.download_dynamic_index(&manifest, &server_archive_name).await?;
-use anyhow::{format_err, Error};
+use anyhow::{bail, format_err, Error};
use std::io::{Read, Write, Seek, SeekFrom};
use std::fs::File;
use std::sync::Arc;
}
/// Download backup manifest (index.json)
- pub async fn download_manifest(&self) -> Result<BackupManifest, Error> {
+ ///
+ /// The manifest signature is verified if we have a crypt_config.
+ pub async fn download_manifest(&self) -> Result<(BackupManifest, Vec<u8>), Error> {
use std::convert::TryFrom;
self.download(MANIFEST_BLOB_NAME, &mut raw_data).await?;
let blob = DataBlob::from_raw(raw_data)?;
blob.verify_crc()?;
- let data = blob.decode(self.crypt_config.as_ref().map(Arc::as_ref))?;
+ let data = blob.decode(None)?;
let json: Value = serde_json::from_slice(&data[..])?;
- BackupManifest::try_from(json)
+ let signature = json["signature"].as_str().map(String::from);
+
+ let manifest = BackupManifest::try_from(json)?;
+
+ if let Some(ref crypt_config) = self.crypt_config {
+ if let Some(signature) = signature {
+ let expected_signature = proxmox::tools::digest_to_hex(&manifest.signature(crypt_config));
+ if signature != expected_signature {
+ bail!("wrong signature in manifest");
+ }
+ } else {
+ // warn/fail?
+ }
+ }
+
+ Ok((manifest, data))
}
/// Download a .blob file