Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Cosmian provides readily available, easy to use software components, based on next-generation cryptography to secure data and applications in all environments, from the cloud to the edge.

This is the technical documentation

This site provides the technical documentation of the components. It provides information for technical users, architects to developers.

The documentation is divided by security use cases and then by products.

Security use cases

  1. Architecture security of data and applications with a mapping of threats to solutions and a review of common architecture uses cases.

  2. Use modern encryption schemes such as:

    • Advanced encryption schemes on client-side encryption (or application level encryption) and server-side encryption:

      • Cosmian Covercrypt: Protect data with the latest post-quantum encryption, access control, and traceable keys.

      • Cosmian KMS: Central point of security for managing encryption keys and enabling server-side encryption.

    • Alternative encryption schemes

      • Cosmian FPE: Selectively encrypt data while preserving the data format with format-preserving encryption.

      • Anonymizing clear-text data with Cosmian Anonymization

  3. Compute over encrypted data with Cosmian's Verifiable Confidential Computing solutions:

    • Solutions based on Confidential Virtual Machine:

      • Cosmian VM: quickly secure data in any running application in the cloud.

      • Cosmian AI: Benefit of Cosmian VM and leverage advanced AI models with built-in privacy and security features.

    • Solutions based on Intel SGX enclave:

      • Cosmian Enclave (formerly MSE): protects algorithms and data in the cloud and on 3rd party premises with maximum security.
  4. Searching encrypted data with Cosmian Findex and Findex server: run encrypted queries on an encrypted index.

Cosmian products

  1. Cosmian KMS: A modern, open-source, highly-scalable key management system that is KMIP 1.x/2.x compliant.
  2. Cosmian VM Secure data in any running application in the cloud.
  3. Cosmian AI: Leverage advanced AI models with built-in privacy and security features.
  4. Cosmian Enclave: Protect algorithms and data in the cloud and on third-party premises with maximum security.
  5. Cosmian Findex Server: Run encrypted queries on an encrypted index with server-side capabilities.
  6. Cosmian CLI: Command-line interface for managing and interacting with Cosmian's suite of tools.

If you want to see our technology in action and "play" with it, please visit our Interactive Tutorial

Interactive Tutorial

For pricing, check the website

For pricing information, general information on Cosmian and its products, please visit our website

All our code is open-sourced on GitHub

All our code is available on GitHub

Chat with us

To chat with our technical team, please join our Discord server

End User License Agreements

Terms and Conditions

The first step in understanding how to secure data and applications in a zero-trust environment is to look at potential attackers and their threat model. We assume the application code is safe and that we do not need to protect against the software developer.

AttackerAccessThreats
Passive 3rd partyinsidermay get a copy of the disks content
Man in the middleexternal networkmay intercept the data in transit
Infrastructure admin.physical access + networkmay externally look at memory, disk contents
and network traffic
may change hardware or spoof server names, redirect traffic
System admin.OS level accessmay access and change memory, disks content and network traffic.
may modify the code or run malicious code
Application admin.authorization stackmay escalate privileges to access the data

Protection against all attackers

Client-side encryption is the only ubiquitous solution that protects against all attackers because data is encrypted by the data owner under its own key before it reaches any of these attackers. Enabling client-side encryption without loss of functionality usually requires a modification to the application and the use of a few Cosmian products: Cosmian Covercrypt, Cosmian KMS, and possibly Cosmian VM and Cosmian Findex.

See the last use case on this page for details.

Protection against a reduced list of attackers

When the threat model is limited to a reduced list of attackers, other solutions may be used. The following table summarizes the solutions that Cosmian provides to protect against each attacker.

AttackerSolution NameCosmian Products
Passive 3rd partyEncryption at restCosmian Covercrypt: post-quantum encryption with access policies
Protects against the "Harvest now, decrypt later" risk + limits key leakage
Man in the middleEncryption in transitTLS
Infrastructure admin.Memory EncryptionCosmian Enclave or Cosmian VM: verifiable encrypted memory at runtime
System admin.Sealed Encrypted MemoryCosmian Enclave: verifiable enclaved encrypted memory at runtime that is immutable from the outside (the underlying OS)
Application admin.Covercrypt encryptionClient-side encryption with Cosmian Covercrypt: access policies in user decryption keys prevent the user from decrypting data not authorized by its key

Use case: move to the cloud

When moving an application for its own users to the cloud, the main threat is the cloud provider itself. It is assumed that the system administrators and the application administrators are trusted.

In that particular case the use of TLS connections to applications running in Cosmian VMs with a simple at-rest encryption scheme is sufficient to protect the data and the application from the cloud provider. The Cosmian VM integrity is remotely verifiable by the user, preventing the infrastructure administrator from tampering with the VM hardware or software.

Use case: protect code at client's premises (or client's cloud)

When the application code is to be deployed at a client premises (or cloud), and needs to remain secret (such as an AI model), the main threat is the client's system administrator.

Cosmian Enclave is the solution that lets the code provider encrypt its code under its own key, deliver it encrypted to the client's system administrator. The encrypted code is then loaded in an enclave with encrypted memory, and before securely provisioning the code encryption key to the enclave, the code provider can then verify that the code is correctly deployed inside the enclave. Check this section for more details.

Use case: a 3rd party SaaS platform running in the cloud

From a user's perspective this is the use case where its data is exposed to all potential attackers: client side encryption will be the answer to protecting its data.

Enabling client-side encryption

From the SaaS provider perspective, this means making a client-side encryption API available to its users. If the application offers a web interface, the web application will need to be modified to encrypt the data before sending it to the servers. Encryption will require the ability to connect to a KMS from the web application. Performing encryption client-side will require

  • either Cosmian javascript (or Dart, or Java, etc.) encryption libraries running client-side with calls to the Cosmian KMS server to fetch keys
  • or direct encryption/decryption calls to the Cosmian KMS which can perform encryption on the fly in a scalable manner.

cse

An example: Google Workspace

Google Workspace is a good example of a SaaS application that offers client-side encryption out of the box. Google made an API available that allows connecting compatible Key Management Systems such as Cosmian KMS to encrypting data in the browser.

google CSE

Thanks to Cosmian VM, the user does not even have to install its KMS on premise. The KMS can be deployed in a Cosmian VM on GCP, which will protect it from Google and its browser can connect to it over a TLS connection.

Likewise, other services, such as document summarization using a language model, can be deployed in a Cosmian VM and made available to the user through a web interface. The user's data will be encrypted by the TLS connection to the AI service and will only be available to service in encrypted memory which is protected from Google.

Cosmian VM and Cosmian KMS are now offered as IaaS services on the marketplaces of all major hyperscalers Azure, AWS and GCP.

SaaS platform: distributing keys between clients

When using client-side encryption in a SaaS platform setting, some users may need to be able to decrypt data from another user that was encrypted with a key that they do not have. This is the case, for example, when a user shares a document with another user. The document is encrypted with the key of the user that created it.

The Cosmian KMS offers a key sharing mechanism that allows users to share keys with other users. The key sharing mechanism is based on a standard Public Key Infrastructure (PKI) available in the KMS which offers the ability to wrap a decryption key under the public key of another user. The receiving user can then unwrap the key using its own private key.

The users can safely use the Cosmian KMS PKI deployed in the cloud because the KMS does not have access to the private keys of the users.

key sharing

Say Client 1 wants to provide Client 2 with a decryption key sk_a to decrypt data previously encrypted with Covercrypt with Client 1's public key pk_1.

This decryption key is sensitive and should be wrapped under Client 2's public key to be transferred from Client 1's KMS to Client 2's KMS. Since their KMS cannot communicate, the clients are using the Public Key Infrastructure (PKI) built in Cosmian KMS to exchange keys.

The typical flow for the distribution of a decryption key is illustrated in the following diagram.

Say Client 1 wants to provide Client 2 with a decryption key sk_a to decrypt data previously encrypted under Client 1's key.

  1. Client 2 (the recipient) generates a key pair sk_2/pk_2 and publishes its public key pk_2 wrapped in a certificate to the SaaS PKI.
  2. Client 1 recovers Client 2's certificate containing pk_2.
  3. Client 1 wraps (i.e., encrypts) the decryption key sk_a under the public key pk_2 and publishes the wrapped key in the SaaS PKI.
  4. Client 2 recovers the wrapped key sk_a from the SaaS PKI and unwraps it (i.e. decrypts it) using its private key sk_2.

Covercrypt is a post-quantum public key encryption scheme that allows encrypting data for a set of attributes in order to provide fine-grained partitioning.

Covercrypt is being developed in partnership with the ENS cryptographic Lab , has been published at the ESORICS 2023 conference and is being standardized at ETSI. Covercrypt is coded in Rust and the source is available on GitHub.

Unique Decryption Keys with Access Policies

Consider the following 2 policy axes, Department and Country which data are partitioned by the following attributes:

  1. Department, Finance, Marketing, Human Res. and Sales
  2. Country, France, UK, Spain and Germany

Each pair (Department, Country) constitutes one of the $4^2=16$ data partitions.

When defining the Policy for these partitions, Covercrypt offers the ability to selectively choose which partition is protected by post-quantum cryptography.

With Covercrypt, the encryption key is public. Encrypting systems (A browser, a mobile phone, a Spark process, data engineering applications, ETLs, etc...) do not have to be secured and can directly hold the key, relaxing constraints on the infrastructure. The public key can encrypt for any partition defined by the policy.

Decryption keys can decrypt a subset of the partitions defined by the policy.

abe-partitions

Key 1 can decrypt all the France data with the following access policy:

Country::France

Key 2 can decrypt all the Sales data with the following access policy:

Department::Sales

Key 3 can decrypt the Marketing and Sales data from Spain and Germany with the following access policy:

(Department::Marketing || Department::Sales) && (Country::Spain || Country::Germany )

User keys are truly unique: even though two users have the same access policy, their key fingerprints will be different. This makes it much easier for forensic cyber teams to trace a key leak.

Policy axes can be hierarchical. Suppose three levels on a Confidentiality axis: Medium, Secret, and Top Secret. This hierarchical axis will let users with a Top Secret attribute in their key access policy decrypt Medium, Secret, and Top Secret data – whereas users with a Medium attribute will only be able to decrypt Medium data.

Finally, attributes can be rotated providing forward secrecy on selected partitions only.

The libraries are available in multiple languages to facilitate encryption close to the data source and decryption close to the decryption target, including mobile devices and browsers.

This section describes the Application Level Encryption API, the Encrypted Search API is on this page.

Supported Languages

The application-level encryption scheme is called Covercrypt. It is open-source and written in Rust. For the cryptographic documentation and implementation details, please check its GitHub repository.

Unless low-level programming in Rust, implementers should use Covercrypt through the various cloudproof_xxx user libraries:

All these libraries are open-source and available on GitHub

The user libraries all contain extensive tests, and it is highly recommended to start by hacking those tests.

Installing the Libraries

It is possible to create keys using the libraries; however, it is more likely that you will want to store them in a KMS. Please go to the KMS documentation to fetch and run a local KMS.

The library is published on npm: simply install the library in your package.json

npm install cloudproof_js

The library contains web assembly for cryptographic speed

You need Java 8+ and Maven version 3+ The cloudproof_java library is deployed on Maven central. Add the library to your POM:

<dependency>
  <groupId>com.cosmian</groupId>
  <artifactId>cloudproof_java</artifactId>
  <version>8.0.1</version>
  <type>jar</type>
</dependency>

Encryption and decryption can be accomplished in the KMS, however, you will likely want to perform them locally using the native library, compiled from Rust. The cloudproof_java library leverages Java Native Access to call the native library.

Builds of the native libraries are already provided on package.cosmian.com and can be fetched and copied via the Python script ./scripts/get_native_libraries.py to:

  • src/main/resources/linux-x86-64 (with support of GLIBC 2.17+)
  • src/main/resources/darwin-x86-64
  • src/main/resources/win32-x86-64
python3 scripts/get_native_libraries.py

To build the library yourself, please see the Covercrypt project. In your java project, the library build needs to be placed inside in

  • src/main/resources/linux-x86-64 folder for a Linux Intel machine
  • src/main/resources/linux-amd64 folder for a Linux AMD machine
  • src/main/resources/darwin folder for a Mac running MacOS
  • src/main/resources/win32-x86 folder for a Windows machine

To run tests only, place the library inside the corresponding folder above replacing main with test

Check the JNA documentation for alternative locations for the native library.

To use the native library, use statically the class Covercrypt object.

byte[] ciphertext = CoverCrypt.encrypt(...);

The instance is thread-safe and can be used for all subsequent calls.

The version 5.0.0 is available on PyPI:

pip install cloudproof_py

Import classes:

from cloudproof_py.cover_crypt import Policy, PolicyAxis, Attribute, CoverCrypt

# needed if you use Cosmian KMS
from cloudproof_py.kms import KmsClient

The library is published on pub.dev as a Dart library: https://pub.dev/packages/cloudproof. To install it:

flutter pub add cloudproof

The published package contains cryptographic native libraries called through FFI functions.

Download the prebuilt libraries and header for released versions at https://github.com/Cosmian/cloudproof_cpp/releases.

Include header file:

#include "cloudproof.h"
  • Compile and run on Linux
g++ main.cpp -o main -I include/ -L lib/ -lcloudproof
LD_LIBRARY_PATH=lib ./main
  • Compile and run on MacOS
g++ main.cpp -o main -I include/ -L lib/ -lcloudproof
DYLD_FALLBACK_LIBRARY_PATH=lib ./main
  • Compile and run on Windows
cl main.cpp  /I "include" /link "lib/libcloudproof.dll.a" /OUT:main.exe
main.exe

cloudproof.dll should be in the same directory as main.exe

Creating a Policy

A policy defines the space of rights that are used for encryption. It is composed by a set of axes that contain attributes.

For example, the following two axes are divided into four attributes and define a policy:

  1. Department: Finance, Marketing, Human Res., Sales
  2. Country: France, UK, Spain, Germany

A combination of axis attributes is associated to a partition. For example Department::Finance && Country France points to a valid partition under the above policy.

An access policy is defined by a set of partitions. It can be written as a boolean expression of axis attributes:

(Department::Marketing || Department::Sales) && Country::France

In the following demo, we will create a policy that combines two axes, a security level, and a department. A user will be able to decrypt data only if it possesses a key with a sufficient security level and the correct department.

Policy Axes

Security Level Axis:

The first policy axis is the Security Level. It is hierarchical and is composed of 3 levels: Protected, Confidential, and Top Secret. Since this axis is hierarchical, when a user is granted access to a given level, it is granted access to all lower levels. The attributes must be provided in ascending order.

Department Security Axis:

The second policy axis is the Department axis. It is composed of 3 values: HR, MKG, and FIN. This axis is not hierarchical: granting access to an attribute of this axis to a user does not give access to any other attribute.

Hybridization

Covercrypt offers the possibility to hybridize the classic asymmetric scheme with the winner of the NIST post-quantum algorithm selection process: Kyber. Hybridization can be applied with granularity since it is specified at the attribute level. A partition corresponding to a combination of attributes with at least one hybridized attribute is hybridized. Any data encrypted for a hybridized partition will result in a hybridized ciphertext.

Rotations

Rotations allow to refresh the value of a given partition. The maximum number of creations of partition values is set in the policy.

Generating the Master Keys

The master authority possesses the master key pair:

  • a master secret key which is used to generate user keys;
  • and a public key which is used for encryption. Since it cannot decrypt, the public key can be freely distributed to encrypting systems.

Keys can be generated locally using the WASM code

To generate keys you can use a Cosmian KMS server and, unless you run in dev mode, an API Key to authenticate to the server.

To generate keys from a Cosmian KMS server, instantiate a KmsClient with its URL and, unless you run in dev mode, an API Key to authenticate to the server.

And create master keys

The master keys can be exported from the KMS and their bytes recovered to use with the native library

To use the native library, simply instantiate a CoverCrypt object.

To generate keys from a Cosmian KMS server, instantiate a KmsClient with its URL and, unless you run in dev mode, an API Key to authenticate to the server.

The Python methods calling the server are asynchronous.

Encrypting Data

Data is encrypted using the Public Key and an encryption policy that determines the target partitions of the ciphertext.

Anyone who has access to the Public Key can encrypt data, however, only users possessing user keys with the right access policy can decrypt the ciphertext.

A Covercrypt ciphertext is the concatenation of an encrypted header and an encrypted content.

  • The encrypted content is a plaintext symmetrically encrypted using an ephemeral secret key (i.e. a random key generated on the fly).
  • The header is the encapsulation of the ephemeral key for a given encryption policy, concatenated with some optional metadata. The metadata, if present, is symmetrically encrypted using the same ephemeral key.

Header and encrypted content can be generated separately using the various APIs. Check the inline documentation of the various languages and the test suites for details.

The code below shows the encryption of 3 messages with 3 different encryption policies targeting 3 different partitions.

Encrypting data is done using the encrypt() function which calls the WASM function.

Encrypting data is done using the coverCryptEncrypt() function which calls the KMS to encrypt

Encrypting using the native library is fast and provides many options.

a protected marketing message

a top-secret marketing message

a protected finance message

Encrypting using the KMS is possible (and compatible with the native library) but is inefficient compared to using the native library, due to the added I/O and stress on the KMS server.

The only reason to encrypt using the KMS is to enable a "pure" java solution without native code (which is, for instance, a requirement for a plugin in an Oracle database )

a protected marketing message

a top-secret marketing message

a protected finance message

Encrypting data is done using the encrypt() function that concatenates the header (encapsulation of the ephemeral symmetric key) and the symmetrically encrypted data.

These can be done separately; see CoverCrypt.encrypt_header

a protected marketing message

a top secret marketing message

a protected finance message

Encrypting using the KMS is possible with the Python library but is inefficient compared to using the native library, due to the added I/O and stress on the KMS server.

a protected marketing message

a top secret marketing message

a protected finance message

Generating User Decryption Keys

User Decryption Keys are generated from the Master Private Key using access policies.

Let us create 2 users with different access policies to illustrate their effect.

User Decryption Keys can be generated by calling the local WASM

User keys can be locally generated using the native library

the confidential marketing user

This user can decrypt messages from the marketing department only, with a security level of Confidential or below:

the top secret marketing financial user

This user can decrypt messages from the marketing department OR the financial department, with a security level of Top Secret or below:

the confidential marketing user

This user can decrypt messages from the marketing department only, with a security level of Confidential or below:

the top secret marketing financial user

This user can decrypt messages from the marketing department OR the financial department, with a security level of Top Secret or below:

exporting the keys

As with the master keys, the user keys can be exported to be used with the native library

the confidential marketing user

This user can decrypt messages from the marketing department only, with a security level of Confidential or below:

the top secret marketing financial user

This user can decrypt messages from the marketing department OR the financial department, with a security level of Top Secret or below:

the confidential marketing user

This user can decrypt messages from the marketing department only, with a security level of Confidential or below:

the top secret marketing financial user

This user can decrypt messages from the marketing department OR the financial department, with a security level of Top Secret or below:

the confidential marketing user

This user can decrypt messages from the marketing department only, with a security level of Confidential or below:

the top secret marketing financial user

This user can decrypt messages from the marketing department OR the financial department, with a security level of Top Secret or below:

the confidential marketing user

This user can decrypt messages from the marketing department only, with a security level of Confidential or below:

Decrypting Ciphertexts

The confidential marketing user can successfully decrypt a protected marketing message:

.. however, it can neither decrypt a marketing message with higher security:

... nor decrypt a message from another department even with lower security:

As expected, the top-secret marketing financial user can successfully decrypt all messages

Rekeying keys

At any time the Master Authority can generate new keys for any access policy.

When that happens future encryption of data for a given access policy cannot be decrypted with keys that have not been "refreshed".

It is best to perform rekeying using the KMS to automatically refresh each user key if it is active in the KMS (the key has not been revoked).

rekeying all keys having access to "Department::MKG"

This operation will rekey all keys with access to Department::MKG. It is also possible to rekey all keys with a precise access policy like Department::MKG && Security Level::Protected.

You successfully rekeyed the master keys and all active user keys with access to marketing data.

See more

creating a new confidential marketing message

decrypting the messages with the re-keyed key

The automatically re-keyed confidential marketing user key can still decrypt the "old" protected marketing message, as well as the new confidential marketing message.

Pruning keys

After a rekey operation, the affected keys will store both the old and the newly generated subkeys to be able to decrypt old and new ciphertexts. If you only need access to the new ciphertexts, you can save space by pruning these keys.

By performing the pruning operation using a KMS, the active user keys will be automatically synced with the master keys.

WARNING: This operation will permanently remove access to old ciphertexts for the pruned access policy.

pruning all keys having access to "Department::MKG"

The master and user keys will only keep the latest subkeys associated to the access policy Department::MKG.

Pruned keys will only be able to decrypt ciphertexts generated after the last rekey operation for the corresponding access policy.

See more

try to decrypt an old marketing message

decrypting new marketing message

Policy editing

The Policy in use by a KMS can be modified on the fly through various operations:

  • Rename attributes: purely visual modification of the Policy.

  • Add new attributes to an existing Policy dimension. ONLY supported by unordered dimension.

  • Disable attributes: soft removal of an attribute, prevents the encryption of new messages for this attribute while keeping the ability to decrypt existing ciphertexts.

  • Remove attributes: will permanently remove the ability to use this attribute in both encryptions and decryptions. ONLY supported by unordered dimension.

The masters keys and users keys will automatically be updated accordingly by the KMS.

Rename attributes

Specify the existing attribute Dimension::Name and the desired name for the attribute NewName. The resulting attribute will be Dimension::NewName.

rename attribute "MKG" to "Marketing"

The rename operation is purely visual and will not impact the existing user keys and ciphertexts.

From now on, be sure to use the new attribute name when encrypting data or issuing new user keys. New messages can still be decrypted by marketing user keys created before the renaming and vice versa.

See more

encrypt new data with the renamed attribute

decrypt "Marketing" message with "MKG" user keys

Add attributes

Add a new attribute to an unordered dimension, specify the attribute to create with Dimension::Name and whether to use hybridized keys.

add new attribute "Department::R&D"

Create a non-hybridized attribute R&D in the existing dimension Department.

The newly added attribute can be used to encrypt and generate keys as seen previously.

See more

encrypt a message for the newly created attribute

This message will be readable by users with access to the R&D department and the security level protected or above.

generate a user key with access rights for this attribute

This user can decrypt messages from the R&D department OR the financial department, with a security level of confidential or below:

the new user key can decrypt the R&D message

Disable attributes

Remove the ability to encrypt new message for an existing attribute using the KMS. Existing messages associated to the disabled attribute will remain readable.

disable attribute "Department::R&D"

Prevent the generation of new R&D messages while keeping access to existing ones.

Encryption of new R&D messages is no longer possible.

See more

try to encrypt a new message for the disabled attribute

decryption of existing R&D ciphertext remains possible

Remove attributes

Completely removes an attribute from the policy and from all associated keys in the KMS.

WARNING: Encryption and decryption for this attribute will no longer be possible after this operation. We strongly recommend to make a local copy of the master secret key beforehand, otherwise messages whose encryption policy does not contain any other attributes belonging to the dimension of the deleted attribute will be lost.

remove "Department::R&D"

All user keys in the KMS associated with this attribute will automatically loose the ability to decrypt for this attribute.

Ciphertexts for which the only Department access was R&D are now unreadable by all KMS keys.

See more

decrypting R&D ciphertexts will fail

The Whole Code

The code used in this documentation as well as more examples can be found for each library:

If you want to see full code of usage of cloudproof_js, please check the examples directory in Github.

If you want to see full code of usage of cloudproof_java, please check the examples directory in Github.

The full code is available in the examples directory of the Github repo.

The full code is available in the examples directory of the Github repo.

Format Preserving Encryption

Format Preserving Encryption (FPE) is a cryptographic technique standardized in NIST SP 800-38G specification that allows the encryption of data while preserving its original format or structure. Unlike traditional encryption methods that transform data into ciphertext with a different format, FPE ensures that the encrypted output maintains the same format as the original input, such as preserving the length, character set, and other specific formatting requirements.

The purpose of Format Preserving Encryption is to provide confidentiality for sensitive data while minimizing the impact on applications or systems that rely on the original data format. This technique finds applications in scenarios where data format constraints are critical, such as databases, legacy systems, or data interchange processes.

FPE-FF1, the underlying cryptographic primitive, is a normalized algorithm that uses symmetric encryption, but it's not as fast or secure as standardized symmetric (or public key) encryption methods like AES or ChaCha. It should only be used where the format of the ciphertext container is constrained (e.g., a fixed database schema that cannot be changed).

Features

Cosmian FPE implementation supports the encryption of strings, floats and big integers.

In order to keep the format of the input plaintext, the alphabet used for plaintext must be specified.

There are multiple pre-defined alphabets available:

  • alpha-> abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
  • alpha_lower-> abcdefghijklmnopqrstuvwxyz
  • alpha_upper-> ABCDEFGHIJKLMNOPQRSTUVWXYZ
  • numeric-> 0123456789
  • hexa_decimal-> 0123456789abcdef
  • alpha_numeric-> 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
  • chinese
  • latin1sup
  • latin1sup_alphanum

These alphabets can easily be extended with any character.

Implementations and quick start

The FPE techniques are open-source and written in Rust. For the cryptographic documentation and implementation details, please check its GitHub repository.

Unless low-level programming in Rust, implementers should use those techniques through the various Cloudproof user libraries:

All these libraries are open-source and available on GitHub

The user libraries all contain extensive tests, and it is highly recommended to start by hacking those tests.

Setup

The library is published on npm: simply install the library in your package.json

npm install cloudproof_js

The library contains web assembly for cryptographic speed

The version 4.0.0 is available on PyPI:

pip install cloudproof_py

Import classes:

from cloudproof_py.cover_crypt import Policy, PolicyAxis, Attribute, CoverCrypt

# needed if you use Cosmian KMS
from cloudproof_py.kms import KmsClient

Encrypting integers

The most common example when using FPE is the encryption of credit card numbers. Here the alphabet numeric is used (characters of this alphabet being 0123456789) as follows:

Warning encryption integers or big integers

When encrypting integers, the important parameters are radix and digits.

  • radix is the base of the number representation (must be between 2 and 16 inclusive)
  • digits is the maximum number of digits in the representation. A value of 10 will limit the ciphertext output size to 10.

Also, integers must be passed as string

Here are various examples for integers encryption:

Encrypting chinese plaintexts

Using the chinese alphabet can be done as follows:

Encrypting using a custom alphabet

Data Anonymization

Data anonymization is the process of transforming data in such a way that it can no longer be used to identify individuals without the use of additional information. This is often done to protect the privacy of individuals whose data is being collected or processed.

Anonymization techniques can include removing identifying information such as names and addresses, replacing identifying information with pseudonyms, and aggregating data so that individual data points cannot be distinguished. It's important to note that while anonymization can reduce the risk of re-identification, it is not foolproof and must be used in conjunction with other security measures to fully protect personal data.

Features

Cosmian anonymization provides multiple methods:

  • Hashing: transforms data into a fixed-length representation that is difficult to reverse and provides a high level of anonymity.

  • Noise Addition: adds random noise to data in order to preserve privacy. Support various types of noise distributions to float, integer, and date.

  • Word Masking: hides sensitive words in a text.

  • Word Pattern Masking: replaces a sensitive pattern in text with specific characters or strings. Supports Regex.

  • Word Tokenization: removes sensitive words from text by replacing them with tokens.

  • Number Aggregation: rounds numbers to a desired power of ten. This method is used to reduce the granularity of data and prevent re-identification of individuals.

  • Date Aggregation: rounds dates based on the specified time unit. This helps to preserve the general time frame of the original data while removing specific details that could potentially identify individuals.

  • Number Scaling: scales numerical data by a specified factor. This can be useful for anonymizing data while preserving its relative proportions.

  • Format preserving encryption: encrypts data preserving its format.

Implementations and quick start

The data anonymization techniques are open-source and written in Rust. For the cryptographic documentation and implementation details, please check its GitHub repository.

Unless low-level programming in Rust, implementers should use those techniques through the various Cloudproof user libraries:

All these libraries are open-source and available on GitHub

The user libraries all contain extensive tests, and it is highly recommended to start by hacking those tests.

Setup

The library is published on npm: simply install the library in your package.json

npm install cloudproof_js

The library contains web assembly for cryptographic speed

The version 4.0.0 is available on PyPI:

pip install cloudproof_py

Import classes:

from cloudproof_py.anonymization import (
    DateAggregator,
    Hasher,
    NoiseGenerator,
    NumberAggregator,
    NumberScaler,
    WordMasker,
    WordPatternMasker,
    WordTokenizer,
)

Hashing

The goal of hashing techniques for data anonymization is to transform sensitive or personally identifiable information (PII) into a fixed-length string of characters, called a hash value, while preserving the privacy and confidentiality of the original data. Hashing is a one-way process, meaning that it is computationally infeasible to reverse-engineer the original data from the hash value.

The supported hashing algorithms are SHA2, SHA3 and Argon2:

  • SHA-2: SHA-2 consists of multiple hash functions, including SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, and SHA-512/256. These functions use the Merkle-Damgård construction and operate on a block size of 512 bits. They employ different combinations of logical functions, such as bitwise logical operations, modular addition, and logical rotation.
  • SHA-3: SHA-3 is a cryptographic hash function primarily designed for data integrity, digital signatures, and message authentication codes. It takes an input of any length and produces a fixed-length hash value.
  • Argon2: Argon2 is a password hashing function that is specifically designed for secure password storage. It aims to protect against password cracking attacks, such as brute-force and dictionary attacks, by incorporating memory-hardness and resistance to parallel computation.

In JavaScript, the hasher object needs to be instantiated with a specified algorithm and then call the method apply.

Noise addition

The goal of the Noise Addition technique for data anonymization is to introduce random noise or perturbation to sensitive data to preserve privacy while still allowing for meaningful analysis or processing. Noise addition helps protect the confidentiality of individual records by obscuring specific details while preserving the statistical properties and patterns of the dataset.

Three noise distributions are supported:

  • Laplace: The Laplace distribution, also known as the double-exponential distribution, is characterized by its peakiness around the mean and its exponential decay in the tails. It is often used to model data with heavy-tailed or skewed distributions.
  • Gaussian (Normal): The Gaussian distribution, also known as the normal distribution, is a symmetric bell-shaped distribution. It is one of the most widely used distributions in statistics and probability theory, often employed to model data that is normally distributed or approximately so.
  • Uniform: The uniform distribution is a probability distribution where all values within a specified range are equally likely. It forms a rectangle-shaped distribution with a constant probability density function (PDF) over the range.

The noiser object needs to be instantiated with a specified distribution and then call the method apply.

Either mean and standard deviation can be specified:

Or minimum or maximum bounds can be specified:

Noise can be added with correlated information and apply_correlated function as follows:

Word Masking

The purpose of the Word Masking technique in data anonymization is to protect sensitive information by replacing specific words or terms with more generic or generalized placeholders. This technique is commonly applied to text data, such as documents, emails, or chat logs, where preserving privacy is crucial while maintaining the overall meaning and context of the text.

Word Pattern Masking

Word Pattern Masking technique is similar to Word Masking but using a Regular Expression (RegEx).

Word Tokenization

Word tokenization involves splitting a given text or document into its constituent words, and then replace specific sensitive words.

Number Aggregation

The purpose of the Number Aggregation technique in data anonymization is to group or summarize numerical data to protect individual privacy while preserving the overall statistical properties and trends of the dataset. This technique is commonly applied to datasets containing sensitive numerical information, such as income, age, or medical measurements.

Date Aggregation

The purpose of the Date Aggregation technique is similar to the Number Aggregation technique.

Number Scaling

Data Scaling, also known as feature scaling or normalization, is a preprocessing technique used to standardize or transform numerical data to a common scale. The purpose of Data Scaling is not specifically related to data anonymization but rather to ensure that features or variables have similar ranges or distributions, which can benefit various data analysis and machine learning tasks. It aims to address issues that may arise when variables have different scales or units, which can impact the performance and accuracy of algorithms.

Format Preserving Encryption

Format Preserving Encryption (FPE) aims to encrypt plaintext while retaining its format (alphabet). FPE-FF1 is a normalized algorithm that uses symmetric encryption, but it's not as fast or secure as standardized symmetric (or public key) encryption methods like AES or ChaCha. It should only be used where the format of the ciphertext container is constrained (e.g., a fixed database schema that cannot be changed).

FPE can be applied to string, specifying an alphabet. Characters of the plaintext that belong to the alphabet are encrypted while the others are left unchanged at their original location in the ciphertext.

FPE can be applied to int or float, specifying radix and digits parameters:

Anonymization application flow

To facilitate the usage of these anonymization techniques, we offer advanced tools that simplify the process of anonymizing datasets.

Python package

Utilizing the cosmian-anonymization Python package, anonymize a dataframe based on a specified configuration stored in a JSON file. This file defines the techniques to be applied to each column along with specific parameters. The JSON configuration file adheres to a specific format and can be generated through the provided user interface (UI).

The library is available on PyPI:

pip install cosmian-anonymization

UI generating configuration

This user interface (UI) is crafted for constructing JSON configuration files, subsequently parsed by the Python cosmian-anonymization package. Choose the appropriate technique and parameters for each column in a given data sample, and easily download the configured JSON file.

The UI is available on GitHub, and can be run locally.

Verifiable Confidential Computing

Cosmian Enclave and Cosmian VM are solutions to compute over data

  • that keeps data encrypted at all times, even when in use
  • and that can be remotely verified at any time, i.e., that a correct non-tampered application is currently running in encrypted memory.

In other words, these solutions ensure that you know what processes your data, and that the data is always encrypted.

Both of these solutions

  • have low performance overhead compared to processing in clear text,
  • do not require modifications to existing software,
  • provide real-time remote verifiability of the hardware environment and the running software
  • can run either on the cloud or on-premise.
  • are remotely administered and verified using simple CLI tools provided by Cosmian
  • do not require external key management while in use

Security Models and Use Cases

The two solutions protect applications and data against malicious infrastructure providers (cloud administrators, third-party premises administrators, etc...) that can have physical access to the machine.

  • Cosmian Enclave is a solution that provides additional protection against the system administrator of the machine, i.e. someone with operating system level access. Cosmian Enclave is a sealed secured environment best-suited for scenarios where the provider of the application code - or its parameters such as a neural network - wants to protect its intellectual property from the system administrator. This is typically the case of collaborative confidential computing scenarios where the code provider wants to deploy its code (or its parameters) on a system administered by a third party, such as the data provider.
  • Cosmian VM provides additional flexibility and performance and is appropriate for scenarios where the running code does not have to be protected against the system administrator. This is typically the migration of an on-premise application to the cloud, or the security upgrade of an on-premise application to allow it to process data with increased confidentiality.
SolutionInfra. Admins (physical access)Sys. Admins (OS access)Use Cases
Cosmian EnclaveNo access to data or codeNo access to data or codeCollaborative confidential computing, code protection
Cosmian VMNo access to data or codeAccessMove to cloud, security upgrade of on-premise apps

Note: it is possible to "seal" a Cosmian VM by shutting down all operating system level access to the VM (such as the SSH daemon)

Cosmian Enclave

Please check the dedicated documentation at Cosmian Enclave

Cosmian VM

Please check the dedicated documentation at Cosmian VM

Summary Comparison of Cosmian Enclave and Cosmian VM

FeatureCosmian EnclaveCosmian VM
Memory encryptionYesYes
Remote verifiabilityYesYes
Remote code deploymentYes (Python ASGI)No
Offline encrypted code deploymentYesNo
Protection againstInfra. Admins + Sys. AdminsInfra. Admins
Required hardwareIntel SGXIntel TDX or AMD-SEV-SNP
Cloud providers availabilityAzure, OVHAWS, Azure, GCP, OVH
Applications typePython, Singularity containersAny application
Code modificationNoneNone
Performance overhead~+20%~+10%
Re-usability of OS imagesNoYes
Attack SurfaceSmallLarge

Note

These solutions are now available as IaaS on all the major cloud providers.

Search encrypted data

Building an index is required to search an encrypted database without having to decrypt it entirely. However, this index can also contain sensitive data and thus need to be encrypted before it can be outsourced to a Zero-Trust such as a public cloud.

Cosmian Findex is a Searchable Encryption scheme that allows building and securely retrieving information from an encrypted index.

Architecture client server

A detailed functional description of Findex is available in its functional documentation.

Implementations

The Findex library provides a Rust implementation of the Findex algorithm. This implementation is storage-technology agnostic. This means that in order for Findex to use a given storage (e.g. a database), all it needs is a Rust implementation of the Findex DB interface on top of this storage.

Two implementations of Findex are available:

  • using Cloudproof Rust library which exposes binding for other languages
  • using Findex server that provides a HTTP server offering REST API.

The current documentation focuses on the Cloudproof Rust library.

The Cloudproof Rust library provides:

  • implementations of the Findex DB interface for some popular databases;
  • a wrapper that allows easily instantiating Findex using a selected DB interface;
  • bindings to other languages (FFI/WASM/Python).

The other Cloudproof libraries build easy-to-use abstractions on top of these bindings to allow users to use the Findex API and to implement a custom Findex DB interface from languages such as JavaScript, Python or Java. See the section on DB interface implementation to know more about how to code, select and instantiate Findex with a given DB interface.

All those libraries are open-source and contain extensive tests. It is highly recommended to start by hacking some of those tests.

Using Findex using the REST DB interface

The REST DB interface relies on a DB server implementing the Findex REST API.

Setup

The library is published on NPM: simply install the library in your package.json.

npm install cloudproof_js

The library is based on WASM for speed.

TODO: update version after release

You need Java 8+ and Maven version 3+. The Cloudproof Java library is deployed on Maven central. Add the library to your POM:

<dependency>
  <groupId>com.cosmian</groupId>
  <artifactId>cloudproof_java</artifactId>
  <version>7.0.0</version>
  <type>jar</type>
</dependency>

Cloudproof Python is available on PyPI:

pip install cloudproof_py

The library is published on pub.dev as a Dart library: https://pub.dev/packages/cloudproof. To install it:

flutter pub add cloudproof

The library contains cryptographic native libraries called through FFI functions.

Instantiating Findex

Findex relies on the user to provide it with:

  • the private key;
  • the public label.

These are used to encrypt the index before it is sent to the DB for storage. See the security documentation to know more about the cryptographic algorithms used.

The REST DB interface requires an additional parameter: the server URL. A different server can be used for the two Findex tables. If only one URL is provided, it is used for both tables.

The complete example can be found in the JavaScript tests.

import { Findex, } from "cloudproof_js"

const key = randomBytes(16)
const label = 'hello, world!'
const url = 'http://localhost:8080'

const findex = new Findex(key, label)

// Instantiating an interface is performed in a dedicated step in JS since
// it is an asynchronous one.
await findex.instantiateRestInterface(serverUrl)

The complete example can be found in the Java tests.

// Dependencies:
//
// import java.security.SecureRandom;
// import com.cosmian.jna.findex.Findex;

byte[] key = new byte[16];
SecureRandom rng = new SecureRandom();
rng.nextBytes(key);

String label = "hello, world!";

String url = "http://localhost:8080";

Findex findex = new Findex(key, label, url);

The complete example can be found in the Python tests.

from cloudproof_findex import (Findex, Key)

key = Key.random()
label = 'hello, world!'
url = 'http://localhost:8080'

findex = Findex.new_with_rest_interface(key, label, url)

The REST DB interface is not (yet) available in Flutter. Therefore the SQLite is used here (see the complete test).

const dbPath = "./build/sqlite2.db";
await initDb(dbPath);
final key = base64Decode("6hb1TznoNQFvCWisGWajkA==");
SqliteFindex.init(dbPath, key, "Some Label");

Adding and deleting associations to and from the index

Adding associations to the index

Populating the index is done using the add API. It takes as argument a list of associations that map values to sets of keywords. Each value passed as input can then be retrieved by searching for any associated keyword.

This API returns the keywords that have been added to the index (meaning that no value where associated to these before).

The following snippet indexes the data 1337 under the keywords "John" and "Doe".

await findex.add([
  {
    indexedValue: Location.fromNumber(1337),
    keywords: ["John", "Doe"],
  }
])
HashMap<IndexedValue, Set<Keyword>> indexedValuesAndWords =
    new HashMap<>();

indexedValuesAndWords.put(
    new Location(1337).toIndexedValue(),
    new HashSet<>(Arrays.asList(new Keyword("John"),
                                new Keyword("Doe")))
);

KeywordSet res = findex.add(indexedValuesAndWords);
res = findex.add({ Location.from_int(1337): ['John', 'Doe'] })

It is also possible to index keywords under other keywords. Indexing keywords under other keywords can be seen as building a graph where nodes represent keywords and contain the data associated to them and a directed vertex from a node "A" to a node "B" represents "B" being indexed under "A".

In the following example the ID "0xA43C" represents John Doe. It is indexed under both "John" and "Doe" such that a search for either "John" or "Dow" would find the results indexed under this ID.

await findex.add([
  {
    indexedValue: Keyword.fromString("0xA43C"),
    keywords: ["John", "Doe"],
  }
])
HashMap<IndexedValue, Set<Keyword>> indexedValuesAndWords =
    new HashMap<>();

indexedValuesAndWords.put(
    new Keyword("0xA43C").toIndexedValue(),
    new HashSet<>(Arrays.asList(new Keyword("John"), new Keyword("Doe")))
);

KeywordSet res = findex.add(indexedValuesAndWords);
res = findex.add({ Keyword.from_string('0xA43C'): ['John', 'Doe'] })

Deleting associations from the index

Similarly to the add operation, the delete operation takes associations as argument. Instead of removing these associations from the index, it adds their negation. No association added before its negation was indexed can be found during a search.

The consequence is that the index size increases during this operation. The compact operation collapses negated associations, which allows deflating the index size.

Searching the index

The input of the search API is a set of keywords to search for, and an optional interrupt function.

It searches the index for all values indexed under the given keywords. If new keywords are found, the interrupt is called with all found values (both data and keywords). If it returns true, the search process is interrupted and the function returns with the data found so far, otherwise the index is searched for the keywords found in the previous search operation.

The result of the search is a map of the searched keywords to a set containing the data directly associated to them, the data associated to a keyword that is associated to them etc.

In the following example, the index is searched for "Mar". When provided, the interrupt stops the search after more than 10 results have been found.

// Search without interruption.
const results = await findex.search(["Mar"])

// Search with interruption.
let searchCounter = 0
const filteredResults = await findex.search(
    ["Mar"],
    async (results: IntermediateSearchResults) => {
        searchCounter += results.total()
        return 10 <= searchCounter
    }
)
// Search without interruption.
SearchResults results = findex.search(new String[] {"Mar"});

// Search with interruption.
SearchResults filteredResults = findex.search(new String[] {"Mar"},
                                              new Interrupt() {
     int counter = 0;

    @Override
    public boolean interrupt(Map<Keyword, Set<IndexedValue>> results)
    throws CloudproofException {
        for (Set<IndexedValue> values: results.values()) {
            counter += values.size();
        }
        return (10 <= counter);
    }
});
# Search without interruption.
results = instance.search(['Mar'])

counter = 0
def interrupt(res: ProgressResults, counter: int) -> bool:
    for _, values in res.items():
        counter += len(values)
    return 10 <= counter

# Search with interruption.
filtered_results = instance.search(['Mar'],
                                   lambda res: interrupt(res, counter))

Compacting the Index

The compact operation allows to:

  • collapse negated associations;
  • increase the index density;
  • remove obsolete data;
  • remove keywords not associated to any value (which can happen if all of them where negated values or obsolete data);
  • reset any knowledge gained by an attacker watching index requests. This gives a lower bound on the frequency of the compact operation allowing to maintain the safety of index (see the Findex security documentation).

The compact operation takes as argument:

  • The new key and a new label used to encrypt the index. At least one of them needs to be different from the old key and old label.
  • The compacting rate, which is a float between 0 and 1 and determines how many compact operations (1/compacting_rate) would allow to compact the index entirely.
  • An optional filter that it is called on all the data found during the compact operation and allows to filter out data that does not need to be indexed back (only the data that is returned from the filter is present in the compacted version of the index).

In the following example, the index is entirely compacted in one operation using the old key and a new label, and the data Location(1337) is removed from the index.

The JavaScript binding for the compact operation is not available yet.

Set<Location> filteredLocations = new HashSet<Location>(Arrays.asList(new Location(1337)));

findex.compact(key, "new label", 1, new FilterLocations() {
    @Override
    public List<Location> filter(List<Location> locations)
    throws CloudproofException {
        return locations.stream()
                        .filter((Location l) -> filteredLocations.contains(l))
                        .collect(Collectors.toList());
        }
});
filtered_locations = { Location.from_int(1337) }

def filter(dataset: Set[Location]):
    res = set()
    for data in dataset:
        if data not in filtered_locations:
            res.add(data)
    return res

instance.compact(key, "new label", 1, filter)

The Flutter for the compact operation is not available yet.

Using Findex with other DB interfaces

The following figure describes the relationship between the user code, Cloudproof Rust, Findex and the DB interface.

+-----------------------------------------------------------------+
| +-----------------------------------------+                     |
| |  +-------+   +------+         +-----+   | //                  |
| |  | Redis |   | HTTP |   ...   | FFI |   | // DB interfaces    |
| |  +-------+   +------+         +-----+   | //                  |
| +-----------------------------------------+                     |
| |             Findex instance             |                     |
| +-----------------------------------------+                     |
|              ^                ^                                 |
|              |                |                                 |
|      +---------------+      Findex                              |
|      | Configuration |       API                                |
|      +---------------+        |                                 |
|              |                v                                 |
|           ------------------------                              |
|          /           |            \                             |
|     +-----+      +------+     +--------+     //                 |
|     | FFI |      | WASM |     | Python |     // User interface  |
|     +-----+      +------+     +--------+     //                 |
|                                                                 |
+-----------------------------------------------------------------+
|                         Cloudproof Rust                         |
+-----------------------------------------------------------------+
                                 ^
                                 |
                                 v
+-----------------------------------------------------------------+
|           Cloudproof Js/Python/Java/Flutter (optional)          |
+-----------------------------------------------------------------+
                                 ^
                                 |
                                 v
+-----------------------------------------------------------------+
|                            User Code                            |
+-----------------------------------------------------------------+

The user code calls Cloudproof Rust (directly or using a binding from one of the other Cloudproof libraries) to instantiate a Findex. It provides the configuration with the required information (which depends on the targeted DB interface). Cloudproof Rust instantiates the selected DB interface and a Findex object on top of it, which can then be used by the client code through the Findex API.

Available Rust implementations of the Findex DB interface

The following Findex DB interfaces are implemented in Cloudproof Rust:

  • REST: interface relying on a server implementing the Findex REST API. It is called by passing the URL of the server to the configuration. It can be identical for the Entry Table and the Chain Table.
  • SQLite: called passing a path for the Entry Table and Chain Table. The path can be identical since a dedicated SQL table (entry_table resp. chain_table) is created in the SQLite DB at the given path.
  • Redis: called passing a Redis URL for the Entry Table and the Chain table in the configuration. The URL can be identical since the tokens are prefixed with an identifier allowing to determine which DB they belong to.
  • Custom: each language that exports the Findex API offers the possibility to provide an custom DB interface implementation. It is called by passing the implementation of the Findex DB interface for the Entry Table and for the Chain Table. The following section describes in details how to implement this interface in different languages.

Implementing the Findex DB interface

The DB interface needs to be implemented for:

  • the Entry Table;
  • the Chain Table.

To understand why two tables are used, see the functional documentation. What is important to know here is that these two tables are simple key/value stores, and that the Entry Table keys and the Chain Table keys are collision free. This means that a single key/value store can be used for both of them, as long as the Entry Table keys can be retrieved for the compact operation.

The following section describes the requirement of each function from the Findex DB interface and the following table describes which function from the Findex DB interface (the columns) needs to be implemented in order to use a function from the Findex API (the rows).

          +-----------+-----------+-----------+-----------+---------------+
          | `fetch`   | `upsert`  | `insert`  | `delete`  | `dump_tokens` |
+---------+-----------+-----------+-----------+-----------+---------------+
| search  |  ET + CT  |           |           |           |               |
+---------+-----------+-----------+-----------+-----------+---------------+
| add     |  ET + CT  |     ET    |    CT     |           |               |
+---------+-----------+-----------+-----------+-----------+---------------+
| delete  |  ET + CT  |     ET    |    CT     |           |               |
+---------+-----------+-----------+-----------+-----------+---------------+
| compact |  ET + CT  |           |  ET + CT  |  ET + CT  |       ET      |
+---------+-----------+-----------+-----------+-----------+---------------+

Fetch

The fetch function takes as argument a list of keys. It returns the list of key/value pairs of each key for which a value was found. The keys that didn't match any value should not be returned.

It is needs to be implemented for the both tables, for each Findex operation.

Implementation of the fetch function for an in-memory DB. The complete implementation can be found in the Cloudproof Js library.

const fetch= async (
  uids: Uint8Array[],
  table: Map<string, Uint8Array>,
): Promise<Index[]> => {
  const results = []
  for (const k of uids) {
    const v = table.get(k.toString())
    if (v !== undefined) {
      results.push(new Index(k, v))
    }
  }
  return results
}

See one of the Redis Entry Table and Redis Chain Table or the SQLite Entry Table SQLite Chain Table implementation in the Cloudproof Java library.

Implementation of the fetch function for an in-memory DB. The complete implementation can be found in the Cloudproof Rust library.

def fetch(uids, table):
    res = {}
    for k in uids:
        if k in table:
            res[k] = table[k]
    return res

Upsert

This is the most difficult function to implement since it is in charge of ensuring the concurrence safety of the index.

It takes as argument a list of old key/value pairs and a list of new key/value pairs. For each new key, it performs the following operation atomically:

If the current value stored under this key equals the old value given for this key, replace the current value with the new value. Otherwise, if the current value is not defined, return an error. Otherwise stack the current key/value pair for return.

The returned value is therefore the map of current key/value pairs for which the corresponding old key/value pair was different. If no conflict happened nothing should be returned.

It needs to be implemented for the add and delete Findex operations for the Entry Table only.

Implementation of the upsert function for an in-memory DB. The complete implementation can be found in the Cloudproof Js library.

const upsert = async (
  oldValues: UidsAndValues,
  newValues: UidsAndValues,
  table: Map<string, Uint8Array>,
): Promise<UidsAndValues> => {
  const rejected = [] as UidsAndValues

  // Add old values in a map to efficiently search for matching UIDs.
  const mapOfOldValues = new Map()
  for (const { uid, value } of oldValues) {
    mapOfOldValues.set(uid.toString(), value)
  }

  for (const { uid, value: newValue } of newValues) {
    const currentValue = table.get(uid.toString())

    if (currentValue?.toString() === mapOfOldValues.get(uid.toString())?.toString()) {
      table.set(uid.toString(), newValue)
    } else if (currentValue === undefined) {
      throw new Error(
        "Rust shouldn't send us an oldValue if the table never contained a value… (except if there is a compact between)",
      )
    } else {
      rejected.push({ uid, value: currentValue })
    }
  }
  return rejected
}

See one of the Redis Entry Table and Redis Chain Table or the SQLite Entry Table SQLite Chain Table implementation in the Cloudproof Java library.

Implementation of the upsert function for an in-memory DB. The complete implementation can be found in the Cloudproof Rust library.

def upsert(old_values: dict, new_values: dict, table: dict):
    res = {}
    for uid, new_value in new_values.items():
        current_value = entry_table.get(uid)
        old_value = old_values.get(uid)
        if old_value == current_value:
            entry_table[uid] = new_value
        elif not current_value:
            raise ValueError('current value for uid ' + uid
                + ' needs to be defined as long as the old value is defined')
        else:
            res[uid] = current_value
    return res

Insert

It takes as input a list of key/value pairs. It stores each pair and returns. An error should be returned if there already was a value for any given key.

It needs to be implemented for the add, delete and compact Findex operations for the Chain Table and for the compact operation only for the Entry Table.

Implementation of the insert function for an in-memory DB. The complete implementation can be found in the Cloudproof Js library.

const insert = async (
  links: UidsAndValues,
  table: Map<string, Uint8Array>,
): Promise<void> => {
  for (const { uid: newUid, value: newValue } of links) {
    table.set(newUid.toString(), newValue)
  }
}

See one of the Redis Entry Table and Redis Chain Table or the SQLite Entry Table SQLite Chain Table implementation in the Cloudproof Java library.

Implementation of the insert function for an in-memory DB. The complete implementation can be found in the Cloudproof Rust library.

def insert(items: dict, table):
    for uid, value in items.items():
        if uid in table:
            raise ValueError('collision on insert for UID: ' + uid)
        table[uid] = value

Delete

It takes as argument a list of keys. It removes all values stored for those keys from the DB.

It needs to be implemented for both tables for the compact Findex operation only.

The JavaScript binding for the compact operation is not available yet.

See one of the Redis Entry Table and Redis Chain Table or the SQLite Entry Table SQLite Chain Table implementation in the Cloudproof Java library.

def delete(uids, table):
    for uid in uids:
        table.pop(uid)

Dump tokens

It returns all keys stored in the Entry Table.

The JavaScript binding for the compact operation is not available yet.

See one of the Redis Entry Table and Redis Chain Table or the SQLite Entry Table SQLite Chain Table implementation in the Cloudproof Java library.

def dump_entry_tokens(table: dict):
    return table.keys()

Instantiating Findex with a custom DB interface

Given that the required functions have been implemented, Findex can then be instantiated with a custom DB interface.

await loadWasm()

const entryTable: Map<string, Uint8Array> = new Map()
const chainTable: Map<string, Uint8Array> = new Map()

const entryInterface = new DbInterface()
entryInterface.fetch = async (uids: Uint8Array[]) => {
    await fetch(uids, entryTable)
}
entryInterface.upsert = async (oldEntries: UidsAndValues,
                               newEntries: UidsAndValues) => {
    await upsert(oldEntries, newEntries, entryTable)
}

const chainInterface = new DbInterface()
chainInterface.fetch = async (uids: Uint8Array[]) => {
    await fetch(uids, chainTable)
}
chainInterface.insert = async (links: UidsAndValues) => {
    await insert(links, chainTable)
}

const findex = new Findex(key, label)
findex.instantiateCustomInterface(entryInterface, chainInterface)
Findex findex = new Findex(key,
                           label,
                           new SqliteEntryTable("/tmp/sqlite.db"),
                           new SqliteChainTable("/tmp/sqlite.db"));
entry_interface = PythonCallbacks.new()
entry_interface.set_fetch(lambda uids: fetch(uids, entry_table))
entry_interface.set_insert(lambda entries: insert(entries, entry_table))
entry_interface.set_delete(lambda entries: delete(entries, entry_table))
entry_interface.set_dump_tokens(lambda : dump_tokens(entry_table))
entry_interface.set_upsert(
    lambda old_entries, new_entries: upsert(old_entries, new_entries, entry_table))

chain_interface = PythonCallbacks.new()
chain_interface.set_fetch(lambda uids: fetch(uids, chain_table))
chain_interface.set_insert(lambda links: insert(links, chain_table))
chain_interface.set_delete(lambda links: delete(links, chain_table))

findex = Findex.new_with_custom_interface(key,
                                          label,
                                          entry_interface,
                                          chain_interface)

Eviden KMS

Note: Cosmian has become part of Eviden. The Cosmian KMS is now the Eviden KMS.

The Eviden KMS is a high-performance, source available, FIPS 140-3 compliant server application written in Rust with unique capabilities.

High-scale, secure encryption, anywhere

  • High-performance: Delivers encryption and decryption services at up to millions of operations per second, with master keys held in a secure HSM-backed environment.
  • Flexible pricing: Per-CPU pricing with no hidden costs, all connectors are included; deploying any number of servers.
  • Runs securely in public clouds: or zero-trust environments using Eviden VMs available on Azure, GCP, and AWS marketplaces. See our deployment guide.

Standards' compliance

  • FIPS 140-3 mode
  • KMIP support (versions 1.0-1.4, 2.0-2.1) in both binary and JSON formats - see KMIP documentation.
  • HSM support for Trustway Proteccio & Crypt2Pay, Utimaco general purpose, Nitrokey HSM 2, Smartcard HSMs, etc. with KMS keys wrapped by the HSM.
  • Developed in Rust, a memory safe language, with the source code available on GitHub.
  • 100% developed in the European Union.

Modern technology

Integrations

Three-in-one: Key lifecycle management + Encryption oracle + Public key infrastructure

The Eviden KMS combines the functions of a Key Management System, an Encryption Oracle, and a Public Key Infrastructure:

  • Key Management System: Manages the full key lifecycle, including on-the-fly generation and revocation, including for connected HSMs.
  • Encryption Oracle: Provides high-availability, high-scalability encryption and decryption operations at millions of operations per second with HSM-backed security.
  • PKI: Manages root and intermediate certificates, signs and verifies certificates, and uses public keys for encryption/decryption. Certificates can be exported in various formats (including PKCS#12) for applications like S/MIME encrypted emails.

The Eviden KMS supports all standard NIST cryptographic algorithms as well as advanced post-quantum cryptography algorithms like Covercrypt. See the complete supported algorithms list.

Deployment options

The Eviden KMS is available as:

User Interface

The Eviden KMS includes an intuitive graphical user interface (GUI) with support for client certificate and OIDC token authentication.

Eviden KMS UI

The UI can be fully customized to match your organization's branding.

Client CLI

The KMS CLI provides a powerful command-line interface for managing the server, handling keys, and performing encryption/decryption operations. It features integrated help and is available for multiple operating systems.

The KMS CLI is packaged as:

Quick start

To quick-start a Eviden KMS server on http://localhost:9998 that stores its data inside the container, run the following command:

docker run -p 9998:9998 --name kms ghcr.io/cosmian/kms:latest
# Minimal default configuration
[http]
port = 9998
hostname = "0.0.0.0"

To run a KMS where the database will survive restarts, run:

docker run  -p 9998:9998 --name kms --volume cosmian-kms:/cosmian-kms  ghcr.io/cosmian/kms:latest
# Same default configuration; persistence is handled by Docker volume
[http]
port = 9998
hostname = "0.0.0.0"

Please note that if you want to use the KMIP wire protocol, you need to configure TLS and the socket server using the server configuration file first.

If you do not have Docker available, install a Eviden KMS server as described in installation guide:

Using the Web UI

Open your web browser and navigate to http://localhost:9998/ui to access the GUI. Eviden KMS UI

Using the CLI

Get the KMS CLI from Cosmian packages. You can then easily manage the server:

  1. Create a 256-bit symmetric key

    ckms sym keys create --number-of-bits 256 --algorithm aes --tag my-file-key
    
    The symmetric key was successfully generated.
          Unique identifier: 87e9e2a8-4538-4701-aa8c-e3af94e44a9e
    
  2. Encrypt the image.png file with AES GCM using the key

    ckms sym encrypt --tag my-file-key --output-file image.enc image.png
    The encrypted file is available at "image.enc"
    
  3. Decrypt the image.enc file using the key

    ckms sym decrypt --tag my-file-key --output-file image2.png image.enc
    The decrypted file is available at "image2.png"
    

Encrypting and Decrypting at Scale

The Eviden KMS is architected specifically to deliver high-performance cryptography at scale for enterprise applications.

Scalability Design

The KMS scales efficiently through:

  • Vertical scaling: Adding CPUs on the same machine
  • Horizontal scaling: Deploying across multiple machines with load balancing
  • High availability: Support for high-availability configuration

Performance Considerations

Cryptographic operations themselves are rarely the bottleneck. The key factors affecting overall performance are:

  1. Network latency: Minimizing the distance between the KMS and client applications
  2. CPU resources: Allocating sufficient processing power for concurrent operations
  3. Database performance: Using optimized database configurations

Deployment Architecture

The optimal architecture places KMS instances as close as possible to the applications they serve. Thanks to Eviden KMS's:

  • Stateless design
  • Per-CPU pricing model
  • Containerized an OS packaged deployment option

This makes it practical to deploy KMS instances in multiple locations, including:

  • Private data centers
  • Edge locations
  • Public cloud environments:

architecture

Security Considerations

Placing KMS instances closer to applications can introduce security challenges by potentially exposing key material in less trusted environments.

HSM Integration

To maintain strong security while preserving performance, Eviden KMS can be backed by Hardware Security Modules (HSMs):

  • HSM integration details are explained in the HSM support section
  • HSM master keys wrap all application keys handled by the KMS
  • This protects keys at rest while only exposing the cleartext application keys in memory cache; the cache expiration delay is configurable
  • Supported HSMs include Trustway Proteccio and Utimaco

For additional security details, see the FIPS 140-3 compliance and zeroization documentation.

Additional Protection in Public Clouds

For deployments in public clouds, consider using the Eviden VM to run the KMS. The Eviden VM provides memory and disk encryption and verifiability of the KMS's integrity.

Pre-packaged KMS in Eviden VMs is available on the Azure, GCP, and AWS marketplaces.

Integration Examples

For practical examples of integrating the KMS for high-volume operations, see:

The Eviden KMS is particularly suited for application-level and client-side encryption scenarios, which may require high-performance encryption and decryption.

The KMS offers two mechanisms for encrypting and decrypting data:

  • by calling the Encrypt and Decrypt operations on the KMS KMIP API and benefiting from its parallelization, concurrency, and optimized batching capabilities.
  • by using the cosmian CLI client to encrypt and decrypt data locally, including large files.

Calling the KMS API

The KMS provides a high-performance encryption and decryption API for encrypting and decrypting data at scale.

Parallelization, concurrency, and batching

Due to its stateless user session model, the Eviden KMS is designed to use modern multi-core processors and can parallelize encryption and decryption operations across multiple cores. Parallelization can be achieved by scaling vertically (increasing the number of cores on a single machine) or horizontally (increasing the number of machines in a cluster).

The Eviden KMS can also handle multiple concurrent encryption and decryption requests on a single core using (async) concurrency primitives. The asynchronous model optimizes the use of CPU resources by allowing the CPU to perform other tasks while waiting for I/O operations to complete.

Finally, batching can further optimize the performance of encryption and decryption operations. Batching allows multiple encryption or decryption operations to be performed in a single request, reducing the overhead of making multiple requests to the KMS.

Efficient batching

The KMIP way

Batching in KMIP is achieved by sending multiple Operations in a single KMIP RequestMessage operation. The protocol is highly flexible and allows various operations to be batched together.

The Eviden KMS supports batching using the KMIP protocol.

Optimized batching

However, the overhead of the KMIP protocol can be significant, especially for small data sizes. Each Operation in a KMIP message carries significant metadata, which can be prohibitively expensive for small data sizes.

When batching encryption or decryption requests, the metadata is likely identical for each request, and the overhead of unnecessarily sending the metadata multiple times can be significant.

To address this issue, the Eviden KMS provides an optimized batching API that allows multiple encryption or decryption requests to be batched together in a single request, without the overhead of the KMIP protocol. The optimized batching API is designed to be lightweight and efficient, allowing multiple encryption or decryption requests to be batched together with minimal overhead.

The method is to use a single Encrypt or Decrypt operation with multiple data items encoded in the data field of the request.

Encoding scheme

The encoding scheme is called BulkData and encodes an array of items, each item being an array of bytes. It works as follows.

  • The encoded data starts with the 2-byte fixed sequence 0x87 0x87
  • followed by the unsigned leb128 encoded number of items in the array
  • followed, for each item, by
    • the unsigned leb128 encoded byte length of the item
    • the item itself
BulkData = 0x87 0x87 <number of items> <item 1 length> <item 1> ... <item n length> <item n>

number of items = leb128 encoded number of items
item 1 length = leb128 encoded length of item 1

When the server receives an Encrypt or Decrypt operation and detects the header 0x87 0x87, it attempts to decode the data as a BulkData array. If the decoding is unsuccessful, the server returns to the standard KMIP protocol (i.e., single item encryption or decryption).

If the decoding is successful, the server processes each item in the array as a separate encryption or decryption request and re-encodes the results.

When processing symmetric encryption results, the server will first, for each encrypted item, concatenate the IV, the ciphertext, and the MAC, and then encode the result as a single item in the BulkData array.

For AES-GCM encryption, the concatenation is as follows:

  • the IV (12 bytes)
  • the ciphertext (same size as the plaintext)
  • the MAC (16 bytes)

Performance heuristics

The Eviden KMS uses heuristics to determine the optimal batch size for encryption and decryption requests. The heuristics consider the size of the data, the number of cores available, and the expected latency of the KMS.

Typically, for 64-byte data items, the optimal batch size is around 100,000. With these kinds of batch sizes, each CPU core should be sent an average of five batches to maximize concurrency.

Hence, to encrypt 5 million messages on 10 core machines, 50 requests of 100,000 items each should be sent in parallel. On a standard server CPU, the total processing time should be around 8 seconds, excluding network latency.

Using the KMS CLI client

The KMS CLI client can be used to encrypt and decrypt data locally, including large files.

Encryption can be performed in two modes:

  • server side: the file data is sent server-side and encrypted there. This mode is well suited for small or medium files, and where a direct encryption scheme is required.
  • client side: the file data is encrypted locally using a hybrid encryption scheme with key wrapping. This mode is well-suited for any type of file, including large ones, requiring high performance.

Server-side encryption and decryption

The file content is sent to the server when using server-side encryption or decryption. To use This method uses the encrypt or decrypt command of the KMS CLI client WITHOUT specifying a --key-encryption-algorithm option.

Say, the KMS holds a 256-bit AES key with the ID 43d28ec7-7438-4d2c-a1a0-00379fa4fe5d, and you want to encrypt a file image.png with AES 256-bit GCM encryption:

ckms sym encrypt \
--data-encryption-algorithm aes-gcm \
--key-id 43d28ec7-7438-4d2c-a1a0-00379fa4fe5d \
--output-file image.enc \
image.png

To decrypt the file, use the decrypt command:

ckms sym decrypt \
--data-encryption-algorithm aes-gcm \
--key-id 43d28ec7-7438-4d2c-a1a0-00379fa4fe5d \
--output-file decrypted-image.png \
image.enc

Available ciphers

The following ciphers are available for server-side encryption and decryption:

CipherDescriptionNIST Certified?
aes-gcmAES in Galois Counter Modeyes
aes-xtsAES XTSyes
aes-gcm-sivAES GCM SIVno
chacha20-poly1305ChaCha20 Poly1305no

When in doubt, use AES GCM with a 256-bit key. AES GCM is NIST-certified (as NIST SP 800–38D) and well suited for arbitrary data encryption with a length of up to 2^39–256 bits ~ 64 GB.

Please note that for AES XTS, that.

  • The key size must be doubled to achieve the same security level: 256 bits for AES 128 and 512 bits for AES 256.
  • There is no authentication

Format of the encrypted file

The encrypted file is the concatenation of the IV (or Tweak for XTS), the ciphertext, and the MAC (None for XTS).

IV || Ciphertext || MAC

With these symmetric block ciphers, the size of the ciphertext is equal to the size of the plaintext.

The table below shows the size of the IV (tweak for XTS) and the MAC in bytes.

CipherIV sizeMAC size
aes-gcm1216
aes-xts160
aes-gcm-siv1216
chacha20-poly13051216

Client-side encryption and decryption

When using client-side encryption or decryption, the file content is encrypted locally using a hybrid encryption scheme with key wrapping:

  • A random data encryption key (DEK) is generated. The key size is 256 bits for all schemes except for AES, which is 512 bits to provide 256 bits of classic security, and 128 bits post-quantum security.
  • The DEK is used to encrypt the file content using the specified locally --data-encryption-algorithm for the data encryption mechanism (DEM).
  • The DEK is server-side encrypted (i.e., wrapped) using the specified --key-encryption-algorithm for the key encryption mechanism (KEM) and the KMS key encryption key (KEK) identified by --key-id.

To use this method, use the encrypt or decrypt command and specify BOTH the --key-encryption-algorithm and --data-encryption-algorithm.

Say, the KMS holds a 256-bit AES KEK (key encryption key) with the ID 43d28ec7-7438-4d2c-a1a0-00379fa4fe5d and you want to client-side encrypt a file image.png with AES-GCM encryption, the ephemeral KEK key is wrapped with RFC5649 (a.k.a. NIST key wrap):

ckms sym encrypt \
--data-encryption-algorithm aes-gcm \
--key-encryption-algorithm rfc5649 \
--key-id 43d28ec7-7438-4d2c-a1a0-00379fa4fe5d \
--output-file image.enc \
image.png

To decrypt the file, use the decrypt command:

ckms sym decrypt \
--data-encryption-algorithm aes-gcm \
--key-encryption-algorithm rfc5649 \
--key-id 43d28ec7-7438-4d2c-a1a0-00379fa4fe5d \
--output-file decrypted-image.png \
image.enc

Available ciphers

The following ciphers are available for client-side encryption and decryption:

  • Data Encryption
CipherDescriptionNIST Certified?
aes-gcmAES in Galois Counter Modeyes
aes-xtsAES XTSyes
chacha20-poly1305ChaCha20 Poly1305no
  • Key Wrapping (Encryption)
CipherDescriptionNIST Certified?
rfc5649NIST Key Wrapyes
aes-gcmAES in Galois Counter Modeyes
aes-xtsAES XTSyes
aes-gcm-sivAES GCM SIVno
chacha20-poly1305ChaCha20 Poly1305no

When in doubt, use the AES GCM data encryption scheme with the AES GCM key encryption scheme (or RFC5649) with a 256-bit key. These are the most widely used schemes, and they are NIST-certified.

Format of the encrypted file

The encrypted file is the concatenation of

  • the length of the key wrapping (a.k.a. encapsulation) in unsigned LEB 128 format
  • the key encapsulation
  • the data encryption mechanism (DEM) IV (or tweak for XTS)
  • the ciphertext (same size as the plaintext)
  • the data encryption mechanism (DEM) MAC
encapsulation length || encapsulation || DEM IV || Ciphertext || DEM MAC

The key encapsulation is the concatenation of

  • the key encryption mechanism (KEM) IV (or tweak for XTS, none for RFC5649)
  • the encrypted DEK (same length as the DEK, +8 bytes for RFC5649)
  • the key encryption mechanism (KEM) MAC (none for XTS and RFC5649)
KEM IV || Encrypted DEK || KEM MAC

Using AES GCM as a KEM and a DEM, the details will be as follows:

  • 1 unsigned LEB 128 byte holding the length of the encapsulation (60)
  • 60 bytes of encapsulation decomposed in :
    • 12 byte KEM IV
    • 32 bytes encrypted DEK
    • 16 byte KEM MAC
  • 12 bytes of DEM IV
  • x bytes of ciphertext (same size as plaintext)
  • 16 bytes of DEM MAC

Public Key Infrastructure (PKI)

The Eviden KMS is a full-featured X.509 certificate authority capable of issuing, storing, validating, and revoking certificates for both classical and post-quantum algorithms.

Supported standards

StandardDescriptionScope
RFC 5280X.509 PKI Certificate and CRL ProfileAll certificate operations
RFC 8017PKCS #1 v2.2 — RSA CryptographyRSA key pairs & certificates
RFC 5480ECC Subject Public Key InformationEC/ECDSA key pairs & certificates
RFC 8032Edwards-Curve Digital Signature Algorithm (EdDSA)Ed25519/Ed448 certificates
RFC 9881ML-DSA in X.509 (FIPS 204)Post-quantum signing certificates
RFC 9909SLH-DSA in X.509 (FIPS 205)Post-quantum signing certificates
RFC 9935ML-KEM in X.509 (FIPS 203)Post-quantum KEM certificates
RFC 9608No Revocation Available extensionAll self-signed end-entity certs

Not supported

The following specifications are not currently implemented:

  • Merkle Tree Certificates (IETF draft) — transparency-based certificate format.
  • Composite Certificates (draft-ietf-lamps-pq-composite-sigs / draft-ietf-lamps-pq-composite-kem) — hybrid classical+PQC keys in a single certificate.
  • OCSP responder — the KMS does not act as an OCSP responder.
  • CRL generation — the KMS does not generate CRLs; it can include crlDistributionPoints pointing to an external CRL.

Certificate export formats

Certificates and their associated private keys can be exported in:

  • PEM (.pem) — Base64-encoded, human-readable.
  • DER (.der) — Binary ASN.1 encoding.
  • PKCS#12 (.p12 / .pfx) — Bundled certificate + private key, password-protected.

Post-quantum algorithms

Non-FIPS build required

PQC algorithms (ML-DSA, SLH-DSA, ML-KEM) are only available in the non-FIPS build (--features non-fips). The FIPS build restricts algorithms to those approved under the FIPS 140-3 boundary.

Algorithm identifiers (OIDs)

ML-DSA — signing (RFC 9881, FIPS 204)

VariantOID
ML-DSA-442.16.840.1.101.3.4.3.17
ML-DSA-652.16.840.1.101.3.4.3.18
ML-DSA-872.16.840.1.101.3.4.3.19

SLH-DSA — signing (RFC 9909, FIPS 205)

VariantOID
SLH-DSA-SHA2-128s2.16.840.1.101.3.4.20
SLH-DSA-SHA2-128f2.16.840.1.101.3.4.21
SLH-DSA-SHA2-192s2.16.840.1.101.3.4.22
SLH-DSA-SHA2-192f2.16.840.1.101.3.4.23
SLH-DSA-SHA2-256s2.16.840.1.101.3.4.24
SLH-DSA-SHA2-256f2.16.840.1.101.3.4.25
SLH-DSA-SHAKE-128s2.16.840.1.101.3.4.26
SLH-DSA-SHAKE-128f2.16.840.1.101.3.4.27
SLH-DSA-SHAKE-192s2.16.840.1.101.3.4.28
SLH-DSA-SHAKE-192f2.16.840.1.101.3.4.29
SLH-DSA-SHAKE-256s2.16.840.1.101.3.4.30
SLH-DSA-SHAKE-256f2.16.840.1.101.3.4.31

ML-KEM — key encapsulation (RFC 9935, FIPS 203)

VariantOID
ML-KEM-5122.16.840.1.101.3.4.4.1
ML-KEM-7682.16.840.1.101.3.4.4.2
ML-KEM-10242.16.840.1.101.3.4.4.3

Key usage requirements

RFC-mandated key usage extensions

The Eviden KMS automatically adds the correct critical keyUsage extension to every PQC certificate it generates, per the applicable IETF standard.

ML-DSA and SLH-DSA (signing algorithms)

Per RFC 9881 §5 and RFC 9909 §6:

  • The keyUsage extension MUST be present and MUST be critical.
  • It MUST include digitalSignature.
  • For CA certificates: keyCertSign and cRLSign are added.
X509v3 Key Usage: critical
    Digital Signature

ML-KEM (key encapsulation algorithm)

Per RFC 9935 §5:

  • The keyUsage extension MUST be present and MUST be critical.
  • It MUST contain keyEncipherment and MUST NOT contain any other bit.
X509v3 Key Usage: critical
    Key Encipherment

ML-KEM cannot self-sign

ML-KEM is a key encapsulation mechanism (KEM), not a signature scheme. An ML-KEM key cannot be used to sign its own certificate. ML-KEM certificates must always be CA-issued: supply --issuer-private-key-id and --issuer-certificate-id pointing to a signing key (RSA, EC, ML-DSA, or SLH-DSA).

Generating certificates with the CLI

Self-signed ML-DSA-44 certificate

ckms certificates certify \
  --generate-key-pair \
  --algorithm ml-dsa-44 \
  --subject-name "CN=My ML-DSA CA,O=Acme,C=FR" \
  --days 365

The server will:

  1. Generate an ML-DSA-44 key pair.
  2. Issue a self-signed X.509 v3 certificate.
  3. Automatically add a critical keyUsage extension with digitalSignature (per RFC 9881).
  4. Return the certificate identifier.

CA-issued ML-KEM-512 certificate (RFC 9935)

First create an ML-DSA-44 CA:

# Step 1: create the CA certificate (self-signed ML-DSA-44)
CA_CERT_ID=$(ckms certificates certify \
  --generate-key-pair \
  --algorithm ml-dsa-44 \
  --subject-name "CN=PQC Root CA,O=Acme,C=FR" \
  --days 3650 \
  | grep "Certificate ID" | awk '{print $NF}')

# Retrieve the CA private key ID from the certificate attributes
CA_SK_ID=$(ckms certificates export \
  --certificate-id "$CA_CERT_ID" \
  --output-format json-ttlv \
  | python3 -c "import sys,json; d=json.load(sys.stdin); print(next(l['LinkedObjectIdentifier'] for l in d.get('Link',[]) if l['LinkType']=='PrivateKeyLink'))")

Then issue an ML-KEM-512 leaf certificate signed by the CA:

# Step 2: create an ML-KEM-512 leaf certificate signed by the CA
ckms certificates certify \
  --generate-key-pair \
  --algorithm ml-kem-512 \
  --subject-name "CN=ML-KEM-512 Leaf,O=Acme,C=FR" \
  --issuer-private-key-id "$CA_SK_ID" \
  --issuer-certificate-id "$CA_CERT_ID" \
  --days 365

The server will:

  1. Generate an ML-KEM-512 key pair.
  2. Issue an X.509 certificate signed by the ML-DSA-44 CA.
  3. Automatically add a critical keyUsage extension with keyEncipherment only (per RFC 9935).

SLH-DSA-SHA2-128s self-signed certificate

ckms certificates certify \
  --generate-key-pair \
  --algorithm slh-dsa-sha2-128s \
  --subject-name "CN=SLH-DSA Test,O=Acme,C=FR" \
  --days 365

RSA or EC self-signed certificate

# RSA 4096
ckms certificates certify \
  --generate-key-pair \
  --algorithm rsa4096 \
  --subject-name "CN=RSA CA,O=Acme,C=FR" \
  --days 365

# NIST P-256
ckms certificates certify \
  --generate-key-pair \
  --algorithm nist-p256 \
  --subject-name "CN=EC CA,O=Acme,C=FR" \
  --days 365

Verifying certificates with OpenSSL 3.5+

OpenSSL 3.5+ supports all NIST PQC algorithms. Use the following commands to inspect and verify PQC certificates:

# Inspect the certificate
openssl x509 -text -noout -in ml-dsa-cert.pem

# Verify a leaf certificate against a CA certificate
openssl verify -CAfile ca-cert.pem leaf-cert.pem

Using the Web UI

The Eviden KMS Web UI exposes certificate generation through the Certificate Issuance and Renewal page at /ui/certificates/certs/certify.

  • Select "4. Generate New Keypair".
  • Choose an algorithm from the Key Algorithm dropdown (e.g. ML-DSA-44 (PQC), ML-KEM-512 (KEM), RSA-4096, NIST-P256).
  • Enter a subject name.
  • Optionally provide issuer key and certificate IDs for CA-signed certificates.

Note

ML-KEM algorithms appear under (KEM) in the dropdown to signal that they require a CA issuer. Attempting to create a self-signed ML-KEM certificate will return an error.

Cross-algorithm PKI

RFC 9881 and RFC 9935 explicitly support cross-algorithm PKI: the CA signing key does not need to match the subject key algorithm.

Examples of supported combinations:

CA algorithmLeaf/subject algorithmUse case
ML-DSA-44ML-KEM-512PQC-only PKI (RFC 9935)
ML-DSA-44ML-DSA-65Hierarchical PQC signing chain
SLH-DSA-SHA2-128sML-DSA-44Cross-family PQC PKI
ML-DSA-44RSA 4096PQC CA, classical leaf (transition)
RSA 4096ML-DSA-44Classical CA, PQC leaf (transition)

Certificate lifecycle

All standard KMIP certificate lifecycle operations work with certificates:

OperationDescription
CertifyGenerate a new certificate (self-signed or CA-issued)
ExportExport in PEM, DER, or PKCS#12 format
ImportImport an externally generated certificate
ValidateValidate a certificate chain
RevokeRevoke a certificate
DestroyPermanently delete a certificate and its keys

Revocation handling

CRL distribution points

To include a CRL distribution point in a certificate, add a crlDistributionPoints entry in the extension config file passed via --certificate-extensions:

[ v3_ext ]
crlDistributionPoints=URI:http://ca.example.com/crl.pem

Authority Information Access (AIA)

The AIA extension (authorityInfoAccess, OID 1.3.6.1.5.5.7.1.1) can be added via the extension config file to point to an OCSP responder or CA issuer:

[ v3_ext ]
authorityInfoAccess=OCSP;URI:http://ocsp.example.com/,caIssuers;URI:http://ca.example.com/ca.crt

No Revocation Available (id-ce-noRevAvail, RFC 9608)

For self-signed certificates (no issuer key provided) that do not carry a CRL distribution point, the KMS automatically adds the id-ce-noRevAvail extension (OID 2.5.29.56, RFC 9608 §2). This signals to relying parties that no revocation information is available for this certificate, and that they should not reject it for lack of a CRL or OCSP response.

This behavior applies to all algorithms (RSA, EC, ML-DSA, SLH-DSA, …), not only PQC.

When validating a chain, the KMS skips CRL fetching for any certificate that carries this extension.

Anonymization

Cosmian KMS exposes a set of data anonymization methods through a dedicated REST endpoint. These methods are stateless, require no cryptographic key, and are available under /tokenize/{method} on any KMS instance built with the non-fips feature.

All endpoints accept a JSON body and return a JSON object with a single result field. Errors come back as HTTP 422 with { "code": 422, "message": "..." }.

The ckms tokenize CLI command wraps each of these endpoints if you prefer working from the command line.

Endpoints

Hash

POST /tokenize/hash

Produces an irreversible, base64-encoded hash of a string. Three algorithms are available: SHA2 (SHA-256), SHA3 (SHA3-256), and Argon2. A salt can be passed as a base64-encoded byte string; for Argon2 it is required.

{
  "data": "test sha2",
  "method": "SHA2"
}

Response:

{ "result": "Px0txVYqBePXWF5K4xFn0Pa2mhnYA/jfsLtpIF70vJ8=" }

Use Argon2 when the input has low entropy (passwords, short identifiers) and you need resistance to brute-force lookup. Use SHA2 or SHA3 for high-entropy values or when throughput matters.

Noise

POST /tokenize/noise

Adds random noise to a numeric or date value. Three distributions are supported: Gaussian, Laplace, and Uniform. You can parameterize the distribution either by mean and standard deviation, or by lower and upper bounds.

The data_type field controls what kind of input to expect: float, integer, or date. Dates must be in RFC 3339 format (2023-04-07T12:34:56Z); the timezone is preserved in the output.

{
  "data": 42000.0,
  "data_type": "float",
  "method": "Laplace",
  "mean": 0.0,
  "std_dev": 500.0
}
{
  "data": "2023-04-07T12:34:56+02:00",
  "data_type": "date",
  "method": "Uniform",
  "min_bound": -3600.0,
  "max_bound": 3600.0
}

Laplace noise is the standard choice for differential privacy budgets. Uniform noise is appropriate when you need a hard bound on the perturbation. Gaussian is a reasonable default for datasets where outliers are acceptable.

Word Mask

POST /tokenize/word-mask

Replaces every occurrence of a listed word with XXXX, case-insensitively, matching only on word boundaries.

{
  "data": "Confidential: contains -secret- documents",
  "words": ["confidential", "secret"]
}
{ "result": "XXXX: contains -XXXX- documents" }

Non-word characters surrounding a target word (dashes, punctuation, spaces) are preserved.

Word Tokenize

POST /tokenize/word-tokenize

Replaces listed words with random 16-byte hex tokens. All occurrences of the same word receive the same token within a single request, so joins across a single document remain consistent. Across separate requests, the tokens are different.

{
  "data": "confidential meeting with confidential sources",
  "words": ["confidential"]
}

The output might be:

{ "result": "3A9F1C8B2E7D4A60 meeting with 3A9F1C8B2E7D4A60 sources" }

Word Pattern Mask

POST /tokenize/word-pattern-mask

Replaces all substrings matching a regular expression with a fixed replacement string. The pattern is limited to 1024 characters.

{
  "data": "Call me at +33 6 12 34 56 78 or +1-555-123-4567",
  "pattern": "\\+[\\d\\s\\-]+",
  "replace": "[PHONE]"
}
{ "result": "Call me at [PHONE] or [PHONE]" }

Aggregate Number

POST /tokenize/aggregate-number

Rounds a number to the nearest power of ten. power_of_ten: 2 rounds to the nearest hundred; power_of_ten: -1 keeps one decimal place.

{
  "data": 1234567,
  "data_type": "integer",
  "power_of_ten": 3
}
{ "result": "1235000" }

data_type is either float or integer.

Aggregate Date

POST /tokenize/aggregate-date

Truncates a date to the specified precision. The time_unit field accepts Second, Minute, Hour, Day, Month, or Year. The timezone offset is preserved.

{
  "data": "2023-04-07T12:34:56+02:00",
  "time_unit": "Month"
}
{ "result": "2023-04-01T00:00:00+02:00" }

Scale Number

POST /tokenize/scale-number

Applies a z-score normalization followed by a linear transformation. Given the mean and standard deviation of the original distribution, each value is standardized to zero mean and unit variance, then multiplied by scale and shifted by translate.

{
  "data": 75.0,
  "data_type": "float",
  "mean": 50.0,
  "std_deviation": 15.0,
  "scale": 10.0,
  "translate": 100.0
}
{ "result": 116.6666666666667 }

data_type is either float or integer. For integers the result is rounded to the nearest integer.

Relation to Format-Preserving Encryption

The methods above are one-way or statistically approximate transformations. They do not require a key and cannot be reversed to recover the original value.

For reversible, key-based field-level encryption that preserves the format of the original value (digit strings stay digit strings, alphanumeric stays alphanumeric), see Format-Preserving Encryption (FPE FF1). FPE is accessed through the KMIP Encrypt and Decrypt operations, not through the /tokenize endpoint.

PropertyAnonymization (/tokenize)FPE (KMIP)
ReversibleNoYes
Requires a keyNoYes (256-bit AES)
Format-preservingPartial (noise, rounding)Exact
FIPS modeNoNo
Use caseAnalytics, data sharingTokenized storage, round-trip

HSM Support

The Eviden KMS can be configured to use HSMs to store and manage keys and create KMS keys wrapped by the HSM keys. This provides the best of both worlds: the security of an HSM at rest and the scalability of a KMS at runtime.

Eviden KMS natively integrates with the Proteccio, the Utimaco general purpose, the Smartcard HSM, and any HSM that has a standard PKCS#11 interface such as SoftHSMv2.

Integration Workflow

integration workflow

Main use case and benefits

Aside from providing a single interface to manage both KMS and HSM keys, the main use case for HSM support is to host keys in the KMS that are wrapped by keys stored in the HSM.

This combination provides the best of both worlds:

  • the scalability and performance of the KMS at runtime to answer a large number of requests concurrently,
  • and the hardware security of the HSM at rest, which may be a compliance requirement in some industries.

Typical use cases include:

  • securing workplace applications such as MS 365 or Google Workspace, where concurrent requests from potentially a large number of users need to be processed quickly,
  • securing big data applications such as Hadoop/Spark, Snowflake, Databricks, where a large number of encryption and decryption requests need to be processed on the fly for each request.

At Rest

KMS keys are stored in the KMS database in a wrapped form, and the wrapping key is stored in the HSM. This provides an additional layer of security for the keys stored in the KMS, since the keys stored in the HSM are protected by the HSM's hardware security mechanisms, and benefit from the HSM certifications.

At Runtime

Encryption and decryption requests from applications are processed by the KMS, which first unwraps the keys stored in the KMS database using the keys stored in the HSM. Contrary to the HSM, the KMS is a highly scalable and performant system that can handle a large number of requests concurrently.

In addition to managing its keys, Eviden KMS can act as a proxy to an HSM, storing and managing keys within the HSM.

HSM keys

HSM keys are prefixed keys. They are created with a unique identifier that is prefixed by the hsm keyword and the slot number in the form:

hsm::<slot_number>::<key_identifier>

For instance, the key hsm::1::mykey is stored in the HSM slot 1 with the identifier mykey. Technically, the identifier is stored in the LABEL field of the key object in the HSM.

!!! warning Labels must be unique within a slot The PKCS#11 standard does not enforce label uniqueness: multiple key objects can share the same LABEL in the same slot. Eviden KMS however uses the label as the sole key identifier within a slot, so it requires labels to be unique per slot per key type. If two objects of the same type share a label in the same slot, Eviden KMS will return an error when that label is referenced. Always verify that no existing object already uses a label before creating a new key with pkcs11-tool --list-objects.

!!! info CKA_ID is automatically set by Eviden KMS Eviden KMS sets both CKA_LABEL and CKA_ID (to the same bytes as the label) on every key it creates in the HSM. This conforms to PKCS#11 v2.40 and prevents spurious warnings from tools such as pkcs11-tool --list-objects. Keys provisioned externally (via pkcs11-tool or the HSM vendor software) should also have CKA_ID set to match the label bytes if they are intended to be used with Eviden KMS.

Non-prefixed keys are considered KMS keys and are stored in the KMS database.

HSM admin

The KMS server maintains a list of HSM admin users that are allowed to create and destroy objects directly in the HSM. This is configured with the hsm_admin key in kms.toml:

# One or more KMS usernames with HSM admin privileges
hsm_admin = ["alice@example.com", "bob@example.com"]

# Wildcard: any authenticated user becomes an HSM admin
hsm_admin = ["*"]

From the command line, pass one --hsm-admin flag per username (or a single comma-separated value):

# Two explicit admins
cosmian_kms --hsm-admin alice@example.com --hsm-admin bob@example.com ...

# Or via environment variable
KMS_HSM_ADMIN=alice@example.com,bob@example.com cosmian_kms ...

!!! note Authorization of HSM keys is still managed by the KMS Although key material is stored in the HSM, the KMS continues to enforce the standard ownership and access-rights model for all other operations (Encrypt, Decrypt, Get, etc.). An HSM admin can therefore grant these operations to ordinary users, who can then use the HSM key without themselves being HSM admins. See HSM keys and authorization for details.

HSM key authorization model

HSM keys follow a stricter permission model than regular KMS keys. The key principles are:

  • HSM admins are the only users who can create and destroy HSM keys.
  • All HSM admins share ownership of all HSM keys (any admin can grant, revoke, or destroy any HSM key).
  • Non-admin users can only use HSM keys they have been explicitly granted operations on.
  • The Get permission does not act as a wildcard for HSM keys — each operation must be granted individually.
  • Locate only returns HSM keys the user is authorized to see (all keys for admins, granted keys for non-admins).
  • The server KEK (Key Encryption Key) is a shared wrapping resource accessible to all users for wrapping/unwrapping, but direct cryptographic operations on the KEK itself require explicit grants.

Operations by role

OperationHSM AdminNon-admin (granted)Non-admin (no grant)
Create / CreateKeyPair
Destroy❌ (cannot be granted)
LocateAll HSM keysGranted keys onlyNone
Grant / Revoke (access rights)✅ (any HSM key)
Encrypt
Decrypt
Sign
SignatureVerify
MAC
Get / Export✅ (metadata if sensitive)
GetAttributes
SetAttribute / ModifyAttribute / AddAttribute / DeleteAttribute
Revoke (lifecycle state)❌ (not supported)

Grantable operations on HSM keys

OperationCan be granted?Notes
encryptSymmetric (AES) and asymmetric (RSA)
decryptSymmetric (AES) and asymmetric (RSA)
signRSA private keys only
signature_verifyRSA public keys only
macCompute a Message Authentication Code using the HSM key
getRetrieve key material or metadata; also implies export
exportExport key; also implies get
get_attributesRead KMS metadata
locateSearch visibility for this key
set_attributeModify KMS metadata — does not access HSM hardware
modify_attributeModify KMS metadata — does not access HSM hardware
add_attributeModify KMS metadata — does not access HSM hardware
delete_attributeModify KMS metadata — does not access HSM hardware
destroyAdmin-only — irreversible hardware operation
revokeHSM keys do not support KMIP lifecycle state changes
createAdmin-only — not a per-key grant

!!! warning Get is not a wildcard for HSM keys Unlike regular KMS keys, granting Get on an HSM key does not implicitly grant all other operations. Each operation (Encrypt, Decrypt, Sign, etc.) must be granted individually.

Creating a KMS key wrapped by an HSM key

KMS Keys can be created wrapped by an HSM key, either manually or automatically.

Manually using the CLI

To create a KMS key wrapped by an HSM key, the --wrapping-key-id argument must be used to specify the unique identifier of the HSM key.

The user creating the key must be the HSM admin (see above) or have been granted the Encrypt operation on the HSM key.

!!! note Server-level key_encryption_key is accessible to all users When the server is configured with a key_encryption_key (see Automatically using the server configuration), that KEK is a shared server resource and can be used as a wrapping key by any authenticated user, not just the HSM admin. This allows non-admin users to create their own KMS keys wrapped by the server KEK.

For instance, the following command creates a 256-bit AES key wrapped by the HSM RSA (public) key hsm::4::my_rsa_key_pk:

> ckms sym keys create --algorithm aes --number-of-bits 256 --sensitive \
  --wrapping-key-id hsm::4::my_rsa_key_pk my_sym_key
The symmetric key was successfully generated.
      Unique identifier: my_sym_key

The symmetric key is now stored in the database encrypted (wrapped) by the HSM key. The encryption happened in the HSM.

Manually using the Web UI

In the web UI, fill in the Wrapping Key ID field with the unique identifier of the HSM key. ui-wrapping

Automatically using the server configuration

The KMS server can automatically wrap all KMS keys with a specific HSM key. This is done by setting the key_encryption_key property in the TOML server configuration file or using the corresponding command line switch.

When key_encryption_key is configured, all newly created and imported keys will be automatically wrapped by the specified Key Encryption Key (KEK), typically an HSM key. Keys are stored wrapped in the KMS database, ensuring no clear-text key material is persisted.

The server provides selective automatic unwrapping through the default_unwrap_type configuration parameter. This controls which KMIP object types are automatically unwrapped when retrieved via Get or Export operations:

  • Valid values: ["PrivateKey", "PublicKey", "SymmetricKey", "SecretData"]
  • Default: [] (no automatic unwrapping)
  • When a key_encryption_key is set, it's common to configure default_unwrap_type = ["SymmetricKey", "SecretData"]

Example configuration:

# Force all keys to be wrapped by an HSM key
key_encryption_key = "hsm::4::master_kek"

# Automatically unwrap symmetric keys and secret data when retrieved
default_unwrap_type = ["SymmetricKey", "SecretData"]

When an object matching the configured types is retrieved, it is automatically unwrapped and cached in the server's memory cache (see The Unwrapped Objects Cache). This enables transparent encryption/decryption operations without storing clear-text keys in the database while minimizing HSM calls through expiring caching.

Using the wrapped KMS key

The symmetric key created above can now be used to encrypt and decrypt data, and the KMS will transparently unwrap the key using the HSM key.

This unwrapping will happen once, and the unwrapped symmetric key will be cached in memory for later operations; no clear-text symmetric key will be stored in the KMS database.

Small data: encrypting server-side

For example, to encrypt a message with the key my_sym_key server-side, the following command can be used:

> ckms sym encrypt --key-id my_sym_key /tmp/secret.txt
The encrypted file is available at "/tmp/secret.enc"

To decrypt a message with the key my_sym_key, the following command can be used:

> ckms sym decrypt --key-id my_sym_key --output-file /tmp/secret.recovered.txt /tmp/secret.enc
The decrypted file is available at "/tmp/secret.recovered.txt"

Large data: encrypting client side with key wrapping

To encrypt a large file with the key my_sym_key client side, the following command can be used:

>ckms sym encrypt --key-id my_sym_key_2 --data-encryption-algorithm aes-gcm \
--key-encryption-algorithm rfc5649 /tmp/large.bin
The encrypted file is available at "/tmp/large.enc"

In this case, an ephemeral symmetric key (the Data Encryption Key, DEK) is generated and used to encrypt the data. The DEK is then encrypted/wrapped with RFC4659 (a.k.a NIST AES Key Wrap) with the key my_sym_key, called the Key Encryption Key, KEK. The wrapping of the DEK by the KEK is stored at the beginning of the encrypted file. At rest, in the KMS database, my_sym_key is stored encrypted/wrapped with the HSM key hsm::4::my_rsa_key_pk.

To decrypt a large file with the KEK my_sym_key client side, the following command can be used:

> ckms sym decrypt --key-id my_sym_key_2 --data-encryption-algorithm aes-gcm \
  --key-encryption-algorithm rfc5649 --output-file /tmp/large.recovered.bin /tmp/large.enc
The decrypted file is available at "/tmp/large.recovered.bin"

Unwrapping a KMS-wrapped key from a file

If a KMS key was exported in wrapped KMIP JSON TTLV format (for example, via ckms sym keys export without --unwrap), it can later be unwrapped using ckms sym keys unwrap.

When the unwrapping key is an HSM key (identified by the hsm:: prefix), the KMS performs the unwrap server-side using its crypto oracle: the wrapped file is imported to the KMS with key_wrap_type=NotWrapped, the server decrypts it using the HSM key, and the result is exported back to the output file. This is transparent to the caller and works even when the HSM key is marked sensitive (non-extractable).

# Export a wrapped DEK to disk
ckms sym keys export --key-id my_sym_key /tmp/my_sym_key_wrapped.json

# Unwrap it using the HSM KEK — the KMS handles the decryption server-side
ckms sym keys unwrap --unwrap-key-id hsm::4::master_kek \
  /tmp/my_sym_key_wrapped.json /tmp/my_sym_key_unwrapped.json

The Unwrapped Objects Cache

The unwrapped cache is a memory cache, and it is not persistent. The unwrapped cache is used to store unwrapped objects that are fetched from the database.

When a wrapped object is fetched from the database, it is unwrapped and stored in the unwrapped cache. Further calls to the same object will use the unwrapped object from the cache until the cache expires.

The time in minutes after an unused object is evicted from the cache is configurable using the unwrapped_cache_max_age setting. The default is 15 minutes.

When HSM keys wrap objects, a long expiration time reduces the number of calls made to the HSM to unwrap the object. However, increasing the cache time will increase the memory the KMS server uses and expose the key in clear text in the memory for a longer time.

HSM KMIP operations

Some KMIP operations can be performed directly via the KMS server API on the HSM keys.

Create

Create a new key in the HSM. The key unique must be provided on the request and must follow the hsm::<slot_number>::<key_identifier> format described above. Only HSM admin users can create keys directly in the HSM (see HSM admin above).

RSA and AES keys are supported.

When creating an RSA key, the key_identifier will be that of the private key. The corresponding public key will be automatically created and stored in the HSM with the same key_identifier but with the _pk suffix, for example, the public key of the hsm::1::mykey private key will be created with a unique identifier hsm::1::mykey_pk.

Create an RSA 4096-bit key on the HSM slot 4, with the KMS CLI:

❯ ckms rsa keys create --size_in_bits 4096 hsm::4::my_rsa_key
The RSA key pair has been created.
      Public key unique identifier: hsm::4::my_rsa_key_pk
      Private key unique identifier: hsm::4::my_rsa_key

Create an AES 256-bit key on HSM slot 4, with the KMS CLI:

❯ ckms sym keys create --algorithm aes --number-of-bits 256 hsm::4::my_aes_key
The symmetric key was successfully generated.
   Unique identifier: hsm::4::my_aes_key

HSM keys are always created with CKA_SENSITIVE=true (private/symmetric key material cannot be exported). Pass --sensitive false explicitly only if you intentionally need an extractable key.

Note: HSM keys do not support object tagging in this release.

Using pkcs11-tool directly

Keys can also be provisioned directly in the HSM with pkcs11-tool (part of OpenSC), bypassing the Eviden KMS entirely. This is useful for pre-provisioning a master KEK before the KMS server starts, or for HSM models where the Eviden PKCS#11 integration does not yet support key generation.

The LABEL set with --label becomes the <key_identifier> part of the Eviden KMS unique identifier hsm::<slot_number>::<label>.

Step 1 — List available slots
pkcs11-tool --module /tw/oemDist/libnethsmpkcs11.so --list-slots
Step 2 — Create an AES key

--key-type AES:<bytes> — size in bytes (16 = 128-bit, 24 = 192-bit, 32 = 256-bit).

# AES-128 key on slot 1, label "master_kek"
pkcs11-tool --module /tw/oemDist/libnethsmpkcs11.so \
  --slot 1 \
  --key-type AES:16 \
  --keygen \
  --label master_kek

# AES-256 key on slot 1, label "data_kek"
pkcs11-tool --module /tw/oemDist/libnethsmpkcs11.so \
  --slot 1 \
  --key-type AES:32 \
  --keygen \
  --label data_kek

If the slot requires a PIN, add --login --pin <PIN> (or --login alone to be prompted interactively):

pkcs11-tool --module /tw/oemDist/libnethsmpkcs11.so \
  --slot 1 \
  --login --pin 1234 \
  --key-type AES:32 \
  --keygen \
  --label data_kek
Step 3 — Create an RSA key pair
# RSA-4096 key pair on slot 4, label "my_rsa_key"
pkcs11-tool --module /tw/oemDist/libnethsmpkcs11.so \
  --slot 4 \
  --login --pin 1234 \
  --key-type RSA:4096 \
  --keypairgen \
  --label my_rsa_key

The private key label becomes hsm::4::my_rsa_key in Eviden KMS. For RSA key pairs, Eviden KMS appends _pk to the label to build the public key identifier: hsm::4::my_rsa_key_pk.

Step 4 — Verify the objects are visible

Always check for existing objects with the same label before creating a new key — Eviden KMS requires labels to be unique within a slot and key type:

pkcs11-tool --module /tw/oemDist/libnethsmpkcs11.so \
  --slot 1 \
  --list-objects

The AES key created above will then be addressable in Eviden KMS as hsm::1::master_kek and can immediately be used as a KEK:

# kms.toml
key_encryption_key = "hsm::1::master_kek"

Destroy

Unlike KMS keys, HSM keys must not be revoked before being destroyed. The Destroy operation will remove the key from the HSM.

Only HSM admin users can destroy keys in the HSM. The Destroy operation cannot be delegated to non-admin users.

To destroy the key hsm::4::my_rsa_key, the following command can be used:

❯ ckms rsa keys destroy --key-id hsm::4::my_rsa_key
Successfully destroyed the key.
      Unique identifier: hsm::4::mykey

To destroy the corresponding public key hsm::4::my_rsa_key_pk, the following command can be used:

❯ ckms rsa keys destroy --key-id hsm::4::my_rsa_key_pk
Successfully destroyed the object.
   Unique identifier: hsm::4::my_rsa_key_pk

Get - Export

The Get and Export operations are used to retrieve the key material from the HSM. Only HSM admin users, or a user granted the Get operation by an HSM admin, can retrieve keys from the HSM.

Private or symmetric keys marked as sensitive cannot be retrieved from the HSM. The public key of a key pair can always be retrieved.

To export the public key hsm::4::my_rsa_key_pk in PKCS#8 PEM format, the following command can be used:

❯ ckms rsa keys export --key-id hsm::4::my_rsa_key_pk --key-format pkcs8-pem /tmp/pubkey.pem
The key hsm::4::my_rsa_key_pk of type PublicKey was exported to "/tmp/pubkey.pem"
   Unique identifier: hsm::4::my_rsa_key_pk

To export the private key hsm::4::mykey in PKCS#8 PEM format, the following command can be used:

❯ ckms rsa keys export --key-id hsm::4::my_rsa_key --key-format pkcs8-pem /tmp/privkey.pem
The key hsm::4::my_rsa_key of type PrivateKey was exported to "/tmp/privkey.pem"
   Unique identifier: hsm::4::my_rsa_key

To export the symmetric key hsm::4::my_aes_key in raw format (i.e., raw bytes), the following command can be used:

❯ ckms sym keys export --key-id hsm::4::my_aes_key --key-format raw /tmp/symkey.raw
The key hsm::4::my_aes_key of type SymmetricKey was exported to "/tmp/symkey.raw"
   Unique identifier: hsm::4::my_aes_key

Encrypt

Symmetric keys and public keys can be used to encrypt data. Only HSM admin users, or a user granted the Encrypt operation by an HSM admin, can encrypt data with keys stored in the HSM.

For symmetric keys, only AES GCM is supported. CKM_RSA_PKCS_OAEP and the now-deprecated, but still widely used, CKM_RSA_PKCS (v1.5) are supported for RSA keys. The hashing algorithm is fixed to SHA256.

When using RSA, the maximum message size in bytes is:

  • PKCS#1 v1.5: (key size in bits / 8) - 11
  • OAEP: (key size in bits / 8) - 66

To encrypt a message with the public key hsm::4::my_rsa_key_pk and the CKM RSA PKCS OAEP algorithm, the following command can be used:

❯ ckms rsa encrypt --key-id hsm::4::my_rsa_key_pk --encryption-algorithm ckm-rsa-pkcs-oaep \
/tmp/secret.txt
The encrypted file is available at "/tmp/secret.enc"

To encrypt a message using AES GCM with the symmetric key hsm::4::my_aes_key, the following command can be used:

❯ ckms sym encrypt --key-id hsm::4::my_aes_key --data-encryption-algorithm aes-gcm /tmp/secret.txt
The encrypted file is available at "/tmp/secret.enc"

Decrypt

Symmetric keys and private keys can be used to decrypt data. Only HSM admin users, or a user granted the Decrypt operation by an HSM admin, can decrypt data with keys stored in the HSM.

For symmetric keys, only AES GCM is supported. CKM_RSA_PKCS_OAEP and the now-deprecated, but still widely used, CKM_RSA_PKCS (v1.5) are supported for RSA keys. The hashing algorithm is fixed to SHA256.

To decrypt a message with the private key key hsm::4::hsm::4::my_rsa_key and the CKM RSA PKCS OAEP algorithm, the following command can be used:

❯ ckms rsa decrypt --key-id hsm::4::my_rsa_key --encryption-algorithm ckm-rsa-pkcs-oaep \
  --output-file /tmp/secret.recovered.txt /tmp/secret.enc
The decrypted file is available at "/tmp/secret.plain"

To decrypt a message using AES GCM with the symmetric key hsm::4::my_aes_key, the following command can be used:

> ckms sym decrypt --key-id hsm::4::my_aes_key --data-encryption-algorithm aes-gcm \
  --output-file /tmp/secret.recovered.txt /tmp/secret.enc
The decrypted file is available at "/tmp/secret.recovered.txt"

Multi-HSM Support

Eviden KMS can connect to multiple Hardware Security Modules simultaneously. Each HSM instance is independently initialised via PKCS#11 and exposed to the rest of the server through a unique routing prefix derived from its model name.


Routing prefix scheme

Every KMS object stored inside an HSM has a UID of the form:

hsm::<model>::<slot_id>::<key_id>

The model name embedded in the prefix allows the server to route any KMIP operation to the correct HSM without additional configuration.

ConfigurationPrefixExample UID
Single softhsm2 instancehsm::softhsm2hsm::softhsm2::0::my-aes-key
Single utimaco instancehsm::utimacohsm::utimaco::0::another-key
First of two softhsm2 instanceshsm::softhsm2hsm::softhsm2::0::key-a
Second of two softhsm2 instanceshsm::softhsm2_1hsm::softhsm2_1::1::key-b
Third of two softhsm2 instanceshsm::softhsm2_2hsm::softhsm2_2::2::key-c

When the same model appears more than once, the second instance gets the suffix _1, the third _2, and so on (e.g. hsm::softhsm2, hsm::softhsm2_1, hsm::softhsm2_2).


Configuration

Option A — flat single-HSM CLI flags (backward compatible)

The existing --hsm-model, --hsm-admin, --hsm-slot, and --hsm-password flags continue to work exactly as before. They configure one HSM instance whose prefix is "hsm::<model>".

Option B — TOML [[hsm_instances]] array (multi-HSM)

Add one [[hsm_instances]] section per HSM in kms.toml. When this section is present it takes precedence over the flat CLI flags.

# First HSM — prefix "hsm::softhsm2"
[[hsm_instances]]
hsm_model    = "softhsm2"
hsm_admin    = ["tech@example.com"]
hsm_slot     = [0]
hsm_password = ["changeme"]

# Second HSM — prefix "hsm::utimaco"
[[hsm_instances]]
hsm_model    = "utimaco"
hsm_admin    = ["tech@example.com"]
hsm_slot     = [0, 1]
hsm_password = ["slot0pass", "slot1pass"]

# Third HSM — same model as first, disambiguated as "hsm::softhsm2_1"
[[hsm_instances]]
hsm_model    = "softhsm2"
hsm_admin    = ["tech@example.com"]
hsm_slot     = [2]
hsm_password = ["anotherpass"]
FieldDescription
hsm_modelHSM model: softhsm2, utimaco, proteccio, crypt2pay, smartcardhsm, other
hsm_adminList of KMS user identities that have admin rights on this HSM
hsm_slotPKCS#11 slot indices to open
hsm_passwordLogin passwords for the corresponding slots (same order as hsm_slot)

Checking HSM status at runtime

The GET /hsm/status endpoint returns a JSON array of all configured HSM instances. Authentication is required (pass the usual credentials):

curl http://localhost:9998/hsm/status

Example response:

[
  {
    "prefix": "hsm::softhsm2",
    "model": "softhsm2",
    "slots": [
      { "slot_id": 0, "accessible": true }
    ]
  },
  {
    "prefix": "hsm::utimaco",
    "model": "utimaco",
    "slots": [
      { "slot_id": 0, "accessible": true },
      { "slot_id": 1, "accessible": false }
    ]
  }
]

The same information is available in the HSM Status page of the Web UI (Objects → HSM Status).


Supported platforms

HSM support is available on:

  • Linux x86_64 — all models (softhsm2, utimaco, proteccio, crypt2pay, smartcardhsm, other)
  • macOS (arm64 / x86_64) — softhsm2 and smartcardhsm only

See also

The Smartcard HSM integration is supported on Linux (x86_64). It has been tested with the following devices: BULL PKCS11 C2P 5.0.7 (Release) Unix64

HSM files installation

Copy the Crypt2pay PKCS#11 library to /lib

Copy libpkcs11c2p.so to /lib. Make sure it is readable by the user running the KMS.

Create the c2p directory

Create a c2p directory in, say, /etc/c2p (hereafter called [C2P_DIR])

In this directory copy the following files:

  • c2padmin <- The Crypt2pay admin tool
  • c2p.xml <- The Crypt2pay configuration file
  • ca.der <- The CA certificate
  • installca <- The Crypt2pay CA installation tool
  • p11tool <- The PKCS#11 tool used to test the connection
  • The two files with extensions .kdk and .ksk <- The client key files

Install the CA certificate

This certificate is the one that signed the HSM certificate and will be used to authenticate the HSM. In the [C2P_DIR], run the installca tool:

./installca -i ./ca.der ssl

This will create an ssl directory in the [C2P_DIR] and copy the CA certificate there. To check that the CA certificate is installed correctly, run:

./installca -l ./ssl/

Edit the c2p.xml file and insert the full path to the CA certificate ssl directory in C2Pconfig/sslDefinition/Authorities:

<C2Pconfig>
  ...
  <sslDefinition>
   <Authorities>[C2P_DIR]/ssl</Authorities>
  </sslDefinition>
  ...
</C2Pconfig>

replace [C2P_DIR] with the actual path.

Set logging and Verify the c2p.xml file

In the c2p.xml file, set the logging to

<C2Pconfig>
  <TraceLevel>debug functions parameters pkcs hsm</TraceLevel>
  <TraceFile>+logs\c2p.trc</TraceFile>
  ...
</C2Pconfig>

Check the Crypt2pay manual to make sure that other elements of the c2p.xml are correct, in particular,

  • the name of the . ksk file in C2Pconfig/KSKfile
  • the name of the . kdk file in C2Pconfig/C2pSlot/C2PBox/KDKfile
  • the IP address of the HSM in C2Pconfig/C2pSlot/C2PBox/IP

.. and recover the configured Slot ID(s) in C2Pconfig/C2pSlot[Id]"

IMPORTANT NOTE: The configuration above authenticates the HSM only. To configure mutual authentication with mTLS, additional configuration is required. Check the Crypt2pay manual for details.

Set the C2P_CONF environment variable to [C2P_DIR]/c2p.xml

export C2P_CONF=[C2P_DIR]/c2p.xml

replace [C2P_DIR] with the actual path.

Test the configuration

Run the p11tool tool to create a new 256-bit AES key:

./p11tool -genkey -keyalg aes -keysize 256 -shared /lib/libpkcs11c2p.so -slot 1 -verbose

The creation should be successful and print the key alias and ID:

use slot #1
Alias 'mykey' selected
Secret key #1000004 created

The logs are available in [C2P_DIR]/logs/c2p.trc.

KMS Configuration

When using the TOML configuration file, enable HSM support by setting these parameters:

hsm_model = "crypt2pay"
hsm_admin = "<HSM_ADMIN_USERNAME>" # defaults to "admin"
hsm_slot = [0, 0, ] # example [0,4] for slots 0 and 4
hsm_password = ["<password>", "<password>", ] # example ["648219", "648219"] for slots 0 and 4

NOTE: hsm_slot and hsm_password must always be arrays, even if only one slot is used.

The order of the passwords must match the order of the slots in the hsm_slot array.

If you want to login with an empty (null) password, use an empty string.

If you do not want to login, use the special password value <NO_LOGIN>

Configuration via command-line

HSM support can also be enabled with command-line arguments:

--hsm-model "crypt2pay" \
--hsm-admin "<HSM_ADMIN_USERNAME>"  \
--hsm-slot <number_of_1st_slot> --hsm-password <password_of_1st_slot> \
--hsm-slot <number_of_2and_slot> --hsm-password <password_of_2and_slot>

The hsm-model parameter is the HSM model. Use crypt2pay.

The hsm-admin parameter is the username of the HSM administrator. The HSM administrator is the only user who can create objects on the HSM via the KMIP Create operation and delegate other operations to other users.

The hsm-slot and hsm-password parameters are the slot number and user password (PIN) of the HSM slots used by the KMS. These options can be repeated to configure multiple slots.

NOTE: To list available slots and keys run from the [C2P_DIR] directory:

./p11tool -list -shared /lib/libpkcs11c2p.so -verbose

Eviden KMS natively integrates with the Trustway Proteccio HSM.

Proteccio library setup

This solution works on Linux (x86_64) and has been validated against the Proteccio nethsm library version 3.17.

The KMS expects:

  • the Proteccio nethsm library to be installed in /lib/libnethsm.so
  • and the Proteccio configuration files in /etc/proteccio.

/etc/proteccio configuration files

The Proteccio client library reads its configuration from /etc/proteccio. The directory must contain the following files:

FileDescription
proteccio.rc or proteccio.iniMain INI configuration file (see below)
proteccio.crtServer TLS certificate (PEM) used to authenticate the HSM appliance
proteccio_client.keyClient TLS private key (PEM) for mutual TLS authentication
proteccio_client.crtClient TLS certificate (PEM) for mutual TLS authentication
secchannel_hsm.pemHSM secure-channel public key (EC, PEM)
secchl_clt_privkey.pemClient secure-channel private key (PEM)
secchl_clt_pubkey.pemClient secure-channel public key (PEM)

The proteccio.rc / proteccio.ini file contains two sections:

[PROTECCIO]
IPaddr=<HSM_IP_ADDRESS>   ; IP address of the Proteccio appliance
SSL=1                     ; Enable TLS (1) or plain TCP (0)
SrvCert=proteccio.crt     ; Server certificate filename (relative to /etc/proteccio)
SEC_CHANNEL=1             ; Enable encrypted secure channel (1) or not (0)
SecChlSrvKey=secchannel_hsm.pem  ; HSM secure-channel public key filename

[CLIENT]
Mode=0                    ; 0 = synchronous
LoggingLevel=7            ; Verbosity (0–7, 7 = most verbose)
LogFile=proteccio_log_file.log   ; Path to the library log file
ClntKey=proteccio_client.key     ; Client TLS private key filename
ClntCert=proteccio_client.crt    ; Client TLS certificate filename
SecChlClntPrivKey=secchl_clt_privkey.pem  ; Client secure-channel private key
SecChlClntPubKey=secchl_clt_pubkey.pem    ; Client secure-channel public key

NOTE: All filenames in the configuration file are relative to /etc/proteccio unless an absolute path is given.

A secondary status log (HSM_Status.log) is written to /etc/proteccio by the nethsmstatus monitoring daemon and records HSM availability events. It is not read by the KMS.

Please run the nethsmstatus tool to check the status of the HSM before proceeding with the rest of the installation.

KMS configuration

At least one slot and its corresponding password must be configured. Any slot and any number of slots may be used.

When using the TOML configuration file, the HSM support is enabled by configuring these 4 parameters:

hsm_model = "proteccio"
hsm_admin = "<HSM_ADMIN_USERNAME>" # defaults to "admin"
hsm_slot = [0, 0, ] # example [1,4] for slots 1 and 4
hsm_password = ["<password>", "<password>", ] # example ["pass1", "pass4"] for slots 1 and 4

NOTE: hsm_slot and hsm_password must always be arrays, even if only one slot is used.

The order of the passwords must match the order of the slots in the hsm_slot array.

If you want to login with an empty (null) password, use an empty string.

If you do not want to login, use the special password value <NO_LOGIN>

When the KMS is started from the command line, the HSM support can be enabled by using the following arguments:

--hsm-model "proteccio" \
--hsm-admin "<HSM_ADMIN_USERNAME>"  \
--hsm-slot <number_of_1st_slot> --hsm-password <password_of_1st_slot> \
--hsm-slot <number_of_2and_slot> --hsm-password <password_of_2and_slot>

The hsm-model parameter is the HSM model to be used; use proteccio

The hsm-admin parameter is the username of the HSM administrator. The HSM administrator is the only user that can create objects on the HSM via the KMIP Create operation the delegate other operations to other users. (see below)

The hsm-slot and hsm-password parameters are the slot number and password of the HSM slots to be used by the KMS. These arguments can be repeated multiple times to specify multiple slots.

This solution works on Linux (x86_64) and has been validated against the Utimaco client library version 6.0.

Utimaco library setup

This solution works on Linux (x64_86) and has been validated against the Utimaco libcs_pkcs11_R3.so library version 6.0.

The KMS expects:

  • the Utimaco cs_pkcs11_R3 library to be installed in /lib/libcs_pkcs11_R3.so
  • the Utimaco configuration file cs_pkcs11_R3.cfg to be in /etc/utimaco and
  • and the environment variable CS_PKCS11_R3_CF to point to it, i.e.,
export CS_PKCS11_R3_CFG=/etc/utimaco/cs_pkcs11_R3.cfg

Please make sure the cs_pkcs11_R3.cfg is set with the correct parameter, and validate your installation with the p11tool2 utility, by running, for instance,

./p11tool2 Slot=0 GetSlotInfo

KMS configuration

At least one slot and its corresponding password must be configured. Any slot and any number of slots may be used.

When using the TOML configuration file, the HSM support is enabled by configuring these 4 parameters:

hsm_model = "utimaco"
hsm_admin = "<HSM_ADMIN_USERNAME>" # defaults to "admin"
hsm_slot = [0, 0, ] # example [0,4] for slots 0 and 4
hsm_password = ["<password>", "<password>", ] # example ["pass0", "pass4"] for slots 0 and 4

NOTE: hsm_slot and hsm_password must always be arrays, even if only one slot is used.

The order of the passwords must match the order of the slots in the hsm_slot array.

If you want to login with an empty (null) password, use an empty string.

If you do not want to login, use the special password value <NO_LOGIN>

When the KMS is started from the command line, the HSM support can be enabled by using the following arguments:

--hsm-model "utimaco" \
--hsm-admin "<HSM_ADMIN_USERNAME>"  \
--hsm-slot <number_of_1st_slot> --hsm-password <password_of_1st_slot> \
--hsm-slot <number_of_2and_slot> --hsm-password <password_of_2and_slot>

The hsm-model parameter is the HSM model; use utimaco.

The hsm-admin parameter is the username of the HSM administrator. The HSM administrator is the only user who can create objects on the HSM via the KMIP Create operation and delegate other operations to other users. (see below)

The hsm-slot and hsm-password parameters are the slot number and user password of the HSM slots used by the KMS. These arguments can be repeated multiple times to specify various slots.

Using the simulator

Utimaco provides a simulator that can be used instead of a physical HSM to test your installation. The simulator is a 32-bit Linux i386 library (it also exists as a Windows binary).

Follow these general steps to install the simulator on a Debian-based (e.g., Ubuntu) Linux amd64/x86_64.

  1. Enable 32-bit support

    sudo dpkg --add-architecture i386
    

    Then

    sudo apt-get update
    sudo apt-get install libc6:i386 libncurses5:i386 libstdc++6:i386
    
  2. Start the simulator

    In <eval-bundle-6.0.0>\Software\Windows\Simulator\sim5_windows\bin, run

    .\bl_sim5.exe -h -o -d ..\devices\
    
  3. Make sure the Device in cs_pkcs11_R3.cfg points to the simulator.

  4. Initialize a slot and create the Security Officer and User pins.

    Due to a bug (?) in the simulator, the Security Officer PIN must be set then changed before the User PIN can be set, and then changed as well.

    # Set the SO PIN to 11223344
    ./p11tool2 Slot=0 login=ADMIN,./key/ADMIN_SIM.key  InitToken=11223344
    # Change the SO PIN to 12345678
    ./p11tool2 Slot=0 LoginSO=11223344 SetPin=11223344,12345678
    

    Failing to change the SO PIN before setting the User PIN will result in the following error: Error 0x000001B8 ( CKR_PIN_TOO_WEAK)

    # Set the User PIN to 11223344
    ./p11tool2 Slot=0 LoginSO=12345678 InitPin=11223344
    # Change the User PIN to 12345678
    ./p11tool2 Slot=0 LoginUser=11223344 SetPin=11223344,12345678
    

    Now, both the SO and User PINs have been set to 12345678.

    To list objects on Slot 0, use:

    ./p11tool2 Slot=0 LoginUser=12345678 ListObjects
    

The Smartcard HSM integration is supported on Linux (x86_64). It has been tested with the following devices:

  • SC HSM 4k (Smartcard and USB variants)
  • Nitrokey HSM 2

Smartcard HSM library setup

To use a Smartcard HSM with the KMS, the sc-hsm-embedded PKCS#11 library must be installed. The integration has been validated with version 2.12.

The KMS expects the library to be installed at /usr/local/lib/libsc-hsm-pkcs11.so.

To build and install the library, follow the instructions in the sc-hsm-embedded GitHub repository.

Smartcard HSM initialisation

⚠️ Warning:

  • Initialization is destructive. It will erase all existing keys and objects on the HSM.
  • Losing the Security Officer (SO) PIN will prevent future resets of the HSM. This can make the device permanently unusable. Keep it secure.

NOTE: The default PINs shown below are recommended by the manufacturer for development purposes. Always change them in production.

Before use, the HSM must be initialized. There are two ways to accomplish this:

Graphical Initialization (SmartCard-HSM Key Manager)

  1. Download and install Smart Card Shell.
  2. Launch Smart Card Shell and insert the HSM.
  3. From the File menu (or with CTRL+M), open the SmartCard-HSM Key Manager.
  4. Right-click on the HSM entry and select Initialize Device.
  5. Follow the on-screen guide.

Using sc-hsm-tool

  1. Install the OpenSC for your distribution.

  2. Initialize the card with a Security Officer and a User PIN.

    sc-hsm-tool --initialize --so-pin 3537363231383830 --pin 648219 --label "SC HSM test"
    

    Additional initialisation options are documented in the sc-hsm-tool man page.

KMS configuration

At least one slot and its corresponding PIN must be configured. Multiple slots can be used simultaneously, with each represented by a separate slot.

Configuration via config file

When using the TOML configuration file, enable HSM support by setting these parameters:

hsm_model = "smartcardhsm"
hsm_admin = "<HSM_ADMIN_USERNAME>" # defaults to "admin"
hsm_slot = [0, 0, ] # example [0,4] for slots 0 and 4
hsm_password = ["<password>", "<password>", ] # example ["648219", "648219"] for slots 0 and 4

NOTE: hsm_slot and hsm_password must always be arrays, even if only one slot is used.

The order of the passwords must match the order of the slots in the hsm_slot array.

If you want to login with an empty (null) password, use an empty string.

If you do not want to login, use the special password value <NO_LOGIN>

Configuration via command-line

HSM support can also be enabled with command-line arguments:

--hsm-model "smartcardhsm" \
--hsm-admin "<HSM_ADMIN_USERNAME>"  \
--hsm-slot <number_of_1st_slot> --hsm-password <password_of_1st_slot> \
--hsm-slot <number_of_2and_slot> --hsm-password <password_of_2and_slot>

The hsm-model parameter is the HSM model. Use smartcardhsm.

The hsm-admin parameter is the username of the HSM administrator. The HSM administrator is the only user who can create objects on the HSM via the KMIP Create operation and delegate other operations to other users.

The hsm-slot and hsm-password parameters are the slot number and user password (PIN) of the HSM slots used by the KMS. These options can be repeated to configure multiple slots.

NOTE: To list available slots, run:

pkcs11-tool --module /usr/local/lib/libsc-hsm-pkcs11.so --list-slots

The SoftHSMv2 integration is supported on Linux (x86_64).

SoftHSMv2 library setup

To use SoftHSMv2 with the KMS, the library must first be installed.

The KMS expects the library to be installed at /usr/lib/softhsm/libsofthsm2.so.

Install the library using your distributions package manager, or build it by following the instructions in the SoftHSMv2 GitHub repository.

SoftHSMv2 initialisation

Before use, SoftHSMv2 requires initialization:

softhsm2-util --init-token --slot 0 --so-pin 000000 --pin 000000 --label "SoftHSM"

NOTE: The slot number will be reassigned randomly. Use the new slot number in your KMS configuration.

KMS configuration

At least one slot and its corresponding PIN must be configured. Multiple slots can be used at the same time.

Configuration via config file

When using the TOML configuration file, enable HSM support by setting these parameters:

hsm_model = "softhsm2"
hsm_admin = "<HSM_ADMIN_USERNAME>" # defaults to "admin"
hsm_slot = [0, 0, ] # example [0,4] for slots 0 and 4
hsm_password = ["<password>", "<password>", ] # example ["000000", "444444"] for slots 0 and 4

NOTE: hsm_slot and hsm_password must always be arrays, even if only one slot is used.

The order of the passwords must match the order of the slots in the hsm_slot array.

If you want to login with an empty (null) password, use an empty string.

If you do not want to login, use the special password value <NO_LOGIN>

Configuration via command-line

HSM support can also be enabled with command-line arguments:

--hsm-model "softhsm2" \
--hsm-admin "<HSM_ADMIN_USERNAME>"  \
--hsm-slot <number_of_1st_slot> --hsm-password <password_of_1st_slot> \
--hsm-slot <number_of_2and_slot> --hsm-password <password_of_2and_slot>

The hsm-model parameter is the HSM model. Use softhsm2.

The hsm-admin parameter is the username of the HSM administrator. The HSM administrator is the only user who can create objects on the HSM via the KMIP Create operation and delegate other operations to other users.

The hsm-slot and hsm-password parameters are the slot number and user password (PIN) of the HSM slots used by the KMS. These options can be repeated to configure multiple slots.

NOTE: To list available slots, run:

softhsm2-util --show-slots

Integration of other HSMs is supported on Linux (x86_64).

Even if your HSM is not explicitly listed in this documentation, there is a great chance that it will work, in particular its PKCS#11 driver API is compatible with that of SofHsmV2.

Library setup

The KMS expects the HSM linux PKCS#11 library to be installed at /lib/libkmshsm.so. Rename your HSM library to libkmshsm.so if necessary.

HSM initialization

Before using the HSM with the KMS, follow your documentation to initialize a slot and a user pin/password for that slot.

KMS configuration

At least one slot and its corresponding PIN must be configured. Multiple slots can be used at the same time.

Configuration via config file

When using the TOML configuration file, enable HSM support by setting these parameters:

hsm_model = "other"
hsm_admin = "<HSM_ADMIN_USERNAME>" # defaults to "admin"
hsm_slot = [0, 0, ] # example [0,4] for slots 0 and 4
hsm_password = ["<password>", "<password>", ] # example ["000000", "444444"] for slots 0 and 4

NOTE: hsm_slot and hsm_password must always be arrays, even if only one slot is used.

The order of the passwords must match the order of the slots in the hsm_slot array.

If you want to login with an empty (null) password, use an empty string.

If you do not want to login, use the special password value <NO_LOGIN>

Configuration via command-line

HSM support can also be enabled with command-line arguments:

--hsm-model "other" \
--hsm-admin "<HSM_ADMIN_USERNAME>"  \
--hsm-slot <number_of_1st_slot> --hsm-password <password_of_1st_slot> \
--hsm-slot <number_of_2and_slot> --hsm-password <password_of_2and_slot>

The hsm-model parameter is the HSM model. Use other.

The hsm-admin parameter is the username of the HSM administrator. The HSM administrator is the only user who can create objects on the HSM via the KMIP Create operation and delegate other operations to other users.

The hsm-slot and hsm-password parameters are the slot number and user password (PIN) of the HSM slots used by the KMS. These options can be repeated to configure multiple slots.

API

The Eviden KMS server implements the JSON TTLV profile of the KMIP 1.x and 2.x specifications. In addition, the server exposes a few additional endpoints for authorization operations.

Calling the KMIP API

This API is documented in the KMIP section of this manual.

Calling the authorization API

This API is documented in the authorization section of this manual.

Authentication

The Eviden server supports various authorization mechanisms: see the authentication section of this manual for details. When authenticating using JWT, an HTTP Authorization header must be passed with the JWT token as a bearer token.

For example: Authorization: Bearer <JWT_TOKEN>

REST Native Crypto API

In addition to the KMIP protocol, the server exposes a lightweight JOSE-compatible REST API under /v1/crypto for encrypt, decrypt, sign, verify, and MAC operations. See the REST Native Crypto API page for full documentation.

OpenAPI specification and interactive documentation

The KMS server exposes its full API as an OpenAPI 3.1 specification and serves an interactive Swagger UI browser directly from the server binary.

EndpointDescription
GET /openapi.yamlFull OpenAPI 3.1 schema in YAML format
GET /swaggerInteractive Swagger UI (locally served, authentication required)

Using the Swagger UI

Navigate to <server_url>/swagger in a browser. The page serves Swagger UI assets directly from the KMS server binary (no external CDN dependency) and applies a strict Content-Security-Policy header with frame-ancestors 'none' for clickjacking protection.

https://<your-kms-host>:9998/swagger

The UI displays all documented operations grouped by tag:

  • KMIP — TTLV-over-HTTP (POST /kmip/2_1, POST /kmip/1_4)
  • REST Crypto API — key lifecycle, encrypt/decrypt/sign/verify/MAC under /v1/crypto
  • Server — health, version, server-info
  • Access control — grant, revoke, list, and check permissions
  • HSM — HSM status
  • Download — CLI download endpoint

Downloading the spec

curl -O https://<your-kms-host>:9998/openapi.yaml

The downloaded YAML can be imported into any OpenAPI-compatible tooling (Postman, Insomnia, code generators, etc.).

REST Native Crypto API (/v1/crypto)

The Eviden KMS exposes a lightweight REST Native Crypto API under the /v1/crypto path. This API follows the JOSE (JSON Object Signing and Encryption) conventions from RFC 7516 (JWE), RFC 7515 (JWS), and RFC 7518 (JWA).

Key material never leaves the KMS. Only ciphertext, signatures, and MACs travel over the network.


Table of Contents


Authentication

The /v1/crypto endpoints share the same authentication as the rest of the KMS API. Pass a JWT bearer token, a TLS client certificate, or an API token, depending on your server configuration. See Authentication.


Endpoints

POST /v1/crypto/keys

Generate or import a cryptographic key (or key pair) in the KMS. The request follows JWK conventions — specify kty (key type) and alg (algorithm) at minimum.

  • Generate: omit key material fields to create a new random key.
  • Import: include key material (k for symmetric, d for asymmetric) to import an existing key.

Request body

{
  "kty": "oct",
  "alg": "A256GCM"
}
FieldRequiredDescription
ktyKey type: oct (symmetric), RSA, EC, OKP (non-FIPS)
algJOSE algorithm identifier (determines key size and usage)
crvEC/OKPCurve name: P-256, P-384, P-521, Ed25519 (non-FIPS)
kimport octBase64url-encoded symmetric key material
dimport EC/RSA/OKPBase64url-encoded private key scalar (EC/OKP) or private exponent (RSA)
nimport RSABase64url-encoded RSA modulus
eimport RSABase64url-encoded RSA public exponent
pimport RSABase64url-encoded first prime factor
qimport RSABase64url-encoded second prime factor
dpimport RSABase64url-encoded d mod (p-1)
dqimport RSABase64url-encoded d mod (q-1)
qiimport RSABase64url-encoded CRT coefficient (q^-1 mod p)

Response body

{
  "kid": "<key-uuid>",
  "kid_public": "<public-key-uuid>"  // only for generated asymmetric key pairs
}

Note: Imported asymmetric keys return kid_public: null because only the private key is stored. The KMS derives the public key internally when needed for verification or encryption.

Examples

Symmetric key generation (AES-256-GCM):

{ "kty": "oct", "alg": "A256GCM" }

Symmetric key import (HMAC-SHA256):

{ "kty": "oct", "alg": "HS256", "k": "<base64url-encoded-32-byte-key>" }

RSA key pair generation (PS256):

{ "kty": "RSA", "alg": "PS256" }

EC key import (P-256):

{ "kty": "EC", "crv": "P-256", "alg": "ES256", "d": "<base64url-encoded-scalar>" }

RSA key import:

{
  "kty": "RSA", "alg": "RS256",
  "n": "<modulus>", "e": "AQAB", "d": "<private-exponent>",
  "p": "<prime1>", "q": "<prime2>",
  "dp": "<exponent1>", "dq": "<exponent2>", "qi": "<coefficient>"
}

DELETE /v1/crypto/keys/{kid}

Destroy (permanently delete) a key from the KMS. The key is first revoked (deactivated) then removed from the database, including any linked public/private key in the pair.

Path parameters

ParameterDescription
kidThe UUID of the key to destroy

Response

  • 204 No Content on success (empty body).

POST /v1/crypto/keys/unwrap

Unwrap (decrypt) a JWE encrypted_key using an RSA private key and import the resulting symmetric CEK into the KMS as a managed key. The raw key material is never returned to the caller — only a kid identifier is provided.

This endpoint is designed for the JWE decryption use case where the same CEK is reused across multiple messages: unwrap once, then decrypt subsequent ciphertexts using POST /v1/crypto/decrypt with alg=dir.

Supported alg values: RSA-OAEP, RSA-OAEP-256. Supported enc values: A128GCM, A192GCM, A256GCM.

Flow — Unwrap CEK then decrypt

sequenceDiagram
    participant Client
    participant KMS
    Note over Client,KMS: Prerequisite: RSA key pair exists in KMS
    Client->>KMS: POST /v1/crypto/keys/unwrap<br/>{protected: {alg,enc,kid}, encrypted_key}
    Note over KMS: RSA-OAEP decrypt → CEK<br/>Import CEK as Active symmetric key
    KMS-->>Client: {"kid":"<cek-uuid>","kty":"oct","alg":"A256GCM","key_ops":[...]}
    Client->>KMS: POST /v1/crypto/decrypt<br/>{protected: {alg:"dir",enc,kid:"<cek-uuid>"},<br/>iv, ciphertext, tag}
    Note over KMS: AES-GCM decrypt with persisted CEK
    KMS-->>Client: {"kid":"<cek-uuid>","data":"..."}

Request body

{
  "protected":     "<base64url JWE Protected Header>",
  "encrypted_key": "<base64url wrapped CEK>"
}
FieldRequiredDescription
protectedBase64url-encoded JWE Protected Header (must contain alg, enc, kid)
encrypted_keyBase64url-encoded RSA-OAEP-wrapped CEK (must be non-empty)

The protected header must decode to a JSON object containing:

Header fieldRequiredDescription
kidUUID of the RSA private key (or its linked public key)
algRSA-OAEP or RSA-OAEP-256
encA128GCM, A192GCM, or A256GCM (determines expected CEK size)

Response body

{
  "kid":     "<cek-uuid>",
  "kty":     "oct",
  "alg":     "A256GCM",
  "key_ops": ["encrypt", "decrypt"]
}
FieldDescription
kidUUID of the imported symmetric key in the KMS
ktyAlways oct (symmetric key)
algThe enc algorithm from the protected header
key_opsAllowed operations: ["encrypt", "decrypt"]

Error conditions

ConditionStatusError code
Invalid base64url in protected or encrypted_key400bad_request
Missing kid, alg, or enc in protected header400bad_request
Empty encrypted_key400bad_request
Unsupported algorithm (e.g., dir, A128KW)422unsupported_algorithm
Private key not found404not_found
Decryption failure (wrong key, corrupted data)422decryption_failed
CEK size does not match enc algorithm422crypto_failure

Example (curl)

# Assume $PROTECTED and $ENCRYPTED_KEY come from a JWE token
# The protected header contains {"alg":"RSA-OAEP-256","enc":"A256GCM","kid":"<priv-uuid>"}

# 1. Unwrap the CEK
UNWRAP_RESP=$(curl -s -X POST https://kms.example.com/v1/crypto/keys/unwrap \
  -H 'Content-Type: application/json' \
  -d "{\"protected\":\"$PROTECTED\",\"encrypted_key\":\"$ENCRYPTED_KEY\"}")
CEK_KID=$(echo "$UNWRAP_RESP" | jq -r '.kid')

# 2. Decrypt subsequent ciphertexts using the persisted CEK
# Build a protected header for direct decryption:
DIR_HEADER=$(printf '{"alg":"dir","enc":"A256GCM","kid":"%s"}' "$CEK_KID" \
  | base64 | tr '+/' '-_' | tr -d '=')
curl -s -X POST https://kms.example.com/v1/crypto/decrypt \
  -H 'Content-Type: application/json' \
  -d "{\"protected\":\"$DIR_HEADER\",\"iv\":\"$IV\",\"ciphertext\":\"$CT\",\"tag\":\"$TAG\"}" \
  | jq -r '.data | @base64d'

# 3. Clean up
curl -s -X DELETE https://kms.example.com/v1/crypto/keys/$CEK_KID

POST /v1/crypto/encrypt

Encrypt plaintext using either:

  • Direct encryption (alg=dir): uses a symmetric key directly as the content encryption key (CEK).
  • RSA-OAEP key wrapping (alg=RSA-OAEP or alg=RSA-OAEP-256): generates an ephemeral CEK, wraps it with the RSA public key, and encrypts data with AES-GCM.

Supported alg values: dir, RSA-OAEP, RSA-OAEP-256. Supported enc values: A128GCM, A192GCM, A256GCM.

Flow — Direct (dir)

sequenceDiagram
    participant Client
    participant KMS
    Client->>KMS: POST /v1/crypto/keys {"kty":"oct","alg":"A256GCM"}
    KMS-->>Client: {"kid":"<uuid>"}
    Client->>KMS: POST /v1/crypto/encrypt {"kid":"<uuid>","alg":"dir","enc":"A256GCM","data":"..."}
    Note over KMS: AES-GCM encrypt with symmetric key
    KMS-->>Client: {"protected":"...","encrypted_key":"","iv":"...","ciphertext":"...","tag":"..."}
    Client->>KMS: POST /v1/crypto/decrypt {"protected":"...","iv":"...","ciphertext":"...","tag":"..."}
    Note over KMS: AES-GCM decrypt with symmetric key
    KMS-->>Client: {"kid":"<uuid>","data":"..."}
    Client->>KMS: DELETE /v1/crypto/keys/<uuid>
    KMS-->>Client: 204 No Content

Flow — RSA-OAEP

sequenceDiagram
    participant Client
    participant KMS
    Client->>KMS: POST /v1/crypto/keys {"kty":"RSA","bits":2048}
    KMS-->>Client: {"kid":"<priv-uuid>","kid_public":"<pub-uuid>"}
    Client->>KMS: POST /v1/crypto/encrypt {"kid":"<pub-uuid>","alg":"RSA-OAEP-256","enc":"A256GCM","data":"..."}
    Note over KMS: Generate ephemeral CEK<br/>Wrap CEK with RSA-OAEP-256<br/>AES-GCM encrypt data<br/>Wipe CEK from memory
    KMS-->>Client: {"protected":"...","encrypted_key":"<wrapped-CEK>","iv":"...","ciphertext":"...","tag":"..."}
    Client->>KMS: POST /v1/crypto/decrypt {"protected":"...","encrypted_key":"<wrapped-CEK>","iv":"...","ciphertext":"...","tag":"..."}
    Note over KMS: RSA-OAEP unwrap → CEK<br/>AES-GCM decrypt<br/>(implicit rejection on OAEP failure)
    KMS-->>Client: {"kid":"<priv-uuid>","data":"..."}
    Client->>KMS: DELETE /v1/crypto/keys/<priv-uuid>
    KMS-->>Client: 204 No Content (cascades to public key)

Request body

{
  "kid": "<key-uuid>",
  "alg": "RSA-OAEP",
  "enc": "A256GCM",
  "data": "<base64url-encoded plaintext>",
  "aad": "<base64url-encoded AAD>"   // optional
}
FieldRequiredDescription
kidKey UUID (symmetric key for dir; RSA private or public key for RSA-OAEP/RSA-OAEP-256)
algKey management algorithm: dir, RSA-OAEP, or RSA-OAEP-256
encContent encryption algorithm: A128GCM, A192GCM, A256GCM
dataBase64url-encoded plaintext
aadOptional additional authenticated data (base64url)

Response body (Flattened JWE)

{
  "protected":     "<base64url JWE Protected Header>",
  "encrypted_key": "<base64url wrapped CEK>",
  "iv":            "<base64url IV>",
  "ciphertext":    "<base64url ciphertext>",
  "tag":           "<base64url GCM authentication tag>",
  "aad":           "<base64url AAD>"   // omitted when not supplied
}

For alg=dir, the encrypted_key field is an empty string. For RSA-OAEP, it contains the RSA-OAEP-wrapped CEK.

Example — Direct encryption (curl)

# 1. Create a 256-bit AES key
KID=$(curl -s -X POST https://kms.example.com/v1/crypto/keys \
  -H 'Content-Type: application/json' \
  -d '{"kty":"oct","alg":"A256GCM"}' | jq -r '.kid')

# 2. Encrypt
DATA_B64=$(printf 'Hello KMS!' | base64 | tr '+/' '-_' | tr -d '=')
JWE=$(curl -s -X POST https://kms.example.com/v1/crypto/encrypt \
  -H 'Content-Type: application/json' \
  -d "{\"kid\":\"$KID\",\"alg\":\"dir\",\"enc\":\"A256GCM\",\"data\":\"$DATA_B64\"}")

# 3. Decrypt
curl -s -X POST https://kms.example.com/v1/crypto/decrypt \
  -H 'Content-Type: application/json' \
  -d "$JWE" | jq -r '.data | @base64d'

# 4. Clean up
curl -s -X DELETE https://kms.example.com/v1/crypto/keys/$KID

Example — RSA-OAEP (curl)

# 1. Create an RSA-2048 key pair
KEYS=$(curl -s -X POST https://kms.example.com/v1/crypto/keys \
  -H 'Content-Type: application/json' \
  -d '{"kty":"RSA","bits":2048}')
KID=$(echo "$KEYS" | jq -r '.kid')
KID_PUB=$(echo "$KEYS" | jq -r '.kid_public')

# 2. Encrypt with RSA-OAEP-256 using the public key
DATA_B64=$(printf 'Secret message' | base64 | tr '+/' '-_' | tr -d '=')
JWE=$(curl -s -X POST https://kms.example.com/v1/crypto/encrypt \
  -H 'Content-Type: application/json' \
  -d "{\"kid\":\"$KID_PUB\",\"alg\":\"RSA-OAEP-256\",\"enc\":\"A256GCM\",\"data\":\"$DATA_B64\"}")

# 3. Decrypt (kid in protected header resolves to private key automatically)
curl -s -X POST https://kms.example.com/v1/crypto/decrypt \
  -H 'Content-Type: application/json' \
  -d "$JWE" | jq -r '.data | @base64d'

# 4. Clean up (cascades to the linked public key)
curl -s -X DELETE https://kms.example.com/v1/crypto/keys/$KID

POST /v1/crypto/decrypt

Decrypt a Flattened JWE token.

Supports both alg=dir (direct AES-GCM) and alg=RSA-OAEP/RSA-OAEP-256 (RSA-OAEP key unwrapping + AES-GCM content decryption).

The kid in the JWE protected header identifies the decryption key. For RSA-OAEP, this can be either the private key or the public key UUID (the server resolves to the private key automatically).

The decrypt endpoint has no standalone flow diagram — it is the second half of the encrypt/decrypt flows shown in the encrypt section above.

Request body

{
  "protected":     "<base64url JWE Protected Header>",
  "encrypted_key": "<base64url wrapped CEK>",
  "iv":            "<base64url IV>",
  "ciphertext":    "<base64url ciphertext>",
  "tag":           "<base64url GCM authentication tag>",
  "aad":           "<base64url AAD>"  // optional; must match the value used during encryption
}
FieldRequiredDescription
protectedBase64url-encoded JWE Protected Header (must contain alg, enc, kid)
encrypted_keyFor RSA-OAEPThe RSA-OAEP-wrapped CEK (empty or absent for dir)
iv12-byte initialization vector (base64url)
ciphertextEncrypted data (base64url)
tag16-byte GCM authentication tag (base64url)
aadAdditional authenticated data (base64url)

Response body

{
  "kid":  "<key-uuid>",
  "data": "<base64url plaintext>"
}

Security: implicit rejection

For RSA-OAEP decryption, the server implements implicit rejection as recommended by RFC 7516 §11.5. If the RSA-OAEP unwrap fails (indicating a tampered or invalid encrypted_key), the server substitutes a random CEK and proceeds with AES-GCM decryption — which will fail with the same generic "Decryption failed" error. This prevents padding oracle attacks.

Example (curl)

# JWE is the JSON response from POST /v1/crypto/encrypt
PLAINTEXT=$(curl -s -X POST https://kms.example.com/v1/crypto/decrypt \
  -H 'Content-Type: application/json' \
  -d "$JWE" | jq -r '.data | @base64d')
echo "$PLAINTEXT"

POST /v1/crypto/sign

Sign data using an asymmetric private key. Returns a detached JWS (payload not included in the response).

Supported algorithms: RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384, ES512; EdDSA and MLDSA44 (non-FIPS builds only).

Flow — Sign and verify

sequenceDiagram
    participant Client
    participant KMS
    Client->>KMS: POST /v1/crypto/keys {"kty":"RSA","alg":"PS256"}
    KMS-->>Client: {"kid":"<priv-uuid>","kid_public":"<pub-uuid>"}
    Client->>KMS: POST /v1/crypto/sign {"kid":"<priv-uuid>","alg":"PS256","data":"<b64>"}
    Note over KMS: RSA-PSS sign with private key
    KMS-->>Client: {"protected":"<b64-header>","signature":"<b64>"}
    Client->>KMS: POST /v1/crypto/verify {"protected":"<b64-header>","data":"<b64>","signature":"<b64>"}
    Note over KMS: Decode protected header → kid<br/>Fetch public key<br/>RSA-PSS verify
    KMS-->>Client: {"kid":"<pub-uuid>","valid":true}
    Client->>KMS: DELETE /v1/crypto/keys/<priv-uuid>
    KMS-->>Client: 204 No Content (cascades to public key)

Request body

{
  "kid":  "<private-key-uuid>",
  "alg":  "RS256",
  "data": "<base64url payload>"
}

Response body

{
  "protected": "<base64url JWS Protected Header>",
  "signature": "<base64url signature>"
}

Example (curl)

# 1. Create an RSA-2048 key pair
KEYS=$(curl -s -X POST https://kms.example.com/v1/crypto/keys \
  -H 'Content-Type: application/json' \
  -d '{"kty":"RSA","alg":"RS256"}')
KID=$(echo "$KEYS" | jq -r '.kid')

# 2. Sign
DATA_B64=$(printf 'data to sign' | base64 | tr '+/' '-_' | tr -d '=')
SIGN_RESP=$(curl -s -X POST https://kms.example.com/v1/crypto/sign \
  -H 'Content-Type: application/json' \
  -d "{\"kid\":\"$KID\",\"alg\":\"RS256\",\"data\":\"$DATA_B64\"}")

# 3. Verify
curl -s -X POST https://kms.example.com/v1/crypto/verify \
  -H 'Content-Type: application/json' \
  -d "$(jq -n --argjson s "$SIGN_RESP" --arg d "$DATA_B64" \
       '{protected:$s.protected,data:$d,signature:$s.signature}')"

# 4. Clean up
curl -s -X DELETE https://kms.example.com/v1/crypto/keys/$KID

POST /v1/crypto/verify

Verify a detached JWS signature. The KMS looks up the public key from the protected header.

Required: the JWS protected header decoded from protected must contain a kid field set to the KMS public key UUID. Requests without kid are rejected with 400. This field is set automatically by POST /v1/crypto/sign.

The verify endpoint is the second half of the sign/verify flow shown in the sign section above.

Request body

{
  "protected": "<base64url JWS Protected Header (contains kid + alg)>",
  "data":      "<base64url payload>",
  "signature": "<base64url signature>"
}

Response body

{
  "kid":   "<public-key-uuid>",
  "valid": true   // false when the signature does not match
}

POST /v1/crypto/mac

Compute or verify a MAC (Message Authentication Code).

  • Compute: omit the mac field → the KMS returns the computed MAC value.
  • Verify: include the mac field → the KMS returns valid: true/false.

Supported algorithms: HS256, HS384, HS512.

Flow — MAC compute and verify

sequenceDiagram
    participant Client
    participant KMS
    Client->>KMS: POST /v1/crypto/keys {"kty":"oct","alg":"HS256"}
    KMS-->>Client: {"kid":"<uuid>"}
    Client->>KMS: POST /v1/crypto/mac {"kid":"<uuid>","alg":"HS256","data":"<b64>"}
    Note over KMS: HMAC-SHA256 compute
    KMS-->>Client: {"kid":"<uuid>","mac":"<b64>"}
    Client->>KMS: POST /v1/crypto/mac {"kid":"<uuid>","alg":"HS256","data":"<b64>","mac":"<b64>"}
    Note over KMS: HMAC-SHA256 verify
    KMS-->>Client: {"kid":"<uuid>","valid":true}
    Client->>KMS: DELETE /v1/crypto/keys/<uuid>
    KMS-->>Client: 204 No Content

Request body

{
  "kid":  "<key-uuid>",
  "alg":  "HS256",
  "data": "<base64url data>",
  "mac":  "<base64url MAC to verify>"  // omit to compute
}

Response body — compute

{
  "kid": "<key-uuid>",
  "mac": "<base64url MAC>"
}

Response body — verify

{
  "kid":   "<key-uuid>",
  "valid": true   // false when the MAC does not match
}

RFC 7515 §A.1 known-answer vector

The MAC value below is pinned by an integration test (test_rfc7515_a1_hs256_known_answer). It reproduces the exact HS256 result from RFC 7515 §Appendix A.1.

FieldValue
Key (base64url)AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow
DataJWS Signing Input from RFC 7515 §A.1
algHS256
Expected macdBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

Example (curl)

# 1. Create an HMAC-SHA256 key
KID=$(curl -s -X POST https://kms.example.com/v1/crypto/keys \
  -H 'Content-Type: application/json' \
  -d '{"kty":"oct","alg":"HS256"}' | jq -r '.kid')

# 2. Compute MAC
DATA_B64=$(printf 'message' | base64 | tr '+/' '-_' | tr -d '=')
MAC_RESP=$(curl -s -X POST https://kms.example.com/v1/crypto/mac \
  -H 'Content-Type: application/json' \
  -d "{\"kid\":\"$KID\",\"alg\":\"HS256\",\"data\":\"$DATA_B64\"}")
MAC_VALUE=$(echo "$MAC_RESP" | jq -r '.mac')

# 3. Verify MAC
curl -s -X POST https://kms.example.com/v1/crypto/mac \
  -H 'Content-Type: application/json' \
  -d "{\"kid\":\"$KID\",\"alg\":\"HS256\",\"data\":\"$DATA_B64\",\"mac\":\"$MAC_VALUE\"}"

# 4. Clean up
curl -s -X DELETE https://kms.example.com/v1/crypto/keys/$KID

Key tag management

User-defined string tags can be attached to any key stored in the KMS. Tags enable key discovery (for example the JWKS endpoint returns only keys tagged "jwks") and custom key grouping.

Rules:

  • Tags are arbitrary, non-empty strings.
  • Tags starting with _ are system tags (_kk, _sk, _pk, _cert, …) and cannot be created or deleted through these endpoints.
  • All write operations are idempotent: adding an existing tag or removing a missing tag has no effect.

POST /v1/crypto/keys/{kid}/tags

Add one or more user tags to a key. Existing tags are preserved.

Request body

{ "tags": ["jwks", "prod"] }
FieldRequiredDescription
tagsArray of non-empty tag strings (must not start with _)

Response body

{ "kid": "<key-uuid>", "tags": ["jwks", "prod"] }

The tags array reflects the complete current set of user tags after the operation (sorted, system tags excluded).

Status codes

StatusMeaning
200Tags updated successfully
400Empty tag string or _-prefixed (system) tag supplied
403Key access denied
404Key not found

curl example

curl -s -X POST "https://kms.example.com/v1/crypto/keys/${KID}/tags" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{"tags": ["jwks", "prod"]}'

DELETE /v1/crypto/keys/{kid}/tags

Remove one or more user tags from a key. Tags not present on the key are silently ignored.

Request body

{ "tags": ["prod"] }
FieldRequiredDescription
tagsArray of tag strings to remove (must not start with _)

Response body

{ "kid": "<key-uuid>", "tags": ["jwks"] }

The tags array reflects the remaining user tags after removal.

Status codes

StatusMeaning
200Tags updated successfully
400Empty tag string or _-prefixed (system) tag supplied
403Key access denied
404Key not found

curl example

curl -s -X DELETE "https://kms.example.com/v1/crypto/keys/${KID}/tags" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{"tags": ["prod"]}'

GET /v1/crypto/keys/{kid}/tags

List the current user tags on a key. No body required.

Response body

{ "kid": "<key-uuid>", "tags": ["jwks"] }

System tags are never included.

Status codes

StatusMeaning
200OK
403Key access denied
404Key not found

curl example

curl -s "https://kms.example.com/v1/crypto/keys/${KID}/tags" \
  -H "Authorization: Bearer ${TOKEN}"

Error responses

All endpoints return a JSON body { "error": "<type>", "description": "<detail>" } with an appropriate HTTP status code:

StatusMeaning
400Bad request (malformed base64, missing field, etc.)
403Forbidden (key access denied)
404Key not found
422Unsupported or unknown algorithm
500Internal server error / cryptographic failure

Known limitations

The following JOSE features are not yet implemented in this API.

Missing featureBlocked by
JWE alg=RSA-PKCS1-v1_5 + enc=A128CBC-HS256 (RFC 7516 §A.2)RSA-PKCS1v1.5 + AES-CBC not implemented
JWE alg=A128KW + enc=A128CBC-HS256 (RFC 7516 §A.3)AES key-wrap + AES-CBC not implemented
JWE alg=dir + enc=A128CBC-HS256 (RFC 7516 §A.5)AES-CBC enc not implemented
JWE alg=ECDH-ES (RFC 7518 §C)ECDH-ES key agreement not implemented
JWS verify without kid in protected headerkid-less verify not yet supported

Full RFC 7515 §A.2/A.3/A.4 known-answer tests are deferred until kid-less verification is supported (current tests do round-trips with freshly generated keys).


Algorithm support matrix

Key management algorithms (alg)

AlgorithmFIPSOperationNotes
direncrypt / decryptDirect use of symmetric key as CEK
RSA-OAEPencrypt / decrypt / unwrapRSA-OAEP with SHA-1 (RFC 7518 §4.3)
RSA-OAEP-256encrypt / decrypt / unwrapRSA-OAEP with SHA-256 (RFC 7518 §4.3)

Content encryption algorithms (enc)

AlgorithmFIPSKey sizeNotes
A128GCM128 bitsAES-GCM with 96-bit IV, 128-bit tag
A192GCM192 bitsAES-GCM with 96-bit IV, 128-bit tag
A256GCM256 bitsAES-GCM with 96-bit IV, 128-bit tag

Signature algorithms

AlgorithmFIPSOperation
RS256/384/512sign / verify
PS256/384/512sign / verify
ES256/384/512sign / verify
EdDSAsign / verify (non-FIPS)
MLDSA44sign / verify (non-FIPS)

MAC algorithms

AlgorithmFIPSOperation
HS256/384/512mac compute / verify

JWE Decryption — Unwrap without Key Exposure

This use case describes how to decrypt JWE (JSON Web Encryption) tokens using the Cosmian KMS without ever exposing the symmetric content encryption key (CEK) to the calling application.

Problem Statement

You receive JWE tokens with the structure: Kpub(Ksym) . Ksym(value)

  • Ksym(value): the payload value is encrypted with a symmetric AES key Ksym (the CEK)
  • Kpub(Ksym): the CEK is wrapped (encrypted) with an RSA public key Kpub
  • The kid (Key ID) of the RSA key pair is known

Security constraint: the calling application must never have access to Ksym.

Solution: One-Shot JWE Decrypt (No Key Persistence)

If the full JWE is available at each decryption call (including the encrypted_key field), the REST crypto endpoint performs all operations server-side in a single request. Ksym is used ephemerally in memory and immediately zeroized.

No additional development required.

Flow

sequenceDiagram
    participant App as Application
    participant KMS as KMS Server
    participant HSM as HSM (optional)

    App->>KMS: POST /v1/crypto/decrypt<br/>{protected, encrypted_key=Kpub(Ksym),<br/>iv, ciphertext=Ksym(value), tag}
    alt Kpriv in HSM
        KMS->>HSM: C_Decrypt(handle_kpriv, Kpub(Ksym))
        HSM-->>KMS: Ksym (stays in server RAM only)
    else Kpriv in database
        KMS->>KMS: RSA-OAEP decrypt(Kpriv, Kpub(Ksym)) → Ksym
    end
    KMS->>KMS: AES-GCM decrypt(Ksym, iv, ciphertext, tag) → value
    KMS->>KMS: Zeroize(Ksym)
    KMS-->>App: {"kid":"<kid>", "data":"<base64url(value)>"}

Request

curl -s -X POST https://kms.example.com/v1/crypto/decrypt \
  -H 'Content-Type: application/json' \
  -d '{
    "protected": "<base64url JWE header: {\"alg\":\"RSA-OAEP-256\",\"enc\":\"A256GCM\",\"kid\":\"<kid>\"}>",
    "encrypted_key": "<base64url of Kpub(Ksym)>",
    "iv": "<base64url of 12-byte IV>",
    "ciphertext": "<base64url of Ksym(value)>",
    "tag": "<base64url of 16-byte GCM tag>"
  }'

Response

{ "kid": "<kid>", "data": "<base64url of decrypted value>" }

The application receives valuenever Ksym.

Server Configuration (TOML)

Minimal configuration with HSM holding the RSA private key:

[[hsm_instances]]
hsm_model    = "softhsm2"     # or: utimaco, proteccio, crypt2pay, smartcardhsm
hsm_admin    = ["admin"]
hsm_slot     = [0]
hsm_password = ["changeme"]

[http]
port = 9998
hostname = "0.0.0.0"

[db]
database_type = "sqlite"
sqlite_path   = "data/kms-objects"

If Kpriv is stored in the HSM with UID hsm::softhsm2::0::my_rsa_key, set the kid in the JWE protected header to this UID. The KMS automatically routes the RSA-OAEP decrypt to the HSM via the crypto oracle.

Re-encryption

After decryption, re-encrypt value with a new key in the same call flow:

curl -s -X POST https://kms.example.com/v1/crypto/encrypt \
  -H 'Content-Type: application/json' \
  -d '{
    "kid": "<kid_new_key>",
    "alg": "RSA-OAEP-256",
    "enc": "A256GCM",
    "data": "<base64url of value>"
  }'

Advanced: Persisting Ksym in the KMS (Key Unwrap Endpoint)

If the application receives subsequent ciphertexts Ksym(value) without the Kpub(Ksym) wrapper (i.e., the same CEK is reused across multiple messages), then Ksym must be persisted for later reuse. The POST /v1/crypto/keys/unwrap endpoint unwraps the CEK using RSA-OAEP and imports it as a managed symmetric key — without ever exposing the raw key material to the calling application.

Flow

sequenceDiagram
    participant App as Application
    participant KMS as KMS Server
    participant HSM as HSM (optional)

    Note over App,KMS: Step 1 — Unwrap and persist Ksym (once)
    App->>KMS: POST /v1/crypto/keys/unwrap<br/>{protected: {alg:"RSA-OAEP-256",<br/>enc:"A256GCM", kid:"<kpriv_uid>"},<br/>encrypted_key: Kpub(Ksym)}
    alt Kpriv in HSM
        KMS->>HSM: C_Decrypt(handle_kpriv, Kpub(Ksym))
        HSM-->>KMS: Ksym
    else Kpriv in database
        KMS->>KMS: RSA-OAEP decrypt(Kpriv, Kpub(Ksym)) → Ksym
    end
    KMS->>KMS: Import Ksym as Active symmetric key
    KMS-->>App: {"kid":"<ksym_uid>", "kty":"oct",<br/>"alg":"A256GCM", "key_ops":["encrypt","decrypt"]}

    Note over App,KMS: Step 2 — Decrypt with persisted Ksym (subsequent calls)
    App->>KMS: POST /v1/crypto/decrypt<br/>{protected: {alg:"dir", enc:"A256GCM",<br/>kid:"<ksym_uid>"},<br/>iv, ciphertext, tag}
    KMS->>KMS: AES-GCM decrypt(Ksym, iv, ciphertext, tag) → value
    KMS-->>App: {"kid":"<ksym_uid>", "data":"<base64url(value)>"}

Step 1: Unwrap the CEK

curl -s -X POST https://kms.example.com/v1/crypto/keys/unwrap \
  -H 'Content-Type: application/json' \
  -d '{
    "protected": "<base64url of {\"alg\":\"RSA-OAEP-256\",\"enc\":\"A256GCM\",\"kid\":\"<kpriv_uid>\"}>",
    "encrypted_key": "<base64url of Kpub(Ksym)>"
  }'

Response

{
  "kid": "<ksym_uid>",
  "kty": "oct",
  "alg": "A256GCM",
  "key_ops": ["encrypt", "decrypt"]
}

The symmetric key is now stored in the KMS. The application receives only the kid identifier — never the raw key bytes.

Step 2: Decrypt with the Persisted Key

curl -s -X POST https://kms.example.com/v1/crypto/decrypt \
  -H 'Content-Type: application/json' \
  -d '{
    "protected": "<base64url of {\"alg\":\"dir\",\"enc\":\"A256GCM\",\"kid\":\"<ksym_uid>\"}>",
    "iv": "<base64url of 12-byte IV>",
    "ciphertext": "<base64url of Ksym(value)>",
    "tag": "<base64url of 16-byte GCM tag>"
  }'

Response

{ "kid": "<ksym_uid>", "data": "<base64url of decrypted value>" }

Supported Algorithms

alg (key wrapping)enc (content encryption)
RSA-OAEPA128GCM, A192GCM, A256GCM
RSA-OAEP-256A128GCM, A192GCM, A256GCM

Error Conditions

ConditionHTTP StatusError Code
Invalid base64url in protected or encrypted_key400bad_request
Missing kid, alg, or enc in protected header400bad_request
Empty encrypted_key400bad_request
Unsupported algorithm (e.g., dir, A128KW)422unsupported_algorithm
Private key not found404not_found
Decryption failure (wrong key, corrupted data)422decryption_failed
CEK size mismatch with enc algorithm422crypto_failure

Advanced: Persisting Ksym in the HSM (Requires Development)

If the application requires that Ksym never leaves the HSM boundary (even transiently in KMS server memory), the PKCS#11 C_UnwrapKey operation can be used to import the key directly into the HSM hardware.

Development Required

The PKCS#11 C_UnwrapKey operation is implemented in the HSM driver code but is not yet exposed by any production KMS endpoint. Integrating it into a server endpoint requires additional development.

Target Flow (After Development)

sequenceDiagram
    participant App as Application
    participant KMS as KMS Server
    participant HSM as HSM

    Note over App,HSM: Step 1 — Unwrap and persist Ksym (once)
    App->>KMS: KMIP Import {wrapped_key=Kpub(Ksym),<br/>wrapping_key="hsm::softhsm2::0::kpriv",<br/>store_in_hsm=true}
    KMS->>HSM: C_UnwrapKey(CKM_RSA_PKCS_OAEP, handle_kpriv, Kpub(Ksym))
    HSM->>HSM: Decrypt + store AES key<br/>CKA_SENSITIVE=TRUE, CKA_EXTRACTABLE=FALSE
    HSM-->>KMS: handle_ksym (opaque)
    KMS-->>App: {"UniqueIdentifier": "hsm::softhsm2::0::ksym_123"}

    Note over App,HSM: Step 2 — Decrypt with persisted Ksym (subsequent calls)
    App->>KMS: POST /v1/crypto/decrypt<br/>{protected: {alg:"dir", enc:"A256GCM",<br/>kid:"hsm::softhsm2::0::ksym_123"},<br/>iv, ciphertext, tag}
    KMS->>HSM: C_Decrypt(handle_ksym, iv||ciphertext||tag)
    HSM-->>KMS: value
    KMS-->>App: {"kid":"hsm::softhsm2::0::ksym_123", "data":"<base64url(value)>"}

Step 2 already works

The POST /v1/crypto/decrypt endpoint with alg=dir and a kid referencing an HSM key (hsm::...) already routes to C_Decrypt on the HSM. Only Step 1 (C_UnwrapKey integration) requires development.

What Needs to be Developed

ComponentCurrent StateRequired Change
PKCS#11 C_UnwrapKey wrapper✅ Implemented (test-only)Wire into production endpoint
HsmStore::atomic()Only RSA keypairsSupport symmetric key import
UID routing for unwrapped keysN/AAssign hsm::model::slot::label UID
KMIP Import with HSM targetNot supportedAdd store_in_hsm flag or detect HSM wrapping key

Summary

ApproachKsym exposed?Ksym persisted?Dev required?Conformant?
POST /v1/crypto/decrypt (RSA-OAEP, full JWE)❌ No❌ No❌ No✅ If full JWE always available
POST /v1/crypto/keys/unwrap + alg=dir decrypt❌ No✅ Yes (KMS DB)❌ No✅ Yes
KMIP Decrypt + Import✅ Yes✅ Yes❌ No❌ No (violates constraint)
HSM C_UnwrapKey + alg=dir❌ No✅ Yes (HSM)⚠️ Yes✅ Yes

JWKS endpoint

The KMS exposes a standard RFC 7517 JWKS endpoint at:

GET /.well-known/jwks.json

By default, every public key pair created through the REST API is automatically tagged jwks and immediately advertised in the JWKS document. No extra step is needed for the common case.

To exclude a specific public key from the JWKS remove its jwks tag via the tag management endpoint.

How it works

sequenceDiagram
    participant Client
    participant KMS

    Client->>KMS: POST /v1/crypto/keys  {kty, alg}
    KMS-->>Client: {kid, kid_public}

    Note over Client,KMS: kid_public is auto-tagged "jwks" — no extra step

    Client->>KMS: GET /.well-known/jwks.json
    KMS-->>Client: {keys: [{kid, kty, alg, use, n, e, ...}]}

Quickstart

# EC P-256 key for JWS (ES256)
curl -s -X POST "https://kms.example.com/v1/crypto/keys" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{"kty":"EC","alg":"ES256"}'

The response contains the KMS identifier for both the private key (kid) and the public key (kid_public):

{
  "kid": "<private-key-uuid>",
  "kid_public": "<public-key-uuid>",
  "kty": "EC",
  "alg": "ES256",
  "crv": "P-256",
  "key_ops": ["sign", "verify"]
}

Verify the key is advertised

curl -s "https://kms.example.com/.well-known/jwks.json" | jq .

Expected output (abbreviated):

{
  "keys": [
    {
      "kty": "EC",
      "use": "sig",
      "alg": "ES256",
      "kid": "<public-key-uuid>",
      "crv": "P-256",
      "x": "...",
      "y": "..."
    }
  ]
}

Opt a key out of JWKS

Remove the jwks tag from the public key to stop advertising it. The key material is not deleted.

curl -s -X DELETE "https://kms.example.com/v1/crypto/keys/${KID_PUBLIC}/tags" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{"tags": ["jwks"]}'

The response confirms the updated tag set (the jwks tag is absent):

{ "kid": "<public-key-uuid>", "tags": [] }

If you later want to re-advertise a key that was previously opted out, add thejwks tag back via https://kms.example.com/v1/crypto/keys/${KID_PUBLIC}/tags

Key rotation

During rotation, both the retiring key and the new key are present in the JWKS document for a brief overlap period, allowing relying parties to verify tokens signed by either key.

# 1. Create the new key pair — automatically tagged "jwks".
NEW_KID_PUBLIC=$(curl -s -X POST "https://kms.example.com/v1/crypto/keys" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{"kty":"EC","alg":"ES256"}' | jq -r .kid_public)

# 2. (overlap window) Both old and new keys appear in JWKS.

# 3. Opt the old key out of JWKS once all outstanding tokens have expired.
curl -s -X DELETE "https://kms.example.com/v1/crypto/keys/${OLD_KID_PUBLIC}/tags" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{"tags": ["jwks"]}'

Keys created outside the REST API

Keys created directly through the KMIP protocol (e.g. via ckms or a KMIP client library) are not auto-tagged. To include such a key in the JWKS document, explicitly add the jwks tag:

curl -s -X POST "https://kms.example.com/v1/crypto/keys/${KID_PUBLIC}/tags" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{"tags": ["jwks"]}'

Disable auto-tagging globally

By default the KMS tags every REST-created key pair for JWKS inclusion. If you prefer a fully manual opt-in model, disable auto-tagging in the server configuration:

MethodValue
CLI flag--jwks-endpoint-auto-tag=false
Environment variableKMS_JWKS_ENDPOINT_AUTO_TAG=false
TOML configjwks_endpoint_auto_tag = false

When auto-tagging is disabled, newly created key pairs do not receive the jwks tag. To publish a key you must tag it explicitly:

curl -s -X POST "https://kms.example.com/v1/crypto/keys/${KID_PUBLIC}/tags" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{"tags": ["jwks"]}'

Existing keys are not affected when you change this setting.


See also

Eviden KMS on AWS ECS

Deploy Eviden KMS on AWS ECS (Fargate) using either a production-ready setup with an Application Load Balancer (ALB) or a fast, fully managed Express Mode deployment.


Overview

This repository documents two supported ways to run Eviden KMS on AWS ECS.

ModeUse case
Fargate + ALBProduction, custom networking, HTTPS, persistence
Express ModePoC, demos, quick testing

Prerequisites


Standard Deployment (Fargate + ALB)

1. Architecture

Traffic flows from the client to the ALB on the public HTTP port, which then routes requests to the private Fargate container on the application port.

flowchart LR
    client["Client Station<br/>(KMS CLI)"]
    subgraph aws["AWS Cloud"]
        alb["ALB<br/>(Public Subnet)<br/>Listener Port 80"]
        subgraph ecs["ECS Fargate (Private/Public)"]
            container["Container<br/>Port 9998"]
        end
        sg_alb["Security Group<br/>(SG-ALB)"]
        sg_ecs["Security Group<br/>(SG-ECS)"]
    end
    client -- "HTTP :80" --> alb
    alb --> container
    sg_alb -. controls .-> alb
    sg_ecs -. controls .-> container

2. Deployment Steps

Networking

  • ALB runs in public subnets
  • ECS tasks can run in public or private subnets
  • NAT Gateway is required if tasks run in private subnets

Security Groups

ALB Security Group

  • Inbound: 80/TCP (and 443/TCP if HTTPS is enabled)
  • Outbound: all traffic

ECS Task Security Group

  • Inbound: 9998/TCP
  • Source: ALB security group only
  • Outbound: as required (database, external services)

Application Load Balancer

  • Type: Internet-facing ALB
  • Listener: HTTP on port 80

Target Group

  • Target type: IP
  • Protocol and port: HTTP / 9998

Health Check

  • Path: /ui
  • Success codes: 200
  • Interval: 30 seconds
  • Timeout: 10 seconds

ECS Task Definition

  • Image: ghcr.io/cosmian/kms:latest
  • Container port: 9998
  • Environment variables:
    • KMS configuration
    • Database connection (if persistence is enabled)

ECS Service

  • Attach the target group to container port 9998
  • Health check grace period: minimum 60–120 seconds

3. Client Setup (KMS CLI)

install KMS CLI

Run: ckms configure

KMS URL example:
http(s)://<ALB-DNS-NAME>

4. Validation & Testing

Connectivity Test Verify that the ALB is correctly routing traffic to a healthy container.

curl http(s)://<YOUR-ALB-DNS-NAME>/ui
# Expected response: 200 OK with a JSON payload containing the version.

Create a symmetric key:

ckms sym keys create --tag test --algorithm aes --number-of-bits 256
# Expected response:
The symmetric key was successfully generated.
      Unique identifier: xxxxxxx_xxxxxxx

  Tags:
    - test


5. Troubleshooting

503 Service Unavailable usually means:

  • No running ECS tasks
  • No healthy targets in the target group
  • Health checks failing

Actions:

  • Check ECS Service events
  • Verify target group target health
  • Increase health check grace period

Unhealthy targets:

  • Ensure port 9998 is exposed
  • Ensure ECS SG allows traffic from ALB SG
  • Confirm /ui endpoint is reachable

6. Production Recommendations

  • Enable HTTPS using ACM certificates
  • Redirect HTTP to HTTPS
  • Use an external database (e.g. RDS PostgreSQL) for persistence
  • Configure Route53 DNS pointing to the ALB

Express Mode Deployment

Express Mode Architecture

flowchart LR
    client["Client (CLI)"]
    alb["Express ALB"]
    task["Fargate Task (KMS:9998)"]
    container["Container"]
    client -->|HTTPS| alb
    alb -->|HTTP| task
    task --> container

Express Mode Deployment Steps

AWS Console → ECS → Express Mode

  • Image: ghcr.io/cosmian/kms:latest
  • Service name: kms-express
  • Container port: 9998
  • Desired tasks: 1
  • healthcheck path : /ui

Wait until service status is Active.


Express Mode Client Setup

install KMS CLI

Run:

ckms configure
KMS URL example:
https://ex-xxxxxxxx.ecs.<region>.on.aws

Express Mode Validation

Connectivity test:

curl https://ex-xxxxxxxx.ecs.<region>.on.aws/ui
# Expected response: 200 OK with a JSON payload containing the version.

Create a symmetric key:

ckms sym keys create --tag test-express --algorithm aes --number-of-bits 256
# Expected response:
The symmetric key was successfully generated.
      Unique identifier: xxxxxxx_xxxxxxx

  Tags:
    - test-express

Production Notes

  • Express Mode is suitable for development, demos, and PoCs
  • HTTPS is included by default
  • Storage is ephemeral
  • Use an external database for persistence
  • Deleting the service removes all associated resources

License

Refer to Eviden documentation for licensing and support details.

Integration to AWS External Key Service (XKS)

Background

AWS XKS (External Key Store) is a feature of AWS Key Management Service (AWS KMS) that allows you to use cryptographic keys stored in an external key management system with AWS KMS. It enables you to maintain control over your keys while leveraging AWS services that integrate with AWS KMS.

Source: AWS KMS XKS Proxy API Specification - Background

Architecture

The Eviden KMS integrates to AWS XKS and proposes a novel architecture (dubbed xksv2) that solves the traditional XKS performance issues without compromising on security.

xksv2 architecture diagram

The Eviden XKSv2 architecture is composed of the following components:

Eviden Confidential KMS

This is the Confidential Key Management System, deployed as IaaS, in the customer AWS tenant. It is responsible for managing the Key Encryption Keys (KEKs) wrapping the XKS keys in AWS KMS and for answering encryption and decryption requests from the AWS KMS.

To protect the KEKs, the Eviden KMS runs inside an Eviden VM on top of confidential computing machines. Eviden VM provides strong security and verifiability guarantees.

The Eviden KMS is deployed in AWS infrastructure, solving the XKS scaling problem, as it benefits from a stable high bandwidth network and can easily scale to reliably support large amount of transactions from the AWS KMS.

The Confidential KMS is available as a ready-to-deploy product from the AWS Marketplace.

HSM

The HSM is responsible for storing the Master keys and securing the Eviden KMS keys. It is deployed in the customer premises or offered as a managed service by Atos. See the HSM integration documentation for more details.

Deployment

  1. Deploy a Eviden KMS in your AWS tenant. You can find the product on the AWS Marketplace and follow the deployment instructions in the product documentation.

  2. Configure the KMS for use with AWS XKS by filling up the aws_xks_config section of the configuration file with the following values:

    [aws_xks_config]
    # set this to true
    aws_xks_enable = true
    # this is the region you Eviden KMS is deployed in
    aws_xks_region = "us-east-1"
    # keep this to this value
    aws_xks_service = "xks-kms"
    # used for sigv4. The values set here must match the values configured
    # when setting up the KMS as an external keystore for AWS KMS (see next step)
    aws_xks_sigv4_access_key_id = "AKIAIOSFODNN7EXAMPLE"
    aws_xks_sigv4_secret_access_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
    
  3. Configure the KMS to act as an External Key Store for AWS KMS. Follow the instructions in the AWS documentation to create an External Key Store.

  4. Create an external key in AWS KMS and specify the key store created in the previous step as the key store for the key.

    Configure the key Choose the external key Review the key and create it

  5. Enforce the correct permissions for the key on the Eviden KMS. Make sure the user used by AWS has the permissions for Encrypt, Decrypt and GetAttributes. For instance, when using DynamoDB, the user should be called something like dynamodb.amazonaws.com, for Salesforce, it is the user configured as part of the setup. In doubt, or for testing, grant theses permissions to all users (*).

Migrating an AWS External Key Store to a New Eviden KMS Instance

This guide describes how to migrate an AWS External Key Store (XKS) from one Eviden KMS instance (Server A, the source) to another (Server B, the target). This is typically required when moving to a new server, a new region, or upgrading infrastructure.

The migration must be performed with minimal downtime. During the transition window, AWS KMS marks the key store as DISCONNECTED, meaning any AWS service that attempts to use an XKS key will receive a KMSInvalidStateException until the connection is restored on Server B.

Prerequisites

  • Server B must be deployed and configured with AWS XKS enabled (see the XKS deployment guide).
  • You must know the AWS principal ARN that was granted permissions on each key (e.g. dynamodb.amazonaws.com, arn:aws:iam::123456789012:user/Alice).
  • The SigV4 credentials configured on Server B must match (or be updated in) the AWS XKS key store settings.

Step 1 — Export all XKS keys from Server A

On Server A, export every key used by the XKS integration in json-ttlv format (the default). This format preserves all KMIP metadata and can be re-imported identically on Server B.

For each key (replace <KEY_ID> with the actual unique identifier or tag):

# Export a single key to a local file
ckms -c /path/to/server_a.toml \
  sym keys export \
  --key-id <KEY_ID> \
  keys/<KEY_ID>.json

Security note: The exported file contains the raw key material in KMIP JSON TTLV format. Store these files securely (encrypted storage, restricted filesystem permissions) and delete them once the import on Server B is complete.


Step 2 — Disconnect the XKS key store in AWS KMS

Before switching the endpoint, disconnect the external key store in the AWS Management Console or via the AWS CLI. This prevents AWS from sending requests to Server A while the endpoint is being updated.

Using the AWS CLI:

aws kms disconnect-custom-key-store \
  --custom-key-store-id <CUSTOM_KEY_STORE_ID>

Confirm that the key store status is DISCONNECTED:

aws kms describe-custom-key-stores \
  --custom-key-store-id <CUSTOM_KEY_STORE_ID> \
  --query 'CustomKeyStores[0].ConnectionState'

Expected output:

"DISCONNECTED"

Alternatively, from the AWS Management Console, open the key store and click Key store actions → Disconnect:

Select Disconnect in Key store actions menu

Type disconnect in the confirmation dialog and click Disconnect key store:

Confirm disconnection

A success banner confirms the key store is now disconnected:

Key store successfully disconnected


Step 3 — Configure Server B

Ensure Server B's configuration file contains the correct AWS XKS parameters. The SigV4 credentials must match the values registered in the AWS key store configuration. If you are rotating the credentials, update both the KMS config and the AWS key store settings in the same operation (step 4).

[aws_xks_config]
aws_xks_enable = true
aws_xks_region = "eu-west-1"           # region where your new KMS is deployed
aws_xks_service = "xks-kms"
aws_xks_sigv4_access_key_id = "YOUR_AWS_KEYID"
aws_xks_sigv4_secret_access_key = "YOUR_AWS_SECRETKEY"
aws_xks_kek_user = "admin"

Start (or restart) Server B after updating the configuration.


Step 4 — Update the XKS proxy URI in AWS KMS

Update the external key store to point to Server B's public endpoint. This can be done via the AWS Management Console (KMS → Custom key stores → Edit) or with the AWS CLI:

aws kms update-custom-key-store \
  --custom-key-store-id <CUSTOM_KEY_STORE_ID> \
  --xks-proxy-uri-endpoint "https://<SERVER_B_PUBLIC_HOSTNAME>" \
  --xks-proxy-authentication-credential \
    "AccessKeyId=YOUR_AWS_KEYID,RawSecretAccessKey=YOUR_AWS_SECRETKEY"

If you are keeping the same SigV4 credentials, the --xks-proxy-authentication-credential argument can be omitted.

Alternatively, from the AWS Management Console, click Key store actions → Edit:

Select Edit in Key store actions menu

Update the Proxy URI endpoint field with Server B's hostname, then click Update external key store:

Update the proxy URI endpoint

Check the acknowledgement checkbox and confirm:

Confirm the update

A success banner confirms the endpoint has been updated:

Key store successfully updated


Step 5 — Import keys on Server B

On Server B, import each key exported in step 1. The --key-id parameter must preserve the exact same unique identifier that was used on Server A, because AWS KMS references keys by their ID in the XKS metadata endpoint path (/kms/xks/v1/keys/{key_id}/metadata).

ckms -c /path/to/server_b.toml \
  sym keys import \
  --key-id <KEY_ID> \
  keys/<KEY_ID>.json

Repeat for every XKS key.


Step 6 — Re-grant access rights on Server B

The Eviden KMS access control is per-instance: permissions granted on Server A are not transferred automatically. For each imported key, re-grant the operations required by AWS KMS (get_attributes, encrypt, decrypt).

# Grant the required permissions to the AWS principal ARN
ckms -c /path/to/server_b.toml \
  access-rights grant \
  --id <KEY_ID> \
  <AWS_PRINCIPAL_ARN> \
  get_attributes encrypt decrypt

Step 7 — Reconnect the XKS key store

Once Server B is ready and all keys are imported with the correct permissions, reconnect the AWS external key store:

aws kms connect-custom-key-store \
  --custom-key-store-id <CUSTOM_KEY_STORE_ID>

Poll until the status returns CONNECTED:

aws kms describe-custom-key-stores \
  --custom-key-store-id <CUSTOM_KEY_STORE_ID> \
  --query 'CustomKeyStores[0].ConnectionState'

Expected output:

"CONNECTED"

If the connection fails, check Server B's logs for SigV4 authentication errors or TLS issues. Verify the aws_xks_sigv4_access_key_id and aws_xks_sigv4_secret_access_key values match exactly what was provided to update-custom-key-store.


Step 8 — Verify the migration

Test an encrypt/decrypt round-trip through AWS KMS using one of the migrated keys to confirm end-to-end connectivity:

# Encrypt a small test payload
aws kms encrypt \
  --key-id <AWS_KMS_KEY_ARN> \
  --plaintext "SGVsbG8gV29ybGQ=" \
  --output text \
  --query CiphertextBlob \
  > /tmp/ciphertext.b64

# Decrypt it back
aws kms decrypt \
  --key-id <AWS_KMS_KEY_ARN> \
  --ciphertext-blob fileb://<(base64 -d /tmp/ciphertext.b64) \
  --output text \
  --query Plaintext | base64 -d

Expected output: Hello World


Post-migration cleanup

Once the migration is validated:

  1. Delete the exported key files from your local filesystem:

    rm keys/*.json
    
  2. Shut down Server A (or reconfigure it to no longer serve the XKS endpoints).

  3. If applicable, revoke access rights on Server A to prevent any residual use:

    ckms -c /path/to/server_a.toml \
      access-rights revoke \
      --id <KEY_ID> \
      <AWS_PRINCIPAL_ARN> \
      get_attributes encrypt decrypt
    

AWS KMS - Bring Your Own Key (BYOK)

Eviden KMS provides an aws byok command in its CLI (also available in the ui) to facilitate the import of an AWS wrapping key (KEK) in Eviden KMS, and the export of the wrapped keys for direct import in AWS KMS. To use the AWS KMS terminology, the key that will be created in the Eviden KMS will be called the external key material as stated in the AWS KMS docs.

The key material refers to the actual cryptographic key bytes that form the basis of a KMS key. While AWS KMS keys include additional metadata, policies, and access controls, the BYOK process allows Eviden KMS users to maintain full control over key generation while leveraging AWS KMS's infrastructure for other usages.

Table of Contents

Overview

Since AWS KMS is a managed service where private key material never leaves AWS HSMs, the key import process requires:

  1. Creating a KMS key with EXTERNAL origin (no key material)
  2. Download the wrapping public key and import token from AWS
  3. Wrap your key material using Eviden KMS
  4. Import the wrapped key material into AWS KMS

Supported wrapping algorithms:

Wrapping AlgorithmDescriptionSupported Key Material Types
RSAES_OAEP_SHA_256
RSAES_OAEP_SHA_1
The RSA encryption algorithm with Optimal Asymmetric Encryption Padding (OAEP) with SHA-256 or SHA-1 hash function.• 256-bit AES Symmetric keys
• HMAC keys
• Asymmetric ECC private keys*
RSA_AES_KEY_WRAP_SHA_256
RSA_AES_KEY_WRAP_SHA_1
Hybrid wrapping (RSA + AES Key Wrap) with SHA-256 or SHA-1 hash function.• Asymmetric RSA private keys
• Asymmetric ECC private keys

Wrapping Key Specs:

  • RSA_2048 (Note: cannot be used to wrap ECC_NIST_P521 keys with RSAES_OAEP_SHA*_)
  • RSA_3072
  • RSA_4096

⚠️ WARNING: Invalid combinations of wrapping algorithms, key material types may lead to errors. Ensure that your selected key material type is supported by the chosen wrapping algorithm and that the wrapping key spec is compatible with both.

Prerequisites

  • An active AWS account
  • Either: AWS CLI installed and configured on your machine (recommended) or an access to AWS Management Console and open the AWS Key Management Service (AWS KMS) console at https://console.aws.amazon.com/kms.
  • A running Eviden KMS instance.
  • Either: Eviden KMS CLI installed and configured on your machine or an access to the Eviden KMS UI of your deployed KMS instance.
  • Any tool to convert base64 values to their binary counterparts (e.g. openssl, python, etc).

Creating an AES key and importing it using the AWS CLI and the Eviden CLI

1. Create a KMS key with EXTERNAL origin

To use the AWS KMS API to create a symmetric encryption KMS key with no key material, send a CreateKey request with the Origin parameter set to EXTERNAL :

    aws kms create-key --origin EXTERNAL

If successful, the output should look like :

{
    "KeyMetadata": {
        "AWSAccountId": "447182645454",
        "KeyId": "350e35ef-ac51-4dbb-82a4-9bc50b0ea42b",
        "Arn": "arn:aws:kms:eu-west-3:447182645454:key/350e35ef-ac51-4dbb-82a4-9bc50b0ea42b",
        "CreationDate": "2026-02-18T15:05:59.358000+01:00",
        "Enabled": false,
        "Description": "",
        "KeyUsage": "ENCRYPT_DECRYPT",
        "KeyState": "PendingImport",
        "Origin": "EXTERNAL",
        "KeyManager": "CUSTOMER",
        "CustomerMasterKeySpec": "SYMMETRIC_DEFAULT",
        "KeySpec": "SYMMETRIC_DEFAULT",
        "EncryptionAlgorithms": [
            "SYMMETRIC_DEFAULT"
        ],
        "MultiRegion": false
    }
}

Copy the key ARN, you will need it in the next step. In this example, the key ARN is arn:aws:kms:eu-west-3:447182645454:key/350e35ef-ac51-4dbb-82a4-9bc50b0ea42b.

Note: If no key spec is specified, a symmetric key is created by default. To create a different key, pass the key-spec argument like below for an ECC_NIST_P384 key :

    aws kms create-key \
        --origin EXTERNAL \
        --key-spec ECC_NIST_P384 \
        --description "External NIST-P384 key for signing"

2. Create a symmetric key in Eviden KMS

ckms sym keys create symmetric_key_test1

Response:

The symmetric key was successfully generated.
        Unique identifier: symmetric_key_test1

3. Download wrapping public key and import token from AWS

After you create a AWS KMS key with no key material, download a wrapping public key and an import token for that KMS key by using the AWS KMS console or the GetParametersForImport API. The wrapping public key and import token are an indivisible set that must be used together.

A very detailed example on how to do this is detailed on this page, please refer to it for more info.

The command you should use is the following :

aws kms get-parameters-for-import \
    --key-id arn:aws:kms:eu-west-3:447182645454:key/350e35ef-ac51-4dbb-82a4-9bc50b0ea42b \
    --wrapping-algorithm RSAES_OAEP_SHA_256 \
    --wrapping-key-spec RSA_4096

Response:

{
    "KeyId": "arn:aws:kms:eu-west-3:447182645454:key/350e35ef-ac51-4dbb-82a4-9bc50b0ea42b",
    "ImportToken": "<YourBase64EncodedToken>",
    "PublicKey": "<YourBase64EncodedPublicKey>",
    "ParametersValidTo": "2026-02-19T15:07:13.227000+01:00"
}

As mentioned in the AWS documentation for importing key material, you will have to convert the base64 values to their binary counterparts before importing them. This can be done with the method of your choice, the most straightforward method (If you have openssl on your cli), would be to copy the token and paste it with the following command to get the file token.bin :

echo -n "<YourBase64EncodedToken>" | openssl enc -d -base64 -A -out token.bin

In a similar manner, you can use the command if you want to keep your public key as a binary blob :

echo -n "<YourBase64EncodedPublicKey>" | openssl enc -d -base64 -A -out kek.bin

4. Import the AWS KEK into Eviden KMS

ckms aws byok import \
    --kek-base64 "<YourBase64EncodedPublicKey>" \
    --wrapping-algorithm RSAES_OAEP_SHA_256 \
    --key-arn "<YourKeyArn>" \
    --key-id aws_kek_1

Feel free to change the key id to whatever you want, we will call the kek aws_kek_1 in this example.

Alternative : importing the kek as file

ckms aws byok import \
    --kek-file kek.bin \
    --wrapping-algorithm RSAES_OAEP_SHA_256 \
    --key-arn arn:aws:kms:eu-west-3:447182645454:key/350e35ef-ac51-4dbb-82a4-9bc50b0ea42b \
    --key-id aws_kek_1

Response:

The PublicKey in file /tmp/ca9f45ad-8596-45a6-bc57-5591e662cb61 was successfully imported with id: aws_kek_1.
        Unique identifier: aws_kek_1
        Tags:
                - aws
                - wrapping_algorithm:RsaesOaepSha256
                - key_arn:arn:aws:kms:eu-west-3:447182645454:key/350e35ef-ac51-4dbb-82a4-9bc50b0ea42b

5. Export the wrapped key material from Eviden KMS

# Provide token file simply to display the correct AWS CLI command for import
# If the output file is not specified, the CLI returns base64 encoded encrypted key material
ckms aws byok export \
    symmetric_key_test1 \
    kek_test1 \
    token.bin \
    EncryptedKeyMaterial.bin

Response:

The encrypted key material was successfully written to 512 for key symmetric_key_test1.

To import into AWS KMS using the API, run:
aws kms import-key-material \
--key-id arn:aws:kms:eu-west-3:447182645454:key/350e35ef-ac51-4dbb-82a4-9bc50b0ea42b \
--encrypted-key-material fileb://EncryptedKeyMaterial.bin \
--import-token fileb://token.bin \
--expiration-model KEY_MATERIAL_DOES_NOT_EXPIRE

6. Import the wrapped key material into AWS KMS

If you have filled all the fields on the previous step, you can directly use the command that the Eviden CLI automatically generated for you:

aws kms import-key-material \
    --key-id arn:aws:kms:eu-west-3:447182645454:key/350e35ef-ac51-4dbb-82a4-9bc50b0ea42b \
    --encrypted-key-material fileb://EncryptedKeyMaterial.bin \
    --import-token fileb://token.bin \
    --expiration-model KEY_MATERIAL_DOES_NOT_EXPIRE

Response:

{
    "KeyId": "arn:aws:kms:eu-west-3:447182645454:key/350e35ef-ac51-4dbb-82a4-9bc50b0ea42b",
    "KeyMaterialId": "60a5374a2457941eaf5c26b75fea0236bcdc5ddbe70c4aa15046e6fdda49e334"
}

Receiving this response means that the key material has been successfully imported into AWS KMS.

Creating an RSA key and importing it using the AWS CLI and the Eviden CLI

For this example, we will create an 2048 bits RSA key material, wrapped using a 4096 kek with RSAES_OAEP_SHA_256.

First, Sign in to the AWS Management Console and open the AWS Key Management Service (AWS KMS) console at https://console.aws.amazon.com/kms and complete the necessary steps to create a KMS key with external key material. Be mindful to provide the correct key spec - otherwise the console will expect a symmetric key by default. For this example, we will use RSA_2048.

The next step is to download the wrapping public key and import token. Be mindful that an RSA_AES_KEY_WRAP_SHA wrapping algorithm is required for wrapping RSA private key material (except in China Regions). Choosing RSAES_OAEP_SHA_256 will work for this example.

Once this is done, create your key on the cosmian KMS like follow, we call it rsa_key_material :

Create an RSA key in Eviden KMS

Then, navigate to the AWS - Import Kek section and fill with the adequate data. In this example, we paste the kek in the base64 format for convenience, and call it aws_kek_2.

Import the AWS KEK into Eviden KMS

Finally, export the wrapped key material from Eviden KMS to import it into AWS KMS.

Export the wrapped key material from Eviden KMS

We named the file to export EncryptedKeyMaterial.bin. You can import the wrapped key material using the AWS CLI or the AWS Management Console.

An in depth explanation of the import process can be found in the AWS documentation to Import key material (console).

Automated BYOK Scripts

To facilitate testing and automating the BYOK flow, we provide ready-to-use Bash scripts that demonstrate the end-to-end process of creating an AWS KMS key, generating the corresponding key material using the Eviden CLI, and performing the import/export procedures.

The following scripts are available for download:

  • RSA BYOK Script: Automates the import of RSA private key material. Supports RSA key sizes: 2048, 3072, and 4096. Find run_rsa_byok.sh on repository

  • ECC BYOK Script: Automates the import of Elliptic Curve private key material. Supports NIST curves: p256, p384, and p521. Find run_ecc_byok.sh on repository

  • AES BYOK Script: Automates the import of 256-bit AES symmetric key material. Find run_aes_byok.sh on repository

Usage

Each script requires the cosmian CLI to be available either in your PATH, the current directory, or specified via the COSMIAN_KMS_CLI environment variable. Also, ensure the aws CLI is available in your PATH, authenticated, and configured.

Example usage:

# Make the script executable
chmod +x run_rsa_byok.sh

# Run the script with the desired key size and an optional description
./run_rsa_byok.sh 2048 "My RSA External Key"

Eviden KMS provides a azure byok command in its CLI to facilitate the import of an Azure wrapping key (KEK) in Eviden KMS, and the export of the wrapped keys in the .byok format for easy import in Azure Key Vault.

Follow this Azure documentation, for a description of Bring Your Own Key (BYOK) in Azure Key Vault,

The vault must be a premium Azure Key Vault, and the user performing the operations must have the role of Crypto Officer.

Table of Contents

Create two test keys in the Eviden KMS

These keys will later be exported wrapped by the Azure (KEK) key, and imported into Azure Key Vault.

Create an RSA Key

The key will be a 4096-bit key.

ckms rsa keys create --size_in_bits 4096 TestRSAKey

   Public key unique identifier: TestRSAKey_pk
   Private key unique identifier: TestRSAKey

Create an Elliptic Curve Key

The key will be created on the NIST P-256 curve.

ckms ec keys create --curve nist-p256 TestECKey

   Public key unique identifier: TestECKey_pk
   Private key unique identifier: TestECKey

Create an Azure Key Encryption Key (KEK)

This key will be used to wrap the KMS RSA and EC private keys on export.

Using the Azure console:

Navigate to the Key Vault (say MyPremiumKeyVault) and create a 4096-bit RSA key.

Make sure to select Generate Key Encryption Key for importing HSM-protected Keys.

Create a Key Encryption Key using the console

Using the az CLI:

The supported operations must be restricted to import.

az keyvault key create --kty RSA-HSM --size 4096 --name KEK-BYOK --ops import --vault-name MyPremiumKeyVault

In the azoutput, the key identifier (kid) will be something like: "https://mypremiumkeyvault.vault.azure.net/keys/KEK-BYOK/664f5aa2797a4075b8e36ca4500636d8"

Please take note of the kid for next steps.

Export the Key Encryption (Public) Key

The KEK public key must be exported, then imported into the KMS.

Using the Azure console:

Edit the KEK by double-clicking on it.

Please take note of the key identifier (kid), which will be required later.

Download the public key and rename the file KEK-BYOK.public.pem.

Export the Key Encryption (Public) Key

Using the az CLI:

az keyvault key download --name KEK-BYOK --vault-name MyPremiumKeyVault --file KEK-BYOK.public.pem

Import the key encryption key in Eviden KMS

The KMS CLI provides an azure byok command to facilitate both the import of the key encryption key in Eviden KMS, and the export of the wrapped keys in the .byok format for easy import in Azure Key Vault.

ckms azure byok import KEK-BYOK.public.pem \
https://hsmbackedkeyvault.vault.azure.net/keys/BYOK_KEK/5e617a4d39c74f47b0b7d345f6a49d1b \
BYOK_KEK

The PublicKey in file KEK-BYOK.public.pem was imported with id: BYOK_KEK
          Unique identifier: BYOK_KEK

  Tags:
    - azure
    - kid:https://hsmbackedkeyvault.vault.azure.net/keys/BYOK_KEK/5e617a4d39c74f47b0b7d345f6a49d1b

Please note the presence of the azure and kid: tags on the key. These identify the key as an Azure Key Encryption Key (KEK) in the Eviden KMS.

Export the wrapped keys from the Eviden KMS

The KMS CLI azure byok export command will generate a .byok file containing the wrapped private key, which can be directly imported into Azure Key Vault.

Export the RSA key

ckms azure byok export TestRSAKey BYOK_KEK

The byok file was written to "TestRSAKey.byok" for key TestRSAKey

Export the EC key

ckms azure byok export TestECKey BYOK_KEK

The byok file was written to "TestECKey.byok" for key TestECKey

Upload the byok transfer blob in Azure Key Vault

Wrapped private RSA keys can be imported using either the console or the az LI. Elliptic curve keys can only be imported using the az CLI.

For an RSA Private Key

Using the Azure console:

Upload the transfer blob in Azure Key Vault

Using the az CLI:

az keyvault key import --vault-name MyPremiumKeyVault --name TestRSAKey --byok-file TestRSAKey.byok \
--ops sign verify

For an EC Private Key

The key type and curve must be specified on import for elliptic curve keys.

az keyvault key import --vault-name MyPremiumKeyVault --name TestECKey --byok-file TestECKey.byok \
--kty EC --curve P-256  --ops sign verify

Eviden KMS implements the Azure External Key Manager (EKM) Proxy API, enabling it to serve as an external key management service for an Azure Managed HSM.

This integration allows organizations to maintain complete physical control over their encryption keys outside of Azure infrastructure while seamlessly integrating with Azure services that support Customer Managed Keys (CMK).

The Eviden KMS implementation follows and implements the Microsoft EKM Proxy API Specification for v0.1-preview.

High level architecture

High level architecture

The customer's Azure services (configured with Customer Managed Keys) communicate with Azure Managed HSM to perform encryption/decryption operations. A full list of Azure services that support CMK can be consulted on this page.

With this integration, your protected secrets remain under your complete control while maintaining compatibility with Azure's managed services.

The following diagram illustrates a possible use case where Eviden KMS acts as the EKM Proxy :

Sequence diagram: Using an Azure Service with keys saved on customer's infrastructure

Api specification

URL format

All requests and responses for Azure EKM APIs are sent as JSON objects over HTTPS. Each request includes context information to associate Azure Managed HSM logs and audits with Eviden KMS logs.

The URI format for EKM Proxy API calls is:

https://{public-KMS-URI}/azureekm/[path-prefix]/{api-specific-paths}?api-version={client-api-version}

The parameters between brackets {} can be edited on the KMS configuration and must follow the following constraints :

Path Prefix:

  • Maximum 64 characters
  • Allowed characters: letters (a-z, A-Z), numbers (0-9), slashes (/), and dashes (-)

External Key ID:

  • Referenced as {key-name} in the endpoints below
  • Maximum 64 characters
  • Allowed characters: letters (a-z, A-Z), numbers (0-9), and dashes (-)

Endpoints

EndpointMethodPathDescription
Get Proxy InfoPOST/azureekm/[path-prefix]/infoHealth check and proxy details
Get Key MetadataPOST/azureekm/[path-prefix]/{key-name}/metadataRetrieve key type, size, and supported operations
Wrap KeyPOST/azureekm/[path-prefix]/{key-name}/wrapkeyWrap (encrypt) a DEK with a KEK
Unwrap KeyPOST/azureekm/[path-prefix]/{key-name}/unwrapkeyUnwrap (decrypt) a previously wrapped DEK

Supported algorithms

AlgorithmKey TypeDescription
A256KWAES-256AES Key Wrap (RFC 3394)
A256KWPAES-256AES Key Wrap with Padding (RFC 5649)
RSA-OAEP-256RSARSA-OAEP using SHA-256 and MGF1
RSA-OAEPRSARSA-OAEP using SHA-1 and MGF1

⚠️ Notice: For RSA operations, always use the private key reference as {key-name} in all endpoints, including wrap operations. The KMS will automatically derive and use the associated public key for encryption (wrap) operations. Passing the public key ID might lead to errors.

Getting started

Azure Managed HSM Setup

You must have an Azure Managed HSM Pool already created and activated in your Azure subscription. Refer to the Azure Managed HSM documentation for setup instructions.

Once the configuration is done, you will need the root CA certificate that the Azure Managed HSM uses for client authentication. This certificate will be configured in Eviden KMS to validate incoming mTLS connections.

Let's save the root CA as mhsm-root-ca.pem - We will need it in the next step.

Eviden KMS setup

Follow the Eviden KMS installation guide to install the KMS server on your infrastructure. The KMS server typically uses the configuration file located at /etc/cosmian/kms.toml when installed manually with default parameters.

Alternatively, you can deploy a pre-configured Eviden Confidential VM like explained in this guide.. For confidential VMs, the KMS configuration file is located in the encrypted LUKS container at /var/lib/cosmian_vm/data/app.conf.

Environment variables can also be used for all the configurations below.

The following guide will consider running Eviden KMS on confidential VM in non-FIPS mode.

mTLS Configuration

Configure mutual TLS authentication to accept connections from Azure Managed HSM by adding of editing to following lines in your configuration file: For detailed information about TLS client certificate authentication, see the TLS Client Certificate configuration guide.

[tls]
# Your server certificate and private key (PKCS#12 format)
tls_p12_file = "/etc/cosmian/server-cert.p12"
tls_p12_password = "your-secure-password"

# The certificate downloaded in the previous section
# This validates the client certificate presented by Azure MHSM
clients_ca_cert_file = "/etc/cosmian/mhsm-root-ca.pem"

Note : If you have a server's key and certificate files, you can convert them to PKCS#12 format using openssl:

openssl pkcs12 -export \
  -in server.crt \
  -inkey server.key \
  -out server-cert.p12 \
  -name "cosmian-kms-server" \
  -passout pass:your-secure-password

Azure EKM Configuration

[azure_ekm_config]
# Enable Azure EKM endpoints
azure_ekm_enable = true

# Optional: Path prefix for multi-tenant isolation (max 64 chars: a-z, A-Z, 0-9, /, -)
azure_ekm_path_prefix = "cosmian0"

# The fields below will be reported in the /info endpoint, edit according to your needs
azure_ekm_proxy_vendor = "Eviden"
azure_ekm_proxy_name = "EKM Proxy Service v0.1-preview"
azure_ekm_ekm_vendor = "Eviden"
azure_ekm_ekm_product = "Eviden KMS"

# WARNING: Only set to true for testing! Never in production.
azure_ekm_disable_client_auth = false

Configuration Parameters:

ParameterTypeDefaultDescription
azure_ekm_enablebooleanfalseEnable/disable Azure EKM API endpoints.
azure_ekm_path_prefixstringnoneOptional path prefix for routing and multi-tenant isolation. Max 64 chars: a-z, A-Z, 0-9, /, -. Example: "cosmian0", "customer-a/prod"
azure_ekm_proxy_vendorstring"Eviden"Proxy vendor name reported in /info endpoint.
azure_ekm_proxy_namestring"EKM Proxy Service"Proxy name and version reported in /info endpoint. Auto-inserts the KMS package version by default.
azure_ekm_ekm_vendorstring"Eviden"EKMS vendor name reported in /info endpoint.
azure_ekm_ekm_productstring"Eviden KMS v{CARGO_PKG_VERSION}"EKMS product name and version reported in /info endpoint.
azure_ekm_disable_client_authbooleanfalse⚠️ Bypasses mTLS authentication. Only use for testing.

Grant the Managed HSM identity access rights on the KMS

When Azure Managed HSM connects to the EKM proxy over mTLS, Eviden KMS authenticates it as a regular KMIP user, using the Subject CN of the client certificate as the username (see TLS Client Certificate configuration and the Authentication guide). This identity is generally of the form <hsm-name>.managedhsmclient.azure.net — check your own Managed HSM client certificate to confirm its exact Subject CN.

Because the external key is owned by whichever KMS user created it, you must explicitly grant this Managed HSM identity the rights to read and use that key, using ckms access-rights grant:

ckms access-rights grant <hsm-name>.managedhsmclient.azure.net -i <kms-key-uid> get get_attributes encrypt decrypt

Without this step, Azure's key registration call (see below) fails with a KeyNotFound-type error, because the KMS rejects the Managed HSM identity's request for an object it does not own and has not been granted access to.

Connect Azure Managed HSM to the EKM proxy

The following az keyvault ekm-connection commands configure the Managed HSM side of the connection to your Eviden KMS.

First, create the EKM connection on the Managed HSM, pointing it at your Eviden KMS:

az keyvault ekm-connection create \
  --hsm-name <hsm-name> \
  --host <kms-fqdn>:443 \
  --path-prefix /azureekm/<azure_ekm_path_prefix> \
  --server-ca-certificate <path-to-kms-tls-ca-or-server-cert.pem>

⚠️ Port 443 is required. The --host argument's own help text states: "EKM proxy host (FQDN or FQDN:port). If port is omitted, 443 is assumed." In practice, the Managed HSM EKM proxy calls have been observed to require port 443 — make sure your Eviden KMS is reachable on port 443 (in addition to any other port you use for KMIP/administrative access), even if [http] port in your KMS configuration is set to a different value.

--path-prefix must match azure_ekm_path_prefix. If you configured azure_ekm_path_prefix = "cosmian0" on the KMS side, the Azure-side --path-prefix must be the corresponding path (e.g. /azureekm/cosmian0). A mismatch here causes requests to fail to route, without necessarily producing an obvious error on the Azure side.

Verify the connection:

az keyvault ekm-connection check --hsm-name <hsm-name>

A successful check returns the /info payload reported by the KMS (apiVersion, proxyVendor, proxyName, ekmVendor, ekmProduct).

Register the external key and grant Azure RBAC

Register the KMS key as an external Managed HSM key, referencing its KMS UID:

az keyvault key create \
  --hsm-name <hsm-name> \
  --name <azure-key-name> \
  --external-key-id <kms-key-uid>

The consuming Azure service (for example, a Storage account's managed identity) then needs the Managed HSM Crypto Service Encryption User role (built-in Managed HSM local RBAC role, which grants the keys/wrap, keys/unwrap, and keys/read data actions) on that key:

az keyvault role assignment create \
  --hsm-name <hsm-name> \
  --role "Managed HSM Crypto Service Encryption User" \
  --assignee <consuming-service-principal-id> \
  --scope /keys/<azure-key-name>

Testing the integration

For testing purposes or for debugging, you can temporarily disable client authentication by commenting out the following configuration fields:

[tls]
# Comment this field to disable client auth and allow incoming requests from anyone
# clients_ca_cert_file = "/etc/cosmian/mhsm-root-ca.pem"

[azure_ekm_config]
# Set to true to bypass mTLS for local testing only — NEVER use in production
azure_ekm_disable_client_auth = true

Restart the KMS server:

sudo systemctl restart cosmian-kms

Test the /info endpoint:

curl -X POST "https://ekm.yourdomain.com/azureekm/cosmian0/info?api-version=0.1-preview" \
  -H "Content-Type: application/json" \
  -d '{
    "request_context": {
      "request_id": "test-request-123",
      "correlation_id": "test-correlation-456",
      "pool_name": "test-pool"
    }
  }'

Expected response (if you used the config above):

{
  "api_version": "0.1-preview",
  "proxy_vendor": "Eviden",
  "proxy_name": "EKM Proxy Service v=0.1-preview",
  "ekm_vendor": "Eviden",
  "ekm_product": "Eviden KMS v5.15.0"
}

Troubleshooting

SymptomLikely causeFix
az keyvault ekm-connection create succeeds, but Azure later fails to reach the proxyKMS not reachable on port 443Expose the KMS on port 443, in addition to any other configured port — see the port 443 note above.
Requests never reach the KMS logs, or Azure reports a connectivity/routing error--path-prefix used in az keyvault ekm-connection create does not match azure_ekm_path_prefix in the KMS configurationMake both values match exactly, including the /azureekm/ prefix.
az keyvault key create --external-key-id ... fails with a KeyNotFound-type errorThe Managed HSM's mTLS identity has not been granted access rights on the KMS keyGrant get, get_attributes, encrypt, decrypt to the Managed HSM identity with ckms access-rights grant — see above.
Wrap/unwrap fails during a CMK-enabling operation (for example Azure Storage CMK) with a generic error on the Azure sideThe client sent a wrapping algorithm not currently listed under Supported algorithmsCheck the KMS server logs for the exact alg value received, and confirm it is one of the algorithms in the table above.

Microsoft Double Key Encryption (DKE)

Microsoft Double Key Encryption (DKE) is a feature of Microsoft 365 that allows you to protect your most sensitive data by encrypting data on the client computer before sending it to Microsoft servers. One of the keys used to encrypt remains under your control and makes the data unreadable by Microsoft. This key is kept inside your instance of Eviden KMS which exposes the required API to integrate with Microsoft DKE.

See How it works for details on the cryptographic process.

Microsoft Prerequisites

The DKE feature is currently only available for the Office Windows clients. In addition, to use Microsoft Double Key Encryption (DKE) you must have a Microsoft 365 Business Premium license and must have access to the Microsoft Purview compliance portal.

How it works

Once DKE is configured, the whole process consists in assigning a specific sensitivity label to a document. The label will indicate that the document is encrypted and that the key to decrypt it is stored in your Eviden KMS.

Please check the dedicated Microsoft documentation for a complete overview of the feature.

From a cryptographic standpoint, the feature works as follows:

Before saving an encrypted document, the Office client will:

  1. Generate an ephemeral 128-bit AES key and use it to encrypt the document
  2. Call the Eviden KMS to get your 2048-bit RSA public key (the Office client will cache the key for 24 hours)
  3. Use that key to wrap the AES key using the PKCS#11 CKM_RSA_PKCS_OAEP (NIST 800 56B Rev2) algorithm; the hashing algorithm is set to SHA-256 (see the list of supported algorithms for details)
  4. Send the wrapped AES key and the encrypted document to Microsoft servers, where Azure RMS will also wrap the wrapped AES key with their own key (hence the "double key" acronym)

Retrieving an encrypted document works as follows, the Office client will:

  1. Request Azure RMS to perform the first unwrapping using their key, to recover the wrapped AES key
  2. Download the encrypted document and the wrapped AES key
  3. Call your Eviden KMS to unwrap the AES key using your private RSA key. Please note that the private RSA key never leaves the Eviden KMS.
  4. Decrypt the document using the recovered AES key and display it.

Note: The Eviden KMS implementation of the PKCS#11 CKM_RSA_PKCS_OAEP algorithm is FIPS compliant. The DKE API is therefore available on the Eviden server running in FIPS mode.

Configuring the Eviden KMS server

The Eviden KMS server needs to be started with the --ms-dke-service-url <MS_DKE_SERVICE_URL> option.

The <MS_DKE_SERVICE_URL>should contain the external URL of this server as configured in Azure App Registrations for the DKE Service

The URL should be something like https://kms.my_domain.com/ms_dke

Alternatively, you can set the KMS_MS_DKE_SERVICE_URL environment variable to the same value, or set the corresponding entry in the server TOML configuration file.

No authentication => firewalling is critical

The Office client does not send any authentication information when calling the Eviden KMS. Firewalling the Eviden KMS server to only accept requests from valid Office clients is critical.

Running the KMS server in the cloud for DKE

It is possible to confidentially run the Eviden KMS server in the cloud inside an Eviden VM. However, due to the lack of authentication, and thus the need to firewall the server, one should make sure to use OS-level firewalling and not rely on the cloud provider's firewalling capabilities, particularly if running on Azure.

Create an RSA key with tag dke_key

Using the KMS CLI, create a 2048-bit RSA key with the tag dke_key:

ckms rsa keys create --tag dke_key --size_in_bits 2048

The tag can be changed to any value, but it must be used in the URL of the sensitivity label in the Microsoft Purview compliance portal. See Create a sensitivity label for encryption for details.

Rotate the DKE key

If later on you need to rotate the DKE key, you can use the KMS CLI to create a new key with a new tag. You must then create a new sensitivity label where the Double Key Encryption URL ends with the new tag value. See Create a sensitivity label for encryption for details.

Users should now select the new label when creating new documents. As long as the old key is available in the Eviden KMS, users will still be able to open documents encrypted with the old key.

Configuring Microsoft DKE in Purview

Please follow the main documentation provided by Microsoft. The following paragraphs add details and tips to the process.

Activating the protection service from Azure Information Protection

The protection service must be activated in order to use DKE. If not activated, "Group and Sites" will not be available in the scopes of the sensitivity label.

Run PowerShell as Admin

Install-Module -Name AIPService

In case of troubles with the execution policy, try

powershell -ExecutionPolicy ByPass

Get the status of the AIP service

Import-Module AIPService
Get-AIPService

You may have to connect to it first

Connect-AipService

If it is disabled, enable it

Enable-AipService

More options for phased deployments here.

Microsoft Entra configuration for encrypted content

Check if there is anything to configure.

Activate sensitivity labels for MS 365 groups

Sensitivity labels must be activated for MS 365 groups which are also called unified groups. The main documentation on configuring sensitivity labels is available here.

The objective is to set the EnableMIPLabels parameter to True at the Entra ID Directory level (which is set to False by default), using Group.Unified template.

The EnableMIPLabels flag indicates whether sensitivity labels published in Microsoft Purview compliance portal can be applied to Microsoft 365 groups. For more information, see Assign Sensitivity Labels for Microsoft 365 groups.

To verify the current value of the EnableMIPLabels parameter, run the following command:

$Setting = Get-AzureADDirectorySetting | ? { $_.DisplayName -eq "Group.Unified"}
&Setting.Values

See this doc and Enable sensitivity label support in PowerShell which will probably require configuring groups first.

De-activate co-authoring in Microsoft Purview

Do NOT click the box on this page, doing so will prevent the use of DKE in Sensitivity Labels.

If you need to deactivate co-authoring, you can do so by running the following commands:

Install-Module -Name PSWSMan # if not already installed
Install-WSMan
Install-Module -Name ExchangeOnlineManagement
Import-Module ExchangeOnlineManagement
Connect-IPPSSession -UserPrincipalName you_admin_user@your_domain.com
Set-PolicyConfig -EnableLabelCoauth:$false

Create a sensitivity label for encryption

Navigate to Purview then Solutions > Information protection > Labels

Follow these instructions to create the label. Select Use Double Key Encryption on the encryption configuration screen and make sure you do not activate co-authoring.

Use the correct URL

The URL must be set to a form similar to https://dke.acme.com/ms_dke/dke_key where

  • dke.acme.com is the address of the Eviden KMS server. A valid certificate must be installed on the server.
  • ms_dke is the root of REST path for the DKE services.
  • dke_key is the tag set for the RSA key pair to use for this label.

Sensitivity Label

Activating the label (scope) for meetings does not seem to work.

label scope

Windows CNG Key Storage Provider (KSP)

Microsoft Intune integration — The Cosmian KMS CNG KSP integrates natively with Microsoft Intune SCEP and PKCS certificate profiles. When Intune provisions a device certificate, the private key is created and permanently stored in Cosmian KMS — never on the endpoint. See the Intune integration section for setup details.

Intune + CNG KSP architecture

sequenceDiagram
    participant Admin as IT Administrator
    participant Intune as Microsoft Intune<br/>(Cloud UEM)
    participant Endpoint as Windows Endpoint<br/>(Intune-managed)
    participant KSP as cosmian_cng.dll<br/>(CNG KSP)
    participant KMS as Cosmian KMS<br/>(KMIP 2.1)
    participant SCEP as SCEP / PKCS<br/>Certificate Authority

    Admin->>Intune: 1. Create SCEP certificate profile<br/>KSP = "Cosmian KMS Key Storage Provider"
    Intune->>Endpoint: 2. Push certificate profile
    Endpoint->>KSP: 3. NCryptCreatePersistedKey()
    KSP->>KMS: 4. KMIP CreateKeyPair<br/>(private key stays in KMS)
    KMS-->>KSP: 5. Public key + key UUID
    KSP-->>Endpoint: 6. Key handle (public key blob)
    Endpoint->>SCEP: 7. Submit CSR<br/>(public key only)
    SCEP-->>Endpoint: 8. Signed certificate
    Note over Endpoint,KMS: All subsequent Sign / Decrypt operations<br/>are forwarded to Cosmian KMS via the KSP
    Endpoint->>KSP: 9. NCryptSignHash() (e.g. TLS handshake)
    KSP->>KMS: 10. KMIP Sign
    KMS-->>KSP: 11. Signature
    KSP-->>Endpoint: 12. Signature bytes

What is it?

Cosmian KMS provides a Windows CNG Key Storage Provider (KSP) DLL — cosmian_cng.dll — that stores private keys inside Cosmian KMS rather than the local Windows machine store.

A Key Storage Provider is a pluggable module defined by the Windows Cryptography API: Next Generation (CNG) framework (introduced in Windows Vista / Server 2008). CNG replaces the older CAPI (CryptoAPI). Any application that calls the standard NCrypt* family of functions — including the Windows certificate enrollment engine, Schannel (TLS), and Code Signing — automatically uses whichever KSP is associated with a given key, with no application changes required. By deploying cosmian_cng.dll, all private key material is kept inside Cosmian KMS: it never exists on the device disk.


Why use it?

NeedHow the KSP addresses it
Private-key protectionKeys are generated and stored exclusively inside Cosmian KMS — never on the endpoint disk or in the Windows registry.
FIPS 140-3 complianceCosmian KMS is FIPS 140-3 validated. All cryptographic operations (sign, decrypt, key generation) are executed by the KMS, not by Windows.
Centralised audit trailEvery sign / decrypt operation is logged in the KMS with user identity, timestamp, and key identifier.
Key revocationRevoking a key in the KMS immediately blocks all devices using it, without requiring an MDM policy push.
Zero-touch provisioningWorks natively with Microsoft Intune SCEP and PKCS certificate profiles — no custom enrollment agent is needed.
Export preventionExport policy is disabled by default (NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG is off), so private keys cannot be extracted from the device.

Who should use it?

  • Enterprise IT / Security teams deploying Microsoft Intune device certificates where device private keys must be hardware-protected or centrally managed.
  • PKI administrators who need a hardware-security-module (HSM)-equivalent experience without deploying a physical HSM on every endpoint.
  • Compliance officers who need full audit logs of every private key usage on corporate endpoints.
  • Windows developers integrating with any NCrypt-based API (Schannel, Code Signing, S/MIME, BitLocker recovery) who want keys protected by a remote KMS.

When to use it?

Use the CNG KSP when:

  • You are enrolling device certificates through Microsoft Intune SCEP or PKCS profiles and you want the private key to remain inside Cosmian KMS rather than in the Windows software KSP or TPM.
  • You need to remotely wipe a key (e.g. lost device): destroy the key in the KMS and all signing / decryption operations on that device fail immediately.
  • Your organisation is subject to regulations (FIPS 140-3, Common Criteria, eIDAS, NIS2) that mandate private key custody inside a certified key store.
  • You want a single, centralised, KMIP 2.1-compliant inventory of all device keys with metadata and access-control policies.

Do not use the CNG KSP for keys that must be available offline (e.g. full-disk encryption unlock keys), because operations require a live HTTPS connection to the KMS.


How it works

CNG plug-in mechanism

Windows CNG defines the NCRYPT_KEY_STORAGE_FUNCTION_TABLE interface: a struct of 30 function pointers that Windows calls for every key operation. A KSP DLL exports a single entry point:

SECURITY_STATUS GetKeyStorageInterface(
    LPCWSTR pszProviderName,
    NCRYPT_KEY_STORAGE_FUNCTION_TABLE **ppFunctionTable,
    DWORD dwFlags
);

cosmian_cng.dll implements this interface entirely in Rust. When Windows loads the DLL, it calls GetKeyStorageInterface to obtain the function table, and from that point on every NCrypt* call is handled by the corresponding Rust function.

Request flow

sequenceDiagram
    participant App as Windows Application<br/>(Intune SCEP, MMC, certutil)
    participant DLL as cosmian_cng.dll
    participant KMS as Cosmian KMS<br/>(HTTPS / KMIP 2.1)

    App->>DLL: NCrypt* call<br/>(OpenStorageProvider, SignHash, …)
    Note over DLL: 1. Validate handle (magic check)<br/>2. Translate NCrypt params → KMIP request<br/>3. Block on shared Tokio runtime
    DLL->>KMS: KMIP operation<br/>(CreateKeyPair, Sign, Decrypt, …)
    KMS-->>DLL: KMIP response
    Note over DLL: 4. Translate KMS response → NCrypt buffer<br/>5. Return SECURITY_STATUS
    DLL-->>App: SECURITY_STATUS + output buffer

Private keys never leave the KMS: only the public key blob (BCRYPT_RSAKEY_BLOB or BCRYPT_ECCKEY_BLOB) is serialised and returned to Windows for certificate construction.

Provider and key context objects

Windows conceptRust typeWhat it holds
NCRYPT_PROV_HANDLECngProviderCtx (heap box → raw usize)Shared Arc<KmsClient>, provider config path
NCRYPT_KEY_HANDLECngKeyCtx (heap box → raw usize)Key state (Persisted or Pending), Arc<KmsClient>

Both types carry a magic number (0xC05_1A_AC) that is validated on every handle dereference — a stale or forged handle returns NTE_INVALID_HANDLE instead of causing undefined behaviour.

Key lifecycle

flowchart LR
    subgraph Key Creation
        A[CreatePersistedKey] -->|allocates PendingCreation context| B[SetKeyProperty]
        B --> C[FinalizeKey]
        C -->|calls KMS CreateKeyPair,<br/>tags key with cng-ksp::name| D["Key exists in KMS<br/>(Persisted state)"]
    end
flowchart LR
    subgraph Key Lookup
        E[OpenKey] -->|look up KMS UUID<br/>by tag cng-ksp::name| F[allocate CngKeyCtx]
    end
flowchart LR
    subgraph Key Operations
        G[SignHash] -->|digested_data = pre-hashed input| H[KMS Sign]
        I[Decrypt] --> J[KMS Decrypt]
        K[ExportKey] -->|SPKI DER → BCRYPT blob| L[KMS export public key only]
        M[DeleteKey] -->|revoke + destroy both key UUIDs| N[KMS Destroy]
    end

Key naming and tagging

Each key is tagged in the KMS with two vendor tags (namespace Cosmian):

TagPurpose
cng-kspMarks the object as managed by this KSP
cng-ksp::<name>Allows lookup by the CNG key name string

Authentication

The DLL reads ckms.toml using the same search order as the ckms CLI:

  1. Path in the CKMS_CONF environment variable.
  2. ckms.toml in the same directory as cosmian_cng.dll.
  3. %APPDATA%\.cosmian\ckms.toml.

The configuration file supports bearer-token OAuth 2.0, mTLS (PKCS#12 client certificate), or unauthenticated (for local dev/test only).

SECURITY_STATUS error mapping

KSP errorSECURITY_STATUS returned
Handle invalid / nullNTE_INVALID_HANDLE (0x80090026)
Key not found in KMSNTE_BAD_KEYSET (0x80090016)
Algorithm not in supported listNTE_BAD_ALGID (0x80090008)
Export requested on non-exportable keyNTE_PERM (0x80090010)
Output buffer too smallNTE_BUFFER_TOO_SMALL (0x80090028)
Any KMS REST errorNTE_FAIL (0x8009002A)

Architecture

flowchart LR
    App["Windows Application<br/>(Intune SCEP, MMC, certutil)"] -->|NCrypt API calls| DLL["cosmian_cng.dll<br/><br/>NCRYPT_KEY_STORAGE_FUNCTION_TABLE<br/>GetKeyStorageInterface()"]
    DLL -->|"CreateKeyPair / Sign / Decrypt<br/>Locate / Destroy"| KMS["Cosmian KMS<br/>(REST / KMIP 2.1)"]
    style DLL fill:#f5f5f5,stroke:#333
    style KMS fill:#e8f4e8,stroke:#2d7d2d

Private keys never touch the local machine. Only public key blobs are returned to Windows.


Supported algorithms

AlgorithmKey sizes / curvesOperations
RSA2048, 3072, 4096 bitsSign, Decrypt
ECDSA P-256NIST P-256Sign
ECDSA P-384NIST P-384Sign
ECDSA P-521NIST P-521Sign
ECDH P-256/384/521NIST curvesKey Agreement

Installation

1. Install the Cosmian KMS CLI

The cosmian_cng.dll is bundled inside the Cosmian KMS CLI installer alongside ckms.exe. Download and run the installer for your platform:

# Download the CLI installer (adjust the version number as needed)
Invoke-WebRequest `
    -Uri "https://package.cosmian.com/kms/5.25.0/windows/x86_64/non-fips/static-openssl/cosmian-kms-cli-non-fips-static-openssl_5.25.0_x86_64.exe" `
    -OutFile "$env:TEMP\cosmian-kms-cli.exe"

# Run the installer (accepts the default installation directory)
Start-Process -FilePath "$env:TEMP\cosmian-kms-cli.exe" -ArgumentList "/S" -Wait

The installer places both files in the default installation directory:

C:\Users\<username>\AppData\Local\Cosmian KMS CLI\ckms.exe
C:\Users\<username>\AppData\Local\Cosmian KMS CLI\cosmian_cng.dll

Ensure the installation directory is on your PATH (the installer does this automatically):

$env:PATH += ";$env:LOCALAPPDATA\Cosmian KMS CLI"

2. Configure the KMS connection

Run the interactive configuration wizard to create ckms.toml:

ckms configure

The wizard prompts for the KMS server URL and authentication method (none, bearer token, PEM client certificate, or PKCS#12 client certificate). It writes the configuration to %APPDATA%\.cosmian\ckms.toml.

3. Verify the DLL

Before registering the KSP, confirm that the DLL loads correctly and can reach the KMS server:

ckms cng verify --dll "$env:LOCALAPPDATA\Cosmian KMS CLI\cosmian_cng.dll"

Expected output (with a running KMS):

=== Cosmian CNG KSP Verification ===

Loading DLL: C:\Users\<user>\AppData\Local\Cosmian KMS CLI\cosmian_cng.dll

  [OK]   OpenProvider
── RSA key pair + sign + export + lookup ──
  [OK]   RSA key pair + sign + export + lookup
  ...
─────────────────────────────────────────
All 15 verification step(s) PASSED.

If any step shows [FAIL] with NTE_FAIL (0x8009002A), the DLL cannot reach the KMS server — check ckms.toml and network connectivity before proceeding.

4. Register the KSP (requires Administrator)

# From an elevated PowerShell prompt:
ckms cng register --dll "$env:LOCALAPPDATA\Cosmian KMS CLI\cosmian_cng.dll"

This performs the following steps:

  1. Copies cosmian_cng.dll to %SystemRoot%\System32 (CNG resolves provider DLLs from that directory)

  2. Calls BCryptRegisterProvider to create the proper registry structure:

    HKLM\SYSTEM\CurrentControlSet\Control\Cryptography\Providers\
        Cosmian KMS Key Storage Provider\
            UM\
                Image        REG_SZ     "cosmian_cng.dll"
                00010001\
                    (Default)   REG_SZ         "CRYPT_KEY_STORAGE_INTERFACE"
                    Functions   REG_MULTI_SZ   "KEY_STORAGE"
    
  3. Calls BCryptAddContextFunctionProvider to make the provider discoverable by NCryptOpenStorageProvider and certutil -csplist

5. Verify registration

ckms cng status
# Expected: Cosmian KMS CNG KSP: REGISTERED

# Also verify Windows sees the provider:
certutil -csplist | Select-String "Cosmian"
# Expected output includes: "Cosmian KMS Key Storage Provider"

Using the KSP

Create a key pair via NCrypt API

The KSP is automatically available to any Windows application that calls NCryptOpenStorageProvider with the provider name "Cosmian KMS Key Storage Provider".

NCRYPT_PROV_HANDLE hProv;
NCryptOpenStorageProvider(
    &hProv,
    L"Cosmian KMS Key Storage Provider",
    0
);

NCRYPT_KEY_HANDLE hKey;
NCryptCreatePersistedKey(hProv, &hKey, BCRYPT_RSA_ALGORITHM, L"my-key", 0, 0);
NCryptSetProperty(hKey, NCRYPT_LENGTH_PROPERTY, (PBYTE)&keyLength, sizeof(DWORD), 0);
NCryptFinalizeKey(hKey, 0);
// The private key is now stored in Cosmian KMS
// FinalizeKey returns only after the KMS confirms creation

Intune SCEP device certificate

Microsoft Intune uses the Windows CNG CSR pipeline. Once the KSP is registered, configure the Intune SCEP profile to use the Cosmian KMS Key Storage Provider as the key storage location. The private key backing the device certificate will be created and permanently stored in Cosmian KMS.

List all CNG KSP keys in the KMS

ckms cng list-keys

Unregister the KSP

# Elevated PowerShell:
ckms cng unregister

Logging

Log output is written to cosmian_cng.log in the same directory as the DLL by default — no additional configuration is required. If the DLL directory is not writable, the log file is created in %APPDATA%\.cosmian\cosmian_cng.log instead. Logging to a file always happens without any configuration.

Set the COSMIAN_CNG_KSP_LOGGING_LEVEL environment variable to control verbosity: trace, debug, info (default), warn, or error.


Key naming and tag convention

Each key created through the KSP is tagged in the KMS with:

  • cng-ksp — identifies all KSP-managed keys.
  • cng-ksp::<key_name> — identifies the key by its CNG name (the string passed to NCryptCreatePersistedKey / NCryptOpenKey).

Use ckms cng list-keys or the standard ckms locate command to find keys:

ckms locate --tag "cng-ksp::my-key"

Security considerations

  • Private keys are never stored locally; they exist only in Cosmian KMS.
  • All key operations require an authenticated connection to the KMS.
  • The NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG is off by default; private keys cannot be exported in plaintext.
  • Use mTLS or bearer-token authentication in ckms.toml for production deployments.
  • Run ckms cng register as Administrator. The DLL path stored in the registry is read by the Windows LSASS process — only trusted, signed DLLs should be registered.
  • Consider restricting read access to ckms.toml so that unprivileged users cannot extract the KMS server URL or client certificate.

Microsoft Intune integration

Microsoft Intune is Microsoft's cloud-based Unified Endpoint Management (UEM) service. It lets IT administrators manage devices (Windows, macOS, iOS, Android) and enforce security policies — including deploying certificates — without on-premises infrastructure. When Intune provisions a certificate on a Windows endpoint, the private key is generated locally via a Key Storage Provider. By default Windows uses its built-in software KSP, which stores keys in the registry.

With the Cosmian KMS CNG KSP registered on the device, Intune can be configured to use it instead: the private key is created and permanently stored in Cosmian KMS rather than on the device. This gives the organisation centralised custody, FIPS 140-3 compliance, real-time revocation, and a full audit trail — all without any change to the Intune enrollment workflow itself.

SCEP certificate profile

  1. In the Microsoft Intune admin center, create a SCEP certificate profile for Windows 10/11.

  2. Set Key storage provider (KSP) to "Enroll to Custom KSP, otherwise fail" and enter the provider name exactly as:

    Cosmian KMS Key Storage Provider
    
  3. Ensure the ckms.toml configuration file and the DLL are deployed to managed endpoints via an Intune Win32 app or PowerShell script before the certificate profile is applied.

  4. The Intune SCEP agent will call NCryptCreatePersistedKey on the Cosmian KSP, then send the resulting CSR (public key only) to the SCEP server. The private key never leaves Cosmian KMS.

PKCS certificate profile

Set Key storage provider (KSP) to "Enroll to Custom KSP, otherwise fail" and enter the same provider name. The Intune PKCS connector will use the Cosmian KSP to generate the key pair on the device.

Imported PFX certificate (PFXImport PowerShell module)

For importing existing PFX certificates via the IntunePfxImport PowerShell module, follow these steps on the Certificate Connector server:

  1. Ensure the KMS server is running and reachable (e.g. http://localhost:9998).

  2. Register the KSP (see Installation above):

    ckms cng register --dll "$env:LOCALAPPDATA\Cosmian KMS CLI\cosmian_cng.dll"
    
  3. Verify Windows discovers the provider:

    certutil -csplist | Select-String "Cosmian"
    # Expected: "Cosmian KMS Key Storage Provider"
    
  4. Import the IntunePfxImport module (from the IntunePfxImportUtilities release folder):

    Import-Module .\IntunePfxImport.psd1
    
  5. Create the encryption key pair in Cosmian KMS:

    Add-IntuneKspKey `
        -ProviderName "Cosmian KMS Key Storage Provider" `
        -KeyName "PFXEncryptionKey" `
        -MakeExportable
    

    This calls NCryptCreatePersistedKeyNCryptSetProperty(Export Policy)NCryptFinalizeKey on the Cosmian KSP. The RSA key pair is created and stored exclusively in Cosmian KMS. The -MakeExportable flag sets the NCRYPT_ALLOW_EXPORT_FLAG so the public key can be exported for use on other connector servers.

  6. Export the public key (if multiple connector servers share the key):

    Export-IntunePublicKey `
        -ProviderName "Cosmian KMS Key Storage Provider" `
        -KeyName "PFXEncryptionKey" `
        -FilePath "C:\temp\PFXEncryptionKey.pfx"
    
  7. Import PFX certificates to Intune using the key:

    $userPFXObject = New-IntuneUserPfxCertificate `
        -PathToPfxFile "C:\temp\userA.pfx" `
        $SecureFilePassword `
        "userA@contoso.com" `
        "Cosmian KMS Key Storage Provider" `
        "PFXEncryptionKey" `
        "smimeEncryption"
    Import-IntuneUserPfxCertificate -CertificateList $userPFXObject
    

Remote key revocation for lost devices

When a device is lost or decommissioned:

# From the KMS administrator workstation:
ckms locate --tag "cng-ksp::intune-device-<serial>"
ckms revoke --id <uuid> "Device decommissioned"
ckms destroy --id <uuid>

Once the key is destroyed in the KMS, any subsequent sign or decrypt attempt from the device returns NTE_FAIL, effectively revoking the private key without requiring a certificate revocation list (CRL) propagation delay.


Integration testing

The CNG KSP is tested at three levels — all runnable without Azure credentials or an Intune tenant.

Prerequisites

RequirementWhy
Windows 10/11 or Server 2019+The DLL uses the Windows CNG API (NCrypt*), which is Windows-only.
Rust toolchain (MSVC target)cargo build produces the DLL, server binary, and verification tool.
Administrator privilegesRequired to write the KSP registry key under HKLM.
vcpkg with openssl_x64-windows-staticSet OPENSSL_DIR or VCPKG_INSTALLATION_ROOT.

No Azure account, Intune license, or SCEP infrastructure is needed — all tests run against a local Cosmian KMS server with a SQLite backend.

Test layers

LayerWhat it testsRunnerLocation
Rust lib testsBackend functions (backend::create_rsa_key_pair, sign_hash, list_cng_keys, …) via an in-process KMScargo test --lib -p cosmian_cngcrate/clients/cng/src/tests.rs
DLL surface testsLoads cosmian_cng.dll at runtime, calls GetKeyStorageInterface, exercises every NCrypt* function pointer against a live KMSckms cng verify --dll <path>crate/clients/clap/src/actions/cng_verify.rs
CLI commandsckms cng register, status, list-keys, unregisterPowerShell assertions.mise/scripts/windows/test_cng_ksp.ps1
Intune PFX ImportAdd-IntuneKspKey + Export-IntunePublicKey via the IntunePfxImportUtilities module against the registered KSPPowerShell (requires Admin + module).mise/scripts/windows/test_cng_ksp.ps1

Running the full test suite

From an elevated PowerShell prompt at the repository root:

.\.github\scripts\windows\test_cng_ksp.ps1

The script performs the following steps:

sequenceDiagram
    participant PS as PowerShell script
    participant Build as Cargo build
    participant KMS as Local KMS server
    participant Reg as Windows Registry
    participant Verify as cng_verify.exe
    participant Tests as cargo test
    participant Intune as IntunePfxImport module

    PS->>Build: 1. Build KMS server, cosmian_cng.dll, cng_verify, ckms
    Build-->>PS: Binaries ready
    PS->>KMS: 2. Start KMS (SQLite, port 9998)
    PS->>PS: 3. Write ckms.toml (server_url = localhost:9998)
    PS->>KMS: 4. GET /version (smoke-test)
    KMS-->>PS: 200 OK
    PS->>Reg: 5. ckms cng register (write HKLM KSP key)
    Reg-->>PS: Registry key created
    PS->>Verify: 6. Run cng_verify.exe (NCrypt surface tests)
    Verify->>KMS: NCrypt* calls via cosmian_cng.dll
    KMS-->>Verify: Key operations
    Verify-->>PS: All tests passed
    PS->>Tests: 7. cargo test --lib (in-process backend tests)
    Tests->>KMS: KMIP operations
    Tests-->>PS: Tests passed
    PS->>KMS: 8. ckms cng list-keys / ckms cng status
    KMS-->>PS: Key list and status
    PS->>Intune: 8b. Add-IntuneKspKey + Export-IntunePublicKey
    Intune->>KMS: NCryptCreatePersistedKey → NCryptFinalizeKey → NCryptExportKey
    KMS-->>Intune: Key created + public blob exported
    Intune-->>PS: Intune PFX workflow passed
    PS->>PS: 9. Parse KMS logs for ERROR / PANIC
    PS->>Reg: 10. ckms cng unregister (remove HKLM KSP key)
    PS->>KMS: Stop KMS server

Test coverage

The cng_verify tool exercises the following NCrypt operations against a live KMS:

TestOperations exercised
RSA key pair + sign + export + lookupCreatePersistedKeySetKeyPropertyFinalizeKeyExportKeyOpenKeySignHash (PKCS1v15) → DeleteKey
RSA encrypt / decrypt (OAEP)EncryptDecrypt (round-trip validation)
RSA-PSS signSignHash with PSS padding + salt
RSA signature verifySignHashVerifySignature (valid + invalid hash)
EC P-256 key pair + sign + exportCreatePersistedKeyFinalizeKeyExportKeySignHash (ECDSA) → DeleteKey
ECDSA signature verify (P-256)SignHashVerifySignature
EC P-384 key pair + signCreatePersistedKeyFinalizeKeyExportKeySignHash (SHA-384)
EC P-521 key pair + exportCreatePersistedKeyFinalizeKeyExportKey
DeleteKey + verify goneDeleteKeyOpenKey (expect NTE_BAD_KEYSET)

Environment variables

VariableDefaultPurpose
CKMS_CONF<target_dir>/ckms.tomlPath to the KMS client configuration file
OPENSSL_DIRAuto-detected from vcpkgOpenSSL static library directory
VCPKG_INSTALLATION_ROOTFallback for OPENSSL_DIR
CNG_TEST_RELEASE0Set to 1 to build and test in release mode
RUST_LOGcosmian_kms_server=info,cosmian_cng=debugLog verbosity for KMS server and DLL
COSMIAN_CNG_KSP_LOGGING_LEVELinfoDLL-specific logging (trace/debug/info/warn/error)
INTUNE_PFX_MODULE_PATHPath to IntunePfxImport.psd1; enables the Intune PFX Import test step

Testing the Intune enrollment flow

The automated tests above validate the CNG KSP DLL itself — the same code path that Intune SCEP uses when it calls NCryptCreatePersistedKey and NCryptSignHash. To test the full Intune → endpoint → KMS flow you need:

RequirementPurpose
Microsoft Intune tenant (Azure AD / Entra ID)Manages the SCEP certificate profile
SCEP server (NDES or third-party)Signs the CSR generated by the endpoint
Intune-enrolled Windows deviceReceives the SCEP profile and triggers key creation
Cosmian KMS instance reachable from the deviceStores the private key

This is typically validated manually or in a dedicated staging environment, not in CI.


Troubleshooting

ProblemLikely causeSolution
NTE_FAIL on OpenKey or CreatePersistedKeyckms.toml missing or KMS unreachableVerify ckms.toml is present and server_url is reachable (curl https://<kms>/kmip/2_1).
NTE_BAD_KEYSET on OpenKeyKey name not found in the KMSRun ckms cng list-keys; verify the name and that the cng-ksp::<name> tag exists.
NTE_PERM on ExportKeyExport policy disabled (by design)Private key export is intentionally blocked. Use ExportKey only for public key blobs.
NTE_BAD_ALGIDAlgorithm string not recognisedUse RSA, ECDSA_P256, ECDSA_P384, ECDSA_P521, ECDH_P256, ECDH_P384, or ECDH_P521.
RegCreateKeyExW returns 0x80070005 (Access Denied)Not running as AdministratorRun ckms cng register from an elevated PowerShell prompt.
KSP not listed in certutil -csplistRegistry key missing or CryptSvc cached old listRe-run ckms cng register and restart the CryptSvc service (Restart-Service CryptSvc).
NTE_INVALID_HANDLEStale handle or DLL unloaded mid-operationEnsure the DLL is not forcibly unloaded during an active key operation.
Intune SCEP enrollment fails with "Custom KSP not found"DLL not deployed before profile appliesDeploy the Win32 app containing the DLL and ckms.toml before the certificate profile, using an Intune assignment filter or dependency.
Log file not createdDLL directory not writableSet COSMIAN_CNG_KSP_LOGGING_LEVEL and check stderr, or grant write access to the DLL directory.

Glossary

TermFull nameDefinition
BCRYPTBase Cryptography API: Next GenerationThe symmetric/hash/key-derivation half of the Windows CNG API (BCrypt* functions), as opposed to the asymmetric/key-storage half (NCrypt*).
BYOKBring Your Own KeyA cloud-provider feature that lets customers supply (and control) the encryption key used by a cloud service, rather than having the provider generate it.
CACertificate AuthorityA trusted entity that issues and signs digital certificates, binding a public key to an identity.
CAPICryptography APIThe original Windows cryptographic subsystem (pre-Vista), replaced by CNG. Also written CryptoAPI.
CNGCryptography API: Next GenerationThe Windows cryptographic framework introduced in Vista / Server 2008. It defines pluggable Key Storage Providers and Algorithm Providers via well-known function tables, replacing the older CAPI/CryptoAPI.
CRLCertificate Revocation ListA signed list published by a CA of certificates that have been revoked before their expiry date.
CSRCertificate Signing RequestA PKCS#10 message containing a public key and subject information, sent to a CA to obtain a signed certificate.
DLLDynamic-Link LibraryA Windows shared library loaded at runtime, providing functions and data to calling processes without static linking.
ECDHElliptic-Curve Diffie–HellmanA key-agreement protocol that allows two parties to establish a shared secret over an insecure channel using elliptic-curve arithmetic.
ECDSAElliptic Curve Digital Signature AlgorithmA digital signature scheme based on elliptic-curve cryptography, offering equivalent security to RSA with significantly smaller key sizes.
eIDASElectronic Identification, Authentication and Trust ServicesEU regulation establishing a legal framework for electronic signatures, seals, timestamps, and authentication across EU member states.
FIPS 140-3Federal Information Processing Standard Publication 140-3A US government standard (NIST) specifying security requirements for cryptographic modules. Level 1–4 define increasing physical and logical security.
HKLMHKEY_LOCAL_MACHINEThe Windows registry hive containing system-wide configuration settings that apply to all users on the machine.
HSMHardware Security ModuleA tamper-resistant physical device that generates, stores, and protects cryptographic keys and performs cryptographic operations.
HTTPSHypertext Transfer Protocol SecureHTTP layered over TLS, providing encrypted and authenticated communication between client and server.
KMSKey Management System / Key Management ServiceA centralised service that manages cryptographic keys throughout their lifecycle (creation, distribution, rotation, revocation, destruction). In this document, refers to Cosmian KMS.
KMIPKey Management Interoperability ProtocolAn OASIS standard (current version 2.1) defining a protocol for communication between clients and key management servers.
KSPKey Storage ProviderA pluggable Windows CNG component (a DLL implementing NCRYPT_KEY_STORAGE_FUNCTION_TABLE) that stores private keys and performs asymmetric key operations on behalf of Windows and applications.
LSASSLocal Security Authority Subsystem ServiceThe Windows process responsible for enforcing security policy, handling authentication, and loading CNG/CAPI providers.
MDMMobile Device ManagementA class of software and protocols that allow organisations to remotely configure, monitor, and enforce policies on employee devices.
MMCMicrosoft Management ConsoleA Windows host application for administrative snap-in tools, including the Certificates snap-in (certmgr.msc).
MSVCMicrosoft Visual C++Microsoft's C/C++ compiler and runtime toolchain, required to build Windows native binaries and DLLs (including Rust code targeting x86_64-pc-windows-msvc).
mTLSMutual TLSA variant of TLS in which both the client and the server present X.509 certificates to authenticate each other.
NCryptN-Crypt (CNG asymmetric API)The asymmetric-key and key-storage half of the Windows CNG API. NCrypt* functions delegate to the registered KSP DLL for private key operations.
NDESNetwork Device Enrollment ServiceA Microsoft Windows Server role that implements the SCEP protocol, acting as a proxy between Intune/MDM and an enterprise CA.
NIS2Network and Information Systems Directive 2EU cybersecurity directive (2022/2555) that expands the scope of the original NIS directive, imposing stricter security requirements and incident reporting obligations.
NISTNational Institute of Standards and TechnologyUS federal agency that publishes cryptographic standards (AES, SHA, ECDSA, FIPS 140, etc.).
OAEPOptimal Asymmetric Encryption PaddingA padding scheme (RFC 8017) used with RSA encryption that provides semantic security and resistance to chosen-ciphertext attacks.
OAuth 2.0Open Authorization 2.0An open standard for token-based delegated authorisation, widely used for API access. Bearer tokens issued by an identity provider are used to authenticate calls to the KMS.
PKCSPublic Key Cryptography StandardsA family of cryptographic standards published by RSA Security / IETF. Commonly referenced standards include PKCS#1 (RSA), PKCS#7/CMS (signed data), PKCS#10 (CSR), PKCS#11 (token interface), and PKCS#12 (PFX key store).
PKCS#12Public Key Cryptography Standard #12A binary format (.pfx / .p12) for storing a private key together with its certificate chain, protected by a password.
PKIPublic Key InfrastructureThe set of roles, policies, hardware, software, and procedures needed to create, manage, distribute, use, store, and revoke digital certificates.
PSSProbabilistic Signature SchemeA padding scheme (RFC 8017 / RSASSA-PSS) for RSA digital signatures that adds randomness, providing provable security without requiring a full-domain hash.
RSARivest–Shamir–AdlemanA widely used public-key cryptosystem based on the difficulty of factoring large integers, supporting both encryption and digital signatures.
SCEPSimple Certificate Enrollment ProtocolAn IETF protocol (RFC 8894) that automates the issuance of X.509 certificates, typically used by MDM systems like Microsoft Intune to enroll device certificates.
S/MIMESecure/Multipurpose Internet Mail ExtensionsA standard for public-key encryption and digital signing of email messages, using X.509 certificates.
SPKISubject Public Key InfoThe ASN.1 structure (from X.509 / RFC 5480) that encodes a public key together with its algorithm identifier, used in CSRs and DER-format public key exports.
TLSTransport Layer SecurityThe cryptographic protocol that secures communications over networks, successor to SSL. Used for all HTTPS connections between the KSP DLL and the KMS server.
TPMTrusted Platform ModuleA hardware chip embedded in many PCs that provides secure key generation and storage tied to the physical machine. The CNG KSP is an alternative to TPM-backed key storage, offering remote management capabilities.
UEMUnified Endpoint ManagementA category of IT management solutions that provide a single platform for managing all endpoint types (PCs, mobiles, IoT). Microsoft Intune is a UEM service.
UUIDUniversally Unique IdentifierA 128-bit identifier (RFC 4122) used by Cosmian KMS to uniquely identify every managed key object.
vcpkgVisual C++ Package ManagerMicrosoft's open-source C/C++ package manager, used in this project to obtain pre-built OpenSSL static libraries for Windows builds.

To import a Customer Managed Encryption Key in the Google KMS, follow the instructions on Google documentation related to importing a manually wrapped key.

Prepare the symmetric key and wrapping key in Google Cloud KMS

Using the Google Console, first create a key in an existing Cloud KMS key ring.

Create a key in an existing Cloud KMS key ring

Click Continue and select Imported Key

Select Imported Key

Then click Create

In the Select Import Job dropdown, select Create Import Job

Create Import Job

Select 4096 bit RSA -OAEP Padding - SHA256 digest + 256 bit AES-KWP as import method then click Create

Then click Download wrapping key

Download the wrapping key

The key will be downloaded as a PKCS#8 PEM named ImportWith4096RsaAesKeyWrapSha256.pem

Create a symmetric key in Eviden KMS

This is the symmetric key that will be wrapped by the Google wrapping key and imported in the Google KMS.

ckms sym keys create  --number-of-bits 256 CMEK_Sym_Key

The symmetric key was successfully generated.
          Unique identifier: CMEK_Sym_Key

Import the wrapping key in Eviden KMS

ckms rsa keys import --key-format pem --key-usage encrypt --key-usage wrap-key \
ImportWith4096RsaAesKeyWrapSha256.pem CMEK_Wrapping_Key

The PublicKey in file ImportWith4096RsaAesKeyWrapSha256.pem was imported with id: CMEK_Wrapping_Key
          Unique identifier: CMEK_Wrapping_Key

Export the symmetric key wrapped by the wrapping key from Eviden KMS

ckms sym keys export --key-id CMEK_Sym_Key --wrap-key-id CMEK_Wrapping_Key \
 --key-format raw --wrapping-algorithm rsa-aes-key-wrap wrapped_key.bin

The key CMEK_Sym_Key of type SymmetricKey was exported to "wrapped_key.bin"
          Unique identifier: CMEK_Sym_Key

Note 1: The rsa-aes-key-wrap wrapping algorithm uses the SHA256 digest.

Note 2: the wrapped key should be 552 bytes (4416 bits) long:

  • 4096 bits (RSA key length) +
  • 256 bits (Symmetric key length) +
  • 64 bits of AES KWP overhead.

Import the wrapped key in Google Cloud KMS

Import the wrapped key in Google Cloud KMS

The key should now be available in the Google Cloud KMS.

The key should now be available in the Google Cloud KMS

Automated testing

The entire Eviden KMS side of this workflow (steps 2–4) is covered by the script .mise/scripts/test_gcp_cmek.sh. It uses the test wrapping key at test_data/google_cmek/Import_RSA_AES_WRAP.pem and can be executed via:

mise run test:gcp_cmek --variant non-fips

To use Customer Supplied Encryption Keys, follow the general instructions on using RSA wrapping keys.

Generate a symmetric key in Eviden KMS

This is the symmetric key that will be used as the CSEK.

ckms sym keys create  --number-of-bits 256 CSEK_Sym_Key

The symmetric key was successfully generated.
          Unique identifier: CSEK_Sym_Key

Download the Google CSEK Certificate and extract the RSA wrapping key

Download the certificate

curl  https://cloud-certs.storage.googleapis.com/google-cloud-csek-ingress.pem > test_data/cse/google-cloud-csek-ingress.pem

Import the certificates in Eviden KMS

Name it google_csek on import.

ckms certificates import --format pem test_data/cse/google-cloud-csek-ingress.pem google_csek

The certificate in the PEM file was successfully imported!
          Unique identifier: google_csek

Export the wrapped CSEK Symmetric Key

The key must be wrapped using CKM_RSA_PKCS_OAEP with a SHA1 digest.

The export function will automatically recover the wrapping public key from the certificate and perform the key wrapping.

 ckms rsa keys export --key-id CSEK_Sym_Key --wrap-key-id google_csek \
 --wrapping-algorithm rsa-oaep-sha1 --key-format raw wrapped_key.bin

The key CSEK_Sym_Key of type SymmetricKey was exported to "wrapped_key.bin"
          Unique identifier: CSEK_Sym_Key

Note 1: Make sure you use rsa-oaep-sha1 to force the SHA1 digest. Note 2: The wrapped key should be 2048 bits (256 bytes) long.

Convert the wrapped CSEK Symmetric Key to base64

cat wrapped_key.bin | base64

BtE+r06qy4isyfMR29n5uGSPj1qbOQTA42nxVJ...Hw==

Getting started

The Eviden Key Management Server is compatible with Google Workspace client-side encryption. Using this feature, your users can encrypt files and documents, in their browsers, before sending them to Google servers. The ephemeral encryption keys are protected by "key wrapping keys", stored in the KMS and unavailable to Google. Only users that have the right to unwrap the ephemeral encryption keys inside the KMS, can decrypt the files. An overview is provided in Google's About client-side encryption page.

Info

To enable client-side encryption (CSE) in Google Workspace, connect as an admin to the admin console and go to the Google Workspace client-side encryption page.

Google has extensive documentation on how to enable CSE in Google Workspace. The Use client-side encryption for users' data page is a good starting point. It may be slightly overwhelming, and this documentation adds some details to help you get started.

Prerequisites

Info

You will need:

Choosing the Certificate Authority

First you need an intermediate CA (Certificate Authority) that is trusted by Google and compliant with Gmail X509 expectations (read the expected X509 extensions).

This CA will issue your users certificates:

  • either your CA is one of the Google recommended CA, follow this page to make sure it is (note that Actalis can provide CA certificates for free). In that case, your users S/MIME certificates will be issued directly from your CA and you will need to import them one by one using KMS CLI
  • either this is a custom CA:
    • that you already have (remember that this custom CA must have the expected Google X509 extensions). You will have to upload the full CA chain in admin.google.com->Apps/Google Workspace/Settings for Gmail/User Settings/S/MIME (and wait for provisioning to be fully done, few hours expected).
    • that you do not have, in that case read this page to generate your own CA and upload the full CA chain in admin.google.com.

Actalis CA

In order to import the Actalis CA in admin.google.com->Apps/Google Workspace/Settings for Gmail/User Settings/S/MIME, you have to build the full chain of certificates from an existing PKCS#12 certificate.

openssl pkcs12 -in certificate_s_mime.p12 -cacerts -nokeys -out ca.pem -passin pass:'YOUR_PASSWORD'
openssl pkcs12 -in certificate_s_mime.p12 -clcerts -nokeys -out certificate.pem -passin pass:'YOUR_PASSWORD'
cp certificate.pem fullchain.pem
cat ca.pem >> fullchain.pem
cat fullchain.pem

Choosing and configuring the Identity Provider

The first thing that will need to be done is to configure the Identity Provider. This is the service that the Eviden Key Management Server will use to authenticate users before they can encrypt files or access encrypted files.

The Identity Provider (IdP) is either a third party IdP or Google identity.

Warning

Using Google Identity is not recommended since Google as the authority could issue tokens to impersonate users and recover their keys. However, since configuring an Identity Provider is hard and Google Identity is the easiest to configure, we will use it in this tutorial.

Enable CSE

Tip

The initial page should look like this. What matters here is the link shown at the tip of the red-arrow. This is the URL at which Google client-side encryption expects the .well-known file to be served. Assuming your domain is example.com, the URL will likely be https://cse.example.com/.well-known/cse-configuration.

To configure a .well-known file, you need to:

  1. Create the .well-known file with the proper content

  2. Set up a server that serves the file at the URL shown in the image above

Instructions are provided in the Configuring the .well-known file section

Once this is complete, the screen on refresh should turn to this:

IdP configuration is successful

Configuring the Key Management Server

The KMS must be behind a valid TLS certificate when started. Assuming it is running at https://cse.example.com (kms-public-url parameter from server configuration), you should add the External Key Service with KACLS URL https://cse.example.com/google_cse in the Client-Side Encryption page of the Google Workspace admin console.

Tip

To enable Client Side Encryption on the Eviden KMS server, it must be started with the --kms-public-url option, and the --google-cse-enable option. This URL option is at which the KMS will serve the Key Access Control Lists (KACLs) for the Google CSE service. The KACLs are used by the Google CSE service to determine which users have access to which keys. The KACLs are served by the KMS at the URL https://cse.example.com/google_cse in the example above.

The Key Management Server must be configured to use the same Identity Provider as the one configured in the previous step. When using Google Identity, the server should be configured with the following options set in the corresponding Google documentation.

Assuming Google is the Identity Provider, the KMS should be started with the following options:

--jwt-issuer-uri=https://accounts.google.com
--jwks-uri=https://www.googleapis.com/oauth2/v3/certs
--kms-public-url=https://cse.example.com
--google-cse-enable

For example, if you are using the docker image, you can run the following command:

docker run -p 9998:9998 --name kms ghcr.io/cosmian/kms:latest \
    --jwt-issuer-uri=https://accounts.google.com \
    --jwks-uri=https://www.googleapis.com/oauth2/v3/certs \
    --kms-public-url=https://cse.example.com \
    --google-cse-enable
# Server configuration
kms_public_url = "http://localhost:9998"

# JWT authentication with Google
[idp_auth]
# issuer,jwks[,aud1[,aud2...]]; audiences optional (any-of when multiple)
jwt_auth_provider = ["https://accounts.google.com,https://www.googleapis.com/oauth2/v3/certs,"]

# Google CSE configuration
[google_cse_config]
google_cse_enable = true

external keys service

Then test the connection; it should show:

external key service ok

Finalize the configuration. The Client Side Encryption page should now show the service to be active and you will now have to decide whether to assign this service to all users or to a subset of users.

Eviden KMS active

Creating google_cse key

Once your CSE Eviden KMS is up and running, you need to import the AES wrapping key, which will be responsible for wrapping the keys managed by Google. This key MUST be created under the google_cse ID.

Using the KMS CLI, ensure that it is properly configured and that authentication is handled correctly.

Tip

Concerning the KMS CLI, you will have to log in the first time you use it. This is done by running ckms login.

# create it
ckms sym keys create -t google_cse google_cse

# or import an existing key
ckms sym keys import -t google_cse PATH_TO_YOUR_KEY google_cse

Next, you'll need to assign access rights to each user who requires CSE functionality, whether they are part of your organization or a guest. You can also grant wildcard access ('*') to allow all users to use this key in CSE endpoints.

ckms access-rights grant USER_ID google_cse get encrypt decrypt

# or give access to everyone
ckms access-rights grant '*' google_cse get encrypt decrypt

Handling Guest identity providers

As an administrator, you can allow external users to access your encrypted content via Google Workspace Client-Side Encryption (CSE), including sharing encrypted documents or hosting encrypted Google Meet sessions.

For more information on this configuration, refer to Google documentation.

Eviden KMS supports this feature, and to enable it:

  • Add the identity provider's information in the server-side Eviden KMS configuration
  • Ensure that external users can access the Google CSE symmetric key

User experience

Learn more details about CSE user experience over all supported applications.

Configuring the .well-known file

General configuration instructions for Google client-side encryption is available at this url in paragraph (Option 1) To connect to your IdP using a .well-known file

Using Google as an Identity Provider

To use Google as an Identity Provider, you first need to create a dedicated client ID in the Google Cloud Console. Detailed instructions are available on the page referenced above in the section entitled Create-a-client-id-for-google-identity.

The general idea is to create a project, then in APIs & Services > Credentials, create a client ID for a web application. This Client ID will be used in the .well-known file.

Once created the Client ID should look like this

Create Client ID

Warning

Do not forget to add http://localhost:17899/authorization in the Authorized redirect URIs list. It will allow the KMS CLI to authenticate with Google.

The list of URLs for Authorized origins and Authorized redirect are available in the Google documentation above, in paragraph Create a client ID for Google identity.

Generating the .well-known file

The format of the .well-known file is specified by RFC 8259

{
  "name": "Google identity for workspace client-side encryption",
  "client_id": "996739510374-au9fdbgp72dacrsag267ckg32jf3d3e2.apps.googleusercontent.com",
  "discovery_uri": "https://accounts.google.com/.well-known/openid-configuration",
  "grant_type": "implicit",
  "applications": {
    "drivefs": {
      "client_id": "947318989803-k88lapdik9bledfml8rr69ic6d3rdv57.apps.googleusercontent.com"
    },
    "drive-android": {
      "client_id": "313892590415-6lbccuf47cou4q45vanraqp3fv5jt9do.apps.googleusercontent.com"
    },
    "drive-ios": {
      "client_id": "313892590415-d3h1l7kl4htab916r6jevqdtu8bfmh9m.apps.googleusercontent.com"
    },
    "calendar-android": {
      "client_id": "313892590415-q84luo8fon5on5vl8a6rppo1qvcd3qvn.apps.googleusercontent.com"
    },
    "calendar-ios": {
      "client_id": "313892590415-283b3nilr8561tedgu1n4dcm9hd6g3hr.apps.googleusercontent.com"
    },
    "gmail-android": {
      "client_id": "313892590415-samhd32i4piankgs42o9sit5e9dug452.apps.googleusercontent.com"
    },
    "gmail-ios": {
      "client_id": "313892590415-ijvjpbnsh0gauuunjgsdn64ngg37k6rc.apps.googleusercontent.com"
    },
    "meet-android": {
      "client_id": "313892590415-i06v47su4k03ns7ot38akv7s9ari5oa5.apps.googleusercontent.com"
    },
    "meet-ios": {
      "client_id": "313892590415-32ha2bvs0tr1b12s089i33o58hjvqt55.apps.googleusercontent.com"
    }
  }
}

client_id is the OAuth 2.0 client ID of the Google Workspace domain that is created using the Google Cloud Console

Configuring a static web server to serve the .well-known file

The URL at which Google client-side encryption expects the .well-known file is on the link pointed by the red arrow below.

URL of well-known file

Assuming your organization is on the domain acme.com (which should match that of your email address domain), the URL would be: https://cse.acme.com/.well-known/cse-configuration

1. Configure a server running Ubuntu 23.04

The server should be reachable using an external IP; configure your DNS so that a A record with value cse.acme.com points to that external IP address of the server.

Make sure ports 80 and 443 are open to external traffic on this machine. Access to port 80 can be closed at the end of this procedure.

2. Install nginx on the server

sudo apt update
sudo apt install nginx

3. Transfer your .well-known file

sudo mkdir /var/www/html/.well-known
sudo touch /var/www/html/.well-known/cse-configuration
sudo scp 'path_to_your_well_known_file' user@IP:/var/www/html/.well-known/cse-configuration"

The file will put your created .well-known file to you web server in order to expose it.

4. Configure nginx to serve the .well-known file

Since, the .well-known file is served from a different domain than the one used by Google client-side encryption, CORS calls need to be enabled on NGINX to allow the browser to fetch the .well-known file.

Edit the file /etc/nginx/sites-available/default and add the following location:

location /.well-known/ {
    root /var/www/html;
    # Allow CORS calls: see https://support.google.com/a/answer/10743588?hl=en
    add_header 'Access-Control-Allow-Origin' '*';
}

Then restart the nginx service

sudo systemctl restart nginx

Finally, verify that nginx is correctly serving the file by running

➜ curl http://localhost/.well-known/cse-configuration

5. Enable HTTPS with certbot and Lets's Encrypt

Install certbot on the machine using snap (the snap daemon should already be installed and activated on Ubuntu 23.04)

sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot

General instructions on installing certbot are available at this URL.

Get a certificate and configure nginx

sudo certbot --nginx

The command will ask you to provide an email address and a domain name. The domain name should be cse.acme.com (or whatever domain you chose in step 1).

That's it, the empty .well-known file should now be served using HTTPS. From another machine, verify that it is now available on the public address

➜ curl https://cse.acme.com/.well-known/cse-configuration
{}

Port 80 can now be closed on the machine (or nginx configuration can be updated to redirect HTTP requests to HTTPS)

6. Enable CORS calls

The .well-known file is served from a different domain than the one used by Google client-side encryption. CORS calls need to be enabled on the server to allow the browser to fetch the .well-known file.

Edit the file /etc/nginx/sites-available/default and add the following at the top of the file (before the server block):

# Allow CORS calls: see https://apps.google.com/supportwidget/articlehome?hl=en&article_url=https%3A%2F%2Fsupport.google.com%2Fa%2Fanswer%2F10743588%3Fhl%3Den&assistant_id=generic-unu&product_context=10743588&product_name=UnuFlow&trigger_context=a
add_header 'Access-Control-Allow-Origin' '*';

Then restart the nginx service

sudo systemctl restart nginx

7. Optional: download the .well-known file as a proper JSON

The Client-side encryption service does not require this setting to work properly. However, it is useful to be able to download the .well-known file as a proper JSON object when viewing it in a browser.

To do so, the content-type header of the response must be set to application/json. Edit the file /etc/nginx/sites-available/default and add the following inside the server block that serves the HTTPS requests:

location /.well-known/ {
    default_type application/json;
}

Then restart the nginx service

sudo systemctl restart nginx

Follow the Google documentation to enable S/MIME for client-side encryption in your organization and create a service account to interact with the Gmail API.

Configure KMS CLIent (CLI)

The cosmian command line interface (CLI) simplifies the setup of S/MIME keys and certificates for users.

After completing the setup, update your KMS CLI with the necessary information for the service account you created for the Gmail API.

Choosing the Certificate Authority

First you need an intermediate CA (Certificate Authority) that is trusted by Google and compliant with Gmail X509 expectations (read the expected X509 extensions).

This CA will issue your users certificates:

  1. either your intermediate CA is one of the Google recommended CA, follow this page to make sure it is (note that Actalis can provide CA certificates for free). In that case:

    • at first, you need to import the issuer intermediate CA certificate into Eviden KMS since Google already trusts the root CA.

      ckms certificates import -f pkcs12 issuer_ca_certificate.p12 -p \
          PASSWORD issuer_ca_certificate
      
    • then, you need to import your users S/MIME certificates one by one using KMS CLI.

  2. either your intermediate CA is an internal or custom CA:

    • that you already have (remember that this custom CA must have the expected Google X509 extensions). You will have to upload the full CA chain in admin.google.com->Apps/Google Workspace/Settings for Gmail/User Settings/S/MIME (and wait for provisioning to be fully done, few hours expected).

    !!! warning Google Root CA The Google Root CA is actually the following chain (root CA + intermediate CA + any leaf public certificates). Allow a few hours for Google to complete the provisioning.

     Build your Google Root CA following those steps: [Exporting and viewing](../../smime.md#exporting-and-viewing) and do not forget to export the [Google expected root CA](../../smime.md#exporting-for-google-cse-smime)
    
     - that you do not have, in that case follow [those instructions to generate your own CA chain](../../smime.md#creating-an-smime-certificate-authority-with-a-root-and-intermediate-ca) and upload the Google Root CA in admin.google.com.
    

Managing administrator access rights

If multiple administrators will be generating key-pairs for users, ensure that each administrator has the appropriate access rights to the imported certificate chain elements:

  • the issuer certificate ID (given when importing the Certificate Authority)
  • the issuer private key ID
  • the issuer public key ID

In order to get the issuer private key ID and issuer public key ID, run the following command:

ckms attributes get -i issuer_ca_certificate

You'll use the ID of the issuer's private key (imported from the certificate chain) to later sign users' public keys and create their certificates.

Create user key-pair

Gmail uses key-pairs (an RSA private key wrapped with its associated certificate) and identities (the ID of an uploaded key-pair, associated with a user) to sign and encrypt emails using S/MIME. These objects are uploaded to Google via the Gmail API.

You can create a key-pair (RSA private key and user certificate chain) and upload it to the Gmail API using the following command:

ckms google key-pairs create --cse-key-id CSE_KEY_ID \
    --subject-name "C=FR, ST=IdF, L=Paris, O=ORGANIZATION, OU=DEPARTMENT, CN=user@organization.com, emailAddress=user@organization.com" \
    -i ISSUER_PRIVATE_KEY_ID --leaf-certificate-extensions user.ext user@your_organization.com

If you already have an existing RSA key-pair for the user, you can specify it in the command.

Using an existing leaf certificate

Instead of generating a new leaf certificate, you can use an existing one by specifying either:

  • --leaf-certificate-id CERT_ID: Use a certificate already stored in KMS
  • --leaf-certificate-pkcs12-file /path/to/cert.p12: Use a local certificate file

When using an existing leaf certificate, the --leaf-certificate-extensions parameter is not required.

!!! warning X509 flags extensions Remember that existing X509 certificate must comply the Google CSE requirements. X509 flags are expected by Google Gmail CSE for S/MIME.

Example with an existing PKCS12 certificate:

ckms google key-pairs create --cse-key-id CSE_KEY_ID \
    --subject-name "C=FR, ST=IdF, L=Paris, O=ORGANIZATION, OU=DEPARTMENT, CN=user@organization.com, emailAddress=user@organization.com" \
    --leaf-certificate-pkcs12-file /path/to/cert.p12 --leaf-certificate-pkcs12-password secret user@your_organization.com

Once created, the ID of the key pair and the certificate will be displayed.

Note: It may take up to 24 hours for Google to propagate the Client-Side Encryption (CSE) activation for a user within the Gmail API, allowing you to upload the S/MIME elements.

Insert user identity

After creating the key-pair, you must associate it with the user's identity. To do so, run the following command:

ckms google identities insert \
    --user-id user@your_organization.com CREATED_KEYPAIR_ID

You can manage key-pairs (get, list, enable, disable, obliterate) and identities (get, list, delete, patch) using the other available commands in the ckms commands documentation.

Note: It may take a few hours for Google to propagate the uploaded elements, after which users can begin using S/MIME for secure email exchanges.

User experience

To send a client-side encrypted email within your organization, a user needs to turn on the additional encryption option in the message window (lock -> Turn on additional encryption). Only users with CSE activated can encrypt and decrypt encrypted emails.

To send a client-side encrypted email outside your organization, a user needs to send a message to the recipient with their digital signature, without CSE turned on. The recipient then needs to reply to the message with their digital signature. Then the sender can choose to add CSE to email sent to the external recipient.

Encrypted emails will be automatically decrypted from Gmail interface.

Learn more about CSE user experience

Migrating existing Google Drive encrypted content to a new key service

This guide explains how to migrate encrypted content in Google Drive to a new key management service (KMS), specifically using Eviden KMS.

For a complete overview of Google's migration flow and all available options, refer to Google's official technical documentation.

Eviden KMS migration support and configuration

Eviden KMS supports the Google Client-Side Encryption (CSE) migration process by implementing the required endpoints, including:

  • POST <KMS_PUBLIC_URL>/google_cse/rewrap

  • POST <KMS_PUBLIC_URL>/google_cse/privilegedunwrap

These endpoints allow Eviden KMS to serve as either the source or target key service during a migration.

JWT Authentication for Migration

Google's migration flow requires both key services (KACLS) to authenticate with each other via signed JWTs. To support this:

Eviden KMS generates a dedicated RSA key pair at startup (if not already present in the database).

  • The private key is stored under the ID: google_cse_rsa
  • The public key is stored under the ID: google_cse_rsa_pk

Note: the JWT tokens MUST have their audience set to kacls-migration.

Key Persistence Across Restarts & Multiple Instances

To ensure consistent JWT signatures and seamless privileged unwrap operations across multiple instances or restarts, you can manually provide a persistent RSA private key using the --google-cse-migration-key CLI option.

This key must be in PEM-encoded PKCS#8 format.

Public Key Exposure

To allow other KACLS to verify JWT signatures, Eviden KMS exposes its public RSA key at:

  • GET <KMS_PUBLIC_URL>/google_cse/certs

This endpoint serves a JWKS (JSON Web Key Set) containing the public signing key.

Migrating existing emails to Gmail CSE

This documentation focuses on migrating email messages from a PST or Mbox archive to Gmail CSE. An overview of all migration options is provided in this blog article and covers other options such as migrating from Virtru or Microsoft Exchange.

A more general view of all the migration options to and from Gmail can be found at this link

Install the migration utility

Download the migration utility from the first link above.

Make sure you look at the included readme file for the scopes that must be associated with the service account (see below).

Create a service account credential with domain-wide access

Head to the GCP console and follow the Google Documentation on creating a Service Account

Create a service account

service account creation

Create a key in JSON format

create a service key

The key will be saved to your computer. The key will be provided to the migration utility.

Grant the GMail API domain-wide access

Open the Google Workspace Admin and follow the instructions in the paragraph Set up domain-wide delegation for a client

api_controls

Create a new API client, the client ID is available in the Details of the service account client id on the Google Cloud Console.

The OAuth scopes required are:

  • https://www.googleapis.com/auth/gmail.readonly for a dry run

  • https://www.googleapis.com/auth/gmail.modify when performing the actual migration

For more details on Google OAuth scopes, check this reference

Migrate example usage

Perform a dry-run over two input files to verify that they can be parsed and that their messages can be encrypted in preparation for migration:

For Windows:

gmail-cse-migrate.exe -input user1.pst -input user2.pst -api-credential c:\my_svc_acct.json -dryrun -logfile log.txt

If it looks good, then run the command again in migration mode:

gmail-cse-migrate.exe -input user1.pst -input user2.pst -api-credential c:\my_svc_acct.json -logfile log.txt

For macOS:

/gmail-cse-migrate \
   -api-credential=./gmail-migration-service-account-key.json \
   -input=user1.mbox \
   -logfile=./log.txt -dryrun

Version: gmail_cse_migrate-24.11.0.2 (darwin)
Download URI:
  https://dl.google.com/clientsideencryption/gmail-cse-migrate/macos/latest/gmail-cse-migrate.dmg


COMPLETED
  Inspected: 1 file in 0 directories
  Found:     163 messages in 1 .mbox file
  Failures:  6 messages in .mbox files failed to parse

MIGRATION
  Processed: 127 messages
  Skipped:   127 messages were not uploaded due to dryrun
  Skipped:   36 already encrypted messages were not migrated

PARTIAL SUCCESS

LOG FILE WRITTEN TO
  ./log.txt

Export Data from GMail

If you need to export data from Gmail, the easiest is to use TakeOut, which you can find here

To export data for a single user, follow this documentation

To export the whole organization data, follow this documentation

Manual Setup for MongoDB Client-Side Field Level Encryption (CSFLE) with KMIP

This guide walks through the manual setup process for enabling Client-Side Field Level Encryption in MongoDB using a KMIP-compatible Key Management System (KMS).

Overview

ItemDetails
ProtocolKMIP 1.0 over TCP/TLS with mutual certificate authentication
Port5696 (IANA-registered KMIP port)
Key typeAES-256 symmetric key
MongoDB versionMongoDB Enterprise 6.0 and above
Eviden KMS featureRequires non-FIPS build

Prerequisites

Packages

  • MongoDB Enterprise6.0
  • Python3.8
  • Python packages:
    • pymongo
    • dnspython
  • MongoDB Crypt Shared Library:
    • Download from the MongoDB Enterprise downloads section (e.g., mongo_crypt_v1.so)
    • Install to: /opt/mongo_crypt_shared/mongo_crypt_v1.so
  • Eviden KMS:
    • Eviden KMS endpoint: your-cosmian_kms-server:port
    • TLS certs:
      • Client cert: /opt/kms-certs/client.pem
      • CA cert: /opt/kms-certs/ca.pem
  • Key Vault Namespace:
    • Database: encryption
    • Collection: __keyVault
  • Schema Target Collection (example):
    • Database: medical
    • Collection: patients
  • Alternative Key Name (for the DEK): yourSecretKeyAlias

Step-by-Step Guide

⚠️ This tutorial was done in a DB test environment with an external Eviden KMS.

1. Define environment variable

Ensure that the path to the crypt shared library is defined:

export CRYPT_SHARED_LIB_PATH=/opt/mongo_crypt_shared/mongo_crypt_v1.so

2. Clean up existing DEKs (optional)

from pymongo import MongoClient

client = MongoClient("mongodb://localhost:27017")
client["encryption"]["__keyVault"].delete_many({"keyAltNames": ["yourSecretKeyAlias"]})

3. Generate a new DEK manually

from pymongo.encryption import ClientEncryption
from pymongo import MongoClient
from bson.codec_options import CodecOptions

kms_providers = {
    "kmip": {
        "endpoint": "your-cosmian_kms-server:port"
    }
}

tls_options = {
    "kmip": {
        "tlsCertificateKeyFile": "/opt/kms-certs/client.pem",
        "tlsCAFile": "/opt/kms-certs/ca.pem"
    }
}

client = MongoClient("mongodb://localhost:27017")
client_encryption = ClientEncryption(
    kms_providers=kms_providers,
    key_vault_namespace="encryption.__keyVault",
    key_vault_client=client,
    codec_options=CodecOptions(),
    kms_tls_options=tls_options
)

data_key_id = client_encryption.create_data_key("kmip", key_alt_names=["yourSecretKeyAlias"])
print(f"Created DEK with ID: {data_key_id}")

4. Define your encryption schema

schema_map = {
    "medical.patients": {
        "bsonType": "object",
        "properties": {
            "ssn": {
                "encrypt": {
                    "keyId": [data_key_id],
                    "bsonType": "string",
                    "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
                }
            }
        }
    }
}

5. Create the encrypted MongoClient

from pymongo.encryption_options import AutoEncryptionOpts

auto_encryption_opts = AutoEncryptionOpts(
    kms_providers=kms_providers,
    key_vault_namespace="encryption.__keyVault",
    schema_map=schema_map,
    kms_tls_options=tls_options,
    crypt_shared_lib_path=CRYPT_SHARED_LIB_PATH
)

secure_client = MongoClient("mongodb://localhost:27017", auto_encryption_opts=auto_encryption_opts)

6. Insert encrypted data

db = secure_client["medical"]
collection = db["patients"]
collection.drop()

collection.insert_one({
    "nom": "Doe",
    "ssn": "123-45-6789"
})

print("Encrypted document inserted.")

Notes

  • Replace your-cosmian_kms-server:port with your KMIP endpoint (e.g. cosmian-kms.example.com:5696).
  • Replace certificate paths if different.
  • The ssn field will be transparently encrypted on insert, and decrypted on read.
  • This setup requires MongoDB Enterprise, as CSFLE is not supported in the Community Edition.

Reading Encrypted Data with MongoDB CSFLE (Client-Side Field Level Encryption)

Step-by-Step Guide

This guide walks you through reading encrypted documents stored in MongoDB using automatic decryption with the CSFLE feature.


1. Set the Shared Crypt Library Path

Make sure the environment variable is set to the location of your shared crypt library:

export CRYPT_SHARED_LIB_PATH=/opt/mongo_crypt_shared/mongo_crypt_v1.so

2. (Optional) Read Raw Encrypted Documents

You can view the encrypted form of the documents using a regular (non-CSFLE-enabled) MongoDB client:

from pymongo import MongoClient

client = MongoClient("mongodb://localhost:27017")

print("🔒 Raw encrypted documents:")
for doc in client.medical.patients.find():
    print(doc)

3. Define KMS Provider Configuration

kms_providers = {
    "kmip": {
        "endpoint": "your-cosmian_kms-server:port"
    }
}

tls_options = {
    "kmip": {
        "tlsCertificateKeyFile": "/opt/kms-certs/client.pem",
        "tlsCAFile": "/opt/kms-certs/ca.pem"
    }
}

4. Configure AutoEncryptionOpts

This will enable automatic decryption using the shared library:

import os
from pymongo.encryption_options import AutoEncryptionOpts

CRYPT_SHARED_LIB_PATH = os.getenv("CRYPT_SHARED_LIB_PATH", "/opt/mongo_crypt_shared/mongo_crypt_v1.so")

auto_encryption_opts = AutoEncryptionOpts(
    kms_providers=kms_providers,
    key_vault_namespace="encryption.__keyVault",
    kms_tls_options=tls_options,
    crypt_shared_lib_path=CRYPT_SHARED_LIB_PATH
)

5. Create a Secure Client with CSFLE Enabled

from pymongo import MongoClient

secure_client = MongoClient(
    "mongodb://localhost:27017",
    auto_encryption_opts=auto_encryption_opts
)

6. Read Decrypted Documents

secure_db = secure_client["medical"]
secure_coll = secure_db["patients"]

print("🔓 Automatically decrypted documents:")
for doc in secure_coll.find():
    print(doc)

Result

Encrypted fields will be automatically decrypted in the returned documents, provided the proper key is stored in your key vault and accessible via the configured Eviden KMS.


Additional Notes

  • Make sure the key vault namespace (encryption.__keyVault) matches the namespace used during encryption.
  • This method works only with MongoDB Enterprise or MongoDB Atlas with CSFLE support.

MySQL Enterprise 8.4 + Eviden KMS Integration

Executive Summary

This document provides a comprehensive guide for integrating MySQL Enterprise with Eviden Key Management System (KMS) using the keyring_okv plugin for Transparent Data Encryption (TDE). This lab demonstrates secure key management and data encryption at rest in a controlled environment.



Version Requirements

Minimum Versions for TDE with keyring_okv

ComponentMinimum VersionNotes
MySQL Enterprise8.0+keyring_okv plugin available in Enterprise Edition
Eviden KMS5.14+Full KMIP 1.1 compatibility
KMIP Protocol1.1Standard protocol version supported by keyring_okv
OpenSSL3.0+For TLS 1.3 and mTLS certificate support

Note: This lab uses MySQL 8.4.7 and Eviden KMS 5.14+ which are the stable, production-ready versions recommended for new deployments.


Technical Specifications

System Requirements

ComponentVersionSpecification
MySQL Enterprise Server8.4.7-commercialGeneric Linux x86_64 binary (glibc 2.28)
Eviden KMS5.14+KMIP 1.1 server with socket support
Operating SystemUbuntu 24.04 LTS (Noble)Debian 10+, RHEL 8+, or any modern Linux (x86_64)
OpenSSL3.6.2 (+ 3.1.2 FIPS provider)For TLS/mTLS communication
NetworkDedicated subnetLow-latency, isolated lab network

Network Architecture

flowchart LR
    subgraph network["Isolated Lab Network"]
        kms["Eviden KMS v5.14+<br/>HTTP: 9998 (HTTPS)<br/>KMIP: 5696 (TLS)<br/>DB: SQLite"]
        mysql["MySQL Enterprise v8.4.7<br/>Port 3306 (MySQL)<br/>KMIP via TLS<br/>keyring_okv plugin"]
        kms <-->|"KMIP 1.1 over TLS 1.3"| mysql
    end

Key Management Flow

flowchart TB
    innodb["MySQL InnoDB"]
    tek["Generates encryption key (TEK)"]
    okv["keyring_okv plugin (MySQL 8.0+)"]
    kms["Eviden KMS v5.14+<br/>(Port 5696)"]
    db["SQLite Database (persistent)"]
    innodb --> tek
    tek --> okv
    okv -->|"Connects via KMIP (TLS)"| kms
    kms -->|"Stores / retrieves master key"| db

Architecture Overview

Component Roles

Eviden KMS (v5.14+):

  • Acts as external Key Management System
  • Manages encryption keys for MySQL
  • Provides KMIP 1.1 protocol support
  • Stores keys in persistent SQLite database
  • Enforces mutual TLS (mTLS) authentication with client certificates
  • Independent of MySQL lifecycle

MySQL Enterprise (v8.0.13+, ideally 8.4+):

  • Runs keyring_okv plugin as KMIP client
  • Generates and manages Transparent Data Encryption (TDE) keys
  • Encrypts table data at rest using master key from KMS
  • Communicates with KMS via secure KMIP protocol
  • Does NOT store master keys locally (security by design)
  • Maintains in-memory cache during uptime for performance

Security Model

  • Authentication: Mutual TLS (mTLS) with X.509 certificates
  • Encryption: AES-256 for data, TLS 1.3 for transport
  • Key Storage: Centralized in Eviden KMS (never persisted on MySQL filesystem)
  • Key Access: Only via authenticated KMIP protocol over TLS
  • Cache Invalidation: Automatic on KMS unavailability

Prerequisites

Hardware Requirements

  • Eviden KMS Server: 1 vCPU, 2 GB RAM, 10 GB storage
  • MySQL Enterprise Server: 2 vCPU, 4 GB RAM, 20 GB storage
  • Network: Low-latency connection (<10 ms ping recommended)

Software Requirements

On Eviden KMS Host (v5.14+):

  • Ubuntu 20.04+, | RHEL 8+ | or equivalent
  • Ope | x86_64 architecturenSSL 3.0+
  • Rust toolchain (if building from source)

On MySQL Enterprise Host (v8.0.13+, v8.4.7+ recommended):

  • Ubuntu 24.04 LTS
  • libaio1t64 (or libaio compatibility layer)
  • libncurses-dev or libncurses6
  • xz-utils
  • OpenSSL 3.0+

Certificates and Keys (PKI)

For mutual TLS authentication:

  • CA certificate (ca.crt)
  • CA private key (ca.key) - for signing
  • Server certificate with key (kms.p12 or separate files)
  • Client certificate for MySQL (mysql-client.crt)
  • Client private key for MySQL (mysql-client.key)

Installation and Configuration

Step 1: Prepare Eviden KMS (v5.14+)

1.1 Deploy Eviden KMS Server

Choose your Linux distribution and refer to installation guide

Verify the installed version and configuration file location:

cosmian_kms --version
cat /etc/cosmian/kms.toml

1.2 Configure KMS TOML File

Edit kms.toml:

default_username = "admin"
force_default_username = true

[logging]
otlp = ""  # Disabled for lab
enable_metering = false
rust_log = "info,cosmian_kms=debug"
## rolling_log_dir = "path_to_logging_directory"
## rolling_log_dir = "path_to_logging_directory"

[tls]
tls_p12_file = "scripts/certifs/kms.p12"
tls_p12_password = "YOURTLSPASSWORD"
clients_ca_cert_file = "scripts/certifs/ca.crt"

[socket_server]
socket_server_start = true
socket_server_port = 5696
socket_server_hostname = "0.0.0.0"

[http]
port = 9998
hostname = "0.0.0.0"

[ui_config]
ui_index_html_folder = "/usr/local/cosmian/ui/dist"
kms_public_url = "https://localhost:9998"

[db]
database-type="mysql"
database-url="mysql://kms_user:kms_password@mysql-server:3306/kms"

Key Configuration Notes:

  • clients_ca_cert_file is required for mTLS validation
  • socket_server_port 5696 is standard KMIP port
  • Ensure database path is on persistent storage

1.3 Start Eviden KMS (v5.14+)

export COSMIAN_KMS_CONF=/path/to/kms.toml
RUST_LOG=INFO ./cosmian_kms > /tmp/kms.log 2>&1 &

# Verify listening ports
ss -lntp | grep cosmian_kms

# Expected output:
# LISTEN ... :5696 ... cosmian_kms (KMIP socket)
# LISTEN ... :9998 ... cosmian_kms (HTTP API)

2.1 Install Prerequisites

# On MySQL host
sudo apt update
sudo apt install -y libaio1t64 libncurses-dev xz-utils openssl

# Create libaio compatibility symlink for Ubuntu 24.04
sudo ln -s /usr/lib/x86_64-linux-gnu/libaio.so.1t64 \
           /usr/lib/x86_64-linux-gnu/libaio.so.1

2.2 Extract MySQL Enterprise Binary (v8.0.13+)

cd /usr/local
# For MySQL 8.4.7:
sudo tar xvf /tmp/mysql-commercial-8.4.7-linux-glibc2.28-x86_64.tar.xz
# Or for MySQL 8.0.x (8.0.13+):
# sudo tar xvf /tmp/mysql-commercial-8.0.xx-linux-glibc2.28-x86_64.tar.xz

sudo ln -sf mysql-commercial-8.4.7-linux-glibc2.28-x86_64 mysql
sudo chown -R mysql:mysql /usr/local/mysql*

2.3 Create MySQL System User and Directories

sudo groupadd mysql 2>/dev/null || true
sudo useradd -r -g mysql -s /bin/false mysql 2>/dev/null || true

sudo mkdir -p /var/lib/mysql-data /var/log/mysql /var/run/mysqld
sudo chown -R mysql:mysql /var/lib/mysql-data /var/log/mysql /var/run/mysqld

2.4 Initialize MySQL Data Directory

cd /usr/local/mysql
sudo -u mysql ./bin/mysqld \
  --initialize \
  --user=mysql \
  --datadir=/var/lib/mysql-data \
  --log-error=/var/log/mysql/mysqld.log

# Capture temporary root password
grep "temporary password" /var/log/mysql/mysqld.log
# Save this password for next step

Step 3: Configure KMIP Client Certificates

3.1 Generate MySQL Client Certificate

# On a machine with CA key access
cd /path/to/pki

# Generate MySQL client private key
openssl genrsa -out mysql-client.key 4096

# Create Certificate Signing Request
openssl req -new \
  -key mysql-client.key \
  -out mysql-client.csr \
  -subj "/CN=mysql-kmip-client/O=Lab/C=FR"

# Sign with CA
openssl x509 -req \
  -in mysql-client.csr \
  -CA ca.crt \
  -CAkey ca.key \
  -CAcreateserial \
  -out mysql-client.crt \
  -days 365 \
  -sha256

# Resulting files:
# - mysql-client.crt (client certificate)
# - mysql-client.key (client private key)
# - ca.crt (CA certificate - already existing)

3.2 Copy Certificates to MySQL Host

# From PKI machine to MySQL host
scp mysql-client.crt mysql-client.key ca.crt \
    user@mysql-host:/tmp/

# On MySQL host
sudo mkdir -p /usr/local/mysql/mysql-keyring-okv/ssl
sudo cp /tmp/mysql-client.crt /usr/local/mysql/mysql-keyring-okv/ssl/cert.pem
sudo cp /tmp/mysql-client.key /usr/local/mysql/mysql-keyring-okv/ssl/key.pem
sudo cp /tmp/ca.crt /usr/local/mysql/mysql-keyring-okv/ssl/CA.pem

# Set proper permissions
sudo chown -R mysql:mysql /usr/local/mysql/mysql-keyring-okv/ssl
sudo chmod 600 /usr/local/mysql/mysql-keyring-okv/ssl/key.pem
sudo chmod 644 /usr/local/mysql/mysql-keyring-okv/ssl/cert.pem
sudo chmod 644 /usr/local/mysql/mysql-keyring-okv/ssl/CA.pem

Step 4: Configure keyring_okv Plugin (MySQL 8.0+)

4.1 Create okvclient.ora Configuration File

# On MySQL host
sudo tee /usr/local/mysql/mysql-keyring-okv/okvclient.ora > /dev/null <<'EOF'
SERVER=<kms-host>:5696
SSL_DIR=/usr/local/mysql/mysql-keyring-okv/ssl
SSL_CERT=cert.pem
SSL_KEY=key.pem
SSL_CA=CA.pem
EOF

sudo chown mysql:mysql /usr/local/mysql/mysql-keyring-okv/okvclient.ora
sudo chmod 600 /usr/local/mysql/mysql-keyring-okv/okvclient.ora

Configuration Notes:

  • Replace <kms-host> with actual KMS hostname/IP
  • Port 5696 is standard KMIP port
  • SSL_DIR must point to directory containing cert, key, and CA

4.2 Create MySQL Configuration File (v8.0+)

# Create /etc/my.cnf
sudo tee /etc/my.cnf > /dev/null <<'EOF'
[mysqld]
user=mysql
basedir=/usr/local/mysql
datadir=/var/lib/mysql-data
socket=/var/run/mysqld/mysqld.sock
log-error=/var/log/mysql/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
port=3306

# KMIP Keyring Plugin (MUST load early, before InnoDB)
# Available in MySQL 8.0 Enterprise Edition
early-plugin-load=keyring_okv.so
keyring_okv_conf_dir=/usr/local/mysql/mysql-keyring-okv

# Optional: Enable TDE by default for new tables (MySQL 8.0.16+)
default_table_encryption=ON
EOF

Critical Configuration Points:

  • early-plugin-load must be set before InnoDB initialization
  • keyring_okv_conf_dir must point to directory with okvclient.ora
  • Plugin must be loaded before any encrypted tables are accessed
  • Requires MySQL 8.0 Enterprise Edition
  • default_table_encryption requires MySQL 8.0.16+

4.3 Set MySQL Root Password

# Start MySQL
sudo -u mysql /usr/local/mysql/bin/mysqld \
  --datadir=/var/lib/mysql-data \
  --log-error=/var/log/mysql/mysqld.log \
  --socket=/var/run/mysqld/mysqld.sock &

sleep 5

# Connect with temporary password (from step 2.4)
/usr/local/mysql/bin/mysql -u root -p \
  --socket=/var/run/mysqld/mysqld.sock
# Enter temporary password when prompted

# Inside MySQL:
ALTER USER 'root'@'localhost' IDENTIFIED BY 'NewSecurePassword123!';
FLUSH PRIVILEGES;
EXIT;

# Stop MySQL
sudo systemctl stop mysqld
sleep 3

Step 5: Start MySQL with KMS Integration

# Clean up any residual lock files
sudo rm -f /var/lib/mysql-data/*.lock
sudo rm -f /var/run/mysqld/mysqld.lock

# Start MySQL with configuration file
sudo -u mysql /usr/local/mysql/bin/mysqld \
  --defaults-file=/etc/my.cnf &

sleep 5

# Verify running
ps aux | grep mysqld | grep -v grep

Capabilities and Features

Transparent Data Encryption (TDE)

Master Key Rotation

-- Rotate the master encryption key (generates new key in KMS)
ALTER INSTANCE ROTATE INNODB MASTER KEY;

Note: Rotation takes ~0.5 seconds; existing data remains accessible

Create Encrypted Tables

CREATE TABLE sensitive_data (
  id INT PRIMARY KEY,
  credit_card VARCHAR(20),
  ssn VARCHAR(11)
) ENCRYPTION='Y';

Encrypt Existing Tables

-- Enable encryption on an unencrypted table
ALTER TABLE existing_table ENCRYPTION='Y';

Per-Tablespace Encryption (MySQL 8.0.13+)

-- Create a general tablespace with encryption (MySQL 8.0.13+)
-- All tables created in this tablespace will be encrypted
CREATE TABLESPACE ts_encrypted ADD DATAFILE 'ts_encrypted.ibd' ENCRYPTION='Y';

-- Create a table in the encrypted tablespace
CREATE TABLE secure_table (id INT PRIMARY KEY) TABLESPACE=ts_encrypted;
-- secure_table is automatically encrypted (inherits from tablespace)

-- OR encrypt an existing tablespace (MySQL 8.0.13+)
-- This affects all tables within the tablespace
ALTER TABLESPACE ts_existing ENCRYPTION='Y';

Key Management Verification

-- Check keyring plugin status
SELECT PLUGIN_NAME, PLUGIN_STATUS
FROM INFORMATION_SCHEMA.PLUGINS
WHERE PLUGIN_NAME = 'keyring_okv';

-- Verify encryption on table
SELECT * FROM INFORMATION_SCHEMA.INNODB_TABLESPACES
WHERE NAME = 'database/table_name';

-- Verify table definition
SHOW CREATE TABLE table_name\G

Data At Rest Verification

# Verify data is encrypted in file (no plaintext strings)
strings /var/lib/mysql-data/database/table_name.ibd | \
  grep -i "sensitive_keyword" || \
  echo "✓ No plaintext data found (encryption OK)"

Key Storage Architecture

  • Master Key: Stored only in Eviden KMS database
  • Tablespace Keys (TEK): Encrypted with master key, stored in InnoDB
  • Cache: Kept in MySQL memory during runtime for performance
  • Persistence: Survives MySQL restart (key retrieved from KMS)
  • Security Boundary: Cache invalidated if KMS unreachable at startup

Limitations

  • Single Master Key: One master encryption key per MySQL instance
  • Network Dependency: KMS unavailability blocks new MySQL starts
  • Cache Behavior: Data accessible from cache until MySQL restart
  • No Local Fallback: Encrypted data inaccessible without KMS access at startup
  • Version Requirements: Requires MySQL Enterprise 8.0+ with keyring_okv support

Testing and Validation

Test 1: Verify Plugin is Active

/usr/local/mysql/bin/mysql -u root -pYourPassword \
  --socket=/var/run/mysqld/mysqld.sock <<'EOF'
SELECT PLUGIN_NAME, PLUGIN_STATUS
FROM INFORMATION_SCHEMA.PLUGINS
WHERE PLUGIN_NAME = 'keyring_okv';
EOF

Expected Output:

| keyring_okv | ACTIVE |

Pass Criteria: Plugin status is ACTIVE


Test 2: Create Encrypted Table and Verify Encryption

/usr/local/mysql/bin/mysql -u root -pYourPassword \
  --socket=/var/run/mysqld/mysqld.sock <<'EOF'
-- Create encrypted table
CREATE TABLE mysql.test_tde (
  id INT PRIMARY KEY,
  secret VARCHAR(255)
) ENCRYPTION='Y';

-- Insert test data
INSERT INTO mysql.test_tde (id, secret) VALUES
  (1, 'Sensitive-Data-001'),
  (2, 'Sensitive-Data-002');

-- Retrieve data
SELECT * FROM mysql.test_tde;

-- Verify encryption setting
SELECT * FROM INFORMATION_SCHEMA.INNODB_TABLESPACES
WHERE NAME = 'mysql/test_tde';
EOF

Expected Output:

| ENCRYPTION: Y |
| STATE: normal |

Pass Criteria:

  • Table created successfully
  • Data inserted and retrieved
  • ENCRYPTION field shows 'Y'

Test 3: Verify Data Encryption at Rest

# Verify no plaintext in datafile
strings /var/lib/mysql-data/mysql/test_tde.ibd | \
  grep -i "Sensitive-Data" || \
  echo "✓ No plaintext detected (encryption confirmed)"

Pass Criteria: No plaintext keywords found in binary file


Test 4: Convert Unencrypted Table to Encrypted

# Create a non-encrypted table with data
/usr/local/mysql/bin/mysql -u root -pYourPassword \
  --socket=/var/run/mysqld/mysqld.sock <<'EOF'
CREATE TABLE mysql.test_unencrypted (
  id INT PRIMARY KEY,
  secret VARCHAR(255)
) ENCRYPTION='N';

-- Insert test data
INSERT INTO mysql.test_unencrypted (id, secret) VALUES
  (1, 'Plaintext-Data-001'),
  (2, 'Plaintext-Data-002'),
  (3, 'Plaintext-Data-003');

-- Verify plaintext is visible in file (for reference)
SELECT * FROM mysql.test_unencrypted;
EOF

# Verify plaintext exists before encryption
strings /var/lib/mysql-data/mysql/test_unencrypted.ibd | \
  grep -i "Plaintext-Data" && echo "✓ Plaintext found (expected)"

# Now convert to encrypted
/usr/local/mysql/bin/mysql -u root -pYourPassword \
  --socket=/var/run/mysqld/mysqld.sock <<'EOF'
-- Convert table to encrypted (uses ALGORITHM=COPY internally)
ALTER TABLE mysql.test_unencrypted ENCRYPTION='Y';

-- Verify encryption is now active
SELECT * FROM INFORMATION_SCHEMA.INNODB_TABLESPACES
WHERE NAME = 'mysql/test_unencrypted';

-- Verify data is still accessible
SELECT * FROM mysql.test_unencrypted;
EOF

# Verify plaintext is now gone
strings /var/lib/mysql-data/mysql/test_unencrypted.ibd | \
  grep -i "Plaintext-Data" || \
  echo "✓ No plaintext found (encryption successful)"

# Restart MySQL to verify persistence
sudo systemctl stop mysqld
sleep 3

sudo -u mysql /usr/local/mysql/bin/mysqld \
  --defaults-file=/etc/my.cnf &

sleep 5

# Verify data survives restart
/usr/local/mysql/bin/mysql -u root -pYourPassword \
  --socket=/var/run/mysqld/mysqld.sock \
  -e "SELECT * FROM mysql.test_unencrypted;"

Pass Criteria:

  • Table encryption conversion succeeds
  • Data remains accessible before and after conversion
  • Plaintext data encrypted at rest
  • Data persists after MySQL restart

Test 5: Master Key Rotation

/usr/local/mysql/bin/mysql -u root -pYourPassword \
  --socket=/var/run/mysqld/mysqld.sock <<'EOF'
-- Rotate the master key
ALTER INSTANCE ROTATE INNODB MASTER KEY;

-- Verify table still accessible after rotation
SELECT COUNT(*) FROM mysql.test_tde;

-- Confirm data integrity
SELECT * FROM mysql.test_tde;
EOF

Expected Output:

Query OK, 0 rows affected (0.58 sec)

Pass Criteria:

  • Key rotation succeeds
  • Data remains accessible and intact

Test 6: Data Persistence After MySQL Restart

# Insert marker data
/usr/local/mysql/bin/mysql -u root -pYourPassword \
  --socket=/var/run/mysqld/mysqld.sock <<'EOF'
INSERT INTO mysql.test_tde (id, secret) VALUES (999, 'Persistence-Check');
SELECT COUNT(*) as row_count FROM mysql.test_tde;
EOF

# Restart MySQL
sudo systemctl stop mysqld
sleep 3

sudo -u mysql /usr/local/mysql/bin/mysqld \
  --defaults-file=/etc/my.cnf &

sleep 5

# Verify data survived
/usr/local/mysql/bin/mysql -u root -pYourPassword \
  --socket=/var/run/mysqld/mysqld.sock <<'EOF'
SELECT * FROM mysql.test_tde WHERE id = 999;
SELECT COUNT(*) as rows_after_restart FROM mysql.test_tde;
EOF

Expected Output:

| id  | secret             |
|-----|--------------------|
| 999 | Persistence-Check  |

rows_after_restart: (same as before restart)

Pass Criteria:

  • Data persists after MySQL restart
  • Master key successfully retrieved from KMS
  • Row count unchanged

Test 7: TLS Communication Verification

# Test direct TLS connection to KMS KMIP endpoint
timeout 5 openssl s_client -connect <kms-host>:5696 \
  -cert /usr/local/mysql/mysql-keyring-okv/ssl/cert.pem \
  -key /usr/local/mysql/mysql-keyring-okv/ssl/key.pem \
  -CAfile /usr/local/mysql/mysql-keyring-okv/ssl/CA.pem \
  < /dev/null 2>&1 | grep "Verify return code"

Expected Output:

Verify return code: 0 (ok)

Pass Criteria: TLS handshake succeeds with valid certificates


Advanced Resilience Testing

Resilience Test 1: KMS Unavailability with MySQL Running

Scenario: Brief KMS outage while MySQL is operational

# Step 1: Verify data accessible (KMS running)
/usr/local/mysql/bin/mysql -u root -pYourPassword \
  --socket=/var/run/mysqld/mysqld.sock \
  -e "SELECT COUNT(*) FROM mysql.test_tde;"

# Step 2: Stop KMS
systemctl stop cosmian_kms

# Step 3: Verify data still accessible (from cache)
/usr/local/mysql/bin/mysql -u root -pYourPassword \
  --socket=/var/run/mysqld/mysqld.sock \
  -e "SELECT COUNT(*) FROM mysql.test_tde;"
# ✓ Data accessible (master key in MySQL memory cache)

# Step 4: Restart KMS
systemctl start cosmian_kms

# Step 5: Verify access continues
/usr/local/mysql/bin/mysql -u root -pYourPassword \
  --socket=/var/run/mysqld/mysqld.sock \
  -e "SELECT * FROM mysql.test_tde LIMIT 1;"

Results:

✓ Data accessible while KMS down (cache hit)
✓ Data accessible after KMS restart
✓ No service interruption during brief outage

Resilience Test 2: KMS Unavailability with MySQL Restart

Scenario: KMS down when MySQL starts (critical test)

# Step 1: Verify KMS is stopped
ps aux | grep cosmian_kms | grep -v grep
# (should return nothing)

# Step 2: Restart MySQL (KMS OFFLINE)
sudo systemctl stop mysqld
sleep 3

sudo -u mysql /usr/local/mysql/bin/mysqld \
  --defaults-file=/etc/my.cnf &

sleep 5

# Step 3: Try to access encrypted data
/usr/local/mysql/bin/mysql -u root -pYourPassword \
  --socket=/var/run/mysqld/mysqld.sock \
  -e "SELECT * FROM mysql.test_tde;" 2>&1

Expected Error:

ERROR 3185 (HY000): Can't find master key from keyring,
please check in the server log if a keyring is loaded and initialized successfully.

Result:

✓ Security working correctly
✓ Encrypted data inaccessible without KMS
✓ MySQL started but cannot decrypt

Resilience Test 3: Recovery After KMS Outage + Restart

Scenario: Full recovery workflow after KMS was offline

# Step 1: KMS is still down, MySQL started with error above

# Step 2: Start KMS (v5.14+)
systemctl start cosmian_kms

# Step 3: MySQL still cannot access (cache was invalidated)
/usr/local/mysql/bin/mysql -u root -pYourPassword \
  --socket=/var/run/mysqld/mysqld.sock \
  -e "SELECT * FROM mysql.test_tde;" 2>&1
# Still ERROR 3185

# Step 4: Restart MySQL to reconnect to KMS
sudo systemctl stop mysqld
sleep 3

sudo -u mysql /usr/local/mysql/bin/mysqld \
  --defaults-file=/etc/my.cnf &

sleep 5

# Step 5: Data now accessible
/usr/local/mysql/bin/mysql -u root -pYourPassword \
  --socket=/var/run/mysqld/mysqld.sock \
  -e "SELECT * FROM mysql.test_tde;"

Results:

✓ KMS restart does NOT automatically recover cached state
✓ MySQL restart required to re-establish KMS connection
✓ Data fully recoverable with proper startup order

Lessons Learned:

  1. Keep KMS and MySQL in sync (start/stop in order)
  2. Monitor KMS availability separately
  3. Set up automated alerts for KMS downtime

TDE Benchmark Report (1,000,000 rows)

Execution Times

OperationEncrypted Table (TDE)Non-Encrypted TableTDE Overhead
Bulk INSERT12.11 s11.05 s+9%
Full scan SELECT (cold cache)2.06 s1.41 s+47%
Full scan SELECT (warm cache)2.26 s1.46 s+55%
Lookup SELECT (PK, warm)0.018 s0.015 s≈ 0%

Performance by Workload Type

Workload TypeTDE ImpactComments
OLTP (INSERT / PK lookup)🟢 LowOverhead negligible in production
Sequential scan🟠 ModerateCrypto cost visible, especially with warm cache
Analytical (aggregations)🔴 HigherCPU-bound, plan for extra CPU resources

Troubleshooting

Issue: "Can't find master key from keyring"

Symptom:

ERROR 3185 (HY000): Can't find master key from keyring,
please check in the server log if a keyring is loaded and initialized successfully.

Root Causes:

  1. Eviden KMS (v5.14+) is not running or unreachable
  2. Network connectivity issue between MySQL and KMS
  3. Certificate authentication failed
  4. Plugin failed to initialize

Solutions:

  1. Verify Eviden KMS (v5.14+) is running:

    ps aux | grep cosmian_kms | grep -v grep
    ss -lntp | grep 5696
    
  2. Check network connectivity:

    nc -zv <kms-host> 5696
    ping <kms-host>
    
  3. Verify certificate validity:

    openssl x509 -in /usr/local/mysql/mysql-keyring-okv/ssl/cert.pem -text -noout
    # Check NotBefore and NotAfter dates
    
  4. Check MySQL error log:

    tail -n 100 /var/log/mysql/mysqld.log | grep -i "keyring\|error\|tls"
    
  5. Verify okvclient.ora configuration:

    cat /usr/local/mysql/mysql-keyring-okv/okvclient.ora
    
  6. Verify MySQL Enterprise version has keyring_okv support:

    /usr/local/mysql/bin/mysql --version
    # Must be MySQL 8.0 Enterprise Edition or higher
    

Issue: "Encryption information can't be decrypted"

Symptom:

ERROR 12226: Encryption information in datafile: ./mysql/test_tde.ibd
can't be decrypted

Causes:

  • Table was created with a key that's no longer accessible
  • Datafile corruption
  • KMS unavailable during startup

Solutions:

  1. If test/development data:

    # Remove problematic datafile
    sudo mv /var/lib/mysql-data/mysql/test_tde.ibd \
            /var/lib/mysql-data/mysql/test_tde.ibd.backup
    
    # Restart MySQL
    sudo systemctl stop mysqld
    sleep 3
    sudo -u mysql /usr/local/mysql/bin/mysqld \
      --defaults-file=/etc/my.cnf &
    
    # Drop table and recreate
    /usr/local/mysql/bin/mysql -u root -pYourPassword \
      --socket=/var/run/mysqld/mysqld.sock \
      -e "DROP TABLE mysql.test_tde;"
    
  2. If production data, investigate:

    • Check KMS (v5.14+) availability during startup
    • Verify master key is accessible
    • Check certificate validity
    • Review MySQL error logs

Issue: Certificate Expiry

Symptom:

SSL: CERTIFICATE_VERIFY_FAILED

Solution:

# Check certificate expiry
openssl x509 -in /usr/local/mysql/mysql-keyring-okv/ssl/cert.pem \
  -noout -dates

# Generate new certificate before expiry
openssl genrsa -out mysql-client.key.new 4096
openssl req -new \
  -key mysql-client.key.new \
  -out mysql-client.csr.new \
  -subj "/CN=mysql-kmip-client/O=Lab/C=FR"

openssl x509 -req \
  -in mysql-client.csr.new \
  -CA ca.crt \
  -CAkey ca.key \
  -CAcreateserial \
  -out mysql-client.crt.new \
  -days 365 \
  -sha256

# Replace certificates (during maintenance window)
sudo cp mysql-client.crt.new \
     /usr/local/mysql/mysql-keyring-okv/ssl/cert.pem

sudo cp mysql-client.key.new \
     /usr/local/mysql/mysql-keyring-okv/ssl/key.pem

sudo chown mysql:mysql /usr/local/mysql/mysql-keyring-okv/ssl/cert.pem
sudo chown mysql:mysql /usr/local/mysql/mysql-keyring-okv/ssl/key.pem
sudo chmod 600 /usr/local/mysql/mysql-keyring-okv/ssl/key.pem

# Restart MySQL to use new certificates
sudo systemctl stop mysqld
sleep 3
sudo -u mysql /usr/local/mysql/bin/mysqld \
  --defaults-file=/etc/my.cnf &

References

Official Documentation

Key Files and Paths

ComponentPath
MySQL Binary/usr/local/mysql/bin/mysqld
MySQL Config/etc/my.cnf
Data Directory/var/lib/mysql-data/
Error Log/var/log/mysql/mysqld.log
Socket File/var/run/mysqld/mysqld.sock
KMIP Config/usr/local/mysql/mysql-keyring-okv/okvclient.ora
SSL Certificates/usr/local/mysql/mysql-keyring-okv/ssl/
Eviden Config/path/to/kms.toml
Eviden DB/path/to/cosmian-kms/sqlite-data/kms.db

Default Credentials (Lab Only)

UserPasswordScope
root (MySQL)Set during installationLocal MySQL
admin (Eviden)Configured in kms.tomlKMS REST API

SECURITY NOTE: Change all default credentials before production use.


Contact and Support

For issues related to:

Using pg_tde with Eviden KMS and PostgreSQL 17 (Percona)

This guide demonstrates how to configure PostgreSQL 17 with Percona's pg_tde extension to use Eviden KMS for transparent data encryption (TDE).

Overview

ItemDetails
ProtocolKMIP 1.4 over TCP/TLS with mutual certificate authentication
Port5696 (IANA-registered KMIP port)
Key typeAES-128 symmetric key
PostgreSQL versionPercona Server for PostgreSQL 17.x and above
Eviden KMS featureWorks with both FIPS and non-FIPS builds

Prerequisites

Before starting, ensure you have:


Before You Start: Understanding pg_tde Architecture

Global Architecture Overview

flowchart TB
    app["Application / SQL Queries<br/>(SELECT, INSERT, UPDATE, DELETE)"]
    plain["Data in plaintext in memory"]
    subgraph pg["PostgreSQL + pg_tde Extension + Percona Patches"]
        smgr["SMGR (Storage Manager) — Interception Layer"]
        tde["TDE Tables<br/>(USING tde_heap)"]
        nontde["Non-TDE Tables<br/>(standard heap)"]
        enc["Encryption of pages<br/>(CBC-128)"]
        noenc["No encryption"]
        walenc["Encryption (optional, WAL only)<br/>(CTR-128) WAL"]
        smgr --> tde & nontde
        tde --> enc
        nontde --> noenc & walenc
    end
    disk["Disk (encrypted data)<br/>WAL Files (encrypted if pg_tde.wal_encrypt = on)"]
    app --> plain --> smgr
    enc --> disk
    noenc --> disk
    walenc --> disk

KMIP Communication Flow

flowchart TB
    pg["PostgreSQL + pg_tde<br/>(KMIP Client)"]
    kms["Eviden KMS 5.6+<br/>(KMIP Server)<br/><br/>Supported operations:<br/>✓ Create · Get · Destroy · Register<br/>✓ Locate · Activate · Revoke<br/><br/>Protocol: KMIP 1.x and 2.x<br/>Profile: Baseline Server"]
    pg -->|"KMIP over TLS 1.2/1.3<br/>Port 5696 (binary)<br/>Required: client_cert.pem, client_key.pem, ca_cert.pem"| kms

Configuration Steps

1. Configure PostgreSQL

Edit your postgresql.conf file to activate the TDE extension:1

shared_preload_libraries = 'pg_tde,percona_pg_telemetry'

Important: Changes to shared_preload_libraries require a PostgreSQL restart to take effect.1

sudo systemctl restart postgresql@17-main.service

2. Enable TDE Extension

Create the pg_tde extension in your target database(s):1

CREATE EXTENSION pg_tde;

This will automatically create event triggers needed for pg_tde operation.

3. Configure the KMS Key Provider

Connect to your PostgreSQL database and add the Eviden KMS as a key provider using the KMIP protocol:1

SELECT pg_tde_add_global_key_provider_kmip(
  'kms_provider',                -- Provider name (can be customized)
  'kms-host.example.com',        -- Your KMS server hostname
  5696,                          -- KMIP port (default is 5696)
  '/path/to/client_cert.pem',    -- Client certificate file path
  '/path/to/client_key.pem',     -- Client private key file path
  '/path/to/ca_cert.pem'         -- Certificate Authority file path
);

Note: Replace the placeholder values with your actual KMS server details and certificate paths.1

Certificate Requirements:2

  • All certificates must be in PEM format
  • Client certificates must be X.509 compliant
  • TLS 1.2 or higher is required for KMIP communication
  • Certificate files must be readable by the PostgreSQL system user

4. Set the Default Encryption Key

Configure the default encryption key using the KMS provider:1

SELECT pg_tde_create_key_using_global_key_provider('key_01', 'kms_provider');

SELECT pg_tde_set_server_key_using_global_key_provider('key_01', 'kms_provider');

SELECT pg_tde_set_default_key_using_global_key_provider('key_01', 'kms_provider');

The first parameter (key_01) is the key identifier, and the second parameter (kms_provider) must match the provider name from step 3.1

What happens in this step:

  • pg_tde_create_key_using_global_key_provider() creates a Principal Key managed by Eviden KMS
  • pg_tde_set_server_key_using_global_key_provider() sets the server-level default key
  • pg_tde_set_default_key_using_global_key_provider() sets the database-level default key

5. Ensure Event Triggers Are Set

Event triggers are usually created automatically when the extension is installed. To verify or recreate them:1

CREATE EVENT TRIGGER pg_tde_ddl_start ON ddl_command_start
  EXECUTE FUNCTION pg_tde_ddl_command_start_capture();

CREATE EVENT TRIGGER pg_tde_ddl_end ON ddl_command_end
  EXECUTE FUNCTION pg_tde_ddl_command_end_capture();

If the trigger already exists, this error can be safely ignored.1

These triggers ensure that DDL operations (table creation, modification) properly handle encryption metadata.

6. Enable WAL Encrypt and TDE Enforce Encryption

Edit your postgresql.conf again and add:1

pg_tde.wal_encrypt = on
pg_tde.enforce_encryption = on

Important: Changes to pg_tde.wal_encrypt or pg_tde.enforce_encryption require a PostgreSQL restart to take effect.1

sudo systemctl restart postgresql@17-main.service

About these parameters:1

  • pg_tde.wal_encrypt = on encrypts Write-Ahead Log files (production-ready as of Percona PostgreSQL 17.5.3)
  • pg_tde.enforce_encryption = on prevents creation of unencrypted tables when a default key is set (strongly recommended)

7. Create Encrypted Tables

CREATE TABLE sensitive_data (
  id serial PRIMARY KEY,
  secret_text text
) USING tde_heap;

The USING tde_heap clause ensures the table is encrypted using pg_tde.1

Important: Only tables created with USING tde_heap are encrypted. Existing non-TDE tables remain unencrypted unless migrated.

8. Verify if a Table is Encrypted

SELECT pg_tde_is_encrypted('public.sensitive_data'::regclass);
-- Expected output: t (true)

9. Insert and Query Data Transparently

Encryption is transparent; use standard SQL commands:1

INSERT INTO sensitive_data (secret_text) VALUES ('Top secret info');

SELECT * FROM sensitive_data;

Data is stored encrypted on disk but returned in plaintext when queried.1

10. Check Current Encryption Settings

SHOW pg_tde.wal_encrypt;
SHOW pg_tde.enforce_encryption;
SHOW pg_tde.inherit_global_providers;

Encryption Scope and What Gets Encrypted

✅ WHAT IS ENCRYPTED

Application Data

ComponentStatusDetails
Tables USING tde_heap✓ EncryptedComplete row data, all columns3
Index on TDE tables✓ EncryptedB-trees, Hash, GiST, GIN, BRIN, etc.3
TOAST tables✓ EncryptedCompressed/out-of-page data e.g. long TEXT
Sequences (TDE tables)✓ EncryptedRelated to encrypted tables3
Temporary tables (TDE)✓ EncryptedTemporary tables for TDE data operations3

Logs and Transactions

ComponentWith pg_tde.wal_encrypt = onDetails
WAL (Write-Ahead Log)✓ EncryptedTransaction logs GA status since v17.5.3
WAL before images✓ EncryptedRow states before modification4
WAL after images✓ EncryptedRow states after modification4

Backup and Recovery

ToolStatusNotes
pg_tde_basebackup✓ SupportedWith --wal-method=stream or --wal-method=none1
pgBackRest✓ SupportedCompatible with encrypted WAL4
WAL restore✓ SupportedVia pg_tde_restore_encrypt wrapper1

❌ WHAT IS NOT ENCRYPTED

System Metadata and Catalogs

ComponentReasonConsequence
PostgreSQL system catalogsArchitecturalTable/column names, types remain plaintext
TDE table metadataArchitecturalSchema, table name, column name, data types
Statistics (pg_stat_*)Not supportedSystem statistics information
Configuration filesNot encryptedpostgresql.conf, pg_hba.conf

Tables and Files

ComponentReasonSolution
Standard heap tablesBy designOnly tde_heap tables are encrypted
Non-TDE tablesSelectiveCreate with USING tde_heap for encryption
Temporary files (>work_mem)LimitationOverflow data unencrypted on disk[5]
System log filesNot supportedPostgreSQL logs in plaintext on disk

Key Management: DEK, Internal Keys and Principal Keys

Two-Level Key Architecture

pg_tde uses a two-level key hierarchy for data encryption:3

flowchart TB
    principal["PRINCIPAL KEY (Master Key)<br/><br/>Stored externally in Eviden KMS via KMIP<br/>ONE per database<br/>Encrypts Internal Keys (AES-128-GCM)<br/>Accessible only via TLS KMIP connection<br/><br/>Created with pg_tde_create_key_using_global_key_provider()"]
    internal["INTERNAL KEYS (Data Encryption Keys / DEK)<br/><br/>Stored locally in $PGDATA/pg_tde/<br/>Encrypted by Principal Key<br/>ONE unique key per relation (OID)<br/><br/>Tables: AES-128-CBC · WAL: AES-128-CTR · Keys: AES-128-GCM"]
    data["ENCRYPTED DATA (User Data)<br/><br/>Table pages stored encrypted on disk<br/>Index pages stored encrypted<br/>WAL data encrypted (if pg_tde.wal_encrypt = on)"]
    principal -->|"Encrypts via KMIP wrap (AES-128-GCM)"| internal
    internal -->|"Encrypt (AES-128-CBC / CTR)"| data

Internal Keys (DEK) Details

Generation and Storage

AspectDetail
GenerationAutomatic when CREATE TABLE ... USING tde_heap3
IdentifierUnique OID (Object Identifier) per relation3
Location$PGDATA/pg_tde/<database_oid>/3
File<relation_oid>.key binary, encrypted
VisibilityNot readable directly without Principal Key3
RotationVia VACUUM FULL, ALTER TABLE SET ACCESS METHOD, or CREATE TABLE AS SELECT3

Disk Structure Example

$PGDATA/pg_tde/
├── global/                          # Global section
│   ├── provider_config              # Global provider configuration
│   └── server_key.key               # WAL server key (if enabled)
│
└── 16384/                           # Database OID (example)
    ├── provider_config              # Database provider configuration
    ├── 16385.key                    # Internal key for table OID=16385
    ├── 16386.key                    # Internal key for index OID=16386
    ├── 16387.key                    # Internal key for index OID=16387
    ├── 16388.key                    # Internal key for TOAST OID=16388
    └── 16389.key                    # Internal key for sequence OID=16389

Security Considerations

AspectDetailRecommendation
File PermissionsInherited from $PGDATA pg:pg 700✓ Good, ensure root cannot read
Backup ProtectionDEK files copied with backup remain encrypted✓ Safe for off-site storage
RAM CachePrincipal Key and DEKs decrypted in RAM3⚠️ Protect with: lock_memory, disable core dumps
Swap MemoryKeys can be paged to swap3⚠️ Use encrypted swap (dm-crypt, zswap)
# 1. Disable core dumps
echo "kernel.core_pattern = /dev/null" >> /etc/sysctl.conf
sysctl -p

# 2. Encrypt swap (optional but recommended)
# Use dm-crypt or zswap

# 3. Lock PostgreSQL memory (optional but recommended)
ulimit -l unlimited
# or in postgresql.conf:
# lock_memory = true

# 4. Secure KMS certificates
sudo chmod 400 /path/to/client_cert.pem
sudo chmod 400 /path/to/client_key.pem
sudo chown postgres:postgres /path/to/*.pem

Key Monitoring

Verify Current Keys

-- Check server default Principal Key
SELECT pg_tde_server_key_info();
-- Result: (key_name, provider_name)

-- Check current database Principal Key
SELECT pg_tde_key_info();

-- Check default Principal Key (if used)
SELECT pg_tde_default_key_info();

-- List all configured providers
SELECT * FROM pg_tde_list_all_global_key_providers();
SELECT * FROM pg_tde_list_all_database_key_providers();

Verify KMS Connectivity

-- Test Principal Key availability
SELECT pg_tde_verify_key();
SELECT pg_tde_verify_server_key();
SELECT pg_tde_verify_default_key();

-- If these fail: Check PostgreSQL logs for KMIP errors
-- tail -f $PGDATA/log/postgresql.log | grep -i kmip

Verification and Testing

Initial Setup Verification

-- 1. Verify extension is loaded
SELECT * FROM pg_extension WHERE extname = 'pg_tde';

-- 2. Verify configuration parameters
SHOW shared_preload_libraries;
SHOW pg_tde.wal_encrypt;
SHOW pg_tde.enforce_encryption;

-- 3. Verify KMS provider is configured
SELECT * FROM pg_tde_list_all_global_key_providers();

-- 4. Verify keys are set
SELECT pg_tde_server_key_info();
SELECT pg_tde_default_key_info();

-- 5. Verify KMS connectivity
SELECT pg_tde_verify_key();
SELECT pg_tde_verify_server_key();

-- 6. Create test table
CREATE TABLE test_encrypted (
  id serial PRIMARY KEY,
  data text
) USING tde_heap;

-- 7. Verify it's encrypted
SELECT pg_tde_is_encrypted('public.test_encrypted'::regclass);
-- Expected output: t (true)

-- 8. Test data insertion and retrieval
INSERT INTO test_encrypted (data) VALUES ('Test data');
SELECT * FROM test_encrypted;
-- Data should be returned in plaintext

WAL Encryption Verification

-- Check if WAL encryption is enabled
SHOW pg_tde.wal_encrypt;

-- Verify encryption is active
SELECT pg_tde_is_wal_encrypted();

-- Check WAL files (encrypted WAL segments have standard naming)
SELECT name FROM pg_ls_waldir()
ORDER BY name DESC LIMIT 5;

Troubleshooting & Solutions

Common Issues and Diagnostic Flow

1. PostgreSQL Fails to Start

ERROR: could not load shared library "pg_tde"
or
ERROR: could not connect to KMIP server

Diagnostic steps:

# 1. Check if extension is compiled correctly
ls -la $PGINSTALL/lib/pg_tde.so

# 2. Check PostgreSQL logs
tail -f $PGDATA/log/postgresql.log

# 3. Verify KMS is reachable
telnet <kms-host> 5696

# 4. Check certificate paths
ls -la /path/to/*.pem
file /path/to/client_cert.pem

SQL Verification:

SELECT pg_tde_verify_key();
SELECT pg_tde_verify_server_key();
SELECT * FROM pg_tde_list_all_global_key_providers();

Solutions:

  • Verify shared_preload_libraries contains pg_tde
  • Restart PostgreSQL after configuration changes
  • Ensure Eviden KMS is running: telnet <kms-host> 5696
  • Check firewall/network between PostgreSQL and KMS

2. TLS Certificate Verification Failed

ERROR: SSL/TLS certificate verification failed
DETAIL: certificate verify failed / self signed certificate

Diagnostic:

# Verify certificate details
openssl x509 -in /path/to/ca_cert.pem -text -noout

# Check certificate expiration
openssl x509 -in /path/to/client_cert.pem -noout -dates

# Verify certificate chain
openssl verify -CAfile /path/to/ca_cert.pem /path/to/client_cert.pem

# Test KMIP connection manually (if PyKMIP available)
python3 -m kmip.demos.client -b /path/to/client_cert.pem \
  -k /path/to/client_key.pem \
  -ca /path/to/ca_cert.pem \
  --server <kms-host> --port 5696

Solutions:

  • Verify certificate files exist and are readable: ls -la /path/to/*.pem
  • Check certificate expiration dates
  • Verify CA certificate chain is complete
  • Ensure certificate paths in pg_tde_add_global_key_provider_kmip() are correct

3. Key Not Found or Access Denied

ERROR: Failed to retrieve principal key 'key_01' from KMS provider 'kms_provider'
DETAIL: Key not found / Access denied

Diagnostic:

-- Verify KMS connectivity
SELECT pg_tde_verify_key();
SELECT pg_tde_verify_server_key();

-- Check configured keys
SELECT pg_tde_key_info();
SELECT pg_tde_server_key_info();

-- Check provider configuration
SELECT * FROM pg_tde_list_all_global_key_providers();

Solutions:

  • Verify the key exists on Eviden KMS
  • Verify the key name matches exactly (case-sensitive)
  • Check KMS user/role has permissions to access the key
  • Verify database OID if using database-level keys: SELECT datoid FROM pg_database WHERE datname = current_database();
  • Check $PGDATA/pg_tde/ directory permissions

4. Performance Degradation After Enabling TDE

Problem: Slower queries, high CPU usage

Diagnostic:

-- Check cache hit ratio
SELECT sum(heap_blks_read) / (sum(heap_blks_read) +
        sum(heap_blks_hit)) AS cache_hit_ratio
FROM pg_stat_user_tables;

-- Check query plans
EXPLAIN ANALYZE SELECT * FROM sensitive_data LIMIT 1000;

Solutions:

  • Increase shared_buffers to reduce disk I/O

  • Check CPU supports AES-NI (hardware acceleration):

    grep -o 'aes' /proc/cpuinfo | head -1
    
  • Monitor I/O performance with iostat -x 1

  • Note: Percona reports ~10% overhead in most cases4

Certificate Management Best Practices

# 1. Organize certificate files
mkdir -p /etc/postgresql/kmip-certs
sudo cp /path/to/*.pem /etc/postgresql/kmip-certs/
sudo chmod 400 /etc/postgresql/kmip-certs/*.pem
sudo chown postgres:postgres /etc/postgresql/kmip-certs/

# 2. Set correct paths in pg_tde configuration
SELECT pg_tde_add_global_key_provider_kmip(
  'kms_provider',
  'kms-host.example.com',
  5696,
  '/etc/postgresql/kmip-certs/client_cert.pem',
  '/etc/postgresql/kmip-certs/client_key.pem',
  '/etc/postgresql/kmip-certs/ca_cert.pem'
);

# 3. Monitor certificate expiration
openssl x509 -in /etc/postgresql/kmip-certs/client_cert.pem -noout -dates | \
  grep notAfter

# 4. Plan certificate rotation before expiration
# Test with new certificates before cutover

Operational Considerations

Migration from Non-TDE to TDE Tables

flowchart TB
    start["Migration: non-TDE → TDE table"]
    m1["Method 1: CREATE TABLE AS"]
    m2["Method 2: ALTER TABLE"]
    m1a["CREATE TABLE t_new USING tde_heap<br/>AS SELECT * FROM t_old"]
    m2a["ALTER TABLE t_old<br/>SET ACCESS METHOD tde_heap"]
    m1b["DROP TABLE t_old<br/>ALTER TABLE t_new RENAME TO t_old"]
    m2b["Recreates index, constraints, foreign keys"]
    done["✓ TDE table created (exclusive lock)"]
    start --> m1 & m2
    m1 --> m1a --> m1b --> done
    m2 --> m2a --> m2b --> done

Impact:

  • Exclusive lock on table during migration
  • Complete data rewrite
  • Time proportional to table size
  • Disk usage x2 during operation

Recommendation: Perform during maintenance window on production systems.

Key Rotation

Principal Key Rotation

-- 1. Create new key on Eviden KMS
SELECT pg_tde_create_key_using_global_key_provider(
  'key_02', 'kms_provider'
);

-- 2. Switch to new key
-- (Re-encrypts all internal keys)
SELECT pg_tde_set_default_key_using_global_key_provider(
  'key_02', 'kms_provider'
);

-- 3. Verify rotation is complete
SELECT pg_tde_default_key_info();

-- 4. Old key can be archived/destroyed after verification
-- (Eviden KMS: key revoke/destroy operations)

Notes:

  • Internal keys are re-encrypted (non-blocking operation)
  • Old key retained for recovery purposes
  • Does not re-encrypt user data (only wraps internal keys)
  • Minimal performance impact

Internal Key Rotation (Existing Tables)

No direct in-place internal key rotation. Use one of these workarounds:

-- Method 1: VACUUM FULL (simpler but locks table)
VACUUM FULL sensitive_data;
-- Note: Table remains in memory during operation

-- Method 2: CREATE TABLE AS (more controlled)
CREATE TABLE sensitive_data_new USING tde_heap AS
  SELECT * FROM sensitive_data;

-- Recreate indexes
CREATE INDEX idx_sensitive_data_id ON sensitive_data_new(id);

-- Swap tables
DROP TABLE sensitive_data;
ALTER TABLE sensitive_data_new RENAME TO sensitive_data;

-- Verify new encryption
SELECT pg_tde_is_encrypted('public.sensitive_data'::regclass);

Performance Comparison:

MethodLock DurationDisk I/ODowntime
VACUUM FULLFull tableLowMinimal
CREATE TABLE ASFull tableHigh (copy all)Moderate

Backup and Recovery

Physical Backup with Encrypted WAL

# Full backup with streaming WAL
pg_tde_basebackup -D /path/to/backup \
  --wal-method=stream \
  -R \
  -v

# Or with archived WAL (requires WAL archiving configured)
pg_tde_basebackup -D /path/to/backup \
  --wal-method=none \
  -R

# WAL archiving setup (in postgresql.conf)
archive_mode = on
archive_command = 'pg_tde_archive_decrypt %f %p | pgbackrest archive-push %p'

# WAL restore setup (in recovery.conf or postgresql.conf)
restore_command = 'pgbackrest archive-get %f %p'

Recovery Point Objective (RPO)

flowchart TB
    base["pg_tde_basebackup (baseline)"]
    wal["WAL segments (archived via archive_command)"]
    pitr["Allows PITR up to last WAL segment"]
    rpo["RPO = 1 WAL segment (16 MB by default)<br/>TO = Time to replay WAL"]
    base --> wal --> pitr --> rpo

Failover and Standby Setup

Standby Configuration with Patroni

# patroni.yml for pg_tde with Eviden KMS
postgresql:
  # Use pg_tde_rewind, NOT standard pg_rewind if WAL encrypted
  pg_rewind: pg_tde_rewind

  parameters:
    shared_preload_libraries: pg_tde
    pg_tde.wal_encrypt: on
    pg_tde.enforce_encryption: on
    # Archive settings
    archive_mode: on
    archive_command: "pg_tde_archive_decrypt %f %p | pgbackrest archive-push %p"
    restore_command: "pgbackrest archive-get %f %p"

Failover Considerations

AspectImpactSolution
KMS AvailabilityCriticalPrimary and standby must both access KMS
CertificatesCriticalDistribute certificates to all replicas
Principal KeyCriticalKey must be accessible during recovery
Encrypted WALHighUse pg_tde_rewind not standard pg_rewind[5]

Important: If using encrypted WAL, pg_rewind is incompatible. Use pg_tde_rewind instead.[5]

Eviden KMS Compatibility

Supported features with pg_tde:2

  • KMIP 1.x and 2.x protocols
  • Baseline Server profile (fully compliant)
  • AES-128-CBC, AES-128-CTR, AES-128-GCM algorithms
  • Key creation, retrieval, destruction, rotation
  • TLS 1.2+ for secure communication

Backup and Replication Tools Compatibility:

ToolWith WAL EncryptionNotes
pg_tde_basebackup✓ Supported1Recommended tool
pgBackRest✓ Supported4Production-ready
Patroni✓ Supported4Use pg_tde_rewind
pg_rewind✗ Incompatible[5]Use pg_tde_rewind instead
pg_createsubscriber✗ Incompatible[5]Create subscriber manually
pg_receivewal✗ Incompatible[5]Use pgBackRest or Patroni
Barman✗ Incompatible[5]Use pgBackRest instead

Ongoing Maintenance

  1. Monitor KMS connectivity:

    SELECT pg_tde_verify_key();
    
  2. Plan key rotation:

    • Principal keys: Via Eviden KMS management
    • Internal keys: Via table recreation (VACUUM FULL or ALTER TABLE SET ACCESS METHOD)
  3. Backup strategy:

    • Use pg_tde_basebackup or pgBackRest
    • Archive encrypted WAL files
    • Test recovery procedures regularly
  4. Certificate rotation:

    • Plan before expiration
    • Test with new certificates before cutover

References


Eviden KMS integrates with Microsoft SQL Server External key management using a Windows DLL implementing the SQL Server Extensible Key Management (EKM).

The Windows DLL is a SQL Server EKM provider that forwards key operations to the Eviden KMS over mutual TLS. This allows SQL Server features like column-level encryption and Transparent Data Encryption (TDE) to use keys managed by the Eviden KMS, without exposing key material to the SQL Server host.

The Windows DLL is available in a separate project on GitHub; pre-built signed DLLs are available for download.

Deployment Guide

This document explains how to install and configure the Cosmian EKM SQL Server provider, starting from a signed DLL. Follow the steps in order.


Prerequisites

RequirementNotes
Windows Server 2016+ or Windows 10/11x64
SQL Server 2016+ (any edition) with EKM supportStandard or Enterprise; Express does not support EKM
Eviden KMS 5.xMust run on the same machine (or be network-reachable)
Signed cosmian_ekm_sql_server.dllSee the KMS repository for build instructions
PowerShell 5.1 (elevated)For the setup scripts
sqlcmd on the system PATHPart of SQL Server Tools or installable standalone

1. Install SQL Server

Install SQL Server with default settings. The instance name used throughout this guide is the default instance (MSSQLSERVER). For a named instance, substitute MSSQL$<InstanceName> wherever a service name is needed.

Ensure the SQL Server Database Engine service account is noted (default: NT SERVICE\MSSQLSERVER).


2. Start the Eviden KMS

The KMS must be running and accessible before SQL Server loads the EKM provider.

# Example — adjust paths and config file as needed
cosmian_kms_server.exe --config kms_server.toml

The KMS must be configured with:

  • TLS certificate trusted by this machine (or accept_invalid_certs = true in config.toml during development)
  • At least one client certificate entry matching the SQL Server credential identity

Verify the KMS is reachable:

Test-NetConnection -ComputerName localhost -Port 9998

3. Prepare the machine (one-time)

# Creates directories and grants ACLs to the SQL Server service account
scripts\Initialize-EkmEnvironment.ps1

# Creates the Authenticode code-signing certificate (if not already present)
scripts\New-EkmCertificate.ps1

These are one-time operations per machine.


4. Install the provider configuration

Create C:\ProgramData\Cosmian\EKM\config.toml (see config.toml.example for the full schema):

max_age_seconds = 1800
stale_collector_period_seconds = 120

[kms]
server_url = "https://localhost:9998"
accept_invalid_certs = false          # set true only for local dev

[[kms.certificates]]
username = "admin"
client_cert = 'C:\ProgramData\Cosmian\EKM\admin.cert.pem'
client_key  = 'C:\ProgramData\Cosmian\EKM\admin.key.pem'

Copy the mTLS client certificate and key files to the paths referenced above, and copy the KMS CA certificate so TLS verification succeeds.


5. Deploy the DLL

# Must be run as Administrator
scripts\Deploy-EkmDll.ps1

This script:

  1. Verifies the DLL's Authenticode signature.
  2. Stops the MSSQLSERVER service.
  3. Copies the DLL to C:\Program Files\Cosmian\EKM\cosmian_ekm_sql_server.dll.
  4. Starts the MSSQLSERVER service.
  5. Registers (or re-registers) CosmianEKM as a cryptographic provider.

To deploy to a named instance or skip the service restart:

scripts\Deploy-EkmDll.ps1 -SqlServiceName "MSSQL`$MyInstance"
scripts\Deploy-EkmDll.ps1 -SkipServiceRestart   # register only

6. Enable EKM in SQL Server

Connect to SQL Server (e.g. via SSMS or sqlcmd) and run:

EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;

EXEC sp_configure 'EKM provider enabled', 1;
RECONFIGURE;

7. Register the provider

If Deploy-EkmDll.ps1 was run with -SkipRegistration, register manually:

CREATE CRYPTOGRAPHIC PROVIDER CosmianEKM
FROM FILE = 'C:\Program Files\Cosmian\EKM\cosmian_ekm_sql_server.dll';

Verify:

SELECT name, is_enabled FROM sys.cryptographic_providers WHERE name = 'CosmianEKM';

8. Create a credential and map it to a login

Each SQL Server login that uses EKM keys must have a credential that maps to a KMS identity (the username in config.toml).

-- Create the credential
CREATE CREDENTIAL Cosmian_EKM_Cred
WITH IDENTITY = 'admin',      -- must match [[kms.certificates]] username
     SECRET   = 'unused'
FOR CRYPTOGRAPHIC PROVIDER CosmianEKM;

-- Map to a Windows login (adjust the login name)
ALTER LOGIN [HOSTNAME\YourLogin] ADD CREDENTIAL Cosmian_EKM_Cred;

9. Create keys

USE master;

-- Asymmetric key (RSA 2048)
CREATE ASYMMETRIC KEY MyRsaKey
FROM PROVIDER CosmianEKM
WITH ALGORITHM = RSA_2048,
     PROVIDER_KEY_NAME = 'my-rsa-key',
     CREATION_DISPOSITION = CREATE_NEW;

-- Symmetric key (AES 256)
CREATE SYMMETRIC KEY MyAesKey
FROM PROVIDER CosmianEKM
WITH ALGORITHM = AES_256,
     PROVIDER_KEY_NAME = 'my-aes-key',
     CREATION_DISPOSITION = CREATE_NEW;

10. Column-level encryption (quick check)

USE master;

-- Symmetric key protected by the EKM asymmetric key
CREATE SYMMETRIC KEY ColKey
WITH ALGORITHM = AES_256
ENCRYPTION BY ASYMMETRIC KEY MyRsaKey;

-- Encrypt
OPEN SYMMETRIC KEY ColKey DECRYPTION BY ASYMMETRIC KEY MyRsaKey;
SELECT ENCRYPTBYKEY(KEY_GUID('ColKey'), N'Hello EKM!');
CLOSE SYMMETRIC KEY ColKey;

11. Transparent Data Encryption (TDE)

-- Create a login backed by the EKM asymmetric key
CREATE LOGIN Cosmian_TDE_Login FROM ASYMMETRIC KEY MyRsaKey;

-- Credential for unattended access
CREATE CREDENTIAL Cosmian_TDE_Cred
WITH IDENTITY = 'admin', SECRET = 'unused'
FOR CRYPTOGRAPHIC PROVIDER CosmianEKM;
ALTER LOGIN Cosmian_TDE_Login ADD CREDENTIAL Cosmian_TDE_Cred;

-- Enable TDE on a database
USE MyDatabase;
CREATE DATABASE ENCRYPTION KEY
WITH ALGORITHM = AES_256
ENCRYPTION BY SERVER ASYMMETRIC KEY MyRsaKey;

ALTER DATABASE MyDatabase SET ENCRYPTION ON;

Monitor encryption progress:

SELECT db.name, dek.encryption_state, dek.percent_complete
FROM sys.dm_database_encryption_keys dek
JOIN sys.databases db ON dek.database_id = db.database_id;

State 3 means fully encrypted.


12. Run integration tests

# All-in-one: build + sign + deploy + copy test config
scripts\Prepare-Integration-Tests.ps1

# Then run the tests
cargo test --test integration -- --test-threads=1

Troubleshooting

Provider not loading

Check the EKM log file:

C:\ProgramData\Cosmian\EKM\logs\cosmian_ekm.log.<date>

"Cannot open database / invalid object name"

The SQL Server service account needs Modify rights on C:\ProgramData\Cosmian\EKM\. Re-run Initialize-EkmEnvironment.ps1.

KMS connection errors

  • Verify config.toml has the correct server_url.
  • Confirm the client certificate CN or SAN matches the SQL credential identity.
  • Check the KMS server is running: Test-NetConnection localhost -Port 9998.

Key operations fail after redeployment

If the DLL is replaced, SQL Server must be restarted to reload it. Run Deploy-EkmDll.ps1 (which handles the restart automatically).

Oracle Database Transparent Data Encryption (TDE)

Oracle Database Transparent Data Encryption (TDE) enables automatic encryption of data at rest in Oracle databases. Users can execute SQL queries normally while TDE handles encryption transparently in the background. Encryption keys are stored directly in the database but can be encrypted using Oracle Key Vault or directly with Hardware Security Modules (HSM) via PKCS#11.

Eviden provides two deployment modes for Oracle TDE integration:

  1. Oracle Key Vault + HSM Mode: Uses Oracle Key Vault as an intermediary with HSM as Root-of-Trust
  2. Direct HSM Mode: Direct communication between Oracle Database and HSM via PKCS#11 interface

Mode 1: Oracle Key Vault + HSM Integration

Oracle Key Vault centralizes encryption key management, offering secure storage and distribution for Oracle databases and enterprise applications. It uses wallets to the crucial TDE master key which acts as the Key-Encryption-Key (KEK) for TDE. The master key is stored in a wallet that is protected by a password. This wallet provides a secure and centralized location for managing encryption keys.

For enhanced security, Hardware Security Modules (HSM) can be integrated with Oracle Key Vault to provide additional protection for these wallets. This configuration establishes a Root-of-Trust (RoT): when an HSM is deployed with Oracle Key Vault, the RoT remains in the HSM. The HSM RoT protects the Transparent Data Encryption (TDE) wallet password, which protects the TDE master key, which in turn protects all the encryption keys, certificates, and other security artifacts managed by the Oracle Key Vault server. Note that the HSM in this RoT usage scenario does not store any customer encryption keys. The customer keys are stored and managed directly by the Oracle Key Vault server.

Using HSM as a RoT is intended to mitigate attempts to recover keys from an Oracle Key Vault server which has been started in an unauthorized environment. Physical loss of an Oracle Key Vault server from a facility is one example of such a scenario.

When an Oracle Key Vault server is HSM-enabled, Oracle Key Vault contacts the HSM every five minutes (or whatever you have set the monitoring interval to) to ensure that the Root of Trust key is available and the TDE wallet password can be decrypted.

What Eviden provides is:

  • a HSM client: this is a PKCS#11 provider library that make the Oracle Key Vault a HSM client itself. It enables the Root-of-Trust by protecting the Oracle Key Vault wallets passwords. That library also provides a KMS client to communicate with the KMS server.
  • a KMS server that is interrogated by the KMS client. The KMS server can either front a HSM or act as a HSM but deployed in a secure environment.
graph TD
    subgraph oracle_db[Oracle Database]
        okvclient[okvclient.jar]
        dek[Encryption Keys
        encrypted by
        TDE Master Key]
    end
    okvclient -- OKV endpoint --> OKV
    subgraph OKV[Oracle Key Vault]
        subgraph hsm_client[Eviden HSM client]
            kms_client[Eviden KMS client]
            pkcs11_lib[libcosmian_pkcs11.so]
        end
        subgraph wallet[Wallet protected by HSM]
            tde[TDE Master Key]
        end
        tde --> hsm_client
    end
    kms_client -- REST API --> KMS[Eviden KMS Server]
    KMS --> HSM
    subgraph HSM[HSM]
        kek[Wallet Encryption Key]
    end

Oracle Key Vault Configuration

Before configuring a HSM such as described in Oracle Key Vault, some steps are needed:

For Oracle Database OS, the PKCS#11 library is available here: cosmian-pkcs11.

  • Extract the package:

    unzip cosmian-pkcs11-non-fips-static-openssl_5.25.0_linux-amd64.zip
    
  • Copy the PKCS#11 provider library to the Oracle Key Vault server to /usr/local/okv/hsm/generic/libcosmian_pkcs11.so

  • Copy the configuration of the PKCS#11 provider library to /usr/local/okv/hsm/generic/ckms.toml

  • Override the OKV generic HSM configuration files:

    • /usr/local/okv/hsm/generic/okv_hsm_env
    COSMIAN_PKCS11_LOGGING_LEVEL="trace"
    CKMS_CONF="/usr/local/okv/hsm/generic/ckms.toml"
    COSMIAN_PKCS11_LOGGING_FOLDER="/var/okv/log/hsm"
    
    • /usr/local/okv/hsm/generic/okv_hsm_conf
    # Oracle Key Vault HSM vendor configuration file
    # Lines must be shorter than 4096 characters.
    
    # The vendor name, to be displayed on the HSM page on the management console.
    VENDOR_NAME="Cosmian"
    
    # The location of the PKCS#11 library. This file must be preserved on upgrade.
    PKCS11_LIB_LOC="/usr/local/okv/hsm/generic/libcosmian_pkcs11.so"
    
    # A colon-separated list of the full paths of files and directories that must
    # be preserved on upgrade. All of these files and directories should have been
    # created by the HSM client software setup; none should have existed on Oracle
    # Key Vault by default. These will be necessary when upgrading to a version
    # of Oracle Key Vault that is running on a higher major OS version.
    # Do not use wildcards.
    PRESERVED_FILES=""
    
  • At this point, the symmetric key labeled OKV 18.1 HSM Root Key has been created in KMS server by Oracle Key Vault.

  • Then you can follow the official HSM-Enabling in a Standalone Oracle Key Vault Deployment.

Mode 2: Direct HSM Integration

For simplified deployments or environments where Oracle Key Vault is not available, Oracle Database can communicate directly with HSM via PKCS#11. In this mode, the Eviden PKCS#11 library (libcosmian_pkcs11.so) provides direct access to the Eviden KMS server, which manages the TDE master keys in the HSM.

This approach eliminates Oracle Key Vault from the architecture, reducing complexity while maintaining the security benefits of HSM-protected keys.

graph TD
    subgraph oracle_server[Oracle Database Server]
        subgraph oracle_db[Oracle Database]
            tde_engine[TDE Engine]
            dek[Data Encryption Keys
            encrypted by
            TDE Master Key]
        end
        pkcs11[Eviden PKCS#11 Library
        libcosmian_pkcs11.so]
    end
    tde_engine -- PKCS#11 --> pkcs11
    pkcs11 -- REST API --> KMS[Eviden KMS Server]
    KMS --> HSM
    subgraph HSM[HSM]
        master_key[TDE Master Key]
    end

Direct HSM Configuration

Linux

  1. Install Eviden PKCS#11 Library

    For Oracle Database OS, the PKCS#11 library is available here: cosmian-pkcs11.

    # Extract library from PKCS#11 ZIP package.
    unzip cosmian-pkcs11-non-fips-static-openssl_5.25.0_linux-amd64.zip
    
    # Copy to Oracle's HSM directory
    mkdir -p /opt/oracle/extapi/64/hsm/Cosmian/
    cp libcosmian_pkcs11.so /opt/oracle/extapi/64/hsm/Cosmian/
    chown oracle:oinstall /opt/oracle/extapi/64/hsm/Cosmian/libcosmian_pkcs11.so
    
  2. Configure Eviden PKCS#11 Library

    Create the configuration file /home/oracle/.cosmian/ckms.toml:

    [http_config]
    server_url = "http://kms:9998"
    

    Set proper ownership:

    mkdir -p /home/oracle/.cosmian/
    chown oracle:oinstall /home/oracle/.cosmian/ckms.toml
    
  3. Prepare Oracle Directory Structure

    # Create keystore directories
    mkdir -p /etc/ORACLE/KEYSTORES/FREE
    chown -R oracle:oinstall /etc/ORACLE/KEYSTORES/FREE
    
    # Setup logging
    chown -R oracle:oinstall /var/log
    
  4. Configure Oracle Database for PKCS#11

    Set up TDE to use the HSM via PKCS#11:

    -- Set WALLET_ROOT to point to the PKCS#11 library
    ALTER SYSTEM SET WALLET_ROOT='/opt/oracle/extapi/64/hsm/Cosmian/libcosmian_pkcs11.so' SCOPE=SPFILE;
    SHUTDOWN IMMEDIATE;
    STARTUP;
    
    -- Configure TDE to use HSM keystore
    ALTER SYSTEM SET TDE_CONFIGURATION='KEYSTORE_CONFIGURATION=HSM' SCOPE=BOTH SID='*';
    SHUTDOWN IMMEDIATE;
    STARTUP;
    
  5. Create and Configure HSM Keystore

    -- Open the HSM keystore
    ADMINISTER KEY MANAGEMENT SET KEYSTORE OPEN IDENTIFIED BY hsm_identity_pass;
    
    -- Create TDE master key in HSM with backup
    ADMINISTER KEY MANAGEMENT SET KEY IDENTIFIED BY hsm_identity_pass WITH BACKUP;
    
  6. Verify Configuration

    -- Check keystore status
    COLUMN WRL_PARAMETER FORMAT A50;
    SET LINES 200;
    SELECT WRL_TYPE, WRL_PARAMETER, WALLET_TYPE, STATUS FROM V$ENCRYPTION_WALLET;
    
    -- Verify keys are stored in HSM
    COLUMN NAME FORMAT A40;
    SET LINES 400;
    SELECT KEY_ID, KEYSTORE_TYPE, CREATOR_DBNAME, ACTIVATION_TIME, KEY_USE, ORIGIN
    FROM V$ENCRYPTION_KEYS;
    
  7. Optional: Create Test Encrypted Table

    -- Create a table with encrypted columns to verify TDE is working
    CREATE TABLE test_tde (something CHAR(32) ENCRYPT);
    

Windows

Oracle 26ai Free for Windows has two unfixed HSM/PKCS#11 issues that require workarounds when configuring TDE directly (without Oracle Key Vault):

  1. skgdllDiscover finds nothing on Windows. The auto-discovery function only scans the hard-coded Linux path /opt/oracle/extapi/64/pkcs11/. No equivalent Windows path is scanned, so Oracle cannot locate any PKCS#11 DLL automatically.

  2. pkcs11_library_location rejects Windows paths. The ALTER SYSTEM SET validator checks that the supplied path starts with /opt/oracle/extapi/64/pkcs11/. Any Windows path (C:\...) is rejected with ORA-46707 / ORA-32017.

The steps below apply the required workarounds.

  1. Install Eviden PKCS#11 Library

    Download cosmian_pkcs11.dll from the release packages.

    The DLL must be placed at the drive-relative Linux path so that Oracle's LoadLibrary call resolves it. On Windows a path starting with / is treated as drive-relative (/opt/...C:\opt\... on a system where C: is the current drive).

    # Create the required directory structure
    New-Item -ItemType Directory -Force -Path 'C:\opt\oracle\extapi\64\pkcs11'
    
    # Install the DLL (both locations are used by Oracle)
    Copy-Item cosmian_pkcs11.dll 'C:\opt\oracle\extapi\64\pkcs11\cosmian_pkcs11.dll'
    
    New-Item -ItemType Directory -Force -Path "$env:ORACLE_HOME\extapi\64\hsm\Cosmian"
    Copy-Item cosmian_pkcs11.dll "$env:ORACLE_HOME\extapi\64\hsm\Cosmian\cosmian_pkcs11.dll"
    
  2. Configure Eviden PKCS#11 Library

    Place ckms.toml alongside the DLL so it is found regardless of which Windows user account Oracle's service runs under:

    @'
    [http_config]
    server_url = "http://kms:9998"
    '@ | Set-Content -Path 'C:\opt\oracle\extapi\64\pkcs11\ckms.toml' -Encoding UTF8
    

    Note: The PKCS#11 library searches for ckms.toml in the following order: CKMS_CONF environment variable → directory containing the DLL → %USERPROFILE%\.cosmian\ckms.toml.

  3. Prepare Oracle Wallet Directory

    # Create the wallet directory (adjust path to match your ORACLE_BASE)
    New-Item -ItemType Directory -Force -Path 'C:\app\oracle\admin\FREE\wallet'
    
  4. Configure Oracle Database for PKCS#11

    Because ALTER SYSTEM SET pkcs11_library_location rejects Windows paths, set all three TDE parameters via a plain PFILE and restart with STARTUP PFILE=:

    -- Step 1: capture current in-memory parameters to a text PFILE
    CREATE PFILE='C:\app\oracle\dbhomeFree\database\initFREE_pkcs11.ora' FROM MEMORY;
    SHUTDOWN IMMEDIATE;
    

    Edit the generated initFREE_pkcs11.ora with a text editor and add (or update) the following three lines — using forward slashes throughout:

    *.wallet_root='C:/app/oracle/admin/FREE/wallet'
    *.tde_configuration='KEYSTORE_CONFIGURATION=HSM'
    *.pkcs11_library_location='/opt/oracle/extapi/64/pkcs11/cosmian_pkcs11.dll'
    

    Then restart and persist:

    -- Step 2: start the instance using the edited PFILE
    STARTUP PFILE='C:\app\oracle\dbhomeFree\database\initFREE_pkcs11.ora';
    
    -- Step 3: write the active configuration back to SPFILE
    CREATE SPFILE FROM MEMORY;
    
  5. Create and Configure HSM Keystore

    -- Open the HSM keystore (loads cosmian_pkcs11.dll)
    ADMINISTER KEY MANAGEMENT SET KEYSTORE OPEN IDENTIFIED BY hsm_identity_pass;
    
    -- Create TDE master key in HSM
    ADMINISTER KEY MANAGEMENT SET KEY IDENTIFIED BY hsm_identity_pass;
    
  6. Verify Configuration

    -- Check keystore status
    COLUMN WRL_PARAMETER FORMAT A50;
    SET LINES 200;
    SELECT WRL_TYPE, WRL_PARAMETER, WALLET_TYPE, STATUS FROM V$ENCRYPTION_WALLET;
    
    -- Verify keys are stored in HSM
    SET LINES 400;
    SELECT KEY_ID, KEYSTORE_TYPE, CREATOR_DBNAME, ACTIVATION_TIME, KEY_USE, ORIGIN
    FROM V$ENCRYPTION_KEYS;
    
  7. Optional: Create Test Encrypted Table

    CREATE TABLE test_tde (something CHAR(32) ENCRYPT);
    

Troubleshooting:

  • PKCS#11 log (service user): C:\WINDOWS\ServiceProfiles\OracleService<SID>\.cosmian\cosmian-pkcs11.log
  • PKCS#11 log (current user): %USERPROFILE%\.cosmian\cosmian-pkcs11.log
  • Oracle alert log: %ORACLE_BASE%\diag\rdbms\free\<SID>\trace\alert_<SID>.log
  • Oracle trace dir: %ORACLE_BASE%\diag\rdbms\free\<SID>\trace\

HSM Identity and Authentication

Architecture: libcosmian_pkcs11.so as a proxy

libcosmian_pkcs11.so is not an HSM driver. It is a thin proxy between Oracle Database and the Eviden KMS server:

Oracle TDE Engine  →  libcosmian_pkcs11.so  →  (HTTP/HTTPS)  →  Eviden KMS Server  →  HSM
                       (PKCS#11 C API)          (REST API via                         (slot + PIN
                                                 ckms.toml)                            managed here)

The KMS server is responsible for the actual HSM connection — including slot selection and HSM PIN. Oracle never communicates directly with the HSM; libcosmian_pkcs11.so simply forwards PKCS#11 operations to the KMS over the network.

What the SQL command does

ADMINISTER KEY MANAGEMENT SET KEYSTORE OPEN IDENTIFIED BY hsm_identity_pass;

Oracle's ADMINISTER KEY MANAGEMENT syntax always ends with IDENTIFIED BY <value>. This command internally translates into two PKCS#11 calls:

  1. C_OpenSession — opens a PKCS#11 session on the slot exposed by libcosmian_pkcs11.so.
  2. C_Login(CKU_USER, pin=hsm_identity_pass) — passes hsm_identity_pass as the PKCS#11 user PIN.

hsm_identity_pass and authentication modes

The value after IDENTIFIED BY in Oracle SQL is passed as the PIN to C_Login. The library supports two modes, controlled by pkcs11_use_pin_as_access_token in ckms.toml:

pkcs11_use_pin_as_access_tokenBehavior
false (default)C_Login accepts any PIN without validation and returns CKR_OK. The value hsm_identity_pass is a self-documenting placeholder — any non-empty string works. KMS authentication uses credentials from ckms.toml.
trueC_Login treats the PIN as a short-lived OIDC/JWT bearer token forwarded to the KMS REST API. No credentials are stored in ckms.toml; Oracle supplies the token at keystore-open time.

Bearer token mode (pkcs11_use_pin_as_access_token = true):

# ckms.toml
pkcs11_use_pin_as_access_token = true

[http_config]
server_url = "https://kms.example.com:9998"

Oracle then passes the JWT token directly:

ADMINISTER KEY MANAGEMENT SET KEYSTORE OPEN IDENTIFIED BY '<jwt_token>';

This approach lets Oracle authenticate to the KMS without storing any credentials in ckms.toml or on the filesystem.

For all other KMS authentication methods (mTLS, static bearer token, OAuth2/OIDC), configure ckms.toml using the CLI Authentication guide.

Wallet Migration

Oracle TDE supports migrating the master encryption key between a software wallet (file-based keystore) and the HSM wallet backed by libcosmian_pkcs11.so. Both directions are supported and are verified in CI by .mise/scripts/oracle/run_sql_commands.sh.

  • Forward (SW → HSM): Oracle calls C_GenerateKey(CKM_AES_KEY_GEN) to create a new master key in the KMS, then C_Encrypt to re-wrap DEKs.
  • Reverse (HSM → SW): Oracle calls C_Decrypt on the HSM master key to unwrap DEKs before re-encrypting under the new software key.

Configuration note: In pure HSM mode, WALLET_ROOT points to the PKCS#11 library file path. In hybrid or file-only modes, WALLET_ROOT must be a directory; Oracle then auto-discovers the library from /opt/oracle/extapi/64/hsm/<vendor>/lib<name>.so.

Note: libcosmian_pkcs11.so prior to version 5.20 reported CKF_WRITE_PROTECTED, blocking C_GenerateKey during forward migration. This flag was removed in 5.20.

HSM Wallet → Software Wallet (Reverse Migration)

Starting from HSM-only mode, follow these steps. Each TDE_CONFIGURATION change requires a restart.

-- 1. Switch WALLET_ROOT from library path to a directory (required for file-based keystore)
ALTER SYSTEM SET WALLET_ROOT='/etc/ORACLE/KEYSTORES/FREE' SCOPE=SPFILE;
SHUTDOWN IMMEDIATE;
STARTUP;

-- 2. Create the software keystore
ADMINISTER KEY MANAGEMENT CREATE KEYSTORE IDENTIFIED BY <sw_password>;

-- 3. Switch to FILE|HSM hybrid mode (file primary, HSM secondary)
ALTER SYSTEM SET TDE_CONFIGURATION='KEYSTORE_CONFIGURATION=FILE|HSM' SCOPE=BOTH SID='*';
SHUTDOWN IMMEDIATE;
STARTUP;

-- 4. Open both keystores (software wallet first, then HSM)
ADMINISTER KEY MANAGEMENT SET KEYSTORE OPEN IDENTIFIED BY <sw_password>;
ADMINISTER KEY MANAGEMENT SET KEYSTORE OPEN IDENTIFIED BY <token>;
-- <token> = hsm_identity_pass (or JWT token if pkcs11_use_pin_as_access_token = true)

-- 5. Generate a new software master key and re-wrap DEKs
--    Oracle calls C_Decrypt on the HSM key to unwrap DEKs, then re-encrypts under the new software key.
ADMINISTER KEY MANAGEMENT SET ENCRYPTION KEY
  IDENTIFIED BY <sw_password>
  REVERSE MIGRATE USING <token>
  WITH BACKUP;

-- 6. Switch to file-only mode
ALTER SYSTEM SET TDE_CONFIGURATION='KEYSTORE_CONFIGURATION=FILE' SCOPE=BOTH SID='*';
SHUTDOWN IMMEDIATE;
STARTUP;
ADMINISTER KEY MANAGEMENT SET KEYSTORE OPEN IDENTIFIED BY <sw_password>;

Software Wallet → HSM Wallet (Forward Migration)

Starting from file-only mode (after a reverse migration, or from a fresh software wallet), with WALLET_ROOT already pointing to a directory:

-- 1. Switch to HSM|FILE hybrid mode (HSM primary, file secondary)
ALTER SYSTEM SET TDE_CONFIGURATION='KEYSTORE_CONFIGURATION=HSM|FILE' SCOPE=BOTH SID='*';
SHUTDOWN IMMEDIATE;
STARTUP;

-- 2. Open both keystores (HSM first, then software wallet)
ADMINISTER KEY MANAGEMENT SET KEYSTORE OPEN IDENTIFIED BY <token>;
ADMINISTER KEY MANAGEMENT SET KEYSTORE OPEN IDENTIFIED BY <sw_password>;

-- 3. Generate a new HSM master key and re-wrap DEKs
--    Oracle calls C_GenerateKey → C_Encrypt internally.
--    WITH BACKUP is omitted: ORA-46623 prevents backup creation in HSM|FILE mode.
ADMINISTER KEY MANAGEMENT SET ENCRYPTION KEY
  IDENTIFIED BY <token>
  MIGRATE USING <sw_password>;

-- 4. Switch to HSM-only mode
ALTER SYSTEM SET TDE_CONFIGURATION='KEYSTORE_CONFIGURATION=HSM' SCOPE=BOTH SID='*';
SHUTDOWN IMMEDIATE;
STARTUP;
ADMINISTER KEY MANAGEMENT SET KEYSTORE OPEN IDENTIFIED BY <token>;

Multiple Wallets Handling with the Same Database

Oracle TDE supports several multi-wallet configurations on a single database instance. The Eviden PKCS#11 library is transparent to these layouts: every keystore operation that reaches the HSM is forwarded to the KMS server regardless of the number of active wallets.

Combined HSM and Software Wallet

Oracle supports a combined keystore by setting KEYSTORE_CONFIGURATION=HSM|FILE. In this mode the TDE master key is generated and stored in the HSM (via the Eviden PKCS#11 library), while Oracle also maintains a local file-based software wallet. This is useful when an auto-login wallet is required for datafile decryption during automatic instance startup, or for keystore redundancy.

Configure the combined mode:

ALTER SYSTEM SET TDE_CONFIGURATION='KEYSTORE_CONFIGURATION=HSM|FILE' SCOPE=BOTH SID='*';
SHUTDOWN IMMEDIATE;
STARTUP;

With the combined configuration, both keystores must be opened explicitly before any key operation:

-- Open the HSM keystore (Eviden PKCS#11)
ADMINISTER KEY MANAGEMENT SET KEYSTORE OPEN IDENTIFIED BY hsm_identity_pass FOR HSM;

-- Open the file-based wallet (use the wallet password here)
ADMINISTER KEY MANAGEMENT SET KEYSTORE OPEN IDENTIFIED BY wallet_password FOR WALLET;

To rotate the TDE master key and update both keystores simultaneously:

ADMINISTER KEY MANAGEMENT SET KEY IDENTIFIED BY hsm_identity_pass
    WITH BACKUP USING 'pre_rotation_backup' CONTAINER = ALL;

Per-PDB Isolated Keystores (Multitenant CDB)

In a multitenant Container Database (CDB), each Pluggable Database (PDB) can hold its own TDE master key. All PDB master keys are ultimately protected by the same Eviden KMS through the shared PKCS#11 library, while Oracle enforces key isolation at the PDB boundary.

Activate per-PDB isolated keystores:

-- In the CDB root: enable HSM for all containers
ALTER SYSTEM SET TDE_CONFIGURATION='KEYSTORE_CONFIGURATION=HSM' SCOPE=BOTH SID='*';
SHUTDOWN IMMEDIATE;
STARTUP;

-- Open the root keystore
ADMINISTER KEY MANAGEMENT SET KEYSTORE OPEN IDENTIFIED BY hsm_identity_pass CONTAINER = ALL;

-- Switch to a specific PDB and create its own master key
ALTER SESSION SET CONTAINER = pdb1;
ADMINISTER KEY MANAGEMENT SET KEY IDENTIFIED BY hsm_identity_pass
    WITH BACKUP CONTAINER = CURRENT;

Each PDB master key is stored in the Eviden KMS under a unique label prefixed with ORACLE.SECURITY.TDE.HSM.MASTERKEY., ensuring tenant isolation within the same KMS server. No additional KMS configuration is required to support multiple PDBs.

Key Rotation Across Wallets

When rotating TDE master keys in a multi-wallet setup, always open all active keystores before issuing the SET KEY command so Oracle can re-wrap the existing data encryption keys:

-- Open both keystores (combined HSM+FILE example)
ADMINISTER KEY MANAGEMENT SET KEYSTORE OPEN IDENTIFIED BY hsm_identity_pass FOR HSM;
ADMINISTER KEY MANAGEMENT SET KEYSTORE OPEN IDENTIFIED BY wallet_password FOR WALLET;

-- Rotate the master key
ADMINISTER KEY MANAGEMENT SET KEY IDENTIFIED BY hsm_identity_pass
    WITH BACKUP USING 'rotation_backup';

After rotation, verify the new key is active:

SELECT KEY_ID, KEYSTORE_TYPE, CREATOR_DBNAME, ACTIVATION_TIME, KEY_USE, ORIGIN
FROM V$ENCRYPTION_KEYS
ORDER BY ACTIVATION_TIME DESC;

Environment Variables Used by cosmian_pkcs11

The Eviden PKCS#11 library reads the following environment variables at startup (i.e. when C_GetFunctionList is called). All variables are optional unless otherwise noted.

VariableDefaultDescription
CKMS_CONF(none)Explicit path to a ckms.toml configuration file. Overrides all other discovery paths.
COSMIAN_PKCS11_LOGGING_LEVELinfoLog verbosity level: trace, debug, info, warn, or error.
COSMIAN_PKCS11_LOGGING_FOLDER~/.cosmian/(Linux only) Directory where cosmian-pkcs11.log is written. On macOS/Windows the log is always written to ~/.cosmian/.
COSMIAN_PKCS11_DISK_ENCRYPTION_TAGdisk-encryptionKMS object tag used to identify disk-encryption keys (VeraCrypt, LUKS). Override if your KMS objects use a different tag.
COSMIAN_PKCS11_SSH_KEY_TAGssh-authKMS object tag used to identify SSH authentication keys. Override if your KMS objects use a different tag.
COSMIAN_PKCS11_IGNORE_SESSIONSfalseWhen set to true, the module reuses a single internal session handle instead of creating one per C_OpenSession call. Intended for applications that call C_OpenSession more times than the token supports.

Notes

  • Variables must be set before the application loads the library (i.e. before the first call to C_GetFunctionList). Changes made after loading have no effect.
  • For Oracle Database (Linux), set variables in the Oracle instance environment before starting the process, or via the okv_hsm_env file for Oracle Key Vault.
  • For Oracle Database (Windows), set them as system environment variables (visible to the Oracle service account) or in the Oracle service user's profile.
  • CKMS_CONF is the most commonly needed variable. If ckms.toml is not found through any discovery path, C_GetFunctionList returns CKR_FUNCTION_FAILED and the library refuses to load.
  • The log file is always appended (never rotated). Monitor its size in long-running deployments.

Making variables persistent for the Oracle Database process (Linux)

Oracle Database runs as the OS user oracle (or a custom service account). Environment variables must be present in that user's process environment at the moment the Oracle instance starts, not only in an interactive shell. There are several standard approaches:

Option A — Oracle instance environment file (oraenv-style .env)

Oracle Database 12c and later can load a per-instance environment file from $ORACLE_HOME/dbs/oraenv_<ORACLE_SID> or the path specified by ORACLE_ENV_PATH. Variables placed there are injected before the instance starts:

# /etc/oratab (verified)
ORCL:/opt/oracle/product/19c/dbhome_1:Y

# /opt/oracle/product/19c/dbhome_1/dbs/oraenv_ORCL   (create if absent, owned by oracle)
CKMS_CONF="/home/oracle/.cosmian/ckms.toml"
COSMIAN_PKCS11_DISK_ENCRYPTION_TAG="disk-encryption"
COSMIAN_PKCS11_LOGGING_LEVEL="info"
COSMIAN_PKCS11_LOGGING_FOLDER="/var/log/cosmian/"

Permissions: the file must be readable by the oracle user and must not be world-writable.

Option B — oracle user's login profile (~oracle/.bash_profile or ~oracle/.profile)

When Oracle is started manually (e.g. by running sqlplus / as sysdba then startup) or via a wrapper script that sources the user profile, exporting variables in the login profile is sufficient:

# ~oracle/.bash_profile  (or ~/.profile for sh/ksh)
export CKMS_CONF="/home/oracle/.cosmian/ckms.toml"
export COSMIAN_PKCS11_DISK_ENCRYPTION_TAG="disk-encryption"
export COSMIAN_PKCS11_LOGGING_LEVEL="info"
export COSMIAN_PKCS11_LOGGING_FOLDER="/var/log/cosmian/"

Warning

~/.bashrc is not read for non-interactive sessions. Use .bash_profile (Bash login), .profile (POSIX sh / ksh), or .zprofile (zsh). When Oracle is started by systemd or another init system, this file is only sourced if the unit uses User=oracle and the init system spawns a login shell — which many do not.

This is the most reliable option when Oracle is controlled by a systemd unit file (common for Oracle 19c+ on RHEL/OEL 8+):

# /etc/systemd/system/oracle-database.service.d/cosmian.conf
# (drop-in override — create the .d/ directory if absent)
[Service]
Environment="CKMS_CONF=/home/oracle/.cosmian/ckms.toml"
Environment="COSMIAN_PKCS11_DISK_ENCRYPTION_TAG=disk-encryption"
Environment="COSMIAN_PKCS11_LOGGING_LEVEL=info"
Environment="COSMIAN_PKCS11_LOGGING_FOLDER=/var/log/cosmian/"

Reload and restart after adding the drop-in:

sudo systemctl daemon-reload
sudo systemctl restart oracle-database

Verify the variables are visible to the running process:

sudo cat /proc/$(pgrep -o pmon)/environ | tr '\0' '\n' | grep -E 'CKMS|COSMIAN'

Option D — Oracle Key Vault okv_hsm_env (OKV deployments only)

If Oracle Key Vault (OKV) is the HSM manager, the canonical location is the OKV environment file that is sourced before every HSM operation:

# /usr/local/okv/hsm/generic/okv_hsm_env
CKMS_CONF="/usr/local/okv/hsm/generic/ckms.toml"
COSMIAN_PKCS11_DISK_ENCRYPTION_TAG="disk-encryption"
COSMIAN_PKCS11_LOGGING_LEVEL="info"
COSMIAN_PKCS11_LOGGING_FOLDER="/var/log/cosmian/"

See the Oracle Key Vault Administrator's Guide for details on okv_hsm_env format and reload procedure.

Verifying that Oracle sees the variables

After any of the above changes, confirm the library picks them up without restarting Oracle-side applications** by running the diagnostic binary as the oracle user:

sudo -u oracle \
  COSMIAN_PKCS11_LOGGING_LEVEL=debug \
  ckms pkcs11 verify \
  --dll /opt/oracle/extapi/64/hsm/Cosmian/libcosmian_pkcs11.so

If the correct tag and config path appear in the debug output, the variables are propagated correctly.

Verifying the library loads correctly

Use the ckms pkcs11 verify command (part of the Eviden KMS CLI) to confirm that the library is loadable, ckms.toml is found, and the KMS server is reachable.

Modes 0 and 1 (no auth or static token/TLS cert — the default):

# Basic check (uses ~/.cosmian/ckms.toml by default)
ckms pkcs11 verify --dll /usr/local/lib/libcosmian_pkcs11.so

# Explicit config path
ckms pkcs11 verify \
  --dll /opt/oracle/extapi/64/hsm/Cosmian/libcosmian_pkcs11.so \
  --conf /home/oracle/.cosmian/ckms.toml

# Verbose — combine with logging env var to also capture library-side trace output
COSMIAN_PKCS11_LOGGING_LEVEL=debug \
  ckms pkcs11 verify --dll /usr/local/lib/libcosmian_pkcs11.so

Mode 2 (pkcs11_use_pin_as_access_token = true — OIDC token supplied at keystore open):

A short-lived JWT must be obtained from your identity provider and passed via --token (or the COSMIAN_PKCS11_TOKEN environment variable):

TOKEN=$(oidc-token my-oidc-profile)   # example using oidc-agent; adapt to your IdP

ckms pkcs11 verify \
  --dll /opt/oracle/extapi/64/hsm/Cosmian/libcosmian_pkcs11.so \
  --conf /home/oracle/.cosmian/ckms.toml \
  --token "$TOKEN"

Expected output when everything is working (modes 0 and 1):

[conf] Will use default home config: /home/oracle/.cosmian/ckms.toml

[load] Opening: /usr/local/lib/libcosmian_pkcs11.so
[load] OK: shared library opened

[C_GetFunctionList] OK: ckms.toml parsed
[C_Initialize] OK
[C_GetSlotList] OK: using slot ID 1
[C_OpenSession] OK: session opened on slot 1
[C_FindObjects] Enumerating objects by class:
  CKO_DATA: N
  CKO_PUBLIC_KEY: N
  CKO_PRIVATE_KEY: N
  CKO_SECRET_KEY: N
[C_FindObjects] OK: N PKCS#11 object(s) visible on KMS
[C_CloseSession] OK
[C_Finalize] OK

All checks passed.

Expected output for mode 2 (with --token):

[C_GetFunctionList] OK: ckms.toml parsed
[C_Initialize] OK
[C_GetSlotList] OK: using slot ID 1
[C_OpenSession] OK: session opened on slot 1
[C_Login] OK: session authenticated with provided token
[C_FindObjects] Enumerating objects by class:
  ...
All checks passed.

If --token is omitted with mode 2, object enumeration fails with CKR_USER_NOT_LOGGED_IN.


Authentication from Oracle to KMS

The Eviden PKCS#11 library (libcosmian_pkcs11.so on Linux, cosmian_pkcs11.dll on Windows) communicates with the Eviden KMS server over HTTP or HTTPS. Authentication is configured through the ckms.toml file that the library reads at startup.

Configuration File Discovery

The library resolves ckms.toml using the following search order:

  1. CKMS_CONF environment variable — explicit path, takes precedence over everything else. For Oracle Key Vault, set this in /usr/local/okv/hsm/generic/okv_hsm_env.
  2. ckms.toml beside the PKCS#11 library — a file named ckms.toml placed in the same directory as the .so/.dll. Convenient when the library is deployed at a fixed path (e.g. C:\opt\oracle\extapi\64\pkcs11\ckms.toml on Windows).
  3. ~/.cosmian/ckms.toml — home directory of the OS user running the Oracle process (typically /home/oracle/.cosmian/ckms.toml on Linux).
  4. /etc/cosmian/ckms.toml — system-wide fallback on Linux.

For Oracle Key Vault, set CKMS_CONF in the OKV environment file so all Oracle Key Vault processes share the same configuration:

# /usr/local/okv/hsm/generic/okv_hsm_env
CKMS_CONF="/usr/local/okv/hsm/generic/ckms.toml"

No Authentication (Development / Internal Networks)

For development environments or deployments where the KMS is on a fully trusted internal network:

[http_config]
server_url = "http://kms:9998"

Bearer Token Authentication

When the KMS server is configured with API-token or JWT/OIDC authentication, supply an access_token. The token is sent as an Authorization: Bearer <token> HTTP header on every request.

[http_config]
server_url = "https://kms.example.com:9998"
access_token = "<your-bearer-token>"

Note: storing a long-lived token in ckms.toml means any OS user who can read the file gains KMS access. For production, prefer TLS client certificates (below) or the OIDC dynamic-token mode.

OIDC / JWT Keystore Authentication (Dynamic Token)

This mode keeps zero credentials in ckms.toml. Instead, when Oracle opens the encrypted wallet, the DBA supplies a short-lived JWT from an identity provider via the IDENTIFIED BY clause. The library validates it against the KMS on every keystore open.

Modeckms.toml credentialsWhen KmsClient is createdC_Login behavior
0 — No authnonelibrary loadno-op
1 — Static token / TLS certtoken or cert pathslibrary loadno-op
2 — OIDC dynamic tokennone (flag only)C_Login callvalidates + uses token

ckms.toml for mode 2

[http_config]
server_url = "https://kms.example.com:9998"
# No credentials stored here

pkcs11_use_pin_as_access_token = true

Oracle SQL

The value after IDENTIFIED BY becomes the bearer token sent to the KMS:

ADMINISTER KEY MANAGEMENT SET KEYSTORE OPEN
  IDENTIFIED BY 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...';

A wrapper script that fetches a fresh token before opening the keystore is strongly recommended:

#!/usr/bin/env bash
# Fetch a short-lived JWT (example using oidc-agent; adapt to your IdP)
TOKEN=$(oidc-token my-oidc-profile)
sqlplus -s / as sysdba <<EOF
ADMINISTER KEY MANAGEMENT SET KEYSTORE OPEN IDENTIFIED BY '$TOKEN';
EOF

Security properties

  • ckms.toml contains no secrets — reading the file grants nothing.
  • Each keystore open requires a fresh token from the IdP; expired tokens are rejected (401 Unauthorized from KMS → C_Login failure).
  • An empty IDENTIFIED BY string returns CKR_PIN_INCORRECT (Oracle: ORA-28354).
  • Use ckms pkcs11 verify --token <JWT> to test the full flow end-to-end; see Verifying the library loads correctly.

Mutual TLS (mTLS) is the recommended authentication method for production deployments. Each Oracle host or database instance is provisioned with a unique client certificate; the KMS identifies and authorises callers by the certificate's Common Name (CN).

KMS server prerequisites: the server must be started in TLS mode with a CA certificate configured to validate client certificates. See TLS configuration and Authentication for the server-side setup.

[http_config]
server_url = "https://kms.example.com:9998"

# Client certificate in PEM format (leaf certificate, or leaf + intermediate chain)
tls_client_pem_cert_path = "/home/oracle/.cosmian/oracle-client.crt"

# Matching private key in PEM format (PKCS#8 or traditional RSA/EC)
tls_client_pem_key_path = "/home/oracle/.cosmian/oracle-client.key"

PKCS#12 Bundle (Non-FIPS Only)

[http_config]
server_url = "https://kms.example.com:9998"

# PKCS#12 bundle containing the client certificate and private key
tls_client_pkcs12_path = "/home/oracle/.cosmian/oracle-client.p12"
tls_client_pkcs12_password = "changeit"

Combined mTLS + Bearer Token (Multi-Factor)

[http_config]
server_url = "https://kms.example.com:9998"
tls_client_pem_cert_path = "/home/oracle/.cosmian/oracle-client.crt"
tls_client_pem_key_path  = "/home/oracle/.cosmian/oracle-client.key"

# Additional JWT bearer token for a second authentication factor
access_token = "<JWT_BEARER_TOKEN>"

Server Certificate Pinning

To validate the KMS server against a specific PEM certificate (instead of the system CA bundle), use verified_cert. This is particularly useful when the KMS uses a self-signed or internal CA certificate:

[http_config]
server_url = "https://kms.example.com:9998"
# PEM-encoded server certificate expected from the KMS
verified_cert = """
-----BEGIN CERTIFICATE-----
MIIBxTCCAW...
-----END CERTIFICATE-----
"""

For testing only, you can disable server certificate verification entirely (not for production):

[http_config]
server_url = "https://kms.example.com:9998"
accept_invalid_certs = true  # INSECURE — testing only

For the complete set of authentication options available on the KMS server side, see Authentication.

EDB Postgres Advanced Server TDE with Cosmian KMS

This guide demonstrates how to configure EDB Postgres Advanced Server 17 Transparent Data Encryption (TDE) to use Cosmian KMS as its key management server via KMIP over mTLS.

Overview

ItemDetails
ProtocolKMIP 2.1 over TCP/TLS with mutual certificate authentication (mTLS)
Port5696 or 15696 (configurable)
Key typeAES-256 symmetric key (CBC mode with PKCS#5 padding)
EDB versionEDB Postgres Advanced Server 15.2+ and 17.x
Clientedb_tde_kmip_client.py (shipped inside the EDB image)
Cosmian KMS featureWorks in non-FIPS mode (PyKMIP uses ssl.wrap_socket)

Architecture

flowchart TB
    pg["EDB Postgres Advanced Server<br/>(initdb --data-encryption)"]
    client["edb_tde_kmip_client.py<br/>(PGDATAKEYWRAPCMD / PGDATAKEYUNWRAPCMD)"]
    kms["Cosmian KMS<br/>(KMIP 2.1, mTLS, port 15696)"]

    pg -- "wrap DEK on initdb" --> client
    pg -- "unwrap DEK on startup" --> client
    client -- "KMIP Encrypt / Decrypt<br/>AES-256-CBC" --> kms

During initdb --data-encryption:

  1. Postgres generates a random cluster DEK.
  2. It invokes PGDATAKEYWRAPCMD — the edb_tde_kmip_client.py encrypt command.
  3. The client calls KMIP Encrypt on the Cosmian KMS, which returns [IV][ciphertext].
  4. The wrapped DEK is written to pg_encryption/key.bin.

On every subsequent server start, Postgres calls PGDATAKEYUNWRAPCMD (edb_tde_kmip_client.py decrypt) to unwrap the DEK before mounting the cluster.


Wire Format

The edb_tde_kmip_client.py script defines the on-disk format for the wrapped DEK:

key.bin = [16-byte IV][AES-256-CBC ciphertext]

This format is assembled client-side; the KMS is unaware of it.

KMIP Variants

The client supports two --variant options that differ in how they call KMIP:

VariantEncryptDecrypt
pykmip (required for Cosmian KMS)Encrypt(data=DEK) → KMS returns IVCounterNonce=IV, Data=CTDecrypt(Data=CT, IVCounterNonce=IV) — standard KMIP
thalesSame as pykmipDecrypt(Data=IV‖CT) — no IVCounterNonce; specific to Thales CipherTrust Manager

Note: Cosmian KMS follows the KMIP mandatory conformance requirement: a Decrypt request without IVCounterNonce returns Invalid_Message. Always use --variant=pykmip when connecting edb_tde_kmip_client.py to Cosmian KMS.


Prerequisites

  • A running Cosmian KMS server with the KMIP socket enabled (listen_address / port).
  • mTLS certificates: CA, server, and client certificate+key.
  • EDB Postgres Advanced Server 15.2+ or 17.x.
  • edb_tde_kmip_client.py installed via the edb-tde-kmip-client package from EDB repositories.
  • Python 3 with PyKMIP (pip3 install PyKMIP).

Configuration

1. KMS Configuration

Add the KMIP socket listener to kms.toml:

[server]
port = 9998

[server.kmip]
server_port = 15696

Generate or reuse mTLS certificates for the KMS and the EDB client. See Certificates for details.

2. Create the Master Key

The edb_tde_kmip_client.py script does not implement key creation. Use create_master_key.py (bundled with the Cosmian KMS test scripts) or any other KMIP client to create an AES-256 key:

# Using the bundled helper script
python3 .mise/scripts/edb_tde/create_master_key.py \
  --pykmip-config-file=/path/to/pykmip.conf
# → prints the key UID, e.g. 0193a4b2-...

The pykmip.conf file must point to the Cosmian KMS KMIP socket:

[client]
host = localhost
port = 15696
keyfile  = /path/to/client.key
certfile = /path/to/client.crt
ca_certs = /path/to/ca.crt

3. Configure PGDATAKEYWRAPCMD / PGDATAKEYUNWRAPCMD

export PGDATAKEYWRAPCMD='python3 /usr/edb/kmip/client/edb_tde_kmip_client.py \
  encrypt \
  --pykmip-config-file=/path/to/pykmip.conf \
  --key-uid=<UID from step 2> \
  --variant=pykmip \
  --out-file=%p'

export PGDATAKEYUNWRAPCMD='python3 /usr/edb/kmip/client/edb_tde_kmip_client.py \
  decrypt \
  --pykmip-config-file=/path/to/pykmip.conf \
  --key-uid=<UID from step 2> \
  --variant=pykmip \
  --in-file=%p'

4. Initialize the Cluster

initdb -D /var/lib/edb/data -y -U enterprisedb \
  --key-wrap-command="$PGDATAKEYWRAPCMD" \
  --key-unwrap-command="$PGDATAKEYUNWRAPCMD"

initdb calls PGDATAKEYWRAPCMD once to seal the freshly generated cluster DEK.

5. Start the Server

pg_ctl -D /var/lib/edb/data start

Verify TDE is active:

SELECT data_encryption_version FROM pg_control_init();
-- Returns 1 if TDE is enabled

Key Rotation

To rotate the master key:

PGDATA=/var/lib/edb/data
OLD_KEY=<old UID>
NEW_KEY=<new UID created via create_master_key.py>
KMIP_CONF=/path/to/pykmip.conf
CLIENT=/usr/edb/kmip/client/edb_tde_kmip_client.py

cd $PGDATA/pg_encryption
cp key.bin key.bin.bak

python3 $CLIENT decrypt --in-file=key.bin \
  --pykmip-config-file=$KMIP_CONF --key-uid=$OLD_KEY --variant=pykmip \
| python3 $CLIENT encrypt --out-file=key.bin \
  --pykmip-config-file=$KMIP_CONF --key-uid=$NEW_KEY --variant=pykmip

sed -i "s/$OLD_KEY/$NEW_KEY/" $PGDATA/postgresql.conf
pg_ctl -D $PGDATA restart

Running the Integration Tests

The Cosmian KMS repository includes a full integration test suite for EDB TDE. It requires an EDB subscription token to pull the edb-postgres-advanced:17 Docker image.

EDB_SUBSCRIPTION_TOKEN=<token> \
  mise run test:edb_tde --variant non-fips

The test suite:

  1. Logs in to docker.enterprisedb.com, starts the EDB container.
  2. Runs initdb with TDE — the real edb_tde_kmip_client.py seals the cluster DEK against Cosmian KMS over KMIP/mTLS.
  3. Verifies data_encryption_version = 1.
  4. Exercises both --variant=pykmip and --variant=thales wrap/unwrap roundtrips.
  5. Tests key rotation (re-wrap DEK with a new master key).

See .mise/scripts/edb_tde/README.md for full test documentation.


Reference

InterSystems IRIS Database Encryption with Eviden KMS

InterSystems IRIS supports database encryption via the KMIP 2.0 protocol, allowing encryption keys to be managed externally by a dedicated Key Management System. Eviden KMS acts as the KMIP server: IRIS connects to it over a mutual TLS (mTLS) channel, retrieves or creates symmetric keys, and uses them to encrypt database files on disk.



Architecture overview

flowchart LR
    subgraph iris["InterSystems IRIS"]
        enc["Database encryption engine"]
        kmip_client["KMIP 2.0 client\n(^SECURITY / ^EncryptionKey)"]
        enc --> kmip_client
    end
    subgraph kms["Eviden KMS (non-FIPS)"]
        socket["KMIP socket server\nport 5696 (TLS)"]
        db["Key store"]
        socket --> db
    end
    kmip_client -->|"mTLS — KMIP 2.0"| socket

The mTLS channel requires:

SideMaterial
KMSServer certificate + private key (PKCS#12)
IRISClient certificate + private key + CA certificate
BothSame CA that signed both certificates

Prerequisites

ComponentVersionNotes
Eviden KMS5.0+ (non-FIPS build)KMIP socket server requires --features non-fips
InterSystems IRIS2022.1+Community or Enterprise edition
KMIP protocol2.0Configured on the IRIS side
OpenSSL3.0+For TLS 1.3

FIPS note: The KMIP socket server is only available in the non-FIPS build of Eviden KMS. Start the server with --features non-fips or use the non-FIPS Docker image.


Certificate preparation

Both the KMS server and the IRIS client must present certificates signed by the same CA. The repository ships test certificates under test_data/certificates/client_server/; for production, generate your own PKI.

test_data/certificates/client_server/
  ca/
    ca.crt                   ← CA certificate (trusted by both sides)
  server/
    kmserver.acme.com.p12    ← KMS server cert + key (PKCS#12, password: "password")
  owner/
    owner.client.acme.com.crt ← IRIS client certificate
    owner.client.acme.com.key ← IRIS client private key

Starting Eviden KMS with the KMIP socket server

The KMIP socket server is disabled by default. Enable it with:

cosmian_kms \
  --database-type  sqlite \
  --sqlite-path    /var/lib/kms/data \
  --socket-server-start true \
  --socket-server-port  5696 \
  --tls-p12-file        /path/to/kmserver.acme.com.p12 \
  --tls-p12-password    <p12-password> \
  --clients-ca-cert-file /path/to/ca.crt \
  --port     9998 \
  --hostname 0.0.0.0
FlagPurpose
--socket-server-startEnable the KMIP TCP/TLS socket listener
--socket-server-portTCP port for KMIP (default: 5696)
--tls-p12-filePKCS#12 bundle containing the server certificate and key
--tls-p12-passwordPassword for the PKCS#12 bundle
--clients-ca-cert-fileCA that must have signed every connecting client cert

Verify the socket is up:

openssl s_client -connect <kms-host>:5696 \
  -cert /path/to/client.crt \
  -key  /path/to/client.key \
  -CAfile /path/to/ca.crt \
  -quiet 2>&1 | head -5
# Expected: "Verify return code: 0 (ok)"

Configuring IRIS

All configuration is done via ObjectScript in the %SYS namespace. You can run these commands interactively in the IRIS terminal, via the Management Portal, or in batch mode with iris session IRIS -B.

Step 1 — Create a TLS/SSL configuration

IRIS uses named SSL configurations to reference certificate material. Create one for the Cosmian KMS connection:

zn "%SYS"
; Create a client TLS configuration using the Security.SSLConfigs ObjectScript API
set tls = ##class(Security.SSLConfigs).%New()
set tls.Name             = "CosmianKMSTLS"
set tls.Enabled          = 1
set tls.Type             = 1                           ; 1 = client
set tls.CertificateFile  = "/path/to/client.crt"
set tls.PrivateKeyFile   = "/path/to/client.key"
set tls.CAFile           = "/path/to/ca.crt"
set tls.VerifyPeer       = 1                           ; verify KMS server cert
set sc = tls.%Save()
write $select(sc=1:"OK",1:"ERROR: "_$system.Status.GetErrorText(sc)),!

Type = 1 configures the SSL entry as a client configuration. Set VerifyPeer = 1 to enforce server-certificate verification (recommended for production).

Step 2 — Register the KMIP server

The documented command-line interface for KMIP server management is ^SECURITY option 14. Run it from a %SYS terminal session and follow the interactive prompts:

do ^SECURITY
14                  ← KMIP server setup
1                   ← Create KMIP server
CosmianKMS          ← Server name
Eviden KMS – mTLS   ← Description
<kms-host>          ← Host DNS name or IP
5696                ← TCP port
2.0                 ← OASIS KMIP protocol version
CosmianKMSTLS       ← SSL/TLS configuration name (from Step 1)
Y                   ← Non-blocking I/O (recommended)
N                   ← Auto-reconnect
30                  ← I/O timeout in seconds
N                   ← Log KMIP messages
N                   ← Debug SSL/TLS
Y                   ← Confirm creation
0                   ← Back
q                   ← Quit

Step 3 — Create an encryption key

Use the ^EncryptionKey routine to create a key on the external KMIP server:

  1. From the IRIS terminal, run do ^EncryptionKey.
  2. Select 5 – Manage KMIP server.
  3. Enter the server name: CosmianKMS.
  4. Select 2 – Create new key and choose the desired key length (256-bit recommended).

The key is created on the KMS and referenced in IRIS by its KMIP object identifier.

Step 4 — Activate the KMIP key for database encryption

After creating the key, activate it for database encryption using ^EncryptionKey (IRIS docs — Activate a Database Encryption Key from a KMIP Server):

  1. Run do ^EncryptionKey.
  2. Select 3 – Database encryption.
  3. Select 1 – Activate database encryption keys.
  4. Select 2 – Use KMIP server.
  5. Enter the KMIP server name: CosmianKMS.
  6. Select the key number shown in the list (e.g., 1 for the first key).

The key is now activated in IRIS memory and can be used to encrypt databases.

Step 5 — Encrypt a database

  1. Go to the IRIS Management Portal → System Administration → Encryption → Database Encryption.
  2. Select the database you want to encrypt.
  3. Click Encrypt and choose the activated KMIP key.
  4. IRIS starts a background encryption task for that database.

Configuring with Docker

If IRIS runs inside a Docker container and the KMS runs on the Docker host, use host.docker.internal as the KMS hostname and pass the --add-host flag:

docker run \
  --add-host host.docker.internal:host-gateway \
  --volume /path/to/certs:/iris-kmip-certs:ro \
  intersystemsdc/iris-community:latest

Mount the CA certificate, client certificate, and client key under /iris-kmip-certs/ and reference those paths in the ObjectScript configuration above.


CI integration

The repository includes an automated mTLS integration test that:

  1. Builds and starts Eviden KMS (non-FIPS) with the KMIP socket server.
  2. Validates the mTLS sad path (connection without a client certificate is rejected).
  3. Validates the mTLS happy path (connection with a valid client certificate succeeds).
  4. Pulls the IRIS Docker image.
  5. Detects whether KMIP support is available by probing ^SECURITY option 14.
  6. If KMIP is available:
    • Creates a TLS configuration (Security.SSLConfigs API).
    • Creates a KMIP server entry via ^SECURITY option 14 (documented command-line interface).
    • Creates a symmetric encryption key via ^EncryptionKey option 5.
    • Activates the KMIP key for database encryption via ^EncryptionKey option 3 → 1 → 2 (per IRIS docs — Activate a Database Encryption Key from a KMIP Server).
    • Enables encryption on the TEST namespace database (SYS.Database API).
    • Verifies at-rest encryption: inserts test data, proves it is NOT readable in IRIS.DAT, proves it IS readable via IRIS session.
  7. If KMIP is not available (Community edition): reports SKIPPED cleanly — the mTLS test still passes.

IRIS editions and KMIP support

EditionKMIP supportDetection
IRIS Community (intersystemsdc/iris-community)No^SECURITY option 14 not available
IRIS Enterprise / licensedYes^SECURITY option 14 shows KMIP server setup sub-menu

The test script detects KMIP availability at runtime by probing ^SECURITY option 14. If the KMIP server setup sub-menu does not appear in the output, all IRIS KMIP steps are skipped gracefully. The CI job exits 0 with the KMIP steps marked as SKIPPED when using the Community image.

False-positive protection: IRIS terminal batch mode echoes each command before executing it. A naïve grep "OK_MARKER" would match the command echo (WRITE "OK_MARKER",!) even when the command failed. The test uses anchored grep (grep -q "^OK_MARKER$") to match only the bare output line produced by the write command — not the surrounding echo.

Running the test

# Local run (uses Community image by default)
bash .mise/scripts/test/test_iris.sh --variant non-fips

# With a licensed IRIS image (full KMIP test)
IRIS_DOCKER_IMAGE=containers.intersystems.com/intersystems/iris:latest \
bash .mise/scripts/test/test_iris.sh --variant non-fips

# Via the Nix wrapper (matches CI exactly)
mise run test:iris --variant non-fips

Environment variables

VariableDefaultPurpose
IRIS_DOCKER_IMAGEintersystemsdc/iris-community:latestOverride to use a licensed or ARM image
IRIS_LICENSE_KEY(empty)ISC license key injected as ISC_PACKAGE_LICENSEKEY

On ARM hardware (Apple Silicon, AWS Graviton), use intersystemsdc/iris-community-arm64:latest or the script will automatically add --platform linux/amd64 to run the x86-64 image via emulation.

GitHub Actions

The CI job (iris-mtls in .github/workflows/test_all.yml) runs on every PR from the main repository and on every release tag. It supports two optional secrets for accessing enterprise IRIS images from the InterSystems Container Registry:

Secret / VariablePurpose
ISC_DOCKER_USERNAMEUsername for containers.intersystems.com
ISC_DOCKER_PASSWORDPassword for containers.intersystems.com
IRIS_LICENSE_KEYLicense key for licensed IRIS images
IRIS_DOCKER_IMAGE (var)Repository variable to override the image

Troubleshooting

SymptomLikely causeFix
handshake failureIRIS client cert not signed by the KMS trusted CAEnsure both certs share the same CA (--clients-ca-cert-file)
Instance is not runningiris session called before IRIS fully initializedWait for the instance; retry for up to 120 s
ERROR: <status code>Wrong ^SECURITY option 14 prompt answerCheck the IRIS version; menu prompts may vary between releases
KMS log: unexpected EOFClient connected without a certificate (expected for sad path)Normal — mTLS enforced; provide a valid client cert
host.docker.internal not foundDocker Desktop < 4.0 or Linux without --add-hostAdd --add-host host.docker.internal:host-gateway to docker run

Veracrypt integration with Cosmian KMS

The Cosmian KMS is integrated with Veracrypt disk encryption using the Cosmian KMS PKCS#11 provider library.

Veracrypt is a free, open-source disk encryption software for Windows, macOS, and Linux, and is a successor to TrueCrypt, certified by ANSSI. Veracrypt has undergone a formal security evaluation by the BSI.

Using the Cosmian KMS PKCS#11 provider library, Veracrypt can use keys stored in the KMS to encrypt and decrypt volumes.

Veracrypt-cosmian

Installing the PKCS#11 provider library on Veracrypt

Go to Veracrypt Settings > Security Tokens and use the Select Library... button to select the Cosmian KMS PKCS#11 library for your operating system. Then click OK.

Veracrypt PKCS#11 library

Configuring access to the KMS

The PKCS#11 provider library uses the same configuration file as the CLI.

See Authenticating users to the KMS to learn how to configure the KMS to use OpenID Connect or certificate authentication. Please note that the KMS can also manage the machines' certificates.

Here is an example configuration file for the PKCS#11 provider library accessing the KMS using a PKCS#12 file for authentication.

[kms_config.http_config]
server_url = "https://kms.acme.com:9999"
ssl_client_pkcs12_path = "./certificates/machine123.acme.p12"
ssl_client_pkcs12_password = "machine123_pkcs12_password"

To use Open ID connect, install the KMS CLI from https://package.cosmian.com/kms/ and use the ckms login command to authenticate to the KMS first.

Logging

The PKCS#11 module logs to the cosmian-pkcs11.log file in the same .cosmian subdirectory of the configuration file.

The COSMIAN_PKCS11_LOGGING_LEVEL environment variable controls logging of the PKCS#11 module. The logging level can be set to trace, debug, info, warn, or error, and defaults to info when not set.

Creating keys to use with Veracrypt

The keys used to encrypt Veracrypt volumes are created in the KMS as Symmetric Keys and must carry at least two tags, one being disk-encryption. The other tag(s) will be displayed to the user in the Veracrypt GUI when selecting the key to use to mount the volume. The other tag(s) can also be used to identify the key when using Veracrypt CLI.

To create a key using the CLI, which will be shown as vol1 to the user, use the following command:

ckms sym keys create -t disk-encryption -t vol1

The keys are exposed to Veracrypt as PKCS#11 token files (CKO_DATA objects). The label shown in the Veracrypt GUI is the first user-defined tag sorted alphabetically (e.g. vol1).

Custom discovery tag

The PKCS#11 provider discovers disk-encryption keys using the tag disk-encryption by default. To use a different tag, set the COSMIAN_PKCS11_DISK_ENCRYPTION_TAG environment variable before starting Veracrypt.

COSMIAN_PKCS11_DISK_ENCRYPTION_TAG=my-veracrypt-keys veracrypt

Do not use --sensitive

Keys must remain non-sensitive (exportable) so the PKCS#11 provider can retrieve the key material. This is the default for ckms sym keys create.

Do not forget to grant Get access to the key to the machine that will mount the Veracrypt volume.

ckms access-rights grant <MACHINE_CN> <KEY_ID> get

where <MACHINE_CN> is the Common Name of the certificate used to authenticate the machine to the KMS and <KEY_ID> is the ID of the key created in the previous step.

Mounting a Veracrypt volume using the GUI

When mounting a Veracrypt volume, select the Use keyfiles option and select the key to use to mount the volume using the Keyfiles... button.

Veracrypt key selection 1

Click Add Token Files...

Veracrypt key selection 2

Select the key to use to mount the volume and click OK

Veracrypt key selection 3

Mounting a Veracrypt volume using the CLI

When mounting a Veracrypt volume using the CLI, use the -k option followed by token://slot/1/file/vol1 where vol1 is the other tag chosen to identify the key in the KMS.

Revoking the access to a key

To revoke access to the key, either

  • remove the access to the key <KEY_ID> for the machine <MACHINE_CN> using the following command:
ckms access-rights revoke <MACHINE_CN> <KEY_ID> get
  • revoke the key itself using the following command:
ckms sym keys revoke <KEY_ID>

Note: a revoked key can never be reused.

Rotating a key

To rotate a key,

  1. Create a new key.
  2. Grant access to the machine to the new key.
  3. Perform the key rotation/change in the Veracrypt GUI or CLI.
  4. Revoke the access to the old key for the machine.
  5. Optionally revoke the old key itself.

LUKS

The Eviden KMS can provision secrets to open Linux LUKS encrypted partitions. The secret never leaves the KMS and can be used to unlock the partition at boot time.

Installing p11-kit and the Eviden KMS PKCS#11 module

The Eviden KMS provides a PKCS#11 module that can be used to access the KMS from applications that support PKCS#11, using the p11-kit framework.

With LUKS, the system provided systemd-cryptenroll command must have support for p11-kit which you can check by running systemd-cryptenroll --help and checking for the +P11KIT flag.

❯ systemd-cryptenroll --version

systemd 253 (253.5-1ubuntu6.1)
+PAM +AUDIT +SELINUX +APPARMOR +IMA +SMACK +SECCOMP +GCRYPT -GNUTLS +OPENSSL +ACL +BLKID +CURL
+ELFUTILS +FIDO2 +IDN2 -IDN +IPTC +KMOD +LIBCRYPTSETUP +LIBFDISK +PCRE2 -PWQUALITY
+P11KIT +QRENCODE +TPM2 +BZIP2 +LZ4 +XZ +ZLIB +ZSTD -BPF_FRAMEWORK -XKBCOMMON +UTMP +SYSVINIT
default-hierarchy=unified

Unfortunately, Ubuntu 22.04 does not provide p11-kit support; however the setup works fine for Ubuntu 24.04.

1. Install the p11-kit package

Ubuntu 24.04

sudo apt install p11-kit cryptsetup

Rocky Linux 9

sudo dnf install p11-kit cryptsetup

2. Create the PKCS#11 configuration and module directories

sudo mkdir -p /etc/pkcs11/modules

3. Create a configuration file for the PKCS#11 module

sudo tee /etc/pkcs11/pkcs11.conf <<EOF
# This setting controls whether to load user configuration from the
# ~/.config/pkcs11 directory. Possible values:
#    none: No user configuration
#    merge: Merge the user config over the system configuration (default)
#    only: Only user configuration, ignore system configuration
user-config: merge
EOF

4. Copy the PKCS#11 module to the pkcs11 directory

sudo cp libcosmian_pkcs11.so /usr/local/lib/

5. Create a configuration file for the cosmian PKCS#11 module

sudo tee /etc/pkcs11/modules/cosmian_pkcs11.module <<EOF
# Eviden KMS PKCS#11 module
module: /usr/local/lib/libcosmian_pkcs11.so
EOF

6. Check that the module loads correctly

> p11-kit list-modules

...
cosmian_pkcs11: /usr/local/lib/libcosmian_pkcs11.so
 library-description: Eviden KMS PKCS#11 provider
 library-manufacturer: Cosmian
 library-version: x.y
 token: Cosmian-KMS
     manufacturer: Cosmian
     model: software
     serial-number: x.y.z
     flags:
           rng
           write-protected
           login-required
           user-pin-initialized
           protected-authentication-path
           token-initialized

Configuring the access to the KMS

The PKCS#11 module uses the same configuration file as the CLI. Since it may be run as a system user, the configuration file should be made available in /etc/cosmian/ckms.toml.

See Authenticating users to the KMS to learn how to configure the KMS to use Open ID Connect or certificate authentication.

Here is an example configuration file for the PKCS#11 provider library accessing the KMS using a PKCS#12 file for authentication.

[http_config]
server_url = "https://kms.acme.com:9999"
ssl_client_pkcs12_path = "./certificates/machine123.acme.p12"
ssl_client_pkcs12_password = "machine123_pkcs12_password"

To use Open ID connect, install the KMS CLI from Cosmian packages and use the ckms login command to authenticate to the KMS first.

Logging

The PKCS#11 module logs to the cosmian-pkcs11.log file in the same .cosmian subdirectory of the configuration file.

The COSMIAN_PKCS11_LOGGING_LEVEL environment variable controls logging of the PKCS#11 module. The logging level can be set to trace, debug, info, warn, or error, and defaults to info when not set.

Creating an RSA key pair using openssl and importing it into the Eviden KMS

To generate a self-signed certificate with RSA 2048bit key and in PKCS12 format, you can use the OpenSSL command-line tool. Here are the steps:

1. Generate a new private key

openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048

2. Create a self-signed certificate

openssl req -new -x509 -key private_key.pem -out cert.pem -days 365

3. Convert the certificate and private key to PKCS12 format

openssl pkcs12 -export -out certificate.p12 -inkey private_key.pem -in cert.pem

4. Import the PKCS12 file into the Eviden KMS using a disk-encryption tag

ckms certificates import -f pkcs12 -t disk-encryption certificate.p12 disk-encryption

The private key in the PKCS12 file was imported with id: 6fc631...
Tags:
 - disk-encryption

A tag different from disk-encryption can be used, but it must be set in the in the COSMIAN_PKCS11_DISK_ENCRYPTION_TAG environment variable when enrolling the token (sse below).

Creating a LUKS partition

First, allocate some space, then create a LUKS partition using cryptsetup.

1. Allocating space for the LUKS partition

LUKS partitions can be created from either disk partitions or files.

From a file

Use either dd or fallocate to create a file that will be used as the LUKS partition.

# Create a 1GB file
fallocate -l 1G /path/to/file

Then use path/to/file as the device to encrypt.

From a disk partition

Using parted, determine or create a partition on the disk that you want to encrypt. In this example, we assume the disk is available as /dev/vda.

If needed, use partedto resize the last partition and create free space at the end of the disk.

sudo parted /dev/vda
(parted) print free
Number  Start   End     Size    File system  Name  Flags
        17.4kB  1049kB  1031kB  Free Space
 1      1049kB  1128MB  1127MB  fat32              boot, esp
 2      1128MB  3276MB  2147MB  ext4
 3      3276MB  102GB   98.7GB
        102GB   103GB   1079MB  Free Space

Make a 4th partition /dev/vda4 from the free space at the end.

(parted) mkpart 4 102GB 103GB

2. Creating a LUKS 2 partition on the allocated space

Enter a passphrase to protect the partition when prompted. The encrypted passphrase will be stored in the LUKS header in key slot 0.

sudo cryptsetup luksFormat --type luks2 --key-slot 0 /dev/vda4

or alternatively, if you created a file:

sudo cryptsetup luksFormat --type luks2 --key-slot 0 /path/to/file

Make sure to remember the passphrase, as it will be needed to unlock the partition during cryptenroll or when rotating the RSA keys.

Enrolling the LUKS partition with the Eviden KMS

The RSA key pair is searched on the KMS using a tag controlled by the COSMIAN_PKCS11_DISK_ENCRYPTION_TAG environment variable. When not set, the default tag searched is disk-encryption.

1. Enroll the partition with the Eviden KMS

# this is equivalent to
# sudo COSMIAN_PKCS11_LOGGING_LEVEL=info COSMIAN_PKCS11_DISK_ENCRYPTION_TAG=disk-encryption systemd-cryptenroll /dev/vda4  --pkcs11-token-uri=pkcs11:token=Cosmian-KMS
> sudo systemd-cryptenroll --pkcs11-token-uri=pkcs11:token=Cosmian-KMS /dev/vda4

🔐 Please enter current passphrase for disk /dev/vda4: *************
cosmian-pkcs11 module logging at INFO level to file /var/log/cosmian-pkcs11.log
Successfully logged into security token 'Cosmian-KMS' via protected authentication path.
New PKCS#11 token enrolled as key slot 1.

3. Verify the enrollment

 > sudo cryptsetup luksDump /dev/vda4

LUKS header information
Version:        2
Epoch:          5
...
Keyslots:
  0: luks2
  ....
  1: luks2
     Key:        512 bits
     Priority:   normal
     Cipher:     aes-xts-plain64
     Cipher key: 512 bits
     PBKDF:      pbkdf2
     Hash:       sha512
     ...


Tokens:
  0: systemd-pkcs11
     pkcs11-uri: pkcs11:token=Cosmian-KMS
     pkcs11-key: 0b 94 e0 ...
...

4. Test attaching the LUKS partition to /dev/mapper/myluks using the Cosmian-KMS token in slot 0

> sudo cryptsetup open --type luks2  --token-id=0 --token-only /dev/vda4 myluks

cosmian-pkcs11 module logging at INFO level to file /var/log/cosmian-pkcs11.log
Successfully logged into security token 'Cosmian-KMS' via protected authentication path.
Successfully decrypted key with security token.

5. Format the LUKS partition (do this only once)

sudo mkfs.ext4 /dev/mapper/myluks

6. Mount the partition

sudo mkdir /mnt/myluks #only once
sudo mount /dev/mapper/myluks /mnt/myluks

7. Close the LUKS partition

sudo umount /mnt/myluks
sudo cryptsetup close myluks

Automatically unlocking the LUKS partition at boot

To automatically unlock the LUKS partition at boot, you cannot use the /etc/crypttab file because the network is not available when systemd-cyptsetup is run.

You need to create a systemd service that unlocks the LUKS partition at the right time, after the network is available.

1. Create the bash script that unlocks and mounts the partition

sudo tee -a /root/mount_myluks.sh <<EOF
#!/bin/bash
set -e
set -x
# unlock the partition
cryptsetup open --type luks2  --token-id=0 --token-only /dev/vda4 myluks
# mount the partition
mount /dev/mapper/myluks /mnt/myluks
EOF
sudo chmod +x /root/mount_myluks.sh

2. Create the systemd service file

sudo tee -a /etc/systemd/system/mount_myluks.service <<EOF
[Unit]
Description=open and mount the encrypted /dev/vda4 to /mnt/myluks
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
ExecStart=/bin/bash /root/mount_myluks.sh

[Install]
WantedBy=multi-user.target
EOF

3. Enable the service

> sudo systemctl enable mount_myluks.service

Created symlink /etc/systemd/system/multi-user.target.wants/mount_myluks.service → /etc/systemd/system/mount_myluks.service.
sudo systemctl daemon-reload

4. Reboot the machine to test the service

sudo reboot

The LUKS partition should be automatically unlocked and mounted at boot to /mnt/myluks. Check dmesg, and /var/log/cosmian-pkcs11.log for any errors.

Rotating the keys

To rotate the keys used to encrypt the LUKS partition, you can generate a new key pair and import it into the Eviden KMS.

Then, you can re-enroll the LUKS partition with the new key. You MUST know the passphrase to perform this operation.

1. Wipe the old key from the LUKS partition

sudo systemd-cryptenroll /dev/vda4  --wipe-slot=pkcs11

Wiped slot 1.

2. Revoke the old key from the Eviden KMS

ckms certificates revoke -k 6fc631...  "revoked"

Successfully revoked: 6fc631....

3. Follow the steps to generate a new key pair and import it into the Eviden KMS

4. Enroll the LUKS partition with the new key; you will be prompted for the passphrase

External documentation

Prim'X CRYHOD

The Eviden KMS can be used to unlock Prim'x CRYHOD partitions

Configuring Prim'X CRYHOD

If you get the following pop-up when trying to encrypt a disk

Need additional Configuration

Additional configuration is required.

Launch the CRYHOD policies application.

CRYHOD policies app

Configure the P131 policy:

CRYHOD_P131

For testing, you can set the value to th.

Configure the P821 policy:

CRYHOD_P821

For testing, you can keep the default values.

Configuring the location of the PKCS#11 provider using the policy P296

CRYHOD_p296

Enter the full path of the cosmian_pkcs11.dll as the value name and leave the value empty.

Note: if you change the value, kill all CRYHOD processes or restart for the change to take into effect

Encrypting a partition

Launch Computer Encryption Center

CRYHOD_cyc

And select the partition to encrypt.

Then select Key stored in a smart card or USB device (PKCS#11)

CRYHOD_pkcs11

Click next; the Comsian KMS should appear in the list

CRYHOD_cosmian_kms

Click next

Adjusting the KMS location and authentication

The configuration file is located in the User home .cosmian sub-directory, in the ckms.toml file.

See Authenticating users to the KMS to learn how to configure the KMS to use Open ID Connect or certificate authentication. Please note that the KMS can also manage the machines' certificates.

Here is an example configuration file for the PKCS#11 provider library accessing the KMS using a PKCS#12 file for authentication.

[http_config]
server_url = "https://kms.acme.com:9999"
ssl_client_pkcs12_path = "./certificates/machine123.acme.p12"
ssl_client_pkcs12_password = "machine123_pkcs12_password"

Viewing the logs

By default, the logs are available in the User home .cosmian sub-directory.

Get-Content -path C:\<USER HOME>\.cosmian\cosmian-pkcs11.log -wait

The log level can be adjusted using the COSMIAN_PKCS11_LOGGING_LEVEL environment variable.

setx COSMIAN_PKCS11_LOGGING_LEVEL "debug"

Creating an RSA private key and Certificate to use with CRYHOD

Create an RSA key with 2048 bits and the disk-encryption tag. This tag is the default tag searched. The value can be changed by setting the COSMIAN_PKCS11_DISK_ENCRYPTION_TAG environment variable.

cosmian.exe rsa keys create -s 2048 -t disk-encryption
          Public key unique identifier: ec572e57-eab0-481c-9393-805a11c12ac0_pk
          Private key unique identifier: ec572e57-eab0-481c-9393-805a11c12ac0

VMware vSphere VM Encryption with an External KMS

This guide explains how to protect your VMware vSphere infrastructure using Eviden KMS as an external Key Management Server (KMS), and walks you through the full setup:

  1. Why use an external KMS with vSphere?
  2. Architecture and security principles
  3. What gets protected
  4. Setting up certificates
  5. Configuring the KMS server
  6. Connecting vCenter

Overview

ItemDetails
ProtocolKMIP 1.1 over TCP/TLS with mutual certificate authentication
Port5696 (IANA-registered KMIP port)
Key typeAES-256 symmetric key
vSphere versionvSphere 6.5 and above (Standard Key Provider)
Eviden KMS featureWorks with both FIPS and non-FIPS builds

Why Use an External KMS with vSphere?

VMware vSphere supports three key provider types (since vSphere 6.5/7.0):

Key ProviderAvailable SinceExternal KMS Required
Standard Key ProvidervSphere 6.5Yes (KMIP)
Trusted Key ProvidervSphere 7.0Yes (KMIP + vSphere Trust Authority)
Native Key ProvidervSphere 7.0 U2No (built-in, keys stored in vCenter)

Using a Standard Key Provider backed by an external, dedicated KMS is the recommended approach for production environments and regulated industries because it provides:

  • Separation of duties: encryption keys are stored in a dedicated, hardened system — completely separate from the compute infrastructure they protect. A compromised ESXi host or vCenter cannot expose the master keys.
  • Centralised key lifecycle management: key creation, rotation, revocation, and auditing are managed in one place across all workloads.
  • Compliance readiness: many frameworks (PCI-DSS, HIPAA, GDPR, SOC 2, FedRAMP) explicitly require that encryption keys be managed independently from the data they protect.
  • FIPS 140-3 compliance: Eviden KMS operates in FIPS 140-3 validated mode, satisfying the cryptographic requirements of the most demanding regulatory environments.
  • Zero-trust posture: even with physical access to a storage array or datastore, data remains unintelligible without authorised key access from the KMS.

References


Architecture and Security Principles

Key Hierarchy

vSphere VM encryption uses a two-level key hierarchy to minimize key exposure:

KeyGenerated byStoredPurpose
Key Encryption Key (KEK)External KMS (Eviden)KMS onlyWraps (encrypts) the DEK
Data Encryption Key (DEK)ESXi hostWith the VM (wrapped by KEK)Encrypts VM disk I/O

The KEK never leaves the KMS in plaintext. ESXi hosts receive the KEK transiently in memory to unwrap the DEK; neither key is ever persisted unprotected on the hypervisor or storage.

Communication Flow

sequenceDiagram
    autonumber
    participant Admin as vSphere Admin
    participant VC as vCenter Server
    participant KMS as Eviden KMS
    participant ESXi as ESXi Host
    participant DS as Datastore (VMDK)

    Admin->>VC: Assign VM Storage Policy<br/>(Encryption enabled)
    VC->>KMS: KMIP Create — request new KEK<br/>(mTLS, port 5696)
    KMS-->>VC: KEK ID + KEK (AES-256)
    VC->>ESXi: Push KEK (in-memory, encrypted channel)
    ESXi->>ESXi: Generate DEK locally (AES-256 XTS)
    ESXi->>ESXi: Wrap DEK with KEK → wrapped DEK
    ESXi->>DS: Store VM files encrypted<br/>(VMDK, VMX, swap — AES-256 XTS)
    ESXi->>DS: Store wrapped DEK alongside VM
    Note over KMS,DS: KEK resides only in KMS.<br/>Wrapped DEK stored with VM.<br/>Plaintext DEK exists only in ESXi RAM.

    rect rgb(240, 248, 255)
        Note over VC,ESXi: On VM power-on (key refresh)
        ESXi->>VC: Request KEK for this VM
        VC->>KMS: KMIP Get (KEK ID)<br/>(mTLS)
        KMS-->>VC: KEK
        VC-->>ESXi: KEK (in-memory)
        ESXi->>ESXi: Unwrap DEK using KEK
        ESXi->>DS: Decrypt/Encrypt disk I/O using DEK
    end

Network Architecture

graph TB
    subgraph Mgmt["Management Network (isolated)"]
        VC["vCenter Server\n(KMIP client)"]
    end

    subgraph KMSZone["KMS DMZ / Secure Zone"]
        KMS["Eviden KMS\nport 5696 (KMIP/TLS)\nport 9998 (HTTP API)"]
        DB[("Key Store\n(SQLite / PostgreSQL\n/ Redis-findex)")]
        KMS --- DB
    end

    subgraph Compute["Compute Cluster"]
        ESXi1["ESXi Host 1"]
        ESXi2["ESXi Host 2"]
        ESXiN["ESXi Host N"]
    end

    subgraph Storage["Storage Network"]
        SAN["SAN / NFS Datastore\n(encrypted VMDKs)"]
    end

    VC -- "KMIP 1.1+\nmTLS (port 5696)\nCertificate Auth" --> KMS
    VC -- "Push KEK\n(encrypted, in-memory)" --> ESXi1
    VC -- "Push KEK" --> ESXi2
    VC -- "Push KEK" --> ESXiN
    ESXi1 -- "AES-256 XTS\nencrypted I/O" --> SAN
    ESXi2 -- "AES-256 XTS\nencrypted I/O" --> SAN
    ESXiN -- "AES-256 XTS\nencrypted I/O" --> SAN

    style KMSZone fill:#e8f5e9,stroke:#388e3c
    style Mgmt fill:#e3f2fd,stroke:#1565c0
    style Compute fill:#fff3e0,stroke:#e65100
    style Storage fill:#fce4ec,stroke:#c62828

Security Properties

PropertyHow it is enforced
Encryption at restAll VMDK files, VM swap files, VM core dumps, and the VM configuration (VMX) are encrypted with AES-256 XTS on the ESXi host before any write to the datastore
Key separationKEK lives only inside Eviden KMS; the ESXi host holds the plaintext DEK in RAM only for the duration of active I/O
Mutual TLS (mTLS)Both vCenter and Eviden KMS authenticate each other with X.509 certificates over every KMIP connection; no unauthenticated key request can succeed
No keys on storageThe datastore contains only the wrapped DEK (ciphertext); raw key material is never written to disk outside the KMS
Audit trailEvery KMIP operation (Create, Get, Activate, Revoke, Destroy) is logged by Eviden KMS with timestamp, caller identity, and key ID
Key rotationVMware supports re-keying VMs (shallow re-key: new KEK; deep re-key: new DEK + new KEK) without downtime on vSphere 7.0+
FIPS 140-3Eviden KMS uses a FIPS 140-3 validated OpenSSL 3.x provider; all symmetric keys use AES-256; all asymmetric operations use NIST-approved curves
Revocation / disaster recoveryRevoking the KEK in Eviden KMS immediately prevents any new VM power-on or vMotion, enabling a cryptographic kill-switch

What Gets Protected

Encryption at Rest

When a VM Storage Policy with encryption is applied, the following objects are transparently encrypted:

  • VMDK files (flat and sparse) — all disk data via AES-256 XTS
  • VM configuration file (.vmx)
  • VM swap file (.vswp) — prevents memory snooping via storage
  • VM core dump files (.vmss, .vmem) — prevents key material leakage in snapshots and crash dumps
  • Snapshot delta disks (.vmdk delta files created during snapshot)

Items not encrypted by VM encryption: vSphere log files, NVRAM state, and the vSphere datastore catalogue itself. Use datastore-level encryption (vSAN Encryption or storage-array encryption) as a complement for full coverage.

Encryption in Transit

All key material travels over mutually authenticated TLS:

  • vCenter ↔ KMS: KMIP 1.1+ over TLS 1.2+, enforced by certificate pinning on both sides
  • vCenter ↔ ESXi: key distribution uses the internal vSphere encrypted management channel
  • KMS API: Eviden KMS exposes its management API over HTTPS (TLS 1.2+); the KMIP socket server runs on a dedicated port (default 5696)

Access Control

  • Only vCenter Server (identified by its client certificate) can request keys from the KMS
  • ESXi hosts never communicate directly with the KMS; they receive KEKs from vCenter through the authenticated management plane
  • Eviden KMS access control enforces per-key and per-user permissions; the vCenter service account should be granted the minimum set of KMIP operations (Create, Get, Activate, Revoke, Locate)

Prerequisites

  • OpenSSL (≥ 1.1.1) installed and on your PATH
  • A working copy of openssl.cnf with a [ v3_ca ] section
  • UNIX shell (bash, zsh, etc.)
  • A directory to store your certificates, e.g., /etc/ssl/{{ORG_NAME}}_certs
  • VMware vSphere: 6.5 or higher

1. Generate Your CA

Create a 2048-bit RSA private key for your CA, then issue a self-signed root certificate:

# 1. Generate CA private key
openssl genrsa -out ca.key 2048

# 2. Create self-signed CA certificate (10 year validity)
openssl req -x509 -nodes -days 3650 \
  -new -key ca.key \
  -out ca.crt \
  -config openssl.cnf \
  -extensions v3_ca \
  -subj "/C=<COUNTRY>/ST=<STATE>/L=<CITY>/O=<ORG_NAME>/OU=<UNIT>/CN=<CA_COMMON_NAME>"
  • ca.key: CA private key (keep this highly secure!)
  • ca.crt: Public root certificate, used to sign and verify downstream certificates

2. Generate Server Key & CSR

Create a new 2048-bit RSA key for your KMS server and a CSR including EKU extensions:

openssl req -newkey rsa:2048 -nodes \
  -keyout server.key \
  -out server.csr \
  -subj "/CN=<SERVER_COMMON_NAME>/O=<ORG_NAME>/C=<COUNTRY>" \
  -addext "keyUsage = digitalSignature, keyEncipherment" \
  -addext "extendedKeyUsage = clientAuth, serverAuth"
  • server.key: Server's private key
  • server.csr: Certificate Signing Request, with clientAuth & serverAuth EKUs

3. Sign the Server Certificate

Use your CA to sign the CSR, embedding the same EKU settings in the issued certificate:

openssl x509 -req \
  -in server.csr \
  -CA ca.crt -CAkey ca.key -CAcreateserial \
  -out server.crt \
  -days 365 \
  -extfile <(printf "[req_ext]\n\
keyUsage = digitalSignature,keyEncipherment\n\
extendedKeyUsage = clientAuth,serverAuth\n") \
  -extensions req_ext
  • server.crt: The signed certificate, valid for 1 year

4. Verify the Certificate Extensions

Confirm that your certificate contains the correct EKU fields:

openssl x509 -in server.crt -text -noout | grep -A1 "Extended Key Usage"

Expected output:

            X509v3 Extended Key Usage:
                TLS Web Server Authentication, TLS Web Client Authentication

5. Export to PKCS#12

Bundle your server certificate, private key, and CA chain into a single .p12 archive:

openssl pkcs12 -export \
  -in server.crt \
  -inkey server.key \
  -certfile ca.crt \
  -out server.p12 \
  -name "{{SERVER_ALIAS}}" \
  -passout pass:<P12_PASSWORD>
  • server.p12: PKCS#12 archive containing your key and certificates
  • <P12_PASSWORD>: Password to unlock the archive — use a strong secret!

6. Configure the KMS Server (kms.toml)

Below is a template kms.toml. Update file paths, usernames, and passwords as required:

# General Configuration
default_username = "<USERNAME>"
force_default_username = false
socket_server_start = true

[http]
port = 9998
hostname = "0.0.0.0"
# TLS configuration moved to [tls] section
# See the [tls] section below for certificate configuration
authority_cert_file = "/etc/ssl/{{ORG_NAME}}_certs/ca.crt"

Start the KMS with:

systemctl start cosmian_kms

vCenter Integration

Step 1: Go on your vCenter UI and add a Key Provider

Step 1 – Go on vCenter configuration and set KMS

Step 2: Add new Standard KMS Provider

Step 2 – Add KMS provider in vCenter

Step 3: Set up your Standard Key Provider

Step 3 - Set your Eviden KMS

Step 4: Trust the newly added Eviden KMS

Step 4 - Set your Eviden KMS

Step 5: Establish Trust with the Eviden KMS

Step 5 - Set your Eviden KMS


Step 5 - Set your Eviden KMS


Step 5 - Set your Eviden KMS

Step 6: Go on the KMS server and get .crt and .key certificates

Step 6 - Get KMS certificates

Step 7: Upload KMS Credentials and establish trust

Step 7 - Upload KMS Credentials

Step 8: Your KMS is connected

Step 8 - Set your Eviden KMS

Bonus: Encrypt your Virtual Machine

Step 9 – Final verification screen


Keep all private keys secure and back up your CA key (ca.key) offline in an encrypted vault.

Synology DSM — NAS Volume Encryption with Eviden KMS

Synology DiskStation Manager (DSM) 7.x supports delegating NAS volume encryption key management to an external KMIP-compliant Key Management Server. By connecting a Synology NAS to Eviden KMS, you ensure that volume encryption keys are never stored on the NAS itself: they live in a centrally managed, audited, and optionally HSM-backed key store.


Overview

ItemDetails
ProtocolKMIP 1.2 over TCP/TLS with mutual certificate authentication
Port5696 (IANA-registered KMIP port)
Key typeAES-256 symmetric key
DSM versionDSM 7.1 and above
Eviden KMS featureRequires non-FIPS build (PKCS#12 TLS + AES-CBC key wrapping)

What Synology DSM does

When you configure an external KMS in DSM, the NAS performs the following KMIP operations on every volume creation and every subsequent volume mount:

StepKMIP OperationPurpose
1DiscoverVersionsNegotiate KMIP protocol version
2QueryEnumerate server capabilities (6 query functions)
3LocateCheck whether the volume key already exists
4RegisterRegister a SecretData object (opaque 32-byte key material, KMIP 1.x OperationPolicyName="default" attribute included)
5ActivateTransition the key from PreActive to Active state
6ModifyAttributeRename the key to the volume UUID
7GetAttributesVerify key attributes (state, object type)
8GetRetrieve key material to mount the encrypted volume
9LocateFind the key by name after a NAS reboot
10RevokeRevoke the key during volume deletion or key rotation
11DestroyDelete the key from the KMS

Compatibility fixes

Two fixes were required for full Synology DSM 7.2.2 compatibility:

  • OperationPolicyName (issue #796): DSM includes this KMIP 1.x attribute (deprecated in 1.3, removed in 2.0) in every Register request. The server now silently ignores it instead of emitting a confusing WARN log entry.
  • ModifyAttribute (issue #760): DSM calls ModifyAttribute immediately after Register to rename the key to the volume UUID. This operation was not fully implemented and caused DSM to report "cannot create keys". It is now fully supported.

Prerequisites

  • Eviden KMS server running in non-FIPS mode
  • TLS enabled on the KMS server with a valid PKCS#12 certificate
  • Client certificate and CA certificate for mutual TLS authentication
  • DSM 7.1 or later with the Encryption Key Manager feature enabled (see Control Panel → Security → Encryption Key Manager)

Server-Side Setup

1. Configure TLS

Synology DSM requires mutual certificate authentication over TLS. Edit your kms.toml:

[tls]
tls_p12_file    = "/etc/cosmian/kms/server.p12"
tls_p12_password = "your-p12-password"
clients_ca_cert_file = "/etc/cosmian/kms/clients-ca.crt"

[socket_server]
socket_server_start    = true
socket_server_port     = 5696        # standard KMIP port
socket_server_hostname = "0.0.0.0"

2. Issue a client certificate for the NAS

The NAS identifies itself to the KMS using a client TLS certificate signed by the clients_ca_cert_file CA. Use OpenSSL or the cosmian CLI to generate the certificate:

# Generate a CA and a client certificate (example using OpenSSL)
openssl genrsa -out clients-ca.key 4096
openssl req -new -x509 -days 3650 -key clients-ca.key \
  -subj "/CN=KMS Clients CA" -out clients-ca.crt

openssl genrsa -out synology-nas.key 2048
openssl req -new -key synology-nas.key \
  -subj "/CN=synology-nas-01" -out synology-nas.csr
openssl x509 -req -days 3650 -in synology-nas.csr \
  -CA clients-ca.crt -CAkey clients-ca.key -CAcreateserial \
  -out synology-nas.crt

3. Start Eviden KMS

cosmian_kms --config /etc/cosmian/kms/kms.toml

Or via Docker:

docker run -p 9998:9998 -p 5696:5696 \
  -v /etc/cosmian/kms:/etc/cosmian/kms:ro \
  -e COSMIAN_KMS_CONF=/etc/cosmian/kms/kms.toml \
  ghcr.io/cosmian/kms:latest

DSM-Side Configuration

  1. Log in to Synology DSM as an administrator.
  2. Go to Control Panel → Security → Encryption Key Manager.
  3. Click Add KMS Server.
  4. Fill in the connection details:
FieldValue
KMS Server AddressIP or hostname of the Eviden KMS host
Port5696
Client CertificatePEM or PKCS#12 file you issued for the NAS
Private KeyMatching private key (if not bundled in PKCS#12)
CA Certificateclients-ca.crt (the CA that signed the KMS server cert)
  1. Click Test Connection — DSM will run a DiscoverVersions + Query probe.
  2. Click Save.

Encrypting a volume

  1. Open Storage Manager.
  2. Select the volume you want to encrypt.
  3. Click Encrypt and choose the KMS server you just configured.
  4. DSM will call Create → Activate → Get → GetAttributes and mount the encrypted volume.

Automated CI Testing

Because Synology DSM is proprietary hardware/software, there is no official Docker image available for automated testing. Instead, the Eviden KMS test suite includes a Python simulation client (scripts/synology_dsm_client.py) that replays the exact KMIP operation sequence performed by DSM.

Running the simulation locally

# Build KMS server (non-FIPS required)
cargo build --bin cosmian_kms --features non-fips

# Start the KMS server with KMIP socket enabled (uses scripts/kms.toml)
COSMIAN_KMS_CONF=scripts/kms.toml \
  cargo run --bin cosmian_kms --features non-fips &

# Wait until both ports are ready (HTTP: 9998, KMIP: 15696)
# Then run the DSM simulation
python3.11 scripts/synology_dsm_client.py \
  --configuration scripts/pykmip.conf \
  --verbose

Or use the convenience test runner:

bash scripts/test_synology_dsm.sh simulate
mise run test:synology_dsm --variant non-fips

GitHub Actions CI

The Synology DSM simulation is included in the CI matrix in .github/workflows/test_all.yml under the synology_dsm job (non-FIPS only):

matrix:
  type:
    - ...
    - synology_dsm
  features: [fips, non-fips]
  exclude:
    - type: synology_dsm
      features: fips

The CI job:

  1. Builds Eviden KMS with --features non-fips.
  2. Starts the server with TLS and the KMIP socket enabled.
  3. Runs scripts/synology_dsm_client.py against it.
  4. Asserts that all 10 steps (DiscoverVersions → Destroy) succeed.

Simulation Script Reference

ScriptPurpose
scripts/synology_dsm_client.pyPython KMIP client replicating DSM's operation sequence
scripts/test_synology_dsm.shLocal test runner (prereq checks + venv activation)
.mise/scripts/test_synology_dsm.shCI entry point (builds server, starts it, runs test)
scripts/pykmip.confShared PyKMIP TLS configuration (host, port, certs)
scripts/kms.tomlKMS server configuration used by KMIP tests

Troubleshooting

DSM reports "Connection failed"

  • Verify that port 5696 is reachable from the NAS (firewall rules).
  • Confirm socket_server_start = true and tls_p12_file is set in kms.toml.
  • Check KMS logs: RUST_LOG=cosmian_kms_server=debug cargo run …

DSM reports "Certificate verification failed"

  • The NAS client certificate must be signed by the CA listed in clients_ca_cert_file in kms.toml.
  • The KMS server certificate must be trusted by the NAS. Upload the server's CA certificate to DSM under Control Panel → Security → Certificate.

ModifyAttribute returns an error

  • Ensure you are running Eviden KMS ≥ 5.17 which includes the ModifyAttribute fix for Synology DSM compatibility (issue #760).
  • The operation requires the key to be in Active state. Call Activate before ModifyAttribute.

Key not found after NAS reboot

  • DSM uses Locate with the key name to reconnect. Ensure the key name is unique and consistent across reboots.
  • Check that the client certificate presented on reconnect is the same one used at creation time (or that both have access rights granted in the KMS access policy).

OperationPolicyName warning in server logs

Older DSM versions (using the KMIP 1.0 protocol) include an OperationPolicyName attribute in their Register/Create requests. This attribute was deprecated in KMIP 1.3 and removed in KMIP 2.0+. Eviden KMS ≥ 5.18 silently ignores it (issue #796). Earlier versions log a harmless WARN entry; the key operation still succeeds.


Security Considerations

  • Mutual TLS: Always require client certificate authentication (clients_ca_cert_file set in kms.toml) so that only authorised NAS devices can access keys.
  • Key access policy: Use the Eviden KMS policy to restrict each NAS to its own keys.
  • Key rotation: Revoke and re-create keys periodically. DSM will re-encrypt the volume DEK with the new KMS key on the next mount.
  • Audit logging: Enable KMS access logs (RUST_LOG=info) and forward them to a SIEM for compliance.

VAST Data — Storage Encryption with Eviden KMS

VAST Data storage clusters use KMIP for external encryption key management (EKM). By connecting a VAST Data cluster to Eviden KMS, you ensure that data encryption keys (DEKs) and key encryption keys (KEKs) are centrally managed, audited, and never stored unprotected on the storage appliance.


Overview

ItemDetails
ProtocolKMIP 1.4 binary TTLV over HTTP/TLS with mutual certificate authentication
EndpointPOST /kmip on the KMS HTTP port (default 9998)
Key typesAES-256 symmetric keys
Key creationIn batches of 2–3 keys per encryption group
VAST versionVAST Data Platform 5.x and above
Eviden KMS modeFIPS and non-FIPS builds supported

What VAST Data does

When you configure an external KMS in the VAST Data management console, the storage cluster performs the following KMIP operations for encryption key lifecycle management:

StepKMIP OperationPurpose
1DiscoverVersionsSession initialization handshake (once per connection)
2CreateCreate an AES-256 symmetric key (CryptographicUsageMask = Encrypt|Decrypt)
3AddAttribute ×3Set Name, ObjectGroup, and OperationPolicyName (3 calls per key)
4ActivateTransition the key from Pre-Active to Active state
5LocateFind a key by its VAST-assigned name (VAST_EKM_KEY_2_<uuid>_<index>)
6GetRetrieve plaintext key material
7GetAttributesVerify key State (Active) and ActivationDate (polled every ~61 seconds)
8ReKeyRotate an active key — generates new key material with a new Unique Identifier
9RevokeRevoke a key during decommissioning
10DestroyPermanently delete the key from the KMS

Key lifecycle workflow

The following sequence diagram shows the complete lifecycle as observed in production logs (June 2026):

sequenceDiagram
    participant V as VAST Data
    participant K as Eviden KMS

    Note over V,K: Session Start
    V->>K: DiscoverVersions (KMIP 1.4)

    Note over V,K: Key Creation (per encrypted path, 2–3 keys)
    loop For each key in group
        V->>K: Create (AES-256 SymmetricKey)
        V->>K: AddAttribute (Name)
        V->>K: AddAttribute (ObjectGroup)
        V->>K: AddAttribute (OperationPolicyName)
        V->>K: Activate
    end

    Note over V,K: Initial Key Fetch
    loop For each key
        V->>K: Locate (by name)
        V->>K: Get (plaintext key material)
    end

    Note over V,K: Continuous Monitoring (~61s interval)
    loop Forever
        loop For each key
            V->>K: Locate (by name)
            V->>K: GetAttributes (State, ActivationDate)
        end
    end

    Note over V,K: Key Rotation (triggered externally)
    loop For each key to rotate
        V->>K: Locate (find current key by name)
        V->>K: ReKey (new UID returned, old key stays Active)
        V->>K: Locate (verify new key by name)
        V->>K: Get (fetch new key material)
    end

    Note over V,K: Key Decommissioning (later, on encrypted path deletion)
    loop For each key to retire
        V->>K: Locate (find key by name)
        V->>K: Revoke (Active → Deactivated)
        V->>K: Locate (confirm state)
        V->>K: Destroy (permanently delete)
    end

Key naming convention

VAST creates keys with structured names following the pattern:

VAST_EKM_KEY_2_<encryption_group_uuid>_<index>
  • VAST_EKM_KEY_2_ — static prefix (version 2 of VAST's naming scheme)
  • <encryption_group_uuid> — UUID identifying the encrypted path/group
  • _<index> — 0-based index within the group (typically 0 or 1)

These names are used in Locate operations to find keys associated with specific encryption groups. After ReKey, the name is transferred to the new replacement key.


Prerequisites

  • Eviden KMS server running (FIPS or non-FIPS mode)
  • TLS enabled on the KMS with mutual certificate authentication
  • Client certificate and CA certificate configured for the VAST cluster
  • VAST Data Platform 5.x or later with External Key Manager (EKM) feature enabled

Server-Side Setup

1. Configure TLS

VAST Data connects via HTTP POST to /kmip using KMIP 1.4 binary TTLV with mutual TLS authentication. Configure your kms.toml:

[tls]
tls_cert_file        = "/etc/cosmian/kms/server.crt"
tls_key_file         = "/etc/cosmian/kms/server.key"
clients_ca_cert_file = "/etc/cosmian/kms/clients-ca.crt"

2. Generate client certificates

Create a client certificate for the VAST cluster signed by the same CA configured in clients_ca_cert_file:

# Generate VAST client key and CSR
openssl genrsa -out vast-client.key 2048
openssl req -new -key vast-client.key -out vast-client.csr \
    -subj "/CN=vast-cluster-01/O=VAST Data"

# Sign with CA
openssl x509 -req -in vast-client.csr \
    -CA clients-ca.crt -CAkey clients-ca.key -CAcreateserial \
    -out vast-client.crt -days 365

# Convert to PKCS#12 for VAST (if required by your VAST version)
openssl pkcs12 -export -in vast-client.crt -inkey vast-client.key \
    -out vast-client.p12 -name "vast-cluster-01"

VAST-Side Configuration

1. Navigate to EKM settings

In the VAST Data management console:

  1. Go to Settings → Security → External Key Manager
  2. Click Add External KMS

2. Enter KMS connection details

FieldValue
KMS Address<kms-server-hostname>
KMS Port9998 (default KMS HTTP port)
Client CertificateUpload vast-client.crt
Client KeyUpload vast-client.key
CA CertificateUpload the CA that signed the KMS server certificate

3. Test connection

Use the Test Connection button in the VAST management console to verify connectivity. A successful test performs a Create + Get + Destroy cycle.


Compatibility Notes

KMIP 1.x attributes

VAST sends the OperationPolicyName("default") attribute via AddAttribute after key creation (or inline in the Create template, depending on VAST version). This is a KMIP 1.x attribute that was deprecated in KMIP 1.3 and removed in KMIP 2.0. The Eviden KMS stores this attribute as a vendor extension and returns it in GetAttributes responses to KMIP 1.4 clients. A log warning is emitted during deserialization:

WARN KMIP 2.1 does not support the KMIP 1 attribute OperationPolicyName("default")

This warning is informational and does not affect functionality. The attribute is preserved across ReKey rotations (transferred to the replacement key along with Name and other attributes).

ReKey behavior

When VAST sends a ReKey request, the KMS implements KMIP 2.1 §6.1.46:

  1. The KMS creates a new symmetric key with a new Unique Identifier
  2. The Name attribute is transferred from the old key to the new key
  3. Bidirectional links are set: ReplacementObjectLink on old → new, ReplacedObjectLink on new → old
  4. The old key's State is unchanged (it remains Active) — the KMIP spec does not deactivate the existing key during ReKey
  5. VAST can then Locate by name and finds the new key

New UUID after ReKey — old key remains Active

VAST expects the ReKey response to return a different Unique Identifier from the original. After rotation, the old key remains Active in the database. Later, when VAST decommissions an encrypted path, it sends LocateRevokeLocateDestroy for each key to retire.

Monitoring interval

VAST polls the KMS every ~61 seconds per key with Locate + GetAttributes to verify keys remain in Active state with a valid ActivationDate. This is normal health-check behavior and produces high-volume but lightweight traffic.


Troubleshooting

SymptomCauseFix
unsupported KMIP 1 operation: ReKeyKMS version < 5.22.0 missing ReKey supportUpgrade Eviden KMS to 5.22.0+
Rotate key fails / cannot create new encrypted pathKMS < 5.23.0 dropped OperationPolicyName via AddAttributeUpgrade to 5.23.0+; re-apply OPN on affected keys with AddAttribute
OperationPolicyName warnings in KMS logsNormal — VAST sends this deprecated KMIP 1.x attributeNo action required; informational warning only
TLS handshake failureCertificate mismatch or missing CAVerify clients_ca_cert_file matches the CA that signed VAST's client cert
tlsv1 alert decrypt error (SSL alert 51) in KMS logsVAST background reconnection attempt with stale connection stateTransient; no action required — the KMIP workflow itself is unaffected
Connection reset by peer (os error 104)Network instabilityTransient; VAST will reconnect automatically

Verified Operations

The following KMIP operations have been validated with VAST Data production environments (logs from June 2026):

OperationStatusNotes
DiscoverVersionsSession initialization; confirms KMIP 1.4 support
CreateAES-256 SymmetricKey; 76 keys created across 5 days
AddAttributeCalled 3× per key: Name, ObjectGroup, OperationPolicyName
ActivateTransitions key to Active state
LocateBy Name (UninterpretedTextString); ~11,800 calls over 5 days
GetPlaintext key material retrieval; 182 calls
GetAttributesState + ActivationDate; ~11,500 calls (monitoring)
ReKeyKey rotation with new UUID; 16 rotations observed
RevokeKey revocation before destruction; 69 calls
DestroyPermanent key deletion; 69 calls

Veeam Backup & Replication — KMS Encryption Key Management

Veeam Backup & Replication supports delegating backup encryption key management to an external KMIP-compliant Key Management Server. By connecting Veeam to Eviden KMS, encryption keys for backup jobs are never stored on the Veeam infrastructure itself: they are managed in a centrally audited, optionally HSM-backed key store.


Overview

ItemDetails
ProtocolKMIP 1.4 over TCP/TLS with mutual certificate authentication
Port5696 (IANA-registered KMIP port)
Key typeRSA-2048 asymmetric key pair
Veeam versionVeeam Backup & Replication 12 and above
Eviden KMS featureWorks with both FIPS and non-FIPS builds

What Veeam does

When you configure an external KMS in Veeam, the Veeam server performs the following KMIP operations for each protected backup job:

StepKMIP OperationPurpose
1LocateFind an existing key by name / identifier
2Get (PublicKey)Retrieve the RSA public key to encrypt backup metadata
3CreateKeyPairCreate a new RSA-2048 key pair when none exists
4ActivateTransition the key pair from PreActive to Active state
5Get (PrivateKey)Retrieve the RSA private key for decryption during restore
6DestroyDelete the key pair on job removal or rotation

Compatibility fixes in Eviden KMS

Two fixes were required for full Veeam Backup & Replication compatibility. Both are included in Eviden KMS as of the version that introduced this documentation:

  • KeyValue attributes in Get response (bug fix): Veeam's KMIP 1.4 decoder for PublicKey and PrivateKey expects the KeyValue structure to contain only key material — it does not support any Attribute elements inside KeyValue. Previous versions of Eviden KMS embedded all object metadata attributes (state, identifiers, links, etc.) inside KeyValue when converting from the internal KMIP-2.1 representation to KMIP-1.4 wire format. This caused Veeam to throw KmipUnexpectedTagException: Unexpected Tag 66, expected Attribute and abort the key retrieval. The server now strips all attributes from KeyValue for asymmetric keys when responding to KMIP 1.x clients.

  • TLS session ID context (bug fix): Veeam reuses KMIP connections via TLS session resumption. When both SSL_VERIFY_PEER and OpenSSL session caching are active, OpenSSL requires a session-ID context to be set on the server acceptor. Without it, session-resumption attempts fail with ssl_get_prev_session:session id context uninitialized, causing each reconnect to produce an SSPI authentication error on the Veeam side. The server now calls SSL_CTX_set_session_id_context during TLS acceptor initialisation.


Prerequisites

  • Eviden KMS server 4.x or later (both FIPS and non-FIPS builds are supported)
  • TLS enabled on the KMS socket server (port 5696) with mutual certificate authentication
  • A server TLS certificate and the corresponding CA to be trusted by Veeam
  • A client certificate (signed by a CA known to the KMS) to authenticate Veeam

Server-Side Setup

1. Configure TLS and the KMIP socket server

Edit your kms.toml:

[tls]
tls_p12_file     = "/etc/cosmian/kms/server.p12"
tls_p12_password = "your-p12-password"
clients_ca_cert_file = "/etc/cosmian/kms/clients-ca.crt"

[socket_server]
socket_server_start    = true
socket_server_port     = 5696
socket_server_hostname = "0.0.0.0"

2. Issue a client certificate for Veeam

Veeam identifies itself to the KMS using a client TLS certificate signed by the clients_ca_cert_file CA:

# Generate a CA and a client certificate
openssl genrsa -out clients-ca.key 4096
openssl req -new -x509 -days 3650 -key clients-ca.key \
  -subj "/CN=KMS Clients CA" -out clients-ca.crt

openssl genrsa -out veeam-client.key 2048
openssl req -new -key veeam-client.key \
  -subj "/CN=veeam-backup-server" -out veeam-client.csr
openssl x509 -req -days 3650 -in veeam-client.csr \
  -CA clients-ca.crt -CAkey clients-ca.key -CAcreateserial \
  -out veeam-client.crt

Bundle the client certificate and key as a PKCS#12 file for import into Veeam:

openssl pkcs12 -export \
  -in veeam-client.crt -inkey veeam-client.key \
  -certfile clients-ca.crt \
  -out veeam-client.p12 -passout pass:veeam-p12-password

3. Start Eviden KMS

cosmian_kms --config /etc/cosmian/kms/kms.toml

Or via Docker (note the -p 5696:5696 for the KMIP socket port):

docker run -p 9998:9998 -p 5696:5696 \
  -v /etc/cosmian/kms:/etc/cosmian/kms:ro \
  -e COSMIAN_KMS_CONF=/etc/cosmian/kms/kms.toml \
  ghcr.io/cosmian/kms:latest

Veeam-Side Configuration

  1. In the Veeam Backup & Replication console, open Menu → Manage Credentials (or Encryption Manager depending on your version).
  2. Add a new KMS Server with the following parameters:
FieldValue
Server name / IPHostname or IP of your Eviden KMS host
Port5696
CertificateUpload the PKCS#12 file (veeam-client.p12)
PasswordThe PKCS#12 password
KMS CA CertificateUpload the KMS server CA certificate (ca.crt or the CA that signed the server certificate)
  1. Click Test Connection. Veeam will perform a Locate probe against the server. A successful test shows a green check-mark.
  2. Assign the KMS server to a backup job under Job Properties → Storage → Advanced → Encryption.

Troubleshooting

SymptomLikely causeFix
KmipUnexpectedTagException: Unexpected Tag 66, expected AttributeOld Eviden KMS version (pre-fix) embedding attributes inside KeyValueUpgrade Eviden KMS
A call to SSPI failed / SSL_ERROR_SSL on reconnectTLS session-ID context not setUpgrade Eviden KMS
Test Connection fails with certificate errorCA mismatch or self-signed certVerify clients_ca_cert_file contains the CA that signed the Veeam client cert, and that Veeam trusts the KMS server certificate
Key not found after Veeam server migrationUnique identifier stored by Veeam differs from KMSRe-configure the KMS server entry in Veeam; the keys remain in the KMS store

Snowflake Native App for Large-Scale Data Encryption

Eviden offers a Snowflake Native App that seamlessly integrates with the Eviden KMS to provide robust encryption and decryption capabilities directly within your Snowflake environment. This solution builds upon the Python UDF library to deliver enterprise-grade cryptographic operations at scale.

The Native App exposes dedicated encrypt and decrypt functions that can be called directly from Snowflake SQL queries, allowing you to implement column-level encryption without modifying your existing data workflows.

You can find the Snowflake Native App on GitHub, where comprehensive documentation covers deployment procedures, configuration options, and usage examples to help you get started quickly.

User Defined Functions in Python for Eviden KMS

Eviden maintains a Python UDF library that enables efficient, large-scale cryptographic operations with the Eviden KMS.

Purpose and Applications

These User Defined Functions (UDFs) are designed specifically for:

  • Data processing platforms like PySpark, Databricks, Snowflake, and Kafka
  • Implementing application-level encryption in Big Data environments
  • Protecting sensitive data by encrypting it before storage and only decrypting during processing
  • Minimizing clear-text exposure of sensitive information

Technical Capabilities

The library provides:

  • Batch processing capabilities for efficient encryption/decryption
  • Parallelization to optimize throughput
  • Support for multiple encryption contexts and key identifiers
  • Performance optimizations for Big Data workloads

Expected throughput is in the range of 5 million decryption in 5 seconds with a 10 vCPU KMS instance.

Supported Cryptographic Algorithms

The UDFs support several NIST-approved and RFC-standardized algorithms:

  • AES-GCM (NIST SP 800-38D): Standard authenticated encryption
  • AES-GCM-SIV (RFC 8452): Deterministic authenticated encryption with nonce misuse resistance
  • AES-XTS (NIST SP 800-38E): Storage encryption for fixed-length data
  • ChaCha20-Poly1305 (RFC 8439): High-performance authenticated encryption

Performance Considerations

Key performance factors include:

  • Network latency between the UDF execution environment and KMS server
  • Batch size optimization for your specific workload
  • Algorithm selection based on security requirements
  • Potential use of deterministic encryption (like AES-GCM-SIV) for performance-critical applications

Check the encryption and decryption a scale section for details.

Getting Started

For implementation details, examples, and best practices, refer to the GitHub repository, particularly the tests directory which contains practical code samples for various use cases.

FortiGate / FortiOS — External KMS via KMIP

Fortinet FortiGate appliances running FortiOS 7.4+ support delegating cryptographic key storage to an external KMIP-compliant Key Management Server. Connecting FortiOS to Eviden KMS lets network appliances retrieve their encryption keys from a centrally audited, optionally HSM-backed key store.


Overview

ItemDetails
ProtocolKMIP 1.0–1.4 over TCP/TLS
Port5696 (IANA-registered KMIP port)
FortiOS versionFortiOS 7.4 and above (tested on FortiOS 7.6.0 / FortiGate 40F)
Eviden KMS featureWorks with both FIPS and non-FIPS builds

KMIP operations used by FortiOS

KMIP OperationPurpose
CreateCreate a symmetric key
LocateFind an existing key by name using TemplateAttribute filter
GetRetrieve key material
ActivateTransition the key to Active state
DestroyDelete a key on removal or rotation

KMIP 1.0/1.4 compatibility in Eviden KMS

FortiOS uses an older KMIP 1.x encoding that required specific server-side fixes, included as of Eviden KMS 5.22.0:

  • Authentication wrapper (bug fix): FortiOS wraps its credentials using the full Authentication { Credential { CredentialType, CredentialValue } } nesting required by the KMIP 1.0 specification. Earlier server versions looked for CredentialType as a direct child of Authentication and failed with missing field 'CredentialType'. The TTLV deserializer now handles the correct nesting for all KMIP 1.x clients.

  • Locate name filter via TemplateAttribute (bug fix): FortiOS wraps Attribute items inside a TemplateAttribute structure in the Locate request payload. Without the matching template_attribute field on the KMIP 1.4 Locate type, the server silently discarded the name filter, causing every Locate to match all objects and MaximumItems=1 to always return the same first key regardless of the requested name. The server now correctly reads TemplateAttribute-wrapped filters from KMIP 1.0/1.1 clients.


Configuration

1. Enable the KMIP socket server

Add a [socket_server] section to your kms.toml:

[socket_server]
port = 5696
# TLS is required; FortiOS verifies the server certificate
tls_cert_file = "/etc/kms/server.crt"
tls_key_file  = "/etc/kms/server.key"
# Optional: require client certificates
# tls_ca_file = "/etc/kms/ca.crt"

See Enabling TLS and Configuration file reference for full details.

2. Configure FortiOS

In the FortiGate web UI (or via CLI):

config system kmip
    set status enable
    set server-ip   <KMS server IP>
    set server-port 5696
    set ca-cert     <path to KMS CA certificate>
end

Refer to the Fortinet KMIP documentation for your FortiOS version to complete key-encryption policy assignment.


OpenSSH Integration

The Eviden KMS PKCS#11 provider enables OpenSSH to use SSH keys stored in the KMS for authentication. The private key never leaves the KMS: every signing operation is performed server-side and only the signature is returned to the SSH client.


How it works

  1. You create an asymmetric key pair in the KMS and tag it with ssh-auth.
  2. You export the corresponding public key and add it to ~/.ssh/authorized_keys on every server you want to reach.
  3. You tell OpenSSH to use the Eviden PKCS#11 library (libcosmian_pkcs11.so) as its hardware security token provider.
  4. When you ssh to a server, OpenSSH calls C_Sign through the library, which forwards the signing request to the KMS. Only the signature comes back.

Supported algorithms

AlgorithmKey size / curvePKCS#11 mechanism
ECDSANIST P-256CKM_ECDSA
ECDSANIST P-384CKM_ECDSA
EdDSAEd25519CKM_EDDSA
RSA PKCS#1 v1.52048 bitCKM_RSA_PKCS
RSA PKCS#1 v1.54096 bitCKM_RSA_PKCS

Prerequisites

  • A running Eviden KMS instance (see the Quick-start guide).
  • The ckms CLI configured and authenticated against it.
  • OpenSSH client ≥ 7.3 (ships with every modern Linux / macOS).
  • The libcosmian_pkcs11.so (Linux) or libcosmian_pkcs11.dylib (macOS) shared library. If you installed the Eviden KMS CLI package this is already at /usr/local/lib/libcosmian_pkcs11.so.

Step 1 — Create the SSH key pair in the KMS

Tag the key pair with ssh-auth so that the PKCS#11 provider can discover it.

ckms ec keys create --curve nist-p256 --tag ssh-auth --tag my-laptop

Ed25519 (non-FIPS builds only)

ckms ec keys create --curve ed25519 --tag ssh-auth --tag my-laptop

RSA 4096

ckms rsa keys create --size_in_bits 4096 --tag ssh-auth --tag my-laptop

After the command succeeds, record the private-key ID and public-key ID printed by the CLI:

Private key unique identifier  : 3fa85f64-5717-4562-b3fc-2c963f66afa6
Public  key unique identifier  : 3fa85f64-5717-4562-b3fc-2c963f66afa6_pk

Step 2 — Export the public key for authorized_keys

Export the public key from the KMS in PKCS#8 / SubjectPublicKeyInfo format and then convert it to the OpenSSH wire format that goes into authorized_keys.

# Export the public key as PEM-encoded SubjectPublicKeyInfo (SPKI)
ckms ec keys export \
  --key-id 3fa85f64-5717-4562-b3fc-2c963f66afa6_pk \
  --key-format pkcs8-pem \
  /tmp/id_ecdsa_kms.pub.pem

# Convert to OpenSSH public key format
ssh-keygen -f /tmp/id_ecdsa_kms.pub.pem -e -m pkcs8 > /tmp/id_ecdsa_kms.pub

# Optionally add a comment
cat /tmp/id_ecdsa_kms.pub
# ecdsa-sha2-nistp256 AAAA…== (no comment yet)

# Append to authorized_keys on each target server
cat /tmp/id_ecdsa_kms.pub | ssh user@server "cat >> ~/.ssh/authorized_keys"

For RSA keys, substitute ckms ec keys export with ckms rsa keys export.


Step 3 — Configure the OpenSSH client

Option A — Per-connection flag

ssh -I /usr/local/lib/libcosmian_pkcs11.so user@server

Option B — Add to ~/.ssh/config (permanent)

Host *
    PKCS11Provider /usr/local/lib/libcosmian_pkcs11.so

Or scope it to a specific host:

Host my-server.example.com
    User     alice
    PKCS11Provider /usr/local/lib/libcosmian_pkcs11.so

Environment variables

VariableDefaultDescription
COSMIAN_KMS_CLI_CONF~/.cosmian/kms.tomlPath to the ckms client config (KMS URL, credentials).
COSMIAN_PKCS11_LOGGING_LEVELinfoLog level for the provider: trace, debug, info, warn, error.
COSMIAN_PKCS11_SSH_KEY_TAGssh-authThe KMS vendor tag used to discover SSH key pairs. Override to use a custom tag.

Verification

# List the key pairs that the provider will expose
COSMIAN_PKCS11_LOGGING_LEVEL=debug ssh -I /usr/local/lib/libcosmian_pkcs11.so user@server whoami

You should see lines similar to the following in the debug output and the remote command's output:

debug1: provider /usr/local/lib/libcosmian_pkcs11.so: manufacturerID <cosmian>
debug1: have 1 keys from provider /usr/local/lib/libcosmian_pkcs11.so

Troubleshooting

no keys found or authentication rejected

  • Verify the public key is in ~/.ssh/authorized_keys on the server.
  • Check the KMS tag: the private and public key pair must be tagged with ssh-auth (or the value you set in COSMIAN_PKCS11_SSH_KEY_TAG).
  • Run ckms ec keys locate --tag ssh-auth to confirm the KMS returns your key.

provider ... not supported or unsupported algorithm

  • The Ed25519 / EdDSA mechanism (CKM_EDDSA) requires a non-FIPS build of the Eviden KMS and library. In FIPS mode, use ECDSA P-256 or RSA instead.

COSMIAN_KMS_CLI_CONF not set or connection refused

  • The library reads the same ckms configuration file as the CLI. Make sure the environment variable points to a valid configuration and that the KMS server is reachable.

macOS — library not loaded

macOS requires code-signed PKCS#11 libraries to be loaded by SSH. Use the .dylib built for macOS and ensure it is in your library path:

ssh -I /usr/local/lib/libcosmian_pkcs11.dylib user@server

The Eviden KMS strives to be compatible with the PyKMIP client library, which is widely used for KMIP protocol interactions. This document provides a comprehensive guide to testing and validating PyKMIP compatibility with the Eviden KMS server.

Overview

ItemDetails
ProtocolKMIP 1.2 over TCP/TLS with mutual certificate authentication
Port5696 (IANA-registered KMIP port)
LibraryPyKMIP (Python KMIP client library)
Eviden KMS featureRequires non-FIPS build

Purpose of PyKMIP compatibility tests

The PyKMIP compatibility tests serve to:

  • Verify that our KMS server correctly implements the KMIP protocol standard
  • Ensure interoperability with existing KMIP client implementations
  • Catch any regression issues that might break compatibility with PyKMIP clients
  • Validate that common KMIP operations work as expected across different client implementations
  • Test edge cases and error handling for unsupported operations

Prerequisites

  • Python 3.x installed on your system
  • PyKMIP library (will be installed by setup script)
  • Eviden KMS server running and accessible with KMIP socket server enabled
  • All scripts must be executed from the project root directory (not from within the scripts/ directory)
  • Virtual environment (.venv) set up and activated for Python dependencies

Quick Start

The fastest way to test PyKMIP compatibility is to use the automated test runner:

# 1. Set up the environment (from project root)
./scripts/setup_pykmip.sh

# 2. Start the KMS server (in another terminal)\
pkill -f cosmian_kms  # Stop any existing instance
COSMIAN_KMS_CONF=./scripts/kms.toml cargo run --bin cosmian_kms --features non-fips

# 3. Run all compatibility tests
./scripts/test_pykmip.sh all

# 4. Or run specific operations
./scripts/test_pykmip.sh query -v
./scripts/test_pykmip.sh create
./scripts/test_pykmip.sh check

The test runner automatically:

  • Activates the Python virtual environment
  • Validates prerequisites and connectivity
  • Executes the requested operations with proper error handling
  • Provides detailed output and status reporting
  • Runs with timeout protection to prevent hanging tests

Supported Operations

The current test suite supports the following KMIP operations:

Core Operations

  • query - Discover server capabilities and protocol versions
  • create - Create symmetric keys
  • create_keypair - Create RSA key pairs
  • get - Retrieve an object
  • get_attributes - Retrieve object attributes
  • destroy - Delete objects from the server
  • locate - Find objects by search criteria

Cryptographic Operations

  • encrypt - Encrypt data using managed keys
  • decrypt - Decrypt data using managed keys
  • mac - Generate Message Authentication Codes
  • activate - Activate objects for use

Management Operations

  • revoke - Revoke objects and certificates
  • discover_versions - Discover supported KMIP protocol versions

Special Commands

  • all - Execute all supported operations in sequence
  • check - Validate prerequisites and connectivity
  • rust-test - Run Rust-based PyKMIP integration tests

Experimental/Unsupported Operations

  • certify - Certificate signing (implemented via separate module pykmip_certify.py, uses operations not yet supported by Eviden KMS)

Note: Some operations may fail due to server limitations or KMIP 1.x compatibility constraints. The test runner handles these gracefully and provides detailed error reporting.

Understanding Test Results

The test script provides comprehensive output for each operation with clear status indicators:

Success Case

Operations that complete successfully will show:

{
  "operation": "Create",
  "status": "success",
  "unique_identifier": "abc123-def456-789",
  "additional_info": {
    ...
  }
}

Failure Case

Operations that fail will show:

{
  "operation": "MAC",
  "status": "error",
  "error": "Invalid parameter provided",
  "details": "Cryptographic algorithm not supported"
}

Test Summary

After running all operations, you'll see a summary like:

======================================
FINAL TEST RESULTS SUMMARY
======================================
✅ SUCCESSFUL operations (12/13):
  ✅ query
  ✅ create
  ✅ get_attributes
  ✅ get
  ✅ destroy
  ✅ encrypt
  ✅ decrypt
  ✅ locate
  ✅ discover_versions
  ✅ mac
  ✅ activate
  ✅ revoke
  ✅ create_keypair
  🔍 certify (not supported in PyKMIP - workaround under study)

Operation Status Types

  • ✅ SUCCESS: Operation completed and returned successful status
  • ❌ FAILED: Operation returned error status or threw exception
  • ⚠️ TIMEOUT: Operation timed out after 30 seconds
  • 🔍 UNSUPPORTED: Operation not supported by server or KMIP version

Troubleshooting

Common Issues

  1. Script execution fails: Ensure you're running from the project root, not from scripts/

    # Wrong - don't do this
    cd scripts && ./test_pykmip.sh
    
    # Correct - run from project root
    ./scripts/test_pykmip.sh all
    
  2. PyKMIP import errors: Run setup_pykmip.sh to install dependencies

    Error: No module named 'kmip'
    Solution: ./scripts/setup_pykmip.sh
    
  3. Connection refused: Verify the KMS server is running with KMIP socket server enabled

    Error: Connection refused on port 5696
    Solution: Check server is running and socket_server_start = true in config
    
  4. Virtual environment not found: The test runner requires a .venv directory

    Error: Virtual environment not found
    Solution: Run ./scripts/setup_pykmip.sh to create .venv
    
  5. TLS/Certificate errors: Verify certificate paths and validity

    Error: [SSL: CERTIFICATE_VERIFY_FAILED]
    Solution: Check certificate files in test_data/certificates/client_server/
    
  6. Operation timeouts: Some operations may timeout due to server processing time

    Error: Operation timed out after 30 seconds
    Solution: Check server logs, increase timeout, or use -v for verbose output
    

Advanced Troubleshooting

Enable verbose output to see detailed operation information:

./scripts/test_pykmip.sh query -v

Check server logs while running tests:

# In another terminal
tail -f logs/server.log

Test connectivity manually:

./scripts/test_pykmip.sh check

Run individual operations to isolate issues:

./scripts/test_pykmip.sh create -v
./scripts/test_pykmip.sh get -v

File Structure

The PyKMIP test suite consists of the following files:

scripts/
├── README_PYKMIP.md              # Additional documentation
├── setup_pykmip.sh               # Environment setup script
├── verify_pykmip.sh              # Configuration verification script
├── test_pykmip.sh                # Main compatibility test runner
├── pykmip_client.py              # PyKMIP client implementation (12 operations)
├── pykmip_certify.py             # Separate certify operation module
├── pykmip.conf                   # PyKMIP configuration file
├── kms.toml                      # KMS server configuration for testing
├── activate_venv.sh              # Virtual environment activation helper
└── certify_implementation_summary.py  # Certify operation analysis script

Key Files

  • test_pykmip.sh: Main test runner with colored output, timeout protection, and comprehensive error handling
  • pykmip_client.py: Core client implementing 12 KMIP operations with JSON output formatting
  • pykmip_certify.py: Modular implementation of certificate-related operations (experimental)
  • pykmip.conf: Configuration file with TLS settings, authentication, and server connection details
  • kms.toml: KMS server configuration optimized for PyKMIP compatibility testing

Configuration

The pykmip.conf file contains the connection settings for the PyKMIP client. Key configuration sections:

Server Connection

[server]
host=127.0.0.1
port=5696

TLS Configuration

[tls]
client_cert_file=test_data/certificates/client_server/owner/owner.client.acme.com.crt
client_key_file=test_data/certificates/client_server/owner/owner.client.acme.com.key
ca_cert_file=test_data/certificates/client_server/ca/ca.crt

Protocol Settings

[protocol]
kmip_version=1.0

Ensure these parameters match your KMS server setup:

  • Server hostname/IP and port (default: 127.0.0.1:5696)
  • TLS certificate paths (must exist and be valid)
  • KMIP protocol version compatibility

KMS Server Configuration

Your KMS server must be configured to accept KMIP connections. Key settings in scripts/kms.toml:

[server]
# Enable KMIP socket server
socket_server_start = true
socket_server_port = 5696
socket_server_hostname = "0.0.0.0"

[tls]
# Server certificate and key
# For FIPS mode (default build):
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"

# For non-FIPS mode:
# tls_p12_file = "test_data/certificates/client_server/server/kmserver.acme.com.p12"
# tls_p12_password = "password"
clients_ca_cert_file = "test_data/certificates/client_server/ca/ca.crt"

Contributing

When adding new PyKMIP compatibility tests:

  1. Add new operations to the operations array in test_pykmip.sh:

    operations=("activate" "create" "create_keypair" "decrypt" "destroy"
                "discover_versions" "encrypt" "get" "locate" "mac" "query" "revoke")
    
  2. Implement operation functions in pykmip_client.py:

    def perform_new_operation(proxy, verbose=False):
        """Implement new KMIP operation"""
        if verbose:
            print("Performing new operation...")
        # Implementation here
        return {"operation": "NewOperation", "status": "success"}
    
  3. Add to argument parser choices in pykmip_client.py:

    parser.add_argument('--operation', default='query',
                        choices=['activate', 'create', ..., 'new_operation'],
                        help='KMIP operation to perform')
    
  4. Update help text in test_pykmip.sh to include the new operation

  5. Handle modular operations like certify by creating separate files:

    • Create pykmip_operation.py for complex operations
    • Import and call from main client: from pykmip_operation import perform_operation
  6. Test both success and failure scenarios to ensure proper error handling

  7. Update this README if new setup steps or dependencies are required

Testing Best Practices

  • Always test with both verbose (-v) and normal output modes
  • Verify operations work individually and in the "all" test suite
  • Check for JSON serialization issues with complex data types
  • Ensure timeout handling works correctly for long-running operations
  • Document any server limitations or KMIP version constraints

Available Operations (Detailed Examples)

The test runner (test_pykmip.sh) automatically handles environment setup and provides consistent output formatting:

# Run all operations with summary report
./scripts/test_pykmip.sh all

# Run specific operation with verbose output
./scripts/test_pykmip.sh query -v

# Check prerequisites and connectivity
./scripts/test_pykmip.sh check

# Run individual operations
./scripts/test_pykmip.sh create
./scripts/test_pykmip.sh encrypt
./scripts/test_pykmip.sh mac

Manual Client Usage (Advanced)

For direct client access with custom parameters:

1. Query Server Capabilities

source .venv/bin/activate
python scripts/pykmip_client.py \
    --configuration scripts/pykmip.conf \
    --operation query \
    --verbose

2. Create Symmetric Key

source .venv/bin/activate
python scripts/pykmip_client.py \
    --configuration scripts/pykmip.conf \
    --operation create

3. Create RSA Key Pair

source .venv/bin/activate
python scripts/pykmip_client.py \
    --configuration scripts/pykmip.conf \
    --operation create_keypair

4. Test Encryption/Decryption

source .venv/bin/activate
python scripts/pykmip_client.py \
    --configuration scripts/pykmip.conf \
    --operation encrypt

python scripts/pykmip_client.py \
    --configuration scripts/pykmip.conf \
    --operation decrypt

5. Activate Objects

source .venv/bin/activate
python scripts/pykmip_client.py \
    --configuration scripts/pykmip.conf \
    --operation activate

Deploying email encryption with S/MIME

S/MIME is a standard for public key encryption and signing of MIME data. It is used to secure email messages and is supported by most email clients and servers.

Overview of the S/MIME workflow

To enable S/MIME, a user will go through the following steps:

Setup (once)

  • Generate a key pair and a certificate holding the correct x509 extensions for S/MIME
  • Import this certificate and private key (usually in the form of a PKCS#12 file) in its email client

Handshake (once per recipient)

  • Send a signed, non-encrypted, email to a recipient to share its certificate
  • Receive a signed, encrypted, or non-encrypted, email from the recipient and import its certificate

No further actions are required to exchange signed encrypted emails with this recipient.

Generating a key pair and a certificate

For interoperability, the user certificate should be ultimately signed by a public certificate authority commonly found on email clients. If you want to use your own certificate authority, for internal use or test purposes, please see the instructions below on how to create a root CA and an intermediate CA.

Please note that these certificates require certain x509 extensions to be compatible with Google CSE S/MIME and some other operators. Check the Google requirements for details.

For a user, there are 3 main possibilities to generate a key pair and an S/MIME certificate:

Getting a user certificate from a public certificate authority

Many public authorities can issue user S/MIME certificates. Some authorities even provide free certificates for personal use. This is the case of Actalis for instance.

!!! important Case sensitivity Some email clients, such as Outlook, consider the email address to be case-sensitive. Make sure the email address in your request matches the email address you are using in your email client.

Once generated, these authorities will provide you with a PKCS#12 file containing the certificate and the private key. The file is usually protected by a password.

This file can directly be imported in your email client.

If you wish to store it in the KMS, you can import it using the following command:

ckms certificates import --format pkcs12 --pkcs12-password mysecret \
  --replace john_doe.p12 john.doe@acme.com

To export it as a PEM file, use the following command:

ckms certificates export  --format pem --certificate-id john.doe@acme.com john_doe.pem

Getting an intermediate signer certificate from a public certificate authority

If you have many users, you will probably want top buy an intermediate certificate from a public certificate authority. This intermediate certificate will be used to sign the user certificates.

First, import the intermediate certificate and its private key in the KMS:

ckms certificates import --format pkcs12 --pkcs12-password mysecret \
  --replace intermediate.p12 acme_intermediate_ca

Then generate a user certificate signed by the intermediate certificate.

Creating an S/MIME certificate authority with a root and intermediate CA

If you wish to be your own certificate authority, you can create a root and an intermediate CA.

Create a Root CA

Say, we are ACME Inc. Let us create a self-signed root certificate with the following details:

  • Common Name: ACME Root CA
  • Organization: ACME
  • Organizational Unit: IT
  • Locality: New York
  • State: New York
  • Country: US
  • Validity: 10 years (3650 days)
  • Key Algorithm: NIST P-256
ckms certificates certify \
  --certificate-id acme_root_ca \
  --generate-key-pair \
  --algorithm rsa4096 \
  --subject-name "CN=ACME Root CA,OU=IT,O=ACME,L=New York,ST=New York,C=US" \
  --days 3650 \
  --certificate-extensions ca.ext

The ca.ext file should contain the following content (which should meet Google CSE):

[ v3_ca ]
subjectKeyIdentifier=hash
basicConstraints=critical,CA:TRUE

Create an Intermediate CA

Let us create an intermediate CA signed by the Root CA. This intermediate will be used to issue end-users S/MIME certificates. It will be created with the following details:

  • Common Name: ACME Intermediate CA
  • Organization: ACME
  • Organizational Unit: IT
  • Locality: New York
  • State: New York
  • Country: US
  • Validity: 5 years (1825 days)
  • Key Algorithm: NIST P-256
  • Extensions: a intermediate.ext file with the following content:
[ v3_ca ]
basicConstraints=critical,CA:TRUE,pathlen:0
keyUsage=critical,keyCertSign,digitalSignature
extendedKeyUsage=emailProtection
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
crlDistributionPoints=URI:https://acme.com/crl.pem

Note: these extensions make the intermediate CA compatible with Google CSE for GMail S/MIME requirements

ckms certificates certify \
  --certificate-id acme_intermediate_ca \
  --issuer-certificate-id acme_root_ca \
  --generate-key-pair \
  --algorithm rsa4096 \
  --subject-name "CN=ACME S/MIME intermediate,OU=IT,O=ACME,L=New York,ST=New York,C=US" \
  --days 1825 \
  --certificate-extensions intermediate.ext

Generate a user certificate signed by the intermediate certificate

Then, generate a user certificate signed by the intermediate certificate:

Important some email clients, such as Outlook, consider the email address to be case-sensitive. Make sure the email address in the certificate Common Name matches the email address you are using in your email client.

To be used for S/MIME, the user certificate needs to have certain extensions which are set in a file containing a [ v3_ca ] section.

Say we want to create an S/MIME certificate for user john.doe@acme.com, signed by the intermediate certificate, with the following details:

  • Common Name: john.doe@acme.com
  • Organization: ACME
  • Organizational Unit: IT
  • Locality: San Francisco
  • State: California
  • Country: US
  • Validity: 1 year (365 days)
  • Key Algorithm: NIST P-256
  • Extensions: a user.ext file with the following content:

The user.ext file should contain the following content (which should meet Google CSE requirements):

[ v3_ca ]
keyUsage=critical,nonRepudiation,digitalSignature,dataEncipherment,keyEncipherment
extendedKeyUsage=emailProtection
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer

Issue the following command to generate the certificate:

ckms certificates certify --certificate-id john.doe@acme.com \
--issuer-certificate-id acme_intermediate_ca \
--generate-key-pair --algorithm nist-p256  \
--subject-name "CN=john.doe@acme.com,OU=IT,O=ACME,L=San Francisco,ST=California,C=US" --days 365 \
--certificate-extensions user.ext

Exporting and viewing

Use the ckms certificates export command to export the certificate and the private key in the desired format. Most email clients require the certificate and the private key to be in a PKCS#12 file.

PKCS#12 format

To export the certificate and the private key in PKCS#12 format, use either the pkcs12 or pkcs12-legacy format option. The pkcs12-legacy format is deprecated but is compatible with older versions of OpenSSL (1.1.x) and some keystores such as the Java keystore or the macOS keychain.

ckms certificates export --certificate-id john.doe@acme.com \
 --format pkcs12-legacy --pkcs12-password mysecret \
 john_doe.p12

OpenSSL can be used to view the content of the PKCS#12 file. You will need to provide the password you used to protect the file (mysecret above).

If you are using OpenSSL 3.0 and above and want to view a legacy PKCS#12 file, add the -legacy option:

> openssl pkcs12 -info -in john_doe.p12 -nodes

Enter Import Password:
MAC Iteration 2048
MAC verified OK
PKCS7 Encrypted data: Certificate bag
Bag Attributes
    localKeyID: 82 C3 F3 83 32 68 ED B4 71 15 96 12 0B 01 4C 34 8D 58 DC 58
subject=/CN=john.doe@acme.com/OU=IT/C=US/ST=California/L=San Francisco/O=ACME
issuer=/CN=ACME S/MIME intermediate/OU=IT/C=US/ST=New York/L=New York/O=ACME
-----BEGIN CERTIFICATE-----
MIICQDCCAeagAwIBAgIBADAKBggqhkjOPQQDAjByMSEwHwYDVQQDDBhBQ01FIFMv
TUlNRSBpbnRlcm1lZGlhdGUxCzAJBgNVBAsMAklUMQswCQYDVQQGEwJVUzERMA8G
A1UECAwITmV3IFlvcmsxETAPBgNVBAcMCE5ldyBZb3JrMQ0wCwYDVQQKDARBQ01F
MB4XDTI0MDkxMzA5MzkwMloXDTI1MDkxMzA5MzkwMlowcjEaMBgGA1UEAwwRam9o
bi5kb2VAYWNtZS5jb20xCzAJBgNVBAsMAklUMQswCQYDVQQGEwJVUzETMBEGA1UE
CAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwE
QUNNRTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOvAZj+RBjElLMWVipGx+rbG
mniD8NApM2inWLN+1avvd2QslOh9mhiT18u2KJkrjG9hIbVh8cyr4/dxtWL6Goij
bTBrMAsGA1UdDwQEAwIDyDATBgNVHSUEDDAKBggrBgEFBQcDBDAcBgNVHREEFTAT
gRFqb2huLmRvZUBhY21lLmNvbTApBgNVHR8EIjAgMB6gHKAahhhodHRwczovL2Fj
bWUuY29tL2NybC5wZW0wCgYIKoZIzj0EAwIDSAAwRQIgYUf5lGAU3dQ9wIPdX5x+
RtJioGwdVZZmprEAdNL203wCIQDbDk9yRjfPUUWph05LlC45EA8B3OJYEShdo3s9
pImIww==
-----END CERTIFICATE-----
Certificate bag
Bag Attributes: <No Attributes>
subject=/CN=ACME S/MIME intermediate/OU=IT/C=US/ST=New York/L=New York/O=ACME
issuer=/CN=ACME Root CA/OU=IT/C=US/ST=New York/L=New York/O=ACME
-----BEGIN CERTIFICATE-----
MIICJjCCAc2gAwIBAgIBADAKBggqhkjOPQQDAjBmMRUwEwYDVQQDDAxBQ01FIFJv
b3QgQ0ExCzAJBgNVBAsMAklUMQswCQYDVQQGEwJVUzERMA8GA1UECAwITmV3IFlv
cmsxETAPBgNVBAcMCE5ldyBZb3JrMQ0wCwYDVQQKDARBQ01FMB4XDTI0MDkxMzA5
MzYwNVoXDTI5MDkxMjA5MzYwNVowcjEhMB8GA1UEAwwYQUNNRSBTL01JTUUgaW50
ZXJtZWRpYXRlMQswCQYDVQQLDAJJVDELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE5l
dyBZb3JrMREwDwYDVQQHDAhOZXcgWW9yazENMAsGA1UECgwEQUNNRTBZMBMGByqG
SM49AgEGCCqGSM49AwEHA0IABEG7Ei75r1WuGrfMdXEQseoxf4n5KM7Bov5GIP+0
ZSHt2CYEgIkvBElHiLtDs2XP8vPmHKQPA2fOTIqsxGG7NPijYDBeMA8GA1UdEwQI
MAYBAf8CAQAwCwYDVR0PBAQDAgKEMBMGA1UdJQQMMAoGCCsGAQUFBwMEMCkGA1Ud
HwQiMCAwHqAcoBqGGGh0dHBzOi8vYWNtZS5jb20vY3JsLnBlbTAKEBggqhkjOPQQD
AgNHADBEAiBbVpxNtT+wLNxAUIrA9r9fR4UT1aCw15rShZZiCjrvcAIgURwRsODz
bB/+NeaLa9WgwjtZk7Vm1c7fVnb30TBcN78=
-----END CERTIFICATE-----
Certificate bag
Bag Attributes: <No Attributes>
subject=/CN=ACME Root CA/OU=IT/C=US/ST=New York/L=New York/O=ACME
issuer=/CN=ACME Root CA/OU=IT/C=US/ST=New York/L=New York/O=ACME
-----BEGIN CERTIFICATE-----
MIIBujCCAV+gAwIBAgIBADAKBggqhkjOPQQDAjBmMRUwEwYDVQQDDAxBQ01FIFJv
b3QgQ0ExCzAJBgNVBAsMAklUMQswCQYDVQQGEwJVUzERMA8GA1UECAwITmV3IFlv
cmsxETAPBgNVBAcMCE5ldyBZb3JrMQ0wCwYDVQQKDARBQ01FMB4XDTI0MDkxMzA5
MzQxNFoXDTM0MDkxMTA5MzQxNFowZjEVMBMGA1UEAwwMQUNNRSBSb290IENBMQsw
CQYDVQQLDAJJVDELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE5ldyBZb3JrMREwDwYD
VQQHDAhOZXcgWW9yazENMAsGA1UECgwEQUNNRTBZMBMGByqGSM49AgEGCCqGSM49
AwEHA0IABPj86uAwMjnITXnnnLY9wmHbxtGz2IWAN7JLdbmD77RfbpvcK+BxMzVI
20ITcWv8dyfhcMLyBa+ygDVPgVK1jLQwCgYIKoZIzj0EAwIDSQAwRgIhAJenykHa
jjVPke8vX+GyaMTqNJZN94QyCYHgqUeDeZVCAiEAuokqc/TfyzvkzmWcJgeGsFyM
Nk/pqUsPr8eR1sHQWgg=
-----END CERTIFICATE-----
PKCS7 Data
Shrouded Keybag: Bag Attributes
    localKeyID: 82 C3 F3 83 32 68 ED B4 71 15 96 12 0B 01 4C 34 8D 58 DC 58
Key Attributes: <No Attributes>
-----BEGIN PRIVATE KEY-----
MIGHTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQge5si3Le85K18XOLc
ae8QE50qCE0yUMipcPvHwZM1k1ChRANCAATrwGY/kQYxJSzFlYqRsfq2xpp4g/DQ
KTNop1izftWr73dkLJTofZoYk9fLtiiZK4xvYSG1YfHMq+P3cbVi+hqI
-----END PRIVATE KEY-----

Exporting for Google CSE S/MIME

To export the expected Google CSE Root certificate in order to import it in the Apps/Google Workspace/Settings for Gmail/User Settings, proceed as follows:

openssl pkcs12 -in john_doe.p12 -cacerts -nokeys -out ca.pem -passin pass:"$MY_PASSWD"
openssl pkcs12 -in john_doe.p12 -clcerts -nokeys -out certificate.pem -passin pass:"$MY_PASSWD"
cp certificate.pem fullchain.pem
cat ca.pem >>fullchain.pem

PEM format

To export the certificate only, in PEM format, use the following command:

ckms certificates export --certificate-id john.doe@acme.com --format pem john_doe.pem

You can then view its content, using openssl for instance:

> openssl x509 -inform pem -text -in john_doe.pem

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 0 (0x0)
    Signature Algorithm: ecdsa-with-SHA256
        Issuer: CN=ACME S/MIME intermediate, OU=IT, C=US, ST=New York, L=New York, O=ACME
        Validity
            Not Before: Sep 11 14:09:25 2024 GMT
            Not After : Sep 11 14:09:25 2025 GMT
        Subject: CN=john.doe@acme.com, OU=IT, C=US, ST=California, L=San Francisco, O=ACME
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub:
                    04:4b:0e:f2:7b:5b:93:91:1c:4a:a2:d1:91:24:ce:
                    a4:6e:97:5c:41:9f:fd:92:74:70:83:05:64:69:58:
                    41:46:c5:64:bc:5e:89:30:d6:83:c8:06:64:f6:e8:
                    b9:a2:a9:2f:ad:e5:93:fd:49:45:4c:e5:c3:2b:29:
                    e1:7e:a0:16:a9
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        X509v3 extensions:
            X509v3 Key Usage:
                Digital Signature, Non Repudiation, Key Agreement
            X509v3 Extended Key Usage:
                E-mail Protection
            X509v3 Subject Alternative Name:
                <EMPTY>

            X509v3 CRL Distribution Points:

                Full Name:
                  URI:https://acme.com/crl.pem

    Signature Algorithm: ecdsa-with-SHA256
         30:45:02:21:00:cf:31:c9:f1:a7:d7:f5:cd:3a:b6:e3:4e:13:
         20:ef:e1:6d:b9:21:55:66:27:c4:5d:b0:68:29:f2:07:7e:5b:
         eb:02:20:1d:92:ff:52:1d:c2:f1:ab:34:f7:d7:f1:29:87:bc:
         f5:33:3c:0b:6c:93:23:4c:4f:c7:69:c1:df:23:95:0e:78
-----BEGIN CERTIFICATE-----
MIICLTCCAdOgAwIBAgIBADAKBggqhkjOPQQDAjByMSEwHwYDVQQDDBhBQ01FIFMv
TUlNRSBpbnRlcm1lZGlhdGUxCzAJBgNVBAsMAklUMQswCQYDVQQGEwJVUzERMA8G
A1UECAwITmV3IFlvcmsxETAPBgNVBAcMCE5ldyBZb3JrMQ0wCwYDVQQKDARBQ01F
MB4XDTI0MDkxMTE0MDkyNVoXDTI1MDkxMTE0MDkyNVowcjEaMBgGA1UEAwwRam9o
bi5kb2VAYWNtZS5jb20xCzAJBgNVBAsMAklUMQswCQYDVQQGEwJVUzETMBEGA1UE
CAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwE
QUNNRTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABEsO8ntbk5EcSqLRkSTOpG6X
XEGf/ZJ0cIMFZGlYQUbFZLxeiTDWg8gGZPbouaKpL63lk/1JRUzlwysp4X6gFqmj
WjBYMAsGA1UdDwQEAwIDyDATBgNVHSUEDDAKBggrBgEFBQcDBDAJBgNVHREEAjAA
MCkGA1UdHwQiMCAwHqAcoBqGGGh0dHBzOi8vYWNtZS5jb20vY3JsLnBlbTAKEBggq
hkjOPQQDAgNIADBFAiEAzzHJ8afX9c06tuNOEyDv4W25IVVmJ8RdsGgp8gd+W+sC
IB2S/1IdwvGrNPfX8SmHvPUzPAtskyNMT8dpwd8jlQ54
-----END CERTIFICATE-----

Loading the PKCS#12 file in an email client

Apple mail, MacOS

To use your S/MIME certificate, load it in the login keychain:

keychain

Then, in the Mail application, create a new email and tick the "encrypt" and "sign" boxes:

mail

When handshaking with a new recipient, send a signed email to the recipient (not encrypted).

When receiving a signed email from the recipient, import the recipient's certificate in your contacts.

Outlook, Windows

Setup

In Outlook (classic) go to File -> Options -> Trust Center -> Trust Center Settings outlook_0

Then go to Email Security and click on Import/Export to import the PKCS#12 file.

outlook_1

Finally, go to the Email Security and click the Settings... button of Default Setting. In the popup, select the certificate you just imported, adjust the hash algorithm to SHA256 for maximum compatibility and make sure you tick the box "Send these certificates with signed messages."

outlook_2

Sending a message

Click on New Email, then Options -> Encrypt and Sign.

To perform a handshake with a new recipient, send a first email to the recipient that is signed only (not encrypted).

outlook_3

Eviden KMS can be installed on various platforms, including Docker, Ubuntu, Rocky Linux, macOS, and Windows. It is prepackaged with an integrated web ui (except for macOS) that is available on the /ui path of the server.

The KMS is also available on the marketplaces of major cloud providers, prepackaged to run confidentially in an Eviden VM. Please check this page for more information.

When installed using the options below, the KMS server will be automatically configured to run using an SQLite database. If you wish to change the database configuration, please refer to the database guide.

For high availability and scalability, refer to the High Availability Guide.

Verifying release signatures

All Eviden KMS release packages (DEB, RPM, DMG) are GPG-signed. Each package is accompanied by a .asc signature file that can be used to verify its authenticity and integrity.

Import the Eviden public key**

gpg --import cosmian-kms-public.asc

The key is also bundled inside DMG installers and available in the GitHub repository.

Verify a downloaded package**

gpg --verify <package>.asc <package>

For example:

# Debian package
gpg --verify cosmian-kms-server-non-fips-static-openssl_5.25.0_amd64.deb.asc \
             cosmian-kms-server-non-fips-static-openssl_5.25.0_amd64.deb

# RPM package
gpg --verify cosmian-kms-server-non-fips-static-openssl_5.25.0_x86_64.rpm.asc \
             cosmian-kms-server-non-fips-static-openssl_5.25.0_x86_64.rpm

# DMG package
gpg --verify cosmian-kms-server-non-fips-static-openssl-5.25.0_arm64.dmg.asc \
             cosmian-kms-server-non-fips-static-openssl-5.25.0_arm64.dmg

A successful verification prints:

gpg: Good signature from "Eviden KMS Release <tech@cosmian.com>"

!!!warning If the signature does not match, do not use the package.

Installation

!!!info "KMS CLI" The KMS CLI lets you interact with the KMS from the command line. Install it from KMS CLI and configure it.

Run the container as follows:

docker run -p 9998:9998 --name kms ghcr.io/cosmian/kms:latest
  • The KMS UI is available at http://localhost:9998/ui.
  • The KMS REST API is available on http://localhost:9998,
  • The server stores its data inside the container in the /root/cosmian-kms/sqlite-data directory.

A FIPS version is also available:

docker run -p 9998:9998 --name kms ghcr.io/cosmian/kms-fips:latest

To persist data between restarts, mount the /root/cosmian-kms/sqlite-data path to a filesystem directory or a Docker volume:

docker run --rm -p 9998:9998 \
-v cosmian-kms:/root/cosmian-kms/sqlite-data \
--name kms ghcr.io/cosmian/kms:latest

A custom configuration file can be provided by mounting it in the container:

docker run --rm -p 9998:9998 \
-v cosmian-kms:/root/cosmian-kms/sqlite-data \
-v /path/to/your/kms.toml:/etc/cosmian/kms.toml \
--name kms ghcr.io/cosmian/kms:latest

Download the package and install it (works on all Debian distributions from Debian 10):

sudo apt update && sudo apt install -y wget
# Standard build (non-FIPS, static OpenSSL)
wget https://package.cosmian.com/kms/5.25.0/deb/amd64/non-fips/static/cosmian-kms-server-non-fips-static-openssl_5.25.0_amd64.deb
sudo apt install ./cosmian-kms-server-non-fips-static-openssl_5.25.0_amd64.deb
sudo cosmian_kms --version

Or install the FIPS build:

wget https://package.cosmian.com/kms/5.25.0/deb/amd64/fips/static/cosmian-kms-server-fips-static-openssl_5.25.0_amd64.deb
sudo apt install ./cosmian-kms-server-fips-static-openssl_5.25.0_amd64.deb
sudo cosmian_kms --version

A cosmian_kms service will be configured; the service file is located at /etc/systemd/system/cosmian_kms.service. To start the KMS, run:

sudo systemctl start cosmian_kms
  • The server uses the configuration file located at /etc/cosmian/kms.toml.
  • The KMS UI is available at http://localhost:9998/ui.

Download the package and install it (works for Rocky Linux 8/9/10):

sudo dnf update && sudo dnf install -y wget
wget https://package.cosmian.com/kms/5.25.0/rpm/amd64/non-fips/static/cosmian-kms-server-non-fips-static-openssl_5.25.0_x86_64.rpm
sudo dnf install ./cosmian-kms-server-non-fips-static-openssl_5.25.0_x86_64.rpm
sudo cosmian_kms --version

To start the KMS, run:

sudo systemctl start cosmian_kms
  • The server uses the configuration file located at /etc/cosmian/kms.toml.
  • The KMS UI is available at http://localhost:9998/ui.

Download the installer for your architecture and run it:

  • Apple Silicon (ARM64):

    open "https://package.cosmian.com/kms/5.25.0/dmg/arm64/non-fips/static/cosmian-kms-server-non-fips-static-openssl-5.25.0_arm64.dmg"
    

Then drag-and-drop the app to Applications or follow the DMG instructions.

Note: The 5.25.0 DMG is provided for Apple Silicon (ARM64).

After installation, run:

/Applications/Cosmian\ KMS\ Server.app/Contents/MacOS/cosmian_kms --version
/Applications/Cosmian\ KMS\ Server.app/Contents/MacOS/cosmian_kms
  • The server uses the configuration file located at /etc/cosmian/kms.toml.
  • The KMS UI is available at http://localhost:9998/ui.

Static vs Dynamic builds

  • Static builds: ship with OpenSSL statically linked into the binary. Simplest to deploy; no external crypto libraries required; consistent behavior across environments.
  • Dynamic builds: link OpenSSL dynamically. This allows replacing the OpenSSL shared library at runtime to use custom or system-provided crypto. On Linux, replace the relevant .so files; on macOS, replace the .dylib files, ensuring ABI compatibility.

Available dynamic packages for Debian-based distributions:

# Non-FIPS dynamic (OpenSSL linked dynamically)
wget https://package.cosmian.com/kms/5.25.0/deb/amd64/non-fips/dynamic/cosmian-kms-server-non-fips-dynamic-openssl_5.25.0_amd64.deb
# FIPS dynamic
wget https://package.cosmian.com/kms/5.25.0/deb/amd64/fips/dynamic/cosmian-kms-server-fips-dynamic-openssl_5.25.0_amd64.deb

Available dynamic packages for Rocky Linux:

# Non-FIPS dynamic
wget https://package.cosmian.com/kms/5.25.0/rpm/amd64/non-fips/dynamic/cosmian-kms-server-non-fips-dynamic-openssl_5.25.0_x86_64.rpm
# FIPS dynamic
wget https://package.cosmian.com/kms/5.25.0/rpm/amd64/fips/dynamic/cosmian-kms-server-fips-dynamic-openssl_5.25.0_x86_64.rpm

To use custom OpenSSL with dynamic builds, install or place the desired OpenSSL shared libraries here: /usr/local/cosmian/lib/ossl-modules.

On Windows, download the NSIS installer:

https://package.cosmian.com/kms/5.25.0/windows/x86_64/non-fips/static-openssl/cosmian-kms-server-non-fips-static-openssl_5.25.0_x86_64.exe

Run the installer to install Eviden KMS Server. The installer will:

  • Install the KMS server with integrated web UI
  • Set up the configuration file at C:\Users\<username>\AppData\Local\Cosmian KMS Server\kms.toml

After installation, you can run the server:

cosmian_kms --version
  • The KMS UI is available at http://localhost:9998/ui
  • The server uses the configuration file located at C:\Users\<username>\AppData\Local\Cosmian KMS Server\kms.toml
  • See the server configuration for more information

A KMS-ready instance based on Eviden Confidential VM can be deployed on virtual machines that supports AMD SEV-SNP or Intel TDX technologies, and is available on the marketplace of the major cloud providers.

If you are interested in the confidential computing technology and the Eviden VM, please first read the guide about how to setup an Eviden VM.

Deploy Eviden VM KMS on a cloud provider

Go the Eviden marketplace webpage of the chosen cloud provider.

Select an OS and continue until the Eviden VM KMS instance is spawned.

!!! important Cloud provider support

Here's the list of instance types by cloud provider

| Cloud provider | Azure             | GCP          | AWS           |
| -------------- | ----------------- | ------------ | ------------- |
| **AMD**        | **SNP**           | **SNP**      | **SNP**       |
|                | Standard_DCas_v5  | n2d-standard | M6a           |
|                | Standard_DCads_v5 |              | C6a           |
|                |                   |              | R6a           |
| **Intel**      | **TDX**           | **TDX**      | **TDX**       |
|                | DCes_v5-series    | c3-standard  | Not available |
|                | ECesv5-series     |              |               |
|                | (preview)         |              |               |

The Eviden KMS contains:

  • a ready-to-go Nginx setup (listening on port 443 and locally on port 9998)
  • a ready-to-go KMS service
  • the Eviden VM software stack. As reminder, Eviden VM Agent is listening on port 5555.

Configure the KMS 📜

Default configuration

By default:

  • the KMS server is locally listening on port 9998
  • its database is a local Redis database with encrypted data using the scheme Findex.
  • the KMS configuration file is located in the encrypted LUKS container at /var/lib/cosmian_vm/data/app.conf and has the following content:
default_username = "admin"

[http]
port = 9998
hostname = "0.0.0.0"

[db]
database_type = "redis-findex"
database_url = "redis://0.0.0.0:6379"
redis_master_password = "master-password"

For testing purposes (connectivity, features, etc.), KMS server can also use a SQLite database by modifying the configuration file:

default_username = "admin"

[http]
port = 9998
hostname = "0.0.0.0"

!!! important Protect your secrets

The Eviden KMS configuration can potentially contain secrets
(such as this `redis_master_password` field), that is why
the configuration file is save in a LUKS container (default path: `/var/lib/cosmian_vm/data`).
To override the default
configuration, a new configuration SHOULD be sent remotely and securely via
the Eviden VM CLI following [see app init](#override-the-default-configuration).

Override the default configuration

The default configuration can be overridden remotely by using the Eviden VM CLI without any SSH connection.

It is safe to provide secrets (such as passwords) in the configuration file because this file is going to be stored in the encrypted folder (LUKS) of the Eviden VM KMS (which is mounted by default on /var/lib/cosmian_vm/data).

Eviden VM CLI has to be installed on the client machine (Ubuntu, RHEL or via Docker). Please follow the installation instructions.

Then proceed as follows:

cosmian_vm --url https://${EVIDEN_KMS_IP_ADDR}:5555 \
           --allow-insecure-tls \
           app init -c kms.toml

Processing the init of the deployed app...
The app has been configured and started

This command will send via an encrypted tunnel the configuration that will be written in the remotely path /var/lib/cosmian_vm/data/app.conf which is contained in an encrypted container (LUKS).

where kms.toml can be:

default_username = "admin"

[http]
port = 9998
hostname = "0.0.0.0"

[db]
database_type = "redis-findex"
database_url = "redis://<EXTERNAL_HOSTNAME_OR_IP>:6379"
redis_master_password = "master-password"
  • The database type redis-findex is a Redis database with encrypted data and encrypted indexes thanks to Cosmian Findex.
  • The database_url points to the Redis, typically an external managed Redis database.
  • The redis_master_password is used to encrypt the Redis data and indexes.

Service

Systemd is used to supervise and run the KMS server and the Eviden VM agent. As an administrator, you can see the running services with the following commands:

systemctl status cosmian_kms
systemctl status cosmian_vm_agent

You can read as well full logs using:

journalctl -u cosmian_kms
journalctl -u cosmian_vm_agent

Check the connection with the KMS

$ curl --insecure https://${EVIDEN_VM_IP_ADDR}/version
"5.25.0"

Why --allow-insecure-tls and --insecure flags?

When the agent starts (see Snapshot the VM) self-signed certificate is created to enable HTTPS out of the box.

These certificates must be replaced by trusted ones using tools like cosmian_certtool or Linux tools (certbot with Let's Encrypt for instance).

See how to setup trusted certificates.

Snapshot the VM 📸

Once the VM is configured as needed, Eviden VM Agent can do a snapshot of the VM containing fingerprint of the executables and metadata related to TEE and TPM.

The agent creates an encrypted folder (LUKS container) to store sensitive information, creates self-signed certificate for Nginx and starts a snapshot.

Wait for the agent to initialize the LUKS and generate the certificates. This is automatically at boot.

In short, to generate a snapshot, please follow.

The associated command is:

cosmian_vm --url https://${EVIDEN_VM_IP_ADDR}:5555 --allow-insecure-tls snapshot

Verify the Eviden VM KMS integrity ✅

Verifying trustworthiness of the Eviden VM KMS is exactly the same process as verifying the Eviden VM itself.

In short, to verify a snapshot, please follow.

The associated command is:

cosmian_vm --url https://${EVIDEN_VM_IP_ADDR}:5555 --allow-insecure-tls verify \
--snapshot cosmian_vm.snapshot

This mode offers high availability through redundancy and load-balancing.

The KMS servers are stateless, so they can be scaled horizontally from a single node up to any number of nodes, simply by connecting them all to the same database and fronting them with a load balancer.

high-availability

Architecture with HAProxy and Keepalived

%%{init: {"flowchart": {"curve": "linear"}}}%%
flowchart TB
    %% Styling (reuses the same palette as the previous diagram version)
    classDef client fill:#e1d5e7,stroke:#9673a6,stroke-width:2px,color:#000
    classDef vip fill:#f8cecc,stroke:#b85450,stroke-width:2px,stroke-dasharray: 5 5,color:#000
    classDef lb fill:#dae8fc,stroke:#6c8ebf,stroke-width:2px,color:#000
    classDef kms fill:#d5e8d4,stroke:#82b366,stroke-width:2px,color:#000
    classDef db fill:#ffe6cc,stroke:#d79b00,stroke-width:2px,color:#000

    subgraph clients_group["💻 Clients (KMIP / HTTP)"]
        client1["Client 1"]:::client
        client2["Client 2"]:::client
        clientN["Client n"]:::client
    end

    subgraph lb_cluster["🔀 Load balancers (HAProxy + Keepalived)"]
        direction LR
        vip["🌐 Virtual IP (VIP)"]:::vip
        lb1["HAProxy + Keepalived<br/>LB 1 (MASTER)"]:::lb
        lb2["HAProxy + Keepalived<br/>LB 2 (BACKUP)"]:::lb
    end

    subgraph kms_cluster["🛡️ KMS nodes"]
        node1["Eviden KMS<br/>Node 1"]:::kms
        node2["Eviden KMS<br/>Node 2"]:::kms
        nodeN["Eviden KMS<br/>Node n"]:::kms
    end

    db[("🗄️ Shared database<br/>(SQL / Redis)")]:::db

    client1 -->|single endpoint| vip
    client2 --> vip
    clientN --> vip

    vip -.->|active on| lb1
    vip -.->|standby on| lb2

    lb1 --> node1 & node2 & nodeN
    lb2 --> node1 & node2 & nodeN
    node1 & node2 & nodeN --> db

All KMS clients (backup software, applications) connect exclusively to the Virtual IP (VIP). Individual KMS node IPs are never exposed to clients.

The number of KMS nodes can be adjusted at any time without reconfiguring clients: add a new node to the database and to the HAProxy backend, and it will start receiving traffic immediately.

Because the KMS nodes are stateless, the shared database is the source of truth: ensure you have regular database backups (and periodically test restores) using your database engine's recommended procedures.

Configuring the load balancer

Since the KMS servers are stateless, any load-balancing strategy may be selected, such as a simple round-robin.

When the Eviden KMS servers are configured to export an HTTPS port (as is the case when running inside a confidential VM):

  • all the Eviden KMS servers should expose the same server certificate on their HTTPS port
  • and the load balancer should be configured as an SSL load balancer (HAProxy is a good example of a high-performance SSL load balancer)

HAProxy Configuration

Use the same configuration on all HAProxy nodes — Keepalived handles which one is active.

In a typical two-node setup, HAProxy and Keepalived are installed on two dedicated load balancer nodes. Each load balancer node:

  • runs HAProxy (load-balances requests to all KMS nodes)
  • runs Keepalived (shares a Virtual IP between the HAProxy instances so clients have a single endpoint)

Typical configuration file path (may vary by distribution):

  • HAProxy: /etc/haproxy/haproxy.cfg

On systemd-based distributions, after updating the configuration you can restart HAProxy with:

sudo systemctl restart haproxy

Health checks use the Eviden KMS /health endpoint on the HTTP port (9998), while KMIP traffic is forwarded to port 5696. Add or remove server lines in the backend to match your number of KMS nodes.

global
    log /dev/log local0
    maxconn 2000
    user haproxy
    group haproxy

defaults
    log     global
    mode    tcp                 # TCP mode: required for TLS/KMIP passthrough
    option  tcplog
    timeout connect 5000ms
    timeout client  50000ms
    timeout server  50000ms

# Frontend for KMIP traffic (standard KMIP port)
frontend kmip_frontend
    bind *:5696
    default_backend kmip_backend

backend kmip_backend
    mode tcp
    balance roundrobin

    # Health check via Eviden KMS /health endpoint (HTTP port 9998)
    option httpchk GET /health
    http-check expect status 200

    # Check every 3s; mark DOWN after 3 failures, mark UP after 2 successes
    default-server inter 3s fall 3 rise 2

    # Add one line per KMS node.
    # Traffic goes to port 5696; health is checked on port 9998.
    server kms1 <kms1-ip>:5696 check port 9998
    server kms2 <kms2-ip>:5696 check port 9998
    # server kms3 <kms3-ip>:5696 check port 9998
    # …

Keepalived Configuration

Keepalived manages the Virtual IP (VIP) across the load balancer nodes. Use the same configuration on all load balancer nodes, adjusting only state and priority per node:

You must define a Virtual IP (VIP) that will be shared by the nodes. Keepalived ensures that only the current MASTER node responds to the VIP, so clients always connect to a single stable address.

Typical configuration file path (may vary by distribution):

  • Keepalived: /etc/keepalived/keepalived.conf

On systemd-based distributions, after updating the configuration you can restart Keepalived with:

sudo systemctl restart keepalived
Load balancer nodestatepriority
PrimaryMASTERhighest value (e.g. 101)
SecondaryBACKUPlower value (e.g. 100)
AdditionalBACKUPlower still (e.g. 99, 98, …)
vrrp_script chk_haproxy {
    script "killall -0 haproxy"   # Checks that the HAProxy process is running
    interval 2
    weight 2
}

vrrp_instance VI_1 {
    state MASTER                  # → BACKUP on all non-primary nodes
    interface eth0                # Adapt to your network interface name
    virtual_router_id 51          # Must be identical on all load balancer nodes
    priority 101                  # → Decrease by 1 for each additional node
    advert_int 1

    authentication {
        auth_type PASS
        auth_pass SecretPassword  # Strong shared secret; must be identical on all nodes
    }

    virtual_ipaddress {
        192.168.1.100             # VIP — configure this address in all KMS clients
    }

    track_script {
        chk_haproxy
    }
}

Failover behavior

EventResult
Active HAProxy node goes downKeepalived promotes the next highest-priority HAProxy; VIP moves within ~2s
A KMS node goes downHAProxy detects failure via /health within inter × fall ≈ 9s and stops routing to it; remaining nodes absorb the traffic
A KMS node recoversHAProxy re-adds it after 2 successful checks (~6s)
A new KMS node is addedAdd a server line to the HAProxy backend and reload HAProxy; no client reconfiguration needed

Deployment options

You can run a single HAProxy instance (similar to using Nginx as a reverse-proxy/load balancer) in front of the KMS nodes. This provides load balancing but introduces a single point of failure (SPOF): if that HAProxy server goes down, clients cannot reach the KMS.

Using Keepalived (with at least two HAProxy instances) avoids the SPOF by providing a floating VIP.

TOML configuration file

The KMS server can be configured using a TOML file. When a configuration file is provided, the command line arguments are ignored (except --help / --version).

Interactive configuration wizard

The fastest way to create a valid configuration file is the built-in interactive wizard:

cosmian_kms configure

The wizard guides you step-by-step through all configuration sections:

StepSectionWhat it covers
1/9DatabaseType (SQLite / PostgreSQL / MySQL / Redis-Findex), URL, paths, cache settings
2/9HTTP serverListening port and hostname
3/9TLS / CertificatesEnable TLS; optionally generates a self-signed PKI (CA → server + client certs)
4/9KMIP socket serverEnable the binary KMIP socket listener (port 5696)
5/9AuthenticationAPI token, JWT/OIDC providers, mTLS client certificates
6/9HSMModel, admin user, slot numbers and passwords
7/9LoggingLog level, OTLP endpoint, syslog, rolling logs
8/9ProxyOutbound proxy for JWKS fetch (URL, auth, exclusions)
9/9AdvancedWorkspace paths, KEK, MS DKE, KMIP policy, Google CSE, Azure EKM, AWS XKS, Web UI

At the end the wizard writes the resulting TOML file to the default system path (/etc/cosmian/kms.toml on Linux/macOS, C:\ProgramData\Cosmian\kms.toml on Windows) and prints the command to start the server:

Start the server with:
  cosmian_kms -c /etc/cosmian/kms.toml

Self-signed PKI generation

When TLS is enabled and you choose to generate certificates, the wizard creates a complete PKI under the chosen output directory (default /etc/cosmian/):

FileDescription
ca.crtSelf-signed CA certificate (RSA-4096, valid 10 years by default)
server.crtServer leaf certificate signed by the CA (RSA-2048)
server.keyServer private key (PKCS#8 PEM)
client.crtClient leaf certificate signed by the CA — distribute to mTLS clients
client.keyClient private key (PKCS#8 PEM)

Distribute client.crt and client.key to any client that must authenticate with mutual TLS. You can verify the chain at any time with:

openssl verify -CAfile /etc/cosmian/ca.crt /etc/cosmian/server.crt
openssl verify -CAfile /etc/cosmian/ca.crt /etc/cosmian/client.crt

Manual configuration

Configuration file loading precedence:

  1. Command line flag -c/--config <FILE> (highest precedence). If the file does not exist, the server exits with an error.
  2. Environment variable COSMIAN_KMS_CONF (must point to an existing file).
  3. Default system path: /etc/cosmian/kms.toml (Linux/macOS) or C:\\ProgramData\\Cosmian\\kms.toml (Windows).
  4. If none of the above files is found, the server falls back to parsing the command line arguments and environment variables.

Important: If a configuration file is found via the default system path (rule 3) and extra command-line arguments are also provided, the server exits with an error. This prevents silently ignoring arguments the user intended to take effect. To use a different configuration, point explicitly to it with -c/--config <FILE>. Examples:

# Explicit configuration file
./cosmian-kms -c ./test_data/configs/server/jwt_auth.toml

# Using an environment variable
export COSMIAN_KMS_CONF=./test_data/configs/server/jwt_auth.toml
./cosmian-kms

The file should be a TOML file with the following structure:

# The default username to use when no authentication method is provided
default_username = "admin"

# When an authentication method is provided, perform the authentication
# but always use the default username instead of the one provided by the authentication method
force_default_username = false

# This setting enables the Microsoft Double Key Encryption service feature of this server.
#
# It should contain the external URL of this server as configured in Azure App Registrations
# as the DKE Service (<https://learn.microsoft.com/en-us/purview/double-key-encryption-setup#register-your-key-store>)
#
# The URL should be something like <https://cse.my_domain.com/ms_dke>
# ms_dke_service_url = "<ms dke service url>"

# The exposed URL of the KMS - this is required if Google CSE configuration is activated.
# If this server is running on the domain `cse.my_domain.com` with this public URL,
# The configured URL from Google admin  should be something like <https://cse.my_domain.com/google_cse>
# The URL is also used during the authentication flow initiated from the KMS UI.
# kms_public_url = "kms-public-url"

# Print the server configuration information and exit
info = false

# The HSM model.
# `Trustway Proteccio`, `Trustway Crypt2pay`, `Utimaco General Purpose HSM`,
# `Smartcard HSM`, and `SoftHSM2` are natively supported.
# Other HSMs are supported too; specify `other` and check the documentation
# hsm_model    = "softhsm2"           # softhsm2 | utimaco | proteccio | crypt2pay | smartcardhsm | other
# List of KMS usernames that are granted HSM admin privileges.
# HSM admins can create, destroy, and potentially export objects on the HSM.
# Use `"*"` as the only entry to grant all authenticated users admin access.
# Repeat the option or use a comma-separated list to specify multiple admins:
#   `--hsm-admin alice@example.com --hsm-admin bob@example.com`
#   or set `KMS_HSM_ADMIN=alice@example.com,bob@example.com`
# hsm_admin    = ["admin"]            # KMS users with admin rights on this HSM
# HSM slot number. The slots used must be listed.
# Repeat this option to specify multiple slots
# while specifying a password for each slot (or an empty string for no password)
# e.g.
# ```sh
#   --hsm-slot 1 --hsm-password password1 \
#   --hsm-slot 2 --hsm-password password2
# ```
# hsm_slot     = [0]                  # PKCS#11 slot indices
# Password for the user logging in to the HSM Slot specified with `--hsm_slot`
# Provide an empty string for no password
# see `--hsm_slot` for more information.
# Set `KMS_HSM_PASSWORD` to avoid the password appearing in `ps` output.
# hsm_password = ["changeme"]         # Login passwords (same order as hsm_slot)
#
# ── New multi-instance format ─────────────────────────────────────────────────
# Uses the new UID convention: hsm::<model>::<slot_id>::<key_id>
# To connect HSMs, declare one [[hsm_instances]] section per device.
# `Trustway Proteccio`, `Trustway Crypt2pay`, `Utimaco General Purpose HSM`,
# `Smartcard HSM`, and `SoftHSM2` are natively supported.
# Other HSMs are supported too; specify `other` and check the documentation.
#
# The first entry gets the routing prefix "hsm::<model>", the second
# "hsm::<model>_1", etc. Object UIDs take the form "<prefix>::<slot>::<key-id>".
#
## [[hsm_instances]]
## hsm_model    = "softhsm2"           # softhsm2 | utimaco | proteccio | crypt2pay | smartcardhsm | other
## hsm_admin    = ["tech@example.com"] # KMS users with admin rights on this HSM
## hsm_slot     = [0]                  # PKCS#11 slot indices (use softhsm2-util --show-slots to list them)
## hsm_password = ["changeme"]         # Login passwords (same order as hsm_slot)
#
## [[hsm_instances]]
## hsm_model    = "utimaco"
## hsm_admin    = ["tech@example.com"]
## hsm_slot     = [0, 1]
## hsm_password = ["slot0pass", "slot1pass"]

# Force all newly created and imported keys to be wrapped by the key specified in this field.
# This is most useful to ensure that an HSM key wraps all keys in the KMS database.
# Note: This setting is ignored when a key is imported in JSON TTLV format and is already wrapped.
# key_encryption_key = "kek ID"

# Specifies which KMIP object types should be automatically unwrapped when retrieved.
# Repeat this option to specify multiple object types
# e.g.
# ```sh
#   --default-unwrap-type SecretData \
#   --default-unwrap-type SymmetricKey
# ```
# default_unwrap_type = ["SecretData", "SymmetricKey"]

# List of users who have the right to create and import Objects
# and grant access rights for Create Kmip Operation.
# privileged_users = ["<user_id_1>", "<user_id_2>"]

# Check the database configuration documentation pages for more information
[db]
# The main database of the KMS server that holds default cryptographic objects and permissions.
# - postgresql: `PostgreSQL`. The database URL must be provided
# - mysql: `MySql` or `MariaDB`. The database URL must be provided
# - sqlite: `SQLite`. The data will be stored at the `sqlite_path` directory
#   A key must be supplied on every call
# - redis-findex [non-FIPS]: a Redis database with encrypted data and indexes thanks to Findex.
#   The Redis URL must be provided, as well as the redis-master-password and the redis-findex-label
database_type = "sqlite"
# The URL of the database for `Postgres`, `MySQL`, or `Findex-Redis`
# database_url = "<database-url>"
# The directory path of the `SQLite`
# sqlite_path = "<sqlite-path>"
# redis-findex: a master password used to encrypt the Redis data and indexes
# redis_master_password = "<redis master password>"

# Clear the database on start.
# WARNING: This will delete ALL the data in the database
clear_database = false

# When a wrapped object is fetched from the database,
# it is unwrapped and stored in the unwrapped cache.
# This option specifies the maximum age in minutes of the unwrapped objects in the cache
# after its last use.
# The default is 15 minutes.
# About 2/3 of the objects will be evicted after this time; the other 1/3 will be evicted
# after a maximum of 150% of the time.
unwrapped_cache_max_age = 15 # minutes

# TLS configuration of the Socket server and HTTP server
[tls]
# The server's X.509 certificate in PEM format.
# Provide a PEM containing the server leaf certificate,
# optionally followed by intermediate certificates (full chain). When provided along with
# `--tls-key-file`, the servers will start in TLS mode.
# Do not use in combination with `--tls-p12-file`.
# tls_cert_file = "path/to/server.crt"
# The server's private key in PEM format (PKCS#8 or traditional format).
# Must correspond to the certificate in `--tls-cert-file`.
# Do not use in combination with `--tls-p12-file`.
# tls_key_file = "path/to/server.key"
# Optional certificate chain in PEM format (intermediate CAs).
# If not provided, the chain may be appended to `--tls-cert-file` instead.
# Do not use in combination with `--tls-p12-file`.
# tls_chain_file = "path/to/chain.pem"

# The KMS server optional PKCS#12 Certificates and Key file as an alternative
# to providing the key, certificate and chain in PEM format.
# When provided, the Socket and HTTP server will start in TLS Mode.
# tls_p12_file = "[tls p12 file]"
# The password to open the PKCS#12 Certificates and Key file
# tls_p12_password = "[tls p12 password]"

# The server's optional X. 509 certificate in PEM format validates the client certificate presented for authentication.
# If provided, clients must present a certificate signed by this authority for authentication.
# Mandatory to start the socket server.
# clients_ca_cert_file = "[authority cert file]"

# The socket server listens to KMIP binary requests on the IANA-registered 4696 port.
# The socket server will only start if the TLS configuration is provided **and** client certificate authentication
# is enabled.
[socket_server]
# Start the KMIP socket server? If this is set to true, the TLS config must be provided, featuring a server PKCS#12 file and a client certificate authority certificate file
# socket_server_start = false

# The KMS socket server port
# socket_server_port = 5696

# The KMS socket server hostname
# socket_server_hostname = "0.0.0.0"

# The HTTP server listens to KMIP requests on the /kmip and /kmip/2_1 endpoints.
# It also serves the web UI on the /ui endpoint.
# If the TLS configuration is provided, the server will start in HTTPS mode.
[http]
# The KMS HTTP server port
port = 9998
# The KMS HTTP server hostname
hostname = "0.0.0.0"

# An optional API token to use for authentication on the HTTP server.
# api_token_id = "<secret-api-token>"

# Maximum number of requests per second per IP address allowed by the rate limiter.
# When set, the server enforces this limit to mitigate `DoS` and brute-force attacks.
# Requests exceeding the limit receive HTTP 429 Too Many Requests.
# Leave unset (default) to disable rate limiting.
# rate_limit_per_second = 100

# Number of actix-web HTTP worker threads.
# Defaults to the number of logical CPUs. On I/O-heavy workloads (e.g. `PostgreSQL` backend)
# setting this to `2 * <number of CPU cores>` improves throughput by keeping more Tokio
# threads busy while others are waiting on network I/O.
# Can also be set via the `TOKIO_WORKER_THREADS` environment variable (Tokio runtime),
# but this flag controls only the actix-web application workers.
# http_workers = 8

# Comma-separated list of origins allowed to make cross-origin requests to the KMIP API.
# Required for any Web UI deployment: the browser Fetch API sends an `Origin` header on
# every POST request — even when the page is served by the KMS itself — and actix-cors
# rejects it unless the exact origin appears in this list.
# The value must match byte-for-byte what the user types in the browser address bar
# (scheme + hostname + port). The server bind address (`0.0.0.0`) and the server IP
# are not equivalent to a DNS hostname. The Docker image pre-populates loopback
# addresses; add any custom hostname explicitly. Example: `http://kms.example.com:9998`.
# cors_allowed_origins = ["http://localhost:9998", "http://127.0.0.1:9998"]

# If using a forward proxy for outbound JWKS requests,
# set the proxy parameters here.
[proxy]
# The proxy URL:
#   - e.g., `https://secure.example` for an HTTP proxy
#   - e.g., `socks5://192.168.1.1:9000` for a SOCKS proxy
# proxy_url = "https://proxy.example.com:8080"

# Set the Proxy-Authorization header username using Basic auth.
# proxy_basic_auth_username = "[proxy username]"

# Set the Proxy-Authorization header password using Basic auth.
# proxy_basic_auth_password = "[proxy password]"

# Set the Proxy-Authorization header to a specified value.
# proxy_custom_auth_header = "my_custom_auth_token"

# The No Proxy exclusion list to this Proxy
# proxy_exclusion_list = ["domain1", "domain2"]

# Check the Authenticating Users documentation pages for more information.
[idp_auth]
# JWT authentication provider configuration.
#
# The expected argument is --jwt-auth-provider="`PROVIDER_CONFIG_1`" --jwt-auth-provider="`PROVIDER_CONFIG_2`" ...
# where each `PROVIDER_CONFIG_N` defines one identity provider configuration.
#
# Each provider configuration `PROVIDER_CONFIG_N` should be in the format: "`JWT_ISSUER_URI,JWKS_URI,JWT_AUDIENCE_1,JWT_AUDIENCE_2,...`"
# where:
# - `JWT_ISSUER_URI`: The issuer URI of the JWT token (required)
# - `JWKS_URI`: The JWKS (JSON Web Key Set) URI (optional, defaults to <JWT_ISSUER_URI>/.well-known/jwks.json)
# - `JWT_AUDIENCE_1..N`: One or more audience values for the JWT token (optional)
#
# Examples:
# --jwt-auth-provider="https://accounts.google.com,https://www.googleapis.com/oauth2/v3/certs, kacls-migration, another-audience"
# --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"
# --jwt-auth-provider="https://<your-tenant>.<region>.auth0.com/""
# This argument can be repeated to configure multiple identity providers.
# jwt_auth_provider = [
#   "https://accounts.google.com,https://www.googleapis.com/oauth2/v3/certs,my-audience,another_client_id",
#   "https://auth0.example.com,,my-app",
#   "https://keycloak.example.com/auth/realms/myrealm,,"
# ]

[workspace]
# The root folder where the KMS will store its data A relative path is taken relative to the user's HOME directory
# root_data_path = "./cosmian-kms"

# The folder to store temporary data (non-persistent data readable by no one but the current instance during the current execution)
# tmp_path = "/tmp"

# Check the logging documentation pages for more information
[logging]
# An alternative to setting the `RUST_LOG` environment variable.
# Setting this variable will override the `RUST_LOG` environment variable
rust_log = "info,cosmian_kms=info"

# The OTLP collector URL for gRPC
# (for instance, <https://localhost:4317>)
# If not set, the telemetry system will not be initialized.
# Must use https:// in production.
# Use --otlp-allow-insecure to permit plaintext http:// connections.
# otlp = "http://localhost:4317"

# Do not log to stdout
quiet = false

# Log to syslog
log_to_syslog = false

# The directory for daily rolling logs: <rolling_log_name>.YYYY-MM-DD.
# File logging is disabled unless this option is explicitly set.
# Suggested paths:
#   Linux: /var/log/
#   Windows: C:\Users\<username>\AppData\Local\Cosmian KMS Server
#   macOS: ~/Library/Logs/
#
# WARNING: Windows environment variables (e.g. %LOCALAPPDATA%) are NOT
# expanded. Use the fully-resolved path.
# rolling_log_dir = "/var/log/"
# The name of the rolling log file: <rolling_log_name>.YYYY-MM-DD.
# Defaults to `cosmian_kms` if not set.
# rolling_log_name = "cosmian_kms"

# Enable metering in addition to tracing when telemetry is enabled
# enable_metering = false

# The name of the environment (development, test, production, etc.)
# This will be added to the telemetry data if telemetry is enabled
# environment = "development"

# Enable ANSI colors in the logs to stdout
ansi_colors = false

# Generic configuration to edit the path to static UI application files
# To use the Web UI, ensure the `kms_public_url` is set to the correct public URL above.
[ui_config]
# The UI distribution folder
# ui_index_html_folder = "/usr/local/cosmian/ui/dist"

# Configuration for the handling of authentication with OIDC from the KMS UI.
# This is used to authenticate users when they access the KMS UI.
# The same Identity Provider must **also** be configured in the [idp_auth] section above.
[ui_config.ui_oidc_auth]
# The client ID of the configured OIDC tenant for UI Auth
# ui_oidc_client_id = "<client id>"
# The client secret of the configured OIDC tenant for UI Auth
# ui_oidc_client_secret = "<client secret>" (optional)
# The issuer URI of the configured OIDC tenant for UI Auth
# ui_oidc_issuer_url = "<issuer-url>"
# The logout URI of the configured OIDC tenant for UI Auth
# ui_oidc_logout_url = "<logout-url>"

[google_cse_config]
# This setting turns on endpoints handling Google CSE feature
google_cse_enable = false

# This setting turns off the validation of the tokens used by this server's Google Workspace CSE feature
# google_cse_disable_tokens_validation = false

# This setting contains the list of KACLS server URLs that can access this server for Google CSE migration, through the privilegedunwrap endpoint (used to fetch exposed jwks on server start)
# google_cse_incoming_url_whitelist = ["[kacls_url_1]", "[kacls_url_2]"]

# PEM PKCS8 RSA private key used to ensure consistency of certificate handling and privileged unwrap operations across server restarts and multiple server instances. If not provided, a random key will be generated at server startup
# google_cse_migration_key = "<google_cse_existing_migration_key>"

[azure_ekm_config]
# This setting turns on/off the endpoints handling Azure EKM features
azure_ekm_enable = false

[aws_xks_config]
# This setting turns on endpoints handling the AWS XKS feature
aws_xks_enable = false

[kmip.allowlists]

[notifications.smtp]
# The KMS HTTP server port
port = 0

[jwks_endpoint]
# Enable the `GET /.well-known/jwks.json` endpoint.
#
# When set, the server publicly exposes all active public keys whose
# `CryptographicUsageMask` includes `Verify` as a RFC 7517 JSON Web Key Set.
# The endpoint is **unauthenticated** — no credentials are required to fetch it,
# and no authentication middleware is applied. Defaults to `false`.
jwks_endpoint_enabled = false
# Maximum number of public keys returned in a single JWKS response.
#
# When the server holds more eligible keys than this limit, the response is
# truncated and an `X-JWKS-Truncated: true` header is added to signal consumers.
# Increase this value if your deployment performs frequent key rotation and all
# overlapping verification keys must be simultaneously discoverable.
jwks_endpoint_max_keys = 50
# Automatically tag key pairs created via the REST crypto API for JWKS inclusion.
#
# When `true` (the default), every key pair created via `POST /v1/crypto/keys`
# receives the `"jwks"` tag and immediately appears in
# `GET /.well-known/jwks.json`.
#
# Set to `false` to disable this behaviour globally.  Operators must then
# manually tag each public key via `POST /v1/crypto/keys/{kid}/tags` before
# it is published in the JWKS document.
#
# This setting has no effect on keys created directly through the KMIP protocol.
jwks_endpoint_auto_tag = true

CORS configuration

Cross-Origin Resource Sharing (CORS) controls which browser origins are allowed to make requests to the KMS HTTP API.

You must configure cors_allowed_origins for any Web UI deployment that uses a hostname other than localhost.

When cors_allowed_origins is not set in the configuration file, CLI, or environment, the binary defaults to loopback origins matching the configured scheme (HTTP or HTTPS) and port. This covers localhost, 127.0.0.1, 0.0.0.0, [::1], and [::] so the bundled Web UI works out-of-the-box without any explicit configuration.

Although the KMS serves its own Web UI from the same host and port, the browser's Fetch API sends an Origin header on every non-GET/HEAD request (including POST) — even when the request originates from the same page. The actix-cors middleware compares this header against the explicit allow-list and returns HTTP 400 if the value is not present. There is no DNS resolution or network-interface expansion: the comparison is a byte-for-byte string match.

This means cors_allowed_origins must contain the exact URL the user types in the browser's address bar — scheme, hostname, and port all included. Configuring 0.0.0.0 (the bind address) or the server's IP address does not match a hostname-based origin such as http://kms.example.com:9998, and vice versa.

The binary automatically provides loopback addresses (localhost, 127.0.0.1, 0.0.0.0, [::1], [::] on the configured port) so that browser access from the same machine works out-of-the-box. Any other hostname, IP address, or port must be added explicitly.

CLI clients (ckms, scripts, curl) do not send an Origin header and are not affected by this setting.

[http]
# Allow a Vite dev-server and a custom front-end to reach the KMIP API.
cors_allowed_origins = ["http://127.0.0.1:5173", "https://app.example.com"]

The same list can be provided via the environment variable KMS_CORS_ALLOWED_ORIGINS (comma-separated) or the CLI flag --cors-allowed-origins.

Security implications

Every origin in cors_allowed_origins can issue authenticated cross-origin requests to the KMS — session cookies and credentials are forwarded for each listed origin.

  • Only add origins you fully control and trust. A compromised or malicious site listed here can read and manage all cryptographic objects accessible to the authenticated user.
  • Never use a wildcard (*). actix-cors rejects a wildcard when supports_credentials() is active, and a wildcard CORS policy would expose every user's keys to any website on the internet.
  • Add every URL users will type in their browser — scheme, hostname, and port must all match exactly. 0.0.0.0 (bind address) and the server's IP address are not interchangeable with the DNS hostname used by the browser.
  • Security note: while a user is authenticated to the KMS, avoid browsing untrusted sites in the same browser session. CORS is a browser-enforced defense in depth; its effectiveness depends on the browser correctly implementing the same-origin policy, which cannot be guaranteed across all browser versions and configurations.

Enterprise integration scopes are not affected by this setting. The Google CSE, Microsoft DKE, and AWS XKS endpoints retain their own permissive CORS policy as required by their respective integration contracts — cors_allowed_origins has no effect on those routes.

Configuration Examples

This page provides a comprehensive collection of TOML configuration file examples for the Eviden KMS server. Each configuration has been validated and tested to ensure it works correctly with the KMS server.

All configuration examples can be used by:

  1. Saving the content to a file (e.g., kms.toml)
  2. Starting the server with: cosmian_kms_server -c kms.toml or using the environment variable COSMIAN_KMS_CONF=kms.toml

Note on default path: If no -c flag or COSMIAN_KMS_CONF variable is set, the server automatically loads /etc/cosmian/kms.toml (Linux/macOS) or C:\ProgramData\Cosmian\kms.toml (Windows) when it exists. If that file is present and you also pass command-line arguments, the server exits with an error to prevent your arguments from being silently ignored.

For complete documentation on all available configuration options, see the Configuration file reference.

Quick Start Configurations

basic-http

Basic HTTP server configuration with default settings.

# Basic HTTP configuration
[http]
port = 9998
hostname = "0.0.0.0"
cors_allowed_origins = ["http://localhost:9998", "http://127.0.0.1:9998"]

Use case: Development, testing, or internal networks where TLS is not required.


Authentication Configurations

jwt-auth

JWT authentication using Google as the identity provider.

# JWT authentication configuration using idp_auth format
[idp_auth]
# issuer,jwks[,aud1[,aud2...]] (jwks & audiences optional; any-of when multiple)
jwt_auth_provider = ["https://accounts.google.com,https://www.googleapis.com/oauth2/v3/certs,cosmian_kms,another_client_id"]

Use case: Authenticate users with Google Identity tokens, suitable for organizations using Google Workspace.


api-token-auth

API token authentication using a symmetric key.

# API token authentication configuration
[http]
port = 9998
hostname = "0.0.0.0"
cors_allowed_origins = ["http://localhost:9998", "http://127.0.0.1:9998"]
api_token_id = "test-symmetric-key-id"

Use case: Service-to-service authentication or when using a pre-shared symmetric key for authentication.


cors-configuration

Cross-Origin Resource Sharing (CORS) configuration for browser clients.

# CORS configuration for browser-based clients
[http]
port = 9998
hostname = "0.0.0.0"
# Allow the bundled Web UI and any Vite dev server running on the same machine
cors_allowed_origins = ["http://localhost:9998", "http://127.0.0.1:9998"]

Use case: When a browser-based client (e.g. the bundled Web UI or a Vite dev server) runs on a different origin from the KMS server. In production behind a load balancer, set this to your public URL. Leave unset to restrict to same-origin only.


tls-client-cert

TLS client certificate authentication with mutual TLS.

# TLS Client Certificate Authentication
[tls]
# For FIPS mode (default build):
tls_cert_file = "certificates/server.crt"
tls_key_file = "certificates/server.key"
clients_ca_cert_file = "certificates/ca.crt"

# For non-FIPS mode:
# tls_p12_file = "certificates/server.p12"
# tls_p12_password = "password"
# clients_ca_cert_file = "certificates/ca.crt"

Use case: High-security environments requiring mutual TLS authentication with client certificates.


multifactor-tls-jwt

Multi-factor authentication combining TLS client certificates and JWT tokens.

# Multi-factor TLS + JWT authentication (idp_auth format)
[tls]
# For FIPS mode (default build):
# tls_cert_file = "certificates/server.crt"
# tls_key_file = "certificates/server.key"
# clients_ca_cert_file = "certificates/ca.crt"

# For non-FIPS mode:
tls_p12_file = "certificates/server.p12"
tls_p12_password = "password"
clients_ca_cert_file = "certificates/ca.crt"

[idp_auth]
# Empty audience example: no audiences after JWKS URL (audience check skipped)
jwt_auth_provider = ["https://accounts.google.com,https://www.googleapis.com/oauth2/v3/certs,"]

Use case: Maximum security environments requiring both certificate and token-based authentication.


multifactor-jwt-api

Multi-factor authentication combining JWT tokens and API token authentication.

# Multi-factor JWT + API token authentication (idp_auth format)
[idp_auth]
# Multiple audiences example (any-of)
jwt_auth_provider = ["https://accounts.google.com,https://www.googleapis.com/oauth2/v3/certs,frontend,cli"]

[http]
port = 9998
hostname = "0.0.0.0"
cors_allowed_origins = ["http://localhost:9998", "http://127.0.0.1:9998"]
api_token_id = "test-symmetric-key-id"

Use case: Flexible authentication allowing both user JWT tokens and service API tokens.


Database Configurations

mysql-database

MySQL database configuration for production deployments.

# MySQL database configuration
[db]
database_type = "mysql"
database_url = "mysql://kms_user:kms_password@mysql-server:3306/kms"

Use case: Production deployments requiring a robust, scalable database backend.


mysql-with-cert

MySQL database with client certificate authentication.

# MySQL database configuration with client certificate
[db]
database_type = "mysql"
database_url = "mysql://mysql_server:3306/kms"
# Note: Configure client certificate via command-line option:
# --mysql-user-cert-file cert.p12

Use case: MySQL deployments requiring certificate-based database authentication.


postgresql-database

PostgreSQL database configuration for production deployments.

# PostgreSQL database configuration
[db]
database_type = "postgresql"
database_url = "postgres://kms_user:kms_password@postgres-server:5432/kms"

Use case: Production deployments requiring strong reliability, advanced SQL features, and horizontal scaling options.


sqlite-database

Lightweight embedded SQLite configuration (default when no DB settings are provided).

# SQLite database configuration (default path)
[db]
database_type = "sqlite"
sqlite_path = "./sqlite-data"  # Defaults to ./sqlite-data
# clear_database = false         # Set to true to wipe the DB on each start (DANGEROUS)

Use case: Local development, testing, or small single-instance deployments where simplicity outweighs concurrency needs.


redis-findex-database

Redis with Findex encrypted data and searchable indexes (non-FIPS build only).

# Redis Findex database configuration (non-FIPS feature)
[db]
database_type = "redis-findex"
database_url = "redis://redis-server:6379"
redis_master_password = "change_me_master_password"   # Master password derives encryption key

Use case: Environments requiring encrypted server-side indexes and low-latency lookups with application-level protection.


Google Workspace CSE Configuration

google-cse

Complete Google Workspace Client-Side Encryption setup.

# Google CSE configuration
kms_public_url = "http://localhost:9998"

# JWT authentication with Google
[idp_auth]
jwt_auth_provider = ["https://accounts.google.com,https://www.googleapis.com/oauth2/v3/certs,"]

# Google CSE configuration
[google_cse_config]
google_cse_enable = true

Use case: Enabling Google Workspace Client-Side Encryption for Gmail and Drive.


Logging and Monitoring Configurations

otlp-logging

OpenTelemetry (OTLP) logging configuration with Jaeger integration.

# OTLP logging configuration
[logging]
otlp = "http://localhost:4317"
quiet = true

Use case: Centralized logging and distributed tracing with OpenTelemetry-compatible systems.


file-logging

Rolling file logging configuration.

# Rolling file logging
[logging]
rolling_log_dir = "/var/log/"
rolling_log_name = "kms"
rust_log = "info,cosmian_kms_server=debug"

Use case: Production deployments requiring persistent log files with rotation.


syslog-logging

System logging configuration for Linux systems.

# Syslog logging
[logging]
log_to_syslog = true
quiet = true
rust_log = "warn,cosmian_kms_server=info"

Use case: Integration with system logging infrastructure on Linux servers.


Production Configurations

production-https

Complete production configuration with HTTPS, authentication, and MySQL.

# Production HTTPS configuration
kms_public_url = "https://kms.example.com"

[http]
port = 443
hostname = "0.0.0.0"
cors_allowed_origins = ["https://kms.example.com"]

[tls]
# For FIPS mode (default build):
# tls_cert_file = "/etc/ssl/kms/server.crt"
# tls_key_file = "/etc/ssl/kms/server.key"

# For non-FIPS mode:
tls_p12_file = "/etc/ssl/kms/server.p12"
tls_p12_password = "secure_password"

[idp_auth]
jwt_auth_provider = ["https://accounts.google.com,https://www.googleapis.com/oauth2/v3/certs,kms.example.com"]

[db]
database_type = "mysql"
database_url = "mysql://kms_user:secure_password@mysql.internal:3306/kms"

[logging]
rolling_log_dir = "/var/log/"
rust_log = "warn,cosmian_kms_server=info"

Use case: Full production deployment with security, scalability, and monitoring.


ui-load-balanced

Production configuration exposing the Web UI behind a load balancer, with deterministic session cookies across KMS instances.

# Public URL exposed by the load balancer
kms_public_url = "https://kms-ui.example.com"

[http]
port = 9998
hostname = "0.0.0.0"
cors_allowed_origins = ["https://kms-ui.example.com"]

# UI configuration: static assets served from this folder
[ui_config]
ui_index_html_folder = "./ui/dist"

# Session salt used to derive the UI session cookie encryption key.
# This value MUST be identical across all KMS instances behind the
# same load balancer so any instance can decrypt the UI session cookie.
ui_session_salt = "change-me-and-keep-secret"

[logging]
rust_log = "info,cosmian_kms_server=info"

Use case: Web UI deployments behind a load balancer where UI session cookies must remain valid across all KMS instances.


ha-cluster

High-availability cluster configuration.

# High-availability cluster configuration
kms_public_url = "https://kms-cluster.example.com"

[http]
port = 9998
hostname = "0.0.0.0"
cors_allowed_origins = ["https://kms-cluster.example.com"]

[tls]
# For FIPS mode (default build):
# tls_cert_file = "/etc/ssl/kms/server.crt"
# tls_key_file = "/etc/ssl/kms/server.key"

# For non-FIPS mode:
tls_p12_file = "/etc/ssl/kms/server.p12"
tls_p12_password = "cluster_password"

[db]
database_type = "postgresql"
database_url = "postgres://kms_user:password@postgres-cluster:5432/kms"

[logging]
otlp = "http://jaeger-collector:4317"
rust_log = "info,cosmian_kms_server=info"

Use case: High-availability deployments with load balancing and shared database.


Specialized Configurations

redis-findex

Redis with Findex configuration for encrypted storage.

# Redis with Findex configuration
[db]
database_type = "redis-findex"
database_url = "redis://redis-server:6379"
redis_master_password = "secure_master_password"

[logging]
rust_log = "info,cosmian_kms_server=info,cosmian_findex_client=debug"

Use case: Zero-trust environments with application-level encryption of database contents.


hsm-integration

Hardware Security Module (HSM) integration.

# HSM integration configuration
[hsm]
pkcs11_library = "/usr/lib/libpkcs11.so"
slot_number = 1
pin = "hsm_user_pin"

[logging]
rust_log = "info,cosmian_kms_server=info"

Use case: Hardware-backed key protection using PKCS#11 compatible HSMs.


key-wrapping

Automatic key wrapping with a Key Encryption Key (KEK) and selective automatic unwrapping.

# Key wrapping configuration
# Force all newly created and imported keys to be wrapped by the specified KEK
key_encryption_key = "hsm-kek-id"

# Automatically unwrap specific object types when retrieved
default_unwrap_type = ["SymmetricKey", "SecretData"]

[logging]
rust_log = "info,cosmian_kms_server=info"

Use case: Ensure all keys in the KMS database are wrapped by an HSM key for enhanced security. The server transparently unwraps keys when needed, caching them in memory without storing clear-text keys in the database. Commonly used with HSM integration to protect all KMS keys with a master KEK stored in the HSM. The cache expires after a configurable duration (default 15 minutes).


development-debug

Development configuration with detailed debugging.

# Development debugging configuration
[http]
port = 9998
hostname = "127.0.0.1"
cors_allowed_origins = ["http://localhost:9998", "http://127.0.0.1:9998"]

[logging]
rust_log = "debug,cosmian_kms_server=trace,actix_web=debug"

Use case: Development and troubleshooting with maximum logging detail.


Testing Configurations

integration-test

Configuration for integration testing environments.

# Integration testing configuration
[http]
port = 19998
hostname = "127.0.0.1"
cors_allowed_origins = ["http://localhost:19998", "http://127.0.0.1:19998"]

[db]
database_type = "sqlite"
sqlite_path = "./test-sqlite-data"
cleanup_on_startup = true

[logging]
quiet = true
rust_log = "error"

Use case: Automated testing environments with isolated database and minimal logging.



Usage: cosmian_kms [OPTIONS] [KEY_ENCRYPTION_KEY]

Arguments:
  [KEY_ENCRYPTION_KEY]
          Force all keys imported or created in the KMS, which are not protected by a key encryption key, to be wrapped by the specified key encryption key (KEK)

Options:
  -c, --config <COSMIAN_KMS_CONF>
          Explicit configuration file path provided via -c / --config. When set, this file takes precedence over the `COSMIAN_KMS_CONF` environment variable and the default system path. All other command line arguments (except `--help` / `--version`) and environment variables are ignored once the configuration file is loaded

      --vendor-identification <VENDOR_IDENTIFICATION>
          The vendor identification string reported in KMIP `QueryServerInformation` responses

          [env: KMS_VENDOR_IDENTIFICATION=]
          [default: cosmian]

      --default-username <DEFAULT_USERNAME>
          The default username to use when no authentication method is provided

          [env: KMS_DEFAULT_USERNAME=]
          [default: admin]

      --force-default-username
          When an authentication method is provided, perform the authentication
          but always use the default username instead of the one provided by the authentication method

          [env: KMS_FORCE_DEFAULT_USERNAME=]

      --ms-dke-service-url <MS_DKE_SERVICE_URL>
          This setting enables the Microsoft Double Key Encryption service feature of this server.

          It should contain the external URL of this server as configured in Azure App Registrations
          as the DKE Service (<https://learn.microsoft.com/en-us/purview/double-key-encryption-setup#register-your-key-store>)

          The URL should be something like <https://cse.my_domain.com/ms_dke>

          [env: KMS_MS_DKE_SERVICE_URL=]

      --info
          Print the server configuration information and exit

      --print-default-config
          Serialize the default server configuration as TOML to stdout and exit. This is used to keep the documentation in sync with the Rust struct

      --hsm-model <HSM_MODEL>
          The HSM model.
          `Trustway Proteccio`, `Trustway Crypt2pay`, `Utimaco General Purpose HSM`,
          `Smartcard HSM`, and `SoftHSM2` are natively supported.
          Other HSMs are supported too; specify `other` and check the documentation

          [default: proteccio]
          [possible values: proteccio, crypt2pay, utimaco, softhsm2, smartcardhsm, other]

      --hsm-admin <HSM_ADMIN>...
          List of KMS usernames that are granted HSM admin privileges.
          HSM admins can create, destroy, and potentially export objects on the HSM.
          Use `"*"` as the only entry to grant all authenticated users admin access.
          Repeat the option or use a comma-separated list to specify multiple admins:
            `--hsm-admin alice@example.com --hsm-admin bob@example.com`
            or set `KMS_HSM_ADMIN=alice@example.com,bob@example.com`

          [env: KMS_HSM_ADMIN=]
          [default: admin]

      --hsm-slot <HSM_SLOT>
          HSM slot number. The slots used must be listed.
          Repeat this option to specify multiple slots
          while specifying a password for each slot (or an empty string for no password)
          e.g.
          ```sh
            --hsm-slot 1 --hsm-password password1 \
            --hsm-slot 2 --hsm-password password2
          ```

          [env: KMS_HSM_SLOT=]

      --hsm-password <HSM_PASSWORD>
          Password for the user logging in to the HSM Slot specified with `--hsm_slot`
          Provide an empty string for no password
          see `--hsm_slot` for more information.
          Set `KMS_HSM_PASSWORD` to avoid the password appearing in `ps` output.

          [env: KMS_HSM_PASSWORD=]

      --default-unwrap-type <DEFAULT_UNWRAP_TYPE>
          Specifies which KMIP object types should be automatically unwrapped when retrieved.
          Repeat this option to specify multiple object types
          e.g.
          ```sh
            --default-unwrap-type SecretData \
            --default-unwrap-type SymmetricKey
          ```

          [possible values: All, Certificate, CertificateRequest, OpaqueObject, PGPKey, PrivateKey, PublicKey, SecretData, SplitKey, SymmetricKey]

      --kms-public-url <KMS_PUBLIC_URL>
          The exposed URL of the KMS - this is required if Google CSE configuration is activated.
          If this server is running on the domain `cse.my_domain.com` with this public URL,
          The configured URL from Google admin  should be something like <https://cse.my_domain.com/google_cse>
          The URL is also used during the authentication flow initiated from the KMS UI.

          [env: KMS_PUBLIC_URL=]

      --database-type <DATABASE_TYPE>
          The main database of the KMS server that holds default cryptographic objects and permissions.
          - postgresql: `PostgreSQL`. The database URL must be provided
          - mysql: `MySql` or `MariaDB`. The database URL must be provided
          - sqlite: `SQLite`. The data will be stored at the `sqlite_path` directory
            A key must be supplied on every call
          - redis-findex [non-FIPS]: a Redis database with encrypted data and indexes thanks to Findex.
            The Redis URL must be provided, as well as the redis-master-password and the redis-findex-label

          [env: KMS_DATABASE_TYPE=]
          [possible values: postgresql, mysql, sqlite, redis-findex]

      --database-url <DATABASE_URL>
          The URL of the database for `Postgres`, `MySQL`, or `Findex-Redis`

          [env: KMS_DATABASE_URL=]

      --sqlite-path <SQLITE_PATH>
          The directory path of the `SQLite`

          [env: KMS_SQLITE_PATH=]
          [default: ./sqlite-data]

      --redis-master-password <REDIS_MASTER_PASSWORD>
          redis-findex: a master password used to encrypt the Redis data and indexes

          [env: KMS_REDIS_MASTER_PASSWORD=]

      --clear-database
          Clear the database on start.
          WARNING: This will delete ALL the data in the database

          [env: KMS_CLEAR_DATABASE=]

      --max-connections <MAX_CONNECTIONS>
          Maximum number of connections for the relational database pool. When not provided, falls back to the current defaults: - `PostgreSQL`/`MySQL`: min(10, 2 × CPU cores), fallback 10 - `SQLite`: number of CPUs

          [env: KMS_DB_MAX_CONNECTIONS=]

      --unwrapped-cache-max-age <UNWRAPPED_CACHE_MAX_AGE>
          When a wrapped object is fetched from the database,
          it is unwrapped and stored in the unwrapped cache.
          This option specifies the maximum age in minutes of the unwrapped objects in the cache
          after its last use.
          The default is 15 minutes.
          About 2/3 of the objects will be evicted after this time; the other 1/3 will be evicted
          after a maximum of 150% of the time.

          [env: KMS_UNWRAPPED_CACHE_MAX_AGE=]
          [default: 15]

      --unwrapped-cache-max-size <UNWRAPPED_CACHE_MAX_SIZE>
          Maximum number of entries in the unwrapped key cache.
          When the cache is full, the least-recently-used entry is evicted.
          Set this above the number of distinct wrapped keys in your deployment
          to avoid LRU thrashing. The default is 1000.

          [env: KMS_UNWRAPPED_CACHE_MAX_SIZE=]
          [default: 1000]

      --unwrapped-cache-max-ttl <UNWRAPPED_CACHE_MAX_TTL>
          Absolute time-to-live in minutes for entries in the unwrapped key cache.
          When set, a cached unwrapped key is evicted at most this many minutes after
          it was first inserted, regardless of how frequently it is accessed.
          This caps plaintext key residency for continuously-used (hot) keys and
          satisfies compliance policies that require a hard upper bound on in-memory
          key material exposure.
          When not set (the default), only the time-to-idle window applies and hot
          keys may remain cached indefinitely.
          When set, value must be ≥ `unwrapped-cache-max-age`.

          [env: KMS_UNWRAPPED_CACHE_MAX_TTL=]

      --disable-unwrapped-cache
          Disable the unwrapped key cache entirely.
          When set, every operation that needs plaintext key material will perform
          a full KEK-unwrap (or HSM call) on every request instead of serving the
          key from memory.
          Use this in high-security environments where no plaintext key material
          should persist in process memory beyond a single operation.
          Disabling the cache significantly increases CPU and HSM load.

          [env: KMS_DISABLE_UNWRAPPED_CACHE=]

      --socket-server-start
          Start the KMIP socket server? If this is set to true, the TLS config must be provided, featuring a server PKCS#12 file and a client certificate authority certificate file

          [env: KMS_SOCKET_SERVER_START=]

      --socket-server-port <SOCKET_SERVER_PORT>
          The KMS socket server port

          [env: KMS_SOCKET_SERVER_PORT=]
          [default: 5696]

      --socket-server-hostname <SOCKET_SERVER_HOSTNAME>
          The KMS socket server hostname

          [env: KMS_SOCKET_SERVER_HOSTNAME=]
          [default: 0.0.0.0]

      --tls-p12-file <TLS_P12_FILE>
          The KMS server optional PKCS#12 Certificates and Key file as an alternative
          to providing the key, certificate and chain in PEM format.
          When provided, the Socket and HTTP server will start in TLS Mode.

          [env: KMS_TLS_P12_FILE=]

      --tls-p12-password <TLS_P12_PASSWORD>
          The password to open the PKCS#12 Certificates and Key file

          [env: KMS_TLS_P12_PASSWORD=]

      --tls-cert-file <TLS_CERT_FILE>
          The server's X.509 certificate in PEM format.
          Provide a PEM containing the server leaf certificate,
          optionally followed by intermediate certificates (full chain). When provided along with
          `--tls-key-file`, the servers will start in TLS mode.
          Do not use in combination with `--tls-p12-file`.

          [env: KMS_TLS_CERT_FILE=]

      --tls-key-file <TLS_KEY_FILE>
          The server's private key in PEM format (PKCS#8 or traditional format).
          Must correspond to the certificate in `--tls-cert-file`.
          Do not use in combination with `--tls-p12-file`.

          [env: KMS_TLS_KEY_FILE=]

      --tls-chain-file <TLS_CHAIN_FILE>
          Optional certificate chain in PEM format (intermediate CAs).
          If not provided, the chain may be appended to `--tls-cert-file` instead.
          Do not use in combination with `--tls-p12-file`.

          [env: KMS_TLS_CHAIN_FILE=]

      --clients-ca-cert-file <CLIENTS_CA_CERT_FILE>
          The server's optional X. 509 certificate in PEM format validates the client certificate presented for authentication.
          If provided, clients must present a certificate signed by this authority for authentication.
          Mandatory to start the socket server.

          [env: KMS_CLIENTS_CA_CERT_FILE=]

      --tls-cipher-suites <TLS_CIPHER_SUITES>
          Colon-separated list of TLS cipher suites to enable:
          Example: --tls-cipher-suites `"TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256"`
          If not specified, OpenSSL default cipher suites will be used:
          ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:\
          ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:\
          DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:\
          ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:\
          ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:\
          DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:\
          EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:\
          AES256-SHA:DES-CBC3-SHA:!DSS"
          Otherwise, the ANSSI TLS 1.2 guide recommends prioritizing AEAD suites using ECDHE
          key exchange, with AES-GCM/AES-CCM (preferred) and ChaCha20-Poly1305 as an acceptable
          alternative.

          Example (TLS 1.2):
          `TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:TLS_ECDHE_ECDSA_WITH_AES_256_CCM:TLS_ECDHE_ECDSA_WITH_AES_128_CCM:TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256`

          [env: KMS_TLS_CIPHER_SUITES=]

      --port <PORT>
          The KMS HTTP server port

          [env: KMS_PORT=]
          [default: 9998]

      --hostname <HOSTNAME>
          The KMS HTTP server hostname

          [env: KMS_HOSTNAME=]
          [default: 0.0.0.0]

      --api-token-id <API_TOKEN_ID>
          An optional API token to use for authentication on the HTTP server.

          [env: KMS_API_TOKEN=]

      --rate-limit-per-second <RATE_LIMIT_PER_SECOND>
          Maximum number of requests per second per IP address allowed by the rate limiter.
          When set, the server enforces this limit to mitigate `DoS` and brute-force attacks.
          Requests exceeding the limit receive HTTP 429 Too Many Requests.
          Leave unset (default) to disable rate limiting.

          [env: KMS_RATE_LIMIT_PER_SECOND=]

      --cors-allowed-origins <CORS_ALLOWED_ORIGINS>
          Comma-separated list of origins allowed to make cross-origin requests to the KMIP API.
          Required for any Web UI deployment: the browser Fetch API sends an `Origin` header on
          every POST request — even when the page is served by the KMS itself — and actix-cors
          rejects it unless the exact origin appears in this list.
          The value must match byte-for-byte what the user types in the browser address bar
          (scheme + hostname + port). The server bind address (`0.0.0.0`) and the server IP
          are not equivalent to a DNS hostname. The Docker image pre-populates loopback
          addresses; add any custom hostname explicitly. Example: `http://kms.example.com:9998`.

          [env: KMS_CORS_ALLOWED_ORIGINS=]

      --http-workers <HTTP_WORKERS>
          Number of actix-web HTTP worker threads.
          Defaults to the number of logical CPUs. On I/O-heavy workloads (e.g. `PostgreSQL` backend)
          setting this to `2 * <number of CPU cores>` improves throughput by keeping more Tokio
          threads busy while others are waiting on network I/O.
          Can also be set via the `TOKIO_WORKER_THREADS` environment variable (Tokio runtime),
          but this flag controls only the actix-web application workers.

          [env: KMS_HTTP_WORKERS=]

      --jwks-enabled
          Enable the `GET /.well-known/jwks.json` endpoint.

          When set, the server exposes all public keys with the `Verify` usage mask as a
          RFC 7517 JSON Web Key Set. Defaults to `false`; set to `true` to enable public key
          discovery for JWT verification.

          [env: KMS_JWKS_ENABLED=]

      --proxy-url <PROXY_URL>
          The proxy URL:
            - e.g., `https://secure.example` for an HTTP proxy
            - e.g., `socks5://192.168.1.1:9000` for a SOCKS proxy

          [env: KMS_PROXY_URL=]

      --proxy-basic-auth-username <PROXY_BASIC_AUTH_USERNAME>
          Set the Proxy-Authorization header username using Basic auth.

          [env: KMS_PROXY_BASIC_AUTH_USERNAME=]

      --proxy-basic-auth-password <PROXY_BASIC_AUTH_PASSWORD>
          Set the Proxy-Authorization header password using Basic auth.

          [env: KMS_PROXY_BASIC_AUTH_PASSWORD=]

      --proxy-custom-auth-header <PROXY_CUSTOM_AUTH_HEADER>
          Set the Proxy-Authorization header to a specified value.

          [env: KMS_PROXY_CUSTOM_AUTH_HEADER=]

      --proxy-exclusion-list <PROXY_EXCLUSION_LIST>
          The No Proxy exclusion list to this Proxy

          [env: KMS_PROXY_NO_PROXY=]

      --jwt-auth-provider <JWT_AUTH_PROVIDER>
          JWT authentication provider configuration.

          The expected argument is --jwt-auth-provider="`PROVIDER_CONFIG_1`" --jwt-auth-provider="`PROVIDER_CONFIG_2`" ...
          where each `PROVIDER_CONFIG_N` defines one identity provider configuration.

          Each provider configuration `PROVIDER_CONFIG_N` should be in the format: "`JWT_ISSUER_URI,JWKS_URI,JWT_AUDIENCE_1,JWT_AUDIENCE_2,...`"
          where:
          - `JWT_ISSUER_URI`: The issuer URI of the JWT token (required)
          - `JWKS_URI`: The JWKS (JSON Web Key Set) URI (optional, defaults to <JWT_ISSUER_URI>/.well-known/jwks.json)
          - `JWT_AUDIENCE_1..N`: One or more audience values for the JWT token (optional)

          Examples:
          --jwt-auth-provider="https://accounts.google.com,https://www.googleapis.com/oauth2/v3/certs, kacls-migration, another-audience"
          --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"
          --jwt-auth-provider="https://<your-tenant>.<region>.auth0.com/""
          This argument can be repeated to configure multiple identity providers.

          [env: KMS_JWT_AUTH_PROVIDER=]

      --enable
          Disable the embedded web UI. When set to false, the UI HTML assets are not served and all `/ui/` routes return 404

          [env: KMS_UI_ENABLE=]

  -u, --ui-index-html-folder <UI_INDEX_HTML_FOLDER>
          The UI distribution folder

          [env: COSMIAN_UI_DIST_PATH=]

      --ui-session-salt <UI_SESSION_SALT>
          A secret salt used to derive the session cookie encryption key.
          This MUST be identical across all KMS instances behind the same load balancer.
          This should only be provided when `ui_index_html_folder` is explicitly defined.

          [env: KMS_SESSION_SALT=]

      --ui-oidc-client-id <UI_OIDC_CLIENT_ID>
          The client ID of the configured OIDC tenant for UI Auth

          [env: UI_OIDC_CLIENT_ID=]

      --ui-oidc-client-secret <UI_OIDC_CLIENT_SECRET>
          The client secret of the configured OIDC tenant for UI Auth

          [env: UI_OIDC_CLIENT_SECRET=]

      --ui-oidc-issuer-url <UI_OIDC_ISSUER_URL>
          The issuer URI of the configured OIDC tenant for UI Auth

          [env: UI_OIDC_ISSUER_URL=]

      --ui-oidc-logout-url <UI_OIDC_LOGOUT_URL>
          The logout URI of the configured OIDC tenant for UI Auth

          [env: UI_OIDC_LOGOUT_URL=]

      --google-cse-enable
          This setting turns on endpoints handling Google CSE feature

          [env: KMS_GOOGLE_CSE_ENABLE=]

      --google-cse-disable-tokens-validation
          This setting turns off the validation of the tokens used by this server's Google Workspace CSE feature

          [env: KMS_GOOGLE_CSE_DISABLE_TOKENS_VALIDATION=]

      --google-cse-incoming-url-whitelist <GOOGLE_CSE_INCOMING_URL_WHITELIST>
          This setting contains the list of KACLS server URLs that can access this server for Google CSE migration, through the privilegedunwrap endpoint (used to fetch exposed jwks on server start)

          [env: KMS_GOOGLE_CSE_INCOMING_URL_WHITELIST=]

      --google-cse-migration-key <GOOGLE_CSE_MIGRATION_KEY>
          PEM PKCS8 RSA private key used to ensure consistency of certificate handling and privileged unwrap operations across server restarts and multiple server instances. If not provided, a random key will be generated at server startup

          [env: KMS_GOOGLE_CSE_MIGRATION_KEY=]

      --azure-ekm-enable
          This setting turns on/off the endpoints handling Azure EKM features

          [env: KMS_AZURE_EKM_ENABLE=]

      --azure-ekm-path-prefix <AZURE_EKM_PATH_PREFIX>
          Optional path prefix set within Managed HSM during EKM configuration.

          Enables multi-customer use or isolation of different MHSM pools using the same proxy.
          Must be max 64 characters: letters (a-z, A-Z), numbers (0-9), slashes (/), dashes (-).

          [env: KMS_AZURE_EKM_PATH_PREFIX=]

      --azure-ekm-disable-client-auth
          WARNING: This bypasses mTLS authentication entirely. Only use for testing!

          [env: KMS_AZURE_EKM_DISABLE_CLIENT_AUTH=]

      --azure-ekm-proxy-vendor <AZURE_EKM_PROXY_VENDOR>
          Proxy vendor name to report in /info endpoint

          [env: KMS_AZURE_EKM_PROXY_VENDOR=]
          [default: Cosmian]

      --azure-ekm-proxy-name <AZURE_EKM_PROXY_NAME>
          Proxy name to report in /info endpoint

          [env: KMS_AZURE_EKM_PROXY_NAME=]
          [default: "EKM Proxy Service"]

      --azure-ekm-ekm-vendor <AZURE_EKM_EKM_VENDOR>
          EKMS vendor name report in the /info endpoint

          [env: KMS_AZURE_EKM_VENDOR=]
          [default: Cosmian]

      --azure-ekm-ekm-product <AZURE_EKM_EKM_PRODUCT>
          Product Name and Version of the EKMS to report in the /info endpoint

          [env: KMS_AZURE_EKM_PRODUCT=]
          [default: "Cosmian KMS v5.25.0"]

      --root-data-path <ROOT_DATA_PATH>
          The root folder where the KMS will store its data A relative path is taken relative to the user's HOME directory

          [env: KMS_ROOT_DATA_PATH=]
          [default: ./cosmian-kms]

      --tmp-path <TMP_PATH>
          The folder to store temporary data (non-persistent data readable by no one but the current instance during the current execution)

          [env: KMS_TMP_PATH=]
          [default: /tmp]

      --rust-log <RUST_LOG>
          An alternative to setting the `RUST_LOG` environment variable.
          Setting this variable will override the `RUST_LOG` environment variable

          [env: KMS_RUST_LOG=]

      --otlp <OTLP>
          The OTLP collector URL for gRPC
          (for instance, <https://localhost:4317>)
          If not set, the telemetry system will not be initialized.
          Must use https:// in production.
          Use --otlp-allow-insecure to permit plaintext http:// connections.

          [env: KMS_OTLP_URL=]

      --otlp-allow-insecure
          Allow insecure (plaintext HTTP) OTLP connections. WARNING: Enabling this exposes telemetry data (including encryption operation metadata) over an unencrypted channel. Only use for development or when the collector is on localhost

          [env: KMS_OTLP_ALLOW_INSECURE=]

      --quiet
          Do not log to stdout

          [env: KMS_LOG_QUIET=]

      --log-to-syslog
          Log to syslog

          [env: KMS_LOG_TO_SYSLOG=]

      --rolling-log-dir <ROLLING_LOG_DIR>
          The directory for daily rolling logs: <rolling_log_name>.YYYY-MM-DD.
          File logging is disabled unless this option is explicitly set.
          Suggested paths:
            Linux: /var/log/
            Windows: C:\Users\<username>\AppData\Local\Cosmian KMS Server
            macOS: ~/Library/Logs/

          WARNING: Windows environment variables (e.g. %LOCALAPPDATA%) are NOT
          expanded. Use the fully-resolved path.

          [env: KMS_ROLLING_LOG_DIR=]

      --rolling-log-name <ROLLING_LOG_NAME>
          The name of the rolling log file: <rolling_log_name>.YYYY-MM-DD.
          Defaults to `cosmian_kms` if not set.

          [env: KMS_ROLLING_LOG_NAME=]

      --enable-metering
          Enable metering in addition to tracing when telemetry is enabled

          [env: KMS_ENABLE_METERING=]

      --environment <ENVIRONMENT>
          The name of the environment (development, test, production, etc.)
          This will be added to the telemetry data if telemetry is enabled

          [env: KMS_ENVIRONMENT=]
          [default: development]

      --ansi-colors
          Enable ANSI colors in the logs to stdout

          [env: KMS_ANSI_COLORS=]

      --privileged-users <PRIVILEGED_USERS>
          List of users who have the right to create and import Objects
          and grant access rights for Create Kmip Operation.

      --aws-xks-enable
          This setting turns on endpoints handling the AWS XKS feature

          [env: KMS_AWS_XKS_ENABLE=]

      --aws-xks-region <AWS_XKS_REGION>
          The AWS XKS region to use for signing requests (sigv4)

          [env: KMS_AWS_XKS_REGION=]

      --aws-xks-service <AWS_XKS_SERVICE>
          The AWS XKS service name to use for signing requests (sigv4)

          [env: KMS_AWS_XKS_SERVICE=]

      --aws-xks-sigv4-access-key-id <AWS_XKS_SIGV4_ACCESS_KEY_ID>
          The AWS XKS `SigV4` access key ID used to sign requests

          [env: KMS_AWS_XKS_SIGV4_ACCESS_KEY_ID=]

      --aws-xks-sigv4-secret-access-key <AWS_XKS_SIGV4_SECRET_ACCESS_KEY>
          [env: KMS_AWS_XKS_SIGV4_SECRET_ACCESS_KEY=]

      --kmip-policy-id <POLICY_ID>
          KMIP algorithm policy selector.

          Accepted values (case-insensitive):
          - `DEFAULT`: enforce the built-in conservative allowlists (aligned with ANSSI/NIST/FIPS).
          - `CUSTOM`: enforce the allowlists provided under `[kmip.allowlists]`.

          [env: KMS_POLICY_ID=]

      --auto-rotation-check-interval-secs <AUTO_ROTATION_CHECK_INTERVAL_SECS>
          Interval in seconds between background auto-rotation checks.
          Set to 0 (default) to disable the auto-rotation background task.
          When enabled, must be at least 60 seconds to avoid excessive database churn.

          [default: 0]

      --keyset-warn-depth <KEYSET_WARN_DEPTH>
          Depth at which a successful keyset chain decryption triggers a server-side warning.
          Keyset chain traversal is unbounded (stopped only by cycle detection);
          this threshold emits a warning log so operators can flag stale ciphertexts.
          Default: 5.

          [default: 5]

      --backend <BACKEND>
          Possible values:
          - vault:       `HashiCorp` Vault KV-v2. Path format: `secret://<mount>/<path>[#<field>]`
          - aws-ssm:     AWS Systems Manager Parameter Store. Path format: `secret://<region>/<parameter-name>`
          - azure-kv:    Azure Key Vault. Path format: `secret://<vault-name>/secrets/<name>[/<version>]`
          - cosmian-kms: Another Cosmian KMS server (KMIP Get). Path format: `secret://<host>[:<port>]/<object-id>`

          [env: KMS_SECRET_BACKEND=]

      --vault-addr <VAULT_ADDR>
          [env: VAULT_ADDR=]
          [default: http://127.0.0.1:8200]

      --vault-token <VAULT_TOKEN>
          [env: VAULT_TOKEN=]
          [default: ""]

      --aws-access-key-id <AWS_ACCESS_KEY_ID>
          [env: AWS_ACCESS_KEY_ID=]
          [default: ""]

      --aws-secret-access-key <AWS_SECRET_ACCESS_KEY>
          [env: AWS_SECRET_ACCESS_KEY=]
          [default: ""]

      --aws-session-token <AWS_SESSION_TOKEN>
          [env: AWS_SESSION_TOKEN=]

      --azure-tenant-id <AZURE_TENANT_ID>
          [env: AZURE_TENANT_ID=]
          [default: ""]

      --azure-client-id <AZURE_CLIENT_ID>
          [env: AZURE_CLIENT_ID=]
          [default: ""]

      --azure-client-secret <AZURE_CLIENT_SECRET>
          [env: AZURE_CLIENT_SECRET=]
          [default: ""]

      --cosmian-kms-secret-token <COSMIAN_KMS_SECRET_TOKEN>
          [env: COSMIAN_KMS_SECRET_TOKEN=]

      --cosmian-kms-insecure-certs
          [env: COSMIAN_KMS_INSECURE_CERTS=]

      --jwks-endpoint-enabled
          Enable the `GET /.well-known/jwks.json` endpoint.

          When set, the server publicly exposes all active public keys whose
          `CryptographicUsageMask` includes `Verify` as a RFC 7517 JSON Web Key Set.
          The endpoint is **unauthenticated** — no credentials are required to fetch it,
          and no authentication middleware is applied. Defaults to `false`.

          [env: KMS_JWKS_ENDPOINT_ENABLED=]

      --jwks-endpoint-max-keys <JWKS_ENDPOINT_MAX_KEYS>
          Maximum number of public keys returned in a single JWKS response.

          When the server holds more eligible keys than this limit, the response is
          truncated and an `X-JWKS-Truncated: true` header is added to signal consumers.
          Increase this value if your deployment performs frequent key rotation and all
          overlapping verification keys must be simultaneously discoverable.

          [env: KMS_JWKS_ENDPOINT_MAX_KEYS=]
          [default: 50]

      --jwks-endpoint-auto-tag
          Automatically tag key pairs created via the REST crypto API for JWKS inclusion.

          When `true` (the default), every key pair created via `POST /v1/crypto/keys`
          receives the `"jwks"` tag and immediately appears in
          `GET /.well-known/jwks.json`.

          Set to `false` to disable this behaviour globally.  Operators must then
          manually tag each public key via `POST /v1/crypto/keys/{kid}/tags` before
          it is published in the JWKS document.

          This setting has no effect on keys created directly through the KMIP protocol.

          [env: KMS_JWKS_ENDPOINT_AUTO_TAG=]

  -h, --help
          Print help (see a summary with '-h')

  -V, --version
          Print version

By default, the server runs using a SQLite database, but it can be configured to use a choice of databases: SQLite encrypted, PostgreSQL, MariaDB, MySQL, and Percona XtraDB Cluster, as well as Redis, using the Redis-with-Findex configuration.

Selecting the database

All databases, except SQLite, can be used in a high-availability setup.

The SQLite database can serve high loads and millions of objects, and is very suitable for scenarios that do not demand high availability.

Redis with Findex

Redis with Findex offers the ability to use Redis as a database with application-level encryption: all data is encrypted (using AES 256 GCM) by the KMS servers before being sent to Redis. Findex is an Eviden cryptographic algorithm used to build encrypted indexes on encrypted data, also stored in Redis. This allows the KMS to perform fast encrypted queries on encrypted data. Redis with Findex offers post-quantum resistance on encrypted data and encrypted indexes.

Redis-with-Findex is most useful when:

  • KMS servers are run inside a confidential VM or an enclave. In this case, the secret used to encrypt the Redis data and indexes, is protected by the VM or enclave and cannot be recovered at runtime by inspecting the KMS servers' memory.
  • KMS servers are run by a trusted party but the Redis backend is managed by an untrusted third party.

Redis-with-Findex is the database selected to run the Eviden KMS in the cloud or any other zero-trust environment.

Configuring the database

The database parameters may be configured either:

SQLite

This is the default configuration. To use SQLite, no additional configuration is needed.

[db]
database_type = "sqlite"
sqlite_path = "./sqlite-data"
--database-type=sqlite \
--sqlite-path="./sqlite-data"

PostgreSQL

[db]
database_type = "postgresql"
database_url = "postgres://kms_user:kms_password@pgsql-server:5432/kms"
--database-type=postgresql \
--database-url=postgres://kms_user:kms_password@pgsql-server:5432/kms

!!!info "Setting up a PostgreSQL database" Before running the server, a dedicated database with a dedicated user should be created on the PostgreSQL instance. These sample instructions create a database called kms owned by a user kms_user with password kms_password:

  1. Connect to psql under user postgres

    sudo -u postgres psql  # or `psql -U postgres`
    
  2. Create user kms_user with password kms_password

    create user kms_user with encrypted password 'kms_password';
    
  3. Create database kms under owner kms_user

    create database kms owner=kms_user;
    
PostgreSQL High-Availability (multi-host)

The KMS supports multi-host PostgreSQL connection strings for high-availability deployments (streaming replication, Patroni, pgBouncer clusters, etc.). The database-url value is treated as a raw string so that host1:port,host2:port syntax — which the standard URL parser cannot handle — is passed directly to the PostgreSQL driver.

Use the target_session_attrs query parameter to control which node the driver connects to:

ValueBehaviour
read-writeConnect only to the primary (default for HA)
anyConnect to any available node (suitable for read replicas)
read-onlyConnect only to a standby

Example — two-node HA cluster:

[db]
database_type = "postgresql"
database_url  = "postgresql://kms_user:kms_password@primary:5432,standby:5432/kms?target_session_attrs=read-write"
--database-type=postgresql \
--database-url="postgresql://kms_user:kms_password@primary:5432,standby:5432/kms?target_session_attrs=read-write"

The URL must start with postgresql:// or postgres://; any other scheme is rejected at startup.

PostgreSQL failover retry

When a PostgreSQL primary fails over, the driver may return transient connection errors before the new primary is ready. The KMS automatically retries failed queries with exponential backoff for the following PostgreSQL SQLSTATE codes:

SQLSTATEMeaning
08001sqlclient_unable_to_establish_sqlconnection
08004sqlserver_rejected_establishment_of_sqlconnection
57P02crash_shutdown
57P03cannot_connect_now

No additional configuration is required; the retry behaviour is enabled automatically for all PostgreSQL connections.

MySQL, MariaDB, or Percona XtraDB Cluster

The KMS supports MySQL-compatible databases including MySQL, MariaDB, and Percona XtraDB Cluster. All use the same configuration with database-type=mysql.

!!! note Clustering Support As of version 5.13.0, the KMS schema includes PRIMARY KEY constraints on all tables, making it fully compatible with:

- **Percona XtraDB Cluster** (with `pxc_strict_mode=ENFORCING`)
- **MariaDB Galera Cluster**
- Any MySQL clustering solution requiring PRIMARY KEYs for replication
[db]
database_type = "mysql"
database_url = "mysql://kms_user:kms_password@mysql-server:3306/kms"
--database-type=mysql \
--database-url=mysql://kms_user:kms_password@mysql-server:3306/kms

!!!info "Using a certificate to authenticate to MySQL or MariaDB"

    Use a certificate to authenticate to MySQL or MariaDB with the `mysql-user-cert-file` option to
    specify the certificate file name.

    **Example context**: say the certificate is called `cert.p12`
    and is in a directory called `/certificate` on the host disk.
docker run --rm -p 9998:9998 \
    --name kms ghcr.io/cosmian/kms:latest \
    -v /certificate/cert.p12:/root/cosmian-kms/cert.p12 \
    --database-type=mysql \
    --database-url=mysql://mysql_server:3306/kms \
    --mysql-user-cert-file=cert.p12
[db]
database_type = "mysql"
database_url = "mysql://mysql_server:3306/kms"
# Note: if client certificate authentication is required for MySQL,
# configure it via command-line option `--mysql-user-cert-file` for now.
# A dedicated TOML key may not be available in this version.

Redis with Findex

For Redis with Findex, the --redis-master-password and --redis-findex-label options must also be specified:

  • The redis-master-password is the password from which keys will be derived (using Argon 2) to encrypt the Redis data and indexes.
  • The redis-findex-label is a public, arbitrary label that can be changed to rotate the Findex ciphertexts without changing the password/key.
[db]
database_type = "redis-findex"
database_url = "redis://localhost:6379"
redis_master_password = "password"
redis_findex_label = "label"
--database-type=redis-findex \
--database-url=redis://localhost:6379 \
--redis-master-password=password \
--redis-findex-label=label
  • Redis (with-Findex), use:

Securing database connections with TLS / mTLS

The KMS supports TLS-encrypted connections and mutual TLS (mTLS) client-certificate authentication for PostgreSQL and MySQL-compatible databases. All TLS parameters are configured directly in the database-url as query parameters — no extra CLI flags or TOML keys are needed.

Production certificates must be issued by a trusted Certificate Authority

For production deployments, all TLS certificates (CA, server, and client certificates) must be issued by a trusted Certificate Authority (CA). Self-signed certificates should only be used for testing and development environments.

Using certificates from an untrusted or self-signed CA may expose your KMS deployment to man-in-the-middle (MITM) attacks and should never be done in production.

PostgreSQL TLS / mTLS

PostgreSQL TLS is configured using the standard libpq-style query parameters in the connection URL.

ParameterDescription
sslmodeTLS mode: disable, prefer (default), require, verify-ca, verify-full
sslrootcertPath to the CA certificate (PEM) used to verify the server
sslcertPath to the client certificate (PEM) for mTLS
sslkeyPath to the client private key (PEM) for mTLS

Server-authenticated TLS only (encrypt the connection and verify the server certificate):

[db]
database_type = "postgresql"
database_url = "postgres://kms:kms@pgsql-server:5432/kms?sslmode=verify-ca&sslrootcert=/path/to/ca.crt"
--database-type=postgresql \
--database-url="postgres://kms:kms@pgsql-server:5432/kms?sslmode=verify-ca&sslrootcert=/path/to/ca.crt"

Mutual TLS (mTLS) (encrypt + verify server certificate + present a client certificate):

[db]
database_type = "postgresql"
database_url = "postgres://kms:kms@pgsql-server:5432/kms?sslmode=verify-full&sslrootcert=/path/to/ca.crt&sslcert=/path/to/client.crt&sslkey=/path/to/client.key"
--database-type=postgresql \
--database-url="postgres://kms:kms@pgsql-server:5432/kms?sslmode=verify-full&sslrootcert=/path/to/ca.crt&sslcert=/path/to/client.crt&sslkey=/path/to/client.key"

sslmode behaviour

  • disable – no TLS at all.
  • prefer (default) / require – TLS is used but the server certificate is not verified.
  • verify-ca – the server certificate is verified against the CA but the hostname is not checked.
  • verify-full – the server certificate is verified against the CA and the hostname must match.

All certificates must be in PEM format.

MySQL / MariaDB TLS / mTLS

MySQL TLS is configured using query parameters in the connection URL. Both dash (ssl-mode) and underscore (ssl_mode) variants are accepted.

ParameterDescription
ssl-modeTLS mode: DISABLED, PREFERRED, REQUIRED, VERIFY_CA, VERIFY_IDENTITY
ssl-caPath to the CA certificate (PEM) for server verification
ssl-client-identityPath to the client PKCS#12 (.p12) bundle for mTLS
ssl-client-identity-passwordPassword protecting the PKCS#12 bundle

Server-authenticated TLS only (encrypt the connection and verify the server certificate):

[db]
database_type = "mysql"
database_url = "mysql://kms:kms@mysql-server:3306/kms?ssl-mode=VERIFY_CA&ssl-ca=/path/to/ca.crt"
--database-type=mysql \
--database-url="mysql://kms:kms@mysql-server:3306/kms?ssl-mode=VERIFY_CA&ssl-ca=/path/to/ca.crt"

Mutual TLS (mTLS) (encrypt + verify server certificate + present a client certificate):

[db]
database_type = "mysql"
database_url = "mysql://kms:kms@mysql-server:3306/kms?ssl-mode=VERIFY_CA&ssl-ca=/path/to/ca.crt&ssl-client-identity=/path/to/client.p12&ssl-client-identity-password=secret"
--database-type=mysql \
--database-url="mysql://kms:kms@mysql-server:3306/kms?ssl-mode=VERIFY_CA&ssl-ca=/path/to/ca.crt&ssl-client-identity=/path/to/client.p12&ssl-client-identity-password=secret"

ssl-mode behaviour

  • DISABLED – no TLS at all.
  • PREFERRED / REQUIRED – TLS is used but the server certificate is not verified.
  • VERIFY_CA – the server certificate is verified against the CA.
  • VERIFY_IDENTITY – the server certificate is verified against the CA and the hostname must match.

PKCS#12 client identity

MySQL client-certificate authentication requires the certificate and private key bundled as a PKCS#12 (.p12) file — PEM files are not supported.

You can create the bundle using OpenSSL:

openssl pkcs12 -export \
    -in client.crt -inkey client.key \
    -out client.p12 -passout pass:secret

FIPS mode restriction

PKCS#12 client identity (ssl-client-identity) is not available in FIPS mode. MySQL mTLS with client certificates requires the non-fips feature.

Clearing the database

The KMS server can be configured to clear the database on restart automatically.

Warning: this operation is irreversible

The cleanup operation will delete all objects and keys stored in the database.

[db]
clear_database = true
--clear-database

Database migration

Depending on the KMS database evolution, a migration can happen between 2 versions of the KMS server. It will be clearly written in the CHANGELOG.md. In that case, a generic database upgrade mechanism is run on startup.

At first, the table context is responsible for storing the software run's version and the database's state. The state can be one of the following:

  • ready: the database is ready to be used
  • upgrading: the database is being upgraded

On startup, the server checks if the software version is greater than the last version run:

  • if no, it simply starts;

  • If yes:

    • it looks for all upgrades to apply in order from the last version run to this version;
    • if there is any to run, it sets an upgrading flag on the db state field in the context table;
    • it runs all the upgrades in order.
    • it sets the flag from upgrading to ready;

On every call to the database, a check is performed on the db state field to check if the database is upgrading. If yes, calls fail.

Upgrades resist being interrupted in the middle and resumed from the start if that happens.

MySQL schema update (5.13.0)

As of version 5.13.0, the MySQL schema was updated to include PRIMARY KEY constraints on the tags and read_access tables to ensure compatibility with MySQL clustering solutions (e.g., Percona XtraDB Cluster with pxc_strict_mode=ENFORCING, MariaDB Galera).

New installations of 5.13.0+ automatically create the corrected tables.

Existing installations upgrading to 5.13.0 will keep the old table definitions if those tables already exist. If you rely on clustering/replication that requires PRIMARY KEYs, apply the following manual migration before starting the KMS:

    -- Fix tags table
    ALTER TABLE tags
        DROP INDEX id,
        MODIFY id VARCHAR(128) NOT NULL,
        MODIFY tag VARCHAR(255) NOT NULL,
        ADD PRIMARY KEY (id, tag);

    -- Fix read_access table
    ALTER TABLE read_access
        DROP INDEX id,
        MODIFY id VARCHAR(128) NOT NULL,
        MODIFY userid VARCHAR(255) NOT NULL,
        ADD PRIMARY KEY (id, userid);

Notes:

  • Run these statements using a privileged MySQL user (e.g., root).
  • Ensure application access is paused during the migration.
  • No data loss occurs; this operation converts UNIQUE constraints to PRIMARY KEYs and enforces NOT NULL.

The Unwrapped Objects Cache

Detailed reference

For the full technical reference on the KMS in-memory caches — architecture, public API, configuration options, and security trade-offs — see Object Cache and Unwrapped Cache.

The unwrapped cache is a memory cache, and it is not persistent. The unwrapped cache is used to store unwrapped objects that are fetched from the database.

When a wrapped object is fetched from the database, it is unwrapped and stored in the unwrapped cache. Further calls to the same object will use the unwrapped object from the cache until the cache expires.

The time in minutes after which an unused object is evicted from the cache is configurable using the unwrapped_cache_max_age setting. The default is 15 minutes.

When HSM keys wrap objects, a long expiration time will reduce the number of calls made to HSM to unwrap the object. However, increasing the cache time will increase the memory used by the KMS server and expose the key in clear text in the memory for a longer time.

Object Cache and Unwrapped Cache

The KMS server uses two in-memory caches backed by moka::future::Cache, a lock-free concurrent hash map. Both caches use sharding so multiple Actix-web worker threads can read simultaneously without serialization.


Architecture overview

graph TD
    CALLER[Caller]

    CALLER -->|retrieve_object| DB[Database]
    CALLER -->|get_unwrapped| GU[get_unwrapped]

    DB -->|get| OC[ObjectCache]
    OC -->|miss| BS[(Backing Store<br/>SQLite / Postgres)]

    GU -->|peek| UC[UnwrappedCache]
    UC -->|miss| CRYPTO[unwrap_object<br/>KEK unwrap]

    OC -->|stores| OC_VAL["wrapped ObjectWithMetadata<br/>+ fingerprint"]
    UC -->|stores| UC_VAL["unwrapped key material<br/>+ fingerprint of wrapped"]

    style BS fill:#f9f,stroke:#333
    style CRYPTO fill:#f99,stroke:#333
    style OC fill:#9f9,stroke:#333
    style UC fill:#9f9,stroke:#333
CacheKeyValueMiss path
ObjectCacheUID stringArc<ObjectWithMetadata> (wrapped) + fingerprintDB fetch → insert → return
UnwrappedCacheUID stringunwrapped Object + fingerprint of wrappedCrypto unwrap → insert → return

ObjectCache

Source: crate/server_database/src/core/object_cache.rs

Caches full ObjectWithMetadata as read from the database to eliminate repeated DB round-trips on the hot path.

Public API

MethodReturnsDescription
get(uid)Option<Arc<ObjectWithMetadata>>Lock-free lookup. Returns cheap Arc clone.
insert(uid, owm)DbResult<()>Store with fingerprint of owm.object().
insert_arc(uid, arc_owm)DbResult<()>Store from existing Arc (saves one allocation).
invalidate(uid)Remove entry.
validate_cache(uid, current)DbResult<()>Compare fingerprints; invalidate on mismatch.

Callers

  • Database::retrieve_object()get() on hot path, insert() on miss
  • Database::retrieve_object_arc()get(), insert_arc() on miss
  • Database::update_object()invalidate() on write
  • Database::validate_cache()validate_cache() after forced re-fetch

Scenarios

S1 — Cold start (first access)

sequenceDiagram
    participant C as Caller
    participant DB as Database
    participant OC as ObjectCache
    participant BS as Backing Store

    C->>DB: retrieve_object("key-1")
    DB->>OC: get("key-1")
    OC-->>DB: None
    DB->>BS: retrieve("key-1")
    BS-->>DB: ObjectWithMetadata (wrapped)
    DB->>OC: insert("key-1", owm)
    DB-->>C: owm

S2 — Hot path (cache hit)

sequenceDiagram
    participant C as Caller
    participant DB as Database
    participant OC as ObjectCache

    C->>DB: retrieve_object("key-1")
    DB->>OC: get("key-1")
    OC-->>DB: cached entry found
    DB-->>C: owm via Arc unwrap_or_clone

S3 — retrieve_object_arc (zero-copy Arc path)

sequenceDiagram
    participant C as Caller
    participant DB as Database
    participant OC as ObjectCache
    participant BS as Backing Store

    C->>DB: retrieve_object_arc("key-1")
    DB->>OC: get("key-1")
    alt Cache hit
        OC-->>DB: cached Arc found
        DB-->>C: Arc clone (pointer bump)
    else Cache miss
        OC-->>DB: None
        DB->>BS: retrieve("key-1")
        BS-->>DB: owm
        DB->>OC: insert_arc("key-1", Arc.new(owm))
        DB-->>C: Arc
    end

S4 — Out-of-band mutation detected

sequenceDiagram
    participant DB as Database
    participant OC as ObjectCache

    Note over DB: Another process mutated "key-1"
    DB->>OC: validate_cache("key-1", current_db_object)
    OC->>OC: fingerprint(cached) != fingerprint(current)?
    OC->>OC: invalidate("key-1")
    Note over OC: Next get() triggers fresh DB fetch

S5 — Object update invalidates cache

sequenceDiagram
    participant C as Caller
    participant DB as Database
    participant OC as ObjectCache

    C->>DB: update_object("key-1", new_obj, new_attrs)
    DB->>OC: invalidate("key-1")
    DB->>DB: persist to backing store
    Note over OC: Entry removed, next get() is miss

S6 — Eviction (automatic)

moka eviction requires no explicit call:

PolicyTriggerEffect
LRUmax_capacity exceededLeast-recently-used entry evicted
TTLtime_to_idle elapsed without accessEntry evicted

UnwrappedCache

Source: crate/server_database/src/core/unwrapped_cache.rs

Caches unwrapped key material to avoid repeated KEK-unwrap cryptographic operations. The fingerprint of the wrapped object is stored alongside the unwrapped payload so stale entries (after key re-wrapping or KEK rotation) are silently rejected.

Public API

MethodReturnsDescription
peek(uid, wrapped)DbResult<Option<Object>>Returns unwrapped object if fingerprint matches.
insert(uid, wrapped, unwrapped)DbResult<()>Stores unwrapped with wrapped fingerprint.
clear_cache(uid)Removes entry.
validate_cache(uid, object)DbResult<()>Fingerprint check; invalidate on mismatch.

Security guard: insert returns Err if wrapped == unwrapped (defense-in-depth against plaintext-persistence bugs).

Callers

  • KMS::get_unwrapped()peek() + insert() on miss
  • KMIP ReKey / ReKeyKeyPair — clear_cache() after rotation
  • Database::validate_cache() chain — validate_cache() for defense-in-depth

Scenarios

U1 — First unwrap (cold)

sequenceDiagram
    participant G as get_unwrapped()
    participant UC as UnwrappedCache
    participant CR as Crypto

    G->>UC: peek("key-1", wrapped_obj)
    UC-->>G: None (no entry)
    G->>CR: unwrap_object(wrapped_obj)
    Note over CR: KEK unwrap
    CR-->>G: unwrapped Object
    G->>UC: insert("key-1", wrapped_obj, unwrapped_obj)
    Note over UC: Stores fingerprint(wrapped) + unwrapped
    G-->>G: return unwrapped

U2 — Subsequent unwrap (hot, zero crypto)

sequenceDiagram
    participant G as get_unwrapped()
    participant UC as UnwrappedCache

    G->>UC: peek("key-1", wrapped_obj)
    UC->>UC: fingerprint matches?
    UC-->>G: Some(unwrapped_obj)
    Note over G: No cryptographic operation
    G-->>G: return unwrapped

U3 — Key re-wrapped (KEK rotation)

sequenceDiagram
    participant G as get_unwrapped()
    participant UC as UnwrappedCache
    participant CR as Crypto

    Note over G: wrapped_obj changed (new KEK after rotation)
    G->>UC: peek("key-1", new_wrapped_obj)
    UC->>UC: fingerprint mismatch
    UC-->>G: None (stale entry rejected)
    G->>CR: unwrap_object(new_wrapped_obj)
    CR-->>G: unwrapped Object
    G->>UC: insert("key-1", new_wrapped_obj, unwrapped)
    Note over UC: New fingerprint replaces old entry
    G-->>G: return unwrapped

U4 — Security guard (wrapped == unwrapped)

sequenceDiagram
    participant G as get_unwrapped()
    participant UC as UnwrappedCache

    Note over G: Bug: caller passes already-unwrapped object
    G->>UC: insert("key-1", obj, obj)
    UC->>UC: wrapped == unwrapped?
    UC-->>G: Err("wrapped and unwrapped objects should be different")
    Note over UC: Prevents plaintext-persistence bugs

U5 — Explicit invalidation on key rotation

sequenceDiagram
    participant R as ReKey / Rekeyer
    participant UC as UnwrappedCache

    R->>UC: clear_cache("key-1")
    Note over UC: Entry removed
    Note over UC: Next get_unwrapped() triggers crypto unwrap

U6 — Concurrent workers (double-unwrap acceptable)

sequenceDiagram
    participant W1 as Worker 1
    participant W2 as Worker 2
    participant UC as UnwrappedCache
    participant CR as Crypto

    W1->>UC: peek("key-1", wrapped)
    UC-->>W1: None (cold)
    W2->>UC: peek("key-1", wrapped)
    UC-->>W2: None (W1 has not inserted yet)

    W1->>CR: unwrap_object(...)
    CR-->>W1: unwrapped
    W1->>UC: insert("key-1", wrapped, unwrapped)

    Note over W2: Received None, must also unwrap
    W2->>CR: unwrap_object(...)
    CR-->>W2: unwrapped
    W2->>UC: insert("key-1", wrapped, unwrapped)
    Note over UC: Double unwrap on cold start is acceptable

End-to-end: JOSE decrypt

Full trace of POST /v1/crypto/decrypt with alg: RSA-OAEP. The RSA wrapping key (persistent DB object, identified by kid) passes through both caches. The CEK (ephemeral, from the JWE encrypted_key field) is never cached.

sequenceDiagram
    participant J as JOSE /decrypt
    participant RF as retrieve_object_for_operation
    participant DB as Database
    participant OC as ObjectCache
    participant GU as get_unwrapped()
    participant UC as UnwrappedCache
    participant CR as Crypto
    participant OS as OpenSSL

    J->>RF: retrieve_object_for_operation(kid, Decrypt)
    RF->>DB: retrieve_objects(kid)

    alt ObjectCache hit
        DB->>OC: get(kid)
        OC-->>DB: cached (wrapped)
    else ObjectCache miss
        DB->>OC: get(kid)
        OC-->>DB: None
        DB->>DB: fetch from SQLite or Postgres
        DB->>OC: insert(kid, owm)
    end

    DB-->>RF: owm (wrapped)

    RF->>GU: get_unwrapped(kid, wrapped_obj, user)

    alt UnwrappedCache hit
        GU->>UC: peek(kid, wrapped_obj)
        UC-->>GU: Some(unwrapped_obj)
        Note over GU: No crypto
    else UnwrappedCache miss
        GU->>UC: peek(kid, wrapped_obj)
        UC-->>GU: None
        GU->>CR: unwrap_object(wrapped_obj)
        Note over CR: KEK unwrap
        CR-->>GU: unwrapped Object
        GU->>UC: insert(kid, wrapped_obj, unwrapped_obj)
    end

    GU-->>RF: unwrapped Object
    RF->>RF: owm.set_object(unwrapped)
    RF-->>J: owm with plaintext key

    J->>OS: kmip_private_key_to_openssl(owm.object())
    OS-->>J: OpenSSL PKey Private

    J->>OS: RSA private decrypt(encrypted_key)
    OS-->>J: CEK (Content Encryption Key)

    J->>OS: AES-GCM decrypt(ciphertext, CEK)
    OS-->>J: plaintext

    J->>UC: insert_cek(sha256(encrypted_key), cek)
    Note over J,UC: CEK cached in UnwrappedCache for subsequent decrypt calls

Per-scenario cache behavior

ScenarioObjectCacheUnwrappedCacheDB round-tripCrypto unwrap
1st access, coldmiss → DB → insertmiss → crypto → insert
2nd access, hothithit
Key re-wrapped (new KEK)hit (wrapped)miss → crypto → insert
Cache evicted (TTL / LRU)miss → DB → insertmiss → crypto → insert
Object updated in DBinvalidated → miss → DBstill valid (fingerprint unchanged)
KEK rotated (Rekey)invalidatedclear_cache() called explicitly✓ (next access)


JOSE CEK Cache (RSA-OAEP path)

The /v1/crypto/encrypt and /v1/crypto/decrypt endpoints support a RSA-OAEP / RSA-OAEP-256 key-management algorithm (RFC 7516). Each decryption requires an RSA private-key operation to unwrap the ephemeral Content Encryption Key (CEK). When the same JWE token is decrypted repeatedly, this operation is repeated on every call.

The CEK is stored in UnwrappedCache after the first successful unwrap so subsequent requests for the same token skip the RSA operation entirely.

Cache key

"jose_cek_{SHA-256-hex(encrypted_key_bytes)}"

The JWE encrypted_key field (the RSA-OAEP ciphertext of the CEK) uniquely identifies a token because RSA-OAEP encryption is randomised. SHA-256 keeps the map key compact: 64 hex chars regardless of RSA key size.

Fingerprint source

The RSA private-key KMIP Object is passed as the fingerprint source at insert() and peek() time. If the key is re-imported or re-wrapped (its serialised form changes), the fingerprint stored in the cache entry no longer matches and peek() returns None, forcing a fresh RSA-OAEP unwrap and re-population.

/encrypt/decrypt cross-path caching

When /encrypt is called, the freshly generated CEK is inserted into the cache immediately after wrapping, keyed by the encrypted_key returned in the JWE response. The first /decrypt call for that token will get a cache hit if both calls share the same server instance and the key has not changed.

If /encrypt uses a public-key KMIP object while /decrypt uses the linked private-key KMIP object, the fingerprints differ. In that case the first decrypt call replaces the stale entry with a correctly fingerprinted one; all subsequent decrypts hit the cache.

TTL and sizing

CEK cache entries share the same UnwrappedCache TTL and LRU capacity as wrapped KMS keys:

ParameterCLI flagDescription
Max entries--unwrapped-cache-max-sizeDefault 1000. Increase for workloads with many concurrent JWE tokens.
Time-to-idle--unwrapped-cache-max-ageEntries evicted after this idle period.

RFC 7516 §11.5 — implicit rejection preserved

Only genuinely unwrapped CEKs are inserted. The random substitute key generated when RSA-OAEP decryption fails (padding oracle countermeasure) is never cached. This preserves the constant-time rejection guarantee: an attacker submitting an invalid encrypted_key still causes AES-GCM to fail at tag verification, and no cache entry is written.

dir mode

The dir key-management algorithm uses a KMS-stored symmetric key directly. That path delegates to the KMIP Encrypt/Decrypt pipeline which already passes through UnwrappedCache via get_unwrapped(). No additional caching is required.

Configuration

Both caches are configured via the server configuration file or CLI flags:

ParameterCLI flagDefaultDescription
object_cache_max_size--cache-max-size1000Max entries in ObjectCache before LRU eviction
object_cache_max_age--cache-max-age15 sTime-to-idle: entry evicted after this idle period (seconds)
unwrapped_cache_max_size--unwrapped-cache-max-size1000Max entries in UnwrappedCache
unwrapped_cache_max_age--unwrapped-cache-max-age15 sTime-to-idle for UnwrappedCache entries (seconds)
unwrapped_cache_max_ttl--unwrapped-cache-max-ttl(none)Absolute time-to-live ceiling (seconds). When set, every entry is evicted at min(TTI, TTL) regardless of access frequency. See Security considerations.
disable_unwrapped_cache--disable-unwrapped-cachefalseCompletely bypass the UnwrappedCache. Every operation performs a full KEK unwrap. See Disabling the UnwrappedCache.

TTL vs TTI: max_age (time-to-idle) resets on every cache hit. A frequently accessed key is never evicted by TTI alone. max_ttl is an absolute ceiling independent of access frequency. For compliance environments that require a maximum plaintext-key exposure window, always set max_ttl.


Security considerations

  1. ObjectCache never stores plaintext keys. It mirrors the database: objects are stored as-retrieved (wrapped). All callers provide the DB state.

  2. UnwrappedCache validates against wrapped fingerprint. peek() compares the caller's wrapped_object fingerprint against the stored fingerprint. If the wrapped object changed (re-wrapped, corrupted), the entry is silently rejected.

  3. Security guard rejects wrapped == unwrapped. insert() returns an error if the caller passes an already-unwrapped object as the wrapped argument. This is defense-in-depth against plaintext-persistence bugs.

  4. UnwrappedCache does NOT participate in persistence. Unlike ObjectCache (checked by retrieve_object before every DB read), UnwrappedCache is only consulted by get_unwrapped(). Objects from UnwrappedCache are consumed in-memory for cryptographic operations and never written back to the database.

  5. Concurrent cold-start double-unwrap is acceptable. If two workers call get_unwrapped() simultaneously for a cold key, both perform the cryptographic unwrap. The second insert() overwrites the first. This wastes one unwrap but avoids lock contention — a tradeoff favoring throughput.

  6. TTI amplifies plaintext exposure window. With the default 15-second time-to-idle, a continuously accessed key's plaintext material stays resident indefinitely. For compliance environments with a bounded key-exposure requirement (e.g. FIPS operational security policies, PCI-DSS, or NIS2), set --unwrapped-cache-max-ttl to enforce an absolute ceiling:

    cosmian_kms --unwrapped-cache-max-ttl 60  # evict plaintext after at most 60 s
    
  7. Memory-zeroization on eviction. When an entry is evicted from UnwrappedCache, the plaintext Object is explicitly zeroized before deallocation. All Zeroizing<Vec<u8>> byte buffers (symmetric keys, private-key components) and SafeBigInt fields are overwritten with zeroes. This reduces the window during which sensitive key material could be recovered from freed memory pages.

  8. CPU-level side-channel risk (CacheFX / Prime+Probe). Plaintext key material held in the UnwrappedCache occupies resident DRAM pages. Modern CPU microarchitectural attacks (Prime+Probe, Flush+Reload) can potentially exfiltrate AES round-key material and RSA/EC private-key bits from shared L3 cache lines. These attacks require local code execution on the same physical host. Mitigations:

    • Keep --unwrapped-cache-max-ttl low (≤ 60 s) to limit the attack window.
    • Use --disable-unwrapped-cache in high-security deployments where KEK unwrap latency is acceptable.
    • Run the KMS on dedicated (non-shared) hardware or inside a hardware-enforced enclave (SEV-SNP, TDX) to eliminate co-tenant cache-sharing.

Disabling the UnwrappedCache

The UnwrappedCache trades security for performance: every cache hit saves a full KEK-unwrap operation but keeps plaintext key material in process memory between calls.

To disable it entirely — so that every operation performs a fresh cryptographic unwrap and no plaintext key material is ever cached — use:

# CLI flag
cosmian_kms --disable-unwrapped-cache

# Environment variable
KMS_DISABLE_UNWRAPPED_CACHE=true cosmian_kms

# Configuration file  (kms.toml / server.toml)
disable_unwrapped_cache = true

When disabled:

  • peek() always returns None, causing every get_unwrapped() to perform the full KEK-unwrap cryptographic operation.
  • insert() is a no-op; plaintext key material is never stored in the cache.
  • The ObjectCache (wrapped objects) is not affected: database round-trips are still cached for performance.

Trade-off: Disabling the UnwrappedCache increases per-operation latency by one full asymmetric-key operation (RSA-OAEP or ECDH unwrap). For a 2048-bit RSA KEK on a modern server, this is typically 0.5–2 ms per call. For symmetric KEKs (AES-256 key-wrap), the overhead is under 0.1 ms.

The ObjectCache can be configured to very short TTI or very small capacity to reduce wrapped-object retention, but it cannot be disabled via a flag — set --cache-max-age 1 --cache-max-size 1 to minimise its footprint if needed.

Authentication

The KMS server offers flexible authentication options, supporting multiple authentication methods that can operate independently or in combination with each other, providing multi-factor authentication capabilities.

Authentication Modes

Non-authenticated Mode (Default)

By default, if no authentication methods are configured, the server operates in non-authenticated mode. All requests are mapped to the default user, which can be configured using:

    --default-username <DEFAULT_USERNAME>
    The default username to use when no authentication is configured

    [env: KMS_DEFAULT_USERNAME=]
    [default: admin]

Authenticated Mode

When one or more authentication methods are enabled, the server requires successful authentication for all requests. The authentication mechanism works in a cascading fashion, attempting each configured method until one succeeds.

Available Authentication Methods

The KMS server supports three primary authentication methods:

  1. TLS Client Certificates: Authentication based on X.509 client certificates
  2. JWT Tokens: Authentication with OpenID-compliant JWT access tokens
  3. API Tokens: Authentication using a pre-shared API token

These methods can be used individually or in combination for enhanced security.

Authentication Flow

When multiple authentication methods are configured, the server follows this process:

  1. If TLS Client Certificate authentication is enabled and a valid certificate is presented, the user is authenticated
  2. If JWT authentication is enabled and a valid JWT token is presented, the user is authenticated
  3. If API Token authentication is enabled and a valid token is presented, the user is authenticated
  4. If all configured authentication methods fail, access is denied with a 401 Unauthorized response

A successful authentication at any step will grant access and subsequent authentication methods will be skipped.

Configuring Authentication Methods

TLS Client Certificate Authentication

To enable certificate-based authentication, the server must be started with TLS and a certificate authority (CA) for client verification:

# For FIPS mode (default build):
docker run -p 9998:9998 --name kms ghcr.io/cosmian/kms-fips:latest \
    --tls-cert-file server.crt \
    --tls-key-file server.key \
    --clients-ca-cert-file client_ca.cert.pem

# For non-FIPS mode:
# docker run -p 9998:9998 --name kms ghcr.io/cosmian/kms:latest \
#     --tls-p12-file kms.server.p12 \
#     --tls-p12-password password \
#     --clients-ca-cert-file client_ca.cert.pem
[tls]
# For FIPS mode (default build):
tls_cert_file = "server.crt"
tls_key_file = "server.key"
clients_ca_cert_file = "client_ca.cert.pem"

# For non-FIPS mode:
# tls_p12_file = "kms.server.p12"
# tls_p12_password = "password"
# clients_ca_cert_file = "client_ca.cert.pem"

The server extracts the username from the certificate's Subject Common Name (CN) field. Specifically, the Common Name of the client certificate subject is used directly as the username for authentication purposes. If the certificate is valid but does not contain a Common Name, authentication will fail.

Example of a subject with a CN field:

C=FR, ST=Ile-de-France, L=Paris, O=Cosmian Tech, CN=john.doe@example.com

In this example, john.doe@example.com would become the authenticated username.

Clients must present a valid certificate signed by the specified authority.

JWT Token Authentication

The server supports JWT tokens compatible with OpenID Connect. Configure JWT authentication with:

docker run -p 9998:9998 --name kms ghcr.io/cosmian/kms:latest \
    --jwt-auth-provider="https://accounts.google.com,https://www.googleapis.com/oauth2/v3/certs,cosmian_kms"
[idp_auth]
# issuer,jwks[,aud1[,aud2...]]  (jwks & audiences optional; any-of match when multiple)
jwt_auth_provider = ["https://accounts.google.com,https://www.googleapis.com/oauth2/v3/certs,cosmian_kms,another_client_id"]

The JWT authentication provider configuration uses the format: "JWT_ISSUER_URI,JWKS_URI,JWT_AUDIENCE_1,JWT_AUDIENCE_2,..." where:

  • JWT_ISSUER_URI: The issuer URI of the JWT token (required)
  • JWKS_URI: The JWKS (JSON Web Key Set) URI (optional, defaults to <JWT_ISSUER_URI>/.well-known/jwks.json)
  • JWT_AUDIENCE_n: Zero or more allowed audiences (optional). If multiple are provided, validation succeeds if the token aud contains any of them (any-of). If omitted, audience validation is skipped.

Examples:

  • "https://accounts.google.com,https://www.googleapis.com/oauth2/v3/certs,my-audience"
  • "https://auth0.example.com,,my-app" (JWKS URI will default)
  • "https://keycloak.example.com/auth/realms/myrealm,," (no audience, JWKS URI will default)
  • "https://issuer.example.com,https://issuer.example.com/jwks.json,frontend,cli" (multi-audience)

JWT tokens must be passed in the HTTP Authorization header:

Authorization: Bearer <JWT_TOKEN>

The server extracts the username from the token's email claim.

Supported Signing Algorithms

The KMS server automatically detects the signing algorithm from the JWT header (alg claim) and validates accordingly. The following signing algorithms from the jsonwebtoken library are supported; tokens using the none algorithm (for example, Algorithm::None / alg: "none") are explicitly rejected:

CategoryAlgorithms
HMAC (symmetric)HS256, HS384, HS512
RSA PKCS#1RS256, RS384, RS512
RSA-PSSPS256, PS384, PS512
ECDSAES256, ES384
EdDSAEdDSA (Ed25519)

The algorithm is picked up from the token's alg header — no server-side configuration is required. The signing key must be published in the JWKS endpoint and matched by its kid claim.

PKCE Support

The KMS supports PKCE (Proof Key for Code Exchange) in two complementary ways:

  • CLI / API clients (ckms and direct API usage): PKCE is used to obtain JWT tokens from the IDP. The client secret is optional — PKCE provides the security guarantee instead.
  • KMS Web UI: PKCE is mandatory. The browser login flow always sends code_challenge_method=S256. The IDP application must be configured accordingly (see the PKCE Authentication guide for per-provider instructions).

For detailed information including required IDP settings, provider-specific examples, and troubleshooting, see the PKCE Authentication guide.

Multiple Identity Providers

To support multiple identity providers, repeat the JWT authentication provider parameter:

--jwt-auth-provider="https://accounts.google.com,https://www.googleapis.com/oauth2/v3/certs,cosmian_kms,another_client_id" \
--jwt-auth-provider="https://login.microsoftonline.com/<TENANT_ID>/v2.0,https://login.microsoftonline.com/<TENANT_ID>/discovery/v2.0/keys,<CLIENT_ID>"

API Token Authentication

API Token authentication uses a symmetric key stored in the KMS as the authentication token:

  1. Generate a symmetric key and note its ID:

    ckms sym keys create
    
  2. Export the key in base64 format:

    ckms sym keys export -k <SYMMETRIC_KEY_ID> -f base64 api_token.base64
    
  3. Start the server with the API token ID:

docker run -p 9998:9998 --name kms ghcr.io/cosmian/kms:latest \
    --api-token-id <SYMMETRIC_KEY_ID>
[http]
api_token_id = "<SYMMETRIC_KEY_ID>"
  1. Configure the client to use the API token:

    Authorization: Bearer <BASE64_TOKEN>
    

When using API token authentication, the authenticated user will be the default username.

Force Default Username

If you want to enforce a consistent username regardless of the authentication method, use:

--force-default-username <true|false>
    Force using the default username regardless of the authentication method

    [env: KMS_FORCE_DEFAULT_USERNAME=]
    [default: false]

When enabled, the server still performs the authentication validation to ensure the client has valid credentials, but it ignores the username that would normally be extracted (such as the certificate's Common Name or JWT email claim) and instead maps all authenticated requests to the default username.

This feature is particularly useful in scenarios where:

  • You want consistent user identity across all requests regardless of authentication method
  • You prefer to manage access control independently from the authentication credentials
  • You're transitioning between authentication methods but need to maintain consistent audit trails

When force-default-username is enabled with multiple authentication methods, the server will still cascade through the authentication methods, but always use the default username upon successful authentication.

Multi-Factor Authentication Examples

Example 1: Client Certificate and JWT Authentication

# For FIPS mode (default build):
docker run -p 9998:9998 --name kms ghcr.io/cosmian/kms-fips:latest \
    --tls-cert-file server.crt \
    --tls-key-file server.key \
    --clients-ca-cert-file client_ca.cert.pem \
    --jwt-auth-provider="https://accounts.google.com,https://www.googleapis.com/oauth2/v3/certs,"

# For non-FIPS mode:
# docker run -p 9998:9998 --name kms ghcr.io/cosmian/kms:latest \
#     --tls-p12-file kms.server.p12 \
#     --tls-p12-password password \
#     --clients-ca-cert-file client_ca.cert.pem \
#     --jwt-auth-provider="https://accounts.google.com,https://www.googleapis.com/oauth2/v3/certs,"
[tls]
# For FIPS mode (default build):
tls_cert_file = "server.crt"
tls_key_file = "server.key"
clients_ca_cert_file = "client_ca.cert.pem"

# For non-FIPS mode:
# tls_p12_file = "kms.server.p12"
# tls_p12_password = "password"
# clients_ca_cert_file = "client_ca.cert.pem"

[idp_auth]
# Empty audience example: leave trailing comma after jwks URL
jwt_auth_provider = ["https://accounts.google.com,https://www.googleapis.com/oauth2/v3/certs,"]

In this configuration:

  • Clients can authenticate using either a valid client certificate or a valid JWT token
  • If both are provided, the certificate is checked first

Example 2: JWT and API Token Authentication

docker run -p 9998:9998 --name kms ghcr.io/cosmian/kms:latest \
    --jwt-auth-provider="https://accounts.google.com,https://www.googleapis.com/oauth2/v3/certs," \
    --api-token-id <SYMMETRIC_KEY_ID>
[idp_auth]
jwt_auth_provider = ["https://accounts.google.com,https://www.googleapis.com/oauth2/v3/certs,"]

[http]
api_token_id = "<SYMMETRIC_KEY_ID>"
  • Clients can authenticate using either a valid JWT token or the API token
  • JWT authentication is attempted first, followed by API token verification

Common Identity Provider Configurations

Google ID Tokens

--jwt-auth-provider="https://accounts.google.com,https://www.googleapis.com/oauth2/v3/certs,"

Auth0

--jwt-auth-provider="https://<your-tenant>.<region>.auth0.com/,,"

Note: the trailing / is required in the issuer URI. The JWKS URI will default to the well-known endpoint.

Microsoft Entra ID (Azure AD)

--jwt-auth-provider="https://login.microsoftonline.com/<TENANT_ID>/v2.0,https://login.microsoftonline.com/<TENANT_ID>/discovery/v2.0/keys,<CLIENT_ID>"

Okta

--jwt-auth-provider="https://<OKTA_TENANT_NAME>.com,https://<OKTA_TENANT_NAME>.com/oauth2/v1/keys,<OKTA_CLIENT_ID>"

Using PKCE Authentication with KMS

This document covers PKCE (Proof Key for Code Exchange) authentication with the Eviden KMS, for two distinct contexts:

  1. CLI / API clients — the ckms command-line tool and any direct API client uses PKCE to authenticate against the KMS server. Client secrets are optional in this flow.
  2. KMS Web UI — the browser-based UI uses PKCE to log users in through an OIDC Identity Provider (IDP). PKCE is mandatory for the web UI flow and cannot be disabled.

Overview

PKCE enhances the security of OAuth 2.0 authentication flows by eliminating the need for client secrets. This makes it particularly valuable for:

  • Public clients that cannot securely store client secrets
  • Mobile applications
  • Single-page applications
  • Desktop applications
  • CLI tools

Benefits of PKCE

  • Improved Security: Eliminates the need to store and manage client secrets
  • Simplified Deployment: Reduces configuration complexity for public clients
  • Protection Against CSRF & Authorization Code Interception: The code verifier provides additional security
  • Standards Compliance: Follows OAuth 2.0 and OpenID Connect standards

How PKCE Works

  1. Authorization Request:

    • Client generates a random code verifier and derives a SHA-256 code challenge from it
    • Client requests authorization with the code challenge (code_challenge_method=S256)
    • User authenticates and the IDP issues an authorization code
  2. Token Exchange:

    • Client sends the authorization code and the original code verifier to the token endpoint
    • The IDP verifies that the code verifier matches the previously received challenge
    • The IDP issues access and ID tokens upon successful verification

Web UI — Configuring Your Identity Provider

The KMS web UI login flow is a strict OIDC Authorization Code + PKCE flow. Every parameter listed below is hardcoded by the server; your IDP application must be configured to match them exactly.

Mandatory IDP Application Settings

RequirementValue the KMS sendsWhat to configure in the IDP
Grant typegrant_type=authorization_codeEnable the Authorization Code grant on the IDP application
Token endpoint auth methodClient credentials are sent as form body fields (client_id + optional client_secret)Set the token endpoint authentication method to client_secret_post — not client_secret_basic (HTTP Basic), which is the default in many IDPs
PKCEcode_challenge_method=S256 with a 32-byte random verifierEnable PKCE on the application; S256 must be supported (plain is not accepted)
Response typeresponse_type=codeEnsure the Authorization Code response type is allowed
Scopesscope=openid emailAllow both openid and email scopes; the email claim must be included in the returned ID token — without it the KMS cannot identify the user
Audience (aud) claimValidated against the configured client_id (exact match)The ID token's aud claim must exactly equal the client_id string. Some IDPs (e.g. Keycloak) add extra audience values or service-account names by default — disable this or add an audience mapper that restricts aud to the client_id only
Redirect URI<KMS_PUBLIC_URL>/ui/callbackRegister this exact URI as an allowed redirect/callback URI in the IDP application

Client Secret

The client secret is optional. If configured, it is sent as a client_secret form field alongside the other token exchange parameters (client_secret_post method). If your IDP requires a client secret, set it via:

[oidc]
ui_oidc_client_secret = "your-secret-here"

Provider-Specific Notes

Auth0

  • In the Auth0 dashboard, set Token Endpoint Authentication Method to POST (this corresponds to client_secret_post).
  • Enable PKCE on the application (Applications → Settings → Advanced → Grant Types → tick Authorization Code).
  • Under Advanced Settings → OAuth, set OIDC Conformant to ON.
  • The aud claim in Auth0 ID tokens equals the client_id by default — no extra configuration needed.

Microsoft Entra ID (Azure AD)

  • Register a Single-Page Application (SPA) or Web application. Entra ID enables PKCE automatically for SPA registrations.
  • The aud claim in Entra ID ID tokens equals the Application (client) ID — this matches the KMS expectation.
  • Entra ID uses client_secret_post by default for web applications that have a secret; for SPA applications no secret is used.
  • Required scopes: openid, email (or profile if email is provided via Microsoft Graph).

Keycloak

  • In Client Settings, set Access Type to confidential (if using a secret) or public (secret-less).
  • Standard Flow Enabled: ON.
  • Proof Key for Code Exchange Code Challenge Method: S256.
  • Client Authentication → set Client Authenticator to Client Id and Secret and Token Endpoint Auth Method to POST (client_secret_post).
  • Audience: by default Keycloak may add the Keycloak-internal service account to the aud claim. Add a Hardcoded audience mapper (or Audience resolve mapper restricted to the client ID) to ensure aud contains only the client_id.

Google

  • Google's OAuth2 endpoints officially support PKCE but still require a client secret.
  • The ID token's aud equals the OAuth2 client ID.
  • Required scopes: openid, email.

CLI / API Client — PKCE with Optional Client Secret

The KMS CLI (ckms) and API clients use PKCE to authenticate against the KMS server. The client secret is optional in this flow.

Example: Entra ID

  1. CLI configuration

    [http_config.oauth2_conf]
    client_id = "f052524e-7518-40e7-2579-219c0b48b125"
    authorize_url = "https://login.microsoftonline.com/612da4de-35c0-42de-ba56-174c4e562c96/oauth2/authorize"
    token_url = "https://login.microsoftonline.com/612da4de-35c0-42de-f3c6-174b69062c96/oauth2/token"
    scopes = [
        "email",
        "openid",
    ]
    # client_secret = <-- Not Set
    
  2. KMS Server Configuration

    Important: on Entra ID, configure the redirect URL (http://localhost:17899/authorization) as Native/Desktop (not Single Page Application).

    [idp_auth]
    # issuer,jwks,audience (audience omitted)
    jwt_auth_provider = ["https://login.microsoftonline.com/612da4de-35c0-42de-f3c6-174b69062c96/v2.0,https://login.microsoftonline.com/612da4de-35c0-42de-f3c6-174b69062c96/discovery/v2.0/keys,"]
    

Example: Auth0

  1. CLI configuration

    [http_config.oauth2_conf]
    client_id = "OUfH4FuzDAW99Ck3R4Rb7ROziOZEalIH"
    authorize_url = "https://acme.eu.auth0.com/authorize"
    token_url = "https://acme.eu.auth0.com/oauth/token"
    scopes = [
        "email",
        "openid",
    ]
    # client_secret = <-- Not Set
    

    Note: Google's IdP officially supports PKCE but still requires a client secret.

  2. KMS Server Configuration

    [idp_auth]
    jwt_auth_provider = ["https://acme.eu.auth0.com/,https://acme.eu.auth0.com/.well-known/jwks.json,"]
    

The client handles:

  • Code challenge generation using SHA-256
  • Automatic inclusion of PKCE parameters in authorization requests
  • Code verifier inclusion during token exchange

Server-Side Handling

The KMS server validates JWT tokens using JWKS (JSON Web Key Sets), which is compatible with PKCE-obtained tokens. The server:

  1. Extracts the JWT token from the Authorization: Bearer header
  2. Validates the token's signature using the JWKS endpoint
  3. Verifies the token's claims (issuer, audience, expiration, etc.)
  4. Extracts the user's identity from the email claim

Transitioning from Client Secret to PKCE

  1. Update your client code to use the PKCE-enabled client library
  2. Configure your identity provider to support PKCE (see provider notes above)
  3. Remove client secrets from your configuration (CLI flow only — the web UI always uses client_secret_post if a secret is present)

The server-side JWT validation remains unchanged.

Troubleshooting

SymptomLikely cause
Token exchange returns 401IDP requires client_secret_basic but the KMS sends client_secret_post — change the IDP application's token endpoint auth method
Token validation failed: InvalidAudienceThe ID token aud does not exactly match client_id — see the Keycloak or provider-specific notes above
Missing email claim / user ID is emptyThe IDP application does not include email in the ID token — enable the email scope and/or add an email claim mapper
IDP rejects the authorization requestThe IDP application does not support response_type=code or PKCE with code_challenge_method=S256 — enable these in the IDP application settings
Missing PKCE verifier on callbackThe browser session was lost (e.g. the session cookie expired or the server restarted between login and callback)

References

The authorization system in the Eviden Key Management Service (KMS) operates based on two fundamental principles:

  1. Ownership: Every cryptographic object has an assigned owner. The ownership is established when an object is created using any of the following KMIP operations: Create, CreateKeyPair, or Import. As an owner, a user holds the privilege to carry out all supported KMIP operations on their objects.

  2. Access rights delegation: owners can grant access rights, allowing one or more users to perform certain KMIP operations on an object. When granted such rights, a user can invoke the corresponding KMIP operation on the KMS for that particular object. The owner retains the authority to withdraw these access rights at any given time.

Delegable KMIP operations

Owners can delegate the following KMIP operations to other users via the grant and revoke endpoints (or the CLI commands ckms access-rights grant / ckms access-rights revoke):

OperationDescription
createCreate new cryptographic objects (symmetric keys, key pairs, …)
certifyIssue or renew X.509 certificates
decryptDecrypt ciphertext using a managed key
derive_keyDerive a new key from an existing key
destroyPermanently destroy an object
encryptEncrypt plaintext using a managed key
exportExport an object (key material + metadata) from the KMS
getRetrieve an object — this is a super-privilege (see below)
get_attributesRead the KMIP attributes of an object
hashCompute a cryptographic hash
importImport an external object into the KMS
locateSearch for objects matching given attributes
macCompute a Message Authentication Code
revokeRevoke (deactivate) an object
rekeyRe-key an existing symmetric key
signGenerate a digital signature
signature_verifyVerify a digital signature
validateValidate a certificate chain
set_attributeSet (replace) an attribute on an object
modify_attributeModify an existing attribute on an object
add_attributeAdd a new attribute value to an object
delete_attributeRemove an attribute from an object

Multiple operations can be granted or revoked in a single call. For example, using the CLI:

# Grant encrypt and decrypt to user "alice"
ckms access-rights grant alice -i <object-uid> encrypt decrypt

# Revoke the get privilege from user "bob"
ckms access-rights revoke bob -i <object-uid> get

The Get super-privilege

The Get operation has a special role in the permission model: it acts as a super-privilege that implies every other object-level operation.

When checking whether a user is authorized to perform a given operation on an object, the KMS evaluates the following rules in order:

  1. Owner check — if the requesting user is the owner of the object, access is always granted.
  2. Explicit permission — if the user has been explicitly granted the requested operation (e.g. encrypt), access is granted.
  3. Get fallback — if the user holds the Get permission on the object, access is granted regardless of the specific operation requested.

In other words, granting Get to a user on an object is equivalent to granting that user encrypt, decrypt, export, sign, derive_key, and every other object-level operation — except lifecycle operations (revoke, destroy) which still require their own explicit grant.

This design allows owners to share full read/use access to an object with a single permission, without individually enumerating every operation.

!!! warning Security implication Because Get implies all other operation-level permissions, it should be granted with care. If you only need a user to encrypt data with a key, grant encrypt — not get.

Practical example

Granted permissionsCan the user encrypt?Can the user export?Can the user destroy?
encryptYesNoNo
getYesYesNo
encrypt, destroyYesNoYes
get, destroyYesYesYes

Note

The destroy and revoke operations are never implied by get. They must always be granted explicitly because they are irreversible lifecycle transitions.

Special handling of the Create permission

The Create operation is not bound to a specific object — it controls whether a user is allowed to create new objects in the KMS. Internally it is stored against the wildcard object identifier *.

  • When granting or revoking create, no object UID is required.
  • Create can be combined with object-level operations in the same request; the server will separate and process them accordingly.

Privileged users

By default, all users are allowed to create or import objects in the KMS.

However, when the KMS server is configured with a list of privileged users, object creation rights are restricted as follows:

  • Privileged users can create or import objects and are authorized to grant or revoke object creation permissions for other users.
  • Regular users cannot create or import objects unless they have explicitly been granted permission by a privileged user.
  • Regular users cannot grant or revoke creation permissions for others.
  • Privileged users cannot revoke object creation permissions from other privileged users.

Operations gated by the privileged-user restriction

Because the following operations all result in a new cryptographic object being created in the KMS, they are all subject to the same privileged-user check:

OperationReason
CreateCreates a new symmetric key or secret data object
CreateKeyPairCreates a new asymmetric key pair
ImportImports an external object into the KMS
RegisterRegisters an externally-generated object
CertifyMay create a new key pair when issuing a certificate
ReKeyCreates a new replacement symmetric key
ReKeyKeyPairCreates a new replacement asymmetric key pair

Note

ReKey and ReKeyKeyPair also require the caller to hold the Rekey permission on the existing key being re-keyed. Both conditions must be satisfied: the user must be allowed to create new objects and be allowed to rekey the specific existing key.

The wildcard user *

The Wildcard User: *

In addition to regular users, a special user called * (the wildcard user) can be used to grant access rights on objects to all users. When a permission is granted to *, every authenticated user benefits from that permission on the targeted object. Individual per-user grants are merged with the wildcard grants when evaluating access.

HSM keys and authorization

Keys stored in an HSM follow a stricter permission model than regular KMS keys. Authorization metadata (owner, grants) is still managed by the KMS, but two important differences apply.

Comparison with regular KMS keys

AspectKMS keysHSM keys
Key material stored inKMS database (encrypted)HSM hardware
Get is a super-privilegeYes — implies all operationsNo — each operation must be granted explicitly
GetExport equivalenceNoYes — holding either grants both
Destroy / Revoke delegableYesNo — blocked; admin-only
CreateAny user (or privileged users if configured)HSM admin only
Locate visibilityAll owned / granted objectsNon-admins see only keys with ≥ 1 explicit grant

Who is an HSM admin?

Users listed in the server's hsm_admin configuration for a given HSM instance are its admins. Admins bypass all permission checks for that HSM — they can create, destroy, and perform any operation on its keys.

Permission evaluation for HSM keys

Request arrives for operation OP on key hsm::<model>::<slot>::<id>
│
├─ Is the user an HSM admin for this instance?  ──▶  YES → Granted
│
├─ Does the user have OP explicitly granted?  ──────▶  YES → Granted
│
├─ Is OP = Export and user has Get?  ───────────────▶  YES → Granted
├─ Is OP = Get   and user has Export?  ─────────────▶  YES → Granted
│
└─ Otherwise  ──────────────────────────────────────────────▶  Denied

What can and cannot be delegated

OperationDelegable via grant?Notes
encrypt, decrypt, sign, macYesAll standard cryptographic operations
getYesAlso implies export (equivalence)
exportYesAlso implies get (equivalence)
get_attributes, locateYes
set_attribute, modify_attribute, add_attribute, delete_attributeYesOperate on KMS metadata only; do not access HSM hardware
createYes (admin to another admin)Non-admin cannot receive create on HSM
destroyNoBlocked — admin-only, cannot be delegated
revokeNoBlocked — HSM objects do not use KMIP lifecycle states

Warning

Unlike regular KMS keys, granting Get on an HSM key does not imply encrypt, decrypt, sign, or any other operation. Each operation must be granted individually.

See the HSM operations page for HSM admin configuration details.

Authentication vs. authorization

It is important to distinguish authentication from authorization:

  • Authentication determines who the user is. The KMS supports TLS client certificates, JWT tokens, and API tokens. See the Authentication page for details on how to configure these methods and how user identities are established.
  • Authorization determines what an authenticated user is allowed to do with a given cryptographic object. This is the permission model described on this page.

Tip

An API token (used for authentication) is not the same thing as a symmetric key stored in the KMS. The API token proves the user's identity; the symmetric key is a cryptographic object the user may or may not have permission to use.

Typical workflow: per-user keys with limited permissions

Permissions are managed at runtime, not in kms.toml

The kms.toml configuration file controls server-level settings only (authentication methods, database backend, TLS, privileged users, etc.). It does not contain any user-to-key permission mapping. Per-object access rights are managed dynamically at runtime through the REST API (/access/grant, /access/revoke) or the CLI (ckms access-rights grant / ckms access-rights revoke). The only authorization-related setting in kms.toml is privileged_users, which restricts who can create or import new objects (see Privileged users above).

A common deployment pattern is to have an administrator create one symmetric key per user and grant only the operations each user needs (e.g. encrypt and decrypt).

Step 1 — Create the key (as admin/owner)

# The admin creates a 256-bit AES key and tags it for easy lookup
ckms sym keys create --algorithm aes --number-of-bits 256 --tag user-alice-key

The command returns the key's unique identifier, for example a]b2c3d4-....

Step 2 — Grant limited permissions

# Grant only encrypt and decrypt to alice (identified by her authenticated username)
ckms access-rights grant alice@example.com -i a]b2c3d4-... encrypt decrypt

Alice can now encrypt and decrypt using this key, but she cannot export it, destroy it, or perform any other operation on it.

Step 3 — Alice uses the key

Alice authenticates to the KMS (via her client certificate, JWT token, or API token) and calls the encrypt/decrypt endpoints referencing the key UID. The server verifies she holds the encrypt / decrypt permission before proceeding.

Step 4 — Revoke access (if needed)

ckms access-rights revoke alice@example.com -i a]b2c3d4-... encrypt decrypt

Note

Do not grant get if you only want to allow encrypt/decrypt — get is a super-privilege that implies all object-level operations (see above).

Access management endpoints

The KMS exposes the following REST endpoints to manage access rights:

MethodEndpointDescription
POST/access/grantGrant operations on an object to a user
POST/access/revokeRevoke operations on an object from a user
GET/access/list/{object_id}List all access rights granted on an object (owner only)
GET/access/ownedList all objects owned by the authenticated user
GET/access/obtainedList all access rights obtained by the authenticated user
GET/access/createCheck whether the authenticated user can create objects
GET/access/privilegedCheck whether the authenticated user is privileged

Authorization rules summary

ScenarioAccess granted?
User is the object ownerAlways
User has the exact requested operation grantedYes
User has Get granted (any operation except lifecycle)Yes
User has no matching permissionDenied
User tries to grant/revoke their own permissionsDenied
Non-owner tries to grant permissionsDenied

The KMS activates two internal listeners: a socket server and an HTTP server.

The socket server listens to KMIP binary requests on the IANA-registered 5696 port. The socket server will only start if the TLS configuration is provided and client certificate authentication is enabled.

The HTTP server listens to KMIP requests on the /kmip and /kmip/2_1 endpoints. It also serves the web UI on the /ui endpoint. The HTTP server is always started, even if the TLS configuration is not provided.

The KMS server should be started using HTTPS when running in a zero-trust environment. Check the running in a zero-trust environment section for more information.

To enable TLS, one can provide certificates on the command line interface.

Providing certificates

The KMS server supports two certificate formats depending on the build variant:

FIPS mode (default build): Requires PEM-formatted certificates and keys.

  • Server certificate in PEM format (may include full chain)
  • Private key in PEM format (PKCS#8 or traditional format)
  • Optional separate chain file for intermediate CAs

Non-FIPS mode: Supports PKCS#12 format.

  • Key and full certificate chain in PKCS#12 format
  • Password to open the PKCS#12 file

When enabling client certificate authentication, the server's authority X509 certificate in PEM format must also be provided (for both modes). Multiple CA certificates can be concatenated in a single PEM file to support different certificate authorities.

Configuration using the TOML configuration file

Certificate information must be provided in the [tls] section of the TOML configuration file.

For FIPS mode (default build):

# TLS configuration of the Socket server and HTTP server
[tls]

# The server's X.509 certificate in PEM format.
# Provide a PEM containing the server leaf certificate,
# optionally followed by intermediate certificates (full chain).
tls_cert_file = "path/to/server.crt"

# The server's private key in PEM format (PKCS#8 or traditional format).
tls_key_file = "path/to/server.key"

# Optional certificate chain in PEM format (intermediate CAs).
# If not provided, the chain may be appended to tls_cert_file instead.
tls_chain_file = "path/to/chain.pem"

For non-FIPS mode:

# TLS configuration of the Socket server and HTTP server
[tls]

# The KMS server's optional PKCS#12 Certificates and Key file.
# If provided, this will start the server in HTTPS mode.
tls_p12_file = "path/to/server.p12"

# The password to open the PKCS#12 Certificates and Key file.
tls_p12_password = "your_password"

# The server's optional authority X509 certificate in PEM format
# used to validate the client certificate presented for authentication.
# If provided, clients must present a certificate signed by this authority for authentication.
# The server must run in TLS mode for this to be used.
# Multiple CA certificates can be concatenated in a single PEM file.
clients_ca_cert_file = "[authority cert file]"

# Optional colon-separated list of TLS cipher suites to enable.
# If not specified, OpenSSL default cipher suites will be used.
# Example: "TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256"
# ANSSI-recommended cipher suites:
# - For TLS 1.3 (preferred): TLS_AES_256_GCM_SHA384, TLS_AES_128_GCM_SHA256, TLS_CHACHA20_POLY1305_SHA256
# - For TLS 1.2 (compatibility): TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
tls_cipher_suites = "[cipher suites]"

Configuring using the command line

Certificate information can be provided using the command line interface.

# The KMS server's optional PKCS#12 Certificates and Key file.
# If provided, this will start the server in HTTPS mode.
--tls-p12-file "[tls p12 file]"

# The password to open the PKCS#12 Certificates and Key file.
--tls-p12-password "[tls p12 password]"

# The server's optional authority X509 certificate in PEM format
# used to validate the client certificate presented for authentication.
# If provided, clients must present a certificate signed by this authority for authentication.
# The server must run in TLS mode for this to be used.
# Multiple CA certificates can be concatenated in a single PEM file.
--clients-ca-cert-file "[authority cert file]"

# Optional colon-separated list of TLS cipher suites to enable.
# If not specified, OpenSSL default cipher suites will be used.
# Example: "TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256"
--tls-cipher-suites "[cipher suites]"

!!!info "Generate a PKCS#12 from PEM files" To generate a PKCS12 from PEM files, you can use openssl:

openssl pkcs12 -export \
-in server.mydomain.com.fullchain.pem \
-inkey server.mydomain.com.privkey.pem \
-out server.mydomain.com.p12

Advanced TLS Configuration

TLS Protocol Versions

By default, the KMS server accepts both TLS 1.2 and TLS 1.3 connections. Internally it uses the OpenSSL mozilla_intermediate_v5 profile, which sets TLS 1.2 as the minimum protocol version and TLS 1.3 as the maximum.

The active protocol version range is automatically derived from the cipher suites you configure (see TLS Cipher Suites Selection below):

--tls-cipher-suites valueminimum TLS versionmaximum TLS version
(not set — default)TLS 1.2TLS 1.3
Only TLS 1.3 suitesTLS 1.3TLS 1.3
Only TLS 1.2 suitesTLS 1.2TLS 1.3
Mixed TLS 1.2 + TLS 1.3 suitesTLS 1.2TLS 1.3

Enforce TLS 1.3 only — specify at least one TLS 1.3 cipher suite and no TLS 1.2 suites:

--tls-cipher-suites "TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256"

Keep TLS 1.2 compatibility — either leave --tls-cipher-suites unset (default), or include at least one TLS 1.2 cipher suite:

--tls-cipher-suites "TLS_AES_256_GCM_SHA384:TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"

!!!warning "TLS 1.2 security considerations" TLS 1.2 is still considered secure when used with AEAD cipher suites (GCM/ChaCha20), but TLS 1.3 is preferred. Avoid enabling TLS 1.2 unless required for legacy client compatibility.


TLS Cipher Suites Selection

The KMS server supports custom TLS cipher suite configuration to meet specific security requirements. You can specify which cipher suites to enable using a colon-separated list.

The cipher suites are automatically categorized into TLS 1.3 and TLS 1.2 suites:

  • TLS 1.3 cipher suites (preferred): TLS_AES_256_GCM_SHA384, TLS_AES_128_GCM_SHA256, TLS_CHACHA20_POLY1305_SHA256, TLS_AES_128_CCM_SHA256, TLS_AES_128_CCM_8_SHA256
  • TLS 1.2 cipher suites (for compatibility): TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256

If only TLS 1.3 cipher suites are specified, the server will enforce TLS 1.3 minimum protocol version. If only TLS 1.2 cipher suites are specified, the server will support TLS 1.2 and above.

Example configurations:

# ANSSI-recommended TLS 1.3 only configuration
--tls-cipher-suites "TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256"

# Mixed TLS 1.2/1.3 configuration for compatibility
--tls-cipher-suites "TLS_AES_256_GCM_SHA384:TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"

Multiple Certificate Authorities

The KMS server supports multiple certificate authorities for client certificate validation. You can concatenate multiple CA certificates in PEM format into a single file.

Example of multiple CA certificates in one file:

-----BEGIN CERTIFICATE-----
[First CA Certificate]
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
[Second CA Certificate]
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
[Third CA Certificate]
-----END CERTIFICATE-----

This allows clients with certificates issued by any of the specified CAs to authenticate successfully. The server will validate client certificates against all provided CA certificates.

Telemetry & Observability

By default, the Eviden KMS server outputs logs to the console with a log level of INFO. Beyond console logging, the server supports OpenTelemetry (OTLP) export, which unlocks a full observability stack: distributed traces, RED metrics, and long-term dashboards via Grafana.

Log call-site directory

For a full listing of every log call-site across all components — grouped by domain (KMS server, CLI, PKCS#11 provider, CNG module), with per-level counts and the RUST_LOG filter name for each crate — see the Log Call-Site Directory.


Adjusting the log level

The log level can be adjusted by setting either:

  • the RUST_LOG environment variable,
  • the rust_log setting in the TOML configuration file in the [logging] section,
  • the --rust-log command line argument.

Available levels: trace, debug, info, warn, error. The default is info.

Example:

RUST_LOG=info,cosmian=info,cosmian_kms_server=info,actix_web=info,mysql=info

The first info sets the default log level for all crates. Individual crates can be overridden:

  • To get detailed logs of user requests, set cosmian_kms_server to debug:
RUST_LOG=info,cosmian=info,cosmian_kms_server=debug,actix_web=info,mysql=info
  • To debug HTTP issues, set actix_web to debug:
RUST_LOG=info,cosmian=info,cosmian_kms_server=info,actix_web=debug,mysql=info

⚠️ WARNING: Setting the log level to debug or trace may leak sensitive information in the logs.


Console and syslog logging

Logging to the console is enabled by default. It can be disabled via:

  • the quiet parameter in the TOML configuration file in the [logging] section,
  • the --quiet command line argument,
  • the KMS_LOG_QUIET environment variable set to true.

On Linux, logs can be redirected to syslog instead of stdout by setting:

  • the log_to_syslog parameter in the TOML configuration file in the [logging] section,
  • the --log-to-syslog command line argument,
  • the KMS_LOG_TO_SYSLOG environment variable set to true.

Rolling log files

The server can write daily rolling log files. File logging is disabled unless rolling_log_dir is explicitly configured (via --rolling-log-dir, the KMS_ROLLING_LOG_DIR environment variable, or the TOML configuration).

Log files are named <name>.YYYY-MM-DD, where <name> defaults to kms.

When rolling_log_dir is set without specifying a path (e.g. via the configuration wizard), the recommended platform-specific defaults are:

PlatformDefault directory
Linux/var/log/
WindowsC:\Users\<username>\AppData\Local\Cosmian KMS Server
macOS~/Library/Logs/

Warning (Windows): The server does not expand Windows environment variables such as %LOCALAPPDATA% in configuration files. If you override rolling_log_dir in kms.toml, you must use the fully-expanded path, for example:

rolling_log_dir = "C:\\Users\\<username>\\AppData\\Local\\Cosmian KMS Server"

When rolling_log_dir is not set, the server resolves the LOCALAPPDATA environment variable at runtime and defaults to C:\Users\<username>\AppData\Local\Cosmian KMS Server. When running as a Windows service under LocalSystem, the variable may not be set; the server then falls back to C:\ProgramData\Cosmian KMS Server.

Note (macOS): The server defaults to ~/Library/Logs/ which is the standard per-user log directory on macOS and is writable without root. If you run the server as a LaunchDaemon (root), you may override this with --rolling-log-dir /Library/Logs/.

Graceful fallback: If the configured rolling log directory does not exist and cannot be created, or is not writable by the current process, the server disables file logging with a warning message on stderr and continues operating normally. This prevents the server from panicking due to inaccessible log paths.

The directory and file name can be overridden via:

  • the rolling_log_dir / rolling_log_name entries in the TOML configuration file ([logging] section),
  • the --rolling-log-dir / --rolling-log-name command line arguments,
  • the KMS_ROLLING_LOG_DIR / KMS_ROLLING_LOG_NAME environment variables.

OTLP telemetry

The KMS server can export traces and metering events to any OpenTelemetry collector that supports the OTLP protocol.

To enable OTLP export, set one of:

  • the otlp parameter in the TOML configuration file in the [logging] section,
  • the --otlp command line argument,
  • the KMS_OTLP_URL environment variable.
KMS_OTLP_URL="http://localhost:4317"

What the traces contain

Traces produced by the KMS include:

  • The start configuration of the KMS server
  • KMIP requests (content adjusted by the log level)
  • Access rights management requests
  • Metering events (when --enable-metering is active)

Enabling metering

Metering events are emitted as OTLP spans and converted to Prometheus metrics downstream. Enable the feature with:

  • the --enable-metering command line argument,
  • or the equivalent TOML key in the [logging] section.

For the full list of emitted metrics, their types, and label sets, see the Metrics reference.


Observability stack (OTel Collector + VictoriaMetrics + Grafana)

For production-grade observability, a pre-configured Docker Compose stack is provided. It replaces the Jaeger quick-test setup with a persistent metrics pipeline and Grafana dashboards.

Full tutorial available here

Architecture

KMS ──OTLP gRPC──► OTel Collector ──remote_write──► VictoriaMetrics ◄── Grafana
                        │
                        └──prometheus scrape :8888──► VictoriaMetrics
ComponentRole
OTel CollectorReceives OTLP, enriches with metadata, generates RED metrics from traces, exports to VictoriaMetrics
VictoriaMetricsLong-term metrics storage (configurable retention)
GrafanaDashboard UI — queries VictoriaMetrics via PromQL

Quick start

The stack is configured via a single .env file. Two deployment modes are available:

Mode kms-local — KMS container included in the stack:

# .env
COMPOSE_PROFILES=kms-local
KMS_MODE=local

# Generate a demo TLS certificate (RSA 4096, self-signed, 10 years)
bash generate-demo-cert.sh

# Start everything
docker compose up -d

Mode external — existing KMS, stack only:

# .env
COMPOSE_PROFILES=
KMS_MODE=external

docker compose up -d

In external mode, configure your KMS to send OTLP data to the collector:

# gRPC (recommended)
KMS_OTLP_URL=http://<collector-host>:4317

# or HTTP
KMS_OTLP_URL=http://<collector-host>:4318

.env reference

VariableDefaultDescription
COMPOSE_PROFILESkms-localkms-local to include the KMS container, empty for external mode
KMS_MODElocallocal or external — propagated as label in all metrics/traces
KMS_CLUSTERcosmian-kms-localLogical cluster name — kms.cluster label in dashboards
KMS_VERSIONlatestDocker image tag for the KMS
ENVIRONMENTproductionDeployment environment (production, staging, …)
GRAFANA_ADMIN_PASSWORDpasswordGrafana admin user password
METRICS_RETENTION_MONTHS12VictoriaMetrics retention period (months)

OTel Collector pipeline

The collector enriches every span and metric with the following resource attributes:

AttributeSource
deployment.environmentENVIRONMENT from .env
service.namehardcoded cosmian-kms
service.versionKMS_VERSION from .env
kms.modeKMS_MODE from .env
kms.clusterKMS_CLUSTER from .env
kms.nodehost.name of the KMS container

RED metrics from traces are automatically generated by the spanmetrics connector with the following latency buckets: 10ms, 50ms, 100ms, 250ms, 500ms, 1s, 2s, 5s, 10s, 30s.

Exposed ports

ServicePortProtocolUsage
KMS9998HTTPSKMS API (local mode only)
OTel Collector4317gRPCOTLP traces & metrics ingestion
OTel Collector4318HTTPOTLP traces & metrics ingestion
OTel Collector8888HTTPCollector self-metrics (Prometheus)
OTel Collector13133HTTPHealth check
VictoriaMetrics8428HTTPPromQL API + remote_write endpoint
Grafana3000HTTPDashboard UI

Access Grafana at http://localhost:3000 with user admin.


Quick test with Jaeger

To quickly validate that OTLP export works without the full stack:

docker run -p 16686:16686 -p 4317:4317 \
  -e COLLECTOR_OTLP_ENABLED=true \
  jaegertracing/all-in-one:latest
[logging]
otlp = "http://localhost:4317"
quiet = true

Then start the KMS locally:

./cosmian_kms_server --otlp http://localhost:4317 --quiet

Open http://localhost:16686 to browse traces in the Jaeger UI.

For production use, replace Jaeger with the full OTel Collector + VictoriaMetrics + Grafana stack described above.

Log Call-Site Directory

This page lists every production log call-site across all Cosmian KMS components, grouped by domain and crate.

It is not listed in the navigation menu but is accessible via It is not listed in the navigation menu but is accessible via Logging and telemetry.

How to read this page

The index is organised by usage-domains. Within each domain, each table covers one crate (the UI section is an exception). All tables, ui included, have 5 columns:

ColumnMeaning
Levelerror warn info debug trace
MessageConstant log string; interpolated values shown as {name}
FileSource file relative to the crate root
VariablesEach {name} — description; when none
NotesReserved for security flags and operator guidance

Rows are ordered by severity (errorwarninfodebugtrace), then alphabetically by message. Test files are excluded.

Tip: The level filter defaults to error. Switching to all or trace reveals hundreds of rows and the page will become long.


Domain: KMS Server

cosmian_kms_server

Crate path: crate/server RUST_LOG target: cosmian_kms_server

LevelMessageFileVariablesNotes
errorAuthentication method configured, but no authentication providedsrc/middlewares/ensure_auth.rs-Returns 401. Check middleware stack - auth is configured but no bearer/cert/API-token was sent.
errorAWS XKS Sigv4 middleware should not be enabled if the aws_xks_params are not setsrc/routes/aws_xks/sigv4_middleware.rs-Server misconfiguration - enable XKS routes only when aws_xks_params is set.
errorError handling socket client: {}src/socket_server.rs-Unix socket handler crashed. The server continues running.
errorFailed to open index.html: {e:?}src/start_kms_server.rse: caught errorUI not served - verify ui_index_html_folder points to a built UI artifact.
errorSocket server failed to acquire write lock for stop request: {}src/socket_server.rs-Internal threading issue on shutdown. Server may need SIGTERM if it hangs.
errorSocket server failed to connect to itself: {}src/socket_server.rs-Internal stop-signal socket unreachable. Port may be in use by another process.
errorSocket server failed to get local address for stop signalsrc/socket_server.rs-Network/IPC failure. Check connectivity and port availability.
errorSocket server failed to write stop requestsrc/socket_server.rs-Network/IPC failure. Check connectivity and port availability.
errorSocket server stop signal contained error: {}src/socket_server.rs-Stop channel returned an error. Inspect the formatted error value.
errorWindows service failed: {e}src/windows_service.rse: caught errorFatal Windows service error. Check Windows Event Log for the full stack trace.
error{:?} {} 401 unauthorized: Client and server authentication tokens mismatchsrc/middlewares/api_token/api_token_auth.rs-API token mismatch - rotate the kms_api_token_id / kms_api_token config values.
error{status_code} - {message}src/routes/mod.rsstatus_code: HTTP status code
message: human-readable message text
Unhandled HTTP error response emitted to the client.
error{}src/start_kms_server.rs-Catch-all error log. Inspect the formatted value for the actual error.
warnActivate: object {} is already Active, rejectingsrc/core/operations/activate.rs--
warnAWS XKS create: key {uid} already exists (ignoring creation).src/routes/aws_xks/key_metadata.rsuid: KMIP object UID-
warnAzure EKM client authentication is disabled, this should only be done in tests, and won't work for production environments.src/start_kms_server.rs--
warnCould not insert: certificate: AKI: {}, SKI: {}src/core/operations/validate.rs--
warnFailed to persist auto-activation of object {}: {}src/core/retrieve_object_utils.rs--
warnFetch JWKS: {e}src/middlewares/jwt/jwks.rse: caught error-
warnSigV4 failure: {signature_error}src/routes/aws_xks/sigv4_middleware.rssignature_error: SigV4 signature validation error-
warnSocket server: connection failed: {e}src/socket_server.rse: caught error-
warnUI folder invalid or Linux default detected, falling back to: {fallback:#?}src/config/params/server_params.rsfallback: fallback UI folder path-
warn{:?} {} 401 unauthorized, no email in JWTsrc/middlewares/jwt/jwt_token_auth.rs--
warn{:?} {} 401 unauthorized: bad JWTsrc/middlewares/jwt/jwt_token_auth.rs--
warn{error:?}src/middlewares/jwt/jwt_token_auth.rserror: error detail-
warn{status_code} - {message}src/routes/mod.rsstatus_code: HTTP status code
message: human-readable message text
-
warn{status} - {}src/routes/jose/error.rsstatus: HTTP response status-
infoAUTHENTICATION token: {:?}src/routes/google_cse/jwt.rs--
infoAUTHORIZATION token headers: {:?}src/routes/google_cse/jwt.rs--
infoAUTHORIZATION token: {:?}src/routes/google_cse/jwt.rs--
infoazure EKM API enabled at {}src/start_kms_server.rs--
infoCommand line / file config: {clap_config:#?}src/main.rsclap_config: parsed CLI configuration-
infoCreated Key Pair with cryptographic algorithm {:?}src/core/operations/create_key_pair.rs--
infoCreated Object of type {:?}src/core/operations/create.rs--
infoCreating key pair for certification - private key: {sk_uid}, public key: {pk_uid}src/core/operations/certify/resolve_subject.rssk_uid: private key UID
pk_uid: public key UID
-
infoDestroyed objectsrc/core/operations/destroy.rs--
infoEncrypted Data : {encrypted_data:?}src/routes/ms_dke/mod.rsencrypted_data: encrypted data payload-
infoExported object of type: {}src/core/operations/export_get.rs--
infoExtracted IDP Configs: {:#?}src/config/command_line/idp_auth_config.rs--
infoFeature Test enabledsrc/main.rs--
infoFound Google CSE migration key, importing it.src/start_kms_server.rs--
infoGET /access/list/{object_id} {user}src/routes/access.rsobject_id: KMIP object UID
user: authenticated user identity
-
infoGET /access/obtained {user}src/routes/access.rsuser: authenticated user identity-
infoGET /access/owned {user}src/routes/access.rsuser: authenticated user identity-
infoGET /certssrc/routes/google_cse/mod.rs--
infoGET /download-cli {}src/routes/mod.rs--
infoGET /google_cse/status {}src/routes/google_cse/mod.rs--
infoGET /health {}src/routes/health.rs--
infoGET /hsm/status {}src/routes/mod.rs--
infoGET /me {user}src/routes/access.rsuser: authenticated user identity-
infoGET /server-info {}src/routes/mod.rs--
infoGET /version {}src/routes/mod.rs--
infoGET /version {}src/routes/ms_dke/mod.rs--
infoKey pair created for certificationsrc/core/operations/certify/resolve_subject.rs--
infoKMIP Request message with {} operation(s): {:?}src/core/operations/message.rs--
infoKMS Server configuration: {server_params:#?}src/start_kms_server.rsserver_params: server configuration (debug display)-
infoLoad default providersrc/openssl_providers.rs--
infoLoad FIPS providersrc/openssl_providers.rs--
infoLoad legacy providersrc/openssl_providers.rs--
infoNo migration key found, creating new RSA keypair.src/start_kms_server.rs--
infoOpenSSL version: {ossl_version}, in {ossl_dir}, number: {ossl_number:x}src/main.rsossl_version: OpenSSL version string
ossl_dir: OpenSSL modules directory
ossl_number: OpenSSL version number (hex)
-
infoPOST /access/grantsrc/routes/access.rs--
infoPOST /access/revokesrc/routes/access.rs--
infoPOST /aws/kms/xks/v1/health - request id {} - operation {}src/routes/aws_xks/health_status.rs--
infoPOST /ekm/info api-version={} user={}src/routes/azure_ekm/mod.rs--
infoPOST /ekm/{}/metadata api-version={} user={}src/routes/azure_ekm/mod.rs--
infoPOST /ekm/{}/unwrapkey alg={:?} api-version={} user={}src/routes/azure_ekm/mod.rs--
infoPOST /ekm/{}/wrapkey alg={:?} api-version={} user={}src/routes/azure_ekm/mod.rs--
infoPOST /google_cse/delegate: not implemented yetsrc/routes/google_cse/mod.rs--
infoPOST /google_cse/wrapprivatekey: not implemented yetsrc/routes/google_cse/mod.rs--
infoPOST /google_cse/{info_msg}src/routes/google_cse/mod.rsinfo_msg: route info message-
infoPOST /kms/xks/v1/keys/{key_id}/decrypt - operation: {} - id: {} - user: {}src/routes/aws_xks/encrypt_decrypt/decrypt_.rskey_id: XKS key identifier-
infoPOST /kms/xks/v1/keys/{key_id}/encrypt - operation: {} - id: {} - user: {}src/routes/aws_xks/encrypt_decrypt/encrypt_.rskey_id: XKS key identifier-
infoPOST /kms/xks/v1/keys/{key_id}/metadata - operation: {} - id: {} - user: {}src/routes/aws_xks/key_metadata.rskey_id: XKS key identifier-
infoRefreshing JWKSsrc/middlewares/jwt/jwks.rs--
infoResponse TTLV: {ttlv:?}src/routes/kmip.rsttlv: TTLV-encoded response-
infoRevoked object type: {}src/core/operations/revoke.rs--
infoRSA Keypair for Google CSE already exists (detected by UNIQUE constraint). Continuing without error.src/start_kms_server.rs--
infoRSA Keypair for Google CSE already exists (pre-check).src/start_kms_server.rs--
infoRSA Keypair for Google CSE already exists.src/start_kms_server.rs--
infoRSA Keypair for Google CSE created.src/start_kms_server.rs--
infoServer started with --info. Exitingsrc/main.rs--
infoServing index.html from {}src/start_kms_server.rs--
infoServing UI from {}src/start_kms_server.rs--
infoSocket server listening on {}src/socket_server.rs--
infoSocket server shutting down due to stop requestsrc/socket_server.rs--
infoSocket server stop signal sentsrc/socket_server.rs--
infoStarting Cosmian KMS server version {}src/main.rs--
infoStarting the HTTPS KMS server...src/start_kms_server.rs--
infoWindows service received Stop signal, shutting down...src/windows_service.rs--
infoWindows service reporting Running to SCMsrc/windows_service.rs--
infoXKS Decrypt internal error: {:?}src/routes/aws_xks/encrypt_decrypt/decrypt_.rs--
infoXKS Encrypt internal error: {:?}src/routes/aws_xks/encrypt_decrypt/encrypt_.rs--
info{}src/main.rs--
debug<== encrypted {} ciphertextssrc/core/operations/encrypt.rs--
debug==> encrypting {} clear textssrc/core/operations/encrypt.rs--
debug[metrics-cron] Failed to build runtime: {}src/cron.rs--
debug[metrics-cron] Shutdown signal received; stopping cron threadsrc/cron.rs--
debug[OK] base64 of encrypted_dek has been removedsrc/routes/google_cse/operations.rs--
debug[OK] recovered private_key DER bytes (len: {}). Perform RSA decryptionsrc/routes/google_cse/operations.rs--
debug[{idx}] Certificate carries id-ce-noRevAvail (RFC 9608): skipping CRL checksrc/core/operations/validate.rsidx: certificate index in validation chain-
debug[{idx}] Verifying certificate: subject: {:?}src/core/operations/validate.rsidx: certificate index in validation chain-
debugAccess granted on {}src/routes/access.rs--
debugAccess revoke for {}src/routes/access.rs--
debugActivate: object {} current state = {:?}src/core/operations/activate.rs--
debugAdd Attribute: {}src/core/operations/attributes/add.rs--
debugAES-GCM decryption failed (expected for implicit rejection): {e}src/routes/jose/aes_gcm.rse: caught error-
debugafter getting CRL: url: {url}src/core/operations/validate.rsurl: URL-
debugalgorithm: {ca:?}, ciphertext length: {}src/core/operations/encrypt.rsca: cryptographic algorithm-
debugallocation_size: {allocation_size}src/routes/google_cse/operations.rsallocation_size: allocated buffer size×2 in this file
debugAPI token authentication failed: {e:?}src/middlewares/api_token/api_token_middleware.rse: caught error-
debugAuthentication token check: validation disabledsrc/routes/google_cse/operations.rs--
debugBuilding and signing certificatesrc/core/operations/certify/build_certificate.rs--
debugcheck algorithmsrc/routes/google_cse/operations.rs--
debugClient certificate authentication failed: {e:?}src/middlewares/tls_auth.rse: caught error-
debugcomputing resource_key_hashsrc/routes/google_cse/operations.rs--
debugConfiguration TOML: {conf_str}src/config/command_line/clap_config.rsconf_str: configuration TOML string×2 in this file
debugCreate key pair: {request}src/core/operations/create_key_pair.rsrequest: KMIP request (debug display)-
debugcreate_user_decryption_key_: Access Policy: {access_policy:?}src/core/cover_crypt/create_user_decryption_key.rsaccess_policy: Covercrypt access policy expression-
debugCreated secret data with attributes: {}src/core/kms/other_kms_methods.rs--
debugCreated symmetric key with attributes: {}src/core/kms/other_kms_methods.rs--
debugCreating SecretData objectsrc/core/operations/derive_key.rs--
debugCRL list already contains key: {path}src/core/operations/validate.rspath: filesystem path-
debugCRL list already contains key: {url}src/core/operations/validate.rsurl: URL-
debugCSE Error: {:?}src/routes/google_cse/mod.rs--
debugdecode encrypted_deksrc/routes/google_cse/operations.rs--
debugdecrypt private keysrc/routes/google_cse/operations.rs--
debugdecrypt request: {:?}src/routes/aws_xks/encrypt_decrypt/decrypt_.rs--
debugdecrypt_bulk: ==> decrypted {} plaintextssrc/core/operations/decrypt.rs--
debugdecrypt_bulk: ==> decrypting {} ciphertextssrc/core/operations/decrypt.rs--
debugDecryption Oracle for prefix: {prefix}, total ciphertext is {} bytes longsrc/core/operations/decrypt.rsprefix: decryption oracle prefix bytes-
debugDeriveKey operation completed successfullysrc/core/operations/derive_key.rs--
debugDeriveKey operation startingsrc/core/operations/derive_key.rs--
debugDeriveKey: activation_date={:?} <= now, setting state to Activesrc/core/operations/derive_key.rs--
debugDeriveKey: no activation_date or future date, setting state to PreActivesrc/core/operations/derive_key.rs--
debugDeriveKey: No derivation data (info) provided for HKDFsrc/core/operations/derive_key.rs--
debugDeriveKey: No iteration count provided for PBKDF2, using defaultsrc/core/operations/derive_key.rs--
debugEKM Error: {:?}src/routes/azure_ekm/error.rs--
debugEnable Google CSE JWT Authorization: {enable_google_cse_authentication}src/start_kms_server.rsenable_google_cse_authentication: whether Google CSE JWT auth is on-
debugencode base64 wrapped_deksrc/routes/google_cse/operations.rs--
debugencrypt request: {:?}src/routes/aws_xks/encrypt_decrypt/encrypt_.rs--
debugencrypting with RSA {algorithm:?} {padding:?} hashing_fn:{hashing_fn:?} mgf1:{mgf1_hash_fn:?} label_len:{}src/core/operations/encrypt.rsalgorithm: cryptographic algorithm
padding: asymmetric padding scheme
hashing_fn: hash function
mgf1_hash_fn: MGF1 hash function
-
debugenteringsrc/routes/google_cse/jwt.rs--
debugenteringsrc/routes/google_cse/operations.rs-×9 in this file
debugexiting with successsrc/routes/google_cse/operations.rs-×4 in this file
debugexiting with success: decrypt_size: {decrypt_size}src/routes/google_cse/operations.rsdecrypt_size: decrypted payload length×2 in this file
debugexiting with success: {}src/routes/google_cse/operations.rs--
debugexporting private key with format: {:?}src/core/operations/export_get.rs--
debugfetching {jwks_uri}src/middlewares/jwt/jwks.rsjwks_uri: JWKS endpoint URL-
debugfrom_rsasrc/routes/google_cse/operations.rs--
debugGenerated JWT for original KACLS: {jwt:?}src/routes/google_cse/operations.rsjwt: JWT token (debug display)-
debugGet Attributes: available vendor attributes: [{}]src/core/operations/attributes/get.rs--
debugGet Attributes: fallback vendor attribute match {}:{} from object attributessrc/core/operations/attributes/get.rs--
debugGet Attributes: no vendor attributes present on objectsrc/core/operations/attributes/get.rs--
debugGet Attributes: requested vendor attribute not found {}:{}src/core/operations/attributes/get.rs--
debugGet Attributes: returning vendor attribute match {}:{}src/core/operations/attributes/get.rs--
debugGet input certificates as bytessrc/core/operations/validate.rs--
debugget metadata request: {:?}src/routes/aws_xks/key_metadata.rs--
debugget rsa public key on {current_kacls_url}src/routes/google_cse/operations.rscurrent_kacls_url: current KACLS server URL-
debugget_crl_bytes: exiting in success with {} CRLssrc/core/operations/validate.rs--
debugget_statussrc/routes/google_cse/operations.rs--
debugGoogle CSE request authorized for user {authentication_email}src/routes/google_cse/jwt.rsauthentication_email: email from the authentication token-
debugImport: activation_date={:?} <= now, setting state to Activesrc/core/operations/import.rs--
debugImport: no activation_date or future date, setting state to PreActivesrc/core/operations/import.rs--
debugImported object with uid: {}src/core/operations/import.rs--
debugImporting leaf certificate with attributes: {}src/core/operations/import.rs--
debugImporting PKCS12: private_key_id={:?}, leaf_certificate_id={:?}, chain={:?}src/core/operations/import.rs--
debugJWT Access granted to {email}!src/middlewares/jwt/jwt_token_auth.rsemail: user email address-
debugJWT authentication failed: {e:?}src/middlewares/jwt/jwt_middleware.rse: caught error-
debugKey successfully unwrapped with wrapping key: {}src/core/wrapping/unwrap.rs--
debugKey wrap type: {:?}src/core/operations/export_get.rs--
debugKey wrapped successfully by key {}src/core/wrapping/wrap.rs--
debugModifyAttribute: persisting attributes for {}src/core/operations/attributes/modify.rs--
debugno token validationsrc/routes/google_cse/operations.rs--
debugNumber of certificates in chain: {certificates_number}src/core/operations/validate.rscertificates_number: number of certificates in chain-
debugobject is not wrapped, no need to unwrapsrc/core/wrapping/unwrap.rs--
debugObject type: {}, with unique identifier: {}, destroyed by user {}src/core/operations/destroy.rs--
debugObject with unique identifier: {} destroyedsrc/core/operations/destroy.rs--
debugObject with unique identifier: {} revokedsrc/core/operations/revoke.rs--
debugObject with unique identifier: {} revoked by user {}src/core/operations/revoke.rs--
debugOpenSSL Extensions: {}src/core/operations/certify/build_certificate.rs--
debugParent CRL verification: revocation status: {res:?}src/core/operations/validate.rsres: result (debug display)-
debugproxy_config: {config:#?}src/config/params/proxy_params.rsconfig: configuration (debug display)-
debugre-wrapping key with current KMSsrc/routes/google_cse/operations.rs--
debugreading full bytes of CRL: url: {url}src/core/operations/validate.rsurl: URL-
debugRegister: activation_date={:?} <= now, setting state to Activesrc/core/operations/register.rs--
debugRegister: no activation_date or future date, setting state to PreActivesrc/core/operations/register.rs--
debugRegistered object with uid: {}src/core/operations/register.rs--
debugRequest already authenticated, skipping Ensure Auth middlewaresrc/middlewares/ensure_auth.rs--
debugrequest.encrypted_data_encryption_key: {}src/routes/google_cse/operations.rs--
debugRetrieved Attributes for {} {}, tags {:?}src/core/operations/attributes/get.rs--
debugretrieving RSA private key from KMSsrc/routes/google_cse/operations.rs--
debugRevocation status: result: {res:?}src/core/operations/validate.rsres: result (debug display)-
debugRSA key pair generation: size in bits: {key_size_in_bits}src/core/operations/create_key_pair.rskey_size_in_bits: RSA key size in bits-
debugRSA-OAEP encrypt: wrapped CEK ({} bytes) with {} ({} bit key)src/routes/jose/encrypt.rs--
debugRSA-OAEP unwrap failed or CEK size mismatch — using implicit rejectionsrc/routes/jose/decrypt.rs--
debugSet Attribute: {}src/core/operations/attributes/set.rs--
debugSet Object Attribute: {}src/core/operations/attributes/add.rs--
debugSet Object Attribute: {}src/core/operations/attributes/set.rs--
debugSetting key_wrap_type to NotWrapped due to default_unwrap_typessrc/core/operations/export_get.rs--
debugSignature verification result: {validity_indicator:?}src/core/operations/signature_verify.rsvalidity_indicator: signature validity result-
debugsignature_verify: effective CP => alg={:?} pad={:?} hash={:?} dsa={:?} mgf1_hash={:?}src/core/operations/signature_verify.rs--
debugSigv4 Middleware - Adding missing HOST header: {}src/routes/aws_xks/sigv4_middleware.rs--
debugSkipping non-HTTP CRL URI: {url}src/core/operations/validate.rsurl: URL-
debugSocket server received stop signal: {result:?}src/socket_server.rsresult: operation result-
debugsocket server: client connected from {}src/socket_server.rs--
debugsocket server: client {} disconnectedsrc/socket_server.rs--
debugsocket server: received request: {}src/socket_server.rs--
debugsuccesssrc/routes/google_cse/operations.rs--
debugThe wrapping key {wrapping_key_uid} is itself wrapped, unwrapping it firstsrc/core/wrapping/wrap.rswrapping_key_uid: UID of the wrapping key-
debugtls_config: {config:#?}src/config/params/tls_params.rsconfig: configuration (debug display)-
debugToken authentication successfulsrc/middlewares/api_token/api_token_auth.rs--
debugunwrap keysrc/routes/google_cse/operations.rs-×2 in this file
debugUnwrapped cache hitsrc/core/kms/other_kms_methods.rs--
debugUnwrapped cache miss. Calling unwrapsrc/core/kms/other_kms_methods.rs--
debuguser signed data via crypto oracle using: {uid}src/core/operations/sign.rsuid: KMIP object UID-
debugvalidate_tokenssrc/routes/google_cse/operations.rs--
debugwrap deksrc/routes/google_cse/operations.rs-×2 in this file
debugXks Error: {:?}src/routes/aws_xks/error.rs--
debugXKS Proxy - Path not found: {}src/routes/aws_xks/error.rs--
debug{conf:#?}src/config/params/server_params.rsconf: configuration (debug display)-
debug{request}src/core/operations/attributes/modify.rsrequest: KMIP request (debug display)-
debug{request}src/core/operations/attributes/set.rsrequest: KMIP request (debug display)-
debug{res:#?}src/config/params/server_params.rsres: result (debug display)-
debug{ui_index_html_folder:#?}src/config/params/server_params.rsui_index_html_folder: configured UI folder path×2 in this file
debug{wrap_request:?}src/routes/google_cse/jwt.rswrap_request: CSE wrap request (debug display)-
debug{} identifierssrc/core/operations/validate.rs--
trace:src/core/operations/attributes/dispatch_macros.rs
trace: {:?}src/core/operations/attributes/dispatch_macros.rs
trace[api_token_auth] Authorization header received (length: {} chars)src/middlewares/api_token/api_token_auth.rs
trace[api_token_auth] comparing client token ({} chars) with server tokensrc/middlewares/api_token/api_token_auth.rs
trace[destroy-core] certificate zeroization uid={unique_identifier}src/core/operations/destroy.rsunique_identifier — …
trace[destroy-core] opaque object zeroization uid={unique_identifier} len={}src/core/operations/destroy.rsunique_identifier — …
trace[destroy-core] uid={unique_identifier} type={:?} pre-state={:?} object={object}src/core/operations/destroy.rsunique_identifier — …
object — …
trace[destroy] DENY revoke-before-destroy (explicitly activated) uid={} type={:?} state={:?} activation_date={:?} initial_date={:?}src/core/operations/destroy.rs
trace[destroy] Failed to destroy linked private key {}: {:?}. Continuing with public key destruction.src/core/operations/destroy.rs
trace[destroy] policy-eval uid={} type={:?} db_state={:?} attr_state={:?} effective_state={:?} activation_date={:?} policy_state={:?} remove={}src/core/operations/destroy.rs
trace[revoke] proceed-destroyed uid={uid} state={:?}src/core/operations/revoke.rsuid — …
trace[revoke] skip uid={uid} reason=state-not-revocable state={:?}src/core/operations/revoke.rsuid — …
tracealgorithm: {:?}, padding: {:?}, hashing_fn: {:?}src/core/operations/decrypt.rs
tracealgorithm={:?}, data_len={}src/core/operations/hash.rs
traceAll certificates have been sortedsrc/core/operations/validate.rs×2 in this file
traceAlready an unwrapped keysrc/core/kms/other_kms_methods.rs
traceAttribute: Object type {:?} does not have attributes (nor key block)src/core/operations/attributes/add.rs
traceauthentication token validated for {authentication_email}src/routes/google_cse/jwt.rsauthentication_email — …
traceauthorization token validated successfullysrc/routes/google_cse/jwt.rs
traceAuto-activating object {} (activation_date {} <= now {})src/core/retrieve_object_utils.rs
tracebuild_pkcs12_for_private_key: {} with format: {:?}src/core/operations/export_get.rs
tracebuilding chain from leaf certificate: {}src/core/operations/export_get.rs
tracebuilding the PKCS12src/core/operations/export_get.rs
traceCertificate attribute link: {}src/core/operations/certify/certify_op.rs
traceCertificate attributes links: Nonesrc/core/operations/certify/certify_op.rs
tracecertificate parent id is: {}src/core/operations/export_get.rs
traceCertificate parent id is: {parent_id}src/core/operations/export_get.rsparent_id — …
traceCertify KeypairAndSubjectName:{unique_identifier} : keypair data: {keypair_data}src/core/operations/certify/certify_op.rsunique_identifier — …
keypair_data — …
traceCertify X509Req or Certificate:{unique_identifier}src/core/operations/certify/certify_op.rsunique_identifier — …
traceChecking key with ID: {}src/core/retrieve_object_utils.rs
traceChecking permissions to wrap with key {wrapping_key_uid}src/core/wrapping/wrap.rswrapping_key_uid — …
traceChecking usage mask for wrapping key {wrapping_key_uid}src/core/wrapping/wrap.rswrapping_key_uid — …
traceciphertext (ECB): {ciphertext:?}, aad: {aad:?}, padding_method: {padding_method:?}src/core/operations/decrypt.rsciphertext — …
aad — …
padding_method — …
traceClient certificate common name: {}src/middlewares/tls_auth.rs
traceconfigure_cipher_suites: Enable default cipher suites (mozilla_intermediate_v5)src/tls_config.rs
traceconfigure_cipher_suites: Setting custom cipher string: {suites}src/tls_config.rssuites — …
traceConfiguring client certificate verification for {}src/tls_config.rs
traceconverting the private key {} to openssl pkeysrc/core/operations/export_get.rs
tracecreate_base_openssl_acceptor: creating OpenSSL SslAcceptorBuilder for {server_type}src/tls_config.rsserver_type — …
traceCreating object of type {:?} with UID {} and attributes {}src/core/operations/create.rs
traceCreating OpenSSL SslAcceptor for socket serversrc/socket_server.rs
traceCreating OpenSSL SslAcceptorBuilder with TLS parameterssrc/start_kms_server.rs
traceCRL deserialized OK: {crl_path}src/core/operations/validate.rscrl_path — …×2 in this file
tracecryptographic_algorithm: {cryptographic_algorithm}src/core/operations/create_key_pair.rscryptographic_algorithm — …
tracecurve: {curve}src/core/operations/create_key_pair.rscurve — …
traceDecrypt: uid={:?}, data_len={}src/core/operations/decrypt.rs
traceDELETE /v1/crypto/keys/{kid}src/routes/jose/keys.rskid — …
traceDescription: {:?}src/core/operations/attributes/add.rs
traceDiscover versions: {}src/core/operations/discover_versions.rs
traceECB encrypt result: ciphertext_len={}src/core/operations/encrypt.rs
traceECB encrypt: plaintext_len={}, padding_method={padding_method:?}src/core/operations/encrypt.rspadding_method — …
traceeffective RSA CP -> padding={:?} hashing={:?} mgf1={:?} label_len={}src/core/operations/decrypt.rs
traceencrypt result: ciphertext_len={}, tag_len={}src/core/operations/encrypt.rs
traceenteringsrc/routes/google_cse/jwt.rs
traceEntering get_key_and_ciphersrc/core/operations/encrypt.rs
traceEntering import KMIP operation: uid={}, object_type={}src/core/operations/import.rs
traceEntering message KMIP operation: {request}src/core/operations/message.rsrequest — …
traceentering. owm: {}src/core/operations/encrypt.rs
traceexport_get_private_key: {} for operation: {}src/core/operations/export_get.rs
traceExtracting key block for decryption to identify key format type...src/core/operations/decrypt.rs
traceFetching public key: {public_key_name} and attempting to extract RSA modulus and public exponent from key materialsrc/routes/utils.rspublic_key_name — …
traceFound CRL URI: {crl_uri}src/core/operations/validate.rscrl_uri — …
tracefound link to certificate: {}src/core/certificate/find.rs
tracefound link to public key: {}. Will get certificate link from theresrc/core/certificate/find.rs
traceFound uid: {}, attributes: {}src/core/operations/locate.rs
tracefunction={:?}src/core/operations/pkcs11.rs
traceGet Attributes: Attributes: {}src/core/operations/attributes/get.rs
traceGet Attributes: computing LinkType setsrc/core/operations/attributes/get.rs
traceGet Attributes: Requested attributes: {req_attributes:?}src/core/operations/attributes/get.rsreq_attributes — …
traceGet Attributes: Response: {}src/core/operations/attributes/get.rs
traceGet Attributes: Retrieved object for get attributes: {}src/core/operations/attributes/get.rs
traceGET KEY /{} {:?}src/routes/ms_dke/mod.rs
traceGet: {}src/core/operations/get.rs
traceget_crl_bytes: entering: uri_list: {uri_list:?}src/core/operations/validate.rsuri_list — …
tracegot key bytes of length: {}, aead: {:?}. Proceeding to get the nonce...src/core/operations/decrypt.rs
traceHMAC computed: {} bytessrc/core/operations/mac.rs
traceHSM get_key_type probe for '{uid}' failed: {e}; proceeding with destroysrc/core/operations/destroy.rsuid — …
e — …
traceIgnoring tag {x:?} which does not match to an attributesrc/core/operations/attributes/get.rsx — …
traceimport opaque_object: uid={}src/core/operations/import.rs
traceimport secret_data: uid={}src/core/operations/import.rs
traceInternal create key pairsrc/core/operations/create_key_pair.rs
traceInternal create private keysrc/core/kms/other_kms_methods.rs
traceInternal create private key (FIPS build)src/core/kms/other_kms_methods.rs
traceInternal rekey key pair Covercryptsrc/core/cover_crypt/rekey_keys.rs
traceIssuer certificate id: {}src/core/operations/certify/resolve_issuer.rs
traceIssuer private key id: {}src/core/operations/certify/resolve_issuer.rs
traceIssuer Subject name: {:?}src/core/operations/certify/certify_op.rs
traceiv_counter_nonce: {}, ciphertext: {}, authenticated_tag: {}src/routes/google_cse/operations.rs
traceJWT Authentication...src/middlewares/jwt/jwt_token_auth.rs
tracekey id {}src/core/operations/decrypt.rs
tracekey_format_type: {key_format_type:?}src/core/operations/export_get.rskey_format_type — …
tracekey_material key format: {:?}src/core/operations/digest.rs
traceLeaf certificate extracted from PKCS12src/core/operations/import.rs
traceLink: {}src/core/operations/attributes/add.rs
traceLocate: filtering out HSM key {uid_str} — user {user} has no grantssrc/core/operations/locate.rsuid_str — …
user — …
traceMac: algorithm: {algorithm:?}src/core/operations/mac.rsalgorithm — …
traceMac: data len: {}src/core/operations/mac.rs
tracematching on key format type: {:?}src/core/operations/encrypt.rs
tracematching on key format type: {:?}src/core/operations/sign.rs
tracematching on public key format type: {:?}src/core/operations/decrypt.rs
traceModifyAttribute: ActivationDate: {:?}src/core/operations/attributes/modify.rs
traceModifyAttribute: Link: {}src/core/operations/attributes/modify.rs
traceModifyAttribute: Name: {}src/core/operations/attributes/modify.rs
traceModifyAttribute: retrieved target object {}src/core/operations/attributes/modify.rs
traceModifyAttribute: VendorAttribute: {}src/core/operations/attributes/modify.rs
traceName: {name}src/core/operations/attributes/add.rsname — …
traceno effective cryptographic parameters; defaults will applysrc/core/operations/decrypt.rs
traceNo issuer certificate id providedsrc/core/operations/certify/resolve_issuer.rs
traceNo issuer private key id providedsrc/core/operations/certify/resolve_issuer.rs
traceNo UI folder containing index.html found at {}src/start_kms_server.rs
traceNon revocable keys detected: won't be revoked {non_revocable_key_id:?}src/core/operations/revoke.rsnon_revocable_key_id — …
traceNot using Client Certificate Authentication with OpenSSLsrc/start_kms_server.rs
traceOpenSSL Private Key instantiated before signingsrc/core/operations/sign.rs
traceOpenSSL Public Key instantiated before encryptionsrc/core/operations/encrypt.rs
traceOperation processed successfully: {op}src/core/operations/message.rsop — …
traceOperation processing failed: {e}src/core/operations/message.rse — …
traceparams: {server_params:?}src/core/kms/mod.rsserver_params — …
tracePKCS12 parsed successfullysrc/core/operations/import.rs
traceplaintext length: {} bytessrc/core/operations/decrypt.rs
traceplaintext_len={}, nonce_len={}, aad_len={}, padding_method={padding_method:?}src/core/operations/encrypt.rspadding_method — …
tracePOST /v1/crypto/decryptsrc/routes/jose/decrypt.rs
tracePOST /v1/crypto/encrypt kid={} alg={}src/routes/jose/encrypt.rs
tracePOST /v1/crypto/keys kty={}src/routes/jose/keys.rs
tracePOST /v1/crypto/keys/unwrapsrc/routes/jose/unwrap.rs
tracePOST /v1/crypto/mac kid={}src/routes/jose/mac.rs
tracePOST /v1/crypto/sign kid={}src/routes/jose/sign.rs
tracePOST /v1/crypto/verifysrc/routes/jose/verify.rs
tracePOST /{key_name}/{key_id}/Decrypt {encrypted_data:?}src/routes/ms_dke/mod.rskey_name — …
key_id — …
encrypted_data — …
tracepost_process_private_key: operation type: {operation_type:?}src/core/operations/export_get.rsoperation_type — …
tracePrivate key attributes after lifecycle update: {}src/core/operations/create_key_pair.rs
tracePrivate key extracted from PKCS12src/core/operations/import.rs
tracePrivate key linked to leaf certificatesrc/core/operations/import.rs
tracePrivate key operation createdsrc/core/operations/import.rs
tracePrivate key wrapped and cachedsrc/core/operations/import.rs
traceprocess_secret_data: object_with_metadata: {}src/core/operations/export_get.rs
traceprocess_symmetric_key: object_with_metadata: {}src/core/operations/export_get.rs
traceprocessing key_valuesrc/core/operations/digest.rs
traceProcessing KMIP operation: {operation_name} with user: {user:?}src/core/operations/message.rsoperation_name — …
user — …
traceProcessing PKCS12 importsrc/core/operations/import.rs
tracePublic key attributes after lifecycle update: {}src/core/operations/create_key_pair.rs
tracePublic key extracted from PKCS12src/core/operations/import.rs
traceReKey: {}src/core/operations/rekey/symmetric/mod.rs
traceReKeyKeyPair: {}src/core/operations/rekey/keypair/mod.rs
traceRequest: {:?}src/routes/azure_ekm/mod.rs×4 in this file
tracerequest: {username} {}src/start_kms_server.rsusername — …
traceResponse message: {response_message}src/core/operations/message.rsresponse_message — …
traceResponse sent to {}src/socket_server.rs
traceRetrieved certificate: {} for private key: {}src/core/certificate/find.rs
traceRetrieved object for: {}src/core/operations/activate.rs
traceRetrieved object for: {}src/core/operations/attributes/add.rs
traceRetrieved object for: {}src/core/operations/attributes/delete.rs
traceRetrieving certificate for private key: {}src/core/certificate/find.rs
traceRetrieving issuer private key and certificate: private_key_id: {:?}, certificate_id: {:?}src/core/certificate/find.rs
traceRetrieving private key for certificate: certificate_uid_or_tags: {:?}src/core/certificate/find.rs
traceRNGRetrieve: {}src/core/operations/rng_retrieve.rs
traceRoot and possibly leaf removed from initial certificate list. Left: {}src/core/operations/validate.rs
traceSensitive: {:?}src/core/operations/attributes/add.rs
traceSet Attribute: Link: {}src/core/operations/attributes/set.rs
traceSet Attribute: Name: {}src/core/operations/attributes/set.rs
traceSet Attribute: Object type {:?} does not have attributes (nor key block)src/core/operations/attributes/set.rs
traceSet Attribute: Retrieved target objectsrc/core/operations/attributes/set.rs
traceSet Attribute: Vendor Attribute: {}src/core/operations/attributes/set.rs
tracesk_uid: {sk_uid}, pk_uid: {pk_uid}src/core/operations/create_key_pair.rssk_uid — …
pk_uid — …
tracestate_allows: {state_allows}: state: {state}, operation_type: {operation_type}src/core/retrieve_object_utils.rsstate_allows — …
state — …
operation_type — …
traceSubject name: {:?}src/core/operations/certify/certify_op.rs
traceThe wrapping key {wrapping_key_uid} is authorized to wrap keyssrc/core/wrapping/wrap.rswrapping_key_uid — …
traceTLS Authentication...src/middlewares/tls_auth.rs
traceTLS Authentication: no peer certificate foundsrc/middlewares/tls_auth.rs
traceToken authentication using this API token ID: {api_token_id}src/middlewares/api_token/api_token_auth.rsapi_token_id — …
tracetoken decoded with {app_name} jwt configsrc/routes/google_cse/jwt.rsapp_name — …
traceTry to validate token from issuer: {issuer_uri:?}src/routes/google_cse/jwt.rsissuer_uri — …
traceUID: {:?}, State: {:?}, Attributes: {}src/core/operations/locate.rs×2 in this file
traceuid={:?}src/core/operations/mac.rs
traceuid={:?}, data_len={}src/core/operations/encrypt.rs
traceuid={}src/core/operations/mac.rs
traceuid={} remove={} cascade={}src/core/operations/destroy.rs
traceUIDs count (post-truncate): {}src/core/operations/locate.rs
traceupdated_master_secret_key_bytes len: {}src/core/cover_crypt/create_user_decryption_key.rs
traceUser {user} does not have permission for operation {operation_type:?} on object {}src/core/retrieve_object_utils.rsuser — …
operation_type — …
traceUser {user} has permission for operation {operation_type:?} on object {}src/core/retrieve_object_utils.rsuser — …
operation_type — …
traceUsing Client Certificate Authentication with OpenSSLsrc/start_kms_server.rs
tracevalid_jwt headers: {:?}src/routes/google_cse/jwt.rs
tracevalid_jwt user claims: {:?}src/routes/google_cse/jwt.rs
tracevalidate token: KACLS URL {google_cse_kacls_url}src/routes/google_cse/jwt.rsgoogle_cse_kacls_url — …
traceValidate: {}src/core/operations/validate.rs
tracevalidate_cse_authorization_token: KACLS URL {google_cse_kacls_url}src/routes/google_cse/jwt.rsgoogle_cse_kacls_url — …
tracevalidating authentication token, expected JWT issuer: {}src/middlewares/jwt/jwt_config.rs
tracevalidating CSE authorization token, expected issuer : {}src/routes/google_cse/jwt.rs
traceVendor Attribute: {}src/core/operations/attributes/add.rs
traceverify_chain_signature: entering: number of certificates: {}src/core/operations/validate.rs
traceverify_crls: exiting in successsrc/core/operations/validate.rs
traceWaiting for test socket server to start...src/socket_server.rs
tracewrapped_key length: {} charssrc/routes/google_cse/operations.rs
traceWrapping key retrieved successfully: {wrapping_key}src/core/wrapping/wrap.rswrapping_key — …
traceWrapping key with id: {wrapping_key_id}src/core/wrapping/wrap.rswrapping_key_id — …
trace{debug_msg}. Certificate: subject: {:?}, AKI: {:?}, SKI: {:?}src/core/operations/validate.rsdebug_msg — …
trace{info_msg} request receivedsrc/routes/google_cse/mod.rsinfo_msg — …
trace{jwt_authorization_config:#?}src/routes/google_cse/jwt.rsjwt_authorization_config — …
trace{request}src/core/operations/attributes/get.rsrequest — …
trace{request}src/core/operations/check.rsrequest — …
trace{request}src/core/operations/create.rsrequest — …
trace{request}src/core/operations/destroy.rsrequest — …
trace{request}src/core/operations/export.rsrequest — …
trace{request}src/core/operations/query.rsrequest — …
trace{request}src/core/operations/register.rsrequest — …
trace{request}src/core/operations/revoke.rsrequest — …
trace{request}src/core/operations/rng_seed.rsrequest — …
trace{request}src/core/operations/sign.rsrequest — …
trace{request}src/core/operations/signature_verify.rsrequest — …
trace{}src/core/operations/activate.rs
trace{}src/core/operations/attributes/add.rs
trace{}src/core/operations/attributes/delete.rs
trace{}src/core/operations/certify/certify_op.rs
trace{}src/core/operations/locate.rs
errorFailed to convert response message to TTLV: {}src/routes/kmip.rs×2 in this file
errorFailed to find KMIP versionsrc/routes/kmip.rs
errorFailed to parse RequestMessage: {}src/routes/kmip.rs
errorFailed to process request: {}src/routes/kmip.rs×2 in this file
errorOpenSSL does not appear to be available (version number is 0). Please verify that OpenSSL is correctly installed and accessible.src/main.rs
warnAn Edwards Keypair on curve 25519 should not be requested to perform ECDH. Creating anyway.src/core/operations/create_key_pair.rs
warnAn Edwards Keypair on curve 448 should not be requested to perform ECDH. Creating anyway.src/core/operations/create_key_pair.rs
warnCRL signature could not be verified against chain issuers; issuer: {:?}. Continuing with status checks.src/core/operations/validate.rs
warnImport: CRL check could not be completed ({e}), proceeding with {desired_state:?} statesrc/core/operations/import.rse, desired_state
warnThe UI index HTML folder does not contain an index.html file: {ui_index_html_folder:#?}src/config/params/server_params.rsui_index_html_folder
warnUnsupported Block Cipher Mode for AES: {x:?}. The Authenticated Encryption Tag will NOT be extracted.src/routes/kmip.rsx
warnUser-supplied keyUsage in extension config overrides the RFC-mandated PQC keyUsage extension (RFC 9881/9909/9935)src/core/operations/certify/build_certificate.rs
debug...unwrapping the key block with key uid: {unwrapping_key_uid} using an encryption oracle, user: {user}src/core/wrapping/unwrap.rsunwrapping_key_uid, user
debug...unwrapping the key block with key uid: {unwrapping_key_uid} using the KMS, user: {user}src/core/wrapping/unwrap.rsunwrapping_key_uid, user
debug...wrapping the key block with key uid: {wrapping_key_uid} using an encryption oracle, user: {user}src/core/wrapping/wrap.rswrapping_key_uid, user
debug...wrapping the key block with key uid: {wrapping_key_uid} using the KMS, user: {user}src/core/wrapping/wrap.rswrapping_key_uid, user
debug[kms-init] Failed to seed kms.keys.active.count: {e}src/core/kms/mod.rse
debug[kms-init] Failed to seed kms.objects.total: {e}src/core/kms/mod.rse
debug[metrics-cron] Failed to sync kms.keys.active.count: {}src/cron.rs
debug[metrics-cron] Failed to sync kms.objects.total: {}src/cron.rs
debug[metrics-cron] kms.keys.active.count synced to {}src/cron.rs
debug[metrics-cron] kms.objects.total synced to {}src/cron.rs
debug[metrics-cron] reconcile_all_object_counts failed: {}src/cron.rs
debugAPI Token Middleware: An authenticated user was found; there is no need to authenticate twice...src/middlewares/api_token/api_token_middleware.rs
debugDeriveKey: No derivation data provided - this may be acceptable if a Secret Data object identifier is providedsrc/core/operations/derive_key.rs
debugImport: certificate is revoked per CRL check, setting state to Compromisedsrc/core/operations/import.rs
debugJWT: An authenticated user was found; there is no need to authenticate twice...src/middlewares/jwt/jwt_middleware.rs
debugRequest bytes: {}src/routes/kmip.rs
debugRequest TTLV: {ttlv:#?}src/routes/kmip.rsttlv
debugResponse Message Bytes: {}src/routes/kmip.rs
debugResponse Message TTLV: {response_ttlv:#?}src/routes/kmip.rsresponse_ttlv
debugThe user: {user}, is authorized to wrap with the key {wrapping_key_uid}. Encoding: {:?}, format: {}src/core/wrapping/wrap.rsuser, wrapping_key_uid
debugThis is a {major}.{minor} Decrypt message. Extracting Authenticated Encryption Tag of length {len} from Data fieldsrc/routes/kmip.rsmajor, minor, len
traceCertify PublicKeyAndSubjectName:{unique_identifier}: public key: {from_public_key}src/core/operations/certify/certify_op.rsunique_identifier, from_public_key
traceciphertext: {ciphertext:?}, nonce: {nonce:?}, aad: {aad:?}, tag: {tag:?}, padding_method: {padding_method:?}src/core/operations/decrypt.rsciphertext, nonce, aad, tag, padding_method
traceenter export_get op={:?} req={}src/core/operations/export_get.rs×2 in this file
traceget_attribute_list uid={} refs=[{}]src/core/operations/attributes/get_list.rs
tracepost-process symmetric key uid={} final_format={:?}src/core/operations/export_get.rs
traceprocess_symmetric_key enter uid={} requested_format={:?} wrap_type={:?}src/core/operations/export_get.rs
traceprocess_symmetric_key exit uid={} final_format={:?}src/core/operations/export_get.rs
traceprocess_symmetric_key key_block initial format={:?} wrapped={} uid={}src/core/operations/export_get.rs
traceprocess_symmetric_key missing key_value structure uid={}src/core/operations/export_get.rs
traceprocess_symmetric_key set Raw uid={}src/core/operations/export_get.rs
traceprocess_symmetric_key set TransparentSymmetricKey uid={}src/core/operations/export_get.rs
traceprocessing symmetric key uid={} state={:?} requested_format={:?}src/core/operations/export_get.rs
traceRequest Message: {request_message}src/routes/kmip.rsrequest_message
traceResponse Message: {response_message}src/routes/kmip.rsresponse_message
traceretrieved object uid={} type={:?} state={:?} key_fmt={:?}src/core/operations/export_get.rs
traceuid_or_tags: {uid_or_tags:?}, user: {user}, operation_type: {operation_type:?}src/core/retrieve_object_utils.rsuid_or_tags, user, operation_type
errorFailed to convert Response TTLV to bytes: {}: TTLV:\n{:#?}src/routes/kmip.rs
warnFailed to process request:\n{response_message}src/routes/kmip.rsresponse_message
info\n{:?}src/routes/kmip.rs
traceJWK has been found:\n{jwk:?}src/middlewares/jwt/jwt_config.rsjwk
traceJWK has been found:\n{jwk:?}src/routes/google_cse/jwt.rsjwk
warnFailed to persist auto-deactivation of object {}: {}src/core/retrieve_object_utils.rs-×2 in this file
warnfailed to re-wrap dependant {dep_uid} with new key: {e}, skippingsrc/core/operations/rekey/common.rsdep_uid, e-
warnfailed to unwrap dependant {dep_uid}: {e}, skippingsrc/core/operations/rekey/common.rsdep_uid, e-
warnskipping re-wrap of dependant {dep_uid}: owned by '{}', not by '{owner}'src/core/operations/rekey/common.rsdep_uid, owner-
warnwrapped dependant {dep_uid} not found, skippingsrc/core/operations/rekey/common.rsdep_uid-
warn{}: keyset chain depth {} ≥ warn threshold {} for uid {}; consider re-encrypting with the latest keysrc/core/operations/key_ops/crypto_op.rs--
infoRekey finalized: old={} → new={}, user={user}src/core/operations/rekey/common.rsuser-
debug[auto-rotate-cron] Failed to build runtime: {}src/cron.rs--
debug[auto-rotate-cron] Running scheduled key auto-rotation checksrc/cron.rs--
debug[auto-rotate-cron] Shutdown signal received; stopping cron threadsrc/cron.rs--
traceAuto-deactivating object {} (deactivation_date {} <= now {})src/core/retrieve_object_utils.rs-×2 in this file
traceexecute_keyset_try_each: key {} failed for {}: {}src/core/operations/key_ops/crypto_op.rs--
traceHSM ReKey: old={uid} → new={new_uid} (slot={slot_id}, gen={new_gen}), user={user}src/core/operations/rekey/symmetric/hsm.rsuid, new_uid, slot_id, new_gen, user-
traceReCertify: {}src/core/operations/recertify.rs--
traceReKey: resolved keyset ref '{}' → '{}'src/core/operations/rekey/symmetric/mod.rs--
traceSetAttribute: clearing CKA_START_DATE / CKA_END_DATE on HSM key '{}' (rotation disabled)src/core/operations/attributes/set.rs--
traceSetAttribute: writing CKA_LABEL '{}' on HSM key '{}'src/core/operations/attributes/set.rs--
traceSetAttribute: writing CKA_START_DATE={} CKA_END_DATE={} on HSM key '{}'src/core/operations/attributes/set.rs--
tracewalk_keyset_chain: keyset '{}' has {} keys in chainsrc/core/uid_utils.rs--
warnui_session_salt is not configured — using a randomly generated ephemeral session key. Sessions will be invalidated on server restart and are not portable across instances. For persistent sessions and load-balanced deployments, set `ui_session_salt` (or KMS_UI_SESSION_SALT) to a strong random secret value.src/start_kms_server.rs--
traceFound valid JWK in JWKS at `{jwks_uri}`: {jwk:#?}src/middlewares/jwt/jwks.rsjwks_uri, jwk-
traceIgnoring invalid JWK in JWKS at `{jwks_uri}`: {e}: {v:#?}src/middlewares/jwt/jwks.rsjwks_uri, e, v-
tracePKCS#11 `C_CloseSession` not yet implementedsrc/core/operations/pkcs11.rs--
tracePKCS#11 `C_Decrypt` not yet implementedsrc/core/operations/pkcs11.rs--
tracePKCS#11 `C_DecryptFinal` not yet implementedsrc/core/operations/pkcs11.rs--
tracePKCS#11 `C_DecryptInit` not yet implementedsrc/core/operations/pkcs11.rs--
tracePKCS#11 `C_DecryptUpdate` not yet implementedsrc/core/operations/pkcs11.rs--
tracePKCS#11 `C_DestroyObject` not yet implementedsrc/core/operations/pkcs11.rs--
tracePKCS#11 `C_Encrypt` not yet implementedsrc/core/operations/pkcs11.rs--
tracePKCS#11 `C_EncryptFinal` not yet implementedsrc/core/operations/pkcs11.rs--
tracePKCS#11 `C_EncryptInit` not yet implementedsrc/core/operations/pkcs11.rs--
tracePKCS#11 `C_EncryptUpdate` not yet implementedsrc/core/operations/pkcs11.rs--
tracePKCS#11 `C_Finalize` failed: {e}src/core/operations/pkcs11.rse-
tracePKCS#11 `C_FindObjects` not yet implementedsrc/core/operations/pkcs11.rs--
tracePKCS#11 `C_FindObjectsFinal` not yet implementedsrc/core/operations/pkcs11.rs--
tracePKCS#11 `C_FindObjectsInit` not yet implementedsrc/core/operations/pkcs11.rs--
tracePKCS#11 `C_GenerateKey` not yet implementedsrc/core/operations/pkcs11.rs--
tracePKCS#11 `C_GenerateKeyPair` not yet implementedsrc/core/operations/pkcs11.rs--
tracePKCS#11 `C_GenerateRandom` not yet implementedsrc/core/operations/pkcs11.rs--
tracePKCS#11 `C_GetAttributeValue` not yet implementedsrc/core/operations/pkcs11.rs--
tracePKCS#11 `C_GetInfo` failed: {e}src/core/operations/pkcs11.rse-
tracePKCS#11 `C_GetMechanismInfo` not yet implementedsrc/core/operations/pkcs11.rs--
tracePKCS#11 `C_GetMechanismList` not yet implementedsrc/core/operations/pkcs11.rs--
tracePKCS#11 `C_Initialize` called on already initialized library - treating as successsrc/core/operations/pkcs11.rs--
tracePKCS#11 `C_Initialize` failed: {e}src/core/operations/pkcs11.rse-
tracePKCS#11 `C_Login` not yet implementedsrc/core/operations/pkcs11.rs--
tracePKCS#11 `C_Logout` not yet implementedsrc/core/operations/pkcs11.rs--
tracePKCS#11 `C_OpenSession` not yet implementedsrc/core/operations/pkcs11.rs--
tracePKCS#11 `C_SeedRandom` not yet implementedsrc/core/operations/pkcs11.rs--
tracePKCS#11 `C_UnwrapKey` not yet implementedsrc/core/operations/pkcs11.rs--
tracePKCS#11 `C_WrapKey` not yet implementedsrc/core/operations/pkcs11.rs--
traceReKeyKeyPair: resolved keyset ref '{}' → '{}'src/core/operations/rekey/keypair/mod.rs--
warnfind_wrapped_by({old_uid}) failed — skipping re-wrap of dependants: {e}src/core/operations/rekey/common.rsold_uid, e-
warn[auto-rotate] Failed to query keys due for rotation: {e}src/core/operations/auto_rotate.rse-
warn[auto-rotate] Failed to re-arm rotation policy on replacement {new_uid}: {e}src/core/operations/auto_rotate.rsnew_uid, e-
warn[auto-rotate] Failed to retrieve key {uid}: {e}; skippingsrc/core/operations/auto_rotate.rsuid, e-
warn[auto-rotate] Failed to rotate key {uid} (owner={owner}): {e}src/core/operations/auto_rotate.rsuid, owner, e-
warn[auto-rotate] Key {uid} not found; skippingsrc/core/operations/auto_rotate.rsuid-
info[auto-rotate] Found {} key(s) due for rotationsrc/core/operations/auto_rotate.rs--
info[auto-rotate] Key {uid} (owner={owner}) rotation due in {days} day(s) (warning threshold: {threshold} days)src/core/operations/auto_rotate.rsuid, owner, days, threshold-
info[auto-rotate] Key {uid} (owner={owner}, algo={algorithm}) rotated successfullysrc/core/operations/auto_rotate.rsuid, owner, algorithm-
debug[auto-rotate] Failed to query keys for renewal warnings: {e}src/core/operations/auto_rotate.rse-
debug[auto-rotate] No keys due for rotation at {now}src/core/operations/auto_rotate.rsnow-
debug[auto-rotate] rotate_one_key returned: {e}src/core/operations/auto_rotate.rse-
debug[auto-rotate] Skipping HSM key {uid}src/core/operations/auto_rotate.rsuid-
debug[auto-rotate] Skipping {uid}: object type {other:?} has no auto-rotation supportsrc/core/operations/auto_rotate.rsuid, other-
debug[auto-rotate] Skipping {uid}: PublicKey is rotated as part of its paired PrivateKey rotationsrc/core/operations/auto_rotate.rsuid-
debugSetAttribute: implicitly enabling rotate_automatic for key '{}' (RotateInterval > 0 with no explicit RotateAutomatic)src/core/operations/attributes/set.rs--
warnAzure EKM JSON deserialization error: {err}src/routes/azure_ekm/mod.rserr-
warnJWKS endpoint enabled — all active public keys with the "jwks" tag will be publicly exposed (unauthenticated) at `{kms_public_url}/.well-known/jwks.json`. Up to {} keys will be served. Ensure this is intentional. Configure via the `[jwks_endpoint]` section in the server configuration file.src/start_kms_server.rskms_public_url-
warnJWKS response truncated: more than {} eligible public keys foundsrc/routes/jwks.rs--
warnKey uid={uid} found by Locate but missing on retrieve — skippingsrc/routes/jwks.rsuid-
warnSkipping key uid={uid} from JWKS (conversion failed): {e}src/routes/jwks.rsuid, e-
infoGET /.well-known/jwks.jsonsrc/routes/jwks.rs--
traceDELETE /v1/crypto/keys/{kid}/tagssrc/routes/jose/tags.rskid-
traceGET /v1/crypto/keys/{kid}/tagssrc/routes/jose/tags.rskid-
traceJWKS key order is database insertion order — not stable across restarts or backends. Returning {} eligible key(s); consumers must match by `kid`, not position.src/routes/jwks.rs--
tracePOST /v1/crypto/keys/{kid}/tagssrc/routes/jose/tags.rskid-
errorFailed to serialize response to JSON: {e}src/routes/kmip.rse-
infohttp_workers not configured; defaulting to total core count ({total})src/start_kms_server.rstotal-
infoKMS HTTP server configured with {http_workers} worker thread(s)src/start_kms_server.rshttp_workers-
debugPOST /kmip {}.{} Binary. Request: {:?} {}src/routes/kmip.rs--
debugPOST /kmip {}.{} JSON. Request: {:?} {}src/routes/kmip.rs--
debugPOST /kmip/2_1. Request: {:?} {}src/routes/kmip.rs--
warnJOSE CEK cache insert error for {uid}: {e}src/routes/jose/cek_cache.rsuid, e-
warnJOSE CEK cache peek error for {uid}: {e}src/routes/jose/cek_cache.rsuid, e-
warnJOSE CEK cache: failed to construct KMIP SymmetricKey: {e}src/routes/jose/cek_cache.rse-
warnJOSE CEK cache: unexpected CEK length {other} bytes — not an AES-128/192/256 keysrc/routes/jose/cek_cache.rsother-
warnJOSE CEK cache: unexpected object type for {uid}src/routes/jose/cek_cache.rsuid-
debugJOSE CEK cache hit for {uid}src/routes/jose/cek_cache.rsuid-
debugJOSE CEK cached for {uid}src/routes/jose/cek_cache.rsuid-
debugTLS: an authenticated user was already present; skipping certificate checksrc/middlewares/tls_auth.rs--

cosmian_kms_server_database

Crate path: crate/server_database RUST_LOG target: cosmian_kms_server_database

LevelMessageFileVariablesNotes
debugEmpty Redis database detected. Initializing a new database instance.src/stores/redis/redis_with_findex.rs--
debugExisting Redis database detected (version {version}). Using current database.src/stores/redis/redis_with_findex.rsversion: version-
debugListed {} rowssrc/stores/sql/mysql.rs-×2 in this file
debugOwner = {}src/stores/sql/mysql.rs--
debugPG find query: {}src/stores/sql/pgsql.rs--
debugUid = {}src/stores/sql/mysql.rs--
traceCreated in DB: {uid} / {owner}src/stores/sql/mysql.rsuid — …
owner — …
traceDeleted in DB: {uid}src/stores/sql/mysql.rsuid — …
tracefind: tags: {tags:?}src/stores/redis/redis_with_findex.rstags — …
tracefind: uids before permissions: {:?}src/stores/redis/redis_with_findex.rs
tracefind: user must be ownersrc/stores/redis/redis_with_findex.rs
tracefind_: {:?}src/stores/sql/mysql.rs
traceInsert read access right in DB: {uid} / {userid}src/stores/sql/mysql.rsuid — …
userid — …
traceInvalidating the cache for {}src/core/unwrapped_cache.rs
traceRedis DB size: {count}src/stores/redis/redis_with_findex.rscount — …
traceUpdated in DB: {uid}src/stores/sql/mysql.rsuid — …×2 in this file
traceUpserted in DB: {uid}src/stores/sql/mysql.rsuid — …
debug[redis-bootstrap] skipping key {key}: {e}src/stores/redis/objects_db.rskey, e×2 in this file
debug[redis-metrics] reconcile: live_objects={live_count}, non_destroyed_keys={key_count}src/stores/redis/redis_with_findex.rslive_count, key_count
debug[redis-metrics] bootstrapped {} live object(s) into `{}`src/stores/redis/redis_with_findex.rs--
debug[redis-metrics] bootstrapped {} non-destroyed key(s) into `{}`src/stores/redis/redis_with_findex.rs--
debug[redis-scan-rotation] skipping key {key}: {e}src/stores/redis/objects_db.rskey, e-
warnPostgreSQL BEGIN failed — retryingsrc/stores/sql/pgsql.rsattempt, delay_ms, error-
warnPostgreSQL COMMIT failed — retryingsrc/stores/sql/pgsql.rsattempt, delay_ms, error-
warnPostgreSQL pool error — retryingsrc/stores/sql/pgsql.rsattempt, delay_ms, error-
warnPostgreSQL retryable error — retryingsrc/stores/sql/pgsql.rsattempt, delay_ms, error-
warnPostgreSQL transaction body failed — retryingsrc/stores/sql/pgsql.rsattempt, delay_ms, error-
warnwrapping_key_id backfill: skipping object that failed to deserializesrc/stores/sql/sqlite.rs--
warnwrapping_key_id backfill: skipping object that failed to deserializesrc/stores/sql/pgsql.rs--
warnwrapping_key_id backfill: skipping object {id} that failed to deserialize: {e}src/stores/sql/mysql.rsid, e-
debug[redis-scan-wrapped] skipping key {key}: {e}src/stores/redis/objects_db.rskey, e-

cosmian_kms_crypto

Crate path: crate/crypto RUST_LOG target: cosmian_kms_crypto

LevelMessageFileVariablesNotes
errorError verifying ({:?}) signature: {:?}, data: {:?}, error: {err:?}src/crypto/rsa/verify.rserr: error detail-
errorError verifying digest ({:?}) signature: {:?}, data: {:?}, error: {err:?}src/crypto/rsa/verify.rserr: error detail×2 in this file
errorError verifying raw ({:?}) signature: {:?}, data: {:?}, error: {err:?}src/crypto/rsa/verify.rserr: error detail-
warntest_openssl_cli_compat: openssl CLI call failed, skipping test: {output:#?}src/crypto/rsa/ckm_rsa_aes_key_wrap.rsoutput: output-
warntest_openssl_cli_compat: openssl CLI not found, skipping testsrc/crypto/rsa/ckm_rsa_aes_key_wrap.rs--
warntest_openssl_cli_compat: openssl CLI output is not valid UTF-8src/crypto/rsa/ckm_rsa_aes_key_wrap.rs--
info===> Wrapping asymmetric key with asymmetric keysrc/crypto/wrap/tests.rs--
info===> Wrapping asymmetric key with symmetric keysrc/crypto/wrap/tests.rs--
info===> Wrapping symmetric key with asymmetric keysrc/crypto/wrap/tests.rs--
info===> Wrapping symmetric key with symmetric keysrc/crypto/wrap/tests.rs--
infovalue is: {:?}src/openssl/x509_extensions.rs-×2 in this file
debugattribute: {attribute:?}, encryption_hint: {encryption_hint:?}src/crypto/cover_crypt/access_structure.rsattribute: KMIP attribute (debug display)
encryption_hint: encryption hint
-
debugcreate_dk_object: key len: {}, attributes: {attributes}src/crypto/kem.rsattributes: KMIP attribute (debug display)s-
debugcreate_msk_object: key len: {}, attributes: {attributes}src/crypto/cover_crypt/master_keys.rsattributes: KMIP attribute (debug display)s-
debugDecrypted data with user key {} of len (Plain/Enc): {}/{}src/crypto/cover_crypt/decryption.rs--
debugEncrypted data with auth data {:?} of len (Plain/Enc): {}/{}src/crypto/cover_crypt/encryption.rs--
debugRSA key pair generated: private key id: {private_key_uid}src/crypto/rsa/operation.rsprivate_key_uid: private key uid-
debugRSA key pair generation: size in bits: {key_size_in_bits}src/crypto/rsa/operation.rskey_size_in_bits: RSA key size in bits-
debugserver: access_structure: {access_structure:?}src/crypto/cover_crypt/master_keys.rsaccess_structure: Covercrypt access structure-
debugusing GCMsrc/crypto/wrap/wrap_key.rs--
debugwrapping with CKM_RSA (v1.5)src/crypto/wrap/wrap_key.rs--
debugwrapping with CKM_RSA_AES_KEY_WRAP and hashing function: {hashing_fn}src/crypto/wrap/wrap_key.rshashing_fn: hash function-
debugwrapping with CKM_RSA_OAEP and hashing function: {hashing_fn}src/crypto/wrap/wrap_key.rshashing_fn: hash function-
traceAccess Policy: {access_policy:?}src/crypto/cover_crypt/user_key.rsaccess_policy — …
traceauthenticated_encryption_additional_data: {ad:?}src/crypto/cover_crypt/encryption.rsad — …
tracebytes len: {:?}, bits: {}src/crypto/elliptic_curves/operation.rs×2 in this file
tracebytes len: {}, bits: {}src/crypto/rsa/operation.rs×2 in this file
traceChaCha20 (pure) encryption: key_len={}, nonce_len={}, pt_len={}src/crypto/symmetric/symmetric_ciphers.rs
traceCreated user decryption key with access policy: {access_policy:?}src/crypto/cover_crypt/user_key.rsaccess_policy — …
tracedecrypt: ad: {ad:?}src/crypto/cover_crypt/decryption.rsad — …
traceEd25519src/crypto/elliptic_curves/ecies/salsa_sealbox.rs
traceencrypted_bytes len: {}src/crypto/cover_crypt/decryption.rs
traceencrypted_header parsedsrc/crypto/cover_crypt/decryption.rs
traceencryption_policy: {ap:?}src/crypto/cover_crypt/encryption.rsap — …×2 in this file
traceinstantiate enteringsrc/crypto/cover_crypt/decryption.rs
traceInstantiated hybrid Covercrypt encipher for public key id: {public_key_uid}src/crypto/cover_crypt/encryption.rspublic_key_uid — …
traceKey format type: convert Rsa<Public> openssl objectsrc/openssl/public_key.rs
traceKey format type: TransparentDSAPublicKeysrc/openssl/public_key.rs
traceKey format type: TransparentRSAPublicKeysrc/openssl/public_key.rs
tracekey_wrapping_specification: {}src/crypto/wrap/wrap_key.rs
tracenonce_len={}, tag_len={}src/crypto/wrap/wrap_key.rs
traceoutput object: {output}src/crypto/rsa/operation.rsoutput — …
traceprivate key converted OKsrc/crypto/elliptic_curves/operation.rs
tracepublic key converted OKsrc/crypto/elliptic_curves/operation.rs
traceRefreshed user decryption key {usk:?}src/crypto/cover_crypt/user_key.rsusk — …
traceusing RFC-3394 (AES Key Wrap, no padding)src/crypto/wrap/wrap_key.rs
traceusing RFC-5649 (AES Key Wrap with Padding)src/crypto/wrap/wrap_key.rs
tracewith object type: {:?}src/crypto/wrap/wrap_key.rs
tracewrapping key: format={}src/crypto/wrap/wrap_key.rs
traceX25519src/crypto/elliptic_curves/ecies/salsa_sealbox.rs
trace{}src/openssl/public_key.rs
warntest_openssl_cli_compat: openssl version is not OpenSSL 3: {res}, skipping testsrc/crypto/rsa/ckm_rsa_aes_key_wrap.rsres
info\n\next: {:?}src/openssl/x509_extensions.rs×2 in this file
debugInstantiated hybrid CoverCrypt decipher for user decryption key id: {user_decryption_key_uid}src/crypto/cover_crypt/decryption.rsuser_decryption_key_uid
debugsymmetric wrapping using {cryptographic_algorithm} and block_cipher_mode: {:?}, padding_method: {:?}src/crypto/wrap/wrap_key.rscryptographic_algorithm
tracealgorithm: {algorithm:?}, block_cipher_mode: {block_cipher_mode:?}, key_size: {key_size}src/crypto/symmetric/symmetric_ciphers.rsalgorithm, block_cipher_mode, key_size
traceencrypt: sym_cipher: {sym_cipher:?}, key length: {}, nonce length: {}, aad length: {}, plaintext length: {}, padding_method: {padding_method:?}src/crypto/symmetric/symmetric_ciphers.rssym_cipher, padding_method
warnignored `basicConstraints` extension's value: {value}src/openssl/x509_extensions.rsvalue-
infoRFC 3394 is deprecated in favor of RFC 5649 and is supported only for legacy compatibility. Please consider using `BlockCipherMode::AESKeyWrapPadding` (RFC 5649) for new applications instead of `BlockCipherMode::NISTKeyWrap `.src/crypto/symmetric/symmetric_ciphers.rs-×2 in this file

cosmian_kmip

Crate path: crate/kmip RUST_LOG target: cosmian_kmip

LevelMessageFileVariablesNotes
warnCustom attribute name does not start with 'x-' or 'y-': {}src/kmip_1_4/kmip_attributes.rs--
warnFailed to deserialize KMIP 2.1 attribute: {}src/kmip_1_4/kmip_attributes.rs--
warnKMIP 1.4 Lease Time ({v}) exceeds i32::MAX; clamping to {}src/kmip_1_4/kmip_attributes.rsv: parsed value×2 in this file
warnKMIP 2.1 does not support the KMIP 1 attribute {attribute:?}src/kmip_1_4/kmip_attributes.rsattribute: KMIP attribute (debug display)×9 in this file
warnUnexpected value type for y-unsupported-2_1-attribute: {:?}src/kmip_1_4/kmip_attributes.rs--
debug[serialize] writing tag: {}src/ttlv/wire/ttlv_bytes_serializer.rs--
debugConverting KMIP 2.1 QueryResponse to KMIP 1.4: {value}src/kmip_1_4/kmip_operations.rsvalue: value-
trace... => This is an Object => identifier: key: Objectsrc/ttlv/kmip_ttlv_deserializer/deserializer.rs
trace... added a struct field: {key}, the parent struct is now: {:?}src/ttlv/kmip_ttlv_serializer.rskey — …
trace... assuming deserialization of BigInteger: {:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
trace... assuming deserialization of ByteString: {:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
trace... coerced ShortUniqueIdentifier ByteString -> hex string: {}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
trace... coerced {} Enumeration(code={}) -> stringsrc/ttlv/kmip_ttlv_deserializer/deserializer.rs
trace... coerced {} Enumeration(name={}) -> stringsrc/ttlv/kmip_ttlv_deserializer/deserializer.rs
trace... coerced {} Integer({}) -> stringsrc/ttlv/kmip_ttlv_deserializer/deserializer.rs
trace... deserializing an adjacently tagged structuresrc/ttlv/kmip_ttlv_deserializer/deserializer.rs
trace... deserializing enum at rootsrc/ttlv/kmip_ttlv_deserializer/deserializer.rs
trace... deserializing enum that is the only child of the current elementsrc/ttlv/kmip_ttlv_deserializer/deserializer.rs
trace... deserializing transparent seq with tag: {tag}src/ttlv/kmip_ttlv_deserializer/deserializer.rstag — …
trace... detected VendorAttribute triple children; deserializing element itself as enum variantsrc/ttlv/kmip_ttlv_deserializer/deserializer.rs
trace... enum: index: {:#x}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
trace... enum: name: {}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
trace... identifier: key: {}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
trace... Seq element added, the current parent value is: {:?}src/ttlv/kmip_ttlv_serializer.rs
trace... str: key: {}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
trace... structure recognized as byte-like; concatenated len={} for tag {}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
trace... structure: 1.4 Objectsrc/ttlv/kmip_ttlv_deserializer/deserializer.rs
trace... structure: 2.1 Objectsrc/ttlv/kmip_ttlv_deserializer/deserializer.rs
trace... structure: tag: {}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
trace... tag: {}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
trace... text string: value: {}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
trace===> KeyMaterial: Deserializing Bytes String for key format type: {:?}src/kmip_2_1/kmip_data_structures.rs×2 in this file
trace===> KeyMaterial: Deserializing key format type: {f:?} as structsrc/kmip_1_4/kmip_data_structures.rsf — …
trace===> KeyMaterial: Deserializing Structure for key format type: {f:?}src/kmip_2_1/kmip_data_structures.rsf — …
trace===> KeyMaterial: Deserializing {:?} key format type as seqsrc/kmip_1_4/kmip_data_structures.rs
trace==> Deserializing KeyBlocksrc/kmip_1_4/kmip_data_structures.rs
trace==> Deserializing KeyBlocksrc/kmip_2_1/kmip_data_structures.rs
tracearray_access: next_element_seed in seq: {}, current index: {}, elem at index: {:?}src/ttlv/kmip_ttlv_deserializer/array_deserializer.rs
traceChecking usage mask authorization {:?} for flag: {:?}src/kmip_2_1/kmip_attributes.rs
traceConverting KMIP 2.1 CreateKeyPairResponse to KMIP 1.4: {value}src/kmip_1_4/kmip_operations.rsvalue — …
traceConverting KMIP 2.1 CreateResponse to KMIP 1.4: {value}src/kmip_1_4/kmip_operations.rsvalue — …
traceConverting KMIP 2.1 DecryptResponse to KMIP 1.4: {value}src/kmip_1_4/kmip_operations.rsvalue — …
traceConverting KMIP 2.1 DestroyResponse to KMIP 1.4: {value}src/kmip_1_4/kmip_operations.rsvalue — …
traceConverting KMIP 2.1 EncryptResponse to KMIP 1.4: {value}src/kmip_1_4/kmip_operations.rsvalue — …
traceConverting KMIP 2.1 GetAttributesResponse to KMIP 1.4: {value}src/kmip_1_4/kmip_operations.rsvalue — …
traceConverting KMIP 2.1 GetResponse to KMIP 1.4: {value}src/kmip_1_4/kmip_operations.rsvalue — …
traceConverting KMIP 2.1 ImportResponse to KMIP 1.4: {value}src/kmip_1_4/kmip_operations.rsvalue — …
traceConverting KMIP 2.1 LocateResponse to KMIP 1.4: {value}src/kmip_1_4/kmip_operations.rsvalue — …
traceConverting KMIP 2.1 RegisterResponse to KMIP 1.4: {value}src/kmip_1_4/kmip_operations.rsvalue — …
traceConverting KMIP 2.1 SignResponse to KMIP 1.4: {value}src/kmip_1_4/kmip_operations.rsvalue — …
tracecurrent struct: {}, num children: {}, next child {:?}src/ttlv/kmip_ttlv_deserializer/structure_walker.rs
tracedeserialize_any of enum variant: name: {}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
tracedeserialize_any of enum variant: value: {}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
tracedeserialize_any value of BigInt: {:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
tracedeserialize_any value of Structure: {:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
tracedeserialize_any: map access state: key, tag: {}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
tracedeserialize_any: map access state: {:?}, index: {}, child: {:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
tracedeserialize_any: state: {:?}src/ttlv/kmip_ttlv_deserializer/kmip_big_int_deserializer.rs
tracedeserialize_bool: state: {:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
tracedeserialize_bytes: state: {:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
tracedeserialize_bytes_buff: state: {:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
tracedeserialize_char: state: {:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
tracedeserialize_enum: name {name}, element: {:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rsname — …
tracedeserialize_f32: state: {:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
tracedeserialize_f64: state: {:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
tracedeserialize_i128: element: {:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
tracedeserialize_i16 state: {:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
tracedeserialize_i32: state: {:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
tracedeserialize_i64: state: {:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
tracedeserialize_identifier: map state: {:?}, element: {:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
tracedeserialize_ignored_any: state: {:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
tracedeserialize_map: calling Untagged Enum deserializer for: {:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
tracedeserialize_new_type_struct with name: {name}, state: {:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rsname — …
tracedeserialize_option: state: {:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
tracedeserialize_seq: child index: {}: {:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
tracedeserialize_seq: state: {:?}src/ttlv/kmip_ttlv_deserializer/kmip_big_int_deserializer.rs
tracedeserialize_str: map state: {:?}, element tag: {}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
tracedeserialize_string: state: {:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
tracedeserialize_struct: name {name}, fields: {fields:?}, element: {:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rsname — …
fields — …
tracedeserialize_tuple: child index: {}, current : {:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
tracedeserialize_tuple: state: {:?}src/ttlv/kmip_ttlv_deserializer/kmip_big_int_deserializer.rs
tracedeserialize_tuple_struct: name: {name}, len: {len}, state: {:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rsname — …
len — …
tracedeserialize_u16: state: {:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
tracedeserialize_u32: state: {}: {:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
tracedeserialize_u64: state: {}: {:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
tracedeserialize_u8: state: {:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
tracedeserialize_unit: state: {:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rs
tracedeserialize_unit_struct with name: {name}, state: {:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rsname — …
tracedeserializing bytes string at tag: {}, of len: {}src/ttlv/kmip_ttlv_deserializer/byte_string_deserializer.rs
tracedeserializing OffsetDateTime at tag: {}, value: {}, index: {}src/ttlv/kmip_ttlv_deserializer/offset_date_time_deserializer.rs
traceelement: {:?}src/ttlv/kmip_ttlv_deserializer/enum_walker.rs
traceelement: {:?}src/ttlv/kmip_ttlv_deserializer/structure_walker.rs
traceFinished serializing byte sequence as ByteString, parent: {:?}src/ttlv/kmip_ttlv_serializer.rs
traceFinished serializing the sequence, the parent is: {:?}src/ttlv/kmip_ttlv_serializer.rs
tracefrom_ttlv: {s:?}src/ttlv/kmip_ttlv_deserializer/deserializer.rss — …
traceKey: State ? {:?}src/ttlv/kmip_ttlv_deserializer/adjacently_tagged_structure.rs
tracekey_wrap_type: {key_wrap_type:?}, attributes: {attributes}src/kmip_2_1/requests/import.rskey_wrap_type — …
attributes — …
traceMessageBatchItem operation: {operation:?}src/kmip_1_4/kmip_messages.rsoperation — …
traceMessageBatchItem operation: {operation:?}src/kmip_2_1/kmip_messages.rsoperation — …
traceMessageBatchItem request payload: {request_payload}src/kmip_1_4/kmip_messages.rsrequest_payload — …
traceMessageBatchItem request payload: {request_payload}src/kmip_2_1/kmip_messages.rsrequest_payload — …
traceMessageResponseBatchItem operation: {operation:?}src/kmip_1_4/kmip_messages.rsoperation — …
traceMessageResponseBatchItem operation: {operation:?}src/kmip_2_1/kmip_messages.rsoperation — …
traceMessageResponseBatchItem response payload: {response_payload}src/kmip_1_4/kmip_messages.rsresponse_payload — …
traceMessageResponseBatchItem response payload: {response_payload}src/kmip_2_1/kmip_messages.rsresponse_payload — …
tracenewtype_variant_seed: child index: {}, at root: {}, current tag: {:?}src/ttlv/kmip_ttlv_deserializer/enum_walker.rs
traceNot a BulkDatasrc/kmip_2_1/extra/bulk_data.rs
traceObject Visitor: visit_map: key: {key:?},src/kmip_1_4/kmip_objects.rskey — …
traceObject Visitor: visit_map: key: {key:?},src/kmip_2_1/kmip_objects.rskey — …
traceSeq Element: serializing a seq element with tag {}, stack is: {:?}src/ttlv/kmip_ttlv_serializer.rs
traceseq_access: next_element_seed: state: {:?}src/ttlv/kmip_ttlv_deserializer/kmip_big_int_deserializer.rs
traceserialize_map of len: {len:?}. Current: {:?}src/ttlv/kmip_ttlv_serializer.rslen — …
traceserialize_newtype_variant, name: {name}::{variant} (variant index: {variant_index})src/ttlv/kmip_ttlv_serializer.rsname — …
variant — …
variant_index — …
traceserialize_seq of len: {len:?} in receiver: {:?}src/ttlv/kmip_ttlv_serializer.rslen — …
traceserialize_seq of len: {len:?} in receiver: {:?} (byte accumulation mode)src/ttlv/kmip_ttlv_serializer.rslen — …
traceserialize_struct named: {name} in parent: {:?}src/ttlv/kmip_ttlv_serializer.rsname — …
traceserialize_struct, no parent found, creating a new one with tag: {}src/ttlv/kmip_ttlv_serializer.rs
traceserialize_tuple of len {len}. Current: {:?}src/ttlv/kmip_ttlv_serializer.rslen — …
traceserialize_tuple_struct {name} of len {len}. Current: {:?}src/ttlv/kmip_ttlv_serializer.rsname — …
len — …
traceserialize_unit_variant, name: {name}::{variant}; variant_index: {variant_index}src/ttlv/kmip_ttlv_serializer.rsname — …
variant — …
variant_index — …
traceserializing a struct field with name: {key}, stack: {:?}src/ttlv/kmip_ttlv_serializer.rskey — …
tracestruct_variant with fields: {fields:?}: state: {:?}src/ttlv/kmip_ttlv_deserializer/enum_walker.rsfields — …
traceStructure finalized, stack: {:?}src/ttlv/kmip_ttlv_serializer.rs
tracetag: {:?}, content: {:?}src/ttlv/kmip_ttlv_deserializer/adjacently_tagged_structure.rs
tracetuple_variant of len: {len}, child index: {}, current: {:?}src/ttlv/kmip_ttlv_deserializer/enum_walker.rslen — …
traceunique_identifier: {unique_identifier}src/kmip_2_1/requests/import.rsunique_identifier — …
traceunit_variant: child index: {}, current: {:?}src/ttlv/kmip_ttlv_deserializer/enum_walker.rs
traceUntagged Enum map: next_value_seed: current tag: {:?}, at root: {}src/ttlv/kmip_ttlv_deserializer/untagged_enum_walker.rs
traceValue: State ? {:?}src/ttlv/kmip_ttlv_deserializer/adjacently_tagged_structure.rs
tracevisit_f64: {}src/ttlv/deserialize.rs
tracevisit_i64: {}src/ttlv/deserialize.rs
tracevisit_map: Enumerationsrc/ttlv/deserialize.rs
tracevisit_str: {}src/ttlv/deserialize.rs
tracevisit_u64: {}src/ttlv/deserialize.rs
trace... replacing "Object" with: {name}src/ttlv/kmip_ttlv_serializer.rsname
traceserialize_seq, no parent found. This is a direct vec![] serialization. Creating a new one with tag: {}src/ttlv/kmip_ttlv_serializer.rs
traceserialize_struct_variant {name}::{variant} (variant index: {variant_index}) of len {len}. Current: {:?}src/ttlv/kmip_ttlv_serializer.rsname, variant, variant_index, len
traceserialize_tuple_variant {name}::{variant} (variant index: {variant_index}) of len {len}. Current: {:?}src/ttlv/kmip_ttlv_serializer.rsname, variant, variant_index, len
traceUntagged Enum map: next_key_seed: completed?: {}, at root: {}, index: {}, current tag: {:?}src/ttlv/kmip_ttlv_deserializer/untagged_enum_walker.rs

cosmian_kms_interfaces

Crate path: crate/interfaces RUST_LOG target: cosmian_kms_interfaces

LevelMessageFileVariablesNotes
errorFailed to decode object_id {}src/hsm/hsm_store.rs-Malformed object ID in PKCS#11 session. Key may have been created by a different client.
debugCreated HSM AES Key of length {key_length} with id {uid}src/hsm/hsm_store.rskey_length: key length
uid: KMIP object UID
-
debugCreating RSA keypair with uid: {uid}src/hsm/hsm_store.rsuid: KMIP object UID-
debugHSM find: incompatible filter, skipping HSM search: {e}src/hsm/hsm_store.rse: caught error-
debugIs {owner} an HSM admin? {}src/hsm/hsm_store.rsowner: object owner identity-
debugNo researched_attributes provided. Defaulting to empty filter attributessrc/hsm/hsm_store.rs--
debugsign: using algorithm {algorithm:?} for key {uid}src/hsm/hsm_store.rsalgorithm: cryptographic algorithm
uid: KMIP object UID
-
debugUser '{}' is not an HSM admin; skipping HSM keys for ownership querysrc/hsm/hsm_store.rs--
debugUsing default algorithm to decryptsrc/hsm/hsm_store.rs--
debugUsing default algorithm to encryptsrc/hsm/hsm_store.rs--
debug{e}src/hsm/hsm_store.rse: caught error-
traceFound: {uid}src/hsm/hsm_store.rsuid — …
traceGetting metadata for: {:02X?}src/hsm/hsm_store.rs
warncount_all_non_destroyed not implemented for this ObjectsStore backend — kms.objects.total will read 0 until a real implementation is providedsrc/stores/objects_store.rs
warncount_non_destroyed_keys not implemented for this ObjectsStore backend — kms.keys.active.count will read 0 until a real implementation is providedsrc/stores/objects_store.rs
warnHSM count_non_destroyed_keys: failed to list slots: {e}src/hsm/hsm_store.rse
warnModifyAttribute/SetAttribute on HSM key {uid}: attribute update accepted but not persisted to PKCS#11 slot (HSM does not support KMIP attribute storage)src/hsm/hsm_store.rsuid
debugencrypt: an RSA private key {uid} was specified. Trying to use public key {pk_uid} for encryptionsrc/hsm/hsm_store.rsuid, pk_uid
debugHSM count_non_destroyed_keys: slot {slot_id} query failed: {e}src/hsm/hsm_store.rsslot_id, e
debugHSM key {uid} export failed ({e}); falling back to metadata-only stub for attribute operationssrc/hsm/hsm_store.rsuid, e

cosmian_kms_access

Crate path: crate/access RUST_LOG target: cosmian_kms_access

No production log call-sites in this crate.

cosmian_kms_base_hsm

Crate path: crate/hsm/base_hsm RUST_LOG target: cosmian_kms_base_hsm

LevelMessageFileVariablesNotes
warnHSM library already initialized (CKR_CRYPTOKI_ALREADY_INITIALIZED); continuingsrc/hsm_lib.rs--
warnuser already logged in, ignoring loggingsrc/slots.rs--
debugCreating new session: {session_handle}. Logging in? {logging_in}src/session/session_impl.rssession_handle: session handle
logging_in: logging in
-
debugFound {} possible handlessrc/session/session_impl.rs--
debugInvalid object, skippingsrc/kms_hsm.rs--
debugLogging in session {session_handle} with passwordsrc/slots.rssession_handle: session handle-
debugOpening a session on slot: {slot_id}. Read write? {read_write}. Logging in? {}src/slots.rsslot_id: PKCS#11 slot identifier
read_write: read write
-
debugPerforming multi round AES CBC decryptionsrc/session/session_impl.rs--
debugPerforming multi round AES CBC encryptionsrc/session/session_impl.rs--
debugRetrieved HSM key type for key handle {key_handle}: {key_type:?}src/session/session_impl.rskey_handle: key handle
key_type: key type
-
debugRetrieving HSM key attributes for key handle: {key_handle}src/session/session_impl.rskey_handle: key handle-
debugReusing slot {slot_id}src/base_hsm.rsslot_id: PKCS#11 slot identifier-
debugUsing PKCS#11 library with {:?}src/base_hsm.rs--
traceDoing round with {round_length} bytes. {processed_length} of {total_length} donesrc/session/session_impl.rsround_length — …
processed_length — …
total_length — …
×2 in this file
traceFound {object_count} objectssrc/session/session_impl.rsobject_count — …
debugOAEP hash {hash} not supported: {e}src/session/session_impl.rshash, e-

Domain: CLI (ckms)

cosmian_kms_cli_actions

Crate path: crate/clients/clap RUST_LOG target: cosmian_kms_cli_actions

LevelMessageFileVariablesNotes
info[{email}] - certificate ID used: {certificate_unique_identifier}src/actions/google/key_pairs/create.rsemail: Google Workspace user email
certificate_unique_identifier: KMS UID of the certificate
-
info[{email}] - Import PKCS12 file before using it in Google key pair generationsrc/actions/google/key_pairs/create.rsemail: Google Workspace user email-
infoModifyAttributes response for {effective_uid}: {attribute}src/actions/attributes/modify.rseffective_uid: resolved KMIP object UID
attribute: updated attribute name and value
-
infoSetAttributes response for {unique_identifier}: {attribute}src/actions/attributes/set.rsunique_identifier: KMIP object UID
attribute: attribute name and value that was set
-
debug{decrypt_request}src/actions/cover_crypt/decrypt.rsdecrypt_request: full Covercrypt decrypt request (debug display)-
debug{encrypt_request}src/actions/cover_crypt/encrypt.rsencrypt_request: full Covercrypt encrypt request (debug display)-
debugaccess_structure: {access_structure}src/actions/cover_crypt/keys/create_key_pair.rsaccess_structure: Covercrypt access structure (debug display)-
debugCreating new leaf certificate with attributes: {attributes}src/actions/google/key_pairs/create.rsattributes: X.509 certificate attributes being submitted-
debugGetAttributes response for {unique_identifier}: {attributes}src/actions/attributes/get.rsunique_identifier: KMIP object UID
attributes: structured attributes response
-
debugimport certificate as {format_label} filesrc/actions/certificates/import_certificate.rsformat_label: certificate format name (e.g. PEM, DER)-
debugimport certificate as PKCS12 filesrc/actions/certificates/import_certificate.rs--
debugimport certificate chain as PEM filesrc/actions/certificates/import_certificate.rs--
trace{self}src/actions/attributes/delete.rsself — full delete action configuration (debug display)
trace{self}src/actions/attributes/get.rsself — full get-attributes action configuration (debug display)
trace{self}src/actions/attributes/modify.rsself — full modify action configuration (debug display)
trace{self}src/actions/attributes/set.rsself — full set-attributes action configuration (debug display)
trace{self}src/actions/certificates/export_certificate.rsself — full export action configuration (debug display)
trace{self}src/actions/certificates/import_certificate.rsself — full import action configuration (debug display)
trace{id}src/actions/shared/get_key_uid.rsid — resolved KMIP object UID
tracedata_encryption_algorithm: {data_encryption_algorithm}src/actions/symmetric/encrypt.rsdata_encryption_algorithm — selected symmetric encryption algorithm
traceDetermine the certificate to use - either existing or newly createdsrc/actions/google/key_pairs/create.rs
traceimport certificate as TTLV JSON filesrc/actions/certificates/import_certificate.rs
traceresponse for {unique_identifier}: <none>src/actions/attributes/delete.rsunique_identifier — KMIP object UID (no attribute was present)
traceunwrap using server-side HSM crypto oracle for key: {key_id}src/actions/shared/unwrap_key.rskey_id — UID of the key being unwrapped via the HSM crypto oracle
info[{email}] - Generating new leaf certificate with extensions file: {:?}src/actions/google/key_pairs/create.rsemail
debugGetAttributes response for {unique_identifier}: {}src/actions/attributes/get.rsunique_identifier
debugMoveFileExW(DELAY_UNTIL_REBOOT) failed for '{}'; leftover file is harmless.src/actions/cng.rs
tracedek (len={}): {dek:?}src/actions/symmetric/encrypt.rsdek
tracedek length {}src/actions/symmetric/decrypt.rs×2 in this file
traceencapsulation length {}src/actions/symmetric/decrypt.rs
traceencryption algorithm {:?}, key id {:?}, ciphertext (len={}): {:?}src/actions/symmetric/decrypt.rs
traceLeaf certificate attributes: {}src/actions/certificates/import_certificate.rs
tracepkcs7_object: {:?}src/actions/google/key_pairs/create.rs
traceresponse for {unique_identifier}: {}src/actions/attributes/delete.rsunique_identifier
tracewrapped_key_bytes: {:?}src/actions/google/key_pairs/create.rs

ckms

Crate path: crate/clients/ckms RUST_LOG target: ckms

LevelMessageFileVariablesNotes
infoStarting KMS CLIsrc/commands.rs--
infoStarting KMS CLI configuration wizardsrc/commands.rs--
debugLoading configuration from: {conf_path_buf}src/config.rsconf_path_buf: filesystem path of the configuration file being loaded-
traceConfiguration: {config}src/commands.rsconfig — full resolved CLI configuration (pretty debug display)
infoConfiguration saved at {}src/commands.rs

cosmian_kms_client

Crate path: crate/clients/client RUST_LOG target: cosmian_kms_client

LevelMessageFileVariablesNotes
infoGET {server_url}src/kms_rest_client.rsserver_url: full URL of the GET request being sent-
infoThe decrypted file is available at {output_file}src/file_utils.rsoutput_file: path to the decrypted output file×2 in this file
infoThe encrypted file is available at {output_file}src/file_utils.rsoutput_file: path to the encrypted output file×2 in this file
infoUsing server URL: {}src/http_client/client.rs
trace<==\n{}src/kms_rest_client.rs×2 in this file
trace==>\n{}src/kms_rest_client.rs×2 in this file
warnFailed to set TLS 1.2 cipher list '{}' (using defaults): {}src/http_client/tls.rs
warnFailed to set TLS 1.3 ciphersuites '{}' (using defaults): {}src/http_client/tls.rs
warnUnknown TLS 1.2 IANA cipher suite '{}' (skipping)src/http_client/tls.rs
infoUsing proxy: {:?}src/http_client/client.rs
debugCONNECT tunnel established: {target_host}:{target_port}src/http_client/proxy.rstarget_host, target_port
debugCONNECT tunnel: {proxy_addr} → {target_host}:{target_port}src/http_client/proxy.rsproxy_addr, target_host, target_port
traceError response on {endpoint}: status={status}, body={text}src/kms_rest_client.rsendpoint, status, text
warnckms config: `{}` is deprecated — rename it to `{}` in your ckms.toml to silence this warning.src/http_client/client.rs--

cosmian_kms_client_utils

Crate path: crate/clients/client_utils RUST_LOG target: cosmian_kms_client_utils

LevelMessageFileVariablesNotes
infoWARNING: the PEM file contains multiple objects. Only the private key will be imported. A corresponding public key will be generated automatically.src/import_utils.rs

Domain: PKCS#11

cosmian_pkcs11

Crate path: crate/clients/pkcs11/provider RUST_LOG target: cosmian_pkcs11

LevelMessageFileVariablesNotes
errorC_GetFunctionList: failed to instantiate base KMS client: {}src/lib.rs-PKCS#11 module failed to create the KMS client on load. Check server URL and auth in ckms.toml.
errorFailed to parse EC P256 public key: {:?}src/pkcs11_public_key.rs-HSM returned a malformed EC P256 key. Verify key generation parameters.
errorFailed to parse RSA private key: {:?}src/pkcs11_private_key.rs--
errorFailed to parse RSA public key: {:?}src/pkcs11_public_key.rs--
errorPublic key is not an EC P256 keysrc/pkcs11_private_key.rs--
errorPublic key is not an EC P256 keysrc/pkcs11_public_key.rs--
errorPublic key is not an RSA keysrc/pkcs11_public_key.rs--
errorremote_sign failed for Pkcs11PrivateKey with remote_id {}: {e}src/pkcs11_private_key.rse: caught errorRemote signing via PKCS#11 failed. Check KMS connectivity and key permissions.
errorUnsupported cryptographic algorithm: {:?}src/kms_object.rs--
errorUnsupported key algorithm: {:?}src/kms_object.rs--
errorverify not implemented for Pkcs11PublicKeysrc/pkcs11_public_key.rs--
warncreate_private_key_from_id: {id} has type {:?} (expected PrivateKey), skippingsrc/backend.rsid: object ID-
warncreate_symmetric_key_from_id: {id} has type {:?} (expected SymmetricKey), skippingsrc/backend.rsid: object ID-
debugdecrypt: decrypt_ctx: {ctx:?}src/backend.rsctx: ctx-
debugencrypt: ctx: {ctx:?}src/backend.rsctx: ctx-
debugkms_encrypt_async: ciphertext: {}src/kms_object.rs--
debugkms_import_object_async: label: {label}, data (length): {}src/kms_object.rslabel: label-
debugLocate response: ids: {:?}src/kms_object.rs--
debugLocated objects: tags: {tags:?}, type: {object_type:?} => {uniques_identifiers:?}src/kms_object.rstags: KMIP tag set
object_type: KMIP object type
uniques_identifiers: uniques identifiers
-
debugremote_sign: remote_id: {remote_id}, algorithm: {algorithm:?}src/backend.rsremote_id: remote id
algorithm: cryptographic algorithm
-
debugvol1: {}src/tests.rs--
tracecreate_object: {label:?}src/backend.rslabel — …
tracefind_all_certificatessrc/backend.rs
tracefind_all_data_objects: enteringsrc/backend.rs
tracefind_all_data_objects: found {} objectssrc/backend.rs
tracefind_all_objects: enteringsrc/backend.rs
tracefind_all_objects: found {} keyssrc/backend.rs
tracefind_all_private_keyssrc/backend.rs
tracefind_all_public_keyssrc/backend.rs
tracefind_all_symmetric_keyssrc/backend.rs
tracefind_certificatesrc/backend.rs
tracefind_data_object: {:?}src/backend.rs
tracefind_private_key: {:?}src/backend.rs
tracefind_public_key: {:?}src/backend.rs
tracefind_symmetric_key: {:?}src/backend.rs
traceFound {} objectssrc/kms_object.rs
tracegenerate_key: {algorithm:?}-{key_length}, {label:?}src/backend.rsalgorithm — …
key_length — …
label — …
traceget_kms_certificate_objects_async: found {} Certificate objectssrc/kms_object.rs
traceget_kms_certificate_objects_async: no Certificate objects found for tags: {:?}src/kms_object.rs
traceget_kms_objects_async: no objects found for tags: {:?}src/kms_object.rs
traceget_kms_secret_data_objects_async: found {} SecretData objectssrc/kms_object.rs
traceget_kms_secret_data_objects_async: no SecretData objects found for tags: {:?}src/kms_object.rs
errorC_GetFunctionList: failed to instantiate KMS client: {}. Check that ckms.toml exists alongside the DLL (C:\opt\oracle\extapi\64\pkcs11\ckms.toml), at ~/.cosmian/ckms.toml, or set CKMS_CONF to its path.src/lib.rs
errorC_GetFunctionList: failed to load ckms.toml: {}. Check that ckms.toml exists alongside the DLL (C:\opt\oracle\extapi\64\pkcs11\ckms.toml), at ~/.cosmian/ckms.toml, or set CKMS_CONF to its path.src/lib.rs
warncreate_object_from_attributes: unsupported object type: {other}, skipping {id}src/backend.rsother, id
warncreate_private_key_from_id: unsupported key/algorithm for PrivateKey {id}: {e}, skippingsrc/backend.rsid, e
warncreate_private_key_object: unsupported key/algorithm for PrivateKey {id}: {e}, skippingsrc/backend.rsid, e
warncreate_public_key_object: unsupported key/algorithm for PublicKey {id}: {e}, skippingsrc/backend.rsid, e
warncreate_symmetric_key_from_id: unsupported key/algorithm for SymmetricKey {id}: {e}, skippingsrc/backend.rsid, e
warncreate_symmetric_key_object: unsupported key/algorithm for SymmetricKey {id}: {e}, skippingsrc/backend.rsid, e
warnfind_all_public_keys: failed to build Pkcs11PublicKey for {id}: {e}, skippingsrc/backend.rsid, e
warnfind_all_public_keys: failed to export public key {id}: {e}, skippingsrc/backend.rsid, e
warnfind_all_data_objects: failed to build DataObject for disk-encryption key: {e}, skippingsrc/backend.rse
warnfind_all_data_objects: failed to fetch disk-encryption data objects: {e}, returning empty listsrc/backend.rse
warnfind_all_objects: failed to build DataObject for disk-encryption key: {e}, skippingsrc/backend.rse
warnfind_all_objects: failed to fetch disk-encryption data objects: {e}, returning empty listsrc/backend.rse
tracefind_all_objects: total {} objects (including disk-encryption DataObjects)src/backend.rs
traceget_kms_disk_encryption_data_objects_async: found {} SymmetricKey objectssrc/kms_object.rs
traceget_kms_disk_encryption_data_objects_async: no SymmetricKey objects found for tag: {disk_encryption_tag}src/kms_object.rsdisk_encryption_tag

cosmian_pkcs11_module

Crate path: crate/clients/pkcs11/module RUST_LOG target: cosmian_pkcs11_module

LevelMessageFileVariablesNotes
errorC_GetAttributeValue: error: {e}, session: {:?}, object: {:?}, type: {:?}src/pkcs11.rse: caught error-
errorcertificate: type_ unimplemented: {type_:?}src/core/object.rstype_: type-
errorData object: type_ unimplemented: {type_:?}src/core/object.rstype_: type-
errorFailed to extract RSA key parameters: {e:?}src/core/object.rse: caught error- Could not read RSA key components from HSM object. Key may be non-exportable.
errorFailed to parse RSA private key from PKCS#8 DER: {e:?}src/core/object.rse: caught error- HSM returned a malformed RSA PKCS#8 blob. Check HSM firmware and key type.
errorpParameter incorrect: {} != {}src/core/mechanism.rs-PKCS#11 mechanism parameter size mismatch. Client sent the wrong parameter struct.
errorprivate_key: type_ unimplemented: {type_:?}src/core/object.rstype_: type-
errorprofile: type_ unimplemented: {type_:?}src/core/object.rstype_: type-
errorpublic_key: type_ unimplemented: {type_:?}src/core/object.rstype_: type-
errorsymmetric_key: type_ unimplemented: {type_:?}src/core/object.rstype_: type-
errorUnsupported hashAlg: {}src/core/mechanism.rs-PKCS#11 hash algorithm not supported. Use SHA-256 or SHA-384.
errorUnsupported mgf: {}src/core/mechanism.rs-PKCS#11 MGF algorithm not supported. Use MGF1 with a supported hash.
error{}: {}src/pkcs11.rs-Generic two-part error - inspect both values for the error type and detail.
warnload_find_context: id {label} not found in storesrc/sessions.rslabel: label-
infoC_CloseAllSessions: slot: {:?}src/pkcs11.rs--
infoC_CloseSession: session: {:?}src/pkcs11.rs--
infoC_FindObjects: session: {:?}, no more objects to returnsrc/pkcs11.rs--
infoC_FindObjects: session: {:?}, returning {} object with handles {:?}src/pkcs11.rs--
infoC_GetAttributeValue: session: {:?}, object: {:?} [handle: {}], type: {:?}src/pkcs11.rs--
infoC_OpenSession: slot={slotID:?} flags={flags:?} session handle writtensrc/pkcs11.rsslotID: slotID
flags: flags
-
debugC_DestroyObject: session: {hSession:?}, hObject: {hObject}src/pkcs11.rshSession: hSession
hObject: hObject
-
debugCKO_DATA match: remote_id={}, handle={}src/sessions.rs--
debugCKO_DATA search: label_filter={:?}, store has {} DataObjectssrc/sessions.rs--
debugcreate_object: attributes: {attributes:?}src/sessions.rsattributes: KMIP attribute (debug display)s-
debugcreate_object: created object with handle: {handle}src/sessions.rshandle: PKCS#11 object handle-
debugdestroy_object: handle: {handle}src/sessions.rshandle: PKCS#11 object handle×2 in this file
debuggenerate_key: generated key with handle: {handle}src/sessions.rshandle: PKCS#11 object handle-
debuggenerate_key: generating key with mechanism: {:?} and attributes: {:?}src/sessions.rs--
debugload_find_context: display current store: {find_ctx}src/sessions.rsfind_ctx: find ctx-
debugload_find_context: loading for label: {label:?} and attributes: {attributes:?}src/sessions.rslabel: label
attributes: KMIP attribute (debug display)s
-
debugload_find_context: search by id: {label} -> handle: {} -> object: {}: {}src/sessions.rslabel: label-
debugload_find_context_by_class: added {} objects with handles: {:?}src/sessions.rs--
debugmap_oracle_tde_security_to_mk: processing label: {label}src/sessions.rslabel: label-
debugObject: {}, attribute: {:?} => {:?}src/core/object.rs--
debugparse_mechanism: iv: {iv:?}src/core/mechanism.rsiv: iv-
debugparse_mechanism: {mechanism:?}src/core/mechanism.rsmechanism: PKCS#11 mechanism-
debugsession: {h} foundsrc/sessions.rsh: h-
debugSTORE: inserting new object with remote id: {id} and handle: {handle}src/objects_store.rsid: object ID
handle: PKCS#11 object handle
-
debugSTORE: updating object with remote id: {id} and handle: {handle}src/objects_store.rsid: object ID
handle: PKCS#11 object handle
-
traceAttribute::try_from: attribute parsed: {:?} => {:?}src/core/attribute.rs
traceAttribute::try_from: parsing attribute: {:?}src/core/attribute.rs
traceAttribute::try_from: type: {attr_type:?}src/core/attribute.rsattr_type — …
traceAttribute::try_from: value: {val:?}src/core/attribute.rsval — …
traceAttributes::try_from: parsing single attribute: {attr:?}src/core/attribute.rsattr — …
traceC_FindObjects: session: {:?}, objects available: {:?}src/pkcs11.rs
traceC_GetAttributeValue: session: {:?}, object: {:?}src/pkcs11.rs
traceC_GetSessionInfo: session: {:?}, slot: {:?}, state: {:?}, flags: {:?}src/pkcs11.rs
tracecreate_object: class: {class:?}src/sessions.rsclass — …
tracecreate_object: Object not supported: {o}src/sessions.rso — …
traceGenerated random: {}src/pkcs11.rs
traceload_find_context succeededsrc/sessions.rs
infoC_FindObjectsInit: session: {hSession:?}, load Objects Store context for attributes: {attributes:?}src/pkcs11.rshSession, attributes
debugC_CreateObject: session: {hSession:?}, pTemplate: {pTemplate:?}, ulCount: {ulCount:?}, phObject: {phObject:?}src/pkcs11.rshSession, pTemplate, ulCount, phObject
debugC_Decrypt: pEncryptedData: {pEncryptedData:?}, ulEncryptedDataLen: {ulEncryptedDataLen:?}, pData: {pData:?}, pulDataLen: {pulDataLen:?}src/pkcs11.rspEncryptedData, ulEncryptedDataLen, pData, pulDataLen
debugC_Decrypt: session: {:?}, encrypted_data_len: {:?}, cleartext_len: {:?}, ciphertext: {:?}src/pkcs11.rs
debugC_DecryptInit: session: {hSession:?}, hKey: {hKey:?}, mechanism: {mechanism:?}, object: {object:?}src/pkcs11.rshSession, hKey, mechanism, object
debugC_Encrypt: pData: {pData:?}, ulDataLen: {ulDataLen:?}, pEncryptedData: {pEncryptedData:?}, pulEncryptedDataLen: {pulEncryptedDataLen:?}src/pkcs11.rspData, ulDataLen, pEncryptedData, pulEncryptedDataLen
debugC_Encrypt: session: {:?}, plain_data_len: {:?}, ciphertext_len: {:?}, cleartext: {:?}src/pkcs11.rs
debugC_EncryptInit: session: {hSession:?}, hKey: {hKey:?}, mechanism: {mechanism:?}, object: {object:?}src/pkcs11.rshSession, hKey, mechanism, object
debugC_GenerateKey: session: {hSession:?}, pMechanism: {pMechanism:?}, pTemplate: {pTemplate:?}, ulCount: {ulCount:?}, phKey: {phKey:?}src/pkcs11.rshSession, pMechanism, pTemplate, ulCount, phKey
debugload_find_context_by_class: loading for class: {search_class:?} and options: {search_options:?}, attributes: {attributes:?}src/sessions.rssearch_class, search_options, attributes
debugload_find_context_by_class: search by id: {} -> handle: {} -> certificate: {}:{}src/sessions.rs
debugload_find_context_by_class: search by id: {} -> handle: {} -> object: {}:{}src/sessions.rs

Domain: CNG (Windows)

cosmian_cng

Crate path: crate/clients/cng RUST_LOG target: cosmian_cng

LevelMessageFileVariablesNotes
errorCNG KSP decrypt: {e}src/provider.rse: caught errorDecryption via CNG failed. Check key state and CNG provider logs.
errorCNG KSP delete_key: {e}src/provider.rse: caught error-
errorCNG KSP encrypt: {e}src/provider.rse: caught error-
errorCNG KSP enum_keys: {e}src/provider.rse: caught error-
errorCNG KSP export_key private: {e}src/provider.rse: caught error-
errorCNG KSP export_key: {e}src/provider.rse: caught error-
errorCNG KSP finalize_key: {e}src/provider.rse: caught error-
errorCNG KSP import_key attrs: {e}src/provider.rse: caught error-
errorCNG KSP import_key backend: {e}src/provider.rse: caught error-
errorCNG KSP import_key parse blob: {e}src/provider.rse: caught error-
errorCNG KSP open_key attrs({name}): {e}src/provider.rsname: key or object name
e: caught error
-
errorCNG KSP open_key pub({name}): {e}src/provider.rsname: key or object name
e: caught error
-
errorCNG KSP open_key({name}): {e}src/provider.rsname: key or object name
e: caught error
-
errorCNG KSP open_provider: {e}src/provider.rse: caught errorCNG provider DLL failed to load or initialise. Verify the DLL is registered.
errorCNG KSP sign_hash: {e}src/provider.rse: caught errorSigning via CNG failed. Key may be non-exportable or in an invalid state.
errorCNG KSP verify_signature: {e}src/provider.rse: caught error-
infoCNG KSP provider already registered; skipping BCryptRegisterProvidersrc/registry.rs--
infoDLL was locked; old copy renamed to '{}' and scheduled for deletion on reboot.src/registry.rs--
debugBCryptAddContextFunction returned {status:#010x} (may already exist)src/registry.rsstatus: HTTP response status-
debugBCryptRemoveContextFunctionProvider returned {status:#010x}src/registry.rsstatus: HTTP response status-
debugCNG KSP GetKeyStorageInterface calledsrc/lib.rs--
debugCNG KSP register: source dll={}src/registry.rs--
debugCNG KSP unregistersrc/registry.rs--
debugCNG KSP: create_ec_key_pair name={key_name} curve={curve:?}src/backend.rskey_name: CNG key name
curve: elliptic curve identifier
-
debugCNG KSP: create_rsa_key_pair name={key_name} bits={bit_length}src/backend.rskey_name: CNG key name
bit_length: key size in bits
-
debugCNG KSP: decoded PEM to {} bytes DERsrc/key.rs--
debugCNG KSP: decrypt_data uid={uid} len={}src/backend.rsuid: KMIP object UID-
debugCNG KSP: destroy_key uid={uid}src/backend.rsuid: KMIP object UID-
debugCNG KSP: encrypt_data uid={uid} len={}src/backend.rsuid: KMIP object UID-
debugCNG KSP: export_private_key_pkcs8 uid={uid}src/backend.rsuid: KMIP object UID-
debugCNG KSP: export_public_key_spki uid={uid}src/backend.rsuid: KMIP object UID-
debugCNG KSP: finalize import blob len={}, first_bytes={:?}src/key.rs--
debugCNG KSP: finalized key '{}' → priv={}, pub={}src/key.rs--
debugCNG KSP: import_rsa_private_key name={key_name}src/backend.rskey_name: CNG key name-
debugCNG KSP: revoke_key uid={uid}src/backend.rsuid: KMIP object UID-
debugCNG KSP: sign_hash uid={uid} hash_len={}src/backend.rsuid: KMIP object UID-
debugCNG KSP: verify_signature uid={uid} hash_len={} sig_len={}src/backend.rsuid: KMIP object UID-
debugCopying {} -> {}src/registry.rs--
debugMoveFileExW(DELAY_UNTIL_REBOOT) failed for '{}'; leftover file is harmless.src/registry.rs--
traceCNG KSP: list_cng_keyssrc/backend.rs
traceCNG KSP: locate_key_by_name tag={tag}src/backend.rstag — …
traceCNG KSP: locate_public_key_by_name tag={tag}src/backend.rstag — …

Domain: Web UI

The Web UI emits browser console messages via console.*. These are visible in the browser developer tools (F12 → Console tab) and are not captured by RUST_LOG or the OTLP pipeline.

Crate path: ui/src/

LevelMessageFileVariablesNotes
warnrevoke_ttlv_request not available in WASM packagecomponents/common/Locate.tsx--
info[KMS] vendor_id set to "{vendorId}"App.tsxvendorId: vendor identifier string received from the server-
errorAggregate date error:actions/Tokenize/TokenizeAggregateDate.tsx
errorAggregate number error:actions/Tokenize/TokenizeAggregateNumber.tsx
errorCertificate validation failed:pages/LoginPage.tsx
errorError creating FPE key:actions/FPE/FpeKeysCreate.tsx
errorError fetching create permission:actions/Access/AccessObtained.tsx
errorError fetching CSE information:actions/Keys/CseInfo.tsx
errorError fetching Get for ${uid}:components/common/Locate.tsxuid×4 in this file
errorError fetching HSM status:actions/Objects/HsmStatus.tsx
errorError fetching privileged access:actions/Access/AccessGrant.tsx
errorError fetching privileged access:actions/Access/AccessRevoke.tsx
errorError getting attributes:actions/Attributes/AttributeGet.tsx
errorError listing objects:actions/Access/AccessObtained.tsx
errorError listing objects:actions/Objects/ObjectsOwned.tsx
errorError loading certificate algorithms from WASM:actions/Certificates/CertificateCertify.tsx
errorError loading EC algorithms from WASM:actions/EC/ECKeysCreate.tsx
errorError loading hash algorithms from WASM:actions/Symmetric/SymmetricHash.tsx
errorError loading PQC algorithms from WASM:actions/PQC/PqcKeysCreate.tsx
errorError loading symmetric algorithms from WASM:actions/Keys/SymKeysCreate.tsx
errorError parsing tags JSON:actions/CloudProviders/AwsExportKeyMaterial.tsx
errorError parsing tags JSON:utils/azureByok.ts
errorFallback Locate without KFT failed:components/common/Locate.tsx
errorFPE decrypt error:actions/FPE/FpeDecrypt.tsx
errorFPE encrypt error:actions/FPE/FpeEncrypt.tsx
errorHash tokenize error:actions/Tokenize/TokenizeHash.tsx
errorLogin error:contexts/AuthContext.tsx
errorLogin error:pages/LoginPage.tsx
errorNoise tokenize error:actions/Tokenize/TokenizeNoise.tsx
errorScale number error:actions/Tokenize/TokenizeScaleNumber.tsx
errorWASM init failed:App.tsx
errorWord mask error:actions/Tokenize/TokenizeWordMask.tsx
errorWord pattern mask error:actions/Tokenize/TokenizeWordPatternMask.tsx
errorWord tokenize error:actions/Tokenize/TokenizeWordTokenize.tsx
warn[KMS] Could not query server vendor_id, using default:App.tsx
warnState+KFT fallback Locate without KFT failed:components/common/Locate.tsx
warnSymmetric google_cse key check failed:actions/Keys/CseInfo.tsx
debugECSign: signature lengthactions/EC/ECSign.tsx
debugECVerify: dataBuf lenactions/EC/ECVerify.tsx
debugRsaSign: signature lengthactions/RSA/RsaSign.tsx
debugRsaVerify: dataBuf lenactions/RSA/RsaVerify.tsx
errorJWT fallback failed:App.tsx--

Monitoring Stack — Setup Guide

This guide walks you through deploying the KMS observability stack step by step. For a full reference of OTLP options and pipeline internals, see → KMS Telemetry


Table of contents

  1. Download
  2. Prerequisites
  3. Local mode — KMS included
  4. External mode — existing KMS
  5. Explore Grafana
  6. Troubleshooting

Download

The monitoring stack is distributed as a standalone archive containing the Docker Compose file, configuration templates, and helper scripts.

Download the archive from the KMS GitHub Releases page (cosmian-kms-monitoring.zip), or use wget with the version you are deploying:

wget https://package.cosmian.com/kms/<version>/monitoring/cosmian-kms-monitoring.zip
unzip cosmian-kms-monitoring.zip
cd monitoring/

Replace <version> with the KMS version you are deploying (e.g. 5.25.0). The list of available versions is at https://package.cosmian.com/kms/.

Alternatively, clone the KMS repository and navigate to the monitoring/ directory.


Prerequisites

Before getting started, make sure the following tools are installed on your machine.

Required

  • Docker Engine ≥ 20.10 Compose profiles (COMPOSE_PROFILES) require Docker Compose v2 and Docker Engine 20.10+. Verify your version:

    docker --version
    docker compose version
    
  • Docker Compose V2 (included in Docker Desktop 3.4+) The stack uses the docker compose command (with a space), not the legacy docker-compose.

Local mode only

  • openssl — used by generate-demo-cert.sh to generate the demo TLS certificate. Verify it is available:

    openssl version
    

    On macOS it is pre-installed. On Linux: apt install openssl or dnf install openssl.

External mode only

  • Your existing KMS must be configured to export telemetry via OTLP to the collector:

    # gRPC (recommended)
    KMS_OTLP_URL=http://<collector-host>:4317
    # or HTTP
    KMS_OTLP_URL=http://<collector-host>:4318
    

    See → KMS Telemetry — OTLP configuration

Ports availability

The following ports must be free on the host before starting the stack:

PortService
3000Grafana
4317OTel Collector (gRPC)
4318OTel Collector (HTTP)
8428VictoriaMetrics
9998KMS (local mode only)

Local mode — KMS included

In this mode, the full stack starts together: KMS, OTel Collector, VictoriaMetrics, and Grafana. Use this mode for local development or to evaluate the stack end-to-end.

All files referenced below (docker-compose.yml, .env, generate-demo-cert.sh) are located in the monitoring/ directory of the KMS repository. Navigate there first:

cd monitoring/

1. Configure .env

Open the .env file — this is the only file you need to edit.

Make sure the following values are set:

# ── KMS Mode / Docker profiles ───────────────────────────────────
COMPOSE_PROFILES=kms-local
KMS_MODE=local

# ── TLS certificate password (used by generate-demo-cert.sh) ─────
KMS_P12_PASSWORD=testpassword

# ── KMS topology metadata ────────────────────────────────────────
KMS_CLUSTER=cosmian-kms-local

# ── Metadata for metrics/traces ──────────────────────────────────
KMS_VERSION=latest
ENVIRONMENT=demo

# ── Grafana ──────────────────────────────────────────────────────
GRAFANA_ADMIN_USER=demo
GRAFANA_ADMIN_PASSWORD=demo

# ── Metrics retention (VictoriaMetrics, in months) ───────────────
METRICS_RETENTION_MONTHS=12

✏️ Screenshot placeholder.env file open in editor, COMPOSE_PROFILES and KMS_MODE highlighted.

2. Generate the demo TLS certificate

The KMS container requires a PKCS#12 certificate to serve its API over TLS. The provided script generates a self-signed certificate valid for 10 years, using the password defined in KMS_P12_PASSWORD:

bash generate-demo-cert.sh

Expected output:

Generating a 4096 bit RSA private key
.............................................++++
.............................................++++
.............................................++++
writing new private key to './certs/kms-demo.key'
-----
PKCS#12 demo certificate generated at: ./certs/kms.p12
Password: testpassword

monitor00

⚠️ This certificate is for demo purposes only. Do not use it in production.

3. Start the stack

docker compose up -d

Docker will pull the required images and start all containers. On first run, this may take a minute depending on your network speed.

monitor01

4. Verify the stack is healthy

docker compose ps

All containers should show Up or Up (healthy):

NAME                STATUS
kms                 Up (healthy)
otel-collector      Up
victoria-metrics    Up
grafana             Up (healthy)

You can also check the OTel Collector health endpoint:

curl http://localhost:13133

Expected response: {"status":"Server available"}

monitor02


External mode — existing KMS

Use this mode when you already have a running KMS instance and only want to attach the observability stack to it.

All files referenced below are located in the monitoring/ directory of the KMS repository. Navigate there first:

cd monitoring/

1. Configure .env

Only a few lines change compared to local mode:

-COMPOSE_PROFILES=kms-local
-KMS_MODE=local
+COMPOSE_PROFILES=
+KMS_MODE=external

-# KMS_P12_PASSWORD is not needed in external mode
-KMS_P12_PASSWORD=testpassword

 KMS_CLUSTER=cosmian-kms-local
 KMS_VERSION=latest
-ENVIRONMENT=demo
+ENVIRONMENT=production

 GRAFANA_ADMIN_USER=demo
 GRAFANA_ADMIN_PASSWORD=demo
 METRICS_RETENTION_MONTHS=12

monitor03

2. Point your KMS to the collector

On your existing KMS, enable OTLP export toward the collector host:

# via environment variable
KMS_OTLP_URL=http://<collector-host>:4317

# or via CLI argument
./cosmian_kms_server --otlp http://<collector-host>:4317 --enable-metering

If the KMS runs on the same machine as the stack, use http://localhost:4317.

3. Start the stack (without KMS)

docker compose up -d

With COMPOSE_PROFILES left empty, Docker Compose will start only OTel Collector, VictoriaMetrics, and Grafana — the KMS container is skipped.

4. Verify the stack is healthy

docker compose ps

Expected output (no kms container):

NAME                STATUS
otel-collector      Up
victoria-metrics    Up
grafana             Up (healthy)

Verify the collector is receiving data from your KMS:

# Check that otelcol_receiver_accepted_spans is incrementing
curl -s http://localhost:8888/metrics | grep otelcol_receiver_accepted_spans

Or

monitor04


Explore Grafana

Once the stack is running, open Grafana in your browser:

http://localhost:3000

Login with:

FieldValue
Usernamevalue of GRAFANA_ADMIN_USER in .env (default: demo)
Passwordvalue of GRAFANA_ADMIN_PASSWORD in .env (default: demo)

monitor05

Pre-provisioned dashboards are available under Dashboards → Browse:

monitor06

After few seconds metrics will be available

monitor07


Troubleshooting

Containers not starting

docker compose logs -f otel-collector
docker compose logs -f kms          # local mode only

No data in Grafana dashboards

  1. Confirm the OTel Collector is up:

    curl http://localhost:13133
    
  2. Confirm VictoriaMetrics is receiving data:

    curl http://localhost:8428/health
    
  3. Check that your KMS is sending OTLP — look for incoming spans in collector logs:

    docker compose logs otel-collector | grep "traces"
    

KMS container fails to start (local mode)

Verify the certificate was generated:

ls -la .certs/kms.p12

If missing, re-run:

bash generate-demo-cert.sh

Port conflict

If a port is already in use, identify the process:

# macOS / Linux
lsof -i :<port>

# Windows
netstat -ano | findstr :<port>

Then either stop the conflicting process or edit the port mapping in docker-compose.yml.


For a complete reference of all OTLP options, log levels, and pipeline internals, see → KMS Telemetry

OTLP Metrics Reference

The KMS server pushes metrics to any OpenTelemetry collector via OTLP/gRPC every 30 seconds. No HTTP /metrics endpoint is exposed — metrics are always pushed, never scraped.

For deployment instructions and Grafana setup, see Monitoring Setup. To enable the feature, see Telemetry & Observability.

KMIP Operations

MetricTypeDescriptionLabels
kms.kmip.operations.totalcounterTotal KMIP operations executedoperation
kms.kmip.operations.per_user.totalcounterTotal KMIP operations per useroperation, user
kms.kmip.operation.durationhistogram (s)Duration of each KMIP operationoperation

Users & Permissions

MetricTypeDescriptionLabels
kms.active.usersup-down counterUnique users who issued at least one request
kms.permissions.granted.per_user.totalcounterAccess rights granted, broken down by useruser, permission_type
kms.permissions.granted.totalcounterTotal access rights granted

Database

MetricTypeDescriptionLabels
kms.database.operations.totalcounterDB operations by type and resultoperation, backend, outcome
kms.database.operation.durationhistogram (s)Wall-clock time of each DB calloperation, backend, outcome

Label values:

  • backend: sqlite · postgresql · mysql · redis
  • outcome: success · error

HTTP

MetricTypeDescriptionLabels
kms.http.requests.totalcounterIncoming HTTP requestsmethod, path, status
kms.http.request.durationhistogram (s)HTTP request latencymethod, path, status

path is normalised (e.g. /kmip/2_1, /google_cse/...) to avoid high cardinality from object identifiers.

Server Health

MetricTypeDescriptionLabels
kms.server.uptimecounter (monotonic, s)Seconds elapsed since server start
kms.server.start_timeup-down counterServer start time as Unix timestamp (s)
kms.active.connectionsup-down counterCurrent open HTTP connections
kms.errors.totalcounterErrors categorised by typeerror_type

Objects & Keys

MetricTypeDescriptionLabels
kms.objects.totalgaugeTotal non-destroyed objects in the KMS
kms.keys.active.countgaugeNon-destroyed key objects (SymmetricKey, PrivateKey, PublicKey, SplitKey) across all states: PreActive, Active, Deactivated, Compromised

Both metrics are refreshed every 30 s by the metrics cron task and seeded at server startup.

Cache

MetricTypeDescriptionLabels
kms.cache.operations.totalcounterUnwrap-cache lookupsoperation, result

HSM

MetricTypeDescriptionLabels
kms.hsm.operations.totalcounterHSM operations by type and modeloperation, hsm_model

KMS User Interface

The KMS User Interface (UI) is a web-based application served from the KMS server, allowing users to perform key management operations easily.

Accessing the User Interface

Once the KMS server is running, open the following URL in your browser:

https://YOUR_KMS_URL/ui

Replace YOUR_KMS_URL with the actual KMS server address.

If the KMS is running behind a reverse proxy, set kms_public_url in the server configuration to the public-facing URL — this is required for the OIDC redirect flow to work correctly.

The UI bundle is served from the path configured by ui_index_html_folder (defaults to the built-in bundle shipped with the server).

Authentication Configuration

The UI automatically detects the authentication method configured on the KMS server and adapts its login flow accordingly:

  • OIDC Authentication: The UI presents a LOGIN button that redirects to the identity provider. See Configuring OIDC Authentication below.
  • Certificate Authentication: The UI presents an ACCESS KMS button. The browser negotiates the mTLS handshake and submits the client certificate automatically. If no valid certificate is available, the login page is shown again with an error. See Configuring Certificate Authentication below.
  • No authentication configured: No login is required — the UI takes you directly to the key management interface. However, a warning banner is displayed:

Warning

To remove the warning "Authentication is disabled on this KMS server", configure an [authentication method like explained in the next sections](#authentication-configuration).

Configuring OIDC Authentication

To enable authentication via OIDC, configure the KMS UI with details from the selected OIDC compliant tenant.

1. Using the Configuration File (.toml)

Add the following section to your KMS configuration file:

[ui_config.ui_oidc_auth]
ui_oidc_client_id = "your_client_id"
ui_oidc_client_secret = "your_client_secret"  # (optional)
ui_oidc_issuer_url = "https://your_oidc_issuer_url"
ui_oidc_logout_url = "https://your_oidc_logout_url"

If your KMS is accessible behind a proxy, also specify the public KMS URL in the generic section:

kms_public_url = "your_kms_public_url"

You may also need to register the following URIs in your Identity Provider (IdP) application settings:

  • Allowed redirect/callback URI: https://YOUR_KMS_URL/ui/callback
  • Application Login URI: https://YOUR_KMS_URL/ui/login
  • Logout URI: https://YOUR_KMS_URL/ui/login

2. Using Command-Line Arguments

--ui-oidc-client-id "your_client_id" \
--ui-oidc-client-secret "your_client_secret" \
--ui-oidc-issuer-url "https://your_oidc_issuer_url" \
--ui-oidc-logout-url "https://your_oidc_logout_url"

Note

The UI login flow always uses PKCE (code_challenge_method=S256). The client secret is optional — see the PKCE Authentication guide for per-provider configuration instructions.

Note

API Token authentication is not supported by the UI.


Configuring Certificate Authentication (mTLS)

When the KMS server is started with mutual TLS and a client CA (clients_ca_cert_file), the UI switches to certificate-based login. See Enabling TLS for server-side configuration.

The browser handles the mTLS handshake transparently: Chrome presents the client certificate during the TLS handshake and the KMS server extracts the Subject CN of the certificate and uses it as the KMS username. For example, a certificate with CN=alice identifies the user as alice for all access-control decisions.

Users who have no valid client certificate installed will see a "CERT identity verification failed" error on the login page.

Step 1 — Obtain your client certificate

The client certificate must be signed by the CA configured in clients_ca_cert_file on the server. Obtain it from your PKI or administrator. If you are using the KMS setup wizard, it generates a client.crt + client.key pair automatically.

Step 2 — Convert PEM to PKCS#12 (if needed)

Browsers import certificates as PKCS#12 (.p12) bundles. If you have separate PEM files, convert them:

openssl pkcs12 -export \
  -certpbe PBE-SHA1-3DES -keypbe PBE-SHA1-3DES -macalg sha1 \
  -in client.crt \
  -inkey client.key \
  -certfile ca.crt \
  -out client.p12 \
  -passout pass:your-password

macOS / Chrome compatibility

OpenSSL 3.x generates PKCS#12 files with AES-256 ciphers by default. macOS Security.framework (which Chrome on macOS uses) cannot import that format. The -certpbe PBE-SHA1-3DES -keypbe PBE-SHA1-3DES -macalg sha1 flags select the older 3DES format that macOS accepts.

If you already have a .p12 file generated by the KMS wizard (client.p12), skip this step — it is already in the correct format.

Step 3 — Install the certificate in the browser

  1. Open chrome://settings/certificates (or Settings → Privacy and security → Security → Manage certificates).
  2. Go to the Your certificates tab.
  3. Click Import, select client.p12, and enter the password when prompted.

Chrome and Safari on macOS read client certificates from the system keychain — do not use Chrome's built-in certificate manager.

  1. Double-click client.p12 in Finder, or run:
    security import client.p12 -k ~/Library/Keychains/login.keychain-db
    
  2. Open Keychain Access, find your certificate under My Certificates.
  3. Double-click the certificate → expand Trust → set "When using this certificate" to Always Trust (or ensure the signing CA is already trusted).

Chrome and Edge on Windows read client certificates from the Windows Certificate Store.

  1. Double-click client.p12 → follow the Import Wizard → store in Personal.
  2. If the signing CA is not already trusted, also import ca.crt into Trusted Root Certification Authorities using certmgr.msc.

Tip

After installing or changing a client certificate, fully close and relaunch the browser. Browsers cache TLS session state and will not renegotiate with the new certificate until all windows are closed.

Certificate CN = KMS username

The Common Name (CN) of the client certificate becomes the KMS username. Make sure the CN matches the identity you want to use for access control. A wildcard CN (*) is explicitly rejected by the server.


UI Branding (post-install, no rebuild)

The KMS UI can be white‑labeled at runtime by editing the installed UI static files (no UI rebuild required).

What to change after install

The UI is served under /ui, from the UI dist directory on disk.

  • Branding file: /usr/local/cosmian/ui/dist/branding.json
  • Theme assets: /usr/local/cosmian/ui/dist/themes/<theme>/...

All asset URLs in branding.json are URLs under /ui/... (for example: /ui/themes/example/example-logo-light.svg).

Post-install customization (deb/rpm)

When the KMS server is installed from Linux packages, branding can be customized after install by editing files directly in the UI dist folder (no UI rebuild required).

  • Default UI dist path (Linux): /usr/local/cosmian/ui/dist/
  • Branding file: /usr/local/cosmian/ui/dist/branding.json
  • Theme assets: /usr/local/cosmian/ui/dist/themes/<theme>/...

The packaging scripts preserve these files across upgrades by backing them up and restoring them (upgrade-safe customization).

In this repo, theme assets are stored under ui/public/themes/ and the build step publishes them into ui/dist/themes/.

Switch to the example theme

Edit /usr/local/cosmian/ui/dist/branding.json and point the URLs to the example theme assets:

{
  "title": "Example",
  "faviconUrl": "/ui/themes/example/favicon-32x32.png",
  "logoAlt": "Example",
  "logoLightUrl": "/ui/themes/example/example-logo-light.svg",
  "logoDarkUrl": "/ui/themes/example/example-logo-dark.svg",
  "backgroundImageUrl": "/ui/themes/example/example-login-bg.jpg"
}

Restart the service (or clear browser cache) to see changes.

Start from the blank theme

The blank theme is a minimal starter template with placeholder assets — copy it as your starting point for a new white-label deployment:

# Copy the blank theme to the installed UI dist directory
cp -r /usr/local/cosmian/ui/dist/themes/blank /usr/local/cosmian/ui/dist/themes/acme

# Customise the copy
nano /usr/local/cosmian/ui/dist/themes/acme/branding.json
# Replace logo-light.svg, logo-dark.svg, favicon.svg, login-bg.svg with your assets

# Activate it by pointing branding.json to the new theme
cp /usr/local/cosmian/ui/dist/themes/acme/branding.json /usr/local/cosmian/ui/dist/branding.json

The blank theme ships the following placeholder files:

FilePurpose
branding.jsonFull example with every supported field
logo-light.svgLogo for light-mode header (dark text)
logo-dark.svgLogo for dark-mode header (white text)
favicon.svgBrowser tab icon
login-bg.svgLogin page background (dark blue gradient)

branding.json schema

Example:

{
  "title": "Example",
  "faviconUrl": "/ui/themes/example/favicon-32x32.png",
  "logoAlt": "Example Key Management",
  "logoLightUrl": "/ui/themes/example/example-logo-light.svg",
  "logoDarkUrl": "/ui/themes/example/example-logo-dark.svg",
  "loginTitle": "Example",
  "loginSubtitle": "Welcome",
  "backgroundImageUrl": "/ui/themes/example/example-login-bg.jpg",
  "loginCardColor": "rgba(126,34,206,0.3)",
  "menuTheme": "dark",
  "tokens": {
    "light": { "colorPrimary": "#0057ff", "colorText": "#111827" },
    "dark": { "colorPrimary": "#7c3aed", "colorText": "#e5e7eb" }
  }
}

Keys

  • title: Sets document.title.
  • faviconUrl: URL for the page favicon.
  • logoAlt: Used as the header label and <img alt>.
  • logoLightUrl / logoDarkUrl: Header logo depending on light/dark mode.
  • loginTitle / loginSubtitle: Login page texts.
  • backgroundImageUrl: Login background image.
  • loginCardColor: CSS background color of the card overlaid on the login background. Accepts any CSS color value (e.g. "rgba(0,87,255,0.3)", "#0057ff4d"). Defaults to "rgba(126,34,206,0.3)" (semi-transparent purple).
  • menuTheme: "light" or "dark".
  • tokens.light / tokens.dark: Ant Design theme token overrides.

Behavior and fallbacks

  • If /ui/branding.json is missing or invalid, the UI uses built-in defaults.
  • If a key is missing, the UI falls back to the default value.

Notes

  • The UI fetches branding.json on startup and applies title/favicon before React renders.
  • branding.json is fetched with cache busting by default.

About branding.ts

The UI branding loader/helpers live in ui/src/branding.ts. It is a plain TypeScript module (no JSX), so it uses the .ts extension rather than .tsx.

Upgrade behavior

Linux packages are designed so you can customize UI files in place.

  • On upgrade: package maintainer scripts back up UI dist content to /var/lib/cosmian/ui/ and restore it after installing the new version.
  • If you want to reset to package defaults: remove your customized branding.json and/or themes/<theme>/ overrides, then reinstall or restore from a clean package.

Custom OpenSSL Build

The Eviden KMS dynamic package links against OpenSSL at runtime. You can redirect the service to a custom OpenSSL build — for example, a vendor-supplied or hardware-accelerated variant — without touching any Cosmian-owned files, by overriding the systemd service environment.

Applies to dynamic builds only

Static builds embed OpenSSL at compile time and cannot be redirected at runtime.

How it works

The KMS systemd unit (/lib/systemd/system/cosmian_kms.service) sets three environment variables that together control which OpenSSL is loaded:

VariableDefault value (package install)Purpose
LD_LIBRARY_PATH/usr/local/cosmian/libDirectory searched first for libssl.so.3 / libcrypto.so.3
OPENSSL_CONF/usr/local/cosmian/lib/ssl/openssl.cnfOpenSSL configuration file
OPENSSL_MODULES/usr/local/cosmian/lib/ossl-modulesDirectory containing provider modules (e.g. fips.so, legacy.so)

Pointing all three at your custom OpenSSL installation is sufficient to make the service use it exclusively.

On startup the server logs the active OpenSSL version:

INFO cosmian_kms: [run] OpenSSL version: OpenSSL 3.X.Y DD Mon YYYY,
     in OPENSSLDIR: "/usr/local/ssl", number: 30X000Y0

Overriding via systemd drop-in

1. Install your custom OpenSSL build

Place the libraries anywhere that is accessible to the cosmian_kms service (i.e. not under /home/ — the systemd unit sets ProtectHome=yes). Recommended locations: /opt/ or /usr/local/lib/.

Vendor CC/FIPS packages commonly ship with a nested usr/local/ tree inside the archive. Copy the whole folder as-is:

sudo cp -r /path/to/openssl-<version>-linux-x86_64 /usr/local/lib/

Resulting layout:

/usr/local/lib/openssl-<version>-linux-x86_64/
  usr/
    local/
      lib64/
        libssl.so.3
        libcrypto.so.3
        ossl-modules/
          fips.so
          legacy.so
      ssl/
        openssl.cnf
        fipsmodule.cnf

In the override.conf you then reference the nested usr/local/lib64/ path:

LD_LIBRARY_PATH=/usr/local/lib/openssl-<version>-linux-x86_64/usr/local/lib64
OPENSSL_MODULES=/usr/local/lib/openssl-<version>-linux-x86_64/usr/local/lib64/ossl-modules

If the archive extracts directly to lib64/ at the top level (no usr/local/ subdirectory), use a simpler install path such as /opt/openssl-custom/ and reference lib64/ directly.

2. Create the systemd drop-in override

sudo systemctl edit cosmian_kms

This opens $EDITOR with a drop-in template saved at /etc/systemd/system/cosmian_kms.service.d/override.conf. Add the three environment variables, replacing the paths with those of your installation:

[Service]
Environment="LD_LIBRARY_PATH=/opt/openssl-custom/lib"
Environment="OPENSSL_CONF=/opt/openssl-custom/ssl/openssl.cnf"
Environment="OPENSSL_MODULES=/opt/openssl-custom/lib/ossl-modules"
# Required when OIDC/JWKS auth is enabled: the default unit blocks outbound
# traffic to public IPs.  See "Network firewall" below.
IPAddressAllow=any

3. Reload and restart

sudo systemctl daemon-reload
sudo systemctl restart cosmian_kms

4. Verify

sudo journalctl -u cosmian_kms --no-pager | grep "OpenSSL version"

The log line should reflect your custom build string.

Network firewall

Affects OIDC / JWKS authentication

The default systemd unit ships with IPAddressDeny=any and only allows traffic to localhost and RFC-1918 private ranges:

IPAddressDeny=any
IPAddressAllow=localhost
IPAddressAllow=10.0.0.0/8
IPAddressAllow=172.16.0.0/12
IPAddressAllow=192.168.0.0/16

When jwt_auth_provider is configured, the KMS fetches JWKS keys from a public OIDC endpoint (e.g. login.microsoftonline.com) at startup. Because the endpoint's IP is not in the allowed list, the BPF firewall silently drops the outbound SYN packets — the service hangs indefinitely at the Refreshing JWKS log line and never starts listening.

Fix: allow outbound HTTPS in the drop-in

Add IPAddressAllow=any to the override.conf drop-in (taking precedence over the IPAddressDeny=any in the unit):

[Service]
# … OpenSSL entries …
IPAddressAllow=any

If a stricter policy is preferred, add only the OIDC provider's IP ranges instead of any. For Microsoft Entra ID / Azure AD (as of 2026), the range is documented in the Microsoft 365 IP address list.

FIPS provider setup for custom builds

If the custom OpenSSL was compiled with OPENSSLDIR=/usr/local/ssl (check via openssl version -a), the FIPS provider looks for its configuration at that path regardless of OPENSSL_CONF. Create the path and run fipsinstall before starting the service:

sudo mkdir -p /usr/local/ssl
sudo env LD_LIBRARY_PATH=/opt/openssl-custom/lib64 \
  /opt/openssl-custom/bin/openssl fipsinstall \
  -module /opt/openssl-custom/lib64/ossl-modules/fips.so \
  -out /usr/local/ssl/fipsmodule.cnf

# Create a minimal openssl.cnf at the compiled-in OPENSSLDIR
sudo tee /usr/local/ssl/openssl.cnf > /dev/null <<'EOF'
openssl_conf = openssl_init
config_diagnostics = 1
.include /usr/local/ssl/fipsmodule.cnf

[openssl_init]
providers = provider_sect

[provider_sect]
fips = fips_sect
default = default_sect

[default_sect]
activate = 1
EOF

Verify FIPS loads before restarting the service:

sudo env LD_LIBRARY_PATH=/opt/openssl-custom/lib64 \
  OPENSSL_MODULES=/opt/openssl-custom/lib64/ossl-modules \
  /opt/openssl-custom/bin/openssl list -providers
# Expected: both "default" and "fips" providers show status: active

Then reload and restart:

sudo systemctl daemon-reload
sudo systemctl restart cosmian_kms

ProtectHome restriction

The systemd unit sets ProtectHome=yes. Custom OpenSSL builds placed under /home/… are inaccessible to the service. Install custom builds under /opt/ or /usr/local/ so the service can load them.

Rollback

Remove the drop-in override file and restart the service to revert to the Cosmian-bundled OpenSSL:

sudo rm -f /etc/systemd/system/cosmian_kms.service.d/override.conf
sudo systemctl daemon-reload
sudo systemctl restart cosmian_kms

Notes

  • ABI compatibility: libssl.so.3 / libcrypto.so.3 must be ABI-compatible with OpenSSL 3.x (major version 3).
  • Provider modules: legacy.so (or any other provider) must be compiled against the same OpenSSL version as the libraries you are installing.
  • Package upgrades: a deb/rpm upgrade does not touch the drop-in override file, so the custom OpenSSL remains active after an upgrade.

Secret backends

Store sensitive configuration values — database passwords, API keys, tokens — in an external secret manager instead of plain text in the KMS TOML config file.

How it works

sequenceDiagram
    participant Operator
    participant KMS Server
    participant Secret Backend

    Operator->>KMS Server: cosmian_kms --backend vault -c kms.toml
    KMS Server->>KMS Server: Parse TOML, find secret:// URIs
    KMS Server->>Secret Backend: Resolve each secret:// URI
    Secret Backend-->>KMS Server: Return plaintext value
    KMS Server->>KMS Server: Replace URIs with values, apply config
    KMS Server->>Operator: Server ready
  1. In your kms.toml, replace any sensitive value with a secret://... URI.
  2. Start the KMS with --backend <backend> (or set KMS_SECRET_BACKEND env var).
  3. At startup the KMS resolves every secret:// URI once, then applies the config normally.

The config file never contains actual secrets — only references.


Supported backends

Backend--backendURI formatRequired env vars
HashiCorp Vaultvaultsecret://<mount>/<path>[#<field>]VAULT_ADDR, VAULT_TOKEN
AWS SSM Parameter Storeaws-ssmsecret://<region>/<parameter-name>AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY
Azure Key Vaultazure-kvsecret://<vault>/secrets/<name>[/<version>]AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET
Cosmian KMScosmian-kmssecret://<host>[:<port>]/<object-id>(optional) COSMIAN_KMS_SECRET_TOKEN

HashiCorp Vault

Uses the KV-v2 secrets engine. The URI fragment #field selects a specific key in the secret's JSON data; defaults to value if omitted.

1. Store a secret in Vault

vault kv put secret/kms/db password=my-db-password

2. Reference it in kms.toml

[db]
database_type = "postgresql"
postgres_url = "postgresql://kms:secret://secret/kms/db#password@db.internal:5432/kms"

Or for a standalone field:

[db]
database_type = "postgresql"
postgres_url = "secret://secret/kms/postgres-url#value"

3. Start the KMS

export VAULT_ADDR=http://vault.internal:8200
export VAULT_TOKEN=s.xxxxxxxx
cosmian_kms --backend vault -c /etc/cosmian/kms.toml

AWS SSM Parameter Store

Fetches a SecureString parameter. The leading / is prepended automatically — do not include it in the URI.

1. Store a secret in SSM

aws ssm put-parameter \
  --name /kms/db-password \
  --value "my-db-password" \
  --type SecureString \
  --region eu-west-1

2. Reference it in kms.toml

[db]
postgres_url = "secret://eu-west-1/kms/db-password"

3. Start the KMS

export AWS_ACCESS_KEY_ID=AKIA...
export AWS_SECRET_ACCESS_KEY=...
# Optional: export AWS_SESSION_TOKEN=...
cosmian_kms --backend aws-ssm -c /etc/cosmian/kms.toml

Azure Key Vault

Uses service-principal client credentials (OAuth2) to authenticate to Azure AD, then fetches the secret from the Key Vault REST API (v7.4).

1. Store a secret

az keyvault secret set \
  --vault-name myvault \
  --name db-password \
  --value "my-db-password"

2. Reference it in kms.toml

[db]
postgres_url = "secret://myvault/secrets/db-password"

To pin a specific version:

postgres_url = "secret://myvault/secrets/db-password/abc123def456"

3. Start the KMS

export AZURE_TENANT_ID=...
export AZURE_CLIENT_ID=...
export AZURE_CLIENT_SECRET=...
cosmian_kms --backend azure-kv -c /etc/cosmian/kms.toml

Cosmian KMS (self-referencing)

Use another Cosmian KMS instance as the secret store. The secret must be stored as a SecretData or OpaqueObject whose raw bytes are the UTF-8 value.

This is useful for hierarchical deployments where a "vault KMS" holds credentials used by downstream KMS instances.

1. Import a secret into the vault KMS

ckms secret-data create --value "my-db-password" --type password
# → UniqueIdentifier: 550e8400-e29b-41d4-a716-446655440000

2. Reference it in kms.toml

[db]
postgres_url = "secret://vault-kms.internal:9998/550e8400-e29b-41d4-a716-446655440000"

For localhost (uses http automatically):

postgres_url = "secret://localhost:9998/550e8400-e29b-41d4-a716-446655440000"

3. Start the KMS

# Optional token if the vault KMS requires authentication:
export COSMIAN_KMS_SECRET_TOKEN=eyJhbGciOi...
# Accept self-signed certs (dev only):
export COSMIAN_KMS_INSECURE_CERTS=true
cosmian_kms --backend cosmian-kms -c /etc/cosmian/kms.toml

Security considerations

ConcernMitigation
Secret exposure in config filesOnly secret:// references are stored on disk — never plaintext values.
Secret lifetimeURIs are resolved once at startup. Resolved values live only in process memory.
Backend credentialsAlways inject via environment variables, never in the TOML file.
Network securityUse TLS for all backend communication. For Vault and Cosmian KMS, ensure VAULT_ADDR uses https:// in production.
RotationRestart the KMS to pick up rotated secrets. There is no hot-reload.

Do not mix backends

Only one --backend can be active at a time. All secret:// URIs in the config must be resolvable by the selected backend.

FIPS 140-3

The Federal Information Processing Standard (FIPS) Publication 140-3, Security Requirements for Cryptographic Modules, is a US government standard that specifies the security requirements for cryptographic modules protecting sensitive information.

When compiled in FIPS mode, the Eviden KMS uses only cryptographic primitives that are compliant with the standards of the National Institute of Standards and Technology (NIST) and uses implementations of an NIST FIPS 140-3 compliant cryptographic module: the OpenSSL FIPS provider.

The OpenSSL FIPS provider is certified under

  • #4779 when used un Ubuntu 22.04
  • #4776 when used on Red Hat Enterprise Linux 9 (RHEL 9)

Eviden produces pre-built Debian based Linux packages and docker containers of the KMS (on gcr.io).

By default, the Eviden KMS is built with FIPS compliance enabled. Alternatively, you can enable all cryptographic mechanisms by building from source, using the --features non-fips flag:

cargo build --release --features non-fips

Cryptographic Algorithms

The Eviden server supports a large, and growing, list of cryptographic algorithms. This page lists the supported algorithms, their details and their reference in various standards. FIPS compliant algorithms are also listed with the corresponding NIST standard.

Keys and certificates for all the listed algorithms can be generated, imported, exported, wrapped, unwrapped... using the Eviden KMS server API or KMS CLI

Should you require a specific algorithm or standard to be supported, please directly open a ticket or pull request on the Github repository.

Key-wrapping schemes

The Eviden server supports key-wrapping via the Import(unwrapping) and Export (wrapping) kmip operations. The (un)wrapping key identifier may be that of a key or a certificate. In the latter case, the public key (or the associated private key for unwrapping, if any) will be retrieved and used.

The supported key-wrapping algorithms are:

AlgorithmWrap Key TypeFIPS modeDescription
AES-KWSymmetric key wrappingNIST SP 800-38FSymmetric key-wrapping without padding as defined in RFC3394.
AES-KWPSymmetric key wrappingNIST SP 800-38FSymmetric key-wrapping with padding as defined in RFC5649.
CKM_RSA_PKCSRSA PKCS#1 v1.5Not anymoreRSA WITH PKCS#1 v1.5 padding - removed by NIST approved algorithms for key wrapping in FIPS 140-3
CKM_RSA_PKCS_OAEPRSA key wrappingNIST 800-56B rev. 2RSA OAEP with NIST approved hashing functions for RSA key size 2048, 3072 or 4096 bits.
CKM_RSA_AES_KEY_WRAPRSA-AES hybrid key wrappingNIST SP 800-38FRSA OAEP with NIST approved hashing functions and AES-KWP for RSA key size 2048, 3072 or 4096 bits.
Salsa Sealed BoxX25519, Ed25519 and Salsa20 Poly1305NoECIES compatible with libsodium Sealed Boxes.
ECIESP-256, P-384, P-521NoECIES with a NIST curve: P-256 uses SHAKE128 + AES-128-GCM; P-384 and P-521 use SHAKE256 + AES-256-GCM.

Any encryption scheme below can be used for key-wrapping as well.

Encryption schemes

Encryption is supported via the Encrypt and Decrypt kmip operations. For bulk operations (i.e. encrypting/decrypting multiple data with the same key), please refer to KMIP Messages that allow combining multiple operations in a single request.

Encryption can be performed using a key or a certificate. Decryption can be performed using a key.

The supported encryption algorithms are:

AlgorithmEncryption Key TypeFIPS modeDescription
CovercryptCovercryptNoA fast post-quantum attribute based scheme: Covercrypt.
AES GCMSymmetric authenticated encryption with additional dataNIST FIPS 197The NIST standardized symmetric encryption in FIPS 197.
AES XTSSymmetric, not authenticatedNIST SP 800-38EUsed in disk encryption. Requires 2 keys (e.g. a double-sized key)
AES GCM-SIVSymmetric, authenticated, synthetic IVNoUsed for deterministic encryption and encryption of very large data sets.
FPE FF1Symmetric, format-preserving encryptionNoNIST SP 800-38G FF1 format-preserving encryption for text, integers, and floating-point values through KMIP Encrypt/Decrypt.
ChaCha20-Poly1305Symmetric authenticated encryption with additional dataNoA popular symmetric encryption algorithm standardized in RFC-8439
CKM_RSA_PKCSRSA PKCS#1 v1.5Not anymoreRSA WITH PKCS#1 v1.5 padding - removed by NIST approved algorithms for encryption in FIPS 140-3
CKM_RSA_PKCS_OAEPRSA encryption with OAEP paddingNIST 800-56B rev. 2RSA OAEP with NIST approved hashing functions for RSA key size 2048, 3072 or 4096 bits.
Salsa Sealed BoxX25519, Ed25519 and Salsa20 Poly1305NoECIES compatible with libsodium Sealed Boxes.
ECIESP-256, P-384, P-521NoECIES with a NIST curve: P-256 uses SHAKE128 + AES-128-GCM; P-384 and P-521 use SHAKE256 + AES-256-GCM.

Algorithms Details

Covercrypt

Covercrypt is a new post-quantum cryptographic algorithm, being standardized at ETSI that allows creating ciphertexts for a set of attributes and issuing user keys with access policies over these attributes. User keys are traceable with a unique fingerprint.

AES GCM

AES is described in NIST FIPS 197. In Eviden KMS it is used as a data encryption mechanism (DEM) with the Galois Counter Mode of operation (GCM) with a 96 bits nonce, a 128 bits tag with and key sizes of 128, 192 or 256 bits.

FPE FF1

Cosmian KMS supports NIST SP 800-38G FF1 format-preserving encryption in non-FIPS mode through the KMIP Encrypt and Decrypt operations when the Cryptographic Algorithm is set to FPE_FF1.

The server supports three data shapes:

  • text values, using the alphabet name or a custom alphabet in AuthenticatedEncryptionAdditionalData
  • integer values, using JSON metadata such as {"type":"integer","alphabet":"numeric"}
  • floating-point values, using JSON metadata such as {"type":"float"}

The tweak is supplied through IVCounterNonce. The CLI exposes the same flow via ckms fpe commands: ckms fpe keys create, ckms fpe encrypt, and ckms fpe decrypt.

ChaCha20-Poly1305

ChaCha20-Poly1305 is a symmetric authenticated encryption algorithm that is described in RFC-8439. The algorithm is not standardized by NIST but is a popular secure alternative to AES-GCM and is used in the same way, in particular by Google.

AES-KWP

Allows to symmetrically wrap keys using RFC5649 which is also standardized as PKCS#11 CKM_AES_KEY_WRAP_PAD and described in NIST SP 800-38F.

CKM_RSA_PKCS

A.k.a PKCS #1 v1.5 RSA as specified in PKCS#11 v2.40.

The algorithm is not recommended by NIST for encryption or key wrapping in FIPS 140-3. It is still supported for signature operations.

The maximum plaintext length is k-11 where k is the length in octets of the RSA modulus The output length is the same as the modulus length.

CKM_RSA_PKCS_OAEP

A.k.a PKCS #1 RSA OAEP as specified in PKCS#11 v2.40. This scheme is part of the NIST 800-56B rev. 2 recommendation available at section 7.2.2.

The maximum plaintext length is k-2-2*hLen where

  • k is the length in octets of the RSA modulus
  • hLen is the length in octets of the hash function output

The output length is the same as the modulus length.

The default hash function is SHA-256 but any NIST approved hash functions can be used for the OAEP scheme as listed in

Set the corresponding name of the hash function in the Cryptographic Parameters when performing a KMIP operation.

To request this algorithm using the KMIP Encrypt/Decrypt operation, or key-wrapping as part of the Import/Export operations, specify the id/tags of an RSA key and set the Cryptographic Algorithm to RSA.

CKM_RSA_AES_KEY_WRAP

A PKCS#11 key wrapping mechanism that is supported by most HSMs.

The scheme asymmetrically wrap keys as described here and allows wrapping keys of any size using using a hybrid RSA/AES scheme.

Since old similar wrapping methods based on RSA used naive RSA encryption and could present some flaws, it aims at a generally more secure method to wrap keys:

  • Receive data of the form c|wk where | is the concatenation operator. Distinguish c and wk, respectively the encrypted kek and the wrapped key. First decrypt the key-encryption-key kek using RSA-OAEP, then proceed to unwrap the key by decrypting m = dec(wk, kek) using AES-KWP as specified in RFC5649.

The algorithm can be used with any NIST approved hash function described above; set the corresponding value in the Cryptographic Parameters when performing a KMIP operation.

To request this algorithm using key-wrapping as part of the Import/Export operations, specify the id/tags of an RSA key as the wrapping key and set the Cryptographic Algorithm to RSA and the Padding Method to None in the Key Wrapping Specification.

Why PaddingMethod::None selects CKM_RSA_AES_KEY_WRAP

In the Eviden KMIP routing logic, the padding method discriminates between the three RSA wrapping schemes:

Padding MethodAlgorithm selected
NoneCKM_RSA_AES_KEY_WRAP (this scheme)
OAEPCKM_RSA_PKCS_OAEP
PKCS1v15CKM_RSA_PKCS (non-FIPS only)

PaddingMethod::None does not mean unpadded RSA. It is the signal to route toward the hybrid RSA/AES wrapping scheme. The RSA OAEP padding is still applied internally — the field merely selects which wrapping scheme to invoke.

The following JSON TTLV snippet shows the CryptographicParameters to embed in a KeyWrappingSpecification on an Export or Import request:

{
  "tag": "CryptographicParameters",
  "type": "Structure",
  "value": [
    {
      "tag": "CryptographicAlgorithm",
      "type": "Enumeration",
      "value": "RSA"
    },
    {
      "tag": "PaddingMethod",
      "type": "Enumeration",
      "value": "None"
    },
    {
      "tag": "HashingAlgorithm",
      "type": "Enumeration",
      "value": "SHA256"
    }
  ]
}

This algorithm is compatible with the one used in Google KMS.

RSA OAEP AES 128 GCM

CKM_RSA_AES_KEY_WRAP can only be used for key wrapping and not for encryption. This scheme adds authentication by using AES 128 in Galois Counter Mode (GCM). Combined with RSA OAEP to encapsulate the AES key, this scheme is compatible with NIST SP 800-38F.

To request this algorithm using the KMIP Encrypt/Decrypt operation, specify the id/tags of an RSA key and set the Cryptographic Algorithm to AES.

Salsa sealed box

An ECIES scheme that uses X25519 and XSalsa20-Poly1305 and is compatible with libsodium Sealed Boxes.

An Ed25519 key can be used; it will be automatically converted to X25519 first.

Ecies with NIST Curves

Although there is no specific FIPS standard for hybrid encryption, the ECIES encryption scheme is based on FIPS compliant cryptographic primitives only and uses the same algorithm as the Salsa Sealed Boxes. It supports the NIST P curves starting at P-256, and uses AES-128-GCM and SHAKE128 for curves with security strength $s \leq 128$ bits:

  • P-256

and AES-256-GCM and SHAKE256 for curves with security strength $s > 128$ bits:

  • P-384
  • P-521

PQC Hybridized KEM

The Eviden KMS supports Post-Quantum Cryptography (PQC) hybridized Key Encapsulation Mechanisms (KEM) via the cosmian_cover_crypt crate. This crate provides a configurable KEM framework that can operate in pure classical, pure post-quantum, or hybrid mode by combining a pre-quantum KEM with a post-quantum KEM through a KEM combiner (using SHA-256).

The available pre-quantum KEMs are:

  • P-256 — ECDH-based KEM on the NIST P-256 curve (via OpenSSL).
  • Curve25519 — ECDH-based KEM on Curve25519.

The available post-quantum KEMs are:

  • ML-KEM-512 — NIST FIPS 203 lattice-based KEM at security level 1.
  • ML-KEM-768 — NIST FIPS 203 lattice-based KEM at security level 3.

The hybridized combinations pair one classical and one post-quantum KEM:

Hybrid variantPre-quantumPost-quantumApproximate security
ML-KEM-512 + P-256P-256ML-KEM-512~128 bits
ML-KEM-768 + P-256P-256ML-KEM-768~192 bits
ML-KEM-512 + Curve25519Curve25519ML-KEM-512~128 bits
ML-KEM-768 + Curve25519Curve25519ML-KEM-768~192 bits

The hybrid approach ensures that security is maintained even if one of the two underlying KEMs is broken: the combined shared secret remains secure as long as at least one component KEM is secure.

Key generation, encapsulation and decapsulation are exposed via the KMS CLI under ckms pqc (non-FIPS builds only) using algorithm names ml-kem-512-p256, ml-kem-768-p256, ml-kem-512-curve25519, and ml-kem-768-curve25519.

OpenSSL Native PQC Algorithms

Starting with OpenSSL 3.5, the Eviden KMS also provides direct support for OpenSSL-native Post-Quantum Cryptography algorithms standardized by NIST in August 2024. These algorithms are available in non-FIPS builds only.

ML-KEM — Key Encapsulation Mechanism (NIST FIPS 203)

ML-KEM (Module-Lattice-Based Key-Encapsulation Mechanism) is a lattice-based KEM standardized in NIST FIPS 203. It supersedes the CRYSTALS-Kyber candidate from the NIST PQC competition.

VariantSecurity levelApproximate security
ML-KEM-512NIST L1~128 bits
ML-KEM-768NIST L3~192 bits
ML-KEM-1024NIST L5~256 bits

Key generation, encapsulation, and decapsulation are exposed via the KMS CLI under ckms pqc ml-kem.

OpenSSL Hybrid KEM

OpenSSL also provides hybrid combinations that pair a classical elliptic-curve Diffie-Hellman key exchange with ML-KEM, following the IETF draft for hybrid key exchange in TLS 1.3.

VariantClassical componentPost-quantum componentApproximate security
X25519MLKEM768X25519ML-KEM-768~192 bits
X448MLKEM1024X448ML-KEM-1024~256 bits

Key generation, encapsulation, and decapsulation for these hybrid variants are also exposed via ckms pqc ml-kem.

ML-DSA — Lattice-Based Digital Signature (NIST FIPS 204)

ML-DSA (Module-Lattice-Based Digital Signature Algorithm) is a lattice-based signature scheme standardized in NIST FIPS 204. It supersedes the CRYSTALS-Dilithium candidate.

VariantSecurity levelApproximate security
ML-DSA-44NIST L2~128 bits
ML-DSA-65NIST L3~192 bits
ML-DSA-87NIST L5~256 bits

Key generation, signing, and verification are exposed via the KMS CLI under ckms pqc ml-dsa.

SLH-DSA — Hash-Based Digital Signature (NIST FIPS 205)

SLH-DSA (Stateless Hash-Based Digital Signature Algorithm) is a hash-based signature scheme standardized in NIST FIPS 205. It supersedes the SPHINCS+ candidate and is the only standardized PQC signature scheme not based on lattices.

Variants follow the naming convention SLH-DSA-<hash>-<level><speed> where:

  • <hash> is either SHA2 or SHAKE
  • <level> is 128, 192, or 256 (security level in bits)
  • <speed> is s (small signature, slower signing) or f (fast signing, larger signature)
VariantHashSecuritySignature sizeSigning speed
SLH-DSA-SHA2-128sSHA2~128 bitSmallSlow
SLH-DSA-SHA2-128fSHA2~128 bitLargeFast
SLH-DSA-SHA2-192sSHA2~192 bitSmallSlow
SLH-DSA-SHA2-192fSHA2~192 bitLargeFast
SLH-DSA-SHA2-256sSHA2~256 bitSmallSlow
SLH-DSA-SHA2-256fSHA2~256 bitLargeFast
SLH-DSA-SHAKE-128sSHAKE~128 bitSmallSlow
SLH-DSA-SHAKE-128fSHAKE~128 bitLargeFast
SLH-DSA-SHAKE-192sSHAKE~192 bitSmallSlow
SLH-DSA-SHAKE-192fSHAKE~192 bitLargeFast
SLH-DSA-SHAKE-256sSHAKE~256 bitSmallSlow
SLH-DSA-SHAKE-256fSHAKE~256 bitLargeFast

Key generation, signing, and verification are exposed via the KMS CLI under ckms pqc slh-dsa.

Signature schemes

Digital signature signing and verification are supported via both the Certify operation (for certificate signing) and the Sign operation (for direct data signing).

The Certify operation is used to create a certificate either by signing a certificate request, or building it from an existing public key.

The Sign operation is used to perform digital signature operations on provided data using a private key. This operation supports:

  • Signing raw data (the operation will hash the data using the specified or default hash algorithm)
  • Signing pre-hashed data (digested data) for cases where the client has already computed the hash
AlgorithmSignature Key TypeFIPS modeDescription
RSASSA-PSSRSA-2048, RSA-3072, RSA-4096YesRSA signatures using PKCS#1 PSS padding with approved hash functions (SHA-256, SHA-384, SHA-512).
ECDSAP-256, P-384, P-521Restricted to curves P-256, P-384 and P-521.See FIPS-186.5 and NIST.SP.800-186 - Section 3.1.2 table 2.
ECDSAsecp256k1NoECDSA on the secp256k1 curve (RFC 6979 deterministic). Non-FIPS only.
EdDSAEd25519, Ed448YesSee FIPS-186.5.
ML-DSAML-DSA-44, ML-DSA-65, ML-DSA-87No (non-FIPS only)Lattice-based signatures. NIST FIPS 204. Supersedes CRYSTALS-Dilithium.
SLH-DSASLH-DSA-SHA2/SHAKE-128/192/256-s/f (12 variants)No (non-FIPS only)Hash-based signatures. NIST FIPS 205. Supersedes SPHINCS+.

Digital Signature Operations

  • RSASSA-PSS performs digital signatures using RSA keys with PSS padding and NIST-approved hash functions.
  • ECDSA performs digital signatures on elliptic curves P-256, P-384, P-521 (FIPS), and secp256k1 (non-FIPS only). Note: X25519 and X448 are Diffie-Hellman exchange curves and are not used for ECDSA signatures.
  • EdDSA performs digital signatures on Edwards curves Ed25519 and Ed448.

Key derivation (KMIP DeriveKey)

The KMS exposes the KMIP DeriveKey operation, which derives a new symmetric key or secret data from an existing key material. Two methods are supported:

MethodHash algorithmsFIPS modeNotes
PBKDF2SHA-{1,224,256,384,512}YesDefault: SHA-256, 600 000 iterations (OWASP). Salt is mandatory.
HKDFSHA-{1,224,256,384,512}YesDefault: SHA-256. Salt is optional; info (context) is optional.

Both derivation methods are implemented via OpenSSL.

Password-based key derivation

The randomness of cryptographic keys is essential for the security of cryptographic applications. Sometimes, passwords may be the only input required from the users who are eligible to access the data. Due to the low entropy and possibly poor randomness of those passwords, they are not suitable to be used directly as cryptographic keys. The KMS addresses this problem by providing methods to derive a password into a secure cryptographic key.

In normal mode, passwords are derived using Argon2 hash algorithm with a random 128-bit salt. Argon2 has the property of being computationally intensive making it significantly harder to crack by brute force only.

In FIPS mode, passwords are derived using FIPS compliant PBKDF2_HMAC with SHA512 and recommended 210,000 iterations by OWASP which follows FIPS recommendations as well. An additional random 128-bit salt is used.

Hash functions

The Eviden server supports the following hashing algorithms:

FIPS 180-4 (SHA-2 family):

  • SHA-224
  • SHA-256
  • SHA-384
  • SHA-512

NIST FIPS 202 (SHA-3 family):

  • SHA3-224
  • SHA3-256
  • SHA3-384
  • SHA3-512

References

  • NIST.FIPS.140-3, Implementation Guidance for FIPS 140-3 and the Cryptographic Module Validation Program, August 1, 2023

    • General information and pointers to other NIST documents concerning the FIPS standard.
  • NIST.SP.800-186, Recommendations for Discrete Logarithm-based Cryptography: Elliptic Curve Domain Parameters, February 2023

    • Recommended curves for specific usage (ECDH, ECDSA, EdDSA, ...) and associated security strength. Describes each curves parameters in details.
  • NIST.SP.800-38F, Recommendation for Block Cipher Modes of Operation: Methods for Key Wrapping, December 2012

    • Description of symmetric key wrapping using AES-KW and AES-KWP. Approving RFC 5649.
  • NIST.FIPS.800-132, Recommendation for Password-Based Key Derivation, December 2010

    • Description of low-entropy data derivation into secure master key.
  • NIST.SP.800-56Cr2, Recommendation for Key-Derivation Methods in Key-Establishment Schemes, August 2020

    • Description of high-entropy data derivation into secure master key.
  • NIST.SP.800-131Ar2, Transitioning the Use of Cryptographic Algorithms and Key Lengths, March 2019

    • Key length specification for different domain parameters, algorithms and cryptographic schemes.
  • NIST.SP.800-56Ar3, Recommendation for Pair-Wise Key-Establishment Schemes Using Discrete Logarithm Cryptography, April 2018

    • General information on discrete logarithm parameters.
  • NIST.SP.800-56Br2, Recommendation for Pair-Wise Key Establishment Using Integer Factorization Cryptography, March 2019

    • Information regarding RSA primitive specifications: key length, encryption, decryption and padding to use.
  • NIST.FIPS.180-4, Secure Hash Standard (SHS), August 2015

    • Specification regarding SHA family of hash functions.
  • NIST.FIPS.202, SHA-3 Standard: Permutation-Based Hash and Extendable-Output Functions, August 2015

    • Specification for SHA3.
  • NIST.FIPS.186-5, Digital Signature Standard (DSS), February 3, 2023

    • Information on ECDSA, EdDSA and key generation.
  • NIST.FIPS.203, Module-Lattice-Based Key-Encapsulation Mechanism Standard, August 13, 2024

    • Specification for ML-KEM (supersedes CRYSTALS-Kyber). Key encapsulation at security levels 1, 3, and 5.
  • NIST.FIPS.204, Module-Lattice-Based Digital Signature Standard, August 13, 2024

    • Specification for ML-DSA (supersedes CRYSTALS-Dilithium). Lattice-based digital signatures at security levels 2, 3, and 5.
  • NIST.FIPS.205, Stateless Hash-Based Digital Signature Standard, August 13, 2024

    • Specification for SLH-DSA (supersedes SPHINCS+). Hash-based digital signatures using SHA2 or SHAKE, 12 parameter sets.
  • NIST.FIPS.800-135r1, Recommendation for Existing Application-Specific Key Derivation Functions, December 2011

  • OpenSSL FIPS 140-2 Security Policy, 26 January 2023

    • OpenSSL official documentation

KMIP algorithm policy (server-side)

The cosmian_kms_server crate can enforce a KMIP algorithm policy at request entry points (and on retrieved keys).

The policy selector is kmip.policy_id.

You can also set it via:

  • CLI: --kmip-policy-id (case-insensitive)
  • Env var: KMS_POLICY_ID

Accepted values (case-insensitive):

  • DEFAULT: enforce the built-in conservative allowlists.
  • CUSTOM: enforce the allowlists you provide under [kmip.allowlists] (use with caution).

Enabling the default policy

If kmip.policy_id is unset, the KMIP policy layer is disabled.

To explicitly select the built-in default policy, set kmip.policy_id = "DEFAULT" in kms.toml (or pass it on the command line):

[kmip]
policy_id = "DEFAULT"

CLI:

cosmian_kms --kmip-policy-id DEFAULT

Env var:

export KMS_POLICY_ID=DEFAULT

Policies to use with caution

  • CUSTOM: enforces the allowlists under [kmip.allowlists]. Misconfiguration can unintentionally allow weak choices or, conversely, deny most operations (e.g., if you set an empty list []).

Notes:

  • When kmip.policy_id = "DEFAULT", any [kmip.allowlists] values in the configuration file are ignored (the server uses the built-in defaults).
  • When kmip.policy_id = "CUSTOM" and an allowlist key is omitted, that parameter is not restricted by an allowlist.

What the DEFAULT policy enforces

The DEFAULT policy is a conservative (ANSSI/NIST/FIPS-aligned) allowlist. It constrains KMIP requests by validating their declared cryptographic parameters and, when applicable, the characteristics of the referenced keys.

In particular, it allowlists:

  • Cryptographic algorithms (all builds): AES, RSA, ECDSA, ECDH, EC, HMACSHA256, HMACSHA384, HMACSHA512
  • Cryptographic algorithms (non-FIPS builds only): ChaCha20Poly1305, Ed25519, SHAKE128, SHAKE256, ConfigurableKEM, MLKEM_512, MLKEM_768, MLKEM_1024
  • Hash functions: SHA256, SHA384, SHA512, SHA3256, SHA3384, SHA3512
  • Signature algorithms: SHA256WithRSAEncryption, SHA384WithRSAEncryption, SHA512WithRSAEncryption, RSASSAPSS, ECDSAWithSHA256, ECDSAWithSHA384, ECDSAWithSHA512
  • Curves: P256, P384, P521, CURVE25519, CURVE448
  • Block cipher modes: GCM, CCM, XTS, NISTKeyWrap, AESKeyWrapPadding, GCMSIV
  • Padding methods: OAEP, PSS, PKCS5
  • MGF hashes: SHA256, SHA384, SHA512
  • Mask generators: MGF1

It also enforces key-size constraints:

  • RSA key sizes: 3072, 4096 (in addition to baseline structural constraints)
  • AES key sizes: 128, 192, 256

Baseline constraints (independent from the allowlists):

  • RSA keys smaller than 2048 bits are always rejected when the policy layer is enabled.

Custom allowlists (CUSTOM)

When kmip.policy_id = "CUSTOM", the policy layer uses the allowlists under [kmip.allowlists].

All allowlists follow these semantics:

  • omitted key: no allowlist restriction for that parameter
  • empty list []: deny everything for that parameter
  • non-empty list: allow only listed values

Allowlists are config-file only (there are no per-allowlist CLI flags).

Allowlist keys

[kmip.allowlists] supports:

  • algorithms: KMIP CryptographicAlgorithm (e.g. "AES", "RSA")
  • hashes: KMIP HashingAlgorithm (e.g. "SHA256")
  • signature_algorithms: KMIP DigitalSignatureAlgorithm (e.g. "RSASSAPSS")
  • curves: KMIP RecommendedCurve (e.g. "P256", "CURVE25519")
  • block_cipher_modes: KMIP BlockCipherMode (e.g. "GCM")
  • padding_methods: KMIP PaddingMethod (e.g. "OAEP", "PSS", "PKCS1v15", "PKCS5")
  • rsa_key_sizes: RSA key sizes in bits (strings): "2048", "3072", "4096"
  • aes_key_sizes: AES key sizes in bits (strings): "128", "192", "256", and optionally "512" for some AES-XTS client encodings
  • mgf_hashes: KMIP HashingAlgorithm for MGF1 (e.g. "SHA256")
  • mask_generators: KMIP MaskGenerator (e.g. "MGF1")

Values are matched case-insensitively against the KMIP enum display names.

Example: AES-GCM only

[kmip]
policy_id = "CUSTOM"

[kmip.allowlists]
algorithms = ["AES"]
block_cipher_modes = ["GCM"]
aes_key_sizes = ["256"]

Example: RSA-OAEP with RSA-3072+

[kmip]
policy_id = "CUSTOM"

[kmip.allowlists]
algorithms = ["RSA"]
padding_methods = ["OAEP"]
hashes = ["SHA256", "SHA384", "SHA512"]
mask_generators = ["MGF1"]
mgf_hashes = ["SHA256", "SHA384", "SHA512"]
rsa_key_sizes = ["3072", "4096"]

Zeroization

Secret keys and plaintexts are considered highly sensitive information and to securely erase from memory or storage Eviden KMS implements data zeroization. The term zeroization originates from the process of setting all relevant bits to zero, effectively rendering the data unreadable and unrecoverable. This practice is essential to prevent unwanted memory residue of critical data, especially in scenarios where the confidentiality and integrity of data are paramount.

This is particularly crucial in environments where multiple users or processes access the same resources, but this does not prevent in any way an attacker reading from live memory as the latter may not be encrypted.

It is good to notice that in the source code, only keys and plaintexts represented as byte arrays are being zeroized using the zeroize crate. Regarding OpenSSL PKey types, they are being zeroized by OpenSSL itself, which is triggered on drop by Rust.

EVP_PKEYs are dropped with EVP_PKEY_free, which should use the appropriate cipher-internal freeing function, which in turn should cleanse all private data unless there is a bug in the underlying OpenSSL library [...]. source

Regulatory Compliance

Eviden KMS thus adheres to relevant industry standards and regulatory requirements concerning data security and privacy such as the Rust guidelines from ANSSI. The use of zeroization aligns with this standard, demonstrating our commitment to safeguarding sensitive information and meeting the necessary compliance obligations.

Software Bill of Materials (SBOM)

A Software Bill of Materials (SBOM) is a formal record of all components, libraries, and dependencies used to build a software artifact — analogous to an ingredient list for software. Eviden KMS ships SBOMs for every release to support:

  • Supply chain security: identify exactly which packages and versions are included in each build.
  • Vulnerability management: cross-reference components against CVE databases to detect known vulnerabilities before they reach production.
  • License compliance: audit open-source licenses across the full dependency tree.
  • Regulatory requirements: satisfy NTIA, CISA, and EU CRA SBOM mandates for critical software.

Artifacts

SBOMs are generated from Nix build outputs, providing a complete and reproducible view of runtime dependencies. They are available for all build variants:

ArtifactVariantsLocation
KMS serverfips / non-fips × static / dynamicsbom/server/<variant>/<link>/
ckms CLIfips / non-fips × static / dynamicsbom/ckms/<variant>/<link>/
OpenSSL 3.1.2 (FIPS derivation)sbom/openssl_3_1_2/
OpenSSL 3.6.0 (non-FIPS derivation)sbom/openssl_3_6_0/

Each output directory contains:

FileFormatPurpose
bom.cdx.jsonCycloneDX 1.5 JSONImport into SBOM platforms (e.g., Dependency-Track)
bom.spdx.jsonSPDX 2.3 JSONLicense compliance and SPDX tooling
sbom.csvCSVTabular component inventory
vulns.csvCSVDeduplicated vulnerability rows (CVE/OSV/Grype/Vulnix)
graph.pngPNGVisual dependency graph
meta.jsonJSONBuild metadata (variant, link, counts, timestamps)

The default FIPS static server SBOM is at sbom/server/fips/static/bom.cdx.json.

Vulnerability scanning

Vulnerability data is produced by vulnxscan, which aggregates three sources:

  • Grype — scans against NVD, GitHub Security Advisories, and other databases.
  • Vulnix — scans against the NixOS security tracker with Nix-specific package context.
  • OSV — queries the OSV database for additional coverage.

Duplicate CVE rows (e.g. CVE-2026-0915, UBUNTU-CVE-2026-0915, DEBIAN-CVE-2026-0915) are collapsed to a single entry by nix/scripts/dedup_cves.py before the file is committed.

Regenerating the SBOM

Re-run after any change to Cargo.lock or the package version:

# All combinations (server + ckms + OpenSSL derivations)
mise run sbom:generate

# Server only — one specific combination
mise run sbom:generate --target server --variant fips --link static

# OpenSSL 3.6.0 derivation only
mise run sbom:generate --target openssl_3_6_0

See sbom/README.md for the full regeneration guide and usage examples (Dependency-Track upload, jq queries, vulnerability review).

Standards & references

Cryptography Bill of Materials (CBOM)

A Cryptography Bill of Materials (CBOM) is a formal inventory of all cryptographic assets used in a software system — algorithms, key types, parameters, and the libraries that implement them. It follows the CycloneDX 1.6 standard and is designed to support:

  • Crypto-agility audits: identify which algorithms are in use and where, to plan migration when standards change (e.g. deprecation of RSA-2048 or SHA-1).
  • Post-quantum readiness: track which operations still rely on classical algorithms vulnerable to quantum attacks.
  • Compliance reporting: demonstrate that only approved algorithms are in use for FIPS or other regulatory frameworks.
  • Supply chain security: record implementing libraries (e.g. OpenSSL, RustCrypto) with their versions alongside the algorithms they provide.

CBOM artifact

The Eviden KMS CBOM is generated from the actual source code and Cargo.lock, ensuring it reflects the running codebase rather than a manual registry.

FileFormatDescription
cbom/cbom.cdx.jsonCycloneDX 1.6 JSONFull cryptographic asset inventory

The CBOM covers:

  • All symmetric and asymmetric algorithms (AES-GCM, ChaCha20-Poly1305, RSA, ECDSA, EdDSA, …)
  • Post-quantum algorithms (ML-KEM, ML-DSA, SLH-DSA, Covercrypt)
  • Key derivation functions (PBKDF2, HKDF, Argon2)
  • Hash functions (SHA-2, SHA-3)
  • Key encapsulation and wrapping schemes
  • The implementing Rust crates (RustCrypto, OpenSSL, Eviden) with versions from Cargo.lock
  • FIPS compliance status per algorithm

Regenerating the CBOM

Re-run the generator after any algorithm or dependency change:

python3 .mise/scripts/sbom/generate_cbom.py --output cbom/cbom.cdx.json

The script:

  1. Runs cargo metadata --features non-fips to read implementing-library versions from Cargo.lock.
  2. Scans Rust source files under crate/ to confirm each algorithm is actively referenced.
  3. Emits a CycloneDX 1.6 CBOM JSON document.

Only the algorithm catalogue inside the script requires manual maintenance when a new algorithm is added or removed; library versions and source-scan results are refreshed automatically.

Importing into SBOM platforms

The CBOM uses the same CycloneDX format as the SBOM, so it can be imported into any CycloneDX-compatible platform such as Dependency-Track:

curl -X POST "https://dtrack.example.com/api/v1/bom" \
  -H "X-Api-Key: ${API_KEY}" \
  -H "Content-Type: multipart/form-data" \
  -F "project=${PROJECT_UUID}" \
  -F "bom=@cbom/cbom.cdx.json"

Standards & references

🔐 kms — Cryptographic Posture Report

ℹ️ Auto-generated report — do not edit by hand

Last commit: 70512c0b

To regenerate:

bash .mise/scripts/audit/crypto_sensor.sh --repo-root .

🎯 Security Posture Scorecard

✅ None
Unmitigated CRITICAL
21 total CRITICAL
1
Unmitigated HIGH
41 total HIGH
59%
PQC Readiness
asymmetric ops with PQC alternative
49%
FIPS Coverage
FIPS 140-3 approved algorithm refs
204
Zeroize References
key material cleared on drop

📊 Discovery Overview

SeverityCountContext
🔴 CRITICAL21Broken algorithms (DES·MD5·RC4) — all KMIP spec enums, blocked at runtime
🟠 HIGH41Weak key sizes (RSA-1024·EC-P192) and deprecated SHA-1
🟡 MEDIUM0Medium-severity issues
🔵 LOW / ⚪ INFO2449Informational algorithm usage references
pie title Sensor findings by severity
"CRITICAL" : 21
"HIGH" : 41
"INFO" : 2449

Reference counts = source lines matching each algorithm pattern.

AlgorithmCategoryFIPS 140-3PQCRefs
PKCS#11/HSMHSM interface559
RSAAsymmetric248
X.509 certificatePKI / TLS233
ML-KEM (FIPS 203)Post-Quantum KEM225
SLH-DSA (FIPS 205)Post-Quantum Signature223
Covercrypt (ABE)Attribute-Based Encryption197
ML-DSA (FIPS 204)Post-Quantum Signature141
EdDSA (Ed25519/Ed448)Asymmetric141
AES-GCM/GCM-SIVSymmetric66
Argon2KDF26
Hybrid KEMClassical + PQC24
EC-P192Asymmetric — WEAK KEY20
EC (ECDSA/ECDH)Asymmetric15
DES/3DESSymmetric — DEPRECATED15
SHA-1Hash — deprecated for signing13
ChaCha20-Poly1305Symmetric (non-FIPS)8
RSA-1024Asymmetric — WEAK KEY8
RC4Symmetric — BROKEN5
SHA-2/SHA-3Hash1
MD5Hash — BROKEN1

Deprecated entries in kmip_1_4/ are KMIP spec enum definitions — not active operations. Blocked at runtime by algorithm_policy.rs.

pie title Algorithm usage by category
"PKCS#11 / HSM" : 559
"Asymmetric (RSA)" : 248
"TLS / X.509" : 233
"PQC (ML-KEM)" : 225
"PQC (SLH-DSA)" : 223
"ABE (Covercrypt)" : 197
"PQC (ML-DSA)" : 141
"Asymmetric (EdDSA)" : 141
"Symmetric (AES)" : 66
"Asymmetric — weak" : 28
"KDF (Argon2)" : 26
"PQC (Hybrid KEM)" : 24
"Asymmetric (EC)" : 15
"Symmetric (deprecated)" : 15
"Hash (deprecated)" : 13
"Symmetric (ChaCha20)" : 8
"Symmetric (RC4)" : 5
"Hash (SHA-2/3)" : 1
"Hash (MD5)" : 1
DependencyDescriptionStandardCargo.toml refs
openssl (FIPS provider)openssl (FIPS provider)85
opensslOpenSSL 3.6 (FIPS provider)FIPS 140-334
cosmian_crypto_corecosmian_crypto_core5
x509-parserx509-parserRFC 52804
p256p256 (NIST P-256)FIPS 186-53
rustlsrustls (TLS)RFC 84462
aes-gcmRustCrypto/aes-gcm-sivRFC 84521
argon2RustCrypto/argon2RFC 91061
cosmian_cover_cryptcosmian_cover_crypt1
k256k256 (secp256k1)1
ringring (BoringSSL subset)1
flowchart TD
    KMS["kms"]
    KMS --> OPENSSL__FIPS_PROVIDER_["openssl (FIPS provider)"]
    KMS --> OPENSSL["OpenSSL (FIPS provider)"]
    KMS --> COSMIAN_CRYPTO_CORE["cosmian_crypto_core (KEM combiner)"]
    KMS --> X509_PARSER["x509-parser"]
    KMS --> P256["p256 NIST P-256"]
    KMS --> RUSTLS["rustls (TLS)"]
    KMS --> AES_GCM["RustCrypto/aes-gcm"]
    KMS --> ARGON2["RustCrypto/argon2"]
    KMS --> COSMIAN_COVER_CRYPT["cosmian_cover_crypt (ABE)"]
    KMS --> K256["k256 secp256k1"]
    KMS --> RING["ring (BoringSSL subset)"]

⚡ Priority Remediation

62 CRITICAL + HIGH total | 1 actionable | 61 suppressed by KMIP policy

#SeverityAlgorithmFileDetailRemediation
1🟠 HIGHEC-P192ttlv/enum_lookup.rs:200P-192 is below the 112-bit security level. Use P-256 or higher.Upgrade to P-256 or higher curve.

🚀 Post-Quantum Readiness

Score: 59% — 59% of asymmetric operations have a PQC alternative.

pie title PQC vs Classical asymmetric coverage
    "PQC-ready (ML-KEM, ML-DSA, SLH-DSA, Hybrid)" : 59
    "Classical-only (RSA, EC, EdDSA)" : 41
StandardAlgorithmStatus
FIPS 203ML-KEM (CRYSTALS-Kyber)
FIPS 204ML-DSA (CRYSTALS-Dilithium)
FIPS 205SLH-DSA (SPHINCS+)
CNSA 2.0Hybrid KEM (classical + PQC)
RFC 8032EdDSA (Ed25519 / Ed448)
FIPS 186-5ECDH / ECDSA (P-256+)

All four NIST PQC standards implemented

FIPS 203, 204, 205 and CNSA 2.0 Hybrid KEM are already deployed. The European Commission end-of-2026 inventory mandate is addressed.


🔒 FIPS 140-3 Compliance

Score: 49% of detected algorithm references are FIPS 140-3 approved.

The remaining 51% are:

CategoryReason
PKCS#11 / HSMFIPS status depends on the certified HSM hardware
Covercrypt ABEAttribute-based encryption — FIPS not applicable
ChaCha20-Poly1305Non-FIPS builds only (--features non-fips)
KMIP 1.4 legacy enumsType definitions — not active crypto operations

FIPS build mode

cargo build (without --features non-fips) exercises only FIPS 140-3 approved algorithms at runtime.


🛡️ Memory Safety — Zeroize Discipline

The sensor found 204 references to Zeroizing<T> / ZeroizeOnDrop across the codebase — automatic key-material zeroing on drop (CWE-316 mitigation).

Best practice implemented

All derived key material (HKDF, PBKDF2) and private key bytes are wrapped in Zeroizing<Vec<u8>> — secrets are scrubbed from memory when their scope ends.


🔍 How the Sensor Works

flowchart LR
    A["Discover\nScan Rust sources\n& Cargo.toml"] --> B["Analyze\nApply risk rules\nMatch KMIP context"]
    B --> C["Prioritize\nSeverity scoring\nMitigation tagging"]
    C --> D["Report\nCBOM & MkDocs\nJSON + Markdown"]
    D --> E["Monitor\nPre-commit hook\nCI integration"]
    style A fill:#f0f9ff,stroke:#0ea5e9
    style B fill:#fefce8,stroke:#eab308
    style C fill:#fff7ed,stroke:#f97316
    style D fill:#f0fdf4,stroke:#22c55e
    style E fill:#faf5ff,stroke:#a855f7
LayerToolWhat it discovers
Source codescan_source.pyAlgorithm usage, deprecated primitives, weak keys, hardcoded material, PQC/zeroize
Dependency treecdxgen (OWASP CycloneDX)Cryptographic library versions from Cargo.lock
CVE feedcargo audit (RustSec)Known vulnerabilities in crypto dependencies
Live TLStestssl.sh (optional)Cipher suites, certificate chain, TLS version

The sensor outputs a Cryptographic Bill of Materials (CBOM) in CycloneDX 1.6 format (see cbom/cbom.cdx.json).


▶️ How to Run

Full scan — source + CVE + CBOM (also updates this page)

bash .mise/scripts/audit/crypto_sensor.sh --repo-root .
# With live TLS scan:
bash .mise/scripts/audit/crypto_sensor.sh \\
    --repo-root . --server-url https://localhost:9998 --update-cbom

Source scanner only (fast, no network)

python3 .mise/scripts/audit/scan_source.py \\
    --repo-root . --output /tmp/findings.json

Risk scorer + page regeneration

python3 .mise/scripts/audit/risk_score.py \\
    --input /tmp/findings.json \\
    --output-json /tmp/risk_report.json \\
    --docs-output documentation/docs/certifications_and_compliance/audit/crypto_inventory.md

Output files are written to cbom/sensor/ (stable path — overwritten on each run):

FileContent
findings.jsonRaw per-line source scanner findings
risk_report.jsonRisk-scored findings + CVE data
cargo_audit.jsonCVE advisory data
dep_cbom.jsonDependency-level CBOM (cdxgen)
tls_report.txtTLS scan output (if --server-url was given)

Eviden KMS — OWASP Security Audit Plan & Report

Document type: Security Audit Plan & Report Template Standard: OWASP Top 10 (2021) + OWASP ASVS v4.0 (selective) Repository: Eviden/kms — branch develop Workspace root: crate/ (Rust workspace) + ui/ (React/TypeScript) Audit date: 2026-06-05 (re-run with remediation verification: 2026-04-14) Auditor(s): GitHub Copilot (automated static analysis) Status: ☑ Complete — automated pass (audit.sh ran 2026-06-05)

Tools available during this audit

ToolVersionStatusNotes
cargo-audit0.22.1✅ AvailableAdvisory DB scan
cargo-deny0.19.0✅ AvailablePolicy check (bans, licenses, advisories, sources)
cargo-outdated0.17.0✅ AvailableOutdated dep detection
cargo-geiger0.13.0⚠️ Installed — partialFails on virtual workspace manifest with bug (#378); fallback: manual grep -r "unsafe "
semgrep❌ Not installedStatic pattern matching — install per §1
gitleaks / trufflehog3❌ Not installedSecret scanning — install per §1

cargo-geiger 0.13.0 fails with error: No such file or directory — crate/cli/src/lib.rs when invoked against this virtual workspace. This is a known upstream bug. The A06 section below uses manual grep -r "unsafe" counts as a validated substitute.


Table of Contents

  1. Tool Installation
  2. A01 – Broken Access Control
  3. A02 – Cryptographic Failures
  4. A03 – Injection
  5. A04 – Insecure Design
  6. A05 – Security Misconfiguration
  7. A06 – Vulnerable and Outdated Components
  8. A07 – Identification and Authentication Failures
  9. A08 – Software and Data Integrity Failures
  10. A09 – Security Logging and Monitoring Failures
  11. A10 – Server-Side Request Forgery (SSRF)
  12. EXT-0 – KMS Own Authorization System
  13. EXT-1 – Cryptographic Key Lifecycle & Zeroization
  14. EXT-2 – Denial of Service / Resource Exhaustion
  15. Remediation Priority Matrix
  16. Report Sign-off

1. Tool Installation

Install every tool before starting the audit. Run all commands from the workspace root.

1.1 Rust security toolchain

# Audit Rust dependencies for known CVEs (RustSec Advisory DB)
cargo install cargo-audit

# Policy-based dependency checking (licenses, bans, advisories)
# cargo-deny is already configured: deny.toml exists at workspace root
cargo install cargo-deny

# Count and locate `unsafe` blocks across the workspace
cargo install cargo-geiger

# Find outdated dependencies
cargo install cargo-outdated

# Verify tool versions
cargo audit --version
cargo deny --version
cargo geiger --version
cargo outdated --version

1.2 Secrets scanning

# Option A: gitleaks (scan git history + working tree for accidentally committed secrets)
curl -sSfL \
  https://github.com/gitleaks/gitleaks/releases/latest/download/gitleaks_linux_amd64.tar.gz \
  | tar xz && sudo mv gitleaks /usr/local/bin/
gitleaks version

# Option B: trufflehog (entropy-based secret detection)
pip3 install truffleHog3
trufflehog3 --version

1.3 SAST — Semgrep

# Install semgrep
pip3 install semgrep
semgrep --version

# Verify the Rust and OWASP rulesets are reachable (requires internet)
semgrep --config p/rust        --test 2>/dev/null || true
semgrep --config p/owasp-top-ten --test 2>/dev/null || true
semgrep --config p/secrets     --test 2>/dev/null || true

1.4 Readiness check

echo "=== Tool readiness ===" && \
  cargo audit   --version && \
  cargo deny    --version && \
  cargo geiger  --version && \
  cargo outdated --version && \
  semgrep       --version && \
  (gitleaks version 2>/dev/null || trufflehog3 --version 2>/dev/null || \
    echo "WARNING: no secrets scanner found")

2. A01 – Broken Access Control

OWASP description: Restrictions on what authenticated users are allowed to do are not properly enforced. Attackers can exploit these flaws to access unauthorized functionality and/or data.

2.1 Scope

AreaKey files
Auth middleware stackcrate/server/src/middlewares/ensure_auth.rs
KMIP route handlercrate/server/src/routes/kmip.rs
Access grant / revoke endpointscrate/server/src/routes/access.rs
Unauthenticated public endpointscrate/server/src/routes/health.rs, root_redirect.rs
Server app builder (middleware order)crate/server/src/start_kms_server.rs
Enterprise route authcrate/server/src/routes/aws_xks/, azure_ekm/, google_cse/, ms_dke/

2.2 Investigation Steps

# Step 1 — Enumerate all HTTP routes; identify routes registered WITHOUT
# the standard auth middleware chain
grep -rn \
  "web::resource\|web::route\|web::scope\|#\[get\]\|#\[post\]\|#\[put\]\|#\[delete\]" \
  crate/server/src/routes/ --include="*.rs"

# Step 2 — Inspect the full middleware assembly order
# Auth wrappers MUST be registered before route handlers
grep -n "App::new\|\.wrap\|\.configure\|\.service" \
  crate/server/src/start_kms_server.rs

# Step 3 — Confirm which middleware types protect which routes
grep -rn "EnsureAuth\|JwtMiddleware\|ApiTokenMiddleware\|TlsAuthMiddleware" \
  crate/server/src/ --include="*.rs"

# Step 4 — Look for any explicit auth skip or bypass condition
grep -rn "skip\|bypass\|no_auth\|#\[cfg(feature.*insecure" \
  crate/server/src/middlewares/ --include="*.rs"

# Step 5 — Horizontal privilege escalation: can a user access another user's objects
# without an explicit grant? Inspect owner check in retrieve_object_for_operation
grep -n "owner\|user_has_permission\|is_object_owned_by" \
  crate/server/src/core/retrieve_object_utils.rs

# Step 6 — Semgrep: path traversal and broken access control patterns
semgrep --config p/owasp-top-ten \
  crate/server/src/routes/ --lang rust 2>/dev/null \
  | grep -i "access\|path\|traversal" || true

2.3 Findings

Status: ⚠️ Review needed

Files inspected:

  • crate/server/src/start_kms_server.rs
  • crate/server/src/middlewares/ensure_auth.rs, tls_auth.rs, api_token/, jwt/
  • crate/server/src/routes/health.rs, access.rs, kmip.rs
  • crate/server/src/core/retrieve_object_utils.rs
IDFile:LineSeverityDescription
A01-1start_kms_server.rs:776,795,806,901,962MediumCors::permissive() applied to all scopes including the main KMIP endpoint. This allows cross-origin requests from any domain, enabling browser-based cross-site attacks against the KMIP API.
A01-2routes/health.rs:47Info/health is a public (unauthenticated) endpoint that calls kms.get_user(&req) and logs the result. On an unconfigured server the default username is logged on every health probe. Not a direct vulnerability but reveals configuration state.
A01-3middlewares/ensure_auth.rs:128InfoEnsureAuth correctly skips itself when the request is already authenticated (comment confirms intent). No bypass risk.

Recommended fix:

  • A01-1: Replace Cors::permissive() with an explicit allowlist (Cors::default().allowed_origin(…)) for all scopes. For KMIP, restrict to known client origins or require mTLS instead of relying on CORS.
  • A01-2: Move the get_user log line behind an if tracing::enabled!(Level::DEBUG) guard, or remove it from public endpoints.

OWASP description: Failures related to cryptography (data at rest, data in transit, key management, algorithm choices) that often lead to exposure of sensitive data.

3. A02 – Cryptographic Failures

3.1 Scope

AreaKey files
Crypto primitivescrate/crypto/src/ (all subdirectories)
Key export enforcementcrate/server/src/core/operations/export_get.rs
Key wrapping / unwrappingcrate/server/src/core/wrapping/wrap.rs, unwrap.rs
Key storage formatcrate/kmip/src/EncryptedKeyBlock, KeyMaterial types
OpenSSL build & hashcrate/crypto/build.rs
TLS configurationcrate/server/src/config/command_line/tls_config.rs

3.2 Investigation Steps

# Step 1 — Search for hardcoded keys, zero IVs, or predictable nonces
grep -rn "\[0u8; 16\]\|\[0u8; 32\]\|\[0u8; 12\]\|nonce.*=.*\[0\|iv.*=.*\[0" \
  crate/crypto/src/ --include="*.rs"

# Step 2 — Deprecated / weak algorithms (ECB mode, DES, RC4, MD5, SHA-1)
grep -rni "ecb\|des\b\|rc4\|md5\|sha1\b\|sha-1\|rc2\|md4" \
  crate/ --include="*.rs" | grep -v "//\|#\[doc\|test_" | head -30

# Step 3 — Sensitive key export: verify the sensitive flag blocks unprotected export
grep -n "sensitive\|key_wrapping_specification\|Sensitive\|is_sensitive" \
  crate/server/src/core/operations/export_get.rs

# Step 4 — Non-FIPS algorithm use must be gated behind the non-fips feature flag
grep -rn "#\[cfg(feature.*non.fips\|cfg.*non_fips" \
  crate/crypto/src/ --include="*.rs" | head -20
# Cross-check: algorithms that appear outside a cfg gate
grep -rni "Covercrypt\|AES.XTS\|chacha\|xchacha\|kyber\|dilithium" \
  crate/crypto/src/ --include="*.rs" | grep -v "#\[cfg" | head -15

# Step 5 — In-memory cache for unwrapped key material: check zeroization on eviction
grep -n "cache\|HashMap\|DashMap\|insert\|remove\|evict\|zeroize\|Zeroizing" \
  crate/server/src/core/wrapping/wrap.rs

# Step 6 — OpenSSL download: verify SHA-256 check is non-bypassable
grep -A5 -B5 "sha256\|checksum\|expected\|download\|verify" crate/crypto/build.rs

# Step 7 — Semgrep crypto-specific rules
semgrep --config p/rust crate/crypto/src/ --lang rust 2>/dev/null \
  | grep -i "crypto\|cipher\|hash\|random\|seed" || true

3.3 Findings

Status: ⚠️ Review needed

Files inspected:

  • crate/crypto/src/crypto/symmetric/symmetric_ciphers.rs
  • crate/crypto/src/crypto/elliptic_curves/ecies/standard_curves.rs
  • crate/server/src/core/operations/export_get.rs
  • crate/server/src/core/wrapping/wrap.rs
  • crate/crypto/build.rs
  • crate/clients/clap/src/actions/mac.rs, actions/aws/byok/export_key_material.rs
IDFile:LineSeverityDescription
A02-1symmetric_ciphers.rs:429,784InfoZero-initialised buffers (vec![0; nonce_size], [0_u8; 16]) are overwritten immediately by rand_bytes() or by structured counter/nonce construction (ChaCha20). No actual hardcoded IVs.
A02-2mac.rs:17,31,47 / export_key_material.rs:92,106LowSHA-1 is exposed as a user-selectable MAC/hash algorithm and used in AWS BYOK RSA-OAEP wrapping. SHA-1 is weak for MAC and PKCS#1 v1.5 is weak for key wrapping. These paths are not gated behind #[cfg(feature = "non-fips")] in the CLI actions layer even though FIPS forbids SHA-1.
A02-3standard_curves.rs:31Infolet mut iv = vec![0; iv_size] in ECIES — filled by OpenSSL rand_bytes. Safe; zero is a temporary placeholder.
A02-4export_get.rs:77sensitive=true objects are correctly blocked from export unless key_wrapping_specification is present. Per KMIP BL-M-12-21.
A02-5wrap.rs:118–121MediumUnwrapped key objects are stored in an in-memory DashMap/HashMap cache with no TTL or eviction policy visible in wrap.rs. Cached plaintext key material persists indefinitely in process heap, increasing the window for cold-boot / process-dump attacks.
A02-6build.rs:234,274–275verify_hash() enforces SHA-256 check on the OpenSSL tarball. Non-empty sha256 param is required. Supply-chain integrity protected.

Recommended fix:

  • A02-2: Gate SHA-1 MAC and RSA-OAEP-SHA1 behind #[cfg(feature = "non-fips")] or emit a deprecation warning when selected. Replace with SHA-256 defaults.
  • A02-5: Add a TTL (e.g. 60 s) or LRU eviction to the unwrapped-object cache. Call zeroize() on cache values before removal.

OWASP description: Injection flaws occur when untrusted data is sent to an interpreter as part of a command or query (SQL, LDAP, OS shell, TTLV, XML).

4. A03 – Injection

4.1 Scope

AreaKey files
Dynamic SQL query buildercrate/server_database/src/stores/sql/locate_query.rs
SQL backendscrate/server_database/src/stores/sql/sqlite.rs, pgsql.rs, mysql.rs
TTLV binary parsercrate/kmip/src/ttlv/wire/ttlv_bytes_deserializer.rs
TTLV XML parsercrate/kmip/src/ttlv/xml/parser.rs, deserializer.rs
JSON → TTLV entry pointcrate/server/src/routes/kmip.rsfrom_ttlv() call
CLI file path argumentscrate/clients/clap/src/actions/

4.2 Investigation Steps

# Step 1 — SQL injection: flag any dynamic SQL built with format! or string concatenation
grep -rn \
  'format!.*SELECT\|format!.*INSERT\|format!.*UPDATE\|format!.*DELETE\|"SELECT.*{}' \
  crate/server_database/src/ --include="*.rs"

# Step 2 — Verify parameterized query usage throughout the SQL backends
grep -n "bind\|execute\|query_as\|fetch" \
  crate/server_database/src/stores/sql/locate_query.rs | head -30

# Step 3 — TTLV binary parser: length field checked before allocation?
grep -n "len\|size\|capacity\|Vec::with_capacity\|read_exact\|take\|limit" \
  crate/kmip/src/ttlv/wire/ttlv_bytes_deserializer.rs

# Step 4 — Recursion depth guard in TTLV struct parser
grep -rn "depth\|recursion\|stack\|max_depth\|fn from_ttlv\|fn parse" \
  crate/kmip/src/ttlv/ --include="*.rs"

# Step 5 — OS shell injection: any std::process::Command with user-supplied data?
grep -rn "Command::new\|std::process\|exec\|popen" \
  crate/clients/ crate/server/ --include="*.rs"

# Step 6 — Tag / object-group injection: these are stored as strings — verify binding
grep -n "tag\|Tag\|object_group\|ObjectGroup" \
  crate/server_database/src/stores/sql/locate_query.rs | head -20

# Step 7 — Semgrep injection ruleset
semgrep --config p/owasp-top-ten crate/ --lang rust 2>/dev/null \
  | grep -i "inject\|sql\|command\|exec" || true

4.3 Findings

Status: ⚠️ Review needed

Files inspected:

  • crate/server_database/src/stores/sql/locate_query.rs
  • crate/kmip/src/ttlv/wire/ttlv_bytes_deserializer.rs
  • crate/kmip/src/ttlv/wire/ttlv_bytes_serializer.rs
  • crate/server/src/core/operations/locate.rs
  • crate/server/src/routes/kmip.rs
  • crate/server/benchmarks/bench.rs (CLI-only)
IDFile:LineSeverityDescription
A03-1stores/sql/locate_query.rs (all)All SQL queries use bind-parameter style (sqlx::query!, placeholder ? / $N). No string concatenation into SQL statements. No SQL injection.
A03-2ttlv_bytes_deserializer.rs:56Highread_ttlv() is purely recursive with no depth counter and no maximum depth check. A malicious deeply-nested TTLV binary can exhaust the call stack (stack overflow / uncontrolled recursion). An unauthenticated TTLV POST triggers this code path before any authentication (parsed at HTTP handler level).
A03-3routes/kmip.rs (XML path)MediumThe TTLV XML parser (ttlv_xml_deserializer.rs) has a depth counter but the loop contains no max_depth enforcement — deep XML nesting proceeds without limit.
A03-4bench.rs:2815InfoCLI benchmark code builds a gnuplot script embedding the op operation name variable inline. This is benchmark CLI code, not reachable from the server. No server-side command injection.
A03-5All server routesNo OS std::process::Command::new() / shell_exec calls found in production server code.

Recommended fix:

  • A03-2: Add a depth: u32 parameter to read_ttlv() and return an error when depth > MAX_TTLV_DEPTH (suggested: 64). Example: fn read_ttlv(reader: &mut impl Read, depth: u32) -> Result<Ttlv> { if depth > 64 { return Err(...) } ... read_ttlv(reader, depth + 1) ... }
  • A03-3: Add const MAX_XML_DEPTH: usize = 64; and fail when depth >= MAX_XML_DEPTH.

5. A04 – Insecure Design

OWASP description: Missing or ineffective control design. Unlike misconfiguration, this is a structural flaw that cannot be fixed by correct configuration alone.

5.1 Scope

AreaKey files
KMIP operation dispatchercrate/server/src/core/operations/dispatch.rs
Actix-web app buildercrate/server/src/start_kms_server.rs
Server configurationcrate/server/src/config/
KMIP lifecycle state machinecrate/kmip/src/ — object state types
Rate limitingcrate/server/src/middlewares/

5.2 Investigation Steps

# Step 1 — Max request body size: Actix default is 256 KB; look for an explicit override
grep -n "LimitPayloadSize\|max_payload_size\|PayloadConfig\|content_length\|max_body" \
  crate/server/src/start_kms_server.rs crate/server/src/routes/kmip.rs

# Step 2 — Rate limiting middleware
grep -rn "RateLimiter\|rate_limit\|throttle\|governor\|leaky_bucket\|token_bucket" \
  crate/server/src/ --include="*.rs"

# Step 3 — KMIP state machine: transitions must follow spec
# (PreActive → Active → Deactivated → Destroyed; no skipping)
grep -rn "Deactivated\|PreActive\|Compromised\|Destroyed\|state.*transition\|update_state" \
  crate/server/src/core/operations/ --include="*.rs"

# Step 4 — Bulk operations: can one request mutate thousands of objects?
grep -rn "Locate\|for.*objects\|batch\|BulkRequest\|RequestBatchItem" \
  crate/server/src/core/operations/ --include="*.rs" | head -20

# Step 5 — Locate result set: is it bounded / paginated?
grep -rn "limit\|offset\|pagination\|page_size\|LIMIT\|OFFSET\|max_items" \
  crate/server_database/src/ --include="*.rs"

# Step 6 — SecretData / Opaque objects: are there constraints on size or type?
grep -rn "SecretData\|Opaque\|OpaqueData\|CertificateRequest" \
  crate/server/src/core/operations/ --include="*.rs"

5.3 Findings

Status: ❌ Vulnerability found

Files inspected:

  • crate/server/src/start_kms_server.rs
  • crate/server/src/core/operations/locate.rs
  • crate/server_database/src/stores/sql/locate_query.rs
  • crate/server/src/middlewares/
IDFile:LineSeverityDescription
A04-1start_kms_server.rs:767-768HighHTTP request body limit is set to 10 GB: PayloadConfig::new(10_000_000_000) and JsonConfig::default().limit(10_000_000_000). Any unauthenticated or authenticated client can send a 10 GB request body, potentially exhausting server RAM and causing out-of-memory termination.
A04-2All server middlewaresHighNo rate-limiting middleware is registered anywhere in the Actix-web application. There is no throttling on KMIP operations, API token verification, or UI login. An attacker can issue unlimited requests per second, allowing brute-force of API tokens and CPU/memory exhaustion.
A04-3operations/locate.rs:96-101MediumThe MaximumItems field in a Locate request is fully client-controlled and optional. When absent, the server returns all matching objects. There is no server-side cap on the result set size. A client owning or co-owning a large number of objects can trigger a very large response.
A04-4operations/dispatch.rsKMIP state machine transitions (PreActive→Active→Deactivated→Destroyed) are enforced via activate.rs, revoke.rs, destroy.rs. Invalid transitions are rejected with ItemNotFound or state-error.
A04-5operations/locate.rs + batch_items.rsBatch request item limit: the KMIP spec allows batch requests; each item is individually dispatched and access-controlled. No uncontrolled bulk mutation discovered beyond the Locate unbounded-result issue.

Recommended fix:

  • A04-1: Reduce PayloadConfig and JsonConfig limits to a realistic maximum (e.g. 64 MB for KMIP bodies, 1 MB for JSON config). Use PayloadConfig::new(64 * 1024 * 1024).
  • A04-2: Add a rate-limiting middleware using actix-governor or tower-governor. Apply globally plus a stricter limit on authentication endpoints.
  • A04-3: Add a server-side cap: if MaximumItems is absent or exceeds a configured max_locate_items (default 1 000), clamp the SQL LIMIT to that value.

6. A05 – Security Misconfiguration

OWASP description: The most commonly seen issue, resulting from insecure default configurations, missing hardening, verbose error messages, or open cloud storage.

6.1 Scope

AreaKey files
All server CLI flags & defaultscrate/server/src/config/command_line/ (all files)
TLS settingscrate/server/src/config/command_line/tls_config.rs
UI authentication & CORScrate/server/src/routes/ui_auth.rs
OpenSSL provider initcrate/server/src/openssl_providers.rs
Error response formatcrate/server/src/routes/kmip.rs — error handling

6.2 Investigation Steps

# Step 1 — Enumerate all default values for security-sensitive flags
grep -n "default_value\|default.*=\s*\"\|fn default()" \
  crate/server/src/config/command_line/clap_config.rs | head -50

# Step 2 — TLS: minimum protocol version and cipher suite defaults
grep -rn "TLSv1\|tls_cipher\|cipher_suite\|TlsVersion\|min_protocol\|SSLv" \
  crate/server/src/config/ --include="*.rs"
grep -n "tls_p12\|tls_cert\|tls_key\|tls_chain\|clients_ca" \
  crate/server/src/config/command_line/tls_config.rs

# Step 3 — CORS: look for wildcard origins or missing CORS restrictions
grep -rn "Cors\|allow_origin\|AllowedOrigin\|allow_any_origin" \
  crate/server/src/ --include="*.rs"

# Step 4 — Error messages: do they expose stack traces or internal paths?
grep -rn "backtrace\|debug_info\|internal_err\|unwrap()\|expect(" \
  crate/server/src/routes/ --include="*.rs" | head -20

# Step 5 — Dangerous feature flags that weaken security
grep -rn "insecure\|skip.*expir\|no.verify\|allow_self_signed" \
  crate/server/src/ --include="*.rs" | grep -v "#\[cfg\|cfg(feature\|//.*insecure"

# Step 6 — Environment variable names that carry secrets: verify none are logged
grep -rn 'KMS_.*PASSWORD\|KMS_.*SECRET\|KMS_.*KEY\|KMS_.*TOKEN' \
  crate/server/src/ --include="*.rs"

# Step 7 — Secrets scanner: detect hardcoded credentials
semgrep --config p/secrets crate/ --lang rust 2>/dev/null | head -30 || true
gitleaks detect --source . --no-git 2>/dev/null | head -30 || true

6.3 Findings

Status: ⚠️ Review needed

Files inspected:

  • crate/server/src/start_kms_server.rs
  • crate/server/src/config/command_line/tls_config.rs
  • crate/server/src/openssl_providers.rs
  • deny.toml
IDFile:LineSeverityDescription
A05-1start_kms_server.rs:776,795,806,901,962MediumCors::permissive() applied on every scope: main KMIP, UI, Google CSE, MS DKE, AWS XKS. Sets Access-Control-Allow-Origin: *, allowing any browser origin to reach the KMIP endpoint cross-origin.
A05-2config/command_line/tls_config.rsLowTLS minimum version is not explicitly configured in code. Relies on OpenSSL 3.6.2 default (TLS 1.2+). Should be made explicit.
A05-3middlewares/jwt/jwt_config.rs:10,155MediumThe insecure feature completely disables all JWT validation (signature, expiry, audience). No compile-time guard prevents this feature from reaching production. If accidentally included in a production build, any token is accepted.
A05-4deny.tomlLowRUSTSEC-2026-0097 is allow-listed in the cargo deny policy. Should be resolved by upgrading rand instead of permanently suppressing the advisory.
A05-5All routesError responses use KMIP error codes; no stack traces or file paths exposed in HTTP responses.

Recommended fix:

  • A05-1: Replace Cors::permissive() with Cors::default().allowed_origin("https://your-kms-domain.example.com") or configure allowed origins from server config.
  • A05-3: Add #[cfg(all(not(test), feature = "insecure"))] compile_error!("insecure feature must not be enabled in production"); near the top of jwt_config.rs.

7. A06 – Vulnerable and Outdated Components

OWASP description: Components run with the same privileges as the application. If a vulnerable component is exploited it can cause serious data loss or server takeover.

7.1 Scope

AreaFiles
Workspace dependenciesCargo.toml (root) + all crate/*/Cargo.toml
Deny policydeny.toml
OpenSSL build + hashcrate/crypto/build.rs
UI dependenciesui/package.json, ui/pnpm-lock.yaml
Git history.git/

7.2 Investigation Steps

# Step 1 — Scan Rust dependencies for known CVEs (RustSec Advisory DB)
cargo audit

# Step 2 — Policy-based check: licenses, banned crates, advisories, duplicates
cargo deny check

# Step 3 — List outdated crates vs latest on crates.io
cargo outdated --workspace

# Step 4 — Count unsafe code per crate (establish risk baseline)
cargo geiger --workspace 2>/dev/null | tail -50

# Step 5 — Verify OpenSSL source is pinned and SHA-256-verified in build script
grep -A5 -B5 "openssl.*version\|OPENSSL_VERSION\|sha256\|expected_hash\|verify" \
  crate/crypto/build.rs | head -30

# Step 6 — UI dependency audit
cd ui && pnpm audit 2>/dev/null || npm audit 2>/dev/null; cd ..

# Step 7 — Git-sourced Rust deps (bypass crates.io auditing)
grep -rn "git = \|branch = \|rev = " Cargo.toml crate/*/Cargo.toml

# Step 8 — Scan full git history for accidentally committed secrets
gitleaks detect --source . 2>/dev/null | head -30 || \
  trufflehog3 --no-entropy . 2>/dev/null | head -30 || \
  echo "Run a secrets scanner manually"

7.3 Findings

Status: ⚠️ Review needed

Files inspected: Cargo.toml (workspace), deny.toml, crate/crypto/build.rs

cargo audit output (2026-04-13):

Warning: 2 vulnerabilities found!
crate:   rand  version: 0.8.5  advisory: RUSTSEC-2026-0097 (allow-listed)
crate:   rand  version: 0.9.2  advisory: RUSTSEC-2026-0097 (allow-listed)

cargo deny check: passes with warnings (advisory allow-listed in deny.toml)

IDFile:LineSeverityDescription
A06-1Cargo.toml (workspace)MediumRUSTSEC-2026-0097 affects rand 0.8.5 and rand 0.9.2 — two separate version trees coexisting in the dependency graph. Advisory covers unseeded PRNG panic under unusual usage. Suppressed rather than resolved.
A06-2Cargo.toml (workspace)Low20+ duplicate crate versions: base64 (×3: 0.13, 0.21, 0.22), hashbrown (×4: 0.12–0.15), rand (×2), opentelemetry (×2), time (×3), etc. Increases supply-chain surface and binary size.
A06-3crate/crypto/build.rs:234,274OpenSSL 3.6.2 tarball is SHA-256-verified (verify_hash()) before extraction. Version pinned by constant. Supply-chain integrity protected.
A06-4All Cargo.toml filesNo git = sourced Rust dependencies. All deps come from crates.io.
A06-5ui/pnpm-lock.yamlInfoUI npm/pnpm audit was not run during this automated pass. Manual pnpm audit --audit-level moderate recommended.
A06-6Workspace (crate/clients/wasm/Cargo.toml)Lowcargo outdated shows cosmian_kms_client_wasm requires getrandom with the js feature, but the installed version (0.4.2) no longer exposes that feature. This causes cargo outdated to fail for the WASM crate. The WASM build may silently use a mismatched getrandom version in non-browser target builds.
A06-7workspace (geiger scan)Infocargo-geiger 0.13.0 fails on this virtual workspace with a known upstream bug. Manual grep -r "unsafe " shows: crate/server/src/ (5 files, 22 uses — all OpenSSL FFI + FIPS env setup), crate/crypto/src/ (5 files, 35 uses — OpenSSL/PQC FFI), crate/server_database/src/ (0), crate/kmip/src/ (0), crate/access/src/ (0). PKCS#11 module/provider and HSM loaders contain additional unsafe as expected for C FFI. No unexpected unsafe blocks were found in core server logic.

Recommended fix:

  • A06-1: Upgrade rand to the version that resolves RUSTSEC-2026-0097. Run cargo update -p rand and verify with cargo audit.
  • A06-2: Dedup by aligning version requirements across crates using [patch] or resolving transitive dependency mismatches. Track with cargo deny check duplicates.
  • A06-5: Add cd ui && pnpm audit --audit-level moderate to CI pipeline.
  • A06-6: Align getrandom version in crate/clients/wasm/Cargo.toml to a version that still exposes the js feature gate, or remove the feature if no longer applicable.
  • A06-7 (geiger): Upgrade cargo-geiger when a version supporting Rust 2024 virtual workspaces is available. Track cargo-geiger#378.

8. A07 – Identification and Authentication Failures

OWASP description: Incorrectly implemented authentication and session management allow attackers to compromise passwords, keys, or session tokens, or to assume other users' identities.

8.1 Scope

AreaKey files
JWT middlewarecrate/server/src/middlewares/jwt/jwt_middleware.rs, jwt_token_auth.rs, jwt_config.rs
JWKS managercrate/server/src/middlewares/jwt/jwks.rs
API token authcrate/server/src/middlewares/api_token/api_token_auth.rs, api_token_middleware.rs
TLS client cert authcrate/server/src/middlewares/tls_auth.rs
AWS SigV4 authcrate/server/src/routes/aws_xks/sigv4_middleware.rs
Auth fallbackcrate/server/src/middlewares/ensure_auth.rs
insecure feature gateEverywhere gated by #[cfg(feature = "insecure")]

8.2 Investigation Steps

# Step 1 — JWT algorithm restriction: must be RS256/ES256; reject HS256 and "none"
grep -rn "Algorithm\|alg\|Validation\|decode\|jsonwebtoken\|DecodingKey" \
  crate/server/src/middlewares/jwt/ --include="*.rs"

# Step 2 — Token expiration enforcement (insecure feature relaxes this)
grep -rn "exp\|expir\|validate_exp\|insecure\|leeway" \
  crate/server/src/middlewares/jwt/ --include="*.rs"

# Step 3 — JWKS fetch URL: must be HTTPS-only; no plain HTTP fallback
grep -n "http://\|reqwest\|fetch_jwks\|jwks_uri\|jwks_url\|url" \
  crate/server/src/middlewares/jwt/jwks.rs

# Step 4 — API token comparison: must be constant-time to prevent timing attacks
grep -n "==\|compare\|ConstantTimeEq\|constant_time\|subtle\|ct_eq" \
  crate/server/src/middlewares/api_token/api_token_auth.rs

# Step 5 — TLS cert auth: CN alone is insufficient; check if full DN or SAN is used
grep -n "CommonName\|CN\|SubjectAltName\|SAN\|subject\|peer_cert\|peer_certificate" \
  crate/server/src/middlewares/tls_auth.rs

# Step 6 — Session fixation in UI auth: cookie must be HttpOnly, Secure, SameSite=Lax (OIDC requires Lax, not Strict)
grep -rn "actix.session\|Session\|cookie\|HttpOnly\|Secure\|SameSite" \
  crate/server/src/routes/ui_auth.rs crate/server/src/middlewares/ --include="*.rs"

# Step 7 — Brute-force protection: lookout for rate limiting on auth endpoints
grep -rn "rate_limit\|lockout\|backoff\|attempt\|max_fail" \
  crate/server/src/middlewares/ --include="*.rs"

# Step 8 — Semgrep auth patterns
semgrep --config p/owasp-top-ten \
  crate/server/src/middlewares/ --lang rust 2>/dev/null || true

8.3 Findings

Status: ⚠️ Review needed

Files inspected:

  • crate/server/src/middlewares/jwt/jwt_config.rs
  • crate/server/src/middlewares/api_token/api_token_auth.rs
  • crate/server/src/middlewares/tls_auth.rs
  • crate/server/src/start_kms_server.rs
  • All crate/server/src/middlewares/ files
IDFile:LineSeverityDescription
A07-1jwt_config.rs:170HighValidation::new(header.alg) — JWT validation algorithm is taken directly from the alg field of the incoming token header without any allowlist. An attacker who obtains a valid kid could craft a token with alg: HS256 and use the RSA public key as the HMAC secret (algorithm confusion / "none" alg attack vector). The jsonwebtoken crate's Validation::new initialises with the given algorithm but does NOT restrict to it unless algorithms is explicitly set.
A07-2api_token_auth.rs:126MediumAPI token comparison uses client_token == api_token.as_str() — standard == operator, which is not timing-safe. Timing differences can reveal whether the token length matches or prefix bytes are correct. A high-volume attacker on a low-latency network could exploit this to brute-force API tokens.
A07-3tls_auth.rs:147LowTLS client certificate identity uses CN (Common Name) only, not SAN (Subject Alternative Name). Wildcard CNs (*) are explicitly rejected (correct). Modern TLS stacks prefer SAN; CN-only auth may fail on strict clients.
A07-4start_kms_server.rs:759LowSession cookie SameSite is set to None (cross-site allowed). SameSite::Strict or Lax would protect against CSRF for browser-initiated requests.
A07-5jwt_config.rs:10,155MediumThe insecure feature disables all JWT validation including signature and expiration. Must not appear in production builds (see also A05-3).
A07-6All endpointsHighNo brute-force protection or account lockout on any authentication endpoint (JWT, API token, TLS). Combined with A04-2 (no rate limiting), this allows unlimited credential-guessing attempts.

Recommended fix:

  • A07-1: After calling Validation::new(alg), immediately set validation.algorithms = vec![alg] (already in scope) AND add a check that alg is in an explicit server-side allowlist (e.g. [RS256, RS384, RS512, ES256, ES384]). Reject tokens with alg: none, alg: HS256 against asymmetric keys.
  • A07-2: Replace client_token == api_token.as_str() with a constant-time comparison: use subtle::ConstantTimeEq or ring::constant_time::verify_slices_are_equal().
  • A07-4: Change SameSite::None to SameSite::Strict unless cross-site embedding of the KMS UI is required.

9. A08 – Software and Data Integrity Failures

OWASP description: Code and infrastructure that does not protect against integrity violations, including insecure deserialization, CI/CD pipeline attacks, and unsigned updates.

9.1 Scope

AreaKey files
OpenSSL download + verifycrate/crypto/build.rs
Imported object validationcrate/server/src/core/operations/import.rs
KMIP deserializercrate/kmip/src/ttlv/
Cargo lock and git depsCargo.lock, Cargo.toml
CI pipeline scripts.github/workflows/, .mise/scripts/
Nix vendor hashesnix/expected-hashes/

9.2 Investigation Steps

# Step 1 — OpenSSL build: SHA-256 check must be present and non-bypassable
grep -A5 -B5 "sha256\|checksum\|expected\|download\|verify" crate/crypto/build.rs

# Step 2 — Imported keys: are cryptographic properties validated on import?
# (RSA modulus size, EC curve OID, key material length)
grep -n "validate\|check\|verify\|length\|CryptographicLength\|Algorithm\|curve" \
  crate/server/src/core/operations/import.rs | head -30

# Step 3 — KMIP deserialization: unknown fields rejected (not silently ignored)?
grep -rn "deny_unknown_fields\|flatten\|#\[serde\|untagged\|tag" \
  crate/kmip/src/ --include="*.rs" | head -20

# Step 4 — Git-sourced or path-sourced Rust deps (integrity not guaranteed by crates.io)
grep -rn "git =\|path =\|branch =\|rev =" Cargo.toml crate/*/Cargo.toml

# Step 5 — CI scripts: unauthenticated downloads or pipe-to-shell patterns
grep -rn "curl.*sh\|wget.*sh\|pip install\|npm install" \
  .mise/scripts/ .github/workflows/ 2>/dev/null \
  | grep -v "^#\|#.*curl" | head -20

# Step 6 — Nix vendor hashes: all four variants must be present and non-trivial
ls -la nix/expected-hashes/
grep -v "^sha256-AAAA" nix/expected-hashes/*.sha256 2>/dev/null | head -10

9.3 Findings

Status: ⚠️ Review needed

Files inspected:

  • crate/server/src/middlewares/ensure_auth.rs
  • crate/kmip/src/
  • crate/server/src/core/operations/import_export.rs
  • deny.toml
IDFile:LineSeverityDescription
A08-1crate/kmip/src/kmip_0/kmip_operations.rs (all structs)LowKMIP request/response structs do not use #[serde(deny_unknown_fields)]. Unknown fields in incoming KMIP JSON/XML are silently ignored rather than rejected. An attacker can add arbitrary fields to requests without triggering any error; this makes protocol-level fuzzing harder to detect.
A08-2start_kms_server.rs:737MediumThe UI session signing key is derived from the server URL. If the server is deployed with the default or a predictable URL (e.g. http://localhost:9998), the session key is predictable, allowing cookie forgery. The fallback salt "cosmian_kms_default_ui_session_key_v1" in build_session_key() makes this exploitable when no explicit key is configured.
A08-3crate/crypto/build.rs:234,274OpenSSL tarball SHA-256 is verified before build. Prevents tampered source from being compiled in.
A08-4All Cargo.toml filesNo git = sourced deps. All Rust crates come from crates.io with version pinning.
A08-5crate/server/src/core/operations/import.rsCRL validation is performed on certificate import. Key parameter validation relies on OpenSSL's own enforcement (key size, curve OID, etc.), which is appropriate.

Recommended fix:

  • A08-1: Add #[serde(deny_unknown_fields)] to all top-level KMIP request structs where spec compliance is required, or at minimum log a warning when unknown fields are encountered.
  • A08-2: Generate and persist a random 32-byte session key on first startup (saving it to the DB or a config file), and require it to be explicitly set in production. Remove the predictable URL-derived default.

10. A09 – Security Logging and Monitoring Failures

OWASP description: Insufficient logging and monitoring allows attackers to maintain persistence, pivot, and tamper with or exfiltrate data undetected.

10.1 Scope

AreaKey files
KMIP request tracingcrate/server/src/routes/kmip.rs, routes/access.rs
Configuration secret loggingcrate/server/src/config/command_line/ (all files)
Auth failure loggingcrate/server/src/middlewares/
Privileged operation audit eventscrate/server/src/core/operations/export_get.rs, destroy.rs, revoke.rs
Error response contentcrate/server/src/routes/

10.2 Investigation Steps

# Step 1 — Secrets in config log output: DB URLs, TLS passwords, API tokens
grep -rn "tracing::\|info!\|warn!\|error!\|debug!\|trace!" \
  crate/server/src/config/ --include="*.rs" \
  | grep -i "password\|secret\|token\|key\|credential\|url\|connection"

# Step 2 — Auth middleware: failure messages must NOT include the raw token value
grep -rn "info!\|warn!\|error!\|debug!" \
  crate/server/src/middlewares/ --include="*.rs" \
  | grep -i "token\|bearer\|api.key\|password\|secret"

# Step 3 — Privileged operations logged with user identity?
# (Export, Destroy, Revoke are high-value audit events)
grep -n "tracing\|info!\|warn!\|error!" \
  crate/server/src/core/operations/export_get.rs \
  crate/server/src/core/operations/destroy.rs \
  crate/server/src/core/operations/revoke.rs 2>/dev/null | head -40

# Step 4 — Failed auth events: are 401/403 responses logged?
grep -rn "401\|403\|Unauthorized\|Forbidden\|warn!\|error!" \
  crate/server/src/middlewares/ --include="*.rs" \
  | grep -i "auth\|denied\|failed\|reject"

# Step 5 — Log injection: user-controlled strings inserted into tracing macros
# without sanitization allow log-forging via embedded newlines
grep -rn 'info!.*user\|debug!.*uid\|trace!.*tag\|warn!.*{.*}' \
  crate/server/src/core/operations/ --include="*.rs" | head -20

# Step 6 — Coverage: all KMIP operations should have an instrumentation span
grep -rn "span!\|instrument\|#\[tracing::instrument\]" \
  crate/server/src/core/operations/ --include="*.rs" | wc -l
echo "Total operation files:"
ls crate/server/src/core/operations/*.rs 2>/dev/null | wc -l

10.3 Findings

Status: ❌ Vulnerability found

Files inspected:

  • crate/server/src/config/command_line/db.rs
  • crate/server/src/config/command_line/tls_config.rs
  • crate/server/src/middlewares/jwt/jwks.rs
  • crate/server/src/routes/ui_auth.rs
IDFile:LineSeverityDescription
A09-1config/command_line/db.rs:160,168HighThe Display impl for MainDBConfig prints PostgreSQL and MySQL database_url completely unmasked. A URL like postgresql://kms_user:secretpassword@host/kms_db is logged verbatim at server startup. Any log aggregation system (Splunk, ELK, CloudWatch) will store the database password in plaintext. Redis correctly masks the password as [****].
A09-2config/command_line/tls_config.rs:81MediumTLS P12 password is masked via .replace('.', "*") — this only replaces dot characters with asterisks. A password like my_pass is logged unchanged; my.pass becomes my*pass. The masking is ineffective for most passwords.
A09-3middlewares/jwt/jwt_token_auth.rs:115LowJWT authentication failures (invalid signature, expired token) are logged only at DEBUG level. These are security-relevant events that should be logged at WARN or ERROR to ensure they appear in production monitoring dashboards.
A09-4core/operations/ (decrypt.rs, locate.rs, sign.rs)LowUser-controlled strings (uid, user) are embedded in tracing macro log calls without sanitization. An attacker can inject newline characters into a UID field to insert fake log records.
A09-5core/operations/export_get.rs, destroy.rsExport and Destroy operations are logged with both uid and user at INFO level. Privileged operation audit trail is present.
A09-6routes/ui_auth.rsui_oidc_client_secret is masked as **** in Display output. OIDC secret does not leak into logs.

Recommended fix:

  • A09-1: Critical — Mask the password component in database URLs before logging. Parse the URL, replace the password with [****], then log. Example: use the url crate to parse and rebuild. Fix the Display impl for MainDBConfig::PostgreSQL and MainDBConfig::MySQL.
  • A09-2: Fix the P12 password masking to replace all characters: .repeat("*", password.len()) or simply log "[****]" as a constant.
  • A09-3: Change JWT auth failure tracing to tracing::warn! and include the source IP address.
  • A09-4: Sanitize or percent-encode uid and user strings before embedding them in log calls. Consider using tracing structured fields (uid = %uid) and ensuring the log formatter strips control characters.

11. A10 – Server-Side Request Forgery (SSRF)

OWASP description: SSRF flaws occur when a web application fetches a remote resource without validating the user-supplied URL, allowing an attacker to coerce outbound requests to unexpected destinations (cloud metadata endpoints, internal services, etc.).

11.1 Scope

AreaKey files
JWKS URL fetchcrate/server/src/middlewares/jwt/jwks.rs
Google CSE callbackscrate/server/src/routes/google_cse/
AWS XKS outbound callscrate/server/src/routes/aws_xks/
Azure EKM connectionscrate/server/src/routes/azure_ekm/
HTTP client constructioncrate/clients/client/src/

11.2 Investigation Steps

# Step 1 — Enumerate all outbound HTTP client instantiations
grep -rn "reqwest::Client\|reqwest::get\|Client::new\|ClientBuilder\|hyper::Client" \
  crate/ --include="*.rs" | grep -v "test\|#\[cfg(test"

# Step 2 — JWKS URL: validated / allowlisted, or taken verbatim from config?
grep -n "jwks_uri\|jwks_url\|fetch\|get\|url\|https\|http" \
  crate/server/src/middlewares/jwt/jwks.rs

# Step 3 — User-controlled URL fields in KMIP extensions or attributes
# (an attacker could embed a URL in a KMIP extension field)
grep -rn "url\|http\|endpoint\|callback\|webhook" \
  crate/server/src/core/operations/ --include="*.rs" \
  | grep -i "user\|request\|input\|attribute\|extension"

# Step 4 — Google CSE: user-controlled redirect or callback URLs?
grep -rn "redirect\|callback\|url\|uri" \
  crate/server/src/routes/google_cse/ --include="*.rs"

# Step 5 — HTTP client redirect policy: SSRF via 301 redirect chain
grep -rn "redirect\|follow_redirect\|redirect_policy\|max_redirect" \
  crate/ --include="*.rs"

# Step 6 — Outbound URL allowlists
grep -rn "allowlist\|whitelist\|allowed_url\|valid_host\|parse.*url\|Url::parse" \
  crate/ --include="*.rs" | grep -v test | head -15

11.3 Findings

Status: ⚠️ Review needed

Files inspected:

  • crate/server/src/middlewares/jwt/jwks.rs
  • crate/server/src/routes/ui_auth.rs
  • crate/clients/client/src/http_client/tls.rs
  • crate/clients/ckms/src/config.rs
  • All crate/server/src/routes/ URL-consuming code
IDFile:LineSeverityDescription
A10-1clients/ckms/src/config.rs:115Mediumaccept_invalid_certs: true is hardcoded in the CLI's KmsClientConfig::default(). This disables outbound TLS certificate validation for CLI-to-KMS connections. While the CLI is not server-side code, cosmian_kms_client is also used in integration tests and potentially in embedded contexts where this default silently bypasses TLS chain validation.
A10-2middlewares/jwt/jwks.rsLowreqwest::Client::new() is used for JWKS endpoint fetching. By default reqwest follows HTTP redirects. The JWKS URL comes from admin configuration (jwt_issuer_uri), not from user input at request time. If an admin passes an internal URL as the JWKS issuer, the server could fetch internal endpoints (low-risk SSRF). No redirect allowlist or redirect::none() is applied.
A10-3routes/ui_auth.rsLowSame pattern: reqwest::Client::new() used for OAuth token exchange, following redirects without restriction.
A10-4All server routesNo user-controlled URL parameters are used for server-side HTTP fetches at request time. JWKS/OIDC URLs come from static admin configuration only. The SSRF surface is limited to the initial server startup configuration.
A10-5routes/aws_xks/, routes/azure_ekm/, routes/google_cse/Enterprise routes do not make outbound requests based on incoming request data.

Recommended fix:

  • A10-1: Change CLI default to accept_invalid_certs: false. Provide an explicit --insecure flag or COSMIAN_KMS_NO_VERIFY_CERT=true env var that must be explicitly set.
  • A10-2 / A10-3: Build the reqwest client with .redirect(reqwest::redirect::Policy::none()) or restrict to a maximum of 1 redirect, and add a URL scheme allowlist (only https://).

12. EXT-0 – KMS Own Authorization System

OWASP ASVS V4 (Access Control) The KMS implements a custom, multi-layer authorization model that sits on top of KMIP. This section audits the full call chain from HTTP request to permission decision, covering privilege escalation paths, bypass mechanisms, and delegation controls.

12.1 Architecture

Every KMIP operation follows this authorization chain:

HTTP request
  └─ TlsAuthMiddleware | JwtMiddleware | ApiTokenMiddleware
       └─ EnsureAuthMiddleware  (fallback: uses default_username)
            └─ routes/kmip.rs  →  kms.get_user(&req)
                 │  ↑ force_default_username=true → ALL identities silently discarded
                 └─ dispatch(kms, ttlv, user)  →  operation handler
                      └─ retrieve_object_for_operation(uid, op, kms, user)
                           └─ user_has_permission(user, owm, op, kms)
                                ├─ SHORTCUT  user == owm.owner()  →  true  (owner bypass)
                                ├─ IMPLICIT  ops.contains(Get)   →  ALL ops allowed
                                └─ DB query  list_user_operations_on_object(uid, user)
                                        ├─ SQL: WHERE object_id = uid   AND user_id = user
                                        └─     UNION object_id = "*"  AND user_id = user

Key design decisions with security implications:

MechanismLocationRisk if abused
Owner bypassuser_has_permission()Ownership must be immutable post-creation
Get → all operationsuser_has_permission()Anyone with Get can Encrypt/Decrypt/Sign/Export
Wildcard "*" stores Create permDB schemaUser with Create on "*" can create unlimited objects
force_default_username=truekms.get_user()Discards all user identities — complete authorization bypass
privileged_users listclap_config.rsMembers bypass Create/Import permission checks
EnsureAuth fallbackensure_auth.rsDefault single-user mode if no auth is configured

12.2 Scope

AreaKey files
Core permission gatecrate/server/src/core/retrieve_object_utils.rs
KMS identity resolution & delegationcrate/server/src/core/kms/permissions.rs
Permission data typescrate/access/src/access.rs
SQL permission queriescrate/server_database/src/stores/sql/ — permissions table
Redis permission storecrate/server_database/src/stores/redis/permissions.rs
Privilege config (force_default_username, privileged_users)crate/server/src/config/command_line/clap_config.rs
Create / Import authorizationcrate/server/src/core/operations/create.rs, import.rs, register.rs
Delegation controlscrate/server/src/core/kms/permissions.rsgrant_access(), revoke_access()

12.3 Investigation Steps

# Step 1 — Map every bypass mechanism and their activation conditions
grep -n "force_default_username\|privileged_users\|default_username" \
  crate/server/src/config/command_line/clap_config.rs \
  crate/server/src/core/kms/permissions.rs

# Step 2 — Owner bypass: trace user_has_permission() — verify the owner check
# cannot be spoofed (e.g. owners set at Create time, never updated)
grep -n "owner\|is_object_owned_by\|user.*==.*owner\|owner.*==" \
  crate/server/src/core/retrieve_object_utils.rs \
  crate/server/src/core/kms/permissions.rs

# Step 3 — "Get implies all operations": find the implicit grant and verify scope
grep -n "KmipOperation::Get\|contains.*Get\|implies\|all.*ops" \
  crate/server/src/core/retrieve_object_utils.rs

# Step 4 — Wildcard "*" permission for Create: only privileged_users may grant it
grep -n '"\\*"\|wildcard\|Create\|privileged\|grant_access\|is_create' \
  crate/server/src/core/kms/permissions.rs \
  crate/server/src/core/operations/create.rs \
  crate/server/src/core/operations/import.rs

# Step 5 — Non-owner grant delegation: is_object_owned_by() enforced in grant_access()?
grep -A10 "fn grant_access" crate/server/src/core/kms/permissions.rs | head -20

# Step 6 — Self-grant prevention: a caller must not grant themselves extra permissions
grep -n "user_id.*owner\|owner.*user_id\|grant.*self\|access.user_id" \
  crate/server/src/core/kms/permissions.rs

# Step 7 — KMIP lifecycle state filter: Destroyed / Compromised block operations
grep -n "Destroyed\|Compromised\|PreActive\|Deactivated\|state.*check\|filter.*state" \
  crate/server/src/core/retrieve_object_utils.rs

# Step 8 — Public endpoints: verify /health, /version, /server-info leak no data
grep -rn "health\|version\|server.info\|configure\|service\|wrap" \
  crate/server/src/start_kms_server.rs | head -30
# Read actual health and version handler to confirm response content
cat crate/server/src/routes/health.rs 2>/dev/null | head -60

# Step 9 — Enterprise routes: verify AWS/Azure/Google/DKE routes cannot call
# standard KMIP operations using their own auth as a bypass
grep -rn "dispatch\|kms\.\|KMS\b" \
  crate/server/src/routes/aws_xks/ \
  crate/server/src/routes/azure_ekm/ \
  crate/server/src/routes/google_cse/ \
  crate/server/src/routes/ms_dke/ --include="*.rs" | head -30

# Step 10 — Redis PermTriple: concurrent grant + revoke atomicity
grep -n "PermTriple\|insert\|delete\|transaction\|atomic\|pipeline\|multi" \
  crate/server_database/src/stores/redis/permissions.rs | head -20

# Step 11 — EnsureAuth: confirm it is a last-resort fallback, not a global bypass
cat crate/server/src/middlewares/ensure_auth.rs

12.4 Checklist

  • force_default_username=true cannot be set via any unauthenticated endpoint or environment variable injection
  • privileged_users list is logged at startup so admins can detect unauthorized changes
  • Get → all-operations implicit grant is intentional, documented, and cannot cross user boundaries
  • A non-owner holding Get permission cannot call grant_access() to escalate other users
  • A user cannot grant Create permission to themselves or others unless they are in privileged_users
  • /health, /version, /server-info return no object metadata, user identities, or internal state
  • Enterprise routes (XKS, EKM, CSE, DKE) authenticate independently and cannot reach standard KMIP key material without passing the full KMIP auth chain
  • KMIP lifecycle transitions from Compromised or Destroyed are irreversible and enforced at DB level
  • Redis PermTriple updates are atomic (no TOCTOU between concurrent grant and revoke requests)

12.5 Findings

Status: ⚠️ Review needed

Files inspected:

  • crate/server/src/core/retrieve_object_utils.rs
  • crate/server/src/core/kms/permissions.rs
  • crate/server/src/config/command_line/clap_config.rs (for force_default_username)
IDFile:LineSeverityDescription
EXT0-1retrieve_object_utils.rs:191Mediumpermissions.contains(&KmipOperation::Get) is used as the universal "has some access" check. Any user with Get permission on an object is treated as having permission for ALL operations on it (Encrypt, Decrypt, Sign, Verify, GetAttributes, etc.). This is an intentional design decision but undocumented as a security policy. It makes permission grants broader than the receiver may expect.
EXT0-2core/kms/permissions.rs:23–100grant_access() correctly enforces: (1) only the owner can grant, (2) Create can only be granted by privileged users to non-privileged users, (3) self-grant is prevented. Logic is sound.
EXT0-3config/command_line/clap_config.rsforce_default_username defaults to false. When false, the authenticated user's identity is used. No anonymous or identity-collapse by default.
EXT0-4config/command_line/clap_config.rsprivileged_users defaults to None (empty list). Privilege escalation via config is absent by default.
EXT0-5core/kms/permissions.rs — wildcard "*"The "*" wildcard Create permission is documented and guarded: only privileged_users can grant Create, and it cannot be granted to another privileged user. Correct.

Recommended fix:

  • EXT0-1: Document the Get-implies-all-operations policy explicitly in the KMIP operation access control spec and in operator documentation. Consider adding a fine-grained per-operation permission check (separate Encrypt, Decrypt, Sign permissions) for objects where the Get permission holder should not have crypto access. This is a design-level decision.

13. EXT-1 – Cryptographic Key Lifecycle & Zeroization

OWASP ASVS V6 (Stored Cryptography) Sensitive cryptographic material (private keys, AES keys, shared secrets) must be zeroized from memory immediately after use. Residual key material in heap memory enables cold-boot and process-dump attacks.

13.1 Scope

AreaKey files
Key wrapping cachecrate/server/src/core/wrapping/wrap.rs
Key unwrap pathscrate/server/src/core/wrapping/unwrap.rs
PKCS#11 key objectscrate/clients/pkcs11/provider/src/kms_object.rs
Crypto operationscrate/crypto/src/
KMIP key material typescrate/kmip/src/KeyMaterial, EncryptedKeyBlock

13.2 Investigation Steps

# Step 1 — Baseline: count all zeroize usages in the workspace
grep -rn "zeroize\|Zeroizing\|ZeroizeOnDrop\|SecretBox" \
  crate/ --include="*.rs" | wc -l

# Step 2 — Find Vec<u8> likely holding key material WITHOUT Zeroizing<> wrapper
# Focus on operations that touch raw key bytes
grep -rn "Vec<u8>\|Box<\[u8\]>\|key_bytes\|key_material\|private_key\|secret_key" \
  crate/server/src/core/operations/ --include="*.rs" \
  | grep -v "Zeroizing\|zeroize\|#\[derive.*Zeroize\|//\|test_" | head -30

# Step 3 — Wrapping cache lifecycle: entries must be zeroized on eviction and drop
grep -n "cache\|remove\|evict\|drop\|DashMap\|HashMap\|clear\|Zeroizing" \
  crate/server/src/core/wrapping/wrap.rs

# Step 4 — KMIP type zeroization: KeyMaterial and EncryptedKeyBlock implement Zeroize?
grep -rn "impl Zeroize\|impl Drop\|#\[derive.*Zeroize\|ZeroizeOnDrop" \
  crate/kmip/src/ --include="*.rs"

# Step 5 — Key bytes in error messages (could surface in logs)
grep -rn 'format!.*\bkey\b\|error!.*\bkey\b\|warn!.*\bkey\b' \
  crate/server/src/core/operations/ --include="*.rs" | head -15

# Step 6 — PKCS#11 provider: key material lifecycle before drop
grep -n "Zeroizing\|zeroize\|drop\|key_value\|private\|CK_BYTE" \
  crate/clients/pkcs11/provider/src/kms_object.rs | head -30

# Step 7 — Unsafe block inventory (potential for bypassing Drop guarantees)
cargo geiger --workspace --output-format Ratio 2>/dev/null | head -30

13.3 Findings

Status: ⚠️ Review needed

Files inspected:

  • crate/server/src/core/operations/derive_key.rs
  • crate/server/src/core/wrapping/wrap.rs
  • crate/crypto/src/crypto/symmetric/symmetric_ciphers.rs
  • crate/server/src/core/operations/mac.rs
IDFile:LineSeverityDescription
EXT1-1operations/derive_key.rs:397,421Mediumderive_key() returns Vec<u8> for the wrapped/derived key material. The caller stores this in a ByteString which is not Zeroizing. If the derived key is sensitive (a root key, a DEK), it remains in heap memory as plaintext until the allocator reuses that memory. 96 other files in the codebase correctly use Zeroizing<Vec<u8>>.
EXT1-2core/wrapping/wrap.rs (unwrap cache)MediumThe DashMap/HashMap used as the unwrapping cache stores unwrapped (plaintext) key objects in process RAM. No TTL, no max-size, no explicit zeroization on eviction is visible in wrap.rs. A memory dump of the server process exposes all recently unwrapped keys.
EXT1-3crypto/src/crypto/ (96 files)Broad use of Zeroizing<T> and ZeroizeOnDrop trait implementations throughout the crypto layer. Key material in bytes_set_de.rs:137 is wrapped in ZeroizeOnDrop. Good coverage.
EXT1-4operations/mac.rs:22HMAC output (Vec<u8>) is not secret key material; does not require zeroization. The HMAC key is a borrowed slice — caller is responsible for its lifecycle. No issue in the MAC module itself.

Recommended fix:

  • EXT1-1: Change the derive_key return type to Zeroizing<Vec<u8>> (or wrap the result before returning). Propagate Zeroizing through ByteString usage if that type supports it.
  • EXT1-2: Add a TTL-based eviction (e.g. 60 seconds) or use mlock/Zeroizing wrappers on cache values. On eviction, call zeroize() before dropping. Consider using a dedicated memory-safe cache like secrecy::Secret<T> for cached key objects.

14. EXT-2 – Denial of Service / Resource Exhaustion

OWASP ASVS V12 / CWE-400 All user-controlled resource consumption must be bounded: request body size, TTLV nesting depth, database result set cardinality, and concurrent connection count.

14.1 Scope

AreaKey files
Actix-web payload limitcrate/server/src/start_kms_server.rs
TTLV binary parser depthcrate/kmip/src/ttlv/wire/ttlv_bytes_deserializer.rs
TTLV XML parser depthcrate/kmip/src/ttlv/xml/parser.rs
Locate result paginationcrate/server_database/src/
Batch request handlingcrate/server/src/core/operations/dispatch.rs
Rate limitingcrate/server/src/middlewares/

14.2 Investigation Steps

# Step 1 — HTTP body size limit (Actix default is 256 KB)
grep -n "LimitPayloadSize\|max_payload_size\|PayloadConfig\|max_body\|content_length" \
  crate/server/src/start_kms_server.rs crate/server/src/routes/kmip.rs

# Step 2 — TTLV binary recursion depth guard before recursive call
grep -n "depth\|recursion\|max_depth\|stack\|recursive\|fn parse\|fn decode\|fn from_ttlv" \
  crate/kmip/src/ttlv/wire/ttlv_bytes_deserializer.rs

# Step 3 — TTLV XML: entity expansion (billion laughs), XXE, recursive entities
grep -n "expand_entities\|entity\|xml_parse\|XmlParser\|depth\|recursive\|ENTITY" \
  crate/kmip/src/ttlv/xml/parser.rs

# Step 4 — Locate result set: is it bounded by max_items or a database LIMIT?
grep -n "Locate\|limit\|max_results\|LIMIT\|pagination\|fetch_all\|count\|MaxItems" \
  crate/server/src/core/operations/locate.rs 2>/dev/null
grep -n "SELECT\|LIMIT\|offset\|page" \
  crate/server_database/src/stores/sql/locate_query.rs | head -20

# Step 5 — KMIP batch requests: unbounded RequestBatchItem count?
grep -n "RequestBatchItem\|batch_items\|iter\|for.*batch\|MaximumResponseSize" \
  crate/server/src/core/operations/dispatch.rs | head -20

# Step 6 — Rate limiting on KMIP endpoint
grep -rn "RateLimiter\|Governor\|leaky\|throttle\|rate\|max_conn" \
  crate/server/src/middlewares/ --include="*.rs"

# Step 7 — Allocation without bound (Semgrep TTLV parser)
semgrep --config p/rust \
  crate/kmip/src/ttlv/ --lang rust 2>/dev/null \
  | grep -i "alloc\|capacity\|len\|size\|vec" || true

14.3 Findings

Status: ❌ Vulnerability found

Files inspected:

  • crate/server/src/start_kms_server.rs
  • crate/kmip/src/ttlv/wire/ttlv_bytes_deserializer.rs
  • crate/kmip/src/ttlv/xml/parser.rs
  • crate/server/src/core/operations/locate.rs
IDFile:LineSeverityDescription
EXT2-1start_kms_server.rs:767-768HighHTTP request body limit is 10_000_000_000 bytes (10 GB) — same as A04-1. Any client can send a 10 GB payload, exhausting server RAM and causing OOM / process crash. No pre-read size check.
EXT2-2ttlv_bytes_deserializer.rs:56Highread_ttlv() recurses without a depth counter. A crafted deeply-nested TTLV binary blob (e.g. 100 000 levels of nested Structure items) will overflow the stack and crash the process (stack overflow). This code path is reached before authentication.
EXT2-3ttlv/xml/parser.rs:196MediumThe TTLV XML parser has a depth counter variable but the parse loop contains no maximum depth enforcement — the counter is incremented but never checked against a limit. Deep XML nesting can cause the same stack overflow as the binary parser.
EXT2-4operations/locate.rs:96-101MediumMaximumItems in a Locate request is optional and fully client-controlled. When absent, all matching objects are returned. A user who owns many objects (or to whom many are shared) can trigger a very large DB query and large response, consuming CPU, DB, and network resources.
EXT2-5All middlewaresHighNo rate limiting (see A04-2). Without request rate limits, all DoS vectors listed above are amplifiable by a single unauthenticated client flooding the server.

Recommended fix:

  • EXT2-2 / EXT2-3: Add depth tracking to both parsers with const MAX_TTLV_DEPTH: usize = 64 and early-return errors when exceeded. This is the highest priority fix as it is reachable before authentication.
  • EXT2-1: Reduce payload limit to a realistic maximum (32–64 MB). See A04-1 fix.
  • EXT2-4: Cap MaximumItems server-side: if absent or > configured max_locate_items (default 1 000), clamp the SQL LIMIT to max_locate_items.
  • EXT2-5: Implement rate limiting (see A04-2 fix).

15. Remediation Priority Matrix

All confirmed findings from sections 2–14, ordered by severity.

IDSectionFile:LineSeverityCVSS (est.)StatusAssigned toDue
A03-2 / EXT2-2A03 Injection / EXT-2 DoSttlv_bytes_deserializer.rs:56High7.5✅ Fixed2026-04-14
A04-1 / EXT2-1A04 Insecure Design / EXT-2 DoSstart_kms_server.rs:767-768High7.5✅ Fixed2026-04-14
A04-2 / EXT2-5A04 Insecure Design / EXT-2 DoSAll server middlewaresHigh7.5✅ Fixed2026-04-14
A07-1A07 Auth Failuresjwt_config.rs:170High8.1✅ Fixed2026-04-14
A07-6A07 Auth FailuresAll auth endpointsHigh7.3✅ Fixed2026-04-14
A09-1A09 Loggingconfig/command_line/db.rs:160,168High7.2✅ Fixed2026-04-14
A02-3 / EXT1-2A02 Crypto / EXT-1 Zeroizationcore/wrapping/wrap.rsMedium5.5Open
A03-3 / EXT2-3A03 Injection / EXT-2 DoSttlv/xml/parser.rs:196Medium5.9✅ Fixed2026-04-14
A04-3 / EXT2-4A04 Insecure Design / EXT-2 DoSoperations/locate.rs:96-101Medium5.3✅ Fixed2026-04-14
A05-1 / A01-1A05 Misconfiguration / A01 Accessstart_kms_server.rs:776–962Medium5.4✅ Fixed2026-04-14
A05-3 / A07-5A05 Misconfiguration / A07 Authjwt_config.rs:10,155Medium5.0⚠️ Mitigated2026-04-14
A07-2A07 Auth Failuresapi_token_auth.rs:126Medium5.9✅ Fixed2026-04-14
A08-2A08 Integritystart_kms_server.rs:737Medium4.3✅ Fixed2026-04-14
A09-2A09 Loggingconfig/command_line/tls_config.rs:81Medium4.0✅ Fixed2026-04-14
A10-1A10 SSRFclients/ckms/src/config.rs:115Medium4.8⚠️ Mitigated2026-04-14
EXT1-1EXT-1 Zeroizationoperations/derive_key.rs:397,421Medium4.0✅ Fixed2026-04-14
A02-2A02 Cryptoactions/mac.rs:17Low3.1Open
A06-1A06 ComponentsCargo.toml (rand advisory)Low3.7Open
A06-2A06 ComponentsCargo.toml (dup crates)Low2.0Open
A07-3A07 Auth Failurestls_auth.rs:147Low2.0Open
A07-4A07 Auth Failuresstart_kms_server.rs:759Low2.6✅ Fixed2026-04-14
A08-1A08 IntegrityKMIP structsLow2.0Open
A09-3A09 Loggingjwt_token_auth.rs:115Low2.0✅ Fixed2026-04-14
A09-4A09 Loggingcore/operations/Low2.0Open
A10-2 / A10-3A10 SSRFjwks.rs, ui_auth.rsLow2.3✅ Fixed2026-04-14
EXT0-1EXT-0 Authorizationretrieve_object_utils.rs:191Low3.1Open
A02-1A02 Cryptosymmetric_ciphers.rs:429,784InfoN/AClosed (FP)
A01-2A01 Accessroutes/health.rs:47InfoN/AOpen

Severity definitions (CVSS 3.1 base score ranges)

SeverityCVSS scoreResponse SLA
Critical9.0 – 10.0Fix before next release; block merge
High7.0 – 8.9Fix within 1 sprint
Medium4.0 – 6.9Fix within 3 sprints
Low0.1 – 3.9Schedule in backlog
InfoN/ADocument / accept risk

16. Report Sign-off

RoleNameSignatureDate
Lead auditorGitHub Copilot (automated)2026-04-13
Security reviewer(human review pending)
Engineering lead(human review pending)

Automated audit completed: 2026-04-13 Next audit scheduled: Quarterly, or on every major dependency or KMIP spec change.

Review frequency: Quarterly, or on every major dependency update / KMIP spec change.

Summary statistics

SeverityCount
High6
Medium10
Low10
Info / FP3
Total29

No Critical (CVSS ≥ 9.0) findings were identified. The most urgent items are the TTLV recursion depth limit (stack-overflow DoS reachable before auth), 10 GB payload limit (memory exhaustion), missing rate limiting, JWT algorithm confusion (authentication bypass potential), and database credential leak in logs.

Eviden KMS — Multi-Framework Security Audit

Document type: Security Audit Plan & Report Frameworks: NIST CSF 2.0 / SSDF (SP 800-218) · CIS Controls v8 · ISO/IEC 27034 · OSSTMM Repository: Eviden/kms Workspace root: crate/ (Rust workspace) + ui/ (React/TypeScript) Audit script: .mise/scripts/audit/multi_framework.sh — run bash .mise/scripts/audit/multi_framework.sh to reproduce all automated checks

See also .mise/scripts/audit/audit.sh for the unified entry-point that runs both OWASP and multi-framework checks.


Table of Contents

  1. Scope & Methodology
  2. NIST Cybersecurity Framework 2.0
  3. NIST SSDF SP 800-218
  4. CIS Controls v8
  5. ISO/IEC 27034 — Application Security
  6. OSSTMM
  7. Cross-Framework Remediation Matrix
  8. Automated Audit Checks
  9. Report Sign-off

1. Scope & Methodology

1.1 In-scope components

ComponentTechnologyRisk level
KMS server binary (cosmian_kms)Rust (Actix-web, tokio)Critical
KMIP protocol engine (cosmian_kmip)RustHigh
JWT/JWKS authentication middlewareRust (jsonwebtoken, reqwest)High
Database backends (SQLite, PostgreSQL, Redis-findex)Rust (sqlx, redis)High
CLI client (ckms)Rust (clap)Medium
WASM clientRust → WASMMedium
Web UIReact 19 / TypeScript / Ant DesignMedium
OpenSSL 3.6.x (custom build)C (bundled, vendored)High

1.2 Out of scope

  • Physical HSM devices (Utimaco, Proteccio, Crypt2Pay) — covered by vendor certifications
  • Third-party cloud services (AWS XKS, Azure EKM, GCP CMEK) — covered by cloud-provider SLAs
  • Infrastructure layer (OS, network) — covered by deployment hardening guides

1.3 Methodology

This audit combines:

  1. Automated static analysiscargo audit, cargo deny, semgrep, gitleaks, grep-based pattern checks (orchestrated by .mise/scripts/audit/multi_framework.sh)
  2. Manual code review — targeted review of authentication, cryptographic key handling, input parsing, and inter-service communication paths
  3. Integration testing — Rust #[test] modules in crate/clients/clap/src/tests/security/ and crate/server/src/middlewares/jwt/jwks.rs
  4. Control gap analysis — mapping findings to each framework's control catalogue

2. NIST Cybersecurity Framework 2.0

NIST CSF 2.0 organises controls into six functions: Govern, Identify, Protect, Detect, Respond, Recover.

2.1 GOVERN (GV)

ControlRequirementStatusEvidence
GV.OC-01Organisational context understoodSECURITY.md, CONTRIBUTING.md define security scope and disclosure process
GV.OC-05Legal/regulatory requirements trackedFIPS 140-3 documentation maintained at certifications_and_compliance/fips.md
GV.RM-01Risk management strategyOWASP audit (owasp_security_audit.md) + this document
GV.SC-06Supplier/component vettingdeny.toml (bans, licenses); deny.toml bans serde_json::unbounded_depth

2.2 IDENTIFY (ID)

ControlRequirementStatusEvidence
ID.AM-01Asset inventorySBOM at sbom/ + CBOM at cbom/
ID.AM-02Cryptographic inventoryCBOM (cbom/cbom.cdx.json); NIST-approved algorithms documented
ID.RA-01Vulnerability identificationcargo audit in CI; advisory DB updated weekly
ID.RA-06Risk response prioritisedOWASP remediation priority matrix; see §7

2.3 PROTECT (PR)

ControlRequirementStatusEvidence
PR.AA-01AuthenticationOAuth2/OIDC via JWKS; JWT algorithm allowlist (RS256/PS256/ES256 only)
PR.AA-03Multi-factor authentication supported⚠️MFA delegated to OIDC provider; KMS does not enforce MFA directly
PR.AC-01Access control policyPer-object KMIP access control in crate/access/; privileged_users config
PR.AC-03Protected remote accessTLS mutual auth supported; JWKS HTTPS guard (startup validation)
PR.DS-01Data-at-rest protectionDatabase encrypted by wrapping keys; FIPS-grade AES-256
PR.DS-02Data-in-transit protectionTLS 1.2+ required; no legacy TLS 1.0/1.1 configuration
PR.DS-10Data destructionZeroize applied to key material; Destroy KMIP operation
PR.PS-01Configuration managementTOML config file; documented defaults; no hard-coded secrets

2.4 DETECT (DE)

ControlRequirementStatusEvidence
DE.CM-01Networks monitored⚠️OTLP/Prometheus metrics exported; alerting rules are deployment-specific
DE.CM-03Personnel activity monitoredAll KMIP operations logged via tracing, with user identity
DE.CM-09Computing hardware and software monitoredOTEL metrics (request counts, error rates, latency)

2.5 RESPOND (RS) & RECOVER (RC)

ControlRequirementStatusEvidence
RS.CO-02Incidents reportedSECURITY.md — responsible disclosure process
RC.RP-01Recovery plan⚠️Backup/restore procedures are deployment-specific; SQLite WAL docs available

3. NIST SSDF SP 800-218

SSDF organises secure development practices into four groups: Prepare (PO), Protect (PS), Produce (PW), Respond (RV).

3.1 PO — Prepare the organisation

PracticeKMS implementationStatus
PO.1 — Security requirementsOWASP audit plan; FIPS certification requirements
PO.3 — Secure development environmentNix reproducible builds; vendored OpenSSL
PO.5 — Security trainingCONTRIBUTING.md coding rules; AI agent instructions

3.2 PS — Protect the software

PracticeKMS implementationStatus
PS.1 — Code integritySigned releases; GPG-signed packages; git tags
PS.2 — Supply chaindeny.toml bans + license checks; vendored deps
PS.3 — Archive and protect releasesGPG-signed deb/rpm/dmg; GitHub Releases

3.3 PW — Produce well-secured software

PracticeSub-practiceKMS implementationStatus
PW.1Design aligned with requirementsKMIP 2.1 compliant; FIPS 140-3 mode
PW.4.4Validate inputsTTLV depth limit (MAX_TTLV_DEPTH = 64); XML depth limit; JSON depth via serde_json built-in
PW.5.1Ban vulnerable componentsserde_json::unbounded_depth banned in deny.toml
PW.6.1Use vetted librariesring, openssl, jsonwebtoken — all widely audited
PW.7.1Avoid unsafe practicesunsafe count < 30; clippy::unwrap_used enforced in #[deny]
PW.7.2Document unsafe usageAll unsafe blocks in FIPS-interface FFI wrappers; commented
PW.8.1Test during developmentUnit + integration + E2E tests; Playwright UI tests
PW.8.2Code reviewPR reviews required; AI agent assisted review

3.4 RV — Respond to vulnerabilities

PracticeKMS implementationStatus
RV.1.1 — Monitor vulnerabilitiescargo audit in CI (weekly advisory DB sync)
RV.1.2 — Deny HIGH/CRITICAL CVEscargo audit --deny warnings in CI; breaks build
RV.2.2 — Assess and prioritiseOWASP remediation priority matrix
RV.3.3 — Test remediationRegression tests added for every finding (see test files)

4. CIS Controls v8

Relevant CIS Controls mapped to KMS implementation:

4.1 Inventory & Configuration

CIS ControlDescriptionKMS status
CIS 1 — Asset inventorySBOM + CBOM generated and committed
CIS 2 — Software asset inventoryCargo.lock / pnpm-lock.yaml pinned; reproducible builds
CIS 4.1 — Secure configurationDefault bind 0.0.0.0; TLS required in production; serde_json::unbounded_depth banned
CIS 4.2 — Default account hardeningNo default credentials; OIDC-mandatory in production mode

4.2 Access Control

CIS ControlDescriptionKMS status
CIS 5 — Account managementPer-user KMIP object ownership; privileged_users whitelist
CIS 6 — Access control managementGrant/Revoke KMIP operations; access-control tests (security/access_control.rs)
CIS 12.2 — Network traffic filteringCORS restricted (no wildcard origin by default)
CIS 13.9 — Encrypt data in transitTLS 1.2+ required; legacy TLS absent from config
CIS 13.10 — Prevent SSRFJWKS HTTP client Policy::none() (no redirect following)
CIS 16 — Application software securityJWKS HTTPS startup guard; JWT algorithm allowlist

4.3 Continuous monitoring

CIS ControlDescriptionKMS status
CIS 8.2 — Collect audit log datatracing structured logs; OTLP export; rolling log option
CIS 8.5 — Collect detailed audit logsUser identity logged with every KMIP operation
CIS 10.2 — Protection of data backupsSQLite WAL mode; documented restore procedure⚠️

5. ISO/IEC 27034 — Application Security

ISO 27034 defines Organisational Normative Frameworks (ONF) and Application Normative Frameworks (ANF) with four assurance levels (L1–L4).

5.1 Assurance level mapping

LevelRequirementKMS evidence
L1 — BasicDocumented security requirementsOWASP audit; this document; SECURITY.md
L2 — StandardInput validation; CORS; error handlingTTLV depth limits; CORS tests; structured error types
L3 — AdvancedAccess control; audit trails; key lifecycleKMIP ACL; tracing logs; Destroy + zeroization
L4 — Highly secureFormal verification of cryptographic propertiesFIPS 140-3 mode (validated provider); algorithm allowlist

5.2 Application Normative Framework controls

ANF controlDescriptionKMS implementationStatus
ANF-1 — Input validationAll KMIP inputs validated before processingTTLV parser depth limit; serde type validation
ANF-2 — AuthenticationOIDC token validated on every requestJwksManager verifies signature, expiry, algorithm
ANF-3 — AuthorisationObject-level KMIP permissions checkedcrate/access/ module; GetAttributes checks
ANF-4 — Cryptographic controlsFIPS-approved algorithms only in default modeFIPS provider; algorithm policy documented
ANF-5 — Audit loggingAll security-relevant events loggedtracing at INFO/WARN/ERROR; operation ID tracked
ANF-6 — Error handlingErrors do not expose internal detailsKmsError sanitised before HTTP response
ANF-7 — Dependency managementRegular CVE scanningcargo audit in CI; cargo deny on every PR
ANF-8 — Secure communicationsTransport encryption enforcedTLS 1.2+; JWKS HTTPS-only startup guard

6. OSSTMM

The Open Source Security Testing Methodology Manual (OSSTMM) defines five security channels: Human, Physical, Wireless, Telecommunications, Data Networks. The KMS is primarily a data-network application.

6.1 Data Networks channel

OSSTMM sectionTest areaFindingStatus
5.1 — PostureServer does not broadcast version by defaultConfirmed: no Server: header in default config
5.3 — EnumerationKMIP endpoint returns 422 (not 404) for invalid bodiescurl -X POST -d '{}' .../kmip/2_1 → 422
5.4 — VisibilitySensitive fields masked in debug outputDB URL password → ****; TLS passphrase masked
5.6 — AccessCORS headers do not reflect attacker originCORS tests cors_config.rs (C1–C3) confirm
5.7 — TrustJWKS source must use HTTPSvalidate_jwks_uris_are_https() enforced at startup
5.8 — ControlsSSRF via open redirect blockedPolicy::none() on JWKS client; SR1 test confirms
5.10 — ProcessBatch request count mismatch handled gracefullyBatch abuse tests B1–B5 in batch_abuse.rs
5.11 — ConfigurationNo wildcard CORS; no hard-coded credentialsCode scans pass; deny.toml bans enforced

6.2 Residual risk summary

Risk areaResidual riskMitigation
MFA enforcementLow–MediumDepends on OIDC provider configuration
SQLite backup integrityLowWAL mode; deployment guide recommends periodic backups
Rate limitingLowNot implemented at KMS level; recommend reverse-proxy (nginx, Caddy)
Side-channel attacksVery lowFIPS provider; constant-time primitives via OpenSSL

7. Cross-Framework Remediation Matrix

The table below maps each finding to its framework references, severity, and corresponding code change or test:

IDFindingSeverityFrameworksRemediationStatus
F-01JWKS URIs could use HTTP (man-in-the-middle risk)HighCSF PR.AC-03, CIS 16, OSSTMM 5.7validate_jwks_uris_are_https() in start_kms_server.rs + J1–J4 tests✅ Closed
F-02serde_json::unbounded_depth feature not bannedMediumSSDF PW.5.1, CIS 4.1Added [[bans.features]] in deny.toml✅ Closed
F-03JWKS HTTP client followed redirects (SSRF vector)HighCSF ID.RA, OWASP A10, OSSTMM 5.8Policy::none() already in parse_jwks(); SR1–SR2 regression tests added✅ Closed
F-04JWT algorithm allowlist not covered by testsMediumCSF PR.AA-01, SSDF PW.8.1, ISO 27034 ANF-2A1–A6 tests in jwt_config.rs using production constant✅ Closed
F-05DB URL password visible in debug logsMediumCSF PR.DS-01, OSSTMM 5.4mask_db_url_password() + N1–N5 regression tests✅ Closed
F-06Batch count mismatch not explicit-testedLowSSDF PW.4.4, OWASP A04B1–B5 tests in batch_abuse.rs✅ Closed
F-07CORS policy not integration-testedLowISO 27034 L2, CIS 12.2, OSSTMM 5.6C1–C3 tests in cors_config.rs✅ Closed
F-08Privilege-bypass boundary untestedLowCSF PR.AC-01, CIS 5/6, ISO 27034 L4PB1–PB4 tests in privilege_bypass.rs✅ Closed

8. Automated Audit Checks (audit.sh)

.mise/scripts/audit/multi_framework.sh contains 21 automated checks that can be run locally or in CI:

bash .mise/scripts/audit/multi_framework.sh          # run all checks
bash .mise/scripts/audit/multi_framework.sh --verbose  # show additional detail
bash .mise/scripts/audit/audit.sh                    # run unified OWASP + multi-framework
CheckFramework(s)Description
1SSDF PW.1.1gitleaks — no hard-coded secrets
2SSDF PW.7.2unsafe block count < 30
3SSDF RV.1.2cargo audit — no HIGH/CRITICAL CVEs
4SSDF PW.5.1cargo deny bans
5CIS 4.1 / OWASP A05serde_json unbounded_depth banned
6CIS 8.2OTLP/rolling log configuration present
7CIS 4.1Safe default bind address present
8CIS 16 / OSSTMM TrustJWKS HTTPS startup guard present
9OSSTMM VisibilityDB URL password masking (**** placeholder)
10OSSTMM VisibilityTLS passphrase masking
11OWASP A10 / CSF ID.RAJWKS HTTP client disables redirect following
12ISO 27034 L2 / CIS 12.2CORS header not wildcard by default
13SSDF PW.4.4TTLV binary/XML recursion depth limit
14CSF PR.AA-01JWT algorithm allowlist enforced
15CIS 13.9No legacy TLS 1.0/1.1 configuration
16SSDF PW.4.4No bare panic!() in production paths
17CIS 5.1Privileged user list not hard-coded in source
18CSF PR.DS-01Sensitive key material uses Zeroize
19OSSTMM / SSDFunwrap() count in server/src/ < 5
20ISO 27034 L3Access-control module present
21CSF DE.CMsemgrep static analysis (if installed)

9. Report Sign-off

RoleNameDateSignature
Security ReviewerGitHub Copilot (automated)2026-04-16
Lead DeveloperEviden EngineeringPending
Security OfficerEviden SecurityPending

Overall status: ✅ All automated checks pass — 8 findings identified and closed.

Next review date: Before next major release or when any of the following occur:

  • A new authentication mechanism is added
  • A new dependency with cryptographic primitives is introduced
  • A new external integration (cloud provider, HSM) is added

Runtime Network Security Audit

Scope

This report documents the live runtime security assessment methodology and expected results for the Eviden KMS server. The analysis targets the running server process over the network — complementing static source analysis and multi-framework compliance reports.

Run the analyser with:

bash .mise/scripts/audit/runtime_security.sh \
    --server-url https://HOST:PORT \
    [--cert client.pem] [--key client.key] [--ca ca.pem]

Assessment Architecture

graph TD
    A([Security Analyst]) -->|"runtime_security.sh"| B[Runtime Analyser]
    B --> C[Reachability Probe]
    B --> D[TLS Inspector]
    B --> E[Certificate Chain]
    B --> F[HTTP Headers]
    B --> G[mTLS Verifier]
    B --> H[KMIP Protocol Probes]
    B --> I[Optional: nmap / sslyze / nuclei]

    C --> J[(cbom/runtime/)]
    D --> J
    E --> J
    F --> J
    G --> J
    H --> J
    I --> J

    J --> K[runtime_results.json]
    J --> L[tls_analysis.txt]
    J --> M[certificate.pem]
    J --> N[http_headers.txt]
    J --> O[kmip_probes.json]

Network & Attack Surface Map

graph LR
    subgraph Internet ["Public Internet / Zero-trust network"]
        C1([CLI client])
        C2([Web UI])
        C3([Enterprise app])
        A([Attacker])
    end

    subgraph DMZ ["DMZ / Load Balancer"]
        LB["TLS Termination or passthrough"]
    end

    subgraph KMS ["KMS Server Process"]
        direction TB
        P9998["Port 9998 — HTTPS/KMIP\n(main)"]
        AUTH["Auth middleware\n(JWT / mTLS / API-key)"]
        KMIP_ROUTE["KMIP 2.1 routes"]
        UI_ROUTE["Web UI routes\n/ui/"]
        HEALTH["Health — /version"]
    end

    subgraph DB ["Persistent Storage"]
        SQL[(SQLite / PostgreSQL\nRedis-findex)]
    end

    C1 -- "HTTPS + mTLS / JWT" --> LB
    C2 -- "HTTPS + auth cookie" --> LB
    C3 -- "HTTPS + API key" --> LB
    A -. "scan / probe" .-> LB

    LB --> P9998
    P9998 --> AUTH
    AUTH --> KMIP_ROUTE
    AUTH --> UI_ROUTE
    AUTH --> HEALTH
    KMIP_ROUTE --> SQL

Key attack surfaces

SurfaceExposureMitigation
Port 9998 / KMIP endpointExternalmTLS or JWT, TLS 1.2+ only
Web UIExternalCookie auth, CSP header
/version health endpointExternalRead-only, no secrets
Server certificatePublicAuto-renew, SHA-256+, RSA-2048+
DatabaseInternal onlyNot exposed to network

TLS Security Scorecard

graph LR
    S3("SSLv3") -- REJECT --> N1["POODLE — CVE-2014-3566"]
    T10("TLS 1.0") -- REJECT --> N2["BEAST / PCI-DSS deprecated"]
    T11("TLS 1.1") -- REJECT --> N3["Deprecated — RFC 8996"]
    T12("TLS 1.2") -- ACCEPT --> Y1["FIPS 140-3 minimum"]
    T13("TLS 1.3") -- ACCEPT --> Y2["Preferred — PFS enforced"]
    style S3  fill:#ef4444,color:#fff,stroke:#dc2626
    style T10 fill:#ef4444,color:#fff,stroke:#dc2626
    style T11 fill:#f97316,color:#fff,stroke:#ea580c
    style T12 fill:#22c55e,color:#fff,stroke:#16a34a
    style T13 fill:#16a34a,color:#fff,stroke:#15803d
    style N1  fill:#fee2e2,color:#991b1b,stroke:#fca5a5
    style N2  fill:#fee2e2,color:#991b1b,stroke:#fca5a5
    style N3  fill:#ffedd5,color:#9a3412,stroke:#fdba74
    style Y1  fill:#dcfce7,color:#166534,stroke:#86efac
    style Y2  fill:#dcfce7,color:#166534,stroke:#86efac
ProtocolExpectedReason
SSLv3❌ RejectedPOODLE attack (CVE-2014-3566)
TLS 1.0❌ RejectedBEAST, POODLE, deprecated PCI-DSS 3.2
TLS 1.1❌ RejectedDeprecated per RFC 8996
TLS 1.2✅ AcceptedMinimum for FIPS 140-3
TLS 1.3✅ AcceptedPreferred — mandatory for new deployments
pie title Accepted Cipher Suites by Category
    "ECDHE-AESGCM (strong)" : 4
    "DHE-AESGCM (PFS)" : 2
    "AES256-SHA256 (compat)" : 1
    "Weak / rejected" : 0
CategoryExample CipherStatusFIPS 140-3Forward Secrecy
ECDHE-RSA-AES256-GCM-SHA384TLS 1.2 ECDHE✅ Allowed
ECDHE-RSA-AES128-GCM-SHA256TLS 1.2 ECDHE✅ Allowed
TLS_AES_256_GCM_SHA384TLS 1.3✅ Allowed
TLS_CHACHA20_POLY1305_SHA256TLS 1.3✅ Allowed⚠️ non-FIPS only
NULL / aNULLExport❌ Rejected
RC4 / DES / 3DESLegacy❌ Rejected
MD5-basedLegacy❌ Rejected
EXPORT-gradeLegacy❌ Rejected
sequenceDiagram
    participant C as Client
    participant S as KMS Server (TLS 1.3)
    C->>S: ClientHello (supported protocols, ciphers)
    S-->>C: ServerHello (TLS 1.3, TLS_AES_256_GCM_SHA384)
    S-->>C: Certificate (RSA-2048 / ECDSA-256, SHA-256 signed)
    S-->>C: CertificateVerify
    S-->>C: Finished
    C->>S: [Optional] Certificate (mTLS)
    C->>S: Finished
    Note over C,S: Symmetric keys derived from ephemeral ECDHE<br/>(Perfect Forward Secrecy)
    C->>S: POST /kmip/2_1 (encrypted)
    S-->>C: KMIP ResponseMessage (encrypted)

Certificate Chain Analysis

graph TB
    ROOT["Root CA\nself-signed or public CA\nKey: RSA-4096 or EC-384\nSig: SHA-256"]
    INTER["Intermediate CA optional\nKey: RSA-2048 or EC-256\nSig: SHA-256 Valid: 3 years"]
    LEAF["KMS Server Certificate\nSAN: kms.example.com\nKey: RSA-2048 or EC-256\nSig: SHA-256 Valid: 1 year max"]
    ROOT --> INTER
    INTER --> LEAF

Certificate requirements

  • Key algorithm: RSA ≥ 2048 bits or EC ≥ P-256
  • Signature: SHA-256 minimum (SHA-1 rejected by modern browsers and RFC 9155)
  • SAN: must match server hostname — bare CN no longer sufficient (RFC 2818)
  • Expiry: warning at 30 days; auto-renewal recommended (ACME/Let's Encrypt)
  • OCSP stapling: recommended for client-side revocation checking

HTTP Security Headers

graph LR
    subgraph Required ["Required Headers"]
        HSTS["Strict-Transport-Security\nmax-age=31536000 includeSubDomains"]
        XCTO["X-Content-Type-Options: nosniff"]
    end
    subgraph Recommended ["Recommended Headers"]
        XFO["X-Frame-Options: DENY"]
        CSP["Content-Security-Policy\ndefault-src self"]
        CC["Cache-Control: no-store"]
    end
    subgraph Avoid ["Must not disclose"]
        SRV["Server: omit or generic"]
        CORS_W["CORS wildcard forbidden on KMIP"]
    end
HeaderExpected ValueImportanceOWASP
Strict-Transport-Securitymax-age=31536000; includeSubDomainsRequiredA05
X-Content-Type-OptionsnosniffRequiredA05
X-Frame-OptionsDENYRecommendedA05
Content-Security-Policydefault-src 'self'; script-src 'self'RecommendedA03
Cache-Controlno-store on API routesRecommendedA02
ServerEmpty or genericAvoid disclosureA05
CORS on /kmip/*None or restricted originRequiredA01

mTLS Authentication Model

sequenceDiagram
    participant CLI as ckms CLI
    participant KMS as KMS Server
    participant DB as Database

    CLI->>KMS: TLS ClientHello
    KMS-->>CLI: ServerHello + Certificate
    KMS-->>CLI: CertificateRequest (if mTLS mode)
    CLI->>KMS: Certificate (client cert, signed by trusted CA)
    CLI->>KMS: CertificateVerify
    Note over CLI,KMS: TLS session established
    CLI->>KMS: POST /kmip/2_1 (encrypted TTLV)
    KMS->>KMS: Extract CN from client cert → username
    KMS->>DB: Look up access control for user
    KMS-->>CLI: KMIP Response
ModeHow it worksWhen to use
mTLSClient presents X.509 certificate signed by trusted CAInternal services, CLI tooling
JWT (OAuth2)Bearer token from Auth0 / Keycloak / OIDC providerWeb UI, end-user access
API keyShared secret in headerMachine-to-machine, simple integrations
No authDisabled — dev/test only (--auth-type none)Local development only

Warning

Never deploy with --auth-type none in production. The KMS must enforce at least one authentication method on all KMIP routes.

# Test mTLS with ckms-generated certs
bash .mise/scripts/audit/runtime_security.sh \
    --server-url https://localhost:9998 \
    --cert test_data/certs/client.crt \
    --key  test_data/certs/client.key \
    --ca   test_data/certs/server_ca.crt

KMIP Protocol Security Probes

flowchart TD
    P1["Empty payload probe\nPOST /kmip/2_1 {}"] -->|"Expected: 400/422"| OK1([PASS])
    P2["Oversized BatchCount\nBatchCount: 99999"] -->|"Expected: 400/422/413"| OK2([PASS])
    P3["SQL injection in UID\n'OR 1=1; DROP TABLE"] -->|"Expected: 400/422/401"| OK3([PASS])
    P4["70 MiB payload\nAbove 64 MiB limit"] -->|"Expected: 400/413"| OK4([PASS])
    P5["Rate limit probe\n10 rapid requests"] -->|"429 expected for excess"| OK5([PASS / INFO])

    style OK1 fill:#22c55e,color:#fff
    style OK2 fill:#22c55e,color:#fff
    style OK3 fill:#22c55e,color:#fff
    style OK4 fill:#22c55e,color:#fff
    style OK5 fill:#84cc16,color:#fff
ProbePayloadExpected HTTPRisk if wrong
Empty KMIP request{}400 or 422Server crash / 500
OversizedBatchCountBatchCount: 99999400, 422, or 413DoS / OOM
SQL injection in UID' OR '1'='1'; DROP ...400, 422, 401SQL injection
70 MiB payloadRandom bytes400 or 413DoS / memory exhaustion
Rapid 10 requestsEmpty KMIP422 (or 429 if rate-limit active)Brute-force

Threat Model (STRIDE)

mindmap
  root((KMS Server\nAttack Surface))
    Spoofing
      Fake client certificate
      JWT token forgery
      MITM on HTTP
    Tampering
      Replay KMIP request
      Key ID enumeration
      Packet injection
    Repudiation
      Missing audit log
      Log injection
    Information Disclosure
      TLS version downgrade
      Server header leaks version
      Error message leaks DB schema
    Denial of Service
      OversizedBatchCount
      Large payload flood
      TLS session exhaustion
    Elevation of Privilege
      CORS wildcard on KMIP
      JWT scope confusion
      Insecure object ownership
ThreatSTRIDEMitigationStatus
MITM — weak TLS versionTamperingTLS 1.2+ enforced; SSLv3/TLS1.0/1.1 rejected✅ Mitigated
Weak cipher negotiationTamperingNULL/RC4/DES/EXPORT rejected by server✅ Mitigated
Certificate spoofingSpoofingmTLS or JWT required; CA pinning optional✅ Mitigated
SQL injection via UIDTamperingParameterised queries in all DB backends✅ Mitigated
OOM via large batchDoSBatchCount validated; payload size limit 64 MiB✅ Mitigated
Rate-based brute-forceDoSRate limiting middleware (configurable)⚠️ Configurable
Server version disclosureInfo DisclosureServer header suppressed✅ Mitigated
CORS wildcard on KMIPElevation of PrivilegeNo CORS header on /kmip/*✅ Mitigated
Expired certificateSpoofing30-day expiry warning in checker✅ Monitored
Insecure direct object refsElevation of PrivilegeObject ownership enforced in DB✅ Mitigated

Running the Analyser

Prerequisites

# Required (always present on Linux)
openssl version   # ≥ 3.0
curl --version    # ≥ 7.68

# Optional — enable richer analysis when installed
apt-get install nmap                  # port scan + TLS NSE scripts
pip3 install sslyze                   # deep TLS / cert-transparency analysis
go install github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest  # template scanner

Basic run (plain HTTPS)

bash .mise/scripts/audit/runtime_security.sh \
    --server-url https://localhost:9998 \
    --insecure    # skip cert verification on self-signed cert

Full run with mTLS

bash .mise/scripts/audit/runtime_security.sh \
    --server-url https://kms.prod.example.com:9998 \
    --cert  certs/client.crt \
    --key   certs/client.key \
    --ca    certs/ca.crt \
    --report documentation/docs/certifications_and_compliance/audit/runtime_security_audit_latest.md

Output files

cbom/runtime/
├── runtime_results.json   ← machine-readable summary (all checks + status)
├── tls_analysis.txt       ← raw openssl s_client output
├── cert_details.txt       ← openssl x509 -text of server certificate
├── certificate.pem        ← server certificate in PEM format
├── http_headers.txt       ← HTTP response headers
├── mtls_analysis.txt      ← mTLS negotiation log
├── kmip_probes.json       ← KMIP protocol probe results
├── nmap.txt               ← nmap scan (if installed)
├── sslyze.json            ← sslyze report (if installed)
└── nuclei.txt             ← nuclei scan (if installed)

Exit codes

CodeMeaning
0All checks passed
1One or more FAIL findings (critical)
2Tool error (missing required utility or bad arguments)

Integration with CI

Add to .github/workflows/main_base.yml as a post-deploy smoke test:

- name: Runtime Security Scan
  run: |
    bash .mise/scripts/audit/runtime_security.sh \
      --server-url https://localhost:9998 \
      --insecure \
      --report cbom/runtime_security_report.md
  env:
    KMS_URL: https://localhost:9998

Relation to other security reports

ReportLayerTool
OWASP Source AuditStatic — source codescan_source.py + risk_score.py
Multi-framework AuditStatic — policy compliancemulti_framework.sh
Runtime Security Audit (this file)Dynamic — running serverruntime_security.sh

KMIP Support

The Eviden KMS server implements both KMIP 1.x and 2.x interfaces. KMIP (Key Management Interoperability Protocol) is an OASIS standard designed to standardize communication between key management systems and encryption clients.

Connection Options

  • Binary Protocol: Available on port 5696

    • TLS secured
    • Client certificate required for authentication
  • JSON Protocol: Available on port 9998 via REST POST

    • Optional TLS security
    • Multiple authentication mechanisms supported
    • Endpoints:
      • /kmip: Handles KMIP 1.x and 2.x RequestMessage
      • /kmip/2_1: Specifically for KMIP 2.1 RequestMessage or operations like Create, Encrypt, Decrypt, etc.

Implementation Details

Internally, all KMIP messages are translated to KMIP 2.1 specifications and converted back to KMIP 1.x when necessary. The Eviden KMS server implements a targeted subset of the KMIP 2.1 protocol.

Purpose of KMIP

The OASIS KMIP standard aims to:

  • Define a comprehensive protocol for communication between encryption systems and enterprise applications
  • Support diverse applications including email, databases, and storage devices
  • Eliminate redundant and incompatible key management processes
  • Enhance data security while reducing costs associated with multiple products

Scope of Implementation

KMIP is an extensive specification. While the Eviden KMS server does not implement the entire standard, it supports the features necessary for advanced cryptographic use cases. The implementation continues to evolve to meet customer requirements, though like most KMS servers, it doesn't support all possible cryptographic objects and operations.

The following pages document the supported features of the KMIP 2.1 specification and Cosmian-specific extensions.

KMIP support by Cosmian KMS

This page summarizes the KMIP coverage in Cosmian KMS. The support status is derived from the actual implementation in crate/server/src/core/operations.

Cosmian KMS Server supports KMIP versions: 2.1, 2.0, 1.4, 1.3, 1.2, 1.1, 1.0

Legend:

  • ✅ Fully supported
  • ❌ Not implemented
  • 🚫 Deprecated
  • N/A Not applicable (operation/attribute not defined in that KMIP version)

KMIP Baseline Profile Compliance

Baseline Server: ✅ Compliant (all 9 required + 18/18 optional)

The Baseline Server profile (defined in KMIP Profiles v2.1 Section 4.1) requires:

  • Required operations: Discover Versions, Query, Create, Register, Get, Destroy, Locate, Activate, Revoke
  • Optional operations: Many additional operations for extended functionality

KMIP Coverage

Messages

MessageSupport
Request Message
Response Message

Operations by KMIP Version

The following table shows operation support across all KMIP versions.

Operation1.01.11.21.31.42.02.1
Activate
Add Attribute
Archive
Cancel
Certify
Check
Create
Create Key Pair
Create Split KeyN/AN/AN/A
DecryptN/A
Delete Attribute
DeriveKey
Destroy
Discover Versions
EncryptN/A
ExportN/AN/AN/AN/A
Get
Get Attribute List
Get Attributes
Get Usage Allocation
HashN/A
ImportN/AN/AN/AN/A
Join Split KeyN/AN/AN/A
Locate
MACN/A
MAC VerifyN/A
Notify
Obtain Lease
Poll
Put
Query
RNG RetrieveN/A
RNG SeedN/A
Re-certify
Re-key
Re-key Key PairN/AN/A
Recover
Register
Revoke
Set Attribute (Modify)N/AN/A
SignN/A
Signature VerifyN/A
Validate

Methodology

  • Operations marked ✅ are backed by a Rust implementation file under crate/server/src/core/operations.
  • Operations marked ❌ are defined in the KMIP specification but not implemented in Cosmian KMS.
  • Operations marked N/A do not exist in that particular KMIP version.
  • This documentation is auto-generated by analyzing source code and KMIP specifications.

If you spot a mismatch or want to extend coverage, please open an issue or PR.

Managed Objects

The following table shows managed object support across all KMIP versions.

Managed Object1.01.11.21.31.42.02.1
Certificate
Symmetric Key
Public Key
Private Key
Split Key
Template🚫🚫🚫🚫🚫🚫🚫
Secret Data
Opaque Data
PGP KeyN/AN/AN/AN/AN/AN/AN/A

Notes:

  • Opaque Object import support is present (see import.rs).
  • PGP Key types appear in digest and attribute handling but full object import/register is not implemented, hence ❌.
  • Template objects are deprecated in newer KMIP versions.

Base Objects

The following table shows base object support across all KMIP versions.

Base Object1.01.11.21.31.42.02.1
Attribute
Credential
Key Block
Key Value
Key Wrapping Data
Key Wrapping Specification
Transparent Key Structures
Template-Attribute Structures
Server Information
Extension Information
Data
Data Length
Signature Data
MAC Data
Nonce
Correlation Value
Init Indicator
Final Indicator
RNG Parameters
Profile Information
Validation Information
Capability Information
Authenticated Encryption Additional Data
Authenticated Encryption Tag

Notes:

  • AEAD Additional Data and Tag are supported in encrypt/decrypt APIs.
  • Nonce and RNG Parameter are used by symmetric encryption paths.
  • Base objects are fundamental structures present across all KMIP versions.

Transparent Key Structures

The following table shows transparent key structure support across all KMIP versions.

Structure1.01.11.21.31.42.02.1
Symmetric Key
DSA Private Key
DSA Public Key
RSA Private Key
RSA Public Key
DH Private KeyN/AN/AN/AN/AN/AN/AN/A
DH Public KeyN/AN/AN/AN/AN/AN/AN/A
EC Private Key
EC Public Key
ECDSA Private KeyN/AN/AN/AN/AN/AN/AN/A
ECDSA Public KeyN/AN/AN/AN/AN/AN/AN/A
ECDH Private KeyN/AN/AN/AN/AN/AN/AN/A
ECDH Public KeyN/AN/AN/AN/AN/AN/AN/A
ECMQV Private KeyN/AN/AN/AN/AN/AN/AN/A
ECMQV Public KeyN/AN/AN/AN/AN/AN/AN/A

Note: EC/ECDSA support is present; DH/DSA/ECMQV are not implemented.

Attributes

AttributeCurrent
Activation Date
Alternative Name
Always Sensitive
Application Specific Information
Archive Date
Attribute Index
Certificate Attributes
Certificate Length
Certificate Type
Comment
Compromise Date
Compromise Occurrence Date
Contact Information
Critical
Cryptographic Algorithm
Cryptographic Domain Parameters
Cryptographic Length
Cryptographic Parameters
Cryptographic Usage Mask
Deactivation Date
Description
Destroy Date
Digest
Digital Signature Algorithm
Extractable
Fresh
Initial Date
Key Format Type
Key Value Location
Key Value Present
Last Change Date
Lease Time
Link
Name
Never Extractable
Nist Key Type
Object Group
Object Group Member
Object Type
Opaque Data Type
Original Creation Date
PKCS#12 Friendly Name
Process Start Date
Protect Stop Date
Protection Level
Protection Period
Protection Storage Masks
Quantum Safe
Random Number Generator
Revocation Reason
Rotate Automatic
Rotate Date
Rotate Generation
Rotate Interval
Rotate Latest
Rotate Name
Rotate Offset
Sensitive
Short Unique Identifier
State
Unique Identifier
Usage Limits
Vendor Attribute
X.509 Certificate Identifier
X.509 Certificate Issuer
X.509 Certificate Subject

Notes:

  • GetAttributes returns a union of metadata attributes and those embedded in KeyBlock structures.
  • "Vendor Attributes" are available via the Cosmian vendor namespace and are accessible via GetAttributes.
  • A ✅ indicates the attribute is used or updated by at least one KMIP operation implementation in crate/server/src/core/operations, including attribute handlers (Add/Delete/Set/Get Attribute).
  • Most attributes are present across all KMIP versions with some additions in newer versions.

The KMIP 2.1 specification pre-defines a set of 9 cryptographic objects. Eviden supports the use of 4 of these objects

ObjectsCryptographic primitives
CertificateX509
Certificate Request-
Opaque Object-
PGP Key-
Private KeyCovercrypt, X25519, Ed25519, X448, Ed448, NIST EC, RSA
Public KeyCovercrypt, X25519, Ed25519, X448, Ed448, NIST EC, RSA
Secret Data-
Split Key-
Symmetric keySSE, AES, FPE, Salsa20, XChacha20, Findex

Notes:

  • Certificates can be
    • imported using the Import operation as X509 (PEM, DER) or PKCS12 (PFX) files.
    • created using the Certify operation from a PKCS#10 Certificate Request or from an existing public key.
  • For the supported algorithms for key wrapping, encryption and decryption, see the supported algorithms page.
  • For the supported formats, see the supported formats page.

KMIP Objects may be manipulated in various standardized formats.

Import - Export

The KMIP 2.1 specification states that keys have a default Key Format Type that SHALL be produced by KMIP servers. When requesting the export of an Object without specifying the Key Format Type, a default Key Format Type by object (and algorithm) should be used as listed in the following table:

TypeDefault Key Format TypeAvailable Key Format Types Import/Export
CertificateX.509X.509
Certificate RequestPKCS#10PKCS#10
Opaque ObjectOpaque
PGP KeyRaw
Secret DataRaw
Symmetric KeyRawRaw
Split KeyRaw
RSA Private KeyPKCS#1PKCS#1, PKCS#8, Transparent RSA Private Key
RSA Public KeyPKCS#1PKCS#1, PKCS#8 (SPKI), Transparent RSA Public Key
EC Private KeyTransparent EC Private KeyTransparent EC Private Key, PKCS#8, EC Private Key (SEC1)
EC Public KeyTransparent EC Public KeyTransparent EC Public Key, PKCS#8 (SPKI)
DSA Private KeyTransparent DSA Private Key
DSA Public KeyTransparent DSA Public Key
  • All ASN.1 based formats are available as PEM or DER encoded
  • SPKI is denoted PKCS8 for public keys in KMIP
  • SEC1 (called ECPrivateKey in KMIP) is only available for NIST curves (i.e. not for curve 25519 and curve 448) private keys.
  • The D field of the Transparent EC Private Key is:
    • the (absolute) value of the Big Integer of the scalar for NIST curves
    • the Big Integer value of the private key raw bytes (as big endian) for Curve 25519 and Curve 448
  • The Q String field of the Transparent EC Public Key is:
    • the uncompressed point octet form as defined in RFC5480 and used in certificates and TLS records for NIST curves.
    • the raw bytes of the public key for Curve 25519 and Curve 448

Internal storage

The IETF now recommends using PKCS#8 and Subject Public Key Info (SPKI) as default formats for inter-operability. This server enforces the KMIP 2.1 default export formats above but the storage formats used in the database are:

  • PKCS#8 DER for RSA and EC private Keys (RFC 5208 and 5958).
  • SPKI DER (RFC 5480) for RSA and EC public keys, using the Key Format Type PKCS#8, since SPKI is not listed.
  • X509 DER for certificates (RFC 5280).
  • PKCS#10 DER for certificate requests (RFC 2986).
  • TransparentSymmetricKey for symmetric keys
  • Raw for opaque objects and Secret Data

Users requesting keys are therefore encouraged to request them in these storage formats to avoid conversions and match recent RFCs.

The Eviden KMS server supports the tagging of objects. Tags are arbitrary strings that can be attached to objects. Tags can be used to group objects together, and to find objects for most operations, such as export, import, encrypt, decrypt, etc.

In addition, the KMS server will automatically add a system tag to objects based on the object type:

  • _sk: for a private key
  • _pk: for a public key
  • _kk: for a symmetric key
  • _uk: for a Covercrypt user decryption key
  • _cert: for a X509 certificate
  • _sd: for a secret data
  • _oo: for an opaque object

Since there is no provision in the KMIP 2.1 specification for tagging. The Eviden KMS server implements tagging using the following KMIP 2.1 extensions:

  1. When Attributes are passed as part of the KMIP operation, such as in the Create, Create Key Pair, Locate, Certify and Import operations, the tags are passed as VendorAttributes with the server's configured vendor identification (default: cosmian, configurable via --vendor-identification / KMS_VENDOR_IDENTIFICATION) and attribute name tag. The value is the serialization of the tags as a JSON array of strings.

  2. When unique identifiers are passed as part of the KMIP operation, such as in the Certify, Encrypt, Export, Decrypt, Get, Get Attributes, Revoke, and Destroy operations, the tags are in the unique identifier itself as a serialized JSON array e.g. [ "tag1", "tag2" ].

Example

Export the Symmetric key (tag _kk) with user tag myTag:

{
  "tag": "Export",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "[\"_kk\",\"myTag\"]"
    },
    {
      "tag": "KeyFormatType",
      "type": "Enumeration",
      "value": "AsRegistered"
    }
  ]
}

In chapter 4, the KMIP 2.1 specification specifies a list of 63 Attributes, mostly made of enumerations and data structures, often nested in each other. Despite this impressive list, and as expected in such a large specification, KMIP allows for extensions to support new cryptographic schemes such as the ones enabled by Eviden.

Extensions in KMIP consist mostly in augmenting enumerations with new values and attributing a specific prefix values, usually 0x8880 to the new variants.

To support Covercrypt, Cosmian is using a few extensions listed below.

Key Format Type

The Key Format Type attribute is a required attribute of a Cryptographic Object.

It is set by the server, but a particular Key Format Type MAY be requested by the client if the cryptographic material is produced by the server (i.e., Create, Create Key Pair, Create Split Key, Re-key, Re-key Key Pair, Derive Key) on the client's behalf. The server SHALL comply with the client's requested format or SHALL fail the request. When the server calculates a Digest for the object, it SHALL compute the digest on the data in the assigned Key Format Type, as well as a digest in the default KMIP Key Format Type for that type of key and the algorithm requested (if a non-default value is specified).

Extensions
CoverCryptSecretKey = 0x8880_000C,
CoverCryptPublicKey = 0x8880_000D,

Cryptographic Algorithm

The Cryptographic Parameters attribute is a structure that contains a set of OPTIONAL fields that describe certain cryptographic parameters to be used when performing cryptographic operations using the object. Specific fields MAY pertain only to certain types of Managed Objects. The Cryptographic Parameters attribute of a Certificate object identifies the cryptographic parameters of the public key contained within the Certificate.

The Cryptographic Algorithm is also used to specify the parameters for cryptographic operations. For operations involving digital signatures, either the Digital Signature Algorithm can be specified or the Cryptographic Algorithm and Hashing Algorithm combination can be specified.

Random IV can be used to request that the KMIP server generate an appropriate IV for a cryptographic operation that uses an IV. The generated Random IV is returned in the response to the cryptographic operation.

IV Length is the length of the Initialization Vector in bits. This parameter SHALL be provided when the specified Block Cipher Mode supports variable IV lengths such as CTR or GCM.

Tag Length is the length of the authenticator tag in bytes. This parameter SHALL be provided when the Block Cipher Mode is GCM.

The IV used with counter modes of operation (e.g., CTR and GCM) cannot repeat for a given cryptographic key. To prevent an IV/key reuse, the IV is often constructed of three parts: a fixed field, an invocation field, and a counter as described in [SP800-38A] and [SP800-38D]. The Fixed Field Length is the length of the fixed field portion of the IV in bits. The Invocation Field Length is the length of the invocation field portion of the IV in bits. The Counter Length is the length of the counter portion of the IV in bits.

Initial Counter Value is the starting counter value for CTR mode (for [RFC3686] it is 1).

Extensions
CoverCrypt = 0x8880_0004,

Vendor Attributes

All keys managed by the Eviden KMS server are primarily a KeyMaterial made of bytes. Some keys, typically those of ABE, also carry information regarding the underlying access policies. This information is carried together with the keys using VendorAttributes

Typically a vendor attribute is made of 3 values: a Vendor Identification - set to the server's configured vendor ID (default: cosmian, configurable via --vendor-identification / KMS_VENDOR_IDENTIFICATION) - and a tuple Attribute Name, Attribute Value.

Covercrypt uses a few vendor attributes which names can be seen in the code attributes.rs file.

The attributes names and corresponding values used for a given KeyFormatType are as follows:

  • VENDOR_ATTR_COVER_CRYPT_POLICY = "cover_crypt_policy": the JSONified Policy found in the master keys.

  • VENDOR_ATTR_COVER_CRYPT_ACCESS_POLICY = "cover_crypt_access_policy": the JSONified boolean Access Policy found in a user key

In addition, the VENDOR_ATTR_COVER_CRYPT_ATTR = "cover_crypt_attributes" name is used in Locate requests to identify User Decryption Keys holding certain Policy Attributes.

The easiest way to call the KMIP API is to use the cosmian Command Line Interface or one of the Eviden cloudproof libraries which provide wrapper calls in the corresponding language.

Without the use of a library, the client must build the JSON TTLV messages from an Operation as described in the KMIP 2.1 specification, and issue an HTTP POST call to the /kmip/2_1 endpoint of the server.

Multiple operations can be sent in a single call using the Messages API .

!!!info "Easily building JSON TTLV messages"

Building JSON TTLV messages is a complex task and the easiest way to get started is to use
the [KMS CLI](../../kms_clients/index.md) in `JSON mode` mode to print the corresponding request and response messages.

The `JSON mode` is activated by passing the `--kms-print-json` flag to the `cosmian` command.
See the [JSON mode example](#json-mode-example) below.

To send multiple requests in a single call, se the Messages API .

Sample JSON TTLV messages

Please refer to the various operations pages for sample JSON TTLV messages.

JSON mode example

This creates a (default AES 256) symmetric key which will be tagged with the string myKey.

ckms --kms-print-json sym keys create --tag myKey

The CLI will then show the JSON TTLV requests and response:

2024-09-13T13:39:20.001395Z  INFO ThreadId(01) cosmian_kms_client::kms_rest_client:  ==>
{
  "tag": "Create",
  "type": "Structure",
  "value": [
    {
      "tag": "ObjectType",
      "type": "Enumeration",
      "value": "SymmetricKey"
    },
    {
      "tag": "Attributes",
      "type": "Structure",
      "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"
        },
        {
          "tag": "VendorAttributes",
          "type": "Structure",
          "value": [
            {
              "tag": "VendorAttributes",
              "type": "Structure",
              "value": [
                {
                  "tag": "VendorIdentification",
                  "type": "TextString",
                  "value": "cosmian"
                },
                {
                  "tag": "AttributeName",
                  "type": "TextString",
                  "value": "tag"
                },
                {
                  "tag": "AttributeValue",
                  "type": "ByteString",
                  "value": "5B226D794B6579225D"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}
2024-09-13T13:39:20.018203Z  INFO ThreadId(01) cosmian_kms_client::kms_rest_client <==
{
  "tag": "CreateResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "ObjectType",
      "type": "Enumeration",
      "value": "SymmetricKey"
    },
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "925f2175-0676-4785-b9d9-61fff30771f3"
    }
  ]
}
The symmetric key was successfully generated.
          Unique identifier: 925f2175-0676-4785-b9d9-61fff30771f3

  Tags:
    - myKey

OpenAPI Specification and Swagger UI

The Eviden KMS server publishes its complete API as an OpenAPI 3.1 specification and serves an interactive Swagger UI browser directly from the server binary.

Endpoints

EndpointMethodDescription
/openapi.yamlGETFull OpenAPI 3.1 schema in YAML format
/swaggerGETInteractive Swagger UI (HTML page, locally served)

No authentication is required to access either endpoint; the UI itself uses the configured authentication mechanism to authorize API calls made through the browser.

Using the Swagger UI

Navigate to <server_url>/swagger in a browser.

https://<your-kms-host>:9998/swagger

The page displays all documented operations grouped by tag:

TagOperations
KMIPTTLV-over-HTTP via POST /kmip/2_1 and POST /kmip/1_4
REST Crypto APIEncrypt, decrypt, sign, verify, and MAC under /v1/crypto
ServerHealth, version, and server-info endpoints
Access controlGrant, revoke, list, and check object permissions
HSMHSM status and diagnostics
DownloadCLI binary download endpoint

Authentication in the UI

When the KMS is configured with JWT bearer authentication, click the Authorize button in the Swagger UI and enter your token. The UI will send it as an Authorization: Bearer <token> header on every subsequent request.

Downloading the spec

curl -O https://<your-kms-host>:9998/openapi.yaml

The downloaded YAML can be imported into any OpenAPI-compatible tooling:

  • PostmanImport > OpenAPI to generate a full collection with example requests.

  • InsomniaImport > OpenAPI 3.0.

  • openapi-generator — generate a typed client in any language:

    openapi-generator-cli generate \
      -i openapi.yaml \
      -g python \
      -o ./kms-client-python
    
  • swagger-codegen — similar code-generation approach.

Security

All assets (JavaScript bundle and stylesheet) are served directly from the KMS binary — there is no external CDN dependency. The server is therefore fully usable in air-gapped environments.

The Swagger UI page is protected by the following HTTP response headers:

HeaderValue
Content-Security-Policydefault-src 'none'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; frame-ancestors 'none'

The frame-ancestors 'none' directive prevents the page from being embedded in an <iframe>, protecting against clickjacking.

Relationship to the JSON TTLV API

The OpenAPI spec documents the HTTP transport layer of the KMS API. The KMIP operations themselves are sent as JSON TTLV payloads in the body of POST /kmip/2_1 or POST /kmip/1_4. See The JSON TTLV KMIP API for the full description of the KMIP message format.

Spec version and server URL

The version field in the OpenAPI YAML is injected dynamically to match the running KMS binary version. When the kms_public_url configuration option is set, the servers[].url field is replaced with the configured public URL, making the downloaded spec self-contained for use in external tooling.

See Configuration file for the kms_public_url option.

Chapter 8 of the KMIP 2.1 specification defines the Messages functionality, which enables sending/receiving multiple requests/responses at once (also known as bulk mode). This is the standard way to communicate with the KMIP server using the Binary protocol on port 5696 or the JSON protocol on port 9998 and endpoint /kmip.

The /kmip/2_1 endpoint is a Eviden extension allowing the posting of Operations directly to the server without the need for a RequestMessage wrapper. This is particularly useful for operations like Encrypt and Decrypt.

Multiple operations can be included in a single RequestMessage query. The server processes these operations sequentially, though they appear to execute simultaneously. Each batch item contains an independent request.

For every request message sent, the server returns a response message containing the result status of the requested operation, along with any relevant result data or error messages.

Request and response example

Below are canonical KMIP 2.1 message examples, following the mandatory XML schema AKLC-M-1-21.xml. Each request and response is shown in JSON TTLV format.

1. CreateKeyPair

{
    "tag": "RequestMessage",
    "type": "Structure",
    "value": [
        {
            "tag": "RequestHeader",
            "type": "Structure",
            "value": [
                { "tag": "ProtocolVersion", "type": "Structure", "value": [
                    { "tag": "ProtocolVersionMajor", "type": "Integer", "value": 2 },
                    { "tag": "ProtocolVersionMinor", "type": "Integer", "value": 1 }
                ] },
                { "tag": "ClientCorrelationValue", "type": "TextString", "value": "AKLC-M-1-21 step=0" },
                { "tag": "BatchCount", "type": "Integer", "value": 1 }
            ]
        },
        {
            "tag": "BatchItem",
            "type": "Structure",
            "value": [
                { "tag": "Operation", "type": "Enumeration", "value": "CreateKeyPair" },
                { "tag": "RequestPayload", "type": "Structure", "value": [
                    { "tag": "CommonAttributes", "type": "Structure", "value": [
                        { "tag": "CryptographicAlgorithm", "type": "Enumeration", "value": "RSA" },
                        { "tag": "CryptographicLength", "type": "Integer", "value": 2048 }
                    ] },
                    { "tag": "PrivateKeyAttributes", "type": "Structure", "value": [
                        { "tag": "Name", "type": "Structure", "value": [
                            { "tag": "NameValue", "type": "TextString", "value": "AKLC-M-1-21-private" },
                            { "tag": "NameType", "type": "Enumeration", "value": "UninterpretedTextString" }
                        ] },
                        { "tag": "CryptographicUsageMask", "type": "Integer", "value": "Sign" }
                    ] },
                    { "tag": "PublicKeyAttributes", "type": "Structure", "value": [
                        { "tag": "Name", "type": "Structure", "value": [
                            { "tag": "NameValue", "type": "TextString", "value": "AKLC-M-1-21-public" },
                            { "tag": "NameType", "type": "Enumeration", "value": "UninterpretedTextString" }
                        ] },
                        { "tag": "CryptographicUsageMask", "type": "Integer", "value": "Verify" }
                    ] }
                ] }
            ]
        }
    ]
}
{
    "tag": "ResponseMessage",
    "type": "Structure",
    "value": [
        {
            "tag": "ResponseHeader",
            "type": "Structure",
            "value": [
                { "tag": "ProtocolVersion", "type": "Structure", "value": [
                    { "tag": "ProtocolVersionMajor", "type": "Integer", "value": 2 },
                    { "tag": "ProtocolVersionMinor", "type": "Integer", "value": 1 }
                ] },
                { "tag": "TimeStamp", "type": "DateTime", "value": "$NOW" },
                { "tag": "BatchCount", "type": "Integer", "value": 1 }
            ]
        },
        {
            "tag": "BatchItem",
            "type": "Structure",
            "value": [
                { "tag": "Operation", "type": "Enumeration", "value": "CreateKeyPair" },
                { "tag": "ResultStatus", "type": "Enumeration", "value": "Success" },
                { "tag": "ResponsePayload", "type": "Structure", "value": [
                    { "tag": "PrivateKeyUniqueIdentifier", "type": "TextString", "value": "$UNIQUE_IDENTIFIER_0" },
                    { "tag": "PublicKeyUniqueIdentifier", "type": "TextString", "value": "$UNIQUE_IDENTIFIER_1" }
                ] }
            ]
        }
    ]
}

2. GetAttributes (Private Key)

{
    "tag": "RequestMessage",
    "type": "Structure",
    "value": [
        {
            "tag": "RequestHeader",
            "type": "Structure",
            "value": [
                { "tag": "ProtocolVersion", "type": "Structure", "value": [
                    { "tag": "ProtocolVersionMajor", "type": "Integer", "value": 2 },
                    { "tag": "ProtocolVersionMinor", "type": "Integer", "value": 1 }
                ] },
                { "tag": "ClientCorrelationValue", "type": "TextString", "value": "AKLC-M-1-21 step=1" },
                { "tag": "BatchCount", "type": "Integer", "value": 1 }
            ]
        },
        {
            "tag": "BatchItem",
            "type": "Structure",
            "value": [
                { "tag": "Operation", "type": "Enumeration", "value": "GetAttributes" },
                { "tag": "RequestPayload", "type": "Structure", "value": [
                    { "tag": "UniqueIdentifier", "type": "TextString", "value": "$UNIQUE_IDENTIFIER_0" },
                    { "tag": "AttributeReference", "type": "Enumeration", "value": "State" },
                    { "tag": "AttributeReference", "type": "Enumeration", "value": "CryptographicUsageMask" },
                    { "tag": "AttributeReference", "type": "Enumeration", "value": "UniqueIdentifier" },
                    { "tag": "AttributeReference", "type": "Enumeration", "value": "ObjectType" },
                    { "tag": "AttributeReference", "type": "Enumeration", "value": "CryptographicAlgorithm" },
                    { "tag": "AttributeReference", "type": "Enumeration", "value": "CryptographicLength" },
                    { "tag": "AttributeReference", "type": "Enumeration", "value": "Digest" },
                    { "tag": "AttributeReference", "type": "Enumeration", "value": "InitialDate" },
                    { "tag": "AttributeReference", "type": "Enumeration", "value": "LastChangeDate" },
                    { "tag": "AttributeReference", "type": "Enumeration", "value": "ActivationDate" },
                    { "tag": "AttributeReference", "type": "Enumeration", "value": "OriginalCreationDate" },
                    { "tag": "AttributeReference", "type": "Enumeration", "value": "RandomNumberGenerator" },
                    { "tag": "AttributeReference", "type": "Enumeration", "value": "KeyFormatType" }
                ] }
            ]
        }
    ]
}
{
    "tag": "ResponseMessage",
    "type": "Structure",
    "value": [
        {
            "tag": "ResponseHeader",
            "type": "Structure",
            "value": [
                { "tag": "ProtocolVersion", "type": "Structure", "value": [
                    { "tag": "ProtocolVersionMajor", "type": "Integer", "value": 2 },
                    { "tag": "ProtocolVersionMinor", "type": "Integer", "value": 1 }
                ] },
                { "tag": "TimeStamp", "type": "DateTime", "value": "$NOW" },
                { "tag": "BatchCount", "type": "Integer", "value": 1 }
            ]
        },
        {
            "tag": "BatchItem",
            "type": "Structure",
            "value": [
                { "tag": "Operation", "type": "Enumeration", "value": "GetAttributes" },
                { "tag": "ResultStatus", "type": "Enumeration", "value": "Success" },
                { "tag": "ResponsePayload", "type": "Structure", "value": [
                    { "tag": "UniqueIdentifier", "type": "TextString", "value": "$UNIQUE_IDENTIFIER_0" },
                    { "tag": "Attributes", "type": "Structure", "value": [
                        { "tag": "State", "type": "Enumeration", "value": "PreActive" },
                        { "tag": "CryptographicUsageMask", "type": "Integer", "value": "Sign" },
                        { "tag": "UniqueIdentifier", "type": "TextString", "value": "$UNIQUE_IDENTIFIER_0" },
                        { "tag": "ObjectType", "type": "Enumeration", "value": "PrivateKey" },
                        { "tag": "CryptographicAlgorithm", "type": "Enumeration", "value": "RSA" },
                        { "tag": "CryptographicLength", "type": "Integer", "value": 2048 },
                        { "tag": "Digest", "type": "Structure", "value": [
                            { "tag": "HashingAlgorithm", "type": "Enumeration", "value": "SHA_256" },
                            { "tag": "DigestValue", "type": "ByteString", "value": "8eb422ae2b006a05d3c8a542a28536735241b6dc1c37926bc8007bd6220d9230" },
                            { "tag": "KeyFormatType", "type": "Enumeration", "value": "PKCS_1" }
                        ] },
                        { "tag": "InitialDate", "type": "DateTime", "value": "$NOW" },
                        { "tag": "LastChangeDate", "type": "DateTime", "value": "$NOW" },
                        { "tag": "OriginalCreationDate", "type": "DateTime", "value": "$NOW" },
                        { "tag": "RandomNumberGenerator", "type": "Structure", "value": [
                            { "tag": "RNGAlgorithm", "type": "Enumeration", "value": "ANSIX9_31" },
                            { "tag": "CryptographicAlgorithm", "type": "Enumeration", "value": "AES" },
                            { "tag": "CryptographicLength", "type": "Integer", "value": 256 }
                        ] },
                        { "tag": "KeyFormatType", "type": "Enumeration", "value": "PKCS_1" }
                    ] }
                ] }
            ]
        }
    ]
}

3. Destroy (Private Key)

{
    "tag": "RequestMessage",
    "type": "Structure",
    "value": [
        {
            "tag": "RequestHeader",
            "type": "Structure",
            "value": [
                { "tag": "ProtocolVersion", "type": "Structure", "value": [
                    { "tag": "ProtocolVersionMajor", "type": "Integer", "value": 2 },
                    { "tag": "ProtocolVersionMinor", "type": "Integer", "value": 1 }
                ] },
                { "tag": "ClientCorrelationValue", "type": "TextString", "value": "AKLC-M-1-21 step=3" },
                { "tag": "BatchCount", "type": "Integer", "value": 1 }
            ]
        },
        {
            "tag": "BatchItem",
            "type": "Structure",
            "value": [
                { "tag": "Operation", "type": "Enumeration", "value": "Destroy" },
                { "tag": "RequestPayload", "type": "Structure", "value": [
                    { "tag": "UniqueIdentifier", "type": "TextString", "value": "$UNIQUE_IDENTIFIER_0" }
                ] }
            ]
        }
    ]
}
{
    "tag": "ResponseMessage",
    "type": "Structure",
    "value": [
        {
            "tag": "ResponseHeader",
            "type": "Structure",
            "value": [
                { "tag": "ProtocolVersion", "type": "Structure", "value": [
                    { "tag": "ProtocolVersionMajor", "type": "Integer", "value": 2 },
                    { "tag": "ProtocolVersionMinor", "type": "Integer", "value": 1 }
                ] },
                { "tag": "TimeStamp", "type": "DateTime", "value": "$NOW" },
                { "tag": "BatchCount", "type": "Integer", "value": 1 }
            ]
        },
        {
            "tag": "BatchItem",
            "type": "Structure",
            "value": [
                { "tag": "Operation", "type": "Enumeration", "value": "Destroy" },
                { "tag": "ResultStatus", "type": "Enumeration", "value": "Success" },
                { "tag": "ResponsePayload", "type": "Structure", "value": [
                    { "tag": "UniqueIdentifier", "type": "TextString", "value": "$UNIQUE_IDENTIFIER_0" }
                ] }
            ]
        }
    ]
}

In chapter 6, the KMIP 2.1 specifications describe 57 potential operations that can be performed on a KMS.

Supported Operations

Out of this list, the Eviden KMS server only requires 12 operations to provide all required functionalities to support the cryptographic schemes available on the server.

The list of supported Operations is that of the menu entries below this one.

Messages and Bulk Operations

A KMIP request may be made by POSTing a single Operation serialized as JSON TTLV, or by combining multiple operations in a Message request. See the bulk mode page for more details.

No support for "ID Placeholders"

KMIP states that a number of the operations are affected by a mechanism referred to as the ID Placeholder. It is a variable stored inside the server that is preserved during the execution of a batch of operations.

Maintaining this value requires maintaining a state during a batch session across multiple requests and potentially multiple servers. The performance gain of using placeholder IDs is not obvious, and the added complexity of maintaining sessions across multiple servers when scaling horizontally is not worth in the Eviden view for the type of operations conducted on the server.

The Eviden KMS servers are kept stateless to simplify horizontal scaling and therefore do not support placeholder IDs for now.

Specification

This operation requests one or more attributes associated with a Managed Object. The object is specified by its Unique Identifier, and the attributes are specified by their name in the request. If a specified attribute has multiple instances, then all instances are returned. If a specified attribute does not exist (i.e., has no value), then it SHALL NOT be present in the returned response. If none of the requested attributes exist, then the response SHALL consist only of the Unique Identifier. The same Attribute Reference SHALL NOT be present more than once in a request.

If no Attribute Reference is provided, the server SHALL return all attributes.

Implementation

This operation can be applied to all supported objects.

Example - A symmetric key

Get the attributes of a symmetric key by its unique identifier 027cced1-ff2b-4bd3-a200-db1041583bdc.

Corresponding KMS CLI command:

  ckms attributes get -i 027cced1-ff2b-4bd3-a200-db1041583bdc

The request has an empty AttributeReference structure, which means that all attributes are requested.

The response contains all the system and user tags associated with the key. This is the hex encoded value of a JSON array with value

["MySymmetricKey", "_kk"]

The VendorIdentification field in the response reflects the server's configured vendor ID (default: cosmian, configurable via --vendor-identification / KMS_VENDOR_IDENTIFICATION).

{
  "tag": "GetAttributes",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "027cced1-ff2b-4bd3-a200-db1041583bdc"
    },
    {
      "tag": "AttributeReference",
      "type": "Structure",
      "value": []
    }
  ]
}

{
  "tag": "GetAttributesResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "027cced1-ff2b-4bd3-a200-db1041583bdc"
    },
    {
      "tag": "Attributes",
      "type": "Structure",
      "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": "SplitKey"
        },
        {
          "tag": "VendorAttributes",
          "type": "Structure",
          "value": [
            {
              "tag": "VendorAttributes",
              "type": "Structure",
              "value": [
                {
                  "tag": "VendorIdentification",
                  "type": "TextString",
                  "value": "cosmian"
                },
                {
                  "tag": "AttributeName",
                  "type": "TextString",
                  "value": "tag"
                },
                {
                  "tag": "AttributeValue",
                  "type": "ByteString",
                  "value": "5B224D7953796D6D65747269634B6579222C225F6B6B225D"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

Example - A NIST P-256 private key

Get the attributes of a NIST P-256 private key.

Corresponding KMS CLI command:

  ckms attributes get -i 927adccb-f59a-4cc9-a9e3-1eeb958c601f

The request has an empty AttributeReference structure, which means that all attributes are requested.

In the response, please note:

  • the RecommendedCurve is P256, the KMIP name for NIST P-256
  • the tag is the hex encoded value of a JSON array with value ["_sk"], the system tag for a private key
{
  "tag": "GetAttributes",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "927adccb-f59a-4cc9-a9e3-1eeb958c601f"
    },
    {
      "tag": "AttributeReference",
      "type": "Structure",
      "value": []
    }
  ]
}

{
  "tag": "GetAttributesResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "927adccb-f59a-4cc9-a9e3-1eeb958c601f"
    },
    {
      "tag": "Attributes",
      "type": "Structure",
      "value": [
        {
          "tag": "CryptographicAlgorithm",
          "type": "Enumeration",
          "value": "ECDH"
        },
        {
          "tag": "CryptographicLength",
          "type": "Integer",
          "value": 256
        },
        {
          "tag": "CryptographicDomainParameters",
          "type": "Structure",
          "value": [
            {
              "tag": "QLength",
              "type": "Integer",
              "value": 256
            },
            {
              "tag": "RecommendedCurve",
              "type": "Enumeration",
              "value": "P256"
            }
          ]
        },
        {
          "tag": "KeyFormatType",
          "type": "Enumeration",
          "value": "TransparentECPrivateKey"
        },
        {
          "tag": "ObjectType",
          "type": "Enumeration",
          "value": "PrivateKey"
        },
        {
          "tag": "VendorAttributes",
          "type": "Structure",
          "value": [
            {
              "tag": "VendorAttributes",
              "type": "Structure",
              "value": [
                {
                  "tag": "VendorIdentification",
                  "type": "TextString",
                  "value": "cosmian"
                },
                {
                  "tag": "AttributeName",
                  "type": "TextString",
                  "value": "tag"
                },
                {
                  "tag": "AttributeValue",
                  "type": "ByteString",
                  "value": "5B225F736B225D"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

Example - A certificate imported as part of a PKCS#12 container

Get the attributes of the certificate imported as part of a PKCS#12 container (see Import for reference to the imported PKCS#12 container). The certificate is linked to a private key was signed by an intermediate certificate imported as part of the same container.

Corresponding KMS CLI command:

  ckms attributes get -i d2f4e937-dda9-4a86-bbe8-c866646a612f

The request has an empty AttributeReference structure, which means that all attributes are requested.

Please note in the response:

  • the Link to the private key
  • the Link to the intermediate certificate
  • the presence of all the system and user tags associated with the certificate. This is the hex encoded value of a JSON array with value
{
  "tag": "GetAttributes",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "d2f4e937-dda9-4a86-bbe8-c866646a612f"
    },
    {
      "tag": "AttributeReference",
      "type": "Structure",
      "value": []
    }
  ]
}

{
  "tag": "GetAttributesResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "d2f4e937-dda9-4a86-bbe8-c866646a612f"
    },
    {
      "tag": "Attributes",
      "type": "Structure",
      "value": [
        {
          "tag": "KeyFormatType",
          "type": "Enumeration",
          "value": "X509"
        },
        {
          "tag": "Link",
          "type": "Structure",
          "value": [
            {
              "tag": "Link",
              "type": "Structure",
              "value": [
                {
                  "tag": "LinkType",
                  "type": "Enumeration",
                  "value": "PrivateKeyLink"
                },
                {
                  "tag": "LinkedObjectIdentifier",
                  "type": "TextString",
                  "value": "bf614d45-5a3e-49b9-95c0-5586d3c0d17b"
                }
              ]
            },
            {
              "tag": "Link",
              "type": "Structure",
              "value": [
                {
                  "tag": "LinkType",
                  "type": "Enumeration",
                  "value": "CertificateLink"
                },
                {
                  "tag": "LinkedObjectIdentifier",
                  "type": "TextString",
                  "value": "0c9028bc-c518-40d3-8362-12a1edfddab0"
                }
              ]
            }
          ]
        },
        {
          "tag": "ObjectType",
          "type": "Enumeration",
          "value": "Certificate"
        },
        {
          "tag": "VendorAttributes",
          "type": "Structure",
          "value": [
            {
              "tag": "VendorAttributes",
              "type": "Structure",
              "value": [
                {
                  "tag": "VendorIdentification",
                  "type": "TextString",
                  "value": "cosmian"
                },
                {
                  "tag": "AttributeName",
                  "type": "TextString",
                  "value": "tag"
                },
                {
                  "tag": "AttributeValue",
                  "type": "ByteString",
                  "value": "5B225F63657274222C225F636572745F636E3D4D7920736572766572222C225F636572745F73706B693D36353565303430393938383461663363636133653362313164393038626238666432373237306263222C224D79504B43533132222C225F636572745F6973737565723D30633930323862632D633531382D343064332D383336322D313261316564666464616230222C225F636572745F736B3D62663631346434352D356133652D343962392D393563302D353538366433633064313762225D"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

Specification

This operation requests the server to either add or modify an attribute. The request contains the Unique Identifier of the Managed Object to which the attribute pertains, along with the attribute and value. If the object did not have any instances of the attribute, one is created. If the object had exactly one instance, then it is modified. If it has more than one instance an error is raised. Read-Only attributes SHALL NOT be added or modified using this operation.

Implementation

This operation can be applied to all supported objects. One or more attributes can be set at once. The operation is idempotent, meaning that if the attribute is already set, the operation will not fail.

Example - A symmetric key

Set an attribute of a symmetric key by its unique identifier 027cced1-ff2b-4bd3-a200-db1041583bdc.

Corresponding KMS CLI command:

First, create a symmetric key:

  ckms sym keys create my_symmetric_key
  ckms attributes set -i my_symmetric_key --activation-date 1726211157791

The request sets the activation date of a symmetric key.

The response contains all the system and user tags associated with the key. This is the hex encoded value of a JSON array with value

Attribute set successfully
          Unique identifier: 6209aa2a-900f-4a1c-b2ca-9b4af1bbd1d1
{
  "tag": "SetAttribute",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "my_symmetric_key"
    },
    {
      "tag": "NewAttribute",
      "type": "Structure",
      "value": [
        {
          "tag": "Link",
          "type": "Structure",
          "value": [
            {
              "tag": "LinkType",
              "type": "Enumeration",
              "value": "CertificateLink"
            },
            {
              "tag": "LinkedObjectIdentifier",
              "type": "TextString",
              "value": "certificate_id"
            }
          ]
        }
      ]
    }
  ]
}
{
  "tag": "SetAttributeResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "my_symmetric_key"
    }
  ]
}

Set the links on a certificate object. It will set 2 KMIP attributes.

Corresponding KMS CLI command:

  ckms attributes set -i 03948573-9348-aaaa-aaaa-93857383 --public-key-id xxxxxxxx-yyyy-yyyy-yyyy-zzzzzzzzzzzz --private-key-id xxxxxxxx-yyyy-yyyy-yyyy-zzzzzzzzzzzz

The request set the KMIP links of the public key and the private key of the underlying certificate. The SetAttribute operation being unitary, 2 requests are sent to the server, one for each link.

Output is:

Attribute set successfully
          Unique identifier: 6209aa2a-900f-4a1c-b2ca-9b4af1bbd1d1
[
  {
    "LinkType": "PublicKeyLink",
    "LinkedObjectIdentifier": "xxxxxxxx-yyyy-yyyy-yyyy-zzzzzzzzzzzz"
  }
]
Attribute set successfully
          Unique identifier: 6209aa2a-900f-4a1c-b2ca-9b4af1bbd1d1
[
  {
    "LinkType": "PrivateKeyLink",
    "LinkedObjectIdentifier": "xxxxxxxx-yyyy-yyyy-yyyy-zzzzzzzzzzzz"
  }
]
{
  "tag": "SetAttribute",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "my_symmetric_key"
    },
    {
      "tag": "NewAttribute",
      "type": "Structure",
      "value": [
        {
          "tag": "Link",
          "type": "Structure",
          "value": [
            {
              "tag": "LinkType",
              "type": "Enumeration",
              "value": "PublicKeyLink"
            },
            {
              "tag": "LinkedObjectIdentifier",
              "type": "TextString",
              "value": "xxxxxxxx-yyyy-yyyy-yyyy-zzzzzzzzzzzz"
            }
          ]
        }
      ]
    }
  ]
}
{
  "tag": "SetAttribute",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "my_symmetric_key"
    },
    {
      "tag": "NewAttribute",
      "type": "Structure",
      "value": [
        {
          "tag": "Link",
          "type": "Structure",
          "value": [
            {
              "tag": "LinkType",
              "type": "Enumeration",
              "value": "PrivateKeyLink"
            },
            {
              "tag": "LinkedObjectIdentifier",
              "type": "TextString",
              "value": "xxxxxxxx-yyyy-yyyy-yyyy-zzzzzzzzzzzz"
            }
          ]
        }
      ]
    }
  ]
}
{
  "tag": "SetAttributeResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "my_symmetric_key"
    }
  ]
}

Specification

This operation requests the server to modify an attribute value associated with a Managed Object. The request contains the Unique Identifier of the Managed Object whose attribute is to be modified, along with the attribute and its new value. Unlike SetAttribute, ModifyAttribute is intended to replace the value of an existing attribute — it does not create the attribute if it is absent.

Read-Only attributes SHALL NOT be modified using this operation.

Implementation

This operation can be applied to all supported objects.

Supported attributes

The following KMIP attributes can be modified:

AttributeNotes
ActivationDatePre-Active objects only. If the new date ≤ now the object automatically transitions to Active (KMIP spec §3.22).
CryptographicAlgorithmReplaces the algorithm of the managed object.
CryptographicLengthReplaces the key length in bits.
CryptographicParametersReplaces the cryptographic parameters structure.
CryptographicDomainParametersReplaces the domain parameters structure.
CryptographicUsageMaskReplaces the bitmask of allowed cryptographic usages.
DeactivationDateSets or replaces the deactivation date.
DigestReplaces the digest structure.
LinkReplaces the linked object identifier for the given link type.
NameReplaces the first Name entry if one exists, otherwise adds it.
ObjectGroupReplaces the object group string.
ObjectTypeReplaces the object type.
UniqueIdentifierReplaces the unique identifier attribute.
VendorAttributeReplaces a vendor attribute value identified by vendor ID + attribute name.

Read-only attributes

The following attributes are read-only and SHALL NOT be modified. Any attempt returns an Attribute_Read_Only error:

  • State
  • CertificateLength

Example - Modify the cryptographic length of a symmetric key

Corresponding KMS CLI command:

  ckms sym keys create my_symmetric_key
  ckms attributes set   -i my_symmetric_key --cryptographic-length 128
  ckms attributes modify -i my_symmetric_key --cryptographic-length 256

Output:

Attribute modified successfully
          Unique identifier: my_symmetric_key
{
  "tag": "ModifyAttribute",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "my_symmetric_key"
    },
    {
      "tag": "NewAttribute",
      "type": "Structure",
      "value": [
        {
          "tag": "CryptographicLength",
          "type": "Integer",
          "value": 256
        }
      ]
    }
  ]
}
{
  "tag": "ModifyAttributeResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "my_symmetric_key"
    }
  ]
}

Example - Modify the cryptographic algorithm

  ckms attributes modify -i my_symmetric_key --cryptographic-algorithm chacha20
{
  "tag": "ModifyAttribute",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "my_symmetric_key"
    },
    {
      "tag": "NewAttribute",
      "type": "Structure",
      "value": [
        {
          "tag": "CryptographicAlgorithm",
          "type": "Enumeration",
          "value": "ChaCha20"
        }
      ]
    }
  ]
}
{
  "tag": "ModifyAttributeResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "my_symmetric_key"
    }
  ]
}

Example - Modify the activation date on a Pre-Active key

ActivationDate can only be modified on Pre-Active objects. Setting a date that is in the past or equal to the current time will automatically transition the object to the Active state.

  ckms attributes modify -i my_preactive_key --activation-date 1773571883
{
  "tag": "ModifyAttribute",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "my_preactive_key"
    },
    {
      "tag": "NewAttribute",
      "type": "Structure",
      "value": [
        {
          "tag": "ActivationDate",
          "type": "DateTime",
          "value": "2026-03-15T10:11:23+00:00"
        }
      ]
    }
  ]
}
{
  "tag": "ModifyAttributeResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "my_preactive_key"
    }
  ]
}

Example - Read-only attribute rejection

Attempting to modify a read-only attribute such as State will return an Attribute_Read_Only error:

{
  "tag": "ModifyAttribute",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "my_symmetric_key"
    },
    {
      "tag": "NewAttribute",
      "type": "Structure",
      "value": [
        {
          "tag": "State",
          "type": "Enumeration",
          "value": "Active"
        }
      ]
    }
  ]
}
{
  "tag": "ResponseMessage",
  "type": "Structure",
  "value": [
    {
      "tag": "ResponseHeader",
      "type": "Structure",
      "value": [
        {
          "tag": "ProtocolVersion",
          "type": "Structure",
          "value": [
            { "tag": "ProtocolVersionMajor", "type": "Integer", "value": 2 },
            { "tag": "ProtocolVersionMinor", "type": "Integer", "value": 1 }
          ]
        },
        { "tag": "TimeStamp", "type": "DateTime", "value": "2026-03-15T10:11:23+00:00" },
        { "tag": "BatchCount", "type": "Integer", "value": 1 }
      ]
    },
    {
      "tag": "BatchItem",
      "type": "Structure",
      "value": [
        { "tag": "Operation", "type": "Enumeration", "value": "ModifyAttribute" },
        { "tag": "ResultStatus", "type": "Enumeration", "value": "OperationFailed" },
        { "tag": "ResultReason", "type": "Enumeration", "value": "Attribute_Read_Only" },
        { "tag": "ResultMessage", "type": "TextString", "value": "DENIED" }
      ]
    }
  ]
}

Specification

This operation requests the server to delete an attribute associated with a Managed Object. The request contains the Unique Identifier of the Managed Object whose attribute is to be deleted, the Current Attribute of the attribute. Attributes that are always REQUIRED to have a value SHALL never be deleted by this operation. Attempting to delete a non-existent attribute or specifying an Current Attribute for which there exists no attribute value SHALL result in an error. If no Current Attribute is specified in the request, and an Attribute Reference is specified, then all instances of the specified attribute SHALL be deleted.

Implementation

This operation can be applied to all supported objects. One or more attributes can be set at once.

First, for the demo purpose create a certificate:

  ckms certificates certify --generate-key-pair --subject-name C=yo --algorithm rsa2048 -c my_certificate

Delete the links on a certificate object.

Corresponding KMS CLI command:

  ckms attributes delete -i my_certificate --public-key-id xxxxxxxx-yyyy-yyyy-yyyy-zzzzzzzzzzzz --private-key-id xxxxxxxx-yyyy-yyyy-yyyy-zzzzzzzzzzzz --certificate-id xxxxxxxx-yyyy-yyyy-yyyy-zzzzzzzzzzzz

The request deletes the KMIP links of the public key and the private key of the underlying certificate. The DeleteAttribute operation being unitary, 3 requests are sent to the server, one for each link.

Output is:

Attribute deleted successfully
          Unique identifier: my_certificate
[
  {
    "LinkType": "PublicKeyLink",
    "LinkedObjectIdentifier": "xxxxxxxx-yyyy-yyyy-yyyy-zzzzzzzzzzzz"
  }
]
Attribute deleted successfully
          Unique identifier: my_certificate
[
  {
    "LinkType": "PrivateKeyLink",
    "LinkedObjectIdentifier": "xxxxxxxx-yyyy-yyyy-yyyy-zzzzzzzzzzzz"
  }
]
Attribute deleted successfully
          Unique identifier: my_certificate
[
  {
    "LinkType": "CertificateLink",
    "LinkedObjectIdentifier": "xxxxxxxx-yyyy-yyyy-yyyy-zzzzzzzzzzzz"
  }
]
{
  "tag": "DeleteAttribute",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "my_certificate"
    },
    {
      "tag": "CurrentAttribute",
      "type": "Structure",
      "value": [
        {
          "tag": "Link",
          "type": "Structure",
          "value": [
            {
              "tag": "LinkType",
              "type": "Enumeration",
              "value": "PublicKeyLink"
            },
            {
              "tag": "LinkedObjectIdentifier",
              "type": "TextString",
              "value": "xxxxxxxx-yyyy-yyyy-yyyy-zzzzzzzzzzzz"
            }
          ]
        }
      ]
    }
  ]
}
{
  "tag": "DeleteAttribute",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "my_certificate"
    },
    {
      "tag": "CurrentAttribute",
      "type": "Structure",
      "value": [
        {
          "tag": "Link",
          "type": "Structure",
          "value": [
            {
              "tag": "LinkType",
              "type": "Enumeration",
              "value": "PrivateKeyLink"
            },
            {
              "tag": "LinkedObjectIdentifier",
              "type": "TextString",
              "value": "xxxxxxxx-yyyy-yyyy-yyyy-zzzzzzzzzzzz"
            }
          ]
        }
      ]
    }
  ]
}
{
  "tag": "DeleteAttribute",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "my_certificate"
    },
    {
      "tag": "CurrentAttribute",
      "type": "Structure",
      "value": [
        {
          "tag": "Link",
          "type": "Structure",
          "value": [
            {
              "tag": "LinkType",
              "type": "Enumeration",
              "value": "CertificateLink"
            },
            {
              "tag": "LinkedObjectIdentifier",
              "type": "TextString",
              "value": "xxxxxxxx-yyyy-yyyy-yyyy-zzzzzzzzzzzz"
            }
          ]
        }
      ]
    }
  ]
}
{
  "tag": "DeleteAttributeResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "my_certificate"
    }
  ]
}

Specifications

This request is used to generate a Certificate object for a public key. This request supports the certification of a new public key, as well as the certification of a public key that has already been certified (i.e., certificate update). Only a single certificate SHALL be requested at a time.

The Certificate Request object MAY be omitted, in which case the public key for which a Certificate object is generated SHALL be specified by its Unique Identifier only. If the Certificate Request Type and the Certificate Request objects are omitted from the request, then the Certificate Type SHALL be specified using the Attributes object.

The Certificate Request is passed as a Byte String, which allows multiple certificate request types for X.509 certificates (e.g., PKCS#10, PEM, etc.) to be submitted to the server.

The generated Certificate object whose Unique Identifier is returned MAY be obtained by the client via a Get operation in the same batch, using the ID Placeholder mechanism.

For the public key, the server SHALL create a Link attribute of Link Type Certificate pointing to the generated certificate. For the generated certificate, the server SHALL create a Link attribute of Link Type Public Key pointing to the Public Key.

The server SHALL copy the Unique Identifier of the generated certificate returned by this operation into the ID Placeholder variable.

If the information in the Certificate Request conflicts with the attributes specified in the Attributes, then the information in the Certificate Request takes precedence.

Implementation

The KMIP implementation does not:

  • specify how the signer is selected and whether self-signing is allowed
  • specify how the certificate extensions are provided
  • and only supports Certificate Signing Request (CSR) and certifying public keys

Eviden has extended the specifications and offers 4 possibilities to generate a certificate

  1. Providing a Certificate Signing Request (CSR)
  2. Providing a public key id to certify as well as a subject name
  3. Providing an existing certificate id to re-certify
  4. Generating a keypair then signing the public key to generate a certificate specifying a subject name and an algorithm

The signer is specified by providing an issuer private key id and/or an issuer certificate via the Links in the attributes of the request. If only one of this parameter is specified, the other one will be inferred from the links of the cryptographic object behind the provided parameter.

If no signer is provided, the certificate will be self-signed. It is not possible to self-sign a CSR.

When re-certifying a certificate, if no certificate unique identifier is provided, the original certificate id will be used and the original certificate will be replaced by the new one. In all other cases, a random certificate id will be generated.

Post-quantum (PQC) certificates

The Eviden KMS generates fully standards-compliant X.509 v3 certificates for all NIST-standardized post-quantum algorithms:

  • ML-DSA (CRYSTALS-Dilithium) — RFC 9881: self-signed or CA-signed; the KMS automatically adds a critical keyUsage extension with digitalSignature.
  • SLH-DSA (SPHINCS+) — draft-ietf-lamps-x509-slh-dsa: self-signed or CA-signed; same digitalSignature key usage.
  • ML-KEM (CRYSTALS-Kyber) — RFC 9935: always CA-issued (ML-KEM cannot self-sign); the KMS automatically adds a critical keyUsage extension with keyEncipherment only.

See Post-Quantum X.509 Certificates for detailed usage instructions, OID tables, CLI examples, and cross-algorithm PKI guidance.

Supply X509 extensions (optional)

Specify X509 extensions for a Certify operation is possible using the KMS CLI.

The --certificate-extensions arg (short version -e) expects a path to a configuration file written in ini format (roughly the same format as OpenSSL X509 v3 cert extension cnf format).

The extensions may be part of a paragraph called v3_ca.

Example of a configuration file containing v3_ca parag describing extensions to add:

[v3_ca]
basicConstraints=critical,CA:FALSE,pathlen:0
keyUsage=keyCertSign,digitalSignature
extendedKeyUsage=emailProtection
crlDistributionPoints=URI:http://cse.example.com/crl.pem

These extensions are embedded in the Certify request within the vendor attributes.

Example of the corresponding KMS CLI command:

ckms certificates certify \
  -r my_cert.csr -k 854d7914-3b1d-461a-a2dd-7aad27043b56 -d 365 -t "MyCert" \
  -e /some/path/to/ext.cnf

Example - PKCS#10 Certificate Signing Request

Certify a PKCS#10 Certificate Signing Request (CSR) with the issuer private key unique identifier 854d7914-3b1d-461a-a2dd-7aad27043b56, and set the certificate requested validity to 365 days and the tag to MyCert.

The corresponding KMS CLI command is:

ckms certificates certify -r my_cert.csr -k 854d7914-3b1d-461a-a2dd-7aad27043b56 -d 365 -t "MyCert"

Note: the KMS CLIent converts the CSR from PEM TO DER before creating the JSON TTLV and sending it to the server.

{
  "tag": "Certify",
  "type": "Structure",
  "value": [
    {
      "tag": "CertificateRequestType",
      "type": "Enumeration",
      "value": "PEM"
    },
    {
      "tag": "CertificateRequestValue",
      "type": "ByteString",
      "value": "2D2D2D2D2D424547494E20434552544946494341544520524551554553542D2D2D2D2D0A4D494944704443434167774341514177587A454C4D416B474131554542684D43526C49784444414B42674E564241674D41306C6B526A454F4D417747413155450A427777465547467961584D784544414F42674E5642416F4D42304E7663323170595734784444414B42674E564241734D4131496D524445534D424147413155450A4177774A5647567A6443424D5A57466D4D4949426F6A414E42676B71686B6947397730424151454641414F43415938414D49494269674B4341594541773045470A575355754F4E59526C5A3077506139524A7057416C577351515A5050675350786E354D5777464E4F383671676856666378314C387169515079315147687172320A764F766D577A366D752F59772F5663366E44644744694B54555564537341305167566474643770366B71317341393071364C30416E63384D384D46392F6F536F0A7145642F6C4F436774744F6D55667842566C314B6D7146434146464854786E4B5737387954332F3438386B57373952516B6D41367733416246377361787639500A706843365A634F76514F6836644D42326E4E6C574C67537670312B3948674455635956394D53575A6D2B376C524F5468552B41676433363364355A57574F41470A495659544E5A2F6E746B69705270717251352B7356694863752F4E4F544D757733524C632F575347736D6A594E57616465304C6D2F58685032684D67416D6F350A306474792B36307970437342573269504A6652755152743342644249632B3971637946326176786C457431414446556A49726B305353516F39774A45313953440A68534A414A33782B4D31466C6D4C2B34464832726E69777555615A6F6844506938567542367A634430747732524F664471586A2F5A4B356C7A4A6D745A6B53790A5636704B54485035737558372B6A3848324A35554A496F46487A44484764674E315A724C3570773563305A6D65634B516D5756796F394854614B364641674D420A414147674144414E42676B71686B6947397730424151734641414F4341594541594236615738625549306361466443356939334F376542345530535A414745740A612F546B5133486C764456364F2B327A64735042304F4672385262355171784134776A3843536579466C7A775666497172756A48457831557150706E2B7932720A4C6C397236754C7257725A753568664C767749752B774C6A617644425662354E2B7159536548357643334A582B4652385A64787A6B5754776465464C6F4273340A434749456D46494D2F2B666E79676E6B4C455254566B6738337339534B44736838316772755438302F6135365649636D656E373470584830514641615368766F0A7A52486F7670766A5631735A416265595365796B69564E53497179734166594A33327168744173366E5367796751637A546773756A4F746C63776653432B71550A497A4D623932325A654B5445427A7A3859326351394D6245714850787276664B5A675A4A43306B57342F482B736F2B5659776A684956337048705842527944640A6359637275732B434D335A416A456439585A6C39466D37454555736E346D7459486F497541394167756949425152596B473469726863524E5449377A36755A2B0A77544E724D792B47646B436B5271424256714146374E3473696931356E334E716A3535637257452F642F38316B574E36495943504448586C4F38756B4A6E4B750A30524E6A4B52656551624377596B72464C7A4F5A677342674E6C626E364263470A2D2D2D2D2D454E4420434552544946494341544520524551554553542D2D2D2D2D0A"
    },
    {
      "tag": "Attributes",
      "type": "Structure",
      "value": [
        {
          "tag": "Link",
          "type": "Structure",
          "value": [
            {
              "tag": "Link",
              "type": "Structure",
              "value": [
                {
                  "tag": "LinkType",
                  "type": "Enumeration",
                  "value": "PrivateKeyLink"
                },
                {
                  "tag": "LinkedObjectIdentifier",
                  "type": "TextString",
                  "value": "854d7914-3b1d-461a-a2dd-7aad27043b56"
                }
              ]
            }
          ]
        },
        {
          "tag": "ObjectType",
          "type": "Enumeration",
          "value": "Certificate"
        },
        {
          "tag": "VendorAttributes",
          "type": "Structure",
          "value": [
            {
              "tag": "VendorAttributes",
              "type": "Structure",
              "value": [
                {
                  "tag": "VendorIdentification",
                  "type": "TextString",
                  "value": "cosmian"
                },
                {
                  "tag": "AttributeName",
                  "type": "TextString",
                  "value": "requested_validity_days"
                },
                {
                  "tag": "AttributeValue",
                  "type": "ByteString",
                  "value": "333635"
                }
              ]
            },
            {
              "tag": "VendorAttributes",
              "type": "Structure",
              "value": [
                {
                  "tag": "VendorIdentification",
                  "type": "TextString",
                  "value": "cosmian"
                },
                {
                  "tag": "AttributeName",
                  "type": "TextString",
                  "value": "tag"
                },
                {
                  "tag": "AttributeValue",
                  "type": "ByteString",
                  "value": "5B224D7943657274225D"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}
{
  "tag": "CertifyResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "b7225902-a035-45e6-a3d2-fa65c0ca7af1"
    }
  ]
}

Example - Public key

Certify a public key with unique id 45e56e67-d813-468f-9116-4d1e611a1828 using the issuer private key 45e56e67-d813-468f-9116-4d1e611a1828. Set the Subject Name of the certificate to C=FR, ST=IdF, L=Paris, O=AcmeTest, CN=bob@acme.com, the tag to Bob and the certificate requested validity to 365 days.

The corresponding KMS CLI command is

ckms certificates certify -p 45e56e67-d813-468f-9116-4d1e611a1828 -k 854d7914-3b1d-461a-a2dd-7aad27043b56 \
-d 365 -t "Bob" --subject-name "C=FR, ST=IdF, L=Paris, O=AcmeTest, CN=bob@acme.com"

Please note the following in the JSON TTLV of the request:

  • the various Subject Name fields that are set for the certificate
  • the Subject Name issuer fields are ignored: they will be copied from the certificate linked to the issuer private key
{
  "tag": "Certify",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "45e56e67-d813-468f-9116-4d1e611a1828"
    },
    {
      "tag": "Attributes",
      "type": "Structure",
      "value": [
        {
          "tag": "CertificateAttributes",
          "type": "Structure",
          "value": [
            {
              "tag": "CertificateSubjectCn",
              "type": "TextString",
              "value": "bob@acme.com"
            },
            {
              "tag": "CertificateSubjectO",
              "type": "TextString",
              "value": "AcmeTest"
            },
            {
              "tag": "CertificateSubjectOu",
              "type": "TextString",
              "value": ""
            },
            {
              "tag": "CertificateSubjectEmail",
              "type": "TextString",
              "value": ""
            },
            {
              "tag": "CertificateSubjectC",
              "type": "TextString",
              "value": "FR"
            },
            {
              "tag": "CertificateSubjectSt",
              "type": "TextString",
              "value": "IdF"
            },
            {
              "tag": "CertificateSubjectL",
              "type": "TextString",
              "value": "Paris"
            },
            {
              "tag": "CertificateSubjectUid",
              "type": "TextString",
              "value": ""
            },
            {
              "tag": "CertificateSubjectSerialNumber",
              "type": "TextString",
              "value": ""
            },
            {
              "tag": "CertificateSubjectTitle",
              "type": "TextString",
              "value": ""
            },
            {
              "tag": "CertificateSubjectDc",
              "type": "TextString",
              "value": ""
            },
            {
              "tag": "CertificateSubjectDnQualifier",
              "type": "TextString",
              "value": ""
            },
            {
              "tag": "CertificateIssuerCn",
              "type": "TextString",
              "value": ""
            },
            {
              "tag": "CertificateIssuerO",
              "type": "TextString",
              "value": ""
            },
            {
              "tag": "CertificateIssuerOu",
              "type": "TextString",
              "value": ""
            },
            {
              "tag": "CertificateIssuerEmail",
              "type": "TextString",
              "value": ""
            },
            {
              "tag": "CertificateIssuerC",
              "type": "TextString",
              "value": ""
            },
            {
              "tag": "CertificateIssuerSt",
              "type": "TextString",
              "value": ""
            },
            {
              "tag": "CertificateIssuerL",
              "type": "TextString",
              "value": ""
            },
            {
              "tag": "CertificateIssuerUid",
              "type": "TextString",
              "value": ""
            },
            {
              "tag": "CertificateIssuerSerialNumber",
              "type": "TextString",
              "value": ""
            },
            {
              "tag": "CertificateIssuerTitle",
              "type": "TextString",
              "value": ""
            },
            {
              "tag": "CertificateIssuerDc",
              "type": "TextString",
              "value": ""
            },
            {
              "tag": "CertificateIssuerDnQualifier",
              "type": "TextString",
              "value": ""
            }
          ]
        },
        {
          "tag": "Link",
          "type": "Structure",
          "value": [
            {
              "tag": "Link",
              "type": "Structure",
              "value": [
                {
                  "tag": "LinkType",
                  "type": "Enumeration",
                  "value": "PrivateKeyLink"
                },
                {
                  "tag": "LinkedObjectIdentifier",
                  "type": "TextString",
                  "value": "854d7914-3b1d-461a-a2dd-7aad27043b56"
                }
              ]
            }
          ]
        },
        {
          "tag": "ObjectType",
          "type": "Enumeration",
          "value": "Certificate"
        },
        {
          "tag": "VendorAttributes",
          "type": "Structure",
          "value": [
            {
              "tag": "VendorAttributes",
              "type": "Structure",
              "value": [
                {
                  "tag": "VendorIdentification",
                  "type": "TextString",
                  "value": "cosmian"
                },
                {
                  "tag": "AttributeName",
                  "type": "TextString",
                  "value": "requested_validity_days"
                },
                {
                  "tag": "AttributeValue",
                  "type": "ByteString",
                  "value": "333635"
                }
              ]
            },
            {
              "tag": "VendorAttributes",
              "type": "Structure",
              "value": [
                {
                  "tag": "VendorIdentification",
                  "type": "TextString",
                  "value": "cosmian"
                },
                {
                  "tag": "AttributeName",
                  "type": "TextString",
                  "value": "tag"
                },
                {
                  "tag": "AttributeValue",
                  "type": "ByteString",
                  "value": "5B22426F62225D"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}
{
  "tag": "CertifyResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "974b3a79-25a8-4ace-bdd9-70f5b07695c9"
    }
  ]
}

Specification

This operation requests the server to generate a new symmetric key or generate Secret Data as a Managed Cryptographic Object.

The request contains information about the type of object being created, and some of the attributes to be assigned to the object (e.g., Cryptographic Algorithm, Cryptographic Length, etc.).

The response contains the Unique Identifier of the created object. The server SHALL copy the Unique Identifier returned in this operation into the ID Placeholder variable.

Implementation

This operation can be used to create a new symmetric key or a new Covercrypt user decryption key.

Example - Symmetric Key

Creating a 256 bit AES Symmetric Key with the tag MySymmetricKey.

The tags are assembled in a JSON array and encoded in hex.

The CryptographicUsageMask is optional.

Corresponding KMS CLI command:

ckms sym keys create --tag MySymmetricKey
{
  "tag": "Create",
  "type": "Structure",
  "value": [
    {
      "tag": "ObjectType",
      "type": "Enumeration",
      "value": "SymmetricKey"
    },
    {
      "tag": "Attributes",
      "type": "Structure",
      "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"
        },
        {
          "tag": "Attribute",
          "type": "Structure",
          "value": [
            {
              "tag": "VendorIdentification",
              "type": "TextString",
              "value": "cosmian"
            },
            {
              "tag": "AttributeName",
              "type": "TextString",
              "value": "tag"
            },
            {
              "tag": "AttributeValue",
              "type": "TextString",
              "value": "[\"MySymmetricKey\"]"
            }
          ]
        }
      ]
    }
  ]
}
{
  "tag": "CreateResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "ObjectType",
      "type": "Enumeration",
      "value": "SymmetricKey"
    },
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "027cced1-ff2b-4bd3-a200-db1041583bdc"
    }
  ]
}

Example - Covercrypt User Decryption Key

Creating a Covercrypt User Decryption Key with the tag MyUserKey and the access policy Security Level::Confidential && (Department::FIN || Department::HR) (see Create Key Pair for the corresponding master key access structure).

Corresponding KMS CLI command:

ckms cc keys create-user-key -t "MyUserKey"\
 b652a48a-a48c-4dc1-bd7e-cf0e5126b7b9 \
 "Security Level::Confidential && (Department::FIN || Department::HR)"

Please note:

  • The tag(s) is (are) assembled in a JSON array and encoded in hex.
  • The access policy is encoded in hex.
{
  "tag": "Create",
  "type": "Structure",
  "value": [
    {
      "tag": "ObjectType",
      "type": "Enumeration",
      "value": "PrivateKey"
    },
    {
      "tag": "Attributes",
      "type": "Structure",
      "value": [
        {
          "tag": "CryptographicAlgorithm",
          "type": "Enumeration",
          "value": "CoverCrypt"
        },
        {
          "tag": "KeyFormatType",
          "type": "Enumeration",
          "value": "CoverCryptSecretKey"
        },
        {
          "tag": "Link",
          "type": "Structure",
          "value": [
            {
              "tag": "Link",
              "type": "Structure",
              "value": [
                {
                  "tag": "LinkType",
                  "type": "Enumeration",
                  "value": "ParentLink"
                },
                {
                  "tag": "LinkedObjectIdentifier",
                  "type": "TextString",
                  "value": "b652a48a-a48c-4dc1-bd7e-cf0e5126b7b9"
                }
              ]
            }
          ]
        },
        {
          "tag": "ObjectType",
          "type": "Enumeration",
          "value": "PrivateKey"
        },
        {
          "tag": "VendorAttributes",
          "type": "Structure",
          "value": [
            {
              "tag": "VendorAttributes",
              "type": "Structure",
              "value": [
                {
                  "tag": "VendorIdentification",
                  "type": "TextString",
                  "value": "cosmian"
                },
                {
                  "tag": "AttributeName",
                  "type": "TextString",
                  "value": "cover_crypt_access_policy"
                },
                {
                  "tag": "AttributeValue",
                  "type": "ByteString",
                  "value": "5365637572697479204C6576656C3A3A436F6E666964656E7469616C20262620284465706172746D656E743A3A46494E207C7C204465706172746D656E743A3A485229"
                }
              ]
            },
            {
              "tag": "VendorAttributes",
              "type": "Structure",
              "value": [
                {
                  "tag": "VendorIdentification",
                  "type": "TextString",
                  "value": "cosmian"
                },
                {
                  "tag": "AttributeName",
                  "type": "TextString",
                  "value": "tag"
                },
                {
                  "tag": "AttributeValue",
                  "type": "ByteString",
                  "value": "5B224D79557365724B6579225D"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}
{
  "tag": "CreateResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "ObjectType",
      "type": "Enumeration",
      "value": "PrivateKey"
    },
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "df871e79-0923-47cd-9078-bbec83287c85"
    }
  ]
}

Specification

This operation requests the server to generate a new public/private key pair and register the two corresponding new Managed Cryptographic Objects.

The request contains attributes to be assigned to the objects (e.g., Cryptographic Algorithm, Cryptographic Length, etc.). Attributes MAY be specified for both keys at the same time by specifying a Common Attributes object in the request.

Attributes not common to both keys (e.g., Name, Cryptographic Usage Mask) MAY be specified using the Private Key Attributes and Public Key Attributes objects in the request, which take precedence over the Common Attributes object.

For the Private Key, the server SHALL create a Link attribute of Link Type Public Key pointing to the Public Key. For the Public Key, the server SHALL create a Link attribute of Link Type Private Key pointing to the Private Key. The response contains the Unique Identifiers of both created objects. The ID Placeholder value SHALL be set to the Unique Identifier of the Private Key.

Implementation

Please see the supported objects for the list of key pairs that can be created with this operation.

Example - X25519 Key Pair

Creating an X25519 key pair with the tag MyECKeyPair.

Please note:

  • The tag is set in a JSON array and is hex-encoded before being added to the KMIP request.
  • The KeyFormatType is set to ECPrivateKey.
  • The ObjectType is set to PrivateKey.
  • The CryptographicAlgorithm is set to ECDH.
  • The RecommendedCurve is set to CURVE25519.

Corresponding KMS CLI command:

ckms ec keys create
{
  "tag": "CreateKeyPair",
  "type": "Structure",
  "value": [
    {
      "tag": "CommonAttributes",
      "type": "Structure",
      "value": [
        {
          "tag": "CryptographicAlgorithm",
          "type": "Enumeration",
          "value": "ECDH"
        },
        {
          "tag": "CryptographicLength",
          "type": "Integer",
          "value": 253
        },
        {
          "tag": "CryptographicDomainParameters",
          "type": "Structure",
          "value": [
            {
              "tag": "QLength",
              "type": "Integer",
              "value": 253
            },
            {
              "tag": "RecommendedCurve",
              "type": "Enumeration",
              "value": "CURVE25519"
            }
          ]
        },
        {
          "tag": "CryptographicUsageMask",
          "type": "Integer",
          "value": 2108
        },
        {
          "tag": "KeyFormatType",
          "type": "Enumeration",
          "value": "ECPrivateKey"
        },
        {
          "tag": "ObjectType",
          "type": "Enumeration",
          "value": "PrivateKey"
        },
        {
          "tag": "VendorAttributes",
          "type": "Structure",
          "value": [
            {
              "tag": "VendorAttributes",
              "type": "Structure",
              "value": [
                {
                  "tag": "VendorIdentification",
                  "type": "TextString",
                  "value": "cosmian"
                },
                {
                  "tag": "AttributeName",
                  "type": "TextString",
                  "value": "tag"
                },
                {
                  "tag": "AttributeValue",
                  "type": "ByteString",
                  "value": "5B224D7945434B657950616972225D"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

{
  "tag": "CreateKeyPairResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "PrivateKeyUniqueIdentifier",
      "type": "TextString",
      "value": "1ac18648-ab17-4755-97a3-7a24b8198b97"
    },
    {
      "tag": "PublicKeyUniqueIdentifier",
      "type": "TextString",
      "value": "52573030-0fed-4c67-b311-ceac944b2afc"
    }
  ]
}

Example -Covercrypt Master Key Pair

Creating a Covercrypt master key pair with the following access structure specifications which is hex-encoded before being added to the KMIP request.

Note: it is much easier to use the KMS CLI to create Covercrypt master keys where a simple specification file can be used. Use the debug mode to get the hex-encoded access structure from the specifications.

For a specification file

{
    "Security Level::<": [
        "Protected",
        "Confidential",
        "Top Secret::+"
    ],
    "Department": [
        "RnD",
        "HR",
        "MKG",
        "FIN"
    ]
}

The access structure to hex-encode to the call will be:

{
  "version": "V2",
  "last_attribute_value": 7,
  "dimensions": {
    "Security Level": {
      "order": [
        "Protected",
        "Confidential",
        "Top Secret"
      ],
      "attributes": {
        "Confidential": {
          "rotation_values": [
            6
          ],
          "encryption_hint": "Classic",
          "write_status": "EncryptDecrypt"
        },
        "Top Secret": {
          "rotation_values": [
            7
          ],
          "encryption_hint": "Hybridized",
          "write_status": "EncryptDecrypt"
        },
        "Protected": {
          "rotation_values": [
            5
          ],
          "encryption_hint": "Classic",
          "write_status": "EncryptDecrypt"
        }
      }
    },
    "Department": {
      "order": null,
      "attributes": {
        "MKG": {
          "rotation_values": [
            3
          ],
          "encryption_hint": "Classic",
          "write_status": "EncryptDecrypt"
        },
        "FIN": {
          "rotation_values": [
            4
          ],
          "encryption_hint": "Classic",
          "write_status": "EncryptDecrypt"
        },
        "RnD": {
          "rotation_values": [
            1
          ],
          "encryption_hint": "Classic",
          "write_status": "EncryptDecrypt"
        },
        "HR": {
          "rotation_values": [
            2
          ],
          "encryption_hint": "Classic",
          "write_status": "EncryptDecrypt"
        }
      }
    }
  }
}

Corresponding KMS CLI command:

ckms cc keys create-master-key-pair -s access_structure_specifications.json
{
  "tag": "CreateKeyPair",
  "type": "Structure",
  "value": [
    {
      "tag": "CommonAttributes",
      "type": "Structure",
      "value": [
        {
          "tag": "CryptographicAlgorithm",
          "type": "Enumeration",
          "value": "CoverCrypt"
        },
        {
          "tag": "KeyFormatType",
          "type": "Enumeration",
          "value": "CoverCryptSecretKey"
        },
        {
          "tag": "ObjectType",
          "type": "Enumeration",
          "value": "PrivateKey"
        },
        {
          "tag": "VendorAttributes",
          "type": "Structure",
          "value": [
            {
              "tag": "VendorAttributes",
              "type": "Structure",
              "value": [
                {
                  "tag": "VendorIdentification",
                  "type": "TextString",
                  "value": "cosmian"
                },
                {
                  "tag": "AttributeName",
                  "type": "TextString",
                  "value": "cover_crypt_policy"
                },
                {
                  "tag": "AttributeValue",
                  "type": "ByteString",
                  "value": "7B2276657273696F6E223A225632222C226C6173745F6174747269627574655F76616C7565223A372C2264696D656E73696F6E73223A7B225365637572697479204C6576656C223A7B226F72646572223A5B2250726F746563746564222C22436F6E666964656E7469616C222C22546F7020536563726574225D2C2261747472696275746573223A7B22436F6E666964656E7469616C223A7B22726F746174696F6E5F76616C756573223A5B365D2C22656E6372797074696F6E5F68696E74223A22436C6173736963222C2277726974655F737461747573223A22456E637279707444656372797074227D2C22546F7020536563726574223A7B22726F746174696F6E5F76616C756573223A5B375D2C22656E6372797074696F6E5F68696E74223A22487962726964697A6564222C2277726974655F737461747573223A22456E637279707444656372797074227D2C2250726F746563746564223A7B22726F746174696F6E5F76616C756573223A5B355D2C22656E6372797074696F6E5F68696E74223A22436C6173736963222C2277726974655F737461747573223A22456E637279707444656372797074227D7D7D2C224465706172746D656E74223A7B226F72646572223A6E756C6C2C2261747472696275746573223A7B224D4B47223A7B22726F746174696F6E5F76616C756573223A5B335D2C22656E6372797074696F6E5F68696E74223A22436C6173736963222C2277726974655F737461747573223A22456E637279707444656372797074227D2C2246494E223A7B22726F746174696F6E5F76616C756573223A5B345D2C22656E6372797074696F6E5F68696E74223A22436C6173736963222C2277726974655F737461747573223A22456E637279707444656372797074227D2C22522644223A7B22726F746174696F6E5F76616C756573223A5B315D2C22656E6372797074696F6E5F68696E74223A22436C6173736963222C2277726974655F737461747573223A22456E637279707444656372797074227D2C224852223A7B22726F746174696F6E5F76616C756573223A5B325D2C22656E6372797074696F6E5F68696E74223A22436C6173736963222C2277726974655F737461747573223A22456E637279707444656372797074227D7D7D7D7D"
                }
              ]
            },
            {
              "tag": "VendorAttributes",
              "type": "Structure",
              "value": [
                {
                  "tag": "VendorIdentification",
                  "type": "TextString",
                  "value": "cosmian"
                },
                {
                  "tag": "AttributeName",
                  "type": "TextString",
                  "value": "tag"
                },
                {
                  "tag": "AttributeValue",
                  "type": "ByteString",
                  "value": "5B5D"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}
{
  "tag": "CreateKeyPairResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "PrivateKeyUniqueIdentifier",
      "type": "TextString",
      "value": "b652a48a-a48c-4dc1-bd7e-cf0e5126b7b9"
    },
    {
      "tag": "PublicKeyUniqueIdentifier",
      "type": "TextString",
      "value": "0fd1f684-156c-4ca6-adc2-0a6f4b620463"
    }
  ]
}

Specification

This operation requests the server to perform a decryption operation on the provided data using a Managed Cryptographic Object as the key for the decryption operation.

The request contains information about the cryptographic parameters (mode and padding method), the data to be decrypted, and the IV/Counter/Nonce to use. The cryptographic parameters MAY be omitted from the request as they can be specified as associated attributes of the Managed Cryptographic Object. The initialization vector/counter/nonce MAY also be omitted from the request if the algorithm does not use an IV/Counter/Nonce.

The response contains the Unique Identifier of the Managed Cryptographic Object used as the key and the result of the decryption operation.

The success or failure of the operation is indicated by the Result Status (and if failure, the Result Reason) in the response header.

Implementation

To see the list of supported cryptographic algorithms, please refer to Supported Algorithms.

Example - AES GCM decryption

Decrypting the text Hello, world! with symmetric key 027cced1-ff2b-4bd3-a200-db1041583bd (go to [Create](. /_create.md) to see how to create the symmetric key).

Instead of using the UID of the key, we can use the unique tag of the key MySymmetricKey. The key must be uniquely identified. It is possible to use multiple tags to identify a key; for instance symmetric keys automatically get a system tag _kk. See tagging for more information on tags.

Corresponding KMS CLI command:

ckms sym decrypt /tmp/encrypted.bin -t "MySymmetricKey"

where /tmp/encrypted.bin contains the a concatenation of the the nonce, the encrypted and the authentication tag in that order.

The JSON TTLV request the same information as in the Encrypt Response:

  • the encrypted data
  • the nonce: 12 bytes
  • the authentication tag: 16 bytes
{
  "tag": "Decrypt",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "[\"MySymmetricKey\"]"
    },
    {
      "tag": "Data",
      "type": "ByteString",
      "value": "40D59A0735811135749A507FDEB3"
    },
    {
      "tag": "IVCounterNonce",
      "type": "ByteString",
      "value": "DBDD622A64F7D65E75894B1B"
    },
    {
      "tag": "AuthenticatedEncryptionTag",
      "type": "ByteString",
      "value": "50FCE680540BD3E96EFA9218A2F1009D"
    }
  ]
}

{
  "tag": "DecryptResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "027cced1-ff2b-4bd3-a200-db1041583bdc"
    },
    {
      "tag": "Data",
      "type": "ByteString",
      "value": "48656C6C6F2C20776F726C64210A"
    }
  ]
}

Example - Covercrypt

Decrypting the text Hello, world! with Covercrypt user decryption key df871e79-0923-47cd-9078-bbec83287c85 (go to Create to see how to create the Covercrypt user decryption key).

Instead of using the UID of the key, we can use the unique tag of the key MyUserKey. The key must be uniquely identified. It is possible to use multiple tags to identify a key; for instance Covercrypt user decryption keys automatically get a system tag _uk. See tagging for more information on tags.

Corresponding KMS CLI command:

ckms cc decrypt /tmp/encrypted.bin  -t "MyUserKey"
{
  "tag": "Decrypt",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "[\"MyUserKey\"]"
    },
    {
      "tag": "CryptographicParameters",
      "type": "Structure",
      "value": [
        {
          "tag": "CryptographicAlgorithm",
          "type": "Enumeration",
          "value": "CoverCrypt"
        }
      ]
    },
    {
      "tag": "Data",
      "type": "ByteString",
      "value": "AEA6CF824612448B8445CAF46F9D987161706DAD6E43DFD1A57DD0F39869DC39A68096657A3EDC03CBC619D563744D2CC9819B6A9AB9A3893FD27F452F49A244A8CAA42279C4705D4D3A9E04D2B7887F0100D947F27D27BBD1D06F5A65087F73B8AAB617568761273282D4C14770FFCBA47200D02DDB4C48E1028DC5C50DE860A10A26E35AC405EFE6405486B56E9968594471075687D7BF6935BD003D"
    }
  ]
}
{
  "tag": "DecryptResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "df871e79-0923-47cd-9078-bbec83287c85"
    },
    {
      "tag": "Data",
      "type": "ByteString",
      "value": "0048656C6C6F2C20776F726C64210A"
    }
  ]
}

Specification

This operation is used to indicate to the server that the key material for the specified Managed Object SHALL be destroyed or rendered inaccessible. The meta-data for the key material SHALL be retained by the server. Objects SHALL only be destroyed if they are in either Pre-Active or Deactivated state.

Implementation

To destroy a key, it must be revoked using the Revoke operation first, unless it belongs to an external store, such as an HSM.

Eviden has added an option Remove flag to the Destroy operation. If the Remove flag is set to true, the key is completely removed from the database. This does not follow the KMIP 2.1 specification, but is useful in scenarios where the key was incorrectly created and the ID must ne re-used or for GDPR compliance, when the key is associated with personal data.

HSM keys are systematically removed when calling the destroy operation.

Unless, they are removed, destroyed keys are set in the state destroyed on the Eviden KMS Server. They can only be retrieved using theExport operation. The Get operation will return an error. No key material will be returned by the Export operation, only metadata.

Example - Symmetric key

Destroying key f54f14a3-5639-4054-8c23-54af891669db:

Corresponding cosmian command:

  ckms sym keys destroy -k f54f14a3-5639-4054-8c23-54af891669db --remove
{
  "tag": "Destroy",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "f54f14a3-5639-4054-8c23-54af891669db"
    },
    {
      "tag": "Remove",
      "type": "Boolean",
      "value": true
    }
  ]
}
{
  "tag": "DestroyResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "f54f14a3-5639-4054-8c23-54af891669db"
    }
  ]
}

Specification

This operation requests the server to perform an encryption operation on the provided data using a Managed Cryptographic Object as the key for the encryption operation.

The request contains information about the cryptographic parameters (mode and padding method), the data to be encrypted, and the IV/Counter/Nonce to use. The cryptographic parameters MAY be omitted from the request as they can be specified as associated attributes of the Managed Cryptographic Object. The IV/Counter/Nonce MAY also be omitted from the request if the cryptographic parameters indicate that the server shall generate a Random IV on behalf of the client or the encryption algorithm does not need an IV/Counter/Nonce. The server does not store or otherwise manage the IV/Counter/Nonce.

If the Managed Cryptographic Object referenced has a Usage Limits attribute, then the server SHALL obtain an allocation from the current Usage Limits value prior to performing the encryption operation. If the allocation is unable to be obtained, the operation SHALL return with a result status of Operation Failed and result reason of Permission Denied.

The response contains the Unique Identifier of the Managed Cryptographic Object used as the key and the result of the encryption operation.

The success or failure of the operation is indicated by the Result Status (and, if failure, the Result Reason) in the response header.

Implementation

To see the list of supported cryptographic algorithms, please refer to Supported Algorithms.

Example - AES GCM encryption

Encrypting the text Hello, world! with symmetric key 027cced1-ff2b-4bd3-a200-db1041583bd (go to Create to see how to create the symmetric key).

Corresponding KMS CLI command:

ckms sym encrypt -t "MySymmetricKey" /tmp/hello_world.txt

Note: the file /tmp/hello_world.txt contains the text Hello, world!.

Please note that the response contains:

  • the encrypted data
  • the nonce: 12 bytes
  • the authentication tag: 16 bytes
{
  "tag": "Encrypt",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "[\"MySymmetricKey\"]"
    },
    {
      "tag": "CryptographicParameters",
      "type": "Structure",
      "value": [
        {
          "tag": "BlockCipherMode",
          "type": "Enumeration",
          "value": "GCM"
        },
        {
          "tag": "CryptographicAlgorithm",
          "type": "Enumeration",
          "value": "AES"
        }
      ]
    },
    {
      "tag": "Data",
      "type": "ByteString",
      "value": "48656C6C6F2C20776F726C64210A"
    }
  ]
}

{
  "tag": "EncryptResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "027cced1-ff2b-4bd3-a200-db1041583bdc"
    },
    {
      "tag": "Data",
      "type": "ByteString",
      "value": "40D59A0735811135749A507FDEB3"
    },
    {
      "tag": "IVCounterNonce",
      "type": "ByteString",
      "value": "DBDD622A64F7D65E75894B1B"
    },
    {
      "tag": "AuthenticatedEncryptionTag",
      "type": "ByteString",
      "value": "50FCE680540BD3E96EFA9218A2F1009D"
    }
  ]
}

Example - Covercrypt

Encrypting the text Hello, world! with the Covercrypt master public key 0fd1f684-156c-4ca6-adc2-0a6f4b620463 (go to Create Key Pair to see how to create the mater key pair) and attributes Security Level::Confidential && Department::FIN.

Corresponding KMS CLI command:

ckms cc encrypt -k 0fd1f684-156c-4ca6-adc2-0a6f4b620463 \
 /tmp/hello_world.txt "Security Level::Confidential && Department::FIN"

Note: the file /tmp/hello_world.txt contains the text Hello, world!.

In the request, please note that the Data parameter contains:

  • the length of the bytes of the attributes: 47 = 2F in hexadecimal
  • the attributes as bytes: Security Level::Confidential && Department::FIN
  • the bytes to encrypt: Hello, world! as UTF-8 bytes
{
  "tag": "Encrypt",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "0fd1f684-156c-4ca6-adc2-0a6f4b620463"
    },
    {
      "tag": "CryptographicParameters",
      "type": "Structure",
      "value": [
        {
          "tag": "CryptographicAlgorithm",
          "type": "Enumeration",
          "value": "CoverCrypt"
        }
      ]
    },
    {
      "tag": "Data",
      "type": "ByteString",
      "value": "2F5365637572697479204C6576656C3A3A436F6E666964656E7469616C202626204465706172746D656E743A3A46494E0048656C6C6F2C20776F726C64210A"
    }
  ]
}
{
  "tag": "EncryptResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "0fd1f684-156c-4ca6-adc2-0a6f4b620463"
    },
    {
      "tag": "Data",
      "type": "ByteString",
      "value": "AEA6CF824612448B8445CAF46F9D987161706DAD6E43DFD1A57DD0F39869DC39A68096657A3EDC03CBC619D563744D2CC9819B6A9AB9A3893FD27F452F49A244A8CAA42279C4705D4D3A9E04D2B7887F0100D947F27D27BBD1D06F5A65087F73B8AAB617568761273282D4C14770FFCBA47200D02DDB4C48E1028DC5C50DE860A10A26E35AC405EFE6405486B56E9968594471075687D7BF6935BD003D"
    },
    {
      "tag": "IVCounterNonce",
      "type": "ByteString",
      "value": "1A2B3C4D5E6F708192A3B4C5D6E7F801"
    },
    {
      "tag": "AuthenticatedEncryptionTag",
      "type": "ByteString",
      "value": "0DCB3857D841616BF7262193BC19CBC5"
    }
  ]
}

Specification

This operation requests that the server returns a Managed Object specified by its Unique Identifier, together with its attributes.

The Key Format Type, Key Wrap Type, Key Compression Type and Key Wrapping Specification SHALL have the same semantics as for the Get operation. If the Managed Object has been Destroyed then the key material for the specified managed object SHALL not be returned in the response.

The server SHALL copy the Unique Identifier returned by this operations into the ID Placeholder variable.

Implementation

The Export operation - contrarily to the Getoperation - allows exporting objects which have been revoked or destroyed. When an object is destroyed, the key material cannot be exported anymore; only the attributes are returned.

To be able to export an Object the user must have the export permission on the object or be the object owner.

Key wrapping and unwrapping on export is supported for all keys. Please check the algorithms page for more details, including how to invoke CKM_RSA_AES_KEY_WRAP using CryptographicAlgorithm::RSA + PaddingMethod::None.

For the list of supported key formats, please check the formats page.

Examples - Check Get

An export example is provided below but it is in every point similar to the Get operation save for the name of the operation. To run Export instead of Get with he KMS CLI, pass the --allow-revoked flag on the command line.

Please check the Get page for more examples.

Example - Symmetric Key

Exporting a symmetric key 027cced1-ff2b-4bd3-a200-db1041583bdc (go to Create to see how to create the symmetric key).

Instead of using the UID of the key, we can use the unique tag of the key MySymmetricKey. The key must be uniquely identified. It is possible to use multiple tags to identify a key; for instance symmetric keys automatically get a system tag _kk. See tagging for more information on tags.

The response is in Rawformat, the default format for symmetric keys specified by KMIP 2.1; see the [formats page](. /formats.md) for details.

Corresponding KMS CLI command:

ckms sym keys export -t "MySymmetricKey" /tmp/sym_key.json  --allow-revoked
{
  "tag": "Export",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "[\"MySymmetricKey\"]"
    },
    {
      "tag": "KeyWrapType",
      "type": "Enumeration",
      "value": "AsRegistered"
    }
  ]
}

{
  "tag": "ExportResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "ObjectType",
      "type": "Enumeration",
      "value": "SymmetricKey"
    },
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "027cced1-ff2b-4bd3-a200-db1041583bdc"
    },
    {
      "tag": "Object",
      "type": "Structure",
      "value": [
        {
          "tag": "KeyBlock",
          "type": "Structure",
          "value": [
            {
              "tag": "KeyFormatType",
              "type": "Enumeration",
              "value": "Raw"
            },
            {
              "tag": "KeyValue",
              "type": "Structure",
              "value": [
                {
                  "tag": "KeyMaterial",
                  "type": "ByteString",
                  "value": "0B3E539510BABD291BB9FEC2A390C833B05465F33374575CE4AAFFABD5E93020"
                },
                {
                  "tag": "Attributes",
                  "type": "Structure",
                  "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"
                    }
                  ]
                }
              ]
            },
            {
              "tag": "CryptographicAlgorithm",
              "type": "Enumeration",
              "value": "AES"
            },
            {
              "tag": "CryptographicLength",
              "type": "Integer",
              "value": 256
            }
          ]
        }
      ]
    }
  ]
}

Specification

This operation requests that the server returns the Managed Object specified by its Unique Identifier. Only a single object is returned.

The response contains the Unique Identifier of the object, along with the object itself, which MAY be wrapped using a wrapping key as specified in the request. The following key format capabilities SHALL be assumed by the client; restrictions apply when the client requests the server to return an object in a particular format:

  • If a client registers a key in a given format, the server SHALL be able to return the key during the Get operation in the same format that was used when the key was registered.

  • Any other format conversion MAY be supported by the server.

If Key Format Type is specified to be PKCS#12 then the response payload shall be a PKCS#12 container as specified by [RFC7292].

The Unique Identifier shall be either that of a private key or certificate to be included in the response.

The container shall be protected using the Secret Data object specified via the private key or certificate's PKCS#12 Password Link. The current certificate chain shall also be included as determined by using the private key's Public Key link to get the corresponding public key (where relevant) and then using that public key's PKCS#12 Certificate Link to get the base certificate, and then using each certificate's Certificate Link to build the certificate chain. It is an error if there is more than one valid certificate chain.

Implementation

The Get operation allows exporting Active objects only. When an object is Destroyed or Deactivated, the Export operation must be used instead.

Key wrapping and unwrapping on export is supported for all keys. Please check the algorithms page for more details.

For the list of supported key formats, please check the formats page.

Example - A symmetric key

Exporting a symmetric key 027cced1-ff2b-4bd3-a200-db1041583bdc (go to Create to see how to create the symmetric key).

Instead of using the UID of the key, we can use the unique tag of the key my_symmetric_key. The key must be uniquely identified. It is possible to use multiple tags to identify a key; for instance symmetric keys automatically get a system tag _kk. See tagging for more information on tags.

The response is in Rawformat, the default format for symmetric keys specified by KMIP 2.1; see the [formats page](. /formats.md) for details.

Corresponding KMS CLI command:

  ckms sym keys create my_symmetric_key
  ckms sym keys export -t my_symmetric_key /tmp/sym_key.json
{
  "tag": "Get",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "[my_symmetric_key]"
    },
    {
      "tag": "KeyWrapType",
      "type": "Enumeration",
      "value": "AsRegistered"
    }
  ]
}

{
  "tag": "GetResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "ObjectType",
      "type": "Enumeration",
      "value": "SymmetricKey"
    },
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "027cced1-ff2b-4bd3-a200-db1041583bdc"
    },
    {
      "tag": "Object",
      "type": "Structure",
      "value": [
        {
          "tag": "KeyBlock",
          "type": "Structure",
          "value": [
            {
              "tag": "KeyFormatType",
              "type": "Enumeration",
              "value": "Raw"
            },
            {
              "tag": "KeyValue",
              "type": "Structure",
              "value": [
                {
                  "tag": "KeyMaterial",
                  "type": "ByteString",
                  "value": "0B3E539510BABD291BB9FEC2A390C833B05465F33374575CE4AAFFABD5E93020"
                },
                {
                  "tag": "Attributes",
                  "type": "Structure",
                  "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"
                    }
                  ]
                }
              ]
            },
            {
              "tag": "CryptographicAlgorithm",
              "type": "Enumeration",
              "value": "AES"
            },
            {
              "tag": "CryptographicLength",
              "type": "Integer",
              "value": 256
            }
          ]
        }
      ]
    }
  ]
}

Example - A wrapped Covercrypt user key

Exporting a wrapped Covercrypt user key df871e79-0923-47cd-9078-bbec83287c85 (go to Create to see how to create the Covercrypt user key) after wrapping it with symmetric key 027cced1-ff2b-4bd3-a200-db1041583bdc using RFC 5649.

Corresponding KMS CLI command:

 ckms cc keys export -k df871e79-0923-47cd-9078-bbec83287c85 /tmp/sym_key.json  -w 027cced1-ff2b-4bd3-a200-db1041583bdc

Please note the presence of the KeyWrappingSpecification structure in the request with the unique identifier of the symmetric key.

The response contains a KeyWrappingData structure with a reference to the symmetric key used for wrapping. The encoding option is TTLVEncoding as specified ion the request, which indicates that the ciphertext contains a JSON TTLV structure with the Key Material and its Attributes. Please refer to the KMIP specification for more details.

{
  "tag": "Get",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "df871e79-0923-47cd-9078-bbec83287c85"
    },
    {
      "tag": "KeyWrappingSpecification",
      "type": "Structure",
      "value": [
        {
          "tag": "WrappingMethod",
          "type": "Enumeration",
          "value": "Encrypt"
        },
        {
          "tag": "EncryptionKeyInformation",
          "type": "Structure",
          "value": [
            {
              "tag": "UniqueIdentifier",
              "type": "TextString",
              "value": "027cced1-ff2b-4bd3-a200-db1041583bdc"
            }
          ]
        },
        {
          "tag": "EncodingOption",
          "type": "Enumeration",
          "value": "TTLVEncoding"
        }
      ]
    }
  ]
}

{
  "tag": "GetResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "ObjectType",
      "type": "Enumeration",
      "value": "PrivateKey"
    },
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "df871e79-0923-47cd-9078-bbec83287c85"
    },
    {
      "tag": "Object",
      "type": "Structure",
      "value": [
        {
          "tag": "KeyBlock",
          "type": "Structure",
          "value": [
            {
              "tag": "KeyFormatType",
              "type": "Enumeration",
              "value": "CoverCryptSecretKey"
            },
            {
              "tag": "KeyValue",
              "type": "Structure",
              "value": [
                {
                  "tag": "KeyMaterial",
                  "type": "ByteString",
                  "value": "2DCB9F6F18570EE9400372953342590B13CDAE02DC92ABDE50A873239F4EDFC64081B861B9CAC050D6AEE890CF7167AD44739974507A6D2DAB532B886B369EB00794F2ED4D80BD510556C92FF022F20D1E2E810D1466AF9B526319F8529BA9AFA34964C37BC6070BA5489970C9565785FFF29C1669251DDB6C7A189BA8D9238B2B6D717F5A4680E5458931A994DC4698D1C6E1B1D4B296CFFFA7402911933092B428F8AA9669185ACFC1ADDC7CC8026F96BFA240283F9667D03C2BC597DB1677CFBFA7CB8B88E5AC9456722477845BA6AB075F30C1DAE9BB9D95E4DA67C7E1D69E7394DC8CF6F54040B279D50C6E5C6B1FB92AAB17FD7E766C3E826603EBA9FE357199CF937AAA844B37BC8DA7C7D28E18D9C8D2A44F473D39B377BB04FD0178CE5E78385D31D19420270F7A121AEF4844977E14A5B387EB5032ABA81BFC9BEF2343C58AA1F468E4007AAAA3E7725754B1127A2F89D15DBE4F8237008B68D00F4C00862560A4A5FCB9E47C9E802A70C3AEEEAED6DE4136FFBCF31F50B5745C1975A08CC818C81655F6C5E9DAE96E7E76F5D58EB0A1FC1D9A10839E8ACB7F0327544481CEB60CACFC299C18E40D453DF9466CC9B524CDC31ABADAB2EF78E617E6571D6BE2B4F78F057F68CCB4FD732F39CC1CD6F1AFF80804CF27765A777832E614FC01D6E9967328CA7938D9BE8560B19C1A85C3C99DA43AEE7850FEC158541C968E8287142C306F9E18DD46AF5C8774E91C1786208A595DDDEBA26B28F5F06FE6AD0F2773E65082FA354A293C4924508DD0CBD469711E71F455A83929E0BD5C4A6461CB5F2ED57B7A55E3EA74E99525917FF76488CD7B1CF24515FE77FBAB4D4C0F686A921D50D643C0E7058357B2313DF8D2B376B261DE966E33BF436AAF7EEB200E1E577E0C0B3D6C3C9B3F7199202AC9D7160B15D3DFEC1F395DCCA876AD50201CC85CB07B28160D60DFB6EAF8EC9F591642B946BBD3D9FBBD41565A42383848EE0D2C47AA2715C1AE1E937795098DBF9A161A25DD2705F84261041F4395B93BA49916B6F7A1D386FF0B1C31AF06A15B96D4E43E60510C1E6D9C613E2DDC7E9B0821E99AC1FC82235AFC472FBB4B49923D139F7801DAAA73022B6BF53E0E067A7F6BDB128BB3DF47B9DFC72B1DDE87A0D67EE446FD248BBD4E9CF9983CD0E38D1430672145EF2B84F8CA1C3B04E4CD3C0405BD82523F23057F89BBDBAA04F2241AF87A0CFA76917D34F69B021BEE26BE5DD4F31D446684948A366C23696E64A3B56A9B67541AE575BF4C18933EEB3EAC634590BE9E0995E355FFB66ED1C3D284F20D0246EFD286D98EC337DA91AB9BF62FDAFC80F76D94D9F132CDF75A70F39DE697B1619011E9C7DD5BE305CA699480B276C8B62356626196F00EDBE250512A39E3750DDA8B62B329F20B339630F5BAD63A5B9203CC05170A7BD2F40929886DDEB14EF55C3ED2D7228034F8A632B1E7A7D44272A87E131AF2FE09640496A6D74E88F3D67AA17732884F3EED6C9DF4EC4D41A81F47B0124DA017D4D7AF079F79A1795541A49FB3D8A7F17CA1167FF7B4467813F02A7C97E53BAFDA269EB6D85F6086C17B60763DBD753E1FCC642CCEE072C9AB41FC433CBBEA9E3B97389DD205E05A2BA5DF147032B8C721119A831A29F474F7D9D6CF14F887CC43F5343F94CEBEDA7228BB3A6F7429A58FFF595CA055522B18C32DCB7377E953B2099116517FD32DDD1566C8C0B759C7CEBEE1B8CC3C21B7A8735D791D1F964F8C41A3B02EB9BA47537D03B6226003CDB05C0315B8B9B5D36C44F7FFC242B8783D27D6F2C014396CC88B0AA6B03C798199B1D309A1130A1DFEE0862F8619C127FA922EC83816E0A1FDF24EF7F27EEF5D92E5035D4E5A1BD9F656D1A3F384E617DA90677FFDE819CD618B9A13DA1E4FBD6AE8B61AC1CEE8A5C7D93C4BB4BB97325E75E5264CFD22B0A58BA7D55CB84DD5DADDFBEEDAC39A96D7498017DF1564BC853643764D73EFB370B6E3CB6A876678657A1499B7AFE47F92AD3B946099F7A86E2EDB731CFC7E0560A618EE56944A90BCE1E5928A3B506AD02A197EA71FCF3E290FF2DDFBECC6F79CA8405D87F65D73F3F6E39AFD8BFEA8AA35F246CE8BC85226BF3D7FD70D8C56589A48D38AF721B97FC58B0D"
                },
                {
                  "tag": "Attributes",
                  "type": "Structure",
                  "value": [
                    {
                      "tag": "CryptographicAlgorithm",
                      "type": "Enumeration",
                      "value": "CoverCrypt"
                    },
                    {
                      "tag": "KeyFormatType",
                      "type": "Enumeration",
                      "value": "CoverCryptSecretKey"
                    },
                    {
                      "tag": "Link",
                      "type": "Structure",
                      "value": [
                        {
                          "tag": "Link",
                          "type": "Structure",
                          "value": [
                            {
                              "tag": "LinkType",
                              "type": "Enumeration",
                              "value": "ParentLink"
                            },
                            {
                              "tag": "LinkedObjectIdentifier",
                              "type": "TextString",
                              "value": "b652a48a-a48c-4dc1-bd7e-cf0e5126b7b9"
                            }
                          ]
                        }
                      ]
                    },
                    {
                      "tag": "ObjectType",
                      "type": "Enumeration",
                      "value": "PrivateKey"
                    },
                    {
                      "tag": "VendorAttributes",
                      "type": "Structure",
                      "value": [
                        {
                          "tag": "VendorAttributes",
                          "type": "Structure",
                          "value": [
                            {
                              "tag": "VendorIdentification",
                              "type": "TextString",
                              "value": "cosmian"
                            },
                            {
                              "tag": "AttributeName",
                              "type": "TextString",
                              "value": "tag"
                            },
                            {
                              "tag": "AttributeValue",
                              "type": "ByteString",
                              "value": "5B224D79557365724B6579225D"
                            }
                          ]
                        },
                        {
                          "tag": "VendorAttributes",
                          "type": "Structure",
                          "value": [
                            {
                              "tag": "VendorIdentification",
                              "type": "TextString",
                              "value": "cosmian"
                            },
                            {
                              "tag": "AttributeName",
                              "type": "TextString",
                              "value": "cover_crypt_access_policy"
                            },
                            {
                              "tag": "AttributeValue",
                              "type": "ByteString",
                              "value": "5365637572697479204C6576656C3A3A436F6E666964656E7469616C20262620284465706172746D656E743A3A46494E207C7C204465706172746D656E743A3A485229"
                            }
                          ]
                        }
                      ]
                    }
                  ]
                }
              ]
            },
            {
              "tag": "CryptographicAlgorithm",
              "type": "Enumeration",
              "value": "CoverCrypt"
            },
            {
              "tag": "CryptographicLength",
              "type": "Integer",
              "value": 1832
            },
            {
              "tag": "KeyWrappingData",
              "type": "Structure",
              "value": [
                {
                  "tag": "WrappingMethod",
                  "type": "Enumeration",
                  "value": "Encrypt"
                },
                {
                  "tag": "EncryptionKeyInformation",
                  "type": "Structure",
                  "value": [
                    {
                      "tag": "UniqueIdentifier",
                      "type": "TextString",
                      "value": "027cced1-ff2b-4bd3-a200-db1041583bdc"
                    }
                  ]
                },
                {
                  "tag": "EncodingOption",
                  "type": "Enumeration",
                  "value": "TTLVEncoding"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

Example - An EC private key in PKCS#8, linked to a certificate

Exporting in PKCS#8 an EC private key bf614d45-5a3e-49b9-95c0-5586d3c0d17b which was imported as part of a PKCS#12 container.

Corresponding KMS CLI command:

  ckms ec keys export /tmp/pkey.pem -f pkcs8-pem -k bf614d45-5a3e-49b9-95c0-5586d3c0d17b

Please note:

  • the presence of the KeyFormatType in the request set to PKCS8
  • the presence of a Link structure in the response, which links the private key to the certificate
{
  "tag": "Get",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "bf614d45-5a3e-49b9-95c0-5586d3c0d17b"
    },
    {
      "tag": "KeyFormatType",
      "type": "Enumeration",
      "value": "PKCS8"
    },
    {
      "tag": "KeyWrapType",
      "type": "Enumeration",
      "value": "AsRegistered"
    }
  ]
}

{
  "tag": "GetResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "ObjectType",
      "type": "Enumeration",
      "value": "PrivateKey"
    },
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "bf614d45-5a3e-49b9-95c0-5586d3c0d17b"
    },
    {
      "tag": "Object",
      "type": "Structure",
      "value": [
        {
          "tag": "KeyBlock",
          "type": "Structure",
          "value": [
            {
              "tag": "KeyFormatType",
              "type": "Enumeration",
              "value": "PKCS8"
            },
            {
              "tag": "KeyValue",
              "type": "Structure",
              "value": [
                {
                  "tag": "KeyMaterial",
                  "type": "ByteString",
                  "value": "302E020100300506032B656E042204207D282BE753D7DCAF8E741BBA62C1E8A88DB99161863D89C41275358774B37090"
                },
                {
                  "tag": "Attributes",
                  "type": "Structure",
                  "value": [
                    {
                      "tag": "CryptographicAlgorithm",
                      "type": "Enumeration",
                      "value": "ECDH"
                    },
                    {
                      "tag": "CryptographicLength",
                      "type": "Integer",
                      "value": 253
                    },
                    {
                      "tag": "KeyFormatType",
                      "type": "Enumeration",
                      "value": "PKCS8"
                    },
                    {
                      "tag": "Link",
                      "type": "Structure",
                      "value": [
                        {
                          "tag": "Link",
                          "type": "Structure",
                          "value": [
                            {
                              "tag": "LinkType",
                              "type": "Enumeration",
                              "value": "PKCS12CertificateLink"
                            },
                            {
                              "tag": "LinkedObjectIdentifier",
                              "type": "TextString",
                              "value": "d2f4e937-dda9-4a86-bbe8-c866646a612f"
                            }
                          ]
                        }
                      ]
                    },
                    {
                      "tag": "ObjectType",
                      "type": "Enumeration",
                      "value": "PrivateKey"
                    }
                  ]
                }
              ]
            },
            {
              "tag": "CryptographicAlgorithm",
              "type": "Enumeration",
              "value": "ECDH"
            },
            {
              "tag": "CryptographicLength",
              "type": "Integer",
              "value": 253
            }
          ]
        }
      ]
    }
  ]
}

Example - A certificate in X509

Exporting in X509 a certificate d2f4e937-dda9-4a86-bbe8-c866646a612f which was imported as part of a PKCS#12.

Corresponding KMS CLI command:

  ckms certificates export /tmp/cert.x509 -f pem -c d2f4e937-dda9-4a86-bbe8-c866646a612f

The conversion from DER to PEM is done by the CLI.

Please note:

  • the presence of the KeyFormatType in the request set to X509
  • the presence of a Link structure in the response, which links the certificate to the private key
  • the presence of a CertificateValue structure in the response, which contains the X509 DER encoding of the certificate
{
  "tag": "Get",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "d2f4e937-dda9-4a86-bbe8-c866646a612f"
    },
    {
      "tag": "KeyFormatType",
      "type": "Enumeration",
      "value": "X509"
    },
    {
      "tag": "KeyWrapType",
      "type": "Enumeration",
      "value": "AsRegistered"
    }
  ]
}

{
  "tag": "GetResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "ObjectType",
      "type": "Enumeration",
      "value": "Certificate"
    },
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "d2f4e937-dda9-4a86-bbe8-c866646a612f"
    },
    {
      "tag": "Object",
      "type": "Structure",
      "value": [
        {
          "tag": "CertificateType",
          "type": "Enumeration",
          "value": "X509"
        },
        {
          "tag": "CertificateValue",
          "type": "ByteString",
          "value": "308201443081F7A003020102021004802E89DDF1412B9D8341571667EC9D300506032B657030133111300F06035504030C084B6D735375624341301E170D3233303930383133353033345A170D3234303330393034353033345A30143112301006035504030C094D7920736572766572302A300506032B656E032100124EAAD397D23A8A09847739EFD27E8232846E34AB4B73BDE0D3BC3BB30E842EA360305E301D0603551D0E04160414655E04099884AF3CCA3E3B11D908BB8FD27270BC301F0603551D230418301680148AA1C25FAA8B071496FE96AA8578D4DC6175AB71300C0603551D130101FF04023000300E0603551D0F0101FF0404030203E8300506032B65700341002FDDFC5EE8D0DD0626989F25EE8CEBDC4D28CBEB5AA397D650766426FF30D820662C178622CF2AB101A509ECF11E5F7D6D603181E3C91FBAA2EC2716651D2A0F"
        }
      ]
    }
  ]
}

Example - A PKCS#12 container

Export a PKCS#12 container using the unique identifier of the private key. The Private Key must have a link to:

  • either a certificate with a link of type PKCS12CertificateLink or CertificateLink
  • or a link to a public key with a link of type PublicKeyLink, the public key having a link to a certificate with a link of type CertificateLink.
  • for intermediate certificates to be included, the certificate must have a link to a certificate with a link of type CertificateLink to its issuer.

Corresponding KMS CLI command:

  ckms certificates export /tmp/exported.p12 -c bf614d45-5a3e-49b9-95c0-5586d3c0d17b -f pkcs12 -p secret

Please note:

  • the presence of the KeyFormatType in the request set to PKCS12.
  • the presence of a KeyWrappingSpecification structure in the request with the the secret used to seal the PKCS#12 container.
  • the EncodingOption is ignored in this case.
{
  "tag": "Get",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "bf614d45-5a3e-49b9-95c0-5586d3c0d17b"
    },
    {
      "tag": "KeyFormatType",
      "type": "Enumeration",
      "value": "PKCS12"
    },
    {
      "tag": "KeyWrappingSpecification",
      "type": "Structure",
      "value": [
        {
          "tag": "WrappingMethod",
          "type": "Enumeration",
          "value": "Encrypt"
        },
        {
          "tag": "EncryptionKeyInformation",
          "type": "Structure",
          "value": [
            {
              "tag": "UniqueIdentifier",
              "type": "TextString",
              "value": "secret"
            }
          ]
        },
        {
          "tag": "EncodingOption",
          "type": "Enumeration",
          "value": "TTLVEncoding"
        }
      ]
    }
  ]
}

{
  "tag": "GetResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "ObjectType",
      "type": "Enumeration",
      "value": "PrivateKey"
    },
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "bf614d45-5a3e-49b9-95c0-5586d3c0d17b"
    },
    {
      "tag": "Object",
      "type": "Structure",
      "value": [
        {
          "tag": "KeyBlock",
          "type": "Structure",
          "value": [
            {
              "tag": "KeyFormatType",
              "type": "Enumeration",
              "value": "PKCS12"
            },
            {
              "tag": "KeyValue",
              "type": "Structure",
              "value": [
                {
                  "tag": "KeyMaterial",
                  "type": "ByteString",
                  "value": "308204660201033082042C06092A864886F70D010701A082041D04820419308204153082036706092A864886F70D010706A0820358308203540201003082034D06092A864886F70D010701301C060A2A864886F70D010C0106300E0408A1CE7477CB52D0FB02020800808203201250CC796DD733F83D1412EA850360420552BBD1D6086F55F14694F711BC1AA4940942B651FD196A33F157EA4642E8344C241751E439C3ECF5EFEDAE4602BCF0B624874F5F402F69C0C08BB241760017279B76EA3784CFF3C5CF8F69790D84DDB1702D345F19FBED027CDE104381D4F52CDE37AD8407D36428F371E6A0295006F80651D2A4394B2C7C468A1EDD0F500E51B1A3353CD3E4AB954AF1BE23A9203CEE4FE3D712A6191DAED8F9E391273A7EA1EF9549E9E48176FED98E6B2554A4871638DA1733443817012A5F80750203FDA0E6DED6C8DA27E151682362C184C53425547A60275A3638D2DCA0726FC87C46F4DA9418064E62F14D09085860DD1D13E096E0AB05BBE5CC2162E2973EC154E86819EDE3CCA8A4140EB003543BEDE901DB2F8A5FCBB2E80FEB2B2B2B22B2B4CB4D663D07178F4E93568261759DA834108EA7D1F8BF06947799EAED802436F3C2EAEE8D3218B98B1153B4926C052992C01EB75B3BC032C1A232FACFFC472403DC86C6DD8DF22D700A38DC86EB58B246BFDE3D737F1CEC218383DDDB986657E3AF57D4AC0299ED99D8468CED2D97F14375C4A9AA438086629AA8A651A01701894CF06AA28137A68A70977995E4CF9DBAC4373D483C7E2E1D889E2BEA47E92715F8D540A5C534C43E5E953532264DA581F3DD059E8974AE072C50368F44E93F29D8A34E56DD40338CC69271B14FA7E7CF68FB9D586A26337A449F7CC5222F2F4EFE411691B30E8E25532C1BB6EB9D28EE5EDAF6E54D5C29C38D678AE158425F7959F89B06B10010D7EC1B3BF67C340FE5621D679CA48F2B1AFCF603CAB6581883A32A8733BBE738A7A1F82CBEC590BF69475A3331EB5AC2684EAB6D737E3A1DFACA734A4AC96AEA0E883F9C7B720E68016EEABDC5C88C4B3F405F46B7290CE8BA5258B92BA14122A87624E172CECCF4766066E1AFAF8EF06D1896994265A27080BF3AAB7B73638482A8EF6527B8A8245E514238C8E80A5F9750A663D5468DC5D8460D4793AF77B1D39B1D2A0099114FA8FF8E2D80B10E9F91A295CED84AE3C30C73A27D1CA90CBED4582A7ED0015BAA58DA9850202D1903086802FC5705F5BE0C4FA9F1AD11B82A340ED3DC8A835D08396C3081A706092A864886F70D010701A08199048196308193308190060B2A864886F70D010C0A0102A05A3058301C060A2A864886F70D010C0103300E04089971B59CB1424F9502020800043818AC3DDA45F299AC2D5441F538E49BD14E2AF1DF51A28DC926616D4E16C3100C2B2D9111219688C5BD81F70DAB647B0C8A6F6844150F05033125302306092A864886F70D0109153116041460854FAEF5E61527293E6551F170EF7145957FC430313021300906052B0E03021A0500041459585C94B586A45D6D75323D09976AC88DBEE3CA0408B40773C6DA0DF8BD02020800"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

Specification

This operation requests the server to perform a hash operation on provided data, using a specified Hashing Algorithm. The operation can be performed in a single request or as a series of requests for long data streams or by-parts cryptographic operations.

The response contains the hashed data.

When a stream or by-parts operation is requested:

  • The server SHALL create a unique identifier, called correlation value if Init Indicator is True.
  • The server SHALL expect one or more requests with the same correlation value if Init Indicator is True.
  • The server SHALL return the Correlation Value in the response if Init Indicator is True.
  • The server SHALL use the Correlation Value for subsequent Hash requests.
  • The server SHALL close the stream or by-parts operation when Final Indicator is True.

Implementation

The Eviden KMS server supports the following hashing algorithms:

  • SHA256
  • SHA384
  • SHA512

For the complete list of supported hashing algorithms, please check the algorithms page.

Example - Simple hash

Hashing data with SHA256.

Corresponding KMS CLI command:

ckms hash --algorithm sha3-512 --data 0011223344556677889900
{
  "tag": "Hash",
  "type": "Structure",
  "value": [
    {
      "tag": "CryptographicParameters",
      "type": "Structure",
      "value": [
        {
          "tag": "HashingAlgorithm",
          "type": "Enumeration",
          "value": "SHA3512"
        }
      ]
    },
    {
      "tag": "Data",
      "type": "ByteString",
      "value": "0011223344556677889900"
    },
    {
      "tag": "InitIndicator",
      "type": "Boolean",
      "value": false
    },
    {
      "tag": "FinalIndicator",
      "type": "Boolean",
      "value": false
    }
  ]
}
{
  "tag": "HashResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "Data",
      "type": "ByteString",
      "value": "F91DDB96D12CF8FAB0AA72224836D3F5F659A6634E3508A7C31DBC3727D2030254C57AD90AA5FB7F27FB3AAFABEAEB1204E4AF62BA2DE44E33E761B2C39DBACA"
    }
  ]
}

Example - Stream hash

Hashing a large file in multiple parts using SHA256.

Corresponding KMS CLI command:

# First part with init indicator
ckms hash --algorithm sha3-512 --data 0011223344556677889900 -i
# Middle part using correlation value
ckms hash --algorithm sha3-512 --correlation-value F91DDB96D12CF8FAB0AA72224836D3F5F659A6634E3508A7C31DBC3727D2030254C57AD90AA5FB7F27FB3AAFABEAEB1204E4AF62BA2DE44E33E761B2C39DBACA --data 0011223344556677889900
# Final part with final indicator
ckms hash --algorithm sha3-512 --correlation-value 51A2F7FCA8DECFC106031BE935F28F6EEE7E3850BCDB9D9B41B0F623146D7F51E399FC8F76A8B14EB71463DB0F6D421EF431E33F8CE1897FF988237C890C808F -f --data 0011223344556677889900
{
  "tag": "Hash",
  "type": "Structure",
  "value": [
    {
      "tag": "CryptographicParameters",
      "type": "Structure",
      "value": [
        {
          "tag": "HashingAlgorithm",
          "type": "Enumeration",
          "value": "SHA3512"
        }
      ]
    },
    {
      "tag": "Data",
      "type": "ByteString",
      "value": "0011223344556677889900"
    },
    {
      "tag": "InitIndicator",
      "type": "Boolean",
      "value": true
    },
    {
      "tag": "FinalIndicator",
      "type": "Boolean",
      "value": false
    }
  ]
}
{
  "tag": "HashResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "CorrelationValue",
      "type": "ByteString",
      "value": "F91DDB96D12CF8FAB0AA72224836D3F5F659A6634E3508A7C31DBC3727D2030254C57AD90AA5FB7F27FB3AAFABEAEB1204E4AF62BA2DE44E33E761B2C39DBACA"
    }
  ]
}
{
  "tag": "Hash",
  "type": "Structure",
  "value": [
    {
      "tag": "CryptographicParameters",
      "type": "Structure",
      "value": [
        {
          "tag": "HashingAlgorithm",
          "type": "Enumeration",
          "value": "SHA3512"
        }
      ]
    },
    {
      "tag": "Data",
      "type": "ByteString",
      "value": "0011223344556677889900"
    },
    {
      "tag": "CorrelationValue",
      "type": "ByteString",
      "value": "F91DDB96D12CF8FAB0AA72224836D3F5F659A6634E3508A7C31DBC3727D2030254C57AD90AA5FB7F27FB3AAFABEAEB1204E4AF62BA2DE44E33E761B2C39DBACA"
    },
    {
      "tag": "InitIndicator",
      "type": "Boolean",
      "value": false
    },
    {
      "tag": "FinalIndicator",
      "type": "Boolean",
      "value": false
    }
  ]
}
{
  "tag": "Hash",
  "type": "Structure",
  "value": [
    {
      "tag": "CryptographicParameters",
      "type": "Structure",
      "value": [
        {
          "tag": "HashingAlgorithm",
          "type": "Enumeration",
          "value": "SHA3512"
        }
      ]
    },
    {
      "tag": "Data",
      "type": "ByteString",
      "value": "0011223344556677889900"
    },
    {
      "tag": "CorrelationValue",
      "type": "ByteString",
      "value": "51A2F7FCA8DECFC106031BE935F28F6EEE7E3850BCDB9D9B41B0F623146D7F51E399FC8F76A8B14EB71463DB0F6D421EF431E33F8CE1897FF988237C890C808F"
    },
    {
      "tag": "InitIndicator",
      "type": "Boolean",
      "value": false
    },
    {
      "tag": "FinalIndicator",
      "type": "Boolean",
      "value": true
    }
  ]
}
{
  "tag": "HashResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "Data",
      "type": "ByteString",
      "value": "511BDAFDB2D059BD94FC72B8301ABF01DB9E02127420AED072B891A83952B88063DF3470225ACC6D46AD503E5E86B16BAEB581F218A148472120A9B541E1AF5D"
    }
  ]
}

Specification

This operation requests the server to Import a Managed Object specified by its Unique Identifier. The request specifies the object being imported and all the attributes to be assigned to the object.

The attribute rules for each attribute for "Initially set by" and "When implicitly set" SHALL NOT be enforced as all attributes MUST be set to the supplied values rather than any server-generated values.

The response contains the Unique Identifier provided in the request or assigned by the server. The server SHALL copy the Unique Identifier returned by this operation into the ID Placeholder variable.

Implementation

Key unwrapping on import is supported for all keys. Please check the algorithms page for more details.

For the list of supported key formats, please check the formats page.

Example - A NIST P-256 EC private key in SEC1 format

Importing a NIST P-256 EC Private Key in SEC1 format.

Corresponding KMS CLI command:

ckms ec keys import cra../kms_clients/test_data/certificates/openssl/prime256v1-private-key.pem --key-format pem

The conversion from PEM to DER is done by the KMS CLI.

In the JSON TTLV requests, please note:

  • the empty UniqueIdentifier which requests the server to generate a new UniqueIdentifier for the imported key.
  • the empty tags [] in hex
  • the KeyFormatType set to ECPrivateKey which indicates to the server that the key is in SEC1 format.
{
  "tag": "Import",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": ""
    },
    {
      "tag": "ObjectType",
      "type": "Enumeration",
      "value": "PrivateKey"
    },
    {
      "tag": "ReplaceExisting",
      "type": "Boolean",
      "value": false
    },
    {
      "tag": "Attributes",
      "type": "Structure",
      "value": [
        {
          "tag": "VendorAttributes",
          "type": "Structure",
          "value": [
            {
              "tag": "VendorAttributes",
              "type": "Structure",
              "value": [
                {
                  "tag": "VendorIdentification",
                  "type": "TextString",
                  "value": "cosmian"
                },
                {
                  "tag": "AttributeName",
                  "type": "TextString",
                  "value": "tag"
                },
                {
                  "tag": "AttributeValue",
                  "type": "ByteString",
                  "value": "5B5D"
                }
              ]
            }
          ]
        }
      ]
    },
    {
      "tag": "Object",
      "type": "Structure",
      "value": [
        {
          "tag": "KeyBlock",
          "type": "Structure",
          "value": [
            {
              "tag": "KeyFormatType",
              "type": "Enumeration",
              "value": "ECPrivateKey"
            },
            {
              "tag": "KeyValue",
              "type": "Structure",
              "value": [
                {
                  "tag": "KeyMaterial",
                  "type": "ByteString",
                  "value": "307702010104204CC0D4B807CED6BB2CCFE44D467D6A2A5D706B0E11E9352CF5CA8D3998790B83A00A06082A8648CE3D030107A14403420004A8D465338FB81879378BC9730A07A2BA455009E1B4606A58136C4A73C40B4BE7DDFE9D7959BC16E6BF8D6F2EF4BA4DB43A3DA29FA1A9D2ECA5AD30F129A0A5A4"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

{
  "tag": "ImportResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "927adccb-f59a-4cc9-a9e3-1eeb958c601f"
    }
  ]
}

Example - A RSA 2048 private key in PKCS#8 format

Importing a RSA 2048 key in PKCS#8 format with tags MyRSAKeyand 2048.

Corresponding KMS CLI command:

  ckms ec keys import cra../kms_clients/test_data/certificates/openssl/rsa-2048-private-key.pem --key-format pem --tag "MyRSAKey" --tag "2048"

The conversion from PEM to DER is done by the KMS CLI.

In the JSON TTLV Request, please note:

  • the empty UniqueIdentifier which requests the server to generate a new UniqueIdentifier for the imported key.
  • the tags ["MyRSAKey","2048"] in hex
  • the KeyFormatType set to PKCS8 which indicates to the server that the key is in PKCS#8 format.
  • the KeyMaterial which are the hex-encoded DER bytes of the PKCS#8 structure.
{
  "tag": "Import",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": ""
    },
    {
      "tag": "ObjectType",
      "type": "Enumeration",
      "value": "PrivateKey"
    },
    {
      "tag": "ReplaceExisting",
      "type": "Boolean",
      "value": false
    },
    {
      "tag": "Attributes",
      "type": "Structure",
      "value": [
        {
          "tag": "VendorAttributes",
          "type": "Structure",
          "value": [
            {
              "tag": "VendorAttributes",
              "type": "Structure",
              "value": [
                {
                  "tag": "VendorIdentification",
                  "type": "TextString",
                  "value": "cosmian"
                },
                {
                  "tag": "AttributeName",
                  "type": "TextString",
                  "value": "tag"
                },
                {
                  "tag": "AttributeValue",
                  "type": "ByteString",
                  "value": "5B224D795253414B6579222C2232303438225D"
                }
              ]
            }
          ]
        }
      ]
    },
    {
      "tag": "Object",
      "type": "Structure",
      "value": [
        {
          "tag": "KeyBlock",
          "type": "Structure",
          "value": [
            {
              "tag": "KeyFormatType",
              "type": "Enumeration",
              "value": "PKCS8"
            },
            {
              "tag": "KeyValue",
              "type": "Structure",
              "value": [
                {
                  "tag": "KeyMaterial",
                  "type": "ByteString",
                  "value": "308204BD020100300D06092A864886F70D0101010500048204A7308204A30201000282010100B466EEF95ECB9CEC2F9AFA71F280C3F7671BD18C21819E08521D890CADF4D52EB02B33D2CEC25B45124F4977FC5EB43918FD0717697EBF09B0AD3143915DD233A248DBA42550ECE7530AA98C318F425915E1300600B873B24EB33715A93A96F7AC4198C1EFBD111B7D0D7100FCEB90A3B2549F4D9CDF1C08E0E14098ED07F5442FFFA9775F488778CE967AFB662FBC8FE9E353E778AA49E1976F818CC8C597D9DBFAAD219DA1B5B36037B3146DBB031AA43552B18D1B03AD789BE60B19FCD685ED774BC11E9E2C8D076CEBAB5BD21FB1EB80EDCCE55C47A8BB607A6B3311E9CFBC76E162C4F98FD30E87D39ED0A8BC07CA46C2653A2B04DD47998CDDD2744E5302030100010282010018572D163B6A98952B852B1675C456EC8A1E70430A804D1CCD4B8BE2C893D8B1B0765BCB2D377F0E0E3EE1684D864FA5C68FD94598E7CF89D4AB4192DEC9BC635819A17CFFC5EFF8F434E39475596177D8A26612E14501E344881CBE10F3C54BCC939B334B90047F637B8D4C3753E75396EBA104DD4E231FB58BDEE5E351680F9D5E8F0F555BDF09CECA4E83AE6CCDC0F6E8E07AA0A2EC96ABA1B57F53667B05957B0BE4CB9564CFEC672981CB1BB57BF4785563B87A6AC3354C68B83CD73C4896DB526A5D037224EBEAECDF01EC6F94B637FA2F3128EB1588294942601ECE5B0AEE98E222BD8779ED931734FE27F0C4ADACB586CCFE649FAA0273F333CC43CD02818100EF9732DA98D3AB9F561B602F74AAEFFEBAA4F5D8A3AC93BF560DC097AC853FCD246433D30FC294D81F6FF05B2DFD286155DAF4CFB3666DDA580A992DAE649DAA6B407FD240F07E87AF6DEEA93525EED90799888B488980470F70F749AC8C440B776D0CAF327706638AD46923D14FD5E5D25CDA0DE14FA9E5869F71104D6B9E7502818100C0C1F7B91012BB6794FE901039F0B93C08DFA0D1AB6308A444B34304EBAD516EB76CBE976551F38DA4B78CF67675882A4293765BDAA824CF7DF9912BB77D9CC986A5692CF06A520C0F00148110692EF25814420FB496C8FD60C1402D487980D457FB9B54004D58F19ACDA3317734EA39CFB07DE3446200E283AB6F68B82230A702818020E24CBD8A0A4B5CF4318FB313BD3E7164E6AD438DBB6B0FF0DCB4595CF970F7540E58BA984829ABBC2CDDFD75C3705E63AD48BA9531F2D3EC0F90549FC1F98DE16899E29EBD2370B6184D9075D5FE0155B4B1F40401B3548D7F00C1E0E7E392FB5241526E87BD9DBDF94770FE128A1620185469614A50D44AF4E94CC68385DD028181008129B8A06697D6C21B01D5713A12075DA6288BAFD3A361E8092D01ECAB2C11541A4F210B7BD645589596753BB5B71E0E0B5C9AC4042A05B890168A637BEA0D04B157E7B938445644444CEE706999C1DE9C8CBE939D94288C38A86623B1DBD12AF8E5DD7895573F116E84FB24AB5766D8644644A4E46EB35F1591EBEE84EC4C2302818042F72AC26114C103F011407A8189128E3C2A2CC59CEB559DF3DA198EA1D6A0023E98B7FDCBFEF61D82AC6176981CBD4B4B239DF39189DC15B246DE13B879AC82490C065BA3A007E5413F27CD998546BE17F30AD6A40E4A891CA84CD9D006239BB47E06024DA1DE35B791A79A57CBC5C501B16B1511138DCEB97F111E548F076D"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

{
  "tag": "ImportResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "8623605d-625b-4b4f-99c9-a2802e415609"
    }
  ]
}

Example - Covercrypt user key with unwrapping on import

Importing a wrapped Covercrypt user key after unwrapping it with symmetric key 027cced1-ff2b-4bd3-a200-db1041583bdc using RFC 5649.

Corresponding KMS CLI command:

ckms cc keys import /tmp/sym_key.json  -u

Note: the -u flag is used to indicate that the key should be unwrapped on import; the JSON file (obtained using the export command) contains the wrapped key details.

In the JSON TTLV request, please note:

  • the KeyWrapType set to NotWrapped which indicates to the server that it should look into the KeyWrappingDatastructure to see how to unwrap the key.
  • the empty UniqueIdentifier which requests the server to generate a new UniqueIdentifier for the imported key.
{
  "tag": "Import",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": ""
    },
    {
      "tag": "ObjectType",
      "type": "Enumeration",
      "value": "PrivateKey"
    },
    {
      "tag": "ReplaceExisting",
      "type": "Boolean",
      "value": false
    },
    {
      "tag": "KeyWrapType",
      "type": "Enumeration",
      "value": "NotWrapped"
    },
    {
      "tag": "Attributes",
      "type": "Structure",
      "value": [
        {
          "tag": "CryptographicAlgorithm",
          "type": "Enumeration",
          "value": "CoverCrypt"
        },
        {
          "tag": "KeyFormatType",
          "type": "Enumeration",
          "value": "CoverCryptSecretKey"
        },
        {
          "tag": "Link",
          "type": "Structure",
          "value": [
            {
              "tag": "Link",
              "type": "Structure",
              "value": [
                {
                  "tag": "LinkType",
                  "type": "Enumeration",
                  "value": "ParentLink"
                },
                {
                  "tag": "LinkedObjectIdentifier",
                  "type": "TextString",
                  "value": "b652a48a-a48c-4dc1-bd7e-cf0e5126b7b9"
                }
              ]
            }
          ]
        },
        {
          "tag": "ObjectType",
          "type": "Enumeration",
          "value": "PrivateKey"
        },
        {
          "tag": "VendorAttributes",
          "type": "Structure",
          "value": [
            {
              "tag": "VendorAttributes",
              "type": "Structure",
              "value": [
                {
                  "tag": "VendorIdentification",
                  "type": "TextString",
                  "value": "cosmian"
                },
                {
                  "tag": "AttributeName",
                  "type": "TextString",
                  "value": "tag"
                },
                {
                  "tag": "AttributeValue",
                  "type": "ByteString",
                  "value": "5B5D"
                }
              ]
            },
            {
              "tag": "VendorAttributes",
              "type": "Structure",
              "value": [
                {
                  "tag": "VendorIdentification",
                  "type": "TextString",
                  "value": "cosmian"
                },
                {
                  "tag": "AttributeName",
                  "type": "TextString",
                  "value": "cover_crypt_access_policy"
                },
                {
                  "tag": "AttributeValue",
                  "type": "ByteString",
                  "value": "5365637572697479204C6576656C3A3A436F6E666964656E7469616C20262620284465706172746D656E743A3A46494E207C7C204465706172746D656E743A3A485229"
                }
              ]
            }
          ]
        }
      ]
    },
    {
      "tag": "Object",
      "type": "Structure",
      "value": [
        {
          "tag": "KeyBlock",
          "type": "Structure",
          "value": [
            {
              "tag": "KeyFormatType",
              "type": "Enumeration",
              "value": "CoverCryptSecretKey"
            },
            {
              "tag": "KeyValue",
              "type": "Structure",
              "value": [
                {
                  "tag": "KeyMaterial",
                  "type": "ByteString",
                  "value": "2DCB9F6F18570EE9400372953342590B13CDAE02DC92ABDE50A873239F4EDFC64081B861B9CAC050D6AEE890CF7167AD44739974507A6D2DAB532B886B369EB00794F2ED4D80BD510556C92FF022F20D1E2E810D1466AF9B526319F8529BA9AFA34964C37BC6070BA5489970C9565785FFF29C1669251DDB6C7A189BA8D9238B2B6D717F5A4680E5458931A994DC4698D1C6E1B1D4B296CFFFA7402911933092B428F8AA9669185ACFC1ADDC7CC8026F96BFA240283F9667D03C2BC597DB1677CFBFA7CB8B88E5AC9456722477845BA6AB075F30C1DAE9BB9D95E4DA67C7E1D69E7394DC8CF6F54040B279D50C6E5C6B1FB92AAB17FD7E766C3E826603EBA9FE357199CF937AAA844B37BC8DA7C7D28E18D9C8D2A44F473D39B377BB04FD0178CE5E78385D31D19420270F7A121AEF4844977E14A5B387EB5032ABA81BFC9BEF2343C58AA1F468E4007AAAA3E7725754B1127A2F89D15DBE4F8237008B68D00F4C00862560A4A5FCB9E47C9E802A70C3AEEEAED6DE4136FFBCF31F50B5745C1975A08CC818C81655F6C5E9DAE96E7E76F5D58EB0A1FC1D9A10839E8ACB7F0327544481CEB60CACFC299C18E40D453DF9466CC9B524CDC31ABADAB2EF78E617E6571D6BE2B4F78F057F68CCB4FD732F39CC1CD6F1AFF80804CF27765A777832E614FC01D6E9967328CA7938D9BE8560B19C1A85C3C99DA43AEE7850FEC158541C968E8287142C306F9E18DD46AF5C8774E91C1786208A595DDDEBA26B28F5F06FE6AD0F2773E65082FA354A293C4924508DD0CBD469711E71F455A83929E0BD5C4A6461CB5F2ED57B7A55E3EA74E99525917FF76488CD7B1CF24515FE77FBAB4D4C0F686A921D50D643C0E7058357B2313DF8D2B376B261DE966E33BF436AAF7EEB200E1E577E0C0B3D6C3C9B3F7199202AC9D7160B15D3DFEC1F395DCCA876AD50201CC85CB07B28160D60DFB6EAF8EC9F591642B946BBD3D9FBBD41565A42383848EE0D2C47AA2715C1AE1E937795098DBF9A161A25DD2705F84261041F4395B93BA49916B6F7A1D386FF0B1C31AF06A15B96D4E43E60510C1E6D9C613E2DDC7E9B0821E99AC1FC82235AFC472FBB4B49923D139F7801DAAA73022B6BF53E0E067A7F6BDB128BB3DF47B9DFC72B1DDE87A0D67EE446FD248BBD4E9CF9983CD0E38D1430672145EF2B84F8CA1C3B04E4CD3C0405BD82523F23057F89BBDBAA04F2241AF87A0CFA76917D34F69B021BEE26BE5DD4F31D446684948A366C23696E64A3B56A9B67541AE575BF4C18933EEB3EAC634590BE9E0995E355FFB66ED1C3D284F20D0246EFD286D98EC337DA91AB9BF62FDAFC80F76D94D9F132CDF75A70F39DE697B1619011E9C7DD5BE305CA699480B276C8B62356626196F00EDBE250512A39E3750DDA8B62B329F20B339630F5BAD63A5B9203CC05170A7BD2F40929886DDEB14EF55C3ED2D7228034F8A632B1E7A7D44272A87E131AF2FE09640496A6D74E88F3D67AA17732884F3EED6C9DF4EC4D41A81F47B0124DA017D4D7AF079F79A1795541A49FB3D8A7F17CA1167FF7B4467813F02A7C97E53BAFDA269EB6D85F6086C17B60763DBD753E1FCC642CCEE072C9AB41FC433CBBEA9E3B97389DD205E05A2BA5DF147032B8C721119A831A29F474F7D9D6CF14F887CC43F5343F94CEBEDA7228BB3A6F7429A58FFF595CA055522B18C32DCB7377E953B2099116517FD32DDD1566C8C0B759C7CEBEE1B8CC3C21B7A8735D791D1F964F8C41A3B02EB9BA47537D03B6226003CDB05C0315B8B9B5D36C44F7FFC242B8783D27D6F2C014396CC88B0AA6B03C798199B1D309A1130A1DFEE0862F8619C127FA922EC83816E0A1FDF24EF7F27EEF5D92E5035D4E5A1BD9F656D1A3F384E617DA90677FFDE819CD618B9A13DA1E4FBD6AE8B61AC1CEE8A5C7D93C4BB4BB97325E75E5264CFD22B0A58BA7D55CB84DD5DADDFBEEDAC39A96D7498017DF1564BC853643764D73EFB370B6E3CB6A876678657A1499B7AFE47F92AD3B946099F7A86E2EDB731CFC7E0560A618EE56944A90BCE1E5928A3B506AD02A197EA71FCF3E290FF2DDFBECC6F79CA8405D87F65D73F3F6E39AFD8BFEA8AA35F246CE8BC85226BF3D7FD70D8C56589A48D38AF721B97FC58B0D"
                },
                {
                  "tag": "Attributes",
                  "type": "Structure",
                  "value": [
                    {
                      "tag": "CryptographicAlgorithm",
                      "type": "Enumeration",
                      "value": "CoverCrypt"
                    },
                    {
                      "tag": "KeyFormatType",
                      "type": "Enumeration",
                      "value": "CoverCryptSecretKey"
                    },
                    {
                      "tag": "Link",
                      "type": "Structure",
                      "value": [
                        {
                          "tag": "Link",
                          "type": "Structure",
                          "value": [
                            {
                              "tag": "LinkType",
                              "type": "Enumeration",
                              "value": "ParentLink"
                            },
                            {
                              "tag": "LinkedObjectIdentifier",
                              "type": "TextString",
                              "value": "b652a48a-a48c-4dc1-bd7e-cf0e5126b7b9"
                            }
                          ]
                        }
                      ]
                    },
                    {
                      "tag": "ObjectType",
                      "type": "Enumeration",
                      "value": "PrivateKey"
                    },
                    {
                      "tag": "VendorAttributes",
                      "type": "Structure",
                      "value": [
                        {
                          "tag": "VendorAttributes",
                          "type": "Structure",
                          "value": [
                            {
                              "tag": "VendorIdentification",
                              "type": "TextString",
                              "value": "cosmian"
                            },
                            {
                              "tag": "AttributeName",
                              "type": "TextString",
                              "value": "tag"
                            },
                            {
                              "tag": "AttributeValue",
                              "type": "ByteString",
                              "value": "5B224D79557365724B6579225D"
                            }
                          ]
                        },
                        {
                          "tag": "VendorAttributes",
                          "type": "Structure",
                          "value": [
                            {
                              "tag": "VendorIdentification",
                              "type": "TextString",
                              "value": "cosmian"
                            },
                            {
                              "tag": "AttributeName",
                              "type": "TextString",
                              "value": "cover_crypt_access_policy"
                            },
                            {
                              "tag": "AttributeValue",
                              "type": "ByteString",
                              "value": "5365637572697479204C6576656C3A3A436F6E666964656E7469616C20262620284465706172746D656E743A3A46494E207C7C204465706172746D656E743A3A485229"
                            }
                          ]
                        }
                      ]
                    }
                  ]
                }
              ]
            },
            {
              "tag": "CryptographicAlgorithm",
              "type": "Enumeration",
              "value": "CoverCrypt"
            },
            {
              "tag": "CryptographicLength",
              "type": "Integer",
              "value": 1832
            },
            {
              "tag": "KeyWrappingData",
              "type": "Structure",
              "value": [
                {
                  "tag": "WrappingMethod",
                  "type": "Enumeration",
                  "value": "Encrypt"
                },
                {
                  "tag": "EncryptionKeyInformation",
                  "type": "Structure",
                  "value": [
                    {
                      "tag": "UniqueIdentifier",
                      "type": "TextString",
                      "value": "027cced1-ff2b-4bd3-a200-db1041583bdc"
                    }
                  ]
                },
                {
                  "tag": "EncodingOption",
                  "type": "Enumeration",
                  "value": "TTLVEncoding"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

{
  "tag": "ImportResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "8bfcbfba-77d9-4850-874a-b7c7511b03ae"
    }
  ]
}

Example - A PKCS#12 container

Importing a PKCS#12 container containing an EC private key, a certificate and an intermediate certificate.

Corresponding KMS CLI command:

  ckms certificates import cra../kms_clients/test_data/certificates/p12/output.p12 --tag "MyPKCS12" -f pkcs12 -p secret

In the request, please note:

  • the empty UniqueIdentifier which requests the server to generate a new UniqueIdentifier for the imported private key. The server will also generate unique identifiers for the certificate and the intermediate certificate but the latter are not returned in the response. To recover these identifiers, use the Get Attributes operation on the private key and then on the certificate.
  • the tag ["MyPKCS12"] in hex.
  • the KeyFormatType set to PKCS12 which indicates to the server that the key is in PKCS#12 format.
  • the KeyMaterial which are the hex-encoded DER bytes of the PKCS#12 structure.
  • the LinkType set to PKCS12PasswordLink which indicates to the server that the password is in the LinkedObjectIdentifier field.
{
  "tag": "Import",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": ""
    },
    {
      "tag": "ObjectType",
      "type": "Enumeration",
      "value": "PrivateKey"
    },
    {
      "tag": "ReplaceExisting",
      "type": "Boolean",
      "value": false
    },
    {
      "tag": "Attributes",
      "type": "Structure",
      "value": [
        {
          "tag": "Link",
          "type": "Structure",
          "value": [
            {
              "tag": "Link",
              "type": "Structure",
              "value": [
                {
                  "tag": "LinkType",
                  "type": "Enumeration",
                  "value": "PKCS12PasswordLink"
                },
                {
                  "tag": "LinkedObjectIdentifier",
                  "type": "TextString",
                  "value": "secret"
                }
              ]
            }
          ]
        },
        {
          "tag": "VendorAttributes",
          "type": "Structure",
          "value": [
            {
              "tag": "VendorAttributes",
              "type": "Structure",
              "value": [
                {
                  "tag": "VendorIdentification",
                  "type": "TextString",
                  "value": "cosmian"
                },
                {
                  "tag": "AttributeName",
                  "type": "TextString",
                  "value": "tag"
                },
                {
                  "tag": "AttributeValue",
                  "type": "ByteString",
                  "value": "5B224D79504B43533132225D"
                }
              ]
            }
          ]
        }
      ]
    },
    {
      "tag": "Object",
      "type": "Structure",
      "value": [
        {
          "tag": "KeyBlock",
          "type": "Structure",
          "value": [
            {
              "tag": "KeyFormatType",
              "type": "Enumeration",
              "value": "PKCS12"
            },
            {
              "tag": "KeyValue",
              "type": "Structure",
              "value": [
                {
                  "tag": "KeyMaterial",
                  "type": "ByteString",
                  "value": "308204660201033082042C06092A864886F70D010701A082041D04820419308204153082036706092A864886F70D010706A0820358308203540201003082034D06092A864886F70D010701301C060A2A864886F70D010C0106300E04082A727ACADC7F30FE0202080080820320C807EF330DE2F0D9B5E4B47E457DBCAE52DB41BB85E627645E9C60B51FB4FD148D2DC1C262F64212DFFB07ED5D741E8CD4AED9F684AA20A1AA8CBA372437A5183967E0EA69F7417AE5AF6B18D45864A6508B95DA6302F3892A4FB68BB29580DCC70EE91AB124D029E029E60C04CA8E1037D741E865C7968AD77BB350CE5D91F7482410E8AB3579E3C3D12530B4F402A010BB10E4C33D50ADA3FE2D8105AF8B489D5F446C976F913C48F7232A3448D79B8E6BCE2B2AE72369713017077F6732CCE794A1EFD46F59C3CB5F03978B7DF613FA2B7E9C872967C92F059A857BA935C8A235235A29E65789ADB84B7F907F2B01B11E1EEF69967A187831C6DCA81C3B4140BC20421BCAA0114FD6F2692FDDBD8569D1DE14B6D1A58C85D15F2C9FF7C1A00FE77B10D1278ED6E9AF358E7D169724A0420D4D93ACBB531AD58EA95E65A31F95063BB1C6ED080D16EA85F0E041215BF5FC3FA7C301C8CFCC73BADF46DE1E7AD7AA599ECFCE60D12474A93CBB8E869A65822466DBA2E892617C7D1626EEEED90586B70C9354DF4F5451E4E14BE38D4E7893D7F118255E9F9CBDCF290E6AEA97EBA4599587FC9BD652F346E47A697FD7F497ABB7EE1B1D53A5A10ADD99AC3F635CBE440B8D34C20FF6CEB7D8F1FF4FF4A8F0BF3158B901A42631BFC8C11E0E607344BEE46BAAFAB049FA9AD3F8752AFF90ED26102DE1D905FD389E530F34534FD206B691A82C4E1B6BD180F2F2AE0C0FF7FC943B608A247EC5C49FAB7419222C17638AEA5B5DC225CD898CC6EDD7B98E6FF73CFE9C94795B7BAE7F53DE1BC0FA2C875F75D056B01A341358283294FBE6F5E76B0E03BDC7E497F69E31FC91895940CB654E5A0F669F58C820A2486BC35FA0BC17AF93959E9CD4CAAA0E1141E7E7A12B80F409D2B8B2E36B0E6637189A4180CE6E135702C4C6AB16FB96228FFE58AC92842845FA8A73B48571FFCCC3F7C513B94B72E69C115AC22D7891EE1A5518032C4661654486DC0F2808CEC195E80A0BA6501F0C6E1A454C5A6CD17137F10226EEB9182D7B848AAE0C5FF19FC5ED8EF1E19B45ECA1CAA45BDC7B913BEBB38D4F3B35BE8F5A70A9F58DFEEEBB68BDF83844DE4FE00CDA4745EF2194FF417D943081A706092A864886F70D010701A08199048196308193308190060B2A864886F70D010C0A0102A05A3058301C060A2A864886F70D010C0103300E04086CA064C6D55C2211020208000438FF5E786E0DB3C6D85C358321EF8E5BB0E8A057ACFC33E52281897F152239736B1F2DB061D712E8A83747A767C168737A515EFE1BE7EDAE3E3125302306092A864886F70D0109153116041460854FAEF5E61527293E6551F170EF7145957FC430313021300906052B0E03021A050004149CF1C7AF502B81AB981D5786E4800099B9D37D960408B970C0A9A2BDB46102020800"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

{
  "tag": "ImportResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "bf614d45-5a3e-49b9-95c0-5586d3c0d17b"
    }
  ]
}

Example - A X509 Certificate

Importing a X509 certificate. The certificate must be imported an X509, DER encoded.

Corresponding KMS CLI command:

  ckms certificates import --tag "MyImportedCert" cra../kms_clients/test_data/certificates/ca.crt -f pem

The conversion from PEM to DER is done by the CLI.

In the JSON TTLV request, please note:

  • the empty UniqueIdentifier which requests the server to generate a new UniqueIdentifier for the imported certificate.
  • the tag ["MyImportedCert"] in hex.
  • the ObjectType set to Certificate which indicates to the server that the key is in X509 format.
{
  "tag": "Import",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": ""
    },
    {
      "tag": "ObjectType",
      "type": "Enumeration",
      "value": "Certificate"
    },
    {
      "tag": "ReplaceExisting",
      "type": "Boolean",
      "value": false
    },
    {
      "tag": "Attributes",
      "type": "Structure",
      "value": [
        {
          "tag": "VendorAttributes",
          "type": "Structure",
          "value": [
            {
              "tag": "VendorAttributes",
              "type": "Structure",
              "value": [
                {
                  "tag": "VendorIdentification",
                  "type": "TextString",
                  "value": "cosmian"
                },
                {
                  "tag": "AttributeName",
                  "type": "TextString",
                  "value": "tag"
                },
                {
                  "tag": "AttributeValue",
                  "type": "ByteString",
                  "value": "5B224D79496D706F7274656443657274225D"
                }
              ]
            }
          ]
        }
      ]
    },
    {
      "tag": "Object",
      "type": "Structure",
      "value": [
        {
          "tag": "CertificateType",
          "type": "Enumeration",
          "value": "X509"
        },
        {
          "tag": "CertificateValue",
          "type": "ByteString",
          "value": "3082033030820218020900C03CBFDC0BD87789300D06092A864886F70D01010B0500305A310B3009060355040613024652310C300A06035504080C03496446310E300C06035504070C0550617269733111300F060355040A0C0841636D6554657374311A301806035504030C1141636D65205465737420526F6F74204341301E170D3233303531393135323934355A170D3333303531363135323934355A305A310B3009060355040613024652310C300A06035504080C03496446310E300C06035504070C0550617269733111300F060355040A0C0841636D6554657374311A301806035504030C1141636D65205465737420526F6F7420434130820122300D06092A864886F70D01010105000382010F003082010A0282010100D61FF874D90172D96968F8DEA453A9D2ACD29886EAC934A18ED1172136FFE284EA702B14FA3BD2900782BCE489A6CF2FC8063F1C8BC13C52F92A3B72B44A343786B1A1DC7A3C49CA1EB24AAFF809F5AE3BB64120553B9B40E2E104966C9C8C0CF2DEBDA167FA804BDB1FBC66CDA82052F9918771FDE318D74E4894D51B808A06A10330C3E985EC2B0067C60F955AD74D64FD4667ABA76D9DC17F681B36391698BF2A3303665C19E2C20A9CADD174A49FD236E89E7541F3AACDC8453ECE094D1C886761A39CBBF809F5F27396E1C9CF431280C07670E87A3BCC9ED6C38037F53E5E006FE2D952D48D9B57D765061A5F8C2CA30CCE394DD2ED88EB6856623239190203010001300D06092A864886F70D01010B05000382010100C48C6AC67FD80062816E065637B64B17D8821299A8718CF04E7D3998C761E72C3030F27DD3C955754BFE25F3D750D15C6ABFFC887FD63B27A1C345B8F9C78BA886196866C49180C3BA37BF20D918B0EAD6051C1AEAB3C98798C43580AA10E7909F7F7F122E9349CC74FE6EFCF947C761945C3BABF68973773CC7B36FBF346D4D4286BB88BB45FA5388870F58BD192F6ED6B8541C269C7F4D77D62DE860E981CDB6D5E99579164C048C80898BF6B53E731AB97997FF48DA5FC8BB6357574CDB58BE6922ECA9CD644E7442177B66032665D186A9F6027ECF6295235922EF045285D9118182E12D608EAA21E5879B08B3BCA894D10E947625058B90796FC7FCDD57"
        }
      ]
    }
  ]
}

{
  "tag": "ImportResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "fc0a018d-5581-44e5-baae-052c2ac98757"
    }
  ]
}

Specification

This operation requests that the server search for one or more Managed Objects, depending on the attributes specified in the request. All attributes are allowed to be used. The request MAY contain a Maximum Items field, which specifies the maximum number of objects to be returned. If the Maximum Items field is omitted, then the server MAY return all objects matched or MAY impose an internal maximum limit due to resource limitations.

The request MAY contain an Offset Items field, which specifies the number of objects to skip that satisfy the identification criteria specified in the request. An Offset Items field of 0 is the same as omitting the Offset Items field. If both Offset Items and Maximum Items are specified in the request, the server skips Offset Items objects and returns up to Maximum Items objects.

If more than one object satisfies the identification criteria specified in the request, then the response MAY contain Unique Identifiers for multiple Managed Objects. Responses containing Unique Identifiers for multiple objects SHALL be returned in descending order of object creation (most recently created object first). Returned objects SHALL match all of the attributes in the request. If no objects match, then an empty response payload is returned. If no attribute is specified in the request, any object SHALL be deemed to match the Locate request. The response MAY include Located Items which is the count of all objects that satisfy the identification criteria.

The server returns a list of Unique Identifiers of the found objects, which then MAY be retrieved using the Get operation. If the objects are archived, then the Recover and Get operations are REQUIRED to be used to obtain those objects. If a single Unique Identifier is returned to the client, then the server SHALL copy the Unique Identifier returned by this operation into the ID Placeholder variable. If the Locate operation matches more than one object, and the Maximum Items value is omitted in the request, or is set to a value larger than one, then the server SHALL empty the ID Placeholder, causing any subsequent operations that are batched with the Locate, and which do not specify a Unique Identifier explicitly, to fail. This ensures that these batched operations SHALL proceed only if a single object is returned by Locate.

The Date attributes in the Locate request (e.g., Initial Date, Activation Date, etc.) are used to specify a time or a time range for the search. If a single instance of a given Date attribute is used in the request (e.g., the Activation Date), then objects with the same Date attribute are considered to be matching candidate objects. If two instances of the same Date attribute are used (i.e., with two different values specifying a range), then objects for which the Date attribute is inside or at a limit of the range are considered to be matching candidate objects. If a Date attribute is set to its largest possible value, then it is equivalent to an undefined attribute.

When the Cryptographic Usage Mask attribute is specified in the request, candidate objects are compared against this field via an operation that consists of a logical AND of the requested mask with the mask in the candidate object, and then a comparison of the resulting value with the requested mask. For example, if the request contains a mask value of 10001100010000, and a candidate object mask contains 10000100010000, then the logical AND of the two masks is 10000100010000, which is compared against the mask value in the request (10001100010000) and the match fails. This means that a matching candidate object has all of the bits set in its mask that are set in the requested mask, but MAY have additional bits set.

When the Usage Limits attribute is specified in the request, matching candidate objects SHALL have a Usage Limits Count and Usage Limits Total equal to or larger than the values specified in the request.

When an attribute that is defined as a structure is specified, all of the structure fields are not REQUIRED to be specified. For instance, for the Link attribute, if the Linked Object Identifier value is specified without the Link Type value, then matching candidate objects have the Linked Object Identifier as specified, irrespective of their Link Type.

When the Object Group attribute and the Object Group Member flag are specified in the request, and the value specified for Object Group Member is Group Member Fresh, matching candidate objects SHALL be fresh objects from the object group. If there are no more fresh objects in the group, the server MAY choose to generate a new object on-the-fly, based on server policy. If the value specified for Object Group Member is Group Member Default, the server locates the default object as defined by server policy.

The Storage Status Mask field is used to indicate whether on-line objects (not archived or destroyed), archived objects, destroyed objects or any combination of the above are to be searched.The server SHALL NOT return unique identifiers for objects that are destroyed unless the Storage Status Mask field includes the Destroyed Storage indicator. The server SHALL NOT return unique identifiers for objects that are archived unless the Storage Status Mask field includes the Archived Storage indicator.

Implementation

Locate allows finding objects:

  • by their tags (see tagging)
  • by their Cryptographic Algorithm. Some supported values are: AES, RSA, ECDSA, ECDH, EC, ChaCha20, Poly1305, ChaCha20Poly1305, Ed25519, Ed448, CoverCrypt
  • by their Key Format Type. Some supported values are: Raw, Opaque, PKCS1, PKCS8, X509, ECPrivateKey, TransparentSymmetricKey, TransparentRSAPrivateKey, TransparentRSAPublicKey, TransparentECPrivateKey, TransparentECPublicKey, PKCS12, CoverCryptSecretKey, CoverCryptPublicKey
  • by their links to other objects:
    • a public key (Public Key Link)
    • a private key (Private Key Link)
    • a certificate (Certificate Link)
  • for certificates:
    • by subject common name
    • by certificate spki

Example - Symmetric Keys using the _kk tag

All symmetric keys are tagged with the system tag _kk. Multiple tags can be used locate objects; a JSON array of tags is used to specify multiple tags which is then serialized to hex.

{
  "tag": "Locate",
  "type": "Structure",
  "value": [
    {
      "tag": "Attributes",
      "type": "Structure",
      "value": [
        {
          "tag": "Attribute",
          "type": "Structure",
          "value": [
            {
              "tag": "VendorIdentification",
              "type": "TextString",
              "value": "cosmian"
            },
            {
              "tag": "AttributeName",
              "type": "TextString",
              "value": "tag"
            },
            {
              "tag": "AttributeValue",
              "type": "TextString",
              "value": "[\"_kk\"]"
            }
          ]
        }
      ]
    }
  ]
}

{
  "tag": "LocateResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "LocatedItems",
      "type": "Integer",
      "value": 8
    },
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "027cced1-ff2b-4bd3-a200-db1041583bdc"
    },
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "1a35b3be-1a1a-4798-a3aa-d9fc67298461"
    },
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "5dc81bb2-648f-485f-b804-c6ea45467056"
    },
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "6ce69a21-5b4b-470a-84e7-0e1385947527"
    },
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "ad9ba3be-93c7-4fac-a271-ef186fd645ce"
    },
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "bac520f6-461f-40e5-b8f2-7927d8ae310b"
    },
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "da5844b6-4d29-46b8-a657-9dfd449f8560"
    },
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "ebddca55-6027-4c86-ac1f-6b38dcfd6ead"
    }
  ]
}

Specification

This operation requests the server to perform a Message Authentication Code (MAC) operation on provided data using a specified MAC key and algorithm. The operation can be performed in a single request or as a series of requests for long data streams or by-parts cryptographic operations.

The response contains the MAC value or correlation value for streamed operations.

When a stream or by-parts operation is requested:

  • The server SHALL create a unique identifier, called correlation value if Init Indicator is True.
  • The server SHALL expect one or more requests with the same correlation value if Init Indicator is True.
  • The server SHALL return the Correlation Value in the response if Init Indicator is True.
  • The server SHALL use the Correlation Value for subsequent MAC requests.
  • The server SHALL close the stream or by-parts operation when Final Indicator is True.

Implementation

The Eviden KMS server supports the following MAC algorithms:

  • HMAC-SHA-1 (NIST SP 800-131A Rev. 2 — Acceptable)
  • HMAC-SHA-224 (NIST SP 800-131A Rev. 2 — Acceptable)
  • HMAC-SHA-256
  • HMAC-SHA-384
  • HMAC-SHA-512

For the complete list of supported MAC algorithms, please check the algorithms page.

Example - Simple MAC

Computing MAC with SHA3-512 using a MAC key.

Corresponding KMS CLI command:

ckms mac --mac-key-id 027cced1-ff2b-4bd3-a200-db1041583bdc --algorithm sha3-512 --data 0011223344556677889900
{
  "tag": "Mac",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "027cced1-ff2b-4bd3-a200-db1041583bdc"
    },
    {
      "tag": "CryptographicParameters",
      "type": "Structure",
      "value": [
        {
          "tag": "HashingAlgorithm",
          "type": "Enumeration",
          "value": "SHA3512"
        }
      ]
    },
    {
      "tag": "Data",
      "type": "ByteString",
      "value": "0011223344556677889900"
    }
  ]
}
{
  "tag": "MacResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "027cced1-ff2b-4bd3-a200-db1041583bdc"
    },
    {
      "tag": "Data",
      "type": "ByteString",
      "value": "F91DDB96D12CF8FAB0AA72224836D3F5F659A6634E3508A7C31DBC3727D2030254C57AD90AA5FB7F27FB3AAFABEAEB1204E4AF62BA2DE44E33E761B2C39DBACA"
    }
  ]
}

Example - Stream MAC

Computing MAC for a large file in multiple parts using SHA3-512.

Corresponding KMS CLI commands:

# First part with init indicator
ckms mac --mac-key-id 027cced1-ff2b-4bd3-a200-db1041583bdc --algorithm sha3-512 --data 0011223344556677889900 -i
# Middle part using correlation value
ckms mac --mac-key-id 027cced1-ff2b-4bd3-a200-db1041583bdc --algorithm sha3-512 --correlation-value F91DDB96D12CF8FAB0AA72224836D3F5F659A6634E3508A7C31DBC3727D2030254C57AD90AA5FB7F27FB3AAFABEAEB1204E4AF62BA2DE44E33E761B2C39DBACA --data 0011223344556677889900
# Final part with final indicator
ckms mac --mac-key-id 027cced1-ff2b-4bd3-a200-db1041583bdc --algorithm sha3-512 --correlation-value 51A2F7FCA8DECFC106031BE935F28F6EEE7E3850BCDB9D9B41B0F623146D7F51E399FC8F76A8B14EB71463DB0F6D421EF431E33F8CE1897FF988237C890C808F -f --data 0011223344556677889900
{
  "tag": "Mac",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "027cced1-ff2b-4bd3-a200-db1041583bdc"
    },
    {
      "tag": "CryptographicParameters",
      "type": "Structure",
      "value": [
        {
          "tag": "HashingAlgorithm",
          "type": "Enumeration",
          "value": "SHA3512"
        }
      ]
    },
    {
      "tag": "Data",
      "type": "ByteString",
      "value": "0011223344556677889900"
    },
    {
      "tag": "InitIndicator",
      "type": "Boolean",
      "value": true
    }
  ]
}
{
  "tag": "MacResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "027cced1-ff2b-4bd3-a200-db1041583bdc"
    },
    {
      "tag": "CorrelationValue",
      "type": "ByteString",
      "value": "F91DDB96D12CF8FAB0AA72224836D3F5F659A6634E3508A7C31DBC3727D2030254C57AD90AA5FB7F27FB3AAFABEAEB1204E4AF62BA2DE44E33E761B2C39DBACA"
    }
  ]
}
{
  "tag": "Mac",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "027cced1-ff2b-4bd3-a200-db1041583bdc"
    },
    {
      "tag": "CryptographicParameters",
      "type": "Structure",
      "value": [
        {
          "tag": "HashingAlgorithm",
          "type": "Enumeration",
          "value": "SHA3512"
        }
      ]
    },
    {
      "tag": "Data",
      "type": "ByteString",
      "value": "0011223344556677889900"
    },
    {
      "tag": "CorrelationValue",
      "type": "ByteString",
      "value": "F91DDB96D12CF8FAB0AA72224836D3F5F659A6634E3508A7C31DBC3727D2030254C57AD90AA5FB7F27FB3AAFABEAEB1204E4AF62BA2DE44E33E761B2C39DBACA"
    }
  ]
}
{
  "tag": "MacResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "027cced1-ff2b-4bd3-a200-db1041583bdc"
    },
    {
      "tag": "CorrelationValue",
      "type": "ByteString",
      "value": "51A2F7FCA8DECFC106031BE935F28F6EEE7E3850BCDB9D9B41B0F623146D7F51E399FC8F76A8B14EB71463DB0F6D421EF431E33F8CE1897FF988237C890C808F"
    }
  ]
}
{
  "tag": "Mac",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "027cced1-ff2b-4bd3-a200-db1041583bdc"
    },
    {
      "tag": "CryptographicParameters",
      "type": "Structure",
      "value": [
        {
          "tag": "HashingAlgorithm",
          "type": "Enumeration",
          "value": "SHA3512"
        }
      ]
    },
    {
      "tag": "Data",
      "type": "ByteString",
      "value": "0011223344556677889900"
    },
    {
      "tag": "CorrelationValue",
      "type": "ByteString",
      "value": "51A2F7FCA8DECFC106031BE935F28F6EEE7E3850BCDB9D9B41B0F623146D7F51E399FC8F76A8B14EB71463DB0F6D421EF431E33F8CE1897FF988237C890C808F"
    },
    {
      "tag": "FinalIndicator",
      "type": "Boolean",
      "value": true
    }
  ]
}
{
  "tag": "MacResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "027cced1-ff2b-4bd3-a200-db1041583bdc"
    },
    {
      "tag": "Data",
      "type": "ByteString",
      "value": "511BDAFDB2D059BD94FC72B8301ABF01DB9E02127420AED072B891A83952B88063DF3470225ACC6D46AD503E5E86B16BAEB581F218A148472120A9B541E1AF5D"
    }
  ]
}

Specification

This request is used to generate a replacement key for an existing symmetric key. It is analogous to the Create operation, except that attributes of the replacement key are copied from the existing key, with the exception of the attributes listed in Re-key Attribute Requirements.

As the replacement key takes over the name attribute of the existing key, Re-key SHOULD only be performed once on a given key.

The server SHALL copy the Unique Identifier of the replacement key returned by this operation into the ID Placeholder variable.

For the existing key, the server SHALL create a Link attribute of Link Type Replacement Object pointing to the replacement key. For the replacement key, the server SHALL create a Link attribute of Link Type Replaced Key pointing to the existing key.

An Offset MAY be used to indicate the difference between the Initial Date and the Activation Date of the replacement key. If no Offset is specified, the Activation Date, Process Start Date, Protect Stop Date and Deactivation Date values are copied from the existing key. If Offset is set and dates exist for the existing key, then the dates of the replacement key SHALL be set based on the dates of the existing key as follows.

Implementation

The Re-Key Operation refreshes Symmetric keys.

Example - Refresh a Symmetric Key

Corresponding KMS CLI command:

ckms sym keys re-key -k 64c60363-6660-4fd4-9f30-c965a0f72fc3
{
  "tag": "ReKey",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "64c60363-6660-4fd4-9f30-c965a0f72fc3"
    }
  ]
}
{
  "tag": "ReKeyResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "64c60363-6660-4fd4-9f30-c965a0f72fc3"
    }
  ]
}

Specification

This request is used to generate a replacement key pair for an existing public/private key pair. It is analogous to the Create Key Pair operation, except that attributes of the replacement key pair are copied from the existing key pair, with the exception of the attributes listed in Re-key Key Pair Attribute Requirements tor.

As the replacement of the key pair takes over the name attribute for the existing public/private key pair, Re-key Key Pair SHOULD only be performed once on a given key pair.

For both the existing public key and private key, the server SHALL create a Link attribute of Link Type Replacement Key pointing to the replacement public and private key, respectively. For both the replacement public and private key, the server SHALL create a Link attribute of Link Type Replaced Key pointing to the existing public and private key, respectively.

The server SHALL copy the Private Key Unique Identifier of the replacement private key returned by this operation into the ID Placeholder variable.

An Offset MAY be used to indicate the difference between the Initial Date and the Activation Date of the replacement key pair. If no Offset is specified, the Activation Date and Deactivation Date values are copied from the existing key pair. If Offset is set and dates exist for the existing key pair, then the dates of the replacement key pair SHALL be set based on the dates of the existing key pair as follows

Implementation

The Re-Key Key Pair Operation is the main mechanism to rotate Covercrypt attributes on the Eviden KMS Server. By updating, through this operation, the Policy held by a master secret key in it Vendor Attributes, the Eviden KMS Server will automatically

  • update the Policy held by the Master Public Key
  • and re-key all non revoked User Decryption Keys holding the rotated policy attributes in a way that they will now be able to decrypt cipher texts encrypted with attributes before and after the rotation.

The operation has currently no other usages on the Eviden server.

Example - Rotate the Security Level::Confidential attribute

Corresponding KMS CLI command:

ckms cc keys rekey -k b652a48a-a48c-4dc1-bd7e-cf0e5126b7b9 "Security Level::Confidential"

Using a JSON TTLV request to rotate the Security Level::Confidential attribute on a master secret key, construct a JSON object containing an array of the attributes that must be rotated:

{
  "RotateAttributes":
    [
      "Security Level::Confidential"
    ]
}

Then hex encode the JSON and add it as a VendorAttribute with name cover_crypt_policy_edit_action to the Re-Key Key Pair request.

The Private Key Unique Identifier of the Master Secret Key must be passed in the request.

{
  "tag": "ReKeyKeyPair",
  "type": "Structure",
  "value": [
    {
      "tag": "PrivateKeyUniqueIdentifier",
      "type": "TextString",
      "value": "b652a48a-a48c-4dc1-bd7e-cf0e5126b7b9"
    },
    {
      "tag": "PrivateKeyAttributes",
      "type": "Structure",
      "value": [
        {
          "tag": "CryptographicAlgorithm",
          "type": "Enumeration",
          "value": "CoverCrypt"
        },
        {
          "tag": "KeyFormatType",
          "type": "Enumeration",
          "value": "CoverCryptSecretKey"
        },
        {
          "tag": "ObjectType",
          "type": "Enumeration",
          "value": "PrivateKey"
        },
        {
          "tag": "VendorAttributes",
          "type": "Structure",
          "value": [
            {
              "tag": "VendorAttributes",
              "type": "Structure",
              "value": [
                {
                  "tag": "VendorIdentification",
                  "type": "TextString",
                  "value": "cosmian"
                },
                {
                  "tag": "AttributeName",
                  "type": "TextString",
                  "value": "cover_crypt_policy_edit_action"
                },
                {
                  "tag": "AttributeValue",
                  "type": "ByteString",
                  "value": "7B22526F7461746541747472696275746573223A5B225365637572697479204C6576656C3A3A436F6E666964656E7469616C225D7D"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

{
  "tag": "ReKeyKeyPairResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "PrivateKeyUniqueIdentifier",
      "type": "TextString",
      "value": "b652a48a-a48c-4dc1-bd7e-cf0e5126b7b9"
    },
    {
      "tag": "PublicKeyUniqueIdentifier",
      "type": "TextString",
      "value": "0fd1f684-156c-4ca6-adc2-0a6f4b620463"
    }
  ]
}

Specification

This request is used to generate a replacement certificate for an existing X.509 certificate. It is analogous to the Certify operation, except that the source is an already-issued certificate rather than a public key or CSR.

The server creates a new certificate with a fresh Unique Identifier, copies the subject public key and the requested validity period from the existing certificate, and re-signs it using the specified (or inherited) issuer key.

[KMIP 2.1], §6.1.6, "Certify" — "This request supports the certification of a new public key, as well as the certification of a public key that has already been certified (i.e., certificate update)."

The dedicated ReCertify operation tag (distinct from Certify) creates the replacement certificate as a new managed object with a new UID, and wires bidirectional ReplacementObjectLink / ReplacedObjectLink attributes between old and new certificates.

Server behaviour:

  1. A new Certificate object is created with a fresh Unique Identifier.
  2. The new certificate is issued with the requested Offset (activation delay) and NumberOfDays validity, or the issuer's defaults if omitted.
  3. ReplacementObjectLink is added to the old certificate pointing to the new UID.
  4. ReplacedObjectLink is added to the new certificate pointing back to the old UID.
  5. The new certificate enters Active state (or Pre-Active if Offset > 0).
  6. The old certificate is not automatically deactivated — its state remains unchanged. Call Revoke explicitly to deactivate it when ready.

Request Payload

ItemRequiredDescription
UniqueIdentifierNoUID of the existing certificate to re-certify. Defaults to the ID Placeholder if omitted.
CertificateRequestTypeNoType of an optional accompanying certificate request (e.g., PKCS10).
CertificateRequestValueNoRaw certificate request bytes (PEM or DER). Overrides the subject information from the old cert.
OffsetNoSeconds between Initial Date and the new certificate's Activation Date. 0 → Active immediately; > 0 → Pre-Active.
AttributesNoDesired attributes for the new certificate (e.g., issuer private key ID, issuer certificate ID, number of days).
ProtectionStorageMasksNoPermissible protection storage mask selections for the new object.

Response Payload

ItemDescription
UniqueIdentifierThe Unique Identifier of the newly created replacement certificate.

Implementation

The ReCertify operation rotates an X.509 certificate. It is the certificate equivalent of Re-Key for symmetric keys and Re-Key Key Pair for asymmetric key pairs.

Key differences vs. Certify with certificate-id-to-re-certify:

AspectReCertify operationCertify with existing cert UID
New UIDAlways freshOptionally same as old
Object linksBidirectional (old ↔ new)Not set automatically
Old cert deactivationManual (call Revoke)Manual (call Revoke)
KMIP operation tagReCertifyCertify

CLI command:

ckms certificates certify \
  --certificate-id-to-re-certify <OLD_CERT_UID> \
  --issuer-private-key-id <ISSUER_SK_UID> \
  --issuer-certificate-id <ISSUER_CERT_UID> \
  --number-of-days 365

Example — Re-certify a self-signed certificate

{
  "tag": "ReCertify",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "c3d4e5f6-0000-0000-0000-aabbccddeeff"
    }
  ]
}
{
  "tag": "ReCertifyResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "a1b2c3d4-1111-2222-3333-445566778899"
    }
  ]
}

Example — Re-certify with a future activation date (Offset)

An Offset of 86400 (24 h) produces a Pre-Active certificate whose Activation Date is set to Initial Date + 86400 s, allowing a zero-downtime switchover by pre-staging the replacement before the old one expires.

{
  "tag": "ReCertify",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "c3d4e5f6-0000-0000-0000-aabbccddeeff"
    },
    {
      "tag": "Offset",
      "type": "Interval",
      "value": 86400
    }
  ]
}
{
  "tag": "ReCertifyResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "b2c3d4e5-aaaa-bbbb-cccc-ddeeff001122"
    }
  ]
}
flowchart LR
    C0["Cert₀ (old)<br/>Active"] -->|ReplacementObjectLink| C1["Cert₁ (new)<br/>Active"]
    C1 -->|ReplacedObjectLink| C0

The old certificate remains Active until explicitly revoked. To complete the rotation:

# Revoke the old certificate when no longer needed
ckms certificates revoke --certificate-id <OLD_CERT_UID> \
  --revocation-reason "Superseded"

Key Rotation

Cosmian KMS supports manual key rotation for all key types through the standard KMIP operations:

KMIP operationApplies toCLI command
Re-KeySymmetric keys, secret datackms sym keys rekey
Re-Key Key PairAsymmetric key pairs (RSA, EC, PQC, …)ckms {rsa,ec,pqc} keys rekey
ReCertifyX.509 certificatesckms certificates certify --certificate-id-to-re-certify

On every rotation the server:

  1. Generates a new cryptographic object (or new certificate) under a fresh UID.
  2. Sets a ReplacementObjectLink on the old object pointing to the new UID.
  3. Sets a ReplacedObjectLink on the new object pointing back to the old UID.
  4. Transitions the old key to Deactivated (KMIP §4.57, transition 6 — see below).
  5. Increments x-rotate-generation and records x-rotate-date on the new object.

Auto-rotation (scheduler-driven, policy-based) is covered separately in Auto-Rotation Policy. HSM key rotation is covered in HSM Key Rotation.


State restrictions

Only objects in the following states can be the source of a rotation:

StateRotation allowed?Rationale
ActivePrimary valid source state.
DeactivatedKMIP §6.1.46 permits it; a replacement key should still be issued.
CompromisedRotating a compromised key is the recommended incident response.
Pre-ActiveKey material was never activated — rotating unused material is premature.
DestroyedObject no longer exists.
Destroyed_CompromisedObject no longer exists.

This restriction applies to the source key only. The output of a rotation can enter Pre-Active if the request includes an Offset > 0 (the new key's Activation Date is set to Initial Date + Offset).


Old key Deactivated after rotation

Why deactivation is enforced

A key in the Deactivated state SHALL NOT be used for applying cryptographic protection (encryption, signing, wrapping, MACing, deriving). It MAY still be used to process already-protected data (decryption, signature verification, unwrapping). This asymmetry is the key principle behind post-rotation key handling:

  • Limit blast radius. If the old key material is compromised after rotation, no new data was ever encrypted with it — the damage is bounded.
  • Enforce the cryptoperiod. NIST SP800-57-1 defines a cryptoperiod as the span during which a key is authorised to protect data. Key rotation ends that period; the Deactivated state makes the boundary enforceable by the server.
  • Preserve backward compatibility. Consumers holding ciphertexts produced before rotation must still be able to decrypt. Deactivated keeps the key accessible for decryption without allowing it to encrypt new data.

How KMIP §4.57 mandates it

KMIP §4.57 defines the State attribute and lists the only legal state transitions. Transition 6 governs Active → Deactivated and states that it SHALL occur in one of three ways:

  1. The object's Deactivation Date is reached.
  2. A client issues a Revoke operation with a non-Compromised reason.
  3. A client Modify Attribute sets the Deactivation Date to now or the past.

Re-Key / Re-Key Key Pair are not listed as a direct cause of transition 6. The spec mandates deactivation indirectly through path 1: the KMIP §4.33 Deactivation Date attribute rules list Re-key and Re-key Key Pair under "When implicitly set", meaning the server MUST set the Deactivation Date on the old key as part of the rotation operation. Because that date is set to now (or already passed), the condition "Deactivation Date is reached" immediately triggers transition 6.

In short: the spec does not have a direct Re-Key → Deactivated transition, but it mandates deactivation by requiring the server to set the Deactivation Date during Re-Key, which then fires transition 6 automatically.

Consequences

  • The old key can no longer be used for Encrypt or Sign operations (those require Active state).
  • The old key remains available for Decrypt and Verify (processing operations accept Active, Deactivated, and Compromised states per KMIP §3.31), so in-flight ciphertexts continue to decrypt.
  • You can call Revoke on an already-Deactivated key; the call succeeds as a no-op (the state is already revoked).
  • You can call Destroy on a Deactivated key directly — no prior Revoke is required.

Keysets

A keyset is a named group of related key generations. Each generation is a distinct cryptographic key (different material, different UID) produced by successive Re-Key operations.

Creating a SQL key in a keyset

For SQL-backed keys the key's UID must equal the keyset name from the start. Supply both UniqueIdentifier and x-rotate-name in the same Create request:

# Create a key whose UID is the keyset name
ckms sym keys create --key-id my-keyset --algorithm aes --length 256
ckms sym keys set-rotation-policy --key-id my-keyset --name my-keyset

Attempting set-rotation-policy --name X on a SQL key whose UID is not X is rejected:

Invalid Request: SetAttribute: rotate_name ('X') must equal the key's UID — create the key with the keyset name as its ID

SQL keyset UID scheme

UIDs are assigned deterministically at each generation:

my-keyset        ← gen 0  (UID equals the keyset name)
my-keyset@1      ← gen 1  (after first Re-Key)
my-keyset@2      ← gen 2  (after second Re-Key)

The Re-Key response always returns the new key's real UID (e.g. my-keyset@1).

Addressing syntax

A keyset can be referenced by name wherever a UniqueIdentifier is accepted (Encrypt, Decrypt, Get, Re-Key, …):

SyntaxResolves to
my-keysetLatest generation (bare name = @latest)
my-keyset@latestLatest generation (explicit alias)
my-keyset@firstGeneration 0
my-keyset@0Generation 0 (numeric alias for @first)
my-keyset@NGeneration N

Encrypt / Sign always resolves to the latest generation.

Decrypt / Verify walks the chain newest-to-oldest until one generation succeeds, allowing ciphertexts encrypted with an older (now Deactivated) key to continue to decrypt after rotation.

@latest is a virtual alias. It is never stored in the database and is never returned in a response. my-keyset (bare name) also resolves to the latest generation even though my-keyset is the literal UID of gen-0. To access gen-0 explicitly, use my-keyset@0 or my-keyset@first.

Non-latest guard

Only the latest generation of a keyset can be rotated via Re-Key. Attempting to rotate a retired member is rejected:

Invalid Request: ReKey: key '<uid>' is not the latest in its keyset —
only the latest generation can be rotated

Use my-keyset@0 (explicit generation) rather than my-keyset (bare name) to target an older generation — and expect that call to fail.

Keyset internals (SQL)

Keyset state is stored as KMIP vendor attributes in the database:

AttributeTypeMeaning
x-rotate-namestringKeyset name; equals the key UID for gen-0.
x-rotate-generationi320 for gen-0, incremented on each Re-Key.
x-rotate-latestbooltrue on the current key; false on older keys.

KMIP link attributes mirror the chain for protocol compliance: ReplacementObjectLink (old → new) and ReplacedObjectLink (new → old). Keyset chain traversal for Decrypt / Verify uses x-rotate-generation (sorted descending), not the link back-pointers.


Rotation flows by key type

1. Plain symmetric key

A plain symmetric key carries only its own policy.

What happens:

  1. Fresh key material is generated (same algorithm and length).
  2. The new key gets a new UID (my-keyset@N for keyset keys, fresh UUID otherwise).
  3. ReplacedObjectLink on the new key → old key.
  4. ReplacementObjectLink on the old key → new key.
  5. Old key → Deactivated.

KMIP link chain after two rotations:

flowchart LR
    K0["Key₀ (gen 0)<br/>Deactivated"] -->|ReplacementObjectLink| K1["Key₁ (gen 1)<br/>Deactivated"]
    K1 -->|ReplacementObjectLink| K2["Key₂ (gen 2)<br/>Active"]
    K2 -->|ReplacedObjectLink| K1
    K1 -->|ReplacedObjectLink| K0

CLI:

# Manual rotation of a SQL keyset key
ckms sym keys rekey --key-id my-keyset
# Response: new UID = my-keyset@1

# Manual rotation of a plain UUID key
ckms sym keys rekey --key-id <KEY_UID>

2. Wrapping key

A wrapping key is referenced by one or more wrapped keys via a WrappingKeyLink attribute. Rotating a wrapping key re-wraps all its dependants atomically.

What happens:

  1. A new wrapping key is created and committed to the database (Phase 1).
  2. Every key owned by the same user whose WrappingKeyLink points to the old wrapping key is fetched — regardless of its state (Active, Deactivated, Pre-Active, Compromised, …) — unwrapped in memory (plaintext never stored), and re-wrapped with the new wrapping key (Phase 2).
  3. Each wrapped key's WrappingKeyLink is updated to the new wrapping key UID.
  4. Standard rotation metadata is applied; old wrapping key → Deactivated.

No state filter on dependants. The server queries all keys with a wrapping_key_id matching the rotated key, with no restriction on their lifecycle state. Only the owner check applies: dependants owned by a different user are skipped (with a warning logged).

sequenceDiagram
    participant Client
    participant KMS
    participant DB

    Client->>KMS: Re-Key(wrapping_key_uid)
    KMS->>DB: Phase 1 — create new wrapping key (committed)
    loop For each wrapped dependant
        KMS->>DB: retrieve wrapped key
        KMS->>KMS: unwrap with old wrapping key
        KMS->>KMS: re-wrap with new wrapping key
        KMS->>DB: update WrappingKeyLink → new wrapping key UID
    end
    KMS->>DB: Phase 2 — retire old wrapping key (Deactivated, links set)
    KMS-->>Client: Re-KeyResponse(new_wrapping_key_uid)

3. Wrapped key

A wrapped key stores its key material encrypted under a wrapping key.

What happens:

  1. The wrapped key is exported and unwrapped in memory using the current wrapping key (plaintext never stored).
  2. Fresh plaintext key material is generated from the unwrapped attributes.
  3. The new material is re-wrapped with the same wrapping key.
  4. The new ciphertext is stored under a new UID with an active WrappingKeyLink.
  5. Standard rotation metadata is applied; old wrapped key → Deactivated.
sequenceDiagram
    participant Client
    participant KMS
    participant DB

    Client->>KMS: Re-Key(wrapped_key_uid)
    KMS->>DB: retrieve wrapped key + wrapping key
    Note over KMS: unwrap in-memory (plaintext never stored)
    KMS->>KMS: generate new key material
    KMS->>KMS: re-wrap with same wrapping key
    KMS->>DB: store new wrapped key (new UID, same WrappingKeyLink)
    KMS->>DB: retire old key (Deactivated, ReplacementObjectLink → new key)
    KMS-->>Client: Re-KeyResponse(new_key_uid)

4. Asymmetric key pair

Re-Key Key Pair targets the private key UID. The server resolves the associated public key via the PublicKeyLink attribute.

What happens:

  1. A new private key + public key pair is generated (same algorithm).
  2. Both receive new UIDs; the new private key carries a PublicKeyLink to the new public key.
  3. Standard ReplacementObjectLink / ReplacedObjectLink links are set on both pairs.
  4. Old private key and old public key → Deactivated.
sequenceDiagram
    participant Client
    participant KMS
    participant DB

    Client->>KMS: Re-Key Key Pair(private_key_uid)
    KMS->>DB: retrieve private key
    KMS->>DB: retrieve linked public key (PublicKeyLink)
    KMS->>KMS: generate new key pair (same algorithm)
    KMS->>DB: Phase 1 — store new private key + new public key
    KMS->>DB: Phase 2 — retire old private key + public key (Deactivated, links)
    KMS-->>Client: Re-Key Key PairResponse(new_sk_uid, new_pk_uid)

CLI:

# EC key pair
ckms ec keys rekey --key-id <PRIVATE_KEY_UID>

# RSA key pair
ckms rsa keys rekey --key-id <PRIVATE_KEY_UID>

# Post-quantum (ML-KEM, ML-DSA, SLH-DSA)
ckms pqc keys rekey --key-id <PRIVATE_KEY_UID>

5. Wrapped private key (Covercrypt)

A Covercrypt master private key follows the same Re-Key Key Pair flow. The wrapped key is unwrapped in memory, the Covercrypt partition attributes are re-keyed, and the new wrapped private key is stored under a fresh UID.

Setting a rotation policy on a wrapped private key always works: the x-rotate-* attributes are stored in the metadata column (not inside the ciphertext block) and do not require the key to be unwrapped first.


6. Certificate renewal (ReCertify)

Certificate renewal creates a new certificate for the same key pair — no new key material is generated.

What happens:

  1. The existing certificate is retrieved and its issuer / subject are resolved.
  2. A new certificate is built and signed (same key pair, same issuer).
  3. The new certificate receives a fresh UID.
  4. ReplacedObjectLink on the new cert → old cert.
  5. ReplacementObjectLink on the old cert → new cert.
  6. All keys linked to the old certificate have their CertificateLink updated.
  7. x-rotate-generation and x-rotate-date are updated.
sequenceDiagram
    participant Client
    participant KMS
    participant DB

    Client->>KMS: ReCertify(old_cert_uid)
    KMS->>DB: retrieve old certificate
    KMS->>KMS: resolve issuer + subject from old cert
    KMS->>KMS: build & sign new certificate (same key pair)
    KMS->>DB: Phase 1 — store new cert (fresh UID)
    KMS->>DB: Phase 2 — update old cert (ReplacementObjectLink)
    KMS->>DB: Phase 2 — relink keys (CertificateLink → new cert)
    KMS-->>Client: ReCertifyResponse(new_cert_uid)

Attribute changes (KMIP 2.1 §6.1.45):

AttributeNew certificateOld certificate
Unique IdentifierFresh UUIDUnchanged
Initial DateNowUnchanged
Link[ReplacedObjectLink]→ old cert UID
Link[ReplacementObjectLink]→ new cert UID
Link[PublicKeyLink]Copied from oldUnchanged
Link[PrivateKeyLink]Copied from oldUnchanged
NameInherited from oldRemoved (per KMIP spec)
StateActiveActive (not Deactivated — certificates are exempt from §4.57)
x-rotate-generationold + 1Unchanged
x-rotate-dateNowUnchanged

CLI:

# Renew a CA-signed certificate (same key pair, new validity period)
ckms certificates certify \
    --certificate-id-to-re-certify <OLD_CERT_UID> \
    --issuer-private-key-id <ISSUER_SK_UID> \
    --days 365

# Self-signed certificate renewal
ckms certificates certify \
    --certificate-id-to-re-certify <OLD_CERT_UID> \
    --days 3650

Standards:

StandardRelevance
KMIP 2.1 §6.1.45Normative definition of ReCertify
RFC 4210 §5.3.5–5.3.6CMP Key Update Request / Response (kur/kup) — the wire-protocol equivalent
RFC 5280X.509v3 certificate structure and validity periods

7. KEK-protected key (server-wide key-encryption key)

When the KMS server is started with --key-encryption-key <KEK_UID>, every object stored in the database is transparently wrapped by the KEK. Rotation works identically to the wrapped-key flow above — the server unwraps in memory, generates fresh material, re-wraps, and stores the result.

# Example server startup with SoftHSM2 KEK
cosmian_kms \
  --database-type sqlite \
  --hsm-model softhsm2 \
  --hsm-slot 0 \
  --hsm-password 12345678 \
  --key-encryption-key "hsm::softhsm2::0::my-kek"

No special handling is required for rotation policy — SetAttribute on a KEK-wrapped key writes the x-rotate-* attributes to the metadata column, not into the ciphertext, so no unwrap is needed.


Rotation dispatch overview

flowchart TD
    subgraph "Re-Key dispatch"
        REQ["Re-Key / Re-Key Key Pair / ReCertify"] --> DISPATCH{"Object type?"}
        DISPATCH -->|SymmetricKey, no dependants| PLAIN["Plain rekey<br/>(new material, new UID)"]
        DISPATCH -->|SymmetricKey, has WrappingKeyLink dependants| WRAP_K["Wrapping-key rotation<br/>(Phase 1 commit → Phase 2 re-wrap)"]
        DISPATCH -->|SymmetricKey, is wrapped| WRAP_D["Wrapped-key rotation<br/>(unwrap → new material → re-wrap)"]
        DISPATCH -->|PrivateKey| ASYM["Re-Key Key Pair"]
        DISPATCH -->|Certificate| CERT["ReCertify<br/>(same key pair, new cert UID)"]
        PLAIN & WRAP_K & WRAP_D & ASYM & CERT --> META["Update metadata<br/>(generation++, date, links,<br/>old key → Deactivated)"]
    end

KMIP attribute changes on manual rotation

When the user explicitly calls Re-Key (e.g. ckms sym keys rekey), the following attributes are set on the old and new key:

AttributeOld keyNew key
Unique Identifierunchangedfresh UUID (or name@N for keyset keys)
StateDeactivated (§4.57)Active
Link[ReplacementObjectLink]→ new key UID
Link[ReplacedObjectLink]→ old key UID
Link[WrappingKeyLink]unchangedcopied from old key
x-rotate-generationunchangedold value + 1
x-rotate-dateunchangedtimestamp of rotation
x-rotate-intervalset to 0 (disabled)0 — must be re-armed explicitly on the new key
x-rotate-nameunchangedinherited from old key
x-rotate-offsetunchangedNone (not inherited for manual rekey)
Nameremovedinherited from old key

Manual vs auto-rotation difference: x-rotate-interval is intentionally set to 0 (not inherited) on the new key after a manual rotation. This forces the operator to re-evaluate the rotation policy for the new key rather than blindly continuing the old schedule.

# After a manual rekey, re-arm the rotation policy on the new key:
ckms sym keys set-rotation-policy \
    --key-id  <NEW_KEY_UID> \
    --interval 3600 \
    --name    "my-keyset"

Revoking superseded keys

After rotation the old key is Deactivated (not Destroyed). Its material persists so that in-flight Decrypt / Verify operations against old ciphertexts continue to work. Once all consumers have migrated, destroy the old key:

# Find the old key UID from the new key's ReplacedObjectLink attribute
ckms objects get-attributes --key-id <NEW_KEY_UID>

# Destroy the old key directly (Deactivated keys do not need a prior Revoke)
ckms sym keys destroy --key-id <OLD_KEY_UID>

If you need to place the old key into Compromised state (e.g. for audit records), call Revoke first with a compromise reason:

# Symmetric key
ckms sym keys revoke -k <OLD_KEY_UID> "Superseded"

# Asymmetric key pair (revokes both private and linked public key)
ckms ec keys revoke -k <OLD_KEY_UID> "Superseded"

# Certificate
ckms certificates revoke -c <OLD_CERT_UID> "Superseded"

HSM Key Rotation

Cosmian KMS supports manual Re-Key for keys that reside on a PKCS#11-capable Hardware Security Module (SoftHSM2, Utimaco, Proteccio, …). The flow mirrors SQL-backed rotation but all keyset metadata lives in PKCS#11 attributes rather than the KMS database.

Capabilities

CapabilitySupportedNotes
Manual Re-Key via KMIPCalls C_GenerateKey on the same HSM slot.
Keyset membership (x-rotate-name)Stored in CKA_LABEL; keyset name must be the full base UID (hsm::model::slot::key_id). Supports @latest, @first, @N generation addressing.
x-rotate-interval attributeWrites CKA_START_DATE / CKA_END_DATE for validity tracking.
Auto-rotation schedulerfind_due_for_rotation never returns HSM UIDs; scheduler skips them.
x-rotate-offsetNot applicable to PKCS#11 scheduling; rejected with NotSupported.

CKA_LABEL convention

HSM keyset metadata is stored entirely in the PKCS#11 CKA_LABEL attribute — no SQL shadow rows are written.

CKA_LABEL valueMeaning
{key_id} (plain, before enrollment)Initial state after Create — key has not yet been added to a keyset
{base_uid}::0::{key_id}@latestGen-0 key immediately after SetAttribute enrollment
{base_uid}::{gen}::{base_id}@latestCurrent latest key after any Re-Key
{base_uid}::{gen}::{base_id}Retired key (any generation that is no longer the latest)
(anything else)Key does not belong to a keyset

Where:

  • base_uid — the full base UID of the gen-0 key: hsm::<model>::<slot_id>::<key_id>. Because it embeds the slot ID, the keyset name is unique across all HSM slots. A key on slot 0 and a key on slot 1 with the same local name will have different keyset names (hsm::softhsm2::0::my-key vs hsm::softhsm2::1::my-key).
  • gen — integer starting at 0, incremented on every Re-Key.
  • base_id / key_id — the PKCS#11 CKA_ID of the gen-0 key.

Why does my-hsm-key appear twice in the label?

The label format is {rotate_name}::{gen}::{key_id}. For HSM keys, rotate_name is the full base UID (hsm::softhsm2::0::my-hsm-key), which already embeds the local key name. The same name then appears again as the separate key_id field required by parse_label_metadata to reconstruct the per-key PKCS#11 CKA_ID.

hsm::softhsm2::0::my-hsm-key  ::  0  ::  my-hsm-key@latest
├─ rotate_name (full base UID)  gen   key_id

This duplication is intentional and handled by the right-split parser.

CKA_LABEL parsing uses right-split

Because rotate_name itself contains ::, the label is parsed from the right (rsplitn(3, "::")):

hsm::softhsm2::0::my-key::1::my-key@latest
├─ rotate_name  = "hsm::softhsm2::0::my-key"   (everything left of the last two ::)
├─ generation   = 1
└─ key_id       = "my-key@latest"

This is backward-compatible: labels written before the full-UID convention (e.g. "my-keyset::0::my-key@latest") still parse correctly.


UID scheme

hsm::<model>::<slot_id>::<base_key_id>      ← gen 0 (original key)
hsm::<model>::<slot_id>::<base_key_id>@1   ← gen 1 (after first Re-Key)
hsm::<model>::<slot_id>::<base_key_id>@2   ← gen 2 (after second Re-Key)

Where <model> is the HSM model name (e.g. softhsm2, utimaco, proteccio). The @N generation suffix is appended to the original base_key_id, using the same convention as SQL keyset keys (my-key@1, my-key@2). Unlike SQL keys, the base portion of the UID never changes, so the full chain can be discovered by scanning CKA_LABEL on the HSM slot.

Backward-compatible two-segment UIDs

The older format hsm::<slot_id>::<key_id> (without model segment) is still accepted for backward compatibility with keys created before the model segment was introduced.


Keyset name constraint

For HSM keys the keyset name (x-rotate-name) must be the key's full base UID (hsm::<model>::<slot>::<key_id> without any @N suffix).

ValueAccepted?Reason
hsm::softhsm2::0::my-hsm-keyFull base UID — unique across slots.
my-hsm-keysetBare name rejected — no slot disambiguation.
hsm::softhsm2::0::my-hsm-key@1Generation suffix rejected — base UID only.
Invalid Request: SetAttribute: for HSM keys, rotate_name must be the key's base UID
('hsm::softhsm2::0::my-hsm-key'), not 'my-hsm-keyset'

Why this constraint? A bare name like "my-hsm-key" is ambiguous when multiple HSM slots are in use — two keys on different slots could have the same local name. Embedding the slot ID in the keyset name prevents cross-slot collisions without requiring a central name registry.


Keyset resolution

When a base UID (e.g. hsm::softhsm2::0::my-hsm-key) or generation-scoped syntax (hsm::softhsm2::0::my-hsm-key@latest, hsm::softhsm2::0::my-hsm-key@0, hsm::softhsm2::0::my-hsm-key@N) is used, the server calls find_by_rotate_name, which scans PKCS#11 objects in the HSM slot and parses CKA_LABEL to extract the rotate_name and rotate_generation. Results are sorted by generation descending:

  • Encrypt / Sign → uses the key with the highest rotate_generation (current latest). The @latest suffix in CKA_LABEL is not used for this determination — generation sorting is the authoritative source.
  • Decrypt / Verify → tries each generation newest-to-oldest until one succeeds, allowing old ciphertexts to continue to decrypt after rotation.

Using a direct generation UID with an explicit @N suffix (e.g. hsm::softhsm2::0::my-hsm-key@0) bypasses keyset resolution entirely: only that specific generation is used, with no chain walk.

Using the base UID without @N (e.g. hsm::softhsm2::0::my-hsm-key) always resolves to the latest generation (see Second rotation and non-latest guard below for the Re-Key-specific stable-handle behaviour).

HSM keysets do not use ReplacedObjectLink / ReplacementObjectLink back-pointers; all state lives in PKCS#11 attributes.


Full rotation workflow

Setup

# 1. Start the KMS server with HSM backend
cosmian_kms \
  --database-type sqlite \
  --hsm-model softhsm2 \
  --hsm-slot 0 \
  --hsm-password 12345678

Create and enrol

# 2. Create an AES-256 key directly on the HSM
ckms sym keys create \
    --key-id "hsm::softhsm2::0::my-hsm-key" \
    --algorithm aes --length 256

# 3. Enrol the key in a keyset.
#    The keyset name MUST equal the key's full base UID.
#    Before: CKA_LABEL = "my-hsm-key"  (plain key_id set at creation)
#    After:  CKA_LABEL = "hsm::softhsm2::0::my-hsm-key::0::my-hsm-key@latest"
#             └─ rotate_name ──────────────────────┘ gen └─ key_id ──┘
ckms sym keys set-rotation-policy \
    --key-id "hsm::softhsm2::0::my-hsm-key" \
    --name   "hsm::softhsm2::0::my-hsm-key"

Encrypt using the base UID as the keyset address

# 4. Encrypt — base UID resolves to the latest key (gen-0 initially)
ckms sym encrypt --key-id "hsm::softhsm2::0::my-hsm-key" plaintext.bin -o ciphertext.enc

First rotation

# 5. Rotate using the base UID as a stable keyset handle.
#    No @N suffix → server redirects to the latest generation (gen-0 here).
ckms sym keys rekey --key-id "hsm::softhsm2::0::my-hsm-key"
# Response: new UID = hsm::softhsm2::0::my-hsm-key@1
#
# CKA_LABEL (gen-0): "hsm::softhsm2::0::my-hsm-key::0::my-hsm-key"          (retired)
# CKA_LABEL (gen-1): "hsm::softhsm2::0::my-hsm-key::1::my-hsm-key@latest"   (current)
sequenceDiagram
    participant Client
    participant KMS
    participant HSM

    Client->>KMS: Re-Key("hsm::softhsm2::0::my-hsm-key")
    KMS->>HSM: C_FindObjects (CKA_ID = "my-hsm-key")
    HSM-->>KMS: {gen-0 object handle}
    KMS->>KMS: build new key attributes<br/>(algorithm, length, gen=1)
    KMS->>HSM: C_GenerateKey → gen-1 object
    KMS->>HSM: C_SetAttributeValue gen-0<br/>CKA_LABEL = "hsm::softhsm2::0::my-hsm-key::0::my-hsm-key"
    KMS->>HSM: C_SetAttributeValue gen-1<br/>CKA_LABEL = "hsm::softhsm2::0::my-hsm-key::1::my-hsm-key@latest"
    KMS-->>Client: Re-KeyResponse("hsm::softhsm2::0::my-hsm-key@1")

Decrypt old ciphertext after rotation

# 6. Decrypt using the base UID — server tries gen-1 then gen-0
ckms sym decrypt --key-id "hsm::softhsm2::0::my-hsm-key" ciphertext.enc -o plaintext.bin
sequenceDiagram
    participant Client
    participant KMS
    participant HSM

    Client->>KMS: Decrypt("hsm::softhsm2::0::my-hsm-key", ciphertext)
    KMS->>HSM: find_by_rotate_name("hsm::softhsm2::0::my-hsm-key")<br/>scan CKA_LABEL right-split, sort by gen desc
    HSM-->>KMS: [gen-1, gen-0]
    KMS->>HSM: C_DecryptInit / C_Decrypt with gen-1
    HSM-->>KMS: DecryptFailed (wrong key material)
    KMS->>HSM: C_DecryptInit / C_Decrypt with gen-0
    HSM-->>KMS: plaintext
    KMS-->>Client: DecryptResponse(plaintext)

Second rotation and non-latest guard

The Re-Key operation distinguishes two addressing styles for HSM keys:

Addressing styleExampleBehaviour when not latest
Stable handle (base UID, no @N)hsm::softhsm2::0::my-hsm-keySilently redirects to the latest generation
Explicit generation (@N)hsm::softhsm2::0::my-hsm-key@0Returns an error
# 7. Second rotation using the stable handle (always rotates the latest)
ckms sym keys rekey --key-id "hsm::softhsm2::0::my-hsm-key"
# → redirected to gen-1 (current latest)
# Response: new UID = hsm::softhsm2::0::my-hsm-key@2

# Attempting to re-key an explicit retired generation is rejected:
ckms sym keys rekey --key-id "hsm::softhsm2::0::my-hsm-key@0"   # explicit @0 — REJECTED
# Error: ReKey: HSM key 'hsm::softhsm2::0::my-hsm-key@0' is not the latest in its keyset
#   — only the latest generation can be rotated.
#   Use 'hsm::softhsm2::0::my-hsm-key' to always rotate the current head.

Differences from SQL rotation

AspectSQL-backed keysHSM-resident keys
Keyset metadata storageKMIP vendor attributes (x-rotate-*) in SQL DBCKA_LABEL on PKCS#11 objects
UID schemePlain identifier (keyset name is a rotate_name attribute, not in the UID)hsm::<model>::<slot>::<base_key_id>[@N] (generation suffix appended)
Auto-rotation scheduler✅ Supported❌ Not supported
x-rotate-offset✅ Supported❌ Not supported
KMIP link back-pointersReplacedObjectLink / ReplacementObjectLink❌ Not used; chain lives in CKA_LABEL
Chain discoverySQL query on x-rotate-name + x-rotate-generationPKCS#11 C_FindObjects on CKA_LABEL prefix
Old key state after rotationDeactivated (§4.57)Retired label set in CKA_LABEL (no KMIP state)

Auto-Rotation Policy

Cosmian KMS supports scheduled, policy-driven key rotation for SQL-backed symmetric keys and asymmetric key pairs. A per-key rotation policy is attached to a key object; a background scheduler then rotates any key whose interval has elapsed — without any operator action.

HSM keys support manual rotation only; the auto-rotation scheduler never picks up HSM UIDs. See HSM Key Rotation.

For the rotation flows and sequence diagrams for each key type, see Key Rotation.


Rotation policy attributes

All rotation-policy state is stored as vendor-extension KMIP attributes on the key object itself:

AttributeTypeMutableDescription
x-rotate-intervali64 (seconds)How often to rotate. 0 disables auto-rotation.
x-rotate-namestringKeyset name this key belongs to (see Key Rotation — Keysets).
x-rotate-offseti64 (seconds)Shift the first rotation trigger by this many seconds after Initial Date.
x-rotate-generationi32Incremented on every rotation; 0 for never-rotated keys. Server-managed.
x-rotate-datedatetimeTimestamp of the last rotation. Server-managed.
x-rotate-latestbooltrue on the most-recent keyset member; false on all older keys. Server-managed.

Read-only attributes

x-rotate-generation, x-rotate-date, and x-rotate-latest are set exclusively by the server during the Re-Key operation. Any attempt to modify them via AddAttribute, SetAttribute, ModifyAttribute, or DeleteAttribute is rejected with Attribute_Read_Only.

These restrictions maintain two invariants relied on by the scheduler and the keyset resolution logic:

  • Monotonic generation counterx-rotate-generation starts at 0 and increments by exactly 1 per rotation. Within a keyset the generation is unique and strictly increasing, which lets the scheduler and client tooling identify the current key without inspecting every member.
  • Authoritative rotation timestampx-rotate-date is the only reliable source for "when was this key last rotated". The scheduler computes the next trigger as x-rotate-date + x-rotate-interval (previously rotated) or initial_date + x-rotate-offset + x-rotate-interval (never rotated). External modifications to x-rotate-date would cause missed or premature rotations.

Assigning a rotation policy

Use ckms sym keys set-rotation-policy (or SetAttribute for key pairs) to configure the mutable attributes on an existing key:

# Rotate every hour; first rotation is 60 seconds after Initial Date
ckms sym keys set-rotation-policy \
    --key-id  my-keyset   \
    --interval 3600       \
    --offset   60         \
    --name     my-keyset

SetAttribute initialises x-rotate-generation = 0 and x-rotate-latest = true on the key. Every subsequent Re-Key increments the generation, marks the new key as latest, and marks the old key as non-latest.

For asymmetric key pairs, use SetAttribute directly:

ckms objects set-attribute \
    --id <PRIVATE_KEY_UID> \
    x-rotate-interval 86400

Server configuration

Enable the scheduler

The background scheduler is disabled by default. Enable it in kms.toml or via the command-line flag:

# kms.toml
auto_rotation_check_interval_secs = 300   # check every 5 minutes
# Command-line equivalent
cosmian_kms --auto-rotation-check-interval-secs 300

Set the interval to a value smaller than the shortest x-rotate-interval on any key — otherwise some rotations will be delayed by up to one scheduler period.

HSM backend + KEK

The scheduler works with any database backend. If the server is started with a --key-encryption-key, KEK-wrapped keys are rotated the same as plain keys (the server unwraps in memory, generates fresh material, re-wraps):

cosmian_kms \
  --database-type sqlite \
  --hsm-model softhsm2 \
  --hsm-slot 0 \
  --hsm-password 12345678 \
  --key-encryption-key "hsm::softhsm2::0::my-kek" \
  --auto-rotation-check-interval-secs 300

How the scheduler works

On each tick, the scheduler:

  1. Queries all Active symmetric keys and private keys whose x-rotate-interval > 0.
  2. For each candidate, computes the next rotation deadline:
    • Previously rotated key: x-rotate-date + x-rotate-interval
    • Never-rotated key with Initial Date: initial_date + x-rotate-offset + x-rotate-interval
  3. Rotates every key whose deadline is in the past.
  4. Emits an OpenTelemetry counter kms.key.auto_rotation labelled with uid and algorithm for each successful rotation.

The scheduler never returns HSM UIDs — auto-rotation is SQL-only.

Attribute changes on auto-rotation

AttributeOld keyNew key
Unique Identifierunchangedfresh UUID (or name@N for keysets)
StateDeactivated (§4.57)Active
Link[ReplacementObjectLink]→ new key UID
Link[ReplacedObjectLink]→ old key UID
x-rotate-generationunchangedold value + 1
x-rotate-dateunchangedtimestamp of rotation
x-rotate-intervalset to 0 (cron skips old key)inherited from old key
x-rotate-nameunchangedinherited from old key
x-rotate-offsetunchangedinherited from old key
x-initial-dateclearedset to now (resets next deadline)
Cryptographic Algorithmunchangedcopied from old key
Cryptographic Lengthunchangedcopied from old key

Note: x-rotate-interval is inherited on auto-rotation (policy continues on the new key automatically). On manual Re-Key it is set to 0 on the new key — the operator must re-arm the policy explicitly.


End-to-end setup

Step 1 — Create a key in a keyset

# SQL key: UID must equal the keyset name
ckms sym keys create --key-id my-keyset --algorithm aes --length 256

Step 2 — Attach a rotation policy

ckms sym keys set-rotation-policy \
    --key-id   my-keyset \
    --name     my-keyset \
    --interval 3600      \
    --offset   60

Step 3 — Enable the server scheduler

In kms.toml:

auto_rotation_check_interval_secs = 300

The scheduler will rotate my-keyset for the first time roughly 60 + 3600 seconds after its Initial Date, and every 3600 seconds thereafter.


Disabling auto-rotation on a key

Set x-rotate-interval to 0:

ckms sym keys set-rotation-policy --key-id my-keyset --interval 0

This prevents the scheduler from selecting the key without removing any other keyset metadata.


Keyset chain depth warning

When a Decrypt or Verify call resolves a keyset name (e.g. my-keyset) rather than an explicit key UID, the server walks the generation chain newest-to-oldest until one generation succeeds. A very deep chain indicates that many ciphertexts are still bound to old key generations — a signal that client-side re-encryption may be overdue.

To surface this, configure keyset_warn_depth in kms.toml:

# Emit a server warning when decryption succeeds at chain depth >= this value.
# Default: 5.  0 disables the warning.
keyset_warn_depth = 5

Or via the CLI flag:

cosmian_kms --keyset-warn-depth 5

When the threshold is reached, the server emits a WARN log line:

Decrypt: keyset chain depth 5 >= warn threshold 5 for uid my-keyset@0;
consider re-encrypting with the latest key

Traversal is unbounded

The chain walk has no hard limit. All key generations remain reachable (subject to KMIP state rules). keyset_warn_depth is purely a monitoring hint — it never prevents a decryption from succeeding.


Observability

The server increments the OpenTelemetry counter kms.key.auto_rotation with labels uid and algorithm on every successful auto-rotation. Use your OTel-compatible backend (Prometheus + Grafana, Datadog, …) to:

  • Alert on unexpected gaps in rotation activity.
  • Audit which keys were rotated and when.
  • Track rotation throughput across the fleet.

See Monitoring for the full OTel setup guide.

Specification

This operation requests the server to revoke a Managed Cryptographic Object or an Opaque Object.

The request contains a reason for the revocation (e.g., "key compromise", "cessation of operation", etc.).

The operation has one of two effects. If the revocation reason is "key compromise" or "CA compromise", then the object is placed into the "compromised" state; the Date is set to the current date and time; and the Compromise Occurrence Date is set to the value (if provided) in the Revoke request and if a value is not provided in the Revoke request then Compromise Occurrence Date SHOULD be set to the Initial Date for the object. If the revocation reason is neither "key compromise" nor "CA compromise", the object is placed into the "deactivated" state, and the Deactivation Date is set to the current date and time.

Implementation

The state of the object is kept as specified but the revocation reason is currently not maintained. Once an Object is revoked, it can only be retrieved using the Export operation. The Get operation will return an error.

A Revoked object can be destroyed using the Destroy operation.

Example - Symmetric key

Revoking key f54f14a3-5639-4054-8c23-54af891669db with reason key was compromised.

Corresponding KMS CLI command:

  ckms sym keys revoke -k f54f14a3-5639-4054-8c23-54af891669db "key was compromised"
{
  "tag": "Revoke",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "f54f14a3-5639-4054-8c23-54af891669db"
    },
    {
      "tag": "RevocationReason",
      "type": "TextString",
      "value": "key was compromised"
    }
  ]
}

{
  "tag": "RevokeResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "f54f14a3-5639-4054-8c23-54af891669db"
    }
  ]
}

Specification

The Sign operation requests the server to generate a digital signature for the provided data using a specified Managed Object.

The request contains the unique identifier of the signing key, data to be signed (or already digested data), and cryptographic parameters. The signature is computed using the specified signing key and cryptographic parameters.

The SignatureVerify operation validates a digital signature against provided data using a specified verification key.

Implementation

The Eviden KMS server supports signing with RSA and EC private keys. The signature algorithms supported include:

  • RSA-PKCS#1 v1.5 with SHA-256, SHA-384, SHA-512
  • RSA-PSS with SHA-256, SHA-384, SHA-512
  • ECDSA with SHA-256, SHA-384, SHA-512
  • Ed25519 (signature verification only for imported keys)

Example - Sign with RSA Private Key

Sign data using an RSA private key with SHA-256.

Note: Sign and SignatureVerify operations are available through the KMIP JSON API endpoint but not directly exposed through CLI subcommands. Use direct HTTP requests to the /kmip/2_1 endpoint.

{
  "tag": "Sign",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "9382bfec-bd6c-46ed-8f00-90b467f77a15"
    },
    {
      "tag": "CryptographicParameters",
      "type": "Structure",
      "value": [
        {
          "tag": "DigitalSignatureAlgorithm",
          "type": "Enumeration",
          "value": "SHA256WithRSAEncryption"
        }
      ]
    },
    {
      "tag": "Data",
      "type": "ByteString",
      "value": "48656C6C6F2C207369676E61747572652074657374"
    },
    {
      "tag": "InitIndicator",
      "type": "Boolean",
      "value": true
    },
    {
      "tag": "FinalIndicator",
      "type": "Boolean",
      "value": true
    }
  ]
}
{
  "tag": "SignResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "9382bfec-bd6c-46ed-8f00-90b467f77a15"
    },
    {
      "tag": "SignatureData",
      "type": "ByteString",
      "value": "3A4B5C6D7E8F901234567890ABCDEF1234567890ABCDEF1234567890ABCDEF12"
    }
  ]
}

Example - Signature Verification

Verify a signature using the corresponding public key.

{
  "tag": "SignatureVerify",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "9382bfec-bd6c-46ed-8f00-90b467f77a15_pk"
    },
    {
      "tag": "CryptographicParameters",
      "type": "Structure",
      "value": [
        {
          "tag": "DigitalSignatureAlgorithm",
          "type": "Enumeration",
          "value": "SHA256WithRSAEncryption"
        }
      ]
    },
    {
      "tag": "Data",
      "type": "ByteString",
      "value": "48656C6C6F2C207369676E61747572652074657374"
    },
    {
      "tag": "SignatureData",
      "type": "ByteString",
      "value": "3A4B5C6D7E8F901234567890ABCDEF1234567890ABCDEF1234567890ABCDEF12"
    },
    {
      "tag": "InitIndicator",
      "type": "Boolean",
      "value": true
    },
    {
      "tag": "FinalIndicator",
      "type": "Boolean",
      "value": true
    }
  ]
}
{
  "tag": "SignatureVerifyResponse",
  "type": "Structure",
  "value": [
    {
      "tag": "UniqueIdentifier",
      "type": "TextString",
      "value": "9382bfec-bd6c-46ed-8f00-90b467f77a15_pk"
    },
    {
      "tag": "ValidityIndicator",
      "type": "Enumeration",
      "value": "Valid"
    }
  ]
}

Usage via HTTP API

To use Sign and SignatureVerify operations, send HTTP POST requests to the /kmip/2_1 endpoint:

# Sign operation example
curl -X POST -H "Content-Type: application/json" \
      -d '{
        "tag": "RequestMessage",
        "value": [
          {
            "tag": "RequestHeader",
            "value": [
              {
                "tag": "ProtocolVersion",
                "value": [
                  {"tag": "ProtocolVersionMajor", "type": "Integer", "value": 2},
                  {"tag": "ProtocolVersionMinor", "type": "Integer", "value": 1}
                ]
              },
              {"tag": "BatchCount", "type": "Integer", "value": 1}
            ]
          },
          {
            "tag": "BatchItem",
            "value": [
              {"tag": "Operation", "type": "Enumeration", "value": "Sign"},
              {
                "tag": "RequestPayload",
                "value": [
                  {"tag": "UniqueIdentifier", "type": "TextString", "value": "your-key-id"},
                  {"tag": "Data", "type": "ByteString", "value": "48656C6C6F"}
                ]
              }
            ]
          }
        ]
      }' \
      http://localhost:9998/kmip/2_1

Streaming Support

Both Sign and SignatureVerify operations support streaming for large data:

  • Set InitIndicator: true to start streaming
  • Use CorrelationValue to continue streaming sessions
  • Set FinalIndicator: true to complete the operation

KMS Performance Comparison

Versions: v5.24.0 Generated: 2026-07-09


Benchmark Environment

FieldValue
Date2026-07-09 13:28:17 UTC
Buildrelease / non-fips
DatabaseSQLite (temporary, single benchmark run)
CPUIntel(R) Core(TM) i9-14900T @ 3,302 MHz
CPU cores24 physical / 32 logical (HT)
RAM31.1 GB
OSUbuntu 24.04.4 LTS
Kernel6.8.0-134-generic

Load test parameters

ParameterValue
Modeall
Protocolsall
Measurement window20 s per concurrency level
Concurrency levels1,2,4,8,16
Warm-up5 s
Cooldown between levels2 s

CPU detail (lscpu)

Architecture:                            x86_64
CPU op-mode(s):                          32-bit, 64-bit
Address sizes:                           46 bits physical, 48 bits virtual
Byte Order:                              Little Endian
CPU(s):                                  32
On-line CPU(s) list:                     0-31
Vendor ID:                               GenuineIntel
Model name:                              Intel(R) Core(TM) i9-14900T
CPU family:                              6
Model:                                   183
Thread(s) per core:                      2
Core(s) per socket:                      24
Socket(s):                               1
Stepping:                                1
CPU(s) scaling MHz:                      33%
CPU max MHz:                             5500,0000
CPU min MHz:                             800,0000
BogoMIPS:                                2227,20
Flags:                                   fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf tsc_known_freq pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb ssbd ibrs ibpb stibp ibrs_enhanced tpr_shadow flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid rdseed adx smap clflushopt clwb intel_pt sha_ni xsaveopt xsavec xgetbv1 xsaves split_lock_detect user_shstk avx_vnni dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_epp hwp_pkg_req hfi vnmi umip pku ospke waitpkg gfni vaes vpclmulqdq tme rdpid movdiri movdir64b fsrm md_clear serialize pconfig arch_lbr ibt flush_l1d arch_capabilities ibpb_exit_to_user
Virtualization:                          VT-x
L1d cache:                               896 KiB (24 instances)
L1i cache:                               1,3 MiB (24 instances)
L2 cache:                                32 MiB (12 instances)
L3 cache:                                36 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-31
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Mitigation; Clear Register File
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; usercopy/swapgs barriers and __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; Enhanced / Automatic IBRS; IBPB conditional; PBRSB-eIBRS SW sequence; BHI BHI_DIS_S
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Mitigation; IBPB before exit to userspace

Protocols

The KMS server was exercised over three distinct wire protocols. Each benchmark column is labelled with the protocol name it used.

ProtocolTransportEncodingEndpointDescription
ttlv-jsonHTTP/1.1KMIP 2.1 JSON-TTLVPOST /kmip/2_1Primary interoperability protocol — any KMIP 2.1 compliant client can use it
ttlv-bytesHTTP/1.1KMIP 2.1 binary TTLVPOST /kmipBinary wire format; eliminates JSON parsing overhead — typically 10–30 % faster
joseHTTP/1.1JWE / JWS (JOSE)POST /v1/crypto/REST API for OAuth2/OIDC workloads that prefer JWA algorithm identifiers over KMIP

KMIP TTLV (Tag-Type-Length-Value) is the native encoding of the KMIP 2.1 standard (OASIS KMIP Spec v2.1, §9.1). The JSON variant wraps every field in a {"tag": …, "type": …, "value": …} JSON object and base64-encodes binary values. The binary variant uses a compact 8-byte fixed header (3-byte tag, 1-byte type, 4-byte length) per value, removing JSON tokenisation, base64, and UTF-8 overhead entirely.

JOSE (JSON Object Signing and Encryption, RFC 7516 / RFC 7515) exposes KMS key material through /v1/crypto/ REST endpoints. It is used by cloud integrations (Google CSE, Microsoft DKE, Azure EKM) and any workload that speaks JWA algorithm identifiers (A256GCM, RS256, ES384 …) rather than KMIP semantics.


Benchmark Methodology

Plaintext / payload sizes

All encrypt/decrypt benchmarks use a fixed-size random payload. Sizes represent a realistic key-wrapping or small-message encryption workload without introducing significant data-transfer overhead on a loopback connection.

Algorithm / categoryPlaintext sizeNotes
AES-GCM (128 / 192 / 256-bit key)64 bytesFIPS 140-3
AES-GCM-SIV (128 / 256-bit key)64 bytesNon-FIPS
AES-XTS (128 / 256-bit AES = 256 / 512-bit key)64 bytesFIPS 140-3; requires 16-byte IV
ChaCha20-Poly1305 (256-bit key)64 bytesNon-FIPS
ECIES — P-256 / P-384 / P-52164 bytesNon-FIPS; EC public-key encryption
Salsa Sealed Box (X25519)64 bytesNon-FIPS
Covercrypt (attribute-based encryption)64 bytesNon-FIPS
JOSE JWE — dir + AES-GCM (A128GCM / A192GCM / A256GCM)64 bytesSymmetric (direct key agreement)
JOSE JWE — RSA-OAEP + AES-GCM (2048 / 4096-bit)64 bytesAsymmetric (RSA-OAEP CEK wrapping)
RSA-OAEP (2048 / 3072 / 4096-bit)32 bytesLimited by RSA block size
RSA-PKCS#1 v1.5 (2048 / 3072 / 4096-bit)32 bytesNon-FIPS
RSA-AES Key Wrap — KWP (2048 / 3072 / 4096-bit)32 bytesFIPS 140-3
Sign / Verify — all algorithms32 bytesMessage is hashed internally
JOSE JWS / MAC32 bytes

Load test (ckms bench --load)

The load test sweeps a configurable list of concurrency levels. At each level N concurrent async tasks send pre-serialised requests in tight loops for a fixed measurement window (default: 20 s), preceded by a warm-up phase (default: 5 s) that is excluded from measurements. Pre-serialisation happens once at setup time and the same bytes are reused on every iteration, isolating server-side KMS latency from client-side encoding overhead. Recorded metrics per (protocol, operation, concurrency) triple:

  • Throughput — requests per second (req/s)
  • p50 / p95 / p99 — round-trip latency percentiles (ms)

Criterion micro-benchmarks (ckms bench)

Criterion (Rust, v0.5) measures the round-trip latency of a single request from the ckms client library through the KMS server and back over a loopback TCP connection. The server is started once and kept alive across all benchmarks in the suite. The reported value is the mean ± 95 % confidence interval over a configurable number of samples (preset quick: 3 s warm-up + 5 s measurement per benchmark).

Infrastructure note: Both test types use a local SQLite backend (temporary, discarded after the run). This isolates pure cryptographic and KMIP serialisation overhead from database I/O. Throughput figures will differ on a production deployment backed by PostgreSQL or Redis-Findex.


Load Tests

encrypt/aes-gcm

Concurrencyttlv-json (req/s)ttlv-bytes (req/s)jose (req/s)
11,65516,03635,238
28,73628,30362,117
415,91942,09790,314
824,44752,235105,688
1632,66158,49289,942

Throughput — encrypt/aes-gcm


sign-verify/ecdsa-p256

Concurrencyttlv-json (req/s)ttlv-bytes (req/s)jose (req/s)
11,9024,6014,840
23,5798,4218,899
47,21413,68314,240
812,59819,26020,329
1618,64626,44328,094

Throughput — sign-verify/ecdsa-p256


key-creation/aes-sym

Concurrencyttlv-json (req/s)
12,292
23,743
43,199
83,462
163,487

Throughput — key-creation/aes-sym


batch/aes-gcm-10

Concurrencyttlv-json (req/s)
11,631
22,924
44,767
86,072
167,932

Throughput — batch/aes-gcm-10


Criterion Benchmarks

Symmetric Encryption

Benchmarkttlv-jsonttlv-bytesjose
aes-gcm-siv/decrypt/12846.3 µs47.1 µs
aes-gcm-siv/decrypt/25645.1 µs38.1 µs
aes-gcm-siv/encrypt/12849.8 µs59.8 µs
aes-gcm-siv/encrypt/25642.6 µs45.6 µs
aes-gcm/decrypt/12875.3 µs47.8 µs32.5 µs
aes-gcm/decrypt/19241.4 µs53.1 µs31.6 µs
aes-gcm/decrypt/25646.2 µs61.4 µs30.6 µs
aes-gcm/encrypt/12838.5 µs47.4 µs37.3 µs
aes-gcm/encrypt/19251.9 µs56.2 µs32.4 µs
aes-gcm/encrypt/25658.3 µs49.8 µs32.2 µs
aes-xts/decrypt/12847.8 µs47.6 µs
aes-xts/decrypt/25646.2 µs53.8 µs
aes-xts/encrypt/12844.5 µs44.9 µs
aes-xts/encrypt/25645.9 µs46.5 µs
chacha20-poly1305/decrypt/25645.2 µs42.4 µs
chacha20-poly1305/encrypt/25646.5 µs39.1 µs
salsa-sealed-box/decrypt116.9 µs100.7 µs
salsa-sealed-box/encrypt167.5 µs458.73 ms

Asymmetric Encryption

Benchmarkttlv-jsonttlv-bytesjose
covercrypt/decrypt253.6 µs232.8 µs
covercrypt/encrypt287.7 µs536.10 ms
ecies/decrypt/P-256126.5 µs109.5 µs
ecies/decrypt/P-3841.03 ms947.9 µs
ecies/decrypt/P-5212.29 ms
ecies/encrypt/P-256194.6 µs467.52 ms
ecies/encrypt/P-3841.09 ms536.21 ms
ecies/encrypt/P-5212.40 ms477.34 ms
rsa-aes-kwp/decrypt/4096174.76 ms168.30 ms
rsa-aes-kwp/encrypt/4096177.2 µs531.17 ms
rsa-oaep/decrypt/204823.02 ms
rsa-oaep/decrypt/4096178.91 ms167.20 ms167.28 ms
rsa-oaep/encrypt/204893.5 µs
rsa-oaep/encrypt/4096164.6 µs531.28 ms143.7 µs
rsa-pkcs1v15/decrypt/4096177.32 ms167.42 ms
rsa-pkcs1v15/encrypt/4096155.7 µs492.94 ms

Key Encapsulation (KEM)

Benchmarkttlv-jsonttlv-bytes
configurable/decapsulate/ML-KEM-51269.1 µs57.9 µs
configurable/decapsulate/ML-KEM-512/P-25663.4 µs
configurable/decapsulate/ML-KEM-76872.4 µs66.4 µs
configurable/decapsulate/ML-KEM-768/P-25677.8 µs
configurable/encapsulate/ML-KEM-512147.9 µs469.28 ms
configurable/encapsulate/ML-KEM-512/P-256308.0 µs527.73 ms
configurable/encapsulate/ML-KEM-768167.7 µs462.83 ms
configurable/encapsulate/ML-KEM-768/P-256370.7 µs
pqc/decapsulate/ML-KEM-1024129.6 µs
pqc/decapsulate/ML-KEM-51294.5 µs88.0 µs
pqc/decapsulate/ML-KEM-768108.6 µs91.9 µs
pqc/decapsulate/X25519MLKEM768182.9 µs
pqc/encapsulate/ML-KEM-1024194.5 µs480.22 ms
pqc/encapsulate/ML-KEM-512196.6 µs453.94 ms
pqc/encapsulate/ML-KEM-768230.6 µs531.89 ms
pqc/encapsulate/X25519MLKEM768244.3 µs

Key Creation

Benchmarkttlv-json
EC/ES256
EC/ES384
RSA/2048
aes-gcm/oct/128
aes-gcm/oct/256
covercrypt/master-keypair706.9 µs
ec/ed25519368.5 µs
ec/ed448490.0 µs
ec/p256352.0 µs
ec/p384822.5 µs
ec/p5211.64 ms
ec/secp256k1574.3 µs
kem/ML-KEM-512371.8 µs
kem/ML-KEM-512/P-256499.2 µs
kem/ML-KEM-512/X25519338.1 µs
kem/ML-KEM-768425.3 µs
kem/ML-KEM-768/P-256465.5 µs
kem/ML-KEM-768/X25519378.7 µs
pqc/ML-DSA-44500.6 µs
pqc/ML-DSA-65532.0 µs
pqc/ML-DSA-87581.3 µs
pqc/ML-KEM-1024418.3 µs
pqc/ML-KEM-512385.9 µs
pqc/ML-KEM-768410.1 µs
pqc/X25519MLKEM768300.2 µs
pqc/X448MLKEM1024436.4 µs
rsa/rsa-4096515.10 ms
symmetric/aes-128237.3 µs
symmetric/aes-192224.6 µs
symmetric/aes-256269.6 µs
symmetric/chacha20-256242.2 µs

Sign / Verify

Benchmarkttlv-jsonttlv-bytesjose
ecdsa-p256/sign263.6 µs234.9 µs231.5 µs
ecdsa-p256/verify557.86 ms1.01 s1.18 s
ecdsa-p384/sign1.17 ms966.6 µs998.1 µs
ecdsa-p384/verify554.38 ms1.12 s1.17 s
ecdsa-p521/sign2.50 ms2.62 ms
ecdsa-p521/verify548.90 ms1.10 s
ecdsa-secp256k1/sign351.7 µs356.5 µs
ecdsa-secp256k1/verify474.97 ms1.02 s
eddsa-ed25519/sign87.0 µs92.0 µs87.3 µs
eddsa-ed25519/verify466.31 ms1.03 s1.03 s
eddsa-ed448/sign303.4 µs290.9 µs
eddsa-ed448/verify530.69 ms1.17 s
ml-dsa/sign/44511.8 µs458.9 µs
ml-dsa/sign/65814.0 µs766.5 µs
ml-dsa/sign/871.00 ms
ml-dsa/verify/44536.02 ms1.16 s
ml-dsa/verify/65470.30 ms1.03 s
ml-dsa/verify/87480.09 ms
rsa-pkcs1v15/sign23.42 ms
rsa-pkcs1v15/verify1.16 s
rsa-pss/sign24.11 ms
rsa-pss/sign/4096181.90 ms172.64 ms
rsa-pss/verify1.02 s
rsa-pss/verify/4096532.30 ms1.18 s
slh-dsa/sign/SHA2-128f10.41 ms9.52 ms
slh-dsa/sign/SHA2-256f37.66 ms34.76 ms
slh-dsa/sign/SHAKE-128f26.75 ms
slh-dsa/verify/SHA2-128f543.67 ms1.02 s
slh-dsa/verify/SHA2-256f487.30 ms1.18 s
slh-dsa/verify/SHAKE-128f477.53 ms

CPU Scaling & Flamegraph Analysis

This page documents the multi-CPU scaling characteristics of the Cosmian KMS and explains how to reproduce the results and generate flamegraphs yourself.

Why this matters

A KMS deployed in production must handle concurrent cryptographic requests from many clients at the same time. To justify a multi-core deployment (and to rule out serialisation bottlenecks such as global mutexes or single-threaded database queues), we measure:

  1. Throughput scaling – how req/s grows as the number of actix-web worker threads increases from 1 → 2 → 4 → 8.
  2. CPU hotspot profile – a flamegraph confirming that the dominant cost is the cryptographic operation itself, not infrastructure overhead (routing, serialisation, database, locks).

Methodology

Server worker count

The KMS exposes a --server-workers N / KMS_SERVER_WORKERS configuration option that pins the number of actix-web OS threads. Setting it explicitly makes the bench deterministic regardless of the host's logical CPU count.

# kms.toml  – leave unset to default to one thread per logical CPU
# server_workers = 8

Benchmark design

The http_throughput Criterion bench (located at crate/test_kms_server/benches/http_throughput.rs) exercises three representative KMIP operations:

OperationWhy chosen
AES-256-GCM encryptLightweight, high-frequency – reveals OS-thread dispatch overhead
RSA-2048 OAEP decryptCPU-heavy asymmetric – shows scaling of the OpenSSL thread pool
ECDSA P-256 signCPU-heavy, short messages – reveals lock contention on key material

For each worker count in {1, 2, 4, 8}:

  1. A fresh in-process KMS is started with server_workers = N and SQLite on /dev/shm (tmpfs).
  2. Cryptographic keys are pre-created (not timed).
  3. 16 concurrent reqwest HTTP tasks are dispatched per Criterion iteration via iter_custom + join_all, so the server is always saturated.
  4. Criterion reports wall-clock throughput in elements/s (= concurrent requests per second).

Flamegraph generation

Flamegraphs are recorded with cargo-flamegraph (Linux perf back-end) and rendered to PNG with rsvg-convert (part of librsvg2-bin). They are generated for all three operations above at 1, 2, 4, and 8 workers.

Why rsvg-convert and not convert/ImageMagick? ImageMagick delegates SVG rasterisation to rsvg-convert internally; if that binary is missing, convert silently falls back to a blank canvas, producing fully-black PNGs. Always invoke rsvg-convert directly.

To reproduce locally (Linux only, requires perf and rsvg-convert):

# Install cargo-flamegraph and rsvg-convert once
cargo install flamegraph --locked
sudo apt-get install -y librsvg2-bin

# Allow perf for unprivileged processes (revert after benchmarking)
echo -1 | sudo tee /proc/sys/kernel/perf_event_paranoid
echo 0   | sudo tee /proc/sys/kernel/kptr_restrict

# Build and profile – SVG lands next to this file
CARGO_PROFILE_BENCH_DEBUG=true \
cargo flamegraph \
  --bench http_throughput \
  -p test_kms_server \
  --features non-fips \
  --output documentation/docs/benchmarks/flamegraph/ecdsa_sign/w8/ecdsa_sign_w8.svg \
  -- \
  --bench \
  "ecdsa_sign/w8" \
  --profile-time 15

# Render the interactive SVG to a static PNG for the docs page
rsvg-convert --output documentation/docs/benchmarks/flamegraph/ecdsa_sign/w8/ecdsa_sign_w8.png \
  documentation/docs/benchmarks/flamegraph/ecdsa_sign/w8/ecdsa_sign_w8.svg

Running the throughput benchmark

# Non-FIPS mode (all three operations)
cargo bench --bench http_throughput -p test_kms_server --features non-fips

# FIPS mode
cargo bench --bench http_throughput -p test_kms_server

Criterion writes an HTML report to target/criterion/kms_bench/ (also copied to flamegraph/ here).

Interpreting the results

What near-linear scaling looks like

If the KMS scales well, throughput doubles when the worker count doubles:

WorkersExpected throughput (relative)
11× baseline
2~1.9×
4~3.6×
8~6–7× (NUMA / HT effects)

Sub-linear but monotonically increasing throughput is normal and expected:

  • HTTP keep-alive and connection pooling overhead does not scale perfectly.
  • SQLite WAL mode serialises writes but allows concurrent reads.
  • OpenSSL's FIPS provider has per-context locking.

Flat or decreasing throughput would indicate a bottleneck worth investigating.

Reading a flamegraph

A flamegraph shows where CPU time is spent, stacked by call depth.

  • Wide frames at the top = the dominant cost; you want to see openssl / ring / cosmian_kms_crypto here.
  • Wide frames in the middle = infrastructure cost; actix-rt, tokio, serde_json, sqlx are expected but should be narrow compared to crypto.
  • Wide frames at the bottom = system calls; syscall, epoll_wait width grows with I/O wait (not CPU saturation).

If std::sync::Mutex or parking_lot::Mutex frames appear wide, that signals lock contention and is a regression signal worth investigating.

CI integration

The flamegraph.yml GitHub Actions workflow runs:

  • On demand via workflow_dispatch (configurable worker counts and profile time).
  • Weekly (Monday 03:00 UTC) against the default branch.
  • On pull requests that touch http_config.rs, start_kms_server.rs, or the bench itself.

Artifacts uploaded per run:

ArtifactContents
criterion-http-throughput-<run_id>Criterion HTML report with throughput charts
flamegraph-svgs-<run_id>SVG flamegraphs per worker count
http-throughput-output-<run_id>Raw bench output (bencher format)

Results

Generated: 2026-07-02 09:27:46 UTC KMS version: 5.24.0 (commit: a099a06e1) Variant: non-fips

Machine Info

Architecture:                            x86_64
CPU op-mode(s):                          32-bit, 64-bit
Address sizes:                           46 bits physical, 48 bits virtual
Byte Order:                              Little Endian
CPU(s):                                  32
On-line CPU(s) list:                     0-31
Vendor ID:                               GenuineIntel
Model name:                              Intel(R) Core(TM) i9-14900T
CPU family:                              6
Model:                                   183
Thread(s) per core:                      2
Core(s) per socket:                      24
Socket(s):                               1
Stepping:                                1
CPU(s) scaling MHz:                      32%
CPU max MHz:                             5500,0000
CPU min MHz:                             800,0000
BogoMIPS:                                2227,20
Flags:                                   fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf tsc_known_freq pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb ssbd ibrs ibpb stibp ibrs_enhanced tpr_shadow flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid rdseed adx smap clflushopt clwb intel_pt sha_ni xsaveopt xsavec xgetbv1 xsaves split_lock_detect user_shstk avx_vnni dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_epp hwp_pkg_req hfi vnmi umip pku ospke waitpkg gfni vaes vpclmulqdq tme rdpid movdiri movdir64b fsrm md_clear serialize pconfig arch_lbr ibt flush_l1d arch_capabilities ibpb_exit_to_user
Virtualization:                          VT-x
L1d cache:                               896 KiB (24 instances)
L1i cache:                               1,3 MiB (24 instances)
L2 cache:                                32 MiB (12 instances)
L3 cache:                                36 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-31
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Mitigation; Clear Register File
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; usercopy/swapgs barriers and __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; Enhanced / Automatic IBRS; IBPB conditional; PBRSB-eIBRS SW sequence; BHI BHI_DIS_S
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Mitigation; IBPB before exit to userspace
/proc/cpuinfo (first core)
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model  : 183
model name : Intel(R) Core(TM) i9-14900T
stepping : 1
microcode : 0x133
cpu MHz  : 3453.279
cache size : 36864 KB
physical id : 0
siblings : 32
core id  : 0
cpu cores : 24
apicid  : 0
initial apicid : 0
fpu  : yes
fpu_exception : yes
cpuid level : 32
wp  : yes
flags  : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf tsc_known_freq pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb ssbd ibrs ibpb stibp ibrs_enhanced tpr_shadow flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid rdseed adx smap clflushopt clwb intel_pt sha_ni xsaveopt xsavec xgetbv1 xsaves split_lock_detect user_shstk avx_vnni dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_epp hwp_pkg_req hfi vnmi umip pku ospke waitpkg gfni vaes vpclmulqdq tme rdpid movdiri movdir64b fsrm md_clear serialize pconfig arch_lbr ibt flush_l1d arch_capabilities ibpb_exit_to_user
vmx flags : vnmi preemption_timer posted_intr invvpid ept_x_only ept_ad ept_1gb flexpriority apicv tsc_offset vtpr mtf vapic ept vpid unrestricted_guest vapic_reg vid ple shadow_vmcs ept_mode_based_exec tsc_scaling usr_wait_pause
bugs  : spectre_v1 spectre_v2 spec_store_bypass swapgs eibrs_pbrsb rfds bhi vmscape
bogomips : 2227.20
clflush size : 64
cache_alignment : 64
address sizes : 46 bits physical, 48 bits virtual
power management:

processor : 1
vendor_id : GenuineIntel
cpu family : 6
model  : 183
model name : Intel(R) Core(TM) i9-14900T
stepping : 1
microcode : 0x133
cpu MHz  : 800.000
cache size : 36864 KB
physical id : 0
siblings : 32
core id  : 0
cpu cores : 24
apicid  : 1
initial apicid : 1
fpu  : yes
fpu_exception : yes
cpuid level : 32
wp  : yes
flags  : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc art arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf tsc_known_freq pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch cpuid_fault epb ssbd ibrs ibpb stibp ibrs_enhanced tpr_shadow flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid rdseed adx smap clflushopt clwb intel_pt sha_ni xsaveopt xsavec xgetbv1 xsaves split_lock_detect user_shstk avx_vnni dtherm ida arat pln pts hwp hwp_notify hwp_act_window hwp_epp hwp_pkg_req hfi vnmi umip pku ospke waitpkg gfni vaes vpclmulqdq tme rdpid movdiri movdir64b fsrm md_clear serialize pconfig arch_lbr ibt flush_l1d arch_capabilities ibpb_exit_to_user
vmx flags : vnmi preemption_timer posted_intr invvpid ept_x_only ept_ad ept_1gb flexpriority apicv tsc_offset vtpr mtf vapic ept vpid unrestricted_guest vapic_reg vid ple shadow_vmcs ept_mode_based_exec tsc_scaling usr_wait_pause
bugs  : spectre_v1 spectre_v2 spec_store_bypass swapgs eibrs_pbrsb rfds bhi vmscape
bogomips : 2227.20
clflush size : 64
cache_alignment : 64
address sizes : 46 bits physical, 48 bits virtual
power management:

Throughput Results

Benchmark: http_throughput Criterion bench, 16 concurrent tasks per iteration, worker sweep: 1 2 4 8.

Benchmark~req/s
kms_bench/aes_gcm_enc/w160964
kms_bench/rsa_oaep_dec/w139
kms_bench/ecdsa_sign/w14962
kms_bench/aes_gcm_enc/w234604
kms_bench/rsa_oaep_dec/w274
kms_bench/ecdsa_sign/w27575
kms_bench/aes_gcm_enc/w423583
kms_bench/rsa_oaep_dec/w4126
kms_bench/ecdsa_sign/w48273
kms_bench/aes_gcm_enc/w882561
kms_bench/rsa_oaep_dec/w8153
kms_bench/ecdsa_sign/w813805

How to Reproduce

# Full run (throughput + flamegraphs for the default worker sweep)
mise bench:flamegraph

# Throughput only (no perf/flamegraph)
mise bench:flamegraph --skip-flamegraph

# Flamegraph for 8 workers only (skip throughput bench)
mise bench:flamegraph --workers 8 --skip-throughput

# Choose variant
mise bench:flamegraph --variant non-fips

Flamegraphs

Generated by cargo-flamegraph / Linux perf during a 15-second profiling window, for each operation and worker count. Click a PNG's caption link to open the interactive SVG (zoom, search) in a browser.

aes_gcm_enc

Flamegraphs not yet regenerated. Criterion throughput data is available in the Criterion report. Run mise bench:flamegraph to populate flamegraphs here.

rsa_oaep_dec

Flamegraphs not yet regenerated. Criterion throughput data is available in the Criterion report. Run mise bench:flamegraph to populate flamegraphs here.

ecdsa_sign

1 worker(s)

flamegraph ecdsa_sign 1 workers

Open interactive SVG

2 worker(s)

flamegraph ecdsa_sign 2 workers

Open interactive SVG

4 worker(s)

flamegraph ecdsa_sign 4 workers

Open interactive SVG

8 worker(s)

flamegraph ecdsa_sign 8 workers

Open interactive SVG

Criterion Report

The full interactive Criterion HTML report is in flamegraph/. It is structured in four levels:

PathContents
flamegraph/report/Group overview — all 3 operations × 4 worker counts
flamegraph/<operation>/report/Per-operation summary — throughput curve across worker counts
flamegraph/<operation>/<workers>/report/Individual benchmark — timing distribution, regression analysis
flamegraph/<operation>/<workers>/Flamegraph SVG + PNG — CPU hotspot profile for this operation at this concurrency

Each <operation>/<workers>/ directory contains:

  • base/ / new/ — Criterion raw measurement data (JSON)
  • report/ — Criterion HTML report (timing distribution, regression analysis)
  • *.svg / *.png — flamegraph (generated by cargo-flamegraph)

Cosmian Command Line Interface (CLI)

KMS CLI is the Command Line Interface to drive KMS.

KMS CLI provides a powerful interface to manage and secure your cryptographic keys and secrets using the Cosmian Key Management System KMS. The KMS offers a high-performance, scalable solution with unique features such as confidential execution in zero-trust environments, compliance with KMIP 2.1, and support for various cryptographic algorithms and protocols.

Beyond the CLI interface, Cosmian also provides a PKCS#11 library (libcosmian_pkcs11.so) that enables seamless integration with existing cryptographic infrastructure. This library acts as a bridge between applications requiring PKCS#11 interfaces and the Eviden KMS, providing:

  • Database Encryption Support: Integration with Oracle Database Transparent Data Encryption (TDE) for automatic encryption of data at rest, either through Oracle Key Vault or direct HSM communication
  • Disk Encryption Support: Compatible with popular disk encryption solutions including VeraCrypt, LUKS, and Cryhod for protecting data on storage devices

The PKCS#11 library enables organizations to leverage Cosmian's advanced cryptographic capabilities while maintaining compatibility with their existing security infrastructure and workflows.

By leveraging KMS CLI, users can seamlessly integrate advanced cryptographic functionalities and secure search capabilities into their applications, enhancing data security and privacy.

!!! info Download cosmian

Please download the latest versions for your Operating System from
the [Cosmian public packages repository](https://package.cosmian.com/kms/5.25.0/)
See below for installation instructions.

Download package and install it:

sudo apt update && sudo apt install -y wget
wget https://package.cosmian.com/kms/5.25.0/deb/amd64/non-fips/static/cosmian-kms-cli-non-fips-static-openssl_5.25.0_amd64.deb
sudo apt install ./cosmian-kms-cli-non-fips-static-openssl_5.25.0_amd64.deb
ckms --version

Download package and install it:

sudo apt update && sudo apt install -y wget
wget https://package.cosmian.com/kms/5.25.0/deb/arm64/non-fips/static/cosmian-kms-cli-non-fips-static-openssl_5.25.0_arm64.deb
sudo apt install ./cosmian-kms-cli-non-fips-static-openssl_5.25.0_arm64.deb
ckms --version

Download package and install it:

sudo dnf update && sudo dnf install -y wget
wget https://package.cosmian.com/kms/5.25.0/rpm/amd64/non-fips/static/cosmian-kms-cli-non-fips-static-openssl_5.25.0_x86_64.rpm
sudo dnf install ./cosmian-kms-cli-non-fips-static-openssl_5.25.0_x86_64.rpm
ckms --version

Download package and install it:

sudo dnf update && sudo dnf install -y wget
wget https://package.cosmian.com/kms/5.25.0/rpm/arm64/non-fips/static/cosmian-kms-cli-non-fips-static-openssl_5.25.0_aarch64.rpm
sudo dnf install ./cosmian-kms-cli-non-fips-static-openssl_5.25.0_aarch64.rpm
ckms --version

Download the DMG installer and install it:

wget https://package.cosmian.com/kms/5.25.0/dmg/arm64/non-fips/static/cosmian-kms-cli-non-fips-static-openssl-5.25.0_arm64.dmg
sudo hdiutil attach cosmian-kms-cli-non-fips-static-openssl-5.25.0_arm64.dmg
sudo installer -pkg /Volumes/cosmian-kms-cli/cosmian-kms-cli.pkg -target /
hdiutil detach /Volumes/cosmian-kms-cli
ckms --version

On Windows, download the installer:

 https://package.cosmian.com/kms/5.25.0/windows/x86_64/non-fips/static-openssl/cosmian-kms-cli-non-fips-static-openssl_5.25.0_x86_64.exe

Run the installer and add the installation directory to your PATH, then run:

ckms --version

Configuration

To communicate with the KMS, the clients ckms expect the same configuration file. Please read the configuration section.

Usage

Command Line Interface used to manage the Cosmian KMS server.

If any assistance is needed, please either visit the Cosmian technical documentation at https://docs.cosmian.com
or contact the Cosmian support team on Discord https://discord.com/invite/7kPMNtHpnz


Usage: ckms [OPTIONS] <COMMAND>

Commands:
  access-rights  Manage the users' access rights to the cryptographic objects
  attributes     Get/Set/Delete/Modify the KMIP object attributes
  azure          Support for Azure specific interactions
  aws            Support for AWS specific interactions
  bench          Run benchmarks using criterion for statistical analysis.
  cc             Manage Covercrypt keys and policies. Rotate attributes. Encrypt and decrypt data
  fpe            Manage FPE keys and perform FPE encryption/decryption through KMIP Encrypt/Decrypt
  pqc            Manage post-quantum keys (ML-KEM, ML-DSA, Hybrid KEM, SLH-DSA). Encapsulate, decapsulate, sign, and verify
  tokenize       Anonymization utilities: hash, noise, word masking, pattern masking, aggregation, and scaling
  certificates   Manage certificates. Create, import, destroy and revoke. Encrypt and decrypt data
  cng            Manage the Windows CNG Key Storage Provider (KSP)
  derive-key     Derive a new key from an existing key
  ec             Manage elliptic curve keys. Encrypt and decrypt data using ECIES
  google         Manage google elements. Handle key pairs and identities from Gmail API
  locate         Locate cryptographic objects inside the KMS
  login          Login to the Identity Provider of the KMS server using the `OAuth2` authorization code flow.
  logout         Logout from the Identity Provider
  hash           Hash arbitrary data.
  mac            MAC utilities: compute or verify a MAC value.
  rng            RNG utilities: retrieve random bytes or seed RNG
  server         Server-related commands
  rsa            Manage RSA keys. Encrypt and decrypt data using RSA keys
  opaque-object  Create, import, export, revoke and destroy Opaque Objects
  pkcs11         Verify PKCS#11 shared library integration
  secret-data    Create, import, export and destroy secret data
  sym            Manage symmetric keys. Encrypt and decrypt data
  markdown       Regenerate the CLI documentation in Markdown format
  configure      Configure the KMS CLI (create ckms.toml)
  help           Print this message or the help of the given subcommand(s)

Options:
  -c, --conf-path <CONF_PATH>
          Configuration file location

          This is an alternative to the env variable `CKMS_CONF_PATH`. Takes precedence over `CKMS_CONF_PATH` env variable.

          [env: CKMS_CONF_PATH=]

      --url <URL>
          The URL of the KMS

          [env: KMS_DEFAULT_URL=]

      --print-json
          Output the KMS JSON KMIP request and response. This is useful to understand JSON POST requests and responses required to programmatically call the KMS on the `/kmip/2_1` endpoint

      --accept-invalid-certs
          Allow to connect using a self-signed cert or untrusted cert chain

          `accept_invalid_certs` is useful if the CLI needs to connect to an HTTPS KMS server running an invalid or insecure SSL certificate

  -H, --header <NAME: VALUE>
          Add a custom HTTP header to every request sent to the KMS server.

          The header must be specified in `"Name: Value"` format, matching the
          curl `-H` / `--header` convention. This option may be repeated to add
          multiple headers.

          The environment variable `CLI_HEADER` may also be used; separate
          multiple headers with a newline character.

          Example: `--header "cf-access-token: <token>"`

          [env: CLI_HEADER=]

      --proxy-url <PROXY_URL>
          The proxy URL:
            - e.g., `https://secure.example` for an HTTP proxy
            - e.g., `socks5://192.168.1.1:9000` for a SOCKS proxy

          [env: CLI_PROXY_URL=]

      --proxy-basic-auth-username <PROXY_BASIC_AUTH_USERNAME>
          Set the Proxy-Authorization header username using Basic auth.

          [env: CLI_PROXY_BASIC_AUTH_USERNAME=]

      --proxy-basic-auth-password <PROXY_BASIC_AUTH_PASSWORD>
          Set the Proxy-Authorization header password using Basic auth.

          [env: CLI_PROXY_BASIC_AUTH_PASSWORD=]

      --proxy-custom-auth-header <PROXY_CUSTOM_AUTH_HEADER>
          Set the Proxy-Authorization header to a specified value.

          [env: CLI_PROXY_CUSTOM_AUTH_HEADER=]

      --proxy-exclusion-list <PROXY_EXCLUSION_LIST>
          The No Proxy exclusion list to this Proxy

          [env: CLI_PROXY_NO_PROXY=]

  -h, --help
          Print help (see a summary with '-h')

  -V, --version
          Print version

Web UI

The KMS server ships with a built-in browser-based client that covers the same operations as the ckms CLI. It is available at:

https://YOUR_KMS_URL/ui

No installation is required — the UI is served directly by the KMS server.

Authentication is handled automatically: the UI detects the server's configured method and adapts its login flow accordingly:

  • OIDC / JWT: a LOGIN button redirects to the identity provider.
  • mTLS (client certificate): the browser negotiates the TLS handshake using a certificate installed in the system or browser store. No extra configuration is needed on the client side.
  • No authentication: direct access, with a warning banner indicating that the server is unsecured.

For server-side configuration and browser certificate installation steps, see the KMS User Interface configuration guide.

Download package and install it:

sudo apt update && sudo apt install -y wget
wget https://package.cosmian.com/kms/5.25.0/deb/amd64/non-fips/static/cosmian-kms-cli-non-fips-static-openssl_5.25.0_amd64.deb
sudo apt install ./cosmian-kms-cli-non-fips-static-openssl_5.25.0_amd64.deb
ckms --version

Download package and install it:

sudo apt update && sudo apt install -y wget
wget https://package.cosmian.com/kms/5.25.0/deb/arm64/non-fips/static/cosmian-kms-cli-non-fips-static-openssl_5.25.0_arm64.deb
sudo apt install ./cosmian-kms-cli-non-fips-static-openssl_5.25.0_arm64.deb
ckms --version

Download package and install it:

sudo dnf update && sudo dnf install -y wget
wget https://package.cosmian.com/kms/5.25.0/rpm/amd64/non-fips/static/cosmian-kms-cli-non-fips-static-openssl_5.25.0_x86_64.rpm
sudo dnf install ./cosmian-kms-cli-non-fips-static-openssl_5.25.0_x86_64.rpm
ckms --version

Download package and install it:

sudo dnf update && sudo dnf install -y wget
wget https://package.cosmian.com/kms/5.25.0/rpm/arm64/non-fips/static/cosmian-kms-cli-non-fips-static-openssl_5.25.0_aarch64.rpm
sudo dnf install ./cosmian-kms-cli-non-fips-static-openssl_5.25.0_aarch64.rpm
ckms --version

Download the DMG installer and install it:

wget https://package.cosmian.com/kms/5.25.0/dmg/arm64/non-fips/static/cosmian-kms-cli-non-fips-static-openssl-5.25.0_arm64.dmg
sudo hdiutil attach cosmian-kms-cli-non-fips-static-openssl-5.25.0_arm64.dmg
sudo installer -pkg /Volumes/cosmian-kms-cli/cosmian-kms-cli.pkg -target /
hdiutil detach /Volumes/cosmian-kms-cli
ckms --version

On Windows, download the installer:

 https://package.cosmian.com/kms/5.25.0/windows/x86_64/non-fips/static-openssl/cosmian-kms-cli-non-fips-static-openssl_5.25.0_x86_64.exe

Run the installer and add the installation directory to your PATH, then run:

ckms --version

CLI Authentication

This guide explains how to configure authentication for the KMS CLI when connecting to the KMS.

Configuration File

The CLI reads its configuration from a TOML file:

  • Default location: ~/.cosmian/ckms.toml
  • Alternative location: Set the CKMS_CONF environment variable

A basic configuration file is created automatically on first use:

[http_config]
server_url = "http://0.0.0.0:9998"

Authentication Methods

The CLI supports multiple authentication methods for the KMS:

MethodConfiguration ElementsUse Case
None (Default)Only server_urlDevelopment environments
Access Tokenaccess_tokenSimple API token authentication
TLS Client Certificate (PEM)tls_client_pem_cert_path, tls_client_pem_key_pathCertificate-based auth — FIPS-compatible
TLS Client Certificate (PKCS#12)tls_client_pkcs12_path, tls_client_pkcs12_passwordCertificate-based auth — non-FIPS only
OAuth2/OIDCoauth2_conf sectionSSO with identity providers
Database Secretdatabase_secretEncrypted database access

Authenticating Using a Bearer / Access Token

When the KMS server is configured with JWT/OIDC authentication, the CLI can authenticate using a static bearer token (e.g., a short-lived JWT obtained from your identity provider) set directly in the configuration file.

[http_config]
server_url = "https://kms.example.com:9998"

# JWT bearer token sent in the Authorization: Bearer <token> header.
# Obtain from your identity provider (Google, Azure AD, Keycloak, …) or
# from ckms login after configuring the oauth2_conf section.
access_token = "<JWT_BEARER_TOKEN>"

See test_data/configs/ckms_jwt.toml for a full example combining access_token, oauth2_conf, and database_secret.

When the server enforces an API token (a symmetric key registered server-side via api_token_id), set the same value as access_token in the CLI config — the CLI sends it as a bearer token and the server validates it against the registered key ID.

Authenticating Using TLS Client Certificates

When the KMS server is configured with mutual TLS (mTLS), ckms must present a client certificate. Two formats are supported.

Provide the certificate and private key as separate PEM files (.crt/.pem and .key/.pem). This format works in both FIPS and non-FIPS builds.

[http_config]
server_url = "https://kms.example.com:9998"
accept_invalid_certs = false

# Path to the client certificate in PEM format (.crt or .pem).
# The file may contain a single leaf certificate or a full chain (leaf + intermediates).
tls_client_pem_cert_path = "/path/to/client.crt"

# Path to the client private key in PEM format (.key or .pem).
# Supported formats: PKCS#8 (-----BEGIN PRIVATE KEY-----) and traditional RSA/EC.
tls_client_pem_key_path = "/path/to/client.key"

Full example: test_data/configs/client/pem_cert_auth.toml

Combined with a bearer token (multi-factor authentication):

[http_config]
server_url = "https://kms.example.com:9998"
accept_invalid_certs = false
tls_client_pem_cert_path = "/path/to/client.crt"
tls_client_pem_key_path = "/path/to/client.key"

# JWT bearer token sent in the Authorization: Bearer <token> header.
# Obtain from your identity provider (Google, Azure AD, Keycloak, …).
access_token = "<JWT_BEARER_TOKEN>"

Full example: test_data/configs/client/pem_cert_and_token_auth.toml

PKCS#12 format (non-FIPS only)

Provide the certificate and private key bundled in a single PKCS#12 file (.p12).

[http_config]
server_url = "https://kms.example.com:9998"
accept_invalid_certs = false

# Path to the PKCS#12 bundle containing the client certificate and private key.
tls_client_pkcs12_path = "/path/to/client.p12"

# Password to decrypt the PKCS#12 bundle.
# Leave absent or set to "" if the bundle has no password.
tls_client_pkcs12_password = "changeit"

Full example: test_data/configs/client/pkcs12_cert_auth.toml

Using the ckms configure wizard

Run ckms configure and choose the certificate format from the interactive menu:

Authentication method
  None
  Bearer token
> Client certificate (PEM)             ← FIPS-compatible, recommended
  Client certificate (PKCS#12)         ← non-FIPS only
  Both (PEM cert + token)
  Both (PKCS#12 cert + token)

The wizard prompts for the certificate and key paths (PEM) or the bundle path and password (PKCS#12) and writes the result to the active configuration profile.

The KMS server authenticates the user using the Common Name (CN) field of the client certificate's subject (e.g. CN=john.doe@example.com becomes the username).

Converting a PKCS#12 bundle to PEM

# Extract the certificate
openssl pkcs12 -in client.p12 -clcerts -nokeys -out client.crt
# Extract the private key (enter the PKCS#12 password when prompted)
openssl pkcs12 -in client.p12 -nocerts -nodes -out client.key

Common Configuration Options

Each product has its own http_config section with these options:

OptionDescriptionRequired
server_urlURL of the serverYes
accept_invalid_certsAccept self-signed certificates (set to "true")No
verified_certPEM certificate for pinningNo

For KMS, you can also configure Gmail API access for S/MIME operations - see the S/MIME Gmail service account configuration.

Quick Configuration Examples

KMS with No Authentication

[http_config]
server_url = "http://127.0.0.1:9998"

OAuth2/OIDC Authentication

Basic Configuration

To authenticate using OAuth2/OIDC:

  1. Configure the oauth2_conf section in your TOML file
  2. Run ckms login to initiate authentication
  3. Use ckms logout to clear the token

The oauth2_conf section requires:

[http_config.oauth2_conf]
client_id = "your-client-id"          # Required
authorize_url = "https://idp.example.com/authorize"  # Required
token_url = "https://idp.example.com/token"          # Required
scopes = ["openid", "email"]          # Recommended
client_secret = "your-client-secret"  # Optional with PKCE

PKCE (Proof Key for Code Exchange) enhances security by eliminating the need for client secrets. To use PKCE:

  1. Configure OAuth2 in your TOML file but omit the client_secret field
  2. Ensure your identity provider supports PKCE
[http_config.oauth2_conf]
client_id = "your-client-id"
authorize_url = "https://idp.example.com/authorize"
token_url = "https://idp.example.com/token"
scopes = ["openid", "email"]
# No client_secret needed with PKCE

PKCE is recommended for:

  • CLI tools
  • Desktop applications
  • Mobile applications
  • Any client that cannot securely store secrets

Provider-Specific Examples

Microsoft Entra ID (Azure AD) with PKCE

[http_config.oauth2_conf]
client_id = "f052524e-7518-40e7-2579-219c0b48b125"
authorize_url = "https://login.microsoftonline.com/612da4de-35c0-42de-ba56-174c4e562c96/oauth2/authorize"
token_url = "https://login.microsoftonline.com/612da4de-35c0-42de-f3c6-174b69062c96/oauth2/token"
scopes = ["email", "openid"]
# No client_secret needed with PKCE

Important: In Entra ID, configure the redirect URL (http://localhost:17899/authorization) as Native/Desktop application type.

Auth0 with PKCE

[http_config.oauth2_conf]
client_id = "OUfH4FuzDAW99Ck3R4Rb7ROziOZEalIH"
authorize_url = "https://acme.eu.auth0.com/authorize"
token_url = "https://acme.eu.auth0.com/oauth/token"
scopes = ["email", "openid"]
# No client_secret needed with PKCE

Important: In Auth0, configure the application as Native and ensure the redirect URL is allowed.

Google with Traditional OAuth2

[http_config.oauth2_conf]
client_id = "99999999-abababababababababab.apps.googleusercontent.com"
client_secret = "your-client-secret"  # Optional with PKCE
authorize_url = "https://accounts.google.com/o/oauth2/v2/auth"
token_url = "https://oauth2.googleapis.com/token"
scopes = ["openid", "email"]

Authentication Flow

When running ckms login:

  1. The CLI generates a URL to open in your browser
  2. You authenticate with your identity provider
  3. The browser redirects to a local endpoint (http://localhost:17899/authorization)
  4. The CLI captures the token and saves it in your configuration file

Corporate Network / Forward Proxy

When the KMS CLI is used inside a corporate network, the system HTTP proxy may intercept outbound connections and block non-standard ports (e.g., 9998) or internal hostnames. This typically manifests as a TunnelUnsuccessful connection error.

Note on --proxy-exclusion-list: this CLI flag is silently ignored unless --proxy-url is also provided. Without --proxy-url, proxy_exclusion_list is dropped and the Windows system proxy operates unconstrained. Use the ckms.toml configuration or environment variables for a persistent solution.

Option 1 — Environment variable (no config change needed)

Set NO_PROXY before running the CLI:

# Linux / macOS
export NO_PROXY="kms-host"
ckms kms locate

# Windows (PowerShell)
$env:NO_PROXY = "kms-host"
cosmian.exe kms locate

Option 2 — CLI flags (one-off, requires --proxy-url)

--proxy-exclusion-list only takes effect when --proxy-url is also supplied:

cosmian.exe \
  --proxy-url="http://corp-proxy.example.com:8080" \
  --proxy-exclusion-list="kms-host" \
  kms locate

Equivalent environment variables (persist for the shell session):

export CLI_PROXY_URL="http://corp-proxy.example.com:8080"
export CLI_PROXY_NO_PROXY="kms-host,127.0.0.1,localhost"

Edit ~/.cosmian/ckms.toml (Windows: %USERPROFILE%\.cosmian\ckms.toml):

[http_config]
server_url = "https://kms-host:9998"

[http_config.proxy_params]
# URL of the corporate proxy (on Windows: netsh winhttp show proxy)
url = "http://corp-proxy.example.com:8080"

# Optional: Basic auth credentials for the proxy
# basic_auth_username = "DOMAIN\\username"
# basic_auth_password = "password"

# Hosts to bypass — add your KMS hostname here
exclusion_list = ["kms-host", "kms-host:9998", "127.0.0.1", "localhost"]

For a proxy requiring a custom Proxy-Authorization header:

[http_config.proxy_params]
url = "http://corp-proxy.example.com:8080"
custom_auth_header = "Bearer <proxy-token>"
exclusion_list = ["kms-host"]

Troubleshooting

  • Authentication Failures: Verify client ID and URLs are correct
  • PKCE Issues: Ensure your identity provider supports PKCE and has it enabled
  • Redirect Errors: Check that your identity provider allows the redirect URL
  • Missing Email Claim: Verify your identity provider includes the email claim in tokens

For more details about PKCE authentication, see the PKCE Authentication Guide.

Configuration file

The CLI looks for its configuration file in the following order:

  1. The path in the CKMS_CONF environment variable (if set).
  2. ~/.cosmian/ckms.toml (user-level default).
  3. /etc/cosmian/ckms.toml (system-wide fallback).
  4. If none of the above exists, all options must be passed as command-line arguments.

Run ckms configure to create or update the file interactively.

Minimal example

[http_config]
server_url = "http://0.0.0.0:9998"

TLS client authentication

With a PKCS#12 bundle

[http_config]
server_url = "https://kms.example.com"
ssl_client_pkcs12_path = "/path/to/client.p12"
ssl_client_pkcs12_password = "password"

With PEM certificate and key

[http_config]
server_url = "https://kms.example.com"
ssl_client_pem_cert_path = "/path/to/client.crt"
ssl_client_pem_key_path  = "/path/to/client.key"

Pinning a server certificate (instead of the system CA)

[http_config]
server_url = "https://kms.example.com"
# PEM-encoded certificate expected from the server
verified_cert = "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"

Accepting invalid server certificates (testing only)

[http_config]
server_url = "https://kms.example.com"
accept_invalid_certs = true

Custom HTTP headers

Some deployments place the KMS server behind a zero-trust proxy (e.g. Cloudflare Zero Trust) that requires an extra HTTP header on every request. Use the custom_headers field — or the --header / -H CLI flag — to pass arbitrary headers in "Name: Value" format.

Via the configuration file

[http_config]
server_url = "https://kms.example.com"

# One or more headers in "Name: Value" format
custom_headers = [
    "cf-access-token: <your-cloudflare-access-token>",
    "X-Custom-Header: my-value",
]

Via the command line or environment variable

The --header (short: -H) flag mirrors curl's convention and can be repeated for multiple headers:

# Single header
ckms --header "cf-access-token: <token>" server-version

# Multiple headers
ckms -H "cf-access-token: <token>" -H "X-Env: production" sym keys create

Set CLI_HEADER in the environment to apply headers without repeating the flag. Multiple headers must be newline-separated:

export CLI_HEADER=$'cf-access-token: <token>\nX-Env: production'
ckms sym keys create

CLI flags merge with any custom_headers already present in ckms.toml.

Forward proxy

Route all KMS traffic through an HTTP or SOCKS proxy using [http_config.proxy_params] in the configuration file, or via CLI flags / environment variables.

Via the configuration file

[http_config]
server_url = "https://kms.example.com"

[http_config.proxy_params]
url = "http://proxy.corp:3128"

# Optional: exclude hosts from the proxy
exclusion_list = ["127.0.0.1", "*.internal"]

# Optional: proxy credentials — choose one of the two forms below.

# Form 1 – HTTP Basic auth
basic_auth_username = "proxyuser"
basic_auth_password = "proxypass"

# Form 2 – arbitrary Proxy-Authorization header value
# custom_auth_header = "Bearer <token>"

Via the command line

FlagEnvironment variableDescription
--proxy-url <URL>CLI_PROXY_URLProxy URL (http://, https://, socks5://)
--proxy-basic-auth-usernameCLI_PROXY_BASIC_AUTH_USERNAMEBasic-auth username
--proxy-basic-auth-passwordCLI_PROXY_BASIC_AUTH_PASSWORDBasic-auth password
--proxy-custom-auth-headerCLI_PROXY_CUSTOM_AUTH_HEADERFull Proxy-Authorization header value
--proxy-exclusion-list <HOST>CLI_PROXY_NO_PROXYHosts to bypass (repeatable)
ckms --proxy-url http://proxy.corp:3128 \
     --proxy-basic-auth-username proxyuser \
     --proxy-basic-auth-password proxypass \
     sym keys create

Combining a proxy with custom headers

Proxy settings and custom headers are independent and can be used together:

ckms --proxy-url http://proxy.corp:3128 \
     --header "cf-access-token: <token>" \
     --header "X-Tenant: acme" \
     sym keys create

Or fully in ckms.toml:

[http_config]
server_url = "https://kms.example.com"
custom_headers = ["cf-access-token: <token>", "X-Tenant: acme"]

[http_config.proxy_params]
url = "http://proxy.corp:3128"

OpenID Connect / OAuth2 authentication

KMS can be configured with OpenID Connect (OIDC) authentication. In that case, KMS CLI must use the oauth2_conf field to authenticate to the server.

[http_config]
server_url = "http://0.0.0.0:9998"
access_token = "eyJhbGciOiJSUz...jsFgROjPY84GRMmvpYZfyaJbDDql3A"

[http_config.oauth2_conf]
client_id = "99999999-abababababababababab.apps.googleusercontent.com"
client_secret = "XXX"
authorize_url = "https://accounts.google.com/o/oauth2/v2/auth"
token_url = "https://oauth2.googleapis.com/token"
scopes = ["openid", "email"]

Advanced options

KeyTypeDefaultDescription
http_config.cipher_suitesstring(platform default)Colon-separated list of TLS cipher suites, e.g. "TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256".
print_jsonboolfalsePrint the raw KMIP JSON request and response for every operation.
# Print raw KMIP JSON (useful for debugging)
print_json = true

[http_config]
server_url = "https://kms.example.com"
cipher_suites = "TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256"

S/MIME Gmail service account configuration for KMS server

Google Workspace can delegate encryption/decryption of Gmail (and other services such as Drive, Meet, Calendar) to an external Key Management System (KMS). In that case, the KMS can be used to encrypt and decrypt the S/MIME elements (identities, key pairs) and store them securely.

When using S/MIME, the gmail_api_conf field should be set in the configuration file to provide the necessary information about the configured service account to interact with Gmail API, and handle identities and keypairs easily from the KMS.

[http_config]
server_url = "http://0.0.0.0:9998"
access_token = "eyJhbGciOiJSUz...jsFgROjPY84GRMmvpYZfyaJbDDql3A"

[http_config.oauth2_conf]
client_id = "99999999-abababababababababab.apps.googleusercontent.com"
client_secret = "XXX"
authorize_url = "https://accounts.google.com/o/oauth2/v2/auth"
token_url = "https://oauth2.googleapis.com/token"
scopes = ["openid", "email"]

[gmail_api_conf]
type = "service_account"
project_id = "project_id"
private_key_id = "abc123abc123abc123abc123abc123abc123"
private_key = "-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----"
client_email = "xxx@yyyy.iam.gserviceaccount.com"
client_id = "12345678901234567890"
auth_uri = "https://accounts.google.com/o/oauth2/auth"
token_uri = "https://oauth2.googleapis.com/token"
auth_provider_x509_cert_url = "https://www.googleapis.com/oauth2/v1/certs"
client_x509_cert_url = "https://www.googleapis.com/robot/v1/metadata/x509/xxx%40yyyy.iam.gserviceaccount.com"
universe_domain = "googleapis.com"

Usage

Command Line Interface used to manage the Cosmian KMS server.

If any assistance is needed, please either visit the Cosmian technical documentation at https://docs.cosmian.com
or contact the Cosmian support team on Discord https://discord.com/invite/7kPMNtHpnz


Usage: ckms [OPTIONS] <COMMAND>

Commands:
  access-rights  Manage the users' access rights to the cryptographic objects
  attributes     Get/Set/Delete/Modify the KMIP object attributes
  azure          Support for Azure specific interactions
  aws            Support for AWS specific interactions
  bench          Run benchmarks using criterion for statistical analysis.
  cc             Manage Covercrypt keys and policies. Rotate attributes. Encrypt and decrypt data
  fpe            Manage FPE keys and perform FPE encryption/decryption through KMIP Encrypt/Decrypt
  pqc            Manage post-quantum keys (ML-KEM, ML-DSA, Hybrid KEM, SLH-DSA). Encapsulate, decapsulate, sign, and verify
  tokenize       Anonymization utilities: hash, noise, word masking, pattern masking, aggregation, and scaling
  certificates   Manage certificates. Create, import, destroy and revoke. Encrypt and decrypt data
  cng            Manage the Windows CNG Key Storage Provider (KSP)
  derive-key     Derive a new key from an existing key
  ec             Manage elliptic curve keys. Encrypt and decrypt data using ECIES
  google         Manage google elements. Handle key pairs and identities from Gmail API
  locate         Locate cryptographic objects inside the KMS
  login          Login to the Identity Provider of the KMS server using the `OAuth2` authorization code flow.
  logout         Logout from the Identity Provider
  hash           Hash arbitrary data.
  mac            MAC utilities: compute or verify a MAC value.
  rng            RNG utilities: retrieve random bytes or seed RNG
  server         Server-related commands
  rsa            Manage RSA keys. Encrypt and decrypt data using RSA keys
  opaque-object  Create, import, export, revoke and destroy Opaque Objects
  pkcs11         Verify PKCS#11 shared library integration
  secret-data    Create, import, export and destroy secret data
  sym            Manage symmetric keys. Encrypt and decrypt data
  markdown       Regenerate the CLI documentation in Markdown format
  configure      Configure the KMS CLI (create ckms.toml)
  help           Print this message or the help of the given subcommand(s)

Options:
  -c, --conf-path <CONF_PATH>
          Configuration file location

          This is an alternative to the env variable `CKMS_CONF_PATH`. Takes precedence over `CKMS_CONF_PATH` env variable.

          [env: CKMS_CONF_PATH=]

      --url <URL>
          The URL of the KMS

          [env: KMS_DEFAULT_URL=]

      --print-json
          Output the KMS JSON KMIP request and response. This is useful to understand JSON POST requests and responses required to programmatically call the KMS on the `/kmip/2_1` endpoint

      --accept-invalid-certs
          Allow to connect using a self-signed cert or untrusted cert chain

          `accept_invalid_certs` is useful if the CLI needs to connect to an HTTPS KMS server running an invalid or insecure SSL certificate

  -H, --header <NAME: VALUE>
          Add a custom HTTP header to every request sent to the KMS server.

          The header must be specified in `"Name: Value"` format, matching the
          curl `-H` / `--header` convention. This option may be repeated to add
          multiple headers.

          The environment variable `CLI_HEADER` may also be used; separate
          multiple headers with a newline character.

          Example: `--header "cf-access-token: <token>"`

          [env: CLI_HEADER=]

      --proxy-url <PROXY_URL>
          The proxy URL:
            - e.g., `https://secure.example` for an HTTP proxy
            - e.g., `socks5://192.168.1.1:9000` for a SOCKS proxy

          [env: CLI_PROXY_URL=]

      --proxy-basic-auth-username <PROXY_BASIC_AUTH_USERNAME>
          Set the Proxy-Authorization header username using Basic auth.

          [env: CLI_PROXY_BASIC_AUTH_USERNAME=]

      --proxy-basic-auth-password <PROXY_BASIC_AUTH_PASSWORD>
          Set the Proxy-Authorization header password using Basic auth.

          [env: CLI_PROXY_BASIC_AUTH_PASSWORD=]

      --proxy-custom-auth-header <PROXY_CUSTOM_AUTH_HEADER>
          Set the Proxy-Authorization header to a specified value.

          [env: CLI_PROXY_CUSTOM_AUTH_HEADER=]

      --proxy-exclusion-list <PROXY_EXCLUSION_LIST>
          The No Proxy exclusion list to this Proxy

          [env: CLI_PROXY_NO_PROXY=]

  -h, --help
          Print help (see a summary with '-h')

  -V, --version
          Print version

ckms

Command Line Interface used to manage the Cosmian KMS server.

If any assistance is needed, please either visit the Cosmian technical documentation at https://docs.cosmian.com or contact the Cosmian support team on Discord https://discord.com/invite/7kPMNtHpnz

Usage

ckms <subcommand> [options]

Arguments

--conf-path [-c] <CONF_PATH> Configuration file location

--url <URL> The URL of the KMS

--print-json <PRINT_JSON> Output the KMS JSON KMIP request and response. This is useful to understand JSON POST requests and responses required to programmatically call the KMS on the /kmip/2_1 endpoint

Possible values: "true", "false"

--accept-invalid-certs <ACCEPT_INVALID_CERTS> Allow to connect using a self-signed cert or untrusted cert chain

Possible values: "true", "false"

--header [-H] <NAME: VALUE> Add a custom HTTP header to every request sent to the KMS server.

--proxy-url <PROXY_URL> The proxy URL:

  • e.g., https://secure.example for an HTTP proxy
  • e.g., socks5://192.168.1.1:9000 for a SOCKS proxy

--proxy-basic-auth-username <PROXY_BASIC_AUTH_USERNAME> Set the Proxy-Authorization header username using Basic auth.

--proxy-basic-auth-password <PROXY_BASIC_AUTH_PASSWORD> Set the Proxy-Authorization header password using Basic auth.

--proxy-custom-auth-header <PROXY_CUSTOM_AUTH_HEADER> Set the Proxy-Authorization header to a specified value.

--proxy-exclusion-list <PROXY_EXCLUSION_LIST> The No Proxy exclusion list to this Proxy

Subcommands

access-rights [1] Manage the users' access rights to the cryptographic objects

attributes [2] Get/Set/Delete/Modify the KMIP object attributes

azure [3] Support for Azure specific interactions

aws [4] Support for AWS specific interactions

bench [5] Run benchmarks using criterion for statistical analysis.

cc [6] Manage Covercrypt keys and policies. Rotate attributes. Encrypt and decrypt data

fpe [7] Manage FPE keys and perform FPE encryption/decryption through KMIP Encrypt/Decrypt

pqc [8] Manage post-quantum keys (ML-KEM, ML-DSA, Hybrid KEM, SLH-DSA). Encapsulate, decapsulate, sign, and verify

tokenize [9] Anonymization utilities: hash, noise, word masking, pattern masking, aggregation, and scaling

certificates [10] Manage certificates. Create, import, destroy and revoke. Encrypt and decrypt data

cng [11] Manage the Windows CNG Key Storage Provider (KSP)

derive-key [12] Derive a new key from an existing key

ec [13] Manage elliptic curve keys. Encrypt and decrypt data using ECIES

google [14] Manage google elements. Handle key pairs and identities from Gmail API

locate [15] Locate cryptographic objects inside the KMS

login [16] Login to the Identity Provider of the KMS server using the OAuth2 authorization code flow.

logout [17] Logout from the Identity Provider

hash [18] Hash arbitrary data.

mac [19] MAC utilities: compute or verify a MAC value.

rng [20] RNG utilities: retrieve random bytes or seed RNG

server [21] Server-related commands

rsa [22] Manage RSA keys. Encrypt and decrypt data using RSA keys

opaque-object [23] Create, import, export, revoke and destroy Opaque Objects

pkcs11 [24] Verify PKCS#11 shared library integration

secret-data [25] Create, import, export and destroy secret data

sym [26] Manage symmetric keys. Encrypt and decrypt data

markdown [27] Regenerate the CLI documentation in Markdown format

configure [28] Configure the KMS CLI (create ckms.toml)


1 ckms access-rights

Manage the users' access rights to the cryptographic objects

Usage

ckms access-rights <subcommand>

Subcommands

grant [1.1] Grant another user one or multiple access rights to an object

revoke [1.2] Revoke another user one or multiple access rights to an object

list [1.3] List the access rights granted on an object to other users

owned [1.4] List the objects owned by the calling user

obtained [1.5] List the access rights obtained by the calling user


1.1 ckms access-rights grant

Grant another user one or multiple access rights to an object

Usage

ckms access-rights grant [options] <USER> <OPERATIONS>...

Arguments

<USER> The user identifier to allow

--object-uid [-i] <OBJECT_UID> The object unique identifier stored in the KMS

<OPERATIONS> The operations to grant (create, get, encrypt, decrypt, import, revoke, locate, rekey, destroy, get_attributes)


1.2 ckms access-rights revoke

Revoke another user one or multiple access rights to an object

Usage

ckms access-rights revoke [options] <USER> <OPERATIONS>...

Arguments

<USER> The user to revoke access to

--object-uid [-i] <OBJECT_UID> The object unique identifier stored in the KMS

<OPERATIONS> The operations to revoke (create, get, encrypt, decrypt, import, revoke, locate, rekey, destroy)


1.3 ckms access-rights list

List the access rights granted on an object to other users

Usage

ckms access-rights list [options] <OBJECT_UID>

Arguments

<OBJECT_UID> The object unique identifier


1.4 ckms access-rights owned

List the objects owned by the calling user

Usage

ckms access-rights owned


1.5 ckms access-rights obtained

List the access rights obtained by the calling user

Usage

ckms access-rights obtained


2 ckms attributes

Get/Set/Delete/Modify the KMIP object attributes

Usage

ckms attributes <subcommand>

Subcommands

get [2.1] Get the KMIP object attributes and tags.

set [2.2] Set the KMIP object attributes.

delete [2.3] Delete the KMIP object attributes.

modify [2.4] Modify existing KMIP object attributes.


2.1 ckms attributes get

Get the KMIP object attributes and tags.

Usage

ckms attributes get [options]

Arguments

--id [-i] <ID> The unique identifier of the cryptographic object. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times

--attribute [-a] <ATTRIBUTE> The KMIP attribute to retrieve. To specify multiple attributes, use the option multiple times. If not specified, all possible attributes are returned. To retrieve the tags, use Tag as an attribute value.

--link-type [-l] <LINK_TYPE> Filter on retrieved links. Only if KMIP tag LinkType is used in attribute parameter. To specify multiple attributes, use the option multiple times. If not specified, all possible link types are returned.

Possible values: "certificate", "public-key", "private-key", "derivation-base-object", "derived-key", "replacement-object", "replaced-object", "parent", "child", "previous", "next", "pkcs12-certificate", "pkcs12-password", "wrapping-key"

--output-file [-o] <OUTPUT_FILE> An optional file where to export the attributes. The attributes will be in JSON TTLV format.


2.2 ckms attributes set

Set the KMIP object attributes.

Usage

ckms attributes set [options]

Arguments

--id [-i] <ID> The unique identifier of the cryptographic object. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times

--activation-date [-d] <ACTIVATION_DATE> Set the activation date of the key. Epoch time (or Unix time) in milliseconds

--cryptographic-algorithm [-a] <CRYPTOGRAPHIC_ALGORITHM> The cryptographic algorithm used by the key

Possible values: "aes", "rsa", "ecdsa", "ecdh", "ec", "chacha20", "chacha20-poly1305", "sha3224", "sha3256", "sha3384", "sha3512", "ed25519", "ed448", "covercrypt", "covercrypt-bulk"

--cryptographic-length <CRYPTOGRAPHIC_LENGTH> The length of the cryptographic key

--key-usage [-u] <KEY_USAGE> The key usage. Add multiple times to specify multiple key usages

Possible values: "sign", "verify", "encrypt", "decrypt", "wrap-key", "unwrap-key", "mac-generate", "mac-verify", "derive-key", "key-agreement", "certificate-sign", "crl-sign", "authenticate", "unrestricted"

--public-key-id <PUBLIC_KEY_ID> The link to the corresponding public key id if any

--private-key-id <PRIVATE_KEY_ID> The link to the corresponding private key id if any

--certificate-id <CERTIFICATE_ID> The link to the corresponding certificate id if any

--p12-id <PKCS12_CERTIFICATE_ID> The link to the corresponding PKCS12 certificate id if any

--p12-pwd <PKCS12_PASSWORD_CERTIFICATE> The link to the corresponding PKCS12 password certificate if any

--parent-id <PARENT_ID> The link to the corresponding parent id if any

--child-id <CHILD_ID> The link to the corresponding child id if any

--name <NAME> The name of the object (standard KMIP Name attribute). The name is stored as an UninterpretedTextString by default

--vendor-identification [-v] <VENDOR_IDENTIFICATION> The vendor identification

--attribute-name [-n] <ATTRIBUTE_NAME> The attribute name

--attribute-value <ATTRIBUTE_VALUE> The attribute value (in hex format)


2.3 ckms attributes delete

Delete the KMIP object attributes.

Usage

ckms attributes delete [options]

Arguments

--id [-i] <ID> The unique identifier of the cryptographic object. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times

--activation-date [-d] <ACTIVATION_DATE> Set the activation date of the key. Epoch time (or Unix time) in milliseconds

--cryptographic-algorithm [-a] <CRYPTOGRAPHIC_ALGORITHM> The cryptographic algorithm used by the key

Possible values: "aes", "rsa", "ecdsa", "ecdh", "ec", "chacha20", "chacha20-poly1305", "sha3224", "sha3256", "sha3384", "sha3512", "ed25519", "ed448", "covercrypt", "covercrypt-bulk"

--cryptographic-length <CRYPTOGRAPHIC_LENGTH> The length of the cryptographic key

--key-usage [-u] <KEY_USAGE> The key usage. Add multiple times to specify multiple key usages

Possible values: "sign", "verify", "encrypt", "decrypt", "wrap-key", "unwrap-key", "mac-generate", "mac-verify", "derive-key", "key-agreement", "certificate-sign", "crl-sign", "authenticate", "unrestricted"

--public-key-id <PUBLIC_KEY_ID> The link to the corresponding public key id if any

--private-key-id <PRIVATE_KEY_ID> The link to the corresponding private key id if any

--certificate-id <CERTIFICATE_ID> The link to the corresponding certificate id if any

--p12-id <PKCS12_CERTIFICATE_ID> The link to the corresponding PKCS12 certificate id if any

--p12-pwd <PKCS12_PASSWORD_CERTIFICATE> The link to the corresponding PKCS12 password certificate if any

--parent-id <PARENT_ID> The link to the corresponding parent id if any

--child-id <CHILD_ID> The link to the corresponding child id if any

--name <NAME> The name of the object (standard KMIP Name attribute). The name is stored as an UninterpretedTextString by default

--vendor-identification [-v] <VENDOR_IDENTIFICATION> The vendor identification

--attribute-name [-n] <ATTRIBUTE_NAME> The attribute name

--attribute-value <ATTRIBUTE_VALUE> The attribute value (in hex format)

--attribute <ATTRIBUTE> The attributes or tags to retrieve. To specify multiple attributes, use the option multiple times.


2.4 ckms attributes modify

Modify existing KMIP object attributes.

Usage

ckms attributes modify [options]

Arguments

--id [-i] <ID> The unique identifier of the cryptographic object. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times

--activation-date [-d] <ACTIVATION_DATE> Set the activation date of the key. Epoch time (or Unix time) in milliseconds

--cryptographic-algorithm [-a] <CRYPTOGRAPHIC_ALGORITHM> The cryptographic algorithm used by the key

Possible values: "aes", "rsa", "ecdsa", "ecdh", "ec", "chacha20", "chacha20-poly1305", "sha3224", "sha3256", "sha3384", "sha3512", "ed25519", "ed448", "covercrypt", "covercrypt-bulk"

--cryptographic-length <CRYPTOGRAPHIC_LENGTH> The length of the cryptographic key

--key-usage [-u] <KEY_USAGE> The key usage. Add multiple times to specify multiple key usages

Possible values: "sign", "verify", "encrypt", "decrypt", "wrap-key", "unwrap-key", "mac-generate", "mac-verify", "derive-key", "key-agreement", "certificate-sign", "crl-sign", "authenticate", "unrestricted"

--public-key-id <PUBLIC_KEY_ID> The link to the corresponding public key id if any

--private-key-id <PRIVATE_KEY_ID> The link to the corresponding private key id if any

--certificate-id <CERTIFICATE_ID> The link to the corresponding certificate id if any

--p12-id <PKCS12_CERTIFICATE_ID> The link to the corresponding PKCS12 certificate id if any

--p12-pwd <PKCS12_PASSWORD_CERTIFICATE> The link to the corresponding PKCS12 password certificate if any

--parent-id <PARENT_ID> The link to the corresponding parent id if any

--child-id <CHILD_ID> The link to the corresponding child id if any

--name <NAME> The name of the object (standard KMIP Name attribute). The name is stored as an UninterpretedTextString by default

--vendor-identification [-v] <VENDOR_IDENTIFICATION> The vendor identification

--attribute-name [-n] <ATTRIBUTE_NAME> The attribute name

--attribute-value <ATTRIBUTE_VALUE> The attribute value (in hex format)


3 ckms azure

Support for Azure specific interactions

Usage

ckms azure <subcommand>

Subcommands

byok [3.1] Azure BYOK support. See: https://learn.microsoft.com/en-us/azure/key-vault/keys/byok-specification


3.1 ckms azure byok

Azure BYOK support. See: https://learn.microsoft.com/en-us/azure/key-vault/keys/byok-specification

Usage

ckms azure byok <subcommand>

Subcommands

import [3.1.1] Import into the KMS an RSA Key Encryption Key (KEK) generated on Azure Key Vault. See: https://learn.microsoft.com/en-us/azure/key-vault/keys/byok-specification#generate-kek

export [3.1.2] Wrap a KMS key with an Azure Key Encryption Key (KEK), previously imported using the ckms azure byok import command. Generate the .byok file that can be used to import the KMS key into Azure Key Vault. See: https://learn.microsoft.com/en-us/azure/key-vault/keys/byok-specification


3.1.1 ckms azure byok import

Import into the KMS an RSA Key Encryption Key (KEK) generated on Azure Key Vault. See: https://learn.microsoft.com/en-us/azure/key-vault/keys/byok-specification#generate-kek

Usage

ckms azure byok import [options] <KEK_FILE> <KID> [KEY_ID]

Arguments

<KEK_FILE> The RSA Key Encryption Key (KEK) file exported from the Azure Key Vault in PKCS#8 PEM format

<KID> The Azure Key ID (kid). It should be something like: https://mypremiumkeyvault.vault.azure.net/keys/KEK-BYOK/664f5aa2797a4075b8e36ca4500636d8

<KEY_ID> The unique ID of the key in this KMS; a random UUID is generated if not specified


3.1.2 ckms azure byok export

Wrap a KMS key with an Azure Key Encryption Key (KEK), previously imported using the ckms azure byok import command. Generate the .byok file that can be used to import the KMS key into Azure Key Vault. See: https://learn.microsoft.com/en-us/azure/key-vault/keys/byok-specification

Usage

ckms azure byok export [options] <WRAPPED_KEY_ID> <KEK_ID> [BYOK_FILE]

Arguments

<WRAPPED_KEY_ID> The unique ID of the KMS private key that will be wrapped and then exported

<KEK_ID> The Azure KEK ID in this KMS

<BYOK_FILE> The file path to export the .byok file to. If not specified, the file will be called <wrapped_key_id>.byok


4 ckms aws

Support for AWS specific interactions

Usage

ckms aws <subcommand>

Subcommands

byok [4.1] AWS BYOK support. See: https://docs.aws.amazon.com/kms/latest/developerguide/importing-keys-conceptual.html


4.1 ckms aws byok

AWS BYOK support. See: https://docs.aws.amazon.com/kms/latest/developerguide/importing-keys-conceptual.html

Usage

ckms aws byok <subcommand>

Subcommands

import [4.1.1] Import an AWS Key Encryption Key (KEK) into the KMS.

export [4.1.2] Wrap a KMS key with an AWS Key Encryption Key (KEK).


4.1.1 ckms aws byok import

Import an AWS Key Encryption Key (KEK) into the KMS.

Usage

ckms aws byok import [options]

Arguments

--kek-base64 [-b] <KEK_BASE64> The RSA Key Encryption public key (the KEK) as a base64-encoded string

--kek-file [-f] <KEK_FILE> In case of KEK provided as a file blob

--wrapping-algorithm [-w] <WRAPPING_ALGORITHM> Possible values: "RSAES_OAEP_SHA_1", "RSAES_OAEP_SHA_256", "RSA_AES_KEY_WRAP_SHA_1", "RSA_AES_KEY_WRAP_SHA_256"

--key-arn [-a] <KEY_ARN> The Amazon Resource Name (key ARN) of the KMS key. It's recommended to provide it for an easier export later

--key-id [-i] <KEY_ID> The unique ID of the key in this KMS; a random UUID is generated if not specified


4.1.2 ckms aws byok export

Wrap a KMS key with an AWS Key Encryption Key (KEK).

Usage

ckms aws byok export [options] <KEY_ID> <KEK_ID> [TOKEN_FILE_PATH] [OUTPUT_FILE_PATH]

Arguments

<KEY_ID> The unique ID of the KMS private key that will be wrapped and then exported

<KEK_ID> The AWS KEK ID in this KMS

<TOKEN_FILE_PATH> The file path containing the import token previously generated when importing the KEK. This file isn't read and neither used by the KMS, it's simply for providing copy-paste ready output for aws cli users upon a successful key material wrapping

<OUTPUT_FILE_PATH> If not specified, a base64 encoded blob containing the key material will be printed to stdout. Can be piped to desired file or command


5 ckms bench

Run benchmarks using criterion for statistical analysis.

Usage

ckms bench [options]

Arguments

--mode [-m] <MODE> Benchmark category (default: all)

Possible values: "all", "encrypt", "key-creation", "sign-verify", "batch" [default: "all"]

--protocol [-p] <PROTOCOL> Protocol / transport to benchmark (default: all). - ttlv-json: KMIP over JSON TTLV (POST /kmip/2_1) - ttlv-bytes: KMIP over binary TTLV (POST /kmip) - jose: REST JOSE endpoints (POST /v1/crypto/*)

Possible values: "all", "ttlv-json", "ttlv-bytes", "jose" [default: "all"]

--format [-f] <FORMAT> Output format

Possible values: "text", "json", "markdown", "compact", "html" [default: "text"]

--speed [-s] <SPEED> Benchmark speed mode: normal (default), quick, or sanity. Sanity auto-selects --format compact when no explicit format is given

Possible values: "normal", "quick", "sanity" [default: "normal"]

--time [-t] <TIME> Maximum measurement time per benchmark in seconds (default: 10). Caps how long criterion spends on each benchmark function. Ignored in quick and sanity speed modes

--max-group-time <MAX_GROUP_TIME> Maximum wall-clock time (in seconds) to spend per benchmark group. Once a group exceeds this budget, its remaining benchmarks are skipped (benchmarks already completed in the group are kept). Unset means no cap. Applies to the KMIP (ttlv-json / ttlv-bytes) benchmark groups

--save-baseline <SAVE_BASELINE> Save results under a named baseline in target/criterion///. Use this to snapshot a run before a change. To compare, run again with --load-baseline (or without any flag to diff against "base"). Example: --save-baseline before-my-change

--load-baseline <LOAD_BASELINE> Compare results against a previously saved baseline. Prints change% in console output for each benchmark. Example: --load-baseline before-my-change

--version-label <VERSION_LABEL> When emitting --format json, insert this label as the version column so that criterion-table renders versions as columns for proper comparison. Run baseline first, compare second, then combine: cat v5.12.json v5.17.json | criterion-table > diff.md

--load <LOAD> Run concurrent load tests instead of criterion statistical benchmarks. Measures throughput (req/s) and latency percentiles (p50/p95/p99) at increasing concurrency levels. Can be combined with --mode to focus on specific operations. Use --format html to produce a gnuplot HTML report

Possible values: "true", "false" [default: "false"]

--load-concurrency <LOAD_CONCURRENCY> Comma-separated concurrency levels for load testing. Only used when --load is set

--load-plaintext-size <LOAD_PLAINTEXT_SIZE> Plaintext size in bytes for load-test encrypt ops (default 4096). Larger payloads make the op CPU-bound (crypto + serialization dominate the fixed HTTP/loopback overhead), which is what a capacity/scaling benchmark wants; the small default keeps the op network/latency-bound

--warmup-time <WARMUP_TIME> Warmup time in seconds before benchmarking starts. Sends requests for this duration to warm HTTP connection pools, TLS sessions, and server-side caches. Set to 0 to skip

--cooldown-time <COOLDOWN_TIME> Cooldown time in seconds between load-test concurrency levels. Lets the server drain TCP TIME_WAIT sockets, checkpoint SQLite WAL, and release memory before the next level starts fresh


6 ckms cc

Manage Covercrypt keys and policies. Rotate attributes. Encrypt and decrypt data

Usage

ckms cc <subcommand>

Subcommands

keys [6.1] Create, destroy, import, export, and rekey Covercrypt master and user keys

access-structure [6.2] Extract, view, or edit policies of existing keys

encrypt [6.3] Encrypt a file using Covercrypt

decrypt [6.4] Decrypt a file using Covercrypt


6.1 ckms cc keys

Create, destroy, import, export, and rekey Covercrypt master and user keys

Usage

ckms cc keys <subcommand>

Subcommands

activate [6.1.1] Activate a cryptographic object (key, certificate, etc.)

create-master-key-pair [6.1.2] Create a new master keypair for a given access structure and return the key IDs.

create-user-key [6.1.3] Create a new user secret key for an access policy, and index it under some (optional) tags, that can later be used to retrieve the key.

export [6.1.4] Export a key or secret data from the KMS

import [6.1.5] Import a secret data or a key in the KMS.

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

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

revoke [6.1.8] Revoke a Covercrypt master or user decryption key

destroy [6.1.9] Destroy a Covercrypt master or user decryption key

rekey [6.1.10] Rekey the given access policy.

prune [6.1.11] Prune all keys linked to an MSK w.r.t an given access policy.


6.1.1 ckms cc keys activate

Activate a cryptographic object (key, certificate, etc.)

Usage

ckms cc keys activate [options]

Arguments

--key-id [-k] <KEY_ID> The key unique identifier of the key to activate. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times


6.1.2 ckms cc keys create-master-key-pair

Create a new master keypair for a given access structure and return the key IDs.

Usage

ckms cc keys create-master-key-pair [options]

Arguments

--specification [-s] <SPECIFICATION> The JSON access structure specifications file to use to generate the keys. See the inline doc of the create-master-key-pair command for details

--tag [-t] <TAG> The tag to associate with the master key pair. To specify multiple tags, use the option multiple times

--sensitive <SENSITIVE> Sensitive: if set, the private key will not be exportable

Possible values: "true", "false" [default: "false"]

--wrapping-key-id [-w] <WRAPPING_KEY_ID> The key encryption key (KEK) used to wrap the keypair with. If the wrapping key is:

  • a symmetric key, AES-GCM will be used
  • a RSA key, RSA-OAEP will be used
  • a EC key, ECIES will be used (salsa20poly1305 for X25519)

6.1.3 ckms cc keys create-user-key

Create a new user secret key for an access policy, and index it under some (optional) tags, that can later be used to retrieve the key.

Usage

ckms cc keys create-user-key [options] <MASTER_SECRET_KEY_ID> <ACCESS_POLICY>

Arguments

<MASTER_SECRET_KEY_ID> The master secret key unique identifier

<ACCESS_POLICY> The access policy should be expressed as a boolean expression of attributes. For example (provided the corresponding attributes are defined in the MSK):

--tag [-t] <TAG> The tag to associate with the user decryption key. To specify multiple tags, use the option multiple times

--sensitive <SENSITIVE> Sensitive: if set, the key will not be exportable

Possible values: "true", "false" [default: "false"]

--wrapping-key-id [-w] <WRAPPING_KEY_ID> The key encryption key (KEK) used to wrap the keypair with. If the wrapping key is:

  • a symmetric key, AES-GCM will be used
  • a RSA key, RSA-OAEP will be used
  • a EC key, ECIES will be used (salsa20poly1305 for X25519)

6.1.4 ckms cc keys export

Export a key or secret data from the KMS

Usage

ckms cc keys export [options] <KEY_FILE>

Arguments

<KEY_FILE> The file to export the key to

--key-id [-k] <KEY_ID> The key or secret data unique identifier stored in the KMS. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key or secret data id is specified. To specify multiple tags, use the option multiple times

--key-format [-f] <EXPORT_FORMAT> The format of the key

  • json-ttlv [default]. It should be the format to use to later re-import the key
  • sec1-pem and sec1-deronly apply to NIST EC private keys (Not Curve25519 or X448)
  • pkcs1-pem and pkcs1-der only apply to RSA private and public keys
  • pkcs8-pem and pkcs8-der only apply to RSA and EC private keys
  • raw returns the raw bytes of
    • symmetric keys
    • Covercrypt keys
    • wrapped keys
    • secret data

Possible values: "json-ttlv", "sec1-pem", "sec1-der", "pkcs1-pem", "pkcs1-der", "pkcs8-pem", "pkcs8-der", "base64", "raw" [default: "json-ttlv"]

--unwrap [-u] <UNWRAP> Unwrap the key if it is wrapped before export

Possible values: "true", "false" [default: "false"]

--wrap-key-id [-w] <WRAP_KEY_ID> The id of the key/certificate (a.k.a. Key Encryption Key - KEK) to use to wrap this key before export

--allow-revoked [-i] <ALLOW_REVOKED> Allow exporting revoked and destroyed keys. The user must be the owner of the key. Destroyed keys have their key material removed.

Possible values: "true", "false" [default: "false"]

--wrapping-algorithm [-m] <WRAPPING_ALGORITHM> Wrapping algorithm to use when exporting the key The possible wrapping algorithms are

  • using a symmetric KEK:
    • nist-key-wrap (default - a.k.a RFC 5649, CKM_AES_KEY_WRAP_PAD)
    • aes-gcm
  • using an RSA KEK:
    • rsa-oaep (default - CKM-RSA-OAEP)
    • rsa-aes-key-wrap (CKM-RSA-AES-KEY-WRP)
    • rsa-pkcs-v15 (CKM-RSA v1.5)

Possible values: "aes-key-wrap-padding", "nist-key-wrap", "aes-gcm", "rsa-pkcs-v15-sha1", "rsa-pkcs-v15", "rsa-oaep-sha1", "rsa-oaep", "rsa-aes-key-wrap-sha1", "rsa-aes-key-wrap"

--authenticated-additional-data [-d] <AUTHENTICATED_ADDITIONAL_DATA> Authenticated encryption additional data Only available for AES GCM wrapping


6.1.5 ckms cc keys import

Import a secret data or a key in the KMS.

Usage

ckms cc keys import [options] <KEY_FILE> [KEY_ID]

Arguments

<KEY_FILE> The file holding the key or secret data to import

<KEY_ID> The unique ID of the key; a random UUID is generated if not specified

--key-format [-f] <KEY_FORMAT> The format of the key

Possible values: "json-ttlv", "pem", "sec1", "pkcs1-priv", "pkcs1-pub", "pkcs8-priv", "pkcs8-pub", "aes", "chacha20" [default: "json-ttlv"]

--public-key-id [-p] <PUBLIC_KEY_ID> For a private key: the corresponding KMS public key ID, if any

--private-key-id [-k] <PRIVATE_KEY_ID> For a public key: the corresponding KMS private key ID, if any

--certificate-id [-c] <CERTIFICATE_ID> For a public or private key: the corresponding certificate ID, if any

--unwrap [-u] <UNWRAP> In the case of a JSON TTLV key, unwrap the key if it is wrapped before storing it

Possible values: "true", "false" [default: "false"]

--replace [-r] <REPLACE_EXISTING> Replace an existing key under the same ID

Possible values: "true", "false" [default: "false"]

--tag [-t] <TAG> The tag to associate with the key. To specify multiple tags, use the option multiple times

--key-usage <KEY_USAGE> The cryptographic operations the key is allowed to perform

Possible values: "sign", "verify", "encrypt", "decrypt", "wrap-key", "unwrap-key", "mac-generate", "mac-verify", "derive-key", "key-agreement", "certificate-sign", "crl-sign", "authenticate", "unrestricted"

--wrapping-key-id [-w] <WRAPPING_KEY_ID> The key encryption key (KEK) used to wrap this imported key with. If the wrapping key is:

  • A symmetric key, AES-GCM will be used,
  • An RSA key, RSA-OAEP with SHA-256 will be used,
  • An EC key, ECIES will be used (salsa20poly1305 for X25519),

6.1.6 ckms cc keys wrap

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

Usage

ckms cc keys wrap [options] <KEY_FILE_IN> [KEY_FILE_OUT]

Arguments

<KEY_FILE_IN> The KMIP JSON TTLV input key file to wrap

<KEY_FILE_OUT> The KMIP JSON output file. When not specified, the input file is overwritten

--wrap-password [-p] <WRAP_PASSWORD> A password to wrap the imported key. This password will be derived into an AES-256 symmetric key. For security reasons, a fresh salt is internally generated by cosmian and handled, and this final AES symmetric key will be displayed only once

--wrap-key-b64 [-k] <WRAP_KEY_B64> A symmetric key as a base 64 string to wrap the imported key

--wrap-key-id [-i] <WRAP_KEY_ID> The ID of a wrapping key in the KMS that will be exported and used to wrap the key

--wrap-key-file [-f] <WRAP_KEY_FILE> A wrapping key in a KMIP JSON TTLV file used to wrap the key


6.1.7 ckms cc keys unwrap

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

Usage

ckms cc keys unwrap [options] <KEY_FILE_IN> [KEY_FILE_OUT]

Arguments

<KEY_FILE_IN> The KMIP JSON TTLV input key file to unwrap

<KEY_FILE_OUT> The KMIP JSON output file. When not specified the input file is overwritten

--unwrap-key-b64 [-k] <UNWRAP_KEY_B64> A symmetric key as a base 64 string to unwrap the imported key

--unwrap-key-id [-i] <UNWRAP_KEY_ID> The id of an unwrapping key in the KMS that will be exported and used to unwrap the key

--unwrap-key-file [-f] <UNWRAP_KEY_FILE> An unwrapping key in a KMIP JSON TTLV file used to unwrap the key


6.1.8 ckms cc keys revoke

Revoke a Covercrypt master or user decryption key

Usage

ckms cc keys revoke [options] <REVOCATION_REASON>

Arguments

<REVOCATION_REASON> The reason for the revocation as a string

--reason-code [-r] <REASON_CODE> The revocation reason code [default: unspecified]

--key-id [-k] <KEY_ID> The key unique identifier of the key to revoke. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times


6.1.9 ckms cc keys destroy

Destroy a Covercrypt master or user decryption key

Usage

ckms cc keys destroy [options]

Arguments

--key-id [-k] <KEY_ID> The key unique identifier. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times

--remove <REMOVE> If the key should be removed from the database If not specified, the key will be destroyed but its metadata will still be available in the database. Please note that the KMIP specification does not support the removal of objects.

Possible values: "true", "false" [default: "false"]


6.1.10 ckms cc keys rekey

Rekey the given access policy.

Usage

ckms cc keys rekey [options] <ACCESS_POLICY>

Arguments

<ACCESS_POLICY> The access policy should be expressed as a boolean expression of attributes. For example (provided the corresponding attributes are defined in the MSK):

--key-id [-k] <MSK_UID> The MSK UID stored in the KMS. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the MSK when no key id is specified. To specify multiple tags, use the option multiple times


6.1.11 ckms cc keys prune

Prune all keys linked to an MSK w.r.t an given access policy.

Usage

ckms cc keys prune [options] <ACCESS_POLICY>

Arguments

<ACCESS_POLICY> The access policy should be expressed as a boolean expression of attributes. For example (provided the corresponding attributes are defined in the MSK):

--key-id [-k] <MSK_UID> The private master key unique identifier stored in the KMS. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times


6.2 ckms cc access-structure

Extract, view, or edit policies of existing keys

Usage

ckms cc access-structure <subcommand>

Subcommands

view [6.2.1] View the access structure of an existing public or private master key.

add-attribute [6.2.2] Add an attribute to the access structure of an existing private master key.

remove-attribute [6.2.3] Remove an attribute from the access structure of an existing private master key. Permanently removes the ability to use this attribute in both encryptions and decryptions.

disable-attribute [6.2.4] Disable an attribute from the access structure of an existing private master key.

rename-attribute [6.2.5] Rename an attribute in the access structure of an existing private master key.


6.2.1 ckms cc access-structure view

View the access structure of an existing public or private master key.

Usage

ckms cc access-structure view [options]

Arguments

--key-id [-i] <KEY_ID> The public or private master key ID if the key is stored in the KMS

--key-file [-f] <KEY_FILE> If key-id is not provided, use --key-file to provide the file containing the public or private master key in TTLV format


6.2.2 ckms cc access-structure add-attribute

Add an attribute to the access structure of an existing private master key.

Usage

ckms cc access-structure add-attribute [options] <ATTRIBUTE>

Arguments

<ATTRIBUTE> The name of the attribute to create. Example: department::rnd

--hybridized <HYBRIDIZED> Hybridize this qualified attribute

Possible values: "true", "false" [default: "false"]

--key-id [-k] <SECRET_KEY_ID> The master secret key unique identifier stored in the KMS. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times


6.2.3 ckms cc access-structure remove-attribute

Remove an attribute from the access structure of an existing private master key. Permanently removes the ability to use this attribute in both encryptions and decryptions.

Usage

ckms cc access-structure remove-attribute [options] <ATTRIBUTE>

Arguments

<ATTRIBUTE> The name of the attribute to remove. Example: department::marketing Note: prevents ciphertexts only targeting this qualified attribute to be decrypted

--key-id [-k] <MASTER_SECRET_KEY_ID> The master secret key unique identifier stored in the KMS. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times


6.2.4 ckms cc access-structure disable-attribute

Disable an attribute from the access structure of an existing private master key.

Usage

ckms cc access-structure disable-attribute [options] <ATTRIBUTE>

Arguments

<ATTRIBUTE> The name of the attribute to disable. Example: department::marketing

--key-id [-k] <MASTER_SECRET_KEY_ID> The master secret key unique identifier stored in the KMS. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times


6.2.5 ckms cc access-structure rename-attribute

Rename an attribute in the access structure of an existing private master key.

Usage

ckms cc access-structure rename-attribute [options] <ATTRIBUTE> <NEW_NAME>

Arguments

<ATTRIBUTE> The name of the attribute to rename. Example: department::mkg

<NEW_NAME> The new name for the attribute. Example: marketing

--key-id [-k] <MASTER_SECRET_KEY_ID> The master secret key unique identifier stored in the KMS. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times


6.3 ckms cc encrypt

Encrypt a file using Covercrypt

Usage

ckms cc encrypt [options] <FILE>... <ENCRYPTION_POLICY>

Arguments

<FILE> The files to encrypt

<ENCRYPTION_POLICY> The encryption policy to encrypt the file with Example: "department::marketing && level::confidential"

--key-id [-k] <KEY_ID> The public key unique identifier. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times

--output-file [-o] <OUTPUT_FILE> The encrypted output file path

--authentication-data [-a] <AUTHENTICATION_DATA> Optional authentication data. This data needs to be provided back for decryption


6.4 ckms cc decrypt

Decrypt a file using Covercrypt

Usage

ckms cc decrypt [options] <FILE>...

Arguments

<FILE> The files to decrypt

--key-id [-k] <KEY_ID> The user key unique identifier If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times

--output-file [-o] <OUTPUT_FILE> The encrypted output file path

--authentication-data [-a] <AUTHENTICATION_DATA> Optional authentication data that was supplied during encryption


7 ckms fpe

Manage FPE keys and perform FPE encryption/decryption through KMIP Encrypt/Decrypt

Usage

ckms fpe <subcommand>

Subcommands

keys [7.1] Create, destroy, import, and export FPE keys

encrypt [7.2] Encrypt data using AES-256 FF1 format-preserving encryption through KMIP

decrypt [7.3] Decrypt data using AES-256 FF1 format-preserving encryption through KMIP


7.1 ckms fpe keys

Create, destroy, import, and export FPE keys

Usage

ckms fpe keys <subcommand>

Subcommands

create [7.1.1] export [7.1.2] Export a key or secret data from the KMS

import [7.1.3] Import a secret data or a key in the KMS.

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

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

revoke [7.1.6] Revoke an FPE key

destroy [7.1.7] Destroy an FPE key


7.1.1 ckms fpe keys create

Usage

ckms fpe keys create [options] [KEY_ID]

Arguments

--tag [-t] <TAG> The tag to associate with the key. To specify multiple tags, use the option multiple times

<KEY_ID> The unique id of the key; a random uuid is generated if not specified

--sensitive <SENSITIVE> Sensitive: if set, the key will not be exportable

Possible values: "true", "false" [default: "false"]


7.1.2 ckms fpe keys export

Export a key or secret data from the KMS

Usage

ckms fpe keys export [options] <KEY_FILE>

Arguments

<KEY_FILE> The file to export the key to

--key-id [-k] <KEY_ID> The key or secret data unique identifier stored in the KMS. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key or secret data id is specified. To specify multiple tags, use the option multiple times

--key-format [-f] <EXPORT_FORMAT> The format of the key

  • json-ttlv [default]. It should be the format to use to later re-import the key
  • sec1-pem and sec1-deronly apply to NIST EC private keys (Not Curve25519 or X448)
  • pkcs1-pem and pkcs1-der only apply to RSA private and public keys
  • pkcs8-pem and pkcs8-der only apply to RSA and EC private keys
  • raw returns the raw bytes of
    • symmetric keys
    • Covercrypt keys
    • wrapped keys
    • secret data

Possible values: "json-ttlv", "sec1-pem", "sec1-der", "pkcs1-pem", "pkcs1-der", "pkcs8-pem", "pkcs8-der", "base64", "raw" [default: "json-ttlv"]

--unwrap [-u] <UNWRAP> Unwrap the key if it is wrapped before export

Possible values: "true", "false" [default: "false"]

--wrap-key-id [-w] <WRAP_KEY_ID> The id of the key/certificate (a.k.a. Key Encryption Key - KEK) to use to wrap this key before export

--allow-revoked [-i] <ALLOW_REVOKED> Allow exporting revoked and destroyed keys. The user must be the owner of the key. Destroyed keys have their key material removed.

Possible values: "true", "false" [default: "false"]

--wrapping-algorithm [-m] <WRAPPING_ALGORITHM> Wrapping algorithm to use when exporting the key The possible wrapping algorithms are

  • using a symmetric KEK:
    • nist-key-wrap (default - a.k.a RFC 5649, CKM_AES_KEY_WRAP_PAD)
    • aes-gcm
  • using an RSA KEK:
    • rsa-oaep (default - CKM-RSA-OAEP)
    • rsa-aes-key-wrap (CKM-RSA-AES-KEY-WRP)
    • rsa-pkcs-v15 (CKM-RSA v1.5)

Possible values: "aes-key-wrap-padding", "nist-key-wrap", "aes-gcm", "rsa-pkcs-v15-sha1", "rsa-pkcs-v15", "rsa-oaep-sha1", "rsa-oaep", "rsa-aes-key-wrap-sha1", "rsa-aes-key-wrap"

--authenticated-additional-data [-d] <AUTHENTICATED_ADDITIONAL_DATA> Authenticated encryption additional data Only available for AES GCM wrapping


7.1.3 ckms fpe keys import

Import a secret data or a key in the KMS.

Usage

ckms fpe keys import [options] <KEY_FILE> [KEY_ID]

Arguments

<KEY_FILE> The file holding the key or secret data to import

<KEY_ID> The unique ID of the key; a random UUID is generated if not specified

--key-format [-f] <KEY_FORMAT> The format of the key

Possible values: "json-ttlv", "pem", "sec1", "pkcs1-priv", "pkcs1-pub", "pkcs8-priv", "pkcs8-pub", "aes", "chacha20" [default: "json-ttlv"]

--public-key-id [-p] <PUBLIC_KEY_ID> For a private key: the corresponding KMS public key ID, if any

--private-key-id [-k] <PRIVATE_KEY_ID> For a public key: the corresponding KMS private key ID, if any

--certificate-id [-c] <CERTIFICATE_ID> For a public or private key: the corresponding certificate ID, if any

--unwrap [-u] <UNWRAP> In the case of a JSON TTLV key, unwrap the key if it is wrapped before storing it

Possible values: "true", "false" [default: "false"]

--replace [-r] <REPLACE_EXISTING> Replace an existing key under the same ID

Possible values: "true", "false" [default: "false"]

--tag [-t] <TAG> The tag to associate with the key. To specify multiple tags, use the option multiple times

--key-usage <KEY_USAGE> The cryptographic operations the key is allowed to perform

Possible values: "sign", "verify", "encrypt", "decrypt", "wrap-key", "unwrap-key", "mac-generate", "mac-verify", "derive-key", "key-agreement", "certificate-sign", "crl-sign", "authenticate", "unrestricted"

--wrapping-key-id [-w] <WRAPPING_KEY_ID> The key encryption key (KEK) used to wrap this imported key with. If the wrapping key is:

  • A symmetric key, AES-GCM will be used,
  • An RSA key, RSA-OAEP with SHA-256 will be used,
  • An EC key, ECIES will be used (salsa20poly1305 for X25519),

7.1.4 ckms fpe keys wrap

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

Usage

ckms fpe keys wrap [options] <KEY_FILE_IN> [KEY_FILE_OUT]

Arguments

<KEY_FILE_IN> The KMIP JSON TTLV input key file to wrap

<KEY_FILE_OUT> The KMIP JSON output file. When not specified, the input file is overwritten

--wrap-password [-p] <WRAP_PASSWORD> A password to wrap the imported key. This password will be derived into an AES-256 symmetric key. For security reasons, a fresh salt is internally generated by cosmian and handled, and this final AES symmetric key will be displayed only once

--wrap-key-b64 [-k] <WRAP_KEY_B64> A symmetric key as a base 64 string to wrap the imported key

--wrap-key-id [-i] <WRAP_KEY_ID> The ID of a wrapping key in the KMS that will be exported and used to wrap the key

--wrap-key-file [-f] <WRAP_KEY_FILE> A wrapping key in a KMIP JSON TTLV file used to wrap the key


7.1.5 ckms fpe keys unwrap

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

Usage

ckms fpe keys unwrap [options] <KEY_FILE_IN> [KEY_FILE_OUT]

Arguments

<KEY_FILE_IN> The KMIP JSON TTLV input key file to unwrap

<KEY_FILE_OUT> The KMIP JSON output file. When not specified the input file is overwritten

--unwrap-key-b64 [-k] <UNWRAP_KEY_B64> A symmetric key as a base 64 string to unwrap the imported key

--unwrap-key-id [-i] <UNWRAP_KEY_ID> The id of an unwrapping key in the KMS that will be exported and used to unwrap the key

--unwrap-key-file [-f] <UNWRAP_KEY_FILE> An unwrapping key in a KMIP JSON TTLV file used to unwrap the key


7.1.6 ckms fpe keys revoke

Revoke an FPE key

Usage

ckms fpe keys revoke [options] <REVOCATION_REASON>

Arguments

<REVOCATION_REASON> The reason for the revocation as a string

--reason-code [-r] <REASON_CODE> The revocation reason code [default: unspecified]

--key-id [-k] <KEY_ID> The key unique identifier of the key to revoke. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times


7.1.7 ckms fpe keys destroy

Destroy an FPE key

Usage

ckms fpe keys destroy [options]

Arguments

--key-id [-k] <KEY_ID> The key unique identifier. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times

--remove <REMOVE> If the key should be removed from the database If not specified, the key will be destroyed but its metadata will still be available in the database. Please note that the KMIP specification does not support the removal of objects.

Possible values: "true", "false" [default: "false"]


7.2 ckms fpe encrypt

Encrypt data using AES-256 FF1 format-preserving encryption through KMIP

Usage

ckms fpe encrypt [options] [FILE]

Arguments

--key-id [-k] <KEY_ID> The FPE key unique identifier

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times

--type <DATA_TYPE> The FPE data type

Possible values: "text", "integer", "float" [default: "text"]

--alphabet <ALPHABET> The alphabet to use for encryption/decryption

--tweak <TWEAK> Optional tweak bytes as a hex string

<FILE> Input file to read from. If not specified, reads from stdin

--output-file [-o] <OUTPUT_FILE> Write the output to a file instead of stdout


7.3 ckms fpe decrypt

Decrypt data using AES-256 FF1 format-preserving encryption through KMIP

Usage

ckms fpe decrypt [options] [FILE]

Arguments

--key-id [-k] <KEY_ID> The FPE key unique identifier

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times

--type <DATA_TYPE> The FPE data type

Possible values: "text", "integer", "float" [default: "text"]

--alphabet <ALPHABET> The alphabet to use for encryption/decryption

--tweak <TWEAK> Optional tweak bytes as a hex string

<FILE> Input file to read from. If not specified, reads from stdin

--output-file [-o] <OUTPUT_FILE> Write the output to a file instead of stdout


8 ckms pqc

Manage post-quantum keys (ML-KEM, ML-DSA, Hybrid KEM, SLH-DSA). Encapsulate, decapsulate, sign, and verify

Usage

ckms pqc <subcommand>

Subcommands

keys [8.1] Manage post-quantum keys (ML-KEM, ML-DSA)

encrypt [8.2] Encapsulate using a PQC public key (ML-KEM-512/768/1024, X25519MLKEM768, X448MLKEM1024)

decrypt [8.3] Decapsulate a KEM ciphertext using a private key (ML-KEM or Hybrid KEM)

sign [8.4] Sign data using a PQC private key (ML-DSA-44/65/87 or SLH-DSA).

sign-verify [8.5] Verify a PQC signature (ML-DSA or SLH-DSA) for a given data file.


8.1 ckms pqc keys

Manage post-quantum keys (ML-KEM, ML-DSA)

Usage

ckms pqc keys <subcommand>

Subcommands

activate [8.1.1] Activate a cryptographic object (key, certificate, etc.)

create [8.1.2] Create a new post-quantum key pair (ML-KEM or ML-DSA).

export [8.1.3] Export a key or secret data from the KMS

import [8.1.4] Import a secret data or a key in the KMS.

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

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

revoke [8.1.7] Revoke a PQC public or private key

destroy [8.1.8] Destroy a PQC public or private key

re-key [8.1.9] Rotate an existing asymmetric key pair, generating a new private/public key pair

set-rotation-policy [8.1.10] Set the automatic rotation policy on a key or key pair.

get-rotation-policy [8.1.11] Get the automatic rotation policy for a key or key pair.


8.1.1 ckms pqc keys activate

Activate a cryptographic object (key, certificate, etc.)

Usage

ckms pqc keys activate [options]

Arguments

--key-id [-k] <KEY_ID> The key unique identifier of the key to activate. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times


8.1.2 ckms pqc keys create

Create a new post-quantum key pair (ML-KEM or ML-DSA).

Usage

ckms pqc keys create [options]

Arguments

--algorithm [-a] <ALGORITHM> The PQC algorithm to use

Possible values: "ml-kem-512", "ml-kem-768", "ml-kem-1024", "ml-dsa-44", "ml-dsa-65", "ml-dsa-87", "x25519-ml-kem-768", "x448-ml-kem-1024", "slh-dsa-sha2-128s", "slh-dsa-sha2-128f", "slh-dsa-sha2-192s", "slh-dsa-sha2-192f", "slh-dsa-sha2-256s", "slh-dsa-sha2-256f", "slh-dsa-shake-128s", "slh-dsa-shake-128f", "slh-dsa-shake-192s", "slh-dsa-shake-192f", "slh-dsa-shake-256s", "slh-dsa-shake-256f", "ml-kem-512-p256", "ml-kem-768-p256", "ml-kem-512-curve25519", "ml-kem-768-curve25519"

--tag [-t] <TAG> Tag to associate with the key pair. To specify multiple tags, use the option multiple times

--sensitive <SENSITIVE> Sensitive: if set, the private key will not be exportable

Possible values: "true", "false" [default: "false"]

--enroll-keyset [-n] <ENROLL_KEYSET> Enroll this key in a keyset so it can be addressed via name@latest, name@first, name@N syntax. The keyset name is set automatically to the key's own ID returned by the server.

Possible values: "true", "false" [default: "false"]

--rotation-interval <ROTATE_INTERVAL> Rotation interval in seconds. The key will be automatically re-keyed at this interval. Set to 0 to disable automatic rotation while preserving other policy fields

--rotation-offset <ROTATE_OFFSET> Offset in seconds from the initial date before the first rotation occurs


8.1.3 ckms pqc keys export

Export a key or secret data from the KMS

Usage

ckms pqc keys export [options] <KEY_FILE>

Arguments

<KEY_FILE> The file to export the key to

--key-id [-k] <KEY_ID> The key or secret data unique identifier stored in the KMS. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key or secret data id is specified. To specify multiple tags, use the option multiple times

--key-format [-f] <EXPORT_FORMAT> The format of the key

  • json-ttlv [default]. It should be the format to use to later re-import the key
  • sec1-pem and sec1-deronly apply to NIST EC private keys (Not Curve25519 or X448)
  • pkcs1-pem and pkcs1-der only apply to RSA private and public keys
  • pkcs8-pem and pkcs8-der only apply to RSA and EC private keys
  • raw returns the raw bytes of
    • symmetric keys
    • Covercrypt keys
    • wrapped keys
    • secret data

Possible values: "json-ttlv", "sec1-pem", "sec1-der", "pkcs1-pem", "pkcs1-der", "pkcs8-pem", "pkcs8-der", "base64", "raw" [default: "json-ttlv"]

--unwrap [-u] <UNWRAP> Unwrap the key if it is wrapped before export

Possible values: "true", "false" [default: "false"]

--wrap-key-id [-w] <WRAP_KEY_ID> The id of the key/certificate (a.k.a. Key Encryption Key - KEK) to use to wrap this key before export

--allow-revoked [-i] <ALLOW_REVOKED> Allow exporting revoked and destroyed keys. The user must be the owner of the key. Destroyed keys have their key material removed.

Possible values: "true", "false" [default: "false"]

--wrapping-algorithm [-m] <WRAPPING_ALGORITHM> Wrapping algorithm to use when exporting the key The possible wrapping algorithms are

  • using a symmetric KEK:
    • nist-key-wrap (default - a.k.a RFC 5649, CKM_AES_KEY_WRAP_PAD)
    • aes-gcm
  • using an RSA KEK:
    • rsa-oaep (default - CKM-RSA-OAEP)
    • rsa-aes-key-wrap (CKM-RSA-AES-KEY-WRP)
    • rsa-pkcs-v15 (CKM-RSA v1.5)

Possible values: "aes-key-wrap-padding", "nist-key-wrap", "aes-gcm", "rsa-pkcs-v15-sha1", "rsa-pkcs-v15", "rsa-oaep-sha1", "rsa-oaep", "rsa-aes-key-wrap-sha1", "rsa-aes-key-wrap"

--authenticated-additional-data [-d] <AUTHENTICATED_ADDITIONAL_DATA> Authenticated encryption additional data Only available for AES GCM wrapping


8.1.4 ckms pqc keys import

Import a secret data or a key in the KMS.

Usage

ckms pqc keys import [options] <KEY_FILE> [KEY_ID]

Arguments

<KEY_FILE> The file holding the key or secret data to import

<KEY_ID> The unique ID of the key; a random UUID is generated if not specified

--key-format [-f] <KEY_FORMAT> The format of the key

Possible values: "json-ttlv", "pem", "sec1", "pkcs1-priv", "pkcs1-pub", "pkcs8-priv", "pkcs8-pub", "aes", "chacha20" [default: "json-ttlv"]

--public-key-id [-p] <PUBLIC_KEY_ID> For a private key: the corresponding KMS public key ID, if any

--private-key-id [-k] <PRIVATE_KEY_ID> For a public key: the corresponding KMS private key ID, if any

--certificate-id [-c] <CERTIFICATE_ID> For a public or private key: the corresponding certificate ID, if any

--unwrap [-u] <UNWRAP> In the case of a JSON TTLV key, unwrap the key if it is wrapped before storing it

Possible values: "true", "false" [default: "false"]

--replace [-r] <REPLACE_EXISTING> Replace an existing key under the same ID

Possible values: "true", "false" [default: "false"]

--tag [-t] <TAG> The tag to associate with the key. To specify multiple tags, use the option multiple times

--key-usage <KEY_USAGE> The cryptographic operations the key is allowed to perform

Possible values: "sign", "verify", "encrypt", "decrypt", "wrap-key", "unwrap-key", "mac-generate", "mac-verify", "derive-key", "key-agreement", "certificate-sign", "crl-sign", "authenticate", "unrestricted"

--wrapping-key-id [-w] <WRAPPING_KEY_ID> The key encryption key (KEK) used to wrap this imported key with. If the wrapping key is:

  • A symmetric key, AES-GCM will be used,
  • An RSA key, RSA-OAEP with SHA-256 will be used,
  • An EC key, ECIES will be used (salsa20poly1305 for X25519),

8.1.5 ckms pqc keys wrap

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

Usage

ckms pqc keys wrap [options] <KEY_FILE_IN> [KEY_FILE_OUT]

Arguments

<KEY_FILE_IN> The KMIP JSON TTLV input key file to wrap

<KEY_FILE_OUT> The KMIP JSON output file. When not specified, the input file is overwritten

--wrap-password [-p] <WRAP_PASSWORD> A password to wrap the imported key. This password will be derived into an AES-256 symmetric key. For security reasons, a fresh salt is internally generated by cosmian and handled, and this final AES symmetric key will be displayed only once

--wrap-key-b64 [-k] <WRAP_KEY_B64> A symmetric key as a base 64 string to wrap the imported key

--wrap-key-id [-i] <WRAP_KEY_ID> The ID of a wrapping key in the KMS that will be exported and used to wrap the key

--wrap-key-file [-f] <WRAP_KEY_FILE> A wrapping key in a KMIP JSON TTLV file used to wrap the key


8.1.6 ckms pqc keys unwrap

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

Usage

ckms pqc keys unwrap [options] <KEY_FILE_IN> [KEY_FILE_OUT]

Arguments

<KEY_FILE_IN> The KMIP JSON TTLV input key file to unwrap

<KEY_FILE_OUT> The KMIP JSON output file. When not specified the input file is overwritten

--unwrap-key-b64 [-k] <UNWRAP_KEY_B64> A symmetric key as a base 64 string to unwrap the imported key

--unwrap-key-id [-i] <UNWRAP_KEY_ID> The id of an unwrapping key in the KMS that will be exported and used to unwrap the key

--unwrap-key-file [-f] <UNWRAP_KEY_FILE> An unwrapping key in a KMIP JSON TTLV file used to unwrap the key


8.1.7 ckms pqc keys revoke

Revoke a PQC public or private key

Usage

ckms pqc keys revoke [options] <REVOCATION_REASON>

Arguments

<REVOCATION_REASON> The reason for the revocation as a string

--reason-code [-r] <REASON_CODE> The revocation reason code [default: unspecified]

--key-id [-k] <KEY_ID> The key unique identifier of the key to revoke

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified


8.1.8 ckms pqc keys destroy

Destroy a PQC public or private key

Usage

ckms pqc keys destroy [options]

Arguments

--key-id [-k] <KEY_ID> The key unique identifier of the key to destroy

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified

--remove <REMOVE> Remove the key from the database entirely

Possible values: "true", "false" [default: "false"]


8.1.9 ckms pqc keys re-key

Rotate an existing asymmetric key pair, generating a new private/public key pair

Usage

ckms pqc keys re-key [options]

Arguments

--key-id [-k] <KEY_ID> The unique identifier of the private key to re-key


8.1.10 ckms pqc keys set-rotation-policy

Set the automatic rotation policy on a key or key pair.

Usage

ckms pqc keys set-rotation-policy [options]

Arguments

--key-id [-k] <KEY_ID> The unique identifier of the key to set the rotation policy on

--interval [-i] <INTERVAL_SECS> Rotation interval in seconds. The key will be automatically re-keyed at this interval. Set to 0 to disable automatic rotation while preserving other policy fields

--offset [-o] <OFFSET_SECS> Offset in seconds from the initial date before the first rotation occurs

--rotation-name [-n] <ROTATE_NAME> A keyset name for addressing key generations via name@latest, name@first, name@N syntax. Must not contain the '@' character


8.1.11 ckms pqc keys get-rotation-policy

Get the automatic rotation policy for a key or key pair.

Usage

ckms pqc keys get-rotation-policy [options]

Arguments

--key-id [-k] <KEY_ID> The unique identifier of the key to get the rotation policy from


8.2 ckms pqc encrypt

Encapsulate using a PQC public key (ML-KEM-512/768/1024, X25519MLKEM768, X448MLKEM1024)

Usage

ckms pqc encrypt [options]

Arguments

--key-id [-k] <KEY_ID> The public key unique identifier

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified

--output-file [-o] <OUTPUT_FILE> The output file path for the encapsulation (ciphertext)


8.3 ckms pqc decrypt

Decapsulate a KEM ciphertext using a private key (ML-KEM or Hybrid KEM)

Usage

ckms pqc decrypt [options] <FILE>

Arguments

<FILE> The encapsulation file to decapsulate

--key-id [-k] <KEY_ID> The private key unique identifier

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified

--output-file [-o] <OUTPUT_FILE> The output file path for the shared secret


8.4 ckms pqc sign

Sign data using a PQC private key (ML-DSA-44/65/87 or SLH-DSA).

Usage

ckms pqc sign [options] <FILE>

Arguments

<FILE> The file to sign

--key-id [-k] <KEY_ID> The private key unique identifier

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified

--output-file [-o] <OUTPUT_FILE> The signature output file path


8.5 ckms pqc sign-verify

Verify a PQC signature (ML-DSA or SLH-DSA) for a given data file.

Usage

ckms pqc sign-verify [options] <FILE> <SIGNATURE_FILE>

Arguments

<FILE> The data that was signed

<SIGNATURE_FILE> The signature file

--key-id [-k] <KEY_ID> The public key unique identifier

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified


9 ckms tokenize

Anonymization utilities: hash, noise, word masking, pattern masking, aggregation, and scaling

Usage

ckms tokenize <subcommand>

Subcommands

hash [9.1] Hash a string with SHA2, SHA3, or Argon2

noise [9.2] Add statistical noise to a number or date

word-mask [9.3] Replace sensitive words with "XXXX"

word-tokenize [9.4] Replace sensitive words with consistent random hex tokens

word-pattern-mask [9.5] Replace regex-matched substrings with a replacement string

aggregate-number [9.6] Round a number to the nearest power of ten

aggregate-date [9.7] Truncate a date to a specified time unit

scale-number [9.8] Normalize and scale a number using z-score transformation


9.1 ckms tokenize hash

Hash a string with SHA2, SHA3, or Argon2

Usage

ckms tokenize hash [options]

Arguments

--data [-d] <DATA> Input string to hash

--method [-m] <METHOD> Hash algorithm: sha2, sha3, or argon2

--salt <SALT> Optional hex-encoded salt bytes


9.2 ckms tokenize noise

Add statistical noise to a number or date

Usage

ckms tokenize noise [options]

Arguments

--data [-d] <DATA> Input value (float, integer, or RFC3339 date string)

--data-type [-t] <DATA_TYPE> Data type: float, integer, or date

--method [-m] <METHOD> Noise distribution: Gaussian, Laplace, or Uniform

--mean <MEAN> Distribution mean (required for Gaussian/Laplace with parameters mode)

--std-dev <STD_DEV> Standard deviation (required for Gaussian/Laplace with parameters mode)

--min-bound <MIN_BOUND> Lower bound (required for bounds mode or Uniform)

--max-bound <MAX_BOUND> Upper bound (required for bounds mode or Uniform)


9.3 ckms tokenize word-mask

Replace sensitive words with "XXXX"

Usage

ckms tokenize word-mask [options]

Arguments

--data [-d] <DATA> Input text

--word [-w] <WORDS> Words to mask. Repeat for multiple: --word foo --word bar


9.4 ckms tokenize word-tokenize

Replace sensitive words with consistent random hex tokens

Usage

ckms tokenize word-tokenize [options]

Arguments

--data [-d] <DATA> Input text

--word [-w] <WORDS> Words to tokenize. Repeat for multiple: --word foo --word bar


9.5 ckms tokenize word-pattern-mask

Replace regex-matched substrings with a replacement string

Usage

ckms tokenize word-pattern-mask [options]

Arguments

--data [-d] <DATA> Input text

--pattern [-p] <PATTERN> Regular expression pattern (max 1024 chars)

--replace [-r] <REPLACE> Replacement string


9.6 ckms tokenize aggregate-number

Round a number to the nearest power of ten

Usage

ckms tokenize aggregate-number [options]

Arguments

--data [-d] <DATA> Number to round

--data-type [-t] <DATA_TYPE> Data type: float or integer

--power-of-ten [-p] <POWER_OF_TEN> Power of ten (e.g., 2 rounds to the nearest 100)


9.7 ckms tokenize aggregate-date

Truncate a date to a specified time unit

Usage

ckms tokenize aggregate-date [options]

Arguments

--data [-d] <DATA> RFC3339 date string (e.g. "2024-07-15T13:45:00Z")

--time-unit [-u] <TIME_UNIT> Time unit precision: Second, Minute, Hour, Day, Month, or Year


9.8 ckms tokenize scale-number

Normalize and scale a number using z-score transformation

Usage

ckms tokenize scale-number [options]

Arguments

--data [-d] <DATA> Number to scale

--data-type [-t] <DATA_TYPE> Data type: float or integer

--mean <MEAN> Mean of the original data distribution

--std-deviation <STD_DEVIATION> Standard deviation of the original data distribution (must be non-zero)

--scale <SCALE> Scaling factor

--translate <TRANSLATE> Translation factor


10 ckms certificates

Manage certificates. Create, import, destroy and revoke. Encrypt and decrypt data

Usage

ckms certificates <subcommand>

Subcommands

activate [10.1] Activate a cryptographic object (key, certificate, etc.)

certify [10.2] Issue or renew a X509 certificate

decrypt [10.3] Decrypt a file using the private key of a certificate

encrypt [10.4] Encrypt a file using the certificate public key

export [10.5] Export a certificate from the KMS

import [10.6] Import one of the following:

  • a certificate: formatted as a X509 PEM (pem), X509 DER (der) or JSON TTLV (json-ttlv)
  • a certificate chain as a PEM-stack (chain)
  • a PKCS12 file containing a certificate, a private key and possibly a chain (pkcs12)
  • the Mozilla Common CA Database (CCADB - fetched by the CLI before import) (ccadb)

revoke [10.7] Revoke a certificate

destroy [10.8] Destroy a certificate

set-rotation-policy [10.9] Set the automatic rotation policy on a certificate (interval, offset, keyset name)

validate [10.10] Validate a certificate


10.1 ckms certificates activate

Activate a cryptographic object (key, certificate, etc.)

Usage

ckms certificates activate [options]

Arguments

--key-id [-k] <KEY_ID> The key unique identifier of the key to activate. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times


10.2 ckms certificates certify

Issue or renew a X509 certificate

Usage

ckms certificates certify [options]

Arguments

--certificate-id [-c] <CERTIFICATE_ID> The unique identifier of the certificate to issue or renew. If not provided, a random one will be generated when issuing a certificate, or the original one will be used when renewing a certificate

--certificate-signing-request [-r] <CERTIFICATE_SIGNING_REQUEST> The path to a certificate signing request

--certificate-signing-request-format [-f] <CERTIFICATE_SIGNING_REQUEST_FORMAT> The format of the certificate signing request

Possible values: "pem", "der" [default: "pem"]

--public-key-id-to-certify [-p] <PUBLIC_KEY_ID_TO_CERTIFY> The id of a public key to certify

--certificate-id-to-re-certify [-n] <CERTIFICATE_ID_TO_RE_CERTIFY> The id of a certificate to re-certify

--generate-key-pair [-g] <GENERATE_KEY_PAIR> Generate a keypair then sign the public key and generate a certificate

Possible values: "true", "false"

--subject-name [-s] <SUBJECT_NAME> When certifying a public key, or generating a keypair, the subject name to use.

--algorithm [-a] <ALGORITHM> The algorithm to use for the keypair generation

Possible values: "nist-p192", "nist-p224", "nist-p256", "nist-p384", "nist-p521", "ed25519", "ed448", "rsa1024", "rsa2048", "rsa3072", "rsa4096", "ml-dsa-44", "ml-dsa-65", "ml-dsa-87", "slh-dsa-sha2-128s", "slh-dsa-sha2-128f", "slh-dsa-sha2-192s", "slh-dsa-sha2-192f", "slh-dsa-sha2-256s", "slh-dsa-sha2-256f", "slh-dsa-shake-128s", "slh-dsa-shake-128f", "slh-dsa-shake-192s", "slh-dsa-shake-192f", "slh-dsa-shake-256s", "slh-dsa-shake-256f", "ml-kem-512", "ml-kem-768", "ml-kem-1024", "x25519-ml-kem-768", "x448-ml-kem-1024", "ml-kem-512-p256", "ml-kem-768-p256", "ml-kem-512-curve25519", "ml-kem-768-curve25519" [default: "rsa4096"]

--issuer-private-key-id [-k] <ISSUER_PRIVATE_KEY_ID> The unique identifier of the private key of the issuer. A certificate must be linked to that private key if no issuer certificate id is provided

--issuer-certificate-id [-i] <ISSUER_CERTIFICATE_ID> The unique identifier of the certificate of the issuer. A private key must be linked to that certificate if no issuer private key id is provided

--days [-d] <NUMBER_OF_DAYS> The requested number of validity days The server may grant a different value

--certificate-extensions [-e] <CERTIFICATE_EXTENSIONS> The path to a X509 extension's file, containing a v3_ca paragraph with the x509 extensions to use. For instance:

--tag [-t] <TAG> The tag to associate to the certificate. To specify multiple tags, use the option multiple times


10.3 ckms certificates decrypt

Decrypt a file using the private key of a certificate

Usage

ckms certificates decrypt [options] <FILE>

Arguments

<FILE> The file to decrypt

--key-id [-k] <PRIVATE_KEY_ID> The private key unique identifier related to certificate If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times

--output-file [-o] <OUTPUT_FILE> The encrypted output file path

--authentication-data [-a] <AUTHENTICATION_DATA> Optional authentication data that was supplied during encryption

--encryption-algorithm [-e] <ENCRYPTION_ALGORITHM> Optional encryption algorithm. This is only available for RSA keys for now. The default for RSA is PKCS_OAEP.

Possible values: "ckm-rsa-pkcs", "ckm-rsa-pkcs-oaep", "ckm-rsa-aes-key-wrap"


10.4 ckms certificates encrypt

Encrypt a file using the certificate public key

Usage

ckms certificates encrypt [options] <FILE>

Arguments

<FILE> The file to encrypt

--certificate-id [-c] <CERTIFICATE_ID> The certificate unique identifier. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times

--output-file [-o] <OUTPUT_FILE> The encrypted output file path

--authentication-data [-a] <AUTHENTICATION_DATA> Optional authentication data. This data needs to be provided back for decryption

--encryption-algorithm [-e] <ENCRYPTION_ALGORITHM> Optional encryption algorithm. This is only available for RSA keys for now. The default for RSA is PKCS_OAEP.

Possible values: "ckm-rsa-pkcs", "ckm-rsa-pkcs-oaep", "ckm-rsa-aes-key-wrap"


10.5 ckms certificates export

Export a certificate from the KMS

Usage

ckms certificates export [options] <CERTIFICATE_FILE>

Arguments

<CERTIFICATE_FILE> The file to export the certificate to

--certificate-id [-c] <CERTIFICATE_ID> The certificate unique identifier stored in the KMS; for PKCS#12, provide the private key id If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the certificate/private key when no unique id is specified. To specify multiple tags, use the option multiple times.

--format [-f] <OUTPUT_FORMAT> Export the certificate in the selected format

Possible values: "json-ttlv", "pem", "pkcs12", "pkcs12-legacy", "pkcs7" [default: "json-ttlv"]

--pkcs12-password [-p] <PKCS12_PASSWORD> Password to use to protect the PKCS#12 file

--allow-revoked [-r] <ALLOW_REVOKED> Allow exporting revoked and destroyed certificates or private key (for PKCS#12). The user must be the owner of the certificate. Destroyed objects have their key material removed.

Possible values: "true", "false" [default: "false"]


10.6 ckms certificates import

Import one of the following:

  • a certificate: formatted as a X509 PEM (pem), X509 DER (der) or JSON TTLV (json-ttlv)
  • a certificate chain as a PEM-stack (chain)
  • a PKCS12 file containing a certificate, a private key and possibly a chain (pkcs12)
  • the Mozilla Common CA Database (CCADB - fetched by the CLI before import) (ccadb)

Usage

ckms certificates import [options] [CERTIFICATE_FILE] [CERTIFICATE_ID]

Arguments

<CERTIFICATE_FILE> The input file in PEM, KMIP-JSON-TTLV or PKCS#12 format

<CERTIFICATE_ID> The unique id of the leaf certificate; a unique id based on the key material is generated if not specified. When importing a PKCS12, the unique id will be that of the private key.

--format [-f] <INPUT_FORMAT> Import the certificate in the selected format

Possible values: "json-ttlv", "pem", "der", "chain", "pkcs12", "ccadb" [default: "json-ttlv"]

--private-key-id [-k] <PRIVATE_KEY_ID> The corresponding private key id if any. Ignored for PKCS12 and CCADB formats

--public-key-id [-q] <PUBLIC_KEY_ID> The corresponding public key id if any. Ignored for PKCS12 and CCADB formats

--issuer-certificate-id [-i] <ISSUER_CERTIFICATE_ID> The issuer certificate id if any. Ignored for PKCS12 and CCADB formats

--pkcs12-password [-p] <PKCS12_PASSWORD> PKCS12 password: only available for PKCS12 format

--replace [-r] <REPLACE_EXISTING> Replace an existing certificate under the same id

Possible values: "true", "false" [default: "false"]

--tag [-t] <TAG> The tag to associate with the certificate. To specify multiple tags, use the option multiple times

--key-usage <KEY_USAGE> For what operations should the certificate be used

Possible values: "sign", "verify", "encrypt", "decrypt", "wrap-key", "unwrap-key", "mac-generate", "mac-verify", "derive-key", "key-agreement", "certificate-sign", "crl-sign", "authenticate", "unrestricted"


10.7 ckms certificates revoke

Revoke a certificate

Usage

ckms certificates revoke [options] <REVOCATION_REASON>

Arguments

<REVOCATION_REASON> The reason for the revocation as a string

--reason-code [-r] <REASON_CODE> The revocation reason code [default: unspecified]

--certificate-id [-c] <CERTIFICATE_ID> The certificate unique identifier of the certificate to revoke. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the certificate when no certificate id is specified. To specify multiple tags, use the option multiple times


10.8 ckms certificates destroy

Destroy a certificate

Usage

ckms certificates destroy [options]

Arguments

--certificate-id [-c] <CERTIFICATE_ID> The certificate unique identifier. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the certificate when no certificate id is specified. To specify multiple tags, use the option multiple times

--remove <REMOVE> If the certificate should be removed from the database If not specified, the certificate will be destroyed but its metadata will still be available in the database. Please note that the KMIP specification does not support the removal of objects.

Possible values: "true", "false" [default: "false"]


10.9 ckms certificates set-rotation-policy

Set the automatic rotation policy on a certificate (interval, offset, keyset name)

Usage

ckms certificates set-rotation-policy [options]

Arguments

--key-id [-k] <KEY_ID> The unique identifier of the key to set the rotation policy on

--interval [-i] <INTERVAL_SECS> Rotation interval in seconds. The key will be automatically re-keyed at this interval. Set to 0 to disable automatic rotation while preserving other policy fields

--offset [-o] <OFFSET_SECS> Offset in seconds from the initial date before the first rotation occurs

--rotation-name [-n] <ROTATE_NAME> A keyset name for addressing key generations via name@latest, name@first, name@N syntax. Must not contain the '@' character


10.10 ckms certificates validate

Validate a certificate

Usage

ckms certificates validate [options]

Arguments

--certificate-id [-k] <CERTIFICATE_ID> One or more Unique Identifiers of Certificate Objects

--validity-time [-t] <VALIDITY_TIME> A Date-Time object indicating when the certificate chain needs to be valid. If omitted, the current date and time SHALL be assumed


11 ckms cng

Manage the Windows CNG Key Storage Provider (KSP)

Usage

ckms cng <subcommand>

Subcommands

register [11.1] Register the Cosmian KMS CNG Key Storage Provider DLL in the Windows Registry

unregister [11.2] Unregister the Cosmian KMS CNG Key Storage Provider from the Windows Registry

status [11.3] Show the CNG KSP registration status (is the provider registered?)

list-keys [11.4] List all private keys stored in Cosmian KMS that belong to this CNG KSP

verify [11.5] Load the CNG KSP DLL and exercise all NCrypt function-table entry points


11.1 ckms cng register

Register the Cosmian KMS CNG Key Storage Provider DLL in the Windows Registry

Usage

ckms cng register [options]

Arguments

--dll [-d] <DLL> Full path to the cosmian_cng.dll file


11.2 ckms cng unregister

Unregister the Cosmian KMS CNG Key Storage Provider from the Windows Registry

Usage

ckms cng unregister


11.3 ckms cng status

Show the CNG KSP registration status (is the provider registered?)

Usage

ckms cng status


11.4 ckms cng list-keys

List all private keys stored in Cosmian KMS that belong to this CNG KSP

Usage

ckms cng list-keys


11.5 ckms cng verify

Load the CNG KSP DLL and exercise all NCrypt function-table entry points

Usage

ckms cng verify [options]

Arguments

--dll [-d] <DLL> Full path to the cosmian_cng.dll file to verify


12 ckms derive-key

Derive a new key from an existing key

Usage

ckms derive-key [options]

Arguments

--key-id [-k] <KEY_ID> The unique identifier of the base key to derive from Mutually exclusive with --password

--password [-p] <PASSWORD> UTF-8 password to use as base material for key derivation Will create a SecretData of type Password internally Mutually exclusive with --key-id

--derivation-method [-m] <DERIVATION_METHOD> The derivation method to use (PBKDF2 or HKDF)

--salt [-s] <SALT> Salt for key derivation (in hex format)

--iteration-count [-i] <ITERATION_COUNT> Number of iterations for PBKDF2 derivation

--initialization-vector [-v] <INITIALIZATION_VECTOR> Initialization vector for derivation (in hex format)

--digest-algorithm [-d] <DIGEST_ALGORITHM> Digest algorithm for derivation

Possible values: "sha1", "sha224", "sha256", "sha384", "sha512", "sha3-224", "sha3-256", "sha3-384", "sha3-512" [default: "SHA256"]

--algorithm [-a] <ALGORITHM> The algorithm

Possible values: "chacha20", "aes", "sha3", "shake" [default: "aes"]

--length [-l] <CRYPTOGRAPHIC_LENGTH> Length of the derived key in bits

--derived-key-id <DERIVED_KEY_ID> Optional unique identifier for the derived key


13 ckms ec

Manage elliptic curve keys. Encrypt and decrypt data using ECIES

Usage

ckms ec <subcommand>

Subcommands

keys [13.1] Create, destroy, import, and export elliptic curve key pairs

encrypt [13.2] Encrypt a file with the given public key using ECIES

decrypt [13.3] Decrypts a file with the given private key using ECIES

sign [13.4] Sign a file using elliptic curve digital signature algorithms (ECDSA)

sign-verify [13.5] Verify an ECDSA signature for a given data file


13.1 ckms ec keys

Create, destroy, import, and export elliptic curve key pairs

Usage

ckms ec keys <subcommand>

Subcommands

activate [13.1.1] Activate a cryptographic object (key, certificate, etc.)

create [13.1.2] Create an elliptic curve key pair

export [13.1.3] Export a key or secret data from the KMS

import [13.1.4] Import a secret data or a key in the KMS.

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

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

revoke [13.1.7] Revoke a public or private key

destroy [13.1.8] Destroy a public or private key

re-key [13.1.9] Rotate an existing asymmetric key pair, generating a new private/public key pair

set-rotation-policy [13.1.10] Set the automatic rotation policy on a key or key pair.

get-rotation-policy [13.1.11] Get the automatic rotation policy for a key or key pair.


13.1.1 ckms ec keys activate

Activate a cryptographic object (key, certificate, etc.)

Usage

ckms ec keys activate [options]

Arguments

--key-id [-k] <KEY_ID> The key unique identifier of the key to activate. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times


13.1.2 ckms ec keys create

Create an elliptic curve key pair

Usage

ckms ec keys create [options] [PRIVATE_KEY_ID]

Arguments

--curve [-c] <CURVE> The elliptic curve

Possible values: "nist-p256", "nist-p384", "nist-p521", "x25519", "ed25519", "x448", "ed448", "secp256k1", "secp224k1" [default: "nist-p256"]

--tag [-t] <TAG> The tag to associate with the master key pair. To specify multiple tags, use the option multiple times

<PRIVATE_KEY_ID> The unique id of the private key; a random uuid is generated if not specified

--sensitive <SENSITIVE> Sensitive: if set, the key will not be exportable

Possible values: "true", "false" [default: "false"]

--wrapping-key-id [-w] <WRAPPING_KEY_ID> The key encryption key (KEK) used to wrap the keypair with. If the wrapping key is:

  • a symmetric key, AES-GCM will be used
  • a RSA key, RSA-OAEP will be used
  • a EC key, ECIES will be used (salsa20poly1305 for X25519)

--enroll-keyset [-n] <ENROLL_KEYSET> Enroll this key in a keyset so it can be addressed via name@latest, name@first, name@N syntax. The keyset name is set automatically to the key's own ID returned by the server.

Possible values: "true", "false" [default: "false"]

--rotation-interval <ROTATE_INTERVAL> Rotation interval in seconds. The key will be automatically re-keyed at this interval. Set to 0 to disable automatic rotation while preserving other policy fields

--rotation-offset <ROTATE_OFFSET> Offset in seconds from the initial date before the first rotation occurs


13.1.3 ckms ec keys export

Export a key or secret data from the KMS

Usage

ckms ec keys export [options] <KEY_FILE>

Arguments

<KEY_FILE> The file to export the key to

--key-id [-k] <KEY_ID> The key or secret data unique identifier stored in the KMS. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key or secret data id is specified. To specify multiple tags, use the option multiple times

--key-format [-f] <EXPORT_FORMAT> The format of the key

  • json-ttlv [default]. It should be the format to use to later re-import the key
  • sec1-pem and sec1-deronly apply to NIST EC private keys (Not Curve25519 or X448)
  • pkcs1-pem and pkcs1-der only apply to RSA private and public keys
  • pkcs8-pem and pkcs8-der only apply to RSA and EC private keys
  • raw returns the raw bytes of
    • symmetric keys
    • Covercrypt keys
    • wrapped keys
    • secret data

Possible values: "json-ttlv", "sec1-pem", "sec1-der", "pkcs1-pem", "pkcs1-der", "pkcs8-pem", "pkcs8-der", "base64", "raw" [default: "json-ttlv"]

--unwrap [-u] <UNWRAP> Unwrap the key if it is wrapped before export

Possible values: "true", "false" [default: "false"]

--wrap-key-id [-w] <WRAP_KEY_ID> The id of the key/certificate (a.k.a. Key Encryption Key - KEK) to use to wrap this key before export

--allow-revoked [-i] <ALLOW_REVOKED> Allow exporting revoked and destroyed keys. The user must be the owner of the key. Destroyed keys have their key material removed.

Possible values: "true", "false" [default: "false"]

--wrapping-algorithm [-m] <WRAPPING_ALGORITHM> Wrapping algorithm to use when exporting the key The possible wrapping algorithms are

  • using a symmetric KEK:
    • nist-key-wrap (default - a.k.a RFC 5649, CKM_AES_KEY_WRAP_PAD)
    • aes-gcm
  • using an RSA KEK:
    • rsa-oaep (default - CKM-RSA-OAEP)
    • rsa-aes-key-wrap (CKM-RSA-AES-KEY-WRP)
    • rsa-pkcs-v15 (CKM-RSA v1.5)

Possible values: "aes-key-wrap-padding", "nist-key-wrap", "aes-gcm", "rsa-pkcs-v15-sha1", "rsa-pkcs-v15", "rsa-oaep-sha1", "rsa-oaep", "rsa-aes-key-wrap-sha1", "rsa-aes-key-wrap"

--authenticated-additional-data [-d] <AUTHENTICATED_ADDITIONAL_DATA> Authenticated encryption additional data Only available for AES GCM wrapping


13.1.4 ckms ec keys import

Import a secret data or a key in the KMS.

Usage

ckms ec keys import [options] <KEY_FILE> [KEY_ID]

Arguments

<KEY_FILE> The file holding the key or secret data to import

<KEY_ID> The unique ID of the key; a random UUID is generated if not specified

--key-format [-f] <KEY_FORMAT> The format of the key

Possible values: "json-ttlv", "pem", "sec1", "pkcs1-priv", "pkcs1-pub", "pkcs8-priv", "pkcs8-pub", "aes", "chacha20" [default: "json-ttlv"]

--public-key-id [-p] <PUBLIC_KEY_ID> For a private key: the corresponding KMS public key ID, if any

--private-key-id [-k] <PRIVATE_KEY_ID> For a public key: the corresponding KMS private key ID, if any

--certificate-id [-c] <CERTIFICATE_ID> For a public or private key: the corresponding certificate ID, if any

--unwrap [-u] <UNWRAP> In the case of a JSON TTLV key, unwrap the key if it is wrapped before storing it

Possible values: "true", "false" [default: "false"]

--replace [-r] <REPLACE_EXISTING> Replace an existing key under the same ID

Possible values: "true", "false" [default: "false"]

--tag [-t] <TAG> The tag to associate with the key. To specify multiple tags, use the option multiple times

--key-usage <KEY_USAGE> The cryptographic operations the key is allowed to perform

Possible values: "sign", "verify", "encrypt", "decrypt", "wrap-key", "unwrap-key", "mac-generate", "mac-verify", "derive-key", "key-agreement", "certificate-sign", "crl-sign", "authenticate", "unrestricted"

--wrapping-key-id [-w] <WRAPPING_KEY_ID> The key encryption key (KEK) used to wrap this imported key with. If the wrapping key is:

  • A symmetric key, AES-GCM will be used,
  • An RSA key, RSA-OAEP with SHA-256 will be used,
  • An EC key, ECIES will be used (salsa20poly1305 for X25519),

13.1.5 ckms ec keys wrap

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

Usage

ckms ec keys wrap [options] <KEY_FILE_IN> [KEY_FILE_OUT]

Arguments

<KEY_FILE_IN> The KMIP JSON TTLV input key file to wrap

<KEY_FILE_OUT> The KMIP JSON output file. When not specified, the input file is overwritten

--wrap-password [-p] <WRAP_PASSWORD> A password to wrap the imported key. This password will be derived into an AES-256 symmetric key. For security reasons, a fresh salt is internally generated by cosmian and handled, and this final AES symmetric key will be displayed only once

--wrap-key-b64 [-k] <WRAP_KEY_B64> A symmetric key as a base 64 string to wrap the imported key

--wrap-key-id [-i] <WRAP_KEY_ID> The ID of a wrapping key in the KMS that will be exported and used to wrap the key

--wrap-key-file [-f] <WRAP_KEY_FILE> A wrapping key in a KMIP JSON TTLV file used to wrap the key


13.1.6 ckms ec keys unwrap

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

Usage

ckms ec keys unwrap [options] <KEY_FILE_IN> [KEY_FILE_OUT]

Arguments

<KEY_FILE_IN> The KMIP JSON TTLV input key file to unwrap

<KEY_FILE_OUT> The KMIP JSON output file. When not specified the input file is overwritten

--unwrap-key-b64 [-k] <UNWRAP_KEY_B64> A symmetric key as a base 64 string to unwrap the imported key

--unwrap-key-id [-i] <UNWRAP_KEY_ID> The id of an unwrapping key in the KMS that will be exported and used to unwrap the key

--unwrap-key-file [-f] <UNWRAP_KEY_FILE> An unwrapping key in a KMIP JSON TTLV file used to unwrap the key


13.1.7 ckms ec keys revoke

Revoke a public or private key

Usage

ckms ec keys revoke [options] <REVOCATION_REASON>

Arguments

<REVOCATION_REASON> The reason for the revocation as a string

--reason-code [-r] <REASON_CODE> The revocation reason code [default: unspecified]

--key-id [-k] <KEY_ID> The key unique identifier of the key to revoke. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times


13.1.8 ckms ec keys destroy

Destroy a public or private key

Usage

ckms ec keys destroy [options]

Arguments

--key-id [-k] <KEY_ID> The key unique identifier of the key to destroy If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times

--remove <REMOVE> If the key should be removed from the database If not specified, the key will be destroyed but its metadata will still be available in the database. Please note that the KMIP specification does not support the removal of objects.

Possible values: "true", "false" [default: "false"]


13.1.9 ckms ec keys re-key

Rotate an existing asymmetric key pair, generating a new private/public key pair

Usage

ckms ec keys re-key [options]

Arguments

--key-id [-k] <KEY_ID> The unique identifier of the private key to re-key


13.1.10 ckms ec keys set-rotation-policy

Set the automatic rotation policy on a key or key pair.

Usage

ckms ec keys set-rotation-policy [options]

Arguments

--key-id [-k] <KEY_ID> The unique identifier of the key to set the rotation policy on

--interval [-i] <INTERVAL_SECS> Rotation interval in seconds. The key will be automatically re-keyed at this interval. Set to 0 to disable automatic rotation while preserving other policy fields

--offset [-o] <OFFSET_SECS> Offset in seconds from the initial date before the first rotation occurs

--rotation-name [-n] <ROTATE_NAME> A keyset name for addressing key generations via name@latest, name@first, name@N syntax. Must not contain the '@' character


13.1.11 ckms ec keys get-rotation-policy

Get the automatic rotation policy for a key or key pair.

Usage

ckms ec keys get-rotation-policy [options]

Arguments

--key-id [-k] <KEY_ID> The unique identifier of the key to get the rotation policy from


13.2 ckms ec encrypt

Encrypt a file with the given public key using ECIES

Usage

ckms ec encrypt [options] <FILE>

Arguments

<FILE> The file to encrypt

--key-id [-k] <KEY_ID> The public key unique identifier. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times

--output-file [-o] <OUTPUT_FILE> The encrypted output file path


13.3 ckms ec decrypt

Decrypts a file with the given private key using ECIES

Usage

ckms ec decrypt [options] <FILE>

Arguments

<FILE> The file to decrypt

--key-id [-k] <KEY_ID> The private key unique identifier If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times

--output-file [-o] <OUTPUT_FILE> The encrypted output file path


13.4 ckms ec sign

Sign a file using elliptic curve digital signature algorithms (ECDSA)

Usage

ckms ec sign [options] <FILE>

Arguments

--curve [-c] <CURVE> The elliptic curve

Possible values: "nist-p256", "nist-p384", "nist-p521", "x25519", "ed25519", "x448", "ed448", "secp256k1", "secp224k1" [default: "nist-p256"]

<FILE> The file to sign

--key-id [-k] <KEY_ID> The private key unique identifier If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times

--output-file [-o] <OUTPUT_FILE> The signature output file path

--digested <DIGESTED> Treat input as already-digested data (pre-hash)

Possible values: "true", "false"


13.5 ckms ec sign-verify

Verify an ECDSA signature for a given data file

Usage

ckms ec sign-verify [options] <FILE> <SIGNATURE_FILE>

Arguments

<FILE> The data that was signed

<SIGNATURE_FILE> The signature file

--key-id [-k] <KEY_ID> The private key unique identifier If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times

--output-file [-o] <OUTPUT_FILE> Optional output file path

--digested <DIGESTED> Treat data input as already-digested (pre-hash)

Possible values: "true", "false"


14 ckms google

Manage google elements. Handle key pairs and identities from Gmail API

Usage

ckms google <subcommand>

Subcommands

key-pairs [14.1] Insert, get, list, enable, disabled and obliterate key pairs to Gmail API

identities [14.2] Insert, get, list, patch and delete identities from Gmail API


14.1 ckms google key-pairs

Insert, get, list, enable, disabled and obliterate key pairs to Gmail API

Usage

ckms google key-pairs <subcommand>

Subcommands

get [14.1.1] Retrieves an existing client-side encryption key pair.

list [14.1.2] Lists client-side encryption key pairs for a user.

enable [14.1.3] Turns on a client-side encryption key pair that was turned off. The key pair becomes active again for any associated client-side encryption identities.

disable [14.1.4] Turns off a client-side encryption key pair. The authenticated user can no longer use the key pair to decrypt incoming CSE message texts or sign outgoing CSE mail. To regain access, use the key pairs.enable to turn on the key pair. After 30 days, you can permanently delete the key pair by using the key pairs.obliterate method.

obliterate [14.1.5] Deletes a client-side encryption key pair permanently and immediately. You can only permanently delete key pairs that have been turned off for more than 30 days. To turn off a key pair, use the key pairs disable method. Gmail can't restore or decrypt any messages that were encrypted by an obliterated key. Authenticated users and Google Workspace administrators lose access to reading the encrypted messages.

create [14.1.6] Creates and uploads a client-side encryption S/MIME public key certificate chain and private key metadata for a user.


14.1.1 ckms google key-pairs get

Retrieves an existing client-side encryption key pair.

Usage

ckms google key-pairs get [options] <KEY_PAIRS_ID>

Arguments

<KEY_PAIRS_ID> The identifier of the key pair to retrieve

--user-id [-u] <USER_ID> The requester's primary email address


14.1.2 ckms google key-pairs list

Lists client-side encryption key pairs for a user.

Usage

ckms google key-pairs list [options] <USER_ID>

Arguments

<USER_ID> The requester's primary email address


14.1.3 ckms google key-pairs enable

Turns on a client-side encryption key pair that was turned off. The key pair becomes active again for any associated client-side encryption identities.

Usage

ckms google key-pairs enable [options] <KEY_PAIRS_ID>

Arguments

<KEY_PAIRS_ID> The identifier of the key pair to enable

--user-id [-u] <USER_ID> The requester's primary email address


14.1.4 ckms google key-pairs disable

Turns off a client-side encryption key pair. The authenticated user can no longer use the key pair to decrypt incoming CSE message texts or sign outgoing CSE mail. To regain access, use the key pairs.enable to turn on the key pair. After 30 days, you can permanently delete the key pair by using the key pairs.obliterate method.

Usage

ckms google key-pairs disable [options] <KEY_PAIRS_ID>

Arguments

<KEY_PAIRS_ID> The identifier of the key pair to disable

--user-id [-u] <USER_ID> The requester's primary email address


14.1.5 ckms google key-pairs obliterate

Deletes a client-side encryption key pair permanently and immediately. You can only permanently delete key pairs that have been turned off for more than 30 days. To turn off a key pair, use the key pairs disable method. Gmail can't restore or decrypt any messages that were encrypted by an obliterated key. Authenticated users and Google Workspace administrators lose access to reading the encrypted messages.

Usage

ckms google key-pairs obliterate [options] <KEY_PAIRS_ID>

Arguments

<KEY_PAIRS_ID> The identifier of the key pair to obliterate

--user-id [-u] <USER_ID> The requester's primary email address


14.1.6 ckms google key-pairs create

Creates and uploads a client-side encryption S/MIME public key certificate chain and private key metadata for a user.

Usage

ckms google key-pairs create [options] <USER_ID>

Arguments

<USER_ID> The requester's primary email address

--cse-key-id <CSE_KEY_ID> CSE key ID to wrap exported user private key

--subject-name [-s] <SUBJECT_NAME> When certifying a public key, or generating a keypair, the subject name to use. For instance: "CN=John Doe,OU=Org Unit,O=Org Name,L=City,ST=State,C=US"

--rsa-private-key-id [-k] <RSA_PRIVATE_KEY_ID> The existing private key id of an existing RSA keypair to use (optional - if no ID is provided, a RSA keypair will be created)

--sensitive <SENSITIVE> Sensitive: if set, the key will not be exportable

Possible values: "true", "false" [default: "false"]

--wrapping-key-id [-w] <WRAPPING_KEY_ID> The key encryption key (KEK) used to wrap the keypair with. If the wrapping key is:

  • a symmetric key, AES-GCM will be used
  • a RSA key, RSA-OAEP will be used
  • a EC key, ECIES will be used (salsa20poly1305 for X25519)

--issuer-private-key-id [-i] <ISSUER_PRIVATE_KEY_ID> The issuer private key id - required when generating a new leaf certificate

--leaf-certificate-extensions [-e] <LEAF_CERTIFICATE_EXTENSIONS> Path to a file containing X.509 extensions, defined under a [v3_ca] section. These extensions will be applied to the generated leaf certificate and must comply with Google's S/MIME certificate requirements. For example:

[ v3_ca ]
keyUsage=nonRepudiation,digitalSignature,dataEncipherment,keyEncipherment
extendedKeyUsage=emailProtection
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer

This parameter is ignored when using an existing leaf certificate.

--leaf-certificate-id <LEAF_CERTIFICATE_ID> The ID of an existing leaf certificate in KMS to use instead of generating a new one. This certificate must be compatible with the private key being used. Cannot be used together with --leaf-certificate-file.

--leaf-certificate-pkcs12-file <LEAF_CERTIFICATE_PKCS12_FILE> Path to a local leaf PKCS12 certificate file to use instead of generating a new one. This PKCS12 certificate also holds the private key. Cannot be used together with --leaf-certificate-id neither --leaf-certificate-extensions.

--leaf-certificate-pkcs12-password <LEAF_CERTIFICATE_PKCS12_PASSWORD> The password for the PKCS12 file containing the leaf certificate.

--days [-d] <NUMBER_OF_DAYS> The requested number of validity days The server may grant a different value

--dry-run <DRY_RUN> Dry run mode. If set, the action will not be executed

Possible values: "true", "false" [default: "false"]


14.2 ckms google identities

Insert, get, list, patch and delete identities from Gmail API

Usage

ckms google identities <subcommand>

Subcommands

get [14.2.1] Retrieves a client-side encryption identity configuration.

list [14.2.2] Lists the client-side encrypted identities for an authenticated user.

insert [14.2.3] Creates and configures a client-side encryption identity that's authorized to send mail from the user account. Google publishes the S/MIME certificate to a shared domain-wide directory so that people within a Google Workspace organization can encrypt and send mail to the identity.

delete [14.2.4] Deletes a client-side encryption identity. The authenticated user can no longer use the identity to send encrypted messages. You cannot restore the identity after you delete it. Instead, use the identities.create method to create another identity with the same configuration.

patch [14.2.5] Associates a different key pair with an existing client-side encryption identity. The updated key pair must validate against Google's S/MIME certificate profiles.


14.2.1 ckms google identities get

Retrieves a client-side encryption identity configuration.

Usage

ckms google identities get [options] <USER_ID>

Arguments

<USER_ID> The primary email address associated with the client-side encryption identity configuration that's retrieved


14.2.2 ckms google identities list

Lists the client-side encrypted identities for an authenticated user.

Usage

ckms google identities list [options] <USER_ID>

Arguments

<USER_ID> The requester's primary email address


14.2.3 ckms google identities insert

Creates and configures a client-side encryption identity that's authorized to send mail from the user account. Google publishes the S/MIME certificate to a shared domain-wide directory so that people within a Google Workspace organization can encrypt and send mail to the identity.

Usage

ckms google identities insert [options] <KEY_PAIRS_ID>

Arguments

<KEY_PAIRS_ID> The keypair id, associated with a given cert/key. You can get the by listing the keypairs associated with the user-id

--user-id [-u] <USER_ID> The primary email address associated with the client-side encryption identity configuration that's retrieved


14.2.4 ckms google identities delete

Deletes a client-side encryption identity. The authenticated user can no longer use the identity to send encrypted messages. You cannot restore the identity after you delete it. Instead, use the identities.create method to create another identity with the same configuration.

Usage

ckms google identities delete [options] <USER_ID>

Arguments

<USER_ID> The primary email address associated with the client-side encryption identity configuration that's retrieved


14.2.5 ckms google identities patch

Associates a different key pair with an existing client-side encryption identity. The updated key pair must validate against Google's S/MIME certificate profiles.

Usage

ckms google identities patch [options] <KEY_PAIRS_ID>

Arguments

<KEY_PAIRS_ID> The key pair id, associated with a given cert/key. You can get the by listing the key pairs associated with the user-id

--user-id [-u] <USER_ID> The primary email address associated with the client-side encryption identity configuration that's retrieved


15 ckms locate

Locate cryptographic objects inside the KMS

Usage

ckms locate [options]

Arguments

--tag [-t] <TAG> User tags or system tags to locate the object. To specify multiple tags, use the option multiple times.

--algorithm [-a] <CRYPTOGRAPHIC_ALGORITHM> Cryptographic algorithm (case insensitive)

--cryptographic-length [-l] <CRYPTOGRAPHIC_LENGTH> Cryptographic length (e.g. key size) in bits

--key-format-type [-f] <KEY_FORMAT_TYPE> Key format type (case insensitive)

--object-type [-o] <OBJECT_TYPE> Object type (case insensitive)

--public-key-id [-p] <PUBLIC_KEY_ID> Locate an object which has a link to this public key id

--private-key-id [-k] <PRIVATE_KEY_ID> Locate an object which has a link to this private key id

--certificate-id [-c] <CERTIFICATE_ID> Locate an object which has a link to this certificate key id


16 ckms login

Login to the Identity Provider of the KMS server using the OAuth2 authorization code flow.

Usage

ckms login


17 ckms logout

Logout from the Identity Provider

Usage

ckms logout


18 ckms hash

Hash arbitrary data.

Usage

ckms hash [options]

Arguments

--algorithm [-a] <ALGORITHM> Hashing algorithm (case insensitive)

Possible values: "sha1", "sha224", "sha256", "sha384", "sha512", "sha3-224", "sha3-256", "sha3-384", "sha3-512"

--data [-d] <DATA> The data to be hashed in hexadecimal format

--correlation-value [-c] <CORRELATION_VALUE> Specifies the existing stream or by-parts cryptographic operation (as returned from a previous call to this operation)

--init-indicator [-i] <INIT_INDICATOR> Initial operation as Boolean

Possible values: "true", "false"

--final-indicator [-f] <FINAL_INDICATOR> Final operation as Boolean

Possible values: "true", "false"


19 ckms mac

MAC utilities: compute or verify a MAC value.

Usage

ckms mac <subcommand>

Subcommands

compute [19.1] Compute a MAC over data with a MAC key

verify [19.2] Verify a MAC over data with a MAC key


19.1 ckms mac compute

Compute a MAC over data with a MAC key

Usage

ckms mac compute [options]

Arguments

--mac-key-id [-k] <MAC_KEY_ID> Locate an object which has a link to this MAC key id

--algorithm [-a] <ALGORITHM> Hashing algorithm (case insensitive)

Possible values: "sha1", "sha224", "sha256", "sha384", "sha512", "sha3-224", "sha3-256", "sha3-384", "sha3-512"

--data [-d] <DATA> The data to be hashed in hexadecimal format. The data to be hashed in hexadecimal format

--correlation-value [-c] <CORRELATION_VALUE> Specifies the existing stream or by-parts cryptographic operation (as returned from a previous call to this operation). The correlation value is represented as a hexadecimal string

--init-indicator [-i] <INIT_INDICATOR> Initial operation as Boolean

Possible values: "true", "false"

--final-indicator [-f] <FINAL_INDICATOR> Final operation as Boolean

Possible values: "true", "false"


19.2 ckms mac verify

Verify a MAC over data with a MAC key

Usage

ckms mac verify [options]

Arguments

--mac-key-id [-k] <MAC_KEY_ID> Locate an object which has a link to this MAC key id

--algorithm [-a] <ALGORITHM> Hashing algorithm (case insensitive)

Possible values: "sha1", "sha224", "sha256", "sha384", "sha512", "sha3-224", "sha3-256", "sha3-384", "sha3-512"

--data [-d] <DATA> The data to verify in hexadecimal format

--mac [-m] <MAC_HEX> The MAC to verify in hexadecimal format


20 ckms rng

RNG utilities: retrieve random bytes or seed RNG

Usage

ckms rng <subcommand>

Subcommands

retrieve [20.1] Retrieve cryptographically secure random bytes from the server RNG

seed [20.2] Seed the server RNG with provided hex-encoded bytes


20.1 ckms rng retrieve

Retrieve cryptographically secure random bytes from the server RNG

Usage

ckms rng retrieve [options]

Arguments

--length [-l] <LENGTH> Number of bytes to retrieve


20.2 ckms rng seed

Seed the server RNG with provided hex-encoded bytes

Usage

ckms rng seed [options]

Arguments

--data [-d] <DATA> Seed data as hex string


21 ckms server

Server-related commands

Usage

ckms server <subcommand>

Subcommands

version [21.1] Show server version information

discover-versions [21.2] Discover KMIP protocol versions supported by the server

query [21.3] Query server capabilities and metadata (KMIP Query)


21.1 ckms server version

Show server version information

Usage

ckms server version


21.2 ckms server discover-versions

Discover KMIP protocol versions supported by the server

Usage

ckms server discover-versions


21.3 ckms server query

Query server capabilities and metadata (KMIP Query)

Usage

ckms server query


22 ckms rsa

Manage RSA keys. Encrypt and decrypt data using RSA keys

Usage

ckms rsa <subcommand>

Subcommands

keys [22.1] Create, destroy, import, and export RSA key pairs

encrypt [22.2] Encrypt a file with the given public key using either

  • CKM_RSA_PKCS a.k.a PKCS #1 RSA V1.5 as specified in PKCS#11 v2.40
  • CKM_RSA_PKCS_OAEP a.k.a PKCS #1 RSA OAEP as specified in PKCS#11 v2.40
  • CKM_RSA_AES_KEY_WRAP as specified in PKCS#11 v2.40

decrypt [22.3] Decrypt a file with the given private key using either

  • CKM_RSA_PKCS a.k.a PKCS #1 RSA V1.5 as specified in PKCS#11 v2.40
  • CKM_RSA_PKCS_OAEP a.k.a PKCS #1 RSA OAEP as specified in PKCS#11 v2.40
  • CKM_RSA_AES_KEY_WRAP as specified in PKCS#11 v2.40

sign [22.4] Digital signature supported is RSASSA-PSS

sign-verify [22.5] Verify an RSASSA-PSS signature for a given data file


22.1 ckms rsa keys

Create, destroy, import, and export RSA key pairs

Usage

ckms rsa keys <subcommand>

Subcommands

activate [22.1.1] Activate a cryptographic object (key, certificate, etc.)

create [22.1.2] Create a new RSA key pair

export [22.1.3] Export a key or secret data from the KMS

import [22.1.4] Import a secret data or a key in the KMS.

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

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

revoke [22.1.7] Revoke a public or private key

destroy [22.1.8] Destroy a public or private key

re-key [22.1.9] Rotate an existing asymmetric key pair, generating a new private/public key pair

set-rotation-policy [22.1.10] Set the automatic rotation policy on a key or key pair.

get-rotation-policy [22.1.11] Get the automatic rotation policy for a key or key pair.


22.1.1 ckms rsa keys activate

Activate a cryptographic object (key, certificate, etc.)

Usage

ckms rsa keys activate [options]

Arguments

--key-id [-k] <KEY_ID> The key unique identifier of the key to activate. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times


22.1.2 ckms rsa keys create

Create a new RSA key pair

Usage

ckms rsa keys create [options] [PRIVATE_KEY_ID]

Arguments

--size_in_bits [-s] <SIZE_IN_BITS> The expected size in bits

--tag [-t] <TAG> The tag to associate with the master key pair. To specify multiple tags, use the option multiple times

<PRIVATE_KEY_ID> The unique id of the private key; a random uuid is generated if not specified

--sensitive <SENSITIVE> Sensitive: if set, the private key will not be exportable

Possible values: "true", "false" [default: "false"]

--wrapping-key-id [-w] <WRAPPING_KEY_ID> The key encryption key (KEK) used to wrap the keypair with. If the wrapping key is:

  • a symmetric key, AES-GCM will be used
  • a RSA key, RSA-OAEP will be used
  • a EC key, ECIES will be used (salsa20poly1305 for X25519)

--enroll-keyset [-n] <ENROLL_KEYSET> Enroll this key in a keyset so it can be addressed via name@latest, name@first, name@N syntax. The keyset name is set automatically to the key's own ID returned by the server.

Possible values: "true", "false" [default: "false"]

--rotation-interval <ROTATE_INTERVAL> Rotation interval in seconds. The key will be automatically re-keyed at this interval. Set to 0 to disable automatic rotation while preserving other policy fields

--rotation-offset <ROTATE_OFFSET> Offset in seconds from the initial date before the first rotation occurs


22.1.3 ckms rsa keys export

Export a key or secret data from the KMS

Usage

ckms rsa keys export [options] <KEY_FILE>

Arguments

<KEY_FILE> The file to export the key to

--key-id [-k] <KEY_ID> The key or secret data unique identifier stored in the KMS. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key or secret data id is specified. To specify multiple tags, use the option multiple times

--key-format [-f] <EXPORT_FORMAT> The format of the key

  • json-ttlv [default]. It should be the format to use to later re-import the key
  • sec1-pem and sec1-deronly apply to NIST EC private keys (Not Curve25519 or X448)
  • pkcs1-pem and pkcs1-der only apply to RSA private and public keys
  • pkcs8-pem and pkcs8-der only apply to RSA and EC private keys
  • raw returns the raw bytes of
    • symmetric keys
    • Covercrypt keys
    • wrapped keys
    • secret data

Possible values: "json-ttlv", "sec1-pem", "sec1-der", "pkcs1-pem", "pkcs1-der", "pkcs8-pem", "pkcs8-der", "base64", "raw" [default: "json-ttlv"]

--unwrap [-u] <UNWRAP> Unwrap the key if it is wrapped before export

Possible values: "true", "false" [default: "false"]

--wrap-key-id [-w] <WRAP_KEY_ID> The id of the key/certificate (a.k.a. Key Encryption Key - KEK) to use to wrap this key before export

--allow-revoked [-i] <ALLOW_REVOKED> Allow exporting revoked and destroyed keys. The user must be the owner of the key. Destroyed keys have their key material removed.

Possible values: "true", "false" [default: "false"]

--wrapping-algorithm [-m] <WRAPPING_ALGORITHM> Wrapping algorithm to use when exporting the key The possible wrapping algorithms are

  • using a symmetric KEK:
    • nist-key-wrap (default - a.k.a RFC 5649, CKM_AES_KEY_WRAP_PAD)
    • aes-gcm
  • using an RSA KEK:
    • rsa-oaep (default - CKM-RSA-OAEP)
    • rsa-aes-key-wrap (CKM-RSA-AES-KEY-WRP)
    • rsa-pkcs-v15 (CKM-RSA v1.5)

Possible values: "aes-key-wrap-padding", "nist-key-wrap", "aes-gcm", "rsa-pkcs-v15-sha1", "rsa-pkcs-v15", "rsa-oaep-sha1", "rsa-oaep", "rsa-aes-key-wrap-sha1", "rsa-aes-key-wrap"

--authenticated-additional-data [-d] <AUTHENTICATED_ADDITIONAL_DATA> Authenticated encryption additional data Only available for AES GCM wrapping


22.1.4 ckms rsa keys import

Import a secret data or a key in the KMS.

Usage

ckms rsa keys import [options] <KEY_FILE> [KEY_ID]

Arguments

<KEY_FILE> The file holding the key or secret data to import

<KEY_ID> The unique ID of the key; a random UUID is generated if not specified

--key-format [-f] <KEY_FORMAT> The format of the key

Possible values: "json-ttlv", "pem", "sec1", "pkcs1-priv", "pkcs1-pub", "pkcs8-priv", "pkcs8-pub", "aes", "chacha20" [default: "json-ttlv"]

--public-key-id [-p] <PUBLIC_KEY_ID> For a private key: the corresponding KMS public key ID, if any

--private-key-id [-k] <PRIVATE_KEY_ID> For a public key: the corresponding KMS private key ID, if any

--certificate-id [-c] <CERTIFICATE_ID> For a public or private key: the corresponding certificate ID, if any

--unwrap [-u] <UNWRAP> In the case of a JSON TTLV key, unwrap the key if it is wrapped before storing it

Possible values: "true", "false" [default: "false"]

--replace [-r] <REPLACE_EXISTING> Replace an existing key under the same ID

Possible values: "true", "false" [default: "false"]

--tag [-t] <TAG> The tag to associate with the key. To specify multiple tags, use the option multiple times

--key-usage <KEY_USAGE> The cryptographic operations the key is allowed to perform

Possible values: "sign", "verify", "encrypt", "decrypt", "wrap-key", "unwrap-key", "mac-generate", "mac-verify", "derive-key", "key-agreement", "certificate-sign", "crl-sign", "authenticate", "unrestricted"

--wrapping-key-id [-w] <WRAPPING_KEY_ID> The key encryption key (KEK) used to wrap this imported key with. If the wrapping key is:

  • A symmetric key, AES-GCM will be used,
  • An RSA key, RSA-OAEP with SHA-256 will be used,
  • An EC key, ECIES will be used (salsa20poly1305 for X25519),

22.1.5 ckms rsa keys wrap

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

Usage

ckms rsa keys wrap [options] <KEY_FILE_IN> [KEY_FILE_OUT]

Arguments

<KEY_FILE_IN> The KMIP JSON TTLV input key file to wrap

<KEY_FILE_OUT> The KMIP JSON output file. When not specified, the input file is overwritten

--wrap-password [-p] <WRAP_PASSWORD> A password to wrap the imported key. This password will be derived into an AES-256 symmetric key. For security reasons, a fresh salt is internally generated by cosmian and handled, and this final AES symmetric key will be displayed only once

--wrap-key-b64 [-k] <WRAP_KEY_B64> A symmetric key as a base 64 string to wrap the imported key

--wrap-key-id [-i] <WRAP_KEY_ID> The ID of a wrapping key in the KMS that will be exported and used to wrap the key

--wrap-key-file [-f] <WRAP_KEY_FILE> A wrapping key in a KMIP JSON TTLV file used to wrap the key


22.1.6 ckms rsa keys unwrap

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

Usage

ckms rsa keys unwrap [options] <KEY_FILE_IN> [KEY_FILE_OUT]

Arguments

<KEY_FILE_IN> The KMIP JSON TTLV input key file to unwrap

<KEY_FILE_OUT> The KMIP JSON output file. When not specified the input file is overwritten

--unwrap-key-b64 [-k] <UNWRAP_KEY_B64> A symmetric key as a base 64 string to unwrap the imported key

--unwrap-key-id [-i] <UNWRAP_KEY_ID> The id of an unwrapping key in the KMS that will be exported and used to unwrap the key

--unwrap-key-file [-f] <UNWRAP_KEY_FILE> An unwrapping key in a KMIP JSON TTLV file used to unwrap the key


22.1.7 ckms rsa keys revoke

Revoke a public or private key

Usage

ckms rsa keys revoke [options] <REVOCATION_REASON>

Arguments

<REVOCATION_REASON> The reason for the revocation as a string

--reason-code [-r] <REASON_CODE> The revocation reason code [default: unspecified]

--key-id [-k] <KEY_ID> The key unique identifier of the key to revoke. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times


22.1.8 ckms rsa keys destroy

Destroy a public or private key

Usage

ckms rsa keys destroy [options]

Arguments

--key-id [-k] <KEY_ID> The key unique identifier of the key to destroy If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times

--remove <REMOVE> If the key should be removed from the database If not specified, the key will be destroyed but its metadata will still be available in the database. Please note that the KMIP specification does not support the removal of objects.

Possible values: "true", "false" [default: "false"]


22.1.9 ckms rsa keys re-key

Rotate an existing asymmetric key pair, generating a new private/public key pair

Usage

ckms rsa keys re-key [options]

Arguments

--key-id [-k] <KEY_ID> The unique identifier of the private key to re-key


22.1.10 ckms rsa keys set-rotation-policy

Set the automatic rotation policy on a key or key pair.

Usage

ckms rsa keys set-rotation-policy [options]

Arguments

--key-id [-k] <KEY_ID> The unique identifier of the key to set the rotation policy on

--interval [-i] <INTERVAL_SECS> Rotation interval in seconds. The key will be automatically re-keyed at this interval. Set to 0 to disable automatic rotation while preserving other policy fields

--offset [-o] <OFFSET_SECS> Offset in seconds from the initial date before the first rotation occurs

--rotation-name [-n] <ROTATE_NAME> A keyset name for addressing key generations via name@latest, name@first, name@N syntax. Must not contain the '@' character


22.1.11 ckms rsa keys get-rotation-policy

Get the automatic rotation policy for a key or key pair.

Usage

ckms rsa keys get-rotation-policy [options]

Arguments

--key-id [-k] <KEY_ID> The unique identifier of the key to get the rotation policy from


22.2 ckms rsa encrypt

Encrypt a file with the given public key using either

  • CKM_RSA_PKCS a.k.a PKCS #1 RSA V1.5 as specified in PKCS#11 v2.40
  • CKM_RSA_PKCS_OAEP a.k.a PKCS #1 RSA OAEP as specified in PKCS#11 v2.40
  • CKM_RSA_AES_KEY_WRAP as specified in PKCS#11 v2.40

Usage

ckms rsa encrypt [options] <FILE>

Arguments

<FILE> The file to encrypt

--key-id [-k] <KEY_ID> The public key unique identifier. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times

--encryption-algorithm [-e] <ENCRYPTION_ALGORITHM> The encryption algorithm

Possible values: "ckm-rsa-pkcs", "ckm-rsa-pkcs-oaep", "ckm-rsa-aes-key-wrap" [default: "ckm-rsa-pkcs-oaep"]

--hashing-algorithm [-s] <HASH_FN> The hashing algorithm

Possible values: "sha1", "sha224", "sha256", "sha384", "sha512", "sha3-224", "sha3-256", "sha3-384", "sha3-512" [default: "sha256"]

--output-file [-o] <OUTPUT_FILE> The encrypted output file path


22.3 ckms rsa decrypt

Decrypt a file with the given private key using either

  • CKM_RSA_PKCS a.k.a PKCS #1 RSA V1.5 as specified in PKCS#11 v2.40
  • CKM_RSA_PKCS_OAEP a.k.a PKCS #1 RSA OAEP as specified in PKCS#11 v2.40
  • CKM_RSA_AES_KEY_WRAP as specified in PKCS#11 v2.40

Usage

ckms rsa decrypt [options] <FILE>

Arguments

<FILE> The file to decrypt

--key-id [-k] <KEY_ID> The private key unique identifier If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times

--encryption-algorithm [-e] <ENCRYPTION_ALGORITHM> The encryption algorithm

Possible values: "ckm-rsa-pkcs", "ckm-rsa-pkcs-oaep", "ckm-rsa-aes-key-wrap" [default: "ckm-rsa-pkcs-oaep"]

--hashing-algorithm [-s] <HASH_FN> The hashing algorithm (for OAEP and AES key wrap)

Possible values: "sha1", "sha224", "sha256", "sha384", "sha512", "sha3-224", "sha3-256", "sha3-384", "sha3-512" [default: "sha256"]

--output-file [-o] <OUTPUT_FILE> The encrypted output file path


22.4 ckms rsa sign

Digital signature supported is RSASSA-PSS

Usage

ckms rsa sign [options] <FILE>

Arguments

<FILE> The file to sign

--key-id [-k] <KEY_ID> The private key unique identifier If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times

--output-file [-o] <OUTPUT_FILE> The signature output file path

--digested <DIGESTED> Treat input as already-digested data (pre-hash)

Possible values: "true", "false"


22.5 ckms rsa sign-verify

Verify an RSASSA-PSS signature for a given data file

Usage

ckms rsa sign-verify [options] <FILE> <SIGNATURE_FILE>

Arguments

<FILE> The data that was signed

<SIGNATURE_FILE> The signature file

--key-id [-k] <KEY_ID> The private key unique identifier If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times

--output-file [-o] <OUTPUT_FILE> Optional output file path

--digested <DIGESTED> Treat data input as already-digested (pre-hash)

Possible values: "true", "false"


23 ckms opaque-object

Create, import, export, revoke and destroy Opaque Objects

Usage

ckms opaque-object <subcommand>

Subcommands

activate [23.1] Activate a cryptographic object (key, certificate, etc.)

create [23.2] Create (register) an OpaqueObject by importing raw bytes.

export [23.3] Export a key or secret data from the KMS

import [23.4] Import a secret data or a key in the KMS.

revoke [23.5] Revoke an OpaqueObject

destroy [23.6] Destroy an OpaqueObject


23.1 ckms opaque-object activate

Activate a cryptographic object (key, certificate, etc.)

Usage

ckms opaque-object activate [options]

Arguments

--key-id [-k] <KEY_ID> The key unique identifier of the key to activate. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times


23.2 ckms opaque-object create

Create (register) an OpaqueObject by importing raw bytes.

Usage

ckms opaque-object create [options]

Arguments

--file [-f] <FILE> Optional file containing the opaque bytes to import

--data [-d] <DATA> Inline opaque data as a UTF-8 string. If provided, it's used instead of --file bytes

--type <OPAQUE_TYPE> Opaque data type (defaults to Vendor)

--id <ID> Optional object unique identifier to assign; otherwise server generates one

--tag [-t] <TAG> Tags to associate with the object. Repeat to add multiple tags


23.3 ckms opaque-object export

Export a key or secret data from the KMS

Usage

ckms opaque-object export [options] <KEY_FILE>

Arguments

<KEY_FILE> The file to export the key to

--key-id [-k] <KEY_ID> The key or secret data unique identifier stored in the KMS. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key or secret data id is specified. To specify multiple tags, use the option multiple times

--key-format [-f] <EXPORT_FORMAT> The format of the key

  • json-ttlv [default]. It should be the format to use to later re-import the key
  • sec1-pem and sec1-deronly apply to NIST EC private keys (Not Curve25519 or X448)
  • pkcs1-pem and pkcs1-der only apply to RSA private and public keys
  • pkcs8-pem and pkcs8-der only apply to RSA and EC private keys
  • raw returns the raw bytes of
    • symmetric keys
    • Covercrypt keys
    • wrapped keys
    • secret data

Possible values: "json-ttlv", "sec1-pem", "sec1-der", "pkcs1-pem", "pkcs1-der", "pkcs8-pem", "pkcs8-der", "base64", "raw" [default: "json-ttlv"]

--unwrap [-u] <UNWRAP> Unwrap the key if it is wrapped before export

Possible values: "true", "false" [default: "false"]

--wrap-key-id [-w] <WRAP_KEY_ID> The id of the key/certificate (a.k.a. Key Encryption Key - KEK) to use to wrap this key before export

--allow-revoked [-i] <ALLOW_REVOKED> Allow exporting revoked and destroyed keys. The user must be the owner of the key. Destroyed keys have their key material removed.

Possible values: "true", "false" [default: "false"]

--wrapping-algorithm [-m] <WRAPPING_ALGORITHM> Wrapping algorithm to use when exporting the key The possible wrapping algorithms are

  • using a symmetric KEK:
    • nist-key-wrap (default - a.k.a RFC 5649, CKM_AES_KEY_WRAP_PAD)
    • aes-gcm
  • using an RSA KEK:
    • rsa-oaep (default - CKM-RSA-OAEP)
    • rsa-aes-key-wrap (CKM-RSA-AES-KEY-WRP)
    • rsa-pkcs-v15 (CKM-RSA v1.5)

Possible values: "aes-key-wrap-padding", "nist-key-wrap", "aes-gcm", "rsa-pkcs-v15-sha1", "rsa-pkcs-v15", "rsa-oaep-sha1", "rsa-oaep", "rsa-aes-key-wrap-sha1", "rsa-aes-key-wrap"

--authenticated-additional-data [-d] <AUTHENTICATED_ADDITIONAL_DATA> Authenticated encryption additional data Only available for AES GCM wrapping


23.4 ckms opaque-object import

Import a secret data or a key in the KMS.

Usage

ckms opaque-object import [options] <KEY_FILE> [KEY_ID]

Arguments

<KEY_FILE> The file holding the key or secret data to import

<KEY_ID> The unique ID of the key; a random UUID is generated if not specified

--key-format [-f] <KEY_FORMAT> The format of the key

Possible values: "json-ttlv", "pem", "sec1", "pkcs1-priv", "pkcs1-pub", "pkcs8-priv", "pkcs8-pub", "aes", "chacha20" [default: "json-ttlv"]

--public-key-id [-p] <PUBLIC_KEY_ID> For a private key: the corresponding KMS public key ID, if any

--private-key-id [-k] <PRIVATE_KEY_ID> For a public key: the corresponding KMS private key ID, if any

--certificate-id [-c] <CERTIFICATE_ID> For a public or private key: the corresponding certificate ID, if any

--unwrap [-u] <UNWRAP> In the case of a JSON TTLV key, unwrap the key if it is wrapped before storing it

Possible values: "true", "false" [default: "false"]

--replace [-r] <REPLACE_EXISTING> Replace an existing key under the same ID

Possible values: "true", "false" [default: "false"]

--tag [-t] <TAG> The tag to associate with the key. To specify multiple tags, use the option multiple times

--key-usage <KEY_USAGE> The cryptographic operations the key is allowed to perform

Possible values: "sign", "verify", "encrypt", "decrypt", "wrap-key", "unwrap-key", "mac-generate", "mac-verify", "derive-key", "key-agreement", "certificate-sign", "crl-sign", "authenticate", "unrestricted"

--wrapping-key-id [-w] <WRAPPING_KEY_ID> The key encryption key (KEK) used to wrap this imported key with. If the wrapping key is:

  • A symmetric key, AES-GCM will be used,
  • An RSA key, RSA-OAEP with SHA-256 will be used,
  • An EC key, ECIES will be used (salsa20poly1305 for X25519),

23.5 ckms opaque-object revoke

Revoke an OpaqueObject

Usage

ckms opaque-object revoke [options] <REVOCATION_REASON>

Arguments

<REVOCATION_REASON> The reason for the revocation as a string

--reason-code [-r] <REASON_CODE> The revocation reason code [default: unspecified]

--key-id [-k] <OBJECT_ID> The opaque object unique identifier to revoke. If not specified, tags should be specified

--tag [-t] <TAG> Tags to locate the object if id is not provided. Repeat to specify multiple tags


23.6 ckms opaque-object destroy

Destroy an OpaqueObject

Usage

ckms opaque-object destroy [options]

Arguments

--key-id [-k] <OBJECT_ID> The opaque object unique identifier. If not specified, tags should be specified

--tag [-t] <TAG> Tags to locate the object if id is not provided. Repeat to specify multiple tags

--remove <REMOVE> If the object should be removed from the database. If not specified, the object will be destroyed but its metadata will still be available.

Possible values: "true", "false" [default: "false"]


24 ckms pkcs11

Verify PKCS#11 shared library integration

Usage

ckms pkcs11 <subcommand>

Subcommands

verify [24.1] Load the PKCS#11 shared library and exercise the standard API sequence


24.1 ckms pkcs11 verify

Load the PKCS#11 shared library and exercise the standard API sequence

Usage

ckms pkcs11 verify [options]

Arguments

--dll <PATH> Path to the PKCS#11 shared library (libcosmian_pkcs11.so / .dylib / .dll)

--conf <PATH> Explicit path to ckms.toml. When set, the CKMS_CONF environment variable is written before the library is loaded so that the provider picks up this configuration file

--token <JWT> Bearer token (OIDC/JWT) to pass to C_Login. Required when ckms.toml has pkcs11_use_pin_as_access_token = true


25 ckms secret-data

Create, import, export and destroy secret data

Usage

ckms secret-data <subcommand>

Subcommands

activate [25.1] Activate a cryptographic object (key, certificate, etc.)

create [25.2] Create a new secret data

export [25.3] Export a key or secret data from the KMS

import [25.4] Import a secret data or a key in the KMS.

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

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

revoke [25.7] Revoke a secret data

destroy [25.8] Destroy a secret data


25.1 ckms secret-data activate

Activate a cryptographic object (key, certificate, etc.)

Usage

ckms secret-data activate [options]

Arguments

--key-id [-k] <KEY_ID> The key unique identifier of the key to activate. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times


25.2 ckms secret-data create

Create a new secret data

Usage

ckms secret-data create [options] [SECRET_ID]

Arguments

--value [-v] <SECRET_VALUE> Optional secret data string, UTF-8 encoded. If not provided, a random 32-byte seed will be generated

--type <SECRET_TYPE> The type of secret data. Defaults to a randomly generated Seed. To use a Password type, you must provide both this and a valid secret value

Possible values: "password", "seed" [default: "seed"]

--tag [-t] <TAG> The tag to associate with the secret data. To specify multiple tags, use the option multiple times

<SECRET_ID> The unique id of the secret; a random uuid is generated if not specified

--sensitive <SENSITIVE> Sensitive: if set, the secret will not be exportable

Possible values: "true", "false" [default: "false"]

--wrapping-key-id [-w] <WRAPPING_KEY_ID> The key encryption key (KEK) used to wrap this new secret data with. If the wrapping key is:

  • a symmetric key, AES-GCM will be used
  • a RSA key, RSA-OAEP will be used
  • a EC key, ECIES will be used (salsa20poly1305 for X25519)

25.3 ckms secret-data export

Export a key or secret data from the KMS

Usage

ckms secret-data export [options] <KEY_FILE>

Arguments

<KEY_FILE> The file to export the key to

--key-id [-k] <KEY_ID> The key or secret data unique identifier stored in the KMS. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key or secret data id is specified. To specify multiple tags, use the option multiple times

--key-format [-f] <EXPORT_FORMAT> The format of the key

  • json-ttlv [default]. It should be the format to use to later re-import the key
  • sec1-pem and sec1-deronly apply to NIST EC private keys (Not Curve25519 or X448)
  • pkcs1-pem and pkcs1-der only apply to RSA private and public keys
  • pkcs8-pem and pkcs8-der only apply to RSA and EC private keys
  • raw returns the raw bytes of
    • symmetric keys
    • Covercrypt keys
    • wrapped keys
    • secret data

Possible values: "json-ttlv", "sec1-pem", "sec1-der", "pkcs1-pem", "pkcs1-der", "pkcs8-pem", "pkcs8-der", "base64", "raw" [default: "json-ttlv"]

--unwrap [-u] <UNWRAP> Unwrap the key if it is wrapped before export

Possible values: "true", "false" [default: "false"]

--wrap-key-id [-w] <WRAP_KEY_ID> The id of the key/certificate (a.k.a. Key Encryption Key - KEK) to use to wrap this key before export

--allow-revoked [-i] <ALLOW_REVOKED> Allow exporting revoked and destroyed keys. The user must be the owner of the key. Destroyed keys have their key material removed.

Possible values: "true", "false" [default: "false"]

--wrapping-algorithm [-m] <WRAPPING_ALGORITHM> Wrapping algorithm to use when exporting the key The possible wrapping algorithms are

  • using a symmetric KEK:
    • nist-key-wrap (default - a.k.a RFC 5649, CKM_AES_KEY_WRAP_PAD)
    • aes-gcm
  • using an RSA KEK:
    • rsa-oaep (default - CKM-RSA-OAEP)
    • rsa-aes-key-wrap (CKM-RSA-AES-KEY-WRP)
    • rsa-pkcs-v15 (CKM-RSA v1.5)

Possible values: "aes-key-wrap-padding", "nist-key-wrap", "aes-gcm", "rsa-pkcs-v15-sha1", "rsa-pkcs-v15", "rsa-oaep-sha1", "rsa-oaep", "rsa-aes-key-wrap-sha1", "rsa-aes-key-wrap"

--authenticated-additional-data [-d] <AUTHENTICATED_ADDITIONAL_DATA> Authenticated encryption additional data Only available for AES GCM wrapping


25.4 ckms secret-data import

Import a secret data or a key in the KMS.

Usage

ckms secret-data import [options] <KEY_FILE> [KEY_ID]

Arguments

<KEY_FILE> The file holding the key or secret data to import

<KEY_ID> The unique ID of the key; a random UUID is generated if not specified

--key-format [-f] <KEY_FORMAT> The format of the key

Possible values: "json-ttlv", "pem", "sec1", "pkcs1-priv", "pkcs1-pub", "pkcs8-priv", "pkcs8-pub", "aes", "chacha20" [default: "json-ttlv"]

--public-key-id [-p] <PUBLIC_KEY_ID> For a private key: the corresponding KMS public key ID, if any

--private-key-id [-k] <PRIVATE_KEY_ID> For a public key: the corresponding KMS private key ID, if any

--certificate-id [-c] <CERTIFICATE_ID> For a public or private key: the corresponding certificate ID, if any

--unwrap [-u] <UNWRAP> In the case of a JSON TTLV key, unwrap the key if it is wrapped before storing it

Possible values: "true", "false" [default: "false"]

--replace [-r] <REPLACE_EXISTING> Replace an existing key under the same ID

Possible values: "true", "false" [default: "false"]

--tag [-t] <TAG> The tag to associate with the key. To specify multiple tags, use the option multiple times

--key-usage <KEY_USAGE> The cryptographic operations the key is allowed to perform

Possible values: "sign", "verify", "encrypt", "decrypt", "wrap-key", "unwrap-key", "mac-generate", "mac-verify", "derive-key", "key-agreement", "certificate-sign", "crl-sign", "authenticate", "unrestricted"

--wrapping-key-id [-w] <WRAPPING_KEY_ID> The key encryption key (KEK) used to wrap this imported key with. If the wrapping key is:

  • A symmetric key, AES-GCM will be used,
  • An RSA key, RSA-OAEP with SHA-256 will be used,
  • An EC key, ECIES will be used (salsa20poly1305 for X25519),

25.5 ckms secret-data wrap

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

Usage

ckms secret-data wrap [options] <KEY_FILE_IN> [KEY_FILE_OUT]

Arguments

<KEY_FILE_IN> The KMIP JSON TTLV input key file to wrap

<KEY_FILE_OUT> The KMIP JSON output file. When not specified, the input file is overwritten

--wrap-password [-p] <WRAP_PASSWORD> A password to wrap the imported key. This password will be derived into an AES-256 symmetric key. For security reasons, a fresh salt is internally generated by cosmian and handled, and this final AES symmetric key will be displayed only once

--wrap-key-b64 [-k] <WRAP_KEY_B64> A symmetric key as a base 64 string to wrap the imported key

--wrap-key-id [-i] <WRAP_KEY_ID> The ID of a wrapping key in the KMS that will be exported and used to wrap the key

--wrap-key-file [-f] <WRAP_KEY_FILE> A wrapping key in a KMIP JSON TTLV file used to wrap the key


25.6 ckms secret-data unwrap

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

Usage

ckms secret-data unwrap [options] <KEY_FILE_IN> [KEY_FILE_OUT]

Arguments

<KEY_FILE_IN> The KMIP JSON TTLV input key file to unwrap

<KEY_FILE_OUT> The KMIP JSON output file. When not specified the input file is overwritten

--unwrap-key-b64 [-k] <UNWRAP_KEY_B64> A symmetric key as a base 64 string to unwrap the imported key

--unwrap-key-id [-i] <UNWRAP_KEY_ID> The id of an unwrapping key in the KMS that will be exported and used to unwrap the key

--unwrap-key-file [-f] <UNWRAP_KEY_FILE> An unwrapping key in a KMIP JSON TTLV file used to unwrap the key


25.7 ckms secret-data revoke

Revoke a secret data

Usage

ckms secret-data revoke [options] <REVOCATION_REASON>

Arguments

<REVOCATION_REASON> The reason for the revocation as a string

--reason-code [-r] <REASON_CODE> The revocation reason code [default: unspecified]

--secret-data-id [-s] <SECRET_ID> The secret unique identifier of the secret to revoke. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the secret data when no secret data id is specified. To specify multiple tags, use the option multiple times


25.8 ckms secret-data destroy

Destroy a secret data

Usage

ckms secret-data destroy [options]

Arguments

--key-id [-s] <SECRET_ID> The secret unique identifier. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the secret when no secret id is specified. To specify multiple tags, use the option multiple times

--remove <REMOVE> If the secret should be removed from the database If not specified, the key will be destroyed but its metadata will still be available in the database. Please note that the KMIP specification does not support the removal of objects.

Possible values: "true", "false" [default: "false"]


26 ckms sym

Manage symmetric keys. Encrypt and decrypt data

Usage

ckms sym <subcommand>

Subcommands

keys [26.1] Create, destroy, import, and export symmetric keys

encrypt [26.2] Encrypt a file using a symmetric cipher

decrypt [26.3] Decrypt a file using a symmetric key.


26.1 ckms sym keys

Create, destroy, import, and export symmetric keys

Usage

ckms sym keys <subcommand>

Subcommands

activate [26.1.1] Activate a cryptographic object (key, certificate, etc.)

create [26.1.2] Create a new symmetric key

re-key [26.1.3] Refresh an existing symmetric key

export [26.1.4] Export a key or secret data from the KMS

import [26.1.5] Import a secret data or a key in the KMS.

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

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

revoke [26.1.8] Revoke a symmetric key

destroy [26.1.9] Destroy a symmetric key

set-rotation-policy [26.1.10] Set the automatic rotation policy on a key or key pair.

get-rotation-policy [26.1.11] Get the automatic rotation policy for a key or key pair.


26.1.1 ckms sym keys activate

Activate a cryptographic object (key, certificate, etc.)

Usage

ckms sym keys activate [options]

Arguments

--key-id [-k] <KEY_ID> The key unique identifier of the key to activate. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times


26.1.2 ckms sym keys create

Create a new symmetric key

Usage

ckms sym keys create [options] [KEY_ID]

Arguments

--number-of-bits [-l] <NUMBER_OF_BITS> The length of the generated random key or salt in bits

--bytes-b64 [-k] <WRAP_KEY_B64> The symmetric key bytes or salt as a base 64 string

--algorithm [-a] <ALGORITHM> The algorithm

Possible values: "chacha20", "aes", "sha3", "shake" [default: "aes"]

--tag [-t] <TAG> The tag to associate with the key. To specify multiple tags, use the option multiple times

<KEY_ID> The unique id of the key; a random uuid is generated if not specified

--sensitive <SENSITIVE> Sensitive: if set, the key will not be exportable

Possible values: "true", "false" [default: "false"]

--wrapping-key-id [-w] <WRAPPING_KEY_ID> The key encryption key (KEK) used to wrap this new key with. If the wrapping key is:

  • a symmetric key, AES-GCM will be used
  • a RSA key, RSA-OAEP will be used
  • a EC key, ECIES will be used (salsa20poly1305 for X25519)

--enroll-keyset [-n] <ENROLL_KEYSET> Enroll this key in a keyset so it can be addressed via name@latest, name@first, name@N syntax. The keyset name is set automatically to the key's own ID returned by the server.

Possible values: "true", "false" [default: "false"]

--rotation-interval <ROTATE_INTERVAL> Rotation interval in seconds. The key will be automatically re-keyed at this interval. Set to 0 to disable automatic rotation while preserving other policy fields

--rotation-offset <ROTATE_OFFSET> Offset in seconds from the initial date before the first rotation occurs


26.1.3 ckms sym keys re-key

Refresh an existing symmetric key

Usage

ckms sym keys re-key [options]

Arguments

--key-id [-k] <KEY_ID> The tag to associate with the key. To specify multiple tags, use the option multiple times


26.1.4 ckms sym keys export

Export a key or secret data from the KMS

Usage

ckms sym keys export [options] <KEY_FILE>

Arguments

<KEY_FILE> The file to export the key to

--key-id [-k] <KEY_ID> The key or secret data unique identifier stored in the KMS. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key or secret data id is specified. To specify multiple tags, use the option multiple times

--key-format [-f] <EXPORT_FORMAT> The format of the key

  • json-ttlv [default]. It should be the format to use to later re-import the key
  • sec1-pem and sec1-deronly apply to NIST EC private keys (Not Curve25519 or X448)
  • pkcs1-pem and pkcs1-der only apply to RSA private and public keys
  • pkcs8-pem and pkcs8-der only apply to RSA and EC private keys
  • raw returns the raw bytes of
    • symmetric keys
    • Covercrypt keys
    • wrapped keys
    • secret data

Possible values: "json-ttlv", "sec1-pem", "sec1-der", "pkcs1-pem", "pkcs1-der", "pkcs8-pem", "pkcs8-der", "base64", "raw" [default: "json-ttlv"]

--unwrap [-u] <UNWRAP> Unwrap the key if it is wrapped before export

Possible values: "true", "false" [default: "false"]

--wrap-key-id [-w] <WRAP_KEY_ID> The id of the key/certificate (a.k.a. Key Encryption Key - KEK) to use to wrap this key before export

--allow-revoked [-i] <ALLOW_REVOKED> Allow exporting revoked and destroyed keys. The user must be the owner of the key. Destroyed keys have their key material removed.

Possible values: "true", "false" [default: "false"]

--wrapping-algorithm [-m] <WRAPPING_ALGORITHM> Wrapping algorithm to use when exporting the key The possible wrapping algorithms are

  • using a symmetric KEK:
    • nist-key-wrap (default - a.k.a RFC 5649, CKM_AES_KEY_WRAP_PAD)
    • aes-gcm
  • using an RSA KEK:
    • rsa-oaep (default - CKM-RSA-OAEP)
    • rsa-aes-key-wrap (CKM-RSA-AES-KEY-WRP)
    • rsa-pkcs-v15 (CKM-RSA v1.5)

Possible values: "aes-key-wrap-padding", "nist-key-wrap", "aes-gcm", "rsa-pkcs-v15-sha1", "rsa-pkcs-v15", "rsa-oaep-sha1", "rsa-oaep", "rsa-aes-key-wrap-sha1", "rsa-aes-key-wrap"

--authenticated-additional-data [-d] <AUTHENTICATED_ADDITIONAL_DATA> Authenticated encryption additional data Only available for AES GCM wrapping


26.1.5 ckms sym keys import

Import a secret data or a key in the KMS.

Usage

ckms sym keys import [options] <KEY_FILE> [KEY_ID]

Arguments

<KEY_FILE> The file holding the key or secret data to import

<KEY_ID> The unique ID of the key; a random UUID is generated if not specified

--key-format [-f] <KEY_FORMAT> The format of the key

Possible values: "json-ttlv", "pem", "sec1", "pkcs1-priv", "pkcs1-pub", "pkcs8-priv", "pkcs8-pub", "aes", "chacha20" [default: "json-ttlv"]

--public-key-id [-p] <PUBLIC_KEY_ID> For a private key: the corresponding KMS public key ID, if any

--private-key-id [-k] <PRIVATE_KEY_ID> For a public key: the corresponding KMS private key ID, if any

--certificate-id [-c] <CERTIFICATE_ID> For a public or private key: the corresponding certificate ID, if any

--unwrap [-u] <UNWRAP> In the case of a JSON TTLV key, unwrap the key if it is wrapped before storing it

Possible values: "true", "false" [default: "false"]

--replace [-r] <REPLACE_EXISTING> Replace an existing key under the same ID

Possible values: "true", "false" [default: "false"]

--tag [-t] <TAG> The tag to associate with the key. To specify multiple tags, use the option multiple times

--key-usage <KEY_USAGE> The cryptographic operations the key is allowed to perform

Possible values: "sign", "verify", "encrypt", "decrypt", "wrap-key", "unwrap-key", "mac-generate", "mac-verify", "derive-key", "key-agreement", "certificate-sign", "crl-sign", "authenticate", "unrestricted"

--wrapping-key-id [-w] <WRAPPING_KEY_ID> The key encryption key (KEK) used to wrap this imported key with. If the wrapping key is:

  • A symmetric key, AES-GCM will be used,
  • An RSA key, RSA-OAEP with SHA-256 will be used,
  • An EC key, ECIES will be used (salsa20poly1305 for X25519),

26.1.6 ckms sym keys wrap

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

Usage

ckms sym keys wrap [options] <KEY_FILE_IN> [KEY_FILE_OUT]

Arguments

<KEY_FILE_IN> The KMIP JSON TTLV input key file to wrap

<KEY_FILE_OUT> The KMIP JSON output file. When not specified, the input file is overwritten

--wrap-password [-p] <WRAP_PASSWORD> A password to wrap the imported key. This password will be derived into an AES-256 symmetric key. For security reasons, a fresh salt is internally generated by cosmian and handled, and this final AES symmetric key will be displayed only once

--wrap-key-b64 [-k] <WRAP_KEY_B64> A symmetric key as a base 64 string to wrap the imported key

--wrap-key-id [-i] <WRAP_KEY_ID> The ID of a wrapping key in the KMS that will be exported and used to wrap the key

--wrap-key-file [-f] <WRAP_KEY_FILE> A wrapping key in a KMIP JSON TTLV file used to wrap the key


26.1.7 ckms sym keys unwrap

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

Usage

ckms sym keys unwrap [options] <KEY_FILE_IN> [KEY_FILE_OUT]

Arguments

<KEY_FILE_IN> The KMIP JSON TTLV input key file to unwrap

<KEY_FILE_OUT> The KMIP JSON output file. When not specified the input file is overwritten

--unwrap-key-b64 [-k] <UNWRAP_KEY_B64> A symmetric key as a base 64 string to unwrap the imported key

--unwrap-key-id [-i] <UNWRAP_KEY_ID> The id of an unwrapping key in the KMS that will be exported and used to unwrap the key

--unwrap-key-file [-f] <UNWRAP_KEY_FILE> An unwrapping key in a KMIP JSON TTLV file used to unwrap the key


26.1.8 ckms sym keys revoke

Revoke a symmetric key

Usage

ckms sym keys revoke [options] <REVOCATION_REASON>

Arguments

<REVOCATION_REASON> The reason for the revocation as a string

--reason-code [-r] <REASON_CODE> The revocation reason code [default: unspecified]

--key-id [-k] <KEY_ID> The key unique identifier of the key to revoke. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times


26.1.9 ckms sym keys destroy

Destroy a symmetric key

Usage

ckms sym keys destroy [options]

Arguments

--key-id [-k] <KEY_ID> The key unique identifier. If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times

--remove <REMOVE> If the key should be removed from the database If not specified, the key will be destroyed but its metadata will still be available in the database. Please note that the KMIP specification does not support the removal of objects.

Possible values: "true", "false" [default: "false"]


26.1.10 ckms sym keys set-rotation-policy

Set the automatic rotation policy on a key or key pair.

Usage

ckms sym keys set-rotation-policy [options]

Arguments

--key-id [-k] <KEY_ID> The unique identifier of the key to set the rotation policy on

--interval [-i] <INTERVAL_SECS> Rotation interval in seconds. The key will be automatically re-keyed at this interval. Set to 0 to disable automatic rotation while preserving other policy fields

--offset [-o] <OFFSET_SECS> Offset in seconds from the initial date before the first rotation occurs

--rotation-name [-n] <ROTATE_NAME> A keyset name for addressing key generations via name@latest, name@first, name@N syntax. Must not contain the '@' character


26.1.11 ckms sym keys get-rotation-policy

Get the automatic rotation policy for a key or key pair.

Usage

ckms sym keys get-rotation-policy [options]

Arguments

--key-id [-k] <KEY_ID> The unique identifier of the key to get the rotation policy from


26.2 ckms sym encrypt

Encrypt a file using a symmetric cipher

Usage

ckms sym encrypt [options] <FILE>

Arguments

<FILE> The file to encrypt

--key-id [-k] <KEY_ID> The symmetric key unique identifier. If not specified, tags should be specified

--data-encryption-algorithm [-d] <DATA_ENCRYPTION_ALGORITHM> The data encryption algorithm. If not specified, aes-gcm is used

Possible values: "chacha20-poly1305", "aes-gcm", "aes-cbc", "aes-xts", "aes-gcm-siv" [default: "aes-gcm"]

--key-encryption-algorithm [-e] <KEY_ENCRYPTION_ALGORITHM> The optional key encryption algorithm used to encrypt the data encryption key.

Possible values: "chacha20-poly1305", "aes-gcm", "aes-xts", "aes-gcm-siv", "rfc3394", "rfc5649"

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times

--output-file [-o] <OUTPUT_FILE> The encrypted output file path

--nonce [-n] <NONCE> Optional nonce/IV (or tweak for XTS) as a hex string. If not provided, a random value is generated

--authentication-data [-a] <AUTHENTICATION_DATA> Optional additional authentication data as a hex string. This data needs to be provided back for decryption. This data is ignored with XTS


26.3 ckms sym decrypt

Decrypt a file using a symmetric key.

Usage

ckms sym decrypt [options] <FILE>

Arguments

<FILE> The file to decrypt

--key-id [-k] <KEY_ID> The private key unique identifier If not specified, tags should be specified

--tag [-t] <TAG> Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times

--data-encryption-algorithm [-d] <DATA_ENCRYPTION_ALGORITHM> The data encryption algorithm. If not specified, aes-gcm is used.

Possible values: "chacha20-poly1305", "aes-gcm", "aes-cbc", "aes-xts", "aes-gcm-siv" [default: "aes-gcm"]

--key-encryption-algorithm [-e] <KEY_ENCRYPTION_ALGORITHM> The optional key encryption algorithm used to decrypt the data encryption key.

Possible values: "chacha20-poly1305", "aes-gcm", "aes-xts", "aes-gcm-siv", "rfc3394", "rfc5649"

--output-file [-o] <OUTPUT_FILE> The encrypted output file path

--authentication-data [-a] <AUTHENTICATION_DATA> Optional authentication data that was supplied during encryption as a hex string


27 ckms markdown

Regenerate the CLI documentation in Markdown format

Usage

ckms markdown [options] <MARKDOWN_FILE>

Arguments

<MARKDOWN_FILE> The file to export the markdown to


28 ckms configure

Configure the KMS CLI (create ckms.toml)

Usage

ckms configure

KMS Authorizing users with access objects

When authentication is enabled, each KMS object requires explicit authorization from its owner to be accessed or used by others. The KMS CLI then allows to manage the access rights of users to cryptographic objects stored in the KMS.

Granting an access right

An owner of an object grants an access right to a specific user for a given operation on a given object. The supported KMIP operations are: get, export, encrypt, decrypt, import, revoke, destroy.

➜ ckms access grant --help
Grant another user an access right to an object.

This command can only be called by the owner of the object.

The right is granted for one of the supported KMIP operations: create, get, encrypt, decrypt, import, revoke, locate, rekey, destroy

Usage: ckms access grant <USER> <OBJECT_UID> <OPERATION>

Arguments:
<USER>
         The user identifier to allow

<OBJECT_UID>
         The object unique identifier stored in the KMS

<OPERATION>
         The KMIP operation to allow

Options:
-h, --help
         Print help (see a summary with '-h')

POST to the /access/grant endpoint with the JSON object:

{
   "unique_identifier": "1ae2...25df",  // the object unique identifier
   "user_id": "john.doe@acem.com", // the user identifier to allow
   "operation_type": "get" // the KMIP operation to allow
}

The response is a JSON object:

{
"success": "a success message"
}

Revoking an access right

An owner of an object can revoke an access right to a specific user for a given operation on a given object at any time.

➜ ckms access revoke --help
Revoke another user access right to an object.

This command can only be called by the owner of the object.

Usage: ckms access revoke <USER> <OBJECT_UID> <OPERATION>

Arguments:
<USER>
         The user to revoke access to

<OBJECT_UID>
         The object unique identifier stored in the KMS

<OPERATION>
         The operation to revoke (create, get, encrypt, decrypt, import, revoke, locate, rekey, destroy)

Options:
-h, --help
         Print help (see a summary with '-h')

POST to the /access/revoke endpoint with the JSON object:

{
   "unique_identifier": "1ae2...25df",  // the object unique identifier
   "user_id": "john.doe@acem.com", // the user identifier to allow
   "operation_type": "get" // the KMIP operation to allow
}

The response is a JSON object:

{
"success": "a success message"
}

Listing an object access rights

The owner of an object can list all the access rights that have been granted to another object.

➜ ckms access list --help
List the access rights granted on an object to other users.

This command can only be called by the owner of the object. Returns a list of users and the operations they have been granted access to.

Usage: ckms access list <OBJECT_UID>

Arguments:
<OBJECT_UID>
         The object unique identifier

Options:
-h, --help
         Print help (see a summary with '-h')

GET to the /access/list/{object_unique_id} endpoint:

The response is a JSON array:

[
   {
      "user_id": "the user identifier the access rights are granted to",
      "operations": [ <operation type> ]
   }
]

where <operation type> is one of the following: export, get, encrypt, decrypt, import, revoke, destroy.

Listing the objects owned by a user

A user can list all the objects it owns (i.e. the objects it created using either the Create, CreateKeyPair, or Import KMIP operations).

➜ ckms access owned --help
List the objects owned by the calling user.

Owners of objects can perform any operation on these objects and can grant access rights on any of these operations to any other user.

Usage: ckms access owned

Options:
-h, --help
         Print help (see a summary with '-h')

GET to the /access/owned endpoint:

The response is a JSON array:

[
   {
      "object_id": "the object unique identifier",
      "state": "<state>",
      "attributes": "<attributes>",
      "is_wrapped": "<wrapped_state>"
   }
]

where:

  • <state> is one of the following KMIP states: PreActive, Active, Deactivated, Compromised, Destroyed_Compromised,
  • <attributes> is the KMIP Attributes structure (see the KMIP documentation)
  • <wrapped_state>: is a boolean indicating whether the object is wrapped or not (see key wrapping).

Listing the access rights obtained by a user

A user can list all the access rights that have been granted to it by object owners.

➜ ckms access obtained --help
List the access rights obtained by the calling user

Returns a list of objects, their state, their owner and the accesses rights granted on the object

Usage: ckms access obtained

Options:
-h, --help
      Print help (see a summary with '-h')

GET to the /access/obtained endpoint:

The response is a JSON array:

[
      {
      "object_id": "the object unique identifier",
      "owner_id": "the user identifier of the owner of the object",
      "state": "<state>",
      "operations": [ <operation type> ]
      "attributes": "<attributes>",
      "is_wrapped": "<wrapped_state>"
      }
]

where:

  • <state> is one of the following KMIP states: PreActive, Active, Deactivated, Compromised, Destroyed_Compromised,
  • <operation type> is one of the following: export, get, encrypt, decrypt, import, revoke, destroy,
  • <wrapped_state>: is a boolean indicating whether the object is wrapped or not (see key wrapping).

S/MIME Gmail service account configuration

When using S/MIME, the gmail_api_conf field should be set in the configuration file to provide the necessary information about the configured service account to interact with Gmail API, and handle identities and keypairs easily from the ckms.

This configuration is mandatory for ckms google subcommands.

The gmail_api_conf field is a TOML object with the following fields:

  • type
  • project_id
  • private_key_id
  • private_key
  • client_email
  • client_id
  • auth_uri
  • token_uri
  • auth_provider_x509_cert_url
  • client_x509_cert_url
  • universe_domain

I can be retrieved directly from a TOML file downloaded from Google interface when creating and configuring the service account (following Google documentation).

Example:

[http_config]
server_url = "http://127.0.0.1:9998"

[gmail_api_conf]
type = "service_account"
project_id = "project_id"
private_key_id = "abc123abc123abc123abc123abc123abc123"
private_key = "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
client_email = "xxx@yyyy.iam.gserviceaccount.com"
client_id = "12345678901234567890"
auth_uri = "https://accounts.google.com/o/oauth2/auth"
token_uri = "https://oauth2.googleapis.com/token"
auth_provider_x509_cert_url = "https://www.googleapis.com/oauth2/v1/certs"
client_x509_cert_url = "https://www.googleapis.com/robot/v1/metadata/x509/xxx%40yyyy.iam.gserviceaccount.com"
universe_domain = "googleapis.com"

Eviden VM

Eviden VM offers:

  • a hardened pre-configured Linux distribution (based on Ubuntu 22.04 or RHEL 9) with LUKS disk encryption designed for confidential computing machines.
  • a seamless, unique experience across all confidential hardware (Intel TDX, AMD SEV-SNP, soon NVIDIA H100) and cloud providers (AWS, Azure, GCP)
  • an extended verifiability of the hardware, OS, and binaries running on the VM.

Specifically, the Eviden VM provides the following features:

  • Confidentiality: the whole environment runs in a Trusted Execution Environment (TEE) with encrypted memory on top of encrypted storage.
  • Verifiability: the user can verify the integrity of the hardware, OS, and executables at any time and compare them against a reference snapshot
  • Genericity: compatible with AMD SEV-SNP, Intel TDX, and TPM or vTPM. Works identically on all cloud providers.
  • No code modification: no third-party library or any change to applications
  • Simplicity: manual configuration is reduced to the bare minimum. A Linux administrator enjoys the same experience as that of a standard VM.

Use cases

Eviden VM is used in these two scenarios:

  • Quickly migrate an on-premise application to the public cloud without modifications while running it with the same level of confidentiality, e.g., migrating an HR application to the cloud.
  • offer confidential SaaS services to your customers/users, protecting their privacy against the cloud provider and any extra-territorial law, e.g., offering confidential AI/Gen. AI in the public cloud.

setup flow

Threat Model

Eviden VM is designed to secure your application against passive (honest-but-curious) and active (malicious) cloud provider staff member

Extended Verifiability

Eviden VM provides memory and disk encryption on a hardened system and extends the hardware remote attestation supplied by Intel and AMD to the verifiability of the operating system, as well as the binary files or other files marked as verifiable on the system. A user can quickly check the integrity of the whole system (no hardware change, no OS change, known binaries running or having run) before sending sensitive data for processing.

To do this, Eviden VM builds on the following components:

  • Trusted Execution Environment (TEE) such as AMD SEV-SNP or Intel TDX for memory encryption
  • Trusted Platform Module (TPM) or vTPM (virtual TPM) to store secrets and attest the content of some memory region
  • Integrity Measurement Architecture (IMA), a Linux kernel module used to maintain a measurement log of all executables

To securely tie these components together, Eviden VM image contains the following software:

  • cosmian_vm_agent: an agent running in the confidential VM to forward attestations, collaterals (e.g. root certificates) and measurement log
  • cosmian_fstool to ease the generation of LUKS container with the secret key stored in the TPM/vTPM (automount on reboot)

Our client CLI cosmian_vm can be used to interact with cosmian_vm_agent and verify the trustworthiness of a specific instance launched with Eviden VM as base image.

Audit

Eviden VM image construction process can be found in this repository: https://github.com/Cosmian/cosmian_vm/packer

Usage

Setup flow

An Eviden VM is instantiated from a cloud provider marketplace on top of a confidential computing machine. Once the system administrator has installed the application and its dependencies, a snapshot of the VM is taken. Integrity checks can then be performed on the running VM to verify the running code, OS, and hardware.

setup flow

Snapshot of the system

The snapshot of the system is a crucial step performed by cosmian_vm_agent to produce a JSON file with:

  • TEE policy
  • TPM policy
  • List of measured files and their hash digests

It's a one-time process that is done before you decide to freeze the instance and expect no more modification.

The content will be compared against TEE attestation, TPM/vTPM attestation and IMA measurement log to verify the trustworthiness of the remote instance.

Verification of the remote instance

The verification process of the Eviden VM is performed using client CLI cosmian_vm, which will check:

  • IMA (Integrity Measurement Architecture) measurement log with the list of executable and configuration file's hash digest, to be compared against the snapshot
  • TPM (Trusted Platform Module) attestation of the IMA measurement log
  • TEE (Trusted Execution Environment) attestation to ensure the instance is running on secure hardware with encrypted memory

The verification is successful if files in IMA measurement log and snapshot have the same hash digests, TPM or vTPM correctly attested the IMA measurement log content and finally if TEE attestation is valid.

simple verification flow

detailed verification flow

Cloud providers support

Eviden VM already supports AMD SEV-SNP and Intel TDX but it might depend on the cloud provider.

AWS images

Official imageOS imageOS versionKernel versionEviden base image
AWS - AMD SEVubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-v20251212Ubuntu24.046.17.0-1007-awsbase-image-X-Y-Z-ubuntu-sev
AWS - AMD SEVRHEL-9.4.0_HVM-20250519-x86_64-0-Hourly2-GP3Redhat9.45.14.0-427.37.1.el9_4.x86_64base-image-X-Y-Z-ubuntu-sev

Azure images

Official imageOS imageOS versionKernel versionEviden base imageVersion
Azure - Intel TDXCanonical-ubuntu-24_04-lts-cvmUbuntu24.046.8.0-1020-azurebase-image-ubuntu-tdxX.Y.Z
Azure - AMD SEVCanonical-ubuntu-24_04-lts-cvmUbuntu24.046.17.0-1010-azure-fdebase-image-ubuntu-sevX.Y.Z
Azure - Intel TDXRedhat-rhel-cvm-9_4_cvmRedhat9.45.14.0-427.37.1.el9_4.x86_64base-image-rhel-tdxX.Y.Z
Azure - AMD SEVRedhat-rhel-cvm-9_4_cvmRedhat9.45.14.0-427.37.1.el9_4.x86_64base-image-rhel-sevX.Y.Z

GCP images

Official imageOS imageOS versionKernel versionEviden base image
GCP - Intel TDXubuntu-2404-noble-amd64-v20260225Ubuntu24.046.17.0-1008-gcpbase-image-X-Y-Z-ubuntu-tdx
GCP - AMD SEVubuntu-2404-noble-amd64-v20260517Ubuntu24.046.17.0-1018-gcpbase-image-X-Y-Z-ubuntu-sev
GCP - AMD SEVrhel-9-v20251016Redhat9.65.14.0-570.52.1.el9_6.x86_64base-image-X-Y-Z-rhel-sev
GCP - Intel TDXrhel-9-v20251016Redhat9.65.14.0-570.52.1.el9_6.x86_64base-image-X-Y-Z-rhel-tdx

Marketplace Image content

The Eviden VM image built on the marketplaces of GCP, Azure, or AWS contains four primary executables:

  • cosmian_vm_agent is designed to be deployed on the Eviden VM. It serves on demand the collaterals used to verify the trustworthiness of the Eviden VM, such as the IMA file, the TEE quote, or the TPM quote

  • cosmian_fstool is designed to generate a LUKS container and enroll the TPM to be automatically started on reboot

  • cosmian_vm is a CLI designed to be used on your host. It queries the cosmian_vm_agent to get the collaterals used to verify the integrity/trustworthiness of the Eviden VM

This image:

  • contains the fully configured IMA
  • contains the fully configured SELinux
  • disables the auto-update (to avoid any modification of the Eviden VM after having snapshotted it)
  • contains the fully configured cosmian_vm_agent

This is an abstract of the updated file tree:

.
├── etc
│   ├── apt
│   │    └── apt.conf.d
│   │       └── 10periodic
│   ├── cosmian_vm
│   │   └── agent.toml
│   ├── default
│   │   └── grub
│   ├── ima
│   │   └── ima-policy
│   └── systemd
│       └── system
│           ├── cosmian_vm_agent.service
│           └── mount_luks.service
├── root
│   ├── mount_luks.sh
├── usr
│   └── local
|       └── bin
│           ├── cosmian_fstool
│           └── cosmian_vm_agent
└── var
    └── lib
        └── cosmian_vm
            ├── container   <--- LUKS container
            ├── tmp
            └── data        <--- LUKS container mounted
                ├── cert.pem
                └── cert.key

Eviden VM reminder

First, read detailed information about Eviden VM or about Eviden VM Agent and related software tools functioning.

As a reminder, the Eviden VM's goal is to verify Confidential VM trustworthiness and integrity at anytime. This verification runs on the operating system where one or more applications have been installed.

First, a snapshot is generated, freezing the state of the system and all executable files.

Then, at anytime, a remote verification of the VM can be done using the Eviden VM CLI tool (cosmian_vm).

setup flow

The Eviden VM can be deployed on virtual machines that supports AMD SEV-SNP or Intel TDX technologies.

The following steps help to deploy a Eviden VM instance on any supported cloud provider.

The Eviden VM contains a set of software to ensure trustworthiness of the executable environment of the VM.

Then, the deployment flow is the following:

  • instantiate a Eviden Confidential VM,
  • connect on this VM and install everything required for your application to run correctly,
  • create for once a VM snapshot remotely using Cosmian VM CLI,
  • verify at anytime the integrity of the VM

Instantiate Eviden VM on your favorite cloud provider 🚚

Go the Eviden marketplace webpage of the chosen cloud provider:

Select an OS, set an external IP and continue until the Eviden VM instance is spawned.

Here's the list of instance types by cloud provider

Cloud providerAzureGCPAWS
AMDSNPSNPSNP
Standard_DCas_v5n2d-standardM6a
Standard_DCads_v5C6a
R6a
IntelTDXTDXTDX
DCes_v5-seriesc3-standardNot available
ECesv5-series
(preview)

Customize your VM 👩‍🔧

Connect to the spawned Eviden VM using SSH and install whatever is required for application and services to run (installing software and dependencies, setting-up configurations and services etc.).

For example, deploy an app and setup it as a Linux service.

Snapshot the VM remotely

Once the VM is configured as needed, Eviden VM Agent can do a snapshot of the VM containing fingerprint of the executables and metadata related to TEE and TPM.

Install the Eviden VM CLI on your local machine

Install the Eviden VM CLI on a local machine

Download the binary and allow it to be executed:

sudo apt update && sudo apt install -y wget
wget https://package.cosmian.com/cosmian_vm/1.3.21/ubuntu-22.04/cosmian-vm_1.3.21-1_amd64.deb
sudo apt install ./cosmian-vm_1.3.21-1_amd64.deb
cosmian_vm --version

Download the binary and allow it to be executed:

sudo apt update && sudo apt install -y wget
wget https://package.cosmian.com/cosmian_vm/1.3.21/ubuntu-24.04/cosmian-vm_1.3.21-1_amd64.deb
sudo apt install ./cosmian-vm_1.3.21-1_amd64.deb
cosmian_vm --version

Download the binary and allow it to be executed:

sudo dnf update && dnf install -y wget
wget https://package.cosmian.com/cosmian_vm/1.3.21/rhel9/cosmian_vm-1.3.21-1.x86_64.rpm
sudo dnf install ./cosmian_vm-1.3.21-1.x86_64.rpm
cosmian_vm --version

Start a Ubuntu-based Docker container and enter it:

docker run -it ubuntu:22.04 /bin/bash

Download the binary and allow it to be executed:

apt update && apt install -y wget
wget https://package.cosmian.com/cosmian_vm/1.3.21/ubuntu-22.04/cosmian-vm_1.3.21-1_amd64.deb
apt install ./cosmian-vm_1.3.21-1_amd64.deb

Generate a snapshot of the Eviden VM:

cosmian_vm --url https://${COSMIAN_VM_IP_ADDR}:5555 --allow-insecure-tls snapshot

Verify the VM snapshot

Take a look at the global flow to fully understand the whole verification process of a Eviden VM.

Previous downloaded snapshot is stored as cosmian_vm.snapshot file (see the previous step).

  • At anytime, the Eviden VM integrity can be verified by running:
cosmian_vm --url https://${COSMIAN_VM_IP_ADDR}:5555 --allow-insecure-tls \
          verify --snapshot cosmian_vm.snapshot

Why --allow-insecure-tls flag?

When the agent starts (see step Snapshot the VM) self-signed certificate is created to enable HTTPS out of the box.

The self-signed certificate can be replaced by a certificate signed by a trusted authority such as Let's Encrypt (see certbot for certificate generation).

See how to setup trusted certificates.

Deploy your application as a service

The benefit of setting the deployed app as a service is that Eviden VM Agent is able to:

  • handle the lifetime cycle of the app (start, stop, restart)
  • deploy safely (in encrypted folder) the config file

Setup the systemd service

Connect to the Eviden VM instance through SSH to perform this setup.

  1. write a service file (here compatible with systemctl)

    [Unit]
    Description=My App
    Requires=multi-user.target
    After=multi-user.target mount_luks.service cosmian_vm_agent.service
    
    [Service]
    Type=simple
    User=root
    ExecStart=/usr/local/bin/my_app
    Restart=on-failure
    RestartSec=3s
    Environment="MY_APP_CONF=/var/lib/cosmian_vm/data/app/app.conf"
    
    [Install]
    WantedBy=multi-user.target
    

About the paths...

Any provided config file will be renamed to `app.conf` and will be stored as filepath `/var/lib/cosmian_vm/data/app/app.conf`, so the app `my_app` may know the location of configuration file.

Here the env variable `MY_APP_CONF` is used to forward this information to `my_app`.
  1. register the new service to systemctl

Copy the my_app.service file into /etc/systemd/system/my_app.service and enable this service (without starting it, Cosmian VM Agent that will be responsible of starting it)

sudo systemctl enable my_app
sudo systemctl daemon-reload

Note: my_app and my_app_svc are indicative naming and can be changed, but don't forget to update the Eviden VM Agent config file (/etc/cosmian_vm/agent.toml) as well.

Configure the remote app safely

On the local machine, write the config file of the app, and then use the Eviden VM CLI to remotely configure the app.

  1. write the app config file

    [my_cfg]
    key = "value"
    secret = "a98jfdol"
    

    The format (TOML, JSON, INI...) of this config file depends on the app but the Eviden VM CLI doesn't care, as the config file is treated as a blob of bytes.

  2. send the configuration using cosmian_vm CLI

    cosmian_vm --url https://app.company.com app init -c my_app.toml
    

    The app conf is written in the encrypted folder.

    Eviden VM Agent start/restart automatically the app after writing the config file when init is called.

Control the remote app as a service

The cosmian_vm CLI also contains two subcommands designed to drive your application running inside the Cosmian VM.

It could be relevant if the personnel in charge of the application doesn't have the rights to connect to the Cosmian VM through SSH for instance.

Security

If your Eviden VM is reachable over Internet, be aware that anyone can control your application. Out of the box the access to the cosmian_vm_agent endpoints is not authenticated.

Before going any further, you need to add a paragraph app inside the Cosmian VM configuration file, as follow:

[agent]
host = "0.0.0.0"
port = 5555
ssl_certificate = "data/cosmianvm.cosmian.dev/cert.pem"
ssl_private_key = "data/cosmianvm.cosmian.dev/key.pem"
tpm_device = "/dev/tpmrm0"

[app]
service_type = "systemd"
service_name = "my_app"
app_storage = "data/app"

The service_type could be standalone, systemd or supervisor and defines the way to start/stop the application. The service_name field defines the name of the service or the binary to start/stop. The app_storage field defines the root path where the application will store its data. If it's a relative path, the root path will be located inside /var/lib/cosmian_vm/. If you put it in the subfolder data, this directory is therefore encrypted and protected from the cloud provider.

only data/ is encrypted

Only the data/app/ part is customizable in the Cosmian VM Agent config file (/etc/cosmian_vm/agent.toml), but it remains relative to /var/lib/cosmian_vm/.

Keep in mind that /var/lib/cosmian_vm/data is the mount point of the encrypted folder. Anything outside this folder is NOT encrypted!

Let's imagine, the application is installed in the Cosmian VM but not configured and started. Then, you can provide a configuration file (containing secrets for instance) and start it using:

cosmian_vm --url https://app.company.com app init --conf app.json

Also, if needed, the application can be restarted using:

cosmian_vm --url https://app.company.com app restart

The configuration file can be anything the application expects. Here, a json file. It will be sent to the cosmian_vm_agent and stored in the LUKS container in /var/lib/cosmian_vm/data/app/app.conf.

If you call again init the previous configuration file is overwritten.

Advanced settings

Eviden VM logs

The logs of Cosmian VM are written in journalctl and can be accessed via journalctl -exu cosmian_vm_agent.

Temporary folder

Cosmian VM also contains an encrypted RAMFS in /var/lib/cosmian_vm/tmp.

The data stored inside is volatile and will be deleted when rebooting the VM. The size of this directory is 500MB.

Data protection on the filesystem

Out of the box, Cosmian VM filesystem is not entirely protected against the cloud provider.

However, there are two ways to store securely sensitive data:

  • LUKS container
  • RAMFS

Encrypted folder

At the first start of cosmian_vm_agent, a LUKS container is created at /var/lib/cosmian_vm/container (size=500MB).

This container is mounted into /var/lib/cosmian_vm/data. This directory can be used to store any sensitive data (ie: to store TLS certificate).

The password of the LUKS container is stored inside the LUKS itself, and can be used to migrate the container to another VM for example.

With Cosmian VM the LUKS is enrolled on the current TPM therefore the password won't be asked again, even at reboot.

To change the size of the container, create it again:

cosmian_fstool --size 100GB --location /data/container

Define your own container size and save the container to an additional disk for back-up (/data/container in this example).

A prompt will invite you to set the password. Save this password as it could be useful if you want to migrate the container to another VM.

If you need to manage the container (extend its size, revoke the password, etc.), you can use cryptsetup.

The fields ssl_certificate and ssl_private_key could be relative paths. In that case, this is always relative to /var/lib/cosmian_vm/.

Add measurement of scripts and configuration files

When using Cosmian VM with RHEL distribution, SELinux is enabled and configured in enforced mode. The SELinux module named cosmiand is present in Cosmian VM with 4 custom labels measured by Integrity Measurement Architecture (IMA):

  • cosmiand_exec_t for Cosmian VM related binaries which will transition to cosmiand_t
  • cosmiand_t for daemons when the binary file is labeled with cosmian_exec_t
  • cosmiand_script_t for any script file such as Python, Perl or shell scripts
  • cosmiand_conf_t for Cosmian VM configuration files

Because IMA only tracks binaries loaded in memory, cosmiand_script_t and cosmiand_conf_t allow to label specific files read by interpreters or applications. It will ensure that these files are not modified after the snapshot.

To label a new file, use semanage and restorecon commands:

# change SELinux context (always use absolute path)
semanage fcontext -a -t cosmiand_script_t "$(realpath my_script.py)"
# restore default context to apply the change (always use absolute path)
restorecon -v "$(realpath my_script.py)"

then you can see the new context with:

$ ls -Z
system_u:object_r:cosmiand_script_t:s0 my_script.py

See RHEL SELinux documentation for more details and advanced usage of SELinux.

Cosmian VM Agent lifecycle

Cosmian VM Agent can be started/restarted/stopped.

Start the agent:

systemctl start cosmian_vm_agent

Restart the agent

systemctl restart cosmian_vm_agent

Stop the agent

systemctl stop cosmian_vm_agent

Configure Cosmian VM Agent

Cosmian VM Agent relies on a configuration file located at /etc/cosmian_vm/agent.toml.

A minimal configuration file is:

[agent]
host = "127.0.0.1"
port = 5555
ssl_certificate = "data/cert.pem"
ssl_private_key = "data/key.pem"
tpm_device = "/dev/tpmrm0"

By default, cosmian_vm_agent uses the port 5555 on localhost.

Configure HTTPS with your own domain

On a fresh install, cosmian_vm_agent uses a self-signed certificate generated at the start of the service and set the CommonName of the certificate to the value of the machine hostname.

Therefore when using the CLI, --allow-insecure-tls must be added to ignore SSL errors (due to self-signed cert). This is not a good practice in production.

To enable HTTPS with trusted certs:

  • Edit your DNS registry to point to that VM

  • Create a trusted certificate using the method of your choice (Let's encrypt for instance)

  • Edit the nginx configuration file to point to the location of the TLS certificate and private key:

    server {
            ...
    
            ssl_certificate /var/lib/cosmian_vm/data/cert.pem;
            ssl_certificate_key /var/lib/cosmian_vm/data/key.pem;
    }
    
  • Edit the cosmian_vm_agent configuration file to point to the location of the TLS certificate and private key:

    [agent]
    ...
    ssl_certificate = "data/cert.pem"
    ssl_private_key = "data/key.pem"
    
  • Restart both services

    sudo systemctl restart nginx
    sudo systemctl restart cosmian_vm_agent
    

Configure HTTPS with another certs

This configuration example sets the certificate from another application running in Cosmian VM:

[agent]
host = "0.0.0.0"
port = 5555
ssl_certificate = "/etc/letsencrypt/live/app.company.com/fullchain.pem"
ssl_private_key = "/etc/letsencrypt/live/app.company.com/privkey.pem"
tpm_device = "/dev/tpmrm0"

Note: in this case, fullchain.pem and privkey.pem are not located in the LUKS (under /var/lib/cosmian_vm/data/) so they are stored in cleartext on the disk hence potentially accessible by the Cloud Provider.

If you want to store these certificate securely, move them into the LUKS and change the location in the agent.toml file (don't forget to do the same for Nginx if applicable) like this:

[agent]
...
ssl_certificate = "data/fullchain.pem" # equivalent to /var/lib/cosmian_vm/data/fullchain.pem
ssl_private_key = "data/privkey.pem"   # equivalent to /var/lib/cosmian_vm/data/privkey.pem

By setting a relative path, the agent will complete the path using the default mounted path for the LUKS, which is /var/lib/cosmian_vm/data (read here for more information about LUKS setup).

Snapshot deep dive

Take a snapshot of the remote system

cosmian_vm --url https://app.company.com snapshot

The snapshot is performed by cosmian_vm_agent to produce a single file with the trusted state of the machine at the current time. This file is returned to the Cosmian VM CLI when using the subcommand snapshot.

The snapshot file is a JSON file containing:

  • IMA measurement log at the current time
  • List of all measured files and their corresponding hash (sha256 by default)
  • TEE metadata
  • TPM metadata

Here is a sample:

{
  "tee_policy":{
    "Sev":{
      "measurement":"73797518025d1d20e09efdf10e383cd0115e00562109b04ec577b5bd5d2ddab12f1f9ee22758f50a121355cf5aac6507",
      "report_data":"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
      "family_id":"00000000000000000000000000000000",
      "image_id":"00000000000000000000000000000000",
      "guest_svn":0,
      "id_key_digest":"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
      "author_key_digest":"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
      "policy":196608,
      "report_id":"b6db1e43f868a4823f708df92942a0448750fc5c37f7a724bc49292ffd111e1a",
      "report_id_ma":"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
    }
  },
  "tpm_policy":{
    "reset_count":15,
    "restart_count":0
  },
  "filehashes":[
    ["/snap/google-cloud-cli/187/platform/gsutil/third_party/mock/mock/__pycache__/__init__.cpython-39.pyc", "f708df92942a044875[...]abc"],
    ...
  ]
}

Verification with multiple domains

Typically, the verification is done using the CLI as follow:

cosmian_vm --url https://app.company.com verify \
        --snapshot cosmian_vm.snapshot

When verifying a Cosmian VM you can also check that the TLS certificate of your services installed inside this VM are the one used when querying the Cosmian VM Agent during the verification.

The goal is to verify your services currently running inside this Cosmian VM.

To do so, use --application as many times as needed:

cosmian_vm --url https://app.company.com verify \
        --snapshot cosmian_vm.snapshot \
        --application service1.company.com:3655 \
        --application service2.company.com

Verification failure after reboot

Due to the IMA design, the VM cannot be trusted after a reboot. Indeed the IMA is reset at each restart.

Cosmian VM uses the TPM to detect a VM reboot: if the reset counter differs from the one captured during the snapshot, the verification fails.

If this reboot is trusted (for instance it has been made by the system administrator), you just need to ask for a new snapshot.

If the reboot occurs without the system administrator consent, the VM should be investigated to detect any malicious modification.

Cosmian VM diff with a Ubuntu/RHEL base image

The modifications are related to the installation and the configuration of Cosmian VM software stack. All the changes are performed using Packer and can be found in Cosmian VM Github page.

Cosmian VM image:

  • contains the fully configured IMA
  • contains the fully configured SELinux (RHEL only)
  • disables the auto-update (see Disabled auto-update)
  • contains the fully configured cosmian_vm_agent

This is an abstract of the updated file tree:

.
├── etc
│   ├── apt
│   │    └── apt.conf.d
│   │       └── 10periodic
│   ├── cosmian_vm
│   │   └── agent.toml
│   ├── default
│   │   └── grub
│   ├── ima
│   │   └── ima-policy
│   └── systemd
│       └── system
│           └── cosmian_vm_agent.service
├── usr
│   └── local
│       └── bin
│           ├── cosmian_fstool
│           └── cosmian_vm_agent
└── var
    └── lib
        └── cosmian_vm
            ├── container   <--- LUKS container
            ├── tmp
            └── data        <--- LUKS container mounted
                ├── cert.pem
                └── cert.key

Disabled auto-update

The verification performed by the Cosmian VM relies on the fact that once the snapshot has been made, the VM content shouldn't be altered. If any modification is detected, the VM is considered compromised.

An auto-update processing alters the VM and makes the comparison with the snapshot impossible.

You shall update the Eviden VM manually and create a new snapshot afterwards.

Use a proxy in front of the Cosmian VM Agent

Although it's technically possible to configure an HTTPS proxy in front of the Cosmian VM Agent, it will prevent you from proceeding the verification through the CLI if you configure the proxy as an SSL forward.

Indeed, the TLS certificate configured in the Cosmian VM Agent is a part of the exchange with the TEE. To avoid any malicious software to send fake collaterals, the TLS certificate used to get these collaterals should be the same as the one configured in the Cosmian VM Agent. This certificate should stay inside the Cosmian VM.

If you use a proxy, the TLS tunnel, from the CLI to the agent, uses the certificate of the proxy and not the certificate of the agent. The verification will therefore fail, being not able to determine if it's a malicious or healthy behavior.

However, if you use a proxy as an SSL passthrough, it will work like a charm.

Here is an example with HAProxy:

[...]

defaults
    mode                 tcp
    option               tcplog
    [...]

# Entrypoint of the ha_proxy listen on 443
frontend https-in
    # Do not decrypt ssl yet
    bind *:443

    tcp-request inspect-delay 60s
    tcp-request content accept if { req_ssl_hello_type 1 }
    use_backend 8d14ff4ac2d452c3.cosmian.io if { req_ssl_sni -i 8d14ff4ac2d452c3.cosmian.io }
    # No default backend: return an error as fallback

backend 8d14ff4ac2d452c3.cosmian.io
    server haproxy-app 162.19.91.151:27283 check

Other resources:

Azure instantiation

Step by step guide

Step 1: Get Eviden Verifiable VM

Step 2: Continue

Step 3: Create

Step 4: Basics

Step 5: No additional encryption at host

Step 6: Create

Step 7: Completed

AWS instantiation

Step by step guide

Step 1: Launch a new instance

Step 2: Configure instance

Step 3: Configure instance

Step 4: Launch succeeded

GCP instantiation

Step by step guide

Step 1: Get started

Step 2: Configuration

Step 3: Completed

Eviden AI

Eviden AI aims to perform query over an AI runner (summary, translation, query, RAG) in a secured workflow.

Deploy Eviden VM AI

Follow the instructions from the deployment guide.

Hardware optimization

When running on Intel Xeon processors, the application can leverage AMX (Advanced Matrix Extensions) to significantly enhance performance, especially for matrix-intensive operations commonly found in machine learning workloads. To enable this feature, simply start the application with the "use_amx" key to true from your config file.

  {
    "use_amx": true,       // Optional - default set to false
  }

HuggingFace authentication

To use certain Hugging Face models—such as the one required by the Translation endpoint—you must provide a Hugging Face access token. This token should be configured in the application's configuration file.

  {
      "hf_token": "hf_token", // Mandatory (used for translation models)
  }

Usage

Eviden AI Runner exposes endpoints which can be fetched:

Summarize text

Summary is made using "facebook/bart-large-cnn" model.

  • Endpoint: /summarize
  • Method: POST
  • Description: get the summary of a given text, using the configured model
  • Request:
    • Headers: 'Content-Type: multipart/form-data'
    • Body: doc - text to summarize, using model configured on summary config section
  • Response:
  {
    "summary": "summarized text..."
  }
  • Example:
curl 'http://0.0.0.0:5000/summarize' \
--form 'doc="Il était une fois, dans un royaume couvert de vert émeraude et voilé dans les secrets murmurants des arbres anciens, vivait une princesse nommée Elara.."'

Translate text

Translation is made using one of the "Helsinki-NLP/opus-mt" models.

  • Endpoint: /translate
  • Method: POST
  • Description: get the translation of a given text, using model configured on translation config section
  • Request:
    • Headers: 'Content-Type: multipart/form-data'
    • Body: doc - text to translate src_lang - source language tgt_lang - targeted language
  • Response:
      {
        "translation": "translated text..."
      }
    
  • Example:
    curl 'http://0.0.0.0:5000/translate' \
    --form 'doc="Il était une fois, dans un royaume couvert de vert émeraude et voilé dans les secrets murmurants des arbres anciens, vivait une princesse nommée Elara.."' --form 'src_lang=fr'  --form 'tgt_lang=en'
    

Predict using text as context

  • Endpoint: /context_predict
  • Method: POST
  • Description: get prediction from a model using current text as a context
  • Request:
    • Headers: 'Content-Type: multipart/form-data'
    • Body: context - text to use as context for prediction query - query to answer
  • Example:
    curl 'http://0.0.0.0:5000/context_predict' \
    --form 'query="Who is Elara?"' --form 'context="Elara is a girl living in a forest..."'
    
  • Response: The response contains the answer to the query, from given context.
      {
        "result": ["Elara is the sovereign of the mystical forests of Eldoria"]
      }
    

Predict using RAG

  • Endpoint: /rag_predict
  • Method: POST
  • Description: get prediction from a model using RAG and configured documentary basis
  • Request:
    • Headers: 'Content-Type: multipart/form-data'
    • Body: db - documentary basis to use for prediction query - query to answer
  • Example:
    curl 'http://0.0.0.0:5000/rag_predict' \
    --form 'query="Who is Esmeralda?"' --form 'db="literature"'
    
  • Response: The response contains the answer to the query, from given context.
      {
        "result": ["a street dancer"]
      }
    

You can list available documentary basis and their uploaded references from current configuration using:

  • Endpoint: /documentary_bases
  • Method: GET
  • Example:
    curl 'http://0.0.0.0:5000/documentary_bases'
    
  • Response:
    {
        "documentary_bases": {
            "literature": [
                "NDame de Paris"
            ],
            "science": []
        }
    }
    

Manage references

You can add an .epub document, .docx document or a PDF to the vector DB of the given RAG associated to a database, using:

  • Endpoint: /add_reference
  • Method: POST
  • Request:
    • File sent on multipart
    • Body: db - database to insert reference reference - reference to insert
  • Example:
    curl -F "file=@/data/doc.pdf" --form 'db="literature"' --form 'reference="crypto"'  http://0.0.0.0:5000/add_reference
    
  • Response:
    File successfully processed
    

So far, only epub, pdf and docx files can be handled.

You can remove a reference to the vector DB of the given RAG associated to a database, using:

  • Endpoint: /delete_reference
  • Method: DELETE
  • Request:
    • Body: db - database to remove reference from reference - reference to delete
  • Example:
    curl --form 'db="Literature"' --form 'reference="NDame de Paris"'  http://0.0.0.0:5000/delete_reference
    
  • Response:
    Reference successfully removed
    

Authentication

If your AI Runner is configured to handle authentication, in order to make requests to Eviden VM AI, users must be authentificated using your Identity Provider application (setup previously for Client-Side encryption). It should be a Single Page or Web application type.

Deploy AI Runner

An Eviden VM with a pre-installed API to serve language models for summarization and translation tasks.

This instance can be deployed on virtual machines that supports AMD SEV-SNP or Intel TDX technologies.

Please first read the guide about how to setup an Eviden VM.

The following steps can help one to deploy its own instance on each available cloud provider.

Please first read the guide about how to setup an Eviden VM.

Deploy Eviden VM AI on a cloud provider 🚚

Go the Eviden marketplace webpage of the chosen cloud provider.

Select an OS and continue until the Eviden VM AI instance is spawned.

Here's the list of instance types by cloud provider

Cloud providerAzureGCPAWS
AMDSNPSNPSNP
Standard_DCas_v5n2d-standardM6a
Standard_DCads_v5C6a
R6a
IntelTDXTDXTDX
DCes_v5-seriesc3-standardNot available
ECesv5-series
(preview)

The Eviden VM AI Runner contains:

  • a ready-to-go Nginx setup (listening on port 443 and locally on port 5001)
  • a AI Runner service which is ready but not started yet (needs a valid configuration to start)
  • the Eviden VM software stack. As reminder, Eviden VM Agent is listening on port 5555.

At the first start of the VM, Eviden AI Runner is not configured by default. Indeed AI Runner configuration can potentially contain secrets that need to be protected. That is why the configuration MUST be sent remotely and securely to the VM using the Eviden VM CLI see app init.

Service

Systemd is used to initialize and run the AI runner and the Eviden VM agent.

See the running services with the following command:

systemctl status cosmian_ai_runner
systemctl status cosmian_vm_agent

You can read as well full logs using:

journalctl -u cosmian_ai_runner
journalctl -u cosmian_vm_agent

Configure the AI Runner 📜

As explained previously, it is safe to provide secrets (such as passwords) in the configuration file because this file is going to be stored in the encrypted folder (LUKS) of the Eviden AI Runner.

{
  "use_amx": false,       // Optional - default set to false
  "hf_token": "hf_token", // Mandatory (used for translation models)
}

Identity Provider application can be configured as follow:

{
  "auth": {
    "openid_configs": [
      {
        "client_id": "XXXX",
        "jwks_uri": "XXXX"
      }
    ]
  }
}

Exposed documentary bases can be configured as follow :

{
  "documentary_bases": [
    {
      "name": "Literature",
      "persist_path": "rag_store_literature",
      "model": "google/flan-t5-large",
      "task": "text2text-generation",
      "kwargs": {
        "max_new_tokens": 500
      }
    },
    {
      "name": "Science",
      "persist_path": "rag_store_science",
      "model": "google/flan-t5-large",
      "task": "text2text-generation",
      "kwargs": {
        "max_new_tokens": 500
      }
    }
  ]
}

More details about the config file can be found here.

Use Eviden VM CLI to send securely the new AI Runner configuration

Eviden VM CLI has to be installed on the client machine (Ubuntu, RHEL or via Docker). Please follow the installation instructions.

Deploy the configuration and starts the Eviden AI Runner

cosmian_vm --url https://${COSMIAN_VM_IP_ADDR}:5555 \
           --allow-insecure-tls \
           app init -c config.json

This command will send via an encrypted tunnel the configuration that will be written in the remotely path /var/lib/cosmian_vm/data/app.conf which is contained in an encrypted container (LUKS).

Hardware optimization

When running on Intel Xeon processors, the application can leverage AMX (Advanced Matrix Extensions) to significantly enhance performance, especially for matrix-intensive operations commonly found in machine learning workloads. To enable this feature, simply start the application with the "use_amx" key to true from your config file.

  {
    "use_amx": true,       // Optional - default set to false
  }

Check the connection with the AI Runner

curl --insecure https://${COSMIAN_VM_IP_ADDR}/health

Why --allow-insecure-tls and --insecure flags?

When the agent starts (see Snapshot the VM) self-signed certificate is created to enable HTTPS out of the box.

These certificates must be replaced by trusted ones using tools like cosmian_certtool or Linux tools (certbot with Let's Encrypt for instance).

See how to setup trusted certificates.

Snapshot the VM

Once the VM is configured as needed, Eviden VM Agent can do a snapshot of the VM containing fingerprint of the executables and metadata related to TEE and TPM.

The agent creates an encrypted folder (LUKS container) to store sensitive information, creates self-signed certificate for Nginx and starts a snapshot.

Wait for the agent to initialize the LUKS and generate the certificates. This is automatically at boot.

Verify the Eviden VM AI Runner integrity ✅

Verifying trustworthiness of the Eviden VM AI Runner is exactly the same process as verifying the Eviden VM itself.

Note that when using Eviden VM AI Runner with RHEL distribution, Python files related to the AI runner are measured and checked during the verification. See SELinux in Eviden VM.

Eviden Enclave

Eviden Enclave allows to quickly deploy confidential Python web applications with the following security features:

  • Code runs in a Trusted Execution Environment (TEE) and is encrypted with your key.
  • Secure channel is established directly and uniquely with your code running inside the TEE.
  • The integrity of the code loaded in TEE's memory is verifiable through a remote attestation process.
  • Everyone interacting with your application can verify that your code runs in a TEE thanks to a Transport Layer Security (TLS) extension called Remote Attestation TLS (RA-TLS).

Use Cases

Two common use cases of Eviden Enclave are:

  1. Offer confidential AI/ML SaaS services to your customers while guaranteeing their data cannot be accessed by you or by the cloud provider.
  2. Deploy confidential code, models, and/or data in a Zero-Trust environment, where you have zero control over the infrastructure and its administration. For instance, an AI company deploying a fine-tuned model in a customer's infrastructure wants to keep the code and model secrets.

Security guarantees

Eviden Enclave protects data and metadata against system administrators with root access and the underlying cloud provider who owns the hardware infrastructure.

Then, privileged users or anyone with physical access to the host machine:

  • Cannot alter the integrity of data and code in the protected area of the TEE.
  • Cannot access your application's unique TLS server key generated inside the TEE or decrypt the TLS session.
  • Cannot decrypt your encrypted code or access the secret key.
  • Cannot access the persistent storage of your application tied to the TEE and the fingerprint of your application.

These assumptions remain valid as long as the TEE hardware, namely Intel SGX, and its software stack known as the Trusted Computing Base (TCB) are not subject to severe vulnerabilities.

Cloud provider availability

As of August 2024, Microsoft Azure, Alibaba Cloud and OVH Cloud offer Intel SGX processors with SGX2 capability. Microsoft Azure and Alibaba Cloud provide VM-based instances with the SGX driver loaded while OVH Cloud provides dedicated servers with Intel SGX processors.

Bare Metal requirements

Eviden Enclave requires Intel Xeon 4th or 5th-gen processors with SGX2. To find all the compatible Intel processors, see Intel Processors supporting Intel SGX.

Workflow

The following diagram illustrates the scenario with two roles:

  • The SGX operator who is the administrator of the SGX machine and the one going to run the confidential code
  • The code provider who writes the Python code and sends it encrypted to the SGX operator

deployment

  1. The Python code must be an ASGI application using Python web framework such as Flask or FastAPI. It can be optionally encrypted with a random key using the Eviden Enclave CLI with --encrypt argument.
  2. Since the code is encrypted under a key you only know, the code can be transmitted through any channel, even insecure.
  3. Using the CLI, The SGX Operator loads the encrypted code in a new Eviden Enclave. It outputs an evidence.json file, which should be returned to the code provider.
  4. The code provider verifies the evidence file using the CLI. It ensures that the correct code is loaded in a fresh new Eviden Enclave and has not been tampered with. This process, called the remote attestation, also verifies if the TEE is genuine and will tell you if there are known vulnerabilities on the remote server's Trusted Computing Base. The file also contains a public key whose corresponding private key is only known to your Eviden Enclave and cannot be recovered by the SGX Operator.
  5. Using the unique enclave's public key tied to your code, the code provider encrypts a secrets.json file that contains the code decryption key and any other secrets specific to your application.
  6. The enclave can now decrypt the code inside Intel SGX encrypted memory and run it.

The provider can send data to the application's endpoints over a secure RA-TLS channel directly with the enclave.

Intel SGX and Gramine

Intel Software Guard eXtensions (SGX) offers hardware-based memory encryption that isolates specific application code and data in memory. Intel SGX allows user-level code to allocate private regions of memory, called enclaves, which are designed to be protected from processes running at higher privilege levels.

Because working with Intel SGX requires low-level C programming with the Intel SDK, we use Gramine, a lightweight guest OS designed to run a single Linux application with minimal host requirements and no modification. It is the foundation of Eviden Enclave which allows us to expose a Python confidential web application in the cloud.

Code encryption

Before sending the Python code of your Eviden Enclave, each file is encrypted with XSalsa20-Poly1305 using a random symmetric key. The symmetric key is provisioned when you are confident that your code is running in the enclave by checking the remote attestation.

Remote attestation

A very important aspect of Intel SGX (and more generally Trusted Execution Environments) is attestation. This is a mechanism for a remote user to verify that the application runs on a real hardware in an up-to-date hardware and software with the expected initial state. In other words, remote attestation provides the assurance to the user that the remotely executing SGX enclave is trusted and that the correct code is executed.

To process the remote attestation, an SGX quote is required as a proof of trustworthiness. It's a structure which contains, among others, interesting fields for the end user:

  • MRENCLAVE, SHA-256 hash digest of the memory footprint during the execution of the code
  • MRSIGNER, SHA-256 hash digest of Eviden's enclave signer public key
  • Debug flag, which must not be set in production
  • Intel's certification chain and signature to attest the quote

Verification of trustworthiness is done using intel-sgx-ra.

The SGX quote is embedded in the TLS certificate generated by all Eviden Enclave in a protocol called RA-TLS.

RA-TLS

To ease the transport of the quote without modifying TLS, the quote is directly embedded in a specific X509 extension of the TLS certificate. In addition, a SHA-256 hash digest of the certificate public key is included in the REPORTDATA field of the quote to link the quote to the certificate (i.e. the certificate has been generated in the code corresponding to MRENCLAVE).

It allows to fetch the certificate before using the API provided by the application in Eviden Enclave and check first that it runs in an up-to-date SGX enclave with the correct code (by checking MRENCLAVE). Then you can add the certificate as CA in your HTTPS client to be sure that you will always interact with the same microservice you checked.

RA-TLS certificate (source: Gramine doc)

                                     ┌────────────────────────────────┐
                                     │                       Server   │
                                     │                                │
                                     │ ┌────────────────────────────┐ │
                                     │ │                Intel SGX   │ │
                                     │ │ ┌────────────────────────┐ │ │
                                     │ │ │                 Code   │ │ │
                                     │ │ │                        │ │ │
┌──────────┐                         │ │ │ ┌────────────────────┐ │ │ │
│          │   RA-TLS Channel        │ │ │ │                    │ │ │ │
│  Client  ├─────────────────────────┼─┼─┼─┤  TLS Private key   │ │ │ │
│          │                         │ │ │ │                    │ │ │ │
└──────────┘                         │ │ │ └────────────────────┘ │ │ │
                                     │ │ │ ┌────────────────────┐ │ │ │
                                     │ │ │ │                    │ │ │ │
                                     │ │ │ │ RA-TLS Certificate │ │ │ │
                                     │ │ │ │                    │ │ │ │
                                     │ │ │ └────────────────────┘ │ │ │
                                     │ │ └────────────────────────┘ │ │
                                     │ └────────────────────────────┘ │
                                     └────────────────────────────────┘

Direct encryption to your enclave

When you can't communicate on the RA-TLS channel (proxy or third party sending data to your enclave), it is still possible to encrypt the payload for your enclave in a programmatic way.

The REPORTDATA field of the SGX quote included in the RA-TLS certificated is 64 bytes field structured as follow:

SHA-256(certificate's public key) || Curve25519 enclave's public key

The purpose of the Curve25519 enclave's public key is to encrypt data for your enclave anonymously using NaCl seal boxes. On the enclave's code side, it's then possible to decrypt using the private key only available inside the enclave in /key/enclave.key file. We recommend to use our cryptographic library cenclave-lib-crypto which is already available in your Eviden Enclave but any library compatible with NaCl should work.

In order to launch and use SGX enclaves, you will need to install some Intel's software: Intel Platform SoftWare (PSW), Trusted RunTime System (TRTS), Untrusted RunTime System (URTS) and a compatible Linux kernel >= 6.0 which includes the Intel SGX driver and the support of EDMM. In addition, Eviden Enclave heavily uses the library OS Gramine for isolation but also to provide the best experience for Python developers with no need for third party libraries.

Host requirements

Recommended configuration:

  • Ubuntu 22.04 or Ubuntu 24.04
  • Linux Kernel >= 6.0
  • Docker >= 27.0

Ansible deployment

A simple Ansible script for Microsoft Azure can be found in Eviden Enclave repository. Contact us for a more complex Ansible script for On Premise infrastructure.

Welcome to Eviden Enclave deployment tutorial

To launch your first Eviden Enclave, follow this tutorial in your favorite terminal. Note that you first need to setup your Intel SGX machine based on the Server Installation section.

Install the Eviden Enclave CLI

The Eviden Enclave CLI requires at least Python 3.8 and OpenSSL 1.1.1 series. It is recommended to use pyenv to manage different Python interpreters if needed.

$ pip3 install cenclave
$ cenclave --help
usage: cenclave [-h] [--version]
                {encrypt,decrypt,evidence,scaffold,list,logs,package,restart,run,status,seal,unseal,spawn,stop,test,localtest,verify}
                ...

Cosmian Enclave CLI - 1.0.0

options:
  -h, --help            show this help message and exit
  --version             version of cenclave CLI

commands:
  {encrypt,decrypt,evidence,scaffold,list,logs,package,restart,run,status,seal,unseal,spawn,stop,test,localtest,verify}
    encrypt             encrypt a file using Fernet symmetric encryption
    decrypt             decrypt a file using Fernet symmetric encryption
    evidence            collect the evidences to verify on offline mode the application and the enclave
    scaffold            create a new boilerplate web application
    list                list the running containers
    logs                print container logs
    package             generate a package containing the container image and the code to run with
                        Cosmian Enclave
    restart             restart a web application
    run                 finalise the configuration of the application docker and run the application
                        code
    status              print the container status
    seal                seal file using NaCl's Seal Box.Recipient is either raw X25519 public key or
                        extracted from RA-TLS certificate with enclave's public key in REPORT_DATA field
                        of SGX quote
    unseal              unseal file using NaCl's Seal Box
    spawn               spawn container with specified web app
    stop                stop and optionally remove a running container
    test                Test a deployed web app
    localtest           test locally a web app without SGX
    verify              verify the trustworthiness of a running Cosmian Enclave web application and get
                        the RA-TLS certificate

Docker configuration

Before deploying the app, verify that Docker service is up and your current user is part of docker group. See Manage Docker as a non-root user.

Scaffold your app

User

This command is for the Code Provider role

$ cenclave scaffold example
$ tree -a example
example
├── Dockerfile
├── README.md
├── config.toml
├── secrets.json
├── secrets_to_seal.json
├── src
│   ├── .cenclaveignore
│   └── app.py
└── tests
    ├── conftest.py
    └── test_app.py

3 directories, 9 files

The Dockerfile should be based on cenclave-image-base image and include all the dependencies required to run your application.

The file app.py is a basic Hello World Flask application. Adapt by addind your own Python code in this boilerplate.

from http import HTTPStatus
from flask import Flask, Response

app = Flask(__name__)


@app.get("/health")
def health_check():
    """Health check of the application."""
    return Response(response="OK", status=HTTPStatus.OK)


@app.route('/')
def hello():
    """Get a simple example."""
    return "Hello world"

# other endpoints
# ...

The configuration to run your application is done in a TOML file config.toml which will be used by the CLI:

name = "example"
python_application = "app:app"
healthcheck_endpoint = "/health"
tests_cmd = "pytest"
tests_requirements = [
    "intel-sgx-ra",
    "pytest",
]

The project tree also contains a test directory to test your application locally and remotely. It might help to debug and detect runtime errors before running your code in an Eviden Enclave.

Compatibility with WSGI/ASGI

To be compliant with Eviden Enclave your Python application must use a compatible web framework based on ASGI or WSGI. Flask and FastAPI are the most popular Python web frameworks. It's not possible to deploy a standalone Python program.

Examples

See Eviden Enclave examples for proof-of-concept of confidential web applications.

Test your app, your docker and your configuration

User

This command is for the code provider role

$ cenclave localtest --code example/src/ \
                     --dockerfile example/Dockerfile \
                     --config example/config.toml \
                     --test example/tests/

or more concisely with default arguments:

cenclave localtest --project example

Testing your code before sending it to the SGX operator is recommended. Note that any error will require to restart the deployment flow from scratch.

Create the application package with the code and the docker image

User

This command is for the code provider role

This command generates a tarball named package_<app_name>_<timestamp>.tar which can be send to to the SGX operator:

$ cenclave package --code example/src/ \
                   --dockerfile example/Dockerfile \
                   --config example/config.toml \
                   --test example/tests/ \
                   --output code_provider/

or more concisely with default arguments:

$ cenclave package --project example \
                   --output code_provider/

Add --encrypt argument if you want to encrypt your code.

Spawn the application docker image

User

This command is for the SGX operator role

The spawn command will run a new Eviden enclave with your code loaded but it will stay in an intermediate state called the configuration server. Configuration server is waiting additional information such as the decryption key of the code or any other secrets needed by your application.

$ cenclave spawn --host 0.0.0.0 \
                 --port 9999 \
                 --size 4096 \
                 --package code_provider/package_<app_name>_<timestamp>.tar \
                 --output sgx_operator/ \
                 --san <EXTERNAL_IP_ADDR | DOMAIN_NAME | localhost>
                 app_name
  • host: usually 127.0.0.1 or 0.0.0.0
  • port: port used by your application
  • size: memory size (in MB) of the enclave to spawn (must be a power of 2 greater than 1024)
  • package: the application package containing the Docker images and the code
  • output: directory to write the evidence JSON file
  • san: Subject Alternative Name to use for routing with SSL pass-through (either domain name, external IP address or localhost)

The output file sgx_operator/evidence.json contains cryptographic proofs related to the enclave and be shared with anyone to interact with your enclave. It will be used by the code provider to verify the running app.

Collect the evidences to verify the application

User

This command is for the SGX operator role

cenclave evidence --output sgx_operator/ app_name

This command collects cryptographic proofs related to the enclave and serialize them in a JSON file evidence.json.

You can choose another PCCS by specifying the --pccs parameter, by default it will determine the PCCS URL by parsing the aesmd service configuration file: /etc/sgx_default_qcnl.conf.

The file sgx_operator/evidence.json can be shared to any user of your application to allow them to do the remote attestation of your service.

Check the trustworthiness of the application

User

This command is for the SGX operator role

The trustworthiness is established by using 2 inputs:

  • the full code package (tarball file)
  • evidences captured from the running enclave (JSON file)
$ cenclave verify --package code_provider/package_<app_name>_<timestamp>.tar \
                  --evidence sgx_operator/evidence.json \
                  --output /tmp

If the verification succeeds, the RA-TLS certificate is written as a file named ratls.pem, and you can now seal the code's secret key to share it with the SGX operator.

Seal your secrets for your Eviden Enclave

User

This command is for the code provider role

Using the unique enclave's public key tied to your code, it's possible to seal secrets which can only be decrypted by a specific Eviden Enclave. The output of seal command is an encrypted file which can be sent to the SGX operator to start your application.

$ cenclave seal --input example/secrets_to_seal.json \
                --receiver-enclave /tmp/ratls.pem \
                --output code_provider/secrets_to_seal.json.seal

Finalize the configuration and run the application

User

This command is for the SGX operator role

$ cenclave run --sealed-secrets code_provider/secrets_to_seal.json.seal \
               --secrets example/secrets.json \
               app_name

If the command is successful, your application now running normally using a RA-TLS certificate.

Test the deployed application

User

This command is for the SGX operator role

$ cenclave test --test sgx_operator/tests/ \
                --config sgx_operator/config.toml \
                app_name

This command helps to check that the application is executed properly as the code provider expects.

Example of encryption on the application side

User

These commands are for the code provider role

If your are using the example from cenclave scaffold command, there are endpoints to show how to encrypt the body of an HTTP response with a key provided with sealed secrets at the start of your application.

Sealed secrets

The JSON file secrets_to_seal.json has been sent sealed to the enclave. The JSON value corresponding to JSON key result_sk inserted in secrets_to_seal.json will be used to encrypt the response.

Remember that seal secrets can only be decrypted by the specific Eviden Enclave used as receiver.

First collect the encrypted result:

curl --cacert /tmp/ratls.pem https://<IP_ADDR>:9999/result/sealed_secrets > result.enc

and decrypt the result with key.bin which contains the raw bytes of result_sk key (not encoded in Base64 such as in secrets_to_seal.json):

$ cenclave decrypt --input result.enc
                   --key key.bin \
                   --output code_provider/result.plain
$ cat code_provider/result.plain
message using result_sk from SEALED_SECRETS

The decrypt command only supports Fernet algorithm. This is a toy example and we advise to use proper encryption methods such as AES256-GCM or XChacha20-Poly1305.

One of the advantages of using Eviden Enclave to protect your application and data in the cloud, is that if you have already developed a Python web application then it does not need to be modified. Just write a configuration TOML file and run the deploy subcommand.

In the following sections, we give good practices and some security considerations you need to know before deploying your application in production.

Requirements

It's recommended to use Ubuntu 22.04 or 22.04 with at least python 3.10 on the host.

Using a third-party service with secrets

Before sending the Python code of your microservice, each file is encrypted except: requirements.txt.

This code is supposed to be sharable to any user, as your convenience, in order to check the trustworthiness of your app. As a matter of fact, do not write any secret into your code. For example: passwords or keys to connect to a third-party service like a remote storage or a database.

If you need such secrets to run your code, write them in a secrets.json file. Please see the example below. This file will be sent to the enclave after the latter has been verified during the app deployment. Note that this file is not encrypted and can be read by the SGX operator. Your application will then be able to read it to retrieve the secrets it needs.

Example of a secrets.json file:

{
  "login": "username",
  "password": "azerty"
}

Which is used by this application code example:

import os
import json

from pathlib import Path
from flask import Flask

app = Flask(__name__)


@app.route('/whoami')
def whoami():
    """A simple example manipulating secrets."""
    secrets = json.loads(Path(os.getenv("SECRETS_PATH")).read_text())
    return secrets["login"]

Encrypting some secrets

If your application requires some secrets to be hidden from the SGX operator, write those secrets in another file, for example secrets_to_seal.json. Then you can seal this secrets_to_seal.json file with the cenclave seal command. This command encrypts the secrets.json file using the trusted RA-TLS certificate. This certificate embeds the public key of the enclave, ensuring that only the enclave is able to decrypt the sealed secrets.json file.

Specific filepath available in Eviden Enclave

Find below a small example to show how to use paths in your Eviden Enclave:

import os

from http import HTTPStatus
from pathlib import Path
from datetime import datetime
from flask import Flask, Response

app = Flask(__name__)

# define a global variable from env variables
WORKFILE: Path = Path(os.getenv("HOME")) / "date.txt"


@app.post('/')
def write_date():
    """A simple example of file writing."""
    # transparently encrypt the file which can't be read on the host
    WORKFILE.write_text(str(datetime.now()))
    return Response(status=HTTPStatus.OK)


@app.route('/')
def read_date():
    """A simple example of file reading."""
    if not WORKFILE.exists():
        return Response(response="You should write before read",
                        status=HTTPStatus.NOT_FOUND)

    # transparently decrypt the file with a key specific to your Eviden Enclave
    txt = WORKFILE.read_text()
    WORKFILE.unlink()
    return txt

Your application owns a dedicated storage up to 10GB. The following directories are all encrypted on the host and can only be decrypted by your Eviden Enclave:

EnvPathEncrypted (1)Persistent (2)Comments
$HOME/root✔️✖️home directory to save anything such as cache or configuration files
$SECRETS_PATH$HOME/.cache/cenclave/secrets.json✔️✖️application secrets file for infrastructure credentials (database, or network access, then known by the SGX operator)
$SEALED_SECRETS_PATH$HOME/.cache/cenclave/sealed_secrets.json✔️✖️application secrets sealed for your specific enclave (content only readable in your enclave)
$TMP_PATH/tmp✔️✖️temporary folder for your application (only readable in your enclave)
$MODULE_PATH/app✔️✖️folder with the decrypted application code (only readable in your enclave)

Note that writing operations in $HOME are about 2.5 times slower than in a $TMP_PATH. However the max file size you can allocate in $TMP_PATH is hardware_memory / 4 and the number of files has no limit as long as the sum of their size is lower than the size still available. Choose wisely the file location based on your own application constraints.

(1) Only the enclave containing this version of your code can decrypt this directory. Another enclave or even another version of your application won't be able to read it

(2) The content will be removed when the application is stopped

.cenclaveignore file

You can edit .cenclaveignore file in your code directory. This file is read by the CLI when deploying an app and avoid packaging some files if not needed. The syntax is the same as .gitignore.

A default .cenclaveignore is generated by the cenclave scaffold command.

Memory size

When you declare the memory size through the field hardware in config.toml, note that a part of this memory is used by the system itself.

All the libraries needed to run your application will be loaded in the enclave memory. Therefore, the effective memory size available for your application is approximately: hardware_memory - libraries_size.

When running the Docker container locally, you can use the option --memory to estimate the memory size of your application. See GitHub repository cenclave-image-base for more details.

Limitations

Please find below limitations that you need to consider to be able to run smoothly your application in Eviden Enclave:

  • Fork are not allowed for performance issues to avoid bad user experience, creating new enclaves is slow.
  • Some application are using a lot of memory and when working with enclaves the memory is limited and must be used with care. Consider using a comfortable amount of EPC memory and RAM regarding your application.

User

This command is for the SGX operator role

List running Eviden Enclave's applications:

cenclave list

User

This command is for the SGX operator role

Application stdout/stderr logs:

cenclave logs [-f] <app_name>

User

This command is for the SGX operator role

Restart your application if needed:

cenclave restart <app_name>

User

This command is for the SGX operator role

Status of a specific application:

cenclave status <app_name>

The status can be in the following states:

  • initializing: the configuration server or the application server is starting
  • waiting secret keys: waiting for the secret key to decrypt the code and/or secrets for your application
  • running: the web app is currently running
  • on_error: the application has stopped with an error, see cenclave logs to inspect the error

User

This command is for the SGX operator role

Stop a running application as follow:

cenclave stop [--remove] <app_name>

If you stop an app without the parameter --remove, you can restart it later by recovering the last app state. However you can't spawn a new app on the same port and with the same name if you don't remove it.

Eviden Findex server

The Eviden Findex is a high-performance, open-source, server application written in Rust that provides a REST API to index and search encrypted data.

What is Findex?

Findex is a cryptographic protocol designed to make search queries on an untrusted cloud server securely. Findex is concurrent and database-independent, allowing large database indexes to be outsourced securely without compromising usability.

Architecture client server

Findex aims to solve the following problem:

Tip

How to securely recover the location of an encrypted data matching a given keyword?

Findex has been published as a scientific paper in the IACR ePrint archive: https://eprint.iacr.org/2024/1541.

What is Findex server?

Findex server is a high-performance, open-source server application written in Rust that implements the Findex protocol and offers a REST API to store encrypted indexes and perform search queries on them. Findex Server mainly is an authentication layer and therefore all data-critical manipulations are performed by a database.

The server is designed to be used in conjunction with the Eviden CLI, a command-line interface that allows users to interact with the server.

Threat Model

The threat model for Findex assumes that the cloud server is untrusted and may attempt to infer information from the encrypted indexes and search queries. However, the server is considered honest-but-curious, meaning it will follow the protocol correctly but will try to learn as much as possible from the data it processes. Findex is designed to protect against such adversaries by ensuring that no useful information about the plaintext data and the minimum of information is leaked during search queries.

Tip

Basically, the server does not know how are encrypted the indexes nor the datasets they contain. It can only perform search queries on the encrypted indexes and return the results to the client.

Quick Start

For server side: Findex server.

For client side: Eviden CLI.

Findex server

Client-Side Encryption

Client-side encryption is a crucial aspect of the Findex protocol, ensuring that data remains secure even when stored on an untrusted server. In this model, all encryption and decryption operations are performed on the client side, meaning that the server only ever sees encrypted data and cannot infer any useful information from it.

architecture-beta
    service user(disk)[Client side encryption]

    group untrusted(cloud)[Cloud]

    service server(server)[Findex Server] in untrusted
    service db(database)[Database] in untrusted
    service kms(server)[KMS] in untrusted
    service db2(database)[Keys] in untrusted

    user:R -- L:server
    server:R <--> L:db
    user:R -- L:kms
    kms:R <--> L:db2

How It Works

  1. Data Encryption: Before sending any data to the Findex server, the client encrypts the data using a secure encryption algorithm. This ensures that the plaintext data is never exposed to the server.

  2. Index Encryption: Similarly, the indexes used for search queries are also encrypted on the client side. This prevents the server from learning anything about the content of the indexes.

  3. Search Queries: When performing a search query, the client encrypts the search keyword and sends the encrypted query to the server. The server processes the query on the encrypted indexes and returns the encrypted results to the client.

  4. Data Decryption: Upon receiving the encrypted results from the server, the client decrypts the data to obtain the plaintext results.

Benefits

  • Data Privacy: Since the server only handles encrypted data, it cannot access the plaintext information, ensuring data privacy.
  • Security: Client-side encryption protects against potential data breaches on the server, as the encrypted data would be useless to an attacker without the decryption keys.
  • Control: Users retain full control over their encryption keys and can manage their own security policies.
  • Database Independence: Findex server is designed to work with any database, as the encryption and decryption operations are performed independently of the underlying storage mechanism. It relies on state-of-the-art solutions like Redis and can therefore provide a strong data-persistence insurance. An identified issue in the existing SSE literature is that all schemes rely on ad-hoc server implementations, which increases the probability of bugs, crashes and data-loss. In comparison, Findex Server is "just" a glorified Redis, which is good.

By leveraging client-side encryption, Findex ensures that sensitive data remains secure and private, even when stored and processed on an untrusted server.

The Findex server, written in Rust and using the Actix-web framework, is a REST-API server used to store encrypted indexes and perform search queries on them. It is designed to be used in conjunction with the Eviden CLI, which is a command-line interface that allows users to interact with the server.

Features

The server offers a REST API with the following functionalities:

Quick start

Multiple options are available to run the Findex server, including using Docker, pre-built binaries, or building from source.

Warning

No authentication is configured for quick start. This is not recommended for production use.

The quickest way to get started with Findex server is to use the Docker image. To run the server binary on http://localhost:6668 that stores its data in a Redis server, run the following command:

docker compose -f docker-compose-quick-start.yml up

where docker-compose-quick-start.yml is the following:

services:
  redis:
    container_name: redis
    image: redis:latest
    ports:
      - 6379:6379
  findex-server:
    container_name: findex-server
    image: ghcr.io/cosmian/findex-server:latest
    ports:
      - 6668:6668
    environment:
      FINDEX_SERVER_DATABASE_TYPE: redis
      FINDEX_SERVER_DATABASE_URL: redis://redis:6379
      FINDEX_SERVER_CLEAR_DATABASE: true

An other option include running the server binary directly installing the Debian package available here.

First, run the Redis server independently:

docker run -d -p 6379:6379 redis

Then, download package and install it:

sudo apt update && sudo apt install -y wget
wget https://package.cosmian.com/findex-server/0.4.14/ubuntu-24.04/cosmian-findex-server_0.4.14-1_amd64.deb
sudo apt install ./cosmian-findex-server_0.4.14-1_amd64.deb
cosmian_findex_server --version

The server should now be running on http://localhost:6668.

An other option include running the server binary directly installing the Debian package available here.

First, run the Redis server independently:

docker run -d -p 6379:6379 redis

Then, download package and install it:

sudo dnf update && dnf install -y wget
wget https://package.cosmian.com/findex-server/0.4.14/rhel9/cosmian_findex_server-0.4.14-1.x86_64.rpm
sudo dnf install ./cosmian_findex_server-0.4.14-1.x86_64.rpm
cosmian_findex_server --version

The server should now be running on http://localhost:6668.

On ARM MacOS, download the build archive and extract it:

wget https://package.cosmian.com/findex-server/0.4.14/macos_arm-release.zip
unzip macos_arm-release.zip
cp ./macos_arm-release/cosmian_findex_server /usr/local/bin/
chmod u+x /usr/local/bin/cosmian_findex_server
cosmian_findex_server --version

On Windows, download the build archive:

 https://package.cosmian.com/findex-server/0.4.14/windows-release.zip

Extract the cosmian_findex_server from:

/windows-release/cosmian_findex_server.exe

Copy it to a folder in your PATH and run it:

cosmian_findex_server --version

To build the server from source, clone the repository and run the following commands:

git clone https://github.com/Cosmian/findex-server.git
cd findex-server
cargo build

First, run the Redis server independently:

docker run -d -p 6379:6379 redis

Then, run the server:

cargo run --bin cosmian_findex_server -- --database-url redis://localhost:6379 --database-type redis

Configuration

Please refer to the configuration documentation for more information on how to configure the Findex server.

Usage

Eviden Findex server

Usage: cosmian_findex_server [OPTIONS]

Options:
      --database-type <DATABASE_TYPE>
          The database type of the Findex server
          - redis: Redis database. The Redis url must be provided [env: FINDEX_SERVER_DATABASE_TYPE=] [default: redis] [possible values: redis]
      --database-url <DATABASE_URL>
          The url of the database [env: FINDEX_SERVER_DATABASE_URL=] [default: redis://localhost:6379]
      --clear-database
          Clear the database on start.
          WARNING: This will delete ALL the data in the database [env: FINDEX_SERVER_CLEAR_DATABASE=]
      --port <PORT>
          The Findex server port [env: FINDEX_SERVER_PORT=] [default: 6668]
      --hostname <HOSTNAME>
          The Findex server hostname [env: FINDEX_SERVER_HOSTNAME=] [default: 0.0.0.0]
      --https-p12-file <HTTPS_P12_FILE>
          The Findex server optional PKCS#12 Certificates and Key file. If provided, this will start the server in HTTPS mode [env: FINDEX_SERVER_HTTPS_P12_FILE=]
      --https-p12-password <HTTPS_P12_PASSWORD>
          The password to open the PKCS#12 Certificates and Key file [env: FINDEX_SERVER_HTTPS_P12_PASSWORD=]
      --authority-cert-file <AUTHORITY_CERT_FILE>
          The server optional authority X509 certificate in PEM format used to validate the client certificate presented for authentication. If provided, this will require clients to present a certificate signed by this authority for authentication. The server must run in TLS mode for this to be used [env: FINDEX_SERVER_AUTHORITY_CERT_FILE=]
      --jwt-issuer-uri <JWT_ISSUER_URI>...
          The issuer URI of the JWT token [env: FINDEX_SERVER_JWT_ISSUER_URI=]
      --jwks-uri <JWKS_URI>...
          The JWKS (Json Web Key Set) URI of the JWT token [env: FINDEX_SERVER_JWKS_URI=]
      --jwt-audience <JWT_AUDIENCE>...
          The audience of the JWT token [env: FINDEX_SERVER_JST_AUDIENCE=]
      --default-username <DEFAULT_USERNAME>
          The default username to use when no authentication method is provided [env: FINDEX_SERVER_DEFAULT_USERNAME=] [default: admin]
      --force-default-username
          When an authentication method is provided, perform the authentication but always use the default username instead of the one provided by the authentication method [env: FINDEX_SERVER_FORCE_DEFAULT_USERNAME=]
  -h, --help
          Print help (see more with '--help')
  -V, --version
          Print version

Configuration file

By default, the server expects to find:

  • a TOML configuration file in /etc/cosmian/findex_server.toml.
  • or an environment variable COSMIAN_FINDEX_SERVER_CONF that contains the path to the configuration file.
  • otherwise, the server will parse the arguments passed in command line.

Example without authentication

default_username = "admin"
force_default_username = false

[db]
database_type = "Redis"
database_url = "redis://localhost:6379"
clear_database = false

[http]
port = 6668
hostname = "0.0.0.0"

Example with X509 authentication

default_username = "admin"
force_default_username = false

[db]
database_type = "Redis"
database_url = "redis://localhost:6379"
clear_database = true

[http]
port = 6660
hostname = "0.0.0.0"
https_p12_file = "/etc/cosmian/certificates/server/findex.server.acme.com.p12"
https_p12_password = "password"
authority_cert_file = "/etc/cosmian/certificates/server/ca.crt"

Example with OpenID authentication

default_username = "admin"
force_default_username = false

[db]
database_type = "Redis"
database_url = "redis://localhost:6379"
clear_database = false

[http]
port = 6668
hostname = "0.0.0.0"

[auth]
jwt_issuer_uri = "eyJhbGciOiJSUzI1NiIsInR5cCI...ydoDOsmYhWTEgf5w"

User Authentication

The application supports two methods of user authentication:

OpenID with JWT Access Token

In that case, the Findex server delegates the authentication process to an external OAuth2 server. In fine, the server issues a JWT Access Token that is used to authenticate the user with the Findex server.

Warning

The Findex server must be run with the following arguments: For example with Google OAuth2:

docker run -p 6668:6668 --name findex-server ghcr.io/cosmian/findex-server:latest \
           --jwt-issuer-uri=https://accounts.google.com \
           --jwks-uri=https://www.googleapis.com/oauth2/v3/certs \
           --jwt-audience=cosmian_findex_server

The authentication Flow can be summarized as follows:

  1. Client Requests Authorization:

    The client application initiates an authorization request. This is done via an OAuth 2.0 Authorization Code Flow. The client redirects the user to the Authorization Server (e.g., Google, GitHub, or a custom server) that has to be owned by the client.

  2. User Authenticates:

    The user authenticates with their credentials (e.g., username/password, social login). The Authorization Server verifies the user's identity.

  3. Access Token Issuance (JWT)

    The Authorization Server issues an Access Token in the form of a JWT. The token contains:

    • Header: Token type (JWT) and signing algorithm (e.g., RS256).
    • Payload (Claims): User information, permissions, expiration time (exp), issuer (iss), audience (aud), etc.
    • Signature: Ensures the integrity and authenticity of the token.
  4. Client Accesses Protected Resources

    The client includes the JWT in the Authorization header of HTTP requests to the Resource Server:

    Authorization: Bearer <JWT Access Token>
    
  5. Token Validation

    The Resource Server validates the JWT by:

    • Verifying the signature (using a public key if signed with RS256).
    • Checking standard claims (e.g., exp, iss, aud) for validity.
    • Decoding the payload to extract user information and permissions.
  6. Access Granted or Denied

    If the JWT is valid, the Resource Server allows access to the requested resources based on the user's claims. If invalid (e.g., expired, tampered), the request is denied with an HTTP 401 Unauthorized error.

sequenceDiagram
  actor User
  participant AuthServer as Authorization Server
  participant ResourceServer as Resource Server

  User->>AuthServer: Authorization Request
  AuthServer->>User: Redirect to Login
  User->>AuthServer: User Credentials
  AuthServer->>User: JWT Access Token
  User->>ResourceServer: Request with JWT
  ResourceServer->>ResourceServer: Validate JWT
  ResourceServer->>User: Access Granted/Denied

Info

JWT is a compact, URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is used as the payload of a JSON Web Signature (JWS) structure or as the plaintext of a JSON Web Encryption (JWE) structure. This enables the claims to be digitally signed or integrity protected with a Message Authentication Code (MAC) and/or encrypted.

Eviden CLI Configuration

Eviden CLI is helpful to authenticate with the Findex server using OpenID Connect. The CLI has to be installed locally and configured with the Findex server URL and OAuth2 configuration.

Info

The Eviden CLI realizes this authentication flow using this simple command:

cosmian findex-server login

By using this default configuration ~/.cosmian/cosmian.toml:

[kms_config]
print_json = false

[kms_config.http_config]
server_url = "http://127.0.0.1:9998"

[findex_config.http_config]
server_url = "https://<FINDEX-SERVER-URL>/"

[findex_config.http_config.oauth2_conf]
client_id = "99999999-abababababababababab.apps.googleusercontent.com"
client_secret = "XXX"
authorize_url = "https://accounts.google.com/o/oauth2/v2/auth"
token_url = "https://oauth2.googleapis.com/token"
scopes = ["openid", "email"]

X509 certificates authentication

In that case, the Findex server uses PKCS12 certificates to authenticate the user. The server requires the client to present a certificate signed by a trusted authority known by itself. The server also presents its own certificate to the client, which the client must verify.

Warning

The Findex server must be run with the following arguments: For example with Google OAuth2:

docker run -p 6668:6668 --name findex-server ghcr.io/cosmian/findex-server:latest \
           --https-p12-file=server/certificate.p12 \
           --https-p12-password=complex_password \
           --authority-cert-file=server/ca.crt

User access authorization

Once authenticated, users must have an authorization to an Index ID.

What is an Index ID?

An Index ID allows the partitioning of indexed data: behind an Index ID, there are:

  • encrypted indexes
  • and the encrypted version of the data that has been indexed.

This Index ID can be shared between users, with a specific permission. Users can also own multiple Index IDs.

Invocation of any method exposed in the API is conditioned by the validation and compatibility of the claimed permissions.

Permissions

sequenceDiagram
  autonumber
  actor User
  participant F as Findex server

  User->>F: User authentication
  User->>F: User create an Index ID
  F->>User: Index ID with admin permission
  User->>F: User set/revoke permission to another user

There are 3 permissions:

  • reader
  • writer
  • admin

The mechanism is pretty simple:

When a user creates a new Index ID, he becomes the admin of this index. He can then set other index users the role of reader, writer or admin.

Every server endpoint is protected by this authorization mechanism: the server checks the user's role before allowing access to the endpoint.

Permission format in database

Currently, there is an entry for each user in database. In the case of a key-value database, the key is the user ID (its email) and the value is a list of tuples (permission, index_id) where permission is 1 byte and index_id is an UUID of 16 bytes.

Example

KeyValue
user@gmail.com(0,d9eee59c-f9df-4edd-97bc-ba5952ce63af) | (1,5b044b87-bced-424c-9dac-f25550c88c20)

Endpoints

EndpointDescription
/create/indexCreate an Index ID
/permission/set/{user_id}/{permission}/{index_id}Set a permission to a user for a specific index
/permission/list/{user_id}List permissions of a user
/permission/revoke/{user_id}/{index_id}Revoke a user's permission for a specific index

Encrypted database

From the server's perspective, only encrypted data is received and stored as-is. The server does not know how the data is encrypted and cannot decrypt it.

The requirements database is a key-value store where the keys are unique identifiers (UIDs) and the values are the encrypted indexes or datasets.

In this scenario, the user is responsible for encrypting the data before sending it to the server. For example, a KEM/DEM encryption scheme can be used. KEM stands for Key Encapsulation Mechanism and DEM for Data Encapsulation Mechanism. A few words, the data is encrypted with a Data Encryption Key (DEK) and the DEK is encrypted with a Key Encryption Key (KEK) and sent along with the encrypted data.

User requires a Key Management System to encrypt the Data Encryption Key (DEK).

How to securely index new data?

Info

The user is already authenticated and has the write permission to a given index.

An example of KEM-DEM encryption scheme is the crypto-system RFC-5649 + AES-256-GCM.

sequenceDiagram
  autonumber
  actor U as User
  participant K as Key Management System
  participant F as Findex server

  U->>K: User requests a Key Encryption Key (KEK)
  K->>U: Send an ID of the KEK
  U->>U: Generate an ephemeral Data Encryption Key (DEK)
  U->>K: Encrypt the DEK with the KEK (RFC5649)
  K->>U: Send the encrypted DEK (encapsulation)

  loop Read and encrypt locally the dataset
    U-->U: Read line by line the dataset
    U-->U: For each line, an unique identifier (UID) is generated
    U-->U: Each line is encrypted with the DEK (AES-256-GCM)
  end

  U->>F: Send all encrypted lines (and encapsulation) and corresponding UIDs to a given Index ID

  loop Index and encrypt locally the dataset
    U->>U: Index plaintext line by keywords resulting encrypted indexes
  end

  U->>F: Send encrypted indexes

Warning

For now, only Redis database is supported.

How to securely search indexed data?

Info

The user is already authenticated and has the write permission to a given index.

sequenceDiagram
  autonumber
  actor U as User
  participant F as Findex server
  participant K as Key Management System

  U->>F: User does a search query by keywords
  F->>U: If data has been indexed with the given keywords, sends dataset UIDs
  U->>F: User requests the values of the dataset UIDs
  F->>U: Sends the encrypted values of the dataset UIDs

  loop Read the encrypted values
    U->>U: For each value, get the encrypted DEK (encapsulation)
    U->>K: Decrypt the DEK with the KEK
    K->>U: Send the decrypted DEK
    U->>U: Decrypt each value with the DEK
  end

Store and retrieve encrypted indexes as Findex requirements

The server presents the following endpoints:

EndpointDescription
/indexes/{index_id}/batch_readretrieve encrypted indexes
/indexes/{index_id}/guarded_writeinsert encrypted indexes

And consequently, the Findex [REST client implementation] (crate/client/src/rest_client.rs) implements the client side of the communication with the server.

Furthermore, the encryption is done by the client before sending the data to the server.

Database structure

In a key-value database (like Redis), indexes are stored as follows:

KeyValue
index_id || idFindex encrypted index

Concerning the format of encrypted indexes, please read the Findex github.

Store and retrieve the encrypted version of the data that has been indexed

Findex server stores as it is the encrypted version of the data that has been indexed. The server presents the following endpoints:

EndpointDescription
/datasets/{index_id}/datasets_add_entriesinsert new encrypted entries
/datasets/{index_id}/datasets_del_entriesdelete encrypted entries
/datasets/{index_id}/datasets_get_entriesget encrypted entries

The encryption is done by the client before sending the data to the server.

Database structure

In a key-value database (like Redis), dataset entries are stored as follows:

KeyValue
dataset entry idencrypted dataset entry

Using the Eviden CLI

The Eviden CLI allows to interact both with the KMS server and Findex server. Then you can realize the following operations illustrated in encrypted database.

As prerequisites, you need to have:

Depending on the authentication of these 2 servers, the configuration will have to be adapted. Refer to the configuration documentation for more information.

As a summary, below are described the steps:

  • to encrypt and store a dataset while indexing it.
  • to search a keyword and decrypt the corresponding result.

Encrypt and store a dataset while indexing it

The following steps allow to encrypt a data using a KEM-DEM crypto-system where:

  • the Data Encryption Key (DEK) is encrypted with a KMS server (with RFC-5649 wrapping key standard) using a Key Encryption Key (KEK)
  • the encrypted DEK is stored with the data encrypted using AES-256-GCM.

Generate the Key Encryption Key:

cosmian kms sym keys create

The symmetric key was successfully generated.
            Unique identifier: 55629c83-5184-4e54-9839-9b686a6f2850

Generate the Seed Key that will be used to encrypt locally the indexes:

cosmian kms sym keys create

The symmetric key was successfully generated.
            Unique identifier: fbf2a111-ae11-4231-9985-c0e3b140caeb

Then create a dedicated index id where all data will be stored:

cosmian findex-server permissions create

[admin] New admin permission successfully created on index: 13348510-75cd-436e-a9ff-60de66cac0d0

Then encrypt and index the following small dataset:

city,region,country,population
Southborough,MA,United States,9686
Northbridge,MA,United States,14061
Westborough,MA,United States,29313
Marlborough,MA,United States,38334
Springfield,MA,United States,152227
Springfield,MO,United States,150443
Springfield,NJ,United States,14976
Springfield,OH,United States,64325
Springfield,OR,United States,56032
Concord,NH,United States,42605

Using this command:

cosmian findex-server encrypt-and-index --seed-key-id fbf2a111-ae11-4231-9985-c0e3b140caeb --index-id 13348510-75cd-436e-a9ff-60de66cac0d0 --csv test_data/datasets/smallpop.csv --kek-id 55629c83-5184-4e54-9839-9b686a6f2850

Data behind those UUIDS were encrypted and indexed: UUID: 7059592d-9cd7-46d6-9e4d-b26436430942
UUID: d9eee59c-f9df-4edd-97bc-ba5952ce63af
UUID: 5b044b87-bced-424c-9dac-f25550c88c20
UUID: 863f39c5-bd6f-4685-97a2-07de8aa67c41
UUID: 90d05319-15c7-4c39-8176-85057c915b7b
UUID: dfe699ab-493d-49fc-b19c-9918376b2aa5
UUID: 61fcac44-0e91-470c-95f5-496f1b67389a
UUID: c6ab6f96-bba5-478b-be7f-2526e5b82e41
UUID: 3329fe2a-06c5-4904-aba7-4a9faf0e0876
UUID: f556fd71-7ef1-46ca-bf0f-30d92c055b44

Search a keyword

To search a keyword, find the corresponding UUIDs, get the values of these UUIDs and decrypt them (if any match is found), just proceed as follow:


cosmian findex-server search-and-decrypt --seed-key-id fbf2a111-ae11-4231-9985-c0e3b140caeb --index-id 13348510-75cd-436e-a9ff-60de66cac0d0 --kek-id 55629c83-5184-4e54-9839-9b686a6f2850 --keyword Southborough

Decrypted record: SouthboroughMAUnited States9686

Configuration

Please refer to the configuration documentation for more information on how to configure the Eviden CLI.