use cosmian_kmip::time_normalize;
use cosmian_kms_client::{
KmsClient, KmsClientError,
cosmian_kmip::{
kmip_0::kmip_types::{CryptographicUsageMask, RevocationReason, RevocationReasonCode},
kmip_2_1::{
kmip_attributes::Attributes,
kmip_objects::ObjectType,
kmip_operations::{Activate, Create, CreateResponse, Destroy},
kmip_types::{CryptographicAlgorithm, UniqueIdentifier},
requests::build_revoke_key_request,
},
},
};
use cosmian_logger::{info, log_init};
use test_kms_server::start_default_test_kms_server;
use crate::error::result::KmsCliResult;
async fn create_preactive_symmetric_key(client: &KmsClient) -> KmsCliResult<String> {
let future_activation = time_normalize()? + time::Duration::hours(1);
let attributes = Attributes {
activation_date: Some(future_activation),
cryptographic_algorithm: Some(CryptographicAlgorithm::AES),
cryptographic_length: Some(256),
cryptographic_usage_mask: Some(
CryptographicUsageMask::Encrypt | CryptographicUsageMask::Decrypt,
),
object_type: Some(ObjectType::SymmetricKey),
..Default::default()
};
let request = Create {
object_type: ObjectType::SymmetricKey,
attributes,
protection_storage_masks: None,
};
let response: CreateResponse = client.create(request).await?;
Ok(response.unique_identifier.to_string())
}
#[tokio::test]
async fn test_activate_success() -> KmsCliResult<()> {
log_init(None);
let ctx = start_default_test_kms_server().await;
let client = ctx.get_owner_client();
let key_id = create_preactive_symmetric_key(&client).await?;
let activate_request = Activate {
unique_identifier: UniqueIdentifier::TextString(key_id.clone()),
};
let response = client.activate(activate_request).await?;
assert_eq!(response.unique_identifier.to_string(), key_id);
Ok(())
}
#[tokio::test]
async fn test_activate_object_not_found() -> KmsCliResult<()> {
log_init(None);
let ctx = start_default_test_kms_server().await;
let client = ctx.get_owner_client();
let non_existent_id = "non-existent-key-id-12345".to_string();
let activate_request = Activate {
unique_identifier: UniqueIdentifier::TextString(non_existent_id),
};
let result = client.activate(activate_request).await;
assert!(result.is_err());
let err = result.unwrap_err();
assert!(
matches!(&err, KmsClientError::RequestFailed(msg) if msg.contains("Object_Not_Found") || msg.contains("Item_Not_Found")),
"Expected Object_Not_Found error, got: {err:?}"
);
Ok(())
}
#[tokio::test]
async fn test_activate_already_active() -> KmsCliResult<()> {
log_init(None);
let ctx = start_default_test_kms_server().await;
let client = ctx.get_owner_client();
let key_id = create_preactive_symmetric_key(&client).await?;
let activate_request = Activate {
unique_identifier: UniqueIdentifier::TextString(key_id.clone()),
};
client.activate(activate_request).await?;
let activate_request2 = Activate {
unique_identifier: UniqueIdentifier::TextString(key_id),
};
let result = client.activate(activate_request2).await;
assert!(result.is_err());
let err = result.unwrap_err();
assert!(
matches!(&err, KmsClientError::RequestFailed(msg) if msg.contains("Wrong_Key_Lifecycle_State")),
"Expected Wrong_Key_Lifecycle_State error, got: {err:?}"
);
Ok(())
}
#[tokio::test]
async fn test_activate_deactivated_key() -> KmsCliResult<()> {
log_init(None);
let ctx = start_default_test_kms_server().await;
let client = ctx.get_owner_client();
let key_id = create_preactive_symmetric_key(&client).await?;
let activate_request = Activate {
unique_identifier: UniqueIdentifier::TextString(key_id.clone()),
};
client.activate(activate_request).await?;
let revoke_request = build_revoke_key_request(
&key_id,
RevocationReason {
revocation_reason_code: RevocationReasonCode::Unspecified,
revocation_message: Some("Test revocation".to_string()),
},
)?;
client.revoke(revoke_request).await?;
let activate_request2 = Activate {
unique_identifier: UniqueIdentifier::TextString(key_id),
};
let result = client.activate(activate_request2).await;
assert!(result.is_err());
let err = result.unwrap_err();
assert!(
matches!(&err, KmsClientError::RequestFailed(msg) if msg.contains("Wrong_Key_Lifecycle_State")),
"Expected Wrong_Key_Lifecycle_State error for deactivated key, got: {err:?}"
);
Ok(())
}
#[tokio::test]
async fn test_activate_state_transition() -> KmsCliResult<()> {
log_init(None);
let ctx = start_default_test_kms_server().await;
let client = ctx.get_owner_client();
let key_id = create_preactive_symmetric_key(&client).await?;
let activate_request = Activate {
unique_identifier: UniqueIdentifier::TextString(key_id.clone()),
};
let response = client.activate(activate_request).await?;
assert_eq!(response.unique_identifier.to_string(), key_id);
Ok(())
}
#[tokio::test]
async fn test_activate_destroyed_key() -> KmsCliResult<()> {
log_init(None);
let ctx = start_default_test_kms_server().await;
let client = ctx.get_owner_client();
let key_id = create_preactive_symmetric_key(&client).await?;
let activate_request = Activate {
unique_identifier: UniqueIdentifier::TextString(key_id.clone()),
};
client.activate(activate_request).await?;
let revoke_request = build_revoke_key_request(
&key_id,
RevocationReason {
revocation_reason_code: RevocationReasonCode::Unspecified,
revocation_message: Some("Test revocation for destroy".to_string()),
},
)?;
client.revoke(revoke_request).await?;
let destroy_request = Destroy {
unique_identifier: Some(UniqueIdentifier::TextString(key_id.clone())),
remove: false,
cascade: false,
expected_object_type: None,
};
client.destroy(destroy_request).await?;
let activate_request2 = Activate {
unique_identifier: UniqueIdentifier::TextString(key_id),
};
let result = client.activate(activate_request2).await;
assert!(result.is_err());
let err = result.unwrap_err();
assert!(
matches!(&err, KmsClientError::RequestFailed(msg) if msg.contains("Wrong_Key_Lifecycle_State")),
"Expected Wrong_Key_Lifecycle_State error for destroyed key, got: {err:?}"
);
Ok(())
}
#[tokio::test]
async fn test_activate_compromised_key() -> KmsCliResult<()> {
log_init(None);
let ctx = start_default_test_kms_server().await;
let client = ctx.get_owner_client();
let key_id = create_preactive_symmetric_key(&client).await?;
let activate_request = Activate {
unique_identifier: UniqueIdentifier::TextString(key_id.clone()),
};
client.activate(activate_request).await?;
let revoke_request = build_revoke_key_request(
&key_id,
RevocationReason {
revocation_reason_code: RevocationReasonCode::KeyCompromise,
revocation_message: Some("Test key compromise".to_string()),
},
)?;
client.revoke(revoke_request).await?;
let activate_request2 = Activate {
unique_identifier: UniqueIdentifier::TextString(key_id),
};
let result = client.activate(activate_request2).await;
assert!(result.is_err());
let err = result.unwrap_err();
assert!(
matches!(&err, KmsClientError::RequestFailed(msg) if msg.contains("Wrong_Key_Lifecycle_State")),
"Expected Wrong_Key_Lifecycle_State error for compromised key, got: {err:?}"
);
Ok(())
}
#[tokio::test]
#[ignore = "Requires authentication setup to test Permission Denied"]
async fn test_activate_permission_denied() -> KmsCliResult<()> {
log_init(None);
let ctx = start_default_test_kms_server().await;
let client = ctx.get_owner_client();
let key_id = create_preactive_symmetric_key(&client).await?;
let activate_request = Activate {
unique_identifier: UniqueIdentifier::TextString(key_id),
};
let result = client.activate(activate_request).await;
assert!(result.is_ok() || result.is_err());
Ok(())
}
#[tokio::test]
async fn test_activate_invalid_object_type() -> KmsCliResult<()> {
log_init(None);
let ctx = start_default_test_kms_server().await;
let client = ctx.get_owner_client();
let attributes = Attributes {
object_type: Some(ObjectType::OpaqueObject),
..Default::default()
};
let create_request = Create {
object_type: ObjectType::OpaqueObject,
attributes,
protection_storage_masks: None,
};
let create_result = client.create(create_request).await;
if let Ok(response) = create_result {
let activate_request = Activate {
unique_identifier: response.unique_identifier,
};
let result = client.activate(activate_request).await;
assert!(result.is_err(), "Activating OpaqueObject should fail");
let err = result.unwrap_err();
assert!(
matches!(&err, KmsClientError::RequestFailed(msg) if msg.contains("Invalid_Object_Type")),
"Expected Invalid_Object_Type error, got: {err:?}"
);
} else {
info!(
"OpaqueObject creation not supported, skipping Invalid Object Type test for activation"
);
}
Ok(())
}