diff --git a/.github/scripts/init-temp-keys.sh b/.github/scripts/init-temp-keys.sh
index 4bb94ea76b..e9227c74d8 100755
--- a/.github/scripts/init-temp-keys.sh
+++ b/.github/scripts/init-temp-keys.sh
@@ -50,6 +50,9 @@ openssl rsa -in "$opt_output/kas-private.pem" -pubout -out "$opt_output/kas-cert
openssl ecparam -name prime256v1 >ecparams.tmp
openssl req -x509 -nodes -newkey ec:ecparams.tmp -subj "/CN=kas" -keyout "$opt_output/kas-ec-private.pem" -out "$opt_output/kas-ec-cert.pem" -days 365
+# Generate hybrid post-quantum key pairs (X-Wing, P256+ML-KEM-768, P384+ML-KEM-1024)
+go run ./service/cmd/keygen -output "$opt_output"
+
mkdir -p keys
openssl req -x509 -nodes -newkey RSA:2048 -subj "/CN=ca" -keyout keys/keycloak-ca-private.pem -out keys/keycloak-ca.pem -days 365
printf "subjectAltName=DNS:localhost,IP:127.0.0.1" >keys/sanX509.conf
diff --git a/.github/scripts/watch.sh b/.github/scripts/watch.sh
index defedcd798..acde7b6b75 100755
--- a/.github/scripts/watch.sh
+++ b/.github/scripts/watch.sh
@@ -53,18 +53,54 @@ done
file_to_watch="$1"
shift
+file_signature() {
+ if [[ ! -e "$1" ]]; then
+ echo "missing"
+ return
+ fi
+
+ if stat -c '%i:%s:%Y' "$1" >/dev/null 2>&1; then
+ stat -c '%i:%s:%Y' "$1"
+ return
+ fi
+
+ stat -f '%i:%z:%m' "$1"
+}
+
wait_for_change_to() {
- if which inotifywait; then
- echo "[INFO] inotifywaiting to [${file_to_watch}]"
- inotifywait -e modify -e move -e create -e delete -e attrib -r "${file_to_watch}"
+ if command -v inotifywait >/dev/null 2>&1; then
+ local watch_dir
+ local watch_name
+ local changed_file
+
+ watch_dir=$(dirname "${file_to_watch}")
+ watch_name=$(basename "${file_to_watch}")
+
+ echo "[INFO] inotifywaiting to [${file_to_watch}] via [${watch_dir}]"
+ while true; do
+ changed_file=$(inotifywait -q \
+ -e close_write \
+ -e moved_to \
+ -e delete \
+ -e attrib \
+ --format '%f' \
+ "${watch_dir}")
+
+ if [[ "${changed_file}" == "${watch_name}" ]]; then
+ return
+ fi
+ done
else
- m=$(date -r "${file_to_watch}" +%s)
+ local m
+ local n
+
+ m=$(file_signature "${file_to_watch}")
echo "[INFO] stat checking [${file_to_watch}] from [${m}]"
while true; do
sleep 1
- n=$(date -r "${file_to_watch}" +%s)
- echo "[INFO] stat checking [${file_to_watch}] from [${m} < ${n}]"
- if [[ $m < $n ]]; then
+ n=$(file_signature "${file_to_watch}")
+ echo "[INFO] stat checking [${file_to_watch}] from [${m} != ${n}]"
+ if [[ "${m}" != "${n}" ]]; then
return
fi
done
diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml
index 237a7ab46d..5ed9bd038c 100644
--- a/.github/workflows/checks.yaml
+++ b/.github/workflows/checks.yaml
@@ -598,7 +598,7 @@ jobs:
- uses: bufbuild/buf-setup-action@a47c93e0b1648d5651a065437926377d060baa99 # v1.50.0
with:
github_token: ${{ github.token }}
- version: "1.68.2"
+ version: "1.68.3"
- uses: bufbuild/buf-lint-action@06f9dd823d873146471cfaaf108a993fe00e5325 # v1.1.1
with:
input: service
diff --git a/.gitignore b/.gitignore
index 4b8411c9f6..7d89ba5ac6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,6 +31,7 @@ opentdf.yaml
tmp-gen/
/examples/examples
/**/kas-*.pem
+!/service/pkg/server/testdata/kas-*.pem
/opentdf
/sdkjava/target
/serviceapp
diff --git a/Makefile b/Makefile
index 404584dcb6..d2b8023e80 100644
--- a/Makefile
+++ b/Makefile
@@ -3,8 +3,8 @@
.PHONY: all buf-check build clean connect-wrapper-generate docker-build fix fmt go-lint license lint otdfctl/otdfctl policy-sql-gen proto-generate proto-helper-generate proto-lint sdk/sdk sqlc-check test tidy toolcheck
-MODS=protocol/go lib/ocrypto lib/fixtures lib/flattening lib/identifier sdk service examples otdfctl
-HAND_MODS=lib/ocrypto lib/fixtures lib/flattening lib/identifier sdk service examples otdfctl
+MODS=protocol/go lib/ocrypto lib/fixtures lib/flattening lib/identifier sdk service examples otdfctl tests-bdd
+HAND_MODS=lib/ocrypto lib/fixtures lib/flattening lib/identifier sdk service examples otdfctl tests-bdd
REQUIRED_BUF_VERSION=1.68.2
REQUIRED_SQLC_VERSION=1.31.0
diff --git a/docs/grpc/index.html b/docs/grpc/index.html
index c1bfdab139..a59d73dd56 100644
--- a/docs/grpc/index.html
+++ b/docs/grpc/index.html
@@ -4116,6 +4116,24 @@
Algorithm
|
+
+ | ALGORITHM_HPQT_XWING |
+ 6 |
+ |
+
+
+
+ | ALGORITHM_HPQT_SECP256R1_MLKEM768 |
+ 7 |
+ |
+
+
+
+ | ALGORITHM_HPQT_SECP384R1_MLKEM1024 |
+ 8 |
+ |
+
+
@@ -4227,6 +4245,24 @@ KasPublicKeyAlgEnum
|
+
+ | KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING |
+ 10 |
+ |
+
+
+
+ | KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 |
+ 11 |
+ |
+
+
+
+ | KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 |
+ 12 |
+ |
+
+
@@ -6279,7 +6315,7 @@ KeyAccess
|
Type of key wrapping used for the data encryption key
Required: Always
-Values: 'wrapped' (RSA-wrapped for ZTDF), 'ec-wrapped' (experimental ECDH-wrapped) |
+Values: 'wrapped' (RSA-wrapped for ZTDF), 'ec-wrapped' (experimental ECDH-wrapped), 'hybrid-wrapped' (experimental X-Wing-wrapped)
@@ -6336,7 +6372,7 @@ KeyAccess
|
Ephemeral public key for ECDH key derivation (ec-wrapped type only)
Required: When key_type="ec-wrapped" (experimental ECDH-based ZTDF)
-Omitted: When key_type="wrapped" (RSA-based ZTDF)
+Omitted: When key_type="wrapped" or key_type="hybrid-wrapped"
Should be a PEM-encoded PKCS#8 (ASN.1) formatted public key
Used to derive the symmetric key for unwrapping the DEK |
@@ -6664,7 +6700,7 @@ RewrapResponse
|
KAS's ephemeral session public key in PEM format
Required: For EC-based operations (key_type="ec-wrapped")
-Optional: Empty for RSA-based ZTDF (key_type="wrapped")
+Optional: Empty for RSA-based or X-Wing-based ZTDF (key_type="wrapped" or key_type="hybrid-wrapped")
Used by client to perform ECDH key agreement and decrypt the kas_wrapped_key values |
diff --git a/docs/openapi/authorization/authorization.openapi.yaml b/docs/openapi/authorization/authorization.openapi.yaml
index dd0cb366aa..5a112b61b6 100644
--- a/docs/openapi/authorization/authorization.openapi.yaml
+++ b/docs/openapi/authorization/authorization.openapi.yaml
@@ -140,6 +140,9 @@ components:
- ALGORITHM_EC_P256
- ALGORITHM_EC_P384
- ALGORITHM_EC_P521
+ - ALGORITHM_HPQT_XWING
+ - ALGORITHM_HPQT_SECP256R1_MLKEM768
+ - ALGORITHM_HPQT_SECP384R1_MLKEM1024
description: Supported key algorithms.
policy.KasPublicKeyAlgEnum:
type: string
@@ -151,6 +154,9 @@ components:
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024
policy.SourceType:
type: string
title: SourceType
@@ -186,6 +192,7 @@ components:
Example Request Get Decisions to answer the question - Do Bob (represented by entity chain ec1)
and Alice (represented by entity chain ec2) have TRANSMIT authorization for
2 resources; resource1 (attr-set-1) defined by attributes foo:bar resource2 (attr-set-2) defined by attribute foo:bar, color:red ?
+
{
"actions": [
{
@@ -257,11 +264,13 @@ components:
Example response for a Decision Request - Do Bob (represented by entity chain ec1)
and Alice (represented by entity chain ec2) have TRANSMIT authorization for
2 resources; resource1 (attr-set-1) defined by attributes foo:bar resource2 (attr-set-2) defined by attribute foo:bar, color:red ?
+
Results:
- bob has permitted authorization to transmit for a resource defined by attr-set-1 attributes and has a watermark obligation
- bob has denied authorization to transmit a for a resource defined by attr-set-2 attributes
- alice has permitted authorization to transmit for a resource defined by attr-set-1 attributes
- alice has denied authorization to transmit a for a resource defined by attr-set-2 attributes
+
{
"entityChainId": "ec1",
"resourceAttributesId": "attr-set-1",
@@ -449,7 +458,9 @@ components:
additionalProperties: false
description: |-
Request to get entitlements for one or more entities for an optional attribute scope
+
Example: Get entitlements for bob and alice (both represented using an email address
+
{
"entities": [
{
@@ -480,6 +491,7 @@ components:
additionalProperties: false
description: |-
Example Response for a request of : Get entitlements for bob and alice (both represented using an email address
+
{
"entitlements": [
{
@@ -551,6 +563,7 @@ components:
Example Request Get Decisions by Token to answer the question - Do Bob and client1 (represented by token tok1)
and Alice and client2 (represented by token tok2) have TRANSMIT authorization for
2 resources; resource1 (attr-set-1) defined by attributes foo:bar resource2 (attr-set-2) defined by attribute foo:bar, color:red ?
+
{
"actions": [
{
diff --git a/docs/openapi/authorization/v2/authorization.openapi.yaml b/docs/openapi/authorization/v2/authorization.openapi.yaml
index 0f4f92d55f..7e9d837275 100644
--- a/docs/openapi/authorization/v2/authorization.openapi.yaml
+++ b/docs/openapi/authorization/v2/authorization.openapi.yaml
@@ -175,6 +175,9 @@ components:
- ALGORITHM_EC_P256
- ALGORITHM_EC_P384
- ALGORITHM_EC_P521
+ - ALGORITHM_HPQT_XWING
+ - ALGORITHM_HPQT_SECP256R1_MLKEM768
+ - ALGORITHM_HPQT_SECP384R1_MLKEM1024
description: Supported key algorithms.
policy.KasPublicKeyAlgEnum:
type: string
@@ -186,6 +189,9 @@ components:
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024
policy.SourceType:
type: string
title: SourceType
diff --git a/docs/openapi/kas/kas.openapi.yaml b/docs/openapi/kas/kas.openapi.yaml
index 2ba076acc5..a357028153 100644
--- a/docs/openapi/kas/kas.openapi.yaml
+++ b/docs/openapi/kas/kas.openapi.yaml
@@ -235,7 +235,7 @@ components:
description: |-
Type of key wrapping used for the data encryption key
Required: Always
- Values: 'wrapped' (RSA-wrapped for ZTDF), 'ec-wrapped' (experimental ECDH-wrapped)
+ Values: 'wrapped' (RSA-wrapped for ZTDF), 'ec-wrapped' (experimental ECDH-wrapped), 'hybrid-wrapped' (experimental X-Wing-wrapped)
url:
type: string
title: kas_url
@@ -282,7 +282,7 @@ components:
description: |-
Ephemeral public key for ECDH key derivation (ec-wrapped type only)
Required: When key_type="ec-wrapped" (experimental ECDH-based ZTDF)
- Omitted: When key_type="wrapped" (RSA-based ZTDF)
+ Omitted: When key_type="wrapped" or key_type="hybrid-wrapped"
Should be a PEM-encoded PKCS#8 (ASN.1) formatted public key
Used to derive the symmetric key for unwrapping the DEK
title: KeyAccess
@@ -472,7 +472,7 @@ components:
description: |-
KAS's ephemeral session public key in PEM format
Required: For EC-based operations (key_type="ec-wrapped")
- Optional: Empty for RSA-based ZTDF (key_type="wrapped")
+ Optional: Empty for RSA-based or X-Wing-based ZTDF (key_type="wrapped" or key_type="hybrid-wrapped")
Used by client to perform ECDH key agreement and decrypt the kas_wrapped_key values
schemaVersion:
type: string
diff --git a/docs/openapi/policy/actions/actions.openapi.yaml b/docs/openapi/policy/actions/actions.openapi.yaml
index 4f4681fc77..db1d5d2a32 100644
--- a/docs/openapi/policy/actions/actions.openapi.yaml
+++ b/docs/openapi/policy/actions/actions.openapi.yaml
@@ -203,6 +203,9 @@ components:
- ALGORITHM_EC_P256
- ALGORITHM_EC_P384
- ALGORITHM_EC_P521
+ - ALGORITHM_HPQT_XWING
+ - ALGORITHM_HPQT_SECP256R1_MLKEM768
+ - ALGORITHM_HPQT_SECP384R1_MLKEM1024
description: Supported key algorithms.
policy.AttributeRuleTypeEnum:
type: string
@@ -229,6 +232,9 @@ components:
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024
policy.SourceType:
type: string
title: SourceType
diff --git a/docs/openapi/policy/attributes/attributes.openapi.yaml b/docs/openapi/policy/attributes/attributes.openapi.yaml
index 5e208ad63b..1eb291ec16 100644
--- a/docs/openapi/policy/attributes/attributes.openapi.yaml
+++ b/docs/openapi/policy/attributes/attributes.openapi.yaml
@@ -722,6 +722,9 @@ components:
- ALGORITHM_EC_P256
- ALGORITHM_EC_P384
- ALGORITHM_EC_P521
+ - ALGORITHM_HPQT_XWING
+ - ALGORITHM_HPQT_SECP256R1_MLKEM768
+ - ALGORITHM_HPQT_SECP384R1_MLKEM1024
description: Supported key algorithms.
policy.AttributeRuleTypeEnum:
type: string
@@ -748,6 +751,9 @@ components:
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024
policy.SortDirection:
type: string
title: SortDirection
diff --git a/docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml b/docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml
index e5ea9f456e..a3eb12f037 100644
--- a/docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml
+++ b/docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml
@@ -523,6 +523,9 @@ components:
- ALGORITHM_EC_P256
- ALGORITHM_EC_P384
- ALGORITHM_EC_P521
+ - ALGORITHM_HPQT_XWING
+ - ALGORITHM_HPQT_SECP256R1_MLKEM768
+ - ALGORITHM_HPQT_SECP384R1_MLKEM1024
description: Supported key algorithms.
policy.KasPublicKeyAlgEnum:
type: string
@@ -534,6 +537,9 @@ components:
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024
policy.KeyMode:
type: string
title: KeyMode
@@ -1165,7 +1171,7 @@ components:
Required The algorithm to be used for the key
The key_algorithm must be one of the defined values.:
```
- this in [1, 2, 3, 4, 5]
+ this in [1, 2, 3, 4, 5, 6, 7, 8]
```
$ref: '#/components/schemas/policy.Algorithm'
@@ -1722,7 +1728,7 @@ components:
Filter keys by algorithm
The key_algorithm must be one of the defined values.:
```
- this in [0, 1, 2, 3, 4, 5]
+ this in [0, 1, 2, 3, 4, 5, 6, 7, 8]
```
$ref: '#/components/schemas/policy.Algorithm'
@@ -1996,7 +2002,7 @@ components:
Required
The key_algorithm must be one of the defined values.:
```
- this in [1, 2, 3, 4, 5]
+ this in [1, 2, 3, 4, 5, 6, 7, 8]
```
$ref: '#/components/schemas/policy.Algorithm'
diff --git a/docs/openapi/policy/namespaces/namespaces.openapi.yaml b/docs/openapi/policy/namespaces/namespaces.openapi.yaml
index 25f2b14fa1..191af3eaf6 100644
--- a/docs/openapi/policy/namespaces/namespaces.openapi.yaml
+++ b/docs/openapi/policy/namespaces/namespaces.openapi.yaml
@@ -353,6 +353,9 @@ components:
- ALGORITHM_EC_P256
- ALGORITHM_EC_P384
- ALGORITHM_EC_P521
+ - ALGORITHM_HPQT_XWING
+ - ALGORITHM_HPQT_SECP256R1_MLKEM768
+ - ALGORITHM_HPQT_SECP384R1_MLKEM1024
description: Supported key algorithms.
policy.KasPublicKeyAlgEnum:
type: string
@@ -364,6 +367,9 @@ components:
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024
policy.SortDirection:
type: string
title: SortDirection
diff --git a/docs/openapi/policy/objects.openapi.yaml b/docs/openapi/policy/objects.openapi.yaml
index ba1c5e3f6f..9c72e91e30 100644
--- a/docs/openapi/policy/objects.openapi.yaml
+++ b/docs/openapi/policy/objects.openapi.yaml
@@ -21,6 +21,9 @@ components:
- ALGORITHM_EC_P256
- ALGORITHM_EC_P384
- ALGORITHM_EC_P521
+ - ALGORITHM_HPQT_XWING
+ - ALGORITHM_HPQT_SECP256R1_MLKEM768
+ - ALGORITHM_HPQT_SECP384R1_MLKEM1024
description: Supported key algorithms.
policy.AttributeRuleTypeEnum:
type: string
@@ -47,6 +50,9 @@ components:
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024
policy.KeyMode:
type: string
title: KeyMode
@@ -987,6 +993,7 @@ components:
authoritative source such as an IDP (Identity Provider) or User Store.
Examples include such ADFS/LDAP, OKTA, etc. For now, a valid property must
contain both a selector expression & a resulting value.
+
The external_selector_value is a specifier to select a value from a flattened
external representation of an Entity (such as from idP/LDAP), and the
external_value is the value selected by the external_selector_value on that
diff --git a/docs/openapi/policy/obligations/obligations.openapi.yaml b/docs/openapi/policy/obligations/obligations.openapi.yaml
index 1780201216..154701d935 100644
--- a/docs/openapi/policy/obligations/obligations.openapi.yaml
+++ b/docs/openapi/policy/obligations/obligations.openapi.yaml
@@ -553,6 +553,9 @@ components:
- ALGORITHM_EC_P256
- ALGORITHM_EC_P384
- ALGORITHM_EC_P521
+ - ALGORITHM_HPQT_XWING
+ - ALGORITHM_HPQT_SECP256R1_MLKEM768
+ - ALGORITHM_HPQT_SECP384R1_MLKEM1024
description: Supported key algorithms.
policy.AttributeRuleTypeEnum:
type: string
@@ -579,6 +582,9 @@ components:
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024
policy.SortDirection:
type: string
title: SortDirection
diff --git a/docs/openapi/policy/registeredresources/registered_resources.openapi.yaml b/docs/openapi/policy/registeredresources/registered_resources.openapi.yaml
index 1a7db3d364..9c761fa78d 100644
--- a/docs/openapi/policy/registeredresources/registered_resources.openapi.yaml
+++ b/docs/openapi/policy/registeredresources/registered_resources.openapi.yaml
@@ -413,6 +413,9 @@ components:
- ALGORITHM_EC_P256
- ALGORITHM_EC_P384
- ALGORITHM_EC_P521
+ - ALGORITHM_HPQT_XWING
+ - ALGORITHM_HPQT_SECP256R1_MLKEM768
+ - ALGORITHM_HPQT_SECP384R1_MLKEM1024
description: Supported key algorithms.
policy.AttributeRuleTypeEnum:
type: string
@@ -439,6 +442,9 @@ components:
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024
policy.SortDirection:
type: string
title: SortDirection
diff --git a/docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml b/docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml
index 8fb2f4e53f..c49765740a 100644
--- a/docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml
+++ b/docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml
@@ -413,6 +413,9 @@ components:
- ALGORITHM_EC_P256
- ALGORITHM_EC_P384
- ALGORITHM_EC_P521
+ - ALGORITHM_HPQT_XWING
+ - ALGORITHM_HPQT_SECP256R1_MLKEM768
+ - ALGORITHM_HPQT_SECP384R1_MLKEM1024
description: Supported key algorithms.
policy.AttributeRuleTypeEnum:
type: string
@@ -439,6 +442,9 @@ components:
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024
policy.SourceType:
type: string
title: SourceType
diff --git a/docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml b/docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml
index bf25db5752..1608ae7962 100644
--- a/docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml
+++ b/docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml
@@ -449,6 +449,9 @@ components:
- ALGORITHM_EC_P256
- ALGORITHM_EC_P384
- ALGORITHM_EC_P521
+ - ALGORITHM_HPQT_XWING
+ - ALGORITHM_HPQT_SECP256R1_MLKEM768
+ - ALGORITHM_HPQT_SECP384R1_MLKEM1024
description: Supported key algorithms.
policy.AttributeRuleTypeEnum:
type: string
@@ -475,6 +478,9 @@ components:
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024
policy.SortDirection:
type: string
title: SortDirection
@@ -1291,6 +1297,7 @@ components:
authoritative source such as an IDP (Identity Provider) or User Store.
Examples include such ADFS/LDAP, OKTA, etc. For now, a valid property must
contain both a selector expression & a resulting value.
+
The external_selector_value is a specifier to select a value from a flattened
external representation of an Entity (such as from idP/LDAP), and the
external_value is the value selected by the external_selector_value on that
diff --git a/docs/openapi/policy/unsafe/unsafe.openapi.yaml b/docs/openapi/policy/unsafe/unsafe.openapi.yaml
index 17c9652a41..279b0154a6 100644
--- a/docs/openapi/policy/unsafe/unsafe.openapi.yaml
+++ b/docs/openapi/policy/unsafe/unsafe.openapi.yaml
@@ -387,6 +387,9 @@ components:
- ALGORITHM_EC_P256
- ALGORITHM_EC_P384
- ALGORITHM_EC_P521
+ - ALGORITHM_HPQT_XWING
+ - ALGORITHM_HPQT_SECP256R1_MLKEM768
+ - ALGORITHM_HPQT_SECP384R1_MLKEM1024
description: Supported key algorithms.
policy.AttributeRuleTypeEnum:
type: string
@@ -413,6 +416,9 @@ components:
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1
- KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768
+ - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024
policy.KeyMode:
type: string
title: KeyMode
diff --git a/examples/cmd/decrypt.go b/examples/cmd/decrypt.go
index e66878ec9c..11279c7999 100644
--- a/examples/cmd/decrypt.go
+++ b/examples/cmd/decrypt.go
@@ -8,6 +8,7 @@ import (
"os"
"path/filepath"
+ "github.com/opentdf/platform/lib/ocrypto"
"github.com/opentdf/platform/sdk"
"github.com/spf13/cobra"
@@ -50,7 +51,7 @@ func decrypt(cmd *cobra.Command, args []string) error {
}
opts := []sdk.TDFReaderOption{}
if alg != "" {
- kt, err := keyTypeForKeyType(alg)
+ kt, err := ocrypto.ParseKeyType(alg)
if err != nil {
return err
}
@@ -79,7 +80,7 @@ func decrypt(cmd *cobra.Command, args []string) error {
opts := []sdk.TDFReaderOption{}
if alg != "" {
- kt, err := keyTypeForKeyType(alg)
+ kt, err := ocrypto.ParseKeyType(alg)
if err != nil {
return err
}
diff --git a/examples/cmd/encrypt.go b/examples/cmd/encrypt.go
index 4b8adc9eb4..d6574335e1 100644
--- a/examples/cmd/encrypt.go
+++ b/examples/cmd/encrypt.go
@@ -2,7 +2,6 @@ package cmd
import (
"encoding/json"
- "fmt"
"os"
"strings"
@@ -73,7 +72,7 @@ func encrypt(cmd *cobra.Command, args []string) error {
}))
}
if alg != "" {
- kt, err := keyTypeForKeyType(alg)
+ kt, err := ocrypto.ParseKeyType(alg)
if err != nil {
return err
}
@@ -91,15 +90,3 @@ func encrypt(cmd *cobra.Command, args []string) error {
cmd.Println(string(manifestJSON))
return nil
}
-
-func keyTypeForKeyType(alg string) (ocrypto.KeyType, error) {
- switch alg {
- case string(ocrypto.RSA2048Key):
- return ocrypto.RSA2048Key, nil
- case string(ocrypto.EC256Key):
- return ocrypto.EC256Key, nil
- default:
- // do not submit add ocrypto.UnknownKey
- return ocrypto.RSA2048Key, fmt.Errorf("unsupported key type [%s]", alg)
- }
-}
diff --git a/lib/ocrypto/BENCHMARK_REPORT.md b/lib/ocrypto/BENCHMARK_REPORT.md
new file mode 100644
index 0000000000..8c01339a51
--- /dev/null
+++ b/lib/ocrypto/BENCHMARK_REPORT.md
@@ -0,0 +1,168 @@
+# Benchmark Report: Hybrid Post-Quantum Key Wrapping Performance
+
+**Platform:** Apple M4, darwin/arm64, Go 1.25.9
+**Date:** 2026-04-29
+**Methodology:** `go test -bench=. -benchmem -count=5` (median of 5 runs)
+
+> **Note:** Wrap and unwrap benchmarks mirror the actual TDF code paths:
+> - **Wrap** follows `sdk/tdf.go` (`generateWrapKeyWithRSA`, `generateWrapKeyWithEC`, `generateWrapKeyWithHybrid`)
+> - **Unwrap** follows `service/internal/security/standard_crypto.go:Decrypt()`
+>
+> This includes PEM parsing, ephemeral keygen, ECDH, HKDF, AES-GCM, and ASN.1 marshaling — not simplified library-level `WrapDEK()` / `UnwrapDEK()` calls.
+
+## How to Run
+
+```bash
+# Full benchmark suite (use -count=5 for statistical significance)
+cd lib/ocrypto && go test -bench=. -benchmem -count=5 -timeout=10m
+
+# Quick single-count run
+cd lib/ocrypto && go test -bench=. -benchmem -count=1 -timeout=5m
+
+# Specific benchmark groups
+cd lib/ocrypto && go test -bench=BenchmarkKeyGeneration -benchmem
+cd lib/ocrypto && go test -bench=BenchmarkWrapDEK -benchmem
+cd lib/ocrypto && go test -bench=BenchmarkUnwrapDEK -benchmem
+cd lib/ocrypto && go test -bench=BenchmarkHybridSubOps -benchmem
+
+# Wrapped key size comparison table
+cd lib/ocrypto && go test -v -run TestWrappedKeySizeComparison
+```
+
+## Results
+
+### Key Generation
+
+| Scheme | Time | B/op | allocs/op | vs EC P-256 |
+|--------|-----:|-----:|----------:|-------------|
+| RSA-2048 | 47.7 ms | 652 KB | 5,929 | ~6,400x slower |
+| EC P-256 | 7.4 us | 984 B | 16 | baseline |
+| EC P-384 | 71.3 us | 1.2 KB | 19 | ~9.6x slower |
+| X-Wing | 43.8 us | 9.8 KB | 9 | ~5.9x slower |
+| P256+ML-KEM-768 | 34.8 us | 11.4 KB | 13 | ~4.7x slower |
+| P384+ML-KEM-1024 | 113.8 us | 17.9 KB | 16 | ~15x slower |
+
+**Takeaway:** RSA-2048 key generation is orders of magnitude slower than everything else (~48ms). All hybrid schemes generate keys in under 115us. EC P-256 is fastest at ~7us; EC P-384 keygen is ~10x slower than P-256 due to the larger field size.
+
+### Wrap DEK (32-byte AES-256 key)
+
+These benchmarks follow the exact TDF wrapping paths:
+- **RSA:** `FromPublicPEM` -> `Encrypt` (OAEP)
+- **EC:** `NewECKeyPair` -> `ComputeECDHKey` -> `CalculateHKDF` -> `AES-GCM Encrypt`
+- **Hybrid:** `PubKeyFromPem` -> `Encapsulate` -> `CalculateHKDF` -> `AES-GCM Encrypt` -> `ASN.1 Marshal`
+
+| Scheme | Time | Wrapped Size | B/op | allocs/op | vs EC P-256 |
+|--------|-----:|-------------:|-----:|----------:|-------------|
+| RSA-2048 | 25.5 us | 256 B | 4.1 KB | 33 | 0.5x (faster) |
+| EC P-256 | 54.5 us | 60 B | 12.0 KB | 158 | baseline |
+| EC P-384 | 449.3 us | 60 B | 14.3 KB | 189 | ~8.2x slower |
+| X-Wing | 77.4 us | 1,190 B | 16.4 KB | 42 | ~1.4x slower |
+| P256+ML-KEM-768 | 75.2 us | 1,223 B | 18.7 KB | 59 | ~1.4x slower |
+| P384+ML-KEM-1024 | 369.9 us | 1,735 B | 27.0 KB | 68 | ~6.8x slower |
+
+**Takeaway:** P256+ML-KEM-768 wrapping (~75us) is only ~1.4x slower than EC P-256 (~55us) — the ephemeral EC keygen + ECDH in the EC path narrows the gap significantly. RSA wrap is fastest since it's just OAEP padding. The two P-384-based schemes are the slowest (EC P-384 ~449us, P384+ML-KEM-1024 ~370us) — the P-384 ECDH operation alone dominates EC P-384's wrap cost since each call re-generates an ephemeral key.
+
+### Unwrap DEK
+
+These benchmarks follow the KAS unwrap paths:
+- **RSA:** pre-loaded `AsymDecryption.Decrypt` (key parsed at startup)
+- **EC:** `NewSaltedECDecryptor(cachedKey, TDFSalt)` -> `DecryptWithEphemeralKey`
+- **Hybrid:** `PrivateKeyFromPem` -> `UnwrapDEK` (PEM parsed each call)
+
+| Scheme | Time | B/op | allocs/op | vs EC P-256 |
+|--------|-----:|-----:|----------:|-------------|
+| RSA-2048 | 737.3 us | 560 B | 8 | ~26x slower |
+| EC P-256 | 28.4 us | 4.1 KB | 40 | baseline |
+| EC P-384 | 230.5 us | 4.6 KB | 55 | ~8.1x slower |
+| X-Wing | 90.4 us | 12.4 KB | 37 | ~3.2x slower |
+| P256+ML-KEM-768 | 96.3 us | 13.8 KB | 51 | ~3.4x slower |
+| P384+ML-KEM-1024 | 400.2 us | 20.1 KB | 60 | ~14x slower |
+
+**Takeaway:** RSA unwrap is the slowest operation in the entire suite (~737us) due to private key exponentiation. P256+ML-KEM-768 unwraps in ~96us — fast enough for real-time use. EC P-384 unwrap (~231us) is ~8x slower than P-256 because of the more expensive curve operations. Hybrid unwraps include PEM parsing overhead that could be optimized by caching parsed keys (as EC already does).
+
+### Wrap + Unwrap Round-Trip Summary
+
+| Scheme | Wrap + Unwrap | Quantum Safe? |
+|--------|-------------:|:-------------:|
+| RSA-2048 | 763 us | No |
+| EC P-256 | 83 us | No |
+| EC P-384 | 680 us | No |
+| X-Wing | 168 us | Yes |
+| P256+ML-KEM-768 | 172 us | Yes |
+| P384+ML-KEM-1024 | 770 us | Yes |
+
+## Analysis: Where Time Is Spent
+
+The `BenchmarkHybridSubOps` benchmarks break down hybrid wrap operations into their constituent parts:
+
+### X-Wing Sub-Operations
+
+| Operation | Time | % of Wrap |
+|-----------|-----:|----------:|
+| Encapsulate (X25519 + ML-KEM-768) | 71.6 us | 92.5% |
+| HKDF key derivation | 0.49 us | 0.6% |
+| AES-GCM encrypt (32B DEK) | 0.37 us | 0.5% |
+| ASN.1 marshal | 0.52 us | 0.7% |
+| PEM parsing + overhead | ~4.4 us | 5.7% |
+
+### P256+ML-KEM-768 Sub-Operations
+
+| Operation | Time | % of Wrap |
+|-----------|-----:|----------:|
+| Encapsulate (ECDH P-256 + ML-KEM-768) | 70.0 us | 93.1% |
+| HKDF key derivation | 0.51 us | 0.7% |
+| AES-GCM encrypt (32B DEK) | 0.37 us | 0.5% |
+| ASN.1 marshal | 0.51 us | 0.7% |
+| PEM parsing + overhead | ~3.8 us | 5.1% |
+
+### P384+ML-KEM-1024 Sub-Operations
+
+| Operation | Time | % of Wrap |
+|-----------|-----:|----------:|
+| Encapsulate (ECDH P-384 + ML-KEM-1024) | 359.9 us | 97.3% |
+| HKDF key derivation | 0.51 us | 0.1% |
+| AES-GCM encrypt (32B DEK) | 0.37 us | 0.1% |
+| ASN.1 marshal | 0.54 us | 0.1% |
+| PEM parsing + overhead | ~8.6 us | 2.3% |
+
+**Conclusion:** KEM encapsulation dominates all hybrid schemes at 93-97% of total time. HKDF, AES-GCM, and ASN.1 marshaling are all sub-microsecond and negligible. The P-384 elliptic curve ECDH is ~5x slower than P-256, which is why P384+ML-KEM-1024 is significantly slower than P256+ML-KEM-768.
+
+## Manifest Size Impact
+
+| Scheme | Wrapped Key | Public Key (PEM) | Base64 Wrapped | Notes |
+|--------|------------:|-----------------:|---------------:|-------|
+| RSA-2048 | 256 B | 451 B | 344 B | No ephemeral key in manifest |
+| EC P-256 | 60 B | 178 B | 80 B | + ephemeral key (91 B) in manifest |
+| EC P-384 | 60 B | 215 B | 80 B | + ephemeral key (120 B) in manifest |
+| X-Wing | 1,190 B | 1,714 B | 1,588 B | All in single ASN.1 blob |
+| P256+ML-KEM-768 | 1,223 B | 1,785 B | 1,632 B | All in single ASN.1 blob |
+| P384+ML-KEM-1024 | 1,735 B | 2,347 B | 2,316 B | All in single ASN.1 blob |
+
+> Base64 overhead = ceil(raw_bytes * 4/3). TDF manifests store wrapped keys as base64.
+
+Hybrid schemes produce wrapped keys that are ~20x larger than EC P-256 (1.2-1.7 KB vs 60 B). For a TDF with a single recipient, this adds ~1-2 KB to the manifest. For multi-recipient TDFs, the overhead scales linearly per recipient.
+
+## Trade-offs Summary
+
+| Concern | RSA-2048 | EC P-256 | EC P-384 | X-Wing | P256+ML-KEM-768 | P384+ML-KEM-1024 |
+|---------|----------|----------|----------|--------|-----------------|-------------------|
+| Quantum resistance | None | None | None | Yes (hybrid) | Yes (hybrid) | Yes (hybrid) |
+| Key generation | 48 ms (slow) | 7.4 us (fastest) | 71 us | 44 us | 35 us | 114 us |
+| Wrap latency | 26 us | 55 us | 449 us | 77 us | 75 us | 370 us |
+| Unwrap latency | 737 us (slow) | 28 us | 231 us | 90 us | 96 us | 400 us |
+| Round-trip | 763 us | 83 us | 680 us | 168 us | 172 us | 770 us |
+| Wrapped key size | 256 B | 60 B | 60 B | 1,190 B | 1,223 B | 1,735 B |
+| Standards basis | PKCS#1 | ECIES | ECIES | IETF draft | NIST SP 800-227 | NIST SP 800-227 |
+
+### Recommendations
+
+- **P256+ML-KEM-768** is the best all-around hybrid choice: NIST-standardized, fastest hybrid round-trip (~172us), and moderate size overhead (1.2 KB wrapped keys). Only ~1.4x slower than EC P-256 for wrapping.
+- **P384+ML-KEM-1024** provides a higher classical security level (Cat 3 classical / Cat 5 PQ) at the cost of ~4-5x more latency. Use when policy requires P-384 or equivalent classical strength.
+- **X-Wing** offers a simpler construction (X25519 + ML-KEM-768) but is based on an IETF draft rather than a NIST standard. Performance is comparable to P256+ML-KEM-768.
+- **EC P-256** remains the fastest and smallest option for environments where quantum resistance is not yet required.
+- **EC P-384** is significantly more expensive than P-256 (~8-10x for both wrap and unwrap) without quantum protection — prefer P384+ML-KEM-1024 if the latency budget already covers P-384, since it adds PQ resistance for similar cost.
+- **RSA-2048** has the worst unwrap performance (~737us) and should be considered legacy.
+
+### Optimization Opportunities
+
+- **Hybrid unwrap PEM caching:** The KAS currently parses hybrid private key PEM on every unwrap call. Caching the parsed key (as EC already does) would save ~5-10us per unwrap.
diff --git a/lib/ocrypto/HYBRID_NIST_KEY_WRAPPING.md b/lib/ocrypto/HYBRID_NIST_KEY_WRAPPING.md
new file mode 100644
index 0000000000..e6cbf4fe74
--- /dev/null
+++ b/lib/ocrypto/HYBRID_NIST_KEY_WRAPPING.md
@@ -0,0 +1,308 @@
+# NIST EC + ML-KEM Hybrid Key Wrapping
+
+## Overview
+
+This document describes the hybrid post-quantum key wrapping scheme used in TDF (Trusted Data Format) that combines classical elliptic curve cryptography (ECDH) with post-quantum lattice-based cryptography (ML-KEM) to protect data encryption keys (split keys).
+
+Two variants are supported. Hybrid security is bounded by the stronger of the two underlying primitives against each adversary class, so the post-quantum strength is set by ML-KEM and the classical strength is set by ECDH.
+
+| Variant | Classical (ECDH) | Post-quantum (ML-KEM) |
+|---------|------------------|-----------------------|
+| P-256 + ML-KEM-768 | NIST Category 1 | NIST Category 3 |
+| P-384 + ML-KEM-1024 | NIST Category 3 | NIST Category 5 |
+
+References:
+- NIST PQC Call for Proposals §4.A.5 (category definitions): https://csrc.nist.gov/csrc/media/projects/post-quantum-cryptography/documents/call-for-proposals-final-dec-2016.pdf
+- FIPS 203 (ML-KEM-768 = Cat 3, ML-KEM-1024 = Cat 5): https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.203.pdf
+- NIST SP 800-57 Part 1 Rev. 5 Table 2 (P-256 = 128-bit ≈ Cat 1, P-384 = 192-bit ≈ Cat 3): https://nvlpubs.nist.gov/nistpubs/specialpublications/nist.sp.800-57pt1r5.pdf
+
+Core implementation: `lib/ocrypto/hybrid_nist.go`
+
+## Key Format
+
+### Combined Public Key
+
+The KAS (Key Access Server) hosts a combined public key in PEM format. The raw bytes inside the PEM are a simple concatenation of the EC and ML-KEM public keys:
+
+```
+[ EC Public Key (uncompressed point) | ML-KEM Public Key ]
+```
+
+| Variant | EC Public Key Size | ML-KEM Public Key Size | Combined Size | PEM Block Type |
+|---------|-------------------|----------------------|---------------|----------------|
+| P-256 + ML-KEM-768 | 65 bytes | 1184 bytes | 1249 bytes | `SECP256R1 MLKEM768 PUBLIC KEY` |
+| P-384 + ML-KEM-1024 | 97 bytes | 1568 bytes | 1665 bytes | `SECP384R1 MLKEM1024 PUBLIC KEY` |
+
+### Combined Private Key
+
+The KAS holds a combined private key, also a concatenation:
+
+```
+[ EC Private Key (raw scalar) | ML-KEM Private Key ]
+```
+
+The ML-KEM portion is stored in the 64-byte seed form (`d || z`) defined by FIPS 203 §7.1, not the expanded ~2400/3168-byte decapsulation key. The seed is what `crypto/mlkem` (Go 1.25) emits via `Bytes()` and consumes via `NewDecapsulationKey768` / `NewDecapsulationKey1024`. The constant `mlkemSeedSize = 64` in `hybrid_nist.go` and the size checks in `decodeSizedPEMBlock` enforce this layout.
+
+| Variant | EC Private Key Size | ML-KEM Seed Size | Combined Size | PEM Block Type |
+|---------|--------------------|------------------|---------------|----------------|
+| P-256 + ML-KEM-768 | 32 bytes | 64 bytes | 96 bytes | `SECP256R1 MLKEM768 PRIVATE KEY` |
+| P-384 + ML-KEM-1024 | 48 bytes | 64 bytes | 112 bytes | `SECP384R1 MLKEM1024 PRIVATE KEY` |
+
+### How the Client Obtains the Public Key
+
+The client obtains the combined public key from KAS in one of two ways:
+
+1. **Fetched at runtime (autoconfigure)**: The SDK calls the KAS `PublicKey` gRPC endpoint, specifying the hybrid algorithm. KAS returns the combined PEM. The response is cached for 5 minutes.
+2. **Provided manually**: The caller supplies the public key PEM via `WithKasInformation(...)` when configuring the SDK.
+
+## Wrap (Encrypt the Split Key)
+
+Function: `hybridNISTWrapDEK` (`hybrid_nist.go`, line 339)
+
+This is performed on the **client side** during TDF encryption.
+
+### Step 1 - Split the Public Key
+
+The combined public key bytes are split at the known EC public key size boundary:
+
+```
+ecPubBytes = publicKeyRaw[:ecPubSize] // 65 bytes for P-256, 97 bytes for P-384
+mlkemPubBytes = publicKeyRaw[ecPubSize:] // 1184 bytes for ML-KEM-768, 1568 bytes for ML-KEM-1024
+```
+
+### Step 2 - ECDH (Classical Key Agreement)
+
+An ephemeral EC key pair is generated on the same curve as the KAS static key:
+
+1. Generate ephemeral EC key pair: `ephemeral_private, ephemeral_public`
+2. Compute ECDH shared secret: `ecdhSecret = ECDH(ephemeral_private, KAS_ec_public)`
+3. Retain `ephemeral_public` bytes for inclusion in the output
+
+This is a standard elliptic curve Diffie-Hellman operation. The ephemeral key provides forward secrecy - even if the KAS static key is later compromised, past wrapped keys remain protected.
+
+### Step 3 - ML-KEM Encapsulate (Post-Quantum KEM)
+
+ML-KEM (Module Lattice-based Key Encapsulation Mechanism, formerly known as Kyber) is a KEM, not a key exchange. The encapsulation operation takes only the public key and produces two outputs:
+
+```
+(mlkemSecret, mlkemCiphertext) = ML-KEM.Encapsulate(KAS_mlkem_public)
+```
+
+- `mlkemSecret` (32 bytes): A shared secret known to the encapsulator
+- `mlkemCiphertext` (1088 bytes for ML-KEM-768, 1568 bytes for ML-KEM-1024): An opaque ciphertext that only the ML-KEM private key holder can decapsulate to recover the same shared secret
+
+Internally, `EncapsulateTo`:
+1. Generates random coins (entropy)
+2. Uses the ML-KEM public key (a matrix over a polynomial ring) to encrypt those random coins into the ciphertext
+3. Derives the shared secret from both the random coins and the ciphertext
+
+No ephemeral ML-KEM key pair is generated by the client. The ciphertext itself serves as the "ephemeral" artifact sent to KAS.
+
+### Step 4 - Combine Secrets
+
+The two shared secrets from the classical and post-quantum operations are concatenated:
+
+```
+combinedSecret = ecdhSecret || mlkemSecret
+```
+
+This is a simple byte concatenation. The security property is that an attacker must break **both** ECDH and ML-KEM to recover the combined secret. If quantum computers break ECDH but ML-KEM remains secure, the combined secret is still protected (and vice versa).
+
+### Step 5 - Key Derivation (HKDF)
+
+A 32-byte AES-256 wrapping key is derived from the combined secret using HKDF-SHA256:
+
+```
+wrapKey = HKDF-SHA256(
+ IKM: combinedSecret, // ecdhSecret || mlkemSecret
+ salt: SHA256("TDF"), // 32-byte fixed salt
+ info: // empty by default
+) -> 32 bytes
+```
+
+Function: `deriveHybridNISTWrapKey` (`hybrid_nist.go`, line 479)
+
+The salt is a hardcoded SHA-256 digest of the ASCII string `"TDF"`, shared across all TDF key wrapping schemes.
+
+### Step 6 - AES-GCM Encrypt the Split Key
+
+The split key (the actual data encryption key shard) is encrypted using AES-256-GCM:
+
+```
+encryptedDEK = AES-256-GCM.Encrypt(key=wrapKey, plaintext=splitKey)
+```
+
+The output format is:
+
+```
+[ nonce (12 bytes) | ciphertext | authentication tag (16 bytes) ]
+```
+
+The nonce is randomly generated. The authentication tag provides integrity verification.
+
+### Step 7 - Package as ASN.1 DER
+
+The ephemeral EC public key, ML-KEM ciphertext, and encrypted DEK are packaged into an ASN.1 DER structure:
+
+```asn1
+HybridNISTWrappedKey ::= SEQUENCE {
+ hybridCiphertext [0] OCTET STRING, -- ephemeralECPub || mlkemCiphertext
+ encryptedDEK [1] OCTET STRING -- AES-GCM nonce + ciphertext + tag
+}
+```
+
+Where `hybridCiphertext` is:
+
+```
+[ ephemeral EC public key (65 or 97 bytes) | ML-KEM ciphertext (1088 or 1568 bytes) ]
+```
+
+| Variant | Ephemeral EC Pub | ML-KEM Ciphertext | hybridCiphertext Size |
+|---------|-----------------|-------------------|----------------------|
+| P-256 + ML-KEM-768 | 65 bytes | 1088 bytes | 1153 bytes |
+| P-384 + ML-KEM-1024 | 97 bytes | 1568 bytes | 1665 bytes |
+
+This DER blob is then base64-encoded and stored as the `wrappedKey` field in the TDF manifest's Key Access Object, with `keyType` set to `"hybrid-wrapped"`.
+
+## Unwrap (Decrypt the Split Key)
+
+Function: `hybridNISTUnwrapDEK` (`hybrid_nist.go`, line 406)
+
+This is performed on the **KAS server side** when a client sends a rewrap request. KAS holds the combined private key on disk, loaded at startup.
+
+### Step 1 - Parse the ASN.1 DER
+
+The base64-decoded DER blob is unmarshalled:
+
+```
+ASN.1 Unmarshal -> HybridNISTWrappedKey {
+ hybridCiphertext: [ephemeralECPub | mlkemCiphertext]
+ encryptedDEK: [nonce | ciphertext | tag]
+}
+```
+
+### Step 2 - Split the Hybrid Ciphertext
+
+The `hybridCiphertext` is split at the known EC public key size boundary:
+
+```
+ephemeralECPub = hybridCiphertext[:ecPubSize] // 65 or 97 bytes
+mlkemCiphertext = hybridCiphertext[ecPubSize:] // 1088 or 1568 bytes
+```
+
+### Step 3 - Split the Private Key
+
+The combined private key is split at the known EC private key size boundary:
+
+```
+ecPrivBytes = privateKeyRaw[:ecPrivSize] // 32 or 48 bytes
+mlkemPrivBytes = privateKeyRaw[ecPrivSize:] // 2400 or 3168 bytes
+```
+
+### Step 4 - ECDH (Reconstruct Classical Shared Secret)
+
+KAS uses its static EC private key with the client's ephemeral EC public key:
+
+```
+ecdhSecret = ECDH(KAS_ec_private, ephemeral_ec_public)
+```
+
+This produces the same `ecdhSecret` that the client computed in Wrap Step 2, because `ECDH(a, g^b) == ECDH(b, g^a)`.
+
+### Step 5 - ML-KEM Decapsulate (Reconstruct Post-Quantum Shared Secret)
+
+KAS uses its ML-KEM private key to decapsulate the ciphertext:
+
+```
+mlkemSecret = ML-KEM.Decapsulate(KAS_mlkem_private, mlkemCiphertext)
+```
+
+The ML-KEM private key contains the secret trapdoor in the lattice structure. Decapsulation recovers the random coins from the ciphertext and derives the same 32-byte shared secret that the client obtained during encapsulation.
+
+### Step 6 - Combine Secrets
+
+Identical to Wrap Step 4:
+
+```
+combinedSecret = ecdhSecret || mlkemSecret
+```
+
+### Step 7 - Key Derivation (HKDF)
+
+Identical to Wrap Step 5:
+
+```
+wrapKey = HKDF-SHA256(
+ IKM: combinedSecret,
+ salt: SHA256("TDF"),
+ info:
+) -> 32 bytes
+```
+
+Both sides derive the same `wrapKey` because both sides have the same `ecdhSecret` and `mlkemSecret`.
+
+### Step 8 - AES-GCM Decrypt the Split Key
+
+```
+splitKey = AES-256-GCM.Decrypt(key=wrapKey, ciphertext=encryptedDEK)
+```
+
+The AES-GCM decryption verifies the authentication tag. If the tag does not match (indicating tampering or a wrong key), decryption fails.
+
+KAS now has the original split key. It enforces policy checks, and if the requesting client is authorized, returns the split key (protected by the session transport layer).
+
+## Security Properties
+
+### Hybrid Security Guarantee
+
+The combined secret is derived from both ECDH and ML-KEM. An attacker must break **both** to recover the wrap key:
+
+- If a quantum computer breaks ECDH (recovers `ecdhSecret` from the ephemeral public key), ML-KEM still protects the combined secret
+- If a classical vulnerability is found in ML-KEM (recovers `mlkemSecret` from the ciphertext), ECDH still protects the combined secret
+
+### Forward Secrecy
+
+- The ephemeral EC key pair is generated fresh for each wrap operation, providing forward secrecy on the classical side
+- ML-KEM encapsulation generates fresh randomness for each operation, providing forward secrecy on the post-quantum side
+
+### What is NOT in the Combiner
+
+Unlike the X-Wing KEM (which uses SHA3-256 and mixes in public keys, ciphertexts, and a domain label), this NIST hybrid implementation:
+
+- Does **not** mix the ephemeral EC public key into the KDF
+- Does **not** mix the ML-KEM ciphertext into the KDF
+- Does **not** mix the static public keys into the KDF
+- Does **not** use a domain separation label in the HKDF info
+
+The two raw shared secrets are simply concatenated and passed through HKDF. This is a common and accepted pattern for hybrid KEM composition, though it provides less identity binding than the X-Wing combiner approach.
+
+## Comparison with Other Key Wrapping Schemes in TDF
+
+| Aspect | RSA (`wrapped`) | EC (`ec-wrapped`) | NIST Hybrid (`hybrid-wrapped`) | X-Wing (`hybrid-wrapped`) |
+|--------|-----------------|-------------------|-------------------------------|--------------------------|
+| Classical | RSA-2048 | ECDH (P-256/384/521) | ECDH (P-256/384) | X25519 |
+| Post-Quantum | None | None | ML-KEM-768/1024 | ML-KEM-768 |
+| Combiner | N/A | HKDF only | Concatenation + HKDF | SHA3-256 (spec-defined, inside circl library) + HKDF |
+| Output Format | Base64(RSA ciphertext) | Base64(AES-GCM ciphertext) | Base64(ASN.1 DER) | Base64(ASN.1 DER) |
+| Ephemeral Key | None | EC ephemeral | EC ephemeral + ML-KEM ciphertext | X-Wing ciphertext (contains both) |
+| Identity Binding | N/A | No | No | Yes (public keys mixed into SHA3-256) |
+
+## Manifest Example
+
+A Key Access Object in the TDF manifest for hybrid wrapping:
+
+```json
+{
+ "type": "hybrid-wrapped",
+ "url": "https://kas.example.com",
+ "kid": "hybrid-key-1",
+ "sid": "split-1",
+ "wrappedKey": "",
+ "policyBinding": {
+ "alg": "HS256",
+ "hash": ""
+ }
+}
+```
+
+Note: Unlike `ec-wrapped`, the `ephemeralPublicKey` field is **not** used in the manifest for hybrid wrapping. The ephemeral EC public key is embedded inside the `wrappedKey` ASN.1 structure alongside the ML-KEM ciphertext.
diff --git a/lib/ocrypto/asym_decryption.go b/lib/ocrypto/asym_decryption.go
index 426f859723..fdd7bb5005 100644
--- a/lib/ocrypto/asym_decryption.go
+++ b/lib/ocrypto/asym_decryption.go
@@ -43,6 +43,14 @@ func FromPrivatePEMWithSalt(privateKeyInPem string, salt, info []byte) (PrivateK
if block == nil {
return AsymDecryption{}, errors.New("failed to parse PEM formatted private key")
}
+ switch block.Type {
+ case PEMBlockXWingPrivateKey:
+ return NewSaltedXWingDecryptor(block.Bytes, salt, info)
+ case PEMBlockP256MLKEM768PrivateKey:
+ return NewSaltedP256MLKEM768Decryptor(block.Bytes, salt, info)
+ case PEMBlockP384MLKEM1024PrivateKey:
+ return NewSaltedP384MLKEM1024Decryptor(block.Bytes, salt, info)
+ }
priv, err := x509.ParsePKCS8PrivateKey(block.Bytes)
switch {
diff --git a/lib/ocrypto/asym_encryption.go b/lib/ocrypto/asym_encryption.go
index c44aa64cce..7e08199509 100644
--- a/lib/ocrypto/asym_encryption.go
+++ b/lib/ocrypto/asym_encryption.go
@@ -23,8 +23,9 @@ import (
type SchemeType string
const (
- RSA SchemeType = "wrapped"
- EC SchemeType = "ec-wrapped"
+ RSA SchemeType = "wrapped"
+ EC SchemeType = "ec-wrapped"
+ Hybrid SchemeType = "hybrid-wrapped"
)
type PublicKeyEncryptor interface {
@@ -69,6 +70,19 @@ func FromPublicPEM(publicKeyInPem string) (PublicKeyEncryptor, error) {
}
func FromPublicPEMWithSalt(publicKeyInPem string, salt, info []byte) (PublicKeyEncryptor, error) {
+ block, _ := pem.Decode([]byte(publicKeyInPem))
+ if block == nil {
+ return nil, errors.New("failed to parse PEM formatted public key")
+ }
+ switch block.Type {
+ case PEMBlockXWingPublicKey:
+ return NewXWingEncryptor(block.Bytes, salt, info)
+ case PEMBlockP256MLKEM768PublicKey:
+ return NewP256MLKEM768Encryptor(block.Bytes, salt, info)
+ case PEMBlockP384MLKEM1024PublicKey:
+ return NewP384MLKEM1024Encryptor(block.Bytes, salt, info)
+ }
+
pub, err := getPublicPart(publicKeyInPem)
if err != nil {
return nil, err
diff --git a/lib/ocrypto/benchmark_test.go b/lib/ocrypto/benchmark_test.go
new file mode 100644
index 0000000000..b86b3075e7
--- /dev/null
+++ b/lib/ocrypto/benchmark_test.go
@@ -0,0 +1,816 @@
+package ocrypto
+
+import (
+ "crypto/sha256"
+ "crypto/x509"
+ "encoding/asn1"
+ "fmt"
+ "testing"
+)
+
+// Sink variables to prevent compiler from optimizing away results.
+var (
+ sinkBytes []byte
+ errSink error
+)
+
+// testDEK is a 32-byte AES-256 key used as the payload for wrap/unwrap benchmarks.
+var testDEK = []byte("0123456789abcdef0123456789abcdef")
+
+func BenchmarkKeyGeneration(b *testing.B) {
+ b.Run("RSA-2048", func(b *testing.B) {
+ for b.Loop() {
+ _, errSink = NewRSAKeyPair(2048)
+ }
+ })
+ b.Run("EC-P256", func(b *testing.B) {
+ for b.Loop() {
+ _, errSink = NewECKeyPair(ECCModeSecp256r1)
+ }
+ })
+ b.Run("EC-P384", func(b *testing.B) {
+ for b.Loop() {
+ _, errSink = NewECKeyPair(ECCModeSecp384r1)
+ }
+ })
+ b.Run("XWing", func(b *testing.B) {
+ for b.Loop() {
+ _, errSink = NewXWingKeyPair()
+ }
+ })
+ b.Run("P256_MLKEM768", func(b *testing.B) {
+ for b.Loop() {
+ _, errSink = NewP256MLKEM768KeyPair()
+ }
+ })
+ b.Run("P384_MLKEM1024", func(b *testing.B) {
+ for b.Loop() {
+ _, errSink = NewP384MLKEM1024KeyPair()
+ }
+ })
+}
+
+// benchTDFSalt matches tdf.go:tdfSalt() — SHA-256("TDF").
+func benchTDFSalt() []byte {
+ digest := sha256.New()
+ digest.Write([]byte("TDF"))
+ return digest.Sum(nil)
+}
+
+// BenchmarkWrapDEK mirrors the actual TDF key-wrapping paths in sdk/tdf.go:
+// - RSA: FromPublicPEM -> Encrypt (generateWrapKeyWithRSA)
+// - EC: NewECKeyPair -> ComputeECDHKey -> HKDF -> AES-GCM (generateWrapKeyWithEC)
+// - Hybrid: PubKeyFromPem -> Encapsulate -> HKDF -> AES-GCM -> ASN.1 (generateWrapKeyWithHybrid)
+func BenchmarkWrapDEK(b *testing.B) {
+ salt := benchTDFSalt()
+
+ // RSA-2048: setup KAS public key
+ rsaKP, err := NewRSAKeyPair(2048)
+ if err != nil {
+ b.Fatal(err)
+ }
+ rsaPubPEM, err := rsaKP.PublicKeyInPemFormat()
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ // EC P-256: setup KAS public key PEM
+ ecKP, err := NewECKeyPair(ECCModeSecp256r1)
+ if err != nil {
+ b.Fatal(err)
+ }
+ ecKASPubPEM, err := ecKP.PublicKeyInPemFormat()
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ // EC P-384: setup KAS public key PEM
+ ec384KP, err := NewECKeyPair(ECCModeSecp384r1)
+ if err != nil {
+ b.Fatal(err)
+ }
+ ec384KASPubPEM, err := ec384KP.PublicKeyInPemFormat()
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ // X-Wing: setup KAS public key PEM
+ xwingKP, err := NewXWingKeyPair()
+ if err != nil {
+ b.Fatal(err)
+ }
+ xwingPubPEM, err := xwingKP.PublicKeyInPemFormat()
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ // P256+MLKEM768: setup KAS public key PEM
+ p256KP, err := NewP256MLKEM768KeyPair()
+ if err != nil {
+ b.Fatal(err)
+ }
+ p256PubPEM, err := p256KP.PublicKeyInPemFormat()
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ // P384+MLKEM1024: setup KAS public key PEM
+ p384KP, err := NewP384MLKEM1024KeyPair()
+ if err != nil {
+ b.Fatal(err)
+ }
+ p384PubPEM, err := p384KP.PublicKeyInPemFormat()
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ // RSA: tdf.go calls FromPublicPEM -> Encrypt
+ b.Run("RSA-2048", func(b *testing.B) {
+ for b.Loop() {
+ enc, err := FromPublicPEM(rsaPubPEM)
+ if err != nil {
+ b.Fatal(err)
+ }
+ sinkBytes, errSink = enc.Encrypt(testDEK)
+ }
+ b.ReportMetric(float64(len(sinkBytes)), "wrapped-bytes")
+ })
+
+ // EC: tdf.go generates ephemeral EC keypair, computes ECDH, derives via HKDF, AES-GCM wraps
+ b.Run("EC-P256", func(b *testing.B) {
+ for b.Loop() {
+ ephKP, err := NewECKeyPair(ECCModeSecp256r1)
+ if err != nil {
+ b.Fatal(err)
+ }
+ ephPrivPEM, err := ephKP.PrivateKeyInPemFormat()
+ if err != nil {
+ b.Fatal(err)
+ }
+ ecdhKey, err := ComputeECDHKey([]byte(ephPrivPEM), []byte(ecKASPubPEM))
+ if err != nil {
+ b.Fatal(err)
+ }
+ sessionKey, err := CalculateHKDF(salt, ecdhKey)
+ if err != nil {
+ b.Fatal(err)
+ }
+ gcm, err := NewAESGcm(sessionKey)
+ if err != nil {
+ b.Fatal(err)
+ }
+ sinkBytes, errSink = gcm.Encrypt(testDEK)
+ }
+ b.ReportMetric(float64(len(sinkBytes)), "wrapped-bytes")
+ })
+
+ b.Run("EC-P384", func(b *testing.B) {
+ for b.Loop() {
+ ephKP, err := NewECKeyPair(ECCModeSecp384r1)
+ if err != nil {
+ b.Fatal(err)
+ }
+ ephPrivPEM, err := ephKP.PrivateKeyInPemFormat()
+ if err != nil {
+ b.Fatal(err)
+ }
+ ecdhKey, err := ComputeECDHKey([]byte(ephPrivPEM), []byte(ec384KASPubPEM))
+ if err != nil {
+ b.Fatal(err)
+ }
+ sessionKey, err := CalculateHKDF(salt, ecdhKey)
+ if err != nil {
+ b.Fatal(err)
+ }
+ gcm, err := NewAESGcm(sessionKey)
+ if err != nil {
+ b.Fatal(err)
+ }
+ sinkBytes, errSink = gcm.Encrypt(testDEK)
+ }
+ b.ReportMetric(float64(len(sinkBytes)), "wrapped-bytes")
+ })
+
+ // X-Wing: tdf.go parses PEM, calls Encapsulate, HKDF, AES-GCM, then ASN.1 marshal
+ b.Run("XWing", func(b *testing.B) {
+ for b.Loop() {
+ pubKey, err := XWingPubKeyFromPem([]byte(xwingPubPEM))
+ if err != nil {
+ b.Fatal(err)
+ }
+ ss, ct, err := XWingEncapsulate(pubKey)
+ if err != nil {
+ b.Fatal(err)
+ }
+ wrapKey, err := CalculateHKDF(salt, ss)
+ if err != nil {
+ b.Fatal(err)
+ }
+ gcm, err := NewAESGcm(wrapKey)
+ if err != nil {
+ b.Fatal(err)
+ }
+ encDEK, err := gcm.Encrypt(testDEK)
+ if err != nil {
+ b.Fatal(err)
+ }
+ sinkBytes, errSink = asn1.Marshal(HybridNISTWrappedKey{
+ HybridCiphertext: ct,
+ EncryptedDEK: encDEK,
+ })
+ }
+ b.ReportMetric(float64(len(sinkBytes)), "wrapped-bytes")
+ })
+
+ // P256+MLKEM768: same flow as X-Wing with different Encapsulate/PEM parse
+ b.Run("P256_MLKEM768", func(b *testing.B) {
+ for b.Loop() {
+ pubKey, err := P256MLKEM768PubKeyFromPem([]byte(p256PubPEM))
+ if err != nil {
+ b.Fatal(err)
+ }
+ ss, ct, err := P256MLKEM768Encapsulate(pubKey)
+ if err != nil {
+ b.Fatal(err)
+ }
+ wrapKey, err := CalculateHKDF(salt, ss)
+ if err != nil {
+ b.Fatal(err)
+ }
+ gcm, err := NewAESGcm(wrapKey)
+ if err != nil {
+ b.Fatal(err)
+ }
+ encDEK, err := gcm.Encrypt(testDEK)
+ if err != nil {
+ b.Fatal(err)
+ }
+ sinkBytes, errSink = asn1.Marshal(HybridNISTWrappedKey{
+ HybridCiphertext: ct,
+ EncryptedDEK: encDEK,
+ })
+ }
+ b.ReportMetric(float64(len(sinkBytes)), "wrapped-bytes")
+ })
+
+ // P384+MLKEM1024: same flow with P384 variant
+ b.Run("P384_MLKEM1024", func(b *testing.B) {
+ for b.Loop() {
+ pubKey, err := P384MLKEM1024PubKeyFromPem([]byte(p384PubPEM))
+ if err != nil {
+ b.Fatal(err)
+ }
+ ss, ct, err := P384MLKEM1024Encapsulate(pubKey)
+ if err != nil {
+ b.Fatal(err)
+ }
+ wrapKey, err := CalculateHKDF(salt, ss)
+ if err != nil {
+ b.Fatal(err)
+ }
+ gcm, err := NewAESGcm(wrapKey)
+ if err != nil {
+ b.Fatal(err)
+ }
+ encDEK, err := gcm.Encrypt(testDEK)
+ if err != nil {
+ b.Fatal(err)
+ }
+ sinkBytes, errSink = asn1.Marshal(HybridNISTWrappedKey{
+ HybridCiphertext: ct,
+ EncryptedDEK: encDEK,
+ })
+ }
+ b.ReportMetric(float64(len(sinkBytes)), "wrapped-bytes")
+ })
+}
+
+// BenchmarkUnwrapDEK mirrors the actual KAS unwrap paths in
+// service/internal/security/standard_crypto.go:Decrypt():
+// - RSA: pre-loaded AsymDecryption.Decrypt (key already parsed)
+// - EC: ECPrivateKeyFromPem (cached) -> NewSaltedECDecryptor(TDFSalt) -> DecryptWithEphemeralKey
+// - Hybrid: PrivateKeyFromPem -> UnwrapDEK (PEM parsed each time in current KAS code)
+func BenchmarkUnwrapDEK(b *testing.B) {
+ salt := benchTDFSalt()
+
+ // RSA-2048: KAS pre-loads the AsymDecryption at startup
+ rsaKP, err := NewRSAKeyPair(2048)
+ if err != nil {
+ b.Fatal(err)
+ }
+ rsaPubPEM, err := rsaKP.PublicKeyInPemFormat()
+ if err != nil {
+ b.Fatal(err)
+ }
+ rsaPrivPEM, err := rsaKP.PrivateKeyInPemFormat()
+ if err != nil {
+ b.Fatal(err)
+ }
+ rsaEnc, err := NewAsymEncryption(rsaPubPEM)
+ if err != nil {
+ b.Fatal(err)
+ }
+ rsaWrapped, err := rsaEnc.Encrypt(testDEK)
+ if err != nil {
+ b.Fatal(err)
+ }
+ rsaDec, err := NewAsymDecryption(rsaPrivPEM)
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ // EC P-256: KAS caches the parsed private key, creates decryptor per request
+ ecKASKP, err := NewECKeyPair(ECCModeSecp256r1)
+ if err != nil {
+ b.Fatal(err)
+ }
+ ecKASPubPEM, err := ecKASKP.PublicKeyInPemFormat()
+ if err != nil {
+ b.Fatal(err)
+ }
+ ecKASPrivPEM, err := ecKASKP.PrivateKeyInPemFormat()
+ if err != nil {
+ b.Fatal(err)
+ }
+ // Wrap using the TDF path: ephemeral keygen + ECDH + HKDF + AES-GCM
+ ecEphKP, err := NewECKeyPair(ECCModeSecp256r1)
+ if err != nil {
+ b.Fatal(err)
+ }
+ ecEphPrivPEM, err := ecEphKP.PrivateKeyInPemFormat()
+ if err != nil {
+ b.Fatal(err)
+ }
+ ecEphPubPEM, err := ecEphKP.PublicKeyInPemFormat()
+ if err != nil {
+ b.Fatal(err)
+ }
+ ecdhKey, err := ComputeECDHKey([]byte(ecEphPrivPEM), []byte(ecKASPubPEM))
+ if err != nil {
+ b.Fatal(err)
+ }
+ ecSessionKey, err := CalculateHKDF(salt, ecdhKey)
+ if err != nil {
+ b.Fatal(err)
+ }
+ ecGCM, err := NewAESGcm(ecSessionKey)
+ if err != nil {
+ b.Fatal(err)
+ }
+ ecWrapped, err := ecGCM.Encrypt(testDEK)
+ if err != nil {
+ b.Fatal(err)
+ }
+ // KAS receives the ephemeral public key as DER (parsed from PEM in the manifest).
+ // DecryptWithEphemeralKey first tries x509.ParsePKIXPublicKey (DER), then compressed.
+ ecEphPubECDH, err := ECPubKeyFromPem([]byte(ecEphPubPEM))
+ if err != nil {
+ b.Fatal(err)
+ }
+ ecEphDER, err := x509.MarshalPKIXPublicKey(ecEphPubECDH)
+ if err != nil {
+ b.Fatal(err)
+ }
+ // KAS parses private key once (cached in StandardECCrypto)
+ ecKASPrivKey, err := ECPrivateKeyFromPem([]byte(ecKASPrivPEM))
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ // EC P-384: same flow as P-256, just on a different curve
+ ec384KASKP, err := NewECKeyPair(ECCModeSecp384r1)
+ if err != nil {
+ b.Fatal(err)
+ }
+ ec384KASPubPEM, err := ec384KASKP.PublicKeyInPemFormat()
+ if err != nil {
+ b.Fatal(err)
+ }
+ ec384KASPrivPEM, err := ec384KASKP.PrivateKeyInPemFormat()
+ if err != nil {
+ b.Fatal(err)
+ }
+ ec384EphKP, err := NewECKeyPair(ECCModeSecp384r1)
+ if err != nil {
+ b.Fatal(err)
+ }
+ ec384EphPrivPEM, err := ec384EphKP.PrivateKeyInPemFormat()
+ if err != nil {
+ b.Fatal(err)
+ }
+ ec384EphPubPEM, err := ec384EphKP.PublicKeyInPemFormat()
+ if err != nil {
+ b.Fatal(err)
+ }
+ ec384DhKey, err := ComputeECDHKey([]byte(ec384EphPrivPEM), []byte(ec384KASPubPEM))
+ if err != nil {
+ b.Fatal(err)
+ }
+ ec384SessionKey, err := CalculateHKDF(salt, ec384DhKey)
+ if err != nil {
+ b.Fatal(err)
+ }
+ ec384GCM, err := NewAESGcm(ec384SessionKey)
+ if err != nil {
+ b.Fatal(err)
+ }
+ ec384Wrapped, err := ec384GCM.Encrypt(testDEK)
+ if err != nil {
+ b.Fatal(err)
+ }
+ ec384EphPubECDH, err := ECPubKeyFromPem([]byte(ec384EphPubPEM))
+ if err != nil {
+ b.Fatal(err)
+ }
+ ec384EphDER, err := x509.MarshalPKIXPublicKey(ec384EphPubECDH)
+ if err != nil {
+ b.Fatal(err)
+ }
+ ec384KASPrivKey, err := ECPrivateKeyFromPem([]byte(ec384KASPrivPEM))
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ // X-Wing: KAS parses PEM each call, then calls UnwrapDEK
+ xwingKP, err := NewXWingKeyPair()
+ if err != nil {
+ b.Fatal(err)
+ }
+ xwingPrivPEM, err := xwingKP.PrivateKeyInPemFormat()
+ if err != nil {
+ b.Fatal(err)
+ }
+ xwingWrapped, err := XWingWrapDEK(xwingKP.publicKey, testDEK)
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ // P256+MLKEM768: KAS parses PEM each call, then calls UnwrapDEK
+ p256KP, err := NewP256MLKEM768KeyPair()
+ if err != nil {
+ b.Fatal(err)
+ }
+ p256PrivPEM, err := p256KP.PrivateKeyInPemFormat()
+ if err != nil {
+ b.Fatal(err)
+ }
+ p256Wrapped, err := P256MLKEM768WrapDEK(p256KP.publicKey, testDEK)
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ // P384+MLKEM1024: KAS parses PEM each call, then calls UnwrapDEK
+ p384KP, err := NewP384MLKEM1024KeyPair()
+ if err != nil {
+ b.Fatal(err)
+ }
+ p384PrivPEM, err := p384KP.PrivateKeyInPemFormat()
+ if err != nil {
+ b.Fatal(err)
+ }
+ p384Wrapped, err := P384MLKEM1024WrapDEK(p384KP.publicKey, testDEK)
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ // RSA: KAS has pre-loaded AsymDecryption, just calls Decrypt
+ b.Run("RSA-2048", func(b *testing.B) {
+ for b.Loop() {
+ sinkBytes, errSink = rsaDec.Decrypt(rsaWrapped)
+ }
+ })
+
+ // EC: KAS creates NewSaltedECDecryptor(cachedSK, TDFSalt, nil) -> DecryptWithEphemeralKey
+ b.Run("EC-P256", func(b *testing.B) {
+ for b.Loop() {
+ dec, err := NewSaltedECDecryptor(ecKASPrivKey, salt, nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ sinkBytes, errSink = dec.DecryptWithEphemeralKey(ecWrapped, ecEphDER)
+ }
+ })
+
+ b.Run("EC-P384", func(b *testing.B) {
+ for b.Loop() {
+ dec, err := NewSaltedECDecryptor(ec384KASPrivKey, salt, nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ sinkBytes, errSink = dec.DecryptWithEphemeralKey(ec384Wrapped, ec384EphDER)
+ }
+ })
+
+ // X-Wing: KAS parses PEM then calls UnwrapDEK
+ b.Run("XWing", func(b *testing.B) {
+ for b.Loop() {
+ privKey, err := XWingPrivateKeyFromPem([]byte(xwingPrivPEM))
+ if err != nil {
+ b.Fatal(err)
+ }
+ sinkBytes, errSink = XWingUnwrapDEK(privKey, xwingWrapped)
+ }
+ })
+
+ // P256+MLKEM768: KAS parses PEM then calls UnwrapDEK
+ b.Run("P256_MLKEM768", func(b *testing.B) {
+ for b.Loop() {
+ privKey, err := P256MLKEM768PrivateKeyFromPem([]byte(p256PrivPEM))
+ if err != nil {
+ b.Fatal(err)
+ }
+ sinkBytes, errSink = P256MLKEM768UnwrapDEK(privKey, p256Wrapped)
+ }
+ })
+
+ // P384+MLKEM1024: KAS parses PEM then calls UnwrapDEK
+ b.Run("P384_MLKEM1024", func(b *testing.B) {
+ for b.Loop() {
+ privKey, err := P384MLKEM1024PrivateKeyFromPem([]byte(p384PrivPEM))
+ if err != nil {
+ b.Fatal(err)
+ }
+ sinkBytes, errSink = P384MLKEM1024UnwrapDEK(privKey, p384Wrapped)
+ }
+ })
+}
+
+func BenchmarkHybridSubOps(b *testing.B) {
+ // Setup X-Wing
+ xwingKP, err := NewXWingKeyPair()
+ if err != nil {
+ b.Fatal(err)
+ }
+ xwingSS, xwingCt, err := XWingEncapsulate(xwingKP.publicKey)
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ // Setup P256+MLKEM768
+ p256KP, err := NewP256MLKEM768KeyPair()
+ if err != nil {
+ b.Fatal(err)
+ }
+ p256SS, p256Ct, err := P256MLKEM768Encapsulate(p256KP.publicKey)
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ // Setup P384+MLKEM1024
+ p384KP, err := NewP384MLKEM1024KeyPair()
+ if err != nil {
+ b.Fatal(err)
+ }
+ p384SS, p384Ct, err := P384MLKEM1024Encapsulate(p384KP.publicKey)
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ salt := defaultTDFSalt()
+
+ // Pre-derive a wrap key for AES-GCM benchmarks
+ wrapKey, err := deriveXWingWrapKey(xwingSS, salt, nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ b.Run("XWing/Encapsulate", func(b *testing.B) {
+ for b.Loop() {
+ sinkBytes, sinkBytes, errSink = XWingEncapsulate(xwingKP.publicKey)
+ }
+ })
+ b.Run("XWing/HKDF", func(b *testing.B) {
+ for b.Loop() {
+ sinkBytes, errSink = deriveXWingWrapKey(xwingSS, salt, nil)
+ }
+ })
+ b.Run("XWing/AES-GCM-Encrypt", func(b *testing.B) {
+ gcm, err := NewAESGcm(wrapKey)
+ if err != nil {
+ b.Fatal(err)
+ }
+ for b.Loop() {
+ sinkBytes, errSink = gcm.Encrypt(testDEK)
+ }
+ })
+ b.Run("XWing/ASN1-Marshal", func(b *testing.B) {
+ wrapped := XWingWrappedKey{XWingCiphertext: xwingCt, EncryptedDEK: testDEK}
+ for b.Loop() {
+ sinkBytes, errSink = asn1.Marshal(wrapped)
+ }
+ })
+
+ // P256+MLKEM768 sub-ops
+ p256WrapKey, err := deriveHybridNISTWrapKey(p256SS, salt, nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ b.Run("P256_MLKEM768/Encapsulate", func(b *testing.B) {
+ for b.Loop() {
+ sinkBytes, sinkBytes, errSink = P256MLKEM768Encapsulate(p256KP.publicKey)
+ }
+ })
+ b.Run("P256_MLKEM768/HKDF", func(b *testing.B) {
+ for b.Loop() {
+ sinkBytes, errSink = deriveHybridNISTWrapKey(p256SS, salt, nil)
+ }
+ })
+ b.Run("P256_MLKEM768/AES-GCM-Encrypt", func(b *testing.B) {
+ gcm, err := NewAESGcm(p256WrapKey)
+ if err != nil {
+ b.Fatal(err)
+ }
+ for b.Loop() {
+ sinkBytes, errSink = gcm.Encrypt(testDEK)
+ }
+ })
+ b.Run("P256_MLKEM768/ASN1-Marshal", func(b *testing.B) {
+ wrapped := HybridNISTWrappedKey{HybridCiphertext: p256Ct, EncryptedDEK: testDEK}
+ for b.Loop() {
+ sinkBytes, errSink = asn1.Marshal(wrapped)
+ }
+ })
+
+ // P384+MLKEM1024 sub-ops
+ p384WrapKey, err := deriveHybridNISTWrapKey(p384SS, salt, nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ b.Run("P384_MLKEM1024/Encapsulate", func(b *testing.B) {
+ for b.Loop() {
+ sinkBytes, sinkBytes, errSink = P384MLKEM1024Encapsulate(p384KP.publicKey)
+ }
+ })
+ b.Run("P384_MLKEM1024/HKDF", func(b *testing.B) {
+ for b.Loop() {
+ sinkBytes, errSink = deriveHybridNISTWrapKey(p384SS, salt, nil)
+ }
+ })
+ b.Run("P384_MLKEM1024/AES-GCM-Encrypt", func(b *testing.B) {
+ gcm, err := NewAESGcm(p384WrapKey)
+ if err != nil {
+ b.Fatal(err)
+ }
+ for b.Loop() {
+ sinkBytes, errSink = gcm.Encrypt(testDEK)
+ }
+ })
+ b.Run("P384_MLKEM1024/ASN1-Marshal", func(b *testing.B) {
+ wrapped := HybridNISTWrappedKey{HybridCiphertext: p384Ct, EncryptedDEK: testDEK}
+ for b.Loop() {
+ sinkBytes, errSink = asn1.Marshal(wrapped)
+ }
+ })
+}
+
+func TestWrappedKeySizeComparison(t *testing.T) {
+ type sizeResult struct {
+ scheme string
+ wrappedLen int
+ pubKeyLen int
+ notes string
+ }
+
+ var results []sizeResult
+
+ // RSA-2048
+ rsaKP, err := NewRSAKeyPair(2048)
+ if err != nil {
+ t.Fatal(err)
+ }
+ rsaPubPEM, err := rsaKP.PublicKeyInPemFormat()
+ if err != nil {
+ t.Fatal(err)
+ }
+ rsaEnc, err := NewAsymEncryption(rsaPubPEM)
+ if err != nil {
+ t.Fatal(err)
+ }
+ rsaWrapped, err := rsaEnc.Encrypt(testDEK)
+ if err != nil {
+ t.Fatal(err)
+ }
+ results = append(results, sizeResult{
+ scheme: "RSA-2048",
+ wrappedLen: len(rsaWrapped),
+ pubKeyLen: len(rsaPubPEM),
+ notes: "No ephemeral key",
+ })
+
+ // EC P-256
+ ecKP, err := NewECKeyPair(ECCModeSecp256r1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ ecPubPEM, err := ecKP.PublicKeyInPemFormat()
+ if err != nil {
+ t.Fatal(err)
+ }
+ ecEnc, err := FromPublicPEM(ecPubPEM)
+ if err != nil {
+ t.Fatal(err)
+ }
+ ecWrapped, err := ecEnc.Encrypt(testDEK)
+ if err != nil {
+ t.Fatal(err)
+ }
+ ecEphemeral := ecEnc.EphemeralKey()
+ results = append(results, sizeResult{
+ scheme: "EC P-256",
+ wrappedLen: len(ecWrapped),
+ pubKeyLen: len(ecPubPEM),
+ notes: fmt.Sprintf("+ ephemeral key (%d bytes)", len(ecEphemeral)),
+ })
+
+ // EC P-384
+ ec384KP, err := NewECKeyPair(ECCModeSecp384r1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ ec384PubPEM, err := ec384KP.PublicKeyInPemFormat()
+ if err != nil {
+ t.Fatal(err)
+ }
+ ec384Enc, err := FromPublicPEM(ec384PubPEM)
+ if err != nil {
+ t.Fatal(err)
+ }
+ ec384Wrapped, err := ec384Enc.Encrypt(testDEK)
+ if err != nil {
+ t.Fatal(err)
+ }
+ ec384Ephemeral := ec384Enc.EphemeralKey()
+ results = append(results, sizeResult{
+ scheme: "EC P-384",
+ wrappedLen: len(ec384Wrapped),
+ pubKeyLen: len(ec384PubPEM),
+ notes: fmt.Sprintf("+ ephemeral key (%d bytes)", len(ec384Ephemeral)),
+ })
+
+ // X-Wing
+ xwingKP, err := NewXWingKeyPair()
+ if err != nil {
+ t.Fatal(err)
+ }
+ xwingPubPEM, err := xwingKP.PublicKeyInPemFormat()
+ if err != nil {
+ t.Fatal(err)
+ }
+ xwingWrapped, err := XWingWrapDEK(xwingKP.publicKey, testDEK)
+ if err != nil {
+ t.Fatal(err)
+ }
+ results = append(results, sizeResult{
+ scheme: "X-Wing",
+ wrappedLen: len(xwingWrapped),
+ pubKeyLen: len(xwingPubPEM),
+ notes: "All in ASN.1 blob",
+ })
+
+ // P256+MLKEM768
+ p256KP, err := NewP256MLKEM768KeyPair()
+ if err != nil {
+ t.Fatal(err)
+ }
+ p256PubPEM, err := p256KP.PublicKeyInPemFormat()
+ if err != nil {
+ t.Fatal(err)
+ }
+ p256Wrapped, err := P256MLKEM768WrapDEK(p256KP.publicKey, testDEK)
+ if err != nil {
+ t.Fatal(err)
+ }
+ results = append(results, sizeResult{
+ scheme: "P256+MLKEM768",
+ wrappedLen: len(p256Wrapped),
+ pubKeyLen: len(p256PubPEM),
+ notes: "All in ASN.1 blob",
+ })
+
+ // P384+MLKEM1024
+ p384KP, err := NewP384MLKEM1024KeyPair()
+ if err != nil {
+ t.Fatal(err)
+ }
+ p384PubPEM, err := p384KP.PublicKeyInPemFormat()
+ if err != nil {
+ t.Fatal(err)
+ }
+ p384Wrapped, err := P384MLKEM1024WrapDEK(p384KP.publicKey, testDEK)
+ if err != nil {
+ t.Fatal(err)
+ }
+ results = append(results, sizeResult{
+ scheme: "P384+MLKEM1024",
+ wrappedLen: len(p384Wrapped),
+ pubKeyLen: len(p384PubPEM),
+ notes: "All in ASN.1 blob",
+ })
+
+ // Print table
+ t.Logf("\n%-20s %20s %20s %s", "Scheme", "Wrapped Key (bytes)", "Public Key (bytes)", "Notes")
+ t.Logf("%-20s %20s %20s %s", "------", "-------------------", "------------------", "-----")
+ for _, r := range results {
+ t.Logf("%-20s %20d %20d %s", r.scheme, r.wrappedLen, r.pubKeyLen, r.notes)
+ }
+}
diff --git a/lib/ocrypto/ec_key_pair.go b/lib/ocrypto/ec_key_pair.go
index f9a9554d4f..70e30cc8df 100644
--- a/lib/ocrypto/ec_key_pair.go
+++ b/lib/ocrypto/ec_key_pair.go
@@ -29,6 +29,19 @@ const (
EC521Key KeyType = "ec:secp521r1"
)
+// ParseKeyType validates a string as a known KeyType, returning an error for
+// unrecognized values.
+func ParseKeyType(alg string) (KeyType, error) {
+ switch KeyType(alg) {
+ case RSA2048Key, RSA4096Key,
+ EC256Key, EC384Key, EC521Key,
+ HybridXWingKey, HybridSecp256r1MLKEM768Key, HybridSecp384r1MLKEM1024Key:
+ return KeyType(alg), nil
+ default:
+ return "", fmt.Errorf("unrecognized key type: %s", alg)
+ }
+}
+
const (
ECCModeSecp256r1 ECCMode = 0
ECCModeSecp384r1 ECCMode = 1
@@ -64,6 +77,8 @@ func NewKeyPair(kt KeyType) (KeyPair, error) {
return nil, err
}
return NewECKeyPair(mode)
+ case HybridSecp256r1MLKEM768Key, HybridSecp384r1MLKEM1024Key, HybridXWingKey:
+ return NewHybridKeyPair(kt)
default:
return nil, fmt.Errorf("unsupported key type: %v", kt)
}
diff --git a/lib/ocrypto/go.mod b/lib/ocrypto/go.mod
index fb1efec86f..206f123530 100644
--- a/lib/ocrypto/go.mod
+++ b/lib/ocrypto/go.mod
@@ -5,8 +5,9 @@ go 1.25.0
toolchain go1.25.9
require (
- github.com/stretchr/testify v1.11.1
- golang.org/x/crypto v0.49.0
+ github.com/cloudflare/circl v1.6.3
+ github.com/stretchr/testify v1.10.0
+ golang.org/x/crypto v0.45.0
)
require (
@@ -14,6 +15,7 @@ require (
github.com/kr/pretty v0.3.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
+ golang.org/x/sys v0.38.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
diff --git a/lib/ocrypto/go.sum b/lib/ocrypto/go.sum
index 8b6a6d0a01..0388adaa2d 100644
--- a/lib/ocrypto/go.sum
+++ b/lib/ocrypto/go.sum
@@ -1,3 +1,5 @@
+github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
+github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -14,10 +16,12 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
-github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
-github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
-golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
-golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
+golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
+golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
+golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
diff --git a/lib/ocrypto/hybrid_common.go b/lib/ocrypto/hybrid_common.go
new file mode 100644
index 0000000000..4f4ee05417
--- /dev/null
+++ b/lib/ocrypto/hybrid_common.go
@@ -0,0 +1,75 @@
+package ocrypto
+
+import (
+ "crypto/sha256"
+ "encoding/pem"
+ "fmt"
+)
+
+// HybridWrapDEK parses the recipient's hybrid public key PEM, encapsulates
+// against it using the scheme implied by ktype, and returns the ASN.1-encoded
+// wrapped DEK envelope used in `hybrid-wrapped` manifests. It dispatches across
+// both the X-Wing and NIST EC + ML-KEM families so SDK call sites do not need
+// to repeat the algorithm switch.
+//
+// The HKDF salt is the default TDF salt; callers that need a non-default salt
+// should call the per-scheme `*WrapDEK` helpers directly.
+func HybridWrapDEK(ktype KeyType, kasPublicKeyPEM string, dek []byte) ([]byte, error) {
+ switch ktype { //nolint:exhaustive // only handle hybrid types
+ case HybridXWingKey:
+ pubKey, err := XWingPubKeyFromPem([]byte(kasPublicKeyPEM))
+ if err != nil {
+ return nil, fmt.Errorf("X-Wing public key: %w", err)
+ }
+ return XWingWrapDEK(pubKey, dek)
+ case HybridSecp256r1MLKEM768Key:
+ pubKey, err := P256MLKEM768PubKeyFromPem([]byte(kasPublicKeyPEM))
+ if err != nil {
+ return nil, fmt.Errorf("P-256+ML-KEM-768 public key: %w", err)
+ }
+ return P256MLKEM768WrapDEK(pubKey, dek)
+ case HybridSecp384r1MLKEM1024Key:
+ pubKey, err := P384MLKEM1024PubKeyFromPem([]byte(kasPublicKeyPEM))
+ if err != nil {
+ return nil, fmt.Errorf("P-384+ML-KEM-1024 public key: %w", err)
+ }
+ return P384MLKEM1024WrapDEK(pubKey, dek)
+ default:
+ return nil, fmt.Errorf("unsupported hybrid key type: %s", ktype)
+ }
+}
+
+// defaultTDFSalt returns the salt used for HKDF derivation in all TDF hybrid
+// key wrapping schemes (X-Wing and NIST EC + ML-KEM). Defined here rather than
+// in a per-scheme file so that any change applies uniformly across schemes.
+func defaultTDFSalt() []byte {
+ digest := sha256.New()
+ digest.Write([]byte("TDF"))
+ return digest.Sum(nil)
+}
+
+// rawToPEM wraps a fixed-size byte slice in a PEM block of the given type. Used
+// by both X-Wing and NIST hybrid key serialization.
+func rawToPEM(blockType string, raw []byte, expectedSize int) (string, error) {
+ if len(raw) != expectedSize {
+ return "", fmt.Errorf("invalid %s size: got %d want %d", blockType, len(raw), expectedSize)
+ }
+
+ pemBytes := pem.EncodeToMemory(&pem.Block{
+ Type: blockType,
+ Bytes: raw,
+ })
+ if pemBytes == nil {
+ return "", fmt.Errorf("failed to encode %s to PEM", blockType)
+ }
+
+ return string(pemBytes), nil
+}
+
+// cloneOrNil returns a copy of data, or nil if data is empty.
+func cloneOrNil(data []byte) []byte {
+ if len(data) == 0 {
+ return nil
+ }
+ return append([]byte(nil), data...)
+}
diff --git a/lib/ocrypto/hybrid_nist.go b/lib/ocrypto/hybrid_nist.go
new file mode 100644
index 0000000000..ee0a575ac5
--- /dev/null
+++ b/lib/ocrypto/hybrid_nist.go
@@ -0,0 +1,519 @@
+package ocrypto
+
+import (
+ "crypto/ecdh"
+ "crypto/mlkem"
+ "crypto/rand"
+ "crypto/sha256"
+ "encoding/asn1"
+ "fmt"
+ "io"
+
+ "golang.org/x/crypto/hkdf"
+)
+
+const (
+ HybridSecp256r1MLKEM768Key KeyType = "hpqt:secp256r1-mlkem768"
+ HybridSecp384r1MLKEM1024Key KeyType = "hpqt:secp384r1-mlkem1024"
+)
+
+// ML-KEM seed size (d || z) used by crypto/mlkem for private key serialization.
+const mlkemSeedSize = 64
+
+// Sizes for P-256 + ML-KEM-768 hybrid.
+const (
+ P256MLKEM768ECPublicKeySize = 65 // uncompressed P-256 point
+ P256MLKEM768ECPrivateKeySize = 32 // P-256 scalar
+ P256MLKEM768MLKEMPubKeySize = 1184 // mlkem768 encapsulation key
+ P256MLKEM768MLKEMPrivKeySize = mlkemSeedSize
+ P256MLKEM768MLKEMCtSize = 1088 // mlkem768 ciphertext
+
+ P256MLKEM768PublicKeySize = P256MLKEM768ECPublicKeySize + P256MLKEM768MLKEMPubKeySize // 1249
+ P256MLKEM768PrivateKeySize = P256MLKEM768ECPrivateKeySize + P256MLKEM768MLKEMPrivKeySize // 96
+ P256MLKEM768CiphertextSize = P256MLKEM768ECPublicKeySize + P256MLKEM768MLKEMCtSize // 1153
+
+ PEMBlockP256MLKEM768PublicKey = "SECP256R1 MLKEM768 PUBLIC KEY"
+ PEMBlockP256MLKEM768PrivateKey = "SECP256R1 MLKEM768 PRIVATE KEY"
+)
+
+// Sizes for P-384 + ML-KEM-1024 hybrid.
+const (
+ P384MLKEM1024ECPublicKeySize = 97 // uncompressed P-384 point
+ P384MLKEM1024ECPrivateKeySize = 48 // P-384 scalar
+ P384MLKEM1024MLKEMPubKeySize = 1568 // mlkem1024 encapsulation key
+ P384MLKEM1024MLKEMPrivKeySize = mlkemSeedSize
+ P384MLKEM1024MLKEMCtSize = 1568 // mlkem1024 ciphertext
+
+ P384MLKEM1024PublicKeySize = P384MLKEM1024ECPublicKeySize + P384MLKEM1024MLKEMPubKeySize // 1665
+ P384MLKEM1024PrivateKeySize = P384MLKEM1024ECPrivateKeySize + P384MLKEM1024MLKEMPrivKeySize // 112
+ P384MLKEM1024CiphertextSize = P384MLKEM1024ECPublicKeySize + P384MLKEM1024MLKEMCtSize // 1665
+
+ PEMBlockP384MLKEM1024PublicKey = "SECP384R1 MLKEM1024 PUBLIC KEY"
+ PEMBlockP384MLKEM1024PrivateKey = "SECP384R1 MLKEM1024 PRIVATE KEY"
+)
+
+// AES-256 key size used for wrap key derivation.
+const hybridNISTWrapKeySize = 32
+
+// HybridNISTWrappedKey is the ASN.1 envelope stored in wrapped_key.
+type HybridNISTWrappedKey struct {
+ HybridCiphertext []byte `asn1:"tag:0"`
+ EncryptedDEK []byte `asn1:"tag:1"`
+}
+
+// hybridNISTParams captures the curve-specific parameters for a NIST hybrid scheme.
+type hybridNISTParams struct {
+ curve ecdh.Curve
+ ecPubSize int
+ ecPrivSize int
+ mlkemPubSize int
+ mlkemPrivSize int
+ mlkemCtSize int
+ pubPEMBlock string
+ privPEMBlock string
+ keyType KeyType
+}
+
+var p256mlkem768Params = hybridNISTParams{
+ curve: ecdh.P256(),
+ ecPubSize: P256MLKEM768ECPublicKeySize,
+ ecPrivSize: P256MLKEM768ECPrivateKeySize,
+ mlkemPubSize: P256MLKEM768MLKEMPubKeySize,
+ mlkemPrivSize: P256MLKEM768MLKEMPrivKeySize,
+ mlkemCtSize: P256MLKEM768MLKEMCtSize,
+ pubPEMBlock: PEMBlockP256MLKEM768PublicKey,
+ privPEMBlock: PEMBlockP256MLKEM768PrivateKey,
+ keyType: HybridSecp256r1MLKEM768Key,
+}
+
+var p384mlkem1024Params = hybridNISTParams{
+ curve: ecdh.P384(),
+ ecPubSize: P384MLKEM1024ECPublicKeySize,
+ ecPrivSize: P384MLKEM1024ECPrivateKeySize,
+ mlkemPubSize: P384MLKEM1024MLKEMPubKeySize,
+ mlkemPrivSize: P384MLKEM1024MLKEMPrivKeySize,
+ mlkemCtSize: P384MLKEM1024MLKEMCtSize,
+ pubPEMBlock: PEMBlockP384MLKEM1024PublicKey,
+ privPEMBlock: PEMBlockP384MLKEM1024PrivateKey,
+ keyType: HybridSecp384r1MLKEM1024Key,
+}
+
+// HybridNISTKeyPair holds a hybrid EC + ML-KEM keypair as raw bytes.
+type HybridNISTKeyPair struct {
+ publicKey []byte
+ privateKey []byte
+ params *hybridNISTParams
+}
+
+// HybridNISTEncryptor implements PublicKeyEncryptor for NIST hybrid schemes.
+type HybridNISTEncryptor struct {
+ publicKey []byte
+ salt []byte
+ info []byte
+ params *hybridNISTParams
+}
+
+// HybridNISTDecryptor implements PrivateKeyDecryptor for NIST hybrid schemes.
+type HybridNISTDecryptor struct {
+ privateKey []byte
+ salt []byte
+ info []byte
+ params *hybridNISTParams
+}
+
+// IsHybridKeyType returns true if the key type is a hybrid post-quantum type.
+func IsHybridKeyType(kt KeyType) bool {
+ switch kt { //nolint:exhaustive // only handle hybrid types
+ case HybridXWingKey, HybridSecp256r1MLKEM768Key, HybridSecp384r1MLKEM1024Key:
+ return true
+ default:
+ return false
+ }
+}
+
+// NewHybridKeyPair creates a key pair for the given hybrid key type.
+func NewHybridKeyPair(kt KeyType) (KeyPair, error) {
+ switch kt { //nolint:exhaustive // only handle hybrid types
+ case HybridXWingKey:
+ return NewXWingKeyPair()
+ case HybridSecp256r1MLKEM768Key:
+ return NewP256MLKEM768KeyPair()
+ case HybridSecp384r1MLKEM1024Key:
+ return NewP384MLKEM1024KeyPair()
+ default:
+ return nil, fmt.Errorf("unsupported hybrid key type: %v", kt)
+ }
+}
+
+func NewP256MLKEM768KeyPair() (HybridNISTKeyPair, error) {
+ return newHybridNISTKeyPair(&p256mlkem768Params, func() ([]byte, []byte, error) {
+ dk, err := mlkem.GenerateKey768()
+ if err != nil {
+ return nil, nil, err
+ }
+ return dk.EncapsulationKey().Bytes(), dk.Bytes(), nil
+ })
+}
+
+func NewP384MLKEM1024KeyPair() (HybridNISTKeyPair, error) {
+ return newHybridNISTKeyPair(&p384mlkem1024Params, func() ([]byte, []byte, error) {
+ dk, err := mlkem.GenerateKey1024()
+ if err != nil {
+ return nil, nil, err
+ }
+ return dk.EncapsulationKey().Bytes(), dk.Bytes(), nil
+ })
+}
+
+func newHybridNISTKeyPair(p *hybridNISTParams, genMLKEM func() (pub, priv []byte, err error)) (HybridNISTKeyPair, error) {
+ ecPriv, err := p.curve.GenerateKey(rand.Reader)
+ if err != nil {
+ return HybridNISTKeyPair{}, fmt.Errorf("ECDH key generation failed: %w", err)
+ }
+ ecPub := ecPriv.PublicKey().Bytes() // uncompressed point
+ ecPrivBytes := ecPriv.Bytes() // raw scalar
+
+ mlkemPub, mlkemPriv, err := genMLKEM()
+ if err != nil {
+ return HybridNISTKeyPair{}, fmt.Errorf("ML-KEM key generation failed: %w", err)
+ }
+
+ pubKey := make([]byte, 0, p.ecPubSize+p.mlkemPubSize)
+ pubKey = append(pubKey, ecPub...)
+ pubKey = append(pubKey, mlkemPub...)
+
+ privKey := make([]byte, 0, p.ecPrivSize+p.mlkemPrivSize)
+ privKey = append(privKey, ecPrivBytes...)
+ privKey = append(privKey, mlkemPriv...)
+
+ return HybridNISTKeyPair{
+ publicKey: pubKey,
+ privateKey: privKey,
+ params: p,
+ }, nil
+}
+
+func (k HybridNISTKeyPair) PublicKeyInPemFormat() (string, error) {
+ return rawToPEM(k.params.pubPEMBlock, k.publicKey, k.params.ecPubSize+k.params.mlkemPubSize)
+}
+
+func (k HybridNISTKeyPair) PrivateKeyInPemFormat() (string, error) {
+ return rawToPEM(k.params.privPEMBlock, k.privateKey, k.params.ecPrivSize+k.params.mlkemPrivSize)
+}
+
+func (k HybridNISTKeyPair) GetKeyType() KeyType {
+ return k.params.keyType
+}
+
+func P256MLKEM768PubKeyFromPem(data []byte) ([]byte, error) {
+ return decodeSizedPEMBlock(data, PEMBlockP256MLKEM768PublicKey, P256MLKEM768PublicKeySize)
+}
+
+func P256MLKEM768PrivateKeyFromPem(data []byte) ([]byte, error) {
+ return decodeSizedPEMBlock(data, PEMBlockP256MLKEM768PrivateKey, P256MLKEM768PrivateKeySize)
+}
+
+func P384MLKEM1024PubKeyFromPem(data []byte) ([]byte, error) {
+ return decodeSizedPEMBlock(data, PEMBlockP384MLKEM1024PublicKey, P384MLKEM1024PublicKeySize)
+}
+
+func P384MLKEM1024PrivateKeyFromPem(data []byte) ([]byte, error) {
+ return decodeSizedPEMBlock(data, PEMBlockP384MLKEM1024PrivateKey, P384MLKEM1024PrivateKeySize)
+}
+
+func NewP256MLKEM768Encryptor(publicKey, salt, info []byte) (*HybridNISTEncryptor, error) {
+ return newHybridNISTEncryptor(&p256mlkem768Params, publicKey, salt, info)
+}
+
+func NewP384MLKEM1024Encryptor(publicKey, salt, info []byte) (*HybridNISTEncryptor, error) {
+ return newHybridNISTEncryptor(&p384mlkem1024Params, publicKey, salt, info)
+}
+
+func newHybridNISTEncryptor(p *hybridNISTParams, publicKey, salt, info []byte) (*HybridNISTEncryptor, error) {
+ expectedSize := p.ecPubSize + p.mlkemPubSize
+ if len(publicKey) != expectedSize {
+ return nil, fmt.Errorf("invalid %s public key size: got %d want %d", p.keyType, len(publicKey), expectedSize)
+ }
+ return &HybridNISTEncryptor{
+ publicKey: append([]byte(nil), publicKey...),
+ salt: cloneOrNil(salt),
+ info: cloneOrNil(info),
+ params: p,
+ }, nil
+}
+
+func (e *HybridNISTEncryptor) Encrypt(data []byte) ([]byte, error) {
+ return hybridNISTWrapDEK(e.params, e.publicKey, data, e.salt, e.info)
+}
+
+func (e *HybridNISTEncryptor) PublicKeyInPemFormat() (string, error) {
+ return rawToPEM(e.params.pubPEMBlock, e.publicKey, e.params.ecPubSize+e.params.mlkemPubSize)
+}
+
+func (e *HybridNISTEncryptor) Type() SchemeType { return Hybrid }
+func (e *HybridNISTEncryptor) KeyType() KeyType { return e.params.keyType }
+func (e *HybridNISTEncryptor) EphemeralKey() []byte { return nil }
+
+func (e *HybridNISTEncryptor) Metadata() (map[string]string, error) {
+ return make(map[string]string), nil
+}
+
+func NewP256MLKEM768Decryptor(privateKey []byte) (*HybridNISTDecryptor, error) {
+ return NewSaltedP256MLKEM768Decryptor(privateKey, defaultTDFSalt(), nil)
+}
+
+func NewSaltedP256MLKEM768Decryptor(privateKey, salt, info []byte) (*HybridNISTDecryptor, error) {
+ return newHybridNISTDecryptor(&p256mlkem768Params, privateKey, salt, info)
+}
+
+func NewP384MLKEM1024Decryptor(privateKey []byte) (*HybridNISTDecryptor, error) {
+ return NewSaltedP384MLKEM1024Decryptor(privateKey, defaultTDFSalt(), nil)
+}
+
+func NewSaltedP384MLKEM1024Decryptor(privateKey, salt, info []byte) (*HybridNISTDecryptor, error) {
+ return newHybridNISTDecryptor(&p384mlkem1024Params, privateKey, salt, info)
+}
+
+func newHybridNISTDecryptor(p *hybridNISTParams, privateKey, salt, info []byte) (*HybridNISTDecryptor, error) {
+ expectedSize := p.ecPrivSize + p.mlkemPrivSize
+ if len(privateKey) != expectedSize {
+ return nil, fmt.Errorf("invalid %s private key size: got %d want %d", p.keyType, len(privateKey), expectedSize)
+ }
+ return &HybridNISTDecryptor{
+ privateKey: append([]byte(nil), privateKey...),
+ salt: cloneOrNil(salt),
+ info: cloneOrNil(info),
+ params: p,
+ }, nil
+}
+
+func (d *HybridNISTDecryptor) Decrypt(data []byte) ([]byte, error) {
+ return hybridNISTUnwrapDEK(d.params, d.privateKey, data, d.salt, d.info)
+}
+
+func P256MLKEM768WrapDEK(publicKeyRaw, dek []byte) ([]byte, error) {
+ return hybridNISTWrapDEK(&p256mlkem768Params, publicKeyRaw, dek, defaultTDFSalt(), nil)
+}
+
+func P256MLKEM768UnwrapDEK(privateKeyRaw, wrappedDER []byte) ([]byte, error) {
+ return hybridNISTUnwrapDEK(&p256mlkem768Params, privateKeyRaw, wrappedDER, defaultTDFSalt(), nil)
+}
+
+func P384MLKEM1024WrapDEK(publicKeyRaw, dek []byte) ([]byte, error) {
+ return hybridNISTWrapDEK(&p384mlkem1024Params, publicKeyRaw, dek, defaultTDFSalt(), nil)
+}
+
+func P384MLKEM1024UnwrapDEK(privateKeyRaw, wrappedDER []byte) ([]byte, error) {
+ return hybridNISTUnwrapDEK(&p384mlkem1024Params, privateKeyRaw, wrappedDER, defaultTDFSalt(), nil)
+}
+
+// hybridNISTEncapsulate performs hybrid encapsulation:
+// 1. Generates an ephemeral EC key and computes ECDH shared secret
+// 2. Encapsulates ML-KEM to produce a post-quantum shared secret
+// 3. Combines both secrets (ECDH || ML-KEM)
+// 4. Builds hybrid ciphertext (ephemeral EC point || ML-KEM ciphertext)
+//
+// Returns (combinedSecret, hybridCiphertext) without applying KDF or encryption.
+func hybridNISTEncapsulate(p *hybridNISTParams, publicKeyRaw []byte) ([]byte, []byte, error) {
+ expectedPubSize := p.ecPubSize + p.mlkemPubSize
+ if len(publicKeyRaw) != expectedPubSize {
+ return nil, nil, fmt.Errorf("invalid %s public key size: got %d want %d", p.keyType, len(publicKeyRaw), expectedPubSize)
+ }
+
+ ecPubBytes := publicKeyRaw[:p.ecPubSize]
+ mlkemPubBytes := publicKeyRaw[p.ecPubSize:]
+
+ // ECDH: generate ephemeral key, compute shared secret
+ ecPub, err := p.curve.NewPublicKey(ecPubBytes)
+ if err != nil {
+ return nil, nil, fmt.Errorf("invalid EC public key: %w", err)
+ }
+ ephemeral, err := p.curve.GenerateKey(rand.Reader)
+ if err != nil {
+ return nil, nil, fmt.Errorf("ECDH ephemeral key generation failed: %w", err)
+ }
+ ecdhSecret, err := ephemeral.ECDH(ecPub)
+ if err != nil {
+ return nil, nil, fmt.Errorf("ECDH failed: %w", err)
+ }
+ ephemeralPub := ephemeral.PublicKey().Bytes()
+
+ // ML-KEM: encapsulate
+ var mlkemSecret, mlkemCt []byte
+ switch p.keyType { //nolint:exhaustive // only NIST hybrid types
+ case HybridSecp256r1MLKEM768Key:
+ ek, ekErr := mlkem.NewEncapsulationKey768(mlkemPubBytes)
+ if ekErr != nil {
+ return nil, nil, fmt.Errorf("mlkem768 encapsulation key: %w", ekErr)
+ }
+ mlkemSecret, mlkemCt = ek.Encapsulate()
+ case HybridSecp384r1MLKEM1024Key:
+ ek, ekErr := mlkem.NewEncapsulationKey1024(mlkemPubBytes)
+ if ekErr != nil {
+ return nil, nil, fmt.Errorf("mlkem1024 encapsulation key: %w", ekErr)
+ }
+ mlkemSecret, mlkemCt = ek.Encapsulate()
+ default:
+ return nil, nil, fmt.Errorf("unsupported ML-KEM key type: %s", p.keyType)
+ }
+
+ // Combine secrets: ECDH || ML-KEM
+ combinedSecret := make([]byte, 0, len(ecdhSecret)+len(mlkemSecret))
+ combinedSecret = append(combinedSecret, ecdhSecret...)
+ combinedSecret = append(combinedSecret, mlkemSecret...)
+
+ // Build hybrid ciphertext: ephemeral EC point || ML-KEM ciphertext
+ hybridCt := make([]byte, 0, len(ephemeralPub)+len(mlkemCt))
+ hybridCt = append(hybridCt, ephemeralPub...)
+ hybridCt = append(hybridCt, mlkemCt...)
+
+ return combinedSecret, hybridCt, nil
+}
+
+// P256MLKEM768Encapsulate performs P-256 ECDH + ML-KEM-768 hybrid encapsulation.
+func P256MLKEM768Encapsulate(publicKeyRaw []byte) ([]byte, []byte, error) {
+ return hybridNISTEncapsulate(&p256mlkem768Params, publicKeyRaw)
+}
+
+// P384MLKEM1024Encapsulate performs P-384 ECDH + ML-KEM-1024 hybrid encapsulation.
+func P384MLKEM1024Encapsulate(publicKeyRaw []byte) ([]byte, []byte, error) {
+ return hybridNISTEncapsulate(&p384mlkem1024Params, publicKeyRaw)
+}
+
+func hybridNISTWrapDEK(p *hybridNISTParams, publicKeyRaw, dek, salt, info []byte) ([]byte, error) {
+ combinedSecret, hybridCt, err := hybridNISTEncapsulate(p, publicKeyRaw)
+ if err != nil {
+ return nil, err
+ }
+
+ // Derive AES-256 wrap key via HKDF
+ wrapKey, err := deriveHybridNISTWrapKey(combinedSecret, salt, info)
+ if err != nil {
+ return nil, err
+ }
+
+ // AES-GCM encrypt DEK
+ gcm, err := NewAESGcm(wrapKey)
+ if err != nil {
+ return nil, fmt.Errorf("NewAESGcm failed: %w", err)
+ }
+ encryptedDEK, err := gcm.Encrypt(dek)
+ if err != nil {
+ return nil, fmt.Errorf("AES-GCM encrypt failed: %w", err)
+ }
+
+ wrappedDER, err := asn1.Marshal(HybridNISTWrappedKey{
+ HybridCiphertext: hybridCt,
+ EncryptedDEK: encryptedDEK,
+ })
+ if err != nil {
+ return nil, fmt.Errorf("asn1.Marshal failed: %w", err)
+ }
+
+ return wrappedDER, nil
+}
+
+func hybridNISTUnwrapDEK(p *hybridNISTParams, privateKeyRaw, wrappedDER, salt, info []byte) ([]byte, error) {
+ expectedPrivSize := p.ecPrivSize + p.mlkemPrivSize
+ if len(privateKeyRaw) != expectedPrivSize {
+ return nil, fmt.Errorf("invalid %s private key size: got %d want %d", p.keyType, len(privateKeyRaw), expectedPrivSize)
+ }
+
+ var wrapped HybridNISTWrappedKey
+ rest, err := asn1.Unmarshal(wrappedDER, &wrapped)
+ if err != nil {
+ return nil, fmt.Errorf("asn1.Unmarshal failed: %w", err)
+ }
+ if len(rest) != 0 {
+ return nil, fmt.Errorf("asn1.Unmarshal left %d trailing bytes", len(rest))
+ }
+
+ expectedCtSize := p.ecPubSize + p.mlkemCtSize
+ if len(wrapped.HybridCiphertext) != expectedCtSize {
+ return nil, fmt.Errorf("invalid %s ciphertext size: got %d want %d",
+ p.keyType, len(wrapped.HybridCiphertext), expectedCtSize)
+ }
+
+ // Split hybrid ciphertext
+ ephemeralPubBytes := wrapped.HybridCiphertext[:p.ecPubSize]
+ mlkemCtBytes := wrapped.HybridCiphertext[p.ecPubSize:]
+
+ // Split private key
+ ecPrivBytes := privateKeyRaw[:p.ecPrivSize]
+ mlkemPrivBytes := privateKeyRaw[p.ecPrivSize:]
+
+ // ECDH: reconstruct shared secret
+ ecPriv, err := p.curve.NewPrivateKey(ecPrivBytes)
+ if err != nil {
+ return nil, fmt.Errorf("invalid EC private key: %w", err)
+ }
+ ephemeralPub, err := p.curve.NewPublicKey(ephemeralPubBytes)
+ if err != nil {
+ return nil, fmt.Errorf("invalid ephemeral EC public key: %w", err)
+ }
+ ecdhSecret, err := ecPriv.ECDH(ephemeralPub)
+ if err != nil {
+ return nil, fmt.Errorf("ECDH failed: %w", err)
+ }
+
+ // ML-KEM: decapsulate. Implicit rejection (FIPS 203 §6.3) means a wrong-key
+ // ciphertext yields a pseudorandom shared secret without an error here;
+ // authentication is enforced by the AES-GCM decrypt below.
+ var mlkemSecret []byte
+ switch p.keyType { //nolint:exhaustive // only NIST hybrid types
+ case HybridSecp256r1MLKEM768Key:
+ dk, dkErr := mlkem.NewDecapsulationKey768(mlkemPrivBytes)
+ if dkErr != nil {
+ return nil, fmt.Errorf("mlkem768 decapsulation key: %w", dkErr)
+ }
+ mlkemSecret, err = dk.Decapsulate(mlkemCtBytes)
+ case HybridSecp384r1MLKEM1024Key:
+ dk, dkErr := mlkem.NewDecapsulationKey1024(mlkemPrivBytes)
+ if dkErr != nil {
+ return nil, fmt.Errorf("mlkem1024 decapsulation key: %w", dkErr)
+ }
+ mlkemSecret, err = dk.Decapsulate(mlkemCtBytes)
+ default:
+ return nil, fmt.Errorf("unsupported ML-KEM key type: %s", p.keyType)
+ }
+ if err != nil {
+ return nil, fmt.Errorf("ML-KEM decapsulate failed: %w", err)
+ }
+
+ // Combine secrets: ECDH || ML-KEM
+ combinedSecret := make([]byte, 0, len(ecdhSecret)+len(mlkemSecret))
+ combinedSecret = append(combinedSecret, ecdhSecret...)
+ combinedSecret = append(combinedSecret, mlkemSecret...)
+
+ // Derive AES-256 wrap key via HKDF
+ wrapKey, err := deriveHybridNISTWrapKey(combinedSecret, salt, info)
+ if err != nil {
+ return nil, err
+ }
+
+ // AES-GCM decrypt DEK
+ gcm, err := NewAESGcm(wrapKey)
+ if err != nil {
+ return nil, fmt.Errorf("NewAESGcm failed: %w", err)
+ }
+ plaintext, err := gcm.Decrypt(wrapped.EncryptedDEK)
+ if err != nil {
+ return nil, fmt.Errorf("AES-GCM decrypt failed: %w", err)
+ }
+
+ return plaintext, nil
+}
+
+func deriveHybridNISTWrapKey(combinedSecret, salt, info []byte) ([]byte, error) {
+ if len(salt) == 0 {
+ salt = defaultTDFSalt()
+ }
+
+ hkdfObj := hkdf.New(sha256.New, combinedSecret, salt, info)
+ derivedKey := make([]byte, hybridNISTWrapKeySize)
+ if _, err := io.ReadFull(hkdfObj, derivedKey); err != nil {
+ return nil, fmt.Errorf("hkdf failure: %w", err)
+ }
+
+ return derivedKey, nil
+}
diff --git a/lib/ocrypto/hybrid_nist_test.go b/lib/ocrypto/hybrid_nist_test.go
new file mode 100644
index 0000000000..e7c9808aae
--- /dev/null
+++ b/lib/ocrypto/hybrid_nist_test.go
@@ -0,0 +1,240 @@
+package ocrypto
+
+import (
+ "encoding/asn1"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestP256MLKEM768KeyPairAndPEM(t *testing.T) {
+ keyPair, err := NewP256MLKEM768KeyPair()
+ require.NoError(t, err)
+
+ publicPEM, err := keyPair.PublicKeyInPemFormat()
+ require.NoError(t, err)
+ privatePEM, err := keyPair.PrivateKeyInPemFormat()
+ require.NoError(t, err)
+
+ publicKey, err := P256MLKEM768PubKeyFromPem([]byte(publicPEM))
+ require.NoError(t, err)
+ privateKey, err := P256MLKEM768PrivateKeyFromPem([]byte(privatePEM))
+ require.NoError(t, err)
+
+ assert.Len(t, publicKey, P256MLKEM768PublicKeySize)
+ assert.Len(t, privateKey, P256MLKEM768PrivateKeySize)
+ assert.Equal(t, HybridSecp256r1MLKEM768Key, keyPair.GetKeyType())
+}
+
+func TestP384MLKEM1024KeyPairAndPEM(t *testing.T) {
+ keyPair, err := NewP384MLKEM1024KeyPair()
+ require.NoError(t, err)
+
+ publicPEM, err := keyPair.PublicKeyInPemFormat()
+ require.NoError(t, err)
+ privatePEM, err := keyPair.PrivateKeyInPemFormat()
+ require.NoError(t, err)
+
+ publicKey, err := P384MLKEM1024PubKeyFromPem([]byte(publicPEM))
+ require.NoError(t, err)
+ privateKey, err := P384MLKEM1024PrivateKeyFromPem([]byte(privatePEM))
+ require.NoError(t, err)
+
+ assert.Len(t, publicKey, P384MLKEM1024PublicKeySize)
+ assert.Len(t, privateKey, P384MLKEM1024PrivateKeySize)
+ assert.Equal(t, HybridSecp384r1MLKEM1024Key, keyPair.GetKeyType())
+}
+
+func TestNewKeyPairP256MLKEM768(t *testing.T) {
+ keyPair, err := NewP256MLKEM768KeyPair()
+ require.NoError(t, err)
+ assert.Equal(t, HybridSecp256r1MLKEM768Key, keyPair.GetKeyType())
+}
+
+func TestNewKeyPairP384MLKEM1024(t *testing.T) {
+ keyPair, err := NewP384MLKEM1024KeyPair()
+ require.NoError(t, err)
+ assert.Equal(t, HybridSecp384r1MLKEM1024Key, keyPair.GetKeyType())
+}
+
+func TestP256MLKEM768WrapUnwrapRoundTrip(t *testing.T) {
+ keyPair, err := NewP256MLKEM768KeyPair()
+ require.NoError(t, err)
+
+ dek := []byte("0123456789abcdef0123456789abcdef")
+ wrapped, err := P256MLKEM768WrapDEK(keyPair.publicKey, dek)
+ require.NoError(t, err)
+
+ plaintext, err := P256MLKEM768UnwrapDEK(keyPair.privateKey, wrapped)
+ require.NoError(t, err)
+ assert.Equal(t, dek, plaintext)
+}
+
+func TestP384MLKEM1024WrapUnwrapRoundTrip(t *testing.T) {
+ keyPair, err := NewP384MLKEM1024KeyPair()
+ require.NoError(t, err)
+
+ dek := []byte("0123456789abcdef0123456789abcdef")
+ wrapped, err := P384MLKEM1024WrapDEK(keyPair.publicKey, dek)
+ require.NoError(t, err)
+
+ plaintext, err := P384MLKEM1024UnwrapDEK(keyPair.privateKey, wrapped)
+ require.NoError(t, err)
+ assert.Equal(t, dek, plaintext)
+}
+
+func TestP256MLKEM768WrapUnwrapWrongKeyFails(t *testing.T) {
+ keyPair, err := NewP256MLKEM768KeyPair()
+ require.NoError(t, err)
+ wrongKeyPair, err := NewP256MLKEM768KeyPair()
+ require.NoError(t, err)
+
+ wrapped, err := P256MLKEM768WrapDEK(keyPair.publicKey, []byte("top secret dek"))
+ require.NoError(t, err)
+
+ _, err = P256MLKEM768UnwrapDEK(wrongKeyPair.privateKey, wrapped)
+ require.Error(t, err)
+ // Wrong-key failure must surface through AES-GCM authentication, not a
+ // parse/size mismatch — ML-KEM uses implicit rejection so DecapsulateTo
+ // returns a pseudorandom secret rather than an error.
+ assert.ErrorContains(t, err, "AES-GCM decrypt failed")
+}
+
+func TestP384MLKEM1024WrapUnwrapWrongKeyFails(t *testing.T) {
+ keyPair, err := NewP384MLKEM1024KeyPair()
+ require.NoError(t, err)
+ wrongKeyPair, err := NewP384MLKEM1024KeyPair()
+ require.NoError(t, err)
+
+ wrapped, err := P384MLKEM1024WrapDEK(keyPair.publicKey, []byte("top secret dek"))
+ require.NoError(t, err)
+
+ _, err = P384MLKEM1024UnwrapDEK(wrongKeyPair.privateKey, wrapped)
+ require.Error(t, err)
+ assert.ErrorContains(t, err, "AES-GCM decrypt failed")
+}
+
+func TestHybridNISTWrappedKeyASN1RoundTrip(t *testing.T) {
+ original := HybridNISTWrappedKey{
+ HybridCiphertext: []byte("hybrid-ciphertext-data"),
+ EncryptedDEK: []byte("encrypted-dek-data"),
+ }
+
+ der, err := asn1.Marshal(original)
+ require.NoError(t, err)
+
+ var decoded HybridNISTWrappedKey
+ rest, err := asn1.Unmarshal(der, &decoded)
+ require.NoError(t, err)
+ assert.Empty(t, rest)
+ assert.Equal(t, original, decoded)
+}
+
+func TestP256MLKEM768PEMDispatch(t *testing.T) {
+ keyPair, err := NewP256MLKEM768KeyPair()
+ require.NoError(t, err)
+
+ publicPEM, err := keyPair.PublicKeyInPemFormat()
+ require.NoError(t, err)
+ privatePEM, err := keyPair.PrivateKeyInPemFormat()
+ require.NoError(t, err)
+
+ encryptor, err := FromPublicPEMWithSalt(publicPEM, []byte("salt"), []byte("info"))
+ require.NoError(t, err)
+
+ decryptor, err := FromPrivatePEMWithSalt(privatePEM, []byte("salt"), []byte("info"))
+ require.NoError(t, err)
+
+ nistEncryptor, ok := encryptor.(*HybridNISTEncryptor)
+ require.True(t, ok)
+ assert.Equal(t, Hybrid, nistEncryptor.Type())
+ assert.Equal(t, HybridSecp256r1MLKEM768Key, nistEncryptor.KeyType())
+ assert.Nil(t, nistEncryptor.EphemeralKey())
+
+ metadata, err := nistEncryptor.Metadata()
+ require.NoError(t, err)
+ assert.Empty(t, metadata)
+
+ nistDecryptor, ok := decryptor.(*HybridNISTDecryptor)
+ require.True(t, ok)
+
+ wrapped, err := nistEncryptor.Encrypt([]byte("dispatch-dek"))
+ require.NoError(t, err)
+
+ plaintext, err := nistDecryptor.Decrypt(wrapped)
+ require.NoError(t, err)
+ assert.Equal(t, []byte("dispatch-dek"), plaintext)
+}
+
+func TestP384MLKEM1024PEMDispatch(t *testing.T) {
+ keyPair, err := NewP384MLKEM1024KeyPair()
+ require.NoError(t, err)
+
+ publicPEM, err := keyPair.PublicKeyInPemFormat()
+ require.NoError(t, err)
+ privatePEM, err := keyPair.PrivateKeyInPemFormat()
+ require.NoError(t, err)
+
+ encryptor, err := FromPublicPEMWithSalt(publicPEM, []byte("salt"), []byte("info"))
+ require.NoError(t, err)
+
+ decryptor, err := FromPrivatePEMWithSalt(privatePEM, []byte("salt"), []byte("info"))
+ require.NoError(t, err)
+
+ nistEncryptor, ok := encryptor.(*HybridNISTEncryptor)
+ require.True(t, ok)
+ assert.Equal(t, Hybrid, nistEncryptor.Type())
+ assert.Equal(t, HybridSecp384r1MLKEM1024Key, nistEncryptor.KeyType())
+ assert.Nil(t, nistEncryptor.EphemeralKey())
+
+ nistDecryptor, ok := decryptor.(*HybridNISTDecryptor)
+ require.True(t, ok)
+
+ wrapped, err := nistEncryptor.Encrypt([]byte("dispatch-dek-384"))
+ require.NoError(t, err)
+
+ plaintext, err := nistDecryptor.Decrypt(wrapped)
+ require.NoError(t, err)
+ assert.Equal(t, []byte("dispatch-dek-384"), plaintext)
+}
+
+func TestP256MLKEM768Encapsulate(t *testing.T) {
+ keyPair, err := NewP256MLKEM768KeyPair()
+ require.NoError(t, err)
+
+ pubKey, err := keyPair.PublicKeyInPemFormat()
+ require.NoError(t, err)
+
+ pubKeyRaw, err := P256MLKEM768PubKeyFromPem([]byte(pubKey))
+ require.NoError(t, err)
+
+ combinedSecret, hybridCt, err := P256MLKEM768Encapsulate(pubKeyRaw)
+ require.NoError(t, err)
+ assert.NotEmpty(t, combinedSecret)
+ assert.Len(t, hybridCt, P256MLKEM768CiphertextSize)
+}
+
+func TestP384MLKEM1024Encapsulate(t *testing.T) {
+ keyPair, err := NewP384MLKEM1024KeyPair()
+ require.NoError(t, err)
+
+ pubKey, err := keyPair.PublicKeyInPemFormat()
+ require.NoError(t, err)
+
+ pubKeyRaw, err := P384MLKEM1024PubKeyFromPem([]byte(pubKey))
+ require.NoError(t, err)
+
+ combinedSecret, hybridCt, err := P384MLKEM1024Encapsulate(pubKeyRaw)
+ require.NoError(t, err)
+ assert.NotEmpty(t, combinedSecret)
+ assert.Len(t, hybridCt, P384MLKEM1024CiphertextSize)
+}
+
+func TestIsHybridKeyTypeIncludesNewTypes(t *testing.T) {
+ assert.True(t, IsHybridKeyType(HybridXWingKey))
+ assert.True(t, IsHybridKeyType(HybridSecp256r1MLKEM768Key))
+ assert.True(t, IsHybridKeyType(HybridSecp384r1MLKEM1024Key))
+ assert.False(t, IsHybridKeyType(EC256Key))
+ assert.False(t, IsHybridKeyType(RSA2048Key))
+}
diff --git a/lib/ocrypto/xwing.go b/lib/ocrypto/xwing.go
new file mode 100644
index 0000000000..1ea9d5ab25
--- /dev/null
+++ b/lib/ocrypto/xwing.go
@@ -0,0 +1,260 @@
+package ocrypto
+
+import (
+ "crypto/rand"
+ "crypto/sha256"
+ "encoding/asn1"
+ "encoding/pem"
+ "fmt"
+ "io"
+
+ "github.com/cloudflare/circl/kem/xwing"
+ "golang.org/x/crypto/hkdf"
+)
+
+const (
+ HybridXWingKey KeyType = "hpqt:xwing"
+
+ XWingPublicKeySize = xwing.PublicKeySize
+ XWingPrivateKeySize = xwing.PrivateKeySize
+ XWingCiphertextSize = xwing.CiphertextSize
+
+ PEMBlockXWingPublicKey = "XWING PUBLIC KEY"
+ PEMBlockXWingPrivateKey = "XWING PRIVATE KEY"
+)
+
+type XWingWrappedKey struct {
+ XWingCiphertext []byte `asn1:"tag:0"`
+ EncryptedDEK []byte `asn1:"tag:1"`
+}
+
+type XWingKeyPair struct {
+ publicKey []byte
+ privateKey []byte
+}
+
+type XWingEncryptor struct {
+ publicKey []byte
+ salt []byte
+ info []byte
+}
+
+type XWingDecryptor struct {
+ privateKey []byte
+ salt []byte
+ info []byte
+}
+
+func NewXWingKeyPair() (XWingKeyPair, error) {
+ sk, pk, err := xwing.GenerateKeyPair(rand.Reader)
+ if err != nil {
+ return XWingKeyPair{}, fmt.Errorf("xwing.GenerateKeyPair failed: %w", err)
+ }
+
+ publicKey := make([]byte, XWingPublicKeySize)
+ privateKey := make([]byte, XWingPrivateKeySize)
+ pk.Pack(publicKey)
+ sk.Pack(privateKey)
+
+ return XWingKeyPair{
+ publicKey: publicKey,
+ privateKey: privateKey,
+ }, nil
+}
+
+func (k XWingKeyPair) PublicKeyInPemFormat() (string, error) {
+ return rawToPEM(PEMBlockXWingPublicKey, k.publicKey, XWingPublicKeySize)
+}
+
+func (k XWingKeyPair) PrivateKeyInPemFormat() (string, error) {
+ return rawToPEM(PEMBlockXWingPrivateKey, k.privateKey, XWingPrivateKeySize)
+}
+
+func (k XWingKeyPair) GetKeyType() KeyType {
+ return HybridXWingKey
+}
+
+func XWingPubKeyFromPem(data []byte) ([]byte, error) {
+ return decodeSizedPEMBlock(data, PEMBlockXWingPublicKey, XWingPublicKeySize)
+}
+
+func XWingPrivateKeyFromPem(data []byte) ([]byte, error) {
+ return decodeSizedPEMBlock(data, PEMBlockXWingPrivateKey, XWingPrivateKeySize)
+}
+
+func NewXWingEncryptor(publicKey, salt, info []byte) (*XWingEncryptor, error) {
+ if len(publicKey) != XWingPublicKeySize {
+ return nil, fmt.Errorf("invalid X-Wing public key size: got %d want %d", len(publicKey), XWingPublicKeySize)
+ }
+
+ return &XWingEncryptor{
+ publicKey: append([]byte(nil), publicKey...),
+ salt: cloneOrNil(salt),
+ info: cloneOrNil(info),
+ }, nil
+}
+
+func (e *XWingEncryptor) Encrypt(data []byte) ([]byte, error) {
+ return xwingWrapDEK(e.publicKey, data, e.salt, e.info)
+}
+
+func (e *XWingEncryptor) PublicKeyInPemFormat() (string, error) {
+ return rawToPEM(PEMBlockXWingPublicKey, e.publicKey, XWingPublicKeySize)
+}
+
+func (e *XWingEncryptor) Type() SchemeType {
+ return Hybrid
+}
+
+func (e *XWingEncryptor) KeyType() KeyType {
+ return HybridXWingKey
+}
+
+func (e *XWingEncryptor) EphemeralKey() []byte {
+ return nil
+}
+
+func (e *XWingEncryptor) Metadata() (map[string]string, error) {
+ return make(map[string]string), nil
+}
+
+func NewXWingDecryptor(privateKey []byte) (*XWingDecryptor, error) {
+ return NewSaltedXWingDecryptor(privateKey, defaultTDFSalt(), nil)
+}
+
+func NewSaltedXWingDecryptor(privateKey, salt, info []byte) (*XWingDecryptor, error) {
+ if len(privateKey) != XWingPrivateKeySize {
+ return nil, fmt.Errorf("invalid X-Wing private key size: got %d want %d", len(privateKey), XWingPrivateKeySize)
+ }
+
+ return &XWingDecryptor{
+ privateKey: append([]byte(nil), privateKey...),
+ salt: cloneOrNil(salt),
+ info: cloneOrNil(info),
+ }, nil
+}
+
+func (d *XWingDecryptor) Decrypt(data []byte) ([]byte, error) {
+ return xwingUnwrapDEK(d.privateKey, data, d.salt, d.info)
+}
+
+func XWingWrapDEK(publicKeyRaw, dek []byte) ([]byte, error) {
+ return xwingWrapDEK(publicKeyRaw, dek, defaultTDFSalt(), nil)
+}
+
+func XWingUnwrapDEK(privateKeyRaw, wrappedDER []byte) ([]byte, error) {
+ return xwingUnwrapDEK(privateKeyRaw, wrappedDER, defaultTDFSalt(), nil)
+}
+
+// XWingEncapsulate performs the X-Wing KEM encapsulation, returning the shared
+// secret and ciphertext without applying KDF or encryption.
+func XWingEncapsulate(publicKeyRaw []byte) ([]byte, []byte, error) {
+ if len(publicKeyRaw) != XWingPublicKeySize {
+ return nil, nil, fmt.Errorf("invalid X-Wing public key size: got %d want %d", len(publicKeyRaw), XWingPublicKeySize)
+ }
+
+ sharedSecret, ciphertext, err := xwing.Encapsulate(publicKeyRaw, nil)
+ if err != nil {
+ return nil, nil, fmt.Errorf("xwing.Encapsulate failed: %w", err)
+ }
+
+ return sharedSecret, ciphertext, nil
+}
+
+func xwingWrapDEK(publicKeyRaw, dek, salt, info []byte) ([]byte, error) {
+ sharedSecret, ciphertext, err := XWingEncapsulate(publicKeyRaw)
+ if err != nil {
+ return nil, err
+ }
+
+ wrapKey, err := deriveXWingWrapKey(sharedSecret, salt, info)
+ if err != nil {
+ return nil, err
+ }
+
+ gcm, err := NewAESGcm(wrapKey)
+ if err != nil {
+ return nil, fmt.Errorf("NewAESGcm failed: %w", err)
+ }
+
+ encryptedDEK, err := gcm.Encrypt(dek)
+ if err != nil {
+ return nil, fmt.Errorf("AES-GCM encrypt failed: %w", err)
+ }
+
+ wrappedDER, err := asn1.Marshal(XWingWrappedKey{
+ XWingCiphertext: ciphertext,
+ EncryptedDEK: encryptedDEK,
+ })
+ if err != nil {
+ return nil, fmt.Errorf("asn1.Marshal failed: %w", err)
+ }
+
+ return wrappedDER, nil
+}
+
+func xwingUnwrapDEK(privateKeyRaw, wrappedDER, salt, info []byte) ([]byte, error) {
+ if len(privateKeyRaw) != XWingPrivateKeySize {
+ return nil, fmt.Errorf("invalid X-Wing private key size: got %d want %d", len(privateKeyRaw), XWingPrivateKeySize)
+ }
+
+ var wrappedKey XWingWrappedKey
+ rest, err := asn1.Unmarshal(wrappedDER, &wrappedKey)
+ if err != nil {
+ return nil, fmt.Errorf("asn1.Unmarshal failed: %w", err)
+ }
+ if len(rest) != 0 {
+ return nil, fmt.Errorf("asn1.Unmarshal left %d trailing bytes", len(rest))
+ }
+ if len(wrappedKey.XWingCiphertext) != XWingCiphertextSize {
+ return nil, fmt.Errorf("invalid X-Wing ciphertext size: got %d want %d", len(wrappedKey.XWingCiphertext), XWingCiphertextSize)
+ }
+
+ sharedSecret := xwing.Decapsulate(wrappedKey.XWingCiphertext, privateKeyRaw)
+
+ wrapKey, err := deriveXWingWrapKey(sharedSecret, salt, info)
+ if err != nil {
+ return nil, err
+ }
+
+ gcm, err := NewAESGcm(wrapKey)
+ if err != nil {
+ return nil, fmt.Errorf("NewAESGcm failed: %w", err)
+ }
+
+ plaintext, err := gcm.Decrypt(wrappedKey.EncryptedDEK)
+ if err != nil {
+ return nil, fmt.Errorf("AES-GCM decrypt failed: %w", err)
+ }
+
+ return plaintext, nil
+}
+
+func deriveXWingWrapKey(sharedSecret, salt, info []byte) ([]byte, error) {
+ if len(salt) == 0 {
+ salt = defaultTDFSalt()
+ }
+
+ hkdfObj := hkdf.New(sha256.New, sharedSecret, salt, info)
+ derivedKey := make([]byte, xwing.SharedKeySize)
+ if _, err := io.ReadFull(hkdfObj, derivedKey); err != nil {
+ return nil, fmt.Errorf("hkdf failure: %w", err)
+ }
+
+ return derivedKey, nil
+}
+
+func decodeSizedPEMBlock(data []byte, blockType string, expectedSize int) ([]byte, error) {
+ block, _ := pem.Decode(data)
+ if block == nil {
+ return nil, fmt.Errorf("failed to parse PEM formatted %s", blockType)
+ }
+ if block.Type != blockType {
+ return nil, fmt.Errorf("unexpected PEM block type: got %s want %s", block.Type, blockType)
+ }
+ if len(block.Bytes) != expectedSize {
+ return nil, fmt.Errorf("invalid %s size: got %d want %d", blockType, len(block.Bytes), expectedSize)
+ }
+
+ return append([]byte(nil), block.Bytes...), nil
+}
diff --git a/lib/ocrypto/xwing_test.go b/lib/ocrypto/xwing_test.go
new file mode 100644
index 0000000000..c0bf783ba9
--- /dev/null
+++ b/lib/ocrypto/xwing_test.go
@@ -0,0 +1,129 @@
+package ocrypto
+
+import (
+ "encoding/asn1"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestXWingKeyPairAndPEM(t *testing.T) {
+ keyPair, err := NewXWingKeyPair()
+ require.NoError(t, err)
+
+ publicPEM, err := keyPair.PublicKeyInPemFormat()
+ require.NoError(t, err)
+ privatePEM, err := keyPair.PrivateKeyInPemFormat()
+ require.NoError(t, err)
+
+ publicKey, err := XWingPubKeyFromPem([]byte(publicPEM))
+ require.NoError(t, err)
+ privateKey, err := XWingPrivateKeyFromPem([]byte(privatePEM))
+ require.NoError(t, err)
+
+ assert.Len(t, publicKey, XWingPublicKeySize)
+ assert.Len(t, privateKey, XWingPrivateKeySize)
+ assert.Equal(t, HybridXWingKey, keyPair.GetKeyType())
+}
+
+func TestNewKeyPairXWing(t *testing.T) {
+ keyPair, err := NewXWingKeyPair()
+ require.NoError(t, err)
+ assert.Equal(t, HybridXWingKey, keyPair.GetKeyType())
+}
+
+func TestXWingWrapUnwrapRoundTrip(t *testing.T) {
+ keyPair, err := NewXWingKeyPair()
+ require.NoError(t, err)
+
+ dek := []byte("0123456789abcdef0123456789abcdef")
+ wrapped, err := XWingWrapDEK(keyPair.publicKey, dek)
+ require.NoError(t, err)
+
+ plaintext, err := XWingUnwrapDEK(keyPair.privateKey, wrapped)
+ require.NoError(t, err)
+ assert.Equal(t, dek, plaintext)
+}
+
+func TestXWingWrapUnwrapWrongKeyFails(t *testing.T) {
+ keyPair, err := NewXWingKeyPair()
+ require.NoError(t, err)
+ wrongKeyPair, err := NewXWingKeyPair()
+ require.NoError(t, err)
+
+ wrapped, err := XWingWrapDEK(keyPair.publicKey, []byte("top secret dek"))
+ require.NoError(t, err)
+
+ _, err = XWingUnwrapDEK(wrongKeyPair.privateKey, wrapped)
+ require.Error(t, err)
+ assert.Contains(t, err.Error(), "AES-GCM decrypt failed")
+}
+
+func TestXWingWrappedKeyASN1RoundTrip(t *testing.T) {
+ original := XWingWrappedKey{
+ XWingCiphertext: []byte("ciphertext"),
+ EncryptedDEK: []byte("encrypted-dek"),
+ }
+
+ der, err := asn1.Marshal(original)
+ require.NoError(t, err)
+
+ var decoded XWingWrappedKey
+ rest, err := asn1.Unmarshal(der, &decoded)
+ require.NoError(t, err)
+ assert.Empty(t, rest)
+ assert.Equal(t, original, decoded)
+}
+
+func TestXWingPEMDispatch(t *testing.T) {
+ keyPair, err := NewXWingKeyPair()
+ require.NoError(t, err)
+
+ publicPEM, err := keyPair.PublicKeyInPemFormat()
+ require.NoError(t, err)
+ privatePEM, err := keyPair.PrivateKeyInPemFormat()
+ require.NoError(t, err)
+
+ encryptor, err := FromPublicPEMWithSalt(publicPEM, []byte("salt"), []byte("info"))
+ require.NoError(t, err)
+
+ decryptor, err := FromPrivatePEMWithSalt(privatePEM, []byte("salt"), []byte("info"))
+ require.NoError(t, err)
+
+ xwingEncryptor, ok := encryptor.(*XWingEncryptor)
+ require.True(t, ok)
+ assert.Equal(t, Hybrid, xwingEncryptor.Type())
+ assert.Equal(t, HybridXWingKey, xwingEncryptor.KeyType())
+ assert.Nil(t, xwingEncryptor.EphemeralKey())
+
+ metadata, err := xwingEncryptor.Metadata()
+ require.NoError(t, err)
+ assert.Empty(t, metadata)
+
+ xwingDecryptor, ok := decryptor.(*XWingDecryptor)
+ require.True(t, ok)
+
+ wrapped, err := xwingEncryptor.Encrypt([]byte("dispatch-dek"))
+ require.NoError(t, err)
+
+ plaintext, err := xwingDecryptor.Decrypt(wrapped)
+ require.NoError(t, err)
+ assert.Equal(t, []byte("dispatch-dek"), plaintext)
+}
+
+func TestXWingEncapsulate(t *testing.T) {
+ keyPair, err := NewXWingKeyPair()
+ require.NoError(t, err)
+
+ sharedSecret, ciphertext, err := XWingEncapsulate(keyPair.publicKey)
+ require.NoError(t, err)
+ assert.Len(t, sharedSecret, 32)
+ assert.Len(t, ciphertext, XWingCiphertextSize)
+}
+
+func TestXWingEncapsulateInvalidKeySize(t *testing.T) {
+ _, _, err := XWingEncapsulate([]byte("too-short"))
+ require.Error(t, err)
+ assert.Contains(t, err.Error(), "invalid X-Wing public key size")
+}
diff --git a/opentdf-dev.yaml b/opentdf-dev.yaml
index 93a471101f..5a90cfa605 100644
--- a/opentdf-dev.yaml
+++ b/opentdf-dev.yaml
@@ -22,6 +22,7 @@ services:
registered_kas_uri: http://localhost:8080 # Should match what you have registered for *this* KAS in the policy db.
preview:
ec_tdf_enabled: false
+ hybrid_tdf_enabled: false
key_management: false
root_key: a8c4824daafcfa38ed0d13002e92b08720e6c4fcee67d52e954c1a6e045907d1 # For local development testing only
keyring:
diff --git a/opentdf-example.yaml b/opentdf-example.yaml
index a0b97da826..9bc9f90ef2 100644
--- a/opentdf-example.yaml
+++ b/opentdf-example.yaml
@@ -123,4 +123,16 @@ server:
alg: ec:secp256r1
private: /keys/kas-ec-private.pem
cert: /keys/kas-ec-cert.pem
+ - kid: x1
+ alg: hpqt:xwing
+ private: /keys/kas-xwing-private.pem
+ cert: /keys/kas-xwing-public.pem
+ - kid: h1
+ alg: hpqt:secp256r1-mlkem768
+ private: /keys/kas-p256mlkem768-private.pem
+ cert: /keys/kas-p256mlkem768-public.pem
+ - kid: h2
+ alg: hpqt:secp384r1-mlkem1024
+ private: /keys/kas-p384mlkem1024-private.pem
+ cert: /keys/kas-p384mlkem1024-public.pem
port: 8080
diff --git a/opentdf-kas-mode.yaml b/opentdf-kas-mode.yaml
index ebcfb6f0c2..4bdd67e376 100644
--- a/opentdf-kas-mode.yaml
+++ b/opentdf-kas-mode.yaml
@@ -33,6 +33,12 @@ services:
- kid: r1
alg: rsa:2048
legacy: true
+ - kid: x1
+ alg: hpqt:xwing
+ - kid: h1
+ alg: hpqt:secp256r1-mlkem768
+ - kid: h2
+ alg: hpqt:secp384r1-mlkem1024
server:
public_hostname: localhost
tls:
@@ -123,4 +129,16 @@ server:
alg: ec:secp256r1
private: kas-ec-private.pem
cert: kas-ec-cert.pem
+ - kid: x1
+ alg: hpqt:xwing
+ private: kas-xwing-private.pem
+ cert: kas-xwing-public.pem
+ - kid: h1
+ alg: hpqt:secp256r1-mlkem768
+ private: kas-p256mlkem768-private.pem
+ cert: kas-p256mlkem768-public.pem
+ - kid: h2
+ alg: hpqt:secp384r1-mlkem1024
+ private: kas-p384mlkem1024-private.pem
+ cert: kas-p384mlkem1024-public.pem
port: 8181
diff --git a/otdfctl/cmd/policy/kasKeys.go b/otdfctl/cmd/policy/kasKeys.go
index 57e0e51471..14f70c55ee 100644
--- a/otdfctl/cmd/policy/kasKeys.go
+++ b/otdfctl/cmd/policy/kasKeys.go
@@ -83,6 +83,12 @@ func generateKeyPair(alg policy.Algorithm) (ocrypto.KeyPair, error) {
key, err = generateECCKey(ecSecp384Len)
case policy.Algorithm_ALGORITHM_EC_P521:
key, err = generateECCKey(ecSecp521Len)
+ case policy.Algorithm_ALGORITHM_HPQT_XWING:
+ key, err = ocrypto.NewKeyPair(ocrypto.HybridXWingKey)
+ case policy.Algorithm_ALGORITHM_HPQT_SECP256R1_MLKEM768:
+ key, err = ocrypto.NewKeyPair(ocrypto.HybridSecp256r1MLKEM768Key)
+ case policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024:
+ key, err = ocrypto.NewKeyPair(ocrypto.HybridSecp384r1MLKEM1024Key)
case policy.Algorithm_ALGORITHM_UNSPECIFIED:
fallthrough
default:
diff --git a/otdfctl/cmd/policy/kasKeys_test.go b/otdfctl/cmd/policy/kasKeys_test.go
new file mode 100644
index 0000000000..f1fc882d3a
--- /dev/null
+++ b/otdfctl/cmd/policy/kasKeys_test.go
@@ -0,0 +1,43 @@
+package policy
+
+import (
+ "testing"
+
+ "github.com/opentdf/platform/lib/ocrypto"
+ "github.com/opentdf/platform/protocol/go/policy"
+ "github.com/stretchr/testify/require"
+)
+
+func TestGenerateKeyPair_Hybrid(t *testing.T) {
+ tests := []struct {
+ name string
+ alg policy.Algorithm
+ keyType ocrypto.KeyType
+ }{
+ {"X-Wing", policy.Algorithm_ALGORITHM_HPQT_XWING, ocrypto.HybridXWingKey},
+ {"P256-MLKEM768", policy.Algorithm_ALGORITHM_HPQT_SECP256R1_MLKEM768, ocrypto.HybridSecp256r1MLKEM768Key},
+ {"P384-MLKEM1024", policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024, ocrypto.HybridSecp384r1MLKEM1024Key},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ kp, err := generateKeyPair(tt.alg)
+ require.NoError(t, err)
+ require.Equal(t, tt.keyType, kp.GetKeyType())
+
+ pubPem, err := kp.PublicKeyInPemFormat()
+ require.NoError(t, err)
+ require.NotEmpty(t, pubPem)
+
+ privPem, err := kp.PrivateKeyInPemFormat()
+ require.NoError(t, err)
+ require.NotEmpty(t, privPem)
+ })
+ }
+}
+
+func TestGenerateKeyPair_Unsupported(t *testing.T) {
+ _, err := generateKeyPair(policy.Algorithm_ALGORITHM_UNSPECIFIED)
+ require.Error(t, err)
+ require.Contains(t, err.Error(), "unsupported algorithm")
+}
diff --git a/otdfctl/docs/man/policy/kas-registry/key/create.md b/otdfctl/docs/man/policy/kas-registry/key/create.md
index 7c5aea0d99..76f5a85af9 100644
--- a/otdfctl/docs/man/policy/kas-registry/key/create.md
+++ b/otdfctl/docs/man/policy/kas-registry/key/create.md
@@ -75,6 +75,9 @@ otdfctl policy kas-registry key create --key-id "aws-key" --algorithm "rsa:2048"
| `ec:secp256r1` |
| `ec:secp384r1` |
| `ec:secp521r1` |
+ | `hpqt:xwing` |
+ | `hpqt:secp256r1-mlkem768` |
+ | `hpqt:secp384r1-mlkem1024` |
2. The `"mode"` specifies where the key that is encrypting TDFs is stored. All keys will be encrypted when stored in Virtru's DB, for modes `"local"` and `"provider"`
diff --git a/otdfctl/docs/man/policy/kas-registry/key/import.md b/otdfctl/docs/man/policy/kas-registry/key/import.md
index e7cc0a7595..b08a2ea843 100644
--- a/otdfctl/docs/man/policy/kas-registry/key/import.md
+++ b/otdfctl/docs/man/policy/kas-registry/key/import.md
@@ -79,3 +79,6 @@ otdfctl policy kas-registry key import --key-id "imported-key" --algorithm "rsa:
| `ec:secp256r1` |
| `ec:secp384r1` |
| `ec:secp521r1` |
+ | `hpqt:xwing` |
+ | `hpqt:secp256r1-mlkem768` |
+ | `hpqt:secp384r1-mlkem1024` |
diff --git a/otdfctl/docs/man/policy/kas-registry/key/rotate.md b/otdfctl/docs/man/policy/kas-registry/key/rotate.md
index 09ec687906..726d85f74b 100644
--- a/otdfctl/docs/man/policy/kas-registry/key/rotate.md
+++ b/otdfctl/docs/man/policy/kas-registry/key/rotate.md
@@ -88,6 +88,9 @@ otdfctl policy kas-registry key rotate --key "public-key-old" --kas "Secondary K
| `ec:secp256r1` |
| `ec:secp384r1` |
| `ec:secp521r1` |
+ | `hpqt:xwing` |
+ | `hpqt:secp256r1-mlkem768` |
+ | `hpqt:secp384r1-mlkem1024` |
2. The `"mode"` specifies where the key that is encrypting TDFs is stored. All keys will be encrypted when stored in Virtru's DB, for modes `"local"` and `"provider"`
diff --git a/otdfctl/pkg/cli/sdkHelpers.go b/otdfctl/pkg/cli/sdkHelpers.go
index 4b1fb9a5c3..943fbbc71e 100644
--- a/otdfctl/pkg/cli/sdkHelpers.go
+++ b/otdfctl/pkg/cli/sdkHelpers.go
@@ -125,6 +125,12 @@ func KeyAlgToEnum(alg string) (policy.Algorithm, error) {
return policy.Algorithm_ALGORITHM_EC_P384, nil
case "ec:secp521r1":
return policy.Algorithm_ALGORITHM_EC_P521, nil
+ case "hpqt:xwing":
+ return policy.Algorithm_ALGORITHM_HPQT_XWING, nil
+ case "hpqt:secp256r1-mlkem768":
+ return policy.Algorithm_ALGORITHM_HPQT_SECP256R1_MLKEM768, nil
+ case "hpqt:secp384r1-mlkem1024":
+ return policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024, nil
default:
return policy.Algorithm_ALGORITHM_UNSPECIFIED, errors.New("invalid algorithm")
}
@@ -142,6 +148,12 @@ func KeyEnumToAlg(enum policy.Algorithm) (string, error) {
return "ec:secp384r1", nil
case policy.Algorithm_ALGORITHM_EC_P521:
return "ec:secp521r1", nil
+ case policy.Algorithm_ALGORITHM_HPQT_XWING:
+ return "hpqt:xwing", nil
+ case policy.Algorithm_ALGORITHM_HPQT_SECP256R1_MLKEM768:
+ return "hpqt:secp256r1-mlkem768", nil
+ case policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024:
+ return "hpqt:secp384r1-mlkem1024", nil
default:
return "", errors.New("invalid enum algorithm")
}
diff --git a/otdfctl/pkg/cli/sdkHelpers_test.go b/otdfctl/pkg/cli/sdkHelpers_test.go
new file mode 100644
index 0000000000..52d3c64fef
--- /dev/null
+++ b/otdfctl/pkg/cli/sdkHelpers_test.go
@@ -0,0 +1,46 @@
+package cli
+
+import (
+ "testing"
+
+ "github.com/opentdf/platform/protocol/go/policy"
+ "github.com/stretchr/testify/require"
+)
+
+func TestKeyAlgToEnum_RoundTrip(t *testing.T) {
+ tests := []struct {
+ alg string
+ enum policy.Algorithm
+ }{
+ {"rsa:2048", policy.Algorithm_ALGORITHM_RSA_2048},
+ {"rsa:4096", policy.Algorithm_ALGORITHM_RSA_4096},
+ {"ec:secp256r1", policy.Algorithm_ALGORITHM_EC_P256},
+ {"ec:secp384r1", policy.Algorithm_ALGORITHM_EC_P384},
+ {"ec:secp521r1", policy.Algorithm_ALGORITHM_EC_P521},
+ {"hpqt:xwing", policy.Algorithm_ALGORITHM_HPQT_XWING},
+ {"hpqt:secp256r1-mlkem768", policy.Algorithm_ALGORITHM_HPQT_SECP256R1_MLKEM768},
+ {"hpqt:secp384r1-mlkem1024", policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.alg, func(t *testing.T) {
+ got, err := KeyAlgToEnum(tt.alg)
+ require.NoError(t, err)
+ require.Equal(t, tt.enum, got)
+
+ back, err := KeyEnumToAlg(got)
+ require.NoError(t, err)
+ require.Equal(t, tt.alg, back)
+ })
+ }
+}
+
+func TestKeyAlgToEnum_Invalid(t *testing.T) {
+ _, err := KeyAlgToEnum("not-a-real-alg")
+ require.Error(t, err)
+}
+
+func TestKeyEnumToAlg_Invalid(t *testing.T) {
+ _, err := KeyEnumToAlg(policy.Algorithm_ALGORITHM_UNSPECIFIED)
+ require.Error(t, err)
+}
diff --git a/otdfctl/pkg/utils/pemvalidate.go b/otdfctl/pkg/utils/pemvalidate.go
index e9de274236..29fc17f8ef 100644
--- a/otdfctl/pkg/utils/pemvalidate.go
+++ b/otdfctl/pkg/utils/pemvalidate.go
@@ -41,6 +41,18 @@ func ValidatePublicKeyPEM(pemBytes []byte, expected policy.Algorithm) error {
if enc.KeyType() != ocrypto.EC521Key {
return errors.New("algorithm mismatch: expected EC P-521")
}
+ case policy.Algorithm_ALGORITHM_HPQT_XWING:
+ if enc.KeyType() != ocrypto.HybridXWingKey {
+ return errors.New("algorithm mismatch: expected hybrid X-Wing (X25519 + ML-KEM-768)")
+ }
+ case policy.Algorithm_ALGORITHM_HPQT_SECP256R1_MLKEM768:
+ if enc.KeyType() != ocrypto.HybridSecp256r1MLKEM768Key {
+ return errors.New("algorithm mismatch: expected hybrid NIST P-256 + ML-KEM-768")
+ }
+ case policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024:
+ if enc.KeyType() != ocrypto.HybridSecp384r1MLKEM1024Key {
+ return errors.New("algorithm mismatch: expected hybrid NIST P-384 + ML-KEM-1024")
+ }
case policy.Algorithm_ALGORITHM_UNSPECIFIED:
fallthrough
default:
diff --git a/otdfctl/pkg/utils/pemvalidate_test.go b/otdfctl/pkg/utils/pemvalidate_test.go
index 082b403f5d..b2b33fa59d 100644
--- a/otdfctl/pkg/utils/pemvalidate_test.go
+++ b/otdfctl/pkg/utils/pemvalidate_test.go
@@ -9,6 +9,7 @@ import (
"encoding/pem"
"testing"
+ "github.com/opentdf/platform/lib/ocrypto"
"github.com/opentdf/platform/protocol/go/policy"
"github.com/stretchr/testify/require"
)
@@ -118,3 +119,45 @@ func TestValidatePublicKeyPEM_UnsupportedAlgorithm(t *testing.T) {
require.Error(t, err)
require.Contains(t, err.Error(), "unsupported or unspecified algorithm")
}
+
+func TestValidatePublicKeyPEM_HybridXWing_OK(t *testing.T) {
+ kp, err := ocrypto.NewKeyPair(ocrypto.HybridXWingKey)
+ require.NoError(t, err)
+ pubPem, err := kp.PublicKeyInPemFormat()
+ require.NoError(t, err)
+
+ err = ValidatePublicKeyPEM([]byte(pubPem), policy.Algorithm_ALGORITHM_HPQT_XWING)
+ require.NoError(t, err)
+}
+
+func TestValidatePublicKeyPEM_HybridP256MLKEM768_OK(t *testing.T) {
+ kp, err := ocrypto.NewKeyPair(ocrypto.HybridSecp256r1MLKEM768Key)
+ require.NoError(t, err)
+ pubPem, err := kp.PublicKeyInPemFormat()
+ require.NoError(t, err)
+
+ err = ValidatePublicKeyPEM([]byte(pubPem), policy.Algorithm_ALGORITHM_HPQT_SECP256R1_MLKEM768)
+ require.NoError(t, err)
+}
+
+func TestValidatePublicKeyPEM_HybridP384MLKEM1024_OK(t *testing.T) {
+ kp, err := ocrypto.NewKeyPair(ocrypto.HybridSecp384r1MLKEM1024Key)
+ require.NoError(t, err)
+ pubPem, err := kp.PublicKeyInPemFormat()
+ require.NoError(t, err)
+
+ err = ValidatePublicKeyPEM([]byte(pubPem), policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024)
+ require.NoError(t, err)
+}
+
+func TestValidatePublicKeyPEM_HybridMismatch(t *testing.T) {
+ // Generate an X-Wing key but validate against a different hybrid algorithm
+ kp, err := ocrypto.NewKeyPair(ocrypto.HybridXWingKey)
+ require.NoError(t, err)
+ pubPem, err := kp.PublicKeyInPemFormat()
+ require.NoError(t, err)
+
+ err = ValidatePublicKeyPEM([]byte(pubPem), policy.Algorithm_ALGORITHM_HPQT_SECP256R1_MLKEM768)
+ require.Error(t, err)
+ require.Contains(t, err.Error(), "algorithm mismatch")
+}
diff --git a/protocol/go/kas/kas.pb.go b/protocol/go/kas/kas.pb.go
index 03317e94c6..7eb3bfbf5c 100644
--- a/protocol/go/kas/kas.pb.go
+++ b/protocol/go/kas/kas.pb.go
@@ -242,7 +242,7 @@ type KeyAccess struct {
Protocol string `protobuf:"bytes,3,opt,name=protocol,proto3" json:"protocol,omitempty"`
// Type of key wrapping used for the data encryption key
// Required: Always
- // Values: 'wrapped' (RSA-wrapped for ZTDF), 'ec-wrapped' (experimental ECDH-wrapped)
+ // Values: 'wrapped' (RSA-wrapped for ZTDF), 'ec-wrapped' (experimental ECDH-wrapped), 'hybrid-wrapped' (experimental X-Wing-wrapped)
KeyType string `protobuf:"bytes,4,opt,name=key_type,json=type,proto3" json:"key_type,omitempty"`
// URL of the Key Access Server that can unwrap this key
// Optional: May be omitted if KAS URL is known from context
@@ -269,7 +269,7 @@ type KeyAccess struct {
Header []byte `protobuf:"bytes,9,opt,name=header,proto3" json:"header,omitempty"`
// Ephemeral public key for ECDH key derivation (ec-wrapped type only)
// Required: When key_type="ec-wrapped" (experimental ECDH-based ZTDF)
- // Omitted: When key_type="wrapped" (RSA-based ZTDF)
+ // Omitted: When key_type="wrapped" or key_type="hybrid-wrapped"
// Should be a PEM-encoded PKCS#8 (ASN.1) formatted public key
// Used to derive the symmetric key for unwrapping the DEK
EphemeralPublicKey string `protobuf:"bytes,10,opt,name=ephemeral_public_key,json=ephemeralPublicKey,proto3" json:"ephemeral_public_key,omitempty"`
@@ -854,7 +854,7 @@ type RewrapResponse struct {
EntityWrappedKey []byte `protobuf:"bytes,2,opt,name=entity_wrapped_key,json=entityWrappedKey,proto3" json:"entity_wrapped_key,omitempty"`
// KAS's ephemeral session public key in PEM format
// Required: For EC-based operations (key_type="ec-wrapped")
- // Optional: Empty for RSA-based ZTDF (key_type="wrapped")
+ // Optional: Empty for RSA-based or X-Wing-based ZTDF (key_type="wrapped" or key_type="hybrid-wrapped")
// Used by client to perform ECDH key agreement and decrypt the kas_wrapped_key values
SessionPublicKey string `protobuf:"bytes,3,opt,name=session_public_key,json=sessionPublicKey,proto3" json:"session_public_key,omitempty"`
// Deprecated: Legacy schema version identifier
diff --git a/protocol/go/policy/kasregistry/key_access_server_registry.pb.go b/protocol/go/policy/kasregistry/key_access_server_registry.pb.go
index 569a2dd0c4..5bb3c2a08b 100644
--- a/protocol/go/policy/kasregistry/key_access_server_registry.pb.go
+++ b/protocol/go/policy/kasregistry/key_access_server_registry.pb.go
@@ -4277,555 +4277,557 @@ var file_policy_kasregistry_key_access_server_registry_proto_rawDesc = []byte{
0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c, 0x69,
0x63, 0x79, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52,
0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x02, 0x18, 0x01, 0x22,
- 0xcc, 0x0c, 0x0a, 0x10, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71,
+ 0xd5, 0x0c, 0x0a, 0x10, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x06, 0x6b, 0x61, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x05,
0x6b, 0x61, 0x73, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18,
0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x05,
- 0x6b, 0x65, 0x79, 0x49, 0x64, 0x12, 0xa4, 0x01, 0x0a, 0x0d, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c,
+ 0x6b, 0x65, 0x79, 0x49, 0x64, 0x12, 0xad, 0x01, 0x0a, 0x0d, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c,
0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e,
0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d,
- 0x42, 0x6c, 0xba, 0x48, 0x69, 0xba, 0x01, 0x66, 0x0a, 0x15, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c,
+ 0x42, 0x75, 0xba, 0x48, 0x72, 0xba, 0x01, 0x6f, 0x0a, 0x15, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c,
0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x12,
0x34, 0x54, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74,
0x68, 0x6d, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f,
0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x76, 0x61,
- 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x1a, 0x17, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x5b,
- 0x31, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x35, 0x5d, 0x52, 0x0c,
- 0x6b, 0x65, 0x79, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x93, 0x01, 0x0a,
- 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32,
- 0x0f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x65, 0x79, 0x4d, 0x6f, 0x64, 0x65,
- 0x42, 0x67, 0xba, 0x48, 0x64, 0xba, 0x01, 0x61, 0x0a, 0x10, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f,
- 0x64, 0x65, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x35, 0x54, 0x68, 0x65, 0x20,
- 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65,
- 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69,
- 0x6e, 0x65, 0x64, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x28, 0x31, 0x2d, 0x34, 0x29,
- 0x2e, 0x1a, 0x16, 0x74, 0x68, 0x69, 0x73, 0x20, 0x3e, 0x3d, 0x20, 0x31, 0x20, 0x26, 0x26, 0x20,
- 0x74, 0x68, 0x69, 0x73, 0x20, 0x3c, 0x3d, 0x20, 0x34, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x4d, 0x6f,
- 0x64, 0x65, 0x12, 0x42, 0x0a, 0x0e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79,
- 0x5f, 0x63, 0x74, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c,
- 0x69, 0x63, 0x79, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78,
- 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x0c, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63,
- 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x12, 0x3d, 0x0a, 0x0f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74,
- 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32,
- 0x15, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65,
- 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x52, 0x0d, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b,
- 0x65, 0x79, 0x43, 0x74, 0x78, 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65,
- 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28,
- 0x09, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69,
- 0x67, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x18, 0x08, 0x20,
- 0x01, 0x28, 0x08, 0x52, 0x06, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x12, 0x33, 0x0a, 0x08, 0x6d,
- 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e,
- 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4d,
- 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
- 0x3a, 0xbb, 0x07, 0xba, 0x48, 0xb7, 0x07, 0x1a, 0x97, 0x03, 0x0a, 0x23, 0x70, 0x72, 0x69, 0x76,
- 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x5f, 0x6f, 0x70, 0x74, 0x69,
- 0x6f, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12,
- 0xbc, 0x01, 0x54, 0x68, 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65,
- 0x79, 0x20, 0x69, 0x73, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x69, 0x66,
- 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59,
- 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x52, 0x4f, 0x4f,
- 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x20, 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44,
- 0x45, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, 0x52, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f,
- 0x4b, 0x45, 0x59, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64,
- 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x65, 0x6d, 0x70,
- 0x74, 0x79, 0x20, 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69,
- 0x73, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54,
- 0x45, 0x20, 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x55,
- 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x2e, 0x1a, 0xb0,
- 0x01, 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65,
- 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65,
- 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x29, 0x20, 0x26, 0x26, 0x20,
- 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79,
- 0x5f, 0x63, 0x74, 0x78, 0x2e, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79,
- 0x20, 0x21, 0x3d, 0x20, 0x27, 0x27, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x28, 0x74, 0x68, 0x69,
- 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x33, 0x20,
- 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65,
- 0x20, 0x3d, 0x3d, 0x20, 0x34, 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70,
- 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x2e, 0x77,
- 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x27,
- 0x29, 0x1a, 0xf4, 0x02, 0x0a, 0x26, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63,
- 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61,
- 0x6c, 0x6c, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0xa8, 0x01, 0x50,
- 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x20, 0x69,
- 0x64, 0x20, 0x69, 0x73, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x69, 0x66,
- 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59,
- 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, 0x52, 0x5f, 0x52,
- 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x20, 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d,
- 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x2e, 0x20, 0x49, 0x74, 0x20, 0x6d,
- 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x66, 0x6f, 0x72,
- 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47,
- 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x4b, 0x45,
+ 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x1a, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x5b,
+ 0x31, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x36,
+ 0x2c, 0x20, 0x37, 0x2c, 0x20, 0x38, 0x5d, 0x52, 0x0c, 0x6b, 0x65, 0x79, 0x41, 0x6c, 0x67, 0x6f,
+ 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x93, 0x01, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f,
+ 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63,
+ 0x79, 0x2e, 0x4b, 0x65, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x42, 0x67, 0xba, 0x48, 0x64, 0xba, 0x01,
+ 0x61, 0x0a, 0x10, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x5f, 0x64, 0x65, 0x66, 0x69,
+ 0x6e, 0x65, 0x64, 0x12, 0x35, 0x54, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64,
+ 0x65, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x76, 0x61, 0x6c,
+ 0x75, 0x65, 0x73, 0x20, 0x28, 0x31, 0x2d, 0x34, 0x29, 0x2e, 0x1a, 0x16, 0x74, 0x68, 0x69, 0x73,
+ 0x20, 0x3e, 0x3d, 0x20, 0x31, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x3c, 0x3d,
+ 0x20, 0x34, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x42, 0x0a, 0x0e, 0x70,
+ 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x18, 0x05, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x75, 0x62,
+ 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01,
+ 0x01, 0x52, 0x0c, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x12,
+ 0x3d, 0x0a, 0x0f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63,
+ 0x74, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63,
+ 0x79, 0x2e, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x52,
+ 0x0d, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x12, 0x2c,
+ 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69,
+ 0x67, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x76,
+ 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06,
+ 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x6c, 0x65,
+ 0x67, 0x61, 0x63, 0x79, 0x12, 0x33, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
+ 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e,
+ 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52,
+ 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x3a, 0xbb, 0x07, 0xba, 0x48, 0xb7, 0x07,
+ 0x1a, 0x97, 0x03, 0x0a, 0x23, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79,
+ 0x5f, 0x63, 0x74, 0x78, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x5f,
+ 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0xbc, 0x01, 0x54, 0x68, 0x65, 0x20, 0x77,
+ 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x73, 0x20, 0x72, 0x65,
+ 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f,
+ 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x43,
+ 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x20, 0x6f,
+ 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49,
+ 0x44, 0x45, 0x52, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x2e, 0x20, 0x54, 0x68,
+ 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x6d, 0x75,
+ 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x69, 0x66, 0x20, 0x6b,
+ 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d,
+ 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x20, 0x6f, 0x72, 0x20, 0x4b, 0x45,
0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45,
- 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x2e, 0x1a, 0x9e, 0x01, 0x28, 0x28, 0x74, 0x68, 0x69, 0x73,
+ 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x2e, 0x1a, 0xb0, 0x01, 0x28, 0x28, 0x74, 0x68, 0x69, 0x73,
0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x20, 0x7c,
0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20,
- 0x3d, 0x3d, 0x20, 0x34, 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, 0x72,
- 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64,
- 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x27, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x28, 0x74, 0x68, 0x69,
- 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x20,
- 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65,
- 0x20, 0x3d, 0x3d, 0x20, 0x33, 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70,
+ 0x3d, 0x3d, 0x20, 0x32, 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, 0x72,
+ 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x2e, 0x77, 0x72,
+ 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x27, 0x29,
+ 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d,
+ 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x33, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73,
+ 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x34, 0x29, 0x20,
+ 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f,
+ 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x2e, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f,
+ 0x6b, 0x65, 0x79, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x27, 0x29, 0x1a, 0xf4, 0x02, 0x0a, 0x26, 0x70,
0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69,
- 0x64, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x27, 0x29, 0x1a, 0xa3, 0x01, 0x0a, 0x23, 0x70, 0x72, 0x69,
- 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x5f, 0x66, 0x6f, 0x72,
- 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x6f, 0x6e, 0x6c, 0x79,
- 0x12, 0x48, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74,
- 0x78, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65,
- 0x74, 0x20, 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73,
- 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43,
- 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x2e, 0x1a, 0x32, 0x21, 0x28, 0x74, 0x68,
- 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x34,
- 0x20, 0x26, 0x26, 0x20, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, 0x72, 0x69,
- 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x29, 0x29, 0x22, 0x3c,
- 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f,
+ 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x5f, 0x72, 0x65, 0x71,
+ 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0xa8, 0x01, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72,
+ 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x20, 0x69, 0x64, 0x20, 0x69, 0x73, 0x20, 0x72, 0x65,
+ 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f,
+ 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50,
+ 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, 0x52, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59,
+ 0x20, 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, 0x4d,
+ 0x4f, 0x54, 0x45, 0x2e, 0x20, 0x49, 0x74, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20,
+ 0x65, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f,
+ 0x44, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b,
+ 0x45, 0x59, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f,
+ 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x2e,
+ 0x1a, 0x9e, 0x01, 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f,
+ 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e,
+ 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x34, 0x29, 0x20, 0x26,
+ 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f,
+ 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x27, 0x29,
+ 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d,
+ 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73,
+ 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x33, 0x29, 0x20,
+ 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72,
+ 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x27,
+ 0x29, 0x1a, 0xa3, 0x01, 0x0a, 0x23, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65,
+ 0x79, 0x5f, 0x63, 0x74, 0x78, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63,
+ 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x12, 0x48, 0x70, 0x72, 0x69, 0x76, 0x61,
+ 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20,
+ 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x74, 0x20, 0x69, 0x66, 0x20, 0x6b, 0x65,
+ 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f,
+ 0x44, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x4e,
+ 0x4c, 0x59, 0x2e, 0x1a, 0x32, 0x21, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f,
+ 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x34, 0x20, 0x26, 0x26, 0x20, 0x68, 0x61, 0x73,
+ 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65,
+ 0x79, 0x5f, 0x63, 0x74, 0x78, 0x29, 0x29, 0x22, 0x3c, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74,
+ 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x07,
+ 0x6b, 0x61, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e,
+ 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x52, 0x06, 0x6b,
+ 0x61, 0x73, 0x4b, 0x65, 0x79, 0x22, 0x7a, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x52,
+ 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01,
+ 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x02,
+ 0x69, 0x64, 0x12, 0x38, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32,
+ 0x24, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69,
+ 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74,
+ 0x69, 0x66, 0x69, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x42, 0x13, 0x0a, 0x0a,
+ 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08,
+ 0x01, 0x22, 0x39, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x07, 0x6b, 0x61, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x61,
- 0x73, 0x4b, 0x65, 0x79, 0x52, 0x06, 0x6b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x22, 0x7a, 0x0a, 0x0d,
- 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a,
- 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03,
- 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x02, 0x69, 0x64, 0x12, 0x38, 0x0a, 0x03, 0x6b, 0x65, 0x79,
- 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e,
- 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b,
- 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03,
- 0x6b, 0x65, 0x79, 0x42, 0x13, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65,
- 0x72, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, 0x22, 0x39, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4b,
- 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x07, 0x6b, 0x61,
- 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f,
- 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x52, 0x06, 0x6b, 0x61, 0x73,
- 0x4b, 0x65, 0x79, 0x22, 0xd5, 0x03, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x73,
- 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0xa7, 0x01, 0x0a, 0x0d, 0x6b, 0x65, 0x79, 0x5f,
- 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32,
- 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74,
- 0x68, 0x6d, 0x42, 0x6f, 0xba, 0x48, 0x6c, 0xba, 0x01, 0x69, 0x0a, 0x15, 0x6b, 0x65, 0x79, 0x5f,
- 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65,
- 0x64, 0x12, 0x34, 0x54, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72,
- 0x69, 0x74, 0x68, 0x6d, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x6f, 0x6e, 0x65,
- 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20,
- 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x1a, 0x1a, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x6e,
- 0x20, 0x5b, 0x30, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x34, 0x2c,
- 0x20, 0x35, 0x5d, 0x52, 0x0c, 0x6b, 0x65, 0x79, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68,
- 0x6d, 0x12, 0x21, 0x0a, 0x06, 0x6b, 0x61, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,
- 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x05, 0x6b,
- 0x61, 0x73, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x08, 0x6b, 0x61, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
- 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x48,
- 0x00, 0x52, 0x07, 0x6b, 0x61, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x07, 0x6b, 0x61,
- 0x73, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07,
- 0x72, 0x05, 0x10, 0x01, 0x88, 0x01, 0x01, 0x48, 0x00, 0x52, 0x06, 0x6b, 0x61, 0x73, 0x55, 0x72,
- 0x69, 0x12, 0x1b, 0x0a, 0x06, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28,
- 0x08, 0x48, 0x01, 0x52, 0x06, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x88, 0x01, 0x01, 0x12, 0x33,
- 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01,
- 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x61, 0x67, 0x65,
- 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74,
- 0x69, 0x6f, 0x6e, 0x12, 0x3d, 0x0a, 0x04, 0x73, 0x6f, 0x72, 0x74, 0x18, 0x0b, 0x20, 0x03, 0x28,
- 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65,
- 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x73, 0x53, 0x6f,
- 0x72, 0x74, 0x42, 0x08, 0xba, 0x48, 0x05, 0x92, 0x01, 0x02, 0x10, 0x01, 0x52, 0x04, 0x73, 0x6f,
- 0x72, 0x74, 0x42, 0x0c, 0x0a, 0x0a, 0x6b, 0x61, 0x73, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72,
- 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x22, 0x73, 0x0a, 0x10, 0x4c,
- 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
- 0x29, 0x0a, 0x08, 0x6b, 0x61, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
- 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65,
- 0x79, 0x52, 0x07, 0x6b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x34, 0x0a, 0x0a, 0x70, 0x61,
- 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14,
- 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70,
- 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e,
- 0x22, 0x86, 0x03, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65,
- 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
- 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12,
- 0x33, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x64, 0x20, 0x01, 0x28,
- 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64,
- 0x61, 0x74, 0x61, 0x4d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61,
- 0x64, 0x61, 0x74, 0x61, 0x12, 0x54, 0x0a, 0x18, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
- 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72,
- 0x18, 0x65, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e,
- 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e,
- 0x75, 0x6d, 0x52, 0x16, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61,
- 0x74, 0x65, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x3a, 0xcc, 0x01, 0xba, 0x48, 0xc8,
- 0x01, 0x1a, 0xc5, 0x01, 0x0a, 0x18, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x75,
- 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x12, 0x52,
- 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20,
- 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65,
- 0x20, 0x65, 0x69, 0x74, 0x68, 0x65, 0x72, 0x20, 0x41, 0x50, 0x50, 0x45, 0x4e, 0x44, 0x20, 0x6f,
- 0x72, 0x20, 0x52, 0x45, 0x50, 0x4c, 0x41, 0x43, 0x45, 0x2c, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20,
- 0x75, 0x70, 0x64, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
- 0x61, 0x2e, 0x1a, 0x55, 0x28, 0x28, 0x21, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e,
- 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x29, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x68,
- 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
- 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61,
+ 0x73, 0x4b, 0x65, 0x79, 0x52, 0x06, 0x6b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x22, 0xde, 0x03, 0x0a,
+ 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+ 0x12, 0xb0, 0x01, 0x0a, 0x0d, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74,
+ 0x68, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63,
+ 0x79, 0x2e, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x42, 0x78, 0xba, 0x48, 0x75,
+ 0xba, 0x01, 0x72, 0x0a, 0x15, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74,
+ 0x68, 0x6d, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x34, 0x54, 0x68, 0x65, 0x20,
+ 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x20, 0x6d, 0x75,
+ 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e,
+ 0x1a, 0x23, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x5b, 0x30, 0x2c, 0x20, 0x31, 0x2c,
+ 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x36, 0x2c, 0x20,
+ 0x37, 0x2c, 0x20, 0x38, 0x5d, 0x52, 0x0c, 0x6b, 0x65, 0x79, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69,
+ 0x74, 0x68, 0x6d, 0x12, 0x21, 0x0a, 0x06, 0x6b, 0x61, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20,
+ 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52,
+ 0x05, 0x6b, 0x61, 0x73, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x08, 0x6b, 0x61, 0x73, 0x5f, 0x6e, 0x61,
+ 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10,
+ 0x01, 0x48, 0x00, 0x52, 0x07, 0x6b, 0x61, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x07,
+ 0x6b, 0x61, 0x73, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba,
+ 0x48, 0x07, 0x72, 0x05, 0x10, 0x01, 0x88, 0x01, 0x01, 0x48, 0x00, 0x52, 0x06, 0x6b, 0x61, 0x73,
+ 0x55, 0x72, 0x69, 0x12, 0x1b, 0x0a, 0x06, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x18, 0x08, 0x20,
+ 0x01, 0x28, 0x08, 0x48, 0x01, 0x52, 0x06, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x88, 0x01, 0x01,
+ 0x12, 0x33, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a,
+ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x61,
+ 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3d, 0x0a, 0x04, 0x73, 0x6f, 0x72, 0x74, 0x18, 0x0b, 0x20,
+ 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73,
+ 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x73,
+ 0x53, 0x6f, 0x72, 0x74, 0x42, 0x08, 0xba, 0x48, 0x05, 0x92, 0x01, 0x02, 0x10, 0x01, 0x52, 0x04,
+ 0x73, 0x6f, 0x72, 0x74, 0x42, 0x0c, 0x0a, 0x0a, 0x6b, 0x61, 0x73, 0x5f, 0x66, 0x69, 0x6c, 0x74,
+ 0x65, 0x72, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x22, 0x73, 0x0a,
+ 0x10, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
+ 0x65, 0x12, 0x29, 0x0a, 0x08, 0x6b, 0x61, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20,
+ 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x61, 0x73,
+ 0x4b, 0x65, 0x79, 0x52, 0x07, 0x6b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x34, 0x0a, 0x0a,
+ 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b,
+ 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65,
+ 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x22, 0x86, 0x03, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79,
+ 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20,
+ 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x02, 0x69,
+ 0x64, 0x12, 0x33, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x64, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74,
+ 0x61, 0x64, 0x61, 0x74, 0x61, 0x4d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x08, 0x6d, 0x65,
+ 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x54, 0x0a, 0x18, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61,
0x74, 0x61, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69,
- 0x6f, 0x72, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x29, 0x22, 0x3c, 0x0a, 0x11, 0x55, 0x70, 0x64,
- 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27,
- 0x0a, 0x07, 0x6b, 0x61, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
- 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x52,
- 0x06, 0x6b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x22, 0xa4, 0x01, 0x0a, 0x10, 0x4b, 0x61, 0x73, 0x4b,
- 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x06,
- 0x6b, 0x61, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48,
- 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x05, 0x6b, 0x61, 0x73, 0x49, 0x64, 0x12,
- 0x1d, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba,
- 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x48, 0x00, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e,
- 0x0a, 0x03, 0x75, 0x72, 0x69, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07,
- 0x72, 0x05, 0x10, 0x01, 0x88, 0x01, 0x01, 0x48, 0x00, 0x52, 0x03, 0x75, 0x72, 0x69, 0x12, 0x19,
- 0x0a, 0x03, 0x6b, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04,
- 0x72, 0x02, 0x10, 0x01, 0x52, 0x03, 0x6b, 0x69, 0x64, 0x42, 0x13, 0x0a, 0x0a, 0x69, 0x64, 0x65,
- 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, 0x22, 0xe5,
- 0x0e, 0x0a, 0x10, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75,
- 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42,
- 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x02, 0x69, 0x64, 0x12,
- 0x38, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x70,
+ 0x6f, 0x72, 0x18, 0x65, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
+ 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
+ 0x45, 0x6e, 0x75, 0x6d, 0x52, 0x16, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x55, 0x70,
+ 0x64, 0x61, 0x74, 0x65, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x3a, 0xcc, 0x01, 0xba,
+ 0x48, 0xc8, 0x01, 0x1a, 0xc5, 0x01, 0x0a, 0x18, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
+ 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72,
+ 0x12, 0x52, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74,
+ 0x65, 0x20, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20,
+ 0x62, 0x65, 0x20, 0x65, 0x69, 0x74, 0x68, 0x65, 0x72, 0x20, 0x41, 0x50, 0x50, 0x45, 0x4e, 0x44,
+ 0x20, 0x6f, 0x72, 0x20, 0x52, 0x45, 0x50, 0x4c, 0x41, 0x43, 0x45, 0x2c, 0x20, 0x77, 0x68, 0x65,
+ 0x6e, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64,
+ 0x61, 0x74, 0x61, 0x2e, 0x1a, 0x55, 0x28, 0x28, 0x21, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69,
+ 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x29, 0x29, 0x20, 0x7c, 0x7c, 0x20,
+ 0x28, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61,
+ 0x74, 0x61, 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61,
+ 0x64, 0x61, 0x74, 0x61, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x62, 0x65, 0x68, 0x61,
+ 0x76, 0x69, 0x6f, 0x72, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x29, 0x22, 0x3c, 0x0a, 0x11, 0x55,
+ 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+ 0x12, 0x27, 0x0a, 0x07, 0x6b, 0x61, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65,
+ 0x79, 0x52, 0x06, 0x6b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x22, 0xa4, 0x01, 0x0a, 0x10, 0x4b, 0x61,
+ 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x21,
+ 0x0a, 0x06, 0x6b, 0x61, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08,
+ 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x05, 0x6b, 0x61, 0x73, 0x49,
+ 0x64, 0x12, 0x1d, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42,
+ 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x48, 0x00, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
+ 0x12, 0x1e, 0x0a, 0x03, 0x75, 0x72, 0x69, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba,
+ 0x48, 0x07, 0x72, 0x05, 0x10, 0x01, 0x88, 0x01, 0x01, 0x48, 0x00, 0x52, 0x03, 0x75, 0x72, 0x69,
+ 0x12, 0x19, 0x0a, 0x03, 0x6b, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba,
+ 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x03, 0x6b, 0x69, 0x64, 0x42, 0x13, 0x0a, 0x0a, 0x69,
+ 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01,
+ 0x22, 0xee, 0x0e, 0x0a, 0x10, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65,
+ 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x02, 0x69,
+ 0x64, 0x12, 0x38, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24,
+ 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73,
+ 0x74, 0x72, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69,
+ 0x66, 0x69, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x44, 0x0a, 0x07, 0x6e,
+ 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x70,
0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72,
- 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69,
- 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x44, 0x0a, 0x07, 0x6e, 0x65, 0x77,
- 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x70, 0x6f, 0x6c,
- 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e,
- 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
- 0x2e, 0x4e, 0x65, 0x77, 0x4b, 0x65, 0x79, 0x52, 0x06, 0x6e, 0x65, 0x77, 0x4b, 0x65, 0x79, 0x1a,
- 0xcf, 0x04, 0x0a, 0x06, 0x4e, 0x65, 0x77, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x06, 0x6b, 0x65,
- 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72,
- 0x02, 0x10, 0x01, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x9d, 0x01, 0x0a, 0x09, 0x61,
- 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11,
- 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68,
- 0x6d, 0x42, 0x6c, 0xba, 0x48, 0x69, 0xba, 0x01, 0x66, 0x0a, 0x15, 0x6b, 0x65, 0x79, 0x5f, 0x61,
- 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64,
- 0x12, 0x34, 0x54, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69,
- 0x74, 0x68, 0x6d, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x6f, 0x6e, 0x65, 0x20,
- 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x76,
- 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x1a, 0x17, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x20,
- 0x5b, 0x31, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x35, 0x5d, 0x52,
- 0x09, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x9e, 0x01, 0x0a, 0x08, 0x6b,
- 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0f, 0x2e,
- 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x65, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x42, 0x72,
- 0xba, 0x48, 0x6f, 0xba, 0x01, 0x67, 0x0a, 0x14, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x5f,
- 0x6d, 0x6f, 0x64, 0x65, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x39, 0x54, 0x68,
- 0x65, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x6d,
- 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68,
- 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73,
- 0x20, 0x28, 0x31, 0x2d, 0x34, 0x29, 0x2e, 0x1a, 0x14, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x6e,
- 0x20, 0x5b, 0x31, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x34, 0x5d, 0x82, 0x01, 0x02,
- 0x10, 0x01, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x42, 0x0a, 0x0e, 0x70,
- 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x18, 0x04, 0x20,
- 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x75, 0x62,
- 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01,
- 0x01, 0x52, 0x0c, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x12,
- 0x3d, 0x0a, 0x0f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63,
- 0x74, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63,
- 0x79, 0x2e, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x52,
- 0x0d, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x12, 0x2c,
- 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69,
- 0x67, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x76,
- 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x49, 0x64, 0x12, 0x33, 0x0a, 0x08,
- 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17,
- 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
- 0x4d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
- 0x61, 0x3a, 0xcd, 0x08, 0xba, 0x48, 0xc9, 0x08, 0x1a, 0xd8, 0x03, 0x0a, 0x23, 0x70, 0x72, 0x69,
- 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x5f, 0x6f, 0x70, 0x74,
- 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64,
- 0x12, 0xcd, 0x01, 0x46, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x6b,
- 0x65, 0x79, 0x2c, 0x20, 0x74, 0x68, 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f,
- 0x6b, 0x65, 0x79, 0x20, 0x69, 0x73, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20,
+ 0x79, 0x2e, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65,
+ 0x73, 0x74, 0x2e, 0x4e, 0x65, 0x77, 0x4b, 0x65, 0x79, 0x52, 0x06, 0x6e, 0x65, 0x77, 0x4b, 0x65,
+ 0x79, 0x1a, 0xd8, 0x04, 0x0a, 0x06, 0x4e, 0x65, 0x77, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x06,
+ 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48,
+ 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x12, 0xa6, 0x01, 0x0a,
+ 0x09, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e,
+ 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69,
+ 0x74, 0x68, 0x6d, 0x42, 0x75, 0xba, 0x48, 0x72, 0xba, 0x01, 0x6f, 0x0a, 0x15, 0x6b, 0x65, 0x79,
+ 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e,
+ 0x65, 0x64, 0x12, 0x34, 0x54, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f,
+ 0x72, 0x69, 0x74, 0x68, 0x6d, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x6f, 0x6e,
+ 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64,
+ 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x1a, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69,
+ 0x6e, 0x20, 0x5b, 0x31, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x35,
+ 0x2c, 0x20, 0x36, 0x2c, 0x20, 0x37, 0x2c, 0x20, 0x38, 0x5d, 0x52, 0x09, 0x61, 0x6c, 0x67, 0x6f,
+ 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x9e, 0x01, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f,
+ 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63,
+ 0x79, 0x2e, 0x4b, 0x65, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x42, 0x72, 0xba, 0x48, 0x6f, 0xba, 0x01,
+ 0x67, 0x0a, 0x14, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x5f,
+ 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x39, 0x54, 0x68, 0x65, 0x20, 0x6e, 0x65, 0x77,
+ 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62,
+ 0x65, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x66,
+ 0x69, 0x6e, 0x65, 0x64, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x28, 0x31, 0x2d, 0x34,
+ 0x29, 0x2e, 0x1a, 0x14, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x5b, 0x31, 0x2c, 0x20,
+ 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x34, 0x5d, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x6b,
+ 0x65, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x42, 0x0a, 0x0e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63,
+ 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14,
+ 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65,
+ 0x79, 0x43, 0x74, 0x78, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x0c, 0x70, 0x75,
+ 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x12, 0x3d, 0x0a, 0x0f, 0x70, 0x72,
+ 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x18, 0x05, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x72, 0x69,
+ 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x52, 0x0d, 0x70, 0x72, 0x69, 0x76,
+ 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x72, 0x6f,
+ 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, 0x18,
+ 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43,
+ 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x49, 0x64, 0x12, 0x33, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64,
+ 0x61, 0x74, 0x61, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
+ 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4d, 0x75, 0x74, 0x61, 0x62,
+ 0x6c, 0x65, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x3a, 0xcd, 0x08, 0xba,
+ 0x48, 0xc9, 0x08, 0x1a, 0xd8, 0x03, 0x0a, 0x23, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f,
+ 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c,
+ 0x6c, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0xcd, 0x01, 0x46, 0x6f,
+ 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x6b, 0x65, 0x79, 0x2c, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x69,
+ 0x73, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x69, 0x66, 0x20, 0x6b, 0x65,
+ 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f,
+ 0x44, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b,
+ 0x45, 0x59, 0x20, 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50,
+ 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, 0x52, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59,
+ 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65,
+ 0x79, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x20,
0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b,
- 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x52,
- 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x20, 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d,
+ 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x20, 0x6f,
+ 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49,
+ 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x2e, 0x1a, 0xe0, 0x01, 0x28, 0x28,
+ 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x6b, 0x65, 0x79,
+ 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68,
+ 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d,
+ 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69,
+ 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74,
+ 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x2e, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65,
+ 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x27, 0x29, 0x20, 0x7c, 0x7c, 0x20,
+ 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x6b,
+ 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x33, 0x20, 0x7c, 0x7c, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x6b, 0x65, 0x79,
+ 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x34, 0x29, 0x20, 0x26, 0x26, 0x20, 0x74,
+ 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x69, 0x76,
+ 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x2e, 0x77, 0x72, 0x61, 0x70,
+ 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x27, 0x29, 0x1a, 0xb5,
+ 0x03, 0x0a, 0x26, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66,
+ 0x69, 0x67, 0x5f, 0x69, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x6c, 0x79,
+ 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0xb9, 0x01, 0x46, 0x6f, 0x72, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x6b, 0x65, 0x79, 0x2c, 0x20, 0x70, 0x72, 0x6f,
+ 0x76, 0x69, 0x64, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x20, 0x69, 0x64, 0x20,
+ 0x69, 0x73, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x69, 0x66, 0x20, 0x6b,
+ 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d,
0x4f, 0x44, 0x45, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, 0x52, 0x5f, 0x52, 0x4f, 0x4f,
- 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70,
- 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x65,
- 0x6d, 0x70, 0x74, 0x79, 0x20, 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65,
- 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, 0x4d,
- 0x4f, 0x54, 0x45, 0x20, 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f,
- 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x2e,
- 0x1a, 0xe0, 0x01, 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65,
- 0x79, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x20,
- 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e,
- 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x29, 0x20, 0x26,
- 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x70,
- 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x2e, 0x77,
- 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x27,
- 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f,
- 0x6b, 0x65, 0x79, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20,
- 0x33, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65,
- 0x79, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x34, 0x29,
- 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79,
- 0x2e, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78,
- 0x2e, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x3d, 0x3d, 0x20,
- 0x27, 0x27, 0x29, 0x1a, 0xb5, 0x03, 0x0a, 0x26, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72,
- 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f,
- 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0xb9,
- 0x01, 0x46, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x6b, 0x65, 0x79,
- 0x2c, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69,
- 0x67, 0x20, 0x69, 0x64, 0x20, 0x69, 0x73, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64,
- 0x20, 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20,
- 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45,
- 0x52, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x20, 0x6f, 0x72, 0x20, 0x4b, 0x45,
- 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x2e, 0x20, 0x49,
- 0x74, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x20,
- 0x66, 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x43, 0x4f, 0x4e,
- 0x46, 0x49, 0x47, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x20, 0x61, 0x6e, 0x64,
- 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43,
- 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x2e, 0x1a, 0xce, 0x01, 0x28, 0x28, 0x74,
- 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x6b, 0x65, 0x79, 0x5f,
- 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69,
- 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f,
- 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x34, 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73,
- 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65,
- 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x27,
- 0x27, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77,
+ 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x20, 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44,
+ 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x2e, 0x20, 0x49, 0x74, 0x20, 0x6d, 0x75, 0x73,
+ 0x74, 0x20, 0x62, 0x65, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x4b,
+ 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x52,
+ 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x4b, 0x45, 0x59, 0x5f,
+ 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f,
+ 0x4f, 0x4e, 0x4c, 0x59, 0x2e, 0x1a, 0xce, 0x01, 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e,
+ 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20,
+ 0x3d, 0x3d, 0x20, 0x31, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77,
0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d,
- 0x20, 0x32, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b,
- 0x65, 0x79, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x33,
- 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65,
- 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69,
- 0x67, 0x5f, 0x69, 0x64, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x27, 0x29, 0x1a, 0xb3, 0x01, 0x0a, 0x23,
- 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x5f,
- 0x66, 0x6f, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x6f,
- 0x6e, 0x6c, 0x79, 0x12, 0x48, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79,
- 0x5f, 0x63, 0x74, 0x78, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65,
- 0x20, 0x73, 0x65, 0x74, 0x20, 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65,
- 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x55, 0x42,
- 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x2e, 0x1a, 0x42, 0x21,
- 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x6b, 0x65,
- 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x34, 0x20, 0x26, 0x26, 0x20, 0x68,
- 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e,
- 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x29,
- 0x29, 0x42, 0x13, 0x0a, 0x0a, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x12,
- 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, 0x22, 0x32, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65,
- 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01,
- 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x71, 0x6e, 0x18,
- 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x66, 0x71, 0x6e, 0x22, 0xe3, 0x02, 0x0a, 0x10, 0x52,
- 0x6f, 0x74, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12,
- 0x36, 0x0a, 0x0f, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x75, 0x74, 0x5f, 0x6b,
- 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63,
- 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x52, 0x0d, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x65,
- 0x64, 0x4f, 0x75, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x66, 0x0a, 0x1d, 0x61, 0x74, 0x74, 0x72, 0x69,
- 0x62, 0x75, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f,
- 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22,
- 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73,
- 0x74, 0x72, 0x79, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e,
- 0x67, 0x73, 0x52, 0x1b, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x44, 0x65, 0x66,
- 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x12,
- 0x5c, 0x0a, 0x18, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x6c,
- 0x75, 0x65, 0x5f, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28,
- 0x0b, 0x32, 0x22, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65,
- 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4d, 0x61, 0x70,
- 0x70, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x16, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65,
- 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x51, 0x0a,
- 0x12, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6d, 0x61, 0x70, 0x70, 0x69,
- 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x70, 0x6f, 0x6c, 0x69,
+ 0x20, 0x34, 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f,
+ 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e,
+ 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x27, 0x29, 0x20, 0x7c, 0x7c,
+ 0x20, 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e,
+ 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x20, 0x7c, 0x7c,
+ 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x6b, 0x65,
+ 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x33, 0x29, 0x20, 0x26, 0x26, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f,
+ 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, 0x20,
+ 0x21, 0x3d, 0x20, 0x27, 0x27, 0x29, 0x1a, 0xb3, 0x01, 0x0a, 0x23, 0x70, 0x72, 0x69, 0x76, 0x61,
+ 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x70,
+ 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x12, 0x48,
+ 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x20,
+ 0x6d, 0x75, 0x73, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x74, 0x20,
+ 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b,
+ 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b,
+ 0x45, 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x2e, 0x1a, 0x42, 0x21, 0x28, 0x74, 0x68, 0x69, 0x73,
+ 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64,
+ 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x34, 0x20, 0x26, 0x26, 0x20, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68,
+ 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x69, 0x76, 0x61,
+ 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x29, 0x29, 0x42, 0x13, 0x0a, 0x0a,
+ 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08,
+ 0x01, 0x22, 0x32, 0x0a, 0x0e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4d, 0x61, 0x70, 0x70, 0x69,
+ 0x6e, 0x67, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
+ 0x02, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x71, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x03, 0x66, 0x71, 0x6e, 0x22, 0xe3, 0x02, 0x0a, 0x10, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65,
+ 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x36, 0x0a, 0x0f, 0x72, 0x6f,
+ 0x74, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x75, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x61, 0x73,
+ 0x4b, 0x65, 0x79, 0x52, 0x0d, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x64, 0x4f, 0x75, 0x74, 0x4b,
+ 0x65, 0x79, 0x12, 0x66, 0x0a, 0x1d, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f,
+ 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x70, 0x70, 0x69,
+ 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x70, 0x6f, 0x6c, 0x69,
0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x43,
- 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x11, 0x6e,
- 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73,
- 0x22, 0x8f, 0x01, 0x0a, 0x11, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65,
- 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x07, 0x6b, 0x61, 0x73, 0x5f, 0x6b, 0x65,
- 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79,
- 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x52, 0x06, 0x6b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x12,
- 0x51, 0x0a, 0x11, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75,
- 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x70, 0x6f, 0x6c,
- 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e,
- 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73,
- 0x52, 0x10, 0x72, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
- 0x65, 0x73, 0x22, 0x7e, 0x0a, 0x11, 0x53, 0x65, 0x74, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79,
- 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20,
- 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52,
- 0x02, 0x69, 0x64, 0x12, 0x38, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
- 0x32, 0x24, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67,
- 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e,
- 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x42, 0x13, 0x0a,
- 0x0a, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x12, 0x05, 0xba, 0x48, 0x02,
- 0x08, 0x01, 0x22, 0x13, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79,
- 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x45, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x42, 0x61,
- 0x73, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a,
- 0x08, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
- 0x14, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x4b,
- 0x61, 0x73, 0x4b, 0x65, 0x79, 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x22, 0x8e,
- 0x01, 0x0a, 0x12, 0x53, 0x65, 0x74, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73,
- 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x36, 0x0a, 0x0c, 0x6e, 0x65, 0x77, 0x5f, 0x62, 0x61, 0x73,
- 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f,
- 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x4b, 0x61, 0x73, 0x4b, 0x65,
- 0x79, 0x52, 0x0a, 0x6e, 0x65, 0x77, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x40, 0x0a,
- 0x11, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x6b,
- 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63,
- 0x79, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x52, 0x0f,
- 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x22,
- 0x36, 0x0a, 0x12, 0x4d, 0x61, 0x70, 0x70, 0x65, 0x64, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4f,
- 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
- 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x71, 0x6e, 0x18, 0x02, 0x20, 0x01,
- 0x28, 0x09, 0x52, 0x03, 0x66, 0x71, 0x6e, 0x22, 0xb4, 0x02, 0x0a, 0x0a, 0x4b, 0x65, 0x79, 0x4d,
- 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x69, 0x64, 0x18, 0x01, 0x20,
- 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x69, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x6b, 0x61, 0x73, 0x5f,
- 0x75, 0x72, 0x69, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6b, 0x61, 0x73, 0x55, 0x72,
- 0x69, 0x12, 0x55, 0x0a, 0x12, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6d,
- 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e,
- 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74,
- 0x72, 0x79, 0x2e, 0x4d, 0x61, 0x70, 0x70, 0x65, 0x64, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4f,
- 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x11, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65,
- 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x55, 0x0a, 0x12, 0x61, 0x74, 0x74, 0x72,
- 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04,
- 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61,
- 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4d, 0x61, 0x70, 0x70, 0x65, 0x64,
- 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x11, 0x61, 0x74,
- 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x12,
- 0x4d, 0x0a, 0x0e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67,
- 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79,
- 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4d, 0x61, 0x70,
- 0x70, 0x65, 0x64, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52,
- 0x0d, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x22, 0xb8,
- 0x01, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e,
- 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x02, 0x69, 0x64, 0x18,
- 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48,
- 0x00, 0x52, 0x02, 0x69, 0x64, 0x12, 0x38, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01,
- 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72,
- 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64,
- 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
- 0x33, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20,
- 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x61, 0x67,
- 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61,
- 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x13, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69,
- 0x65, 0x72, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x00, 0x22, 0x92, 0x01, 0x0a, 0x17, 0x4c, 0x69,
- 0x73, 0x74, 0x4b, 0x65, 0x79, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73,
- 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0c, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x61, 0x70,
- 0x70, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x6f,
+ 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x1b, 0x61,
+ 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69,
+ 0x6f, 0x6e, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x5c, 0x0a, 0x18, 0x61, 0x74,
+ 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x6d, 0x61,
+ 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x70,
+ 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72,
+ 0x79, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73,
+ 0x52, 0x16, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65,
+ 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x51, 0x0a, 0x12, 0x6e, 0x61, 0x6d, 0x65,
+ 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04,
+ 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61,
+ 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65,
+ 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x11, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70,
+ 0x61, 0x63, 0x65, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x8f, 0x01, 0x0a, 0x11,
+ 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
+ 0x65, 0x12, 0x27, 0x0a, 0x07, 0x6b, 0x61, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
+ 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b,
+ 0x65, 0x79, 0x52, 0x06, 0x6b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x12, 0x51, 0x0a, 0x11, 0x72, 0x6f,
+ 0x74, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18,
+ 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b,
+ 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x52, 0x6f, 0x74, 0x61, 0x74,
+ 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x10, 0x72, 0x6f, 0x74,
+ 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x22, 0x7e, 0x0a,
+ 0x11, 0x53, 0x65, 0x74, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65,
+ 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08,
+ 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x02, 0x69, 0x64, 0x12, 0x38,
+ 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x70, 0x6f,
0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79,
- 0x2e, 0x4b, 0x65, 0x79, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, 0x0b, 0x6b, 0x65, 0x79,
- 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x34, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69,
- 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70,
- 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
- 0x73, 0x65, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2a, 0xef,
- 0x01, 0x0a, 0x18, 0x53, 0x6f, 0x72, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73,
- 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2c, 0x0a, 0x28, 0x53,
- 0x4f, 0x52, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x53, 0x53, 0x5f, 0x53,
- 0x45, 0x52, 0x56, 0x45, 0x52, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50,
- 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x25, 0x0a, 0x21, 0x53, 0x4f, 0x52,
- 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x53, 0x53, 0x5f, 0x53, 0x45, 0x52,
- 0x56, 0x45, 0x52, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x41, 0x4d, 0x45, 0x10, 0x01,
- 0x12, 0x24, 0x0a, 0x20, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x41, 0x43, 0x43,
- 0x45, 0x53, 0x53, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45,
- 0x5f, 0x55, 0x52, 0x49, 0x10, 0x02, 0x12, 0x2b, 0x0a, 0x27, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x4b,
+ 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65,
+ 0x72, 0x48, 0x00, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x42, 0x13, 0x0a, 0x0a, 0x61, 0x63, 0x74, 0x69,
+ 0x76, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, 0x22, 0x13, 0x0a,
+ 0x11, 0x47, 0x65, 0x74, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65,
+ 0x73, 0x74, 0x22, 0x45, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79,
+ 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x08, 0x62, 0x61, 0x73, 0x65,
+ 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c,
+ 0x69, 0x63, 0x79, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79,
+ 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x22, 0x8e, 0x01, 0x0a, 0x12, 0x53, 0x65,
+ 0x74, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+ 0x12, 0x36, 0x0a, 0x0c, 0x6e, 0x65, 0x77, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x6b, 0x65, 0x79,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e,
+ 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x52, 0x0a, 0x6e, 0x65,
+ 0x77, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x40, 0x0a, 0x11, 0x70, 0x72, 0x65, 0x76,
+ 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x69, 0x6d,
+ 0x70, 0x6c, 0x65, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x52, 0x0f, 0x70, 0x72, 0x65, 0x76, 0x69,
+ 0x6f, 0x75, 0x73, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x22, 0x36, 0x0a, 0x12, 0x4d, 0x61,
+ 0x70, 0x70, 0x65, 0x64, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74,
+ 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64,
+ 0x12, 0x10, 0x0a, 0x03, 0x66, 0x71, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x66,
+ 0x71, 0x6e, 0x22, 0xb4, 0x02, 0x0a, 0x0a, 0x4b, 0x65, 0x79, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e,
+ 0x67, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03,
+ 0x6b, 0x69, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x6b, 0x61, 0x73, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x02,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6b, 0x61, 0x73, 0x55, 0x72, 0x69, 0x12, 0x55, 0x0a, 0x12,
+ 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e,
+ 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63,
+ 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4d, 0x61,
+ 0x70, 0x70, 0x65, 0x64, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74,
+ 0x52, 0x11, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4d, 0x61, 0x70, 0x70, 0x69,
+ 0x6e, 0x67, 0x73, 0x12, 0x55, 0x0a, 0x12, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65,
+ 0x5f, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32,
+ 0x26, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69,
+ 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4d, 0x61, 0x70, 0x70, 0x65, 0x64, 0x50, 0x6f, 0x6c, 0x69, 0x63,
+ 0x79, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x11, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75,
+ 0x74, 0x65, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x4d, 0x0a, 0x0e, 0x76, 0x61,
+ 0x6c, 0x75, 0x65, 0x5f, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x05, 0x20, 0x03,
+ 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72,
+ 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4d, 0x61, 0x70, 0x70, 0x65, 0x64, 0x50, 0x6f,
+ 0x6c, 0x69, 0x63, 0x79, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x0d, 0x76, 0x61, 0x6c, 0x75,
+ 0x65, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x22, 0xb8, 0x01, 0x0a, 0x16, 0x4c, 0x69,
+ 0x73, 0x74, 0x4b, 0x65, 0x79, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71,
+ 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+ 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x02, 0x69, 0x64,
+ 0x12, 0x38, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e,
+ 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74,
+ 0x72, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66,
+ 0x69, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x33, 0x0a, 0x0a, 0x70, 0x61,
+ 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13,
+ 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75,
+ 0x65, 0x73, 0x74, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42,
+ 0x13, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x05, 0xba,
+ 0x48, 0x02, 0x08, 0x00, 0x22, 0x92, 0x01, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79,
+ 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+ 0x12, 0x41, 0x0a, 0x0c, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73,
+ 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e,
+ 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4b, 0x65, 0x79, 0x4d,
+ 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, 0x0b, 0x6b, 0x65, 0x79, 0x4d, 0x61, 0x70, 0x70, 0x69,
+ 0x6e, 0x67, 0x73, 0x12, 0x34, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79,
+ 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0a, 0x70,
+ 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2a, 0xef, 0x01, 0x0a, 0x18, 0x53, 0x6f,
+ 0x72, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65,
+ 0x72, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2c, 0x0a, 0x28, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x4b,
0x45, 0x59, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x53, 0x53, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52,
- 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x44, 0x5f, 0x41,
- 0x54, 0x10, 0x03, 0x12, 0x2b, 0x0a, 0x27, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x5f,
- 0x41, 0x43, 0x43, 0x45, 0x53, 0x53, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x53, 0x5f, 0x54,
- 0x59, 0x50, 0x45, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x44, 0x5f, 0x41, 0x54, 0x10, 0x04,
- 0x2a, 0x9a, 0x01, 0x0a, 0x0f, 0x53, 0x6f, 0x72, 0x74, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x73,
- 0x54, 0x79, 0x70, 0x65, 0x12, 0x22, 0x0a, 0x1e, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x4b, 0x41, 0x53,
- 0x5f, 0x4b, 0x45, 0x59, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45,
- 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1d, 0x0a, 0x19, 0x53, 0x4f, 0x52, 0x54,
- 0x5f, 0x4b, 0x41, 0x53, 0x5f, 0x4b, 0x45, 0x59, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4b,
- 0x45, 0x59, 0x5f, 0x49, 0x44, 0x10, 0x01, 0x12, 0x21, 0x0a, 0x1d, 0x53, 0x4f, 0x52, 0x54, 0x5f,
- 0x4b, 0x41, 0x53, 0x5f, 0x4b, 0x45, 0x59, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x52,
- 0x45, 0x41, 0x54, 0x45, 0x44, 0x5f, 0x41, 0x54, 0x10, 0x02, 0x12, 0x21, 0x0a, 0x1d, 0x53, 0x4f,
- 0x52, 0x54, 0x5f, 0x4b, 0x41, 0x53, 0x5f, 0x4b, 0x45, 0x59, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45,
- 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x44, 0x5f, 0x41, 0x54, 0x10, 0x03, 0x32, 0x99, 0x0c,
- 0x0a, 0x1e, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65,
- 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
- 0x12, 0x7e, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73,
- 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x2f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63,
- 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4c, 0x69,
- 0x73, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65,
- 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x70, 0x6f, 0x6c, 0x69,
+ 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49,
+ 0x45, 0x44, 0x10, 0x00, 0x12, 0x25, 0x0a, 0x21, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x4b, 0x45, 0x59,
+ 0x5f, 0x41, 0x43, 0x43, 0x45, 0x53, 0x53, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x53, 0x5f,
+ 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x41, 0x4d, 0x45, 0x10, 0x01, 0x12, 0x24, 0x0a, 0x20, 0x53,
+ 0x4f, 0x52, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x53, 0x53, 0x5f, 0x53,
+ 0x45, 0x52, 0x56, 0x45, 0x52, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x52, 0x49, 0x10,
+ 0x02, 0x12, 0x2b, 0x0a, 0x27, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x41, 0x43,
+ 0x43, 0x45, 0x53, 0x53, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x53, 0x5f, 0x54, 0x59, 0x50,
+ 0x45, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x44, 0x5f, 0x41, 0x54, 0x10, 0x03, 0x12, 0x2b,
+ 0x0a, 0x27, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x53,
+ 0x53, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55,
+ 0x50, 0x44, 0x41, 0x54, 0x45, 0x44, 0x5f, 0x41, 0x54, 0x10, 0x04, 0x2a, 0x9a, 0x01, 0x0a, 0x0f,
+ 0x53, 0x6f, 0x72, 0x74, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12,
+ 0x22, 0x0a, 0x1e, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x4b, 0x41, 0x53, 0x5f, 0x4b, 0x45, 0x59, 0x53,
+ 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45,
+ 0x44, 0x10, 0x00, 0x12, 0x1d, 0x0a, 0x19, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x4b, 0x41, 0x53, 0x5f,
+ 0x4b, 0x45, 0x59, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x49, 0x44,
+ 0x10, 0x01, 0x12, 0x21, 0x0a, 0x1d, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x4b, 0x41, 0x53, 0x5f, 0x4b,
+ 0x45, 0x59, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x44,
+ 0x5f, 0x41, 0x54, 0x10, 0x02, 0x12, 0x21, 0x0a, 0x1d, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x4b, 0x41,
+ 0x53, 0x5f, 0x4b, 0x45, 0x59, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x50, 0x44, 0x41,
+ 0x54, 0x45, 0x44, 0x5f, 0x41, 0x54, 0x10, 0x03, 0x32, 0x99, 0x0c, 0x0a, 0x1e, 0x4b, 0x65, 0x79,
+ 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69,
+ 0x73, 0x74, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x7e, 0x0a, 0x14, 0x4c,
+ 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76,
+ 0x65, 0x72, 0x73, 0x12, 0x2f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73,
+ 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79,
+ 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71,
+ 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61,
+ 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65,
+ 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65,
+ 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x78, 0x0a, 0x12, 0x47,
+ 0x65, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65,
+ 0x72, 0x12, 0x2d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65,
+ 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63,
+ 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+ 0x1a, 0x2e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67,
+ 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65,
+ 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+ 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x7e, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b,
+ 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x30,
+ 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73,
+ 0x74, 0x72, 0x79, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63,
+ 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+ 0x1a, 0x31, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67,
+ 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41,
+ 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f,
+ 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7e, 0x0a, 0x15, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b,
+ 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x30,
+ 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73,
+ 0x74, 0x72, 0x79, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63,
+ 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+ 0x1a, 0x31, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67,
+ 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41,
+ 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f,
+ 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7e, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4b,
+ 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x30,
+ 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73,
+ 0x74, 0x72, 0x79, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63,
+ 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+ 0x1a, 0x31, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67,
+ 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41,
+ 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f,
+ 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x90, 0x01, 0x0a, 0x19, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65,
+ 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x61,
+ 0x6e, 0x74, 0x73, 0x12, 0x34, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73,
+ 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79,
+ 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x61, 0x6e,
+ 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x70, 0x6f, 0x6c, 0x69,
0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4c,
0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76,
- 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01,
- 0x12, 0x78, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73,
- 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x2d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e,
- 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x4b,
- 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65,
- 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b,
- 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x4b, 0x65,
- 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73,
- 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x7e, 0x0a, 0x15, 0x43, 0x72,
- 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72,
- 0x76, 0x65, 0x72, 0x12, 0x30, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73,
- 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b,
- 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65,
- 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b,
+ 0x65, 0x72, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+ 0x22, 0x06, 0x88, 0x02, 0x01, 0x90, 0x02, 0x01, 0x12, 0x5a, 0x0a, 0x09, 0x43, 0x72, 0x65, 0x61,
+ 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x24, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b,
0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74,
- 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
- 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7e, 0x0a, 0x15, 0x55, 0x70,
- 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72,
- 0x76, 0x65, 0x72, 0x12, 0x30, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73,
- 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b,
- 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65,
- 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b,
- 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74,
- 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
- 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7e, 0x0a, 0x15, 0x44, 0x65,
- 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72,
- 0x76, 0x65, 0x72, 0x12, 0x30, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73,
- 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4b,
- 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65,
- 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b,
- 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74,
- 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
- 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x90, 0x01, 0x0a, 0x19, 0x4c,
- 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76,
- 0x65, 0x72, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x12, 0x34, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63,
- 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4c, 0x69,
- 0x73, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65,
- 0x72, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35,
+ 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x70, 0x6f,
+ 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79,
+ 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+ 0x73, 0x65, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x06, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x21,
0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73,
- 0x74, 0x72, 0x79, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73,
- 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x61, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73,
- 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x06, 0x88, 0x02, 0x01, 0x90, 0x02, 0x01, 0x12, 0x5a, 0x0a,
- 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x24, 0x2e, 0x70, 0x6f, 0x6c,
- 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e,
- 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
- 0x1a, 0x25, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67,
- 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52,
- 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x06, 0x47, 0x65, 0x74,
- 0x4b, 0x65, 0x79, 0x12, 0x21, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73,
- 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x52,
- 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e,
- 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x4b,
- 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x57, 0x0a, 0x08,
- 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x23, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63,
+ 0x74, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+ 0x74, 0x1a, 0x22, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65,
+ 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73,
+ 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x57, 0x0a, 0x08, 0x4c, 0x69, 0x73, 0x74, 0x4b,
+ 0x65, 0x79, 0x73, 0x12, 0x23, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73,
+ 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79,
+ 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63,
0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4c, 0x69,
- 0x73, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e,
+ 0x73, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00,
+ 0x12, 0x5a, 0x0a, 0x09, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x24, 0x2e,
0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74,
- 0x72, 0x79, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
- 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x09, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b,
- 0x65, 0x79, 0x12, 0x24, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72,
- 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65,
- 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63,
- 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x55, 0x70,
- 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
- 0x00, 0x12, 0x5a, 0x0a, 0x09, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x24,
- 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73,
- 0x74, 0x72, 0x79, 0x2e, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71,
- 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61,
- 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65,
- 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5d, 0x0a,
- 0x0a, 0x53, 0x65, 0x74, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x25, 0x2e, 0x70, 0x6f,
- 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79,
- 0x2e, 0x53, 0x65, 0x74, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65,
- 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72,
- 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x53, 0x65, 0x74, 0x42, 0x61, 0x73, 0x65, 0x4b,
- 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5d, 0x0a, 0x0a,
- 0x47, 0x65, 0x74, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x25, 0x2e, 0x70, 0x6f, 0x6c,
- 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e,
- 0x47, 0x65, 0x74, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
- 0x74, 0x1a, 0x26, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65,
- 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65,
- 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6c, 0x0a, 0x0f, 0x4c,
- 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x2a,
- 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73,
- 0x74, 0x72, 0x79, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x4d, 0x61, 0x70, 0x70, 0x69,
- 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x70, 0x6f, 0x6c,
- 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e,
- 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x52,
- 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0xdb, 0x01, 0x0a, 0x16, 0x63, 0x6f,
- 0x6d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69,
- 0x73, 0x74, 0x72, 0x79, 0x42, 0x1c, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53,
- 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x50, 0x72, 0x6f,
- 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
- 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x64, 0x66, 0x2f, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72,
- 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x6f,
- 0x6c, 0x69, 0x63, 0x79, 0x2f, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79,
- 0xa2, 0x02, 0x03, 0x50, 0x4b, 0x58, 0xaa, 0x02, 0x12, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e,
- 0x4b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0xca, 0x02, 0x12, 0x50, 0x6f,
+ 0x72, 0x79, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75,
+ 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73,
+ 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b,
+ 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x09,
+ 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x24, 0x2e, 0x70, 0x6f, 0x6c, 0x69,
+ 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x52,
+ 0x6f, 0x74, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
+ 0x25, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69,
+ 0x73, 0x74, 0x72, 0x79, 0x2e, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65,
+ 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5d, 0x0a, 0x0a, 0x53, 0x65, 0x74, 0x42,
+ 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x25, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e,
+ 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x53, 0x65, 0x74, 0x42,
+ 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e,
+ 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74,
+ 0x72, 0x79, 0x2e, 0x53, 0x65, 0x74, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73,
+ 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5d, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x42, 0x61,
+ 0x73, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x25, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b,
+ 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61,
+ 0x73, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x70,
+ 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72,
+ 0x79, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70,
+ 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6c, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65,
+ 0x79, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x2a, 0x2e, 0x70, 0x6f, 0x6c, 0x69,
+ 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4c,
+ 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65,
+ 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b,
+ 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4b,
+ 0x65, 0x79, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+ 0x73, 0x65, 0x22, 0x00, 0x42, 0xdb, 0x01, 0x0a, 0x16, 0x63, 0x6f, 0x6d, 0x2e, 0x70, 0x6f, 0x6c,
+ 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x42,
+ 0x1c, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
+ 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a,
+ 0x3a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e,
+ 0x74, 0x64, 0x66, 0x2f, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x2f, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2f,
+ 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0xa2, 0x02, 0x03, 0x50, 0x4b,
+ 0x58, 0xaa, 0x02, 0x12, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x72, 0x65,
+ 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0xca, 0x02, 0x12, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5c,
+ 0x4b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0xe2, 0x02, 0x1e, 0x50, 0x6f,
0x6c, 0x69, 0x63, 0x79, 0x5c, 0x4b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79,
- 0xe2, 0x02, 0x1e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5c, 0x4b, 0x61, 0x73, 0x72, 0x65, 0x67,
- 0x69, 0x73, 0x74, 0x72, 0x79, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
- 0x61, 0xea, 0x02, 0x13, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x3a, 0x3a, 0x4b, 0x61, 0x73, 0x72,
- 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+ 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x13, 0x50,
+ 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x3a, 0x3a, 0x4b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74,
+ 0x72, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
diff --git a/protocol/go/policy/objects.pb.go b/protocol/go/policy/objects.pb.go
index 78282c4edc..112291c6b1 100644
--- a/protocol/go/policy/objects.pb.go
+++ b/protocol/go/policy/objects.pb.go
@@ -237,31 +237,40 @@ func (SourceType) EnumDescriptor() ([]byte, []int) {
type KasPublicKeyAlgEnum int32
const (
- KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED KasPublicKeyAlgEnum = 0
- KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048 KasPublicKeyAlgEnum = 1
- KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096 KasPublicKeyAlgEnum = 2
- KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 KasPublicKeyAlgEnum = 5
- KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 KasPublicKeyAlgEnum = 6
- KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 KasPublicKeyAlgEnum = 7
+ KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED KasPublicKeyAlgEnum = 0
+ KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048 KasPublicKeyAlgEnum = 1
+ KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096 KasPublicKeyAlgEnum = 2
+ KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 KasPublicKeyAlgEnum = 5
+ KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 KasPublicKeyAlgEnum = 6
+ KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 KasPublicKeyAlgEnum = 7
+ KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING KasPublicKeyAlgEnum = 10
+ KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 KasPublicKeyAlgEnum = 11
+ KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 KasPublicKeyAlgEnum = 12
)
// Enum value maps for KasPublicKeyAlgEnum.
var (
KasPublicKeyAlgEnum_name = map[int32]string{
- 0: "KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED",
- 1: "KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048",
- 2: "KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096",
- 5: "KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1",
- 6: "KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1",
- 7: "KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1",
+ 0: "KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED",
+ 1: "KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048",
+ 2: "KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096",
+ 5: "KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1",
+ 6: "KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1",
+ 7: "KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1",
+ 10: "KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING",
+ 11: "KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768",
+ 12: "KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024",
}
KasPublicKeyAlgEnum_value = map[string]int32{
- "KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED": 0,
- "KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048": 1,
- "KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096": 2,
- "KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1": 5,
- "KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1": 6,
- "KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1": 7,
+ "KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED": 0,
+ "KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048": 1,
+ "KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096": 2,
+ "KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1": 5,
+ "KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1": 6,
+ "KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1": 7,
+ "KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING": 10,
+ "KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768": 11,
+ "KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024": 12,
}
)
@@ -296,12 +305,15 @@ func (KasPublicKeyAlgEnum) EnumDescriptor() ([]byte, []int) {
type Algorithm int32
const (
- Algorithm_ALGORITHM_UNSPECIFIED Algorithm = 0
- Algorithm_ALGORITHM_RSA_2048 Algorithm = 1
- Algorithm_ALGORITHM_RSA_4096 Algorithm = 2
- Algorithm_ALGORITHM_EC_P256 Algorithm = 3
- Algorithm_ALGORITHM_EC_P384 Algorithm = 4
- Algorithm_ALGORITHM_EC_P521 Algorithm = 5
+ Algorithm_ALGORITHM_UNSPECIFIED Algorithm = 0
+ Algorithm_ALGORITHM_RSA_2048 Algorithm = 1
+ Algorithm_ALGORITHM_RSA_4096 Algorithm = 2
+ Algorithm_ALGORITHM_EC_P256 Algorithm = 3
+ Algorithm_ALGORITHM_EC_P384 Algorithm = 4
+ Algorithm_ALGORITHM_EC_P521 Algorithm = 5
+ Algorithm_ALGORITHM_HPQT_XWING Algorithm = 6
+ Algorithm_ALGORITHM_HPQT_SECP256R1_MLKEM768 Algorithm = 7
+ Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024 Algorithm = 8
)
// Enum value maps for Algorithm.
@@ -313,14 +325,20 @@ var (
3: "ALGORITHM_EC_P256",
4: "ALGORITHM_EC_P384",
5: "ALGORITHM_EC_P521",
+ 6: "ALGORITHM_HPQT_XWING",
+ 7: "ALGORITHM_HPQT_SECP256R1_MLKEM768",
+ 8: "ALGORITHM_HPQT_SECP384R1_MLKEM1024",
}
Algorithm_value = map[string]int32{
- "ALGORITHM_UNSPECIFIED": 0,
- "ALGORITHM_RSA_2048": 1,
- "ALGORITHM_RSA_4096": 2,
- "ALGORITHM_EC_P256": 3,
- "ALGORITHM_EC_P384": 4,
- "ALGORITHM_EC_P521": 5,
+ "ALGORITHM_UNSPECIFIED": 0,
+ "ALGORITHM_RSA_2048": 1,
+ "ALGORITHM_RSA_4096": 2,
+ "ALGORITHM_EC_P256": 3,
+ "ALGORITHM_EC_P384": 4,
+ "ALGORITHM_EC_P521": 5,
+ "ALGORITHM_HPQT_XWING": 6,
+ "ALGORITHM_HPQT_SECP256R1_MLKEM768": 7,
+ "ALGORITHM_HPQT_SECP384R1_MLKEM1024": 8,
}
)
@@ -3727,7 +3745,7 @@ var file_policy_objects_proto_rawDesc = []byte{
0x00, 0x12, 0x18, 0x0a, 0x14, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45,
0x5f, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x53,
0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x58, 0x54, 0x45, 0x52,
- 0x4e, 0x41, 0x4c, 0x10, 0x02, 0x2a, 0x88, 0x02, 0x0a, 0x13, 0x4b, 0x61, 0x73, 0x50, 0x75, 0x62,
+ 0x4e, 0x41, 0x4c, 0x10, 0x02, 0x2a, 0x9b, 0x03, 0x0a, 0x13, 0x4b, 0x61, 0x73, 0x50, 0x75, 0x62,
0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x41, 0x6c, 0x67, 0x45, 0x6e, 0x75, 0x6d, 0x12, 0x27, 0x0a,
0x23, 0x4b, 0x41, 0x53, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f,
0x41, 0x4c, 0x47, 0x5f, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49,
@@ -3744,40 +3762,56 @@ var file_policy_objects_proto_rawDesc = []byte{
0x38, 0x34, 0x52, 0x31, 0x10, 0x06, 0x12, 0x28, 0x0a, 0x24, 0x4b, 0x41, 0x53, 0x5f, 0x50, 0x55,
0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x41, 0x4c, 0x47, 0x5f, 0x45, 0x4e, 0x55,
0x4d, 0x5f, 0x45, 0x43, 0x5f, 0x53, 0x45, 0x43, 0x50, 0x35, 0x32, 0x31, 0x52, 0x31, 0x10, 0x07,
- 0x2a, 0x9b, 0x01, 0x0a, 0x09, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x19,
- 0x0a, 0x15, 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, 0x55, 0x4e, 0x53, 0x50,
- 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x41, 0x4c, 0x47,
- 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, 0x52, 0x53, 0x41, 0x5f, 0x32, 0x30, 0x34, 0x38, 0x10,
- 0x01, 0x12, 0x16, 0x0a, 0x12, 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, 0x52,
- 0x53, 0x41, 0x5f, 0x34, 0x30, 0x39, 0x36, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x41, 0x4c, 0x47,
- 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, 0x45, 0x43, 0x5f, 0x50, 0x32, 0x35, 0x36, 0x10, 0x03,
- 0x12, 0x15, 0x0a, 0x11, 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, 0x45, 0x43,
- 0x5f, 0x50, 0x33, 0x38, 0x34, 0x10, 0x04, 0x12, 0x15, 0x0a, 0x11, 0x41, 0x4c, 0x47, 0x4f, 0x52,
- 0x49, 0x54, 0x48, 0x4d, 0x5f, 0x45, 0x43, 0x5f, 0x50, 0x35, 0x32, 0x31, 0x10, 0x05, 0x2a, 0x56,
- 0x0a, 0x09, 0x4b, 0x65, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x16, 0x4b,
- 0x45, 0x59, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43,
- 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x4b, 0x45, 0x59, 0x5f, 0x53,
- 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x01, 0x12, 0x16,
- 0x0a, 0x12, 0x4b, 0x45, 0x59, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x52, 0x4f, 0x54,
- 0x41, 0x54, 0x45, 0x44, 0x10, 0x02, 0x2a, 0x94, 0x01, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x4d, 0x6f,
- 0x64, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x55,
- 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18,
- 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f,
- 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x01, 0x12, 0x1e, 0x0a, 0x1a, 0x4b, 0x45,
- 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, 0x52, 0x5f,
- 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x4b, 0x45,
- 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x10, 0x03, 0x12,
- 0x1c, 0x0a, 0x18, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4c,
- 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x04, 0x42, 0x82, 0x01,
- 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x42, 0x0c, 0x4f, 0x62,
- 0x6a, 0x65, 0x63, 0x74, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2e, 0x67, 0x69,
- 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x64, 0x66,
- 0x2f, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,
- 0x6f, 0x6c, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0xa2, 0x02, 0x03, 0x50,
- 0x58, 0x58, 0xaa, 0x02, 0x06, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0xca, 0x02, 0x06, 0x50, 0x6f,
- 0x6c, 0x69, 0x63, 0x79, 0xe2, 0x02, 0x12, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5c, 0x47, 0x50,
- 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x06, 0x50, 0x6f, 0x6c, 0x69,
- 0x63, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+ 0x12, 0x26, 0x0a, 0x22, 0x4b, 0x41, 0x53, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b,
+ 0x45, 0x59, 0x5f, 0x41, 0x4c, 0x47, 0x5f, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x48, 0x50, 0x51, 0x54,
+ 0x5f, 0x58, 0x57, 0x49, 0x4e, 0x47, 0x10, 0x0a, 0x12, 0x33, 0x0a, 0x2f, 0x4b, 0x41, 0x53, 0x5f,
+ 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x41, 0x4c, 0x47, 0x5f, 0x45,
+ 0x4e, 0x55, 0x4d, 0x5f, 0x48, 0x50, 0x51, 0x54, 0x5f, 0x53, 0x45, 0x43, 0x50, 0x32, 0x35, 0x36,
+ 0x52, 0x31, 0x5f, 0x4d, 0x4c, 0x4b, 0x45, 0x4d, 0x37, 0x36, 0x38, 0x10, 0x0b, 0x12, 0x34, 0x0a,
+ 0x30, 0x4b, 0x41, 0x53, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f,
+ 0x41, 0x4c, 0x47, 0x5f, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x48, 0x50, 0x51, 0x54, 0x5f, 0x53, 0x45,
+ 0x43, 0x50, 0x33, 0x38, 0x34, 0x52, 0x31, 0x5f, 0x4d, 0x4c, 0x4b, 0x45, 0x4d, 0x31, 0x30, 0x32,
+ 0x34, 0x10, 0x0c, 0x2a, 0x84, 0x02, 0x0a, 0x09, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68,
+ 0x6d, 0x12, 0x19, 0x0a, 0x15, 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, 0x55,
+ 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12,
+ 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, 0x52, 0x53, 0x41, 0x5f, 0x32, 0x30,
+ 0x34, 0x38, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48,
+ 0x4d, 0x5f, 0x52, 0x53, 0x41, 0x5f, 0x34, 0x30, 0x39, 0x36, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11,
+ 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, 0x45, 0x43, 0x5f, 0x50, 0x32, 0x35,
+ 0x36, 0x10, 0x03, 0x12, 0x15, 0x0a, 0x11, 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d,
+ 0x5f, 0x45, 0x43, 0x5f, 0x50, 0x33, 0x38, 0x34, 0x10, 0x04, 0x12, 0x15, 0x0a, 0x11, 0x41, 0x4c,
+ 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, 0x45, 0x43, 0x5f, 0x50, 0x35, 0x32, 0x31, 0x10,
+ 0x05, 0x12, 0x18, 0x0a, 0x14, 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, 0x48,
+ 0x50, 0x51, 0x54, 0x5f, 0x58, 0x57, 0x49, 0x4e, 0x47, 0x10, 0x06, 0x12, 0x25, 0x0a, 0x21, 0x41,
+ 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, 0x48, 0x50, 0x51, 0x54, 0x5f, 0x53, 0x45,
+ 0x43, 0x50, 0x32, 0x35, 0x36, 0x52, 0x31, 0x5f, 0x4d, 0x4c, 0x4b, 0x45, 0x4d, 0x37, 0x36, 0x38,
+ 0x10, 0x07, 0x12, 0x26, 0x0a, 0x22, 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f,
+ 0x48, 0x50, 0x51, 0x54, 0x5f, 0x53, 0x45, 0x43, 0x50, 0x33, 0x38, 0x34, 0x52, 0x31, 0x5f, 0x4d,
+ 0x4c, 0x4b, 0x45, 0x4d, 0x31, 0x30, 0x32, 0x34, 0x10, 0x08, 0x2a, 0x56, 0x0a, 0x09, 0x4b, 0x65,
+ 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x16, 0x4b, 0x45, 0x59, 0x5f, 0x53,
+ 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45,
+ 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x4b, 0x45, 0x59, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55,
+ 0x53, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x4b, 0x45,
+ 0x59, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x52, 0x4f, 0x54, 0x41, 0x54, 0x45, 0x44,
+ 0x10, 0x02, 0x2a, 0x94, 0x01, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x18,
+ 0x0a, 0x14, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45,
+ 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x4b, 0x45, 0x59, 0x5f,
+ 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x52, 0x4f, 0x4f, 0x54,
+ 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x01, 0x12, 0x1e, 0x0a, 0x1a, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f,
+ 0x44, 0x45, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, 0x52, 0x5f, 0x52, 0x4f, 0x4f, 0x54,
+ 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f,
+ 0x44, 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x10, 0x03, 0x12, 0x1c, 0x0a, 0x18, 0x4b,
+ 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b,
+ 0x45, 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x04, 0x42, 0x82, 0x01, 0x0a, 0x0a, 0x63, 0x6f,
+ 0x6d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x42, 0x0c, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74,
+ 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x64, 0x66, 0x2f, 0x70, 0x6c, 0x61,
+ 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x67,
+ 0x6f, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0xa2, 0x02, 0x03, 0x50, 0x58, 0x58, 0xaa, 0x02,
+ 0x06, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0xca, 0x02, 0x06, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79,
+ 0xe2, 0x02, 0x12, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74,
+ 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x06, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x62, 0x06,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
diff --git a/sdk/basekey.go b/sdk/basekey.go
index 5f3cc12004..96f43f927c 100644
--- a/sdk/basekey.go
+++ b/sdk/basekey.go
@@ -20,37 +20,55 @@ const (
baseKeyPublicKey = "public_key"
)
-// TODO: Move this function to ocrypto?
-func getKasKeyAlg(alg string) policy.Algorithm {
- switch alg {
- case string(ocrypto.RSA2048Key):
- return policy.Algorithm_ALGORITHM_RSA_2048
- case string(ocrypto.RSA4096Key):
- return policy.Algorithm_ALGORITHM_RSA_4096
- case string(ocrypto.EC256Key):
- return policy.Algorithm_ALGORITHM_EC_P256
- case string(ocrypto.EC384Key):
- return policy.Algorithm_ALGORITHM_EC_P384
- case string(ocrypto.EC521Key):
- return policy.Algorithm_ALGORITHM_EC_P521
+func getKasKeyAlg(alg string) (policy.Algorithm, error) {
+ kt, err := ocrypto.ParseKeyType(alg)
+ if err != nil {
+ return policy.Algorithm_ALGORITHM_UNSPECIFIED, fmt.Errorf("invalid alg [%s]: %w", alg, err)
+ }
+ return KeyTypeToPolicyAlgorithm(kt)
+}
+
+func KeyTypeToPolicyAlgorithm(kt ocrypto.KeyType) (policy.Algorithm, error) {
+ switch kt {
+ case ocrypto.RSA2048Key:
+ return policy.Algorithm_ALGORITHM_RSA_2048, nil
+ case ocrypto.RSA4096Key:
+ return policy.Algorithm_ALGORITHM_RSA_4096, nil
+ case ocrypto.EC256Key:
+ return policy.Algorithm_ALGORITHM_EC_P256, nil
+ case ocrypto.EC384Key:
+ return policy.Algorithm_ALGORITHM_EC_P384, nil
+ case ocrypto.EC521Key:
+ return policy.Algorithm_ALGORITHM_EC_P521, nil
+ case ocrypto.HybridXWingKey:
+ return policy.Algorithm_ALGORITHM_HPQT_XWING, nil
+ case ocrypto.HybridSecp256r1MLKEM768Key:
+ return policy.Algorithm_ALGORITHM_HPQT_SECP256R1_MLKEM768, nil
+ case ocrypto.HybridSecp384r1MLKEM1024Key:
+ return policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024, nil
default:
- return policy.Algorithm_ALGORITHM_UNSPECIFIED
+ return policy.Algorithm_ALGORITHM_UNSPECIFIED, fmt.Errorf("unknown key type: %s", kt)
}
}
-// TODO: Move this function to ocrypto?
-func formatAlg(alg policy.Algorithm) (string, error) {
+func PolicyAlgorithmToKeyType(alg policy.Algorithm) (ocrypto.KeyType, error) {
switch alg {
case policy.Algorithm_ALGORITHM_RSA_2048:
- return string(ocrypto.RSA2048Key), nil
+ return ocrypto.RSA2048Key, nil
case policy.Algorithm_ALGORITHM_RSA_4096:
- return string(ocrypto.RSA4096Key), nil
+ return ocrypto.RSA4096Key, nil
case policy.Algorithm_ALGORITHM_EC_P256:
- return string(ocrypto.EC256Key), nil
+ return ocrypto.EC256Key, nil
case policy.Algorithm_ALGORITHM_EC_P384:
- return string(ocrypto.EC384Key), nil
+ return ocrypto.EC384Key, nil
case policy.Algorithm_ALGORITHM_EC_P521:
- return string(ocrypto.EC521Key), nil
+ return ocrypto.EC521Key, nil
+ case policy.Algorithm_ALGORITHM_HPQT_XWING:
+ return ocrypto.HybridXWingKey, nil
+ case policy.Algorithm_ALGORITHM_HPQT_SECP256R1_MLKEM768:
+ return ocrypto.HybridSecp256r1MLKEM768Key, nil
+ case policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024:
+ return ocrypto.HybridSecp384r1MLKEM1024Key, nil
case policy.Algorithm_ALGORITHM_UNSPECIFIED:
fallthrough
default:
@@ -58,6 +76,11 @@ func formatAlg(alg policy.Algorithm) (string, error) {
}
}
+func formatAlg(alg policy.Algorithm) (string, error) {
+ kt, err := PolicyAlgorithmToKeyType(alg)
+ return string(kt), err
+}
+
// GetBaseKey retrieves the platform base KAS key from the well-known configuration.
// The returned key material is expected to be public (algorithm, KID, PEM).
func (s SDK) GetBaseKey(ctx context.Context) (*policy.SimpleKasKey, error) {
@@ -114,7 +137,11 @@ func parseSimpleKasKey(baseKeyMap map[string]interface{}) (*policy.SimpleKasKey,
if !ok {
return nil, ErrBaseKeyInvalidFormat
}
- publicKey[baseKeyAlg] = getKasKeyAlg(alg)
+ a, err := getKasKeyAlg(alg)
+ if err != nil {
+ return nil, fmt.Errorf("%w: %w", ErrMarshalBaseKeyFailed, err)
+ }
+ publicKey[baseKeyAlg] = a
baseKeyMap[baseKeyPublicKey] = publicKey
configJSON, err := json.Marshal(baseKeyMap)
if err != nil {
diff --git a/sdk/basekey_test.go b/sdk/basekey_test.go
index ea0210bdf0..74ad8f5d95 100644
--- a/sdk/basekey_test.go
+++ b/sdk/basekey_test.go
@@ -63,7 +63,7 @@ func (m *mockWellKnownService) GetWellKnownConfiguration(
}
func TestGetKasKeyAlg(t *testing.T) {
- tests := []struct {
+ for _, test := range []struct {
name string
algStr string
expected policy.Algorithm
@@ -94,21 +94,45 @@ func TestGetKasKeyAlg(t *testing.T) {
expected: policy.Algorithm_ALGORITHM_EC_P521,
},
{
- name: "unsupported algorithm",
- algStr: "unsupported",
- expected: policy.Algorithm_ALGORITHM_UNSPECIFIED,
+ name: "hybrid xwing",
+ algStr: string(ocrypto.HybridXWingKey),
+ expected: policy.Algorithm_ALGORITHM_HPQT_XWING,
+ },
+ {
+ name: "hybrid p256 mlkem768",
+ algStr: string(ocrypto.HybridSecp256r1MLKEM768Key),
+ expected: policy.Algorithm_ALGORITHM_HPQT_SECP256R1_MLKEM768,
},
{
- name: "empty string",
- algStr: "",
- expected: policy.Algorithm_ALGORITHM_UNSPECIFIED,
+ name: "hybrid p384 mlkem1024",
+ algStr: string(ocrypto.HybridSecp384r1MLKEM1024Key),
+ expected: policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024,
},
+ } {
+ t.Run(test.name, func(t *testing.T) {
+ result, err := getKasKeyAlg(test.algStr)
+ require.NoError(t, err)
+ assert.Equal(t, test.expected, result, "Algorithm enum mismatch")
+ })
}
- for _, test := range tests {
+ for _, test := range []struct {
+ name string
+ algStr string
+ }{
+ {
+ name: "unsupported algorithm",
+ algStr: "unsupported",
+ },
+ {
+ name: "empty string",
+ algStr: "",
+ },
+ } {
t.Run(test.name, func(t *testing.T) {
- result := getKasKeyAlg(test.algStr)
- assert.Equal(t, test.expected, result, "Algorithm enum mismatch")
+ result, err := getKasKeyAlg(test.algStr)
+ require.Error(t, err)
+ assert.Equal(t, policy.Algorithm_ALGORITHM_UNSPECIFIED, result, "Algorithm enum mismatch")
})
}
}
@@ -150,6 +174,24 @@ func TestFormatAlg(t *testing.T) {
expected: string(ocrypto.EC521Key), // Note: This matches the implementation
expectError: false,
},
+ {
+ name: "Hybrid X-Wing",
+ alg: policy.Algorithm_ALGORITHM_HPQT_XWING,
+ expected: string(ocrypto.HybridXWingKey),
+ expectError: false,
+ },
+ {
+ name: "Hybrid P256+ML-KEM-768",
+ alg: policy.Algorithm_ALGORITHM_HPQT_SECP256R1_MLKEM768,
+ expected: string(ocrypto.HybridSecp256r1MLKEM768Key),
+ expectError: false,
+ },
+ {
+ name: "Hybrid P384+ML-KEM-1024",
+ alg: policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024,
+ expected: string(ocrypto.HybridSecp384r1MLKEM1024Key),
+ expectError: false,
+ },
{
name: "Unspecified algorithm",
alg: policy.Algorithm_ALGORITHM_UNSPECIFIED,
@@ -324,6 +366,9 @@ func TestFormatAlg_GetKasKeyAlg_RoundTrip(t *testing.T) {
{"EC-P256", policy.Algorithm_ALGORITHM_EC_P256},
{"EC-P384", policy.Algorithm_ALGORITHM_EC_P384},
{"EC-P521", policy.Algorithm_ALGORITHM_EC_P521},
+ {"HPQT-XWing", policy.Algorithm_ALGORITHM_HPQT_XWING},
+ {"HPQT-P256-MLKEM768", policy.Algorithm_ALGORITHM_HPQT_SECP256R1_MLKEM768},
+ {"HPQT-P384-MLKEM1024", policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024},
}
for _, tc := range supportedAlgs {
@@ -331,7 +376,8 @@ func TestFormatAlg_GetKasKeyAlg_RoundTrip(t *testing.T) {
formatted, err := formatAlg(tc.alg)
require.NoError(t, err, "formatAlg should not error for %s", tc.name)
- roundTripped := getKasKeyAlg(formatted)
+ roundTripped, err := getKasKeyAlg(formatted)
+ require.NoError(t, err)
assert.Equal(t, tc.alg, roundTripped,
"round-trip mismatch: formatAlg(%s) = %q → getKasKeyAlg returned %s, want %s",
tc.name, formatted, roundTripped, tc.alg)
diff --git a/sdk/experimental/tdf/key_access.go b/sdk/experimental/tdf/key_access.go
index 6e97701ad5..3974b06cf4 100644
--- a/sdk/experimental/tdf/key_access.go
+++ b/sdk/experimental/tdf/key_access.go
@@ -165,6 +165,9 @@ func wrapKeyWithPublicKey(symKey []byte, pubKeyInfo keysplit.KASPublicKey) (stri
// Determine key type based on algorithm
ktype := ocrypto.KeyType(pubKeyInfo.Algorithm)
+ if ocrypto.IsHybridKeyType(ktype) {
+ return wrapKeyWithHybrid(ktype, pubKeyInfo.PEM, symKey)
+ }
if ocrypto.IsECKeyType(ktype) {
// Handle EC key wrapping
return wrapKeyWithEC(ktype, pubKeyInfo.PEM, symKey)
@@ -245,3 +248,11 @@ func wrapKeyWithRSA(kasPublicKeyPEM string, symKey []byte) (string, error) {
return string(ocrypto.Base64Encode(encryptedKey)), nil
}
+
+func wrapKeyWithHybrid(ktype ocrypto.KeyType, kasPublicKeyPEM string, symKey []byte) (string, string, string, error) {
+ wrappedDER, err := ocrypto.HybridWrapDEK(ktype, kasPublicKeyPEM, symKey)
+ if err != nil {
+ return "", "", "", fmt.Errorf("hybrid wrap failed: %w", err)
+ }
+ return string(ocrypto.Base64Encode(wrappedDER)), "hybrid-wrapped", "", nil
+}
diff --git a/sdk/experimental/tdf/key_access_test.go b/sdk/experimental/tdf/key_access_test.go
index 9dac3ca3b6..daea79b69c 100644
--- a/sdk/experimental/tdf/key_access_test.go
+++ b/sdk/experimental/tdf/key_access_test.go
@@ -35,7 +35,7 @@ SQIDAQAB
)
// createTestSplitResult creates a mock SplitResult for testing key access operations
-func createTestSplitResult(kasURL, pubKey string, algorithm string) *keysplit.SplitResult {
+func createTestSplitResult(pubKey string, algorithm string) *keysplit.SplitResult {
// Generate random split data
splitData := make([]byte, 32)
_, err := rand.Read(splitData)
@@ -46,11 +46,11 @@ func createTestSplitResult(kasURL, pubKey string, algorithm string) *keysplit.Sp
split := keysplit.Split{
ID: "test-split-1",
Data: splitData,
- KASURLs: []string{kasURL},
+ KASURLs: []string{testKAS1URL},
}
pubKeyInfo := keysplit.KASPublicKey{
- URL: kasURL,
+ URL: testKAS1URL,
Algorithm: algorithm,
KID: "test-kid-1",
PEM: pubKey,
@@ -58,14 +58,14 @@ func createTestSplitResult(kasURL, pubKey string, algorithm string) *keysplit.Sp
return &keysplit.SplitResult{
Splits: []keysplit.Split{split},
- KASPublicKeys: map[string]keysplit.KASPublicKey{kasURL: pubKeyInfo},
+ KASPublicKeys: map[string]keysplit.KASPublicKey{testKAS1URL: pubKeyInfo},
}
}
func TestBuildKeyAccessObjects(t *testing.T) {
t.Run("successfully creates key access objects with RSA public key", func(t *testing.T) {
// Test that buildKeyAccessObjects correctly processes RSA keys and creates valid KeyAccess objects
- splitResult := createTestSplitResult(testKAS1URL, testRSAPublicKey, "rsa:2048")
+ splitResult := createTestSplitResult(testRSAPublicKey, "rsa:2048")
policyBytes := []byte(testPolicyJSON)
metadata := testMetadata
@@ -96,7 +96,7 @@ func TestBuildKeyAccessObjects(t *testing.T) {
ecPublicKeyPEM, err := ecKeyPair.PublicKeyInPemFormat()
require.NoError(t, err, "Should get public key in PEM format")
- splitResult := createTestSplitResult(testKAS1URL, ecPublicKeyPEM, "ec:secp256r1")
+ splitResult := createTestSplitResult(ecPublicKeyPEM, "ec:secp256r1")
policyBytes := []byte(testPolicyJSON)
metadata := testMetadata
@@ -112,6 +112,69 @@ func TestBuildKeyAccessObjects(t *testing.T) {
assert.NotEmpty(t, keyAccess.WrappedKey, "Should contain wrapped key data")
})
+ t.Run("successfully creates key access objects with X-Wing public key", func(t *testing.T) {
+ xwingKeyPair, err := ocrypto.NewXWingKeyPair()
+ require.NoError(t, err)
+
+ xwingPublicKeyPEM, err := xwingKeyPair.PublicKeyInPemFormat()
+ require.NoError(t, err)
+
+ splitResult := createTestSplitResult(xwingPublicKeyPEM, string(ocrypto.HybridXWingKey))
+ policyBytes := []byte(testPolicyJSON)
+
+ keyAccessList, err := buildKeyAccessObjects(splitResult, policyBytes, testMetadata)
+
+ require.NoError(t, err, "Should successfully create key access objects with valid X-Wing key")
+ require.Len(t, keyAccessList, 1)
+
+ keyAccess := keyAccessList[0]
+ assert.Equal(t, "hybrid-wrapped", keyAccess.KeyType)
+ assert.NotEmpty(t, keyAccess.WrappedKey)
+ assert.Empty(t, keyAccess.EphemeralPublicKey)
+ })
+
+ t.Run("successfully creates key access objects with P256+ML-KEM-768 public key", func(t *testing.T) {
+ keyPair, err := ocrypto.NewP256MLKEM768KeyPair()
+ require.NoError(t, err)
+
+ publicKeyPEM, err := keyPair.PublicKeyInPemFormat()
+ require.NoError(t, err)
+
+ splitResult := createTestSplitResult(publicKeyPEM, string(ocrypto.HybridSecp256r1MLKEM768Key))
+ policyBytes := []byte(testPolicyJSON)
+
+ keyAccessList, err := buildKeyAccessObjects(splitResult, policyBytes, testMetadata)
+
+ require.NoError(t, err, "Should successfully create key access objects with valid P256+ML-KEM-768 key")
+ require.Len(t, keyAccessList, 1)
+
+ keyAccess := keyAccessList[0]
+ assert.Equal(t, "hybrid-wrapped", keyAccess.KeyType)
+ assert.NotEmpty(t, keyAccess.WrappedKey)
+ assert.Empty(t, keyAccess.EphemeralPublicKey)
+ })
+
+ t.Run("successfully creates key access objects with P384+ML-KEM-1024 public key", func(t *testing.T) {
+ keyPair, err := ocrypto.NewP384MLKEM1024KeyPair()
+ require.NoError(t, err)
+
+ publicKeyPEM, err := keyPair.PublicKeyInPemFormat()
+ require.NoError(t, err)
+
+ splitResult := createTestSplitResult(publicKeyPEM, string(ocrypto.HybridSecp384r1MLKEM1024Key))
+ policyBytes := []byte(testPolicyJSON)
+
+ keyAccessList, err := buildKeyAccessObjects(splitResult, policyBytes, testMetadata)
+
+ require.NoError(t, err, "Should successfully create key access objects with valid P384+ML-KEM-1024 key")
+ require.Len(t, keyAccessList, 1)
+
+ keyAccess := keyAccessList[0]
+ assert.Equal(t, "hybrid-wrapped", keyAccess.KeyType)
+ assert.NotEmpty(t, keyAccess.WrappedKey)
+ assert.Empty(t, keyAccess.EphemeralPublicKey)
+ })
+
t.Run("handles multiple KAS URLs in single split", func(t *testing.T) {
// Test that multiple KAS URLs in one split create separate KeyAccess objects
splitData := make([]byte, 32)
@@ -172,7 +235,7 @@ func TestBuildKeyAccessObjects(t *testing.T) {
t.Run("handles empty metadata correctly", func(t *testing.T) {
// Test that empty metadata is handled without creating encrypted metadata
- splitResult := createTestSplitResult(testKAS1URL, testRSAPublicKey, "rsa:2048")
+ splitResult := createTestSplitResult(testRSAPublicKey, "rsa:2048")
keyAccessList, err := buildKeyAccessObjects(splitResult, []byte(testPolicyJSON), "")
@@ -424,6 +487,120 @@ func TestWrapKeyWithPublicKey(t *testing.T) {
"Ephemeral key should end with PEM footer")
})
+ t.Run("wraps key with X-Wing public key", func(t *testing.T) {
+ symKey := make([]byte, 32)
+ _, err := rand.Read(symKey)
+ require.NoError(t, err)
+
+ xwingKeyPair, err := ocrypto.NewXWingKeyPair()
+ require.NoError(t, err)
+
+ xwingPublicKeyPEM, err := xwingKeyPair.PublicKeyInPemFormat()
+ require.NoError(t, err)
+
+ pubKeyInfo := keysplit.KASPublicKey{
+ URL: testKAS1URL,
+ Algorithm: string(ocrypto.HybridXWingKey),
+ KID: "test-kid",
+ PEM: xwingPublicKeyPEM,
+ }
+
+ wrappedKey, keyType, ephemeralPubKey, err := wrapKeyWithPublicKey(symKey, pubKeyInfo)
+
+ require.NoError(t, err, "Should wrap key with X-Wing public key")
+ assert.NotEmpty(t, wrappedKey)
+ assert.Equal(t, "hybrid-wrapped", keyType)
+ assert.Empty(t, ephemeralPubKey)
+
+ decodedWrappedKey, err := ocrypto.Base64Decode([]byte(wrappedKey))
+ require.NoError(t, err)
+
+ privateKeyPEM, err := xwingKeyPair.PrivateKeyInPemFormat()
+ require.NoError(t, err)
+ privateKey, err := ocrypto.XWingPrivateKeyFromPem([]byte(privateKeyPEM))
+ require.NoError(t, err)
+
+ plaintext, err := ocrypto.XWingUnwrapDEK(privateKey, decodedWrappedKey)
+ require.NoError(t, err)
+ assert.Equal(t, symKey, plaintext)
+ })
+
+ t.Run("wraps key with P256+ML-KEM-768 public key", func(t *testing.T) {
+ symKey := make([]byte, 32)
+ _, err := rand.Read(symKey)
+ require.NoError(t, err)
+
+ keyPair, err := ocrypto.NewP256MLKEM768KeyPair()
+ require.NoError(t, err)
+
+ publicKeyPEM, err := keyPair.PublicKeyInPemFormat()
+ require.NoError(t, err)
+
+ pubKeyInfo := keysplit.KASPublicKey{
+ URL: testKAS1URL,
+ Algorithm: string(ocrypto.HybridSecp256r1MLKEM768Key),
+ KID: "test-kid",
+ PEM: publicKeyPEM,
+ }
+
+ wrappedKey, keyType, ephemeralPubKey, err := wrapKeyWithPublicKey(symKey, pubKeyInfo)
+
+ require.NoError(t, err, "Should wrap key with P256+ML-KEM-768 public key")
+ assert.NotEmpty(t, wrappedKey)
+ assert.Equal(t, "hybrid-wrapped", keyType)
+ assert.Empty(t, ephemeralPubKey)
+
+ decodedWrappedKey, err := ocrypto.Base64Decode([]byte(wrappedKey))
+ require.NoError(t, err)
+
+ privateKeyPEM, err := keyPair.PrivateKeyInPemFormat()
+ require.NoError(t, err)
+ privateKey, err := ocrypto.P256MLKEM768PrivateKeyFromPem([]byte(privateKeyPEM))
+ require.NoError(t, err)
+
+ plaintext, err := ocrypto.P256MLKEM768UnwrapDEK(privateKey, decodedWrappedKey)
+ require.NoError(t, err)
+ assert.Equal(t, symKey, plaintext)
+ })
+
+ t.Run("wraps key with P384+ML-KEM-1024 public key", func(t *testing.T) {
+ symKey := make([]byte, 32)
+ _, err := rand.Read(symKey)
+ require.NoError(t, err)
+
+ keyPair, err := ocrypto.NewP384MLKEM1024KeyPair()
+ require.NoError(t, err)
+
+ publicKeyPEM, err := keyPair.PublicKeyInPemFormat()
+ require.NoError(t, err)
+
+ pubKeyInfo := keysplit.KASPublicKey{
+ URL: testKAS1URL,
+ Algorithm: string(ocrypto.HybridSecp384r1MLKEM1024Key),
+ KID: "test-kid",
+ PEM: publicKeyPEM,
+ }
+
+ wrappedKey, keyType, ephemeralPubKey, err := wrapKeyWithPublicKey(symKey, pubKeyInfo)
+
+ require.NoError(t, err, "Should wrap key with P384+ML-KEM-1024 public key")
+ assert.NotEmpty(t, wrappedKey)
+ assert.Equal(t, "hybrid-wrapped", keyType)
+ assert.Empty(t, ephemeralPubKey)
+
+ decodedWrappedKey, err := ocrypto.Base64Decode([]byte(wrappedKey))
+ require.NoError(t, err)
+
+ privateKeyPEM, err := keyPair.PrivateKeyInPemFormat()
+ require.NoError(t, err)
+ privateKey, err := ocrypto.P384MLKEM1024PrivateKeyFromPem([]byte(privateKeyPEM))
+ require.NoError(t, err)
+
+ plaintext, err := ocrypto.P384MLKEM1024UnwrapDEK(privateKey, decodedWrappedKey)
+ require.NoError(t, err)
+ assert.Equal(t, symKey, plaintext)
+ })
+
t.Run("returns error for empty PEM", func(t *testing.T) {
// Test error handling for missing public key PEM
symKey := make([]byte, 32)
diff --git a/sdk/experimental/tdf/keysplit/attributes.go b/sdk/experimental/tdf/keysplit/attributes.go
index e12d838e97..27cc1e386f 100644
--- a/sdk/experimental/tdf/keysplit/attributes.go
+++ b/sdk/experimental/tdf/keysplit/attributes.go
@@ -7,6 +7,7 @@ import (
"log/slog"
"github.com/opentdf/platform/protocol/go/policy"
+ "github.com/opentdf/platform/sdk"
)
const unknownAlgorithm = "unknown"
@@ -186,22 +187,11 @@ func extractKASGrants(grants []*policy.KeyAccessServer, kasKeys []*policy.Simple
// formatAlgorithm converts policy algorithm enum to string
func formatAlgorithm(alg policy.Algorithm) string {
- switch alg {
- case policy.Algorithm_ALGORITHM_UNSPECIFIED:
- return unknownAlgorithm
- case policy.Algorithm_ALGORITHM_EC_P256:
- return "ec:secp256r1"
- case policy.Algorithm_ALGORITHM_EC_P384:
- return "ec:secp384r1"
- case policy.Algorithm_ALGORITHM_EC_P521:
- return "ec:secp521r1"
- case policy.Algorithm_ALGORITHM_RSA_2048:
- return "rsa:2048"
- case policy.Algorithm_ALGORITHM_RSA_4096:
- return "rsa:4096"
- default:
+ kt, err := sdk.PolicyAlgorithmToKeyType(alg)
+ if err != nil {
return unknownAlgorithm
}
+ return string(kt)
}
// convertAlgEnum2Simple converts KAS key algorithm enum to policy algorithm enum
@@ -217,6 +207,12 @@ func convertAlgEnum2Simple(a policy.KasPublicKeyAlgEnum) policy.Algorithm {
return policy.Algorithm_ALGORITHM_RSA_2048
case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096:
return policy.Algorithm_ALGORITHM_RSA_4096
+ case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING:
+ return policy.Algorithm_ALGORITHM_HPQT_XWING
+ case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768:
+ return policy.Algorithm_ALGORITHM_HPQT_SECP256R1_MLKEM768
+ case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024:
+ return policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024
case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED:
return policy.Algorithm_ALGORITHM_UNSPECIFIED
default:
diff --git a/sdk/experimental/tdf/keysplit/attributes_test.go b/sdk/experimental/tdf/keysplit/attributes_test.go
index 4ed96d38a2..b7095e801d 100644
--- a/sdk/experimental/tdf/keysplit/attributes_test.go
+++ b/sdk/experimental/tdf/keysplit/attributes_test.go
@@ -46,6 +46,21 @@ func TestFormatAlgorithm(t *testing.T) {
alg: policy.Algorithm_ALGORITHM_RSA_4096,
expected: "rsa:4096",
},
+ {
+ name: "HPQT X-Wing",
+ alg: policy.Algorithm_ALGORITHM_HPQT_XWING,
+ expected: "hpqt:xwing",
+ },
+ {
+ name: "HPQT P256+ML-KEM-768",
+ alg: policy.Algorithm_ALGORITHM_HPQT_SECP256R1_MLKEM768,
+ expected: "hpqt:secp256r1-mlkem768",
+ },
+ {
+ name: "HPQT P384+ML-KEM-1024",
+ alg: policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024,
+ expected: "hpqt:secp384r1-mlkem1024",
+ },
{
name: "unknown algorithm value",
alg: policy.Algorithm(999),
@@ -97,6 +112,21 @@ func TestConvertAlgEnum2Simple(t *testing.T) {
algEnum: policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096,
expected: policy.Algorithm_ALGORITHM_RSA_4096,
},
+ {
+ name: "HPQT X-Wing",
+ algEnum: policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING,
+ expected: policy.Algorithm_ALGORITHM_HPQT_XWING,
+ },
+ {
+ name: "HPQT P256+ML-KEM-768",
+ algEnum: policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768,
+ expected: policy.Algorithm_ALGORITHM_HPQT_SECP256R1_MLKEM768,
+ },
+ {
+ name: "HPQT P384+ML-KEM-1024",
+ algEnum: policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024,
+ expected: policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024,
+ },
{
name: "unknown enum value",
algEnum: policy.KasPublicKeyAlgEnum(999),
diff --git a/sdk/experimental/tdf/writer_test.go b/sdk/experimental/tdf/writer_test.go
index 296c582041..08f35cfcc8 100644
--- a/sdk/experimental/tdf/writer_test.go
+++ b/sdk/experimental/tdf/writer_test.go
@@ -58,6 +58,9 @@ func TestWriterEndToEnd(t *testing.T) {
{"GetManifestIncludesInitialPolicy", testGetManifestIncludesInitialPolicy},
{"SparseIndicesInOrder", testSparseIndicesInOrder},
{"SparseIndicesOutOfOrder", testSparseIndicesOutOfOrder},
+ {"HybridXWingFlow", testHybridXWingFlow},
+ {"HybridP256MLKEM768Flow", testHybridP256MLKEM768Flow},
+ {"HybridP384MLKEM1024Flow", testHybridP384MLKEM1024Flow},
}
for _, tc := range testCases {
@@ -877,6 +880,157 @@ func createTestAttributeWithRule(fqn, kasURL, kid string, rule policy.AttributeR
return value
}
+func createTestAttributeWithAlgorithm(t *testing.T, fqn, kasURL, kid string, alg policy.Algorithm, pem string) *policy.Value {
+ t.Helper()
+ value := createTestAttribute(fqn, kasURL, kid)
+ require.NotEmpty(t, value.GetGrants(), "createTestAttribute returned no grants")
+ require.NotEmpty(t, value.GetGrants()[0].GetKasKeys(), "createTestAttribute returned no kas keys")
+ value.GetGrants()[0].GetKasKeys()[0].PublicKey.Algorithm = alg
+ value.GetGrants()[0].GetKasKeys()[0].PublicKey.Pem = pem
+ return value
+}
+
+// hybridUnwrapForTest base64-decodes the wrappedKey from a manifest KAO and
+// unwraps it with the matching hybrid private key, asserting the recovered DEK
+// is non-empty. This proves the writer's `hybrid-wrapped` output is consumable
+// by the corresponding ocrypto unwrap path.
+func hybridUnwrapForTest(t *testing.T, ktype ocrypto.KeyType, privatePEM, wrappedKeyB64 string) {
+ t.Helper()
+ wrappedDER, err := ocrypto.Base64Decode([]byte(wrappedKeyB64))
+ require.NoError(t, err, "Base64Decode wrapped key")
+
+ switch ktype { //nolint:exhaustive // only handle hybrid types
+ case ocrypto.HybridXWingKey:
+ raw, err := ocrypto.XWingPrivateKeyFromPem([]byte(privatePEM))
+ require.NoError(t, err)
+ dek, err := ocrypto.XWingUnwrapDEK(raw, wrappedDER)
+ require.NoError(t, err, "XWingUnwrapDEK")
+ assert.NotEmpty(t, dek, "X-Wing recovered DEK")
+ case ocrypto.HybridSecp256r1MLKEM768Key:
+ raw, err := ocrypto.P256MLKEM768PrivateKeyFromPem([]byte(privatePEM))
+ require.NoError(t, err)
+ dek, err := ocrypto.P256MLKEM768UnwrapDEK(raw, wrappedDER)
+ require.NoError(t, err, "P256MLKEM768UnwrapDEK")
+ assert.NotEmpty(t, dek, "P-256+ML-KEM-768 recovered DEK")
+ case ocrypto.HybridSecp384r1MLKEM1024Key:
+ raw, err := ocrypto.P384MLKEM1024PrivateKeyFromPem([]byte(privatePEM))
+ require.NoError(t, err)
+ dek, err := ocrypto.P384MLKEM1024UnwrapDEK(raw, wrappedDER)
+ require.NoError(t, err, "P384MLKEM1024UnwrapDEK")
+ assert.NotEmpty(t, dek, "P-384+ML-KEM-1024 recovered DEK")
+ default:
+ t.Fatalf("unsupported hybrid key type for round-trip: %s", ktype)
+ }
+}
+
+func testHybridXWingFlow(t *testing.T) {
+ ctx := t.Context()
+
+ keyPair, err := ocrypto.NewXWingKeyPair()
+ require.NoError(t, err)
+ pubPEM, err := keyPair.PublicKeyInPemFormat()
+ require.NoError(t, err)
+ privPEM, err := keyPair.PrivateKeyInPemFormat()
+ require.NoError(t, err)
+
+ writer, err := NewWriter(ctx)
+ require.NoError(t, err)
+
+ _, err = writer.WriteSegment(ctx, 0, []byte("hybrid xwing test data"))
+ require.NoError(t, err)
+
+ attributes := []*policy.Value{
+ createTestAttributeWithAlgorithm(
+ t,
+ "https://example.com/attr/Classification/value/Secret",
+ testKAS1, "xwing-kid",
+ policy.Algorithm_ALGORITHM_HPQT_XWING, pubPEM,
+ ),
+ }
+ result, err := writer.Finalize(ctx, WithAttributeValues(attributes))
+ require.NoError(t, err)
+ assert.NotEmpty(t, result.Data)
+
+ keyAccess := result.Manifest.KeyAccessObjs[0]
+ assert.Equal(t, "hybrid-wrapped", keyAccess.KeyType)
+ assert.NotEmpty(t, keyAccess.WrappedKey)
+
+ validateManifestSchema(t, result.Manifest)
+ hybridUnwrapForTest(t, ocrypto.HybridXWingKey, privPEM, keyAccess.WrappedKey)
+}
+
+func testHybridP256MLKEM768Flow(t *testing.T) {
+ ctx := t.Context()
+
+ keyPair, err := ocrypto.NewP256MLKEM768KeyPair()
+ require.NoError(t, err)
+ pubPEM, err := keyPair.PublicKeyInPemFormat()
+ require.NoError(t, err)
+ privPEM, err := keyPair.PrivateKeyInPemFormat()
+ require.NoError(t, err)
+
+ writer, err := NewWriter(ctx)
+ require.NoError(t, err)
+
+ _, err = writer.WriteSegment(ctx, 0, []byte("hybrid p256 mlkem768 test data"))
+ require.NoError(t, err)
+
+ attributes := []*policy.Value{
+ createTestAttributeWithAlgorithm(
+ t,
+ "https://example.com/attr/Classification/value/Secret",
+ testKAS1, "p256mlkem768-kid",
+ policy.Algorithm_ALGORITHM_HPQT_SECP256R1_MLKEM768, pubPEM,
+ ),
+ }
+ result, err := writer.Finalize(ctx, WithAttributeValues(attributes))
+ require.NoError(t, err)
+ assert.NotEmpty(t, result.Data)
+
+ keyAccess := result.Manifest.KeyAccessObjs[0]
+ assert.Equal(t, "hybrid-wrapped", keyAccess.KeyType)
+ assert.NotEmpty(t, keyAccess.WrappedKey)
+
+ validateManifestSchema(t, result.Manifest)
+ hybridUnwrapForTest(t, ocrypto.HybridSecp256r1MLKEM768Key, privPEM, keyAccess.WrappedKey)
+}
+
+func testHybridP384MLKEM1024Flow(t *testing.T) {
+ ctx := t.Context()
+
+ keyPair, err := ocrypto.NewP384MLKEM1024KeyPair()
+ require.NoError(t, err)
+ pubPEM, err := keyPair.PublicKeyInPemFormat()
+ require.NoError(t, err)
+ privPEM, err := keyPair.PrivateKeyInPemFormat()
+ require.NoError(t, err)
+
+ writer, err := NewWriter(ctx)
+ require.NoError(t, err)
+
+ _, err = writer.WriteSegment(ctx, 0, []byte("hybrid p384 mlkem1024 test data"))
+ require.NoError(t, err)
+
+ attributes := []*policy.Value{
+ createTestAttributeWithAlgorithm(
+ t,
+ "https://example.com/attr/Classification/value/Secret",
+ testKAS1, "p384mlkem1024-kid",
+ policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024, pubPEM,
+ ),
+ }
+ result, err := writer.Finalize(ctx, WithAttributeValues(attributes))
+ require.NoError(t, err)
+ assert.NotEmpty(t, result.Data)
+
+ keyAccess := result.Manifest.KeyAccessObjs[0]
+ assert.Equal(t, "hybrid-wrapped", keyAccess.KeyType)
+ assert.NotEmpty(t, keyAccess.WrappedKey)
+
+ validateManifestSchema(t, result.Manifest)
+ hybridUnwrapForTest(t, ocrypto.HybridSecp384r1MLKEM1024Key, privPEM, keyAccess.WrappedKey)
+}
+
// validateManifestSchema validates a TDF manifest against the JSON schema
func validateManifestSchema(t *testing.T, manifest *Manifest) {
t.Helper()
diff --git a/sdk/granter.go b/sdk/granter.go
index 8275a04f42..ede578cc17 100644
--- a/sdk/granter.go
+++ b/sdk/granter.go
@@ -286,6 +286,12 @@ func convertAlgEnum2Simple(a policy.KasPublicKeyAlgEnum) policy.Algorithm {
return policy.Algorithm_ALGORITHM_RSA_2048
case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096:
return policy.Algorithm_ALGORITHM_RSA_4096
+ case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING:
+ return policy.Algorithm_ALGORITHM_HPQT_XWING
+ case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768:
+ return policy.Algorithm_ALGORITHM_HPQT_SECP256R1_MLKEM768
+ case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024:
+ return policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024
case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED:
return policy.Algorithm_ALGORITHM_UNSPECIFIED
default:
@@ -293,24 +299,6 @@ func convertAlgEnum2Simple(a policy.KasPublicKeyAlgEnum) policy.Algorithm {
}
}
-// convertStringToAlgorithm converts a string algorithm representation to policy.Algorithm
-func convertStringToAlgorithm(alg string) policy.Algorithm {
- switch ocrypto.KeyType(strings.ToLower(alg)) {
- case ocrypto.EC256Key:
- return policy.Algorithm_ALGORITHM_EC_P256
- case ocrypto.EC384Key:
- return policy.Algorithm_ALGORITHM_EC_P384
- case ocrypto.EC521Key:
- return policy.Algorithm_ALGORITHM_EC_P521
- case ocrypto.RSA2048Key:
- return policy.Algorithm_ALGORITHM_RSA_2048
- case ocrypto.RSA4096Key:
- return policy.Algorithm_ALGORITHM_RSA_4096
- default:
- return policy.Algorithm_ALGORITHM_UNSPECIFIED
- }
-}
-
type grantType int
const (
@@ -490,31 +478,18 @@ func algProto2String(e policy.KasPublicKeyAlgEnum) string {
return string(ocrypto.RSA2048Key)
case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096:
return string(ocrypto.RSA4096Key)
+ case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING:
+ return string(ocrypto.HybridXWingKey)
+ case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768:
+ return string(ocrypto.HybridSecp256r1MLKEM768Key)
+ case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024:
+ return string(ocrypto.HybridSecp384r1MLKEM1024Key)
case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED:
return ""
}
return ""
}
-func algProto2OcryptoKeyType(e policy.Algorithm) ocrypto.KeyType {
- switch e {
- case policy.Algorithm_ALGORITHM_EC_P256:
- return ocrypto.EC256Key
- case policy.Algorithm_ALGORITHM_EC_P384:
- return ocrypto.EC384Key
- case policy.Algorithm_ALGORITHM_EC_P521:
- return ocrypto.EC521Key
- case policy.Algorithm_ALGORITHM_RSA_2048:
- return ocrypto.RSA2048Key
- case policy.Algorithm_ALGORITHM_RSA_4096:
- return ocrypto.RSA4096Key
- case policy.Algorithm_ALGORITHM_UNSPECIFIED:
- return ocrypto.KeyType("")
- default:
- return ocrypto.KeyType("")
- }
-}
-
func storeKeysToCache(logger *slog.Logger, kases []*policy.KeyAccessServer, keys []*policy.SimpleKasKey, c *kasKeyCache, kc *rlKeyCache) {
for _, kas := range kases {
keys := kas.GetPublicKey().GetCached().GetKeys()
@@ -726,7 +701,10 @@ func (r granter) resolveTemplate(ctx context.Context, kaoKeyAlg string, genSplit
}
o.identifier = kpub.KID
// Convert the string algorithm to the appropriate enum
- algEnum := convertStringToAlgorithm(kpub.Algorithm)
+ algEnum, err := getKasKeyAlg(strings.ToLower(kpub.Algorithm))
+ if err != nil {
+ return nil, fmt.Errorf("unsupported algorithm for kas [%s#%s]: %w", o.KASURI(), kpub.KID, err)
+ }
r.keyCache.c[*o] = &policy.SimpleKasKey{
KasUri: o.KASURI(),
PublicKey: &policy.SimpleKasPublicKey{
@@ -740,7 +718,10 @@ func (r granter) resolveTemplate(ctx context.Context, kaoKeyAlg string, genSplit
if !ok || kpub.GetPublicKey() == nil || kpub.GetPublicKey().GetPem() == "" {
return nil, fmt.Errorf("no key found for resource locator [%s]", o)
}
- algorithm := algProto2OcryptoKeyType(kpub.GetPublicKey().GetAlgorithm())
+ algorithm, err := PolicyAlgorithmToKeyType(kpub.GetPublicKey().GetAlgorithm())
+ if err != nil {
+ return nil, fmt.Errorf("invalid algorithm [%v] for kas %s with kid [%s]: %w", kpub.GetPublicKey().GetAlgorithm(), kpub.GetKasUri(), kpub.GetPublicKey().GetKid(), err)
+ }
p = append(p, kaoTpl{o.KASURI(), splitID, o.ID(), kpub.GetPublicKey().GetPem(), algorithm})
}
}
diff --git a/sdk/schema/manifest-lax.schema.json b/sdk/schema/manifest-lax.schema.json
index a31abd75fb..2d9c730279 100644
--- a/sdk/schema/manifest-lax.schema.json
+++ b/sdk/schema/manifest-lax.schema.json
@@ -52,7 +52,7 @@
"type": {
"description": "The type of key access object.",
"type": "string",
- "enum": ["wrapped", "remote"]
+ "enum": ["wrapped", "ec-wrapped", "hybrid-wrapped", "remote"]
},
"url": {
"description": "A fully qualified URL pointing to a key access service responsible for managing access to the encryption keys.",
diff --git a/sdk/schema/manifest.schema.json b/sdk/schema/manifest.schema.json
index 6488623fa2..8a0e47ac94 100644
--- a/sdk/schema/manifest.schema.json
+++ b/sdk/schema/manifest.schema.json
@@ -52,7 +52,7 @@
"type": {
"description": "The type of key access object.",
"type": "string",
- "enum": ["ec-wrapped", "remote", "wrapped"]
+ "enum": ["ec-wrapped", "hybrid-wrapped", "remote", "wrapped"]
},
"url": {
"description": "A fully qualified URL pointing to a key access service responsible for managing access to the encryption keys.",
@@ -239,4 +239,4 @@
}
},
"required": ["payload", "encryptionInformation"]
- }
\ No newline at end of file
+ }
diff --git a/sdk/tdf.go b/sdk/tdf.go
index db22208b5f..3940291a50 100644
--- a/sdk/tdf.go
+++ b/sdk/tdf.go
@@ -43,6 +43,7 @@ const (
kKeySize = 32
kWrapped = "wrapped"
kECWrapped = "ec-wrapped"
+ kHybridWrapped = "hybrid-wrapped"
kKasProtocol = "kas"
kSplitKeyType = "split"
kGCMCipherAlgorithm = "AES-256-GCM"
@@ -674,7 +675,15 @@ func createKeyAccess(kasInfo KASInfo, symKey []byte, policyBinding PolicyBinding
}
ktype := ocrypto.KeyType(kasInfo.Algorithm)
- if ocrypto.IsECKeyType(ktype) {
+ switch {
+ case ocrypto.IsHybridKeyType(ktype):
+ wrappedKey, err := generateWrapKeyWithHybrid(kasInfo.Algorithm, kasInfo.PublicKey, symKey)
+ if err != nil {
+ return KeyAccess{}, err
+ }
+ keyAccess.KeyType = kHybridWrapped
+ keyAccess.WrappedKey = wrappedKey
+ case ocrypto.IsECKeyType(ktype):
mode, err := ocrypto.ECKeyTypeToMode(ktype)
if err != nil {
return KeyAccess{}, err
@@ -686,7 +695,7 @@ func createKeyAccess(kasInfo KASInfo, symKey []byte, policyBinding PolicyBinding
keyAccess.KeyType = kECWrapped
keyAccess.WrappedKey = wrappedKeyInfo.wrappedKey
keyAccess.EphemeralPublicKey = wrappedKeyInfo.publicKey
- } else {
+ default:
wrappedKey, err := generateWrapKeyWithRSA(kasInfo.PublicKey, symKey)
if err != nil {
return KeyAccess{}, err
@@ -761,6 +770,14 @@ func generateWrapKeyWithRSA(publicKey string, symKey []byte) (string, error) {
return string(ocrypto.Base64Encode(wrappedKey)), nil
}
+func generateWrapKeyWithHybrid(algorithm, publicKeyPEM string, symKey []byte) (string, error) {
+ wrappedDER, err := ocrypto.HybridWrapDEK(ocrypto.KeyType(algorithm), publicKeyPEM, symKey)
+ if err != nil {
+ return "", fmt.Errorf("generateWrapKeyWithHybrid: %w", err)
+ }
+ return string(ocrypto.Base64Encode(wrappedDER)), nil
+}
+
// create policy object
func createPolicyObject(attributes []AttributeValueFQN) (PolicyObject, error) {
uuidObj, err := uuid.NewUUID()
diff --git a/sdk/tdf_hybrid_test.go b/sdk/tdf_hybrid_test.go
new file mode 100644
index 0000000000..86eb1e98e5
--- /dev/null
+++ b/sdk/tdf_hybrid_test.go
@@ -0,0 +1,84 @@
+package sdk
+
+import (
+ "testing"
+
+ "github.com/opentdf/platform/lib/ocrypto"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestCreateKeyAccessWithXWingKey(t *testing.T) {
+ symKey := []byte("0123456789abcdef0123456789abcdef")
+ keyAccess, err := createKeyAccess(KASInfo{
+ URL: "https://kas.example.com",
+ KID: "xwing-kid",
+ Algorithm: string(ocrypto.HybridXWingKey),
+ PublicKey: mockHybridXWingPublicKey,
+ }, symKey, PolicyBinding{}, "", "")
+ require.NoError(t, err)
+
+ assert.Equal(t, kHybridWrapped, keyAccess.KeyType)
+ assert.Empty(t, keyAccess.EphemeralPublicKey)
+ assert.NotEmpty(t, keyAccess.WrappedKey)
+
+ privateKey, err := ocrypto.XWingPrivateKeyFromPem([]byte(mockHybridXWingPrivateKey))
+ require.NoError(t, err)
+
+ wrappedKey, err := ocrypto.Base64Decode([]byte(keyAccess.WrappedKey))
+ require.NoError(t, err)
+
+ plaintext, err := ocrypto.XWingUnwrapDEK(privateKey, wrappedKey)
+ require.NoError(t, err)
+ assert.Equal(t, symKey, plaintext)
+}
+
+func TestCreateKeyAccessWithP256MLKEM768Key(t *testing.T) {
+ symKey := []byte("0123456789abcdef0123456789abcdef")
+ keyAccess, err := createKeyAccess(KASInfo{
+ URL: "https://kas.example.com",
+ KID: "p256mlkem768-kid",
+ Algorithm: string(ocrypto.HybridSecp256r1MLKEM768Key),
+ PublicKey: mockHybridP256MLKEM768PublicKey,
+ }, symKey, PolicyBinding{}, "", "")
+ require.NoError(t, err)
+
+ assert.Equal(t, kHybridWrapped, keyAccess.KeyType)
+ assert.Empty(t, keyAccess.EphemeralPublicKey)
+ assert.NotEmpty(t, keyAccess.WrappedKey)
+
+ privateKey, err := ocrypto.P256MLKEM768PrivateKeyFromPem([]byte(mockHybridP256MLKEM768PrivateKey))
+ require.NoError(t, err)
+
+ wrappedKey, err := ocrypto.Base64Decode([]byte(keyAccess.WrappedKey))
+ require.NoError(t, err)
+
+ plaintext, err := ocrypto.P256MLKEM768UnwrapDEK(privateKey, wrappedKey)
+ require.NoError(t, err)
+ assert.Equal(t, symKey, plaintext)
+}
+
+func TestCreateKeyAccessWithP384MLKEM1024Key(t *testing.T) {
+ symKey := []byte("0123456789abcdef0123456789abcdef")
+ keyAccess, err := createKeyAccess(KASInfo{
+ URL: "https://kas.example.com",
+ KID: "p384mlkem1024-kid",
+ Algorithm: string(ocrypto.HybridSecp384r1MLKEM1024Key),
+ PublicKey: mockHybridP384MLKEM1024PublicKey,
+ }, symKey, PolicyBinding{}, "", "")
+ require.NoError(t, err)
+
+ assert.Equal(t, kHybridWrapped, keyAccess.KeyType)
+ assert.Empty(t, keyAccess.EphemeralPublicKey)
+ assert.NotEmpty(t, keyAccess.WrappedKey)
+
+ privateKey, err := ocrypto.P384MLKEM1024PrivateKeyFromPem([]byte(mockHybridP384MLKEM1024PrivateKey))
+ require.NoError(t, err)
+
+ wrappedKey, err := ocrypto.Base64Decode([]byte(keyAccess.WrappedKey))
+ require.NoError(t, err)
+
+ plaintext, err := ocrypto.P384MLKEM1024UnwrapDEK(privateKey, wrappedKey)
+ require.NoError(t, err)
+ assert.Equal(t, symKey, plaintext)
+}
diff --git a/sdk/tdf_test.go b/sdk/tdf_test.go
index 0aa58daa3f..650fce17b1 100644
--- a/sdk/tdf_test.go
+++ b/sdk/tdf_test.go
@@ -259,6 +259,120 @@ A1UdIwQYMBaAFLg9mMeD25ZGvmjSYaunIPoeekzlMA8GA1UdEwEB/wQFMAMBAf8w
CgYIKoZIzj0EAwIDSAAwRQIhALYXC70t37RlmIkRDlUTehiVEHpSQXz04wQ9Ivw+
4h4hAiBNR3rD3KieiJaiJrCfM6TPJL7TIch7pAhMHdG6IPJMoQ==
-----END CERTIFICATE-----`
+
+ mockHybridXWingPublicKey = `-----BEGIN XWING PUBLIC KEY-----
+4pKOW2Z+TqohuAO7Z8m1J9Ik5jbLOZgpfKKTdEXD4mqLgXa8D5RNaYhtkfJn4js6
+MWO35ZJqlfVuCoBnruillfoEY2VD6OVB7AQqBJyzhLeAFsOYgwLIF9UBXixSo6ym
+eAA6GhYvzViUx/oQSHCI6JWhKhINXqar7usmFlxC/da4rolS9AGAkpx3S8F1VRyN
+tBahkbcLDptIqzRgOJWaXuYdtRqiuqahJyEWLjal/FQa0+dTA5oWn4IWUZRGvVut
+T/AFXMHMPdY7JmmmhNJAJTqcblHJ+FJ1YTBi0bEQE7F4XlqpdRxwISJSAGCZN6WD
+eTVF9CmpM0iJbnlN5cvAcwCFGamL5II0wwEhftMDIoCb+IiR8NQcQfJnFus9DvhR
+k5k7HVtjoumWWksL+tOudUtmYaOnV4CwyAunc8W+vsQ6edoCQWy8peRMvVcoaDUE
+tVuACiiwIWADLbsmGyiBR6x4/ashJLs46XsB4pmB1BfIf4yis+B5VvLCnZIsMvKH
+jLl81PMH4AsIytN0H/GOkJI/y1iGZsJkCZN15WJ2tAuwyMqz0yooh1lZIedgTheE
+lBKYx1YlFvmSCZBfLnKOIWFHpqkQ8OTGXWgBfvhwyhEui3ZhJbhmgBQrYAeN8ZcP
+s2isMmmzKVRcMzVFXaynv+UIiWVAH5BeXUbL0BVIkJulgXJptEyXs6JPGbEu1/ug
+uvaWx6xWQ3NiR+BOA5eIigwjoQEtyYRczlg0kUcivca+/TiE3yh2gkU2xhLNFXdL
+mHw1jNIbD5O5UPoGEdJ2XRLLVZJhhdmSWpN51FoW/OA6tJaMedtaNwjKD9wjJYEM
+izoy9cSNuWZNKMlNdNc9aPfIcQoD2xKKKZR7iHdVNpRQEYiWFrxqT8aY3JN8UjEy
+hdqeS7VAbQc28Ts7kxNa2xRmBQaOn3pb/eO+5LV/Y0Y4E7U7TXt3eBwbo5luf6Sg
+dnBjypqJAmUzpEl6MrOs7Hh7RxByyKDIgYiNAaNUnwOI7USjxlN5sKhpj+iKvCYF
+oCmRBMQ8w+p2s3GiXtB/whh1gwtIzTGkBpyaJrpjrFm67Bp88aUG3GJ5QKy/YPct
+tVKRPcNRhlsrOsEwB0qE/+Qj2gcPDAydGIp3hxiJxFgun1RQp/BkiEJW22xN2Vim
+n+tyUcRRvQCEoSU0+NXHddQvkzwHJVpdvsgRfgxHIlpTGXBReaQnRGZl2zEDVZGb
+vva6ubOu8XhMJAs1n9M41aVlP4bDmwmNv/oTKwi2kSdObkZv2INLucxtiJZ3BhbC
+NMiEXVU9DdS/USqZFpcQHqYB63hAMdmRsaM1wVW7HbRumglJY+A/hqgaV2wGtjNR
+5qNHS4FTT7euxsOs0PIxE3sOgDmseTd0UditGXd3O5xx7LkBe4GdNTuXq+rMgiZf
+P0i2ugDCV4McS4zKfpamYPQKqBufsiNkbMSVu+Mb7kQcgZZBY8gTnEyEY2k9oiJ5
+v6dH/oNa5ohGvHJjEcm4/3y3C7er0OKKM8JLkuIuUYsP7yqPY0V8LPjKI/wS0wxS
+yv8kgAJe+QrgMAOzPsw5fAeK5Lrz+OhSRsF/hRJnlAgbv/UAHZQR7ueSrL6Tj8tY
+Ubd0E5EZlOEecFd/41z5QA==
+-----END XWING PUBLIC KEY-----`
+
+ // X-Wing private key is a 32-byte seed; the full X25519 + ML-KEM-768
+ // components are derived from it at runtime by circl.
+ mockHybridXWingPrivateKey = `-----BEGIN XWING PRIVATE KEY-----
+7uCvk28wGoVrwW5nU2huW28UXWa5tZMom6Zds8uohrA=
+-----END XWING PRIVATE KEY-----`
+
+ mockHybridP256MLKEM768PublicKey = `-----BEGIN SECP256R1 MLKEM768 PUBLIC KEY-----
+BGtgB2txkSmwez5qXBEmetZijnkCuYWdhPvcPoAHr5Aiv1Zk895ARtcgbO1KIye8
+52Wnc48CY2baUfSHptVKmfsnRbmsDEdZoTWyaaaAmieMws9Z9yZuwVNB2sx7AQES
+d7ozp2t+nAftGc0WhUYxXDaup0usxA0QkpR7VqDa/ATqek+XlQm3TJ4pzMMIPK9H
+FY6iUJchpAZ7+nQesKpDErrzQxeoGbCOOIqxAyaHNVgbxAxVIj8c23iwdMZKwMPC
+LDb22Do61X7vFV0WCTTeVRJJ6lXVA6TkyVpXmb2A4gpUtSTGssINSSxxrA/wklA8
+gpqb4lurdYylQxBwArs0wnzhmThm2T/gOW89Mw6XnLbNqosqlcESwjfKXCKt63ZB
+ZDVR2Wlc2RxWu0jysaoqUkYIZREceoFjSUWwagI0o8TVeAiVUBJD0AgtNcouJjad
+pJvB6MYQlpe2F2sr1gvOlZph1IBkgpO8dRlKZ1yEgzUchG1tummcs3/lq2ois8fl
+ZABkxVs9fMkO5G3vOgzucYjQe698sLTzaRygiKHnVS5GsWhBOq6eWVfOiwt+Klvv
+KI6AfBQ2UFXsrBqXW8sjApWQ1xZVICtSBsksBhuf8GDh3AlNYxAJw0AAO6vgc7is
+SRVZKCOsCKby+LxlFiEabGYbES07iHBvqx5ZCGHl+RB5SqJzWV/nPIrHrBmNiFvq
+EYTnnLJ1hyqa7ABjGCeisoU6Js3yw1L6QMwtZS4sjMQdSWCoVqIeBimFtbFacVKR
+OhR2YUVesQ8hgp21CjtJRMF8917OCbc1xrMj4by5tr5Aq0LjRlL+EcIcNgkjJrz9
+sXrXK2GvxZcf/FknshQ4pme+AJIvp771I3SLVEvdhbf35aRJCIQ3xQ5Y6Ijxa5Ag
+wls8WMkAwQw3IBjNeYVcYn6GiTOet7yAoU+Z9Jx06SK/kqQ7HMJDChkJtLJLNJH/
+a7HrRZlZ4TA92kLP+R/ym6Kle8rN1iNiUXLxmKUR51vuNKnPVh4AGKQb0MYQurjo
+YWcVg6I6MYV2XFIJAiqTBdC8kWUyZFYrw5M4E3RM+0M/slEWyQAf1L26FQodUKce
+dqpo94trsCjTpbRQ+aTSNZdAHMwxkrR02TzUmoWTl0TUE8nS6yxTwUcd9wy+5KnX
+mzHD42I6eyTgnAhLiZOOEpBXBX6VM4Zq1z6GO25id7kju2kgEREFnFVf7G3JWrZz
+yXJ1SBLVlina4kmexJTTAyzi9IQqcAewm6nsc7byBpBenBA6aAjYZMQYtaSK+IRE
+6norOKeJ0w+ZpAPdC43Mw7GBhD4eI3FQcJOce2L9yXgPYQCEaEJvDJLmJKo26iq5
+WmYEQwe1abKouruKNyaON2TEWLQBSqujca8MFKBOsXisvApkdm+fK3CLVjgSCAM4
+7LfBDFSHA7wj+KC2oXqUgwinBSgYkkJ6Vahny6GadZdpZAmYQCNLhgDDMAOcVxUx
+aSQeIECzocR08QslRXNV542FlXlVx2b/ZC5yaFwAUYOACbyYq1azeTOIWyFoKGKa
+uRpK+2jglo7wB7kU5LcIB8ZTFwLjGDZsEKdbmgzV+yVDqxXU1llgvKfhFmOfSALF
+S39eOBaIhT1WOWpxinZRaagtmi8UQ0uE0uGhIV7fe+lLGuLxYKa/KYQe7wL6j0sL
+wQ==
+-----END SECP256R1 MLKEM768 PUBLIC KEY-----`
+
+ mockHybridP256MLKEM768PrivateKey = `-----BEGIN SECP256R1 MLKEM768 PRIVATE KEY-----
+j60Fn/LBoCGnWihNB3Q6lXJaO4EkMCrEnOT3/z0zZ6Kdr+6m7ww3N6lLMP+Rb2O1
+3gjrOyiNXWbaTwl1mI2vi03PoW2bJbE1+wJuiT/2njjrBRyIxLqRD/4zbiscmhOp
+-----END SECP256R1 MLKEM768 PRIVATE KEY-----`
+
+ mockHybridP384MLKEM1024PublicKey = `-----BEGIN SECP384R1 MLKEM1024 PUBLIC KEY-----
+BFFAmpsfvYsT+TgT6noyJJ/0402OEfhJCdG9cXJDQPo5Q7VGRTr14roVHy8VP6Me
+p4aiDnR2RaL+K7SmqsGRzP/vOSNz3ApEl24pAI8CnLMHDhkM7fKyAQrDje4aqQQv
+2/a7h0LFLy27wBaSzpKmCzVrDtoFrVY8G9smtWoxyGucRpT5qxvhNcoHxpt3fyaV
+mn0yXgyHZNiyxxsrPtjWA4x3bATWl5+bMRZ8rlFcmVbYFuGmLcnqXzf3ESyaCxxi
+nCRGnIqWSPrlB56kS+6xXIykOsXSLgjaA1QUG/3XSNjXcKlayMa4g6qalMQ0v5jR
+M2Z3FZzKnIpzJT1HejYJOC1JavnsOgWGkjGyIRZwfgpExdkCaMOQn9vVBbF7axDI
+pY48aFNJEUooKYUpfKPypksbnRIJjNd2Wek2F7H2wyYrfx62MalHmOCheNoGaTGW
+diokDS81m8jIUVGGuzGlbaPWLz9wBKgofzGrlPnispojJu2ZRK0aHAM6eOz5N1Zn
+zh6SVgTmVD9GGo3FgVMcBxmICxroLEEUuov8vkq1dkfwDHXZDmoTRTHrefzbyjNj
+K1xUIdlbWWjmUokmZ4OqAck0nRwImL13qYvWMpcWYWUxpxHzPozXBBKIxTnAI9UM
+VU85htoJIh5iMXZoy+/bu3TawdaMHNWbtOlcjyE2m3m2F0Z7v7RVHuP2mYfErW/H
+NRCEKhsmUulJRRDnVvdQCwrpE+rDCCf6zcybaDzhSKMkZlP8V0UDGr4Ghgg7KfYD
+hg1ppiVlFhljvMn8c2fGrWqFu1KjJ2MHbpIBIPu6pnOjjyuJp7xFtsh4ReqMbNsX
+WMcJF83UvJvxd6+AgGiVbckRXUz5PEF6GeLxACKzMnwDqu9VWOSXifYbhuCWbv6M
+diYVqg7zjafUU1Rpwolbc2rpGiwciYX7APrnM7jnVQk6kY7GyyxoXFjhk+vGf0+r
+OAHKei4ldZnLObukFdOQiIhWIdCSoh47MutmsS7RLCmVOPzozkMzg+GgoeMxgt4U
+sgWMhtP2dmi2VVY0PD0HeN1LCGJqqCPyop15QkNxYTiXx5sgwggppfkZrcbJLCuT
+l5q5NRzwF3a3wZerjJd1L3QXM+QprnUIlwGCg25ZWnAzZB0FvxExIQ1wsusJZ24x
+uPiBACs2sSvlZoPAN0imG6EWFvX8SfNzpTnpQP6jR2iAQea0EJPqSYv0kmtYIux3
+shtacT0DH/5TaiG6DITyzTzUV1OTY/mpBqBkzVfwLN9BCzyINHmSDjsmExCKY3CY
+Kd3RLuLDdM0AkjHHcl9zo7RypdAGIKnScqASeoR3yOUaP4LKysCUB6kBDg3ZftQR
+IHiLOC6aMMKMdZbpKDuECBYDxblTOrcBhnAlX7eokZk1IEMSbmWngwHTfVRbfx+A
+A5pUOCjxkYanl234p4KZYLRFMonalmkLb3msgY81ctzZjOxIXAb4XkirAiGDZe2V
+T32HKlVjK2hzZpwRjZZ5rKS5IuRUiF6KZnnqDDzaI4dSfd4Sy1+FtmaAMwvidjf7
+FaPxCaJAccnVObwwjYNFuN1otiK6d860ot9AEUbInOOFEpTqzUUChY7Yr9Y4H/L3
+iYRbiH6LLfzjzIiGUEIkWCcar6jyZoKqwD7sMgMVGBOQjRqGx79KVgbSt1uakCBM
+UaS2AEwsmFVVD8HCBGkFW5n1kAtrtmpAVZQiSSALW1BDl8csWz+SwGFpVqmHXHBp
+EAYQb4HpGOE5X4sIIj1iY/B0vqpZHCUWbTUzJ7TLfVunawHVbrhKfwcIxuU2BHb3
+fg8giZ6GzNrUwN8YLeejct4iOv7IPF1XUJiqH5Qmb5USx96jtaJJLY+QDwtxfDcc
+acsJKxn1MpRgrlS4vx8HOIqUhYlAnc2gmoU5BvZAXhXSUaXTLCpBGJN0uhc0Lyo3
+d2BKQzJEqmEHtB8cU544AKdjwgbWsJhFtukXYg6rSYDZjTlKSztyV1omJ+9EUIN5
+v7CCLVfKunF0Yx1LAL8JjQUhaB3cGHicmq9lC8pSBkAjKn3ZIrO8zuCDt6FTtzYZ
+QVLjh0Hwh2hYanBLTDpLCpPxOdjYtWWWTRMnZ1wLwFQ0dPhbY8d6hQMrH5bBgg37
+dG0Gc/66ok9LqouBxAbrPBiooEhENhm4j2uiUSSKxY/bwlNHkgBle/VImlJ8Gt08
+f+Pe1WdvKDHXa4HUtLeScODyYJAVXQF6Ww1pLvCRy/gd
+-----END SECP384R1 MLKEM1024 PUBLIC KEY-----`
+
+ mockHybridP384MLKEM1024PrivateKey = `-----BEGIN SECP384R1 MLKEM1024 PRIVATE KEY-----
+0Tu+86kOEFj2BP8fRnWqaPq6d+E1Ufhl7/OJqmg1zLoYgPtEW4QBfLgxUM8Q0nUj
+bh9BKtvBsaDd3GXyKpjIM0zXv8mzSf+bZQQ9zfaaSMJ3RsPoPhViUXLYnvbDrKfM
+cql24SfEHKvj8gifFuV/eg==
+-----END SECP384R1 MLKEM1024 PRIVATE KEY-----`
)
type TestReadAt struct {
@@ -349,6 +463,7 @@ func (s *TDFSuite) Test_SimpleTDF() {
tdfOptions []TDFOption
tdfReadOptions []TDFReaderOption
useHex bool
+ expectedSize int64 // override default expectedTdfSize if non-zero
}
metaData := []byte(`{"displayName" : "openTDF go sdk"}`)
@@ -428,6 +543,54 @@ func (s *TDFSuite) Test_SimpleTDF() {
},
useHex: true,
},
+ {
+ name: "metadata-hybrid-p256-mlkem768",
+ tdfOptions: []TDFOption{
+ WithKasInformation(KASInfo{
+ URL: s.kasTestURLLookup["https://f.kas/"],
+ PublicKey: "",
+ }),
+ WithMetaData(string(metaData)),
+ WithDataAttributes(attributes...),
+ WithWrappingKeyAlg(ocrypto.HybridSecp256r1MLKEM768Key),
+ },
+ tdfReadOptions: []TDFReaderOption{
+ WithKasAllowlist([]string{s.kasTestURLLookup["https://f.kas/"]}),
+ },
+ expectedSize: 3364,
+ },
+ {
+ name: "metadata-hybrid-p384-mlkem1024",
+ tdfOptions: []TDFOption{
+ WithKasInformation(KASInfo{
+ URL: s.kasTestURLLookup["https://g.kas/"],
+ PublicKey: "",
+ }),
+ WithMetaData(string(metaData)),
+ WithDataAttributes(attributes...),
+ WithWrappingKeyAlg(ocrypto.HybridSecp384r1MLKEM1024Key),
+ },
+ tdfReadOptions: []TDFReaderOption{
+ WithKasAllowlist([]string{s.kasTestURLLookup["https://g.kas/"]}),
+ },
+ expectedSize: 4048,
+ },
+ {
+ name: "metadata-hybrid-xwing",
+ tdfOptions: []TDFOption{
+ WithKasInformation(KASInfo{
+ URL: s.kasTestURLLookup["https://h.kas/"],
+ PublicKey: "",
+ }),
+ WithMetaData(string(metaData)),
+ WithDataAttributes(attributes...),
+ WithWrappingKeyAlg(ocrypto.HybridXWingKey),
+ },
+ tdfReadOptions: []TDFReaderOption{
+ WithKasAllowlist([]string{s.kasTestURLLookup["https://h.kas/"]}),
+ },
+ expectedSize: 3320,
+ },
}
for _, config := range testConfigs {
@@ -448,11 +611,14 @@ func (s *TDFSuite) Test_SimpleTDF() {
tdfObj, err := s.sdk.CreateTDF(fileWriter, bufReader, config.tdfOptions...)
s.Require().NoError(err)
+ expected := expectedTdfSize
if config.useHex {
- s.InDelta(float64(expectedTdfSizeWithHex), float64(tdfObj.size), 64.0)
- } else {
- s.InDelta(float64(expectedTdfSize), float64(tdfObj.size), 64.0)
+ expected = expectedTdfSizeWithHex
+ }
+ if config.expectedSize != 0 {
+ expected = config.expectedSize
}
+ s.InDelta(float64(expected), float64(tdfObj.size), 64.0)
// test meta data and build meta data
readSeeker, err := os.Open(tdfFilename)
@@ -2630,23 +2796,26 @@ func (s *TDFSuite) startBackend() {
fa := &FakeAttributes{s: s}
kasesToMake := []struct {
- url, private, public, kid string
+ url, private, public, kid, algorithm string
}{
- {"http://localhost:65432/", mockRSAPrivateKey1, mockRSAPublicKey1, defaultKID},
- {"http://[::1]:65432/", mockRSAPrivateKey1, mockRSAPublicKey1, defaultKID},
- {"https://a.kas/", mockRSAPrivateKey1, mockRSAPublicKey1, defaultKID},
- {"https://b.kas/", mockRSAPrivateKey2, mockRSAPublicKey2, defaultKID},
- {"https://c.kas/", mockRSAPrivateKey3, mockRSAPublicKey3, defaultKID},
- {"https://d.kas/", mockECPrivateKey1, mockECPublicKey1, "e1"},
- {"https://e.kas/", mockECPrivateKey2, mockECPublicKey2, defaultKID},
- {kasAu, mockRSAPrivateKey1, mockRSAPublicKey1, defaultKID},
- {kasCa, mockRSAPrivateKey2, mockRSAPublicKey2, defaultKID},
- {kasUk, mockRSAPrivateKey2, mockRSAPublicKey2, defaultKID},
- {kasNz, mockRSAPrivateKey3, mockRSAPublicKey3, defaultKID},
- {kasUs, mockRSAPrivateKey1, mockRSAPublicKey1, defaultKID},
- {baseKeyURL, mockRSAPrivateKey1, mockRSAPublicKey1, baseKeyKID},
- {evenMoreSpecificKas, mockRSAPrivateKey3, mockRSAPublicKey3, "r3"},
- {obligationKas, mockRSAPrivateKey3, mockRSAPublicKey3, "r3"},
+ {"http://localhost:65432/", mockRSAPrivateKey1, mockRSAPublicKey1, defaultKID, "rsa:2048"},
+ {"http://[::1]:65432/", mockRSAPrivateKey1, mockRSAPublicKey1, defaultKID, "rsa:2048"},
+ {"https://a.kas/", mockRSAPrivateKey1, mockRSAPublicKey1, defaultKID, "rsa:2048"},
+ {"https://b.kas/", mockRSAPrivateKey2, mockRSAPublicKey2, defaultKID, "rsa:2048"},
+ {"https://c.kas/", mockRSAPrivateKey3, mockRSAPublicKey3, defaultKID, "rsa:2048"},
+ {"https://d.kas/", mockECPrivateKey1, mockECPublicKey1, "e1", string(ocrypto.EC256Key)},
+ {"https://e.kas/", mockECPrivateKey2, mockECPublicKey2, defaultKID, string(ocrypto.EC256Key)},
+ {kasAu, mockRSAPrivateKey1, mockRSAPublicKey1, defaultKID, "rsa:2048"},
+ {kasCa, mockRSAPrivateKey2, mockRSAPublicKey2, defaultKID, "rsa:2048"},
+ {kasUk, mockRSAPrivateKey2, mockRSAPublicKey2, defaultKID, "rsa:2048"},
+ {kasNz, mockRSAPrivateKey3, mockRSAPublicKey3, defaultKID, "rsa:2048"},
+ {kasUs, mockRSAPrivateKey1, mockRSAPublicKey1, defaultKID, "rsa:2048"},
+ {baseKeyURL, mockRSAPrivateKey1, mockRSAPublicKey1, baseKeyKID, "rsa:2048"},
+ {evenMoreSpecificKas, mockRSAPrivateKey3, mockRSAPublicKey3, "r3", "rsa:2048"},
+ {obligationKas, mockRSAPrivateKey3, mockRSAPublicKey3, "r3", "rsa:2048"},
+ {"https://f.kas/", mockHybridP256MLKEM768PrivateKey, mockHybridP256MLKEM768PublicKey, "h1", string(ocrypto.HybridSecp256r1MLKEM768Key)},
+ {"https://g.kas/", mockHybridP384MLKEM1024PrivateKey, mockHybridP384MLKEM1024PublicKey, "h2", string(ocrypto.HybridSecp384r1MLKEM1024Key)},
+ {"https://h.kas/", mockHybridXWingPrivateKey, mockHybridXWingPublicKey, "h3", string(ocrypto.HybridXWingKey)},
}
fkar := &FakeKASRegistry{kases: kasesToMake, s: s}
@@ -2661,7 +2830,7 @@ func (s *TDFSuite) startBackend() {
s.kases[i] = FakeKas{
s: s, privateKey: ki.private, KASInfo: KASInfo{
- URL: ki.url, PublicKey: ki.public, KID: ki.kid, Algorithm: "rsa:2048",
+ URL: ki.url, PublicKey: ki.public, KID: ki.kid, Algorithm: ki.algorithm,
},
legakeys: map[string]keyInfo{},
attrToRequiredObligations: obligationMap,
@@ -2750,7 +2919,7 @@ type FakeKASRegistry struct {
kasregistryconnect.UnimplementedKeyAccessServerRegistryServiceHandler
s *TDFSuite
kases []struct {
- url, private, public, kid string
+ url, private, public, kid, algorithm string
}
}
@@ -2913,6 +3082,45 @@ func (f *FakeKas) getRewrapResponse(rewrapRequest string, fulfillableObligations
entityWrappedKey, err = asymEncrypt.Encrypt(symmetricKey)
f.s.Require().NoError(err, "ocrypto.AsymEncryption.encrypt failed")
+ case "hybrid-wrapped":
+ kasPrivateKey := strings.ReplaceAll(f.privateKey, "\n\t", "\n")
+ if kao.GetKid() != "" && kao.GetKid() != f.KID {
+ lk, ok := f.legakeys[kaoReq.GetKeyAccessObject().GetKid()]
+ f.s.Require().True(ok, "unable to find key [%s]", kao.GetKid())
+ kasPrivateKey = strings.ReplaceAll(lk.private, "\n\t", "\n")
+ }
+
+ var symmetricKey []byte
+ switch ocrypto.KeyType(f.Algorithm) { //nolint:exhaustive // only handle hybrid types
+ case ocrypto.HybridSecp256r1MLKEM768Key:
+ privateKey, err := ocrypto.P256MLKEM768PrivateKeyFromPem([]byte(kasPrivateKey))
+ f.s.Require().NoError(err, "failed to extract P256+ML-KEM-768 private key from PEM")
+ symmetricKey, err = ocrypto.P256MLKEM768UnwrapDEK(privateKey, wrappedKey)
+ f.s.Require().NoError(err, "failed to unwrap P256+ML-KEM-768 wrapped key")
+ case ocrypto.HybridSecp384r1MLKEM1024Key:
+ privateKey, err := ocrypto.P384MLKEM1024PrivateKeyFromPem([]byte(kasPrivateKey))
+ f.s.Require().NoError(err, "failed to extract P384+ML-KEM-1024 private key from PEM")
+ symmetricKey, err = ocrypto.P384MLKEM1024UnwrapDEK(privateKey, wrappedKey)
+ f.s.Require().NoError(err, "failed to unwrap P384+ML-KEM-1024 wrapped key")
+ case ocrypto.HybridXWingKey:
+ privateKey, err := ocrypto.XWingPrivateKeyFromPem([]byte(kasPrivateKey))
+ f.s.Require().NoError(err, "failed to extract X-Wing private key from PEM")
+ symmetricKey, err = ocrypto.XWingUnwrapDEK(privateKey, wrappedKey)
+ f.s.Require().NoError(err, "failed to unwrap X-Wing wrapped key")
+ default:
+ f.s.Require().Failf("unsupported hybrid algorithm", "algorithm: %s", f.Algorithm)
+ }
+
+ asymEncrypt, err := ocrypto.FromPublicPEMWithSalt(bodyData.GetClientPublicKey(), tdfSalt(), nil)
+ f.s.Require().NoError(err, "ocrypto.FromPublicPEMWithSalt failed")
+ if e, found := asymEncrypt.(ocrypto.ECEncryptor); found {
+ sessionKey, err := e.PublicKeyInPemFormat()
+ f.s.Require().NoError(err, "unable to serialize ephemeral key")
+ resp.SessionPublicKey = sessionKey
+ }
+ entityWrappedKey, err = asymEncrypt.Encrypt(symmetricKey)
+ f.s.Require().NoError(err, "ocrypto.encrypt failed")
+
case "wrapped":
kasPrivateKey := strings.ReplaceAll(f.privateKey, "\n\t", "\n")
if kao.GetKid() != "" && kao.GetKid() != f.KID {
diff --git a/service/cmd/keygen/main.go b/service/cmd/keygen/main.go
new file mode 100644
index 0000000000..721191e286
--- /dev/null
+++ b/service/cmd/keygen/main.go
@@ -0,0 +1,116 @@
+// Package main generates hybrid post-quantum KAS key pairs (X-Wing, P256+ML-KEM-768, P384+ML-KEM-1024)
+// as PEM files for use with the OpenTDF platform.
+package main
+
+import (
+ "flag"
+ "log"
+ "os"
+ "path/filepath"
+
+ "github.com/opentdf/platform/lib/ocrypto"
+)
+
+type keySpec struct {
+ name string
+ newKeyPair func() (privatePEM, publicPEM string, err error)
+ privateOut string
+ publicOut string
+}
+
+func main() {
+ outputDir := flag.String("output", ".", "directory to write PEM files")
+ flag.Parse()
+
+ if err := os.MkdirAll(*outputDir, 0o755); err != nil {
+ log.Fatalf("failed to create output directory: %v", err)
+ }
+
+ specs := []keySpec{
+ {
+ name: "X-Wing",
+ newKeyPair: generateXWing,
+ privateOut: "kas-xwing-private.pem",
+ publicOut: "kas-xwing-public.pem",
+ },
+ {
+ name: "P256+ML-KEM-768",
+ newKeyPair: generateP256MLKEM768,
+ privateOut: "kas-p256mlkem768-private.pem",
+ publicOut: "kas-p256mlkem768-public.pem",
+ },
+ {
+ name: "P384+ML-KEM-1024",
+ newKeyPair: generateP384MLKEM1024,
+ privateOut: "kas-p384mlkem1024-private.pem",
+ publicOut: "kas-p384mlkem1024-public.pem",
+ },
+ }
+
+ for _, s := range specs {
+ privatePEM, publicPEM, err := s.newKeyPair()
+ if err != nil {
+ log.Fatalf("failed to generate %s key pair: %v", s.name, err)
+ }
+
+ privPath := filepath.Join(*outputDir, s.privateOut)
+ pubPath := filepath.Join(*outputDir, s.publicOut)
+
+ if err := os.WriteFile(privPath, []byte(privatePEM), 0o600); err != nil {
+ log.Fatalf("failed to write %s: %v", privPath, err)
+ }
+ if err := os.WriteFile(pubPath, []byte(publicPEM), 0o600); err != nil {
+ log.Fatalf("failed to write %s: %v", pubPath, err)
+ }
+
+ log.Printf("Generated %s key pair:\n - Private: %s\n - Public: %s", s.name, privPath, pubPath)
+ }
+}
+
+func generateXWing() (string, string, error) {
+ kp, err := ocrypto.NewXWingKeyPair()
+ if err != nil {
+ return "", "", err
+ }
+ priv, err := kp.PrivateKeyInPemFormat()
+ if err != nil {
+ return "", "", err
+ }
+ pub, err := kp.PublicKeyInPemFormat()
+ if err != nil {
+ return "", "", err
+ }
+ return priv, pub, nil
+}
+
+func generateP256MLKEM768() (string, string, error) {
+ kp, err := ocrypto.NewP256MLKEM768KeyPair()
+ if err != nil {
+ return "", "", err
+ }
+ priv, err := kp.PrivateKeyInPemFormat()
+ if err != nil {
+ return "", "", err
+ }
+ pub, err := kp.PublicKeyInPemFormat()
+ if err != nil {
+ return "", "", err
+ }
+ return priv, pub, nil
+}
+
+func generateP384MLKEM1024() (string, string, error) {
+ kp, err := ocrypto.NewP384MLKEM1024KeyPair()
+ if err != nil {
+ return "", "", err
+ }
+ priv, err := kp.PrivateKeyInPemFormat()
+ if err != nil {
+ return "", "", err
+ }
+ pub, err := kp.PublicKeyInPemFormat()
+ if err != nil {
+ return "", "", err
+ }
+ return priv, pub, nil
+}
diff --git a/service/internal/security/basic_manager.go b/service/internal/security/basic_manager.go
index 6197e50eb3..139ae7ce31 100644
--- a/service/internal/security/basic_manager.go
+++ b/service/internal/security/basic_manager.go
@@ -97,6 +97,57 @@ func (b *BasicManager) Decrypt(ctx context.Context, keyDetails trust.KeyDetails,
return nil, fmt.Errorf("failed to create protected key: %w", err)
}
return protectedKey, nil
+ case ocrypto.HybridXWingKey:
+ if len(ephemeralPublicKey) > 0 {
+ return nil, errors.New("ephemeral public key should not be provided for X-Wing decryption")
+ }
+ xwingPrivKey, err := ocrypto.XWingPrivateKeyFromPem(privKey)
+ if err != nil {
+ return nil, fmt.Errorf("failed to create X-Wing private key from PEM: %w", err)
+ }
+ plaintext, err := ocrypto.XWingUnwrapDEK(xwingPrivKey, ciphertext)
+ if err != nil {
+ return nil, fmt.Errorf("failed to decrypt with X-Wing: %w", err)
+ }
+ protectedKey, err := ocrypto.NewAESProtectedKey(plaintext)
+ if err != nil {
+ return nil, fmt.Errorf("failed to create protected key: %w", err)
+ }
+ return protectedKey, nil
+ case ocrypto.HybridSecp256r1MLKEM768Key:
+ if len(ephemeralPublicKey) > 0 {
+ return nil, errors.New("ephemeral public key should not be provided for hybrid decryption")
+ }
+ privKeyBytes, err := ocrypto.P256MLKEM768PrivateKeyFromPem(privKey)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse P256-MLKEM768 private key from PEM: %w", err)
+ }
+ plaintext, err := ocrypto.P256MLKEM768UnwrapDEK(privKeyBytes, ciphertext)
+ if err != nil {
+ return nil, fmt.Errorf("failed to decrypt with P256-MLKEM768: %w", err)
+ }
+ protectedKey, err := ocrypto.NewAESProtectedKey(plaintext)
+ if err != nil {
+ return nil, fmt.Errorf("failed to create protected key: %w", err)
+ }
+ return protectedKey, nil
+ case ocrypto.HybridSecp384r1MLKEM1024Key:
+ if len(ephemeralPublicKey) > 0 {
+ return nil, errors.New("ephemeral public key should not be provided for hybrid decryption")
+ }
+ privKeyBytes, err := ocrypto.P384MLKEM1024PrivateKeyFromPem(privKey)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse P384-MLKEM1024 private key from PEM: %w", err)
+ }
+ plaintext, err := ocrypto.P384MLKEM1024UnwrapDEK(privKeyBytes, ciphertext)
+ if err != nil {
+ return nil, fmt.Errorf("failed to decrypt with P384-MLKEM1024: %w", err)
+ }
+ protectedKey, err := ocrypto.NewAESProtectedKey(plaintext)
+ if err != nil {
+ return nil, fmt.Errorf("failed to create protected key: %w", err)
+ }
+ return protectedKey, nil
}
return nil, fmt.Errorf("unsupported algorithm: %s", keyDetails.Algorithm())
diff --git a/service/internal/security/crypto_provider.go b/service/internal/security/crypto_provider.go
index 24f0f3c2ea..dcbe1ae5a1 100644
--- a/service/internal/security/crypto_provider.go
+++ b/service/internal/security/crypto_provider.go
@@ -11,4 +11,11 @@ const (
// Used for encryption with RSA of the KAO
AlgorithmRSA2048 = "rsa:2048"
AlgorithmRSA4096 = "rsa:4096"
+
+ // Used for hybrid X-Wing wrapping of the KAO
+ AlgorithmHPQTXWing = "hpqt:xwing"
+
+ // Used for hybrid NIST EC + ML-KEM wrapping of the KAO
+ AlgorithmHPQTSecp256r1MLKEM768 = "hpqt:secp256r1-mlkem768"
+ AlgorithmHPQTSecp384r1MLKEM1024 = "hpqt:secp384r1-mlkem1024"
)
diff --git a/service/internal/security/in_process_provider.go b/service/internal/security/in_process_provider.go
index 67c86487aa..0b9249f4aa 100644
--- a/service/internal/security/in_process_provider.go
+++ b/service/internal/security/in_process_provider.go
@@ -65,7 +65,7 @@ func (k *KeyDetailsAdapter) ExportPublicKey(_ context.Context, format trust.KeyT
switch format {
case trust.KeyTypeJWK:
// For JWK format (currently only supported for RSA)
- if k.algorithm == AlgorithmRSA2048 {
+ if k.algorithm == AlgorithmRSA2048 || k.algorithm == AlgorithmRSA4096 {
return k.cryptoProvider.RSAPublicKeyAsJSON(kid)
}
// For EC keys, we return the public key in PEM format
@@ -84,6 +84,12 @@ func (k *KeyDetailsAdapter) ExportPublicKey(_ context.Context, format trust.KeyT
if rsaKey, err := k.cryptoProvider.RSAPublicKey(kid); err == nil {
return rsaKey, nil
}
+ if hybridKey, err := k.cryptoProvider.HybridPublicKey(kid); err == nil {
+ return hybridKey, nil
+ }
+ if xwingKey, err := k.cryptoProvider.XWingPublicKey(kid); err == nil {
+ return xwingKey, nil
+ }
return k.cryptoProvider.ECPublicKey(kid)
default:
return "", ErrCertNotFound
@@ -170,31 +176,17 @@ func (a *InProcessProvider) FindKeyByAlgorithm(_ context.Context, algorithm stri
// FindKeyByID finds a key by ID
func (a *InProcessProvider) FindKeyByID(_ context.Context, id trust.KeyIdentifier) (trust.KeyDetails, error) {
- // Try to determine the algorithm by checking if the key works with known algorithms
- for _, alg := range []string{AlgorithmECP256R1, AlgorithmRSA2048} {
- // This is a hack since the original provider doesn't have a way to check if a key exists
- switch alg {
- case AlgorithmECP256R1:
- if _, err := a.cryptoProvider.ECPublicKey(string(id)); err == nil {
- return &KeyDetailsAdapter{
- id: id,
- algorithm: ocrypto.KeyType(alg),
- legacy: a.legacyKeys[string(id)],
- cryptoProvider: a.cryptoProvider,
- }, nil
- }
- case AlgorithmRSA2048:
- if _, err := a.cryptoProvider.RSAPublicKey(string(id)); err == nil {
- return &KeyDetailsAdapter{
- id: id,
- algorithm: ocrypto.KeyType(alg),
- legacy: a.legacyKeys[string(id)],
- cryptoProvider: a.cryptoProvider,
- }, nil
- }
- }
+ keyType, err := a.determineKeyType(string(id))
+ if err != nil {
+ return nil, ErrCertNotFound
}
- return nil, ErrCertNotFound
+
+ return &KeyDetailsAdapter{
+ id: id,
+ algorithm: ocrypto.KeyType(keyType),
+ legacy: a.legacyKeys[string(id)],
+ cryptoProvider: a.cryptoProvider,
+ }, nil
}
// ListKeys lists all available keys
@@ -207,7 +199,7 @@ func (a *InProcessProvider) ListKeysWith(ctx context.Context, opts trust.ListKey
var keys []trust.KeyDetails
// Try to find keys for known algorithms
- for _, alg := range []string{AlgorithmRSA2048, AlgorithmECP256R1} {
+ for _, alg := range []string{AlgorithmRSA2048, AlgorithmRSA4096, AlgorithmECP256R1, AlgorithmHPQTXWing, AlgorithmHPQTSecp256r1MLKEM768, AlgorithmHPQTSecp384r1MLKEM1024} {
if kids, err := a.cryptoProvider.ListKIDsByAlgorithm(alg); err == nil && len(kids) > 0 {
for _, kid := range kids {
if opts.LegacyOnly && !a.legacyKeys[kid] {
@@ -242,7 +234,7 @@ func (a *InProcessProvider) Decrypt(ctx context.Context, keyDetails trust.KeyDet
var err error
// Try to determine the key type
- keyType, err := a.determineKeyType(ctx, kid)
+ keyType, err := a.determineKeyType(kid)
if err != nil {
return nil, err
}
@@ -250,6 +242,8 @@ func (a *InProcessProvider) Decrypt(ctx context.Context, keyDetails trust.KeyDet
var rawKey []byte
switch keyType {
case AlgorithmRSA2048:
+ fallthrough
+ case AlgorithmRSA4096:
if len(ephemeralPublicKey) > 0 {
return nil, errors.New("ephemeral public key should not be provided for RSA decryption")
}
@@ -261,6 +255,12 @@ func (a *InProcessProvider) Decrypt(ctx context.Context, keyDetails trust.KeyDet
}
protectedKey, err = a.cryptoProvider.ECDecrypt(ctx, kid, ephemeralPublicKey, ciphertext)
+ case AlgorithmHPQTXWing, AlgorithmHPQTSecp256r1MLKEM768, AlgorithmHPQTSecp384r1MLKEM1024:
+ if len(ephemeralPublicKey) > 0 {
+ return nil, errors.New("ephemeral public key should not be provided for hybrid decryption")
+ }
+ return a.cryptoProvider.Decrypt(ctx, trust.KeyIdentifier(kid), ciphertext, nil)
+
default:
return nil, errors.New("unsupported key algorithm")
}
@@ -335,17 +335,22 @@ func (a *InProcessProvider) Close() {
a.cryptoProvider.Close()
}
-// determineKeyType tries to determine the algorithm of a key based on its ID
-// This is a helper method for the Decrypt method
-func (a *InProcessProvider) determineKeyType(_ context.Context, kid string) (string, error) {
- // First try RSA
- if _, err := a.cryptoProvider.RSAPublicKey(kid); err == nil {
- return AlgorithmRSA2048, nil
+// determineKeyType returns the configured algorithm for a loaded key.
+func (a *InProcessProvider) determineKeyType(kid string) (string, error) {
+ key, ok := a.cryptoProvider.keysByID[kid]
+ if !ok {
+ return "", errors.New("could not determine key type")
}
- // Then try EC
- if _, err := a.cryptoProvider.ECPublicKey(kid); err == nil {
- return AlgorithmECP256R1, nil
+ switch key := key.(type) {
+ case StandardRSACrypto:
+ return key.Algorithm, nil
+ case StandardECCrypto:
+ return key.Algorithm, nil
+ case StandardXWingCrypto:
+ return key.Algorithm, nil
+ case StandardHybridCrypto:
+ return key.Algorithm, nil
}
return "", errors.New("could not determine key type")
diff --git a/service/internal/security/in_process_provider_test.go b/service/internal/security/in_process_provider_test.go
index 41ce4c125e..9870ad53bb 100644
--- a/service/internal/security/in_process_provider_test.go
+++ b/service/internal/security/in_process_provider_test.go
@@ -218,14 +218,14 @@ func TestInProcessProviderDetermineKeyType(t *testing.T) {
provider, ok := providerIface.(*InProcessProvider)
require.True(t, ok)
- keyType, err := provider.determineKeyType(t.Context(), material.rsaKid)
+ keyType, err := provider.determineKeyType(material.rsaKid)
require.NoError(t, err)
assert.Equal(t, AlgorithmRSA2048, keyType)
- keyType, err = provider.determineKeyType(t.Context(), material.ecKid)
+ keyType, err = provider.determineKeyType(material.ecKid)
require.NoError(t, err)
assert.Equal(t, AlgorithmECP256R1, keyType)
- _, err = provider.determineKeyType(t.Context(), "missing")
+ _, err = provider.determineKeyType("missing")
require.Error(t, err)
}
diff --git a/service/internal/security/standard_crypto.go b/service/internal/security/standard_crypto.go
index 6b86abdcbe..59953a8523 100644
--- a/service/internal/security/standard_crypto.go
+++ b/service/internal/security/standard_crypto.go
@@ -67,6 +67,18 @@ type StandardECCrypto struct {
sk *ecdh.PrivateKey
}
+type StandardXWingCrypto struct {
+ KeyPairInfo
+ xwingPrivateKeyPem string
+ xwingPublicKeyPem string
+}
+
+type StandardHybridCrypto struct {
+ KeyPairInfo
+ hybridPrivateKeyPem string
+ hybridPublicKeyPem string
+}
+
// List of keys by identifier
type keylist map[string]any
@@ -150,6 +162,18 @@ func loadKey(k KeyPairInfo) (any, error) {
ecPrivateKeyPem: string(privatePEM),
ecCertificatePEM: string(certPEM),
}, nil
+ case AlgorithmHPQTXWing:
+ return StandardXWingCrypto{
+ KeyPairInfo: k,
+ xwingPrivateKeyPem: string(privatePEM),
+ xwingPublicKeyPem: string(certPEM),
+ }, nil
+ case AlgorithmHPQTSecp256r1MLKEM768, AlgorithmHPQTSecp384r1MLKEM1024:
+ return StandardHybridCrypto{
+ KeyPairInfo: k,
+ hybridPrivateKeyPem: string(privatePEM),
+ hybridPublicKeyPem: string(certPEM),
+ }, nil
case AlgorithmRSA2048, AlgorithmRSA4096:
asymDecryption, err := ocrypto.NewAsymDecryption(string(privatePEM))
if err != nil {
@@ -329,6 +353,42 @@ func (s StandardCrypto) ECPublicKey(kid string) (string, error) {
return string(pemBytes), nil
}
+func (s StandardCrypto) XWingPublicKey(kid string) (string, error) {
+ k, ok := s.keysByID[kid]
+ if !ok {
+ return "", fmt.Errorf("no xwing key with id [%s]: %w", kid, ErrCertNotFound)
+ }
+ xw, ok := k.(StandardXWingCrypto)
+ if !ok {
+ return "", fmt.Errorf("key with id [%s] is not an X-Wing key: %w", kid, ErrCertNotFound)
+ }
+ if xw.xwingPublicKeyPem == "" {
+ return "", fmt.Errorf("no X-Wing public key with id [%s]: %w", kid, ErrCertNotFound)
+ }
+ return xw.xwingPublicKeyPem, nil
+}
+
+func (s StandardCrypto) HybridPublicKey(kid string) (string, error) {
+ k, ok := s.keysByID[kid]
+ if !ok {
+ return "", fmt.Errorf("no hybrid key with id [%s]: %w", kid, ErrCertNotFound)
+ }
+ switch h := k.(type) {
+ case StandardXWingCrypto:
+ if h.xwingPublicKeyPem == "" {
+ return "", fmt.Errorf("no hybrid public key with id [%s]: %w", kid, ErrCertNotFound)
+ }
+ return h.xwingPublicKeyPem, nil
+ case StandardHybridCrypto:
+ if h.hybridPublicKeyPem == "" {
+ return "", fmt.Errorf("no hybrid public key with id [%s]: %w", kid, ErrCertNotFound)
+ }
+ return h.hybridPublicKeyPem, nil
+ default:
+ return "", fmt.Errorf("key with id [%s] is not a hybrid key: %w", kid, ErrCertNotFound)
+ }
+}
+
func (s StandardCrypto) RSADecrypt(_ crypto.Hash, kid string, _ string, ciphertext []byte) ([]byte, error) {
k, ok := s.keysByID[kid]
if !ok {
@@ -439,6 +499,49 @@ func (s *StandardCrypto) Decrypt(_ context.Context, keyID trust.KeyIdentifier, c
return nil, fmt.Errorf("error decrypting data: %w", err)
}
+ case StandardXWingCrypto:
+ if len(ephemeralPublicKey) > 0 {
+ return nil, errors.New("ephemeral public key should not be provided for X-Wing decryption")
+ }
+
+ privateKey, err := ocrypto.XWingPrivateKeyFromPem([]byte(key.xwingPrivateKeyPem))
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse X-Wing private key: %w", err)
+ }
+
+ rawKey, err = ocrypto.XWingUnwrapDEK(privateKey, ciphertext)
+ if err != nil {
+ return nil, fmt.Errorf("failed to decrypt with X-Wing: %w", err)
+ }
+
+ case StandardHybridCrypto:
+ if len(ephemeralPublicKey) > 0 {
+ return nil, errors.New("ephemeral public key should not be provided for hybrid decryption")
+ }
+
+ switch key.Algorithm {
+ case AlgorithmHPQTSecp256r1MLKEM768:
+ privateKey, err := ocrypto.P256MLKEM768PrivateKeyFromPem([]byte(key.hybridPrivateKeyPem))
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse P256-MLKEM768 private key: %w", err)
+ }
+ rawKey, err = ocrypto.P256MLKEM768UnwrapDEK(privateKey, ciphertext)
+ if err != nil {
+ return nil, fmt.Errorf("failed to decrypt with P256-MLKEM768: %w", err)
+ }
+ case AlgorithmHPQTSecp384r1MLKEM1024:
+ privateKey, err := ocrypto.P384MLKEM1024PrivateKeyFromPem([]byte(key.hybridPrivateKeyPem))
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse P384-MLKEM1024 private key: %w", err)
+ }
+ rawKey, err = ocrypto.P384MLKEM1024UnwrapDEK(privateKey, ciphertext)
+ if err != nil {
+ return nil, fmt.Errorf("failed to decrypt with P384-MLKEM1024: %w", err)
+ }
+ default:
+ return nil, fmt.Errorf("unsupported hybrid algorithm [%s]", key.Algorithm)
+ }
+
default:
return nil, fmt.Errorf("unsupported key type for key ID [%s]", kid)
}
diff --git a/service/kas/access/provider.go b/service/kas/access/provider.go
index e9c01dbe0c..89f90b5e85 100644
--- a/service/kas/access/provider.go
+++ b/service/kas/access/provider.go
@@ -52,13 +52,15 @@ type KASConfig struct {
// Enabling is required to parse KAOs with the `ec-wrapped` type,
// and (currently) also enables responding with ECIES encrypted responses.
ECTDFEnabled bool `mapstructure:"ec_tdf_enabled" json:"ec_tdf_enabled"`
+ HybridTDFEnabled bool `mapstructure:"hybrid_tdf_enabled" json:"hybrid_tdf_enabled"`
Preview Preview `mapstructure:"preview" json:"preview"`
RegisteredKASURI string `mapstructure:"registered_kas_uri" json:"registered_kas_uri"`
}
type Preview struct {
- ECTDFEnabled bool `mapstructure:"ec_tdf_enabled" json:"ec_tdf_enabled"`
- KeyManagement bool `mapstructure:"key_management" json:"key_management"`
+ ECTDFEnabled bool `mapstructure:"ec_tdf_enabled" json:"ec_tdf_enabled"`
+ HybridTDFEnabled bool `mapstructure:"hybrid_tdf_enabled" json:"hybrid_tdf_enabled"`
+ KeyManagement bool `mapstructure:"key_management" json:"key_management"`
}
// Specifies the preferred/default key for a given algorithm type.
@@ -143,13 +145,14 @@ func (kasCfg KASConfig) String() string {
}
return fmt.Sprintf(
- "KASConfig{Keyring:%v, ECCertID:%q, RSACertID:%q, RootKey:%s, KeyCacheExpiration:%s, ECTDFEnabled:%t, Preview:%+v, RegisteredKASURI:%q}",
+ "KASConfig{Keyring:%v, ECCertID:%q, RSACertID:%q, RootKey:%s, KeyCacheExpiration:%s, ECTDFEnabled:%t, HybridTDFEnabled:%t, Preview:%+v, RegisteredKASURI:%q}",
kasCfg.Keyring,
kasCfg.ECCertID,
kasCfg.RSACertID,
rootKeySummary,
kasCfg.KeyCacheExpiration,
kasCfg.ECTDFEnabled,
+ kasCfg.HybridTDFEnabled,
kasCfg.Preview,
kasCfg.RegisteredKASURI,
)
@@ -168,6 +171,7 @@ func (kasCfg KASConfig) LogValue() slog.Value {
slog.String("root_key", rootKeyVal),
slog.Duration("key_cache_expiration", kasCfg.KeyCacheExpiration),
slog.Bool("ec_tdf_enabled", kasCfg.ECTDFEnabled),
+ slog.Bool("hybrid_tdf_enabled", kasCfg.HybridTDFEnabled),
slog.Any("preview", kasCfg.Preview),
slog.String("registered_kas_uri", kasCfg.RegisteredKASURI),
)
diff --git a/service/kas/access/publicKey.go b/service/kas/access/publicKey.go
index eb5b1fe780..d7f381c307 100644
--- a/service/kas/access/publicKey.go
+++ b/service/kas/access/publicKey.go
@@ -76,9 +76,8 @@ func (p *Provider) LegacyPublicKey(ctx context.Context, req *connect.Request[kas
p.Logger.ErrorContext(ctx, "keyDetails.ExportCertificate failed", slog.Any("error", err))
return nil, connect.NewError(connect.CodeInternal, errors.Join(ErrConfig, errors.New("configuration error")))
}
- case security.AlgorithmRSA2048:
- fallthrough
- case "":
+ case security.AlgorithmRSA2048, security.AlgorithmHPQTXWing,
+ security.AlgorithmHPQTSecp256r1MLKEM768, security.AlgorithmHPQTSecp384r1MLKEM1024, "":
// For RSA keys, return the public key in PKCS8 format
pem, err = keyDetails.ExportPublicKey(ctx, trust.KeyTypePKCS8)
if err != nil {
@@ -153,6 +152,14 @@ func (p *Provider) PublicKey(ctx context.Context, req *connect.Request[kaspb.Pub
// For EC keys, export the public key
ecPublicKeyPem, err := keyDetails.ExportPublicKey(ctx, trust.KeyTypePKCS8)
return r(ecPublicKeyPem, kid, err)
+ case security.AlgorithmHPQTXWing,
+ security.AlgorithmHPQTSecp256r1MLKEM768,
+ security.AlgorithmHPQTSecp384r1MLKEM1024:
+ switch fmt {
+ case "pkcs8", "":
+ publicKeyPEM, err := keyDetails.ExportPublicKey(ctx, trust.KeyTypePKCS8)
+ return r(publicKeyPEM, kid, err)
+ }
case security.AlgorithmRSA2048:
fallthrough
case "":
diff --git a/service/kas/access/rewrap.go b/service/kas/access/rewrap.go
index d62975a480..aba7ffa84e 100644
--- a/service/kas/access/rewrap.go
+++ b/service/kas/access/rewrap.go
@@ -746,6 +746,20 @@ func (p *Provider) verifyRewrapRequests(ctx context.Context, req *kaspb.Unsigned
failedKAORewrap(results, kao, err400("bad request"))
continue
}
+ case "hybrid-wrapped":
+ if !p.HybridTDFEnabled && !p.Preview.HybridTDFEnabled {
+ p.Logger.WarnContext(ctx, "hybrid-wrapped not enabled")
+ failedKAORewrap(results, kao, err400("bad request"))
+ continue
+ }
+
+ kid := trust.KeyIdentifier(kao.GetKeyAccessObject().GetKid())
+ dek, err = p.KeyDelegator.Decrypt(ctx, kid, kao.GetKeyAccessObject().GetWrappedKey(), nil)
+ if err != nil {
+ p.Logger.WarnContext(ctx, "failed to decrypt hybrid key", slog.Any("error", err))
+ failedKAORewrap(results, kao, err400("bad request"))
+ continue
+ }
case "wrapped":
var kidsToCheck []trust.KeyIdentifier
if kao.GetKeyAccessObject().GetKid() != "" {
diff --git a/service/kas/kas.go b/service/kas/kas.go
index bf17719c9e..d7694aa12b 100644
--- a/service/kas/kas.go
+++ b/service/kas/kas.go
@@ -191,7 +191,7 @@ func initSecurityProviderAdapter(cryptoProvider *security.StandardCrypto, kasCfg
}
}
if len(defaults) == 0 && len(legacies) == 0 {
- for _, alg := range []string{security.AlgorithmECP256R1, security.AlgorithmRSA2048} {
+ for _, alg := range []string{security.AlgorithmECP256R1, security.AlgorithmRSA2048, security.AlgorithmHPQTXWing} {
kid := cryptoProvider.FindKID(alg)
if kid != "" {
defaults = append(defaults, kid)
diff --git a/service/kas/kas.proto b/service/kas/kas.proto
index a01cd1b888..6168a6a5f8 100644
--- a/service/kas/kas.proto
+++ b/service/kas/kas.proto
@@ -55,7 +55,7 @@ message KeyAccess {
// Type of key wrapping used for the data encryption key
// Required: Always
- // Values: 'wrapped' (RSA-wrapped for ZTDF), 'ec-wrapped' (experimental ECDH-wrapped)
+ // Values: 'wrapped' (RSA-wrapped for ZTDF), 'ec-wrapped' (experimental ECDH-wrapped), 'hybrid-wrapped' (experimental X-Wing-wrapped)
string key_type = 4 [json_name = "type"];
// URL of the Key Access Server that can unwrap this key
@@ -88,7 +88,7 @@ message KeyAccess {
// Ephemeral public key for ECDH key derivation (ec-wrapped type only)
// Required: When key_type="ec-wrapped" (experimental ECDH-based ZTDF)
- // Omitted: When key_type="wrapped" (RSA-based ZTDF)
+ // Omitted: When key_type="wrapped" or key_type="hybrid-wrapped"
// Should be a PEM-encoded PKCS#8 (ASN.1) formatted public key
// Used to derive the symmetric key for unwrapping the DEK
string ephemeral_public_key = 10;
@@ -245,7 +245,7 @@ message RewrapResponse {
// KAS's ephemeral session public key in PEM format
// Required: For EC-based operations (key_type="ec-wrapped")
- // Optional: Empty for RSA-based ZTDF (key_type="wrapped")
+ // Optional: Empty for RSA-based or X-Wing-based ZTDF (key_type="wrapped" or key_type="hybrid-wrapped")
// Used by client to perform ECDH key agreement and decrypt the kas_wrapped_key values
string session_public_key = 3;
diff --git a/service/kas/key_indexer.go b/service/kas/key_indexer.go
index 021b3f418a..9c336e2bae 100644
--- a/service/kas/key_indexer.go
+++ b/service/kas/key_indexer.go
@@ -46,42 +46,6 @@ func NewPlatformKeyIndexer(sdk *sdk.SDK, kasURI string, l *logger.Logger) *KeyIn
}
}
-func convertEnumToAlg(alg policy.Algorithm) ocrypto.KeyType {
- switch alg {
- case policy.Algorithm_ALGORITHM_RSA_2048:
- return ocrypto.RSA2048Key
- case policy.Algorithm_ALGORITHM_RSA_4096:
- return ocrypto.RSA4096Key
- case policy.Algorithm_ALGORITHM_EC_P256:
- return ocrypto.EC256Key
- case policy.Algorithm_ALGORITHM_EC_P384:
- return ocrypto.EC384Key
- case policy.Algorithm_ALGORITHM_EC_P521:
- return ocrypto.EC521Key
- case policy.Algorithm_ALGORITHM_UNSPECIFIED:
- fallthrough
- default:
- return ""
- }
-}
-
-func convertAlgToEnum(alg string) (policy.Algorithm, error) {
- switch alg {
- case string(ocrypto.RSA2048Key):
- return policy.Algorithm_ALGORITHM_RSA_2048, nil
- case string(ocrypto.RSA4096Key):
- return policy.Algorithm_ALGORITHM_RSA_4096, nil
- case string(ocrypto.EC256Key):
- return policy.Algorithm_ALGORITHM_EC_P256, nil
- case string(ocrypto.EC384Key):
- return policy.Algorithm_ALGORITHM_EC_P384, nil
- case string(ocrypto.EC521Key):
- return policy.Algorithm_ALGORITHM_EC_P521, nil
- default:
- return policy.Algorithm_ALGORITHM_UNSPECIFIED, fmt.Errorf("unsupported algorithm: %s", alg)
- }
-}
-
func (p *KeyIndexer) String() string {
return fmt.Sprintf("PlatformKeyIndexer[%s]", p.kasURI)
}
@@ -91,7 +55,11 @@ func (p *KeyIndexer) LogValue() slog.Value {
}
func (p *KeyIndexer) FindKeyByAlgorithm(ctx context.Context, algorithm string, includeLegacy bool) (trust.KeyDetails, error) {
- alg, err := convertAlgToEnum(algorithm)
+ kt, err := ocrypto.ParseKeyType(algorithm)
+ if err != nil {
+ return nil, err
+ }
+ alg, err := sdk.KeyTypeToPolicyAlgorithm(kt)
if err != nil {
return nil, err
}
@@ -192,7 +160,13 @@ func (p *KeyAdapter) ID() trust.KeyIdentifier {
// Might need to convert this to a standard format
func (p *KeyAdapter) Algorithm() ocrypto.KeyType {
- return convertEnumToAlg(p.key.GetKey().GetKeyAlgorithm())
+ kt, err := sdk.PolicyAlgorithmToKeyType(p.key.GetKey().GetKeyAlgorithm())
+ if err != nil {
+ p.log.Error("unable to format key with alg",
+ slog.String("kid", p.key.GetKey().GetKeyId()),
+ slog.Any("err", err))
+ }
+ return kt
}
func (p *KeyAdapter) IsLegacy() bool {
diff --git a/service/pkg/db/marshalHelpers.go b/service/pkg/db/marshalHelpers.go
index 667ab27e21..31ce86bc0b 100644
--- a/service/pkg/db/marshalHelpers.go
+++ b/service/pkg/db/marshalHelpers.go
@@ -8,6 +8,7 @@ import (
"github.com/opentdf/platform/protocol/go/common"
"github.com/opentdf/platform/protocol/go/policy"
"github.com/opentdf/platform/protocol/go/policy/kasregistry"
+ "github.com/opentdf/platform/sdk"
"google.golang.org/protobuf/encoding/protojson"
)
@@ -146,22 +147,11 @@ func KasKeysProtoJSON(keysJSON []byte) ([]*policy.KasKey, error) {
}
func FormatAlg(alg policy.Algorithm) (string, error) {
- switch alg {
- case policy.Algorithm_ALGORITHM_RSA_2048:
- return "rsa:2048", nil
- case policy.Algorithm_ALGORITHM_RSA_4096:
- return "rsa:4096", nil
- case policy.Algorithm_ALGORITHM_EC_P256:
- return "ec:secp256r1", nil
- case policy.Algorithm_ALGORITHM_EC_P384:
- return "ec:secp384r1", nil
- case policy.Algorithm_ALGORITHM_EC_P521:
- return "ec:secp521r1", nil
- case policy.Algorithm_ALGORITHM_UNSPECIFIED:
- fallthrough
- default:
+ kt, err := sdk.PolicyAlgorithmToKeyType(alg)
+ if err != nil {
return "", fmt.Errorf("unsupported algorithm: %s", alg)
}
+ return string(kt), nil
}
func SimpleKasKeysProtoJSON(keysJSON []byte) ([]*policy.SimpleKasKey, error) {
diff --git a/service/pkg/db/marshalHelpers_test.go b/service/pkg/db/marshalHelpers_test.go
index a516a3b129..16a4234e8d 100644
--- a/service/pkg/db/marshalHelpers_test.go
+++ b/service/pkg/db/marshalHelpers_test.go
@@ -12,11 +12,14 @@ import (
// reverseAlgMap mirrors the SDK's getKasKeyAlg mapping: ocrypto.KeyType string → policy.Algorithm.
// If FormatAlg produces a string that isn't in this map, the SDK would return ALGORITHM_UNSPECIFIED.
var reverseAlgMap = map[string]policy.Algorithm{
- string(ocrypto.RSA2048Key): policy.Algorithm_ALGORITHM_RSA_2048,
- string(ocrypto.RSA4096Key): policy.Algorithm_ALGORITHM_RSA_4096,
- string(ocrypto.EC256Key): policy.Algorithm_ALGORITHM_EC_P256,
- string(ocrypto.EC384Key): policy.Algorithm_ALGORITHM_EC_P384,
- string(ocrypto.EC521Key): policy.Algorithm_ALGORITHM_EC_P521,
+ string(ocrypto.RSA2048Key): policy.Algorithm_ALGORITHM_RSA_2048,
+ string(ocrypto.RSA4096Key): policy.Algorithm_ALGORITHM_RSA_4096,
+ string(ocrypto.EC256Key): policy.Algorithm_ALGORITHM_EC_P256,
+ string(ocrypto.EC384Key): policy.Algorithm_ALGORITHM_EC_P384,
+ string(ocrypto.EC521Key): policy.Algorithm_ALGORITHM_EC_P521,
+ string(ocrypto.HybridXWingKey): policy.Algorithm_ALGORITHM_HPQT_XWING,
+ string(ocrypto.HybridSecp256r1MLKEM768Key): policy.Algorithm_ALGORITHM_HPQT_SECP256R1_MLKEM768,
+ string(ocrypto.HybridSecp384r1MLKEM1024Key): policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024,
}
func TestFormatAlg_RoundTrip(t *testing.T) {
@@ -32,6 +35,9 @@ func TestFormatAlg_RoundTrip(t *testing.T) {
{"EC-P256", policy.Algorithm_ALGORITHM_EC_P256},
{"EC-P384", policy.Algorithm_ALGORITHM_EC_P384},
{"EC-P521", policy.Algorithm_ALGORITHM_EC_P521},
+ {"HPQT-XWing", policy.Algorithm_ALGORITHM_HPQT_XWING},
+ {"HPQT-P256-MLKEM768", policy.Algorithm_ALGORITHM_HPQT_SECP256R1_MLKEM768},
+ {"HPQT-P384-MLKEM1024", policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024},
}
for _, tc := range supportedAlgs {
diff --git a/service/pkg/server/start_test.go b/service/pkg/server/start_test.go
index f13c7f2b7c..afa019a4e2 100644
--- a/service/pkg/server/start_test.go
+++ b/service/pkg/server/start_test.go
@@ -15,6 +15,7 @@ import (
"testing"
"time"
+ "github.com/opentdf/platform/lib/ocrypto"
"github.com/opentdf/platform/service/internal/auth"
"github.com/opentdf/platform/service/internal/server"
"github.com/opentdf/platform/service/logger"
@@ -223,7 +224,9 @@ func TestStartTestSuite(t *testing.T) {
}
func (s *StartTestSuite) SetupSuite() {
- // Create dummy KAS key files in testdata
+ // Generate fresh KAS key material for every run so nothing is committed
+ // alongside the source tree. all-no-config.yaml expects all of these paths
+ // to resolve, including the hybrid PQ key pairs added in PR #3276.
keyFiles := map[string]string{
"kas-private.pem": dummyRsaPrivate,
"kas-cert.pem": dummyRsaPublic, // Using public key as cert for dummy purposes
@@ -231,6 +234,23 @@ func (s *StartTestSuite) SetupSuite() {
"kas-ec-cert.pem": dummyEcCert,
}
+ hybridPairs := []struct {
+ name string
+ newPair func() (priv, pub string, err error)
+ priv string
+ pub string
+ }{
+ {"X-Wing", testXWingPair, "kas-xwing-private.pem", "kas-xwing-public.pem"},
+ {"P-256+ML-KEM-768", testP256MLKEM768Pair, "kas-p256mlkem768-private.pem", "kas-p256mlkem768-public.pem"},
+ {"P-384+ML-KEM-1024", testP384MLKEM1024Pair, "kas-p384mlkem1024-private.pem", "kas-p384mlkem1024-public.pem"},
+ }
+ for _, p := range hybridPairs {
+ priv, pub, err := p.newPair()
+ s.Require().NoErrorf(err, "Failed to generate %s key pair", p.name)
+ keyFiles[p.priv] = priv
+ keyFiles[p.pub] = pub
+ }
+
for filename, content := range keyFiles {
filePath := filepath.Join("testdata", filename)
err := os.WriteFile(filePath, []byte(content), 0o600)
@@ -245,7 +265,7 @@ func (s *StartTestSuite) TearDownSuite() {
s.Require().NoError(err, "Failed to read testdata directory")
for _, entry := range entries {
- if !entry.IsDir() {
+ if entry.IsDir() {
continue
}
if entry.Name() == ignoreFile {
@@ -256,6 +276,47 @@ func (s *StartTestSuite) TearDownSuite() {
}
}
+func testXWingPair() (string, string, error) {
+ kp, err := ocrypto.NewXWingKeyPair()
+ if err != nil {
+ return "", "", err
+ }
+ return testHybridPEMs(kp)
+}
+
+func testP256MLKEM768Pair() (string, string, error) {
+ kp, err := ocrypto.NewP256MLKEM768KeyPair()
+ if err != nil {
+ return "", "", err
+ }
+ return testHybridPEMs(kp)
+}
+
+func testP384MLKEM1024Pair() (string, string, error) {
+ kp, err := ocrypto.NewP384MLKEM1024KeyPair()
+ if err != nil {
+ return "", "", err
+ }
+ return testHybridPEMs(kp)
+}
+
+type pemKeyPair interface {
+ PrivateKeyInPemFormat() (string, error)
+ PublicKeyInPemFormat() (string, error)
+}
+
+func testHybridPEMs(kp pemKeyPair) (string, string, error) {
+ priv, err := kp.PrivateKeyInPemFormat()
+ if err != nil {
+ return "", "", err
+ }
+ pub, err := kp.PublicKeyInPemFormat()
+ if err != nil {
+ return "", "", err
+ }
+ return priv, pub, nil
+}
+
func (s *StartTestSuite) Test_Start_When_Extra_Service_Registered() {
testCases := []struct {
name string
diff --git a/service/pkg/server/testdata/all-no-config.yaml b/service/pkg/server/testdata/all-no-config.yaml
index d6db2ae334..29dcd5fe8c 100644
--- a/service/pkg/server/testdata/all-no-config.yaml
+++ b/service/pkg/server/testdata/all-no-config.yaml
@@ -16,6 +16,12 @@ services:
- kid: r1
alg: rsa:2048
legacy: true
+ - kid: x1
+ alg: hpqt:xwing
+ - kid: h1
+ alg: hpqt:secp256r1-mlkem768
+ - kid: h2
+ alg: hpqt:secp384r1-mlkem1024
entityresolution:
log_level: info
url: http://localhost:8888/auth
@@ -117,3 +123,15 @@ server:
alg: ec:secp256r1
private: ./testdata/kas-ec-private.pem
cert: ./testdata/kas-ec-cert.pem
+ - kid: x1
+ alg: hpqt:xwing
+ private: ./testdata/kas-xwing-private.pem
+ cert: ./testdata/kas-xwing-public.pem
+ - kid: h1
+ alg: hpqt:secp256r1-mlkem768
+ private: ./testdata/kas-p256mlkem768-private.pem
+ cert: ./testdata/kas-p256mlkem768-public.pem
+ - kid: h2
+ alg: hpqt:secp384r1-mlkem1024
+ private: ./testdata/kas-p384mlkem1024-private.pem
+ cert: ./testdata/kas-p384mlkem1024-public.pem
diff --git a/service/policy/db/grant_mappings.go b/service/policy/db/grant_mappings.go
index 7c3688af58..7cd8830f92 100644
--- a/service/policy/db/grant_mappings.go
+++ b/service/policy/db/grant_mappings.go
@@ -22,6 +22,12 @@ func mapAlgorithmToKasPublicKeyAlg(alg policy.Algorithm) policy.KasPublicKeyAlgE
return policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1
case policy.Algorithm_ALGORITHM_EC_P521: // ALGORITHM_EC_P521 is an alias
return policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1
+ case policy.Algorithm_ALGORITHM_HPQT_XWING:
+ return policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING
+ case policy.Algorithm_ALGORITHM_HPQT_SECP256R1_MLKEM768:
+ return policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768
+ case policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024:
+ return policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024
case policy.Algorithm_ALGORITHM_UNSPECIFIED:
return policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED
default:
diff --git a/service/policy/kasregistry/key_access_server_registry.proto b/service/policy/kasregistry/key_access_server_registry.proto
index 1c902f95ec..5d0e805f7e 100644
--- a/service/policy/kasregistry/key_access_server_registry.proto
+++ b/service/policy/kasregistry/key_access_server_registry.proto
@@ -433,7 +433,7 @@ message CreateKeyRequest {
Algorithm key_algorithm = 3 [(buf.validate.field).cel = {
id: "key_algorithm_defined"
message: "The key_algorithm must be one of the defined values."
- expression: "this in [1, 2, 3, 4, 5]" // Allow ALGORITHM_RSA_2048, ALGORITHM_RSA_4096, ALGORITHM_EC_P256, ALGORITHM_EC_P384, ALGORITHM_EC_P521
+ expression: "this in [1, 2, 3, 4, 5, 6, 7, 8]" // Allow ALGORITHM_RSA_2048, ALGORITHM_RSA_4096, ALGORITHM_EC_P256, ALGORITHM_EC_P384, ALGORITHM_EC_P521, ALGORITHM_HPQT_XWING, ALGORITHM_HPQT_SECP256R1_MLKEM768, ALGORITHM_HPQT_SECP384R1_MLKEM1024
}]; // The algorithm to be used for the key
// Required
KeyMode key_mode = 4 [(buf.validate.field).cel = {
@@ -477,7 +477,7 @@ message ListKeysRequest {
Algorithm key_algorithm = 1 [(buf.validate.field).cel = {
id: "key_algorithm_defined"
message: "The key_algorithm must be one of the defined values."
- expression: "this in [0, 1, 2, 3, 4, 5]" // Allow unspecified and object.Algorithm values for currently supported RSA bit sizes and EC curve types
+ expression: "this in [0, 1, 2, 3, 4, 5, 6, 7, 8]" // Allow unspecified and all supported algorithm values
}]; // Filter keys by algorithm
oneof kas_filter {
@@ -587,7 +587,7 @@ message RotateKeyRequest {
Algorithm algorithm = 2 [(buf.validate.field).cel = {
id: "key_algorithm_defined"
message: "The key_algorithm must be one of the defined values."
- expression: "this in [1, 2, 3, 4, 5]" // Allow ALGORITHM_RSA_2048, ALGORITHM_RSA_4096, ALGORITHM_EC_P256, ALGORITHM_EC_P384, ALGORITHM_EC_P521
+ expression: "this in [1, 2, 3, 4, 5, 6, 7, 8]" // Allow ALGORITHM_RSA_2048, ALGORITHM_RSA_4096, ALGORITHM_EC_P256, ALGORITHM_EC_P384, ALGORITHM_EC_P521, ALGORITHM_HPQT_XWING, ALGORITHM_HPQT_SECP256R1_MLKEM768, ALGORITHM_HPQT_SECP384R1_MLKEM1024
}];
// Required
KeyMode key_mode = 3 [
diff --git a/service/policy/objects.proto b/service/policy/objects.proto
index 5b6e47023e..3e6ee4d794 100644
--- a/service/policy/objects.proto
+++ b/service/policy/objects.proto
@@ -391,6 +391,9 @@ enum KasPublicKeyAlgEnum {
KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 = 5;
KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 = 6;
KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 = 7;
+ KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING = 10;
+ KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 = 11;
+ KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 = 12;
}
// Deprecated
@@ -561,6 +564,9 @@ enum Algorithm {
ALGORITHM_EC_P256 = 3;
ALGORITHM_EC_P384 = 4;
ALGORITHM_EC_P521 = 5;
+ ALGORITHM_HPQT_XWING = 6;
+ ALGORITHM_HPQT_SECP256R1_MLKEM768 = 7;
+ ALGORITHM_HPQT_SECP384R1_MLKEM1024 = 8;
}
// The status of the key
diff --git a/test/start-up-with-containers/action.yaml b/test/start-up-with-containers/action.yaml
index acc8f85e86..4abe183502 100644
--- a/test/start-up-with-containers/action.yaml
+++ b/test/start-up-with-containers/action.yaml
@@ -115,8 +115,8 @@ runs:
set -e
allowed_algorithms=(ec:secp256r1 rsa:2048)
if echo $PLATFORM_VERSION | awk -F. '{ if ($1 > 0 || ($1 == 0 && $2 > 7) || ($1 == 0 && $2 == 7 && $3 >= 1)) exit 0; else exit 1; }'; then
- # For versions 0.7.1 and later, we allow rsa:4096 ec:secp384r1 ec:secp521r1
- allowed_algorithms+=(rsa:4096 ec:secp384r1 ec:secp521r1)
+ # For versions 0.7.1 and later, we allow rsa:4096 ec:secp384r1 ec:secp521r1
+ allowed_algorithms+=(rsa:4096 ec:secp384r1 ec:secp521r1 hpqt:xwing hpqt:secp256r1-mlkem768 hpqt:secp384r1-mlkem1024)
fi
keyring='[{"kid":"ec1","alg":"ec:secp256r1"},{"kid":"r1","alg":"rsa:2048"}]'
keys='[{"kid":"e1","alg":"ec:secp256r1","private":"kas-ec-private.pem","cert":"kas-ec-cert.pem"},{"kid":"ec1","alg":"ec:secp256r1","private":"kas-ec-private.pem","cert":"kas-ec-cert.pem"},{"kid":"r1","alg":"rsa:2048","private":"kas-private.pem","cert":"kas-cert.pem"}]'
diff --git a/test/tdf-roundtrips.bats b/test/tdf-roundtrips.bats
index cf5d0d5c01..7e5ff3aedb 100755
--- a/test/tdf-roundtrips.bats
+++ b/test/tdf-roundtrips.bats
@@ -26,6 +26,69 @@
printf '%s\n' "$output" | grep "Hello EC wrappers!"
}
+@test "examples: roundtrip Z-TDF with X-Wing wrapped KAO" {
+ echo "[INFO] create a tdf3 format file"
+ run go run ./examples encrypt -o sensitive-with-xwing.txt.tdf --autoconfigure=false -A "hpqt:xwing" "Hello X-Wing wrappers!"
+ echo "[INFO] echoing output; if successful, this is just the manifest"
+ echo "$output"
+
+ echo "[INFO] Validate the manifest lists the expected type in its KAO"
+ kaotype=$(jq -r '.encryptionInformation.keyAccess[0].type' <<<"${output}")
+ echo "kao.type=$kaotype"
+ [ "$kaotype" = hybrid-wrapped ]
+
+ kid=$(jq -r '.encryptionInformation.keyAccess[0].kid' <<<"${output}")
+ echo "kao.kid=$kid"
+ [ "$kid" = x1 ]
+
+ echo "[INFO] decrypting..."
+ run go run ./examples decrypt sensitive-with-xwing.txt.tdf
+ echo "$output"
+ printf '%s\n' "$output" | grep "Hello X-Wing wrappers!"
+}
+
+@test "examples: roundtrip Z-TDF with P256+ML-KEM-768 wrapped KAO" {
+ echo "[INFO] create a tdf3 format file"
+ run go run ./examples encrypt -o sensitive-with-p256mlkem768.txt.tdf --autoconfigure=false -A "hpqt:secp256r1-mlkem768" "Hello P256+ML-KEM-768 wrappers!"
+ echo "[INFO] echoing output; if successful, this is just the manifest"
+ echo "$output"
+
+ echo "[INFO] Validate the manifest lists the expected type in its KAO"
+ kaotype=$(jq -r '.encryptionInformation.keyAccess[0].type' <<<"${output}")
+ echo "$kaotype"
+ [ "$kaotype" = hybrid-wrapped ]
+
+ kid=$(jq -r '.encryptionInformation.keyAccess[0].kid' <<<"${output}")
+ echo "kao.kid=$kid"
+ [ "$kid" = h1 ]
+
+ echo "[INFO] decrypting..."
+ run go run ./examples decrypt sensitive-with-p256mlkem768.txt.tdf
+ echo "$output"
+ printf '%s\n' "$output" | grep "Hello P256+ML-KEM-768 wrappers!"
+}
+
+@test "examples: roundtrip Z-TDF with P384+ML-KEM-1024 wrapped KAO" {
+ echo "[INFO] create a tdf3 format file"
+ run go run ./examples encrypt -o sensitive-with-p384mlkem1024.txt.tdf --autoconfigure=false -A "hpqt:secp384r1-mlkem1024" "Hello P384+ML-KEM-1024 wrappers!"
+ echo "[INFO] echoing output; if successful, this is just the manifest"
+ echo "$output"
+
+ echo "[INFO] Validate the manifest lists the expected type in its KAO"
+ kaotype=$(jq -r '.encryptionInformation.keyAccess[0].type' <<<"${output}")
+ echo "$kaotype"
+ [ "$kaotype" = hybrid-wrapped ]
+
+ kid=$(jq -r '.encryptionInformation.keyAccess[0].kid' <<<"${output}")
+ echo "kao.kid=$kid"
+ [ "$kid" = h2 ]
+
+ echo "[INFO] decrypting..."
+ run go run ./examples decrypt sensitive-with-p384mlkem1024.txt.tdf
+ echo "$output"
+ printf '%s\n' "$output" | grep "Hello P384+ML-KEM-1024 wrappers!"
+}
+
@test "examples: legacy key support Z-TDF" {
echo "[INFO] validating default key is r1"
echo "[INFO] default key result: $(grpcurl "localhost:8080" "kas.AccessService/PublicKey")"
@@ -77,7 +140,7 @@
echo "[INFO] validating default key is r1"
echo "[INFO] default key result: $(grpcurl "localhost:8080" "kas.AccessService/PublicKey")"
- [ $(grpcurl "localhost:8080" "kas.AccessService/PublicKey" | jq -e -r .kid) = r1 ]
+ [ "$(grpcurl "localhost:8080" "kas.AccessService/PublicKey" | jq -e -r .kid)" = r1 ]
echo "[INFO] validating keys are correct by alg"
[ "$(grpcurl -d '{"algorithm":"ec:secp256r1"}' "localhost:8080" "kas.AccessService/PublicKey" | jq -e -r .kid)" = e1 ]
@@ -98,10 +161,21 @@ wait_for_green() {
done
}
+write_opentdf_config() {
+ local tmp
+
+ tmp=$(mktemp "./opentdf.yaml.tmp.XXXXXX")
+ if ! cat >"$tmp"; then
+ rm -f "$tmp"
+ return 1
+ fi
+ mv -f "$tmp" opentdf.yaml
+}
+
downgrade_config() {
ec_current_key=$1
rsa_current_key=$2
- cat >opentdf.yaml <opentdf.yaml <