fix(kms): version local key records safely

This commit is contained in:
overtrue
2026-08-02 19:13:29 +08:00
parent b1ddda3bb2
commit 1131a00fc2
10 changed files with 404 additions and 4 deletions
+32 -1
View File
@@ -48,7 +48,10 @@
//! published. A crash at any earlier point leaves a bundle without a
//! manifest, which decodes as an incomplete bundle and can never be restored.
use crate::backends::local::{LocalKmsClient, StoredKeyProtection, unknown_protection_marker};
use crate::backends::local::{
LocalKmsClient, STORED_MASTER_KEY_FORMAT_VERSION, StoredKeyProtection, stored_master_key_format_version,
unknown_protection_marker,
};
use crate::backup::capability::{AtRestProtection, BackupBackendKind, BackupResponsibility};
use crate::backup::error::BackupError;
use crate::backup::manifest::{
@@ -380,6 +383,12 @@ async fn collect_snapshot(client: &LocalKmsClient) -> Result<CollectedSnapshot>
// classified first so a record from a newer build keeps its own
// verdict — an operator who reads "material corrupt" starts a
// disaster recovery for what is only a version mismatch.
let format_version = stored_master_key_format_version(&raw).map_err(|error| {
KmsError::material_corrupt(&stem, format!("stored key record is not a readable JSON object: {error}"))
})?;
if format_version > STORED_MASTER_KEY_FORMAT_VERSION {
return Err(KmsError::unsupported_format_version(&stem, format_version.to_string()));
}
let unknown_marker = unknown_protection_marker(&raw).map_err(|error| {
KmsError::material_corrupt(&stem, format!("stored key record is not a readable JSON object: {error}"))
})?;
@@ -1150,4 +1159,26 @@ mod tests {
"got {error:?}"
);
}
#[tokio::test]
async fn numeric_record_format_version_from_a_newer_build_aborts_export_as_unsupported_format() {
let (client, _key_dir) = encrypted_client().await;
client.create_key("alpha", "AES_256", None).await.expect("create key");
let record_path = client.key_directory().join("alpha.key");
let mut record: serde_json::Value =
serde_json::from_slice(&std::fs::read(&record_path).expect("read record")).expect("decode record");
record["format_version"] = serde_json::json!(99);
std::fs::write(&record_path, serde_json::to_vec_pretty(&record).expect("encode record")).expect("write record");
let bundle = TempDir::new().expect("bundle dir");
let error = export_local_backup(&client, &test_kek(), &export_request(bundle.path().join("bundle")))
.await
.expect_err("a newer record format must abort the export");
assert!(
matches!(&error, KmsError::UnsupportedFormatVersion { key_id, version }
if key_id == "alpha" && version == "99"),
"got {error:?}"
);
}
}