use cosmian_kmip::time_normalize;
use cosmian_kms_client::cosmian_kmip::{
kmip_0::kmip_types::{CryptographicUsageMask, State},
kmip_2_1::{
kmip_attributes::Attributes,
kmip_objects::ObjectType,
kmip_operations::{Create, CreateResponse, Get},
kmip_types::{CryptographicAlgorithm, ProtectionStorageMasks},
},
};
use cosmian_logger::log_init;
use test_kms_server::start_default_test_kms_server;
use crate::error::{KmsCliError, result::KmsCliResult};
#[tokio::test]
async fn test_create_success_symmetric_key_preactive() -> KmsCliResult<()> {
log_init(None);
let ctx = start_default_test_kms_server().await;
let client = ctx.get_owner_client();
let attributes = Attributes {
cryptographic_algorithm: Some(CryptographicAlgorithm::AES),
cryptographic_length: Some(256),
cryptographic_usage_mask: Some(
CryptographicUsageMask::Encrypt | CryptographicUsageMask::Decrypt,
),
..Default::default()
};
let create = Create {
object_type: ObjectType::SymmetricKey,
attributes,
protection_storage_masks: None,
};
let response: CreateResponse = client.create(create).await?;
assert!(!response.unique_identifier.to_string().is_empty());
assert_eq!(
response.object_type,
ObjectType::SymmetricKey,
"Response object type should match request"
);
let get_response = client
.get(Get {
unique_identifier: Some(response.unique_identifier.clone()),
..Default::default()
})
.await?;
let state = get_response
.object
.attributes()?
.state
.ok_or_else(|| KmsCliError::Default("Missing state".to_owned()))?;
assert_eq!(
state,
State::PreActive,
"Symmetric key without activation date should be PreActive"
);
Ok(())
}
#[tokio::test]
async fn test_create_success_symmetric_key_active() -> KmsCliResult<()> {
log_init(None);
let ctx = start_default_test_kms_server().await;
let client = ctx.get_owner_client();
let activation_date = time_normalize()? - time::Duration::hours(1);
let attributes = Attributes {
cryptographic_algorithm: Some(CryptographicAlgorithm::AES),
cryptographic_length: Some(256),
cryptographic_usage_mask: Some(
CryptographicUsageMask::Encrypt | CryptographicUsageMask::Decrypt,
),
activation_date: Some(activation_date.replace_millisecond(0).unwrap()),
..Default::default()
};
let create = Create {
object_type: ObjectType::SymmetricKey,
attributes,
protection_storage_masks: None,
};
let response: CreateResponse = client.create(create).await?;
assert!(!response.unique_identifier.to_string().is_empty());
let get_response = client
.get(Get {
unique_identifier: Some(response.unique_identifier.clone()),
..Default::default()
})
.await?;
let state = get_response
.object
.attributes()?
.state
.ok_or_else(|| KmsCliError::Default("Missing state".to_owned()))?;
assert_eq!(
state,
State::Active,
"Symmetric key with past activation date should be Active"
);
Ok(())
}
#[tokio::test]
async fn test_create_state_determination_future_activation() -> KmsCliResult<()> {
log_init(None);
let ctx = start_default_test_kms_server().await;
let client = ctx.get_owner_client();
let activation_date = time_normalize()? + time::Duration::days(1);
let attributes = Attributes {
cryptographic_algorithm: Some(CryptographicAlgorithm::AES),
cryptographic_length: Some(256),
cryptographic_usage_mask: Some(
CryptographicUsageMask::Encrypt | CryptographicUsageMask::Decrypt,
),
activation_date: Some(activation_date.replace_millisecond(0).unwrap()),
..Default::default()
};
let create = Create {
object_type: ObjectType::SymmetricKey,
attributes,
protection_storage_masks: None,
};
let response: CreateResponse = client.create(create).await?;
assert!(!response.unique_identifier.to_string().is_empty());
let get_response = client
.get(Get {
unique_identifier: Some(response.unique_identifier.clone()),
..Default::default()
})
.await?;
let state = get_response
.object
.attributes()?
.state
.ok_or_else(|| KmsCliError::Default("Missing state".to_owned()))?;
assert_eq!(
state,
State::PreActive,
"Symmetric key with future activation date should be PreActive"
);
Ok(())
}
#[tokio::test]
async fn test_create_success_secret_data() -> 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::SecretData),
..Default::default()
};
let create = Create {
object_type: ObjectType::SecretData,
attributes,
protection_storage_masks: None,
};
let response: CreateResponse = client.create(create).await?;
assert!(!response.unique_identifier.to_string().is_empty());
assert_eq!(
response.object_type,
ObjectType::SecretData,
"Response object type should be SecretData"
);
Ok(())
}
#[tokio::test]
async fn test_create_error_invalid_object_type() -> KmsCliResult<()> {
log_init(None);
let ctx = start_default_test_kms_server().await;
let client = ctx.get_owner_client();
let attributes = Attributes {
cryptographic_algorithm: Some(CryptographicAlgorithm::AES),
cryptographic_length: Some(256),
..Default::default()
};
let create = Create {
object_type: ObjectType::Certificate, attributes,
protection_storage_masks: None,
};
let result = client.create(create).await;
assert!(
result.is_err(),
"Create should fail for unsupported object type"
);
let error = result.unwrap_err();
let error_msg = error.to_string().to_lowercase();
assert!(
error_msg.contains("not")
&& (error_msg.contains("support") || error_msg.contains("certificate")),
"Error should indicate unsupported object type, got: {error}"
);
Ok(())
}
#[tokio::test]
async fn test_create_error_feature_not_supported() -> KmsCliResult<()> {
log_init(None);
let ctx = start_default_test_kms_server().await;
let client = ctx.get_owner_client();
let attributes = Attributes {
cryptographic_algorithm: Some(CryptographicAlgorithm::AES),
cryptographic_length: Some(256),
cryptographic_usage_mask: Some(
CryptographicUsageMask::Encrypt | CryptographicUsageMask::Decrypt,
),
..Default::default()
};
let create = Create {
object_type: ObjectType::SymmetricKey,
attributes,
protection_storage_masks: Some(ProtectionStorageMasks::empty()),
};
let result = client.create(create).await;
assert!(
result.is_err(),
"Create should fail when protection_storage_masks is provided"
);
let error = result.unwrap_err();
let error_msg = error.to_string().to_lowercase();
assert!(
error_msg.contains("not") && error_msg.contains("support"),
"Error should indicate feature not supported, got: {error}"
);
Ok(())
}
#[tokio::test]
async fn test_create_error_invalid_attribute() -> KmsCliResult<()> {
log_init(None);
let ctx = start_default_test_kms_server().await;
let client = ctx.get_owner_client();
let attributes = Attributes {
cryptographic_length: Some(256),
cryptographic_usage_mask: Some(
CryptographicUsageMask::Encrypt | CryptographicUsageMask::Decrypt,
),
..Default::default()
};
let create = Create {
object_type: ObjectType::SymmetricKey,
attributes,
protection_storage_masks: None,
};
let result = client.create(create).await;
assert!(
result.is_err(),
"Create should fail with missing required attributes"
);
let error = result.unwrap_err();
let error_msg = error.to_string().to_lowercase();
assert!(
error_msg.contains("algorithm")
|| error_msg.contains("attribute")
|| error_msg.contains("missing"),
"Error should indicate invalid/missing attributes, got: {error}"
);
Ok(())
}