Skip to content

Commit 411a05d

Browse files
committed
fix: rotate of HSM wrapping key id
1 parent 10a9373 commit 411a05d

11 files changed

Lines changed: 112 additions & 46 deletions

File tree

crate/hsm/base_hsm/src/session/session_impl.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1892,9 +1892,10 @@ impl Session {
18921892
// Format: "rotate_name::generation::key_id[@latest]"
18931893
//
18941894
// `rotate_name` may itself contain "::" — for HSM-resident keys the convention is
1895-
// rotate_name = "hsm::<slot>::<key_id>" (the full base UID), which is unique across
1896-
// slots. Split from the RIGHT so the variable-length rotate_name is always the
1897-
// residual left segment, regardless of how many "::" it contains.
1895+
// rotate_name = "hsm::<model>::<slot>::<key_id>" (the full base UID, including the
1896+
// model segment), which is unique across slots. Split from the RIGHT so the
1897+
// variable-length rotate_name is always the residual left segment, regardless of
1898+
// how many "::" it contains.
18981899
//
18991900
// rsplitn(3, "::") yields (from right to left):
19001901
// index 0 → key_id[@latest]

crate/server/src/core/operations/attributes/get.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -510,9 +510,11 @@ pub(crate) async fn get_attributes(
510510
res.state = attributes.state;
511511
}
512512
Tag::UniqueIdentifier => {
513-
attributes
514-
.unique_identifier
515-
.clone_into(&mut res.unique_identifier);
513+
// Always authoritative: use the stored object UID from the database row
514+
// rather than the embedded attribute value. The embedded value may be a
515+
// stale random UUID for re-keyed objects created before the `finalize`
516+
// fix stamped the correct new UID into the key material attributes.
517+
res.unique_identifier = Some(UniqueIdentifier::TextString(owm.id().to_owned()));
516518
}
517519
Tag::ShortUniqueIdentifier => {
518520
// Ensure presence: if absent, return an empty string

crate/server/src/core/operations/rekey/common.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ impl KMS {
240240
}
241241

242242
/// Re-wrap all keys that were wrapped by the old wrapping key.
243-
async fn rewrap_dependants(
243+
pub(crate) async fn rewrap_dependants(
244244
&self,
245245
owner: &str,
246246
old_uid: &str,
@@ -408,6 +408,15 @@ impl ReplacementObject {
408408
old_uid,
409409
paired_key,
410410
)?;
411+
// Stamp the embedded attributes with the correct UID.
412+
// `create_symmetric_key_kmip_object` always assigns a random UUID to
413+
// `attributes.unique_identifier`; replace it with `new_uid` so that
414+
// GetAttributes always returns a `unique_identifier` that matches the
415+
// object's actual stored UID.
416+
if let Ok(embedded_attrs) = self.object.attributes_mut() {
417+
embedded_attrs.unique_identifier =
418+
Some(UniqueIdentifier::TextString(self.new_uid.clone()));
419+
}
411420
let attrs = self.object.attributes().cloned().unwrap_or_default();
412421
self.tags = attrs.get_tags(vendor_id);
413422
self.attributes = attrs;

crate/server/src/core/operations/rekey/symmetric/hsm.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
//! on the same HSM slot (via `C_GenerateKey`), assigns a generation-suffixed UID, and
66
//! updates `CKA_LABEL` / `CKA_START_DATE` / `CKA_END_DATE` on both old and new keys.
77
8-
use cosmian_kms_server_database::reexport::cosmian_kmip::kmip_2_1::{
9-
kmip_objects::ObjectType, kmip_operations::ReKeyResponse, kmip_types::UniqueIdentifier,
8+
use cosmian_kms_server_database::reexport::{
9+
cosmian_kmip::kmip_2_1::{
10+
kmip_objects::ObjectType, kmip_operations::ReKeyResponse, kmip_types::UniqueIdentifier,
11+
},
12+
cosmian_kms_interfaces::AtomicOperation,
1013
};
1114
use cosmian_logger::trace;
1215
use time::OffsetDateTime;
@@ -263,6 +266,18 @@ impl KMS {
263266

264267
trace!("HSM ReKey: old={uid} → new={new_uid} (slot={slot_id}, gen={new_gen}), user={user}");
265268

269+
// Re-wrap any DB keys that were wrapped by the old HSM key, so that
270+
// they remain accessible under the new generation without requiring
271+
// the caller to keep the old HSM key alive.
272+
let mut operations: Vec<AtomicOperation> = Vec::new();
273+
Box::pin(self.rewrap_dependants(user, uid, &new_uid, &mut operations)).await?;
274+
if !operations.is_empty() {
275+
self.database
276+
.atomic(user, &operations)
277+
.await
278+
.map_err(KmsError::Database)?;
279+
}
280+
266281
Ok(ReKeyResponse {
267282
unique_identifier: UniqueIdentifier::TextString(new_uid),
268283
})

crate/server/src/core/operations/rekey/symmetric/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ pub(crate) async fn rekey(kms: &KMS, request: ReKey, owner: &str) -> KResult<ReK
5252
// The general RekeyOperation pipeline is designed for SQL-backed keys and
5353
// is not applicable to non-extractable HSM key material.
5454
if has_prefix(&uid).is_some() {
55-
return kms.rekey_hsm_symmetric(&uid, owner).await;
55+
return Box::pin(kms.rekey_hsm_symmetric(&uid, owner)).await;
5656
}
5757

5858
let request = ReKey {

0 commit comments

Comments
 (0)