mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-24 13:16:28 +00:00
feat(kms): introduce KMS unavailability error and enhance data key handling (#5184)
This commit is contained in:
@@ -24,6 +24,7 @@ use crate::error::{KmsError, Result};
|
||||
use async_trait::async_trait;
|
||||
use jiff::Zoned;
|
||||
use rand::Rng;
|
||||
use serde::de::IgnoredAny;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -44,6 +45,30 @@ pub struct DataKeyEnvelope {
|
||||
pub created_at: Zoned,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct DataKeyEnvelopeMarker {
|
||||
#[serde(rename = "key_id")]
|
||||
_key_id: IgnoredAny,
|
||||
#[serde(rename = "master_key_id")]
|
||||
_master_key_id: IgnoredAny,
|
||||
#[serde(rename = "key_spec")]
|
||||
_key_spec: IgnoredAny,
|
||||
#[serde(rename = "encrypted_key")]
|
||||
_encrypted_key: IgnoredAny,
|
||||
#[serde(rename = "nonce")]
|
||||
_nonce: IgnoredAny,
|
||||
#[serde(rename = "encryption_context")]
|
||||
_encryption_context: IgnoredAny,
|
||||
#[serde(rename = "created_at")]
|
||||
_created_at: IgnoredAny,
|
||||
}
|
||||
|
||||
/// Returns whether ciphertext is a RustFS KMS data-key envelope.
|
||||
pub fn is_data_key_envelope(ciphertext: &[u8]) -> bool {
|
||||
ciphertext.iter().copied().find(|byte| !byte.is_ascii_whitespace()) == Some(b'{')
|
||||
&& serde_json::from_slice::<DataKeyEnvelopeMarker>(ciphertext).is_ok()
|
||||
}
|
||||
|
||||
/// Trait for encrypting and decrypting data encryption keys (DEK)
|
||||
///
|
||||
/// This trait abstracts the encryption operations used to protect
|
||||
@@ -329,4 +354,46 @@ mod tests {
|
||||
assert_eq!(deserialized.key_id, "test-key-id");
|
||||
assert_eq!(deserialized.master_key_id, "master-key-id");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_data_key_envelope_discriminator_rejects_local_formats() {
|
||||
let kms_envelope = br#"{
|
||||
"key_id": "test-key-id",
|
||||
"master_key_id": "master-key-id",
|
||||
"key_spec": "AES_256",
|
||||
"encrypted_key": [1, 2, 3, 4],
|
||||
"nonce": [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
"encryption_context": {},
|
||||
"created_at": "2024-01-01T00:00:00+00:00"
|
||||
}"#;
|
||||
let minio_legacy = br#"{"aead":"AES-256-GCM-HMAC-SHA-256","iv":[1],"nonce":[2],"bytes":[3]}"#;
|
||||
let duplicate_key_id = [b"{\"key_id\":\"duplicate\",".as_slice(), &kms_envelope[1..]].concat();
|
||||
|
||||
assert!(is_data_key_envelope(kms_envelope));
|
||||
assert!(is_data_key_envelope(&[b" \n".as_slice(), kms_envelope].concat()));
|
||||
assert!(!is_data_key_envelope(&duplicate_key_id));
|
||||
assert!(!is_data_key_envelope(b"bm9uY2U=:Y2lwaGVydGV4dA=="));
|
||||
assert!(!is_data_key_envelope(minio_legacy));
|
||||
|
||||
let envelope_value: serde_json::Value = serde_json::from_slice(kms_envelope).expect("parse KMS envelope fixture");
|
||||
for required_field in [
|
||||
"key_id",
|
||||
"master_key_id",
|
||||
"key_spec",
|
||||
"encrypted_key",
|
||||
"nonce",
|
||||
"encryption_context",
|
||||
"created_at",
|
||||
] {
|
||||
let mut incomplete = envelope_value.clone();
|
||||
incomplete
|
||||
.as_object_mut()
|
||||
.expect("KMS envelope fixture is an object")
|
||||
.remove(required_field);
|
||||
assert!(
|
||||
!is_data_key_envelope(&serde_json::to_vec(&incomplete).expect("serialize incomplete envelope")),
|
||||
"missing {required_field} must not classify as a KMS envelope"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,4 +17,4 @@
|
||||
pub mod ciphers;
|
||||
pub mod dek;
|
||||
|
||||
pub use dek::{AesDekCrypto, DataKeyEnvelope, DekCrypto, generate_key_material};
|
||||
pub use dek::{AesDekCrypto, DataKeyEnvelope, DekCrypto, generate_key_material, is_data_key_envelope};
|
||||
|
||||
Reference in New Issue
Block a user