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/docs/grpc/index.html b/docs/grpc/index.html index 5d2dd2888a..b552502d7b 100644 --- a/docs/grpc/index.html +++ b/docs/grpc/index.html @@ -4027,6 +4027,24 @@

Algorithm

+ + ALGORITHM_HPQT_XWING + 6 +

+ + + + ALGORITHM_HPQT_SECP256R1_MLKEM768 + 7 +

+ + + + ALGORITHM_HPQT_SECP384R1_MLKEM1024 + 8 +

+ + @@ -4138,6 +4156,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 +

+ + @@ -6276,7 +6312,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)

@@ -6333,7 +6369,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

@@ -6661,7 +6697,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 1938f339fe..16dafb6cfd 100644 --- a/docs/openapi/authorization/authorization.openapi.yaml +++ b/docs/openapi/authorization/authorization.openapi.yaml @@ -341,6 +341,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 @@ -352,6 +355,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/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 b9e18aa899..26c48e22ae 100644 --- a/docs/openapi/kas/kas.openapi.yaml +++ b/docs/openapi/kas/kas.openapi.yaml @@ -214,7 +214,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 @@ -261,7 +261,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 @@ -451,7 +451,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 ec633192ae..ca0f755485 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 4ace6604af..0c24167f65 100644 --- a/docs/openapi/policy/attributes/attributes.openapi.yaml +++ b/docs/openapi/policy/attributes/attributes.openapi.yaml @@ -724,6 +724,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 @@ -750,6 +753,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/kasregistry/key_access_server_registry.openapi.yaml b/docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml index 9cdc346918..da2be2ceaa 100644 --- a/docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml +++ b/docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml @@ -534,6 +534,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 @@ -545,6 +548,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 @@ -1147,7 +1153,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' @@ -1670,7 +1676,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' @@ -1932,7 +1938,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 4a5b7e35cb..6c1e546db0 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 917017b1a0..b3f3c734ec 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 diff --git a/docs/openapi/policy/obligations/obligations.openapi.yaml b/docs/openapi/policy/obligations/obligations.openapi.yaml index dbe62f43ca..37c0ab57ba 100644 --- a/docs/openapi/policy/obligations/obligations.openapi.yaml +++ b/docs/openapi/policy/obligations/obligations.openapi.yaml @@ -518,6 +518,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 @@ -544,6 +547,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/registeredresources/registered_resources.openapi.yaml b/docs/openapi/policy/registeredresources/registered_resources.openapi.yaml index 97b2d07814..2bfd46ffc2 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.SourceType: type: string title: SourceType diff --git a/docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml b/docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml index c768b805c0..880043c4f1 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 ec2033a9c1..d8dda22d01 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.SourceType: type: string title: SourceType diff --git a/docs/openapi/policy/unsafe/unsafe.openapi.yaml b/docs/openapi/policy/unsafe/unsafe.openapi.yaml index f52e95b427..6c8859d581 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/encrypt.go b/examples/cmd/encrypt.go index 4b8adc9eb4..e14f8ba033 100644 --- a/examples/cmd/encrypt.go +++ b/examples/cmd/encrypt.go @@ -93,13 +93,24 @@ func encrypt(cmd *cobra.Command, args []string) error { } func keyTypeForKeyType(alg string) (ocrypto.KeyType, error) { - switch alg { - case string(ocrypto.RSA2048Key): + switch ocrypto.KeyType(alg) { + case ocrypto.RSA2048Key: return ocrypto.RSA2048Key, nil - case string(ocrypto.EC256Key): + case ocrypto.RSA4096Key: + return ocrypto.RSA4096Key, nil + case ocrypto.EC256Key: return ocrypto.EC256Key, nil + case ocrypto.EC384Key: + return ocrypto.EC384Key, nil + case ocrypto.EC521Key: + return ocrypto.EC521Key, nil + case ocrypto.HybridXWingKey: + return ocrypto.HybridXWingKey, nil + case ocrypto.HybridSecp256r1MLKEM768Key: + return ocrypto.HybridSecp256r1MLKEM768Key, nil + case ocrypto.HybridSecp384r1MLKEM1024Key: + return ocrypto.HybridSecp384r1MLKEM1024Key, 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..ac1b501374 --- /dev/null +++ b/lib/ocrypto/BENCHMARK_REPORT.md @@ -0,0 +1,162 @@ +# Benchmark Report: Hybrid Post-Quantum Key Wrapping Performance + +**Platform:** Apple M4, darwin/arm64, Go 1.25.8 +**Date:** 2026-04-15 +**Methodology:** `go test -bench=. -benchmem -count=3` (median of 3 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 | 48.2 ms | 671 KB | 6,101 | ~6,500x slower | +| EC P-256 | 7.4 us | 984 B | 16 | baseline | +| X-Wing | 44.1 us | 9.8 KB | 9 | ~6x slower | +| P256+ML-KEM-768 | 35.3 us | 16.6 KB | 16 | ~5x slower | +| P384+ML-KEM-1024 | 114.8 us | 23.9 KB | 19 | ~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 ~7.4us. + +### 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.6 us | 256 B | 4.1 KB | 33 | 0.5x (faster) | +| EC P-256 | 55.0 us | 60 B | 12.0 KB | 158 | baseline | +| X-Wing | 77.1 us | 1,190 B | 16.4 KB | 42 | ~1.4x slower | +| P256+ML-KEM-768 | 67.6 us | 1,223 B | 18.7 KB | 58 | ~1.2x slower | +| P384+ML-KEM-1024 | 356.3 us | 1,735 B | 27.0 KB | 67 | ~6.5x slower | + +**Takeaway:** P256+ML-KEM-768 wrapping (~68us) is only ~1.2x 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. P384+ML-KEM-1024 is noticeably slower (~356us) due to the P-384 ECDH cost. + +### 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 | 705.0 us | 560 B | 8 | ~25x slower | +| EC P-256 | 28.0 us | 4.1 KB | 40 | baseline | +| X-Wing | 90.0 us | 12.4 KB | 37 | ~3.2x slower | +| P256+ML-KEM-768 | 78.0 us | 22.0 KB | 50 | ~2.8x slower | +| P384+ML-KEM-1024 | 365.2 us | 30.7 KB | 59 | ~13x slower | + +**Takeaway:** RSA unwrap is the slowest operation in the entire suite (~705us) due to private key exponentiation. P256+ML-KEM-768 unwraps in ~78us — fast enough for real-time use. 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 | 731 us | No | +| EC P-256 | 83 us | No | +| X-Wing | 167 us | Yes | +| P256+ML-KEM-768 | 146 us | Yes | +| P384+ML-KEM-1024 | 722 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.9 us | 93.3% | +| HKDF key derivation | 0.53 us | 0.7% | +| AES-GCM encrypt (32B DEK) | 0.39 us | 0.5% | +| ASN.1 marshal | 0.57 us | 0.7% | +| PEM parsing + overhead | ~3.7 us | 4.8% | + +### P256+ML-KEM-768 Sub-Operations + +| Operation | Time | % of Wrap | +|-----------|-----:|----------:| +| Encapsulate (ECDH P-256 + ML-KEM-768) | 63.6 us | 94.1% | +| HKDF key derivation | 0.53 us | 0.8% | +| AES-GCM encrypt (32B DEK) | 0.40 us | 0.6% | +| ASN.1 marshal | 0.55 us | 0.8% | +| PEM parsing + overhead | ~2.5 us | 3.7% | + +### P384+ML-KEM-1024 Sub-Operations + +| Operation | Time | % of Wrap | +|-----------|-----:|----------:| +| Encapsulate (ECDH P-384 + ML-KEM-1024) | 344.8 us | 96.8% | +| HKDF key derivation | 0.53 us | 0.1% | +| AES-GCM encrypt (32B DEK) | 0.40 us | 0.1% | +| ASN.1 marshal | 0.61 us | 0.2% | +| PEM parsing + overhead | ~10.0 us | 2.8% | + +**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 | +| 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 | X-Wing | P256+ML-KEM-768 | P384+ML-KEM-1024 | +|---------|----------|----------|--------|-----------------|-------------------| +| Quantum resistance | None | None | Yes (hybrid) | Yes (hybrid) | Yes (hybrid) | +| Key generation | 48 ms (slow) | 7.4 us (fastest) | 44 us | 35 us | 115 us | +| Wrap latency | 26 us | 55 us | 77 us | 68 us | 356 us | +| Unwrap latency | 705 us (slow) | 28 us | 90 us | 78 us | 365 us | +| Round-trip | 731 us | 83 us | 167 us | 146 us | 722 us | +| Wrapped key size | 256 B | 60 B | 1,190 B | 1,223 B | 1,735 B | +| Standards basis | PKCS#1 | 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 (146us), and moderate size overhead (1.2 KB wrapped keys). Only 1.2x slower than EC P-256 for wrapping. +- **P384+ML-KEM-1024** provides a higher classical security level (192-bit vs 128-bit) at the cost of ~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. +- **RSA-2048** has the worst unwrap performance (705us) 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/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..f3286f2da2 --- /dev/null +++ b/lib/ocrypto/benchmark_test.go @@ -0,0 +1,685 @@ +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("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) + } + + // 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") + }) + + // 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) + } + + // 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) + } + }) + + // 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 := defaultXWingSalt() + + // 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)), + }) + + // 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/go.mod b/lib/ocrypto/go.mod index 8328aa0e4c..1fafd3a069 100644 --- a/lib/ocrypto/go.mod +++ b/lib/ocrypto/go.mod @@ -5,6 +5,7 @@ go 1.25.0 toolchain go1.25.8 require ( + github.com/cloudflare/circl v1.6.3 github.com/stretchr/testify v1.10.0 golang.org/x/crypto v0.45.0 ) @@ -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 63574bef4b..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= @@ -18,6 +20,8 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf 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_nist.go b/lib/ocrypto/hybrid_nist.go new file mode 100644 index 0000000000..c366f2ed17 --- /dev/null +++ b/lib/ocrypto/hybrid_nist.go @@ -0,0 +1,534 @@ +package ocrypto + +import ( + "crypto/ecdh" + "crypto/rand" + "crypto/sha256" + "encoding/asn1" + "fmt" + "io" + + "github.com/cloudflare/circl/kem/mlkem/mlkem1024" + "github.com/cloudflare/circl/kem/mlkem/mlkem768" + "golang.org/x/crypto/hkdf" +) + +const ( + HybridSecp256r1MLKEM768Key KeyType = "hpqt:secp256r1-mlkem768" + HybridSecp384r1MLKEM1024Key KeyType = "hpqt:secp384r1-mlkem1024" +) + +// Sizes for P-256 + ML-KEM-768 hybrid. +const ( + P256MLKEM768ECPublicKeySize = 65 // uncompressed P-256 point + P256MLKEM768ECPrivateKeySize = 32 // P-256 scalar + P256MLKEM768MLKEMPubKeySize = 1184 // mlkem768.PublicKeySize + P256MLKEM768MLKEMPrivKeySize = 2400 // mlkem768.PrivateKeySize + P256MLKEM768MLKEMCtSize = 1088 // mlkem768.CiphertextSize + + P256MLKEM768PublicKeySize = P256MLKEM768ECPublicKeySize + P256MLKEM768MLKEMPubKeySize // 1249 + P256MLKEM768PrivateKeySize = P256MLKEM768ECPrivateKeySize + P256MLKEM768MLKEMPrivKeySize // 2432 + 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.PublicKeySize + P384MLKEM1024MLKEMPrivKeySize = 3168 // mlkem1024.PrivateKeySize + P384MLKEM1024MLKEMCtSize = 1568 // mlkem1024.CiphertextSize + + P384MLKEM1024PublicKeySize = P384MLKEM1024ECPublicKeySize + P384MLKEM1024MLKEMPubKeySize // 1665 + P384MLKEM1024PrivateKeySize = P384MLKEM1024ECPrivateKeySize + P384MLKEM1024MLKEMPrivKeySize // 3216 + 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 + mlkemEncapsulate func(pubKey []byte) (sharedSecret, ciphertext []byte, err error) + mlkemDecapsulate func(privKey, ciphertext []byte) (sharedSecret []byte, err error) +} + +var p256mlkem768Params = hybridNISTParams{ + curve: ecdh.P256(), + ecPubSize: P256MLKEM768ECPublicKeySize, + ecPrivSize: P256MLKEM768ECPrivateKeySize, + mlkemPubSize: P256MLKEM768MLKEMPubKeySize, + mlkemPrivSize: P256MLKEM768MLKEMPrivKeySize, + mlkemCtSize: P256MLKEM768MLKEMCtSize, + pubPEMBlock: PEMBlockP256MLKEM768PublicKey, + privPEMBlock: PEMBlockP256MLKEM768PrivateKey, + keyType: HybridSecp256r1MLKEM768Key, + mlkemEncapsulate: func(pubKey []byte) ([]byte, []byte, error) { + var pk mlkem768.PublicKey + if err := pk.Unpack(pubKey); err != nil { + return nil, nil, fmt.Errorf("mlkem768 public key unpack: %w", err) + } + ct := make([]byte, mlkem768.CiphertextSize) + ss := make([]byte, mlkem768.SharedKeySize) + pk.EncapsulateTo(ct, ss, nil) + return ss, ct, nil + }, + mlkemDecapsulate: func(privKey, ciphertext []byte) ([]byte, error) { + var sk mlkem768.PrivateKey + if err := sk.Unpack(privKey); err != nil { + return nil, fmt.Errorf("mlkem768 private key unpack: %w", err) + } + ss := make([]byte, mlkem768.SharedKeySize) + sk.DecapsulateTo(ss, ciphertext) + return ss, nil + }, +} + +var p384mlkem1024Params = hybridNISTParams{ + curve: ecdh.P384(), + ecPubSize: P384MLKEM1024ECPublicKeySize, + ecPrivSize: P384MLKEM1024ECPrivateKeySize, + mlkemPubSize: P384MLKEM1024MLKEMPubKeySize, + mlkemPrivSize: P384MLKEM1024MLKEMPrivKeySize, + mlkemCtSize: P384MLKEM1024MLKEMCtSize, + pubPEMBlock: PEMBlockP384MLKEM1024PublicKey, + privPEMBlock: PEMBlockP384MLKEM1024PrivateKey, + keyType: HybridSecp384r1MLKEM1024Key, + mlkemEncapsulate: func(pubKey []byte) ([]byte, []byte, error) { + var pk mlkem1024.PublicKey + if err := pk.Unpack(pubKey); err != nil { + return nil, nil, fmt.Errorf("mlkem1024 public key unpack: %w", err) + } + ct := make([]byte, mlkem1024.CiphertextSize) + ss := make([]byte, mlkem1024.SharedKeySize) + pk.EncapsulateTo(ct, ss, nil) + return ss, ct, nil + }, + mlkemDecapsulate: func(privKey, ciphertext []byte) ([]byte, error) { + var sk mlkem1024.PrivateKey + if err := sk.Unpack(privKey); err != nil { + return nil, fmt.Errorf("mlkem1024 private key unpack: %w", err) + } + ss := make([]byte, mlkem1024.SharedKeySize) + sk.DecapsulateTo(ss, ciphertext) + return ss, nil + }, +} + +// 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) { + pk, sk, err := mlkem768.GenerateKeyPair(rand.Reader) + if err != nil { + return nil, nil, err + } + pub := make([]byte, mlkem768.PublicKeySize) + priv := make([]byte, mlkem768.PrivateKeySize) + pk.Pack(pub) + sk.Pack(priv) + return pub, priv, nil + }) +} + +func NewP384MLKEM1024KeyPair() (HybridNISTKeyPair, error) { + return newHybridNISTKeyPair(&p384mlkem1024Params, func() ([]byte, []byte, error) { + pk, sk, err := mlkem1024.GenerateKeyPair(rand.Reader) + if err != nil { + return nil, nil, err + } + pub := make([]byte, mlkem1024.PublicKeySize) + priv := make([]byte, mlkem1024.PrivateKeySize) + pk.Pack(pub) + sk.Pack(priv) + return pub, priv, 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, defaultXWingSalt(), nil) +} + +func NewSaltedP256MLKEM768Decryptor(privateKey, salt, info []byte) (*HybridNISTDecryptor, error) { + return newHybridNISTDecryptor(&p256mlkem768Params, privateKey, salt, info) +} + +func NewP384MLKEM1024Decryptor(privateKey []byte) (*HybridNISTDecryptor, error) { + return NewSaltedP384MLKEM1024Decryptor(privateKey, defaultXWingSalt(), 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, defaultXWingSalt(), nil) +} + +func P256MLKEM768UnwrapDEK(privateKeyRaw, wrappedDER []byte) ([]byte, error) { + return hybridNISTUnwrapDEK(&p256mlkem768Params, privateKeyRaw, wrappedDER, defaultXWingSalt(), nil) +} + +func P384MLKEM1024WrapDEK(publicKeyRaw, dek []byte) ([]byte, error) { + return hybridNISTWrapDEK(&p384mlkem1024Params, publicKeyRaw, dek, defaultXWingSalt(), nil) +} + +func P384MLKEM1024UnwrapDEK(privateKeyRaw, wrappedDER []byte) ([]byte, error) { + return hybridNISTUnwrapDEK(&p384mlkem1024Params, privateKeyRaw, wrappedDER, defaultXWingSalt(), 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 + mlkemSecret, mlkemCt, err := p.mlkemEncapsulate(mlkemPubBytes) + if err != nil { + return nil, nil, fmt.Errorf("ML-KEM encapsulate failed: %w", err) + } + + // 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 + mlkemSecret, err := p.mlkemDecapsulate(mlkemPrivBytes, mlkemCtBytes) + 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 = defaultXWingSalt() + } + + 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..557fb7639e --- /dev/null +++ b/lib/ocrypto/hybrid_nist_test.go @@ -0,0 +1,235 @@ +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) +} + +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) +} + +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.NotEmpty(t, hybridCt) +} + +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.NotEmpty(t, hybridCt) +} + +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..93bd8c5f29 --- /dev/null +++ b/lib/ocrypto/xwing.go @@ -0,0 +1,289 @@ +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, defaultXWingSalt(), 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, defaultXWingSalt(), nil) +} + +func XWingUnwrapDEK(privateKeyRaw, wrappedDER []byte) ([]byte, error) { + return xwingUnwrapDEK(privateKeyRaw, wrappedDER, defaultXWingSalt(), 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 = defaultXWingSalt() + } + + 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 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 +} + +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 +} + +func defaultXWingSalt() []byte { + digest := sha256.New() + digest.Write([]byte("TDF")) + return digest.Sum(nil) +} + +func cloneOrNil(data []byte) []byte { + if len(data) == 0 { + return nil + } + return append([]byte(nil), data...) +} 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..d44cf0772f 100644 --- a/opentdf-dev.yaml +++ b/opentdf-dev.yaml @@ -21,7 +21,8 @@ services: kas: registered_kas_uri: http://localhost:8080 # Should match what you have registered for *this* KAS in the policy db. preview: - ec_tdf_enabled: false + ec_tdf_enabled: true + hybrid_tdf_enabled: true key_management: false root_key: a8c4824daafcfa38ed0d13002e92b08720e6c4fcee67d52e954c1a6e045907d1 # For local development testing only keyring: @@ -35,6 +36,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 @@ -183,4 +190,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: 8080 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/protocol/go/kas/kas.pb.go b/protocol/go/kas/kas.pb.go index 6d6fb574e3..a6ec21ddb1 100644 --- a/protocol/go/kas/kas.pb.go +++ b/protocol/go/kas/kas.pb.go @@ -7,14 +7,15 @@ package kas import ( + reflect "reflect" + sync "sync" + _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" _ "google.golang.org/genproto/googleapis/api/annotations" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" structpb "google.golang.org/protobuf/types/known/structpb" wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" - reflect "reflect" - sync "sync" ) const ( @@ -244,7 +245,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 @@ -271,7 +272,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"` @@ -856,7 +857,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 @@ -1344,28 +1345,30 @@ func file_kas_kas_proto_rawDescGZIP() []byte { return file_kas_kas_proto_rawDescData } -var file_kas_kas_proto_msgTypes = make([]protoimpl.MessageInfo, 17) -var file_kas_kas_proto_goTypes = []interface{}{ - (*InfoRequest)(nil), // 0: kas.InfoRequest - (*InfoResponse)(nil), // 1: kas.InfoResponse - (*LegacyPublicKeyRequest)(nil), // 2: kas.LegacyPublicKeyRequest - (*PolicyBinding)(nil), // 3: kas.PolicyBinding - (*KeyAccess)(nil), // 4: kas.KeyAccess - (*UnsignedRewrapRequest)(nil), // 5: kas.UnsignedRewrapRequest - (*PublicKeyRequest)(nil), // 6: kas.PublicKeyRequest - (*PublicKeyResponse)(nil), // 7: kas.PublicKeyResponse - (*RewrapRequest)(nil), // 8: kas.RewrapRequest - (*KeyAccessRewrapResult)(nil), // 9: kas.KeyAccessRewrapResult - (*PolicyRewrapResult)(nil), // 10: kas.PolicyRewrapResult - (*RewrapResponse)(nil), // 11: kas.RewrapResponse - (*UnsignedRewrapRequest_WithPolicy)(nil), // 12: kas.UnsignedRewrapRequest.WithPolicy - (*UnsignedRewrapRequest_WithKeyAccessObject)(nil), // 13: kas.UnsignedRewrapRequest.WithKeyAccessObject - (*UnsignedRewrapRequest_WithPolicyRequest)(nil), // 14: kas.UnsignedRewrapRequest.WithPolicyRequest - nil, // 15: kas.KeyAccessRewrapResult.MetadataEntry - nil, // 16: kas.RewrapResponse.MetadataEntry - (*structpb.Value)(nil), // 17: google.protobuf.Value - (*wrapperspb.StringValue)(nil), // 18: google.protobuf.StringValue -} +var ( + file_kas_kas_proto_msgTypes = make([]protoimpl.MessageInfo, 17) + file_kas_kas_proto_goTypes = []interface{}{ + (*InfoRequest)(nil), // 0: kas.InfoRequest + (*InfoResponse)(nil), // 1: kas.InfoResponse + (*LegacyPublicKeyRequest)(nil), // 2: kas.LegacyPublicKeyRequest + (*PolicyBinding)(nil), // 3: kas.PolicyBinding + (*KeyAccess)(nil), // 4: kas.KeyAccess + (*UnsignedRewrapRequest)(nil), // 5: kas.UnsignedRewrapRequest + (*PublicKeyRequest)(nil), // 6: kas.PublicKeyRequest + (*PublicKeyResponse)(nil), // 7: kas.PublicKeyResponse + (*RewrapRequest)(nil), // 8: kas.RewrapRequest + (*KeyAccessRewrapResult)(nil), // 9: kas.KeyAccessRewrapResult + (*PolicyRewrapResult)(nil), // 10: kas.PolicyRewrapResult + (*RewrapResponse)(nil), // 11: kas.RewrapResponse + (*UnsignedRewrapRequest_WithPolicy)(nil), // 12: kas.UnsignedRewrapRequest.WithPolicy + (*UnsignedRewrapRequest_WithKeyAccessObject)(nil), // 13: kas.UnsignedRewrapRequest.WithKeyAccessObject + (*UnsignedRewrapRequest_WithPolicyRequest)(nil), // 14: kas.UnsignedRewrapRequest.WithPolicyRequest + nil, // 15: kas.KeyAccessRewrapResult.MetadataEntry + nil, // 16: kas.RewrapResponse.MetadataEntry + (*structpb.Value)(nil), // 17: google.protobuf.Value + (*wrapperspb.StringValue)(nil), // 18: google.protobuf.StringValue + } +) var file_kas_kas_proto_depIdxs = []int32{ 3, // 0: kas.KeyAccess.policy_binding:type_name -> kas.PolicyBinding 14, // 1: kas.UnsignedRewrapRequest.requests:type_name -> kas.UnsignedRewrapRequest.WithPolicyRequest 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 8e5c7b58b5..fed7623120 100644 --- a/protocol/go/policy/kasregistry/key_access_server_registry.pb.go +++ b/protocol/go/policy/kasregistry/key_access_server_registry.pb.go @@ -7,14 +7,15 @@ package kasregistry import ( + reflect "reflect" + sync "sync" + _ "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate" common "github.com/opentdf/platform/protocol/go/common" policy "github.com/opentdf/platform/protocol/go/policy" _ "google.golang.org/genproto/googleapis/api/annotations" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" ) const ( @@ -4012,248 +4013,52 @@ var file_policy_kasregistry_key_access_server_registry_proto_rawDesc = []byte{ 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, 0x3a, 0x02, 0x18, 0x01, 0x22, 0xcc, 0x0c, 0x0a, 0x10, 0x43, + 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x02, 0x18, 0x01, 0x22, 0xcf, 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, 0x67, 0x6f, 0x72, 0x69, 0x74, + 0x12, 0xa7, 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, 0x67, 0x6f, 0x72, 0x69, 0x74, + 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, 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, + 0x1a, 0x1a, 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, 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, - 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, 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, 0x96, - 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, 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, 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, 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, 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, @@ -4264,277 +4069,474 @@ var file_policy_kasregistry_key_access_server_registry_proto_rawDesc = []byte{ 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, + 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, 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, + 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, 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, + 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, - 0xb5, 0x03, 0x0a, 0x26, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, + 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, 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, 0x5f, 0x6b, 0x65, 0x79, - 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x20, 0x7c, + 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, 0x99, 0x03, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0xaa, 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, 0x72, 0xba, 0x48, 0x6f, 0xba, 0x01, 0x6c, 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, 0x1d, 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, 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, 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, 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, 0xe8, 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, 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, 0xd2, 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, 0xa0, 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, 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, 0x31, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x35, + 0x2c, 0x20, 0x36, 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, 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, 0x33, 0x29, 0x20, 0x26, 0x26, + 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, 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, + 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, 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, 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, + 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, 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, 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, + 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, 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, 0x32, 0xb5, 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, 0x99, 0x01, - 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, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x15, 0x12, 0x13, 0x2f, 0x6b, 0x65, 0x79, 0x2d, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x2d, 0x73, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 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, + 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, 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, + 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, 0x32, 0xb5, 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, 0x99, 0x01, 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, 0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x6b, 0x65, 0x79, + 0x2d, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 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, - 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, + 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, 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, 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, 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, + 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, 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, 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, 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, + 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, 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, + 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, 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, 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, + 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, 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 ( @@ -4549,77 +4551,79 @@ func file_policy_kasregistry_key_access_server_registry_proto_rawDescGZIP() []by return file_policy_kasregistry_key_access_server_registry_proto_rawDescData } -var file_policy_kasregistry_key_access_server_registry_proto_msgTypes = make([]protoimpl.MessageInfo, 53) -var file_policy_kasregistry_key_access_server_registry_proto_goTypes = []interface{}{ - (*GetKeyAccessServerRequest)(nil), // 0: policy.kasregistry.GetKeyAccessServerRequest - (*GetKeyAccessServerResponse)(nil), // 1: policy.kasregistry.GetKeyAccessServerResponse - (*ListKeyAccessServersRequest)(nil), // 2: policy.kasregistry.ListKeyAccessServersRequest - (*ListKeyAccessServersResponse)(nil), // 3: policy.kasregistry.ListKeyAccessServersResponse - (*CreateKeyAccessServerRequest)(nil), // 4: policy.kasregistry.CreateKeyAccessServerRequest - (*CreateKeyAccessServerResponse)(nil), // 5: policy.kasregistry.CreateKeyAccessServerResponse - (*UpdateKeyAccessServerRequest)(nil), // 6: policy.kasregistry.UpdateKeyAccessServerRequest - (*UpdateKeyAccessServerResponse)(nil), // 7: policy.kasregistry.UpdateKeyAccessServerResponse - (*DeleteKeyAccessServerRequest)(nil), // 8: policy.kasregistry.DeleteKeyAccessServerRequest - (*DeleteKeyAccessServerResponse)(nil), // 9: policy.kasregistry.DeleteKeyAccessServerResponse - (*GrantedPolicyObject)(nil), // 10: policy.kasregistry.GrantedPolicyObject - (*KeyAccessServerGrants)(nil), // 11: policy.kasregistry.KeyAccessServerGrants - (*CreatePublicKeyRequest)(nil), // 12: policy.kasregistry.CreatePublicKeyRequest - (*CreatePublicKeyResponse)(nil), // 13: policy.kasregistry.CreatePublicKeyResponse - (*GetPublicKeyRequest)(nil), // 14: policy.kasregistry.GetPublicKeyRequest - (*GetPublicKeyResponse)(nil), // 15: policy.kasregistry.GetPublicKeyResponse - (*ListPublicKeysRequest)(nil), // 16: policy.kasregistry.ListPublicKeysRequest - (*ListPublicKeysResponse)(nil), // 17: policy.kasregistry.ListPublicKeysResponse - (*ListPublicKeyMappingRequest)(nil), // 18: policy.kasregistry.ListPublicKeyMappingRequest - (*ListPublicKeyMappingResponse)(nil), // 19: policy.kasregistry.ListPublicKeyMappingResponse - (*UpdatePublicKeyRequest)(nil), // 20: policy.kasregistry.UpdatePublicKeyRequest - (*UpdatePublicKeyResponse)(nil), // 21: policy.kasregistry.UpdatePublicKeyResponse - (*DeactivatePublicKeyRequest)(nil), // 22: policy.kasregistry.DeactivatePublicKeyRequest - (*DeactivatePublicKeyResponse)(nil), // 23: policy.kasregistry.DeactivatePublicKeyResponse - (*ActivatePublicKeyRequest)(nil), // 24: policy.kasregistry.ActivatePublicKeyRequest - (*ActivatePublicKeyResponse)(nil), // 25: policy.kasregistry.ActivatePublicKeyResponse - (*ListKeyAccessServerGrantsRequest)(nil), // 26: policy.kasregistry.ListKeyAccessServerGrantsRequest - (*ListKeyAccessServerGrantsResponse)(nil), // 27: policy.kasregistry.ListKeyAccessServerGrantsResponse - (*CreateKeyRequest)(nil), // 28: policy.kasregistry.CreateKeyRequest - (*CreateKeyResponse)(nil), // 29: policy.kasregistry.CreateKeyResponse - (*GetKeyRequest)(nil), // 30: policy.kasregistry.GetKeyRequest - (*GetKeyResponse)(nil), // 31: policy.kasregistry.GetKeyResponse - (*ListKeysRequest)(nil), // 32: policy.kasregistry.ListKeysRequest - (*ListKeysResponse)(nil), // 33: policy.kasregistry.ListKeysResponse - (*UpdateKeyRequest)(nil), // 34: policy.kasregistry.UpdateKeyRequest - (*UpdateKeyResponse)(nil), // 35: policy.kasregistry.UpdateKeyResponse - (*KasKeyIdentifier)(nil), // 36: policy.kasregistry.KasKeyIdentifier - (*RotateKeyRequest)(nil), // 37: policy.kasregistry.RotateKeyRequest - (*ChangeMappings)(nil), // 38: policy.kasregistry.ChangeMappings - (*RotatedResources)(nil), // 39: policy.kasregistry.RotatedResources - (*RotateKeyResponse)(nil), // 40: policy.kasregistry.RotateKeyResponse - (*SetBaseKeyRequest)(nil), // 41: policy.kasregistry.SetBaseKeyRequest - (*GetBaseKeyRequest)(nil), // 42: policy.kasregistry.GetBaseKeyRequest - (*GetBaseKeyResponse)(nil), // 43: policy.kasregistry.GetBaseKeyResponse - (*SetBaseKeyResponse)(nil), // 44: policy.kasregistry.SetBaseKeyResponse - (*MappedPolicyObject)(nil), // 45: policy.kasregistry.MappedPolicyObject - (*KeyMapping)(nil), // 46: policy.kasregistry.KeyMapping - (*ListKeyMappingsRequest)(nil), // 47: policy.kasregistry.ListKeyMappingsRequest - (*ListKeyMappingsResponse)(nil), // 48: policy.kasregistry.ListKeyMappingsResponse - (*ListPublicKeyMappingResponse_PublicKeyMapping)(nil), // 49: policy.kasregistry.ListPublicKeyMappingResponse.PublicKeyMapping - (*ListPublicKeyMappingResponse_PublicKey)(nil), // 50: policy.kasregistry.ListPublicKeyMappingResponse.PublicKey - (*ListPublicKeyMappingResponse_Association)(nil), // 51: policy.kasregistry.ListPublicKeyMappingResponse.Association - (*RotateKeyRequest_NewKey)(nil), // 52: policy.kasregistry.RotateKeyRequest.NewKey - (*policy.KeyAccessServer)(nil), // 53: policy.KeyAccessServer - (*policy.PageRequest)(nil), // 54: policy.PageRequest - (*policy.PageResponse)(nil), // 55: policy.PageResponse - (*policy.PublicKey)(nil), // 56: policy.PublicKey - (policy.SourceType)(0), // 57: policy.SourceType - (*common.MetadataMutable)(nil), // 58: common.MetadataMutable - (common.MetadataUpdateEnum)(0), // 59: common.MetadataUpdateEnum - (*policy.KasPublicKey)(nil), // 60: policy.KasPublicKey - (*policy.Key)(nil), // 61: policy.Key - (policy.Algorithm)(0), // 62: policy.Algorithm - (policy.KeyMode)(0), // 63: policy.KeyMode - (*policy.PublicKeyCtx)(nil), // 64: policy.PublicKeyCtx - (*policy.PrivateKeyCtx)(nil), // 65: policy.PrivateKeyCtx - (*policy.KasKey)(nil), // 66: policy.KasKey - (*policy.SimpleKasKey)(nil), // 67: policy.SimpleKasKey -} +var ( + file_policy_kasregistry_key_access_server_registry_proto_msgTypes = make([]protoimpl.MessageInfo, 53) + file_policy_kasregistry_key_access_server_registry_proto_goTypes = []interface{}{ + (*GetKeyAccessServerRequest)(nil), // 0: policy.kasregistry.GetKeyAccessServerRequest + (*GetKeyAccessServerResponse)(nil), // 1: policy.kasregistry.GetKeyAccessServerResponse + (*ListKeyAccessServersRequest)(nil), // 2: policy.kasregistry.ListKeyAccessServersRequest + (*ListKeyAccessServersResponse)(nil), // 3: policy.kasregistry.ListKeyAccessServersResponse + (*CreateKeyAccessServerRequest)(nil), // 4: policy.kasregistry.CreateKeyAccessServerRequest + (*CreateKeyAccessServerResponse)(nil), // 5: policy.kasregistry.CreateKeyAccessServerResponse + (*UpdateKeyAccessServerRequest)(nil), // 6: policy.kasregistry.UpdateKeyAccessServerRequest + (*UpdateKeyAccessServerResponse)(nil), // 7: policy.kasregistry.UpdateKeyAccessServerResponse + (*DeleteKeyAccessServerRequest)(nil), // 8: policy.kasregistry.DeleteKeyAccessServerRequest + (*DeleteKeyAccessServerResponse)(nil), // 9: policy.kasregistry.DeleteKeyAccessServerResponse + (*GrantedPolicyObject)(nil), // 10: policy.kasregistry.GrantedPolicyObject + (*KeyAccessServerGrants)(nil), // 11: policy.kasregistry.KeyAccessServerGrants + (*CreatePublicKeyRequest)(nil), // 12: policy.kasregistry.CreatePublicKeyRequest + (*CreatePublicKeyResponse)(nil), // 13: policy.kasregistry.CreatePublicKeyResponse + (*GetPublicKeyRequest)(nil), // 14: policy.kasregistry.GetPublicKeyRequest + (*GetPublicKeyResponse)(nil), // 15: policy.kasregistry.GetPublicKeyResponse + (*ListPublicKeysRequest)(nil), // 16: policy.kasregistry.ListPublicKeysRequest + (*ListPublicKeysResponse)(nil), // 17: policy.kasregistry.ListPublicKeysResponse + (*ListPublicKeyMappingRequest)(nil), // 18: policy.kasregistry.ListPublicKeyMappingRequest + (*ListPublicKeyMappingResponse)(nil), // 19: policy.kasregistry.ListPublicKeyMappingResponse + (*UpdatePublicKeyRequest)(nil), // 20: policy.kasregistry.UpdatePublicKeyRequest + (*UpdatePublicKeyResponse)(nil), // 21: policy.kasregistry.UpdatePublicKeyResponse + (*DeactivatePublicKeyRequest)(nil), // 22: policy.kasregistry.DeactivatePublicKeyRequest + (*DeactivatePublicKeyResponse)(nil), // 23: policy.kasregistry.DeactivatePublicKeyResponse + (*ActivatePublicKeyRequest)(nil), // 24: policy.kasregistry.ActivatePublicKeyRequest + (*ActivatePublicKeyResponse)(nil), // 25: policy.kasregistry.ActivatePublicKeyResponse + (*ListKeyAccessServerGrantsRequest)(nil), // 26: policy.kasregistry.ListKeyAccessServerGrantsRequest + (*ListKeyAccessServerGrantsResponse)(nil), // 27: policy.kasregistry.ListKeyAccessServerGrantsResponse + (*CreateKeyRequest)(nil), // 28: policy.kasregistry.CreateKeyRequest + (*CreateKeyResponse)(nil), // 29: policy.kasregistry.CreateKeyResponse + (*GetKeyRequest)(nil), // 30: policy.kasregistry.GetKeyRequest + (*GetKeyResponse)(nil), // 31: policy.kasregistry.GetKeyResponse + (*ListKeysRequest)(nil), // 32: policy.kasregistry.ListKeysRequest + (*ListKeysResponse)(nil), // 33: policy.kasregistry.ListKeysResponse + (*UpdateKeyRequest)(nil), // 34: policy.kasregistry.UpdateKeyRequest + (*UpdateKeyResponse)(nil), // 35: policy.kasregistry.UpdateKeyResponse + (*KasKeyIdentifier)(nil), // 36: policy.kasregistry.KasKeyIdentifier + (*RotateKeyRequest)(nil), // 37: policy.kasregistry.RotateKeyRequest + (*ChangeMappings)(nil), // 38: policy.kasregistry.ChangeMappings + (*RotatedResources)(nil), // 39: policy.kasregistry.RotatedResources + (*RotateKeyResponse)(nil), // 40: policy.kasregistry.RotateKeyResponse + (*SetBaseKeyRequest)(nil), // 41: policy.kasregistry.SetBaseKeyRequest + (*GetBaseKeyRequest)(nil), // 42: policy.kasregistry.GetBaseKeyRequest + (*GetBaseKeyResponse)(nil), // 43: policy.kasregistry.GetBaseKeyResponse + (*SetBaseKeyResponse)(nil), // 44: policy.kasregistry.SetBaseKeyResponse + (*MappedPolicyObject)(nil), // 45: policy.kasregistry.MappedPolicyObject + (*KeyMapping)(nil), // 46: policy.kasregistry.KeyMapping + (*ListKeyMappingsRequest)(nil), // 47: policy.kasregistry.ListKeyMappingsRequest + (*ListKeyMappingsResponse)(nil), // 48: policy.kasregistry.ListKeyMappingsResponse + (*ListPublicKeyMappingResponse_PublicKeyMapping)(nil), // 49: policy.kasregistry.ListPublicKeyMappingResponse.PublicKeyMapping + (*ListPublicKeyMappingResponse_PublicKey)(nil), // 50: policy.kasregistry.ListPublicKeyMappingResponse.PublicKey + (*ListPublicKeyMappingResponse_Association)(nil), // 51: policy.kasregistry.ListPublicKeyMappingResponse.Association + (*RotateKeyRequest_NewKey)(nil), // 52: policy.kasregistry.RotateKeyRequest.NewKey + (*policy.KeyAccessServer)(nil), // 53: policy.KeyAccessServer + (*policy.PageRequest)(nil), // 54: policy.PageRequest + (*policy.PageResponse)(nil), // 55: policy.PageResponse + (*policy.PublicKey)(nil), // 56: policy.PublicKey + (policy.SourceType)(0), // 57: policy.SourceType + (*common.MetadataMutable)(nil), // 58: common.MetadataMutable + (common.MetadataUpdateEnum)(0), // 59: common.MetadataUpdateEnum + (*policy.KasPublicKey)(nil), // 60: policy.KasPublicKey + (*policy.Key)(nil), // 61: policy.Key + (policy.Algorithm)(0), // 62: policy.Algorithm + (policy.KeyMode)(0), // 63: policy.KeyMode + (*policy.PublicKeyCtx)(nil), // 64: policy.PublicKeyCtx + (*policy.PrivateKeyCtx)(nil), // 65: policy.PrivateKeyCtx + (*policy.KasKey)(nil), // 66: policy.KasKey + (*policy.SimpleKasKey)(nil), // 67: policy.SimpleKasKey + } +) var file_policy_kasregistry_key_access_server_registry_proto_depIdxs = []int32{ 53, // 0: policy.kasregistry.GetKeyAccessServerResponse.key_access_server:type_name -> policy.KeyAccessServer 54, // 1: policy.kasregistry.ListKeyAccessServersRequest.pagination:type_name -> policy.PageRequest diff --git a/protocol/go/policy/objects.pb.go b/protocol/go/policy/objects.pb.go index d2c0921c8b..2e58cf7c33 100644 --- a/protocol/go/policy/objects.pb.go +++ b/protocol/go/policy/objects.pb.go @@ -7,13 +7,14 @@ package policy import ( + reflect "reflect" + sync "sync" + _ "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate" common "github.com/opentdf/platform/protocol/go/common" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" - reflect "reflect" - sync "sync" ) const ( @@ -243,25 +244,34 @@ const ( 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, } ) @@ -302,6 +312,9 @@ const ( 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 +326,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, } ) @@ -3697,7 +3716,7 @@ var file_policy_objects_proto_rawDesc = []byte{ 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, 0x6c, 0x69, 0x63, 0x4b, 0x65, + 0x2a, 0xb0, 0x02, 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, 0x46, 0x49, 0x45, 0x44, 0x10, @@ -3713,41 +3732,45 @@ var file_policy_objects_proto_rawDesc = []byte{ 0x55, 0x4d, 0x5f, 0x45, 0x43, 0x5f, 0x53, 0x45, 0x43, 0x50, 0x33, 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, + 0x53, 0x45, 0x43, 0x50, 0x35, 0x32, 0x31, 0x52, 0x31, 0x10, 0x07, 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, 0x2a, 0xb5, 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, 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, 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 ( @@ -3762,54 +3785,56 @@ func file_policy_objects_proto_rawDescGZIP() []byte { return file_policy_objects_proto_rawDescData } -var file_policy_objects_proto_enumTypes = make([]protoimpl.EnumInfo, 9) -var file_policy_objects_proto_msgTypes = make([]protoimpl.MessageInfo, 33) -var file_policy_objects_proto_goTypes = []interface{}{ - (AttributeRuleTypeEnum)(0), // 0: policy.AttributeRuleTypeEnum - (SubjectMappingOperatorEnum)(0), // 1: policy.SubjectMappingOperatorEnum - (ConditionBooleanTypeEnum)(0), // 2: policy.ConditionBooleanTypeEnum - (SourceType)(0), // 3: policy.SourceType - (KasPublicKeyAlgEnum)(0), // 4: policy.KasPublicKeyAlgEnum - (Algorithm)(0), // 5: policy.Algorithm - (KeyStatus)(0), // 6: policy.KeyStatus - (KeyMode)(0), // 7: policy.KeyMode - (Action_StandardAction)(0), // 8: policy.Action.StandardAction - (*SimpleKasPublicKey)(nil), // 9: policy.SimpleKasPublicKey - (*SimpleKasKey)(nil), // 10: policy.SimpleKasKey - (*KeyProviderConfig)(nil), // 11: policy.KeyProviderConfig - (*Namespace)(nil), // 12: policy.Namespace - (*Attribute)(nil), // 13: policy.Attribute - (*Value)(nil), // 14: policy.Value - (*Action)(nil), // 15: policy.Action - (*SubjectMapping)(nil), // 16: policy.SubjectMapping - (*Condition)(nil), // 17: policy.Condition - (*ConditionGroup)(nil), // 18: policy.ConditionGroup - (*SubjectSet)(nil), // 19: policy.SubjectSet - (*SubjectConditionSet)(nil), // 20: policy.SubjectConditionSet - (*SubjectProperty)(nil), // 21: policy.SubjectProperty - (*ResourceMappingGroup)(nil), // 22: policy.ResourceMappingGroup - (*ResourceMapping)(nil), // 23: policy.ResourceMapping - (*KeyAccessServer)(nil), // 24: policy.KeyAccessServer - (*Key)(nil), // 25: policy.Key - (*KasPublicKey)(nil), // 26: policy.KasPublicKey - (*KasPublicKeySet)(nil), // 27: policy.KasPublicKeySet - (*PublicKey)(nil), // 28: policy.PublicKey - (*RegisteredResource)(nil), // 29: policy.RegisteredResource - (*RegisteredResourceValue)(nil), // 30: policy.RegisteredResourceValue - (*PolicyEnforcementPoint)(nil), // 31: policy.PolicyEnforcementPoint - (*RequestContext)(nil), // 32: policy.RequestContext - (*Obligation)(nil), // 33: policy.Obligation - (*ObligationValue)(nil), // 34: policy.ObligationValue - (*ObligationTrigger)(nil), // 35: policy.ObligationTrigger - (*KasKey)(nil), // 36: policy.KasKey - (*PublicKeyCtx)(nil), // 37: policy.PublicKeyCtx - (*PrivateKeyCtx)(nil), // 38: policy.PrivateKeyCtx - (*AsymmetricKey)(nil), // 39: policy.AsymmetricKey - (*SymmetricKey)(nil), // 40: policy.SymmetricKey - (*RegisteredResourceValue_ActionAttributeValue)(nil), // 41: policy.RegisteredResourceValue.ActionAttributeValue - (*common.Metadata)(nil), // 42: common.Metadata - (*wrapperspb.BoolValue)(nil), // 43: google.protobuf.BoolValue -} +var ( + file_policy_objects_proto_enumTypes = make([]protoimpl.EnumInfo, 9) + file_policy_objects_proto_msgTypes = make([]protoimpl.MessageInfo, 33) + file_policy_objects_proto_goTypes = []interface{}{ + (AttributeRuleTypeEnum)(0), // 0: policy.AttributeRuleTypeEnum + (SubjectMappingOperatorEnum)(0), // 1: policy.SubjectMappingOperatorEnum + (ConditionBooleanTypeEnum)(0), // 2: policy.ConditionBooleanTypeEnum + (SourceType)(0), // 3: policy.SourceType + (KasPublicKeyAlgEnum)(0), // 4: policy.KasPublicKeyAlgEnum + (Algorithm)(0), // 5: policy.Algorithm + (KeyStatus)(0), // 6: policy.KeyStatus + (KeyMode)(0), // 7: policy.KeyMode + (Action_StandardAction)(0), // 8: policy.Action.StandardAction + (*SimpleKasPublicKey)(nil), // 9: policy.SimpleKasPublicKey + (*SimpleKasKey)(nil), // 10: policy.SimpleKasKey + (*KeyProviderConfig)(nil), // 11: policy.KeyProviderConfig + (*Namespace)(nil), // 12: policy.Namespace + (*Attribute)(nil), // 13: policy.Attribute + (*Value)(nil), // 14: policy.Value + (*Action)(nil), // 15: policy.Action + (*SubjectMapping)(nil), // 16: policy.SubjectMapping + (*Condition)(nil), // 17: policy.Condition + (*ConditionGroup)(nil), // 18: policy.ConditionGroup + (*SubjectSet)(nil), // 19: policy.SubjectSet + (*SubjectConditionSet)(nil), // 20: policy.SubjectConditionSet + (*SubjectProperty)(nil), // 21: policy.SubjectProperty + (*ResourceMappingGroup)(nil), // 22: policy.ResourceMappingGroup + (*ResourceMapping)(nil), // 23: policy.ResourceMapping + (*KeyAccessServer)(nil), // 24: policy.KeyAccessServer + (*Key)(nil), // 25: policy.Key + (*KasPublicKey)(nil), // 26: policy.KasPublicKey + (*KasPublicKeySet)(nil), // 27: policy.KasPublicKeySet + (*PublicKey)(nil), // 28: policy.PublicKey + (*RegisteredResource)(nil), // 29: policy.RegisteredResource + (*RegisteredResourceValue)(nil), // 30: policy.RegisteredResourceValue + (*PolicyEnforcementPoint)(nil), // 31: policy.PolicyEnforcementPoint + (*RequestContext)(nil), // 32: policy.RequestContext + (*Obligation)(nil), // 33: policy.Obligation + (*ObligationValue)(nil), // 34: policy.ObligationValue + (*ObligationTrigger)(nil), // 35: policy.ObligationTrigger + (*KasKey)(nil), // 36: policy.KasKey + (*PublicKeyCtx)(nil), // 37: policy.PublicKeyCtx + (*PrivateKeyCtx)(nil), // 38: policy.PrivateKeyCtx + (*AsymmetricKey)(nil), // 39: policy.AsymmetricKey + (*SymmetricKey)(nil), // 40: policy.SymmetricKey + (*RegisteredResourceValue_ActionAttributeValue)(nil), // 41: policy.RegisteredResourceValue.ActionAttributeValue + (*common.Metadata)(nil), // 42: common.Metadata + (*wrapperspb.BoolValue)(nil), // 43: google.protobuf.BoolValue + } +) var file_policy_objects_proto_depIdxs = []int32{ 5, // 0: policy.SimpleKasPublicKey.algorithm:type_name -> policy.Algorithm 9, // 1: policy.SimpleKasKey.public_key:type_name -> policy.SimpleKasPublicKey diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 0000000000..68b624bfc0 --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,112 @@ +# E2E Hybrid Key Testing Scripts + +End-to-end testing of TDF encrypt/decrypt with all supported key algorithms, including hybrid post-quantum (X-Wing, P256+ML-KEM-768, P384+ML-KEM-1024). + +## Prerequisites + +- Go toolchain (matching `go.work` version) +- Docker and Docker Compose +- `curl` + +## Quick Start (Single Command) + +Run everything — setup, tests, and teardown — in one shot: + +```bash +./scripts/e2e-hybrid-test.sh all +``` + +## Two-Terminal Workflow (Recommended) + +This approach lets you keep the platform running and re-run tests as you iterate. + +### Terminal 1: Start services + +```bash +./scripts/e2e-hybrid-test.sh setup +``` + +This will: +1. Generate KAS keys (RSA, EC, X-Wing, P256+ML-KEM-768, P384+ML-KEM-1024) +2. Copy `opentdf-dev.yaml` to `opentdf.yaml` +3. Start Docker containers (Keycloak + PostgreSQL) +4. Provision Keycloak realm and policy fixtures +5. Start the OpenTDF platform on `http://localhost:8080` + +The terminal stays attached to the platform process so you can see server logs. + +### Terminal 2: Run tests + +```bash +# Run all algorithms +./scripts/e2e-hybrid-test.sh test + +# Run a single algorithm +./scripts/e2e-hybrid-test.sh test --alg hpqt:xwing +``` + +### Teardown + +When done, stop everything: + +```bash +# Ctrl+C in Terminal 1 to stop the platform, then: +./scripts/e2e-hybrid-test.sh teardown +``` + +This stops any platform process, tears down Docker containers, and cleans up test artifacts. + +## Supported Algorithms + +| Algorithm | Key Type | Description | +|-----------|----------|-------------| +| `rsa:2048` | `wrapped` | RSA 2048-bit (classic) | +| `ec:secp256r1` | `ec-wrapped` | NIST P-256 Elliptic Curve (classic) | +| `hpqt:xwing` | `hybrid-wrapped` | X-Wing hybrid post-quantum KEM | +| `hpqt:secp256r1-mlkem768` | `hybrid-wrapped` | P-256 + ML-KEM-768 (NIST Level 3) | +| `hpqt:secp384r1-mlkem1024` | `hybrid-wrapped` | P-384 + ML-KEM-1024 (NIST Level 5) | + +## What the Tests Do + +For each algorithm, the test: + +1. **Encrypts** a unique plaintext string into a TDF file using the specified algorithm +2. **Dumps the manifest** showing the key access object, wrapping type, and key ID +3. **Decrypts** the TDF file back to plaintext +4. **Verifies** the decrypted output matches the original plaintext + +## Configuration + +The tests require `ec_tdf_enabled` and `hybrid_tdf_enabled` preview flags to be set to `true` in `opentdf-dev.yaml` under `services.kas.preview`. Without these, the KAS will reject non-RSA rewrap requests. + +```yaml +services: + kas: + preview: + ec_tdf_enabled: true + hybrid_tdf_enabled: true +``` + +## Key Generation Tool + +The hybrid PEM key files are generated by a Go tool at `service/cmd/keygen/`: + +```bash +# Generate all hybrid key pairs to a directory +go run ./service/cmd/keygen -output /path/to/keys +``` + +This produces: +- `kas-xwing-private.pem` / `kas-xwing-public.pem` +- `kas-p256mlkem768-private.pem` / `kas-p256mlkem768-public.pem` +- `kas-p384mlkem1024-private.pem` / `kas-p384mlkem1024-public.pem` + +## Troubleshooting + +| Error | Cause | Fix | +|-------|-------|-----| +| `address already in use` | Platform from a previous run is still on port 8080 | `lsof -ti :8080 \| xargs kill` or `./scripts/e2e-hybrid-test.sh teardown` | +| `http: server gave HTTP response to HTTPS client` | Using `https://` but TLS is disabled | The script uses `http://localhost:8080` by default; ensure you don't override with HTTPS | +| `dial tcp [::1]:8888: connect: connection refused` | Keycloak is not running | Run `./scripts/e2e-hybrid-test.sh setup` or `docker compose up -d` | +| `rewrap request 400 / bad request` | EC or hybrid TDF preview flags are disabled | Set `ec_tdf_enabled: true` and `hybrid_tdf_enabled: true` in `opentdf-dev.yaml` | +| `unable to retrieve public key from KAS at kas.example.com` | Autoconfigure resolved to a non-local KAS | The script passes `--autoconfigure=false`; ensure you're using the script, not manual commands | diff --git a/scripts/e2e-hybrid-test.sh b/scripts/e2e-hybrid-test.sh new file mode 100755 index 0000000000..a504759c84 --- /dev/null +++ b/scripts/e2e-hybrid-test.sh @@ -0,0 +1,379 @@ +#!/usr/bin/env bash +# e2e-hybrid-test.sh +# End-to-end test of TDF encrypt/decrypt with all supported key algorithms, +# including hybrid post-quantum (X-Wing, P256+ML-KEM-768, P384+ML-KEM-1024). +# +# Usage: +# ./scripts/e2e-hybrid-test.sh setup Start services (docker + platform) +# ./scripts/e2e-hybrid-test.sh test [OPTIONS] Run encrypt/decrypt tests +# ./scripts/e2e-hybrid-test.sh teardown Stop services and clean up +# ./scripts/e2e-hybrid-test.sh all [OPTIONS] Setup + test + teardown (legacy mode) +# +# Test options: +# --alg ALGORITHM Test only a specific algorithm (e.g. hpqt:xwing) +# +# Examples: +# ./scripts/e2e-hybrid-test.sh setup # Terminal 1: start everything +# ./scripts/e2e-hybrid-test.sh test # Terminal 2: run all tests +# ./scripts/e2e-hybrid-test.sh test --alg hpqt:xwing # test one algorithm +# ./scripts/e2e-hybrid-test.sh teardown # Terminal 1: stop everything + +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" +cd "$REPO_ROOT" + +PLATFORM_ENDPOINT="http://localhost:8080" +CREDS="opentdf-sdk:secret" +PLATFORM_PID_FILE="/tmp/opentdf-e2e-platform.pid" + +ALL_ALGS=( + "rsa:2048" + "ec:secp256r1" + "hpqt:xwing" + "hpqt:secp256r1-mlkem768" + "hpqt:secp384r1-mlkem1024" +) + +usage() { + sed -n '2,17p' "$0" | sed 's/^# \?//' + exit 1 +} + +# +# ── SETUP ────────────────────────────────────────────────────────────── +# +do_setup() { + echo "============================================" + echo " OpenTDF E2E Setup" + echo "============================================" + echo "" + + # Clean slate: stop any existing services + echo "==> Tearing down any existing services..." + if [ -f "$PLATFORM_PID_FILE" ]; then + local old_pid + old_pid=$(cat "$PLATFORM_PID_FILE") + kill "$old_pid" 2>/dev/null || true + wait "$old_pid" 2>/dev/null || true + rm -f "$PLATFORM_PID_FILE" + fi + local port_pid + port_pid=$(lsof -ti :8080 2>/dev/null || true) + if [ -n "$port_pid" ]; then + kill "$port_pid" 2>/dev/null || true + fi + docker compose down 2>/dev/null || true + echo "" + + # Step 1: Generate keys + echo "==> Generating KAS keys (RSA, EC, hybrid PQ)..." + .github/scripts/init-temp-keys.sh + echo "" + + # Step 2: Create runtime config + echo "==> Creating runtime config..." + cp opentdf-dev.yaml opentdf.yaml + echo "" + + # Step 3: Start docker services + echo "==> Starting docker services (Keycloak + PostgreSQL)..." + docker compose up -d + echo " Waiting for Keycloak to be ready..." + for i in $(seq 1 60); do + if curl -sf http://localhost:8888/auth/realms/master > /dev/null 2>&1; then + echo " Keycloak is ready." + break + fi + if [ "$i" -eq 60 ]; then + echo " ERROR: Keycloak did not become ready in time." + exit 1 + fi + sleep 2 + done + echo "" + + # Step 4: Provision + echo "==> Provisioning Keycloak realm and fixtures..." + go run ./service provision keycloak + go run ./service provision fixtures + echo "" + + # Step 5: Start platform + echo "==> Starting platform..." + go run ./service start & + local pid=$! + echo "$pid" > "$PLATFORM_PID_FILE" + echo " Platform PID: $pid (saved to $PLATFORM_PID_FILE)" + echo " Waiting for platform to be ready..." + for i in $(seq 1 60); do + if curl -sf "$PLATFORM_ENDPOINT/.well-known/opentdf-configuration" > /dev/null 2>&1; then + echo " Platform is ready." + break + fi + if [ "$i" -eq 60 ]; then + echo " ERROR: Platform did not become ready in time." + exit 1 + fi + sleep 2 + done + + echo "" + echo "============================================" + echo " Setup complete. Platform running on $PLATFORM_ENDPOINT" + echo " Run tests with: ./scripts/e2e-hybrid-test.sh test" + echo " Teardown with: ./scripts/e2e-hybrid-test.sh teardown" + echo "============================================" + + # Wait for platform so this terminal stays attached + wait "$pid" 2>/dev/null || true +} + +# +# ── TEST ─────────────────────────────────────────────────────────────── +# +do_test() { + local test_alg="" + + while [ $# -gt 0 ]; do + case "$1" in + --alg) test_alg="$2"; shift 2 ;; + *) echo "Unknown test option: $1"; usage ;; + esac + done + + # Select algorithms + local algs=() + if [ -n "$test_alg" ]; then + algs=("$test_alg") + else + algs=("${ALL_ALGS[@]}") + fi + + # Verify platform is reachable + if ! curl -sf "$PLATFORM_ENDPOINT/.well-known/opentdf-configuration" > /dev/null 2>&1; then + echo "ERROR: Platform is not reachable at $PLATFORM_ENDPOINT" + echo " Run './scripts/e2e-hybrid-test.sh setup' first." + exit 1 + fi + + echo "============================================" + echo " OpenTDF Hybrid Key E2E Tests" + echo "============================================" + echo "" + + # Build examples CLI + echo "==> Building examples CLI..." + (cd examples && go build -o examples .) + echo "" + + echo "==> Running encrypt/decrypt tests..." + echo "" + + local passed=0 + local failed=0 + local results=() + + for alg in "${algs[@]}"; do + local safe_name="${alg//:/-}" + local outfile="test-output-${safe_name}.tdf" + local manifest_file="test-manifest-${safe_name}.json" + local plaintext="Hello from ${alg} at $(date +%s)!" + + echo "--- Testing algorithm: $alg ---" + + # Encrypt + echo " Encrypting..." + if ! examples/examples encrypt "$plaintext" \ + -e "$PLATFORM_ENDPOINT" \ + --insecurePlaintextConn \ + --creds "$CREDS" \ + --autoconfigure=false \ + -A "$alg" \ + -o "$outfile" > "$manifest_file" 2>&1; then + echo " FAIL: Encrypt failed for $alg" + cat "$manifest_file" 2>/dev/null + failed=$((failed + 1)) + results+=("FAIL $alg (encrypt)") + rm -f "$manifest_file" + continue + fi + echo " Encrypted -> $outfile ($(wc -c < "$outfile") bytes)" + echo "" + echo " Key Access ($alg):" + echo " ----------------------------------------" + python3 -c " +import json, sys +m = json.load(open(sys.argv[1])) +ka = m.get('encryptionInformation', {}).get('keyAccess', []) +print(json.dumps(ka, indent=2)) +" "$manifest_file" | sed 's/^/ /' + echo " ----------------------------------------" + rm -f "$manifest_file" + + # Decrypt (session key type defaults to rsa:2048 — independent of wrapping algorithm) + echo " Decrypting..." + local decrypted + decrypted=$(examples/examples decrypt "$outfile" \ + -e "$PLATFORM_ENDPOINT" \ + --insecurePlaintextConn \ + --creds "$CREDS" 2>&1) || true + + if [ "$decrypted" = "$plaintext" ]; then + echo " PASS: Round-trip successful" + passed=$((passed + 1)) + results+=("PASS $alg") + else + echo " FAIL: Decrypted text does not match" + echo " Expected: $plaintext" + echo " Got: $decrypted" + failed=$((failed + 1)) + results+=("FAIL $alg (decrypt mismatch)") + fi + + rm -f "$outfile" + echo "" + done + + # Summary + echo "============================================" + echo " Results: $passed passed, $failed failed" + echo "============================================" + for result in "${results[@]}"; do + echo " $result" + done + echo "" + + if [ "$failed" -gt 0 ]; then + exit 1 + fi +} + +# +# ── TEARDOWN ─────────────────────────────────────────────────────────── +# +do_teardown() { + echo "============================================" + echo " OpenTDF E2E Teardown" + echo "============================================" + echo "" + + # Stop platform + if [ -f "$PLATFORM_PID_FILE" ]; then + local pid + pid=$(cat "$PLATFORM_PID_FILE") + echo "==> Stopping platform (PID $pid)..." + kill "$pid" 2>/dev/null || true + wait "$pid" 2>/dev/null || true + rm -f "$PLATFORM_PID_FILE" + else + # Try to kill anything on port 8080 + local pid + pid=$(lsof -ti :8080 2>/dev/null || true) + if [ -n "$pid" ]; then + echo "==> Stopping process on port 8080 (PID $pid)..." + kill "$pid" 2>/dev/null || true + fi + fi + + # Stop docker + echo "==> Stopping docker services..." + docker compose down 2>/dev/null || true + + # Clean up test artifacts + echo "==> Cleaning up test artifacts..." + rm -f test-output-*.tdf test-manifest-*.json examples/examples + + echo "" + echo " Teardown complete." +} + +# +# ── ALL (legacy: setup + test + teardown) ────────────────────────────── +# +do_all() { + # Setup in background-ish: we need to not block on `wait` + echo "============================================" + echo " OpenTDF E2E Full Run (setup + test + teardown)" + echo "============================================" + echo "" + + # Generate keys + echo "==> Generating KAS keys (RSA, EC, hybrid PQ)..." + .github/scripts/init-temp-keys.sh + echo "" + + echo "==> Creating runtime config..." + cp opentdf-dev.yaml opentdf.yaml + echo "" + + echo "==> Starting docker services (Keycloak + PostgreSQL)..." + docker compose up -d + echo " Waiting for Keycloak to be ready..." + for i in $(seq 1 60); do + if curl -sf http://localhost:8888/auth/realms/master > /dev/null 2>&1; then + echo " Keycloak is ready." + break + fi + if [ "$i" -eq 60 ]; then + echo " ERROR: Keycloak did not become ready in time." + exit 1 + fi + sleep 2 + done + echo "" + + echo "==> Provisioning Keycloak realm and fixtures..." + go run ./service provision keycloak + go run ./service provision fixtures + echo "" + + echo "==> Starting platform..." + go run ./service start & + local platform_pid=$! + echo "$platform_pid" > "$PLATFORM_PID_FILE" + echo " Platform PID: $platform_pid" + echo " Waiting for platform to be ready..." + for i in $(seq 1 60); do + if curl -sf "$PLATFORM_ENDPOINT/.well-known/opentdf-configuration" > /dev/null 2>&1; then + echo " Platform is ready." + break + fi + if [ "$i" -eq 60 ]; then + echo " ERROR: Platform did not become ready in time." + kill "$platform_pid" 2>/dev/null || true + exit 1 + fi + sleep 2 + done + echo "" + + # Run tests (pass remaining args like --alg) + local test_exit=0 + do_test "$@" || test_exit=$? + + # Teardown + echo "" + do_teardown + + exit "$test_exit" +} + +# +# ── MAIN ─────────────────────────────────────────────────────────────── +# +if [ $# -eq 0 ]; then + usage +fi + +COMMAND="$1" +shift + +case "$COMMAND" in + setup) do_setup "$@" ;; + test) do_test "$@" ;; + teardown) do_teardown "$@" ;; + all) do_all "$@" ;; + -h|--help) usage ;; + *) echo "Unknown command: $COMMAND"; usage ;; +esac diff --git a/sdk/basekey.go b/sdk/basekey.go index 5f3cc12004..45a2634ac5 100644 --- a/sdk/basekey.go +++ b/sdk/basekey.go @@ -33,6 +33,12 @@ func getKasKeyAlg(alg string) policy.Algorithm { return policy.Algorithm_ALGORITHM_EC_P384 case string(ocrypto.EC521Key): return policy.Algorithm_ALGORITHM_EC_P521 + case string(ocrypto.HybridXWingKey): + return policy.Algorithm_ALGORITHM_HPQT_XWING + case string(ocrypto.HybridSecp256r1MLKEM768Key): + return policy.Algorithm_ALGORITHM_HPQT_SECP256R1_MLKEM768 + case string(ocrypto.HybridSecp384r1MLKEM1024Key): + return policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024 default: return policy.Algorithm_ALGORITHM_UNSPECIFIED } @@ -51,6 +57,12 @@ func formatAlg(alg policy.Algorithm) (string, error) { return string(ocrypto.EC384Key), nil case policy.Algorithm_ALGORITHM_EC_P521: return string(ocrypto.EC521Key), nil + case policy.Algorithm_ALGORITHM_HPQT_XWING: + return string(ocrypto.HybridXWingKey), nil + case policy.Algorithm_ALGORITHM_HPQT_SECP256R1_MLKEM768: + return string(ocrypto.HybridSecp256r1MLKEM768Key), nil + case policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024: + return string(ocrypto.HybridSecp384r1MLKEM1024Key), nil case policy.Algorithm_ALGORITHM_UNSPECIFIED: fallthrough default: diff --git a/sdk/basekey_test.go b/sdk/basekey_test.go index ea0210bdf0..9703da4275 100644 --- a/sdk/basekey_test.go +++ b/sdk/basekey_test.go @@ -93,6 +93,21 @@ func TestGetKasKeyAlg(t *testing.T) { algStr: string(ocrypto.EC521Key), expected: policy.Algorithm_ALGORITHM_EC_P521, }, + { + 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: "hybrid p384 mlkem1024", + algStr: string(ocrypto.HybridSecp384r1MLKEM1024Key), + expected: policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024, + }, { name: "unsupported algorithm", algStr: "unsupported", @@ -150,6 +165,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 +357,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 { diff --git a/sdk/experimental/tdf/key_access.go b/sdk/experimental/tdf/key_access.go index 6e97701ad5..5ee557aa15 100644 --- a/sdk/experimental/tdf/key_access.go +++ b/sdk/experimental/tdf/key_access.go @@ -4,6 +4,7 @@ package tdf import ( "crypto/sha256" + "encoding/asn1" "encoding/hex" "encoding/json" "errors" @@ -165,6 +166,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 +249,65 @@ 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) { + // Step 1: Parse public key and perform hybrid KEM encapsulation (ECDH + ML-KEM) + var ( + pubKey []byte + sharedSecret, kemCiphertext []byte + err error + ) + + switch ktype { //nolint:exhaustive // only handle hybrid types + case ocrypto.HybridXWingKey: + pubKey, err = ocrypto.XWingPubKeyFromPem([]byte(kasPublicKeyPEM)) + if err != nil { + return "", "", "", fmt.Errorf("failed to parse X-Wing public key: %w", err) + } + sharedSecret, kemCiphertext, err = ocrypto.XWingEncapsulate(pubKey) + case ocrypto.HybridSecp256r1MLKEM768Key: + pubKey, err = ocrypto.P256MLKEM768PubKeyFromPem([]byte(kasPublicKeyPEM)) + if err != nil { + return "", "", "", fmt.Errorf("failed to parse P256+ML-KEM-768 public key: %w", err) + } + sharedSecret, kemCiphertext, err = ocrypto.P256MLKEM768Encapsulate(pubKey) + case ocrypto.HybridSecp384r1MLKEM1024Key: + pubKey, err = ocrypto.P384MLKEM1024PubKeyFromPem([]byte(kasPublicKeyPEM)) + if err != nil { + return "", "", "", fmt.Errorf("failed to parse P384+ML-KEM-1024 public key: %w", err) + } + sharedSecret, kemCiphertext, err = ocrypto.P384MLKEM1024Encapsulate(pubKey) + default: + return "", "", "", fmt.Errorf("unsupported hybrid algorithm: %s", ktype) + } + if err != nil { + return "", "", "", fmt.Errorf("failed to encapsulate with %s: %w", ktype, err) + } + + // Step 2: Derive wrap key from shared secret via HKDF + wrapKey, err := ocrypto.CalculateHKDF(tdfSalt(), sharedSecret) + if err != nil { + return "", "", "", fmt.Errorf("failed to derive HKDF key: %w", err) + } + + // Step 3: AES-GCM encrypt at SDK layer + gcm, err := ocrypto.NewAESGcm(wrapKey) + if err != nil { + return "", "", "", fmt.Errorf("failed to create AES-GCM: %w", err) + } + encryptedDEK, err := gcm.Encrypt(symKey) + if err != nil { + return "", "", "", fmt.Errorf("failed to AES-GCM encrypt: %w", err) + } + + // Step 4: ASN1 marshal at SDK layer + wrappedDER, err := asn1.Marshal(ocrypto.HybridNISTWrappedKey{ + HybridCiphertext: kemCiphertext, + EncryptedDEK: encryptedDEK, + }) + if err != nil { + return "", "", "", fmt.Errorf("failed to ASN1 marshal: %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..a74b1882ba 100644 --- a/sdk/experimental/tdf/keysplit/attributes.go +++ b/sdk/experimental/tdf/keysplit/attributes.go @@ -199,6 +199,12 @@ func formatAlgorithm(alg policy.Algorithm) string { return "rsa:2048" case policy.Algorithm_ALGORITHM_RSA_4096: return "rsa:4096" + case policy.Algorithm_ALGORITHM_HPQT_XWING: + return "hpqt:xwing" + case policy.Algorithm_ALGORITHM_HPQT_SECP256R1_MLKEM768: + return "hpqt:secp256r1-mlkem768" + case policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024: + return "hpqt:secp384r1-mlkem1024" default: return unknownAlgorithm } @@ -217,6 +223,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..e05e50f180 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,105 @@ func createTestAttributeWithRule(fqn, kasURL, kid string, rule policy.AttributeR return value } +func createTestAttributeWithAlgorithm(fqn, kasURL, kid string, alg policy.Algorithm, pem string) *policy.Value { + value := createTestAttribute(fqn, kasURL, kid) + if len(value.GetGrants()) > 0 && len(value.GetGrants()[0].GetKasKeys()) > 0 { + value.GetGrants()[0].GetKasKeys()[0].PublicKey.Algorithm = alg + value.GetGrants()[0].GetKasKeys()[0].PublicKey.Pem = pem + } + return value +} + +func testHybridXWingFlow(t *testing.T) { + ctx := t.Context() + + keyPair, err := ocrypto.NewXWingKeyPair() + require.NoError(t, err) + pubPEM, err := keyPair.PublicKeyInPemFormat() + 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( + "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) +} + +func testHybridP256MLKEM768Flow(t *testing.T) { + ctx := t.Context() + + keyPair, err := ocrypto.NewP256MLKEM768KeyPair() + require.NoError(t, err) + pubPEM, err := keyPair.PublicKeyInPemFormat() + 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( + "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) +} + +func testHybridP384MLKEM1024Flow(t *testing.T) { + ctx := t.Context() + + keyPair, err := ocrypto.NewP384MLKEM1024KeyPair() + require.NoError(t, err) + pubPEM, err := keyPair.PublicKeyInPemFormat() + 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( + "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 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..251be7a323 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: @@ -306,6 +312,12 @@ func convertStringToAlgorithm(alg string) policy.Algorithm { return policy.Algorithm_ALGORITHM_RSA_2048 case ocrypto.RSA4096Key: return policy.Algorithm_ALGORITHM_RSA_4096 + case ocrypto.HybridXWingKey: + return policy.Algorithm_ALGORITHM_HPQT_XWING + case ocrypto.HybridSecp256r1MLKEM768Key: + return policy.Algorithm_ALGORITHM_HPQT_SECP256R1_MLKEM768 + case ocrypto.HybridSecp384r1MLKEM1024Key: + return policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024 default: return policy.Algorithm_ALGORITHM_UNSPECIFIED } @@ -490,6 +502,12 @@ 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 "" } @@ -508,6 +526,12 @@ func algProto2OcryptoKeyType(e policy.Algorithm) ocrypto.KeyType { return ocrypto.RSA2048Key case policy.Algorithm_ALGORITHM_RSA_4096: return ocrypto.RSA4096Key + case policy.Algorithm_ALGORITHM_HPQT_XWING: + return ocrypto.HybridXWingKey + case policy.Algorithm_ALGORITHM_HPQT_SECP256R1_MLKEM768: + return ocrypto.HybridSecp256r1MLKEM768Key + case policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024: + return ocrypto.HybridSecp384r1MLKEM1024Key case policy.Algorithm_ALGORITHM_UNSPECIFIED: return ocrypto.KeyType("") default: diff --git a/sdk/schema/manifest-lax.schema.json b/sdk/schema/manifest-lax.schema.json index a31abd75fb..eb605f0e91 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", "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 dbfdedabf3..06130771d5 100644 --- a/sdk/tdf.go +++ b/sdk/tdf.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "crypto/sha256" + "encoding/asn1" "encoding/hex" "encoding/json" "errors" @@ -43,6 +44,7 @@ const ( kKeySize = 32 kWrapped = "wrapped" kECWrapped = "ec-wrapped" + kHybridWrapped = "hybrid-wrapped" kKasProtocol = "kas" kSplitKeyType = "split" kGCMCipherAlgorithm = "AES-256-GCM" @@ -674,7 +676,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 +696,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 +771,70 @@ func generateWrapKeyWithRSA(publicKey string, symKey []byte) (string, error) { return string(ocrypto.Base64Encode(wrappedKey)), nil } +func generateWrapKeyWithHybrid(algorithm, publicKeyPEM string, symKey []byte) (string, error) { + // Step 1: Parse public key and perform hybrid KEM encapsulation (ECDH + ML-KEM) + // This produces a shared secret and KEM ciphertext without applying KDF. + var ( + pubKey []byte + sharedSecret, kemCiphertext []byte + err error + ) + + ktype := ocrypto.KeyType(algorithm) + switch ktype { //nolint:exhaustive // only handle hybrid types + case ocrypto.HybridXWingKey: + pubKey, err = ocrypto.XWingPubKeyFromPem([]byte(publicKeyPEM)) + if err != nil { + return "", fmt.Errorf("generateWrapKeyWithHybrid: XWingPubKeyFromPem failed: %w", err) + } + sharedSecret, kemCiphertext, err = ocrypto.XWingEncapsulate(pubKey) + case ocrypto.HybridSecp256r1MLKEM768Key: + pubKey, err = ocrypto.P256MLKEM768PubKeyFromPem([]byte(publicKeyPEM)) + if err != nil { + return "", fmt.Errorf("generateWrapKeyWithHybrid: P256MLKEM768PubKeyFromPem failed: %w", err) + } + sharedSecret, kemCiphertext, err = ocrypto.P256MLKEM768Encapsulate(pubKey) + case ocrypto.HybridSecp384r1MLKEM1024Key: + pubKey, err = ocrypto.P384MLKEM1024PubKeyFromPem([]byte(publicKeyPEM)) + if err != nil { + return "", fmt.Errorf("generateWrapKeyWithHybrid: P384MLKEM1024PubKeyFromPem failed: %w", err) + } + sharedSecret, kemCiphertext, err = ocrypto.P384MLKEM1024Encapsulate(pubKey) + default: + return "", fmt.Errorf("unsupported hybrid algorithm: %s", algorithm) + } + if err != nil { + return "", fmt.Errorf("generateWrapKeyWithHybrid: encapsulate failed: %w", err) + } + + // Step 2: Derive wrap key from shared secret via HKDF + wrapKey, err := ocrypto.CalculateHKDF(tdfSalt(), sharedSecret) + if err != nil { + return "", fmt.Errorf("generateWrapKeyWithHybrid: ocrypto.CalculateHKDF failed: %w", err) + } + + // Step 3: AES-GCM encrypt the DEK with the derived wrap key + gcm, err := ocrypto.NewAESGcm(wrapKey) + if err != nil { + return "", fmt.Errorf("generateWrapKeyWithHybrid: ocrypto.NewAESGcm failed: %w", err) + } + encryptedDEK, err := gcm.Encrypt(symKey) + if err != nil { + return "", fmt.Errorf("generateWrapKeyWithHybrid: ocrypto.AESGcm.Encrypt failed: %w", err) + } + + // Step 4: ASN1 marshal at SDK layer + wrappedDER, err := asn1.Marshal(ocrypto.HybridNISTWrappedKey{ + HybridCiphertext: kemCiphertext, + EncryptedDEK: encryptedDEK, + }) + if err != nil { + return "", fmt.Errorf("generateWrapKeyWithHybrid: asn1.Marshal failed: %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 b1d5c0acf3..b5b89d30ff 100644 --- a/sdk/tdf_test.go +++ b/sdk/tdf_test.go @@ -259,6 +259,233 @@ 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----- +BPYx2u2gwPgPG8Jzeu94dWjTTcGpEHKTzGVkGQTAR8dVWr7HP8U9UZpe5tlhOeUF +r34nX9M9Lg+nf6feva6iYNb0aLMPIiRc7IaOVb+rhR5ZysvEMTsHiw3GU40dfFD6 +obRGY37RSElKw6xRKmDha2mt2Lrk0bF30IpRjMDwR6a4IJMdulGzPDa8HCXrypq3 +CJmx0W8b86mlMBsX1slFN6qZg8sQjCwRYVMlVnfWKg1CyE1FQUeF2suIElT+BEyn +wiB1pSOwLLkVEki1Y0CEJr1rq6IRlzd6Cac8TA476mFME3diR04IWDNS+BK/+2Or +tYHwqbYJgggKvCrbibRMXBV8Wj2bGcZFijxhF18VGkl08KFl4SGd5hTfZh7vcqQt +zARs00da93WH2M7DFhvQ86GMezhY04aUoLUXMxS3ya01NbEB5z++U6O30CycYCI4 +Ib0Ae6BnHFoRuola9Ekz/CqBFRGxQchqwG3t+Afsk42yCcMFvL6tFCdNNEUA/bGU +7Bbo+68Rgh0/UUTjAVSM6b21YU8ld1cf1q/SRnOw6xkcSa23qKP+Sz6RM2Vi4Vyp +1GYvMoFuA1y5VMMPHCBUvCt2Sh0qYsPRcMCPQaMbWRQIRwDXNBzsAJSSJBoicCt3 +GxSaVqi7Vyqo28XFyQ5W53CbtRO4wU25UMYL1kO0KplHCFIk9iOToaa5LL1O0HcR +BITaRLNL5pFFjAyQWrJN9xXCojNce1+DxVNC28POKk8Gh4pWgm59xbzxjDOA6XQ3 +YJsawpaU90mZeiATU1B9ORynIVQnlzQIhnREGDEjuCHkCsM/4RE4MRsx1hkgiD1j +VhIA8ZGivG3ikwNc8Ue5KQurcc5UmYGwR4T0J5sJOhgBDHpZgXFZR5+dokLBmIlR +LLZFVB0x+3cXfAWSrIdi6Yi2SakueMzuU206+KwmtBxVABJXeVh+WC1luwgXgGsM +AFmqlxtmdBjd0bUU1LUIoRuVWROrEsfLIkgfVFt9bBwlscEZpnz+U3/89oYbQj+R +6rkiK0+RcgQYh27fyjW4/FTFFWLdVmOK+TWkrCIbEa0mPColi8mfh8SqFSphcXYd +JsLZe3Pnw3G3hauX6TntFA4PIjiEpXvL968fg48Fly7iaCSW2Q8EtHz+oYpLew6l +wjvcEEG9XJ3ahD0yM4FF2jKbiDPuvKJZaLMetEGOZxtgMz+OwmEf1pnZJUqGN3r9 +EaAoDMxAxCpCA3+Accar0RUBqmdzQWYai3YdaT7vkX9IMAae+7vQiT90FCdoQh8c +tHhL+i0I8UXRHKZUpySzAB53t3n702twa2U4BHr0eBbKVn7z92seI2mV2zRRClQL +gnTMGj0DMq9m9kDV+ZOGtlpLuzxW9sZ9DF2YzJvgXHwjTH0QKzgj8cF8JZB5R8zI +9j3hOrzWkrxUxk20Oqhqm1Xla5uDW6LTq57BlBo92F+FchmGAU6bNJj+2l91aK+P +aYx65yRfpUxD2k5pZKmg+4qWZmIvgsiVqyV3iGtQGbi1CxYpPB4SVR3juF2Mygf/ +0GN8EIozpWBfnBw/12a20GnFGw2EZyHGxRjJ+7rGRoca9TZG0MFygnHVcJfxxSki +EyMyy34IliaD+xOCFSMMOlz6suUgEsDfVZEw/FwWOISUwfT9wOLO4K9V2VdFNAD9 +MA== +-----END SECP256R1 MLKEM768 PUBLIC KEY-----` + + mockHybridP256MLKEM768PrivateKey = `-----BEGIN SECP256R1 MLKEM768 PRIVATE KEY----- +n1klZ0/US1beIf9dVVOZXCmw947L71pDnbkZq2BlWd5nqIiZppeHg2plJm8MhL68 +9lFGATAH20fqtbLvbHw8dTqFClKmm2tPFotJFs55mnnyKHGQxHhckiOzlp8YOEZT +cWA2RQvVVG1nGLiFeZUDMzZqZRVe1MVsAh28eEAIskmzVqkvWIi9hqJuW6/vlXJx +WW68xry4e86xCTBxhZOeS4ysJKlSgkgRCFEV2M3HPKGtZWR75RWQxBzSgmJ0MsIE +FmKSA2K4dCSLRIJHlASao5l1N7dIJFP74ZqWJ45LZwUnG8WAMmDWwZlDWBBgiyWa +hmfeymYdJmBASVcRdxwqixLiezOqkAgfZKBFdDaeIoAmR3cc4AYaBhwxWZtAEpky +6cQ39MmTvDWcBb/x7H3SawABkbNzZFmuMiOkMQQkAZ8f2xUaDLNc+XliorYupGEM +Es/rg6T1KaKD5U+aBHMGAsGl129J+1DUdUtxy2m2XMVQFB37EEFGQw+5M8jhuIXL +p8ScZbfrikrbFln0qRO70MFFt8AdlZxxWHpjpIh486/CeB5zw23MRZGqQ19AkahW +SHy8Arr8J74/3Dc6sBvTFaRGKlOUBxnN/I5XRzZANShsC0SGsEF6pLrKwSz11jP+ +NHlx22CUYhWkvBO6mZcDoTJR83cHmHOxYDh0sGI9AXU9RIvjZRFzBrrtR6Gu0sUs +KS4gYE/GJ4RXhgR39yxfHKzJllAJCr4lKLV6xVTKBjxy6JcM5lNcRBTgcz6e5S36 +9KcYhTF1F5y2pIaK8qXsWaB88Hjb9cN/ZRnDOSXkQgxvaY78QFzd4ccs9jDf920b +NymdmiUZsXUjqIqRHCCLgHNnRYAOrDo0eyVXNmidExCJeQJWtj7AcIQZJsHlmkvw +kMWetSb6uGIkFpIvo4jp+3baJSmERMUNF1WfS8B3e0bCDEnY6h8nBbIw6CHEC1gx +x49oErGiAlAnA8QFRU/ghBPS4YWPVAfLOoPlek7/wMwn0LBvOo1WwbOmUpVfqwtW +NzVCipG0AFVy+QbSKhpAsVcxuSbTZI0W9LZF67S0ixe6E2SGtj+WGm6/6JjGOExo +CQToAVDhoUDEU8asJowbZH5eCgwPK4Nh7Ahr6s+NUSpxBRa/FTyTlKPUJ3bl6Enz +CxbIgTOCtVGC1JUu8y6P8BgsMUSG9BxZkc3Cd5sbQwQNwmzfRzPh6Dqrdrxay6DF +mc2A4Yr40D4jZ3Hdaaq+BQZqOqX6jLxcRAB3aS8Des+TqjWq5pMzA7dDt8odbHqP +9kn/xMPX6jtLV2hlQIbmdRwx+IRroF5jNjZW+s785SN8+He6qyw4Kby4gA5rHDS0 +x4POl2PJBqeCu0vI/Mb5jFePplYxJ4eUVrHpkjdVmDKtVm/TYL0oGFeYksV0NJ5v +YiIJZAYpCxulODL4IB9WBM+gS8v5ZgctOa/Su7limVqB6TSeViiVBGE+a0OlsU0f +c8oXTBWIy808crb5NcAgB8A0ZyPG1ESuFm25YXh80x3Pookk2MD4krCYm3hPVqaT +42VaRkAcm1SgsUNzyZQlxrSIS73JZW7ymij0QjMexDH0aLMPIiRc7IaOVb+rhR5Z +ysvEMTsHiw3GU40dfFD6obRGY37RSElKw6xRKmDha2mt2Lrk0bF30IpRjMDwR6a4 +IJMdulGzPDa8HCXrypq3CJmx0W8b86mlMBsX1slFN6qZg8sQjCwRYVMlVnfWKg1C +yE1FQUeF2suIElT+BEynwiB1pSOwLLkVEki1Y0CEJr1rq6IRlzd6Cac8TA476mFM +E3diR04IWDNS+BK/+2OrtYHwqbYJgggKvCrbibRMXBV8Wj2bGcZFijxhF18VGkl0 +8KFl4SGd5hTfZh7vcqQtzARs00da93WH2M7DFhvQ86GMezhY04aUoLUXMxS3ya01 +NbEB5z++U6O30CycYCI4Ib0Ae6BnHFoRuola9Ekz/CqBFRGxQchqwG3t+Afsk42y +CcMFvL6tFCdNNEUA/bGU7Bbo+68Rgh0/UUTjAVSM6b21YU8ld1cf1q/SRnOw6xkc +Sa23qKP+Sz6RM2Vi4Vyp1GYvMoFuA1y5VMMPHCBUvCt2Sh0qYsPRcMCPQaMbWRQI +RwDXNBzsAJSSJBoicCt3GxSaVqi7Vyqo28XFyQ5W53CbtRO4wU25UMYL1kO0KplH +CFIk9iOToaa5LL1O0HcRBITaRLNL5pFFjAyQWrJN9xXCojNce1+DxVNC28POKk8G +h4pWgm59xbzxjDOA6XQ3YJsawpaU90mZeiATU1B9ORynIVQnlzQIhnREGDEjuCHk +CsM/4RE4MRsx1hkgiD1jVhIA8ZGivG3ikwNc8Ue5KQurcc5UmYGwR4T0J5sJOhgB +DHpZgXFZR5+dokLBmIlRLLZFVB0x+3cXfAWSrIdi6Yi2SakueMzuU206+KwmtBxV +ABJXeVh+WC1luwgXgGsMAFmqlxtmdBjd0bUU1LUIoRuVWROrEsfLIkgfVFt9bBwl +scEZpnz+U3/89oYbQj+R6rkiK0+RcgQYh27fyjW4/FTFFWLdVmOK+TWkrCIbEa0m +PColi8mfh8SqFSphcXYdJsLZe3Pnw3G3hauX6TntFA4PIjiEpXvL968fg48Fly7i +aCSW2Q8EtHz+oYpLew6lwjvcEEG9XJ3ahD0yM4FF2jKbiDPuvKJZaLMetEGOZxtg +Mz+OwmEf1pnZJUqGN3r9EaAoDMxAxCpCA3+Accar0RUBqmdzQWYai3YdaT7vkX9I +MAae+7vQiT90FCdoQh8ctHhL+i0I8UXRHKZUpySzAB53t3n702twa2U4BHr0eBbK +Vn7z92seI2mV2zRRClQLgnTMGj0DMq9m9kDV+ZOGtlpLuzxW9sZ9DF2YzJvgXHwj +TH0QKzgj8cF8JZB5R8zI9j3hOrzWkrxUxk20Oqhqm1Xla5uDW6LTq57BlBo92F+F +chmGAU6bNJj+2l91aK+PaYx65yRfpUxD2k5pZKmg+4qWZmIvgsiVqyV3iGtQGbi1 +CxYpPB4SVR3juF2Mygf/0GN8EIozpWBfnBw/12a20GnFGw2EZyHGxRjJ+7rGRoca +9TZG0MFygnHVcJfxxSkiEyMyy34IliaD+xOCFSMMOlz6suUgEsDfVZEw/FwWOISU +wfT9wOLO4K9V2VdFNAD9MNpO9P5ukHVRUM4uu9Zr7SPXt/R7sVivRIoLWxuxAgLh +RMy1GXVGKwc4Va40UVWAM5VyiiFT3kxsPryyTv8ji5A= +-----END SECP256R1 MLKEM768 PRIVATE KEY-----` + + mockHybridP384MLKEM1024PublicKey = `-----BEGIN SECP384R1 MLKEM1024 PUBLIC KEY----- +BH1Lgfame2/B1mB7EVQPtrSOb4lNa/H03q1K6X1KIFhR4HeelPtS75jhDyIQZDAH +qCTI8MpxDBRn+1H1vQZTCOMr9oa/RDicb3UGQsEZVM9pApSZlzurFctZR1nzDpE9 +ASqhC3dlqElBJgxKZGhDFETKkeuzesG0Q4WslBfpVNcHYUTRQg7XptKnmeYDq79w +V8kUFv3xutPLLtfXWlxJg2zRlxDljbnEtsuGR7D0pegzZi3buTspYjZEXznmgCnU +HqpjoVfnVZkhChuAbHw8fXi3Kwgnt7dgiwWiVshmQqYXIVW6LX7imjP0BGpcMKJM +Fjf7rOCKH8Bhj7c7ygE7MaVhR4x5T7UjwMVky7CILjvBhmj6wNd4CmcazGMVzVPq +aiMFwq7II0vmT68XN54UtMt0bdpFa/9SVWILReEJYo4ZN8hAtAZ5o2BitCYYZ56b +Lf6SVEfWCg20zikpRSvLRFooRAcIr+2jQHOcMoiJHhUQj/Jzlg5GCJa2GTv7qsBm +GemDLN/KXgHIxUYzVFgQMBplCqjsQ7lpfVKZCivHuqCnJ4HJHkiZPwvbdkEyD8+r +Ir/xyFS0ZkSmCVtaF2LQImHxL5VBP2C3N9zHwcdnbEiixTJyoY8SKHZHLBpYM155 +l3F7kDCpVjliErF7zo4QZdX8YlZkwDYMIgGlB9tsDzSYMr1ZNL/ZDAHSE7NMRL6m +I2zblG2GlN2FIPWTEHLVX5i0sbw3aZcYaIABZ6wxB8O1hsbrWsjxolVTNK8HAsgy +mWZBbIwifxrqphjgzNt6I8WBN3aSdh9jnLVKnTlsrvh5eqSkgu/HVVxsr430zNTF +TOKMya62Jf0HNbcbaDXxSTmQr3qZA148cs5kpiWYudyse22giQQmTiXgHIUAR4mD +vSTrk5P6azwkqR0AxBBqXMBsBt2aAygEo6/LjrNjerSlbAbRBv3VNJVihCe8DVOn +gN8XuKl8X3JcnggwxNriubJaDemDN3GIzvtBJwFGMMzpQmxciksiKgC7iXenJfQ7 +B95kFcQWZcsDWBdZfXVWfDv5GeXxnMHczD3IisJgnNq1twqYrBQZg2+UjgWFylxE +C/5JK6bVfy6TuYewIsAwniZBYcBhVpQGzPj3snGYi6YbUjNinbJLq545e3MwrMD1 +XELGd+SRLQDbFTE8jTuUAKuUiUqULfiMfo/mYr5cM16XhjRVdhwbTJ8VFAaJM3Ok +hoK7alSFBq92lpKYzlVUwHKTRxQgViPJMirgdfscC0ViQUrGD2YpoIfbgvjkHxFq +x7iJzF6Cq1tSHE5gMCK1a8e1tAJ5pvDUqc1XCu9WAnERrQ1Lb2xDnogJzJkMhIyM +LqmxMzwRH1TZSOMWvrAXfCHhh7IlnWmVBGh4wtCUbD4kG5uRWbW3e1JhbgHJg3Kz +z7qoQRcyfniCntAcOCBpe1+7VV32TWfKyJaLblaAPndAK2AwQgRcyafnVGUklB/c +SVDjNci4HiOAi4eWDJOYyuBrdOYTdfUBYlO5vm+rfOyIFIBCMWmlIvNMaFGibUeg +b5WUo9cSlpTgNsdLsqHjM4E0kViBricsYgoVe4ekGpyjRcIbK0GSOfhYupxqsWUM +a+38LMZyJKCVUhAcP/GqcXCDYSRqmgjDv39wkhmVHibhQvuGJx/MfN9btMWTQCRD +f1TXyCVsCyU5znhEOM+WDAvhr/iiyrcVFAcwnPTstSqRfrszH53CjNVVyzAzaOhh +Q+4MneLWQWfxCicWrLzMa1DSh3hMrUyzxdn4MFEWGgnoVsJjBqfcAzEsK8AUDsnp +kfTAZyVFF7SJoi9osvkxVYbBAj/gDGvFKb/RIjhFEwQclsj0jiV6onA2vHzbNJd8 +hiJAdYGad6FUEAsFN7isQI/KdN2QF8vZKC4qynNarEJKTMJ3U3jBNUPZFKA6rpJK +IF1hRi+aOVdjLLCoEMYmSHsysn8Gkh+VrSLng3EEQRkMljPoe3lTD+QGyg6Ff/Vm +DI1EPCTga6j5YU7qMKZrztRlTqwXILe8h6tgRE10PPPhk3UahdSsdLT1J5YYAbK4 +C0T3A+dFYRq3o3NZwQj5EWIKA/RpWTi8vP2khX7HUfhgSNFjAk/DDoTkYpxkNkPj +U6rGHS2VxplYlFpMtumqPjQ4x6oruVGrM7FEEpaCCRgXwxZ1cLpzlL61WcR6Bb7U +S4SKDN195Gc9OmEyAhREZc2p5VJFZJEyL7qj8nYHffuH +-----END SECP384R1 MLKEM1024 PUBLIC KEY-----` + + mockHybridP384MLKEM1024PrivateKey = `-----BEGIN SECP384R1 MLKEM1024 PRIVATE KEY----- +M57WXgMFtuzS69e108gvEWxnnIQEsn6NyrIyEVbjSupYJUJ14B5gcprEbvJnWQff +lqwlTYJxFDtb9JMxodphWkwYkrkaGliKWBC4mzu6S1IyRGKyYByohnc3apZt9dRx +PRChD8gbUfYN61RMkSUYxveD/HjDcodXYycrKJSrrrJlHZM73ZqklyApIDKJ+caQ +wHsraHRPjIfEIwR9sfLLYApsPbyoblsKhJVhHxRgyHbD7MOmGHF6rbfEAwSSVuG/ +MMo4CigbYBwOYMKChjwqZqrGl9A6qmqFxTcT9siNSJiM6unIviOXKLqUe8qV3PqX +ZLM2PylxVYRZ0oGoJUnKJNZCsZJWQOgU0sQFGAaDVdHAdkIb7ltbAzUUhpEpAdkc +IYFBYODGHdpgjfMYfmemNwpqjUxpj+St+0l0fIEhE5WvvHcp9bOJl6Aal7FjvpMA +dcsxG+y+Z1Mu0ZxorwGkjYkhRDeGDPAcqNmcFyS+BECg3Uuo7bcCXCjD3Pc5KGJ9 +X+tydEabP2WDyLCYQqoOzTd1riAPRZQG5OSdtOAi0WfP2SvObjCiInzIDms+jYyS +gqZT7kqgNOsr/hQ5XmtK5lVwg/RzHApn0qEOzJWWQrRQL6EiU1MH6/gyvfe6Pely ++ftjmMnPWTq4E/mVvlmMqlyG09VoH9V65eBimKoysgAbKVl2KkKTUkFjfiRKFYFg +wvWyj/wp1tARpMxxzDYDmZMdCGJbSyQZ6fm8gTAXGgbD4UfEEbpwBGBwzsh15VU/ +ucOxgrkNJStR44pkTWHE6DV2ofMy/9av8eU4bOgc3iKxV3kYRqdkEJEtxXG143me +3AwjJTZAHkQoHLkv2bq4sYWfXHC3eFxiRsJLeYUu8pVqOQS6WyBp3yVL7PqPp+OD +ApE76Ke2VzhLgVxLUNWGFiANm0oVq+A2ieQik8SjUycmO3WLzfp7s2w0y1JpFOt2 +UZWK7pQKNGCL+imn2kVy1GESoJwyADyxUOWSqqLA1CJ1kWZKwHyiqypAt1W67KIW +KGDAL8GX5YMt4Uk0pcPFJBON/dUZeIkduhaZaGR3pRK2uhAToxAEgJMe1adDmEuu +vpwyYCgB+0lY/laNgidpyrNcarNneRTKNzC6EveTwxuHjnJNSgiQuxtZLfU0MwqI +MjZUNrQubDYRBDYZZ4sBr4YxJwcD+5DAdUMwR4FhfcbN9LmDPkNKxClK3Nd0Keof +oLfCfdgmEHd0O4kz2PfIlUgEzViD6stGKPBdF7K4JYJ4LnKxhxIodaqsaIjGKum/ +TQySQSNbPdGNrAGJR6uAi7gTYmMwa7gKilqg+zZ8FEhglGnDNlsyp8SXqCWb5awJ +cAM67ZiVdjIEeeaCEMqjhDKIKCpxbPA0bvEhOkEDg9QYVdmR1iYplMMTnaoLSPZi +mYQsLMKvKlC6fcEelmTHlQogPcZPlnOmOvF+pXm7qbNgPoAjivx1h3s4aSpLmXVJ +ujYJglIcFhbJ/Be2Smw7WdqlXCTP+pJYAkA1oNsSgbCHI6S0NEzJPQtehoo4KOOM +6YG4l0E4OXjDUWbOO4gS79xodsG0IeEbpfBaq3eGoAIvuqRitpQEOXkN9iZNUHKc +vAtjyyM3WXK3XWlQ8ViCO7Rf4yDKR3RQLUUzBbrONgY9pbY71mFlnrGnm1GO2/Gm +WeMzlQkZVMZxg7NUNSUk5vI0rZZWclPJgqg7tUw7SvGTG6pOpmcFfcnC9WV34XfP +oNO1t4OcBaY9cawEebOK0WquvZRYmPYSOvDHqaek3LB5TteowSBp4tQlsWB0SJwP +MNpR0PaIgVFFOwk45EJFMVDEZRRNgPsCerI93qXBOToEVSDElbu+j2UuhKmuxBNg +/OXEiRqoxreTBHxAXEcZCVux/FhTWbzLJvZU7zYIEfYTgtRxUSZ8wkoD5AuF6yJD +nLUbXwKXohYdhkuobbTPjFLHacGiYQtf3bxNfRTNYQW4pSeYs9VmEqVrk0KwONLO +fya1p5rODxg04uZYENrG5hnA7oIhd0UBTqJ8RbqfQ+VYVZATPnBExCmqg6A0vbEt +87M+gqsSr4k6QvXL1UwsMDrFVwoAlzEeXhZAwcE3kNENYdRjIMQPmZqaKYYhb1th +KqELd2WoSUEmDEpkaEMURMqR67N6wbRDhayUF+lU1wdhRNFCDtem0qeZ5gOrv3BX +yRQW/fG608su19daXEmDbNGXEOWNucS2y4ZHsPSl6DNmLdu5OyliNkRfOeaAKdQe +qmOhV+dVmSEKG4BsfDx9eLcrCCe3t2CLBaJWyGZCphchVbotfuKaM/QEalwwokwW +N/us4IofwGGPtzvKATsxpWFHjHlPtSPAxWTLsIguO8GGaPrA13gKZxrMYxXNU+pq +IwXCrsgjS+ZPrxc3nhS0y3Rt2kVr/1JVYgtF4Qlijhk3yEC0BnmjYGK0Jhhnnpst +/pJUR9YKDbTOKSlFK8tEWihEBwiv7aNAc5wyiIkeFRCP8nOWDkYIlrYZO/uqwGYZ +6YMs38peAcjFRjNUWBAwGmUKqOxDuWl9UpkKK8e6oKcngckeSJk/C9t2QTIPz6si +v/HIVLRmRKYJW1oXYtAiYfEvlUE/YLc33MfBx2dsSKLFMnKhjxIodkcsGlgzXnmX +cXuQMKlWOWISsXvOjhBl1fxiVmTANgwiAaUH22wPNJgyvVk0v9kMAdITs0xEvqYj +bNuUbYaU3YUg9ZMQctVfmLSxvDdplxhogAFnrDEHw7WGxutayPGiVVM0rwcCyDKZ +ZkFsjCJ/GuqmGODM23ojxYE3dpJ2H2OctUqdOWyu+Hl6pKSC78dVXGyvjfTM1MVM +4ozJrrYl/Qc1txtoNfFJOZCvepkDXjxyzmSmJZi53Kx7baCJBCZOJeAchQBHiYO9 +JOuTk/prPCSpHQDEEGpcwGwG3ZoDKASjr8uOs2N6tKVsBtEG/dU0lWKEJ7wNU6eA +3xe4qXxfclyeCDDE2uK5sloN6YM3cYjO+0EnAUYwzOlCbFyKSyIqALuJd6cl9DsH +3mQVxBZlywNYF1l9dVZ8O/kZ5fGcwdzMPciKwmCc2rW3CpisFBmDb5SOBYXKXEQL +/kkrptV/LpO5h7AiwDCeJkFhwGFWlAbM+PeycZiLphtSM2Kdskurnjl7czCswPVc +QsZ35JEtANsVMTyNO5QAq5SJSpQt+Ix+j+ZivlwzXpeGNFV2HBtMnxUUBokzc6SG +grtqVIUGr3aWkpjOVVTAcpNHFCBWI8kyKuB1+xwLRWJBSsYPZimgh9uC+OQfEWrH +uInMXoKrW1IcTmAwIrVrx7W0Anmm8NSpzVcK71YCcRGtDUtvbEOeiAnMmQyEjIwu +qbEzPBEfVNlI4xa+sBd8IeGHsiWdaZUEaHjC0JRsPiQbm5FZtbd7UmFuAcmDcrPP +uqhBFzJ+eIKe0Bw4IGl7X7tVXfZNZ8rIlotuVoA+d0ArYDBCBFzJp+dUZSSUH9xJ +UOM1yLgeI4CLh5YMk5jK4Gt05hN19QFiU7m+b6t87IgUgEIxaaUi80xoUaJtR6Bv +lZSj1xKWlOA2x0uyoeMzgTSRWIGuJyxiChV7h6QanKNFwhsrQZI5+Fi6nGqxZQxr +7fwsxnIkoJVSEBw/8apxcINhJGqaCMO/f3CSGZUeJuFC+4YnH8x831u0xZNAJEN/ +VNfIJWwLJTnOeEQ4z5YMC+Gv+KLKtxUUBzCc9Oy1KpF+uzMfncKM1VXLMDNo6GFD +7gyd4tZBZ/EKJxasvMxrUNKHeEytTLPF2fgwURYaCehWwmMGp9wDMSwrwBQOyemR +9MBnJUUXtImiL2iy+TFVhsECP+AMa8Upv9EiOEUTBByWyPSOJXqicDa8fNs0l3yG +IkB1gZp3oVQQCwU3uKxAj8p03ZAXy9koLirKc1qsQkpMwndTeME1Q9kUoDqukkog +XWFGL5o5V2MssKgQxiZIezKyfwaSH5WtIueDcQRBGQyWM+h7eVMP5AbKDoV/9WYM +jUQ8JOBrqPlhTuowpmvO1GVOrBcgt7yHq2BETXQ88+GTdRqF1Kx0tPUnlhgBsrgL +RPcD50VhGrejc1nBCPkRYgoD9GlZOLy8/aSFfsdR+GBI0WMCT8MOhORinGQ2Q+NT +qsYdLZXGmViUWky26ao+NDjHqiu5UaszsUQSloIJGBfDFnVwunOUvrVZxHoFvtRL +hIoM3X3kZz06YTICFERlzanlUkVkkTIvuqPydgd9+4eakXODFvtYYpzITAPhjVTY +MExDqHROecZV6EN78TKr38WTGPPTAlmQOgOjHen8zHNwJNy50uv3fYfFkQ1xzL1y +-----END SECP384R1 MLKEM1024 PRIVATE KEY-----` ) type TestReadAt struct { @@ -349,6 +576,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 +656,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 +724,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 +2909,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", "rsa:2048"}, + {"https://e.kas/", mockECPrivateKey2, mockECPublicKey2, defaultKID, "rsa:2048"}, + {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 +2943,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 +3032,7 @@ type FakeKASRegistry struct { kasregistryconnect.UnimplementedKeyAccessServerRegistryServiceHandler s *TDFSuite kases []struct { - url, private, public, kid string + url, private, public, kid, algorithm string } } @@ -2913,6 +3195,40 @@ 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.FromPublicPEM(bodyData.GetClientPublicKey()) + f.s.Require().NoError(err, "ocrypto.FromPublicPEM failed") + 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/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 274e09e256..71db5e4922 100644 --- a/service/kas/access/rewrap.go +++ b/service/kas/access/rewrap.go @@ -734,6 +734,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 caaff1d6df..26c86c4efb 100644 --- a/service/kas/kas.go +++ b/service/kas/kas.go @@ -192,7 +192,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 7563a25e49..aad584ed18 100644 --- a/service/kas/kas.proto +++ b/service/kas/kas.proto @@ -68,7 +68,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 @@ -101,7 +101,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; @@ -258,7 +258,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..3cacbed882 100644 --- a/service/kas/key_indexer.go +++ b/service/kas/key_indexer.go @@ -58,6 +58,12 @@ func convertEnumToAlg(alg policy.Algorithm) ocrypto.KeyType { return ocrypto.EC384Key case policy.Algorithm_ALGORITHM_EC_P521: return ocrypto.EC521Key + case policy.Algorithm_ALGORITHM_HPQT_XWING: + return ocrypto.HybridXWingKey + case policy.Algorithm_ALGORITHM_HPQT_SECP256R1_MLKEM768: + return ocrypto.HybridSecp256r1MLKEM768Key + case policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024: + return ocrypto.HybridSecp384r1MLKEM1024Key case policy.Algorithm_ALGORITHM_UNSPECIFIED: fallthrough default: @@ -77,6 +83,12 @@ func convertAlgToEnum(alg string) (policy.Algorithm, error) { return policy.Algorithm_ALGORITHM_EC_P384, nil case string(ocrypto.EC521Key): return policy.Algorithm_ALGORITHM_EC_P521, nil + case string(ocrypto.HybridXWingKey): + return policy.Algorithm_ALGORITHM_HPQT_XWING, nil + case string(ocrypto.HybridSecp256r1MLKEM768Key): + return policy.Algorithm_ALGORITHM_HPQT_SECP256R1_MLKEM768, nil + case string(ocrypto.HybridSecp384r1MLKEM1024Key): + return policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024, nil default: return policy.Algorithm_ALGORITHM_UNSPECIFIED, fmt.Errorf("unsupported algorithm: %s", alg) } diff --git a/service/kas/key_indexer_test.go b/service/kas/key_indexer_test.go index eba2f8278d..5557ba857d 100644 --- a/service/kas/key_indexer_test.go +++ b/service/kas/key_indexer_test.go @@ -168,6 +168,14 @@ func (s *KeyIndexTestSuite) TestKeyDetails_Legacy() { s.True(legacyKey.IsLegacy()) } +func (s *KeyIndexTestSuite) TestAlgorithmConversions_HybridXWing() { + s.Equal(ocrypto.HybridXWingKey, convertEnumToAlg(policy.Algorithm_ALGORITHM_HPQT_XWING)) + + alg, err := convertAlgToEnum(string(ocrypto.HybridXWingKey)) + s.Require().NoError(err) + s.Equal(policy.Algorithm_ALGORITHM_HPQT_XWING, alg) +} + func (s *KeyIndexTestSuite) TestListKeysWith() { mockClient := new(MockKeyAccessServerRegistryClient) keyIndexer := &KeyIndexer{ diff --git a/service/pkg/db/marshalHelpers.go b/service/pkg/db/marshalHelpers.go index 667ab27e21..c78b91ab5f 100644 --- a/service/pkg/db/marshalHelpers.go +++ b/service/pkg/db/marshalHelpers.go @@ -157,6 +157,12 @@ func FormatAlg(alg 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 case policy.Algorithm_ALGORITHM_UNSPECIFIED: fallthrough default: 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/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..7de6a12626 100644 --- a/service/policy/db/grant_mappings.go +++ b/service/policy/db/grant_mappings.go @@ -22,6 +22,8 @@ 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_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 f7c4bdf7f8..bee4e695bd 100644 --- a/service/policy/kasregistry/key_access_server_registry.proto +++ b/service/policy/kasregistry/key_access_server_registry.proto @@ -403,7 +403,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 = { @@ -447,7 +447,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 { @@ -550,7 +550,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 a54bc2517a..8f6b5da374 100644 --- a/service/policy/objects.proto +++ b/service/policy/objects.proto @@ -388,6 +388,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 @@ -553,6 +556,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 1babf8e737..1fd87abb89 100644 --- a/test/start-up-with-containers/action.yaml +++ b/test/start-up-with-containers/action.yaml @@ -111,8 +111,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/tests-bdd/cukes/utils/utils_genKeys.go b/tests-bdd/cukes/utils/utils_genKeys.go index 9038e185ed..3ad0b57599 100644 --- a/tests-bdd/cukes/utils/utils_genKeys.go +++ b/tests-bdd/cukes/utils/utils_genKeys.go @@ -15,6 +15,8 @@ import ( "os/exec" "path" "time" + + "github.com/opentdf/platform/lib/ocrypto" ) const ( @@ -33,6 +35,7 @@ func GenerateTempKeys(ctx context.Context, outputPath string) { generateRSACertificate(outputPath) generateECParameters(outputPath) generateECCertificate(outputPath) + generateHybridKeys(outputPath) generateJavaKeystore(ctx, outputPath) } @@ -203,3 +206,86 @@ func createJavaKeystore(ctx context.Context, certPath, keystorePath string) { log.Printf("Java keystore generated successfully: %s", keystorePath) } + +// generateHybridKeys creates X-Wing, P256+ML-KEM-768, and P384+ML-KEM-1024 key pairs. +func generateHybridKeys(outputPath string) { + specs := []struct { + name string + newKeyPair func() (priv, pub string, err error) + privateOut string + publicOut string + }{ + {"X-Wing", generateXWingKeyPair, "kas-xwing-private.pem", "kas-xwing-public.pem"}, + {"P256+ML-KEM-768", generateP256MLKEM768KeyPair, "kas-p256mlkem768-private.pem", "kas-p256mlkem768-public.pem"}, + {"P384+ML-KEM-1024", generateP384MLKEM1024KeyPair, "kas-p384mlkem1024-private.pem", "kas-p384mlkem1024-public.pem"}, + } + + for _, s := range specs { + priv, pub, err := s.newKeyPair() + if err != nil { + log.Fatalf("Failed to generate %s key pair: %v", s.name, err) + } + + privPath := path.Join(outputPath, s.privateOut) + pubPath := path.Join(outputPath, s.publicOut) + + if err := os.WriteFile(privPath, []byte(priv), 0o600); err != nil { + log.Fatalf("Failed to write %s: %v", privPath, err) + } + if err := os.WriteFile(pubPath, []byte(pub), 0o600); err != nil { + log.Fatalf("Failed to write %s: %v", pubPath, err) + } + + log.Printf("%s key pair generated successfully:", s.name) + log.Printf(" - Private: %s", privPath) + log.Printf(" - Public: %s", pubPath) + } +} + +func generateXWingKeyPair() (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 generateP256MLKEM768KeyPair() (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 generateP384MLKEM1024KeyPair() (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 +}