use cosmian_logger::{log_init, trace};
use tempfile::TempDir;
use test_kms_server::{TestsContext, start_default_test_kms_server};
#[cfg(feature = "non-fips")]
use crate::actions::kms::cover_crypt::keys::{
create_key_pair::CreateMasterKeyPairAction, create_user_key::CreateUserKeyAction,
};
use crate::{
actions::kms::{
elliptic_curves::keys::create_key_pair::CreateKeyPairAction,
secret_data::create_secret::CreateSecretDataAction,
shared::ExportSecretDataOrKeyAction,
symmetric::keys::{
create_key::CreateKeyAction, destroy_key::DestroyKeyAction, revoke_key::RevokeKeyAction,
},
},
cli_bail,
error::result::KmsCliResult,
};
async fn assert_destroyed(ctx: &TestsContext, key_id: &str, remove: bool) -> KmsCliResult<()> {
let tmp_dir = TempDir::new()?;
let tmp_path = tmp_dir.path();
ExportSecretDataOrKeyAction {
key_id: Some(key_id.to_owned()),
key_file: tmp_path.join("output.export"),
..Default::default()
}
.run(ctx.get_owner_client())
.await
.unwrap_err();
let export_res = ExportSecretDataOrKeyAction {
key_id: Some(key_id.to_owned()),
key_file: tmp_path.join("output.export"),
allow_revoked: true,
..Default::default()
}
.run(ctx.get_owner_client())
.await;
if export_res.is_ok() {
cli_bail!(
"Export with allow_revoked should have failed for destroyed key, but it succeeded"
);
}
if remove {
Ok(())
} else {
cli_bail!("Destroyed key cannot be exported (expected by test)")
}
}
#[tokio::test]
async fn test_destroy_symmetric_key() -> KmsCliResult<()> {
log_init(None);
let ctx = start_default_test_kms_server().await;
let key_id = CreateKeyAction::default()
.run(ctx.get_owner_client())
.await?
.to_string();
DestroyKeyAction {
key_id: Some(key_id.clone()),
remove: false,
tags: None,
}
.run(ctx.get_owner_client())
.await
.unwrap_err();
RevokeKeyAction {
key_id: Some(key_id.clone()),
revocation_reason: "revocation test".to_owned(),
tags: None,
}
.run(ctx.get_owner_client())
.await?;
DestroyKeyAction {
key_id: Some(key_id.clone()),
remove: false,
tags: None,
}
.run(ctx.get_owner_client())
.await?;
assert_destroyed(ctx, &key_id, false).await.unwrap_err();
Ok(())
}
#[tokio::test]
async fn test_destroy_and_remove_symmetric_key() -> KmsCliResult<()> {
let ctx = start_default_test_kms_server().await;
let key_id = CreateKeyAction::default()
.run(ctx.get_owner_client())
.await
.unwrap()
.to_string();
DestroyKeyAction {
key_id: Some(key_id.clone()),
remove: true,
tags: None,
}
.run(ctx.get_owner_client())
.await
.unwrap_err();
RevokeKeyAction {
key_id: Some(key_id.clone()),
revocation_reason: "key-compromise".to_owned(),
tags: None,
}
.run(ctx.get_owner_client())
.await
.unwrap();
DestroyKeyAction {
key_id: Some(key_id.clone()),
remove: true,
tags: None,
}
.run(ctx.get_owner_client())
.await
.unwrap();
assert_destroyed(ctx, &key_id, true).await
}
#[tokio::test]
async fn test_destroy_ec_key() -> KmsCliResult<()> {
let ctx = start_default_test_kms_server().await;
{
let (private_key_id, public_key_id) = CreateKeyPairAction::default()
.run(ctx.get_owner_client())
.await?;
DestroyKeyAction {
key_id: Some(private_key_id.to_string()),
remove: false,
tags: None,
}
.run(ctx.get_owner_client())
.await
.unwrap_err();
RevokeKeyAction {
key_id: Some(private_key_id.to_string()),
revocation_reason: "revocation test".to_owned(),
tags: None,
}
.run(ctx.get_owner_client())
.await?;
DestroyKeyAction {
key_id: Some(private_key_id.to_string()),
remove: false,
tags: None,
}
.run(ctx.get_owner_client())
.await?;
assert_destroyed(ctx, &private_key_id.to_string(), false)
.await
.unwrap_err();
assert_destroyed(ctx, &public_key_id.to_string(), false)
.await
.unwrap_err();
};
{
let (private_key_id, public_key_id) = CreateKeyPairAction::default()
.run(ctx.get_owner_client())
.await?;
DestroyKeyAction {
key_id: Some(public_key_id.to_string()),
remove: false,
tags: None,
}
.run(ctx.get_owner_client())
.await
.unwrap_err();
trace!("OK. revoking");
RevokeKeyAction {
key_id: Some(public_key_id.to_string()),
revocation_reason: "revocation test".to_owned(),
tags: None,
}
.run(ctx.get_owner_client())
.await?;
trace!("OK. destroying");
DestroyKeyAction {
key_id: Some(public_key_id.to_string()),
remove: false,
tags: None,
}
.run(ctx.get_owner_client())
.await?;
assert_destroyed(ctx, &private_key_id.to_string(), false)
.await
.unwrap_err();
assert_destroyed(ctx, &public_key_id.to_string(), false)
.await
.unwrap_err();
};
Ok(())
}
#[tokio::test]
async fn test_destroy_and_remove_ec_key() -> KmsCliResult<()> {
let ctx = start_default_test_kms_server().await;
{
let (private_key_id, public_key_id) = CreateKeyPairAction::default()
.run(ctx.get_owner_client())
.await?;
DestroyKeyAction {
key_id: Some(private_key_id.to_string()),
remove: true,
tags: None,
}
.run(ctx.get_owner_client())
.await
.unwrap_err();
RevokeKeyAction {
key_id: Some(private_key_id.to_string()),
revocation_reason: "key-compromise".to_owned(),
tags: None,
}
.run(ctx.get_owner_client())
.await?;
DestroyKeyAction {
key_id: Some(private_key_id.to_string()),
remove: true,
tags: None,
}
.run(ctx.get_owner_client())
.await?;
assert_destroyed(ctx, &private_key_id.to_string(), true).await?;
assert_destroyed(ctx, &public_key_id.to_string(), true).await?;
};
{
let (private_key_id, public_key_id) = CreateKeyPairAction::default()
.run(ctx.get_owner_client())
.await?;
DestroyKeyAction {
key_id: Some(public_key_id.to_string()),
remove: true,
tags: None,
}
.run(ctx.get_owner_client())
.await
.unwrap_err();
trace!("OK. revoking");
RevokeKeyAction {
key_id: Some(public_key_id.to_string()),
revocation_reason: "key-compromise".to_owned(),
tags: None,
}
.run(ctx.get_owner_client())
.await?;
DestroyKeyAction {
key_id: Some(public_key_id.to_string()),
remove: true,
tags: None,
}
.run(ctx.get_owner_client())
.await?;
assert_destroyed(ctx, &private_key_id.to_string(), true).await?;
assert_destroyed(ctx, &public_key_id.to_string(), true).await?;
};
Ok(())
}
#[cfg(feature = "non-fips")]
#[tokio::test]
async fn test_destroy_cover_crypt() -> KmsCliResult<()> {
use std::path::PathBuf;
let ctx = start_default_test_kms_server().await;
{
let (master_private_key_id, master_public_key_id) = {
let action = CreateMasterKeyPairAction {
specification: PathBuf::from(
"../../test_data/access_structure_specifications.json",
),
tags: vec![],
sensitive: false,
wrapping_key_id: None,
};
let key_ids = Box::pin(Box::pin(action.run(ctx.get_owner_client()))).await?;
(key_ids.0.to_string(), key_ids.1.to_string())
};
let user_key_id_1 = CreateUserKeyAction {
master_secret_key_id: master_private_key_id.clone(),
access_policy: "(Department::MKG || Department::FIN) && Security Level::Top Secret"
.to_string(),
tags: vec![],
sensitive: false,
wrapping_key_id: None,
}
.run(ctx.get_owner_client())
.await?
.to_string();
let user_key_id_2 = CreateUserKeyAction {
master_secret_key_id: master_private_key_id.clone(),
access_policy: "(Department::MKG || Department::FIN) && Security Level::Top Secret"
.to_string(),
tags: vec![],
sensitive: false,
wrapping_key_id: None,
}
.run(ctx.get_owner_client())
.await?
.to_string();
DestroyKeyAction {
key_id: Some(master_private_key_id.clone()),
remove: false,
tags: None,
}
.run(ctx.get_owner_client())
.await
.unwrap_err();
RevokeKeyAction {
key_id: Some(master_private_key_id.clone()),
revocation_reason: "revocation test".to_string(),
tags: None,
}
.run(ctx.get_owner_client())
.await?;
DestroyKeyAction {
key_id: Some(master_private_key_id.clone()),
remove: false,
tags: None,
}
.run(ctx.get_owner_client())
.await?;
assert_destroyed(ctx, &master_private_key_id, false)
.await
.unwrap_err();
assert_destroyed(ctx, &master_public_key_id, false)
.await
.unwrap_err();
assert_destroyed(ctx, &user_key_id_1, false)
.await
.unwrap_err();
assert_destroyed(ctx, &user_key_id_2, false)
.await
.unwrap_err();
};
{
let (master_private_key_id, master_public_key_id) = {
let action = CreateMasterKeyPairAction {
specification: PathBuf::from(
"../../test_data/access_structure_specifications.json",
),
tags: vec![],
sensitive: false,
wrapping_key_id: None,
};
let key_ids = Box::pin(action.run(ctx.get_owner_client())).await?;
(key_ids.0.to_string(), key_ids.1.to_string())
};
let user_key_id_1 = CreateUserKeyAction {
master_secret_key_id: master_private_key_id.clone(),
access_policy: "(Department::MKG || Department::FIN) && Security Level::Top Secret"
.to_string(),
tags: vec![],
sensitive: false,
wrapping_key_id: None,
}
.run(ctx.get_owner_client())
.await?
.to_string();
let user_key_id_2 = CreateUserKeyAction {
master_secret_key_id: master_private_key_id.clone(),
access_policy: "(Department::MKG || Department::FIN) && Security Level::Top Secret"
.to_string(),
tags: vec![],
sensitive: false,
wrapping_key_id: None,
}
.run(ctx.get_owner_client())
.await?
.to_string();
DestroyKeyAction {
key_id: Some(master_public_key_id.clone()),
remove: false,
tags: None,
}
.run(ctx.get_owner_client())
.await
.unwrap_err();
RevokeKeyAction {
key_id: Some(master_public_key_id.clone()),
revocation_reason: "revocation test".to_string(),
tags: None,
}
.run(ctx.get_owner_client())
.await?;
DestroyKeyAction {
key_id: Some(master_public_key_id.clone()),
remove: false,
tags: None,
}
.run(ctx.get_owner_client())
.await?;
assert_destroyed(ctx, &master_private_key_id, false)
.await
.unwrap_err();
assert_destroyed(ctx, &master_public_key_id, false)
.await
.unwrap_err();
assert_destroyed(ctx, &user_key_id_1, false)
.await
.unwrap_err();
assert_destroyed(ctx, &user_key_id_2, false)
.await
.unwrap_err();
};
{
let (master_private_key_id, master_public_key_id) = {
let action = CreateMasterKeyPairAction {
specification: PathBuf::from(
"../../test_data/access_structure_specifications.json",
),
tags: vec![],
sensitive: false,
wrapping_key_id: None,
};
let key_ids = Box::pin(action.run(ctx.get_owner_client())).await?;
(key_ids.0.to_string(), key_ids.1.to_string())
};
let user_key_id_1 = CreateUserKeyAction {
master_secret_key_id: master_private_key_id.clone(),
access_policy: "(Department::MKG || Department::FIN) && Security Level::Top Secret"
.to_string(),
tags: vec![],
sensitive: false,
wrapping_key_id: None,
}
.run(ctx.get_owner_client())
.await?
.to_string();
let user_key_id_2 = CreateUserKeyAction {
master_secret_key_id: master_private_key_id.clone(),
access_policy: "(Department::MKG || Department::FIN) && Security Level::Top Secret"
.to_string(),
tags: vec![],
sensitive: false,
wrapping_key_id: None,
}
.run(ctx.get_owner_client())
.await?
.to_string();
DestroyKeyAction {
key_id: Some(user_key_id_1.clone()),
remove: false,
tags: None,
}
.run(ctx.get_owner_client())
.await
.unwrap_err();
RevokeKeyAction {
key_id: Some(user_key_id_1.clone()),
revocation_reason: "revocation test".to_string(),
tags: None,
}
.run(ctx.get_owner_client())
.await?;
DestroyKeyAction {
key_id: Some(user_key_id_1.clone()),
remove: false,
tags: None,
}
.run(ctx.get_owner_client())
.await?;
assert_destroyed(ctx, &user_key_id_1.clone(), false)
.await
.unwrap_err();
let tmp_dir = TempDir::new()?;
let tmp_path = tmp_dir.path();
ExportSecretDataOrKeyAction {
key_id: Some(master_private_key_id.clone()),
key_file: tmp_path.join("output.export"),
..Default::default()
}
.run(ctx.get_owner_client())
.await
.unwrap();
ExportSecretDataOrKeyAction {
key_id: Some(master_public_key_id.clone()),
key_file: tmp_path.join("output.export"),
..Default::default()
}
.run(ctx.get_owner_client())
.await
.unwrap();
ExportSecretDataOrKeyAction {
key_id: Some(user_key_id_2.clone()),
key_file: tmp_path.join("output.export"),
..Default::default()
}
.run(ctx.get_owner_client())
.await
.unwrap();
};
Ok(())
}
#[tokio::test]
async fn test_destroy_secret_data() -> KmsCliResult<()> {
log_init(None);
let ctx = start_default_test_kms_server().await;
let secret_id = CreateSecretDataAction::default()
.run(ctx.get_owner_client())
.await?
.to_string();
DestroyKeyAction {
key_id: Some(secret_id.clone()),
remove: false,
tags: None,
}
.run(ctx.get_owner_client())
.await
.unwrap_err();
RevokeKeyAction {
key_id: Some(secret_id.clone()),
revocation_reason: "revocation test".to_owned(),
tags: None,
}
.run(ctx.get_owner_client())
.await?;
DestroyKeyAction {
key_id: Some(secret_id.clone()),
remove: false,
tags: None,
}
.run(ctx.get_owner_client())
.await?;
assert_destroyed(ctx, &secret_id, false).await.unwrap_err();
Ok(())
}