From 1f29bc06a482d8856a34f5734234e0dd498d60f9 Mon Sep 17 00:00:00 2001 From: Robert Young Date: Thu, 28 Aug 2025 11:12:13 +1200 Subject: [PATCH 01/17] Add azure kms proposal Signed-off-by: Robert Young --- proposals/007-azure-kms.md | 114 +++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 proposals/007-azure-kms.md diff --git a/proposals/007-azure-kms.md b/proposals/007-azure-kms.md new file mode 100644 index 0000000..8321e9e --- /dev/null +++ b/proposals/007-azure-kms.md @@ -0,0 +1,114 @@ +# 007 - Azure KMS Implementation + +One of the flagship Kroxylicious Filters is Record Encryption, enabling users to implement encryption-at-rest for their Apache Kafka cluster. To do this securely, we rely on third-party Key Management Systems (KMS) to protect a set of Key Encryption Keys which are used to encrypt the Data Encryption Keys we write into the records. We currently have AWS, Hashicorp Vault and Fortanix DSM implementations. + +We should implement an Azure Key Vault integration, so that users can use Azure as their KMS for record encryption. + + +* [007 - Azure KMS Implementation](#007---azure-kms-implementation) + * [Background](#background) + * [Key Vault Types](#key-vault-types) + * [Key Types & Wrapping Algorithms](#key-types--wrapping-algorithms) + * [Authentication With Key Vault](#authentication-with-key-vault) + * [National Clouds](#national-clouds) + * [TLS](#tls) + * [DEK Generation](#dek-generation) + * [Proposed Initial Implementation](#proposed-initial-implementation) + * [Configuration](#configuration) + + +## Background + +> Azure Key Vault is a cloud service for securely storing and accessing secrets. A secret is anything that you want to tightly control access to, such as API keys, passwords, certificates, or cryptographic keys. + +### Key Vault Types + +There are several classes of Key Vault + +- `Key Vault` - software encryption. For our purposes only supports one key type recommended for wrapping, that is RSA. This is a cheap option, charging per key rotation and operation. +- `Key Vault (premium SKU)` - software/hardware encryption. As above only supports RSA, but can use hardware (HSM-RSA) for it. More expensive. +- `Managed HSM` - hardware encryption. This supports RSA, but also quantum-resistant 256bit symmetric AES keys/algorithms. This is an expensive option, charging per hour and per key. Also has several flavours, but the key capabilities are the same. + +See also: +- [About Keys](https://learn.microsoft.com/en-us/azure/key-vault/keys/about-keys) +- [How to choose the right Azure key management solution](https://learn.microsoft.com/en-us/azure/security/fundamentals/key-management-choose) + +### Key Types & Wrapping Algorithms + +Microsoft recommends has two recommended Key Types for a Key-wrapping workload. + +1. 2048, 3072, 4096 bit RSA (or HSM-RSA) Keys using `RSA-OAEP-256`. This is a asymmetric key, differing from our existing implementations that rely on the KMS to use an AES-256-GCM key. The difference is that the wrapping operation is done with the public key, and can be done inside or outside of the KMS, but it requires the private key to decrypt. These RSA algorithms are **not** advertised as quantum-resistant. This is the only recommended wrapping type/algorithm available on a cheap software-based Key Vault instance. +2. 256-bit AES keys using `AES-GCM`, `AES-KW` or `AES-CBC`. These are advertised as quantum-resistant. 256 AES-GCM is what we use in the other KMS implementations, so there is some consistency there. It is only available in the expensive Managed HSM mode. + +See [docs](https://learn.microsoft.com/en-us/azure/key-vault/keys/about-keys-details) + +### Authentication With Key Vault + +> Authentication with Key Vault works in conjunction with [Microsoft Entra ID](https://learn.microsoft.com/en-us/azure/active-directory/fundamentals/active-directory-whatis), which is responsible for authenticating the identity of any given security principal. + +The Proxy must obtain a token that it can use to communicate with Key Vault. The options are: +1. [Microsoft identity platform and the OAuth 2.0 client credentials flow](https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-client-creds-grant-flow). This is a mechanism where the application will have a Service Principal created for it in Entra, with permissions assigned to allow get/wrap/unwrap keys in a Key Vault. Then our application will make an HTTP request to Entra passing credentials and a scope, and receive a Bearer token it can attach to Key Vault requests. This flow will work for applications in any environment, they do not need to run on Azure. +2. Via [Managed Identity](https://learn.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview). This is a convenient approach for applications running on Azure to obtain a token from a local non-routable IP. But as said, it's Azure only, which doesn't help if you only have a Key Vault in Azure but your workload elsewhere. + +See [Authentication in Azure Key Vault](https://learn.microsoft.com/en-us/azure/key-vault/general/authentication) + +### National Clouds + +> National clouds are physically isolated instances of Azure. These regions of Azure are designed to make sure that data residency, sovereignty, and compliance requirements are honored within geographical boundaries. + +Public addresses like the Key Vault base url will be different per national cloud. For example in China an address might look like `https://vault-name.vault.azure.cn`. This also impacts the `scope` we request via the oauth client credentials flow, we would likely want to request `https://vault.azure.cn/.default` scope. + +Entra oauth endpoint will vary by National Cloud + +``` +Global https://login.microsoftonline.com +Microsoft Entra ID for US Government https://login.microsoftonline.us +Microsoft Entra China operated by 21Vianet https://login.partner.microsoftonline.cn +... +``` + +The implication for us is that the oauth endpoint and scope should be configurable. The vault base uri is a natural part of the configuration anyway. We could potentially infer the `scope` from the configured vault base uri. + +[National clouds](https://learn.microsoft.com/en-us/entra/identity-platform/authentication-national-cloud) + +### TLS + +With two remote systems involved we want independently customizable TLS for: + +1. the authentication client +2. the key vault client + +So that we can supply custom trust or use insecure testing modes (mock servers using self-signed certs for example). + +### DEK Generation + +Another difference is that basic Key Vault cannot generate the DEK bytes for us. Managed HSM does have an operation to generate random bytes for us https://learn.microsoft.com/en-us/rest/api/keyvault/?view=rest-keyvault-keys-7.4#key-operations-managed-hsm-only + +## Proposed Initial Implementation + +1. Support all flavours of Key Vault. The APIs will be the same, just with a different vault base URI. +2. Support RSA and HSM-RSA key types, wrapping using `RSA-OAEP-256` but emit a warning that it is not quantum-resistant. +3. Support HSM-AES key type and AES-GCM wrapping +4. Support only client credentials oauth flow with Entra using clientId + clientSecret. This supports workloads running anywhere. We could add support for other client credentials (certificates, federated certs) and Managed Identities later. Share a single auth token per Filter Definition until near expiry. +5. Support TLS customization of the authentication client and key vault client. +6. DEK bytes will be generated proxy-side with a SecureRandom. +7. The Azure SDK pulls in netty/jackson/project-reactor, lets try implementing the APIs ourselves as we have for AWS + +### Configuration + +``` +kms: AzureKeyVault +kmsConfig: + keyVaultBaseUri: https://kv-kfesiehfoieaf.vault.azure.net + tls: + ... client trust for key vault + entraIdentity: + oauthEndpointUrl: https://login.microsoftonline.com // optional defaults to this value + clientId: + passwordFile: /path/to/id + clientSecret: + passwordFile: /path/to/id + scope: https://vault.azure.net/.default // optional, could infer from vaultBaseUri + tls: + ... client trust configuration for oauth +``` From 14f6c6d02f7a301ce7d8cffda80c13893797b045 Mon Sep 17 00:00:00 2001 From: Robert Young Date: Thu, 28 Aug 2025 11:27:56 +1200 Subject: [PATCH 02/17] Add EDEK details Signed-off-by: Robert Young --- proposals/007-azure-kms.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/proposals/007-azure-kms.md b/proposals/007-azure-kms.md index 8321e9e..0d49fa2 100644 --- a/proposals/007-azure-kms.md +++ b/proposals/007-azure-kms.md @@ -13,6 +13,7 @@ We should implement an Azure Key Vault integration, so that users can use Azure * [National Clouds](#national-clouds) * [TLS](#tls) * [DEK Generation](#dek-generation) + * [Key Identifiers](#key-identifiers) * [Proposed Initial Implementation](#proposed-initial-implementation) * [Configuration](#configuration) @@ -84,6 +85,30 @@ So that we can supply custom trust or use insecure testing modes (mock servers u Another difference is that basic Key Vault cannot generate the DEK bytes for us. Managed HSM does have an operation to generate random bytes for us https://learn.microsoft.com/en-us/rest/api/keyvault/?view=rest-keyvault-keys-7.4#key-operations-managed-hsm-only +### Key Identifiers + +When we wrap/unwrap we have to identify a key by its name and version. You can obtain the latest version by https://learn.microsoft.com/en-us/rest/api/keyvault/keys/get-key/get-key?view=rest-keyvault-keys-7.4&tabs=HTTP. + +The API usually returns it as a `kid` https://learn.microsoft.com/en-us/azure/key-vault/general/about-keys-secrets-certificates#object-identifiers. Like + +> For Vaults: https://{vault-name}.vault.azure.net/{object-type}/{object-name}/{object-version} + +eg. `https://my-vault.vault.azure.net/keys/my-key/78deebed173b48e48f55abf87ed4cf71` + +The Azure SDK then takes advantage of the fact this happens to be the base URL for various operations like wrap/unwrap. + +ed. for [wrap key](https://learn.microsoft.com/en-us/rest/api/keyvault/keys/wrap-key/wrap-key?view=rest-keyvault-keys-7.4&tabs=HTTP) the url is `POST {vaultBaseUrl}/keys/{key-name}/{key-version}/wrapkey?api-version=7.4 +` + +To minimise EDEK bytes there are a couple of opportunities when encoding the EDEK: +1. we can exclude the vaultBaseUrl and have the limitation that we work only with a single key vault +2. we can encode just the keyName and keyVersion, and use that to decrypt +3. all documented examples of the keyVersion (and from my experimentation too) are 128bits encoded as a hexadecimal string. + +So for the EDEK I think we could optimistically try to encode it as `versionByte,keyNameLength,keyName,keyVersionLength,128-bit-decoded,edek` +and fall back to using the string like `versionByte,keyNameLength,keyName,keyVersionLength,keyVersionString,edek`. So +we would have either a 16-byte version implying it's the bytes, or a 32 character string. + ## Proposed Initial Implementation 1. Support all flavours of Key Vault. The APIs will be the same, just with a different vault base URI. @@ -93,6 +118,7 @@ Another difference is that basic Key Vault cannot generate the DEK bytes for us. 5. Support TLS customization of the authentication client and key vault client. 6. DEK bytes will be generated proxy-side with a SecureRandom. 7. The Azure SDK pulls in netty/jackson/project-reactor, lets try implementing the APIs ourselves as we have for AWS +8. Edek stores the keyName, keyVersion, edek. We attempt to minimise keyVersion size by optimistically decoding it from hex string, else store the string. ### Configuration From 9a6e3110bb0beb7ef118bfa8e84f4e6f35a0a2ac Mon Sep 17 00:00:00 2001 From: Robert Young Date: Thu, 28 Aug 2025 11:34:50 +1200 Subject: [PATCH 03/17] Add tenantId details Signed-off-by: Robert Young --- proposals/007-azure-kms.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/proposals/007-azure-kms.md b/proposals/007-azure-kms.md index 0d49fa2..c6bfea7 100644 --- a/proposals/007-azure-kms.md +++ b/proposals/007-azure-kms.md @@ -53,6 +53,13 @@ The Proxy must obtain a token that it can use to communicate with Key Vault. The See [Authentication in Azure Key Vault](https://learn.microsoft.com/en-us/azure/key-vault/general/authentication) +One component that goes into the oauth request is the tenant id. This is a public piece of information that can be obtained +from Key Vault by making an HTTP request to it with no `Authorization` header. + +The Azure SDK does this to avoid the user having to supply the tenant id. However this is a more fiddly workflow, I +think we should make the user supply the tenant id by configuration so we do not have to obtain it by a request +to Key Vault. + ### National Clouds > National clouds are physically isolated instances of Azure. These regions of Azure are designed to make sure that data residency, sovereignty, and compliance requirements are honored within geographical boundaries. @@ -119,7 +126,7 @@ we would have either a 16-byte version implying it's the bytes, or a 32 characte 6. DEK bytes will be generated proxy-side with a SecureRandom. 7. The Azure SDK pulls in netty/jackson/project-reactor, lets try implementing the APIs ourselves as we have for AWS 8. Edek stores the keyName, keyVersion, edek. We attempt to minimise keyVersion size by optimistically decoding it from hex string, else store the string. - +9. User will supply tenantId for authentication, rather than implementing a more complicated workflow to obtain it using an HTTP request to KeyVault ### Configuration ``` @@ -134,6 +141,7 @@ kmsConfig: passwordFile: /path/to/id clientSecret: passwordFile: /path/to/id + tenantId: "abds-1232dsaa" scope: https://vault.azure.net/.default // optional, could infer from vaultBaseUri tls: ... client trust configuration for oauth From cc11a5abd010a26f148196bfb8ea36449c1e75ce Mon Sep 17 00:00:00 2001 From: Robert Young Date: Fri, 19 Sep 2025 11:24:37 +1200 Subject: [PATCH 04/17] Apply feedback Signed-off-by: Robert Young --- proposals/007-azure-kms.md | 128 +++++++++++++++++++++++++++++++++---- 1 file changed, 117 insertions(+), 11 deletions(-) diff --git a/proposals/007-azure-kms.md b/proposals/007-azure-kms.md index c6bfea7..a95242b 100644 --- a/proposals/007-azure-kms.md +++ b/proposals/007-azure-kms.md @@ -1,11 +1,8 @@ # 007 - Azure KMS Implementation -One of the flagship Kroxylicious Filters is Record Encryption, enabling users to implement encryption-at-rest for their Apache Kafka cluster. To do this securely, we rely on third-party Key Management Systems (KMS) to protect a set of Key Encryption Keys which are used to encrypt the Data Encryption Keys we write into the records. We currently have AWS, Hashicorp Vault and Fortanix DSM implementations. - -We should implement an Azure Key Vault integration, so that users can use Azure as their KMS for record encryption. - * [007 - Azure KMS Implementation](#007---azure-kms-implementation) + * [Motivation](#motivation) * [Background](#background) * [Key Vault Types](#key-vault-types) * [Key Types & Wrapping Algorithms](#key-types--wrapping-algorithms) @@ -14,14 +11,29 @@ We should implement an Azure Key Vault integration, so that users can use Azure * [TLS](#tls) * [DEK Generation](#dek-generation) * [Key Identifiers](#key-identifiers) - * [Proposed Initial Implementation](#proposed-initial-implementation) + * [Proposal](#proposal) * [Configuration](#configuration) + * [Authorization](#authorization) + * [Edek Serialization Scheme](#edek-serialization-scheme) + * [Common Fields](#common-fields) + * [Format 0: Hexadecimal Key Version](#format-0-hexadecimal-key-version) + * [Format 1: String Key Version](#format-1-string-key-version) + * [Compatibility](#compatibility) + * [Affected/not affected projects](#affectednot-affected-projects) + * [Rejected Alternatives](#rejected-alternatives) +## Motivation +One of the flagship Kroxylicious Filters is Record Encryption, enabling users to implement encryption-at-rest for their Apache Kafka cluster. To do this securely, we rely on third-party Key Management Systems (KMS) to protect a set of Key Encryption Keys which are used to encrypt the Data Encryption Keys we write into the records. We currently have AWS, Hashicorp Vault and Fortanix DSM implementations. + +We should implement an Azure Key Vault integration, so that users can use Azure as their KMS for record encryption. + ## Background > Azure Key Vault is a cloud service for securely storing and accessing secrets. A secret is anything that you want to tightly control access to, such as API keys, passwords, certificates, or cryptographic keys. +Users can also install it on-premise by negotiation. + ### Key Vault Types There are several classes of Key Vault @@ -112,21 +124,23 @@ To minimise EDEK bytes there are a couple of opportunities when encoding the EDE 2. we can encode just the keyName and keyVersion, and use that to decrypt 3. all documented examples of the keyVersion (and from my experimentation too) are 128bits encoded as a hexadecimal string. -So for the EDEK I think we could optimistically try to encode it as `versionByte,keyNameLength,keyName,keyVersionLength,128-bit-decoded,edek` -and fall back to using the string like `versionByte,keyNameLength,keyName,keyVersionLength,keyVersionString,edek`. So -we would have either a 16-byte version implying it's the bytes, or a 32 character string. +So for the EDEK I think we could optimistically try to encode it as `versionByte,keyNameLength,keyName,128-bit-decoded,edek` +and fall back to using the string like `versionByte,keyNameLength,keyName,keyVersionLength,keyVersionString,edek` using +different version bytes to discriminate between the formats. -## Proposed Initial Implementation +## Proposal 1. Support all flavours of Key Vault. The APIs will be the same, just with a different vault base URI. 2. Support RSA and HSM-RSA key types, wrapping using `RSA-OAEP-256` but emit a warning that it is not quantum-resistant. 3. Support HSM-AES key type and AES-GCM wrapping 4. Support only client credentials oauth flow with Entra using clientId + clientSecret. This supports workloads running anywhere. We could add support for other client credentials (certificates, federated certs) and Managed Identities later. Share a single auth token per Filter Definition until near expiry. 5. Support TLS customization of the authentication client and key vault client. -6. DEK bytes will be generated proxy-side with a SecureRandom. +6. If the Key Vault is Managed HSM, then we will use the API to generate random bytes. Else, DEK bytes will be generated proxy-side with a SecureRandom. 7. The Azure SDK pulls in netty/jackson/project-reactor, lets try implementing the APIs ourselves as we have for AWS -8. Edek stores the keyName, keyVersion, edek. We attempt to minimise keyVersion size by optimistically decoding it from hex string, else store the string. +8. [Edek](#edek-serialization-scheme) stores the keyName, keyVersion, edek. We attempt to minimise keyVersion size by optimistically decoding it from hex string, else store the string. 9. User will supply tenantId for authentication, rather than implementing a more complicated workflow to obtain it using an HTTP request to KeyVault +10. Endpoints for Entra and Key Vault will be configurable to support national clouds. + ### Configuration ``` @@ -146,3 +160,95 @@ kmsConfig: tls: ... client trust configuration for oauth ``` + +### Authorization + +We want to adhere to the principal of Least Privilege. + +The minimum authorization a Principal needs for the Key Vault using this implementation will be: + +* allow `get` +* allow `unwrapKey` +* allow `wrapKey` + +If using Managed HSM we will also need ` Microsoft.KeyVault/managedhsms/rng/action` action to be allowed so that we +can use it to generate bytes. + +The Key itself in the Key Vault also has a set of supported operations. We require at minimum `unwrapKey` and `wrapKey`. + +### Edek Serialization Scheme + +We want to minimise the EDEK size as we serialize it into every encrypted record. All examples and experimentation show +that the Key Version is going to be represented as a 128 bit Hex string. However, it is not documented as being a hex +string. We therefore attempt to take advantage of this to +reduce the serialized size, but implement a fallback in case the Key Version evolves within their documented limits. + +#### Common Fields + +- **Key Name**: The name of the key in Azure Key Vault, encoded in UTF-8. Its documented format is `1-127 character string, containing only 0-9, a-z, A-Z, and -.` +- **Key Version**: The version identifier for the key in Azure Key Vault. Its documented format is `32 character string` +- **EDEK**: The Encrypted Data Encryption Key, a byte array. + +[See also](https://learn.microsoft.com/en-us/azure/key-vault/general/about-keys-secrets-certificates#object-identifiers) + +#### Format 0: Hexadecimal Key Version + +This format is used when the **Key Version** is represented as a lowercase hexadecimal string (e.g., `78deebed173b48e48f55abf87ed4cf71`). The version is serialized directly as a 128-bit (16-byte) value. + +The byte layout is as follows: + +| Offset | Length (bytes) | Field Name | Description | +|:-------|:---------------|:----------------------|:-------------------------------------------------| +| 0 | 1 | **Format Version** | A constant byte with the value `0x00`. | +| 1 | 1 | **Key Name Length** | The length of the `Key Name` field in bytes (N). | +| 2 | N | **Key Name** | The UTF-8 encoded key name. | +| 2+N | 16 | **Key Version (Hex)** | The 128-bit key version, serialized as 16 bytes. | +| 18+N | M | **EDEK** | The EDEK byte array. | + +Note that the key name is up to 127 characters that are a subset of ascii so its length can be described with 1 byte. +#### Format 1: String Key Version + +This format is used for any **Key Version** that is not a lowercase hexadecimal string (e.g., a custom name or GUID). The version is serialized as a length-prefixed UTF-8 string. + +The byte layout is as follows: + +| Offset | Length (bytes) | Field Name | Description | +|:-------|:---------------|:-------------------------|:-------------------------------------------------------------| +| 0 | 1 | **Format Version** | A constant byte with the value `0x01`. | +| 1 | 1 | **Key Name Length** | The length of the `Key Name` field in bytes (N). | +| 2 | N | **Key Name** | The UTF-8 encoded key name. | +| 2+N | 1 | **Key Version Length** | The length of the `Key Version (String)` field in bytes (P). | +| 3+N | P | **Key Version (String)** | The UTF-8 encoded key version string. | +| 3+N+P | M | **EDEK** | The EDEK byte array. | + +Note that the key name is up to 127 characters that are a subset of ascii so its length can be described with 1 byte. +## Compatibility + +New feature without backwards compatibility requirements. Serialized EDEK contains version identifiers for future compatibility. + +## Affected/not affected projects + +The `kroxylicous` repo. + +## Rejected Alternatives + +1. **Depending on the Azure SDK**. To align with the other KMS implementations and keep our dependencies lean we will use implement + Entra authentication and Key Vault interactions ourselves. Specifically the dependency on netty/jackson is a risk + since the Kroxylicious framework also depends on these and we have no solution for per-plugin classloader isolation yet. +2. **Using Azure Managed Identity to obtain the auth credentials**. This can be implemented later if users request it. This is an + Azure-specific mechanism for applications to conveniently obtain credentials from the environment they are + running in. We've implemented the equivalent functionality for AWS. Initially we only want to implement the OAuth 2 + client credentials flow as it can be used from arbitrary environments and technology, not just Azure. +3. **Storing the entire key id as it emerges from the Azure API**. The Key Vault APIs are documented in terms of + the components. To reduce the size of the serialized data, we assume the usage of single Azure Key Vault containing + all serialized Key Names and Versions. +4. **automatic authentication discovery**. The Azure SDK does some smart work, you don't have to configure it with a tenant id + or entra endpoint or scope. It will send an initial request to the Key Vault without any Authorization header, and + use the response to drive the authentication request. The response contains a header like: + ``` + WWW-Authenticate: Bearer authorization="https://login.microsoftonline.com/72f988bf-86f1-41af-91ab-2d7cd011db47", resource="https://vault.azure.net" + ``` + Which saves configuring the specific Entra endpoint, tenantId and the resource implies the scope. However, all that + convenience adds more Security risks. We must be very careful not to POST credentials at a malicious authorization + endpoint or request a different scope than what we require. So for now, we make the entra endpoint, tenantId and scope fixed pieces of configuration so we absolutely know + where we are sending the client's credentials. Allowing the Key Vault to dictate where to authenticate is a risk. \ No newline at end of file From 996db61e67aab1859bbf0778873c5b6b8714fe61 Mon Sep 17 00:00:00 2001 From: Robert Young Date: Fri, 19 Sep 2025 11:37:40 +1200 Subject: [PATCH 05/17] Add key type support matrix Signed-off-by: Robert Young --- proposals/007-azure-kms.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/proposals/007-azure-kms.md b/proposals/007-azure-kms.md index a95242b..28dcea5 100644 --- a/proposals/007-azure-kms.md +++ b/proposals/007-azure-kms.md @@ -141,6 +141,20 @@ different version bytes to discriminate between the formats. 9. User will supply tenantId for authentication, rather than implementing a more complicated workflow to obtain it using an HTTP request to KeyVault 10. Endpoints for Entra and Key Vault will be configurable to support national clouds. +### Key Types Support Matrix + +For completeness, this covers which key types are supported by this KMS implementation and which Key Vault products they +are currently compatible with. + +| Azure key type | Supported | Wrapping Algorithm | Key Vault Standard | Key Vault Premium SKU | Managed HSM | +|----------------|-----------|--------------------|--------------------|-----------------------|-------------| +| RSA | yes | RSA-OAEP-256 | yes | yes | yes | +| RSA-HSM | yes | RSA-OAEP-256 | no | yes | yes | +| oct | yes | AES-GCM | no | no | yes | +| oct-HSM | yes | AES-GCM | no | no | yes | +| EC | no | - | - | - | - | +| EC-HSM | no | - | - | - | - | + ### Configuration ``` From 42a85fdd749c128d18356a07ed7a0aa03fd7bedf Mon Sep 17 00:00:00 2001 From: Robert Young Date: Fri, 19 Sep 2025 11:38:18 +1200 Subject: [PATCH 06/17] Add to TOC Signed-off-by: Robert Young --- proposals/007-azure-kms.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/007-azure-kms.md b/proposals/007-azure-kms.md index 28dcea5..5fcae9b 100644 --- a/proposals/007-azure-kms.md +++ b/proposals/007-azure-kms.md @@ -12,6 +12,7 @@ * [DEK Generation](#dek-generation) * [Key Identifiers](#key-identifiers) * [Proposal](#proposal) + * [Key Types Support Matrix](#key-types-support-matrix) * [Configuration](#configuration) * [Authorization](#authorization) * [Edek Serialization Scheme](#edek-serialization-scheme) From 9550f95724f1588df096e8a036ce76f802ee0f3c Mon Sep 17 00:00:00 2001 From: Robert Young Date: Fri, 19 Sep 2025 11:46:14 +1200 Subject: [PATCH 07/17] Note that we will not attempt to normalize user topic names Signed-off-by: Robert Young --- proposals/007-azure-kms.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/proposals/007-azure-kms.md b/proposals/007-azure-kms.md index 5fcae9b..1a1dfbb 100644 --- a/proposals/007-azure-kms.md +++ b/proposals/007-azure-kms.md @@ -141,6 +141,8 @@ different version bytes to discriminate between the formats. 8. [Edek](#edek-serialization-scheme) stores the keyName, keyVersion, edek. We attempt to minimise keyVersion size by optimistically decoding it from hex string, else store the string. 9. User will supply tenantId for authentication, rather than implementing a more complicated workflow to obtain it using an HTTP request to KeyVault 10. Endpoints for Entra and Key Vault will be configurable to support national clouds. +11. It will be a documentation/user responsibility to ensure that their topic names are valid Azure key names if they + wish to use the `$(topicName)` KEK selector. ### Key Types Support Matrix @@ -266,4 +268,8 @@ The `kroxylicous` repo. Which saves configuring the specific Entra endpoint, tenantId and the resource implies the scope. However, all that convenience adds more Security risks. We must be very careful not to POST credentials at a malicious authorization endpoint or request a different scope than what we require. So for now, we make the entra endpoint, tenantId and scope fixed pieces of configuration so we absolutely know - where we are sending the client's credentials. Allowing the Key Vault to dictate where to authenticate is a risk. \ No newline at end of file + where we are sending the client's credentials. Allowing the Key Vault to dictate where to authenticate is a risk. +5. **supporting topic name kek selectors that aren't valid Key Names in Azure Key Vault**. Using the topic name as the Key Name is not a very likely + scenario given how Keys are priced. Making and rotating many Keys will be expensive. Also we know our key selection + mechanism is likely to be overhauled. We could attempt to mangle the users topic names to coerce them into valid Azure key + names, but don't think it's a likely production configuration. Better handled by documentation for now. \ No newline at end of file From 792b58ee2fe4338114ff99223f3c8a5a9a9fadd0 Mon Sep 17 00:00:00 2001 From: Robert Young Date: Fri, 19 Sep 2025 11:47:00 +1200 Subject: [PATCH 08/17] remove reference to quantum resistance warning Signed-off-by: Robert Young --- proposals/007-azure-kms.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/007-azure-kms.md b/proposals/007-azure-kms.md index 1a1dfbb..9bcbd21 100644 --- a/proposals/007-azure-kms.md +++ b/proposals/007-azure-kms.md @@ -132,7 +132,7 @@ different version bytes to discriminate between the formats. ## Proposal 1. Support all flavours of Key Vault. The APIs will be the same, just with a different vault base URI. -2. Support RSA and HSM-RSA key types, wrapping using `RSA-OAEP-256` but emit a warning that it is not quantum-resistant. +2. Support RSA and HSM-RSA key types, wrapping using `RSA-OAEP-256`. 3. Support HSM-AES key type and AES-GCM wrapping 4. Support only client credentials oauth flow with Entra using clientId + clientSecret. This supports workloads running anywhere. We could add support for other client credentials (certificates, federated certs) and Managed Identities later. Share a single auth token per Filter Definition until near expiry. 5. Support TLS customization of the authentication client and key vault client. From 75b12803ccdc09ea16e3891ab2aa1be9a7a55249 Mon Sep 17 00:00:00 2001 From: Robert Young Date: Fri, 19 Sep 2025 11:49:00 +1200 Subject: [PATCH 09/17] Add note about quantum-resistant warnings Signed-off-by: Robert Young --- proposals/007-azure-kms.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/proposals/007-azure-kms.md b/proposals/007-azure-kms.md index 9bcbd21..6144d0b 100644 --- a/proposals/007-azure-kms.md +++ b/proposals/007-azure-kms.md @@ -272,4 +272,7 @@ The `kroxylicous` repo. 5. **supporting topic name kek selectors that aren't valid Key Names in Azure Key Vault**. Using the topic name as the Key Name is not a very likely scenario given how Keys are priced. Making and rotating many Keys will be expensive. Also we know our key selection mechanism is likely to be overhauled. We could attempt to mangle the users topic names to coerce them into valid Azure key - names, but don't think it's a likely production configuration. Better handled by documentation for now. \ No newline at end of file + names, but don't think it's a likely production configuration. Better handled by documentation for now. +6. **emitting quantum resistance warnings** the RSA mechanism available in the affordable Key Vault tiers is not advertised + as quantum resistant. Initially I thought we should emit warning logs in code, but given the state of the art is in flux + we should instead rely on documentation to advertise the latest information. \ No newline at end of file From 8d672e08fd11446d556682afc2a315cc6444ef1d Mon Sep 17 00:00:00 2001 From: Robert Young Date: Mon, 22 Sep 2025 12:53:44 +1200 Subject: [PATCH 10/17] Update proposals/007-azure-kms.md Co-authored-by: Tom Bentley Signed-off-by: Robert Young --- proposals/007-azure-kms.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/007-azure-kms.md b/proposals/007-azure-kms.md index 6144d0b..da6bf3e 100644 --- a/proposals/007-azure-kms.md +++ b/proposals/007-azure-kms.md @@ -139,7 +139,7 @@ different version bytes to discriminate between the formats. 6. If the Key Vault is Managed HSM, then we will use the API to generate random bytes. Else, DEK bytes will be generated proxy-side with a SecureRandom. 7. The Azure SDK pulls in netty/jackson/project-reactor, lets try implementing the APIs ourselves as we have for AWS 8. [Edek](#edek-serialization-scheme) stores the keyName, keyVersion, edek. We attempt to minimise keyVersion size by optimistically decoding it from hex string, else store the string. -9. User will supply tenantId for authentication, rather than implementing a more complicated workflow to obtain it using an HTTP request to KeyVault +9. User will supply tenantId for authentication, rather than implementing a more complicated workflow to obtain it using an HTTP request to KeyVault. Doing something smarter and making it optional in the future will be backwards compatible. 10. Endpoints for Entra and Key Vault will be configurable to support national clouds. 11. It will be a documentation/user responsibility to ensure that their topic names are valid Azure key names if they wish to use the `$(topicName)` KEK selector. From 026c1fa5c87616fd70625d0fb22aefadbef96a0b Mon Sep 17 00:00:00 2001 From: Robert Young Date: Mon, 22 Sep 2025 12:54:06 +1200 Subject: [PATCH 11/17] Update proposals/007-azure-kms.md Co-authored-by: Tom Bentley Signed-off-by: Robert Young --- proposals/007-azure-kms.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/007-azure-kms.md b/proposals/007-azure-kms.md index da6bf3e..fa4dfa2 100644 --- a/proposals/007-azure-kms.md +++ b/proposals/007-azure-kms.md @@ -136,7 +136,7 @@ different version bytes to discriminate between the formats. 3. Support HSM-AES key type and AES-GCM wrapping 4. Support only client credentials oauth flow with Entra using clientId + clientSecret. This supports workloads running anywhere. We could add support for other client credentials (certificates, federated certs) and Managed Identities later. Share a single auth token per Filter Definition until near expiry. 5. Support TLS customization of the authentication client and key vault client. -6. If the Key Vault is Managed HSM, then we will use the API to generate random bytes. Else, DEK bytes will be generated proxy-side with a SecureRandom. +6. If the Key Vault is Managed HSM, then we will use the Key Vault API to generate random bytes. Else, DEK bytes will be generated within the proxy JVM using a `SecureRandom`. 7. The Azure SDK pulls in netty/jackson/project-reactor, lets try implementing the APIs ourselves as we have for AWS 8. [Edek](#edek-serialization-scheme) stores the keyName, keyVersion, edek. We attempt to minimise keyVersion size by optimistically decoding it from hex string, else store the string. 9. User will supply tenantId for authentication, rather than implementing a more complicated workflow to obtain it using an HTTP request to KeyVault. Doing something smarter and making it optional in the future will be backwards compatible. From 3c9fc4d80d22d42a42b93d90a9dfd807bed788d0 Mon Sep 17 00:00:00 2001 From: Robert Young Date: Mon, 22 Sep 2025 14:38:14 +1200 Subject: [PATCH 12/17] Update to RBAC permissions Signed-off-by: Robert Young --- proposals/{007-azure-kms.md => 009-azure-kms.md} | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) rename proposals/{007-azure-kms.md => 009-azure-kms.md} (97%) diff --git a/proposals/007-azure-kms.md b/proposals/009-azure-kms.md similarity index 97% rename from proposals/007-azure-kms.md rename to proposals/009-azure-kms.md index fa4dfa2..9ce3296 100644 --- a/proposals/007-azure-kms.md +++ b/proposals/009-azure-kms.md @@ -1,7 +1,7 @@ -# 007 - Azure KMS Implementation +# 009 - Azure KMS Implementation -* [007 - Azure KMS Implementation](#007---azure-kms-implementation) +* [009 - Azure KMS Implementation](#009---azure-kms-implementation) * [Motivation](#motivation) * [Background](#background) * [Key Vault Types](#key-vault-types) @@ -184,11 +184,11 @@ We want to adhere to the principal of Least Privilege. The minimum authorization a Principal needs for the Key Vault using this implementation will be: -* allow `get` -* allow `unwrapKey` -* allow `wrapKey` +* allow data action `Microsoft.KeyVault/vaults/keys/read` to enable access to key metadata +* allow data action `Microsoft.KeyVault/vaults/keys/wrap/action` +* allow data action `Microsoft.KeyVault/vaults/keys/unwrap/action` -If using Managed HSM we will also need ` Microsoft.KeyVault/managedhsms/rng/action` action to be allowed so that we +If using Managed HSM we will also need `Microsoft.KeyVault/managedhsms/rng/action` action to be allowed so that we can use it to generate bytes. The Key itself in the Key Vault also has a set of supported operations. We require at minimum `unwrapKey` and `wrapKey`. From afbed11b7a2d513a916096c5f8955ce91026ad2c Mon Sep 17 00:00:00 2001 From: Robert Young Date: Wed, 24 Sep 2025 10:25:48 +1200 Subject: [PATCH 13/17] Start with oauthEndpointUrl and scope as required values Signed-off-by: Robert Young --- proposals/009-azure-kms.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/009-azure-kms.md b/proposals/009-azure-kms.md index 9ce3296..3486db2 100644 --- a/proposals/009-azure-kms.md +++ b/proposals/009-azure-kms.md @@ -167,13 +167,13 @@ kmsConfig: tls: ... client trust for key vault entraIdentity: - oauthEndpointUrl: https://login.microsoftonline.com // optional defaults to this value + oauthEndpointUrl: https://login.microsoftonline.com clientId: passwordFile: /path/to/id clientSecret: passwordFile: /path/to/id tenantId: "abds-1232dsaa" - scope: https://vault.azure.net/.default // optional, could infer from vaultBaseUri + scope: https://vault.azure.net/.default tls: ... client trust configuration for oauth ``` From a71cc790da41850830bd42aa20bcf83673a15c43 Mon Sep 17 00:00:00 2001 From: Robert Young Date: Wed, 24 Sep 2025 10:29:08 +1200 Subject: [PATCH 14/17] Add note about required-ness Signed-off-by: Robert Young --- proposals/009-azure-kms.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/proposals/009-azure-kms.md b/proposals/009-azure-kms.md index 3486db2..1cbdeea 100644 --- a/proposals/009-azure-kms.md +++ b/proposals/009-azure-kms.md @@ -164,7 +164,7 @@ are currently compatible with. kms: AzureKeyVault kmsConfig: keyVaultBaseUri: https://kv-kfesiehfoieaf.vault.azure.net - tls: + tls: # optional ... client trust for key vault entraIdentity: oauthEndpointUrl: https://login.microsoftonline.com @@ -174,10 +174,13 @@ kmsConfig: passwordFile: /path/to/id tenantId: "abds-1232dsaa" scope: https://vault.azure.net/.default - tls: + tls: # optional ... client trust configuration for oauth ``` +All configuration options are required, except `tls` configuration. Noting that `clientSecret` and `clientId` use the +established `PasswordProvider` mechanism, so can use inline `password` or `passwordFile`. + ### Authorization We want to adhere to the principal of Least Privilege. From 8898ab81fce096f0a0a5c5b99b528ec34a064179 Mon Sep 17 00:00:00 2001 From: Robert Young Date: Thu, 25 Sep 2025 10:09:14 +1200 Subject: [PATCH 15/17] Update proposals/009-azure-kms.md Co-authored-by: Keith Wall Signed-off-by: Robert Young --- proposals/009-azure-kms.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/009-azure-kms.md b/proposals/009-azure-kms.md index 1cbdeea..45b106b 100644 --- a/proposals/009-azure-kms.md +++ b/proposals/009-azure-kms.md @@ -183,7 +183,7 @@ established `PasswordProvider` mechanism, so can use inline `password` or `passw ### Authorization -We want to adhere to the principal of Least Privilege. +We want to adhere to the Principle of Least Privilege. The minimum authorization a Principal needs for the Key Vault using this implementation will be: From 1e7e9b4d2528f3f3183266b72c5e094d67117f09 Mon Sep 17 00:00:00 2001 From: Robert Young Date: Thu, 25 Sep 2025 11:59:26 +1200 Subject: [PATCH 16/17] Update proposal to include vault name in serialized EDEK Signed-off-by: Robert Young --- proposals/009-azure-kms.md | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/proposals/009-azure-kms.md b/proposals/009-azure-kms.md index 45b106b..370ba30 100644 --- a/proposals/009-azure-kms.md +++ b/proposals/009-azure-kms.md @@ -205,10 +205,14 @@ reduce the serialized size, but implement a fallback in case the Key Version evo #### Common Fields +- **Vault Name**: The name of the Azure Key Vault, encoded in UTF-8. Its documented format is `3-24 character string, containing only 0-9, a-z, A-Z, and not consecutive -` - **Key Name**: The name of the key in Azure Key Vault, encoded in UTF-8. Its documented format is `1-127 character string, containing only 0-9, a-z, A-Z, and -.` - **Key Version**: The version identifier for the key in Azure Key Vault. Its documented format is `32 character string` - **EDEK**: The Encrypted Data Encryption Key, a byte array. +Note that we do not serialize the Vault host (for example `vault.azure.net`), this is derived from the Filter configuration. +The Filter will initially only support operating in a single cloud. + [See also](https://learn.microsoft.com/en-us/azure/key-vault/general/about-keys-secrets-certificates#object-identifiers) #### Format 0: Hexadecimal Key Version @@ -217,13 +221,15 @@ This format is used when the **Key Version** is represented as a lowercase hexad The byte layout is as follows: -| Offset | Length (bytes) | Field Name | Description | -|:-------|:---------------|:----------------------|:-------------------------------------------------| -| 0 | 1 | **Format Version** | A constant byte with the value `0x00`. | -| 1 | 1 | **Key Name Length** | The length of the `Key Name` field in bytes (N). | -| 2 | N | **Key Name** | The UTF-8 encoded key name. | -| 2+N | 16 | **Key Version (Hex)** | The 128-bit key version, serialized as 16 bytes. | -| 18+N | M | **EDEK** | The EDEK byte array. | +| Length (bytes) | Field Name | Description | +|:---------------|:----------------------|:---------------------------------------------------| +| 1 | **Format Version** | A constant byte with the value `0x00`. | +| 1 | **Vault Name Length** | The length of the `Vault Name` field in bytes (N). | +| N | **Vault Name** | The UTF-8 encoded vault name. | +| 1 | **Key Name Length** | The length of the `Key Name` field in bytes (N). | +| M | **Key Name** | The UTF-8 encoded key name. | +| 16 | **Key Version (Hex)** | The 128-bit key version, serialized as 16 bytes. | +| M | **EDEK** | The EDEK byte array. | Note that the key name is up to 127 characters that are a subset of ascii so its length can be described with 1 byte. #### Format 1: String Key Version @@ -232,14 +238,16 @@ This format is used for any **Key Version** that is not a lowercase hexadecimal The byte layout is as follows: -| Offset | Length (bytes) | Field Name | Description | -|:-------|:---------------|:-------------------------|:-------------------------------------------------------------| -| 0 | 1 | **Format Version** | A constant byte with the value `0x01`. | -| 1 | 1 | **Key Name Length** | The length of the `Key Name` field in bytes (N). | -| 2 | N | **Key Name** | The UTF-8 encoded key name. | -| 2+N | 1 | **Key Version Length** | The length of the `Key Version (String)` field in bytes (P). | -| 3+N | P | **Key Version (String)** | The UTF-8 encoded key version string. | -| 3+N+P | M | **EDEK** | The EDEK byte array. | +| Length (bytes) | Field Name | Description | +|:---------------|:-------------------------|:-------------------------------------------------------------| +| 1 | **Format Version** | A constant byte with the value `0x01`. | +| 1 | **Vault Name Length** | The length of the `Vault Name` field in bytes (N). | +| N | **Vault Name** | The UTF-8 encoded vault name. | +| 1 | **Key Name Length** | The length of the `Key Name` field in bytes (N). | +| N | **Key Name** | The UTF-8 encoded key name. | +| 1 | **Key Version Length** | The length of the `Key Version (String)` field in bytes (P). | +| P | **Key Version (String)** | The UTF-8 encoded key version string. | +| M | **EDEK** | The EDEK byte array. | Note that the key name is up to 127 characters that are a subset of ascii so its length can be described with 1 byte. ## Compatibility From 6d7d348fb96b377154d6b7b42b1a18a94e0840ba Mon Sep 17 00:00:00 2001 From: Robert Young Date: Fri, 26 Sep 2025 16:41:16 +1200 Subject: [PATCH 17/17] Add keyType to EDEK layout This enables us to execute the unwrap without having to describe the key again to obtain it's type. It also adds a useful record of how the key was encrypted, with the downside that it costs a byte per record. Signed-off-by: Robert Young --- proposals/009-azure-kms.md | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/proposals/009-azure-kms.md b/proposals/009-azure-kms.md index 370ba30..eb23b9a 100644 --- a/proposals/009-azure-kms.md +++ b/proposals/009-azure-kms.md @@ -138,12 +138,18 @@ different version bytes to discriminate between the formats. 5. Support TLS customization of the authentication client and key vault client. 6. If the Key Vault is Managed HSM, then we will use the Key Vault API to generate random bytes. Else, DEK bytes will be generated within the proxy JVM using a `SecureRandom`. 7. The Azure SDK pulls in netty/jackson/project-reactor, lets try implementing the APIs ourselves as we have for AWS -8. [Edek](#edek-serialization-scheme) stores the keyName, keyVersion, edek. We attempt to minimise keyVersion size by optimistically decoding it from hex string, else store the string. +8. [Edek](#edek-serialization-scheme) stores the keyName, keyType, vaultName, keyVersion, edek. We attempt to minimise keyVersion size by optimistically decoding it from hex string, else store the string. 9. User will supply tenantId for authentication, rather than implementing a more complicated workflow to obtain it using an HTTP request to KeyVault. Doing something smarter and making it optional in the future will be backwards compatible. 10. Endpoints for Entra and Key Vault will be configurable to support national clouds. 11. It will be a documentation/user responsibility to ensure that their topic names are valid Azure key names if they wish to use the `$(topicName)` KEK selector. +When resolving the alias, we will expect that the alias is an Azure Key Name. We will `get` that key to determine its +type and latest version, and check it supports wrap/unwrap operations. + +We will serialize everything we need to execute the unwrap operation without having to `get` the key again on the decrypt path. +Meaning we will serialize the key name, version, type and vault name. + ### Key Types Support Matrix For completeness, this covers which key types are supported by this KMS implementation and which Key Vault products they @@ -208,6 +214,7 @@ reduce the serialized size, but implement a fallback in case the Key Version evo - **Vault Name**: The name of the Azure Key Vault, encoded in UTF-8. Its documented format is `3-24 character string, containing only 0-9, a-z, A-Z, and not consecutive -` - **Key Name**: The name of the key in Azure Key Vault, encoded in UTF-8. Its documented format is `1-127 character string, containing only 0-9, a-z, A-Z, and -.` - **Key Version**: The version identifier for the key in Azure Key Vault. Its documented format is `32 character string` +- **Key Type**: The key type, see [table](#key-type) for byte values - **EDEK**: The Encrypted Data Encryption Key, a byte array. Note that we do not serialize the Vault host (for example `vault.azure.net`), this is derived from the Filter configuration. @@ -215,6 +222,16 @@ The Filter will initially only support operating in a single cloud. [See also](https://learn.microsoft.com/en-us/azure/key-vault/general/about-keys-secrets-certificates#object-identifiers) +#### Key Type +A subset of the Azure Key Types which we support. Initially, the key type will deterministically imply a wrapping algorithm. + +| id | Azure Key Type | Wrapping Algorithm | +|:---|:---------------|:-------------------| +| 0 | RSA | RSA-OAEP-256 | +| 1 | RSA-HSM | RSA-OAEP-256 | +| 2 | oct | A256GCM | +| 3 | oct-HSM | A256GCM | + #### Format 0: Hexadecimal Key Version This format is used when the **Key Version** is represented as a lowercase hexadecimal string (e.g., `78deebed173b48e48f55abf87ed4cf71`). The version is serialized directly as a 128-bit (16-byte) value. @@ -224,14 +241,16 @@ The byte layout is as follows: | Length (bytes) | Field Name | Description | |:---------------|:----------------------|:---------------------------------------------------| | 1 | **Format Version** | A constant byte with the value `0x00`. | +| 1 | **Key Name Length** | The length of the `Key Name` field in bytes (M). | +| M | **Key Name** | The UTF-8 encoded key name. | | 1 | **Vault Name Length** | The length of the `Vault Name` field in bytes (N). | | N | **Vault Name** | The UTF-8 encoded vault name. | -| 1 | **Key Name Length** | The length of the `Key Name` field in bytes (N). | -| M | **Key Name** | The UTF-8 encoded key name. | +| 1 | **Key Type** | The [key type](#key-type) | | 16 | **Key Version (Hex)** | The 128-bit key version, serialized as 16 bytes. | -| M | **EDEK** | The EDEK byte array. | +| P | **EDEK** | The EDEK byte array. | Note that the key name is up to 127 characters that are a subset of ascii so its length can be described with 1 byte. + #### Format 1: String Key Version This format is used for any **Key Version** that is not a lowercase hexadecimal string (e.g., a custom name or GUID). The version is serialized as a length-prefixed UTF-8 string. @@ -241,13 +260,14 @@ The byte layout is as follows: | Length (bytes) | Field Name | Description | |:---------------|:-------------------------|:-------------------------------------------------------------| | 1 | **Format Version** | A constant byte with the value `0x01`. | +| 1 | **Key Name Length** | The length of the `Key Name` field in bytes (M). | +| M | **Key Name** | The UTF-8 encoded key name. | | 1 | **Vault Name Length** | The length of the `Vault Name` field in bytes (N). | | N | **Vault Name** | The UTF-8 encoded vault name. | -| 1 | **Key Name Length** | The length of the `Key Name` field in bytes (N). | -| N | **Key Name** | The UTF-8 encoded key name. | +| 1 | **Key Type** | The [key type](#key-type) | | 1 | **Key Version Length** | The length of the `Key Version (String)` field in bytes (P). | | P | **Key Version (String)** | The UTF-8 encoded key version string. | -| M | **EDEK** | The EDEK byte array. | +| Q | **EDEK** | The EDEK byte array. | Note that the key name is up to 127 characters that are a subset of ascii so its length can be described with 1 byte. ## Compatibility