Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
198 changes: 106 additions & 92 deletions .github/copilot-instructions.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion .github/scripts/test/test_ui.sh
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ echo "==> Building UI (VITE_KMS_URL=http://127.0.0.1:9998) …"
chmod -R u+w dist >/dev/null 2>&1 || true
rm -rf dist >/dev/null 2>&1 || true
})
(cd "${UI_DIR}" && env -u LD_PRELOAD -u OPENSSL_CONF -u OPENSSL_MODULES VITE_KMS_URL="http://127.0.0.1:9998" pnpm run build)
(cd "${UI_DIR}" && env -u LD_PRELOAD -u OPENSSL_CONF -u OPENSSL_MODULES VITE_KMS_URL="http://127.0.0.1:9998" pnpm run build:react)

# ── 4. Install Playwright's Chromium browser ─────────────────────────────────
echo "==> Installing Playwright Chromium browser …"
Expand Down
5 changes: 5 additions & 0 deletions .github/scripts/windows/test_ui.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -222,13 +222,18 @@ try {
# SoftHSM2 is not available on Windows; signal to the specs that no HSM
# keys are pre-created so the HSM-specific tests are skipped.
$env:PLAYWRIGHT_HSM_KEY_COUNT = "0"
# Use fewer parallel workers on Windows: the CI runner is slower than
# Linux, and 10 concurrent workers saturate the single KMS server causing
# 408 Request Timeout errors in unrelated tests.
$env:PLAYWRIGHT_WORKERS = "5"
try {
Invoke-Checked $pnpmCmd @("run", "test:e2e")
}
finally {
Remove-Item Env:CI -ErrorAction SilentlyContinue
Remove-Item Env:PLAYWRIGHT_BASE_URL -ErrorAction SilentlyContinue
Remove-Item Env:PLAYWRIGHT_HSM_KEY_COUNT -ErrorAction SilentlyContinue
Remove-Item Env:PLAYWRIGHT_WORKERS -ErrorAction SilentlyContinue
Pop-Location
}
}
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -301,5 +301,5 @@ repos:
rev: v3.13.7
hooks:
- id: markdown-link-check
args: [-q, --config, ./.markdown-link-check.json]
args: [-q, --config, .github/.markdown-link-check.json]
exclude: .github/copilot-instructions.md
50 changes: 50 additions & 0 deletions CHANGELOG/feat_key_rotation_policy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
## [Unreleased]

### 🚀 Features

#### Key auto-rotation (scheduled / policy-driven)

