Skip to content

Crypto library

Nadine Quin edited this page May 18, 2022 · 17 revisions

Tenant

Corda 5 is a multi-tenanted system where the tenant is defined in terms of a member or vnode. However, the Crypto Library manages keys on behalf of members as well as the cluster itself. In the case of the members (notaries, network managers, etc.) the tenant is their vnode id but for the cluster it is the 'cluster'.

Assumptions and considerations

  • An HSM has limited capacity. The number of keys that it can store is measured in few hundreds or in low thousands.
  • Most HSM interface libraries/drivers assume that there is either only one HSM instance per machine, like Utimaco, or per running process (like passing the location of the config files and drivers as environment variables, like AWS Cloud HSM), that require a dedicated crypto workers for such instances and involve some kind of message routing.
  • Not all HSMs have the notion of partition. For those that have it, we treat partitions as separate instances of that HSM.
  • Default Crypto Service (called Soft HSM) which is always present, has weaker security and uses a database to store the keys which are encrypted at rest.
  • There could be several HSMs provided by a cluster host.
  • As an instance of HSM can be shared between different tenants, we cannot accept key aliases directly from members due to high probability of a name clash.
  • Support for the ability to store keys of different roles in different HSMs. For example, a Ledger key may be stored in an Utimaco HSM and the Session Initiating Key in Azure Key Vault.
  • A tenant registers itself with each HSM category explicitly during the onboarding process.

Overview

The library has two groups of operations:

  • Sensitive — implemented in a separate crypto worker, which does not run end user customisable code and is accessible only through a message bus. Note that the Crypto Library enables clients to invoke those operations so there is no need to work with the message bus directly:
    • Key generation
    • Signing
    • HSM assignments
    • Key lookups
  • Generic crypto operations — reside directly in the consuming processes:
    • Calculating digests
    • Signature verification
    • Providing metadata about public keys and digests, like algorithm

Crypto - Simple Overview

Categories

To enable different HSMs depending on key roles for the same tenant, there is a notion of the HSM category (or just a category) which roughly corresponds to key roles. Some of the categories are:

  • ACCOUNTS
  • CI
  • LEDGER
  • TLS
  • JWT_KEY
  • NOTARY

Layering View

Crypto - Layering Overview

The low-level crypto service implementations (the Secure Modules layer) is tenant agnostic (like most of HSM) so access to the keys is partitioned using the tenant id by the higher API level.

The main idea behind layering is that the Crypto Library provides a basis for building specialised services on top of it, which in some cases are responsible only for the correct formatting of the requests to the Crypto Library (but not limited to that as in some case those services would need to have their own persistence, for example) - Accounts, Ledger Transactions, etc.

Summary of Key Generation and Signing

The CorDapp and RPC API don't have direct access to the lower implementations as they are accessible only in the Crypto Worker.

Crypto - Key Generation   Signing

Crypto Library Public API

There are two modules in the public API:

  • corda-crypto — contains basic crypto primitives available for CorDapps and services targeting CorDapps directly as well as definitions for the DigestService and SignatureVerificationService.
  • corda-cipher-suite — contains low level definitions that are required in order to implement integrations with various HSMs or extend capabilities of the Crypto Library. These definitions must not be made available for CorDapp level APIs

Cipher Suite

The API consists of low level artefacts that provide the asymmetric cryptography functionality for Corda. The main interfaces here are CipherSchemeMetadata which provides all required metadata about the suite and CryptoService which is the interface that has to be implemented for each supported HSM and provides the means to securely generate key pair and sign.

interface CryptoService {
    fun requiresWrappingKey(): Boolean

    fun supportedSchemes(): Array<SignatureScheme>

    fun createWrappingKey(
        masterKeyAlias: String,
        failIfExists: Boolean,
        context: Map<String, String>
    )

    fun generateKeyPair(
        spec: KeyGenerationSpec,
        context: Map<String, String>
    ): GeneratedKey

    fun sign(
        spec: SigningSpec,
        data: ByteArray,
        context: Map<String, String>
    ): ByteArray
}

The contexts for the key generation or signing contain at least one item (with the key 'tenantId') specifying on behalf of which tenant the operation is performed. The key generation context also has the 'category' item.

The key generation parameters are more like a hit and it is up to the HSM to decide how exactly the key will be generated - as internally to the HSM or exported as a wrapped key. Note: If the key has to be wrapped and the HSM neds to know the wrapping key, the corresponding HSM configuration should provide sufficient information about which master key alias should be used.

If the HSM will have to store the private key inside the HSM, it cannot use the provided parameter 'alias' directly as different tenants may choose the same value for that parameter. Instead the HSM must either randomly generate it or, ideally, use the tenant id, alias, and secret in order to calculate the actual alias in the deterministic way (using something like HMAC). The calculated alias must be returned as hsmAlias so it can be used in the signing operations later.

class KeyGenerationSpec(
    val signatureScheme: SignatureScheme,
    val alias: String?,
    val masterKeyAlias: String?,
    val secret: ByteArray?
)

interface GeneratedKey {
    val publicKey: PublicKey
}

class GeneratedPublicKey(
    override val publicKey: PublicKey,
    val hsmAlias: String
) : GeneratedKey

class GeneratedWrappedKey(
    override val publicKey: PublicKey,
    val keyMaterial: ByteArray,
    val encodingVersion: Int
) : GeneratedKey

Crypto Library Internal API

Module Description
:libs:crypto:crypto-core The module contains the most basic common crypto artefacts, like constants defining standardised categories, cluster level tenanats, and AES support for secrets.
:libs:crypto:crypto-flow The module contains artefacts which are required to build crypto operation messages and process the results for the flows - CryptoFlowOpsTransformer
:components:crypto:crypto-client The module contains clients nesseraly to execute configure HSMs, assign tenants, generate keys and sign - CryptoOpsClient, HSMConfigurationClient, HSMRegistrationClient (note that CryptoOpsProxyClient should not be used as that a rely client)

Key and configuration persistence

Persistence models

Crypto - Key Generation   Signing

Configuration persistence

... [TBD]

Key Persistence

The main entity is the SigningKeyEntity, which stores information about a know key pair. All known key pairs must be recorded in the corresponding table. The record (entity) stores the public key of the pair, metadata about the pair, and the encrypted private key if the key was wrapped and exported from HSM (note that the private key is still not available outside of such HSM as it's encrypted before exporting using the wrapping key). If the key is not in that table, it means the key is not known as there is no alternative way of configuring or discovering key pairs.

Finding assigned HSM for a crypto operation

HSMs are used for key generation and signing operations only:

  • Key pair generation - the lookup for an associated HSM is done by the tenant id and the category (note that the deprecatedAt must be 0 for the association which links tenant/category and the HSM)

  • Signing - as the generated key pairs are locked to the HSM which was used to generate it so the operation uses the HSM's configuration id (transitively through the two association entities) recorded in the known public key metadata (note that the deprecatedAt value does not play any role)

HSM and Tenants

Before any tenant, either cluster level or vnode, can use any secure crypto operation requiring an HSM (key generation and signing), it must be associated with an HSM for each category. The association is done in a semi-automatic way; the tenant can pick whenever they want to use the SOFT HSM or any real HSM provided by the cluster operator. If the tenant chooses to use real HSM, it does not control which HSM will be assigned to it. The tenant can only provide some criteria such as the category, that the HSM must store only aliased keys (hosted by HSM without thr ability to export them), or that it supports specific key schemes.