Skip to content

Commit

Permalink
feat: covercrypt rekey (#179)
Browse files Browse the repository at this point in the history
* feat: replace attribute rotation to access policy rekey

* refacto: move policy and rekey action in dedicated files

* chore: change vendor attribute name for covercrypt rekey action

* feat: change CLI command rotate to rekey and update tests

* fix: update user key locate tests

* ci: fix pyo3 tests

* ci: use last cloudproof python branch

* refacto: master keys rekey

* feat(pyo3): support and test policy attribute removal and renaming

* refacto: reuse updated private key to refresh user key

* ci: update cloudproof_kms_js branch

* refacto: factor user keys update inner for loop in `refresh_user_decryption_key`

* fix: cli rekey imports

* ci: fix cargo udeps

* feat: add cli command `cc keys rekey` and `cc keys prune`

* feat: add cli policy edit command and tests

* fix: remove deadcode and fix comments

* use closures in CC keys update

* fix: group KMS objects with their IDs

* fix: use release test to avoid worker stack overflow upon test error

* fix: review

* fix: define type `KmipKeyUidObject` to store a key UID and its KmipObject

* fix: apply review suggestions

(cherry picked from commit ebd196e8ed251603b657e1e6445a9e6d8e75ce48)

* ci: double `RUST_MIN_STACK` to `4MB` to avoid stack overflow during tests

* docs: update doc of CLI rekey and policy edit

* docs: update CLI doc and CHANGELOG

* fix: Reduce stack footprint (#200)

* CI: remove min stack size

* Box in Attributes

* Box key_wrapping_data and more cryptographic_parameters

* Box attributes in KeyValue

* Box BigUint and SafeBigUint in KeyMaterial

* chore: update KMS version to `4.13.0`

---------

Co-authored-by: Manuthor <[email protected]>
Co-authored-by: Théophile BRÉZOT <[email protected]>
Co-authored-by: Thibs <[email protected]>
  • Loading branch information
4 people authored Mar 8, 2024
1 parent 2f89b89 commit 4da4327
Show file tree
Hide file tree
Showing 61 changed files with 1,720 additions and 1,264 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/build_docker_image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ jobs:
- build-and-push-image
uses: Cosmian/reusable_workflows/.github/workflows/cloudproof_kms_js.yml@develop
with:
branch: develop
branch: feature/covercrypt_rekey
kms-version: ${{ needs.build-and-push-image.outputs.image-tag }}

cloudproof_java:
Expand All @@ -98,7 +98,7 @@ jobs:
- build-and-push-image
uses: Cosmian/reusable_workflows/.github/workflows/cloudproof_java_in_docker.yml@develop
with:
branch: develop
branch: feature/covercrypt_rekey
target: x86_64-unknown-linux-gnu
extension: so
destination: linux-x86-64
Expand All @@ -113,7 +113,7 @@ jobs:
- build-and-push-image
uses: Cosmian/reusable_workflows/.github/workflows/cloudproof_python.yml@develop
with:
branch: develop
branch: feature/covercrypt-rekey
target: x86_64-unknown-linux-gnu
kms-version: ${{ needs.build-and-push-image.outputs.image-tag }}
copy_fresh_build: true
Expand Down
3 changes: 0 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@ jobs:
with:
token: ${{ secrets.GITHUB_TOKEN }}

cargo-udeps:
uses: Cosmian/reusable_workflows/.github/workflows/cargo-udeps.yml@develop

cargo-lint:
uses: ./.github/workflows/clippy.yml
with:
Expand Down
4 changes: 0 additions & 4 deletions .github/workflows/main_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@ jobs:
with:
token: ${{ secrets.GITHUB_TOKEN }}

cargo-udeps:
uses: Cosmian/reusable_workflows/.github/workflows/cargo-udeps.yml@develop

cargo-lint:
uses: ./.github/workflows/clippy.yml
with:
Expand Down Expand Up @@ -139,7 +136,6 @@ jobs:
name: release
needs:
- cargo-audit
- cargo-udeps
- cargo-lint
- cargo-doc
- build
Expand Down
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@

All notable changes to this project will be documented in this file.

## [X.Y.Z] - 2024-02-XX
## [4.13.0] - 2024-03-08

### Features

- Save KMIP Attributes in a proper column of `Objects` table [#166](https://github.com/Cosmian/kms/pull/166).:
- Remove all custom tags `_cert_spki`, `_cert_cn`, `_cert_issuer` and `_cert_sk`
- Add support for CoverCrypt `rekey`, `prune`, and `Policy` editing methods
- Add CLI commands to perform these actions

### Bug Fixes

- Move internal KMIP Objects into `Box` to avoid stack memory overflow

## [4.12.0] - 2024-02-08

Expand Down
21 changes: 9 additions & 12 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ clap = { version = "4.4", default-features = false, features = [
"derive",
"cargo",
] }
cloudproof = "2.4.1"
cloudproof = { git = "https://github.com/Cosmian/cloudproof_rust", branch = "feature/covercrypt_rekey" }
cloudproof_findex = { version = "5.0", features = ["findex-redis"] }
const-oid = { version = "0.9", features = ["db"] }
der = { version = "0.7", features = ["pem"] }
Expand Down
2 changes: 1 addition & 1 deletion crate/cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "cosmian_kms_cli"
version = "4.12.0"
version = "4.13.0"
edition = "2021"
license-file = "../../LICENSE.md"
description = "CLI used to manage the Cosmian KMS."
Expand Down
15 changes: 8 additions & 7 deletions crate/cli/src/actions/certificates/certify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,13 +142,14 @@ impl CertifyAction {
// Using a Public Key ?
let unique_identifier = if let Some(public_key_to_certify) = &self.public_key_id_to_certify
{
attributes.certificate_attributes = Some(CertificateAttributes::parse_subject_line(
self.subject_name.as_ref().ok_or_else(|| {
CliError::Default(
"subject name is required when certifying a public key".to_string(),
)
})?,
)?);
attributes.certificate_attributes =
Some(Box::new(CertificateAttributes::parse_subject_line(
self.subject_name.as_ref().ok_or_else(|| {
CliError::Default(
"subject name is required when certifying a public key".to_string(),
)
})?,
)?));
Some(UniqueIdentifier::TextString(
public_key_to_certify.to_string(),
))
Expand Down
4 changes: 2 additions & 2 deletions crate/cli/src/actions/cover_crypt/keys/create_key_pair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use cosmian_kmip::crypto::cover_crypt::kmip_requests::build_create_master_keypai
use cosmian_kms_client::KmsRestClient;

use crate::{
actions::cover_crypt::policy::{policy_from_binary_file, policy_from_specifications_file},
actions::cover_crypt::policy::{policy_from_binary_file, policy_from_json_file},
cli_bail,
error::{result::CliResultHelper, CliError},
};
Expand Down Expand Up @@ -65,7 +65,7 @@ impl CreateMasterKeyPairAction {
pub async fn run(&self, kms_rest_client: &KmsRestClient) -> Result<(), CliError> {
// Parse the json policy file
let policy = if let Some(specs_file) = &self.policy_specifications_file {
policy_from_specifications_file(specs_file)?
policy_from_json_file(specs_file)?
} else if let Some(binary_file) = &self.policy_binary_file {
policy_from_binary_file(binary_file)?
} else {
Expand Down
14 changes: 11 additions & 3 deletions crate/cli/src/actions/cover_crypt/keys/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ use clap::Subcommand;
use cosmian_kms_client::KmsRestClient;

use self::{
create_key_pair::CreateMasterKeyPairAction, create_user_key::CreateUserKeyAction,
destroy_key::DestroyKeyAction, revoke_key::RevokeKeyAction,
create_key_pair::CreateMasterKeyPairAction,
create_user_key::CreateUserKeyAction,
destroy_key::DestroyKeyAction,
rekey::{PruneAction, RekeyAction},
revoke_key::RevokeKeyAction,
};
use crate::{
actions::shared::{ExportKeyAction, ImportKeyAction, UnwrapKeyAction, WrapKeyAction},
Expand All @@ -13,9 +16,10 @@ use crate::{
mod create_key_pair;
mod create_user_key;
mod destroy_key;
mod rekey;
mod revoke_key;

/// Create, destroy, import, export `Covercrypt` master and user keys
/// Create, destroy, import, export, and rekey `Covercrypt` master and user keys
#[derive(Subcommand)]
pub enum KeysCommands {
CreateMasterKeyPair(CreateMasterKeyPairAction),
Expand All @@ -26,6 +30,8 @@ pub enum KeysCommands {
Unwrap(UnwrapKeyAction),
Revoke(RevokeKeyAction),
Destroy(DestroyKeyAction),
Rekey(RekeyAction),
Prune(PruneAction),
}

impl KeysCommands {
Expand All @@ -39,6 +45,8 @@ impl KeysCommands {
Self::Unwrap(action) => action.run(kms_rest_client).await?,
Self::Revoke(action) => action.run(kms_rest_client).await?,
Self::Destroy(action) => action.run(kms_rest_client).await?,
Self::Rekey(action) => action.run(kms_rest_client).await?,
Self::Prune(action) => action.run(kms_rest_client).await?,
};

Ok(())
Expand Down
128 changes: 128 additions & 0 deletions crate/cli/src/actions/cover_crypt/keys/rekey.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
use clap::Parser;
use cosmian_kmip::crypto::cover_crypt::{
attributes::RekeyEditAction, kmip_requests::build_rekey_keypair_request,
};
use cosmian_kms_client::KmsRestClient;

use crate::{
cli_bail,
error::{result::CliResultHelper, CliError},
};

/// Rekey the master and user keys for a given access policy.
///
/// Active user decryption keys are automatically re-keyed.
/// Revoked or destroyed user decryption keys are not re-keyed.
///
/// User keys that have not been rekeyed will only be able to decrypt
/// data encrypted before this operation.
#[derive(Parser, Debug)]
#[clap(verbatim_doc_comment)]
pub struct RekeyAction {
/// The access policy to rekey.
/// Example: `department::marketing && level::confidential`
#[clap(required = true)]
access_policy: String,

/// The private master key unique identifier stored in the KMS.
/// If not specified, tags should be specified
#[clap(long = "key-id", short = 'k', group = "key-tags")]
secret_key_id: Option<String>,

/// Tag to use to retrieve the key when no key id is specified.
/// To specify multiple tags, use the option multiple times.
#[clap(long = "tag", short = 't', value_name = "TAG", group = "key-tags")]
tags: Option<Vec<String>>,
}

impl RekeyAction {
pub async fn run(&self, kms_rest_client: &KmsRestClient) -> Result<(), CliError> {
let id = if let Some(key_id) = &self.secret_key_id {
key_id.clone()
} else if let Some(tags) = &self.tags {
serde_json::to_string(&tags)?
} else {
cli_bail!("Either --key-id or one or more --tag must be specified")
};

// Create the kmip query
let query = build_rekey_keypair_request(
&id,
RekeyEditAction::RekeyAccessPolicy(self.access_policy.clone()),
)?;

// Query the KMS with your kmip data
let response = kms_rest_client
.rekey_keypair(query)
.await
.with_context(|| "failed rekeying the master keys")?;

println!(
"The master private key {} and master public key {} were rekeyed for the access \
policy {:?}",
&response.private_key_unique_identifier,
&response.public_key_unique_identifier,
&self.access_policy
);
Ok(())
}
}

/// Prune the master and user keys for a given access policy.
///
/// Active user decryption keys are automatically pruned.
/// Revoked or destroyed user decryption keys are not.
///
/// Pruned user keys will only be able to decrypt ciphertexts
/// generated after the last rekeying.
#[derive(Parser, Debug)]
#[clap(verbatim_doc_comment)]
pub struct PruneAction {
/// The access policy to prune.
/// Example: `department::marketing && level::confidential`
#[clap(required = true)]
access_policy: String,

/// The private master key unique identifier stored in the KMS.
/// If not specified, tags should be specified
#[clap(long = "key-id", short = 'k', group = "key-tags")]
secret_key_id: Option<String>,

/// Tag to use to retrieve the key when no key id is specified.
/// To specify multiple tags, use the option multiple times.
#[clap(long = "tag", short = 't', value_name = "TAG", group = "key-tags")]
tags: Option<Vec<String>>,
}

impl PruneAction {
pub async fn run(&self, kms_rest_client: &KmsRestClient) -> Result<(), CliError> {
let id = if let Some(key_id) = &self.secret_key_id {
key_id.clone()
} else if let Some(tags) = &self.tags {
serde_json::to_string(&tags)?
} else {
cli_bail!("Either --key-id or one or more --tag must be specified")
};

// Create the kmip query
let query = build_rekey_keypair_request(
&id,
RekeyEditAction::PruneAccessPolicy(self.access_policy.clone()),
)?;

// Query the KMS with your kmip data
let response = kms_rest_client
.rekey_keypair(query)
.await
.with_context(|| "failed pruning the master keys")?;

println!(
"The master private key {} and master public key {} were pruned for the access policy \
{:?}",
&response.private_key_unique_identifier,
&response.public_key_unique_identifier,
&self.access_policy
);
Ok(())
}
}
Loading

0 comments on commit 4da4327

Please sign in to comment.