Canton KMS Driver developer guide
Introduction
The Canton protocol relies on cryptographic operations such as asymmetric encryption and digital signatures. To maximize the operational security of a Canton node the corresponding private keys should not be stored or processed in cleartext. A Key Management System (KMS) or Hardware Security Module (HSM) allows you to perform such cryptographic operations where the private key resides securely inside the KMS/HSM. All nodes in Canton can make use of a KMS. AWS KMS and Google Cloud KMS are supported as of Canton v2.7. To broaden the support of other KMSs and HSMs, Canton v2.9 introduced a plug-in approach, called KMS Drivers, which allows the implementation of custom integrations. This document explains the APIs that must be implemented for a KMS Driver, provides a guide on the implementation, and describes how to configure Canton to run with a KMS Driver. Currently, the KMS Driver is only available in a Scala implementation. You can find a “mock” KMS Driver implementation built on a software-based crypto library in the Canton community repository.KMS Driver API
The two main APIs that are required for a KMS Driver are:- Driver Factory: Implements how a driver is instantiated; and the main entry point for Canton to load a driver.
- KMS Driver: Offers cryptographic operations based on the KMS.
KMS Driver Factory API v1
The factory consists of two interfaces: a generic v1 DriverFactory and a specific v1 KmsDriverFactory. Thev1.DriverFactory defines the following aspects for a generic driver:
- The type of the driver
- A name that uniquely identifies the driver
- The version of the API the driver implements and optional build information (driver version number or commit hash)
- A driver-specific configuration object with configuration parser and writer
- A create method that instantiates a driver with that factory
v1.KmsDriverFactory is a specialization of the generic DriverFactory which defines the driver type as a KmsDriver and the API version as 1.
Driver Configuration Reading & Parsing
Canton uses pureconfig configuration library to read its configuration. Given that the configuration of the driver can be embedded inside a Canton configuration file, the factory of the driver needs to specify the type of configuration, as well as configuration readers and writers. AConfigType can be a case class or as basic as a Map[String, String]. In the latter case, the config reader and writer can be defined as:
val configReader = pureconfig.ConfigReader.mapReader[String]val configWriter = pureconfig.ConfigWriter.mapWriter[String]
KMS Driver API v1
The main part of the API is thev1.KmsDriver API that defines the following operations for a KMS Driver:
- Key Generation: Asymmetric signing and encryption key pairs as well as symmetric encryption keys
- Supported Key and Algorithm Specifications: The specs supported by the driver, for example, RSA 2048 keys and RSA OAEP SHA256 asymmetric encryption.
- Signing: Sign data with a key from the KMS and the specified algorithm.
- Asymmetric Decryption: Decrypt a ciphertext with a private key from the KMS and the specified algorithm.
- Symmetric Encryption and Decryption: Encrypt or decrypt with a symmetric encryption key from the KMS. The default symmetric encryption algorithm of the KMS is used.
- Key Management: Get the public key of a private key stored in the KMS, check if a key exists, and delete a key.
- Health: Return the health of the KMS Driver instance.
Error Handling and Health
In case the driver experiences an error theFuture of the operation should be failed with a KmsDriverException. When the exception’s flag retryable is true the caller side, (that is, Canton) performs a retry with exponential backoff. This behavior is suitable for transient errors, such as network issues, resource exhaustion, etc.
In case of permanent errors, a non-retryable exception should be thrown, which either fails the current operation from where the cryptographic operation is called or causes a fatal error in the Canton node.
The driver should report its health through the health method. A Canton node periodically queries the health of the driver and reports it as part of the node’s overall health.
Develop and Test a KMS Driver
Set Up API Dependency
The Canton KMS Driver API is published as an artifact on Digital Asset’s JFrog Artifactory: https://digitalasset.jfrog.io/ui/repos/tree/General/canton-kms-driver-api You must have a Canton enterprise license and account to access the artifact. You may need to configure your build system to authenticate with a personal access token towards JFrog Artifactory. In your build system of choice, you need to depend on the API as a regular Maven-style artifact with:- organization: com.digitalasset.canton
- artifact: kms-driver-api
- version: the Canton release version, e.g., 3.3.0
Implement the API and Build the Driver
Implement thev1.KmsDriverFactory by specifying the driver’s name, configuration type with readers/writers, and the create method to instantiate a driver by creating a new class instance of your KmsDriver implementation. Specify the fully qualified name of the factory class in the file:
src/main/resources/META-INF/services/com.digitalasset.canton.crypto.kms.driver.api.v1.KmsDriverFactory
and in the following file for the base driver factory (without v1):
src/main/resources/META-INF/services/com.digitalasset.canton.crypto.kms.driver.api.KmsDriverFactory
The major part of the implementation is the v1.KmsDriver specific to the KMS/HSM to be integrated with. The supported key and algorithm specifications can be defined statically depending on the capabilities of the underlying KMS/HSM. To ensure the best compatibility with other Canton nodes, all currently specified key and algorithm specifications should be supported.
Any credentials required by the underlying KMS/HSM can either be passed through the Canton configuration file as part of the driver-specific configuration, where secrets can be resolved from the environment, or retrieved by the driver directly from the environment or any other driver-specific means.
Bundle your driver into a self-contained jar, that is, with all required libraries included in the jar. That way you only need a single driver jar when starting Canton with your KMS Driver.
KMS Driver Testing
The reusable test suite for KMS Drivers is published at canton-kms-driver-testing. Configure your build system to depend on this maven artifact in the test scope of your project (e.g. for sbt append % Test to limit the dependency to the test scope).KmsDriverTest
The main part of the test suite is theKmsDriverTest that tests the functionality of a driver against the KmsDriver API.
In the simplest form the specific driver test class extends the KmsDriverTest and allows the generation of new keys as part of the test:
KmsDriverFactoryTest
The test suite for the KMS Driver factory is structured similarly to the above:KmsDriverFactory can write the driver-specific configuration with a confidential flag being true, which means any sensitive information in the configuration such as credentials should be omitted from the written configuration. A specific test case should be added if your driver-specific configuration contains any confidential information, asserting that the sensitive information is omitted.
Run Canton with a KMS Driver
Configure Canton to run with a KMS Driver, for example, for a participant participant1:java -cp driver.jar:canton.jar com.digitalasset.canton.CantonEnterpriseApp -c canton.conf # further canton arguments
Where canton.jar depends on the Canton version, e.g., lib/canton-enterprise-3.3.3.jar. The canton.conf is a configuration file that needs to configure at least one of the nodes to use the driver KMS as outlined above. Run a ping for example with participant1.health.ping(participant1) to validate that the participant can use the configured KMS and driver.