- **KMIP link chain**: `ReKey` now creates a fresh key UUID and links old and new keys via `ReplacementObjectLink` / `ReplacedObjectLink` attributes, enabling full rotation lineage tracking ([#859](https://github.com/Cosmian/kms/issues/859)).
- **Wrapping-key rotation**: when a wrapping key is rotated, all objects it protects are automatically re-wrapped with the new key and their `WrappingKeyLink` attribute is updated.
- **Wrapped-key rotation**: symmetric keys that are themselves wrapped are transparently unwrapped, re-keyed, then re-wrapped with the same wrapping key during rotation.
- **Database query `find_due_for_rotation`**: all DB backends (SQLite, PostgreSQL, MySQL, Redis-Findex) can now query for keys whose `RotateInterval` has elapsed since the last `RotateDate`.
- **Auto-rotation background task**: a new `run_auto_rotation()` operation scans all owned keys due for rotation and rotates them automatically; each rotation is counted in the `kms.key.auto_rotation` OTel metric.
- **Cron wiring**: the background cron thread now schedules auto-rotation checks at a configurable interval (default: disabled, set `--auto-rotation-check-interval-secs` > 0 to enable).
- **CLI `set-rotation-policy`**: new `sym keys set-rotation-policy` sub-command to configure `--interval`, `--name`, and `--offset` rotation attributes on any symmetric key.
- **Web UI — Re-Key**: new `sym/keys/re-key` page allowing users to re-key a symmetric key from the browser; backed by new `rekey_ttlv_request` / `parse_rekey_ttlv_response` WASM bindings.
- **Web UI — Set Rotation Policy**: rotation policy configuration is now available for all key types (Symmetric, RSA, EC, PQC, Covercrypt) via a shared `SetRotationPolicy` component mounted at `<type>/keys/set-rotation-policy`.
- **Server flag `--auto-rotation-check-interval-secs`**: configures (or disables) the server-side background rotation check interval.

### 🐛 Bug Fixes

#### Key rotation correctness

- **`rekey`: set `RotateLatest=true` on new key, `RotateLatest=false` on old key**: the flag was previously copied verbatim from the old key to the new one, making it impossible to locate the most recently rotated key via `Locate` with `rotate_latest=true` (KMIP §4.51). Now exactly one key in a lineage carries `rotate_latest=true` at any point in time.

- **`rekey`: clear stale `link` from new-key attrs before `create_symmetric_key_and_tags`**: links from the source key were embedded in the new key's block and shadowed the correct links stored in the metadata column, causing chained-rekey assertions to fail.
- **`rekey`: clear `key_format_type` from new-key attrs**: the `Raw` presentation format passed to the key generator caused "unable to generate a symmetric key for format: Raw" when rekeying keys with `GetAttributes`-normalised format type.
- **`rekey`: commit new wrapping key before re-wrapping dependants**: the new wrapping key is now persisted in a Phase-1 atomic commit before any wrapped-key re-wrapping occurs, fixing "wrapping key not found" errors during wrapping-key rotation.

### 🧪 Testing

- **Server**: 17 unit tests covering basic rekey, KMIP link chain, rotation metadata propagation, policy preservation, wrapped/wrapping-key rotation, chained rotations, unknown-uid errors, and all `run_auto_rotation` edge cases.
- **CLI (ckms + cosmian_kms_cli)**: updated `test_rekey_symmetric_key` to assert `id != id_2`; added 4 `set-rotation-policy` CLI tests validating `--interval`, `--name`, `--offset`, and disable-by-zero semantics.

### 🔨 Build / Refactor

#### Internalize `cosmian_logger`

- **`crate/logger`**: `cosmian_logger` is now a first-class workspace member at `crate/logger/` instead of being pulled from crates.io. The source is identical to `cosmian_logger 0.5.4` with the following adaptations for the workspace:
- Removed `std::env::set_var` calls (unsafe in edition 2024) — the `rust_log` string from `TracingConfig` is now fed directly to `EnvFilter::try_new()`, removing the need to mutate the process environment.
- Fixed all clippy lints enforced by the workspace (`struct_excessive_bools`, `items_after_statements`, `manual_inspect`, `unnecessary_debug_formatting`, `str_to_string`, `let_underscore_drop`).
- `opentelemetry 0.29.x` packages are pinned directly in the crate manifest (workspace uses 0.27 for the server metrics layer; both versions coexist in the dependency tree).

### 📚 Documentation

#### Key auto-rotation policy

- Added [`documentation/docs/kmip_support/key_auto_rotation.md`](../documentation/docs/kmip_support/key_auto_rotation.md): comprehensive reference for the scheduled key rotation feature covering all key types (plain, wrapping, wrapped, asymmetric), lifecycle diagrams (Mermaid stateDiagram + sequenceDiagram + flowchart), KMIP attribute table, and configuration examples.
- Added entry in `documentation/mkdocs.yml` under *KMIP Support → Key Auto-Rotation Policy*.
- Updated `README.md` *Why Cosmian KMS* section with a one-line summary and link.

Closes #859
6 changes: 3 additions & 3 deletions Cargo.lock

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

34 changes: 23 additions & 11 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ default-members = [
members = [
# Common crates
"crate/kmip",
"crate/logger",
# Client crates
"crate/client_utils",
"crate/kms_client",
Expand Down Expand Up @@ -110,18 +111,18 @@ categories = ["security"]
[profile.release]
# Deterministic build configuration for reproducible binaries
# These settings ensure the same source produces identical binaries across builds
lto = "fat" # Fat LTO: maximum cross-crate optimization for smallest binaries
strip = "symbols" # Strip symbol tables for smaller binaries
opt-level = "z" # Optimize for size while maintaining performance
codegen-units = 1 # Single codegen unit: best optimization and determinism
panic = "abort" # Smaller binaries, no unwinding tables
lto = "fat" # Fat LTO: maximum cross-crate optimization for smallest binaries
strip = "symbols" # Strip symbol tables for smaller binaries
opt-level = "z" # Optimize for size while maintaining performance
codegen-units = 1 # Single codegen unit: best optimization and determinism
panic = "abort" # Smaller binaries, no unwinding tables
incremental = false # Disable incremental compilation for determinism
debug = 0 # No debug info (timestamps/paths)
debug = 0 # No debug info (timestamps/paths)

[profile.dev]
strip = "debuginfo"
incremental = false
panic = "abort" # prevent UB from unwinding panics across extern "C" boundary
panic = "abort" # prevent UB from unwinding panics across extern "C" boundary

# proc-macros and build-scripts
[profile.dev.build-override]
Expand All @@ -148,12 +149,23 @@ base64 = "0.22"
bitflags = "2.9"
chrono = "0.4"
clap = { version = "4.5", default-features = false }
criterion = { version = "0.6", default-features = false, features = ["html_reports", "async_tokio"] }
criterion = { version = "0.6", default-features = false, features = [
"html_reports",
"async_tokio",
] }
cosmian_config_utils = "0.3.1"
cosmian_crypto_core = { version = "11.0", default-features = false, features = ["ser"] }
cosmian_logger = "0.5"
cosmian_crypto_core = { version = "11.0", default-features = false, features = [
"ser",
] }
cosmian_logger = { path = "crate/logger", version = "5.20.0" }
der = { version = "0.7", default-features = false }
dialoguer = { version = "0.11", default-features = false, features = ["editor", "fuzzy-select", "history", "password", "completion"] }
dialoguer = { version = "0.11", default-features = false, features = [
"editor",
"fuzzy-select",
"history",
"password",
"completion",
] }
dotenvy = "0.15"
futures = "0.3"
hex = { version = "0.4", default-features = false }
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ See the [documentation](https://docs.cosmian.com/key_management_system/) for mor
- HSM-first: optional HSM key-wrapping and vendor modules (Utimaco, SmartCard-HSM, Proteccio, Crypt2pay…).
- Cloud-native: official Docker image, simple horizontal scaling, and OpenTelemetry observability.
- End-to-end: server, CLI, and web UI for a complete developer and operator experience.
- **Key auto-rotation**: policy-driven background rotation for plain, wrapped, and wrapping keys — see [Key Auto-Rotation Policy](./documentation/docs/kmip_support/key_auto_rotation.md).

## 🎯 Top Use Cases

Expand Down
45 changes: 33 additions & 12 deletions cli_documentation/docs/cli/main_commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -3497,17 +3497,19 @@ Create, destroy, import, and export symmetric keys

**`re-key`** [[22.1.2]](#2212-ckms-sym-keys-re-key) Refresh an existing symmetric key

**`export`** [[22.1.3]](#2213-ckms-sym-keys-export) Export a key or secret data from the KMS
**`set-rotation-policy`** [[22.1.3]](#2213-ckms-sym-keys-set-rotation-policy) Set the rotation policy for a symmetric key.

**`import`** [[22.1.4]](#2214-ckms-sym-keys-import) Import a secret data or a key in the KMS.
**`export`** [[22.1.4]](#2214-ckms-sym-keys-export) Export a key or secret data from the KMS

**`wrap`** [[22.1.5]](#2215-ckms-sym-keys-wrap) Locally wrap a secret data or key in KMIP JSON TTLV format.
**`import`** [[22.1.5]](#2215-ckms-sym-keys-import) Import a secret data or a key in the KMS.

**`unwrap`** [[22.1.6]](#2216-ckms-sym-keys-unwrap) Locally unwrap a secret data or key in KMIP JSON TTLV format.
**`wrap`** [[22.1.6]](#2216-ckms-sym-keys-wrap) Locally wrap a secret data or key in KMIP JSON TTLV format.

**`revoke`** [[22.1.7]](#2217-ckms-sym-keys-revoke) Revoke a symmetric key
**`unwrap`** [[22.1.7]](#2217-ckms-sym-keys-unwrap) Locally unwrap a secret data or key in KMIP JSON TTLV format.

**`destroy`** [[22.1.8]](#2218-ckms-sym-keys-destroy) Destroy a symmetric key
**`revoke`** [[22.1.8]](#2218-ckms-sym-keys-revoke) Revoke a symmetric key

**`destroy`** [[22.1.9]](#2219-ckms-sym-keys-destroy) Destroy a symmetric key

---

Expand Down Expand Up @@ -3559,7 +3561,26 @@ Refresh an existing symmetric key

---

## 22.1.3 ckms sym keys export
## 22.1.3 ckms sym keys set-rotation-policy

Set the rotation policy for a symmetric key.

### Usage
`ckms sym keys set-rotation-policy [options]`
### Arguments
`--key-id [-k] <KEY_ID>` The unique identifier of the key to configure

`--interval [-i] <INTERVAL>` Rotation interval in seconds. Set to 0 to disable auto-rotation. Example: 86400 for daily rotation, 604800 for weekly

`--name [-n] <NAME>` The name used to track the rotation lineage (optional)

`--offset <OFFSET>` Time offset in seconds from the creation date before the first rotation is triggered (optional). Defaults to the interval if not set



---

## 22.1.4 ckms sym keys export

Export a key or secret data from the KMS

Expand Down Expand Up @@ -3618,7 +3639,7 @@ Possible values: `"aes-key-wrap-padding", "nist-key-wrap", "aes-gcm", "rsa-pkcs

---

## 22.1.4 ckms sym keys import
## 22.1.5 ckms sym keys import

Import a secret data or a key in the KMS.

Expand Down Expand Up @@ -3666,7 +3687,7 @@ If the wrapping key is:

---

## 22.1.5 ckms sym keys wrap
## 22.1.6 ckms sym keys wrap

Locally wrap a secret data or key in KMIP JSON TTLV format.

Expand All @@ -3691,7 +3712,7 @@ Locally wrap a secret data or key in KMIP JSON TTLV format.

---

## 22.1.6 ckms sym keys unwrap
## 22.1.7 ckms sym keys unwrap

Locally unwrap a secret data or key in KMIP JSON TTLV format.

Expand All @@ -3714,7 +3735,7 @@ Locally unwrap a secret data or key in KMIP JSON TTLV format.

---

## 22.1.7 ckms sym keys revoke
## 22.1.8 ckms sym keys revoke

Revoke a symmetric key

Expand All @@ -3732,7 +3753,7 @@ Revoke a symmetric key

---

## 22.1.8 ckms sym keys destroy
## 22.1.9 ckms sym keys destroy

Destroy a symmetric key

Expand Down
7 changes: 6 additions & 1 deletion crate/cli/src/actions/kms/symmetric/keys/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use cosmian_kms_client::KmsClient;

use self::{
create_key::CreateKeyAction, destroy_key::DestroyKeyAction, rekey::ReKeyAction,
revoke_key::RevokeKeyAction,
revoke_key::RevokeKeyAction, set_rotation_policy::SetRotationPolicyAction,
};
use crate::{
actions::kms::shared::{
Expand All @@ -17,12 +17,14 @@ pub mod create_key;
pub mod destroy_key;
pub mod rekey;
pub mod revoke_key;
pub mod set_rotation_policy;

/// Create, destroy, import, and export symmetric keys
#[derive(Subcommand)]
pub enum KeysCommands {
Create(CreateKeyAction),
ReKey(ReKeyAction),
SetRotationPolicy(SetRotationPolicyAction),
Export(ExportSecretDataOrKeyAction),
Import(ImportSecretDataOrKeyAction),
Wrap(WrapSecretDataOrKeyAction),
Expand All @@ -40,6 +42,9 @@ impl KeysCommands {
Self::ReKey(action) => {
action.run(kms_rest_client).await?;
}
Self::SetRotationPolicy(action) => {
action.run(kms_rest_client).await?;
}
Self::Export(action) => {
action.run(kms_rest_client).await?;
}
Expand Down
Loading
Loading