From dd965960d57fa0fd34dfe75218dcf104b2c1ee37 Mon Sep 17 00:00:00 2001 From: Manuthor Date: Thu, 11 Jun 2026 20:37:46 +0200 Subject: [PATCH 01/17] feat: add split keys tests --- configs/client/pqc_tls_owner.toml | 3 + configs/server/privileged_users.toml | 6 +- configs/server/rbac/crypto_officer_only.toml | 52 +++++++++ configs/server/rbac/full_rbac.toml | 58 ++++++++++ configs/server/rbac/operator_only.toml | 45 ++++++++ .../manifest.toml | 34 ++++++ .../step1_create_key.json | 30 +++++ .../step2_locate.json | 9 ++ .../step3_get_attributes.json | 10 ++ .../step4_destroy.json | 10 ++ .../manifest.toml | 25 +++++ .../step1_operator_create_blocked.json | 30 +++++ ...step2_operator_create_keypair_blocked.json | 20 ++++ .../cleanup_destroy_key_id.json | 10 ++ .../cleanup_destroy_reconstructed_id.json | 10 ++ .../cleanup_destroy_share1_id.json | 10 ++ .../cleanup_destroy_share2_id.json | 10 ++ .../cleanup_destroy_share3_id.json | 10 ++ .../cleanup_revoke_key_id.json | 20 ++++ .../cleanup_revoke_reconstructed_id.json | 20 ++++ .../cleanup_revoke_share1_id.json | 20 ++++ .../cleanup_revoke_share2_id.json | 20 ++++ .../cleanup_revoke_share3_id.json | 20 ++++ .../create_split_key_sss/manifest.toml | 106 ++++++++++++++++++ .../step1_create_key.json | 35 ++++++ .../step2_create_split_key.json | 25 +++++ .../step3_join_split_key.json | 25 +++++ .../step4_get_reconstructed.json | 10 ++ .../cleanup_destroy_key_id.json | 10 ++ .../cleanup_destroy_reconstructed_id.json | 10 ++ .../cleanup_destroy_share1_id.json | 10 ++ .../cleanup_destroy_share2_id.json | 10 ++ .../cleanup_revoke_key_id.json | 20 ++++ .../cleanup_revoke_reconstructed_id.json | 20 ++++ .../cleanup_revoke_share1_id.json | 20 ++++ .../cleanup_revoke_share2_id.json | 20 ++++ .../create_split_key_xor/manifest.toml | 95 ++++++++++++++++ .../step1_create_key.json | 35 ++++++ .../step2_create_split_key.json | 25 +++++ .../step3_join_split_key.json | 25 +++++ .../step4_get_reconstructed.json | 10 ++ .../cleanup_destroy_key.json | 10 ++ .../manifest.toml | 28 +++++ .../step1_create_key.json | 35 ++++++ .../step2_create_split_key_bad_parts.json | 25 +++++ .../cleanup_destroy_key.json | 10 ++ .../manifest.toml | 28 +++++ .../step1_create_key.json | 35 ++++++ .../step2_create_split_key_bad_threshold.json | 25 +++++ 49 files changed, 1186 insertions(+), 3 deletions(-) create mode 100644 configs/server/rbac/crypto_officer_only.toml create mode 100644 configs/server/rbac/full_rbac.toml create mode 100644 configs/server/rbac/operator_only.toml create mode 100644 vectors/access_control/crypto_officer_role_allowed_ops/manifest.toml create mode 100644 vectors/access_control/crypto_officer_role_allowed_ops/step1_create_key.json create mode 100644 vectors/access_control/crypto_officer_role_allowed_ops/step2_locate.json create mode 100644 vectors/access_control/crypto_officer_role_allowed_ops/step3_get_attributes.json create mode 100644 vectors/access_control/crypto_officer_role_allowed_ops/step4_destroy.json create mode 100644 vectors/access_control/operator_role_blocked_lifecycle/manifest.toml create mode 100644 vectors/access_control/operator_role_blocked_lifecycle/step1_operator_create_blocked.json create mode 100644 vectors/access_control/operator_role_blocked_lifecycle/step2_operator_create_keypair_blocked.json create mode 100644 vectors/fips/kmip_operations/create_split_key_sss/cleanup_destroy_key_id.json create mode 100644 vectors/fips/kmip_operations/create_split_key_sss/cleanup_destroy_reconstructed_id.json create mode 100644 vectors/fips/kmip_operations/create_split_key_sss/cleanup_destroy_share1_id.json create mode 100644 vectors/fips/kmip_operations/create_split_key_sss/cleanup_destroy_share2_id.json create mode 100644 vectors/fips/kmip_operations/create_split_key_sss/cleanup_destroy_share3_id.json create mode 100644 vectors/fips/kmip_operations/create_split_key_sss/cleanup_revoke_key_id.json create mode 100644 vectors/fips/kmip_operations/create_split_key_sss/cleanup_revoke_reconstructed_id.json create mode 100644 vectors/fips/kmip_operations/create_split_key_sss/cleanup_revoke_share1_id.json create mode 100644 vectors/fips/kmip_operations/create_split_key_sss/cleanup_revoke_share2_id.json create mode 100644 vectors/fips/kmip_operations/create_split_key_sss/cleanup_revoke_share3_id.json create mode 100644 vectors/fips/kmip_operations/create_split_key_sss/manifest.toml create mode 100644 vectors/fips/kmip_operations/create_split_key_sss/step1_create_key.json create mode 100644 vectors/fips/kmip_operations/create_split_key_sss/step2_create_split_key.json create mode 100644 vectors/fips/kmip_operations/create_split_key_sss/step3_join_split_key.json create mode 100644 vectors/fips/kmip_operations/create_split_key_sss/step4_get_reconstructed.json create mode 100644 vectors/fips/kmip_operations/create_split_key_xor/cleanup_destroy_key_id.json create mode 100644 vectors/fips/kmip_operations/create_split_key_xor/cleanup_destroy_reconstructed_id.json create mode 100644 vectors/fips/kmip_operations/create_split_key_xor/cleanup_destroy_share1_id.json create mode 100644 vectors/fips/kmip_operations/create_split_key_xor/cleanup_destroy_share2_id.json create mode 100644 vectors/fips/kmip_operations/create_split_key_xor/cleanup_revoke_key_id.json create mode 100644 vectors/fips/kmip_operations/create_split_key_xor/cleanup_revoke_reconstructed_id.json create mode 100644 vectors/fips/kmip_operations/create_split_key_xor/cleanup_revoke_share1_id.json create mode 100644 vectors/fips/kmip_operations/create_split_key_xor/cleanup_revoke_share2_id.json create mode 100644 vectors/fips/kmip_operations/create_split_key_xor/manifest.toml create mode 100644 vectors/fips/kmip_operations/create_split_key_xor/step1_create_key.json create mode 100644 vectors/fips/kmip_operations/create_split_key_xor/step2_create_split_key.json create mode 100644 vectors/fips/kmip_operations/create_split_key_xor/step3_join_split_key.json create mode 100644 vectors/fips/kmip_operations/create_split_key_xor/step4_get_reconstructed.json create mode 100644 vectors/negative/create_split_key_parts_less_than_threshold/cleanup_destroy_key.json create mode 100644 vectors/negative/create_split_key_parts_less_than_threshold/manifest.toml create mode 100644 vectors/negative/create_split_key_parts_less_than_threshold/step1_create_key.json create mode 100644 vectors/negative/create_split_key_parts_less_than_threshold/step2_create_split_key_bad_parts.json create mode 100644 vectors/negative/create_split_key_threshold_too_low/cleanup_destroy_key.json create mode 100644 vectors/negative/create_split_key_threshold_too_low/manifest.toml create mode 100644 vectors/negative/create_split_key_threshold_too_low/step1_create_key.json create mode 100644 vectors/negative/create_split_key_threshold_too_low/step2_create_split_key_bad_threshold.json diff --git a/configs/client/pqc_tls_owner.toml b/configs/client/pqc_tls_owner.toml index 9b1a622..d3a1803 100644 --- a/configs/client/pqc_tls_owner.toml +++ b/configs/client/pqc_tls_owner.toml @@ -1,3 +1,6 @@ +# Test KMS client — HTTPS with a PQC (ML-DSA-44) TLS server certificate. +# Port is patched at runtime by load_client_config(). +# accept_invalid_certs is required because the server cert is self-signed. vendor_id = "cosmian" [http_config] diff --git a/configs/server/privileged_users.toml b/configs/server/privileged_users.toml index 0ed5015..fc327bf 100644 --- a/configs/server/privileged_users.toml +++ b/configs/server/privileged_users.toml @@ -1,6 +1,6 @@ # Test KMS server — TLS + JWT auth, for privileged-user tests. # Uses PEM cert + key (works in both FIPS and non-FIPS mode). -# Bound to port 10003. privileged_users is injected at runtime. +# Bound to port 10003. crypto_officer_users is injected at runtime. default_username = "tech@cosmian.com" kms_public_url = "https://localhost:10003/google_cse" @@ -11,11 +11,11 @@ hostname = "0.0.0.0" [db] database_type = "sqlite" -sqlite_path = "/tmp/kms_test_privileged_users" +sqlite_path = "/tmp/kms_test_crypto_officer_users" clear_database = true [workspace] -root_data_path = "/tmp/kms_test_workspace_privileged_users" +root_data_path = "/tmp/kms_test_workspace_crypto_officer_users" tmp_path = "/tmp" [tls] diff --git a/configs/server/rbac/crypto_officer_only.toml b/configs/server/rbac/crypto_officer_only.toml new file mode 100644 index 0000000..e51fe47 --- /dev/null +++ b/configs/server/rbac/crypto_officer_only.toml @@ -0,0 +1,52 @@ +# ── CryptoOfficer-only RBAC ────────────────────────────────────────────────── +# +# Role model: CryptoOfficer + default Operator +# Listed users hold the CryptoOfficer role. Unlisted authenticated users +# default to Operator (minimum privilege — crypto use only). +# +# CryptoOfficer permissions: +# • Key lifecycle: Create, Import, Register, Destroy, Revoke, Activate +# • Key pair management: CreateKeyPair, ReKeyKeyPair, Certify +# • Split-key management: CreateSplitKey, JoinSplitKey +# • Delegation: GrantAccess, RevokeAccess +# +# Use-case: key-management engineers who provision and rotate keys. Typical +# deployment separates CryptoOfficers from application users so that +# compromise of a service account cannot trigger key destruction. +# ───────────────────────────────────────────────────────────────────────────── + +default_username = "admin" + +[roles] +# Users with CryptoOfficer role. +# Authenticated users not listed here get default permissions (crypto use only). +# Certs: test_data/certificates/client_server/crypto-officer-1/crypto-officer-1.example.com.{key,crt} +# test_data/certificates/client_server/crypto-officer-2/crypto-officer-2.example.com.{key,crt} +crypto_officer_users = [ + "crypto-officer-1@example.com", + "crypto-officer-2@example.com", +] + +[http] +port = 9998 +hostname = "0.0.0.0" + +[db] +database_type = "sqlite" +sqlite_path = "/tmp/kms-rbac-crypto-officer" +clear_database = false + +[workspace] +root_data_path = "/tmp/kms-rbac-crypto-officer-workspace" +tmp_path = "/tmp" + +[tls] +tls_cert_file = "test_data/certificates/client_server/server/kmserver.acme.com.crt" +tls_key_file = "test_data/certificates/client_server/server/kmserver.acme.com.key" +clients_ca_cert_file = "test_data/certificates/client_server/ca/ca.crt" + +[logging] +rust_log = "info,cosmian_kms=info" + +[ui_config] +ui_index_html_folder = "ui/dist" diff --git a/configs/server/rbac/full_rbac.toml b/configs/server/rbac/full_rbac.toml new file mode 100644 index 0000000..e1e6a17 --- /dev/null +++ b/configs/server/rbac/full_rbac.toml @@ -0,0 +1,58 @@ +# ── Full RBAC — production reference configuration ────────────────────────── +# +# Role model: CryptoOfficer + Operator +# FIPS 140-3 §7.4 / NIST SP 800-57 Part 2 Rev 1 §4.3–§4.4 +# +# Role hierarchy and permissions: +# +# ┌──────────────────┬────────────────────────────────────────────────────┐ +# │ Role │ Permitted operations │ +# ├──────────────────┼────────────────────────────────────────────────────┤ +# │ Operator │ Encrypt, Decrypt, Sign, Verify, MAC, Hash, │ +# │ (default) │ GetAttributes, Locate, Validate │ +# ├──────────────────┼────────────────────────────────────────────────────┤ +# │ CryptoOfficer │ Create, Import, Destroy, Revoke, Activate, │ +# │ │ Get, Export, CreateKeyPair, CreateSplitKey, │ +# │ │ JoinSplitKey, Certify, GrantAccess, RevokeAccess; │ +# │ │ bypass per-object ownership check │ +# └──────────────────┴────────────────────────────────────────────────────┘ +# +# Unenrolled users default to Operator (fail-secure minimum privilege). +# There is no `operator_users` config key — it is the implicit default. +# ───────────────────────────────────────────────────────────────────────────── + +default_username = "admin" + +[roles] +# ── CryptoOfficer role ──────────────────────────────────────────────────────── +# Key-management engineers: create, rotate, revoke keys. Ownership bypass on all objects. +# Certs: test_data/certificates/client_server/crypto-officer-1/crypto-officer-1.example.com.{key,crt} +# test_data/certificates/client_server/crypto-officer-2/crypto-officer-2.example.com.{key,crt} +crypto_officer_users = [ + "crypto-officer-1@example.com", + "crypto-officer-2@example.com", +] + +[http] +port = 9998 +hostname = "0.0.0.0" + +[db] +database_type = "sqlite" +sqlite_path = "/tmp/kms-rbac-full" +clear_database = false + +[workspace] +root_data_path = "/tmp/kms-rbac-full-workspace" +tmp_path = "/tmp" + +[tls] +tls_cert_file = "test_data/certificates/client_server/server/kmserver.acme.com.crt" +tls_key_file = "test_data/certificates/client_server/server/kmserver.acme.com.key" +clients_ca_cert_file = "test_data/certificates/client_server/ca/ca.crt" + +[logging] +rust_log = "info,cosmian_kms=info" + +[ui_config] +ui_index_html_folder = "ui/dist" diff --git a/configs/server/rbac/operator_only.toml b/configs/server/rbac/operator_only.toml new file mode 100644 index 0000000..e14cf01 --- /dev/null +++ b/configs/server/rbac/operator_only.toml @@ -0,0 +1,45 @@ +# ── Minimal RBAC — all users default ───────────────────────────────────────── +# +# When any role list is configured, users not listed in any role receive the +# default permissions (crypto use only, no lifecycle operations). +# +# Default (Operator) permissions: +# • Use key material: Encrypt, Decrypt, Sign, SignatureVerify, MAC, Hash +# • Read metadata: Get, GetAttributes, Locate, Validate, Export (own objects) +# • BLOCKED: Create, Import, Register, Destroy, Revoke, Activate, +# CreateKeyPair, CreateSplitKey, JoinSplitKey, ReKey, etc. +# +# This config sets an empty crypto_officer_users list to activate role enforcement +# while leaving all users at the default level (no one can do lifecycle ops unless +# explicitly granted Create access). +# ───────────────────────────────────────────────────────────────────────────── + +default_username = "admin" + +[roles] +# Empty list: triggers role enforcement. All users get default permissions. +crypto_officer_users = [] + +[http] +port = 9998 +hostname = "0.0.0.0" + +[db] +database_type = "sqlite" +sqlite_path = "/tmp/kms-rbac-operator" +clear_database = false + +[workspace] +root_data_path = "/tmp/kms-rbac-operator-workspace" +tmp_path = "/tmp" + +[tls] +tls_cert_file = "test_data/certificates/client_server/server/kmserver.acme.com.crt" +tls_key_file = "test_data/certificates/client_server/server/kmserver.acme.com.key" +clients_ca_cert_file = "test_data/certificates/client_server/ca/ca.crt" + +[logging] +rust_log = "info,cosmian_kms=info" + +[ui_config] +ui_index_html_folder = "ui/dist" diff --git a/vectors/access_control/crypto_officer_role_allowed_ops/manifest.toml b/vectors/access_control/crypto_officer_role_allowed_ops/manifest.toml new file mode 100644 index 0000000..657df16 --- /dev/null +++ b/vectors/access_control/crypto_officer_role_allowed_ops/manifest.toml @@ -0,0 +1,34 @@ +name = "Access Control: CryptoOfficer allowed lifecycle operations" +description = "CryptoOfficer can perform lifecycle operations: Create, Locate, GetAttributes, Destroy." +server_config = "test_data/configs/server/test/cert_auth_operator_and_crypto_officer.toml" + +[identities.owner] +client_cert = "test_data/certificates/client_server/owner/owner.client.acme.com.crt" +client_key = "test_data/certificates/client_server/owner/owner.client.acme.com.key" + +[identities.user] +client_cert = "test_data/certificates/client_server/user/user.client.acme.com.crt" +client_key = "test_data/certificates/client_server/user/user.client.acme.com.key" + +[[steps]] +operation = "Create" +request = "step1_create_key.json" +assert_success = true + +[steps.capture] +key_id = "UniqueIdentifier" + +[[steps]] +operation = "Locate" +request = "step2_locate.json" +assert_success = true + +[[steps]] +operation = "GetAttributes" +request = "step3_get_attributes.json" +assert_success = true + +[[steps]] +operation = "Destroy" +request = "step4_destroy.json" +assert_success = true diff --git a/vectors/access_control/crypto_officer_role_allowed_ops/step1_create_key.json b/vectors/access_control/crypto_officer_role_allowed_ops/step1_create_key.json new file mode 100644 index 0000000..692c4b5 --- /dev/null +++ b/vectors/access_control/crypto_officer_role_allowed_ops/step1_create_key.json @@ -0,0 +1,30 @@ +{ + "tag": "Create", + "value": [ + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + }, + { + "tag": "Attributes", + "value": [ + { + "tag": "CryptographicAlgorithm", + "type": "Enumeration", + "value": "AES" + }, + { + "tag": "CryptographicLength", + "type": "Integer", + "value": 256 + }, + { + "tag": "CryptographicUsageMask", + "type": "Integer", + "value": 12 + } + ] + } + ] +} diff --git a/vectors/access_control/crypto_officer_role_allowed_ops/step2_locate.json b/vectors/access_control/crypto_officer_role_allowed_ops/step2_locate.json new file mode 100644 index 0000000..f3ceab5 --- /dev/null +++ b/vectors/access_control/crypto_officer_role_allowed_ops/step2_locate.json @@ -0,0 +1,9 @@ +{ + "tag": "Locate", + "value": [ + { + "tag": "Attributes", + "value": [] + } + ] +} diff --git a/vectors/access_control/crypto_officer_role_allowed_ops/step3_get_attributes.json b/vectors/access_control/crypto_officer_role_allowed_ops/step3_get_attributes.json new file mode 100644 index 0000000..e96d057 --- /dev/null +++ b/vectors/access_control/crypto_officer_role_allowed_ops/step3_get_attributes.json @@ -0,0 +1,10 @@ +{ + "tag": "GetAttributes", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{key_id}}" + } + ] +} diff --git a/vectors/access_control/crypto_officer_role_allowed_ops/step4_destroy.json b/vectors/access_control/crypto_officer_role_allowed_ops/step4_destroy.json new file mode 100644 index 0000000..ce2dc0e --- /dev/null +++ b/vectors/access_control/crypto_officer_role_allowed_ops/step4_destroy.json @@ -0,0 +1,10 @@ +{ + "tag": "Destroy", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{key_id}}" + } + ] +} diff --git a/vectors/access_control/operator_role_blocked_lifecycle/manifest.toml b/vectors/access_control/operator_role_blocked_lifecycle/manifest.toml new file mode 100644 index 0000000..0f9998b --- /dev/null +++ b/vectors/access_control/operator_role_blocked_lifecycle/manifest.toml @@ -0,0 +1,25 @@ +name = "Access Control: Operator blocked from lifecycle operations" +description = "Operator role cannot perform lifecycle operations (Create, CreateKeyPair) without explicit Create grant." +server_config = "test_data/configs/server/test/cert_auth_operator_and_crypto_officer.toml" + +[identities.owner] +client_cert = "test_data/certificates/client_server/owner/owner.client.acme.com.crt" +client_key = "test_data/certificates/client_server/owner/owner.client.acme.com.key" + +[identities.user] +client_cert = "test_data/certificates/client_server/user/user.client.acme.com.crt" +client_key = "test_data/certificates/client_server/user/user.client.acme.com.key" + +[[steps]] +operation = "Create" +request = "step1_operator_create_blocked.json" +identity = "user" +assert_success = false +assert_error_reason = "Permission_Denied" + +[[steps]] +operation = "CreateKeyPair" +request = "step2_operator_create_keypair_blocked.json" +identity = "user" +assert_success = false +assert_error_reason = "Permission_Denied" diff --git a/vectors/access_control/operator_role_blocked_lifecycle/step1_operator_create_blocked.json b/vectors/access_control/operator_role_blocked_lifecycle/step1_operator_create_blocked.json new file mode 100644 index 0000000..692c4b5 --- /dev/null +++ b/vectors/access_control/operator_role_blocked_lifecycle/step1_operator_create_blocked.json @@ -0,0 +1,30 @@ +{ + "tag": "Create", + "value": [ + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + }, + { + "tag": "Attributes", + "value": [ + { + "tag": "CryptographicAlgorithm", + "type": "Enumeration", + "value": "AES" + }, + { + "tag": "CryptographicLength", + "type": "Integer", + "value": 256 + }, + { + "tag": "CryptographicUsageMask", + "type": "Integer", + "value": 12 + } + ] + } + ] +} diff --git a/vectors/access_control/operator_role_blocked_lifecycle/step2_operator_create_keypair_blocked.json b/vectors/access_control/operator_role_blocked_lifecycle/step2_operator_create_keypair_blocked.json new file mode 100644 index 0000000..5a8ba68 --- /dev/null +++ b/vectors/access_control/operator_role_blocked_lifecycle/step2_operator_create_keypair_blocked.json @@ -0,0 +1,20 @@ +{ + "tag": "CreateKeyPair", + "value": [ + { + "tag": "CommonAttributes", + "value": [ + { + "tag": "CryptographicAlgorithm", + "type": "Enumeration", + "value": "RSA" + }, + { + "tag": "CryptographicLength", + "type": "Integer", + "value": 2048 + } + ] + } + ] +} diff --git a/vectors/fips/kmip_operations/create_split_key_sss/cleanup_destroy_key_id.json b/vectors/fips/kmip_operations/create_split_key_sss/cleanup_destroy_key_id.json new file mode 100644 index 0000000..ce2dc0e --- /dev/null +++ b/vectors/fips/kmip_operations/create_split_key_sss/cleanup_destroy_key_id.json @@ -0,0 +1,10 @@ +{ + "tag": "Destroy", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{key_id}}" + } + ] +} diff --git a/vectors/fips/kmip_operations/create_split_key_sss/cleanup_destroy_reconstructed_id.json b/vectors/fips/kmip_operations/create_split_key_sss/cleanup_destroy_reconstructed_id.json new file mode 100644 index 0000000..13817a6 --- /dev/null +++ b/vectors/fips/kmip_operations/create_split_key_sss/cleanup_destroy_reconstructed_id.json @@ -0,0 +1,10 @@ +{ + "tag": "Destroy", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{reconstructed_id}}" + } + ] +} diff --git a/vectors/fips/kmip_operations/create_split_key_sss/cleanup_destroy_share1_id.json b/vectors/fips/kmip_operations/create_split_key_sss/cleanup_destroy_share1_id.json new file mode 100644 index 0000000..87a3361 --- /dev/null +++ b/vectors/fips/kmip_operations/create_split_key_sss/cleanup_destroy_share1_id.json @@ -0,0 +1,10 @@ +{ + "tag": "Destroy", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{share1_id}}" + } + ] +} diff --git a/vectors/fips/kmip_operations/create_split_key_sss/cleanup_destroy_share2_id.json b/vectors/fips/kmip_operations/create_split_key_sss/cleanup_destroy_share2_id.json new file mode 100644 index 0000000..b2700d0 --- /dev/null +++ b/vectors/fips/kmip_operations/create_split_key_sss/cleanup_destroy_share2_id.json @@ -0,0 +1,10 @@ +{ + "tag": "Destroy", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{share2_id}}" + } + ] +} diff --git a/vectors/fips/kmip_operations/create_split_key_sss/cleanup_destroy_share3_id.json b/vectors/fips/kmip_operations/create_split_key_sss/cleanup_destroy_share3_id.json new file mode 100644 index 0000000..51ff86b --- /dev/null +++ b/vectors/fips/kmip_operations/create_split_key_sss/cleanup_destroy_share3_id.json @@ -0,0 +1,10 @@ +{ + "tag": "Destroy", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{share3_id}}" + } + ] +} diff --git a/vectors/fips/kmip_operations/create_split_key_sss/cleanup_revoke_key_id.json b/vectors/fips/kmip_operations/create_split_key_sss/cleanup_revoke_key_id.json new file mode 100644 index 0000000..2cffb71 --- /dev/null +++ b/vectors/fips/kmip_operations/create_split_key_sss/cleanup_revoke_key_id.json @@ -0,0 +1,20 @@ +{ + "tag": "Revoke", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{key_id}}" + }, + { + "tag": "RevocationReason", + "value": [ + { + "tag": "RevocationReasonCode", + "type": "Enumeration", + "value": "CessationOfOperation" + } + ] + } + ] +} diff --git a/vectors/fips/kmip_operations/create_split_key_sss/cleanup_revoke_reconstructed_id.json b/vectors/fips/kmip_operations/create_split_key_sss/cleanup_revoke_reconstructed_id.json new file mode 100644 index 0000000..e10ed77 --- /dev/null +++ b/vectors/fips/kmip_operations/create_split_key_sss/cleanup_revoke_reconstructed_id.json @@ -0,0 +1,20 @@ +{ + "tag": "Revoke", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{reconstructed_id}}" + }, + { + "tag": "RevocationReason", + "value": [ + { + "tag": "RevocationReasonCode", + "type": "Enumeration", + "value": "CessationOfOperation" + } + ] + } + ] +} diff --git a/vectors/fips/kmip_operations/create_split_key_sss/cleanup_revoke_share1_id.json b/vectors/fips/kmip_operations/create_split_key_sss/cleanup_revoke_share1_id.json new file mode 100644 index 0000000..083f706 --- /dev/null +++ b/vectors/fips/kmip_operations/create_split_key_sss/cleanup_revoke_share1_id.json @@ -0,0 +1,20 @@ +{ + "tag": "Revoke", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{share1_id}}" + }, + { + "tag": "RevocationReason", + "value": [ + { + "tag": "RevocationReasonCode", + "type": "Enumeration", + "value": "CessationOfOperation" + } + ] + } + ] +} diff --git a/vectors/fips/kmip_operations/create_split_key_sss/cleanup_revoke_share2_id.json b/vectors/fips/kmip_operations/create_split_key_sss/cleanup_revoke_share2_id.json new file mode 100644 index 0000000..4560b60 --- /dev/null +++ b/vectors/fips/kmip_operations/create_split_key_sss/cleanup_revoke_share2_id.json @@ -0,0 +1,20 @@ +{ + "tag": "Revoke", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{share2_id}}" + }, + { + "tag": "RevocationReason", + "value": [ + { + "tag": "RevocationReasonCode", + "type": "Enumeration", + "value": "CessationOfOperation" + } + ] + } + ] +} diff --git a/vectors/fips/kmip_operations/create_split_key_sss/cleanup_revoke_share3_id.json b/vectors/fips/kmip_operations/create_split_key_sss/cleanup_revoke_share3_id.json new file mode 100644 index 0000000..385bb4b --- /dev/null +++ b/vectors/fips/kmip_operations/create_split_key_sss/cleanup_revoke_share3_id.json @@ -0,0 +1,20 @@ +{ + "tag": "Revoke", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{share3_id}}" + }, + { + "tag": "RevocationReason", + "value": [ + { + "tag": "RevocationReasonCode", + "type": "Enumeration", + "value": "CessationOfOperation" + } + ] + } + ] +} diff --git a/vectors/fips/kmip_operations/create_split_key_sss/manifest.toml b/vectors/fips/kmip_operations/create_split_key_sss/manifest.toml new file mode 100644 index 0000000..1cace22 --- /dev/null +++ b/vectors/fips/kmip_operations/create_split_key_sss/manifest.toml @@ -0,0 +1,106 @@ +name = "CreateSplitKey + JoinSplitKey: XOR n-of-n round-trip (via PolynomialSharingGf28 method)" +description = """ +Creates an AES-256 symmetric key, splits it into 2 shares using XOR-based split knowledge +(requested via the PolynomialSharingGf28 KMIP method, which the server maps to XOR). +Threshold must equal total_parts. Joins all shares to reconstruct the key and verifies +the reconstructed object is a SymmetricKey. +""" + +# Uses the default SQLite backend (single user, no cert auth required). + +[[steps]] +operation = "Create" +request = "step1_create_key.json" +assert_success = true + +[steps.capture] +key_id = "UniqueIdentifier" + +# ── Split the key into 3 shares (threshold = 2) ────────────────────────────── + +[[steps]] +operation = "CreateSplitKey" +request = "step2_create_split_key.json" +assert_success = true + +[steps.assert_count] +PrivateKeyUniqueIdentifier = 2 + +[steps.capture] +share1_id = "PrivateKeyUniqueIdentifier" + +[steps.capture_nth.share2_id] +tag = "PrivateKeyUniqueIdentifier" +index = 1 + +# ── Join 2 of the 3 shares to reconstruct the key ─────────────────────────── + +[[steps]] +operation = "JoinSplitKey" +request = "step3_join_split_key.json" +assert_success = true + +[steps.capture] +reconstructed_id = "UniqueIdentifier" + +# ── Verify the reconstructed key is retrievable ────────────────────────────── + +[[steps]] +operation = "Get" +request = "step4_get_reconstructed.json" +assert_success = true + +[steps.assert_fields] +ObjectType = "SymmetricKey" + +# ── Cleanup: revoke + destroy all objects (best-effort) ────────────────────── + +[[steps]] +operation = "Revoke" +request = "cleanup_revoke_key_id.json" +allow_failure = true + +[[steps]] +operation = "Destroy" +request = "cleanup_destroy_key_id.json" +allow_failure = true + +[[steps]] +operation = "Revoke" +request = "cleanup_revoke_share1_id.json" +allow_failure = true + +[[steps]] +operation = "Destroy" +request = "cleanup_destroy_share1_id.json" +allow_failure = true + +[[steps]] +operation = "Revoke" +request = "cleanup_revoke_share2_id.json" +allow_failure = true + +[[steps]] +operation = "Destroy" +request = "cleanup_destroy_share2_id.json" +allow_failure = true + +[[steps]] +operation = "Revoke" +request = "cleanup_revoke_share3_id.json" +allow_failure = true + +[[steps]] +operation = "Destroy" +request = "cleanup_destroy_share3_id.json" +allow_failure = true + +[[steps]] +operation = "Revoke" +request = "cleanup_revoke_reconstructed_id.json" +allow_failure = true + +[[steps]] +operation = "Destroy" +request = "cleanup_destroy_reconstructed_id.json" +allow_failure = true diff --git a/vectors/fips/kmip_operations/create_split_key_sss/step1_create_key.json b/vectors/fips/kmip_operations/create_split_key_sss/step1_create_key.json new file mode 100644 index 0000000..1b5500d --- /dev/null +++ b/vectors/fips/kmip_operations/create_split_key_sss/step1_create_key.json @@ -0,0 +1,35 @@ +{ + "tag": "Create", + "value": [ + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + }, + { + "tag": "Attributes", + "value": [ + { + "tag": "CryptographicAlgorithm", + "type": "Enumeration", + "value": "AES" + }, + { + "tag": "CryptographicLength", + "type": "Integer", + "value": 256 + }, + { + "tag": "CryptographicUsageMask", + "type": "Integer", + "value": 12 + }, + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + } + ] + } + ] +} diff --git a/vectors/fips/kmip_operations/create_split_key_sss/step2_create_split_key.json b/vectors/fips/kmip_operations/create_split_key_sss/step2_create_split_key.json new file mode 100644 index 0000000..897b2fe --- /dev/null +++ b/vectors/fips/kmip_operations/create_split_key_sss/step2_create_split_key.json @@ -0,0 +1,25 @@ +{ + "tag": "CreateSplitKey", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{key_id}}" + }, + { + "tag": "SplitKeyParts", + "type": "Integer", + "value": 2 + }, + { + "tag": "SplitKeyThreshold", + "type": "Integer", + "value": 2 + }, + { + "tag": "SplitKeyMethod", + "type": "Enumeration", + "value": "PolynomialSharingGf28" + } + ] +} diff --git a/vectors/fips/kmip_operations/create_split_key_sss/step3_join_split_key.json b/vectors/fips/kmip_operations/create_split_key_sss/step3_join_split_key.json new file mode 100644 index 0000000..9d85f48 --- /dev/null +++ b/vectors/fips/kmip_operations/create_split_key_sss/step3_join_split_key.json @@ -0,0 +1,25 @@ +{ + "tag": "JoinSplitKey", + "value": [ + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + }, + { + "tag": "PrivateKeyUniqueIdentifier", + "type": "TextString", + "value": "{{share1_id}}" + }, + { + "tag": "PrivateKeyUniqueIdentifier", + "type": "TextString", + "value": "{{share2_id}}" + }, + { + "tag": "SplitKeyMethod", + "type": "Enumeration", + "value": "PolynomialSharingGf28" + } + ] +} diff --git a/vectors/fips/kmip_operations/create_split_key_sss/step4_get_reconstructed.json b/vectors/fips/kmip_operations/create_split_key_sss/step4_get_reconstructed.json new file mode 100644 index 0000000..0467262 --- /dev/null +++ b/vectors/fips/kmip_operations/create_split_key_sss/step4_get_reconstructed.json @@ -0,0 +1,10 @@ +{ + "tag": "Get", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{reconstructed_id}}" + } + ] +} diff --git a/vectors/fips/kmip_operations/create_split_key_xor/cleanup_destroy_key_id.json b/vectors/fips/kmip_operations/create_split_key_xor/cleanup_destroy_key_id.json new file mode 100644 index 0000000..ce2dc0e --- /dev/null +++ b/vectors/fips/kmip_operations/create_split_key_xor/cleanup_destroy_key_id.json @@ -0,0 +1,10 @@ +{ + "tag": "Destroy", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{key_id}}" + } + ] +} diff --git a/vectors/fips/kmip_operations/create_split_key_xor/cleanup_destroy_reconstructed_id.json b/vectors/fips/kmip_operations/create_split_key_xor/cleanup_destroy_reconstructed_id.json new file mode 100644 index 0000000..13817a6 --- /dev/null +++ b/vectors/fips/kmip_operations/create_split_key_xor/cleanup_destroy_reconstructed_id.json @@ -0,0 +1,10 @@ +{ + "tag": "Destroy", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{reconstructed_id}}" + } + ] +} diff --git a/vectors/fips/kmip_operations/create_split_key_xor/cleanup_destroy_share1_id.json b/vectors/fips/kmip_operations/create_split_key_xor/cleanup_destroy_share1_id.json new file mode 100644 index 0000000..87a3361 --- /dev/null +++ b/vectors/fips/kmip_operations/create_split_key_xor/cleanup_destroy_share1_id.json @@ -0,0 +1,10 @@ +{ + "tag": "Destroy", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{share1_id}}" + } + ] +} diff --git a/vectors/fips/kmip_operations/create_split_key_xor/cleanup_destroy_share2_id.json b/vectors/fips/kmip_operations/create_split_key_xor/cleanup_destroy_share2_id.json new file mode 100644 index 0000000..b2700d0 --- /dev/null +++ b/vectors/fips/kmip_operations/create_split_key_xor/cleanup_destroy_share2_id.json @@ -0,0 +1,10 @@ +{ + "tag": "Destroy", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{share2_id}}" + } + ] +} diff --git a/vectors/fips/kmip_operations/create_split_key_xor/cleanup_revoke_key_id.json b/vectors/fips/kmip_operations/create_split_key_xor/cleanup_revoke_key_id.json new file mode 100644 index 0000000..2cffb71 --- /dev/null +++ b/vectors/fips/kmip_operations/create_split_key_xor/cleanup_revoke_key_id.json @@ -0,0 +1,20 @@ +{ + "tag": "Revoke", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{key_id}}" + }, + { + "tag": "RevocationReason", + "value": [ + { + "tag": "RevocationReasonCode", + "type": "Enumeration", + "value": "CessationOfOperation" + } + ] + } + ] +} diff --git a/vectors/fips/kmip_operations/create_split_key_xor/cleanup_revoke_reconstructed_id.json b/vectors/fips/kmip_operations/create_split_key_xor/cleanup_revoke_reconstructed_id.json new file mode 100644 index 0000000..e10ed77 --- /dev/null +++ b/vectors/fips/kmip_operations/create_split_key_xor/cleanup_revoke_reconstructed_id.json @@ -0,0 +1,20 @@ +{ + "tag": "Revoke", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{reconstructed_id}}" + }, + { + "tag": "RevocationReason", + "value": [ + { + "tag": "RevocationReasonCode", + "type": "Enumeration", + "value": "CessationOfOperation" + } + ] + } + ] +} diff --git a/vectors/fips/kmip_operations/create_split_key_xor/cleanup_revoke_share1_id.json b/vectors/fips/kmip_operations/create_split_key_xor/cleanup_revoke_share1_id.json new file mode 100644 index 0000000..083f706 --- /dev/null +++ b/vectors/fips/kmip_operations/create_split_key_xor/cleanup_revoke_share1_id.json @@ -0,0 +1,20 @@ +{ + "tag": "Revoke", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{share1_id}}" + }, + { + "tag": "RevocationReason", + "value": [ + { + "tag": "RevocationReasonCode", + "type": "Enumeration", + "value": "CessationOfOperation" + } + ] + } + ] +} diff --git a/vectors/fips/kmip_operations/create_split_key_xor/cleanup_revoke_share2_id.json b/vectors/fips/kmip_operations/create_split_key_xor/cleanup_revoke_share2_id.json new file mode 100644 index 0000000..4560b60 --- /dev/null +++ b/vectors/fips/kmip_operations/create_split_key_xor/cleanup_revoke_share2_id.json @@ -0,0 +1,20 @@ +{ + "tag": "Revoke", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{share2_id}}" + }, + { + "tag": "RevocationReason", + "value": [ + { + "tag": "RevocationReasonCode", + "type": "Enumeration", + "value": "CessationOfOperation" + } + ] + } + ] +} diff --git a/vectors/fips/kmip_operations/create_split_key_xor/manifest.toml b/vectors/fips/kmip_operations/create_split_key_xor/manifest.toml new file mode 100644 index 0000000..53b0c0b --- /dev/null +++ b/vectors/fips/kmip_operations/create_split_key_xor/manifest.toml @@ -0,0 +1,95 @@ +name = "CreateSplitKey + JoinSplitKey: XOR 2-of-2 round-trip" +description = """ +Creates an AES-256 symmetric key, splits it into 2 shares using XOR splitting (both shares +required). Then joins the 2 shares to reconstruct the key and verifies the reconstructed +object is a SymmetricKey. +""" + +# Uses the default SQLite backend (single user, no cert auth required). + +[[steps]] +operation = "Create" +request = "step1_create_key.json" +assert_success = true + +[steps.capture] +key_id = "UniqueIdentifier" + +# ── Split the key into 2 shares using XOR ──────────────────────────────────── + +[[steps]] +operation = "CreateSplitKey" +request = "step2_create_split_key.json" +assert_success = true + +[steps.assert_count] +PrivateKeyUniqueIdentifier = 2 + +[steps.capture] +share1_id = "PrivateKeyUniqueIdentifier" + +[steps.capture_nth.share2_id] +tag = "PrivateKeyUniqueIdentifier" +index = 1 + +# ── Join both shares to reconstruct the key ────────────────────────────────── + +[[steps]] +operation = "JoinSplitKey" +request = "step3_join_split_key.json" +assert_success = true + +[steps.capture] +reconstructed_id = "UniqueIdentifier" + +# ── Verify the reconstructed key is retrievable ────────────────────────────── + +[[steps]] +operation = "Get" +request = "step4_get_reconstructed.json" +assert_success = true + +[steps.assert_fields] +ObjectType = "SymmetricKey" + +# ── Cleanup ────────────────────────────────────────────────────────────────── + +[[steps]] +operation = "Revoke" +request = "cleanup_revoke_key_id.json" +allow_failure = true + +[[steps]] +operation = "Destroy" +request = "cleanup_destroy_key_id.json" +allow_failure = true + +[[steps]] +operation = "Revoke" +request = "cleanup_revoke_share1_id.json" +allow_failure = true + +[[steps]] +operation = "Destroy" +request = "cleanup_destroy_share1_id.json" +allow_failure = true + +[[steps]] +operation = "Revoke" +request = "cleanup_revoke_share2_id.json" +allow_failure = true + +[[steps]] +operation = "Destroy" +request = "cleanup_destroy_share2_id.json" +allow_failure = true + +[[steps]] +operation = "Revoke" +request = "cleanup_revoke_reconstructed_id.json" +allow_failure = true + +[[steps]] +operation = "Destroy" +request = "cleanup_destroy_reconstructed_id.json" +allow_failure = true diff --git a/vectors/fips/kmip_operations/create_split_key_xor/step1_create_key.json b/vectors/fips/kmip_operations/create_split_key_xor/step1_create_key.json new file mode 100644 index 0000000..1b5500d --- /dev/null +++ b/vectors/fips/kmip_operations/create_split_key_xor/step1_create_key.json @@ -0,0 +1,35 @@ +{ + "tag": "Create", + "value": [ + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + }, + { + "tag": "Attributes", + "value": [ + { + "tag": "CryptographicAlgorithm", + "type": "Enumeration", + "value": "AES" + }, + { + "tag": "CryptographicLength", + "type": "Integer", + "value": 256 + }, + { + "tag": "CryptographicUsageMask", + "type": "Integer", + "value": 12 + }, + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + } + ] + } + ] +} diff --git a/vectors/fips/kmip_operations/create_split_key_xor/step2_create_split_key.json b/vectors/fips/kmip_operations/create_split_key_xor/step2_create_split_key.json new file mode 100644 index 0000000..6215a14 --- /dev/null +++ b/vectors/fips/kmip_operations/create_split_key_xor/step2_create_split_key.json @@ -0,0 +1,25 @@ +{ + "tag": "CreateSplitKey", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{key_id}}" + }, + { + "tag": "SplitKeyParts", + "type": "Integer", + "value": 2 + }, + { + "tag": "SplitKeyThreshold", + "type": "Integer", + "value": 2 + }, + { + "tag": "SplitKeyMethod", + "type": "Enumeration", + "value": "XOR" + } + ] +} diff --git a/vectors/fips/kmip_operations/create_split_key_xor/step3_join_split_key.json b/vectors/fips/kmip_operations/create_split_key_xor/step3_join_split_key.json new file mode 100644 index 0000000..4688286 --- /dev/null +++ b/vectors/fips/kmip_operations/create_split_key_xor/step3_join_split_key.json @@ -0,0 +1,25 @@ +{ + "tag": "JoinSplitKey", + "value": [ + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + }, + { + "tag": "PrivateKeyUniqueIdentifier", + "type": "TextString", + "value": "{{share1_id}}" + }, + { + "tag": "PrivateKeyUniqueIdentifier", + "type": "TextString", + "value": "{{share2_id}}" + }, + { + "tag": "SplitKeyMethod", + "type": "Enumeration", + "value": "XOR" + } + ] +} diff --git a/vectors/fips/kmip_operations/create_split_key_xor/step4_get_reconstructed.json b/vectors/fips/kmip_operations/create_split_key_xor/step4_get_reconstructed.json new file mode 100644 index 0000000..0467262 --- /dev/null +++ b/vectors/fips/kmip_operations/create_split_key_xor/step4_get_reconstructed.json @@ -0,0 +1,10 @@ +{ + "tag": "Get", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{reconstructed_id}}" + } + ] +} diff --git a/vectors/negative/create_split_key_parts_less_than_threshold/cleanup_destroy_key.json b/vectors/negative/create_split_key_parts_less_than_threshold/cleanup_destroy_key.json new file mode 100644 index 0000000..ce2dc0e --- /dev/null +++ b/vectors/negative/create_split_key_parts_less_than_threshold/cleanup_destroy_key.json @@ -0,0 +1,10 @@ +{ + "tag": "Destroy", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{key_id}}" + } + ] +} diff --git a/vectors/negative/create_split_key_parts_less_than_threshold/manifest.toml b/vectors/negative/create_split_key_parts_less_than_threshold/manifest.toml new file mode 100644 index 0000000..cb25121 --- /dev/null +++ b/vectors/negative/create_split_key_parts_less_than_threshold/manifest.toml @@ -0,0 +1,28 @@ +name = "CreateSplitKey: parts < threshold is rejected" +description = """ +A CreateSplitKey request where split_key_parts < split_key_threshold must be rejected. +You cannot require more shares than exist. +""" + +[[steps]] +operation = "Create" +request = "step1_create_key.json" +assert_success = true + +[steps.capture] +key_id = "UniqueIdentifier" + +# ── Attempt CreateSplitKey with parts(3) < threshold(5) ────────────────────── + +[[steps]] +operation = "CreateSplitKey" +request = "step2_create_split_key_bad_parts.json" +assert_success = false +assert_error_contains = "split_key_parts must be >= split_key_threshold" + +# ── Cleanup ────────────────────────────────────────────────────────────────── + +[[steps]] +operation = "Destroy" +request = "cleanup_destroy_key.json" +allow_failure = true diff --git a/vectors/negative/create_split_key_parts_less_than_threshold/step1_create_key.json b/vectors/negative/create_split_key_parts_less_than_threshold/step1_create_key.json new file mode 100644 index 0000000..1b5500d --- /dev/null +++ b/vectors/negative/create_split_key_parts_less_than_threshold/step1_create_key.json @@ -0,0 +1,35 @@ +{ + "tag": "Create", + "value": [ + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + }, + { + "tag": "Attributes", + "value": [ + { + "tag": "CryptographicAlgorithm", + "type": "Enumeration", + "value": "AES" + }, + { + "tag": "CryptographicLength", + "type": "Integer", + "value": 256 + }, + { + "tag": "CryptographicUsageMask", + "type": "Integer", + "value": 12 + }, + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + } + ] + } + ] +} diff --git a/vectors/negative/create_split_key_parts_less_than_threshold/step2_create_split_key_bad_parts.json b/vectors/negative/create_split_key_parts_less_than_threshold/step2_create_split_key_bad_parts.json new file mode 100644 index 0000000..f2cd74c --- /dev/null +++ b/vectors/negative/create_split_key_parts_less_than_threshold/step2_create_split_key_bad_parts.json @@ -0,0 +1,25 @@ +{ + "tag": "CreateSplitKey", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{key_id}}" + }, + { + "tag": "SplitKeyParts", + "type": "Integer", + "value": 3 + }, + { + "tag": "SplitKeyThreshold", + "type": "Integer", + "value": 5 + }, + { + "tag": "SplitKeyMethod", + "type": "Enumeration", + "value": "PolynomialSharingGf28" + } + ] +} diff --git a/vectors/negative/create_split_key_threshold_too_low/cleanup_destroy_key.json b/vectors/negative/create_split_key_threshold_too_low/cleanup_destroy_key.json new file mode 100644 index 0000000..ce2dc0e --- /dev/null +++ b/vectors/negative/create_split_key_threshold_too_low/cleanup_destroy_key.json @@ -0,0 +1,10 @@ +{ + "tag": "Destroy", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{key_id}}" + } + ] +} diff --git a/vectors/negative/create_split_key_threshold_too_low/manifest.toml b/vectors/negative/create_split_key_threshold_too_low/manifest.toml new file mode 100644 index 0000000..cd1e576 --- /dev/null +++ b/vectors/negative/create_split_key_threshold_too_low/manifest.toml @@ -0,0 +1,28 @@ +name = "CreateSplitKey: threshold < 2 is rejected" +description = """ +A CreateSplitKey request with split_key_threshold = 1 must be rejected by the server. +A threshold of at least 2 is required for meaningful secret sharing. +""" + +[[steps]] +operation = "Create" +request = "step1_create_key.json" +assert_success = true + +[steps.capture] +key_id = "UniqueIdentifier" + +# ── Attempt CreateSplitKey with an invalid threshold ───────────────────────── + +[[steps]] +operation = "CreateSplitKey" +request = "step2_create_split_key_bad_threshold.json" +assert_success = false +assert_error_contains = "split_key_threshold must be at least 2" + +# ── Cleanup ────────────────────────────────────────────────────────────────── + +[[steps]] +operation = "Destroy" +request = "cleanup_destroy_key.json" +allow_failure = true diff --git a/vectors/negative/create_split_key_threshold_too_low/step1_create_key.json b/vectors/negative/create_split_key_threshold_too_low/step1_create_key.json new file mode 100644 index 0000000..1b5500d --- /dev/null +++ b/vectors/negative/create_split_key_threshold_too_low/step1_create_key.json @@ -0,0 +1,35 @@ +{ + "tag": "Create", + "value": [ + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + }, + { + "tag": "Attributes", + "value": [ + { + "tag": "CryptographicAlgorithm", + "type": "Enumeration", + "value": "AES" + }, + { + "tag": "CryptographicLength", + "type": "Integer", + "value": 256 + }, + { + "tag": "CryptographicUsageMask", + "type": "Integer", + "value": 12 + }, + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + } + ] + } + ] +} diff --git a/vectors/negative/create_split_key_threshold_too_low/step2_create_split_key_bad_threshold.json b/vectors/negative/create_split_key_threshold_too_low/step2_create_split_key_bad_threshold.json new file mode 100644 index 0000000..7d1e5e1 --- /dev/null +++ b/vectors/negative/create_split_key_threshold_too_low/step2_create_split_key_bad_threshold.json @@ -0,0 +1,25 @@ +{ + "tag": "CreateSplitKey", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{key_id}}" + }, + { + "tag": "SplitKeyParts", + "type": "Integer", + "value": 3 + }, + { + "tag": "SplitKeyThreshold", + "type": "Integer", + "value": 1 + }, + { + "tag": "SplitKeyMethod", + "type": "Enumeration", + "value": "PolynomialSharingGf28" + } + ] +} From 44f7a9f430154230342aebcdb70ea57272753dfe Mon Sep 17 00:00:00 2001 From: Manuthor Date: Mon, 10 Aug 2026 07:10:45 +0200 Subject: [PATCH 02/17] chore: reorganize TOML configuration tree --- configs/ckms.bad.toml | 2 - configs/ckms.toml | 4 -- configs/ckms_partial.toml | 3 - configs/client/auth_plain_owner.toml | 6 -- configs/client/auth_plain_user.toml | 6 -- .../{ckms_for_bash.toml => client/bash.toml} | 0 configs/client/cert_auth_owner.toml | 9 --- configs/client/cert_auth_user.toml | 9 --- ...ers_user.toml => crypto_officer_user.toml} | 0 .../client/crypto_officer_users_owner.toml | 9 --- configs/{ckms_jwt.toml => client/jwt.toml} | 0 .../{pem_cert_auth.toml => pem_cert.toml} | 0 ...nd_token_auth.toml => pem_cert_token.toml} | 0 ...pkcs12_cert_auth.toml => pkcs12_cert.toml} | 0 ...users_owner.toml => privileged_owner.toml} | 0 ...d_users_user.toml => privileged_user.toml} | 0 configs/server/audit.toml | 25 ------- .../api_token.toml} | 0 configs/server/{ => auth}/entra_id.toml | 0 .../server/{jwt_auth.toml => auth/jwt.toml} | 0 .../{ => auth}/multifactor_jwt_api.toml | 0 .../{ => auth}/multifactor_tls_jwt.toml | 0 .../server/{no_auth.toml => auth/none.toml} | 0 .../server/{ => auth}/tls_client_cert.toml | 0 configs/server/auth_https.toml | 32 --------- configs/server/auth_https_client_ca.toml | 29 -------- configs/server/auth_https_jwt.toml | 37 ---------- configs/server/auth_plain.toml | 23 ------- configs/server/auth_plain_jwt.toml | 31 --------- configs/server/auth_verifier.toml | 69 ------------------- configs/server/basic_http.toml | 4 -- configs/server/cert_auth.toml | 33 --------- ...cert_auth_operator_and_crypto_officer.toml | 32 --------- .../server/crypto_officer_users_mysql.toml | 41 ----------- .../server/crypto_officer_users_postgres.toml | 41 ----------- .../crypto_officer_users_redis_findex.toml | 43 ------------ configs/server/{ => db}/mysql.toml | 0 .../mysql_snippet.toml} | 0 configs/server/{ => db}/postgres.toml | 0 .../postgres_lb_primary.toml} | 0 .../postgres_lb_secondary.toml} | 0 configs/server/{ => db}/redis_findex.toml | 0 configs/server/google_cse.toml | 9 --- configs/server/google_cse_new_format.toml | 9 --- configs/server/installation_basic.toml | 4 -- configs/server/jwt_idp_auth.toml | 3 - configs/server/lb_kms3_postgres.toml | 18 ----- configs/server/openapi_validation.toml | 37 ---------- configs/server/otlp_logging.toml | 4 -- configs/server/privileged_users.toml | 41 ----------- configs/server/quick_start_minimal.toml | 4 -- .../server/quick_start_minimal_alt_port.toml | 4 -- configs/server/rbac/crypto_officer_only.toml | 52 -------------- configs/server/rbac/crypto_officers.toml | 66 ++++++++++++------ configs/server/rbac/full_rbac.toml | 58 ---------------- .../{tls_auth_fips.toml => tls/fips.toml} | 0 .../non_fips.toml} | 0 .../tls13_fips.toml} | 0 .../tls13_non_fips.toml} | 0 .../manifest.toml | 2 +- .../grant_access_aes/manifest.toml | 2 +- .../grant_partial_permissions/manifest.toml | 2 +- .../manifest.toml | 2 +- .../owner_full_permissions/manifest.toml | 2 +- .../manifest.toml | 2 +- .../manifest.toml | 2 +- .../manifest.toml | 2 +- .../manifest.toml | 2 +- .../manifest.toml | 2 +- .../revoke_access/manifest.toml | 2 +- .../unauthorized_access/manifest.toml | 2 +- vectors/tls/mtls/manifest.toml | 2 +- vectors/tls/server_tls/manifest.toml | 2 +- 73 files changed, 60 insertions(+), 765 deletions(-) delete mode 100644 configs/ckms.bad.toml delete mode 100644 configs/ckms.toml delete mode 100644 configs/ckms_partial.toml delete mode 100644 configs/client/auth_plain_owner.toml delete mode 100644 configs/client/auth_plain_user.toml rename configs/{ckms_for_bash.toml => client/bash.toml} (100%) delete mode 100644 configs/client/cert_auth_owner.toml delete mode 100644 configs/client/cert_auth_user.toml rename configs/client/{crypto_officer_users_user.toml => crypto_officer_user.toml} (100%) delete mode 100644 configs/client/crypto_officer_users_owner.toml rename configs/{ckms_jwt.toml => client/jwt.toml} (100%) rename configs/client/{pem_cert_auth.toml => pem_cert.toml} (100%) rename configs/client/{pem_cert_and_token_auth.toml => pem_cert_token.toml} (100%) rename configs/client/{pkcs12_cert_auth.toml => pkcs12_cert.toml} (100%) rename configs/client/{privileged_users_owner.toml => privileged_owner.toml} (100%) rename configs/client/{privileged_users_user.toml => privileged_user.toml} (100%) delete mode 100644 configs/server/audit.toml rename configs/server/{api_token_auth.toml => auth/api_token.toml} (100%) rename configs/server/{ => auth}/entra_id.toml (100%) rename configs/server/{jwt_auth.toml => auth/jwt.toml} (100%) rename configs/server/{ => auth}/multifactor_jwt_api.toml (100%) rename configs/server/{ => auth}/multifactor_tls_jwt.toml (100%) rename configs/server/{no_auth.toml => auth/none.toml} (100%) rename configs/server/{ => auth}/tls_client_cert.toml (100%) delete mode 100644 configs/server/auth_https.toml delete mode 100644 configs/server/auth_https_client_ca.toml delete mode 100644 configs/server/auth_https_jwt.toml delete mode 100644 configs/server/auth_plain.toml delete mode 100644 configs/server/auth_plain_jwt.toml delete mode 100644 configs/server/auth_verifier.toml delete mode 100644 configs/server/basic_http.toml delete mode 100644 configs/server/cert_auth.toml delete mode 100644 configs/server/cert_auth_operator_and_crypto_officer.toml delete mode 100644 configs/server/crypto_officer_users_mysql.toml delete mode 100644 configs/server/crypto_officer_users_postgres.toml delete mode 100644 configs/server/crypto_officer_users_redis_findex.toml rename configs/server/{ => db}/mysql.toml (100%) rename configs/server/{mysql_database.toml => db/mysql_snippet.toml} (100%) rename configs/server/{ => db}/postgres.toml (100%) rename configs/server/{lb_kms1_postgres.toml => db/postgres_lb_primary.toml} (100%) rename configs/server/{lb_kms2_postgres.toml => db/postgres_lb_secondary.toml} (100%) rename configs/server/{ => db}/redis_findex.toml (100%) delete mode 100644 configs/server/google_cse.toml delete mode 100644 configs/server/google_cse_new_format.toml delete mode 100644 configs/server/installation_basic.toml delete mode 100644 configs/server/jwt_idp_auth.toml delete mode 100644 configs/server/lb_kms3_postgres.toml delete mode 100644 configs/server/openapi_validation.toml delete mode 100644 configs/server/otlp_logging.toml delete mode 100644 configs/server/privileged_users.toml delete mode 100644 configs/server/quick_start_minimal.toml delete mode 100644 configs/server/quick_start_minimal_alt_port.toml delete mode 100644 configs/server/rbac/crypto_officer_only.toml delete mode 100644 configs/server/rbac/full_rbac.toml rename configs/server/{tls_auth_fips.toml => tls/fips.toml} (100%) rename configs/server/{tls_auth_non_fips.toml => tls/non_fips.toml} (100%) rename configs/server/{tls13_auth_fips.toml => tls/tls13_fips.toml} (100%) rename configs/server/{tls13_auth_non_fips.toml => tls/tls13_non_fips.toml} (100%) diff --git a/configs/ckms.bad.toml b/configs/ckms.bad.toml deleted file mode 100644 index d6d7d1f..0000000 --- a/configs/ckms.bad.toml +++ /dev/null @@ -1,2 +0,0 @@ -[http_config] -access_token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjVVU1FrSVlULW9QMWZrcjQtNnRrciJ9.eyJuaWNrbmFtZSI6InRlY2giLCJuYW1lIjoidGVjaEBjb3NtaWFuLmNvbSIsInBpY3R1cmUiOiJodHRwczovL3MuZ3JhdmF0YXIuY29tL2F2YXRhci81MmZiMzFjOGNjYWQzNDU4MTIzZDRmYWQxNDA4NTRjZj9zPTQ4MCZyPXBnJmQ9aHR0cHMlM0ElMkYlMkZjZG4uYXV0aDAuY29tJTJGYXZhdGFycyUyRnRlLnBuZyIsInVwZGF0ZWRfYXQiOiIyMDIzLTA1LTMwVDA5OjMxOjExLjM4NloiLCJlbWFpbCI6InRlY2hAY29zbWlhbi5jb20iLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImlzcyI6Imh0dHBzOi8va21zLWNvc21pYW4uZXUuYXV0aDAuY29tLyIsImF1ZCI6IkszaXhldXhuVDVrM0Roa0tocWhiMXpYbjlFNjJGRXdJIiwiaWF0IjoxNjg1NDM5MDc0LCJleHAiOjE2ODU0NzUwNzQsInN1YiI6ImF1dGgwfDYzZDNkM2VhOTNmZjE2NDJjNzdkZjkyOCIsInNpZCI6ImJnVUNuTTNBRjVxMlpaVHFxMTZwclBCMi11Z0NNaUNPIiwibm9uY2UiOiJVRUZWTlZWeVluWTVUbHBwWjJScGNqSmtVMEZ4TmxkUFEwc3dTVGMwWHpaV2RVVmtkVnBEVGxSMldnPT0ifQ.HmU9fFwZ-JjJVlSy_PTei3ys0upeWQbWWiESmKBtRSClGnAXJNCpwuP4Jw7fgKn-8IBf-PYmP1_54u2Rw3RcJFVl7EblVoGMghYxVq5hViGpd00st3VwZmyCwOUz2CE5RBnBAoES4C8xA3zWg6oau0xjFQbC3jNU20eyFYMDewXA8UXCHQrEiQ56ylqSbyqlBbQIWbmOO4m5w2WDkx0bVyyJ893JfIJr_NANEQMJITYo8Mp_iHCyKp7llsfgCt07xN8ZqnsrMsJ15zC1n50bHGrTQisxURS1dpuFXF1hfrxhzogxYMX8CEISjsFgROjPY84GRMmvpYZfyaJbDDql3A" diff --git a/configs/ckms.toml b/configs/ckms.toml deleted file mode 100644 index 4725088..0000000 --- a/configs/ckms.toml +++ /dev/null @@ -1,4 +0,0 @@ -print_json = false - -[http_config] -server_url = "http://0.0.0.0:9998" diff --git a/configs/ckms_partial.toml b/configs/ckms_partial.toml deleted file mode 100644 index a3f0a21..0000000 --- a/configs/ckms_partial.toml +++ /dev/null @@ -1,3 +0,0 @@ -[http_config] -server_url = "http://0.0.0.0:9990" -access_token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjVVU1FrSVlULW9QMWZrcjQtNnRrciJ9.eyJuaWNrbmFtZSI6InRlY2giLCJuYW1lIjoidGVjaEBjb3NtaWFuLmNvbSIsInBpY3R1cmUiOiJodHRwczovL3MuZ3JhdmF0YXIuY29tL2F2YXRhci81MmZiMzFjOGNjYWQzNDU4MTIzZDRmYWQxNDA4NTRjZj9zPTQ4MCZyPXBnJmQ9aHR0cHMlM0ElMkYlMkZjZG4uYXV0aDAuY29tJTJGYXZhdGFycyUyRnRlLnBuZyIsInVwZGF0ZWRfYXQiOiIyMDIzLTA1LTMwVDA5OjMxOjExLjM4NloiLCJlbWFpbCI6InRlY2hAY29zbWlhbi5jb20iLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImlzcyI6Imh0dHBzOi8va21zLWNvc21pYW4uZXUuYXV0aDAuY29tLyIsImF1ZCI6IkszaXhldXhuVDVrM0Roa0tocWhiMXpYbjlFNjJGRXdJIiwiaWF0IjoxNjg1NDM5MDc0LCJleHAiOjE2ODU0NzUwNzQsInN1YiI6ImF1dGgwfDYzZDNkM2VhOTNmZjE2NDJjNzdkZjkyOCIsInNpZCI6ImJnVUNuTTNBRjVxMlpaVHFxMTZwclBCMi11Z0NNaUNPIiwibm9uY2UiOiJVRUZWTlZWeVluWTVUbHBwWjJScGNqSmtVMEZ4TmxkUFEwc3dTVGMwWHpaV2RVVmtkVnBEVGxSMldnPT0ifQ.HmU9fFwZ-JjJVlSy_PTei3ys0upeWQbWWiESmKBtRSClGnAXJNCpwuP4Jw7fgKn-8IBf-PYmP1_54u2Rw3RcJFVl7EblVoGMghYxVq5hViGpd00st3VwZmyCwOUz2CE5RBnBAoES4C8xA3zWg6oau0xjFQbC3jNU20eyFYMDewXA8UXCHQrEiQ56ylqSbyqlBbQIWbmOO4m5w2WDkx0bVyyJ893JfIJr_NANEQMJITYo8Mp_iHCyKp7llsfgCt07xN8ZqnsrMsJ15zC1n50bHGrTQisxURS1dpuFXF1hfrxhzogxYMX8CEISjsFgROjPY84GRMmvpYZfyaJbDDql3A" diff --git a/configs/client/auth_plain_owner.toml b/configs/client/auth_plain_owner.toml deleted file mode 100644 index f8372fe..0000000 --- a/configs/client/auth_plain_owner.toml +++ /dev/null @@ -1,6 +0,0 @@ -# Test KMS client — plain HTTP, no authentication. Owner identity. -# Corresponds to server config test/auth_plain.toml (port 12001). - -[http_config] -server_url = "http://localhost:12001" -accept_invalid_certs = true diff --git a/configs/client/auth_plain_user.toml b/configs/client/auth_plain_user.toml deleted file mode 100644 index 234aa83..0000000 --- a/configs/client/auth_plain_user.toml +++ /dev/null @@ -1,6 +0,0 @@ -# Test KMS client — plain HTTP, no authentication. User identity. -# Corresponds to server config test/auth_plain.toml (port 12001). - -[http_config] -server_url = "http://localhost:12001" -accept_invalid_certs = true diff --git a/configs/ckms_for_bash.toml b/configs/client/bash.toml similarity index 100% rename from configs/ckms_for_bash.toml rename to configs/client/bash.toml diff --git a/configs/client/cert_auth_owner.toml b/configs/client/cert_auth_owner.toml deleted file mode 100644 index 9a65057..0000000 --- a/configs/client/cert_auth_owner.toml +++ /dev/null @@ -1,9 +0,0 @@ -# Test KMS client — HTTPS with owner PEM client certificate, no JWT. Owner identity. -# Cert paths are relative to workspace root, resolved by load_client_config(). -# Corresponds to server config test/cert_auth.toml (port 9999). - -[http_config] -server_url = "https://localhost:29998" -accept_invalid_certs = true -tls_client_pem_cert_path = "test_data/certificates/client_server/owner/owner.client.acme.com.crt" -tls_client_pem_key_path = "test_data/certificates/client_server/owner/owner.client.acme.com.key" diff --git a/configs/client/cert_auth_user.toml b/configs/client/cert_auth_user.toml deleted file mode 100644 index 1556a4c..0000000 --- a/configs/client/cert_auth_user.toml +++ /dev/null @@ -1,9 +0,0 @@ -# Test KMS client — HTTPS with user PEM client certificate, no JWT. User identity. -# Cert paths are relative to workspace root, resolved by load_client_config(). -# Corresponds to server config test/cert_auth.toml (port 9999). - -[http_config] -server_url = "https://localhost:29998" -accept_invalid_certs = true -tls_client_pem_cert_path = "test_data/certificates/client_server/user/user.client.acme.com.crt" -tls_client_pem_key_path = "test_data/certificates/client_server/user/user.client.acme.com.key" diff --git a/configs/client/crypto_officer_users_user.toml b/configs/client/crypto_officer_user.toml similarity index 100% rename from configs/client/crypto_officer_users_user.toml rename to configs/client/crypto_officer_user.toml diff --git a/configs/client/crypto_officer_users_owner.toml b/configs/client/crypto_officer_users_owner.toml deleted file mode 100644 index 3c05f51..0000000 --- a/configs/client/crypto_officer_users_owner.toml +++ /dev/null @@ -1,9 +0,0 @@ -# Test KMS client — HTTPS with owner PEM client certificate, no JWT. Owner identity. -# Cert paths are relative to workspace root, resolved by load_client_config(). -# Corresponds to server config crypto_officer_users.toml (port 10003). - -[http_config] -server_url = "https://localhost:10003" -accept_invalid_certs = true -tls_client_pem_cert_path = "test_data/certificates/client_server/owner/owner.client.acme.com.crt" -tls_client_pem_key_path = "test_data/certificates/client_server/owner/owner.client.acme.com.key" diff --git a/configs/ckms_jwt.toml b/configs/client/jwt.toml similarity index 100% rename from configs/ckms_jwt.toml rename to configs/client/jwt.toml diff --git a/configs/client/pem_cert_auth.toml b/configs/client/pem_cert.toml similarity index 100% rename from configs/client/pem_cert_auth.toml rename to configs/client/pem_cert.toml diff --git a/configs/client/pem_cert_and_token_auth.toml b/configs/client/pem_cert_token.toml similarity index 100% rename from configs/client/pem_cert_and_token_auth.toml rename to configs/client/pem_cert_token.toml diff --git a/configs/client/pkcs12_cert_auth.toml b/configs/client/pkcs12_cert.toml similarity index 100% rename from configs/client/pkcs12_cert_auth.toml rename to configs/client/pkcs12_cert.toml diff --git a/configs/client/privileged_users_owner.toml b/configs/client/privileged_owner.toml similarity index 100% rename from configs/client/privileged_users_owner.toml rename to configs/client/privileged_owner.toml diff --git a/configs/client/privileged_users_user.toml b/configs/client/privileged_user.toml similarity index 100% rename from configs/client/privileged_users_user.toml rename to configs/client/privileged_user.toml diff --git a/configs/server/audit.toml b/configs/server/audit.toml deleted file mode 100644 index 838e9d0..0000000 --- a/configs/server/audit.toml +++ /dev/null @@ -1,25 +0,0 @@ -default_username = "admin" - -[http] -port = 9998 -hostname = "0.0.0.0" -cors_allowed_origins = ["http://localhost:9998"] - -[db] -database_type = "sqlite" -sqlite_path = "/tmp/kms-data-audit" -clear_database = false -unwrapped_cache_max_age = 15 - -[logging] -rust_log = "info,cosmian_kms=info" -ansi_colors = false - -[ui_config] -ui_index_html_folder = "ui/dist" - -[audit] -enabled = true - -[audit.file] -path = "/tmp/kms-audit.jsonl" diff --git a/configs/server/api_token_auth.toml b/configs/server/auth/api_token.toml similarity index 100% rename from configs/server/api_token_auth.toml rename to configs/server/auth/api_token.toml diff --git a/configs/server/entra_id.toml b/configs/server/auth/entra_id.toml similarity index 100% rename from configs/server/entra_id.toml rename to configs/server/auth/entra_id.toml diff --git a/configs/server/jwt_auth.toml b/configs/server/auth/jwt.toml similarity index 100% rename from configs/server/jwt_auth.toml rename to configs/server/auth/jwt.toml diff --git a/configs/server/multifactor_jwt_api.toml b/configs/server/auth/multifactor_jwt_api.toml similarity index 100% rename from configs/server/multifactor_jwt_api.toml rename to configs/server/auth/multifactor_jwt_api.toml diff --git a/configs/server/multifactor_tls_jwt.toml b/configs/server/auth/multifactor_tls_jwt.toml similarity index 100% rename from configs/server/multifactor_tls_jwt.toml rename to configs/server/auth/multifactor_tls_jwt.toml diff --git a/configs/server/no_auth.toml b/configs/server/auth/none.toml similarity index 100% rename from configs/server/no_auth.toml rename to configs/server/auth/none.toml diff --git a/configs/server/tls_client_cert.toml b/configs/server/auth/tls_client_cert.toml similarity index 100% rename from configs/server/tls_client_cert.toml rename to configs/server/auth/tls_client_cert.toml diff --git a/configs/server/auth_https.toml b/configs/server/auth_https.toml deleted file mode 100644 index f12cc5f..0000000 --- a/configs/server/auth_https.toml +++ /dev/null @@ -1,32 +0,0 @@ -# Auth test server — HTTPS without client CA (server TLS only, no mutual auth). -# Uses PEM cert + key (works in both FIPS and non-FIPS mode). -# Bound to port 12005. - -default_username = "tech@cosmian.com" -kms_public_url = "https://localhost:12005/google_cse" - -[http] -port = 12005 -hostname = "0.0.0.0" - -[db] -database_type = "sqlite" -sqlite_path = "./sqlite-data-auth-tests" -clear_database = false - -[workspace] -root_data_path = "/tmp/kms_test_workspace_12005" -tmp_path = "/tmp" - -[tls] -tls_cert_file = "test_data/certificates/client_server/server/kmserver.acme.com.crt" -tls_key_file = "test_data/certificates/client_server/server/kmserver.acme.com.key" -# No clients_ca_cert_file — mutual TLS is not enforced. - -[google_cse_config] -google_cse_enable = true -google_cse_disable_tokens_validation = true -google_cse_incoming_url_whitelist = ["https://cse.cosmian.com"] - -[logging] -rust_log = "off" diff --git a/configs/server/auth_https_client_ca.toml b/configs/server/auth_https_client_ca.toml deleted file mode 100644 index 0de1d1b..0000000 --- a/configs/server/auth_https_client_ca.toml +++ /dev/null @@ -1,29 +0,0 @@ -# Auth test server — HTTPS with client-certificate CA, no JWT. -# Uses PEM cert + key (works in both FIPS and non-FIPS mode). -# Bound to port 12004. - -default_username = "tech@cosmian.com" -kms_public_url = "https://localhost:12004/google_cse" - -[http] -port = 12004 -hostname = "0.0.0.0" - -[db] -database_type = "sqlite" -sqlite_path = "./sqlite-data-auth-tests" -clear_database = false - -[workspace] -root_data_path = "/tmp/kms_test_workspace_12004" -tmp_path = "/tmp" - -[tls] -tls_cert_file = "test_data/certificates/client_server/server/kmserver.acme.com.crt" -tls_key_file = "test_data/certificates/client_server/server/kmserver.acme.com.key" -clients_ca_cert_file = "test_data/certificates/client_server/ca/ca.crt" - -[google_cse_config] -google_cse_enable = true -google_cse_disable_tokens_validation = true -google_cse_incoming_url_whitelist = ["https://cse.cosmian.com"] diff --git a/configs/server/auth_https_jwt.toml b/configs/server/auth_https_jwt.toml deleted file mode 100644 index d55549b..0000000 --- a/configs/server/auth_https_jwt.toml +++ /dev/null @@ -1,37 +0,0 @@ -# Auth test server — HTTPS with client-certificate CA + JWT. -# Uses PEM cert + key (works in both FIPS and non-FIPS mode). -# Bound to port 12003. - -default_username = "tech@cosmian.com" -kms_public_url = "https://localhost:12003/google_cse" - -[http] -port = 12003 -hostname = "0.0.0.0" - -[db] -database_type = "sqlite" -sqlite_path = "./sqlite-data-auth-tests" -clear_database = false - -[workspace] -root_data_path = "/tmp/kms_test_workspace_12003" -tmp_path = "/tmp" - -[tls] -tls_cert_file = "test_data/certificates/client_server/server/kmserver.acme.com.crt" -tls_key_file = "test_data/certificates/client_server/server/kmserver.acme.com.key" -clients_ca_cert_file = "test_data/certificates/client_server/ca/ca.crt" - -[idp_auth] -jwt_auth_provider = [ - "https://accounts.google.com,https://www.googleapis.com/oauth2/v3/certs,kacls-migration,996739510374-2tauakcggau7kvk37784k0s2lmveb9c9.apps.googleusercontent.com", - "https://login.microsoftonline.com/612da4de-35c0-42de-ba56-174b69062c96/v2.0,https://login.microsoftonline.com/612da4de-35c0-42de-ba56-174b69062c96/discovery/v2.0/keys", - "https://demo-kms.eu.auth0.com/,https://demo-kms.eu.auth0.com/.well-known/jwks.json", -] - -[google_cse_config] -google_cse_enable = true -# Token validation is active because JWT auth is enabled. -google_cse_disable_tokens_validation = false -google_cse_incoming_url_whitelist = ["https://cse.cosmian.com"] diff --git a/configs/server/auth_plain.toml b/configs/server/auth_plain.toml deleted file mode 100644 index f4189a0..0000000 --- a/configs/server/auth_plain.toml +++ /dev/null @@ -1,23 +0,0 @@ -# Auth test server — plain HTTP, no authentication. -# Bound to port 12001. - -default_username = "tech@cosmian.com" -kms_public_url = "http://localhost:12001/google_cse" - -[http] -port = 12001 -hostname = "0.0.0.0" - -[db] -database_type = "sqlite" -sqlite_path = "./sqlite-data-auth-tests" -clear_database = true - -[workspace] -root_data_path = "/tmp/kms_test_workspace_12001" -tmp_path = "/tmp" - -[google_cse_config] -google_cse_enable = true -google_cse_disable_tokens_validation = true -google_cse_incoming_url_whitelist = ["https://cse.cosmian.com"] diff --git a/configs/server/auth_plain_jwt.toml b/configs/server/auth_plain_jwt.toml deleted file mode 100644 index 9a266d1..0000000 --- a/configs/server/auth_plain_jwt.toml +++ /dev/null @@ -1,31 +0,0 @@ -# Auth test server — plain HTTP with JWT authentication. -# Bound to port 12002. - -default_username = "tech@cosmian.com" -kms_public_url = "http://localhost:12002/google_cse" - -[http] -port = 12002 -hostname = "0.0.0.0" - -[db] -database_type = "sqlite" -sqlite_path = "./sqlite-data-auth-tests" -clear_database = false - -[workspace] -root_data_path = "/tmp/kms_test_workspace_12002" -tmp_path = "/tmp" - -[idp_auth] -jwt_auth_provider = [ - "https://accounts.google.com,https://www.googleapis.com/oauth2/v3/certs,kacls-migration,996739510374-2tauakcggau7kvk37784k0s2lmveb9c9.apps.googleusercontent.com", - "https://login.microsoftonline.com/612da4de-35c0-42de-ba56-174b69062c96/v2.0,https://login.microsoftonline.com/612da4de-35c0-42de-ba56-174b69062c96/discovery/v2.0/keys", - "https://demo-kms.eu.auth0.com/,https://demo-kms.eu.auth0.com/.well-known/jwks.json", -] - -[google_cse_config] -google_cse_enable = true -# Token validation is active because JWT auth is enabled. -google_cse_disable_tokens_validation = false -google_cse_incoming_url_whitelist = ["https://cse.cosmian.com"] diff --git a/configs/server/auth_verifier.toml b/configs/server/auth_verifier.toml deleted file mode 100644 index 3e5fcdf..0000000 --- a/configs/server/auth_verifier.toml +++ /dev/null @@ -1,69 +0,0 @@ -# KMS server configured with Authentication Verifier (auth-verifier) -# as the primary auth method, served over HTTPS. -# -# The KMS server: -# 1. Terminates TLS using a PEM server certificate and private key. -# 2. Validates bearer tokens against the verifier's JWKS endpoint. -# 3. Exposes the Web UI login form (GET /ui/auth_method returns "AUTH_VERIFIER") -# and proxies credentials to the verifier via POST /ui/login_as. -# -# Because kms_public_url uses https://, the session cookie will carry the Secure -# flag — browsers only send it over HTTPS, which is the expected behaviour for -# production. For local dev with a self-signed certificate the KMS client must -# set accept_invalid_certs = true. -# -# Prerequisites: -# - A running Authentication Verifier at auth_verifier_url below. -# - The verifier's JWKS endpoint must be reachable from the KMS server. -# - The TLS cert/key files below must exist (relative to the repo root). -# -# Bound to port 9998 (default KMS port). - -default_username = "admin" -kms_public_url = "https://localhost:9998" - -[http] -port = 9998 -hostname = "0.0.0.0" - -[tls] -# Server certificate and private key (PEM). No client CA → server-TLS only -# (no mutual authentication required). -tls_cert_file = "test_data/certificates/client_server/server/kmserver.acme.com.crt" -tls_key_file = "test_data/certificates/client_server/server/kmserver.acme.com.key" - -[db] -database_type = "sqlite" -sqlite_path = "./sqlite-data-auth-verifier" -clear_database = false - -# ── Authentication Verifier ────────────────────────────────────────────────── -# When both `auth_verifier_url` and `auth_verifier_realm` are set, the -# KMS server: -# 1. Validates bearer tokens against the verifier's JWKS endpoint -# 2. Exposes the Web UI login form (GET /ui/auth_method returns "AUTH_VERIFIER") -# 3. Proxies login credentials to the verifier via POST /ui/login_as -# -# `auth_verifier_accept_invalid_certs` allows the JWKS fetch to target an HTTP -# URL (or a server with a self-signed certificate). In production the verifier -# must be reachable over HTTPS with a valid certificate, and this flag must be -# set to false. -[auth_verifier] -auth_verifier_url = "https://localhost:8443" -auth_verifier_realm = "_" -auth_verifier_accept_invalid_certs = true # dev/test only — never in production - -[ui_config] -ui_index_html_folder = "./ui/dist" -# A stable session salt ties the session cookie encryption key to this server's -# public URL, making it deterministic across restarts and portable across -# load-balanced instances. -# Generate your own with: openssl rand -hex 32 -ui_session_salt = "change-me-use-openssl-rand-hex-32" - -[logging] -rust_log = "info,cosmian_kms=debug" -quiet = false -log_to_syslog = false -environment = "development" -ansi_colors = true diff --git a/configs/server/basic_http.toml b/configs/server/basic_http.toml deleted file mode 100644 index 13f7c52..0000000 --- a/configs/server/basic_http.toml +++ /dev/null @@ -1,4 +0,0 @@ -# Basic HTTP configuration from quick_start.md -[http] -port = 9998 -hostname = "0.0.0.0" diff --git a/configs/server/cert_auth.toml b/configs/server/cert_auth.toml deleted file mode 100644 index 66122af..0000000 --- a/configs/server/cert_auth.toml +++ /dev/null @@ -1,33 +0,0 @@ -# Test KMS server — TLS with mutual client-certificate authentication. -# Uses PEM cert + key (works in both FIPS and non-FIPS mode). -# Bound to port 9999. Loaded by start_default_test_kms_server_with_cert_auth(). - -default_username = "tech@cosmian.com" -kms_public_url = "https://localhost:9999/google_cse" - -[http] -port = 9999 -hostname = "0.0.0.0" - -[db] -database_type = "sqlite" -sqlite_path = "/tmp/kms_test_cert_auth" -clear_database = true - -[workspace] -root_data_path = "/tmp/kms_test_workspace_cert_auth" -tmp_path = "/tmp" - -[tls] -tls_cert_file = "test_data/certificates/client_server/server/kmserver.acme.com.crt" -tls_key_file = "test_data/certificates/client_server/server/kmserver.acme.com.key" -clients_ca_cert_file = "test_data/certificates/client_server/ca/ca.crt" - -[socket_server] -socket_server_start = true -socket_server_port = 10099 - -[google_cse_config] -google_cse_enable = true -google_cse_disable_tokens_validation = true -google_cse_incoming_url_whitelist = ["https://cse.cosmian.com"] diff --git a/configs/server/cert_auth_operator_and_crypto_officer.toml b/configs/server/cert_auth_operator_and_crypto_officer.toml deleted file mode 100644 index e1b637d..0000000 --- a/configs/server/cert_auth_operator_and_crypto_officer.toml +++ /dev/null @@ -1,32 +0,0 @@ -# Test KMS server — TLS with mutual client-certificate authentication + role enforcement. -# `user.client@acme.com` is an unenrolled user (defaults to Operator). -# `owner.client@acme.com` is a CryptoOfficer. -# Bound to port 9992. Loaded by Operator/CryptoOfficer separation-of-duty test vectors. - -default_username = "tech@cosmian.com" -kms_public_url = "https://localhost:9992/google_cse" - -[roles] -crypto_officer_users = ["owner.client@acme.com"] - -[http] -port = 9992 -hostname = "0.0.0.0" - -[db] -database_type = "sqlite" -sqlite_path = "/tmp/kms_test_cert_auth_operator_crypto_officer" -clear_database = true - -[workspace] -root_data_path = "/tmp/kms_test_workspace_cert_auth_operator_crypto_officer" -tmp_path = "/tmp" - -[tls] -tls_cert_file = "test_data/certificates/client_server/server/kmserver.acme.com.crt" -tls_key_file = "test_data/certificates/client_server/server/kmserver.acme.com.key" -clients_ca_cert_file = "test_data/certificates/client_server/ca/ca.crt" - -[socket_server] -socket_server_start = true -socket_server_port = 10092 diff --git a/configs/server/crypto_officer_users_mysql.toml b/configs/server/crypto_officer_users_mysql.toml deleted file mode 100644 index 1bb7eb4..0000000 --- a/configs/server/crypto_officer_users_mysql.toml +++ /dev/null @@ -1,41 +0,0 @@ -# Test KMS server — TLS + JWT auth, for privileged-user tests, MySQL backend. -# Uses PEM cert + key (works in both FIPS and non-FIPS mode). -# Bound to port 10003. crypto_officer_users is injected at runtime. - -default_username = "tech@cosmian.com" -kms_public_url = "https://localhost:10003/google_cse" - -[http] -port = 10003 -hostname = "0.0.0.0" - -[db] -database_type = "mysql" -database_url = "mysql://kms:kms@localhost:3306/kms" -clear_database = false - -[workspace] -root_data_path = "/tmp/kms_test_workspace_crypto_officer_users_mysql" -tmp_path = "/tmp" - -[tls] -tls_cert_file = "test_data/certificates/client_server/server/kmserver.acme.com.crt" -tls_key_file = "test_data/certificates/client_server/server/kmserver.acme.com.key" -clients_ca_cert_file = "test_data/certificates/client_server/ca/ca.crt" - -[socket_server] -socket_server_start = true -socket_server_port = 10103 - -[idp_auth] -jwt_auth_provider = [ - "https://accounts.google.com,https://www.googleapis.com/oauth2/v3/certs,kacls-migration,996739510374-2tauakcggau7kvk37784k0s2lmveb9c9.apps.googleusercontent.com", - "https://login.microsoftonline.com/612da4de-35c0-42de-ba56-174b69062c96/v2.0,https://login.microsoftonline.com/612da4de-35c0-42de-ba56-174b69062c96/discovery/v2.0/keys", - "https://demo-kms.eu.auth0.com/,https://demo-kms.eu.auth0.com/.well-known/jwks.json", -] - -[google_cse_config] -google_cse_enable = true -# Token validation is active because JWT auth is enabled. -google_cse_disable_tokens_validation = false -google_cse_incoming_url_whitelist = ["https://cse.cosmian.com"] diff --git a/configs/server/crypto_officer_users_postgres.toml b/configs/server/crypto_officer_users_postgres.toml deleted file mode 100644 index ab90088..0000000 --- a/configs/server/crypto_officer_users_postgres.toml +++ /dev/null @@ -1,41 +0,0 @@ -# Test KMS server — TLS + JWT auth, for privileged-user tests, PostgreSQL backend. -# Uses PEM cert + key (works in both FIPS and non-FIPS mode). -# Bound to port 10003. crypto_officer_users is injected at runtime. - -default_username = "tech@cosmian.com" -kms_public_url = "https://localhost:10003/google_cse" - -[http] -port = 10003 -hostname = "0.0.0.0" - -[db] -database_type = "postgresql" -database_url = "postgresql://kms:kms@127.0.0.1:5432/kms" -clear_database = false - -[workspace] -root_data_path = "/tmp/kms_test_workspace_crypto_officer_users_postgres" -tmp_path = "/tmp" - -[tls] -tls_cert_file = "test_data/certificates/client_server/server/kmserver.acme.com.crt" -tls_key_file = "test_data/certificates/client_server/server/kmserver.acme.com.key" -clients_ca_cert_file = "test_data/certificates/client_server/ca/ca.crt" - -[socket_server] -socket_server_start = true -socket_server_port = 10103 - -[idp_auth] -jwt_auth_provider = [ - "https://accounts.google.com,https://www.googleapis.com/oauth2/v3/certs,kacls-migration,996739510374-2tauakcggau7kvk37784k0s2lmveb9c9.apps.googleusercontent.com", - "https://login.microsoftonline.com/612da4de-35c0-42de-ba56-174b69062c96/v2.0,https://login.microsoftonline.com/612da4de-35c0-42de-ba56-174b69062c96/discovery/v2.0/keys", - "https://demo-kms.eu.auth0.com/,https://demo-kms.eu.auth0.com/.well-known/jwks.json", -] - -[google_cse_config] -google_cse_enable = true -# Token validation is active because JWT auth is enabled. -google_cse_disable_tokens_validation = false -google_cse_incoming_url_whitelist = ["https://cse.cosmian.com"] diff --git a/configs/server/crypto_officer_users_redis_findex.toml b/configs/server/crypto_officer_users_redis_findex.toml deleted file mode 100644 index d6330e5..0000000 --- a/configs/server/crypto_officer_users_redis_findex.toml +++ /dev/null @@ -1,43 +0,0 @@ -# Test KMS server — TLS + JWT auth, for privileged-user tests, Redis-Findex backend. -# Only valid in non-FIPS mode (redis-findex is gated behind the non-fips feature). -# Uses PEM cert + key (works in both FIPS and non-FIPS mode). -# Bound to port 10003. crypto_officer_users is injected at runtime. - -default_username = "tech@cosmian.com" -kms_public_url = "https://localhost:10003/google_cse" - -[http] -port = 10003 -hostname = "0.0.0.0" - -[db] -database_type = "redis-findex" -database_url = "redis://localhost:6379" -clear_database = true -redis_master_password = "password" - -[workspace] -root_data_path = "/tmp/kms_test_workspace_crypto_officer_users_redis" -tmp_path = "/tmp" - -[tls] -tls_cert_file = "test_data/certificates/client_server/server/kmserver.acme.com.crt" -tls_key_file = "test_data/certificates/client_server/server/kmserver.acme.com.key" -clients_ca_cert_file = "test_data/certificates/client_server/ca/ca.crt" - -[socket_server] -socket_server_start = true -socket_server_port = 10103 - -[idp_auth] -jwt_auth_provider = [ - "https://accounts.google.com,https://www.googleapis.com/oauth2/v3/certs,kacls-migration,996739510374-2tauakcggau7kvk37784k0s2lmveb9c9.apps.googleusercontent.com", - "https://login.microsoftonline.com/612da4de-35c0-42de-ba56-174b69062c96/v2.0,https://login.microsoftonline.com/612da4de-35c0-42de-ba56-174b69062c96/discovery/v2.0/keys", - "https://demo-kms.eu.auth0.com/,https://demo-kms.eu.auth0.com/.well-known/jwks.json", -] - -[google_cse_config] -google_cse_enable = true -# Token validation is active because JWT auth is enabled. -google_cse_disable_tokens_validation = false -google_cse_incoming_url_whitelist = ["https://cse.cosmian.com"] diff --git a/configs/server/mysql.toml b/configs/server/db/mysql.toml similarity index 100% rename from configs/server/mysql.toml rename to configs/server/db/mysql.toml diff --git a/configs/server/mysql_database.toml b/configs/server/db/mysql_snippet.toml similarity index 100% rename from configs/server/mysql_database.toml rename to configs/server/db/mysql_snippet.toml diff --git a/configs/server/postgres.toml b/configs/server/db/postgres.toml similarity index 100% rename from configs/server/postgres.toml rename to configs/server/db/postgres.toml diff --git a/configs/server/lb_kms1_postgres.toml b/configs/server/db/postgres_lb_primary.toml similarity index 100% rename from configs/server/lb_kms1_postgres.toml rename to configs/server/db/postgres_lb_primary.toml diff --git a/configs/server/lb_kms2_postgres.toml b/configs/server/db/postgres_lb_secondary.toml similarity index 100% rename from configs/server/lb_kms2_postgres.toml rename to configs/server/db/postgres_lb_secondary.toml diff --git a/configs/server/redis_findex.toml b/configs/server/db/redis_findex.toml similarity index 100% rename from configs/server/redis_findex.toml rename to configs/server/db/redis_findex.toml diff --git a/configs/server/google_cse.toml b/configs/server/google_cse.toml deleted file mode 100644 index 7e09873..0000000 --- a/configs/server/google_cse.toml +++ /dev/null @@ -1,9 +0,0 @@ -# Google CSE configuration from google_cse/index.md -kms_public_url = "https://cse.example.com" - -# Use Google Identity for JWT validation -[idp_auth] -jwt_auth_provider = ["https://accounts.google.com,https://www.googleapis.com/oauth2/v3/certs,"] - -[google_cse_config] -google_cse_enable = true diff --git a/configs/server/google_cse_new_format.toml b/configs/server/google_cse_new_format.toml deleted file mode 100644 index 36c9be9..0000000 --- a/configs/server/google_cse_new_format.toml +++ /dev/null @@ -1,9 +0,0 @@ -# Google CSE configuration from google_cse/index.md - using new idp_auth format -kms_public_url = "https://cse.example.com" - -[idp_auth] -# Use Google Identity for JWT validation -jwt_auth_provider = ["https://accounts.google.com,https://www.googleapis.com/oauth2/v3/certs,"] - -[google_cse_config] -google_cse_enable = true diff --git a/configs/server/installation_basic.toml b/configs/server/installation_basic.toml deleted file mode 100644 index fdf924c..0000000 --- a/configs/server/installation_basic.toml +++ /dev/null @@ -1,4 +0,0 @@ -# Installation getting started configuration from installation/installation_getting_started.md -[http] -port = 9998 -hostname = "0.0.0.0" diff --git a/configs/server/jwt_idp_auth.toml b/configs/server/jwt_idp_auth.toml deleted file mode 100644 index 1aee3ec..0000000 --- a/configs/server/jwt_idp_auth.toml +++ /dev/null @@ -1,3 +0,0 @@ -# JWT authentication configuration using new idp_auth format -[idp_auth] -jwt_auth_provider = ["https://accounts.google.com,https://www.googleapis.com/oauth2/v3/certs,cosmian_kms"] diff --git a/configs/server/lb_kms3_postgres.toml b/configs/server/lb_kms3_postgres.toml deleted file mode 100644 index f0bfd06..0000000 --- a/configs/server/lb_kms3_postgres.toml +++ /dev/null @@ -1,18 +0,0 @@ -default_username = "admin" - -[http] -port = 9998 -hostname = "0.0.0.0" - -[db] -database_type = "postgresql" -database_url = "postgresql://kms:kms@postgres:5432/kms" -clear_database = false -unwrapped_cache_max_age = 15 - -[logging] -rust_log = "info,cosmian_kms=info" -ansi_colors = false - -[ui_config] -ui_index_html_folder = "/usr/local/cosmian/ui/dist" diff --git a/configs/server/openapi_validation.toml b/configs/server/openapi_validation.toml deleted file mode 100644 index b58549a..0000000 --- a/configs/server/openapi_validation.toml +++ /dev/null @@ -1,37 +0,0 @@ -# OpenAPI path validation test — maximally-configured server. -# Enables all enterprise scopes so the route validation test can verify -# that every path in openapi.yaml is registered. -# No real credentials are needed — routes just need to be *registered*. - -default_username = "tech@cosmian.com" -kms_public_url = "http://localhost:12099/google_cse" -ms_dke_service_url = "https://dke.example.com" - -[http] -port = 12099 -hostname = "0.0.0.0" - -[db] -database_type = "sqlite" -sqlite_path = "./sqlite-data-openapi-validation" -clear_database = true - -[workspace] -root_data_path = "/tmp/kms_test_openapi_validation" -tmp_path = "/tmp" - -[google_cse_config] -google_cse_enable = true -google_cse_disable_tokens_validation = true -google_cse_incoming_url_whitelist = ["https://cse.cosmian.com"] - -[aws_xks_config] -aws_xks_enable = true -aws_xks_region = "us-east-1" -aws_xks_service = "kms-xks-proxy" -aws_xks_sigv4_access_key_id = "AKIAIOSFODNN7EXAMPLE" -aws_xks_sigv4_secret_access_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" - -[azure_ekm_config] -azure_ekm_enable = true -azure_ekm_disable_client_auth = true diff --git a/configs/server/otlp_logging.toml b/configs/server/otlp_logging.toml deleted file mode 100644 index 65e282e..0000000 --- a/configs/server/otlp_logging.toml +++ /dev/null @@ -1,4 +0,0 @@ -# OTLP logging configuration from logging.md -[logging] -otlp = "http://localhost:4317" -quiet = true diff --git a/configs/server/privileged_users.toml b/configs/server/privileged_users.toml deleted file mode 100644 index fc327bf..0000000 --- a/configs/server/privileged_users.toml +++ /dev/null @@ -1,41 +0,0 @@ -# Test KMS server — TLS + JWT auth, for privileged-user tests. -# Uses PEM cert + key (works in both FIPS and non-FIPS mode). -# Bound to port 10003. crypto_officer_users is injected at runtime. - -default_username = "tech@cosmian.com" -kms_public_url = "https://localhost:10003/google_cse" - -[http] -port = 10003 -hostname = "0.0.0.0" - -[db] -database_type = "sqlite" -sqlite_path = "/tmp/kms_test_crypto_officer_users" -clear_database = true - -[workspace] -root_data_path = "/tmp/kms_test_workspace_crypto_officer_users" -tmp_path = "/tmp" - -[tls] -tls_cert_file = "test_data/certificates/client_server/server/kmserver.acme.com.crt" -tls_key_file = "test_data/certificates/client_server/server/kmserver.acme.com.key" -clients_ca_cert_file = "test_data/certificates/client_server/ca/ca.crt" - -[socket_server] -socket_server_start = true -socket_server_port = 10103 - -[idp_auth] -jwt_auth_provider = [ - "https://accounts.google.com,https://www.googleapis.com/oauth2/v3/certs,kacls-migration,996739510374-2tauakcggau7kvk37784k0s2lmveb9c9.apps.googleusercontent.com", - "https://login.microsoftonline.com/612da4de-35c0-42de-ba56-174b69062c96/v2.0,https://login.microsoftonline.com/612da4de-35c0-42de-ba56-174b69062c96/discovery/v2.0/keys", - "https://demo-kms.eu.auth0.com/,https://demo-kms.eu.auth0.com/.well-known/jwks.json", -] - -[google_cse_config] -google_cse_enable = true -# Token validation is active because JWT auth is enabled. -google_cse_disable_tokens_validation = false -google_cse_incoming_url_whitelist = ["https://cse.cosmian.com"] diff --git a/configs/server/quick_start_minimal.toml b/configs/server/quick_start_minimal.toml deleted file mode 100644 index b13062b..0000000 --- a/configs/server/quick_start_minimal.toml +++ /dev/null @@ -1,4 +0,0 @@ -# Minimal default configuration from quick_start.md -[http] -port = 9998 -hostname = "0.0.0.0" diff --git a/configs/server/quick_start_minimal_alt_port.toml b/configs/server/quick_start_minimal_alt_port.toml deleted file mode 100644 index c362b06..0000000 --- a/configs/server/quick_start_minimal_alt_port.toml +++ /dev/null @@ -1,4 +0,0 @@ -# Minimal default configuration from quick_start.md -[http] -port = 19998 -hostname = "0.0.0.0" diff --git a/configs/server/rbac/crypto_officer_only.toml b/configs/server/rbac/crypto_officer_only.toml deleted file mode 100644 index e51fe47..0000000 --- a/configs/server/rbac/crypto_officer_only.toml +++ /dev/null @@ -1,52 +0,0 @@ -# ── CryptoOfficer-only RBAC ────────────────────────────────────────────────── -# -# Role model: CryptoOfficer + default Operator -# Listed users hold the CryptoOfficer role. Unlisted authenticated users -# default to Operator (minimum privilege — crypto use only). -# -# CryptoOfficer permissions: -# • Key lifecycle: Create, Import, Register, Destroy, Revoke, Activate -# • Key pair management: CreateKeyPair, ReKeyKeyPair, Certify -# • Split-key management: CreateSplitKey, JoinSplitKey -# • Delegation: GrantAccess, RevokeAccess -# -# Use-case: key-management engineers who provision and rotate keys. Typical -# deployment separates CryptoOfficers from application users so that -# compromise of a service account cannot trigger key destruction. -# ───────────────────────────────────────────────────────────────────────────── - -default_username = "admin" - -[roles] -# Users with CryptoOfficer role. -# Authenticated users not listed here get default permissions (crypto use only). -# Certs: test_data/certificates/client_server/crypto-officer-1/crypto-officer-1.example.com.{key,crt} -# test_data/certificates/client_server/crypto-officer-2/crypto-officer-2.example.com.{key,crt} -crypto_officer_users = [ - "crypto-officer-1@example.com", - "crypto-officer-2@example.com", -] - -[http] -port = 9998 -hostname = "0.0.0.0" - -[db] -database_type = "sqlite" -sqlite_path = "/tmp/kms-rbac-crypto-officer" -clear_database = false - -[workspace] -root_data_path = "/tmp/kms-rbac-crypto-officer-workspace" -tmp_path = "/tmp" - -[tls] -tls_cert_file = "test_data/certificates/client_server/server/kmserver.acme.com.crt" -tls_key_file = "test_data/certificates/client_server/server/kmserver.acme.com.key" -clients_ca_cert_file = "test_data/certificates/client_server/ca/ca.crt" - -[logging] -rust_log = "info,cosmian_kms=info" - -[ui_config] -ui_index_html_folder = "ui/dist" diff --git a/configs/server/rbac/crypto_officers.toml b/configs/server/rbac/crypto_officers.toml index 029762d..7443351 100644 --- a/configs/server/rbac/crypto_officers.toml +++ b/configs/server/rbac/crypto_officers.toml @@ -1,25 +1,54 @@ -# Test KMS server — ceremony mode (require_ceremony = true). -# Loaded by start_ceremony_test_kms_server(). -# Bound to port 10004. +# ── Full RBAC — production reference configuration ────────────────────────── # -# CO users: owner.client@acme.com, user.client@acme.com, co3.client@acme.com -# The ceremony_secret is a 32-byte test secret (hex-encoded). -# NOTE: this secret is for testing only — never use in production. +# Role model: CryptoOfficer + Operator +# FIPS 140-3 §7.4 / NIST SP 800-57 Part 2 Rev 1 §4.3–§4.4 +# +# Role hierarchy and permissions: +# +# ┌──────────────────┬────────────────────────────────────────────────────┐ +# │ Role │ Permitted operations │ +# ├──────────────────┼────────────────────────────────────────────────────┤ +# │ Operator │ Encrypt, Decrypt, Sign, Verify, MAC, Hash, │ +# │ (default) │ GetAttributes, Locate, Validate │ +# ├──────────────────┼────────────────────────────────────────────────────┤ +# │ CryptoOfficer │ Create, Import, Destroy, Revoke, Activate, │ +# │ │ Get, Export, CreateKeyPair, CreateSplitKey, │ +# │ │ JoinSplitKey, Certify, GrantAccess, RevokeAccess; │ +# │ │ bypass per-object ownership check │ +# └──────────────────┴────────────────────────────────────────────────────┘ +# +# Unenrolled users default to Operator (fail-secure minimum privilege). +# There is no `operator_users` config key — it is the implicit default. +# ───────────────────────────────────────────────────────────────────────────── + +default_username = "admin" + +[roles] +# ── CryptoOfficer role ──────────────────────────────────────────────────────── +# Key-management engineers: create, rotate, revoke keys. Ownership bypass on all objects. +# Certs: test_data/certificates/client_server/crypto-officer-1/crypto-officer-1.example.com.{key,crt} +# test_data/certificates/client_server/crypto-officer-2/crypto-officer-2.example.com.{key,crt} +crypto_officer_users = [ + "user.client@acme.com", + "owner.client@acme.com",] -default_username = "tech@cosmian.com" -kms_public_url = "https://localhost:10004" +# Require a split-key ceremony (XOR n-of-n) to activate the Crypto Officer role. +# All ceremony activation records are AES-256-GCM encrypted with keys derived +# from `ceremony_secret`. Generate one with: openssl rand -hex 32 +crypto_officer_require_ceremony = true +ceremony_secret = "deadbeefcafebabe0102030405060708090a0b0c0d0e0f10deadbeefcafebabe" [http] -port = 10004 +port = 9998 hostname = "0.0.0.0" [db] database_type = "sqlite" -sqlite_path = "/tmp/kms_test_ceremony" -clear_database = true +sqlite_path = "/tmp/kms-rbac-full" +clear_database = false [workspace] -root_data_path = "/tmp/kms_test_workspace_ceremony" +root_data_path = "/tmp/kms-rbac-full-workspace" tmp_path = "/tmp" [tls] @@ -27,11 +56,8 @@ tls_cert_file = "test_data/certificates/client_server/server/kmserver.acme.com.c tls_key_file = "test_data/certificates/client_server/server/kmserver.acme.com.key" clients_ca_cert_file = "test_data/certificates/client_server/ca/ca.crt" -[roles] -crypto_officer_users = [ - "user.client@acme.com", - "owner.client@acme.com", - "co3.client@acme.com", -] -crypto_officer_require_ceremony = true -ceremony_secret = "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20" +[logging] +rust_log = "info,cosmian_kms=info" + +[ui_config] +ui_index_html_folder = "ui/dist" diff --git a/configs/server/rbac/full_rbac.toml b/configs/server/rbac/full_rbac.toml deleted file mode 100644 index e1e6a17..0000000 --- a/configs/server/rbac/full_rbac.toml +++ /dev/null @@ -1,58 +0,0 @@ -# ── Full RBAC — production reference configuration ────────────────────────── -# -# Role model: CryptoOfficer + Operator -# FIPS 140-3 §7.4 / NIST SP 800-57 Part 2 Rev 1 §4.3–§4.4 -# -# Role hierarchy and permissions: -# -# ┌──────────────────┬────────────────────────────────────────────────────┐ -# │ Role │ Permitted operations │ -# ├──────────────────┼────────────────────────────────────────────────────┤ -# │ Operator │ Encrypt, Decrypt, Sign, Verify, MAC, Hash, │ -# │ (default) │ GetAttributes, Locate, Validate │ -# ├──────────────────┼────────────────────────────────────────────────────┤ -# │ CryptoOfficer │ Create, Import, Destroy, Revoke, Activate, │ -# │ │ Get, Export, CreateKeyPair, CreateSplitKey, │ -# │ │ JoinSplitKey, Certify, GrantAccess, RevokeAccess; │ -# │ │ bypass per-object ownership check │ -# └──────────────────┴────────────────────────────────────────────────────┘ -# -# Unenrolled users default to Operator (fail-secure minimum privilege). -# There is no `operator_users` config key — it is the implicit default. -# ───────────────────────────────────────────────────────────────────────────── - -default_username = "admin" - -[roles] -# ── CryptoOfficer role ──────────────────────────────────────────────────────── -# Key-management engineers: create, rotate, revoke keys. Ownership bypass on all objects. -# Certs: test_data/certificates/client_server/crypto-officer-1/crypto-officer-1.example.com.{key,crt} -# test_data/certificates/client_server/crypto-officer-2/crypto-officer-2.example.com.{key,crt} -crypto_officer_users = [ - "crypto-officer-1@example.com", - "crypto-officer-2@example.com", -] - -[http] -port = 9998 -hostname = "0.0.0.0" - -[db] -database_type = "sqlite" -sqlite_path = "/tmp/kms-rbac-full" -clear_database = false - -[workspace] -root_data_path = "/tmp/kms-rbac-full-workspace" -tmp_path = "/tmp" - -[tls] -tls_cert_file = "test_data/certificates/client_server/server/kmserver.acme.com.crt" -tls_key_file = "test_data/certificates/client_server/server/kmserver.acme.com.key" -clients_ca_cert_file = "test_data/certificates/client_server/ca/ca.crt" - -[logging] -rust_log = "info,cosmian_kms=info" - -[ui_config] -ui_index_html_folder = "ui/dist" diff --git a/configs/server/tls_auth_fips.toml b/configs/server/tls/fips.toml similarity index 100% rename from configs/server/tls_auth_fips.toml rename to configs/server/tls/fips.toml diff --git a/configs/server/tls_auth_non_fips.toml b/configs/server/tls/non_fips.toml similarity index 100% rename from configs/server/tls_auth_non_fips.toml rename to configs/server/tls/non_fips.toml diff --git a/configs/server/tls13_auth_fips.toml b/configs/server/tls/tls13_fips.toml similarity index 100% rename from configs/server/tls13_auth_fips.toml rename to configs/server/tls/tls13_fips.toml diff --git a/configs/server/tls13_auth_non_fips.toml b/configs/server/tls/tls13_non_fips.toml similarity index 100% rename from configs/server/tls13_auth_non_fips.toml rename to configs/server/tls/tls13_non_fips.toml diff --git a/vectors/access_control/crypto_officer_role_allowed_ops/manifest.toml b/vectors/access_control/crypto_officer_role_allowed_ops/manifest.toml index 657df16..e7b331e 100644 --- a/vectors/access_control/crypto_officer_role_allowed_ops/manifest.toml +++ b/vectors/access_control/crypto_officer_role_allowed_ops/manifest.toml @@ -1,6 +1,6 @@ name = "Access Control: CryptoOfficer allowed lifecycle operations" description = "CryptoOfficer can perform lifecycle operations: Create, Locate, GetAttributes, Destroy." -server_config = "test_data/configs/server/test/cert_auth_operator_and_crypto_officer.toml" +server_config = "test_data/configs/server/auth/cert_roles.toml" [identities.owner] client_cert = "test_data/certificates/client_server/owner/owner.client.acme.com.crt" diff --git a/vectors/access_control/grant_access_aes/manifest.toml b/vectors/access_control/grant_access_aes/manifest.toml index 6204b63..f722bf7 100644 --- a/vectors/access_control/grant_access_aes/manifest.toml +++ b/vectors/access_control/grant_access_aes/manifest.toml @@ -1,6 +1,6 @@ name = "Access Control: grant Get/Encrypt/Decrypt to user" description = "Owner creates AES key, grants user access, user can Get/Encrypt/Decrypt, owner destroys key" -server_config = "test_data/configs/server/cert_auth.toml" +server_config = "test_data/configs/server/auth/cert.toml" [identities.owner] client_cert = "test_data/certificates/client_server/owner/owner.client.acme.com.crt" diff --git a/vectors/access_control/grant_partial_permissions/manifest.toml b/vectors/access_control/grant_partial_permissions/manifest.toml index 47c3446..360861a 100644 --- a/vectors/access_control/grant_partial_permissions/manifest.toml +++ b/vectors/access_control/grant_partial_permissions/manifest.toml @@ -1,6 +1,6 @@ name = "Access Control: grant partial permissions" description = "Owner grants only Get; user Get succeeds and Encrypt is denied" -server_config = "test_data/configs/server/cert_auth.toml" +server_config = "test_data/configs/server/auth/cert.toml" [identities.owner] client_cert = "test_data/certificates/client_server/owner/owner.client.acme.com.crt" diff --git a/vectors/access_control/operator_role_blocked_lifecycle/manifest.toml b/vectors/access_control/operator_role_blocked_lifecycle/manifest.toml index 0f9998b..8fb9016 100644 --- a/vectors/access_control/operator_role_blocked_lifecycle/manifest.toml +++ b/vectors/access_control/operator_role_blocked_lifecycle/manifest.toml @@ -1,6 +1,6 @@ name = "Access Control: Operator blocked from lifecycle operations" description = "Operator role cannot perform lifecycle operations (Create, CreateKeyPair) without explicit Create grant." -server_config = "test_data/configs/server/test/cert_auth_operator_and_crypto_officer.toml" +server_config = "test_data/configs/server/auth/cert_roles.toml" [identities.owner] client_cert = "test_data/certificates/client_server/owner/owner.client.acme.com.crt" diff --git a/vectors/access_control/owner_full_permissions/manifest.toml b/vectors/access_control/owner_full_permissions/manifest.toml index fa13ef6..6704b3d 100644 --- a/vectors/access_control/owner_full_permissions/manifest.toml +++ b/vectors/access_control/owner_full_permissions/manifest.toml @@ -1,6 +1,6 @@ name = "Access Control: owner full permissions" description = "Owner performs Get/Encrypt/Decrypt/Revoke/Destroy without grants" -server_config = "test_data/configs/server/cert_auth.toml" +server_config = "test_data/configs/server/auth/cert.toml" [identities.owner] client_cert = "test_data/certificates/client_server/owner/owner.client.acme.com.crt" diff --git a/vectors/access_control/privilege_escalation_activate_without_permission/manifest.toml b/vectors/access_control/privilege_escalation_activate_without_permission/manifest.toml index 3539e17..aedd8da 100644 --- a/vectors/access_control/privilege_escalation_activate_without_permission/manifest.toml +++ b/vectors/access_control/privilege_escalation_activate_without_permission/manifest.toml @@ -1,6 +1,6 @@ name = "Access Control: non-owner cannot Activate without explicit grant" description = "Owner creates a PreActive AES key, grants user only Encrypt. User's Activate attempt is denied because Encrypt grant does not imply Activate permission." -server_config = "test_data/configs/server/cert_auth.toml" +server_config = "test_data/configs/server/auth/cert.toml" [identities.owner] client_cert = "test_data/certificates/client_server/owner/owner.client.acme.com.crt" diff --git a/vectors/access_control/privilege_escalation_destroy_without_permission/manifest.toml b/vectors/access_control/privilege_escalation_destroy_without_permission/manifest.toml index 165cde3..013a2b9 100644 --- a/vectors/access_control/privilege_escalation_destroy_without_permission/manifest.toml +++ b/vectors/access_control/privilege_escalation_destroy_without_permission/manifest.toml @@ -1,6 +1,6 @@ name = "Access Control: user cannot destroy without permission" description = "Owner creates AES key, grants user only Get. Get acts as wildcard for crypto ops but NOT for Destroy — user's Destroy attempt is denied." -server_config = "test_data/configs/server/cert_auth.toml" +server_config = "test_data/configs/server/auth/cert.toml" [identities.owner] client_cert = "test_data/certificates/client_server/owner/owner.client.acme.com.crt" diff --git a/vectors/access_control/privilege_escalation_non_owner_grant/manifest.toml b/vectors/access_control/privilege_escalation_non_owner_grant/manifest.toml index df0d026..abd29a1 100644 --- a/vectors/access_control/privilege_escalation_non_owner_grant/manifest.toml +++ b/vectors/access_control/privilege_escalation_non_owner_grant/manifest.toml @@ -1,6 +1,6 @@ name = "Access Control: non-owner cannot grant access" description = "Owner creates AES key, user (non-owner) attempts to grant themselves access — must be denied because user does not own the key" -server_config = "test_data/configs/server/cert_auth.toml" +server_config = "test_data/configs/server/auth/cert.toml" [identities.owner] client_cert = "test_data/certificates/client_server/owner/owner.client.acme.com.crt" diff --git a/vectors/access_control/privilege_escalation_rekey_without_permission/manifest.toml b/vectors/access_control/privilege_escalation_rekey_without_permission/manifest.toml index 0562c8c..6f98cf0 100644 --- a/vectors/access_control/privilege_escalation_rekey_without_permission/manifest.toml +++ b/vectors/access_control/privilege_escalation_rekey_without_permission/manifest.toml @@ -1,6 +1,6 @@ name = "Access Control: non-owner cannot ReKey without explicit grant" description = "Owner creates AES key, grants user only Get. User's ReKey attempt is denied because Get wildcard does NOT apply to lifecycle-mutating operations like ReKey." -server_config = "test_data/configs/server/cert_auth.toml" +server_config = "test_data/configs/server/auth/cert.toml" [identities.owner] client_cert = "test_data/certificates/client_server/owner/owner.client.acme.com.crt" diff --git a/vectors/access_control/privilege_escalation_self_grant/manifest.toml b/vectors/access_control/privilege_escalation_self_grant/manifest.toml index b3d5195..b694e73 100644 --- a/vectors/access_control/privilege_escalation_self_grant/manifest.toml +++ b/vectors/access_control/privilege_escalation_self_grant/manifest.toml @@ -1,6 +1,6 @@ name = "Access Control: owner cannot self-grant" description = "Owner creates AES key, then attempts to grant themselves additional permissions — which must be denied" -server_config = "test_data/configs/server/cert_auth.toml" +server_config = "test_data/configs/server/auth/cert.toml" [identities.owner] client_cert = "test_data/certificates/client_server/owner/owner.client.acme.com.crt" diff --git a/vectors/access_control/revoke_access/manifest.toml b/vectors/access_control/revoke_access/manifest.toml index d3624a5..168cc07 100644 --- a/vectors/access_control/revoke_access/manifest.toml +++ b/vectors/access_control/revoke_access/manifest.toml @@ -1,6 +1,6 @@ name = "Access Control: revoke granted access" description = "Owner grants user Get, revokes it, user can no longer Get" -server_config = "test_data/configs/server/cert_auth.toml" +server_config = "test_data/configs/server/auth/cert.toml" [identities.owner] client_cert = "test_data/certificates/client_server/owner/owner.client.acme.com.crt" diff --git a/vectors/access_control/unauthorized_access/manifest.toml b/vectors/access_control/unauthorized_access/manifest.toml index 55df061..f18a174 100644 --- a/vectors/access_control/unauthorized_access/manifest.toml +++ b/vectors/access_control/unauthorized_access/manifest.toml @@ -1,6 +1,6 @@ name = "Access Control: unauthorized user denied" description = "Owner creates AES key and ungranted user cannot Get it" -server_config = "test_data/configs/server/cert_auth.toml" +server_config = "test_data/configs/server/auth/cert.toml" [identities.owner] client_cert = "test_data/certificates/client_server/owner/owner.client.acme.com.crt" diff --git a/vectors/tls/mtls/manifest.toml b/vectors/tls/mtls/manifest.toml index 0f5c2da..e1f6c72 100644 --- a/vectors/tls/mtls/manifest.toml +++ b/vectors/tls/mtls/manifest.toml @@ -1,6 +1,6 @@ name = "Transport: HTTPS mTLS (mutual TLS with client certificate)" description = "Verifies the KMS can be reached over HTTPS with mutual TLS (client certificate required)" -server_config = "test_data/configs/server/cert_auth.toml" +server_config = "test_data/configs/server/auth/cert.toml" [[steps]] operation = "Create" diff --git a/vectors/tls/server_tls/manifest.toml b/vectors/tls/server_tls/manifest.toml index 54a62d5..794ee6f 100644 --- a/vectors/tls/server_tls/manifest.toml +++ b/vectors/tls/server_tls/manifest.toml @@ -1,6 +1,6 @@ name = "Transport: HTTPS (server-TLS, no client cert)" description = "Verifies the KMS can be reached over HTTPS with server-TLS only (self-signed cert, no mTLS)" -server_config = "test_data/configs/server/auth_https.toml" +server_config = "test_data/configs/server/auth/tls.toml" [[steps]] operation = "Create" From 9923042ca27475d0e1da7341af1d7d5deb6d2550 Mon Sep 17 00:00:00 2001 From: Manuthor Date: Tue, 11 Aug 2026 13:44:35 +0200 Subject: [PATCH 03/17] feat: add co3 --- .../client_server/co3/co3.client.acme.com.crt | 21 ------------------ .../client_server/co3/co3.client.acme.com.p12 | Bin 0 -> 3643 bytes certificates/client_server/generate_certs.sh | 15 +++++++++++++ configs/server/rbac/crypto_officers.toml | 3 ++- 4 files changed, 17 insertions(+), 22 deletions(-) create mode 100644 certificates/client_server/co3/co3.client.acme.com.p12 diff --git a/certificates/client_server/co3/co3.client.acme.com.crt b/certificates/client_server/co3/co3.client.acme.com.crt index f29ed7d..e69de29 100644 --- a/certificates/client_server/co3/co3.client.acme.com.crt +++ b/certificates/client_server/co3/co3.client.acme.com.crt @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDhjCCAm6gAwIBAgIUZAvwiy0sj8QfwjAO8F96mJQZAF8wDQYJKoZIhvcNAQEL -BQAwWjELMAkGA1UEBhMCRlIxDDAKBgNVBAgMA0lkRjEOMAwGA1UEBwwFUGFyaXMx -ETAPBgNVBAoMCEFjbWVUZXN0MRowGAYDVQQDDBFBY21lIFRlc3QgUm9vdCBDQTAe -Fw0yNjA4MTMxMTU0NTBaFw0zNjA4MTAxMTU0NTBaMFwxCzAJBgNVBAYTAkZSMQww -CgYDVQQIDANJZEYxDjAMBgNVBAcMBVBhcmlzMREwDwYDVQQKDAhBY21lVGVzdDEc -MBoGA1UEAwwTY28zLmNsaWVudEBhY21lLmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBALl3txZ9MNI7mJ5Y6UkvjPlKOiA92NJZSsuvwD6ZzfHXC0EP -ktunfCw/GLZmrUJzrO5LTm5pNT7vUf2TkpWEvgC8t+bMjXOrl6qpFEUS0JVvrAMB -PeXeU25Ft0DCFXawE1g/a0+CefpGJ6ee8YH5Ne0CwkOecmfW4/ou4dEjnVzX5N7O -1O3ElODaRipKyt1l8MG6PuEnNQaCA33RrRX9lnCJVd9DVZa/xou+OYPFTLlDlNN7 -sHPUUsN26TSZp0dcyBax3ws1ZUDGbKtVngC85+JWkLi9t4+NeA+wVhiJfiP3I2ko -4trF9Q7s/20HjG6hwwt0JdUpGZG3n50w6lh6bEsCAwEAAaNCMEAwHQYDVR0OBBYE -FPDF8CLmuTR3/P38UVZndL035uWlMB8GA1UdIwQYMBaAFCrsQdVEgrwm8ED+4pCs -lsN3SX3/MA0GCSqGSIb3DQEBCwUAA4IBAQB5ZxqHo5SucStiqn8Z4x0XEWntliTE -s3QEIUl01icjoRdZfFrhLxS7sKp0wbOvxfzsIl3lHAXxxcPW6rQdKcuWwNYY6cOY -B9dfTJbVgGGaAg1vczqCFQ55XOqYkzua7UeZWPUTTpUPI9cD4pEHr25yGgcLs3Jt -yGWkyP3iZEVXHSYCRkDKUMugyC5R29aCjoCyA1sbA2A7PkHz9RB61S7srhYl64Ui -/lC30jOkAH4Rvl8IylISJzfut1WVM9U5qfclCYXCfSDm/BF+68aRePw06ICE0A4+ -RayVj57mkBTePSpcS2c05eFVOyyrRWul1whq0uy9dJN6dWa3m7gh5q8C ------END CERTIFICATE----- diff --git a/certificates/client_server/co3/co3.client.acme.com.p12 b/certificates/client_server/co3/co3.client.acme.com.p12 new file mode 100644 index 0000000000000000000000000000000000000000..c5f305a873c44cc978a0ee26c187acea99c347b4 GIT binary patch literal 3643 zcmai%WmFRk`^LAi(W8+Na7Yi30U{~g&FB&kK}te$goHQ{3F&(1k}e6s2~yG^0;6N3 z)OZ-BBD}um|3A<3@%?b0`&`%c;r@1=-;Ic(k^}*X5K+)AVlp^d6MX>%k^l>%ptIm8 z=+qy391%q-^KV347)2`h$KnG40e=?k-vo%T`$s^a2qZ%EUrCAJ2Gcaat@rYk=5D=7 zx>vz4{5J{&A_X8QiAn$ODu@gMK(G^&*`qaqPDEfJ985zmNOopi+D#r2;NsTLRni*~ zMbe;-HtYSsIFKSpqlB=Uqcngz>ld}aO_yR}p^!Wfq#g@x19UlFQ43ll9~(3SJu3=M zbdpJmhDE8qJ{ed4w4(DRR$=me?Yk8$UOkq0LwWKqm>$$t=@k7%E7I5kBzB>9EUiMH zv1{9I^OG)-7@At8^irN4&=@2=vKc^JklTtfl@*4p-zb0i-pZ?UVWnt1flOsksNWv7 zCZE)QZB}AdP4e_|#QCZjyrki+PteSCn@yK)CrF%Bx6cz7wQE}+uJJIW`Iq-D?fZSBP~IT@TKl3{ zUatX8m+avdqL)U1dj#q?@7%_m%xcLCPVoa;t*hN>I@>!!TTo@P)Z-FL5o0OQ3^#T@ zNvvw)Yio90z`@t&*OR+KJOvtMJ)e1?2SX%_r+fA7Bd0*|j+$q&p=lnBksG3utXw^3^HB*WMJ= z;$m0bqS42}GTdTp`G!1foLNjal+$&JUwK@pLgEAb!!#30EN9LMZJNesx1ha8#V-hn zF`YF^+-jZio*E5{aqyUmSGw0Trom3uTGMP9!pK^na9SvQcexLdixvPl5amOhy??+B zq-~>aMXNH`P~H`&}H`)bCv^K(zjt$75OO@ zA%nE@3(ph_;b`M8B04<%7~zP$!szY6#jV6N=q2NYo{GN1!6r83^BVa?@~dLTgYuZ5z4{; z`pnCu<4MdsIjn7xDVlGLISV&%hNE4>wC52yIn5`BH_wm+x+G>oj3gb3e`X{T<9a%V z?jRIAHc1-skoLCU9`v_hrX*V+`Rxg0QOQsCx2Gu@av?5b_xMM^O7zfPJE zZWD4%{BBkI_am`ubu5eNUI*=P-y(g&Wy?VtncoJ!-lrU+UR(08|(=Lcon@u7hYpOO856wh7$tl6GlH)a-Mm^im}exC zsPr?s>XBrl^bA%Et5_2ol&l`H>)9F@Fdx@x3I*2r$lq}#p1cN~e8%&kB-83r)KnIH zDN3Tk=H0)xUgk@L8xMY(!K$<~n8FM|>Dm{BP|whs`*^Pg`CBJalu}maPdWkVExjLt zA{50=S9A;%F+bLKBpw3IiH8|r7na%|8MB2s^w0>>RglW5K$Qn>ozExDhqB>!l-p;&EsET!Eu4X{+G1QxrC}i?OALhwm|vBDP``mw2~8N*4T6H1tKc0uVQS##4@g)-`=NG zZPe?lt=)-776T@#8e%_8h4_LW03B^{j){?ZCoEXE&8T0i!jUGFMjGPMpUcXqc+X*u z!7J^r2EtS@N-MWyOgZNswEJz}Z<{yGa5xDIu&s2ZKar2{mE&+2b!$r5c|u-474rh_ z9?57wL+w!wcqD6QTGT?mUPLe)Xgvv_Wq0I{ti<@J3kp4%LS>#@DETal$-gL6@h*gC z?UQ6&Qr_D1GanlBlW4VwjrT5Ii}6sIeN7EM^WPZBGDwkLu3yYR=^A^)_+xu=;7N;h zKethKZ5i0a`Cr zXurqYK*&pV&FG(5CWxpT1O*8KmU`l27+b&ccR-4dJB)62ZsvLEZBHbLxZRm~8;b;4 z8Zhx$Vt0i{K2Z>A5Ul?1jrj&!4QOm8hU4D2${BJd#m;t-arT=>KTTE);L^6z30O&f zR*&l@t4Z=JE_UDn8Ci%CSSaYIm+O|lb0iOVly|=DPtnmI)F;@0StRpv*Gz0I+_bY~ zWrnC<@qh|&C|PyH8DlMZWaSb%?tTSTwBycwh5>X7^Z32o13%mKK|HI^-i{A0Emx|q zp32lPv28=qXGhqpI>_%~Z?RFO%duG@`1$4QtWPaE=EFN`GD)>nzzx$NNeooZfpWUm*ejReoR3JBC z)s5qmf6Q19@`+=%N$NGU*(z-&hp_rm99u4H4m^r^7SZxDiz`zAiim>5|HA?Q1q(lL{fk6!Fg+*>Gs12nqHgmbR5yovzLKM)$B{E_>*Fnyb6LLAL64>r zaQ&vzDD8mKxx%4bvH%uK7xtYETNsqq-;*&GzRRt%QM4(7-@dJ^=oSMk7d`f&_~A2= z!_WG@vmO?6nSv*7tQvf4RBv3+xz1;HE$lnbQnQ}cC*(D|xxI-pU(E3}`LvAr!cuun zbcSkD(0fvK_B`O>>9|UqRu46v^W-;c-p3vlfB5(>cE=eeKASLM%O0AMUT|UQX9j2K z1N_e8QM!kM9Tqc`k4*O};=03Z!Bu_w%$#xd!_*=u(Rs1Mnw-flow!)>A&qw66kCuiQ*O}GxnE47 zTzFQwnOs4=xKd0Kk%O{$*6B)|Cf&yLxbqd|Whe|?JN1n8b@!Hr=leKQ7M4~@OQu{c zv`u}S;ijksdervHJfCRZHx_2K6?U{WW~Mu-u>6+9>Jkw&m~kn%CViM&3x23NC}aHT ziX)n;eY}4E{O;1x%#k7}c9D{={8_=3*=Umn@_UoXn>s8^A=_a4E0p0_e_^6b-Qcb2 z=FsJ)aDL~=d5trB3fr|NLgMYG)8C^6__TMht%Ht= zAo(Uz8|a!|Y+u{p+DFaSj=Vd4JC)h-gAlvbJb6s`>tN5F5*F~LfZ3U!oP|2=WHLsA zkM)Pd6b(7U*51$f?IsGo9JC5}g@bW4&gLcMqTu<|ZQ?r&L8@aehA#S2MuVL0-W zMsVqz$x2(n6tT22$(owXmi)51OLDA7v-ZEbM-Ww|+h^2A*HZ353V6*RwN_{8v=CLd zLAop1k;Q@5Z2SR&2PGopL-GCk!&z(_XFBT~^g(>iIib;xwf<1Fmwg9!e|kPMfrB?O z{e=`hHdx_cVIS=NP9T7T&}cNfuVbHbor%FnU3Jg=BD>O%c<5yA*G6qbVfURj<&Xk` zX%yg-e7}>8)7W(C4D}x_$&<=!|2Sd>|N5KkJnL^7?3v>--A;8m-><|Ur&|JKZ;mWJ z9v*&vJ8ddI8wfC)XS-6I12Ptr;HVMeU)Olj)6oT#{-QNBj-+yeZH^EYO~fnw+RIM! z=QX^vO*y9ronDfcIbU}5e`~A7HR;=1BV{~7u({r;Iw+y5d@FLTvgW^(kmpw{%lyYe z%VDU^oHs3Bvi5~1r;N8UeG-ThKQlkLOH06;Ubl2iIVRj*zJn1-R-Sx%#HN{{!kcBp zeTOn|t#WQx(X1YT^uSLyX0qh=5vh6k?~`KO&Z)5kKK5)7$i-%*tbV0ePmfR8uh{=M z^_9}IV0|Xgd#Y{}`_gTirsIRXO)txOXh$ik$z31#DXQF_M_lC^n+uB Date: Wed, 12 Aug 2026 10:47:01 +0200 Subject: [PATCH 04/17] chore: update co3 p12 pass --- .../client_server/co3/co3.client.acme.com.p12 | Bin 3643 -> 3643 bytes certificates/client_server/generate_certs.sh | 144 ++++++++++-------- 2 files changed, 77 insertions(+), 67 deletions(-) diff --git a/certificates/client_server/co3/co3.client.acme.com.p12 b/certificates/client_server/co3/co3.client.acme.com.p12 index c5f305a873c44cc978a0ee26c187acea99c347b4..9895cfed34d54848dd717bfa52738c2b21154a95 100644 GIT binary patch delta 3393 zcmV-H4ZiZb9J?HlYY?mFDWgGDe1^6f!kHl(fiIDJA%6&*XyM!)hnpJIRhx2`oHc-g z2e2fhwmy!eH}iQDj!`BtA5CLv2clRO)Y|YkY>Bd$ovO{7_eR|E$9ShfRZ<27%Be4s znd*B>;io+D6IP7eCgwjYnrg3w+lWDbgRV2IkPOu60Oe8ly%^mGcog{IQ4>YZ<;4^p z0K2|6{eL?~*KE?T?$_NtH${@)1vd&UYJae*l|QEttgC4%;7cLUv}&FK7_@Knyk&juGsUYHK3R>zv_7&{(QSzfH~eBE;6 z_St+SO1>EO0qk0Lsp+jhR>!D|n zJ`mv~Not=j(J82sr>4=ccou`zp-W!s>GreqI%(+67Dqqp+nBMClzR#gkQWMzfZ6%K zMwa}7Q6d^kr>zAAT^rUByw-lvpMsLkt55A}B6quXv_%3_ob=HO@m5YN6}!^Anf={)9T8nfwOGjek|{DW+RO6s=xj2 zowzSw+04wi>ISBVrU=lu*_Bh5CN##FHgT#5ZO%BXcyjt`CO$*L-fILy;nYo9J+uNf z(T^THd3d&54wfEQ%AUZh;WI#b7V0N@R(~mN;S{cLZTc$%J>bU>ww5JsbTToFuLR=; zcF3;O7|zC3@0`~i$JY1U43Y%@OCZoFirVRA11Tr67fCnav-PI`&L4j^z&irdVx${! z&8>Q9Vk7^|K+-}H;VA7IK%1-~QkDHUj=0nNxVwqpDJBElQ?0E71Vk7ORo+nRlz+nn zd6Cs$NX|RFYF0@QE!&~zZb`j~Nj*=x}gA-UlhwgIXGaXuH` zVVAd)tKu3xB>tHJv!IANd1}Q3a^{RyUu1IT6-8F z;LW*EqPN3Akb(o3{MyjY$8A1*N`EMlhiFuwN&8HXJ^v1C9QZGfRYv;(D9I+)WpI^) z>6((J<&0<|MZz2!x)Oo3Ub;)R-{hAWwtwcncUFK8 zGFbh?#;o{DXF5=sqSAI%1twt?`HCtqLA@jk98Cj!37h$}5{Vm0uMFFj6PDS$ znkPG|9uY7wX`35m#4Vpt5p|oTq+a`pC{hvBt?liP&N-#a#|HEvnD3Zpw52Tmj#>k% znOFB-X_<(*2&ACFKLA%k#>W%^USup|Hf02@vi+dVClay9xA zSc=`^ne)HJL&oIS36VW5w=BV{KGpo~G1R75#toj2)vU3Y_rKJ5RA+6uYXJlVHS6!1`1w3@~u^& zu!T^)=AraiqP#rZt&%c97y2M$+ZNr97ok5uV!xHNebvl$*qrhocF~%aM3QAY*s1i* zuLq?b&tP&s%tM21Y)AGlaHjtEA`ac~uW`FxVhe7|pQbEIvu#+jw{V`JX|Z564awWbcc+L8{h87}^I8 zkt8{$eknMT+)5;e=)r9UrEy;nekxNy7QdiQ*5xTXEn=mjf-om_X})~Br)>1db5Lox zV&b>Ir+0{EjH~epS9zPJ7sI#h7dToqsXF0P&~myWEaeJj>*X$LNfF zzKd;E%f^ywc{M8tFb8b;M<%T;HDh`5ZRc3mFr2(SHdpjc_OU``>NDar#$}iXxv-NG zS7-#7w`U=Mz%fN!6z+0xO6(#|WI_=h!1AR$Y*g7DP6}WB-59q_gfQ9$9&m2?+rT}# z242=*@_)MtQb%38N+pYKdHpLL7a{P|oEDXQ6iiO!9$+gG$M#o~Xr>n9FV74?1|eVg z6F*s~qYGfNJ+H6OmTU2r{k2oD1xnR0pcAaZ(~G2RAqzMyy}wCmQyCyiR3QUvXaD_XA@th}2QaOV-c zK;fhd)oH(6)S>**W9Lj*Cm!AjIKDo1OOI*0u7A%Bfh1OlV)v2z#ctvSd@tO}#mk|C zDuSl*&oCK)W)!!ES3P9hBry~4MM&Dx4j@EYn>fMRgX}Js?OHw;mkqzzmm!RQxwclr zcG`00!)YA7Q-(nQL-l#Iudt>MeBB1!D=%uJ7o1B^%70^ZF}+ZQ0jgDI4d+GuvzGdm z2l){5l{Zed*%{fOsoF+8DvwY{zW2}rXEI~%i(Ac))r>!SUt+0VE}|UQfTa~{kTEbJ)q$RxkkSA(eynOT;4EPE?GqM z>bm%3FhI@v+?SQ2H1q|07O~2=CPH;ZBY!7R^GKDWRx_B4GboE`bPsRQTT!?-)*zZ4 z!LvSdsw4C$*fm%!`Buv7#V+#EzV`t>UX>?pN{w^hhQ@e5Bv6ThYTNn(BRuYA3~LU_ zoh)NxbHw~ulhbE&Wg@PJ0Yx1vwLyFL&utQimli8BA#AFdBzUbPv6#tmm2JLrntuup zVqb}lNcEd|$*YS7WZY{vl9B2_TvPcY?Vh(lNHMS*hkWBea*fAJ0sUZt*`8tR86Pcf z;0E&R&YaxAzvcFT+j^O!1ixXe>@(aBRl4hpeI$?;FHxu*5#qjVwnYt()Hj(mGBW!o z#5(0~KdKdXU#Hvs82-D{@?~d}n#|rT~RQA}l zdlPq_Ib6PoXcnc?U`oY=vk8ddolduu1zBe;oc|(7^9oj}#*y}1{-KDq$A7a)G62xL zHnyL}pTvJGQ5NOua8s*LGDohbZqq%a_DuSQa>cwXfqr^p9F10z#2#VeegcUnx?k^* zep@c<99sp*CJo{dUQX|U_;n>oDfDf>=GnKGOcStJb6Xr~W?N5&3LYIO_E}*hr_-MZ z<@1j0!8qsQ5;jYDL4%**Tz@wE#T!9V*E53f{fLtSK30U#FL>nZozwU4JbW_oH7YE# zgC7WRUlbCWKo$d;xeSu4$9sgbl8E7)vd@m#PwHlFRhQ9`rV&?`Ypy zq^@!2QPqbsYD8l5)f)=K}I3{vqXIyBTB~@K}x0)1@EAWye&+>kwqsaO5Lwed>Y@k$jV1_0}4+V?s zjir{~p)VW@u5d-0h&N6#-N!Jg5lIY6)fC0!OdfZe)m;D9e7S4oq9&j5x&#t2B`_lf z2`Yw2hW8Bt2^BFG1e4nhDmLLqEQd!#*!-#K`>g_3{MSaPNB6bRosXmdzTQj6h(FM@t*GAD??#9Q<3iA!AWaQjLG*^lNr6*X0y6ha@Hs;h zmgrr$Tz~o!js^{rszHN369BPzIm_gB2A`X@c~v_t1>yff$LY2vOZQqpQ_oV96hE)# z%v%c3PKj2dR;veR_{(Pevjp5na8C0`npg>}#h7Uu*O&r0OMyFb7w+MqdJrh{&Vf|@ zQOhdUesyXO;!W+G+6JS6V$)o|OuGUz2u#Fo;(vnZU9W#fJ$tkJaOe@V>rs3rcuuWg zp@=4L#vy2#%C!T7Q2;LU4~wX3&1F`t3ZL}v#!0r{z?4ho=Pcz7LJ5=bqYN)oI5C!L z9Va)}M6rom9ZUf1(Q^LK=qx3lN2S8SB@OJ!2ix@Ouei(f0W-R+ZHRo7Y!!jyG0+MX z&wpK6MD&Jyj!0W<+C!(Tb2|$Qhc92TZZ{wz{5ZuaiElvW;TMlm>H~H76Z2&H)PJr((IsdOdyIBVFR88 zW^nl`PdQzJ7KBBC6v1f^_-HWyqplUjhjVE5CQ>tx_eY?3IVfu>HWHb`q9LXLXn&RF zAP%RVlBavf5!u>pK%gpyQ^zk$CB?lhfa;Od=G+3BkYo0FH(H%*b|e*IhJ*7`p@d37 zQ3Uvav{{4#U%RBq=ep)#fKteoGR8c^5hJNBye?WvEh^|=N_x@)8t`{}Rw6!Vrwf~) zBva3P9w>?RZ&bQuhuI1sx6l`aCx6WtnBvCu;t}AzxSa!KW!aH`v(*Q8XpWZ!Q8!_t z`j(Lf;=;PgA22^`qsX^!byt?^zm{zlgsRqDDC%Ea3fjrg_flnHd>@2_#Dy+*C0J+=JJ zL$y4@%*x9y>{wf?HH~)vV|%NpddmJ4JL2Rj#_wDh%Q$-*-AfV#Fc8fv_`1vqsQQ2> zq=vKKuE?EHFOEU_gsNUQv41($*D}{YtcZA#Mu6yUws&^fin~<}0j_aAA87{A{sQvB z?!-vUGQDXMJZ z;U{sFaSX8if?xW8!%`fX6Q&q)7zSZw;!-rqgSEfAv?_tqZ_kA$8k$84E$xZn^}YAF z&vWvBk9Ec*2t(~8F(yz8^p8BIe|}%Bf?$`)zZ7GDNjsTt6#%n^-zQbj@8b0+BX`WT zlp4YodUaO0IF(zQ$bauvFA!5`7{F5aiq{h+D0o*BI6%nH1hpAAY^Ni$okKk>Hj@O?;@@10P_@t>J;#RT3{%-;U zqOYnIu)T;C-}W2p3`DU_#I3F8j8dWi&#JJ9xzc-c1U3O8P{s$g8Sbs zfm94pM>9FWrKJug_8?<=-nfOveM7xL-WEDlA=oy!bmla-*;STd@_lw)sc94PK7eyP z9%0OCvXbXy3a`?KekyB$6=2m5Y^wlXJ6~0ywF=>(^H9dN@^%p&V<>^Cy>Uh>EM(Go zm-6>PaosXLet)1taG)xf>j#$j3>M^cSINzEHMUrYjBujhhipUEi4O$zb>qvJPm(#^ zuiKV+OjB%!b=Sh0Dv#T)`sR7xFrmc}yu%%41zb#*dng!_rX)HH3lr@Bb-My8vj|{< zMf2|SD5H*3EWgeQ`2yjjKe#R!u=AahX_m#+-SaO-Pk(rLD*)WWjE5Dr(I~nFqVT#= z|L6bNCKFxqHv=SV7$&iyD;co8jF>VaEz775SA=JgKz1WaT1$4`k!`QyzzM96bfThRB?3`cGV>V=5)a+3-RT~Y#%!j$gT<0d zPRLI4Tz>=^H=Uc{R9skUN|-x3$q$7k0-xe}J4P_|6|D+^soaK&G^Y-PW9Kv!#)h>R z&MuqmbY0=d?;3IH=W*hcn>fU;4pmT&x8p!@=PhrmcytzoPp5!kzAE<*#3;!qkar=y zjr`<~@K?UfPj+A%9{Xw@-P$9kJ1s*AWd(K8OT@ z1kln6!cB@U2V1~I=EmZV-wBUPqUt%q#?lU)WM&{^x_h-BLR^tbZ+{NU*o3^xaZMc| zhEpCaA=ayO3Db@%OXKT^0Kqt_^#QhUJ9Rki+v<_Yc1IU@s?XhdK-P_o*-Ht(Sk1~c z0fFk8PJb*mY$D&qdogO+0(rn5Yo0-4H*D@^5w>FGd1j@aW$eO8WdY&;sZo^d3}&Zc zrZ=A;h!TD29M_*%_?il*BA=*5u~od4E^insZdX-Mz;dss01?d9puEWUBRY zc0KgYLyAel5APxJ{TiLT!b5c`&ifr>)fXpReJxqS5X-{^prpYRW;yKd=VHtfJu7TG zKBYnaL|&!#7}Uc&xPCur@ADT`q;$PRI)9jb zI)Am(cPj7k?j~jMzXdJhSlpu3dSoE5tgF%vkd3{{Wqm3cB}EGPioHxmDmff%musp9 zuI-sC-%b;kn$8{Vlc5a*_M&0nJXGisGcuy>5K4E_9C$kyn|R#zbcdikf0(9LJfE*K zL5Gh6VL~&Q^xld{664g)X8a8Jd?1Fc(tm9TiNNJYZnTP385y<=Sr?m0hFq_TP~pk;5qG^tpq(p$U0NL{2qi3xX!QZ z_9EQw)$Tw7h}#S&rfr}6R?M}uoKbcSF(G3*vPTJ33o7d|QiOKS1Awk^)}J{^<}k^qtafl2{s zm7-v+uxZ2(9Jo><)Eko+W4SCHTG$9~5{*&1g`5wrz|oIU~Iuo{!7ol_8Nq z<$4Q<5jv4bNElftJ51a>8t&F$`@qXEs-))i5AOarYj{8JNO(oIW;v95L~6(q{5#9r z#@X#HFD!8j>HfL(h_@k?8rTgsjGFfIv0oam?J>h0=i)Tf*_VwTCV!5VemL)l zdOz&i>jZ14DRv(7u~F9ROJI`zm%Y6>{J_=w3H(PjJ!MyPw+~SZU+EE&?F$9Hu_-4v zwC9=PN91L@e09pBoj9GiyvtT9(ftYbnDG$T^<&)<=c}eC`;Pha~e?rL&wtQuh2A-6-%Z-F zZF)`@0<;dmc^%vv5VG*>>f~=(uMN7Sd9rIx0ltE!U?np{{@0rzj4X^*{VAz2B`_lf z2`Yw2hW8Bt2^BFG1e4nhDmHRI^VP@fgW{S<;weJyAw_WP^-1QdXIhj)wc<-q(nSOi X0VItH+RZc*>xZZKR)+0v0s;sC?XrAK diff --git a/certificates/client_server/generate_certs.sh b/certificates/client_server/generate_certs.sh index 647c300..0e5bf1d 100755 --- a/certificates/client_server/generate_certs.sh +++ b/certificates/client_server/generate_certs.sh @@ -1,71 +1,81 @@ #!/bin/bash +set -euo pipefail -# on MacOS, you should pass a link to an actually installed openssl binary, and not use the default `libressl` -# which generates PKCS12 files with the deprecated RC2 algorithm +# On macOS, pass the path to a modern OpenSSL binary as the first argument. +# The system `libressl` generates PKCS12 files with the deprecated RC2 algorithm. OPENSSL_BIN=${1:-openssl} -# Generate CA private key -$OPENSSL_BIN genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out ca.key - -# Generate self-signed CA certificate -$OPENSSL_BIN req -new -x509 -days 3650 -key ca.key -subj "/C=FR/ST=IdF/L=Paris/O=AcmeTest/CN=Acme Test Root CA" -out ca.crt - - -## Server Cert - -# Generate private key for kmserver.acme.com -$OPENSSL_BIN genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out kmserver.acme.com.key - -# Generate certificate signing request for kmserver.acme.com -$OPENSSL_BIN req -new -key kmserver.acme.com.key -subj "/C=FR/ST=IdF/L=Paris/O=AcmeTest/CN=kmserver.acme.com" -out kmserver.acme.com.csr - -# Generate certificate for kmserver.acme.com signed by our own CA -$OPENSSL_BIN x509 -req -days 3650 -in kmserver.acme.com.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out kmserver.acme.com.crt - -# Generate a PKCS12 file -$OPENSSL_BIN pkcs12 -export -out kmserver.acme.com.p12 -inkey kmserver.acme.com.key -in kmserver.acme.com.crt -certfile ca.crt -password pass:password - - -## "owner" client cert - -# Generate private key for owner.client.acme.com -$OPENSSL_BIN genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out owner.client.acme.com.key - -# Generate certificate signing request for owner.client.acme.com -$OPENSSL_BIN req -new -key owner.client.acme.com.key -subj "/C=FR/ST=IdF/L=Paris/O=AcmeTest/CN=owner.client@acme.com" -out owner.client.acme.com.csr - -# Generate certificate for owner.client.acme.com signed by our own CA -$OPENSSL_BIN x509 -req -days 3650 -in owner.client.acme.com.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out owner.client.acme.com.crt - -# Generate a PKCS12 file -$OPENSSL_BIN pkcs12 -export -out owner.client.acme.com.p12 -inkey owner.client.acme.com.key -in owner.client.acme.com.crt -certfile ca.crt -password pass:password - - -## "user" client cert - -# Generate private key for user.client.acme.com -$OPENSSL_BIN genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out user.client.acme.com.key - -# Generate certificate signing request for user.client.acme.com -$OPENSSL_BIN req -new -key user.client.acme.com.key -subj "/C=FR/ST=IdF/L=Paris/O=AcmeTest/CN=user.client@acme.com" -out user.client.acme.com.csr - -# Generate certificate for user.client.acme.com signed by our own CA -$OPENSSL_BIN x509 -req -days 3650 -in user.client.acme.com.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out user.client.acme.com.crt - -# Generate a PKCS12 file -$OPENSSL_BIN pkcs12 -export -out user.client.acme.com.p12 -inkey user.client.acme.com.key -in user.client.acme.com.crt -certfile ca.crt -password pass:password - - -## "co3" client cert — third Crypto Officer for 3-of-3 ceremony - -# Generate private key for co3.client.acme.com -$OPENSSL_BIN genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out co3/co3.client.acme.com.key - -# Generate certificate signing request for co3.client.acme.com -$OPENSSL_BIN req -new -key co3/co3.client.acme.com.key -subj "/C=FR/ST=IdF/L=Paris/O=AcmeTest/CN=co3.client@acme.com" -out co3/co3.client.acme.com.csr - -# Generate certificate for co3.client.acme.com signed by our own CA -$OPENSSL_BIN x509 -req -days 3650 -in co3/co3.client.acme.com.csr -CA ca/ca.crt -CAkey ca/ca.key -CAcreateserial -out co3/co3.client.acme.com.crt - -# Generate a PKCS12 file -$OPENSSL_BIN pkcs12 -export -password pass: -out co3/co3.client.acme.com.p12 -inkey co3/co3.client.acme.com.key -in co3/co3.client.acme.com.crt -certfile ca/ca.crt +# --------------------------------------------------------------------------- +# generate_cert BASENAME CN OUTDIR [CADIR] [PASSOUT] +# +# Issues a CA-signed RSA-2048 certificate and writes four files under OUTDIR: +# BASENAME.key private key +# BASENAME.csr certificate signing request +# BASENAME.crt signed certificate +# BASENAME.p12 PKCS12 bundle (password = PASSOUT, empty string = no password) +# +# Arguments: +# BASENAME filename stem, e.g. "owner.client.acme.com" +# CN X.509 Common Name, e.g. "owner.client@acme.com" +# OUTDIR output directory, e.g. "owner" +# CADIR directory containing ca.crt / ca.key (default: "ca") +# PASSOUT PKCS12 export password (default: empty = no password) +# --------------------------------------------------------------------------- +generate_cert() { + local basename="$1" + local cn="$2" + local outdir="$3" + local cadir="${4:-ca}" + local passout="${5:-password}" + + mkdir -p "$outdir" + + local key="${outdir}/${basename}.key" + local csr="${outdir}/${basename}.csr" + local crt="${outdir}/${basename}.crt" + local p12="${outdir}/${basename}.p12" + + echo "── Generating ${cn} (${outdir}) ──" + + "$OPENSSL_BIN" genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 \ + -out "$key" + + "$OPENSSL_BIN" req -new -key "$key" \ + -subj "/C=FR/ST=IdF/L=Paris/O=AcmeTest/CN=${cn}" \ + -out "$csr" + + "$OPENSSL_BIN" x509 -req -days 3650 \ + -in "$csr" -CA "${cadir}/ca.crt" -CAkey "${cadir}/ca.key" -CAcreateserial \ + -out "$crt" + + "$OPENSSL_BIN" pkcs12 -export \ + -out "$p12" -inkey "$key" -in "$crt" -certfile "${cadir}/ca.crt" \ + -passout "pass:${passout}" +} + + +# --------------------------------------------------------------------------- +# CA +# --------------------------------------------------------------------------- +mkdir -p ca +echo "── Generating CA ──" +"$OPENSSL_BIN" genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out ca/ca.key +"$OPENSSL_BIN" req -new -x509 -days 3650 -key ca/ca.key \ + -subj "/C=FR/ST=IdF/L=Paris/O=AcmeTest/CN=Acme Test Root CA" \ + -out ca/ca.crt + + +# --------------------------------------------------------------------------- +# Server cert +# --------------------------------------------------------------------------- +generate_cert "kmserver.acme.com" "kmserver.acme.com" "server" + + +# --------------------------------------------------------------------------- +# Client certs +# --------------------------------------------------------------------------- +generate_cert "owner.client.acme.com" "owner.client@acme.com" "owner" +generate_cert "user.client.acme.com" "user.client@acme.com" "user" +generate_cert "co3.client.acme.com" "co3.client@acme.com" "co3" + +echo "Done." From 025f86a27760a85c6a80a580f8ec0844e1b78792 Mon Sep 17 00:00:00 2001 From: Manuthor Date: Wed, 12 Aug 2026 22:56:10 +0200 Subject: [PATCH 05/17] fix(tests): use non-expiring CRL DP URL in ext.cnf v3_ca section --- certificates/openssl/ext.cnf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certificates/openssl/ext.cnf b/certificates/openssl/ext.cnf index 9799454..3bb5d47 100644 --- a/certificates/openssl/ext.cnf +++ b/certificates/openssl/ext.cnf @@ -4,6 +4,6 @@ crlDistributionPoints=URI:https://package.cosmian.com/kms/crl_tests/intermediate basicConstraints=CA:FALSE,pathlen:0 keyUsage=keyCertSign,digitalSignature extendedKeyUsage=emailProtection -crlDistributionPoints=URI:https://package.cosmian.com/kms/crl_tests/intermediate.crl.pem +crlDistributionPoints=URI:http://crl.example.com/crl.pem subjectKeyIdentifier=hash authorityKeyIdentifier=keyid:always,issuer From 9f0c3398e504765de4cbbb4bff42a5e3067eeb7c Mon Sep 17 00:00:00 2001 From: Manuthor Date: Thu, 13 Aug 2026 20:33:47 +0200 Subject: [PATCH 06/17] fix: update co3 cert --- certificates/client_server/ca/ca.srl | 2 +- .../client_server/co3/co3.client.acme.com.crt | 21 ++++++++++++++++++ .../client_server/co3/co3.client.acme.com.p12 | Bin 3643 -> 3643 bytes 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/certificates/client_server/ca/ca.srl b/certificates/client_server/ca/ca.srl index a7f72e5..ffe1aa0 100644 --- a/certificates/client_server/ca/ca.srl +++ b/certificates/client_server/ca/ca.srl @@ -1 +1 @@ -640BF08B2D2C8FC41FC2300EF05F7A989419005F +640BF08B2D2C8FC41FC2300EF05F7A9894190060 diff --git a/certificates/client_server/co3/co3.client.acme.com.crt b/certificates/client_server/co3/co3.client.acme.com.crt index e69de29..ae5a42e 100644 --- a/certificates/client_server/co3/co3.client.acme.com.crt +++ b/certificates/client_server/co3/co3.client.acme.com.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDhjCCAm6gAwIBAgIUZAvwiy0sj8QfwjAO8F96mJQZAGAwDQYJKoZIhvcNAQEL +BQAwWjELMAkGA1UEBhMCRlIxDDAKBgNVBAgMA0lkRjEOMAwGA1UEBwwFUGFyaXMx +ETAPBgNVBAoMCEFjbWVUZXN0MRowGAYDVQQDDBFBY21lIFRlc3QgUm9vdCBDQTAe +Fw0yNjA4MTMxODI5NTNaFw0zNjA4MTAxODI5NTNaMFwxCzAJBgNVBAYTAkZSMQww +CgYDVQQIDANJZEYxDjAMBgNVBAcMBVBhcmlzMREwDwYDVQQKDAhBY21lVGVzdDEc +MBoGA1UEAwwTY28zLmNsaWVudEBhY21lLmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBALl3txZ9MNI7mJ5Y6UkvjPlKOiA92NJZSsuvwD6ZzfHXC0EP +ktunfCw/GLZmrUJzrO5LTm5pNT7vUf2TkpWEvgC8t+bMjXOrl6qpFEUS0JVvrAMB +PeXeU25Ft0DCFXawE1g/a0+CefpGJ6ee8YH5Ne0CwkOecmfW4/ou4dEjnVzX5N7O +1O3ElODaRipKyt1l8MG6PuEnNQaCA33RrRX9lnCJVd9DVZa/xou+OYPFTLlDlNN7 +sHPUUsN26TSZp0dcyBax3ws1ZUDGbKtVngC85+JWkLi9t4+NeA+wVhiJfiP3I2ko +4trF9Q7s/20HjG6hwwt0JdUpGZG3n50w6lh6bEsCAwEAAaNCMEAwHQYDVR0OBBYE +FPDF8CLmuTR3/P38UVZndL035uWlMB8GA1UdIwQYMBaAFCrsQdVEgrwm8ED+4pCs +lsN3SX3/MA0GCSqGSIb3DQEBCwUAA4IBAQCi2DoSBefV3t6TRiEDqUT/sufFKbb+ +2Ddl2Gz3Ev5lG854VkkmwzDbHuIy0PvdnseRHetKE8U/Avt+o6KWJCEblBw85Q5Q +FmvOoXtD9YdrQediOr5m6ARKTwjHb5C7vwTQluXL16SuC9ATVWBW5/osaoAu+G7w +OYhcLnSwj15Faf+sL7kdD+k1497xTlzdlQK0g5Cw523IKxwqqrp51or9d9XEKRcO +u0w+3pJJvjkE8hoKTQ1o1cXVxl28q+dZAKd9ThdTEKIkqzdcTRJpV7rXwucY1UOu +yh3Sfh/RDTKlCCNshDfJJ2nNQjRSPNKgjnVrB4s6mlSbjmSBfrZud2Td +-----END CERTIFICATE----- diff --git a/certificates/client_server/co3/co3.client.acme.com.p12 b/certificates/client_server/co3/co3.client.acme.com.p12 index 9895cfed34d54848dd717bfa52738c2b21154a95..7c8b7d60ccabb6cf244475d4859c333ccec0258f 100644 GIT binary patch delta 3415 zcmV-d4XEhGki>_DlL~-> z2e9Z}5j0&L&=f(ta~HOt%L{J(K$pTvGLEBy#=PpkXalIn8EAXIcJ>d2*Yl4(!gzD< zdzwljrQH;75WfVNEMz|P802xXg5nWBAaG(_qGyB?bO^m`;y&8VebE)A0$Lm!_hB{8 zPOlVHaGWE^olaTf?Eprn?)#83$A96w*^ij7?*i-QD&j_IHg}dk*dPp< zA3L(Uw>;yido)#gb1{|VWEMFak8Vbsss4-YO@qL~L!W0FJCTQW2%CAaw5_CS^N25! z-X&Crx9=*A%4{npmic@KP;4BPy6ra7%kR_Ue!U9zG#aVr8Tc~#VWXNq0*L=xX2qF< zW`6_ddLLoFX->a}p0^=qC>xt4?Vm-|WM`#s)4USoDv>zU<%z{uWD;YEF9N)cB%LvL z{?jWvnZJ8Si*1d+Ac`j%;;s1{o$gkshj{Q$dtf?+bQKRLwwcIfkjAeE6mS1NIa`h# zg!VdC$06>lh7D>`f|&>=aq=D?3_FYlX@8v@AP&s^P~57D{DX?Tu8uW^XP;v6ktNsE zVnz)eEvNH>RPWSX$!OB8>6MgJ{>T|!(1YMh(iBI*H&M4`?*KqVFg`a#NN?jYbMr(* zN(bVH495~qRetb8O-7hlm+vzcc_6=B)t(s%a|!i5)IO2!L;b}WzEb2KSBG@_M~i)i14(BTkL22g8v=va!SdW1;woHeG+9d7r zMe9U@j6xpr`lO_D$nDU`shm)g!T}p%s_a^qp2w=-WViQy&rOUANMbWE8zr_=y)yKZ|6Q( z^FG#lj4?M`n;<@|#pRc!>=%`F374ZS+KnN*7}1eZKmOpcNLj{i>|bmnLpMEf%~}6{3_XJ-9s}~ zvDY{u7cF!Ye;ZE7;O3MXqJJ5unJwmWN6g;ZQA@{pt#3_3)(t*f2oQR5SGT-^sfeD% z<_y3uJ6#bx4J=`=HhclD))_q-85-BEM*fDs7S6jyx#Ev`Iw$~@{uL&SGe7vb^iyI{ zcxPE#8mJ+ZHKIIERGt`YojnrYP7I`AEWyXb99MET{m9kYbRD*r@qZ{7=DEM9+>@(Y zM-ya5gOI?+PI07XTMT61#O5U(@yLY?UpIDaomwh|-9z;U&`qDu(1)w7azJ8ZH&CcOg*i{4@`;Zk6*$?-pxR!jme!7m$9^NMI0vF*}>T!FPDz_n5_ z0aP+?4RahB_RHW&j|SQ}ZHjyDN&i6{m|j>i zor+2UtF@(MzW3)#eJG0okV4(`@MZ@Iwb*IrRzRc%ab{+ONW9lDLmPCC6IN;_&J%1BlzEw&m;=bq!CzjG$Y?sL` z;A)N-cR96eNU#-~3)2AZu{s36ceuFJ%CA`IuIm+o&VNl>|&c^ zeVy||dw*Os;9!)$|6c4&Rwa@t_$$Ex=_tx9b^^ zg!?%(-UuZqcI1~pZ|!YDiOe$J`Wne~A3|tVQEllUm~W(u?l5Xkh&Z?ew;3VY^KpXu z1qsB{jK0J!IV|e_;OtI*-=5pVSfjnEKO$?{98(6oM9%yR>CAql6UxP(%dM=RLFN+$ zA2AfC_7fl?W(kQ=VPSP$%1OPC{cL3r7ok&n0$U|oi8?GbS>z2_Cdun-HpTXe33~qe zDaFh6LzRW_%9CISR1mZDiVI~sGDdf9}Z4?KDQVpYCjh0D?Bc zrn@9AtWq(Wzd*kyKiQ!>IWEl4?wiRRG43^jpat>y%FmVRwWIW-Ro!!tse`K(+&Bm1 zuz#We9}EFGYVqXnfp}ugnRG-0a%7{q&7&`mf%+CCnMkLbe-HAZY_VB#vE3=Kwt2N9 zz`nMFXkQbByZbL$GKteo3-i;OKhyP;nU=qo?BJcVL+t*ofP#7z-6{zvUl8jC5-PRH zQ7zXHqUx6ZJ8!O>jrJUoxDJ$ zTpkjtt3qOtHg{2dhbRHPb$7x(gOJf=(uINkC(=2{|5Woq9=-QRZtK)dP{=Z@Dj^(Eg1ysnn^o3T_E-FmXV$Gh?SI?Z z8lz)##ZnCQ`cjWzjEa&(_MzRv2;oW80soiX#&M?ky()yAM26O)$ZWnQLU>loBSJ*G zP_w~7kHE}#p@qWHn!$kxYgB0q`&~?tJ3jYb#U{rZ57u0Ec|AXbh#j-=b?EiZDX}sU zxQ~u%#rQk9e^ zea6KaSqq`v+d)?TIPlqN?*XLDlRzn6JhKfXbw_}4(<-A(6dT`inmV(*u5$S9aqTxd zp&u4BI&eI=E6&``m=GA8)(X}KI)BswZG0*QWwy~p3dCX}58?wV*Of92!++$_1&9S+ zFVw!;O<@vDp&tZpRS&WFJ!5k7FlFJ%IC)nxOjEqKW%i^db`tn=^X$r>jbx`TvD}S7 z>Yei4dG05`*Lr?+Gg)39+~3~)m!V7o(35_7Xyr#Bnn_LB`_lf z2`Yw2hW8Bt2^BFG1QirQSqm5WwESxhyjCb6_z0ANO&&&*@C_9Bd$ovO{7_eR|E$9ShfRZ<27%Be4s znd*B>;io+D6IP7eCgwjYnrg3w+lWDbgRV2IkPOu60Oe8ly%^mGcog{IQ4>YZ<;4^p z0K2|6{eL?~*KE?T?$_NtH${@)1vd&UYJae*l|QEttgC4%;7cLUv}&FK7_@Knyk&juGsUYHK3R>zv_7&{(QSzfH~eBE;6 z_St+SO1>EO0qk0Lsp+jhR>!D|n zJ`mv~Not=j(J82sr>4=ccou`zp-W!s>GreqI%(+67Dqqp+nBMClzR#gkQWMzfZ6%K zMwa}7Q6d^kr>zAAT^rUByw-lvpMsLkt55A}B6quXv_%3_ob=HO@m5YN6}!^Anf={)9T8nfwOGjek|{DW+RO6s=xj2 zowzSw+04wi>ISBVrU=lu*_Bh5CN##FHgT#5ZO%BXcyjt`CO$*L-fILy;nYo9J+uNf z(T^THd3d&54wfEQ%AUZh;WI#b7V0N@R(~mN;S{cLZTc$%J>bU>ww5JsbTToFuLR=; zcF3;O7|zC3@0`~i$JY1U43Y%@OCZoFirVRA11Tr67fCnav-PI`&L4j^z&irdVx${! z&8>Q9Vk7^|K+-}H;VA7IK%1-~QkDHUj=0nNxVwqpDJBElQ?0E71Vk7ORo+nRlz+nn zd6Cs$NX|RFYF0@QE!&~zZb`j~Nj*=x}gA-UlhwgIXGaXuH` zVVAd)tKu3xB>tHJv!IANd1}Q3a^{RyUu1IT6-8F z;LW*EqPN3Akb(o3{MyjY$8A1*N`EMlhiFuwN&8HXJ^v1C9QZGfRYv;(D9I+)WpI^) z>6((J<&0<|MZz2!x)Oo3Ub;)R-{hAWwtwcncUFK8 zGFbh?#;o{DXF5=sqSAI%1twt?`HCtqLA@jk98Cj!37h$}5{Vm0uMFFj6PDS$ znkPG|9uY7wX`35m#4Vpt5p|oTq+a`pC{hvBt?liP&N-#a#|HEvnD3Zpw52Tmj#>k% znOFB-X_<(*2&ACFKLA%k#>W%^USup|Hf02@vi+dVClay9xA zSc=`^ne)HJL&oIS36VW5w=BV{KGpo~G1R75#toj2)vU3Y_rKJ5RA+6uYXJlVHS6!1`1w3@~u^& zu!T^)=AraiqP#rZt&%c97y2M$+ZNr97ok5uV!xHNebvl$*qrhocF~%aM3QAY*s1i* zuLq?b&tP&s%tM21Y)AGlaHjtEA`ac~uW`FxVhe7|pQbEIvu#+jw{V`JX|Z564awWbcc+L8{h87}^I8 zkt8{$eknMT+)5;e=)r9UrEy;nekxNy7QdiQ*5xTXEn=mjf-om_X})~Br)>1db5Lox zV&b>Ir+0{EjH~epS9zPJ7sI#h7dToqsXF0P&~myWEaeJj>*X$LNfF zzKd;E%f^ywc{M8tFb8b;M<%T;HDh`5ZRc3mFr2(SHdpjc_OU``>NDar#$}iXxv-NG zS7-#7w`U=Mz%fN!6z+0xO6(#|WI_=h!1AR$Y*g7DP6}WB-59q_gfQ9$9&m2?+rT}# z242=*@_)MtQb%38N+pYKdHpLL7a{P|oEDXQ6iiO!9$+gG$M#o~Xr>n9FV74?1|eVg z6F*s~qYGfNJ+H6OmTU2r{k2oD1xnR0pcAaZ(~G2RAqzMyy}wCmQyCyiR3QUvXaD_XA@th}2QaOV-c zK;fhd)oH(6)S>**W9Lj*Cm!AjIKDo1OOI*0u7A%Bfh1OlV)v2z#ctvSd@tO}#mk|C zDuSl*&oCK)W)!!ES3P9hBry~4MM&Dx4j@EYn>fMRgX}Js?OHw;mkqzzmm!RQxwclr zcG`00!)YA7Q-(nQL-l#Iudt>MeBB1!D=%uJ7o1B^%70^ZF}+ZQ0jgDI4d+GuvzGdm z2l){5l{Zed*%{fOsoF+8DvwY{zW2}rXEI~%i(Ac))r>!SUt+0VE}|UQfTa~{kTEbJ)q$RxkkSA(eynOT;4EPE?GqM z>bm%3FhI@v+?SQ2H1q|07O~2=CPH;ZBY!7R^GKDWRx_B4GboE`bPsRQTT!?-)*zZ4 z!LvSdsw4C$*fm%!`Buv7#V+#EzV`t>UX>?pN{w^hhQ@e5Bv6ThYTNn(BRuYA3~LU_ zoh)NxbHw~ulhbE&Wg@PJ0Yx1vwLyFL&utQimli8BA#AFdBzUbPv6#tmm2JLrntuup zVqb}lNcEd|$*YS7WZY{vl9B2_TvPcY?Vh(lNHMS*hkWBea*fAJ0sUZt*`8tR86Pcf z;0E&R&YaxAzvcFT+j^O!1ixXe>@(aBRl4hpeI$?;FHxu*5#qjVwnYt()Hj(mGBW!o z#5(0~KdKdXU#Hvs82-D{@?~d}n#|rT~RQA}l zdlPq_Ib6PoXcnc?U`oY=vk8ddolduu1zBe;oc|(7^9oj}#*y}1{-KDq$A7a)G62xL zHnyL}pTvJGQ5NOua8s*LGDohbZqq%a_DuSQa>cwXfqr^p9F10z#2#VeegcUnx?k^* zep@c<99sp*CJo{dUQX|U_;n>oDfDf>=GnKGOcStJb6Xr~W?N5&3LYIO_E}*hr_-MZ z<@1j0!8qsQ5;jYDL4%**Tz@wE#T!9V*E53f{fLtSK30U#FL>nZozwU4JbW_oH7YE# zgC7WRUlbCWKo$d;xeSu4$9sgbl8E7)vd@m#PwHlFRhQ9`rV&?`Ypy zq^@!2QPqbsYD8l5)f)=K}I3{vqXIyBTB~@K}x0)1@EAWye&+>kwqsaO5Lwed>Y@k$jV1_0}4+V?s zjir{~p)VW@u5d-0h&N6#-N!Jg5lIY6)fC0!OdfZe)m;D9e7S4oq9&j5x&#t2B`_lf z2`Yw2hW8Bt2^BFG1Qiq-$N-l}Qy?p4&wp86M19&jV&%<~@C_9<;YTh&+}F}eBv!gY tQePTV=d6FiaKQ7BK0N{g2mrK>nxg;! From 3fe4353739ee7c0b9f3fd6667b4594ea67fea8f2 Mon Sep 17 00:00:00 2001 From: Manuthor Date: Thu, 13 Aug 2026 23:29:15 +0200 Subject: [PATCH 07/17] fix: change crlDP --- certificates/openssl/ext.cnf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certificates/openssl/ext.cnf b/certificates/openssl/ext.cnf index 3bb5d47..9799454 100644 --- a/certificates/openssl/ext.cnf +++ b/certificates/openssl/ext.cnf @@ -4,6 +4,6 @@ crlDistributionPoints=URI:https://package.cosmian.com/kms/crl_tests/intermediate basicConstraints=CA:FALSE,pathlen:0 keyUsage=keyCertSign,digitalSignature extendedKeyUsage=emailProtection -crlDistributionPoints=URI:http://crl.example.com/crl.pem +crlDistributionPoints=URI:https://package.cosmian.com/kms/crl_tests/intermediate.crl.pem subjectKeyIdentifier=hash authorityKeyIdentifier=keyid:always,issuer From 03c0e37e8303efade009308e8f7dbf829fb26f77 Mon Sep 17 00:00:00 2001 From: Manuthor Date: Mon, 17 Aug 2026 11:06:24 +0200 Subject: [PATCH 08/17] fix: ports for TLS configs --- configs/client/cert_co3.toml | 2 +- configs/client/cert_owner.toml | 2 +- configs/client/cert_user.toml | 2 +- configs/server/auth/cert.toml | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/configs/client/cert_co3.toml b/configs/client/cert_co3.toml index 58cfa73..d8112f3 100644 --- a/configs/client/cert_co3.toml +++ b/configs/client/cert_co3.toml @@ -4,7 +4,7 @@ vendor_id = "cosmian" [http_config] -server_url = "https://localhost:10004" +server_url = "https://localhost:9998" accept_invalid_certs = true tls_client_pem_cert_path = "test_data/certificates/client_server/co3/co3.client.acme.com.crt" tls_client_pem_key_path = "test_data/certificates/client_server/co3/co3.client.acme.com.key" diff --git a/configs/client/cert_owner.toml b/configs/client/cert_owner.toml index 046b58b..cc82b77 100644 --- a/configs/client/cert_owner.toml +++ b/configs/client/cert_owner.toml @@ -5,7 +5,7 @@ vendor_id = "cosmian" [http_config] -server_url = "https://localhost:9999" +server_url = "https://localhost:9998" accept_invalid_certs = true tls_client_pem_cert_path = "test_data/certificates/client_server/owner/owner.client.acme.com.crt" tls_client_pem_key_path = "test_data/certificates/client_server/owner/owner.client.acme.com.key" diff --git a/configs/client/cert_user.toml b/configs/client/cert_user.toml index 7bd2551..02cd75a 100644 --- a/configs/client/cert_user.toml +++ b/configs/client/cert_user.toml @@ -5,7 +5,7 @@ vendor_id = "cosmian" [http_config] -server_url = "https://localhost:9999" +server_url = "https://localhost:9998" accept_invalid_certs = true tls_client_pem_cert_path = "test_data/certificates/client_server/user/user.client.acme.com.crt" tls_client_pem_key_path = "test_data/certificates/client_server/user/user.client.acme.com.key" diff --git a/configs/server/auth/cert.toml b/configs/server/auth/cert.toml index 66122af..9b6268a 100644 --- a/configs/server/auth/cert.toml +++ b/configs/server/auth/cert.toml @@ -3,10 +3,10 @@ # Bound to port 9999. Loaded by start_default_test_kms_server_with_cert_auth(). default_username = "tech@cosmian.com" -kms_public_url = "https://localhost:9999/google_cse" +kms_public_url = "https://localhost:9998/google_cse" [http] -port = 9999 +port = 9998 hostname = "0.0.0.0" [db] From 2a2de6cee18e0a44eed15cd3506957bd9b318bd9 Mon Sep 17 00:00:00 2001 From: Manuthor Date: Wed, 19 Aug 2026 11:17:09 +0200 Subject: [PATCH 09/17] test: add new OPA test scenarios --- configs/auth_verifier/kms_opa_rbac.toml | 91 +++++++ configs/auth_verifier/provision_opa_users.sh | 231 ++++++++++++++++++ .../{verifier.toml => auth_verifier.toml} | 0 .../auth_verifier_ui_multi_method.toml} | 0 configs/server/crypto_officer_users.toml | 35 --- configs/server/hsm.toml | 28 --- configs/server/non_revocable.toml | 24 -- configs/server/rbac/opa.toml | 140 +++++++++++ configs/server/test/auth_cosmian.toml | 31 --- configs/server/test/auth_ui.toml | 25 -- configs/server/{ => tls}/pqc_tls.toml | 0 .../manifest.toml | 26 ++ .../step1_create.json | 40 +++ .../manifest.toml | 58 +++++ .../step1_create.json | 40 +++ .../step3_get_attributes.json | 10 + .../step4_destroy.json | 10 + .../manifest.toml | 24 ++ .../step1_create.json | 40 +++ .../manifest.toml | 38 +++ .../step1_create.json | 40 +++ .../step3_destroy.json | 10 + .../manifest.toml | 25 ++ .../step1_create.json | 40 +++ .../manifest.toml | 28 +++ .../step1_create.json | 40 +++ .../manifest.toml | 42 ++++ .../step1_create.json | 40 +++ .../step2_get.json | 10 + .../step3_destroy.json | 10 + 30 files changed, 1033 insertions(+), 143 deletions(-) create mode 100644 configs/auth_verifier/kms_opa_rbac.toml create mode 100755 configs/auth_verifier/provision_opa_users.sh rename configs/server/auth/{verifier.toml => auth_verifier.toml} (100%) rename configs/server/{test/auth_ui_multi_method.toml => auth/auth_verifier_ui_multi_method.toml} (100%) delete mode 100644 configs/server/crypto_officer_users.toml delete mode 100644 configs/server/hsm.toml delete mode 100644 configs/server/non_revocable.toml create mode 100644 configs/server/rbac/opa.toml delete mode 100644 configs/server/test/auth_cosmian.toml delete mode 100644 configs/server/test/auth_ui.toml rename configs/server/{ => tls}/pqc_tls.toml (100%) create mode 100644 vectors/opa/mode_enforcing_auditor_create_denied/manifest.toml create mode 100644 vectors/opa/mode_enforcing_auditor_create_denied/step1_create.json create mode 100644 vectors/opa/mode_enforcing_co_get_attributes_allowed/manifest.toml create mode 100644 vectors/opa/mode_enforcing_co_get_attributes_allowed/step1_create.json create mode 100644 vectors/opa/mode_enforcing_co_get_attributes_allowed/step3_get_attributes.json create mode 100644 vectors/opa/mode_enforcing_co_get_attributes_allowed/step4_destroy.json create mode 100644 vectors/opa/mode_enforcing_empty_roles_denied/manifest.toml create mode 100644 vectors/opa/mode_enforcing_empty_roles_denied/step1_create.json create mode 100644 vectors/opa/mode_enforcing_native_co_cert_allowed/manifest.toml create mode 100644 vectors/opa/mode_enforcing_native_co_cert_allowed/step1_create.json create mode 100644 vectors/opa/mode_enforcing_native_co_cert_allowed/step3_destroy.json create mode 100644 vectors/opa/mode_enforcing_unknown_role_denied/manifest.toml create mode 100644 vectors/opa/mode_enforcing_unknown_role_denied/step1_create.json create mode 100644 vectors/opa/mode_exclusive_native_co_cert_denied/manifest.toml create mode 100644 vectors/opa/mode_exclusive_native_co_cert_denied/step1_create.json create mode 100644 vectors/opa/mode_exclusive_other_domain_allowed/manifest.toml create mode 100644 vectors/opa/mode_exclusive_other_domain_allowed/step1_create.json create mode 100644 vectors/opa/mode_exclusive_other_domain_allowed/step2_get.json create mode 100644 vectors/opa/mode_exclusive_other_domain_allowed/step3_destroy.json diff --git a/configs/auth_verifier/kms_opa_rbac.toml b/configs/auth_verifier/kms_opa_rbac.toml new file mode 100644 index 0000000..edf9ecc --- /dev/null +++ b/configs/auth_verifier/kms_opa_rbac.toml @@ -0,0 +1,91 @@ +# ============================================================================= +# Authentication Verifier — KMS OPA RBAC configuration +# ============================================================================= +# +# Configures the Authentication Verifier for use with the Cosmian KMS +# OPA RBAC integration. The five role names declared here match exactly +# the roles evaluated by `test_data/opa/kms.rego`. +# +# Start from the `authentication/` directory: +# +# ./target/debug/auth_verifier \ +# ../test_data/configs/auth_verifier/kms_opa_rbac.toml +# +# Default super-admin credentials (realm "_"): +# username : admin +# password : change_me +# +# KMS realm seeded on first start: +# realm : kms +# admin : kms-admin / kms-admin-pass +# +# ============================================================================= + +host_name = "127.0.0.1" +host_port = 8443 + +# --------------------------------------------------------------------------- +# RBAC role names — must match the role names used in test_data/opa/kms.rego. +# Exposed at GET /public/roles so the KMS admin UI can list available roles. +# --------------------------------------------------------------------------- +roles = [ + "SuperAdmin", # Unrestricted, cross-domain (ANSI/INCITS 359 §4.2) + "DomainAdmin", # Full access within own domain + "CryptoOfficer", # Key lifecycle within own domain (FIPS 140-3 §7.4) + "Auditor", # Read-only metadata within own domain (NIST SP 800-53 AU-9) + "User", # Crypto-use only, no lifecycle (FIPS 140-3 §7.4) +] + +# Admin UI — serve the pre-built SPA at /admin-ui. +# Path is relative to the directory where auth_verifier is started (authentication/). +admin_ui_path = "admin-ui/dist" + +# --------------------------------------------------------------------------- +# Console logging +# --------------------------------------------------------------------------- +[log] +level = "info" + +# --------------------------------------------------------------------------- +# TLS — paths are relative to the authentication/ submodule root. +# Replace with production certificates for non-development environments. +# --------------------------------------------------------------------------- +[tls_params] +server_private_key = "server/src/tests/certificates/ec/auth.server.key.pem" +server_certificate = "server/src/tests/certificates/ec/auth.server.cert.pem" +server_ca_chain = "server/src/tests/certificates/ec/auth.ca.pem" + +# --------------------------------------------------------------------------- +# JWT signing — reuse the server TLS key for token signing (dev only). +# In production, use a dedicated signing key. +# --------------------------------------------------------------------------- +[session_jwt_params] +jwt_ec_private_key = "server/src/tests/certificates/ec/auth.server.key.pem" +jwt_ec_public_key = "server/src/tests/certificates/ec/auth.server.cert.pem" + +# --------------------------------------------------------------------------- +# Database — persistent SQLite for the KMS RBAC test environment. +# Switch to `sqlite::memory:` for fully ephemeral test runs. +# --------------------------------------------------------------------------- +[database_params] +auto_init_schema = true +backend = "sqlite" +connection_url = "sqlite:///tmp/kms_opa_rbac_auth.db" + +# --------------------------------------------------------------------------- +# Development seed — creates the `kms` realm and a realm-admin on first start. +# All operations are idempotent; the seed is safe to leave enabled. +# --------------------------------------------------------------------------- +[dev_seed] +realm_id = "kms" +admin_username = "kms-admin" +admin_password = "kms-admin-pass" + +# --------------------------------------------------------------------------- +# CORS — allow the KMS server origin on admin endpoints. +# Adjust to match the actual KMS public URL in your deployment. +# --------------------------------------------------------------------------- +allowed_origins = [ + "http://localhost:9998", + "http://127.0.0.1:9998", +] diff --git a/configs/auth_verifier/provision_opa_users.sh b/configs/auth_verifier/provision_opa_users.sh new file mode 100755 index 0000000..1fa0c8b --- /dev/null +++ b/configs/auth_verifier/provision_opa_users.sh @@ -0,0 +1,231 @@ +#!/usr/bin/env bash +# provision_opa_users.sh +# +# Provision the Cosmian Authentication Verifier with users required for KMS OPA +# RBAC testing. Works for both automated CI runs and the manual `opa.toml` setup. +# +# Creates two realms and 8 users covering every role in kms.rego plus two +# edge-case identities (no roles, unknown role): +# +# Realm ${REALM_A} (primary, default: kms-opa-test): +# kms-opa-super-admin SuperAdmin ${PASSWORD} +# kms-opa-officer CryptoOfficer ${PASSWORD} +# kms-opa-user User ${PASSWORD} +# kms-opa-auditor Auditor ${PASSWORD} +# kms-opa-no-roles (no roles) ${PASSWORD} — OPA deny edge-case +# kms-opa-unknown-role Hacker ${PASSWORD} — OPA deny edge-case +# +# Realm ${REALM_B} (cross-domain, default: kms-opa-other): +# kms-opa-domain-admin-other DomainAdmin ${PASSWORD} +# kms-opa-other-officer CryptoOfficer ${PASSWORD} +# +# Outputs shell export statements to STDOUT (suitable for `eval "$(…)"`): +# +# export KMS_TEST_OPA_SUPER_ADMIN_JWT="…" +# export KMS_TEST_OPA_OFFICER_JWT="…" +# export KMS_TEST_OPA_USER_ROLE_JWT="…" +# export KMS_TEST_OPA_AUDITOR_JWT="…" +# export KMS_TEST_OPA_NO_ROLES_JWT="…" +# export KMS_TEST_OPA_UNKNOWN_ROLE_JWT="…" +# export KMS_TEST_OPA_DOMAIN_ADMIN_OTHER_JWT="…" +# export KMS_TEST_OPA_OTHER_DOMAIN_JWT="…" +# +# Human-readable status messages are written to STDERR so they do not +# interfere with `eval "$(…)"`. +# +# ── Usage ───────────────────────────────────────────────────────────────────── +# +# CI (mise test:opa_rbac) — default realms, evaluate exports: +# eval "$(AUTH_URL=… CA_CERT=… bash provision_opa_users.sh)" +# +# Manual (opa.toml setup) — custom realms, read the summary table: +# REALM_A=acme.com REALM_B=partner.acme.com \ +# bash ../test_data/configs/auth_verifier/provision_opa_users.sh +# # Then login in the KMS Web UI with: +# # realm: acme.com username: kms-opa-officer password: change_me +# +# ── Environment ─────────────────────────────────────────────────────────────── +# +# AUTH_URL — auth verifier HTTPS URL (default: https://127.0.0.1:8443) +# REALM_A — primary realm ID (default: kms-opa-test) +# REALM_B — secondary realm ID (default: kms-opa-other) +# PASSWORD — password for all provisioned users (default: change_me) +# CA_CERT — path to the server CA certificate +# (default: authentication/server/src/tests/certificates/ec/auth.ca.pem +# relative to the repo root; override via env when running from an +# arbitrary directory) +# REPO_ROOT — repository root path (default: auto-detected) + +set -euo pipefail + +# ── Configuration ───────────────────────────────────────────────────────────── +AUTH_URL="${AUTH_URL:-https://127.0.0.1:8443}" +REALM_A="${REALM_A:-kms-opa-test}" +REALM_B="${REALM_B:-kms-opa-other}" +PASSWORD="${PASSWORD:-change_me}" +REPO_ROOT="${REPO_ROOT:-$(cd "$(dirname "$0")/../../../.." && pwd)}" +CA_CERT="${CA_CERT:-${REPO_ROOT}/authentication/server/src/tests/certificates/ec/auth.ca.pem}" + +COOKIE_JAR=$(mktemp /tmp/opa-admin-XXXXXX.txt) +trap 'rm -f "${COOKIE_JAR}"' EXIT + +# ── Helpers ─────────────────────────────────────────────────────────────────── + +# All status messages go to STDERR so STDOUT stays clean for `eval "$(…)"`. +log() { echo "$*" >&2; } +warn() { echo " ⚠ $*" >&2; } +fail() { echo " ✗ $*" >&2; exit 1; } + +# Convert a plain-text password to a JSON byte-array for the auth verifier API. +# The UserPass endpoint expects Vec (Rust serde_json → array of integers). +str_to_json_bytes() { + printf '%s' "$1" \ + | od -v -tu1 -An \ + | tr -s ' \n' ' ' \ + | sed 's/^ //; s/ $//' \ + | tr ' ' ',' +} + +# POST with admin session cookie; non-2xx responses are ignored (idempotent). +admin_post_idempotent() { + local path="$1" body="$2" + curl -sk --cacert "${CA_CERT}" \ + -b "${COOKIE_JAR}" -c "${COOKIE_JAR}" \ + -o /dev/null \ + -H "Content-Type: application/json" \ + -X POST "${AUTH_URL}${path}" \ + -d "${body}" || true +} + +# DELETE — idempotent (404 is fine). +admin_delete_idempotent() { + local path="$1" + curl -sk --cacert "${CA_CERT}" \ + -b "${COOKIE_JAR}" -c "${COOKIE_JAR}" \ + -o /dev/null \ + -X DELETE "${AUTH_URL}${path}" || true +} + +# Login and return the JWT from the `_ea_` cookie via STDOUT. +user_login_jwt() { + local realm="$1" username="$2" ****** + local cookie_output + cookie_output=$(mktemp /tmp/opa-login-XXXXXX.txt) + # shellcheck disable=SC2064 + trap "rm -f '${cookie_output}'" RETURN + + local http_status + http_status=$(curl -sk --cacert "${CA_CERT}" \ + -c "${cookie_output}" \ + -o /dev/null -w "%{http_code}" \ + -u "${username}:${password}" \ + -H "Content-Type: application/json" \ + -X POST "${AUTH_URL}/login?realm=${realm}" \ + -d '{"public_key_pem":null,"totp_code":null}') + + if [[ "${http_status}" != 2* ]]; then + fail "login '${username}' in realm '${realm}' → HTTP ${http_status}" + fi + + local jwt + jwt=$(awk '/\t_ea_\t/{print $NF}' "${cookie_output}" | head -1) + if [[ -z "${jwt}" ]]; then + fail "no '_ea_' cookie in login response for '${username}' in '${realm}'" + fi + printf '%s' "${jwt}" +} + +# ── Step 1: super-admin login ───────────────────────────────────────────────── +log "" +log "Auth Verifier: ${AUTH_URL}" +log "Realms: ${REALM_A} (primary) ${REALM_B} (cross-domain)" +log "" + +http_status=$(curl -sk --cacert "${CA_CERT}" \ + -c "${COOKIE_JAR}" -b "${COOKIE_JAR}" \ + -o /dev/null -w "%{http_code}" \ + -u "admin:change_me" \ + -H "Content-Type: application/json" \ + -X POST "${AUTH_URL}/login?realm=_" \ + -d '{"public_key_pem":null,"totp_code":null}') +if [[ "${http_status}" != 2* ]]; then + fail "super-admin login → HTTP ${http_status}. Is the auth verifier running at ${AUTH_URL}?" +fi +log " ✓ super-admin authenticated" + +# ── Step 2: Create realms (idempotent) ─────────────────────────────────────── +for realm in "${REALM_A}" "${REALM_B}"; do + admin_post_idempotent "/admins/realms" \ + "{\"id\":\"${realm}\",\"auth_params\":{\"username_password_params\":{\"allow_expired_passwords\":false}},\"session_max_age_seconds\":3600,\"session_max_stale_age_seconds\":7200}" + log " ✓ realm '${realm}' ready" +done + +# ── Step 3: Create realm admins (idempotent) ───────────────────────────────── +admin_post_idempotent "/admins" \ + '{"id":"kms-opa-officer","realms":["'"${REALM_A}"'"],"userpass":"kms-opa-officer"}' +admin_post_idempotent "/admins" \ + '{"id":"kms-opa-other-officer","realms":["'"${REALM_B}"'"],"userpass":"kms-opa-other-officer"}' + +# ── Step 4: Create users (delete-then-create for idempotency) ──────────────── +# +# Format: "username:password:realm:roles_json_array" +# Empty roles array ("[]") → OPA deny edge-case. +# Unknown role ("Hacker") → OPA deny edge-case (no matching rule in kms.rego). +USERS=( + "kms-opa-super-admin:${PASSWORD}:${REALM_A}:[\"SuperAdmin\"]" + "kms-opa-officer:${PASSWORD}:${REALM_A}:[\"CryptoOfficer\"]" + "kms-opa-user:${PASSWORD}:${REALM_A}:[\"User\"]" + "kms-opa-auditor:${PASSWORD}:${REALM_A}:[\"Auditor\"]" + "kms-opa-no-roles:${PASSWORD}:${REALM_A}:[]" + "kms-opa-unknown-role:${PASSWORD}:${REALM_A}:[\"Hacker\"]" + "kms-opa-domain-admin-other:${PASSWORD}:${REALM_B}:[\"DomainAdmin\"]" + "kms-opa-other-officer:${PASSWORD}:${REALM_B}:[\"CryptoOfficer\"]" +) + +for entry in "${USERS[@]}"; do + IFS=: read -r username password realm roles <<< "${entry}" + admin_delete_idempotent "/realms/${realm}/userpass/${username}" + password_bytes="[$(str_to_json_bytes "${password}")]" + admin_post_idempotent "/realms/${realm}/userpass" \ + "{\"realm\":\"${realm}\",\"username\":\"${username}\",\"password\":${password_bytes},\"change_password\":false,\"roles\":${roles}}" + log " ✓ ${username} (${roles}) → realm '${realm}'" +done + +# ── Step 5: Login as each user, capture JWTs ───────────────────────────────── +log "" +log "Obtaining JWTs…" + +SUPER_ADMIN_JWT=$(user_login_jwt "${REALM_A}" "kms-opa-super-admin" "${PASSWORD}") +OFFICER_JWT=$(user_login_jwt "${REALM_A}" "kms-opa-officer" "${PASSWORD}") +USER_JWT=$(user_login_jwt "${REALM_A}" "kms-opa-user" "${PASSWORD}") +AUDITOR_JWT=$(user_login_jwt "${REALM_A}" "kms-opa-auditor" "${PASSWORD}") +NO_ROLES_JWT=$(user_login_jwt "${REALM_A}" "kms-opa-no-roles" "${PASSWORD}") +UNKNOWN_JWT=$(user_login_jwt "${REALM_A}" "kms-opa-unknown-role" "${PASSWORD}") +DOM_ADMIN_OTHER_JWT=$(user_login_jwt "${REALM_B}" "kms-opa-domain-admin-other" "${PASSWORD}") +OTHER_OFFICER_JWT=$(user_login_jwt "${REALM_B}" "kms-opa-other-officer" "${PASSWORD}") + +log " ✓ all JWTs obtained" + +# ── Step 6: Summary table (to STDERR) ──────────────────────────────────────── +log "" +log " Realm Username Password Role" +log " ────────────── ──────────────────────────── ──────────── ─────────────────────" +log " ${REALM_A} kms-opa-super-admin ${PASSWORD} SuperAdmin" +log " ${REALM_A} kms-opa-officer ${PASSWORD} CryptoOfficer" +log " ${REALM_A} kms-opa-user ${PASSWORD} User" +log " ${REALM_A} kms-opa-auditor ${PASSWORD} Auditor" +log " ${REALM_A} kms-opa-no-roles ${PASSWORD} (none)" +log " ${REALM_A} kms-opa-unknown-role ${PASSWORD} Hacker (invalid)" +log " ${REALM_B} kms-opa-domain-admin-other ${PASSWORD} DomainAdmin" +log " ${REALM_B} kms-opa-other-officer ${PASSWORD} CryptoOfficer" +log "" + +# ── STDOUT: shell exports (caller does `eval "$(this script)"`) ────────────── +printf "export KMS_TEST_OPA_SUPER_ADMIN_JWT='%s'\n" "${SUPER_ADMIN_JWT}" +printf "export KMS_TEST_OPA_OFFICER_JWT='%s'\n" "${OFFICER_JWT}" +printf "export KMS_TEST_OPA_USER_ROLE_JWT='%s'\n" "${USER_JWT}" +printf "export KMS_TEST_OPA_AUDITOR_JWT='%s'\n" "${AUDITOR_JWT}" +printf "export KMS_TEST_OPA_NO_ROLES_JWT='%s'\n" "${NO_ROLES_JWT}" +printf "export KMS_TEST_OPA_UNKNOWN_ROLE_JWT='%s'\n" "${UNKNOWN_JWT}" +printf "export KMS_TEST_OPA_DOMAIN_ADMIN_OTHER_JWT='%s'\n" "${DOM_ADMIN_OTHER_JWT}" +printf "export KMS_TEST_OPA_OTHER_DOMAIN_JWT='%s'\n" "${OTHER_OFFICER_JWT}" diff --git a/configs/server/auth/verifier.toml b/configs/server/auth/auth_verifier.toml similarity index 100% rename from configs/server/auth/verifier.toml rename to configs/server/auth/auth_verifier.toml diff --git a/configs/server/test/auth_ui_multi_method.toml b/configs/server/auth/auth_verifier_ui_multi_method.toml similarity index 100% rename from configs/server/test/auth_ui_multi_method.toml rename to configs/server/auth/auth_verifier_ui_multi_method.toml diff --git a/configs/server/crypto_officer_users.toml b/configs/server/crypto_officer_users.toml deleted file mode 100644 index 69802ba..0000000 --- a/configs/server/crypto_officer_users.toml +++ /dev/null @@ -1,35 +0,0 @@ -# Test KMS server — TLS + JWT auth, for privileged-user tests. -# Uses PEM cert + key (works in both FIPS and non-FIPS mode). -# Bound to port 10003. crypto_officer_users is injected at runtime. - -default_username = "tech@cosmian.com" -kms_public_url = "https://localhost:10003" - -[http] -port = 10003 -hostname = "0.0.0.0" - -[db] -database_type = "sqlite" -sqlite_path = "/tmp/kms_test_crypto_officer_users" -clear_database = true - -[workspace] -root_data_path = "/tmp/kms_test_workspace_crypto_officer_users" -tmp_path = "/tmp" - -[tls] -tls_cert_file = "test_data/certificates/client_server/server/kmserver.acme.com.crt" -tls_key_file = "test_data/certificates/client_server/server/kmserver.acme.com.key" -clients_ca_cert_file = "test_data/certificates/client_server/ca/ca.crt" - -[socket_server] -socket_server_start = true -socket_server_port = 10103 - -[idp_auth] -jwt_auth_provider = [ - "https://accounts.google.com,https://www.googleapis.com/oauth2/v3/certs,kacls-migration,996739510374-2tauakcggau7kvk37784k0s2lmveb9c9.apps.googleusercontent.com", - "https://login.microsoftonline.com/612da4de-35c0-42de-ba56-174b69062c96/v2.0,https://login.microsoftonline.com/612da4de-35c0-42de-ba56-174b69062c96/discovery/v2.0/keys", - "https://demo-kms.eu.auth0.com/,https://demo-kms.eu.auth0.com/.well-known/jwks.json", -] diff --git a/configs/server/hsm.toml b/configs/server/hsm.toml deleted file mode 100644 index 8330934..0000000 --- a/configs/server/hsm.toml +++ /dev/null @@ -1,28 +0,0 @@ -# Test KMS server — plain HTTP with Utimaco HSM. -# Bound to port 10001. Loaded by start_default_test_kms_server_with_utimaco_hsm(). -# HSM fields are at the top level because HsmConfig is flattened into ClapConfig. - -default_username = "tech@cosmian.com" -kms_public_url = "http://localhost:10001/google_cse" -hsm_model = "utimaco" -hsm_admin = ["tech@cosmian.com"] -hsm_slot = [0] -hsm_password = ["12345678"] - -[http] -port = 10001 -hostname = "0.0.0.0" - -[db] -database_type = "sqlite" -sqlite_path = "/tmp/kms_test_hsm" -clear_database = true - -[workspace] -root_data_path = "/tmp/kms_test_workspace_hsm" -tmp_path = "/tmp" - -[google_cse_config] -google_cse_enable = true -google_cse_disable_tokens_validation = true -google_cse_incoming_url_whitelist = ["https://cse.cosmian.com"] diff --git a/configs/server/non_revocable.toml b/configs/server/non_revocable.toml deleted file mode 100644 index e1f204f..0000000 --- a/configs/server/non_revocable.toml +++ /dev/null @@ -1,24 +0,0 @@ -# Test KMS server — plain HTTP, no authentication, for non-revocable key tests. -# Bound to port 10000. non_revocable_key_id is injected at runtime. -# Loaded by start_default_test_kms_server_with_non_revocable_key_ids(). - -default_username = "tech@cosmian.com" -kms_public_url = "http://localhost:10000/google_cse" - -[http] -port = 10000 -hostname = "0.0.0.0" - -[db] -database_type = "sqlite" -sqlite_path = "/tmp/kms_test_non_revocable" -clear_database = true - -[workspace] -root_data_path = "/tmp/kms_test_workspace_non_revocable" -tmp_path = "/tmp" - -[google_cse_config] -google_cse_enable = true -google_cse_disable_tokens_validation = true -google_cse_incoming_url_whitelist = ["https://cse.cosmian.com"] diff --git a/configs/server/rbac/opa.toml b/configs/server/rbac/opa.toml new file mode 100644 index 0000000..63ded65 --- /dev/null +++ b/configs/server/rbac/opa.toml @@ -0,0 +1,140 @@ +# ============================================================================= +# KMS server — OPA RBAC + Cosmian Authentication Verifier (Mode 3) +# ============================================================================= +# +# Authorization model: Mode 3 — Enforcing (OPA + Native KMS) +# +# Every KMIP request must pass TWO authorization gates: +# Gate 1 — OPA evaluates kms.rego: role hierarchy, domain scoping, owner override +# Gate 2 — Native KMS: object ownership, per-object grants, HSM admin +# +# Role hierarchy (defined in test_data/opa/kms.rego): +# SuperAdmin > DomainAdmin > CryptoOfficer > User +# > Auditor +# +# Prerequisites: +# 1. Authentication Verifier running (from authentication/ directory): +# cargo run -p auth_verifier -- \ +# ../test_data/configs/auth_verifier/kms_opa_rbac.toml +# +# 2. OPA running with kms.rego: +# docker compose up -d opa +# +# 3. Provision test users (from the repository root): +# REALM_A=acme.com REALM_B=partner.acme.com \ +# bash test_data/configs/auth_verifier/provision_opa_users.sh +# Usernames: kms-opa-officer, kms-opa-super-admin, kms-opa-auditor, … +# Password: change_me (default) +# +# 4. Start the KMS server from the repository root: +# cargo run -p cosmian_kms_server --features non-fips -- \ +# -c test_data/configs/server/rbac/opa.toml +# +# 5. Open the KMS Web UI at: https://localhost:9998 +# The login page offers two authentication methods: +# a) Auth Verifier (primary) — username/password form with realm selector: +# realm: acme.com username: kms-opa-officer password: change_me +# b) Client Certificate (secondary) — click "Client certificate" button: +# present owner.client@acme.com.p12 (or equivalent) from the browser. +# +# ============================================================================= + +# Used by the Web UI for redirect URLs and CORS. +kms_public_url = "https://localhost:9998" + +# --------------------------------------------------------------------------- +# HTTP server — plain HTTP for local development. +# JWT authentication does not require TLS between client and KMS; +# TLS is required only for the auth_verifier endpoint (handled separately). +# --------------------------------------------------------------------------- +[http] +hostname = "0.0.0.0" +port = 9998 + +# --------------------------------------------------------------------------- +# Authentication — Cosmian Authentication Verifier +# +# The KMS validates bearer tokens issued by the Authentication Verifier: +# - sub → user identity forwarded to OPA as `input.user` +# - roles → JWT `roles` claim (RFC 9068) forwarded as `input.roles` +# - as_rid → realm ID forwarded as `input.user_domain` +# +# JWKS endpoint is derived automatically as: +# {auth_verifier_url}/.well-known/jwks.json +# +# auth_verifier_realm enables the Web UI login form (POST /ui/login_as). +# --------------------------------------------------------------------------- +[auth_verifier] +auth_verifier_url = "https://localhost:8443" +# Multiple realms: the UI shows a realm selector dropdown when more than one is listed. +# Set to a single string or an array. +auth_verifier_realm = ["acme.com", "kms-opa-test"] +# Accept the self-signed test certificate — remove in production. +auth_verifier_accept_invalid_certs = true + +# --------------------------------------------------------------------------- +# Native KMS Crypto Officers (no split-key ceremony required) +# +# These users are trusted at the KMS level and can bypass OPA Gate 1 in +# enforcing mode. In exclusive mode, OPA remains the sole authority. +# +# Use case: cert-authenticated service accounts (e.g. mTLS operator certs) +# that need Create/Import access without an OIDC JWT. +# --------------------------------------------------------------------------- +[roles] +crypto_officer_users = [ + "owner.client@acme.com", + "user.client@acme.com", + "co3.client@acme.com", +] +crypto_officer_require_ceremony = false + +# --------------------------------------------------------------------------- +# Authorization — Open Policy Agent (Mode 3: enforcing) +# +# OPA evaluates test_data/opa/kms.rego on every KMIP operation. +# In "enforcing" mode OPA is Gate 1; the native KMS grant check is Gate 2. +# In "exclusive" mode the native KMS check is bypassed entirely. +# +# Reference: test_data/opa/kms.rego +# --------------------------------------------------------------------------- +[opa] +opa_url = "http://localhost:8181" +opa_mode = "enforcing" + +# --------------------------------------------------------------------------- +# Database +# --------------------------------------------------------------------------- +[db] +database_type = "sqlite" +sqlite_path = "/tmp/kms-opa-rbac" +clear_database = false + +# --------------------------------------------------------------------------- +# Workspace +# --------------------------------------------------------------------------- +[workspace] +root_data_path = "/tmp/kms-opa-rbac-workspace" +tmp_path = "/tmp" + +# --------------------------------------------------------------------------- +# Web UI — serves the pre-built SPA so http://localhost:9998 shows a login page. +# The login form authenticates against auth_verifier_realm = "kms" above. +# Build the UI once with: pnpm -C ui build +# --------------------------------------------------------------------------- +[ui_config] +ui_index_html_folder = "ui/dist" + +# --------------------------------------------------------------------------- +# Logging +# --------------------------------------------------------------------------- +[logging] +rust_log = "info,cosmian_kms_server=info" + +[tls] +tls_cert_file = "test_data/certificates/client_server/server/kmserver.acme.com.crt" +tls_key_file = "test_data/certificates/client_server/server/kmserver.acme.com.key" +# Client CA: enables mTLS authentication and adds "CERT" as a login method in the Web UI. +# Native KMS CO certs signed by this CA (owner.client@acme.com, etc.) can authenticate +# directly without a JWT when listed in [roles] crypto_officer_users. +clients_ca_cert_file = "test_data/certificates/client_server/ca/ca.crt" diff --git a/configs/server/test/auth_cosmian.toml b/configs/server/test/auth_cosmian.toml deleted file mode 100644 index 6249a84..0000000 --- a/configs/server/test/auth_cosmian.toml +++ /dev/null @@ -1,31 +0,0 @@ -# Minimal default configuration from quick_start.md -# Manual test config for the Cosmian authentication server ("AS" / "SA") Web UI login -# (`POST /ui/login_as`). Pair with a locally running `authentication` server, e.g. -# from the sibling `authentication` repo: -# -# cargo run -p auth_server -- server/auth_server.dev.toml -# -# which listens on https://localhost:8443 with a self-signed dev certificate and a -# default admin realm ("_") — hence `cosmian_auth_accept_invalid_certs = true` below. -# See CHANGELOG/test_plan_cosmian-auth-ui-login.md for the full manual test plan. -vendor_identification = "test_vendor" - -[http] -port = 9998 -hostname = "localhost" -kms_public_url = "http://localhost:9998" - -[cosmian_auth] -cosmian_auth_server_url = "https://localhost:8443" -cosmian_auth_realm = "_" -cosmian_auth_accept_invalid_certs = true - -[ui_config] -ui_index_html_folder = "ui/dist" - -[logging] -rust_log = "info,cosmian_kms=debug" -quiet = false -log_to_syslog = false -environment = "development" -ansi_colors = true diff --git a/configs/server/test/auth_ui.toml b/configs/server/test/auth_ui.toml deleted file mode 100644 index 3442a33..0000000 --- a/configs/server/test/auth_ui.toml +++ /dev/null @@ -1,25 +0,0 @@ -# Minimal default configuration from quick_start.md -vendor_identification = "test_vendor" -kms_public_url = "http://localhost:9998" - -[http] -port = 9998 -hostname = "localhost" - -[idp_auth] -jwt_auth_provider = ["https://accounts.google.com,https://www.googleapis.com/oauth2/v3/certs,", "https://demo-kms.eu.auth0.com/,https://demo-kms.eu.auth0.com/.well-known/jwks.json,"] - -[ui_config.ui_oidc_auth] -ui_oidc_client_id = "mlTpGfub8n8PU2PsBQVdxp2VOyxtkL9m" -ui_oidc_issuer_url = "https://demo-kms.eu.auth0.com/" -ui_oidc_logout_url = "https://demo-kms.eu.auth0.com/logout" - -[ui_config] -ui_index_html_folder = "ui/dist" - -[logging] -rust_log = "info,cosmian_kms=debug" -quiet = false -log_to_syslog = false -environment = "development" -ansi_colors = true diff --git a/configs/server/pqc_tls.toml b/configs/server/tls/pqc_tls.toml similarity index 100% rename from configs/server/pqc_tls.toml rename to configs/server/tls/pqc_tls.toml diff --git a/vectors/opa/mode_enforcing_auditor_create_denied/manifest.toml b/vectors/opa/mode_enforcing_auditor_create_denied/manifest.toml new file mode 100644 index 0000000..e99fb41 --- /dev/null +++ b/vectors/opa/mode_enforcing_auditor_create_denied/manifest.toml @@ -0,0 +1,26 @@ +name = "OPA Mode 3 (Enforcing): Auditor denied Create (not in auditor_ops)" +description = """ +OPA enforcing mode. A user holding the `Auditor` role attempts to create a +new symmetric key. `Create` is intentionally absent from `auditor_ops` in +kms.rego — auditors are read-only observers and must not generate key material. +OPA returns allow=false → KMS returns 401 Unauthorized. + +This enforces separation-of-duties: the Auditor role cannot generate or modify +cryptographic objects, even though it can read metadata (GetAttributes, Locate). + +Ref: kms.rego `auditor_ops` set (NIST SP 800-53 AU-9 separation-of-duties; +PCI-DSS v4.0 Req 10 — auditor must not create evidence-generating objects). +""" +requires_env = ["KMS_OPA_URL", "KMS_AUTH_SERVER_URL", "KMS_TEST_OPA_AUDITOR_JWT"] +backends = ["sqlite"] + +# Auditor JWT — valid role, same domain as the server's OPA test realm. +[identities.auditor] +access_token_env = "KMS_TEST_OPA_AUDITOR_JWT" + +[[steps]] +operation = "Create" +request = "step1_create.json" +identity = "auditor" +assert_success = false +assert_error_contains = "access-right" diff --git a/vectors/opa/mode_enforcing_auditor_create_denied/step1_create.json b/vectors/opa/mode_enforcing_auditor_create_denied/step1_create.json new file mode 100644 index 0000000..c994e09 --- /dev/null +++ b/vectors/opa/mode_enforcing_auditor_create_denied/step1_create.json @@ -0,0 +1,40 @@ +{ + "tag": "Create", + "value": [ + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + }, + { + "tag": "Attributes", + "value": [ + { + "tag": "CryptographicAlgorithm", + "type": "Enumeration", + "value": "AES" + }, + { + "tag": "CryptographicLength", + "type": "Integer", + "value": 256 + }, + { + "tag": "CryptographicUsageMask", + "type": "Integer", + "value": 2108 + }, + { + "tag": "KeyFormatType", + "type": "Enumeration", + "value": "TransparentSymmetricKey" + }, + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + } + ] + } + ] +} diff --git a/vectors/opa/mode_enforcing_co_get_attributes_allowed/manifest.toml b/vectors/opa/mode_enforcing_co_get_attributes_allowed/manifest.toml new file mode 100644 index 0000000..17ef9c9 --- /dev/null +++ b/vectors/opa/mode_enforcing_co_get_attributes_allowed/manifest.toml @@ -0,0 +1,58 @@ +name = "OPA Mode 3 (Enforcing): CryptoOfficer GetAttributes on non-owned key — allowed" +description = """ +OPA enforcing mode. A `CryptoOfficer` in realm `kms-opa-test` (the default owner / JWT +client) creates an AES key. A second user — also a `CryptoOfficer` in the same realm +(kms-opa-auditor_jwt in `kms-opa-test` holds the Auditor role, so we use the officer JWT +directly as the non-owning reader) — reads GetAttributes on the key using a JWT identity +that is NOT the owner. + +Wait: to test non-owner access we need a different identity that is also a CryptoOfficer. +The `Auditor` role is in `auditor_ops` for `get_attributes` too, so use `Auditor` identity +for the GetAttributes step. This proves that any role that has `get_attributes` in its +`*_ops` set in kms.rego can read attributes of a key they do not own, once OPA is the +authoritative gate in enforcing mode. + +OPA evaluation for the auditor GetAttributes step: + input.roles = ["Auditor"] + input.user_domain = "kms-opa-test" + input.object_domain= "kms-opa-test" (same domain) + input.is_owner = false + input.operation = "get_attributes" + → auditor_ops contains "get_attributes" ∧ same_domain → allow = true + +Previously (before fix) OPA Gate 1 allowed but KMS Gate 2 (legacy ownership check) +denied → N/A displayed for all fields except UID. +After fix: OPA approval is authoritative in enforcing mode for non-HSM objects. + +Ref: kms.rego `auditor_ops`; NIST SP 800-57 Part 2 §4.3 — Auditor read-only metadata. +""" +requires_env = ["KMS_OPA_URL", "KMS_AUTH_SERVER_URL", "KMS_TEST_OPA_AUDITOR_JWT"] +backends = ["sqlite"] + +# Auditor JWT — same domain as the key owner (kms-opa-test), non-owner. +[identities.auditor] +access_token_env = "KMS_TEST_OPA_AUDITOR_JWT" + +# Step 1: Officer (default identity) creates the key. +[[steps]] +operation = "Create" +request = "step1_create.json" +assert_success = true + +[steps.capture] +key_id = "UniqueIdentifier" + +# Step 3: Auditor (non-owner, same domain) reads GetAttributes → must succeed. +# data-testid scenario: this is the operation that showed N/A in the Web UI +# Locate page before the enforcing-mode Gate 2 fix. +[[steps]] +operation = "GetAttributes" +request = "step3_get_attributes.json" +identity = "auditor" +assert_success = true + +# Step 4: Officer (owner) destroys the key. +[[steps]] +operation = "Destroy" +request = "step4_destroy.json" +allow_failure = true diff --git a/vectors/opa/mode_enforcing_co_get_attributes_allowed/step1_create.json b/vectors/opa/mode_enforcing_co_get_attributes_allowed/step1_create.json new file mode 100644 index 0000000..c994e09 --- /dev/null +++ b/vectors/opa/mode_enforcing_co_get_attributes_allowed/step1_create.json @@ -0,0 +1,40 @@ +{ + "tag": "Create", + "value": [ + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + }, + { + "tag": "Attributes", + "value": [ + { + "tag": "CryptographicAlgorithm", + "type": "Enumeration", + "value": "AES" + }, + { + "tag": "CryptographicLength", + "type": "Integer", + "value": 256 + }, + { + "tag": "CryptographicUsageMask", + "type": "Integer", + "value": 2108 + }, + { + "tag": "KeyFormatType", + "type": "Enumeration", + "value": "TransparentSymmetricKey" + }, + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + } + ] + } + ] +} diff --git a/vectors/opa/mode_enforcing_co_get_attributes_allowed/step3_get_attributes.json b/vectors/opa/mode_enforcing_co_get_attributes_allowed/step3_get_attributes.json new file mode 100644 index 0000000..e96d057 --- /dev/null +++ b/vectors/opa/mode_enforcing_co_get_attributes_allowed/step3_get_attributes.json @@ -0,0 +1,10 @@ +{ + "tag": "GetAttributes", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{key_id}}" + } + ] +} diff --git a/vectors/opa/mode_enforcing_co_get_attributes_allowed/step4_destroy.json b/vectors/opa/mode_enforcing_co_get_attributes_allowed/step4_destroy.json new file mode 100644 index 0000000..ce2dc0e --- /dev/null +++ b/vectors/opa/mode_enforcing_co_get_attributes_allowed/step4_destroy.json @@ -0,0 +1,10 @@ +{ + "tag": "Destroy", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{key_id}}" + } + ] +} diff --git a/vectors/opa/mode_enforcing_empty_roles_denied/manifest.toml b/vectors/opa/mode_enforcing_empty_roles_denied/manifest.toml new file mode 100644 index 0000000..7d91d5c --- /dev/null +++ b/vectors/opa/mode_enforcing_empty_roles_denied/manifest.toml @@ -0,0 +1,24 @@ +name = "OPA Mode 3 (Enforcing): empty JWT roles denied Create" +description = """ +OPA enforcing mode. A bearer token with an empty `roles` claim (and no domain) +is presented to the KMS. OPA evaluates `input.roles = []` against kms.rego: +no allow rule matches → allow=false → KMS returns 401 Unauthorized. + +This proves that a JWT with no role claims (e.g. a misconfigured or legacy token) +cannot bypass Gate 1 even in enforcing mode. + +Ref: kms.rego default deny (NIST SP 800-53 AC-6 least-privilege; fail-closed). +""" +requires_env = ["KMS_OPA_URL", "KMS_AUTH_SERVER_URL", "KMS_TEST_OPA_NO_ROLES_JWT"] +backends = ["sqlite"] + +# Token issued by the auth server for a user with no roles assigned. +[identities.no_roles] +access_token_env = "KMS_TEST_OPA_NO_ROLES_JWT" + +[[steps]] +operation = "Create" +request = "step1_create.json" +identity = "no_roles" +assert_success = false +assert_error_contains = "access-right" diff --git a/vectors/opa/mode_enforcing_empty_roles_denied/step1_create.json b/vectors/opa/mode_enforcing_empty_roles_denied/step1_create.json new file mode 100644 index 0000000..c994e09 --- /dev/null +++ b/vectors/opa/mode_enforcing_empty_roles_denied/step1_create.json @@ -0,0 +1,40 @@ +{ + "tag": "Create", + "value": [ + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + }, + { + "tag": "Attributes", + "value": [ + { + "tag": "CryptographicAlgorithm", + "type": "Enumeration", + "value": "AES" + }, + { + "tag": "CryptographicLength", + "type": "Integer", + "value": 256 + }, + { + "tag": "CryptographicUsageMask", + "type": "Integer", + "value": 2108 + }, + { + "tag": "KeyFormatType", + "type": "Enumeration", + "value": "TransparentSymmetricKey" + }, + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + } + ] + } + ] +} diff --git a/vectors/opa/mode_enforcing_native_co_cert_allowed/manifest.toml b/vectors/opa/mode_enforcing_native_co_cert_allowed/manifest.toml new file mode 100644 index 0000000..18ec553 --- /dev/null +++ b/vectors/opa/mode_enforcing_native_co_cert_allowed/manifest.toml @@ -0,0 +1,38 @@ +name = "OPA Mode 3 (Enforcing): native KMS CO cert user allowed Create (privileged)" +description = """ +OPA enforcing mode. A client authenticated via mTLS (cert CN = owner.client@acme.com) +IS listed as a native KMS Crypto Officer in `[roles] crypto_officer_users`. +Privileged users bypass OPA Gate 1 in enforcing mode — the KMS-native role is +sufficient — so the Create succeeds even though the client presents no JWT. + +Gate 2 (native KMS ownership/grant check) also passes because the user is an +explicitly trusted CO. + +This proves the two-gate enforcing model: privileged native COs can create in +enforcing mode, but NOT in exclusive mode (where OPA is the sole authority). + +Ref: kms.rego enforcing bypass for co_users (FIPS 140-3 §7.4 Crypto Officer role; +NIST SP 800-57 Part 2 §4.3 Key Management Officer). +""" +requires_env = ["KMS_OPA_URL"] +backends = ["sqlite"] + +# mTLS cert client — in crypto_officer_users → KMS bypasses OPA Gate 1. +[identities.co_cert] +client_cert = "test_data/certificates/client_server/owner/owner.client.acme.com.crt" +client_key = "test_data/certificates/client_server/owner/owner.client.acme.com.key" + +[[steps]] +operation = "Create" +request = "step1_create.json" +identity = "co_cert" +assert_success = true + +[steps.capture] +key_id = "UniqueIdentifier" + +[[steps]] +operation = "Destroy" +request = "step3_destroy.json" +identity = "co_cert" +allow_failure = true diff --git a/vectors/opa/mode_enforcing_native_co_cert_allowed/step1_create.json b/vectors/opa/mode_enforcing_native_co_cert_allowed/step1_create.json new file mode 100644 index 0000000..c994e09 --- /dev/null +++ b/vectors/opa/mode_enforcing_native_co_cert_allowed/step1_create.json @@ -0,0 +1,40 @@ +{ + "tag": "Create", + "value": [ + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + }, + { + "tag": "Attributes", + "value": [ + { + "tag": "CryptographicAlgorithm", + "type": "Enumeration", + "value": "AES" + }, + { + "tag": "CryptographicLength", + "type": "Integer", + "value": 256 + }, + { + "tag": "CryptographicUsageMask", + "type": "Integer", + "value": 2108 + }, + { + "tag": "KeyFormatType", + "type": "Enumeration", + "value": "TransparentSymmetricKey" + }, + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + } + ] + } + ] +} diff --git a/vectors/opa/mode_enforcing_native_co_cert_allowed/step3_destroy.json b/vectors/opa/mode_enforcing_native_co_cert_allowed/step3_destroy.json new file mode 100644 index 0000000..ce2dc0e --- /dev/null +++ b/vectors/opa/mode_enforcing_native_co_cert_allowed/step3_destroy.json @@ -0,0 +1,10 @@ +{ + "tag": "Destroy", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{key_id}}" + } + ] +} diff --git a/vectors/opa/mode_enforcing_unknown_role_denied/manifest.toml b/vectors/opa/mode_enforcing_unknown_role_denied/manifest.toml new file mode 100644 index 0000000..57f4f2c --- /dev/null +++ b/vectors/opa/mode_enforcing_unknown_role_denied/manifest.toml @@ -0,0 +1,25 @@ +name = "OPA Mode 3 (Enforcing): unknown JWT role denied Create" +description = """ +OPA enforcing mode. A bearer token carrying an unrecognised role `Hacker` +(not in kms.rego's role hierarchy) is presented. OPA finds no allow rule for +this role → allow=false → KMS returns 401 Unauthorized. + +This proves that an arbitrary or fabricated role claim cannot grant access: +the policy is allow-list based, not deny-list based. + +Ref: kms.rego default deny (ANSI/INCITS 359 §4.2 RBAC named-role principle; +NIST SP 800-53 AC-6 least-privilege). +""" +requires_env = ["KMS_OPA_URL", "KMS_AUTH_SERVER_URL", "KMS_TEST_OPA_UNKNOWN_ROLE_JWT"] +backends = ["sqlite"] + +# Token with a role name that does not appear in kms.rego's allow rules. +[identities.unknown_role] +access_token_env = "KMS_TEST_OPA_UNKNOWN_ROLE_JWT" + +[[steps]] +operation = "Create" +request = "step1_create.json" +identity = "unknown_role" +assert_success = false +assert_error_contains = "access-right" diff --git a/vectors/opa/mode_enforcing_unknown_role_denied/step1_create.json b/vectors/opa/mode_enforcing_unknown_role_denied/step1_create.json new file mode 100644 index 0000000..c994e09 --- /dev/null +++ b/vectors/opa/mode_enforcing_unknown_role_denied/step1_create.json @@ -0,0 +1,40 @@ +{ + "tag": "Create", + "value": [ + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + }, + { + "tag": "Attributes", + "value": [ + { + "tag": "CryptographicAlgorithm", + "type": "Enumeration", + "value": "AES" + }, + { + "tag": "CryptographicLength", + "type": "Integer", + "value": 256 + }, + { + "tag": "CryptographicUsageMask", + "type": "Integer", + "value": 2108 + }, + { + "tag": "KeyFormatType", + "type": "Enumeration", + "value": "TransparentSymmetricKey" + }, + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + } + ] + } + ] +} diff --git a/vectors/opa/mode_exclusive_native_co_cert_denied/manifest.toml b/vectors/opa/mode_exclusive_native_co_cert_denied/manifest.toml new file mode 100644 index 0000000..73c98cc --- /dev/null +++ b/vectors/opa/mode_exclusive_native_co_cert_denied/manifest.toml @@ -0,0 +1,28 @@ +name = "OPA Mode 2 (Exclusive): native KMS CO cert user denied Create (no JWT)" +description = """ +OPA exclusive mode. A client authenticated via mTLS (cert CN = owner.client@acme.com) +is NOT listed as a privileged/crypto-officer user in the server configuration. +Because no JWT is presented, OPA receives `input.roles = []` and no allow rule +fires → allow=false → KMS returns 401 Unauthorized. + +This proves that in exclusive OPA mode, even mTLS-authenticated clients are +subject to OPA policy when they are not explicitly whitelisted as native COs: +the KMS legacy privilege bypass does not apply. + +Ref: kms.rego default deny; OPA Mode 2 "OPA is sole authority" design contract. +""" +requires_env = ["KMS_OPA_URL"] +backends = ["sqlite"] + +# mTLS cert client — authenticated but no JWT → empty roles → OPA denies. +# The server is configured WITHOUT this user in crypto_officer_users. +[identities.co_cert] +client_cert = "test_data/certificates/client_server/owner/owner.client.acme.com.crt" +client_key = "test_data/certificates/client_server/owner/owner.client.acme.com.key" + +[[steps]] +operation = "Create" +request = "step1_create.json" +identity = "co_cert" +assert_success = false +assert_error_contains = "access-right" diff --git a/vectors/opa/mode_exclusive_native_co_cert_denied/step1_create.json b/vectors/opa/mode_exclusive_native_co_cert_denied/step1_create.json new file mode 100644 index 0000000..c994e09 --- /dev/null +++ b/vectors/opa/mode_exclusive_native_co_cert_denied/step1_create.json @@ -0,0 +1,40 @@ +{ + "tag": "Create", + "value": [ + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + }, + { + "tag": "Attributes", + "value": [ + { + "tag": "CryptographicAlgorithm", + "type": "Enumeration", + "value": "AES" + }, + { + "tag": "CryptographicLength", + "type": "Integer", + "value": 256 + }, + { + "tag": "CryptographicUsageMask", + "type": "Integer", + "value": 2108 + }, + { + "tag": "KeyFormatType", + "type": "Enumeration", + "value": "TransparentSymmetricKey" + }, + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + } + ] + } + ] +} diff --git a/vectors/opa/mode_exclusive_other_domain_allowed/manifest.toml b/vectors/opa/mode_exclusive_other_domain_allowed/manifest.toml new file mode 100644 index 0000000..13fb7d8 --- /dev/null +++ b/vectors/opa/mode_exclusive_other_domain_allowed/manifest.toml @@ -0,0 +1,42 @@ +name = "OPA Mode 2 (Exclusive): other-domain owner key lifecycle allowed (multi-tenancy positive)" +description = """ +OPA exclusive mode. A CryptoOfficer from realm `kms-opa-other` (domain=kms-opa-other) +creates an AES key **in their own domain**, then retrieves and destroys it. + +This is the multi-tenancy positive counterpart to `mode_exclusive_wrong_domain`: it +verifies that domain isolation does NOT block legitimate intra-domain operations. +The `same_domain` check succeeds because `user_domain == object_domain == kms-opa-other`. + +Ref: kms.rego same_domain helper (ANSI/INCITS 359-2004 §4.2 Constrained RBAC; +NIST SP 800-53 Rev 5 AC-6 least privilege; domain isolation requirement). +""" +requires_env = ["KMS_OPA_URL", "KMS_AUTH_SERVER_URL", "KMS_TEST_OPA_OTHER_DOMAIN_JWT"] +backends = ["sqlite"] + +# JWT-based identity: other-domain CryptoOfficer (kms-opa-other realm). +[identities.other_domain] +access_token_env = "KMS_TEST_OPA_OTHER_DOMAIN_JWT" + +# Step 1: other-domain officer creates their own key. +[[steps]] +operation = "Create" +request = "step1_create.json" +identity = "other_domain" +assert_success = true + +[steps.capture] +key_id = "UniqueIdentifier" + +# Step 2: same officer retrieves their own key (is_owner=true, same domain → allowed). +[[steps]] +operation = "Get" +request = "step2_get.json" +identity = "other_domain" +assert_success = true + +# Step 3: same officer destroys their own key. +[[steps]] +operation = "Destroy" +request = "step3_destroy.json" +identity = "other_domain" +assert_success = true diff --git a/vectors/opa/mode_exclusive_other_domain_allowed/step1_create.json b/vectors/opa/mode_exclusive_other_domain_allowed/step1_create.json new file mode 100644 index 0000000..c994e09 --- /dev/null +++ b/vectors/opa/mode_exclusive_other_domain_allowed/step1_create.json @@ -0,0 +1,40 @@ +{ + "tag": "Create", + "value": [ + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + }, + { + "tag": "Attributes", + "value": [ + { + "tag": "CryptographicAlgorithm", + "type": "Enumeration", + "value": "AES" + }, + { + "tag": "CryptographicLength", + "type": "Integer", + "value": 256 + }, + { + "tag": "CryptographicUsageMask", + "type": "Integer", + "value": 2108 + }, + { + "tag": "KeyFormatType", + "type": "Enumeration", + "value": "TransparentSymmetricKey" + }, + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + } + ] + } + ] +} diff --git a/vectors/opa/mode_exclusive_other_domain_allowed/step2_get.json b/vectors/opa/mode_exclusive_other_domain_allowed/step2_get.json new file mode 100644 index 0000000..63a255e --- /dev/null +++ b/vectors/opa/mode_exclusive_other_domain_allowed/step2_get.json @@ -0,0 +1,10 @@ +{ + "tag": "Get", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{key_id}}" + } + ] +} diff --git a/vectors/opa/mode_exclusive_other_domain_allowed/step3_destroy.json b/vectors/opa/mode_exclusive_other_domain_allowed/step3_destroy.json new file mode 100644 index 0000000..ce2dc0e --- /dev/null +++ b/vectors/opa/mode_exclusive_other_domain_allowed/step3_destroy.json @@ -0,0 +1,10 @@ +{ + "tag": "Destroy", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{key_id}}" + } + ] +} From 5687f99cc1f110a61b1b0377c1ed23511b404757 Mon Sep 17 00:00:00 2001 From: Manuthor Date: Wed, 19 Aug 2026 12:04:14 +0200 Subject: [PATCH 10/17] fix: restore crypto_officer_users.toml accidentally deleted --- configs/server/crypto_officer_users.toml | 35 ++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 configs/server/crypto_officer_users.toml diff --git a/configs/server/crypto_officer_users.toml b/configs/server/crypto_officer_users.toml new file mode 100644 index 0000000..69802ba --- /dev/null +++ b/configs/server/crypto_officer_users.toml @@ -0,0 +1,35 @@ +# Test KMS server — TLS + JWT auth, for privileged-user tests. +# Uses PEM cert + key (works in both FIPS and non-FIPS mode). +# Bound to port 10003. crypto_officer_users is injected at runtime. + +default_username = "tech@cosmian.com" +kms_public_url = "https://localhost:10003" + +[http] +port = 10003 +hostname = "0.0.0.0" + +[db] +database_type = "sqlite" +sqlite_path = "/tmp/kms_test_crypto_officer_users" +clear_database = true + +[workspace] +root_data_path = "/tmp/kms_test_workspace_crypto_officer_users" +tmp_path = "/tmp" + +[tls] +tls_cert_file = "test_data/certificates/client_server/server/kmserver.acme.com.crt" +tls_key_file = "test_data/certificates/client_server/server/kmserver.acme.com.key" +clients_ca_cert_file = "test_data/certificates/client_server/ca/ca.crt" + +[socket_server] +socket_server_start = true +socket_server_port = 10103 + +[idp_auth] +jwt_auth_provider = [ + "https://accounts.google.com,https://www.googleapis.com/oauth2/v3/certs,kacls-migration,996739510374-2tauakcggau7kvk37784k0s2lmveb9c9.apps.googleusercontent.com", + "https://login.microsoftonline.com/612da4de-35c0-42de-ba56-174b69062c96/v2.0,https://login.microsoftonline.com/612da4de-35c0-42de-ba56-174b69062c96/discovery/v2.0/keys", + "https://demo-kms.eu.auth0.com/,https://demo-kms.eu.auth0.com/.well-known/jwks.json", +] From c311e8bb543da4b35ec754b43199b7c762cd2f1b Mon Sep 17 00:00:00 2001 From: Manuthor Date: Wed, 19 Aug 2026 12:06:00 +0200 Subject: [PATCH 11/17] fix: restore config files accidentally removed during OPA test reorganization --- configs/server/auth/verifier.toml | 23 +++++++ configs/server/hsm.toml | 28 +++++++++ configs/server/non_revocable.toml | 24 ++++++++ configs/server/pqc_tls.toml | 27 +++++++++ configs/server/test/auth_cosmian.toml | 31 ++++++++++ configs/server/test/auth_ui.toml | 25 ++++++++ configs/server/test/auth_ui_multi_method.toml | 60 +++++++++++++++++++ 7 files changed, 218 insertions(+) create mode 100644 configs/server/auth/verifier.toml create mode 100644 configs/server/hsm.toml create mode 100644 configs/server/non_revocable.toml create mode 100644 configs/server/pqc_tls.toml create mode 100644 configs/server/test/auth_cosmian.toml create mode 100644 configs/server/test/auth_ui.toml create mode 100644 configs/server/test/auth_ui_multi_method.toml diff --git a/configs/server/auth/verifier.toml b/configs/server/auth/verifier.toml new file mode 100644 index 0000000..c8490e9 --- /dev/null +++ b/configs/server/auth/verifier.toml @@ -0,0 +1,23 @@ +# Test KMS server — plain HTTP with Auth Verifier (Cosmian authentication server). +# Bound to port 12006. +# Loaded by tests that verify auth_verifier_url round-trip parsing. + +default_username = "tech@cosmian.com" + +[http] +port = 12006 +hostname = "0.0.0.0" + +[db] +database_type = "sqlite" +sqlite_path = "./sqlite-data-auth-verifier-tests" +clear_database = true + +[workspace] +root_data_path = "/tmp/kms_test_workspace_12006" +tmp_path = "/tmp" + +[auth_verifier] +auth_verifier_url = "https://localhost:8443" +auth_verifier_accept_invalid_certs = true +auth_verifier_realm = "_" diff --git a/configs/server/hsm.toml b/configs/server/hsm.toml new file mode 100644 index 0000000..8330934 --- /dev/null +++ b/configs/server/hsm.toml @@ -0,0 +1,28 @@ +# Test KMS server — plain HTTP with Utimaco HSM. +# Bound to port 10001. Loaded by start_default_test_kms_server_with_utimaco_hsm(). +# HSM fields are at the top level because HsmConfig is flattened into ClapConfig. + +default_username = "tech@cosmian.com" +kms_public_url = "http://localhost:10001/google_cse" +hsm_model = "utimaco" +hsm_admin = ["tech@cosmian.com"] +hsm_slot = [0] +hsm_password = ["12345678"] + +[http] +port = 10001 +hostname = "0.0.0.0" + +[db] +database_type = "sqlite" +sqlite_path = "/tmp/kms_test_hsm" +clear_database = true + +[workspace] +root_data_path = "/tmp/kms_test_workspace_hsm" +tmp_path = "/tmp" + +[google_cse_config] +google_cse_enable = true +google_cse_disable_tokens_validation = true +google_cse_incoming_url_whitelist = ["https://cse.cosmian.com"] diff --git a/configs/server/non_revocable.toml b/configs/server/non_revocable.toml new file mode 100644 index 0000000..e1f204f --- /dev/null +++ b/configs/server/non_revocable.toml @@ -0,0 +1,24 @@ +# Test KMS server — plain HTTP, no authentication, for non-revocable key tests. +# Bound to port 10000. non_revocable_key_id is injected at runtime. +# Loaded by start_default_test_kms_server_with_non_revocable_key_ids(). + +default_username = "tech@cosmian.com" +kms_public_url = "http://localhost:10000/google_cse" + +[http] +port = 10000 +hostname = "0.0.0.0" + +[db] +database_type = "sqlite" +sqlite_path = "/tmp/kms_test_non_revocable" +clear_database = true + +[workspace] +root_data_path = "/tmp/kms_test_workspace_non_revocable" +tmp_path = "/tmp" + +[google_cse_config] +google_cse_enable = true +google_cse_disable_tokens_validation = true +google_cse_incoming_url_whitelist = ["https://cse.cosmian.com"] diff --git a/configs/server/pqc_tls.toml b/configs/server/pqc_tls.toml new file mode 100644 index 0000000..ce9490a --- /dev/null +++ b/configs/server/pqc_tls.toml @@ -0,0 +1,27 @@ +# Test KMS server — HTTPS with a PQC (ML-DSA-44) TLS server certificate. +# Requires a TLS client that supports ML-DSA-44 (OpenSSL >= 3.5 or rustls with PQC). +# Uses PEM cert + key generated by OpenSSL 3.5+ (stored in test_data/certificates/). +# Non-FIPS only (PQC algorithms are not available in FIPS mode). + +default_username = "tech@cosmian.com" + +[http] +port = 9998 +hostname = "0.0.0.0" + +[db] +database_type = "sqlite" +sqlite_path = "/tmp/kms_test_pqc_tls" +clear_database = true + +[workspace] +root_data_path = "/tmp/kms_test_workspace_pqc_tls" +tmp_path = "/tmp" + +[tls] +tls_cert_file = "test_data/certificates/client_server/server/ml_dsa_44.crt" +tls_key_file = "test_data/certificates/client_server/server/ml_dsa_44.key" +# No clients_ca_cert_file — mutual TLS is not enforced. + +[logging] +rust_log = "off" diff --git a/configs/server/test/auth_cosmian.toml b/configs/server/test/auth_cosmian.toml new file mode 100644 index 0000000..6249a84 --- /dev/null +++ b/configs/server/test/auth_cosmian.toml @@ -0,0 +1,31 @@ +# Minimal default configuration from quick_start.md +# Manual test config for the Cosmian authentication server ("AS" / "SA") Web UI login +# (`POST /ui/login_as`). Pair with a locally running `authentication` server, e.g. +# from the sibling `authentication` repo: +# +# cargo run -p auth_server -- server/auth_server.dev.toml +# +# which listens on https://localhost:8443 with a self-signed dev certificate and a +# default admin realm ("_") — hence `cosmian_auth_accept_invalid_certs = true` below. +# See CHANGELOG/test_plan_cosmian-auth-ui-login.md for the full manual test plan. +vendor_identification = "test_vendor" + +[http] +port = 9998 +hostname = "localhost" +kms_public_url = "http://localhost:9998" + +[cosmian_auth] +cosmian_auth_server_url = "https://localhost:8443" +cosmian_auth_realm = "_" +cosmian_auth_accept_invalid_certs = true + +[ui_config] +ui_index_html_folder = "ui/dist" + +[logging] +rust_log = "info,cosmian_kms=debug" +quiet = false +log_to_syslog = false +environment = "development" +ansi_colors = true diff --git a/configs/server/test/auth_ui.toml b/configs/server/test/auth_ui.toml new file mode 100644 index 0000000..3442a33 --- /dev/null +++ b/configs/server/test/auth_ui.toml @@ -0,0 +1,25 @@ +# Minimal default configuration from quick_start.md +vendor_identification = "test_vendor" +kms_public_url = "http://localhost:9998" + +[http] +port = 9998 +hostname = "localhost" + +[idp_auth] +jwt_auth_provider = ["https://accounts.google.com,https://www.googleapis.com/oauth2/v3/certs,", "https://demo-kms.eu.auth0.com/,https://demo-kms.eu.auth0.com/.well-known/jwks.json,"] + +[ui_config.ui_oidc_auth] +ui_oidc_client_id = "mlTpGfub8n8PU2PsBQVdxp2VOyxtkL9m" +ui_oidc_issuer_url = "https://demo-kms.eu.auth0.com/" +ui_oidc_logout_url = "https://demo-kms.eu.auth0.com/logout" + +[ui_config] +ui_index_html_folder = "ui/dist" + +[logging] +rust_log = "info,cosmian_kms=debug" +quiet = false +log_to_syslog = false +environment = "development" +ansi_colors = true diff --git a/configs/server/test/auth_ui_multi_method.toml b/configs/server/test/auth_ui_multi_method.toml new file mode 100644 index 0000000..4f1e004 --- /dev/null +++ b/configs/server/test/auth_ui_multi_method.toml @@ -0,0 +1,60 @@ +# Manual test config: Web UI login with all three browser-login methods enabled +# simultaneously (OIDC, auth-verifier username/password, client certificate). +# Corresponds to Scenario C in the multi-method login-page test plan. +# +# Prerequisites: +# 1. Start the auth-verifier (from the authentication/ sibling repo): +# cargo run -p auth_verifier -- server/auth_verifier.dev.toml +# Admin UI at https://127.0.0.1:8443/admin-ui — create a test user in realm "_". +# +# 2. The TLS section below uses the repo's bundled test certificates. +# They work for connections to 127.0.0.1 and localhost in test environments. +# For a custom hostname, replace the paths and update kms_public_url. +# +# 3. Register "https://localhost:9998/ui/callback" as an allowed callback URL +# in the OIDC application (Auth0 or other IdP). +# +# Start the KMS: +# cargo run --bin cosmian_kms -- -c test_data/configs/server/test/auth_ui_multi_method.toml +# +# Expected GET /ui/auth_method response: +# {"auth_method":"JWT","auth_methods":["JWT","AUTH_VERIFIER","CERT"]} + +vendor_identification = "test_vendor" +kms_public_url = "https://localhost:9998" + +[http] +port = 9998 +hostname = "localhost" + +[tls] +tls_cert_file = "test_data/certificates/client_server/server/kmserver.acme.com.crt" +tls_key_file = "test_data/certificates/client_server/server/kmserver.acme.com.key" +clients_ca_cert_file = "test_data/certificates/client_server/ca/ca.crt" + +[idp_auth] +jwt_auth_provider = [ + "https://demo-kms.eu.auth0.com/,https://demo-kms.eu.auth0.com/.well-known/jwks.json,", +] + +[auth_verifier] +auth_verifier_url = "https://127.0.0.1:8443" +auth_verifier_realm = "_" +auth_verifier_accept_invalid_certs = true + +[ui_config.ui_oidc_auth] +ui_oidc_client_id = "mlTpGfub8n8PU2PsBQVdxp2VOyxtkL9m" +ui_oidc_issuer_url = "https://demo-kms.eu.auth0.com/" +ui_oidc_logout_url = "https://demo-kms.eu.auth0.com/logout" + +[ui_config] +ui_index_html_folder = "ui/dist" +# Must be identical across all KMS instances behind the same load balancer. +ui_session_salt = "kms-test-local-salt-do-not-use-in-production" + +[logging] +rust_log = "info,cosmian_kms=debug" +quiet = false +log_to_syslog = false +environment = "development" +ansi_colors = true From 22ed087eb34758110353ef4f04e70fec9974e964 Mon Sep 17 00:00:00 2001 From: Manuthor Date: Thu, 20 Aug 2026 10:42:00 +0200 Subject: [PATCH 12/17] fix: sort TOML conf --- .../server/{hsm.toml => hsm/hsm_test.toml} | 0 configs/server/pqc_tls.toml | 27 ------------------- .../{ => rbac}/crypto_officer_users.toml | 0 configs/server/{ => test}/non_revocable.toml | 0 4 files changed, 27 deletions(-) rename configs/server/{hsm.toml => hsm/hsm_test.toml} (100%) delete mode 100644 configs/server/pqc_tls.toml rename configs/server/{ => rbac}/crypto_officer_users.toml (100%) rename configs/server/{ => test}/non_revocable.toml (100%) diff --git a/configs/server/hsm.toml b/configs/server/hsm/hsm_test.toml similarity index 100% rename from configs/server/hsm.toml rename to configs/server/hsm/hsm_test.toml diff --git a/configs/server/pqc_tls.toml b/configs/server/pqc_tls.toml deleted file mode 100644 index ce9490a..0000000 --- a/configs/server/pqc_tls.toml +++ /dev/null @@ -1,27 +0,0 @@ -# Test KMS server — HTTPS with a PQC (ML-DSA-44) TLS server certificate. -# Requires a TLS client that supports ML-DSA-44 (OpenSSL >= 3.5 or rustls with PQC). -# Uses PEM cert + key generated by OpenSSL 3.5+ (stored in test_data/certificates/). -# Non-FIPS only (PQC algorithms are not available in FIPS mode). - -default_username = "tech@cosmian.com" - -[http] -port = 9998 -hostname = "0.0.0.0" - -[db] -database_type = "sqlite" -sqlite_path = "/tmp/kms_test_pqc_tls" -clear_database = true - -[workspace] -root_data_path = "/tmp/kms_test_workspace_pqc_tls" -tmp_path = "/tmp" - -[tls] -tls_cert_file = "test_data/certificates/client_server/server/ml_dsa_44.crt" -tls_key_file = "test_data/certificates/client_server/server/ml_dsa_44.key" -# No clients_ca_cert_file — mutual TLS is not enforced. - -[logging] -rust_log = "off" diff --git a/configs/server/crypto_officer_users.toml b/configs/server/rbac/crypto_officer_users.toml similarity index 100% rename from configs/server/crypto_officer_users.toml rename to configs/server/rbac/crypto_officer_users.toml diff --git a/configs/server/non_revocable.toml b/configs/server/test/non_revocable.toml similarity index 100% rename from configs/server/non_revocable.toml rename to configs/server/test/non_revocable.toml From 57b6389185b8b5d082d54bcddf4ec4e9205d673d Mon Sep 17 00:00:00 2001 From: Manuthor Date: Sat, 22 Aug 2026 06:45:41 +0200 Subject: [PATCH 13/17] fix: add UI to TOML conf --- configs/server/rbac/crypto_officer_users.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/configs/server/rbac/crypto_officer_users.toml b/configs/server/rbac/crypto_officer_users.toml index 69802ba..08fa892 100644 --- a/configs/server/rbac/crypto_officer_users.toml +++ b/configs/server/rbac/crypto_officer_users.toml @@ -33,3 +33,6 @@ jwt_auth_provider = [ "https://login.microsoftonline.com/612da4de-35c0-42de-ba56-174b69062c96/v2.0,https://login.microsoftonline.com/612da4de-35c0-42de-ba56-174b69062c96/discovery/v2.0/keys", "https://demo-kms.eu.auth0.com/,https://demo-kms.eu.auth0.com/.well-known/jwks.json", ] + +[ui_config] +ui_index_html_folder = "ui/dist" From e90142ba16d463b5cfb45bfa23b88ea71e45d113 Mon Sep 17 00:00:00 2001 From: Manuthor Date: Sun, 23 Aug 2026 20:27:50 +0200 Subject: [PATCH 14/17] feat: add unit tests for all OPA modules --- .../mode_enforcing_wrong_domain/manifest.toml | 49 +++++++++++++++++ .../step1_create.json | 40 ++++++++++++++ .../step2_get.json | 10 ++++ .../step3_destroy.json | 10 ++++ .../manifest.toml | 55 +++++++++++++++++++ .../step1_create.json | 40 ++++++++++++++ .../step3_destroy.json | 10 ++++ .../step3_get_attributes.json | 10 ++++ .../manifest.toml | 55 +++++++++++++++++++ .../step1_create.json | 40 ++++++++++++++ .../step2_get.json | 10 ++++ .../step3_destroy.json | 10 ++++ .../manifest.toml | 55 +++++++++++++++++++ .../step1_create.json | 40 ++++++++++++++ .../step3_destroy.json | 10 ++++ .../step3_get_attributes.json | 10 ++++ 16 files changed, 454 insertions(+) create mode 100644 vectors/opa/mode_enforcing_wrong_domain/manifest.toml create mode 100644 vectors/opa/mode_enforcing_wrong_domain/step1_create.json create mode 100644 vectors/opa/mode_enforcing_wrong_domain/step2_get.json create mode 100644 vectors/opa/mode_enforcing_wrong_domain/step3_destroy.json create mode 100644 vectors/opa/mode_exclusive_auditor_wrong_domain/manifest.toml create mode 100644 vectors/opa/mode_exclusive_auditor_wrong_domain/step1_create.json create mode 100644 vectors/opa/mode_exclusive_auditor_wrong_domain/step3_destroy.json create mode 100644 vectors/opa/mode_exclusive_auditor_wrong_domain/step3_get_attributes.json create mode 100644 vectors/opa/mode_exclusive_super_admin_cross_domain/manifest.toml create mode 100644 vectors/opa/mode_exclusive_super_admin_cross_domain/step1_create.json create mode 100644 vectors/opa/mode_exclusive_super_admin_cross_domain/step2_get.json create mode 100644 vectors/opa/mode_exclusive_super_admin_cross_domain/step3_destroy.json create mode 100644 vectors/opa/mode_exclusive_user_wrong_domain/manifest.toml create mode 100644 vectors/opa/mode_exclusive_user_wrong_domain/step1_create.json create mode 100644 vectors/opa/mode_exclusive_user_wrong_domain/step3_destroy.json create mode 100644 vectors/opa/mode_exclusive_user_wrong_domain/step3_get_attributes.json diff --git a/vectors/opa/mode_enforcing_wrong_domain/manifest.toml b/vectors/opa/mode_enforcing_wrong_domain/manifest.toml new file mode 100644 index 0000000..ceeca15 --- /dev/null +++ b/vectors/opa/mode_enforcing_wrong_domain/manifest.toml @@ -0,0 +1,49 @@ +name = "OPA Mode 3 (Enforcing): CryptoOfficer wrong domain denied (cross-domain isolation in dual-gate mode)" +description = """ +OPA enforcing (dual-gate) mode — multi-tenant isolation. + +The default CryptoOfficer (kms-opa-test, domain=kms-opa-test) creates an AES key. +A CryptoOfficer from realm `kms-opa-other` (domain=kms-opa-other, non-owner) attempts +`Get` on that key. In enforcing mode, BOTH the OPA gate AND the native KMS permission +check must allow. The OPA gate fails first: `same_domain` check returns false because +`user_domain (kms-opa-other) != object_domain (kms-opa-test)` → OPA returns +`allow=false` → KMS returns Object_Not_Found. + +The existing `mode_exclusive_wrong_domain` covers Mode 2 (exclusive). This vector +proves that domain isolation is not an exclusive-mode artefact — it is enforced in +Mode 3 as well, which is the recommended production mode. + +Ref: kms.rego same_domain helper (ANSI/INCITS 359-2004 §4.2 Constrained RBAC; +NIST SP 800-53 Rev 5 AC-3 access enforcement, AC-4 information flow enforcement; +NIST SP 800-53 Rev 5 SC-28 protection of information at rest). +""" +requires_env = ["KMS_OPA_URL", "KMS_AUTH_SERVER_URL"] +backends = ["sqlite"] + +# JWT-based identity: other-domain CryptoOfficer (kms-opa-other). +[identities.other_domain] +access_token_env = "KMS_TEST_OPA_OTHER_DOMAIN_JWT" + +# Step 1: default client (test-domain CO) creates the key. +[[steps]] +operation = "Create" +request = "step1_create.json" +assert_success = true + +[steps.capture] +key_id = "UniqueIdentifier" + +# Step 2: other-domain CO (non-owner, wrong domain) attempts Get — OPA gate denies. +[[steps]] +operation = "Get" +request = "step2_get.json" +identity = "other_domain" +assert_success = false +assert_error_reason = "Object_Not_Found" +assert_error_contains = "not found" + +# Cleanup: test-domain CO (owner) destroys the key. +[[steps]] +operation = "Destroy" +request = "step3_destroy.json" +allow_failure = true diff --git a/vectors/opa/mode_enforcing_wrong_domain/step1_create.json b/vectors/opa/mode_enforcing_wrong_domain/step1_create.json new file mode 100644 index 0000000..c994e09 --- /dev/null +++ b/vectors/opa/mode_enforcing_wrong_domain/step1_create.json @@ -0,0 +1,40 @@ +{ + "tag": "Create", + "value": [ + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + }, + { + "tag": "Attributes", + "value": [ + { + "tag": "CryptographicAlgorithm", + "type": "Enumeration", + "value": "AES" + }, + { + "tag": "CryptographicLength", + "type": "Integer", + "value": 256 + }, + { + "tag": "CryptographicUsageMask", + "type": "Integer", + "value": 2108 + }, + { + "tag": "KeyFormatType", + "type": "Enumeration", + "value": "TransparentSymmetricKey" + }, + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + } + ] + } + ] +} diff --git a/vectors/opa/mode_enforcing_wrong_domain/step2_get.json b/vectors/opa/mode_enforcing_wrong_domain/step2_get.json new file mode 100644 index 0000000..63a255e --- /dev/null +++ b/vectors/opa/mode_enforcing_wrong_domain/step2_get.json @@ -0,0 +1,10 @@ +{ + "tag": "Get", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{key_id}}" + } + ] +} diff --git a/vectors/opa/mode_enforcing_wrong_domain/step3_destroy.json b/vectors/opa/mode_enforcing_wrong_domain/step3_destroy.json new file mode 100644 index 0000000..ce2dc0e --- /dev/null +++ b/vectors/opa/mode_enforcing_wrong_domain/step3_destroy.json @@ -0,0 +1,10 @@ +{ + "tag": "Destroy", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{key_id}}" + } + ] +} diff --git a/vectors/opa/mode_exclusive_auditor_wrong_domain/manifest.toml b/vectors/opa/mode_exclusive_auditor_wrong_domain/manifest.toml new file mode 100644 index 0000000..586ecc2 --- /dev/null +++ b/vectors/opa/mode_exclusive_auditor_wrong_domain/manifest.toml @@ -0,0 +1,55 @@ +name = "OPA Mode 2 (Exclusive): Auditor wrong domain denied (cross-domain isolation)" +description = """ +OPA exclusive mode — multi-tenant isolation. + +A CryptoOfficer from realm `kms-opa-other` (domain=kms-opa-other) creates an AES key. +An Auditor from realm `kms-opa-test` (domain=kms-opa-test) then calls `GetAttributes` +on that key. Even though `GetAttributes` is in `auditor_ops`, the `same_domain` +helper in kms.rego fails because `user_domain (kms-opa-test) != object_domain +(kms-opa-other)` → OPA returns `allow=false` → KMS returns Object_Not_Found. + +This test completes the multi-tenant isolation matrix for the Auditor role: the +existing `mode_exclusive_auditor_get_attributes_allowed` vector proves an Auditor CAN +read key metadata within their own domain; this vector proves they CANNOT read across +domain boundaries — even for read-only metadata operations. + +Ref: kms.rego same_domain helper (ANSI/INCITS 359-2004 §4.2 Constrained RBAC; +NIST SP 800-53 Rev 5 AC-3 access enforcement, AC-4 information flow enforcement; +NIST SP 800-57 Part 2 §4.3 Audit and Compliance Officer role). +""" +requires_env = ["KMS_OPA_URL", "KMS_AUTH_SERVER_URL"] +backends = ["sqlite"] + +# other-domain CryptoOfficer creates the key (kms-opa-other domain). +[identities.other_domain] +access_token_env = "KMS_TEST_OPA_OTHER_DOMAIN_JWT" + +# kms-opa-test Auditor — domain mismatch triggers isolation. +[identities.auditor] +access_token_env = "KMS_TEST_OPA_AUDITOR_JWT" + +# Step 1: other-domain CO creates the key (object_domain = kms-opa-other). +[[steps]] +operation = "Create" +request = "step1_create.json" +identity = "other_domain" +assert_success = true + +[steps.capture] +key_id = "UniqueIdentifier" + +# Step 2: test-domain Auditor tries GetAttributes — same_domain fails → denied. +[[steps]] +operation = "GetAttributes" +request = "step3_get_attributes.json" +identity = "auditor" +assert_success = false +assert_error_reason = "Object_Not_Found" +assert_error_contains = "not found" + +# Cleanup: other-domain CO (owner) destroys the key. +[[steps]] +operation = "Destroy" +request = "step3_destroy.json" +identity = "other_domain" +allow_failure = true diff --git a/vectors/opa/mode_exclusive_auditor_wrong_domain/step1_create.json b/vectors/opa/mode_exclusive_auditor_wrong_domain/step1_create.json new file mode 100644 index 0000000..c994e09 --- /dev/null +++ b/vectors/opa/mode_exclusive_auditor_wrong_domain/step1_create.json @@ -0,0 +1,40 @@ +{ + "tag": "Create", + "value": [ + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + }, + { + "tag": "Attributes", + "value": [ + { + "tag": "CryptographicAlgorithm", + "type": "Enumeration", + "value": "AES" + }, + { + "tag": "CryptographicLength", + "type": "Integer", + "value": 256 + }, + { + "tag": "CryptographicUsageMask", + "type": "Integer", + "value": 2108 + }, + { + "tag": "KeyFormatType", + "type": "Enumeration", + "value": "TransparentSymmetricKey" + }, + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + } + ] + } + ] +} diff --git a/vectors/opa/mode_exclusive_auditor_wrong_domain/step3_destroy.json b/vectors/opa/mode_exclusive_auditor_wrong_domain/step3_destroy.json new file mode 100644 index 0000000..ce2dc0e --- /dev/null +++ b/vectors/opa/mode_exclusive_auditor_wrong_domain/step3_destroy.json @@ -0,0 +1,10 @@ +{ + "tag": "Destroy", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{key_id}}" + } + ] +} diff --git a/vectors/opa/mode_exclusive_auditor_wrong_domain/step3_get_attributes.json b/vectors/opa/mode_exclusive_auditor_wrong_domain/step3_get_attributes.json new file mode 100644 index 0000000..e96d057 --- /dev/null +++ b/vectors/opa/mode_exclusive_auditor_wrong_domain/step3_get_attributes.json @@ -0,0 +1,10 @@ +{ + "tag": "GetAttributes", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{key_id}}" + } + ] +} diff --git a/vectors/opa/mode_exclusive_super_admin_cross_domain/manifest.toml b/vectors/opa/mode_exclusive_super_admin_cross_domain/manifest.toml new file mode 100644 index 0000000..210b243 --- /dev/null +++ b/vectors/opa/mode_exclusive_super_admin_cross_domain/manifest.toml @@ -0,0 +1,55 @@ +name = "OPA Mode 2 (Exclusive): SuperAdmin cross-domain access allowed (isolation bypass for top role)" +description = """ +OPA exclusive mode — SuperAdmin cross-domain positive test. + +A CryptoOfficer from realm `kms-opa-other` (domain=kms-opa-other) creates an AES key. +A SuperAdmin (kms-opa-test realm, no domain restriction) then calls `Get` and `Destroy` +on that key and both succeed. + +The `SuperAdmin` rule in kms.rego is unconditional: `allow if { input.roles[_] == +"SuperAdmin" }`. The `same_domain` helper is NOT invoked for this role, so +`user_domain (kms-opa-test) != object_domain (kms-opa-other)` does not block access. + +This is the positive counterpart to `mode_exclusive_wrong_domain` (CO) and +`mode_exclusive_domain_admin_wrong_domain` (DomainAdmin): it proves that domain +isolation is correctly relaxed only for the one role that is explicitly allowed +cross-domain access, and that no other role accidentally inherits this privilege. + +Ref: kms.rego SuperAdmin rule (ANSI/INCITS 359-2004 §4.2 top of the role hierarchy; +NIST SP 800-53 Rev 5 AC-6(1) least privilege / authorize access to security functions; +NIST SP 800-57 Part 2 §4.3 Key Management Authority role). +""" +requires_env = ["KMS_OPA_URL", "KMS_AUTH_SERVER_URL"] +backends = ["sqlite"] + +# other-domain CryptoOfficer creates the key (object_domain = kms-opa-other). +[identities.other_domain] +access_token_env = "KMS_TEST_OPA_OTHER_DOMAIN_JWT" + +# SuperAdmin is cross-domain; its domain claim is irrelevant to OPA's decision. +[identities.super_admin] +access_token_env = "KMS_TEST_OPA_SUPER_ADMIN_JWT" + +# Step 1: other-domain CO creates the key. +[[steps]] +operation = "Create" +request = "step1_create.json" +identity = "other_domain" +assert_success = true + +[steps.capture] +key_id = "UniqueIdentifier" + +# Step 2: SuperAdmin retrieves the key across domain boundaries → allowed. +[[steps]] +operation = "Get" +request = "step2_get.json" +identity = "super_admin" +assert_success = true + +# Step 3: SuperAdmin destroys the key across domain boundaries → allowed. +[[steps]] +operation = "Destroy" +request = "step3_destroy.json" +identity = "super_admin" +assert_success = true diff --git a/vectors/opa/mode_exclusive_super_admin_cross_domain/step1_create.json b/vectors/opa/mode_exclusive_super_admin_cross_domain/step1_create.json new file mode 100644 index 0000000..c994e09 --- /dev/null +++ b/vectors/opa/mode_exclusive_super_admin_cross_domain/step1_create.json @@ -0,0 +1,40 @@ +{ + "tag": "Create", + "value": [ + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + }, + { + "tag": "Attributes", + "value": [ + { + "tag": "CryptographicAlgorithm", + "type": "Enumeration", + "value": "AES" + }, + { + "tag": "CryptographicLength", + "type": "Integer", + "value": 256 + }, + { + "tag": "CryptographicUsageMask", + "type": "Integer", + "value": 2108 + }, + { + "tag": "KeyFormatType", + "type": "Enumeration", + "value": "TransparentSymmetricKey" + }, + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + } + ] + } + ] +} diff --git a/vectors/opa/mode_exclusive_super_admin_cross_domain/step2_get.json b/vectors/opa/mode_exclusive_super_admin_cross_domain/step2_get.json new file mode 100644 index 0000000..63a255e --- /dev/null +++ b/vectors/opa/mode_exclusive_super_admin_cross_domain/step2_get.json @@ -0,0 +1,10 @@ +{ + "tag": "Get", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{key_id}}" + } + ] +} diff --git a/vectors/opa/mode_exclusive_super_admin_cross_domain/step3_destroy.json b/vectors/opa/mode_exclusive_super_admin_cross_domain/step3_destroy.json new file mode 100644 index 0000000..ce2dc0e --- /dev/null +++ b/vectors/opa/mode_exclusive_super_admin_cross_domain/step3_destroy.json @@ -0,0 +1,10 @@ +{ + "tag": "Destroy", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{key_id}}" + } + ] +} diff --git a/vectors/opa/mode_exclusive_user_wrong_domain/manifest.toml b/vectors/opa/mode_exclusive_user_wrong_domain/manifest.toml new file mode 100644 index 0000000..a1f93d8 --- /dev/null +++ b/vectors/opa/mode_exclusive_user_wrong_domain/manifest.toml @@ -0,0 +1,55 @@ +name = "OPA Mode 2 (Exclusive): User role wrong domain denied (cross-domain isolation)" +description = """ +OPA exclusive mode — multi-tenant isolation. + +A CryptoOfficer from realm `kms-opa-other` (domain=kms-opa-other) creates an AES key. +A User from realm `kms-opa-test` (domain=kms-opa-test) then calls `GetAttributes` +on that key. Even though `GetAttributes` is in `user_ops`, the `same_domain` +helper in kms.rego fails because `user_domain (kms-opa-test) != object_domain +(kms-opa-other)` → OPA returns `allow=false` → KMS returns Object_Not_Found. + +This test completes the multi-tenant isolation matrix for the User role. The +`same_domain` constraint applies to every non-SuperAdmin role, including the +least-privileged User role. A compromised tenant in one domain must not be able to +discover or use key material belonging to another tenant. + +Ref: kms.rego same_domain helper (ANSI/INCITS 359-2004 §4.2 Constrained RBAC; +NIST SP 800-53 Rev 5 AC-3 access enforcement, AC-4 information flow enforcement; +FIPS 140-3 §7.4 User role restrictions). +""" +requires_env = ["KMS_OPA_URL", "KMS_AUTH_SERVER_URL"] +backends = ["sqlite"] + +# other-domain CryptoOfficer creates the key (kms-opa-other domain). +[identities.other_domain] +access_token_env = "KMS_TEST_OPA_OTHER_DOMAIN_JWT" + +# kms-opa-test User — domain mismatch triggers isolation. +[identities.user_role] +access_token_env = "KMS_TEST_OPA_USER_ROLE_JWT" + +# Step 1: other-domain CO creates the key (object_domain = kms-opa-other). +[[steps]] +operation = "Create" +request = "step1_create.json" +identity = "other_domain" +assert_success = true + +[steps.capture] +key_id = "UniqueIdentifier" + +# Step 2: test-domain User tries GetAttributes — same_domain fails → denied. +[[steps]] +operation = "GetAttributes" +request = "step3_get_attributes.json" +identity = "user_role" +assert_success = false +assert_error_reason = "Object_Not_Found" +assert_error_contains = "not found" + +# Cleanup: other-domain CO (owner) destroys the key. +[[steps]] +operation = "Destroy" +request = "step3_destroy.json" +identity = "other_domain" +allow_failure = true diff --git a/vectors/opa/mode_exclusive_user_wrong_domain/step1_create.json b/vectors/opa/mode_exclusive_user_wrong_domain/step1_create.json new file mode 100644 index 0000000..c994e09 --- /dev/null +++ b/vectors/opa/mode_exclusive_user_wrong_domain/step1_create.json @@ -0,0 +1,40 @@ +{ + "tag": "Create", + "value": [ + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + }, + { + "tag": "Attributes", + "value": [ + { + "tag": "CryptographicAlgorithm", + "type": "Enumeration", + "value": "AES" + }, + { + "tag": "CryptographicLength", + "type": "Integer", + "value": 256 + }, + { + "tag": "CryptographicUsageMask", + "type": "Integer", + "value": 2108 + }, + { + "tag": "KeyFormatType", + "type": "Enumeration", + "value": "TransparentSymmetricKey" + }, + { + "tag": "ObjectType", + "type": "Enumeration", + "value": "SymmetricKey" + } + ] + } + ] +} diff --git a/vectors/opa/mode_exclusive_user_wrong_domain/step3_destroy.json b/vectors/opa/mode_exclusive_user_wrong_domain/step3_destroy.json new file mode 100644 index 0000000..ce2dc0e --- /dev/null +++ b/vectors/opa/mode_exclusive_user_wrong_domain/step3_destroy.json @@ -0,0 +1,10 @@ +{ + "tag": "Destroy", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{key_id}}" + } + ] +} diff --git a/vectors/opa/mode_exclusive_user_wrong_domain/step3_get_attributes.json b/vectors/opa/mode_exclusive_user_wrong_domain/step3_get_attributes.json new file mode 100644 index 0000000..e96d057 --- /dev/null +++ b/vectors/opa/mode_exclusive_user_wrong_domain/step3_get_attributes.json @@ -0,0 +1,10 @@ +{ + "tag": "GetAttributes", + "value": [ + { + "tag": "UniqueIdentifier", + "type": "TextString", + "value": "{{key_id}}" + } + ] +} From cbbea68ea5ab77d32a9791b5633f09f10a275f29 Mon Sep 17 00:00:00 2001 From: Manuthor Date: Sun, 23 Aug 2026 20:57:35 +0200 Subject: [PATCH 15/17] =?UTF-8?q?fix(certs):=20add=20cRLSign=20to=20interm?= =?UTF-8?q?ediate=20CA=20keyUsage=20(RFC=205280=20=C2=A74.2.1.3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The intermediate CA certificate was missing the cRLSign bit in its keyUsage extension. RFC 5280 §4.2.1.3 mandates this bit for any CA that issues CRLs; without it the KMS server rejects CRL generation requests with 'InvalidRequest'. Regenerated with openssl using updated extensions.ext that now includes keyUsage=keyCertSign,cRLSign,digitalSignature. Also regenerated leaf.crt (signed by the new intermediate) and all derived PKCS#12 files. --- certificates/csr/extensions.ext | 2 +- certificates/csr/generate.sh | 4 +++- certificates/csr/intermediate.crt | 24 ++++++++++++------------ certificates/csr/intermediate.p12 | Bin 3579 -> 3579 bytes certificates/csr/leaf.crt | 24 ++++++++++++------------ certificates/csr/leaf.p12 | Bin 3579 -> 3579 bytes 6 files changed, 28 insertions(+), 26 deletions(-) diff --git a/certificates/csr/extensions.ext b/certificates/csr/extensions.ext index 12cec5e..bd2b69e 100644 --- a/certificates/csr/extensions.ext +++ b/certificates/csr/extensions.ext @@ -1,4 +1,4 @@ [ v3_ca ] basicConstraints=CA:TRUE,pathlen:0 -keyUsage=keyCertSign,digitalSignature +keyUsage=keyCertSign,cRLSign,digitalSignature extendedKeyUsage=emailProtection diff --git a/certificates/csr/generate.sh b/certificates/csr/generate.sh index 69f0d5c..26cac36 100755 --- a/certificates/csr/generate.sh +++ b/certificates/csr/generate.sh @@ -44,10 +44,12 @@ openssl req -new -key intermediate.key -out intermediate.csr -subj "/C=FR/ST=IdF # ``` # [ v3_ca ] # basicConstraints=CA:TRUE,pathlen:0 -# keyUsage=keyCertSign,digitalSignature +# keyUsage=keyCertSign,cRLSign,digitalSignature # extendedKeyUsage=emailProtection # crlDistributionPoints=URI:http://cse.example.com/crl.pem # ``` +# `cRLSign` is required by RFC 5280 §4.2.1.3 for any CA that issues CRLs — +# without it the KMS server rejects CRL generation requests for this issuer. # The `crlDistributionPoints` option should be replaced with the URL of a publicly accessible HTTP uniformResourceIdentifier that contains the CRL for the intermediate certificate. # This will sign the CSR with the CA's private key and generate an intermediate certificate. The intermediate certificate will be stored in the file `intermediate.crt`. openssl x509 -req -in intermediate.csr -CA ca.crt -CAkey ca.key -out intermediate.crt -days 1825 -extensions v3_ca -extfile extensions.ext diff --git a/certificates/csr/intermediate.crt b/certificates/csr/intermediate.crt index f72a373..bbe5bce 100644 --- a/certificates/csr/intermediate.crt +++ b/certificates/csr/intermediate.crt @@ -1,8 +1,8 @@ -----BEGIN CERTIFICATE----- -MIIExzCCAy+gAwIBAgIUGE93Y+exJtPxiLINE33SsrJ1Z0QwDQYJKoZIhvcNAQEL +MIIExzCCAy+gAwIBAgIUGE93Y+exJtPxiLINE33SsrJ1Z0UwDQYJKoZIhvcNAQEL BQAwXTELMAkGA1UEBhMCRlIxDDAKBgNVBAgMA0lkRjEOMAwGA1UEBwwFUGFyaXMx EDAOBgNVBAoMB0Nvc21pYW4xDDAKBgNVBAsMA1ImRDEQMA4GA1UEAwwHVGVzdCBD -QTAeFw0yNjA2MDcwNzQ4NDFaFw0zMTA2MDYwNzQ4NDFaMGcxCzAJBgNVBAYTAkZS +QTAeFw0yNjA4MjMxODU0NDlaFw0zNjA4MjAxODU0NDlaMGcxCzAJBgNVBAYTAkZS MQwwCgYDVQQIDANJZEYxDjAMBgNVBAcMBVBhcmlzMRAwDgYDVQQKDAdDb3NtaWFu MQwwCgYDVQQLDANSJkQxGjAYBgNVBAMMEVRlc3QgSW50ZXJtZWRpYXRlMIIBojAN BgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA8fqz6gS1zKOw0ITYtiQZyfBJ0j2z @@ -14,15 +14,15 @@ mT2jjBjl2A9U2Nt9pB/htRwK1T1arK6hsgzSOd6C9J4oV6QPZRuZgLdOWy5Tcnyz nhPaLzKnKZrmb8u4SpkIboIFD9L4Z2uBGM1QyHNFbznV13ph37QbQAb1/gdgFmvT RjU/4K0sWSPyCJ6z3iq7blwH4VpXSrCVe5eNkoEhx4+oFtJA6InR33On7zBFAvln Ru9er+PmddNGMcCDYkFqZvyre3rDeec/hTIvAgMBAAGjdTBzMA8GA1UdEwQIMAYB -Af8CAQAwCwYDVR0PBAQDAgKEMBMGA1UdJQQMMAoGCCsGAQUFBwMEMB0GA1UdDgQW +Af8CAQAwCwYDVR0PBAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMEMB0GA1UdDgQW BBQEK8XsqNesHDBu2Xz6rEdg09OK9DAfBgNVHSMEGDAWgBQJbsVPDK4X9gjOFhy+ -sVf/0DzKGDANBgkqhkiG9w0BAQsFAAOCAYEAlhNiN9Lktl+wx/BN/sDPSm2a/4xI -ZpjUj/Yrnp1KWMmj9hgmPZx89XSbrW42FxKLId5o9+eCWrVZDQowlnVjrokgM3WM -wcQ67lqg50vW25UOni5HKFF0vDZiMBP+KVUYvGI1P10JM/6aayGl5r+gxqnDI1m8 -72mO8M4Q/nN7DEzZQ3tCnWo6b2wbHsrRRo1LYqTx4QDL4M2EQ/yseC9qCC8N0JPH -fQU7Crq89+VdEnq9WFemnoiEvOU7fpyh884SegmFGKojzW8QU5xMbzcooZseYdTD -mBTzavPwiU2evs6/YDJO5s9mZTtI/GRhBtHqIVnollMzxEYebcmB5Xy0dB4j0tsi -38UyjlO8QIXo2wQ0xaKD4dDksmvKPKcJgJsX21bMdbGHUMrMYm8qcyvPRr+HNm2j -+njI1i6RTL2qbcqVON9zlxYUYBcYto6GCvufOIna/eVlnKd4R761JtwbHHPZOIdh -Ilq5N2JdqLVERca4700lEht+R2+TESLEKFM+ +sVf/0DzKGDANBgkqhkiG9w0BAQsFAAOCAYEAVgVqVapRDa0rY1nNJ7qot25mdCA3 +ve/j9YcifNpYtk75cMu3+lz7M/DVLyVxbF71O2cc8hQP9pnMATXSxXbsVxvpwsTU +i2d5tvgGRvgJT2Wtkw+5nlrmW5X1SI1mqhB1B2DUhE42r7FDWGcieocowXTPyiuo +zEOMD6Blfx+OJwK2ovnGsaqTcANSrMl1K7ABaelktHkXD+QXa6g9bq5cPdg7QZBH +CsaQKLRmLWg4eZ5W7MeNuTyomMhHABB7MWk3FHqXBEyfg6R8MkzJv7zz5DMELco4 +DZtIZN2rEJodY/0GSk4lvbOsJEBpjm+aXLISzffO7cfxzNgQmF0FVzff0pRHCp9e +Kb5lLaZBia7XaK94QYWh4p17NdbNWaZIxhyx2C1vSvJc7lUdkisyeCowY+nZgD8w +JkKV0hcyAP5WQubP9oVkN2DI/XtBI9YlvlXRgJJvCkMZXPFP72Hwe3afbRzOq1kt +4O9nRSNxmvhKMT6rD0nLZ2pBJqumxlA8KXvK -----END CERTIFICATE----- diff --git a/certificates/csr/intermediate.p12 b/certificates/csr/intermediate.p12 index 8b4569ca918683032cc928fc6f082ab950a9f5c0..e58e0a6147e81d8536d2664b5ccb9f1dd257a7e7 100644 GIT binary patch delta 3350 zcmV+x4e9dx8~YoOYY>1*wE3w{d4p$M$spLfs^yV;A%CJS+aUSVL2aHG!5F`;*OP#P z1u(U7O!5RBPJlrC!3!TrM4%5sppoQMx!6{iQMJ=*t@a-b59y;(&{|9c16PUMzta7; zTbtz&`k0tugl$ z%-%d{=YRGL!?exN1c_$tbcmUoJV{Yy%R6+$;SWjFOqdpz3B@cZQGnY}g?Uf760(2? zOmT%d^K8qU%dVt(@Su9K>)`sOLcDXylN9jnXTM@_3@_0*?*KdU>A^pM5vtb#2p-WG;VJV`0p( z`8pi9dtrvJ>sZc1n%tL^1hYZvDF&Phoi6E4Uy(*~ZT_#MRfSP32V?*T~bJMSr_d@-uYXakhQ5I2@R#U}=AJ zcBA)!6L;{E>p0FI)@j$={b$-!xnZ*HB!55){7%S|nyP^GuFwDeRk_&`xdg~Z$~(Xgs&Zl#&$W7nNagc>YqbUK zBARfAcjFN3l56{UflzZMStT##N(|foycS@ZiP82=>@I+#r0AfpHUF z2f-SEvc)tPFGYq(K=Z>C#1$fLSDB&ORB-S!&Jys07|yLBEe<|5=$~J*uZMZ$ztj-8 zx2PU{5b+Z~+&Vz56;M+q1c0ustMBk|^)UR8FluyZwG;!h#^4kOuKkwOGJi@W$%Mor zI@;mL?5_0lj4(gw=}3*jrx<80HWCww~SNjiS zx12``pYFK_?;Ofg%k(OwF0NLCuUhHZJ5S`XFbd^49;Dw+Z*g5^3ZqFvpZ6t>1*x}y zPIDW3pfEIP(^^2B)D69L6@N{o*e&|stQ6`oVk<~E&5GDG+50pTr9y2!kXreWc2@6+IItC zZ%CWf`kN9Ez z9@3*+Kusc_JA;C|kxt>SCd2Y{iE-5MVPA&(q~Jc}5)&F{xWUv7Q-Sdp+}ZW2<27mD z+FY2pM4P{kamLa#B^^0y%3_vlI|UHdG7|V#=Zj0|gGO`P9xIS}F_u?+>=;IWz@rD^ zm?;%nTDEnJZ&&NQS5Z;bT;1C6zqNcg%gD6yJJR}jQOLcxe>d$jZU*d6Wp6(A&ldZi zjm^=QVn#{+PY)vjp;2^BD3iu7$e`&#!9V^?d#3zeTcG{-pm@UG5 zF5*6Z)b@VBl0^m^T*^D0*bwyZrQhiC)XaKw*(ZW?_?hEtaU3_)7brb$6Wg?7?DkC4 z^GZ4c)hz4ueLW10&E?bv@itCtlzp)ys^QH@XHdsjS1^NpImTLSe5KS|;9hs|k_+~w z-UV;2e@iUsQLHKer86!&W9MiJ1ye_0TLcdi@o7Dq1_e zfecP*S<`h%F&%BCLl^o4KV)kc@r}JiV}38lvc%&Fq^07GB#Zd_i^qUA1`lVR`ER*HeD2;roQj|A9mwm|AswK*;_Okiw1twy+-2 z=w3MR3KfT9Xqo=bKoM&XO4HNmOI|8=n(n+6@Nu$BnGuKe*l|X5+Qs3RlB%L?799i= ze<)MKVq&$EqNiXfENV&-lgnqzT@Y^8Qq_=7rljRu+40y=g{yJKK?JMYz#CQ1j5^rO zc`H^={($!+hl{1rXsNgpKIGQ1{zjX4dXZQ#zQISr*5dnr;a_8UwX7!kG_bnlZL&i_ z&jZpNL}-4tqC(<#VDfDQVNslxejRsMe-4q5zCOx`+YWW&REC-V*#H7+LL%3B5Zz=2}ULTcG@~CvD|(QwO%lNMjZ9@X8v3+nitX}Po1(je=EqD zMl5@S7&MK^Uky8fko!Jh!J2uQi1ZUq3vJX;o+SE8H2{-HI8^t@rk$QevJ&d_9r3*Z zWrf{`h!50bti#T(z<(9y@yVJIuj;X=a zC+jnT~E8i9^$;Kp8&*(9*oQwtd(9rhHirswVx1o*jvJKCNOyeE>klV`Bm?k-EA97b|_UcCXgX^(n%NXSEQc-g-u z(f1ccl!4Z4CWNY6ZE7TsZw&A{(9biz@Pb zJBY`dzg_0@c@KSRX(tnEJ2EKT)7(85>)@aCq^)tQ*cyl2hk!bLWrOH{N2O7h4hDf> zLG$~?%n8(!7Ejb}DkJom!gOrU(#2=ObhaNU=i|OeSu(l4XU~yUFSd>w1wF%)k&ah{?hkU$Jn;I6l^TBMC(ip4&Pt-nhpTa z?Tcuc<>PS^6TPim4msD%WQC=>R-wDVC3aD+5rH>4Ohuk;Svr#tiZ6ONN|1=+%mu)v zQp2#XotEi=+3Q7GiNHk@H1esZO`yyDcJLlsjGteH=~vk?B`_lf2`Yw2hW8Bt2^BFG z1QZt$J2i}@uaPps3>Um-ye2s6VP=!C4HY)fE`3AlEZV3WLYN92e#$|Jpqp*2Slaex g%jVjWgEV*q5R^44T6@fa3YRzY{nwj9;Q|5(0Gj}wNdN!< delta 3350 zcmV+x4e9dx8~YoOYY^?e9+QHb$(<95&$)HX8+DO;A%DyFFn<)Z+gC>zsCKq@5Fdbo z1uzcg6> zZF62$JRd9VAG35WQuuPFb5KI^E9s@_C6$(D7bNSRm|ka-_A-5Ce_7caekN}Kg9Q() zPq`k1a5;i-&xU6S#$sQ)7H00-wJwXxy!PajH=-lM8zYY~dq|mQvb-W)-^pBlrTbfIFI+<#PK;yWEuHc;Wma=WjtYV&iBNYvf`XEW`u_wyuR z+*-0@V&`*?p|_^T6F#9gxMNJ<2dz<14pq-!;Bxj+3CQ_tHB8$svd3m@Y=5-vB-tX8 zeQ$WQL`cb)UJ=f0w1~(6aijWWEdF{|)-=HzGBPI%;5KqN_nyd=61XL&X;dH&{BInN zLUmV!)~SDM%q0!-X^zk7Rc5} zOy=bUW26q$v>J&4{c``Ya)0QX3HE}Y^b1fQPX%3j1KRYLVy@#p2ehC?&YBFfO5bqM z-vPpY<|I}DBuv7#e!H9;GH)VfB5E^PM1C2ItH96jFp=KCMxD1wUKfbxKk>dC*bEtB z+VV0~VaC|!yu{E9bM&@#c<*b+%pug5^|d1&1|s|3i#(6dRh;E~a(_jrPG`t_NQiJ% zhZnifcbPK^4&F?kU=nY#U9~2KvYL<7(-wy`D~(#*S*eK^61Ms~k&tq6_@9>n$vQ+8 z^uyLgNB?0-48&G$j13o$p?(8MYXsXM>>0!#`8XqP!Vz8U!?MPw(go}RmWpFU!-#eCVO2N%OB~>>4t&gz+(&`;o#hASkinuR=QAGu2LFUJW(0x<5E@{g-E@Pvxp%o3Pm)IoqSW3e z$H-Z5&A8=-lbYVPB--%eb0E$ZgTjHw)%>e(|4uE(SRg38fK{U6Wcy%X0=0c@8`>NU zmbS|y&6wVI+?O};7kJZJjFcwM@_HkE4&yfdCHjSmI}?Hre`-8T*_r&Kg*BDNz=bM^ zD!klWRMa}_?;6^mPbp=q7$y@@ssU5t(2id#54CMh$o+ct%Uz!F@=01;u0^r|PgFi0 zXysG*K%p@Yo8I<}^Y`|Fn+WsfB4ggwLF>iWD=gV+WLmtiZRa5HCG_@u|EPjgQ}WkL z35WB?-#ad@e|Cpq`5UnL^bmaf1$6a;=CRLzOc>rM=Z@>i#gMkpePUIZTZ!P&+$zJ) zm}sf8y8R}moVzxñzxcS<~$)N`=keEOq1lF{~$swfNXwmlFnK2CRuy1L}`#!Wcc$xH$b+Y=5vMnM`;4s^Ig7DfBm{ERUPnwOin(fLJQ)87U9lE-e= zrn{NQ5EtX63E`w)5{H10aOZaXk-L9jrvDG9MN5$8=baisf(<->jMRZfn1#Px%6-CCHX2K+I8!bJFLS(S*f9sW0p?`46V&s`{K{%y<6xjQ>iZ{-Cv>Z72 zy$T7Z>OG^^OU=z-Mvv2igP*YY%AcrkGC}EpvDKn4TZIcV!3+{7)B>>;Ul~-z4KUI( zbjHymvN=8frK*0-Iw+Ys{fM{T_7KYVXkkvkH4cj`t%iv{QNIi3k=8do`b64af4d;F z(IwQ&0W&Vu#8^W}2Kfk|e<1{-y(q zG@?Lm{^c8;!^QeqiLLll3O>5{Sfh~zM~?Q&G++Rk;}hL&@E;$9zk1X@-C8ceJNwk; zLY52Eib-ZM=s7$4tJoa_Wmr0O^27bmU3qGY>dr&o}Xh}|mjlHXFVmZL^ zwjEhLHS=lMXRSQ;e;Xt#=W9kwsy!Hj%Wnd#!H8bGfXP28H5p6x2#q-0ngeo^_C1N5 ztNFx^i0$v_7ECjt@du`48y78EPNj=+YCl!8O<&YO+;uMOv<}1z8%I{)Ha7-V{D+o; zo$_Al9bWMe%hu^#k|Wd!|H`Z? zQ`zoGPFTYnSJ#{%;er$lTyGka_QHN(M|ZFNC6o%|!CSI1B`_lf2`Yw2hW8Bt2^BFG z1QcNO%hq-&!@><8b$K72+P?5k-1(ER4HY&(;!PBg{c7NDYf-+Ntiu|_Y2d+BYvbD+ gWMYk^KWK>r5J71)id@$&Xu?y4dO7xeWdZ^S0Ka&6U;qFB diff --git a/certificates/csr/leaf.crt b/certificates/csr/leaf.crt index 9db3676..b6ac516 100644 --- a/certificates/csr/leaf.crt +++ b/certificates/csr/leaf.crt @@ -1,8 +1,8 @@ -----BEGIN CERTIFICATE----- -MIIEyTCCAzGgAwIBAgIURZxo9QcMLcsub4T30OOWua1UF/EwDQYJKoZIhvcNAQEL +MIIEyTCCAzGgAwIBAgIURZxo9QcMLcsub4T30OOWua1UF/IwDQYJKoZIhvcNAQEL BQAwZzELMAkGA1UEBhMCRlIxDDAKBgNVBAgMA0lkRjEOMAwGA1UEBwwFUGFyaXMx EDAOBgNVBAoMB0Nvc21pYW4xDDAKBgNVBAsMA1ImRDEaMBgGA1UEAwwRVGVzdCBJ -bnRlcm1lZGlhdGUwHhcNMjYwNjA3MDc0ODUyWhcNMzEwNjA2MDc0ODUyWjBfMQsw +bnRlcm1lZGlhdGUwHhcNMjYwODIzMTg1NDQ5WhcNMzYwODIwMTg1NDQ5WjBfMQsw CQYDVQQGEwJGUjEMMAoGA1UECAwDSWRGMQ4wDAYDVQQHDAVQYXJpczEQMA4GA1UE CgwHQ29zbWlhbjEMMAoGA1UECwwDUiZEMRIwEAYDVQQDDAlUZXN0IExlYWYwggGi MA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCRHCUJdbJoDRdYOQw4LMsz08Iu @@ -14,15 +14,15 @@ BAuzTranVkPW+ZqsNJ2ct5i/vwQNtvdg4PUPUjLQ0knOnpuJsHHm7V66ZfPluFJn fb+fRkRf60jwKjf8LydqiI8XteaGIo6aPfUzcqGkHAjoKFohZCjRDqqUhNyIbg1x e6TaVPT+FierndR7QdKtte3QRGifhBLKKeKWX6zfNHsPDXP5GxyY42LWTVXSNFgJ XoLh6a7+GaEZOCaRxxKsenO1ot6AHSjnMjzr588CAwEAAaN1MHMwDwYDVR0TBAgw -BgEB/wIBADALBgNVHQ8EBAMCAoQwEwYDVR0lBAwwCgYIKwYBBQUHAwQwHQYDVR0O +BgEB/wIBADALBgNVHQ8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwQwHQYDVR0O BBYEFEXqf3iWtpvlqUYcVC8NbU3QVTe5MB8GA1UdIwQYMBaAFAQrxeyo16wcMG7Z -fPqsR2DT04r0MA0GCSqGSIb3DQEBCwUAA4IBgQAsuH3BBIxT2qR/yeyKqVQsfVIa -2AmpzYd7AE8+017XgGoqvqA71r3gZXaf7/FhBzuIp2+PQTnyu+UarJLztIsnbEMD -BXxRq7t6FHqERf7qQlEZBwhVpE7AcM8/9PyI+qLbHHG5x4ZRsFO7YoxsUjQna7SU -qRhFkj3gwHOQGmbhV46fpQ5ioFqe1X5SIU5SyeezAhO25wn9iBHU7Z4J1v8MAukY -wY+q75xncHa/JK8GQ9v8JtS4buCQNkAEPew0t+AN85rI0t6DHWjHy6kHy8wiACfo -BSno+anWqtiqDyQpzRR8Tk9oXztjOlVhyHxG6r9j4VcUIku2UtJVuaZi3KDrLVwK -L7xURZ0QNEsL9JZ8ojO54F2GLOdF3CW/lG54tQktAWmKzLXZs1jZkIPA3jnzd3G5 -pfp7YlhMJqkHTly2w1qjXgZ8WThh9WbSpWP0RE92lO7H2VFSREwr8o73W6XZpssT -QRsJSRoUulDurvrfKsro5TNskcRX9ZNk9pBWlVo= +fPqsR2DT04r0MA0GCSqGSIb3DQEBCwUAA4IBgQBUPzMNDTt8sb3wsLEIEsLbRBdK +kyDknQv2k3ZhCJmvpctxlSoOPuJhTZi9pF1BCAq7Uy1hJLBWLKr4EYJbHeI3eMgd +O68NTXBmne9AwMKVb3mm51IHt6/bF4dz90KWpsrQswVOMOUMUjGy5zkVGU4S7pHz +86/5P/Hl4hlQUGTEHucsmt7SyLPL5bOfbNERg/m2Z/vtkteDwYqnjyrSbt69XCp2 +Y99B1iWzbseAuOmz95qiw0HNFumAVj1cgulmWK/C2Yeg5TrnuJ68YpRsYV0SzXZg +qaDjgyZ7DbxeQ+r3lzvXfTQHJR04QQtW7L8p3rO+faZawMBqVNIP6cGSsImrChe4 +eVmN4njoBwiavsUjAWuuJ7UNhN0zK1akUBIidWixbxn+iGxriBKIyZ2qF3+2u+hL +bby5x5Y/BgfOejCu/NvJgfzzpjgA8kjrsuySCyRELmXkxtIs4iBbIUcCj1cMasOX +DmNK4GoJAUpfRLdAObRvhtMjFJXCkJduM0Zh3/g= -----END CERTIFICATE----- diff --git a/certificates/csr/leaf.p12 b/certificates/csr/leaf.p12 index b8cbb04afeaaa12d13d16e300a57eea0e9339627..09c26f6d508d15294ec28fee8cc5cd6cc40eb009 100644 GIT binary patch delta 3350 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 delta 3350 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 From f262be2bf5b462966128c53a1ad50575a49abb3d Mon Sep 17 00:00:00 2001 From: Manuthor Date: Sun, 23 Aug 2026 20:58:46 +0200 Subject: [PATCH 16/17] chore: update srl files --- certificates/csr/ca.srl | 2 +- certificates/csr/intermediate.srl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/certificates/csr/ca.srl b/certificates/csr/ca.srl index a57be3f..7b5c7bb 100644 --- a/certificates/csr/ca.srl +++ b/certificates/csr/ca.srl @@ -1 +1 @@ -184F7763E7B126D3F188B20D137DD2B2B2756744 +184F7763E7B126D3F188B20D137DD2B2B2756745 diff --git a/certificates/csr/intermediate.srl b/certificates/csr/intermediate.srl index ef4f7dd..a42e0cf 100644 --- a/certificates/csr/intermediate.srl +++ b/certificates/csr/intermediate.srl @@ -1 +1 @@ -459C68F5070C2DCB2E6F84F7D0E396B9AD5417F1 +459C68F5070C2DCB2E6F84F7D0E396B9AD5417F2 From b728f92e6e83d1168cd72c0859023e5f169273d5 Mon Sep 17 00:00:00 2001 From: Manuthor Date: Tue, 25 Aug 2026 13:59:15 +0200 Subject: [PATCH 17/17] fix: simplify CO conf --- configs/server/rbac/crypto_officers.toml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/configs/server/rbac/crypto_officers.toml b/configs/server/rbac/crypto_officers.toml index 58bc0d4..b0d2915 100644 --- a/configs/server/rbac/crypto_officers.toml +++ b/configs/server/rbac/crypto_officers.toml @@ -45,13 +45,9 @@ hostname = "0.0.0.0" [db] database_type = "sqlite" -sqlite_path = "/tmp/kms-rbac-full" +sqlite_path = "cosmian-kms/kms-rbac-full" clear_database = false -[workspace] -root_data_path = "/tmp/kms-rbac-full-workspace" -tmp_path = "/tmp" - [tls] tls_cert_file = "test_data/certificates/client_server/server/kmserver.acme.com.crt" tls_key_file = "test_data/certificates/client_server/server/kmserver.acme.com.key"