Skip to content

Allow GCP KMS keys to use a custom cryptoKey version#515

Open
same-id wants to merge 2 commits into
digital-asset:mainfrom
same-id:user-sameid/gcp-kms-custom-key-version
Open

Allow GCP KMS keys to use a custom cryptoKey version#515
same-id wants to merge 2 commits into
digital-asset:mainfrom
same-id:user-sameid/gcp-kms-custom-key-version

Conversation

@same-id

@same-id same-id commented May 3, 2026

Copy link
Copy Markdown

Summary

  • Until now, every GCP KMS operation hardcoded cryptoKey version "1". That matches Canton-generated keys (Canton always creates a new cryptoKey rather than adding a version to an existing one), but it prevents using cryptoKeys whose key material was imported into GCP KMS — each import creates a new cryptoKey version, and the version holding the imported material is typically not "1". This is relevant even for asymmetric keys.

  • This PR adds a new optional key-version-overrides field to KmsConfig.Gcp, mapping a cryptoKey id to the cryptoKey version Canton should use for that key. Keys not listed in the map continue to default to version "1", so existing configurations are unchanged.

    canton.participants.participant1.crypto.kms {
      type        = gcp
      location-id = us-east1
      project-id  = my-project
      key-ring-id = my-ring
      key-version-overrides = {
        "my-imported-signing-key"    = "3"
        "my-imported-encryption-key" = "2"
      }
    }
  • All CryptoKeyVersionName.of(...) call sites now go through a single cryptoKeyVersionName helper that reads the version via config.keyVersionOverrides.getOrElse(keyId.unwrap, "1").

  • Symmetric encrypt/decrypt paths are unchanged: GCP picks the primary version on encrypt and recovers it from the ciphertext on decrypt, so no per-key version is needed there.

  • Documentation: adds the new field to kms_gcp_config.rst, links to it from the external-key-storage how-to, and adds an UNRELEASED.md minor-improvement entry.

Until now, every GCP KMS operation hardcoded cryptoKey version "1". This
matches Canton-generated keys (Canton always creates a new cryptoKey rather
than adding a version to an existing one), but it prevents using cryptoKeys
whose key material was imported into GCP KMS, since each import creates a
new cryptoKey version and the version that holds the imported material is
typically not "1" -- and can be relevant even for asymmetric keys.

Add a new optional `key-version-overrides` field to `KmsConfig.Gcp`, mapping
a cryptoKey id to the cryptoKey version Canton should use for that key. Keys
not listed continue to default to "1", so existing configurations are
unchanged. The lookup happens at every call site that previously used the
hardcoded version, via a single `cryptoKeyVersionName` helper.

Symmetric encrypt/decrypt paths are unchanged: GCP picks the primary version
on encrypt and recovers it from the ciphertext on decrypt, so no per-key
version is needed there.

Includes documentation for the new field in the GCP KMS config reference and
the external-key-storage how-to, plus an UNRELEASED.md entry.
@github-actions

github-actions Bot commented May 3, 2026

Copy link
Copy Markdown

✅ All required contributors have signed the CLA for this PR. Thank you!
Posted by the CLA Assistant Lite bot.

@same-id

same-id commented May 3, 2026

Copy link
Copy Markdown
Author

I have hereby read the Digital Asset CLA and agree to its terms

@same-id

same-id commented May 3, 2026

Copy link
Copy Markdown
Author

recheck

@JoaoSa-da

JoaoSa-da commented May 5, 2026

Copy link
Copy Markdown

Hello @same-id , sorry for the delay in replying. I was wondering whether we could instead use listCryptoKeyVersions to retrieve all versions of our key and select the latest (enabled) one: https://docs.cloud.google.com/kms/docs/reference/rest/v1/projects.locations.keyRings.cryptoKeys.cryptoKeyVersions/list?rep_location=global

Do you think this would work? Do you see any use case where choosing the latest version might be incorrect? This way, we could avoid the extra configuration, which can be hard to keep track of and set correctly. What do you think?

@same-id

same-id commented May 5, 2026

Copy link
Copy Markdown
Author

@JoaoSa-da

I initially wanted to do that - the API calls themselves are cheap and only done at startup I guess, to resolve the key itself.

But even so, I thought that maybe a user might want to do key migration and during the key migration the user will want access to both key versions at the same time.

Not 100% sure about that use case.

Generally in hindsight I am guessing the correct way of passing the KMS key inside would be like in terraform packages where you give the entry resource as a url:

gcpkms://......./keyRing/....../key/....../keyVersions/

But seems like Canton already is not working this way and constructs the url itself at each point from the url parts.

I would assume a latest by default makes sense anyway, but since I wanted to get to the point where I can construct the full url for whatever reason, I would give a full override power.

I don't mind for either way - any thoughts?

@JoaoSa-da

Copy link
Copy Markdown

I wonder if we could do the following:

On the register keys command, we could either:

  1. Accept the usual KMS ID that works for both GCP and AWS, and for GCP retrieve the latest key version; or
  2. Accept the full Terraform identifier so that we can select a specific version.

This would require a regex check on the GCP KMS ID to determine whether it is a full Terraform identifier or a regular KMS ID. That way, we could keep the static config and the registerKmsKey API unchanged (apart from updating the API description).

@same-id

same-id commented May 5, 2026

Copy link
Copy Markdown
Author

I wonder if we could do the following:

On the register keys command, we could either:

  1. Accept the usual KMS ID that works for both GCP and AWS, and for GCP retrieve the latest key version; or
  2. Accept the full Terraform identifier so that we can select a specific version.

This would require a regex check on the GCP KMS ID to determine whether it is a full Terraform identifier or a regular KMS ID. That way, we could keep the static config and the registerKmsKey API unchanged (apart from updating the API description).

Would option 2 still require setting all that:

type = gcp
location-id = us-east1
project-id = my-project
key-ring-id = my-ring

Which are a part of the terraform key-id anyway?

We can also just support key-id = my-key/keyVersions/1 as something between option 2 to what exists today, but it just doesn't bode well with the current approach

For option 1 just need to make sure we have the KMS client threaded to all the functions that need it

Whatever you prefer.

@JoaoSa-da

Copy link
Copy Markdown

In my opinion, since we don’t yet have a use case for selecting a key version other than the latest one, I would only implement option 1 for now. We can always revisit this later if we do end up needing to select a version that is not the latest one.

@same-id

same-id commented May 6, 2026

Copy link
Copy Markdown
Author

I opened a different PR - so it would be easier to compare the change size.

#521

Notice that now we need some new permissions and we change the default behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants