feat(kms): add the data key rewrap primitive (#5607)

* feat(kms): add data key rewrap and wrapping inspection primitives

Rewrap re-protects an existing data key envelope with the master key's
current version without touching the data key itself, which is the
precondition for ever retiring an older version: until every envelope a
version wrapped has been moved off it, destroying that version orphans
every object whose data key it wrapped.

Adds KmsBackend::rewrap_data_key and its read-only counterpart
describe_data_key_wrapping, both gated by a new BackendCapabilities::rewrap
flag and defaulting to UnsupportedCapability. Vault KV2 unwraps with the
frozen version record that wrapped the envelope and re-wraps with the
current material; Vault Transit uses the native transit/rewrap endpoint so
the data key never enters this process.

No read or write path changes: nothing calls these yet.

* test(kms): cover the rewrap primitive against a scripted Vault

* fix(kms): resolve both key materials before the data key is unwrapped

Keeps every fallible step out of the window in which the plaintext data
key exists, so no error path can drop it without zeroizing it first.
This commit is contained in:
Zhengchao An
2026-08-02 13:51:12 +08:00
committed by GitHub
parent c147afd19c
commit bd834297da
13 changed files with 1156 additions and 10 deletions
+27 -2
View File
@@ -23,8 +23,10 @@ use crate::error::{KmsError, Result};
use crate::types::{
CancelKeyDeletionRequest, CancelKeyDeletionResponse, CreateKeyRequest, CreateKeyResponse,
DEFAULT_PENDING_DELETION_WINDOW_DAYS, DecryptRequest, DecryptResponse, DeleteKeyRequest, DeleteKeyResponse,
DescribeKeyRequest, DescribeKeyResponse, EncryptRequest, EncryptResponse, GenerateDataKeyRequest, GenerateDataKeyResponse,
ListKeysRequest, ListKeysResponse, MAX_PENDING_DELETION_WINDOW_DAYS, MIN_PENDING_DELETION_WINDOW_DAYS, OperationContext,
DescribeDataKeyWrappingRequest, DescribeDataKeyWrappingResponse, DescribeKeyRequest, DescribeKeyResponse, EncryptRequest,
EncryptResponse, GenerateDataKeyRequest, GenerateDataKeyResponse, ListKeysRequest, ListKeysResponse,
MAX_PENDING_DELETION_WINDOW_DAYS, MIN_PENDING_DELETION_WINDOW_DAYS, OperationContext, RewrapDataKeyRequest,
RewrapDataKeyResponse,
};
use std::collections::HashMap;
use std::sync::Arc;
@@ -167,6 +169,29 @@ impl KmsManager {
self.backend.generate_data_key(request).await
}
/// Re-wrap an existing data key envelope onto the master key's current
/// version, leaving the data key — and therefore every object body it
/// protects — untouched.
///
/// Backends without retained version history reject this with
/// [`KmsError::UnsupportedCapability`]; check
/// [`Self::backend_capabilities`] before offering it.
pub async fn rewrap_data_key(&self, request: RewrapDataKeyRequest) -> Result<RewrapDataKeyResponse> {
self.backend.rewrap_data_key(request).await
}
/// Report which master key version wraps an existing data key envelope.
///
/// The read-only side of [`Self::rewrap_data_key`], and the supported way to
/// ask that question: where the version is recorded differs per backend, so
/// callers must not inspect envelopes themselves.
pub async fn describe_data_key_wrapping(
&self,
request: DescribeDataKeyWrappingRequest,
) -> Result<DescribeDataKeyWrappingResponse> {
self.backend.describe_data_key_wrapping(request).await
}
/// Describe a key
///
/// Audited as an internal operation; callers serving an authenticated