diff --git a/adr/decisions/2026-06-16-mlkem-direct-key-wrap.md b/adr/decisions/2026-06-16-mlkem-direct-key-wrap.md new file mode 100644 index 0000000000..02887394df --- /dev/null +++ b/adr/decisions/2026-06-16-mlkem-direct-key-wrap.md @@ -0,0 +1,112 @@ +--- +status: accepted +date: 2026-06-16 +tags: + - cryptography + - mlkem + - kas + - hsm + - fips +--- +# ML-KEM-wrapped KAOs use the Decaps shared secret directly as the AES-GCM wrap key (no HKDF) + +## Context and Problem Statement + +PR [opentdf/platform#3537](https://github.com/opentdf/platform/pull/3537) introduces a pure ML-KEM-768 / ML-KEM-1024 wrapping scheme for KAOs (key-access objects) — wire type `mlkem-wrapped`. The first draft of that PR derived the AES-256-GCM wrap key from the ML-KEM Decaps output via HKDF-SHA256 over a fixed `"TDF"` salt, mirroring the existing hybrid PQ/T (`hybrid-wrapped`) path. + +The intended downstream consumer is an HSM-backed KAS provider: specifically Thales Luna T-Series with firmware 7.15.1 in strict-FIPS mode. On that HSM, `CKM_ML_KEM_KEY_DECAP` can only materialize its 32-byte shared secret as a sensitive, non-extractable AES key object (`CKK_AES`). The HSM refuses to emit the Decaps result as `CKK_GENERIC_SECRET`, returning `CKR_ATTRIBUTE_TYPE_INVALID`, which means we cannot: + +* run `CKM_SHA256_HMAC` over the shared secret (so no HKDF-on-HSM), nor +* extract the shared secret to run HKDF off-HSM (`CKA_EXTRACTABLE=false`). + +Any KDF in the unwrap chain therefore blocks HSM-backed KAS providers on this firmware. + +## Decision Drivers + +* Must support HSM-backed KAS providers (Thales Luna T-Series 7.15.1 in strict-FIPS mode) without an HSM firmware change, vendor RFE, or unsafe key extraction. +* Must remain FIPS-compliant. +* Must not change the on-wire envelope format (the wire format is the same ASN.1 DER `MLKEMWrappedKey { MLKEMCiphertext, EncryptedDEK }` — only the internal key-derivation step is removed). +* Must not regress security relative to the HKDF-using draft. +* Must not bleed into the hybrid PQ/T (`hybrid-wrapped`) wrap path, where HKDF is load-bearing as the combiner for the two shared-secret halves. + +## Considered Options + +1. Use the ML-KEM Decaps output directly as the AES-256-GCM wrap key for `mlkem-wrapped`. +2. Keep HKDF-SHA256 over `(sharedSecret, salt, info)` and require vendor firmware support for `CKM_GENERIC_SECRET_KEY_GEN` from Decaps. +3. Keep HKDF and require KAS operators to mark the ML-KEM private key as software-only (no HSM) when used with this wire format. + +## Decision Outcome + +Chosen option: **(1) Use the ML-KEM Decaps shared secret directly as the AES-256-GCM wrap key.** + +The 32-byte Decaps output is fed straight into AES-256-GCM with a fresh random nonce; the AES-GCM ciphertext + tag are stored as `EncryptedDEK` inside the existing ASN.1 envelope. The `salt` / `info` parameters that flow into the unified `kemEncryptor` / `kemDecryptor` are ignored by the ML-KEM adapter (they remain meaningful for the X-Wing and NIST EC + ML-KEM hybrid adapters, which still derive their AES key via HKDF as the combiner). + +### Wire format + +Unchanged. The envelope is still: + +```asn1 +MLKEMWrappedKey ::= SEQUENCE { + mlkemCiphertext [0] IMPLICIT OCTET STRING, + encryptedDEK [1] IMPLICIT OCTET STRING +} +``` + +`encryptedDEK` is now `AES-256-GCM(K = mlkemSharedSecret, nonce = random12B, AAD = none, plaintext = DEK)` with the standard 12-byte nonce prefix + 16-byte tag layout produced by `ocrypto.AesGcm.Encrypt`. No HKDF; no `salt`; no `info`. + +### FIPS 203 justification + +FIPS 203 (Module-Lattice-Based Key-Encapsulation Mechanism Standard) specifies the Decaps output `K` as a uniformly random 32-byte shared secret produced by hashing through the spec's internal G/H/J SHA-3 family functions: + +* §7.3 *ML-KEM.Decaps*: "Output: shared secret key K ∈ B^{32}". +* §6.3 *ML-KEM.Decaps* (the variant exposing the implicit-rejection branch) likewise emits a 32-byte K, including in the failure path where K is derived pseudorandomly from `(z, c)` using J — preserving indistinguishability from a real success. + +Because K is already a 32-byte uniformly random string by construction, an additional HKDF expansion would not increase its entropy or change its distribution — at best, HKDF would re-mix uniformly-random input bits into a different uniformly-random 32-byte output. It is not load-bearing. + +ML-KEM also produces a fresh K per encapsulation by construction (encapsulation samples fresh randomness `m` and packs it through K-PKE encrypt, so every wrap operation produces an independent K). The per-call key-isolation property HKDF is conventionally used to provide is therefore already present in the input. + +### Cryptographic argument + +The properties we need for a DEK-wrap key are: + +1. **Uniform 32-byte distribution.** ML-KEM `Decaps` outputs a 32-byte K drawn from the SHA-3 family applied to fresh per-encapsulation randomness; FIPS 203 specifies this directly. +2. **Per-wrap independence.** Encapsulation samples a fresh 32-byte `m` per call, so K is independent across wraps by construction; no domain separation tag is required to keep wraps from colliding. +3. **Authenticated wrap.** AES-256-GCM provides confidentiality and integrity for the wrapped DEK; a wrong-key unwrap fails at the GCM tag-check stage. FIPS 203 §6.3's implicit-rejection design means a wrong-key Decaps still returns a 32-byte K, but that K is pseudorandom and uncorrelated with the encryptor's K, so the AES-GCM tag verification fails. + +Skipping HKDF therefore neither lowers the wrap key's entropy nor weakens the unwrap-failure behaviour observed by the caller. The only thing HKDF would have added is a fixed-string domain-separation tag (`info`); since the `mlkem-wrapped` wire type is itself a domain-separation tag, there is no cross-protocol collision risk to defend against. + +### Code shape + +* `lib/ocrypto/kem.go`: the `kem` interface gains a `wrapKey(sharedSecret, salt, info []byte) ([]byte, error)` method. `mlkemKEM.wrapKey` returns the shared secret verbatim; `xwingKEM.wrapKey` and `nistHybridKEM.wrapKey` both delegate to the existing `hkdfWrapKey` (renamed from `deriveKEMWrapKey`). The `wrapDEKWithKEM` / `unwrapDEKWithKEM` helpers ask the adapter for the key. +* `lib/ocrypto/mlkem.go`: the `MLKEM{768,1024}{Wrap,Unwrap}DEK` entry points pass `nil, nil` for salt/info so the ignore-semantics are obvious at the call site. +* `lib/ocrypto/hybrid_common.go`: `defaultTDFSalt()` is retained — it is still the default HKDF salt for the X-Wing and NIST hybrid adapters and for ECIES (`FromPublicPEMWithSalt`). + +### Consequences + +* **Good**, because HSM-backed KAS providers (Thales Luna T-Series 7.15.1 in strict-FIPS mode) can now perform `mlkem-wrapped` unwrap end-to-end without ever extracting the Decaps shared secret. The 32-byte K stays on-HSM as a `CKK_AES`, sensitive, non-extractable object and is used directly by `CKM_AES_GCM`. +* **Good**, because the wire format does not change: the ASN.1 envelope is byte-identical, and only the internal key derivation is removed. +* **Good**, because the unified `kem` interface keeps the wrap/unwrap path single-source; the per-scheme key-derivation policy is the only thing that diverges, and it is captured in one method on the adapter. +* **Neutral**, because the `salt` and `info` parameters threaded through the unified encryptor / decryptor constructors still exist (they are needed for X-Wing and NIST hybrid). They are silently ignored for ML-KEM. The `TestMLKEMSaltInfoIgnored` test pins this behaviour so it cannot regress. +* **Bad**, because any wire-format artifact produced by the HKDF-using draft of PR #3537 is no longer decryptable. This is acceptable: PR #3537 is not merged and the HKDF-using artifacts existed only in the PR branch and its test fixtures. + +### Migration + +* PR #3537 is not merged. Any `mlkem-wrapped` envelopes that were produced by intermediate versions of that branch are no longer decryptable after this change. +* The hybrid PQ/T (`hybrid-wrapped`) wrap path is **unchanged**. Both X-Wing and NIST EC + ML-KEM continue to use HKDF-SHA256 over the combined `(EC || ML-KEM)` shared secret, because the KDF is the combiner and is load-bearing for those schemes. + +### Out of scope + +* Maintaining an HKDF-using variant of `mlkem-wrapped` for non-HSM consumers. There is no consumer that requires HKDF — software KAS implementations can use the Decaps output directly with no measurable difference in behaviour or security, and the KDF only adds compute cost on the unwrap path. A second wire variant would split the ecosystem with no upside. +* Generalising direct-shared-secret wrapping to the hybrid PQ/T schemes. For X-Wing and NIST EC + ML-KEM the AES wrap key must be derived from `(ecdhSecret || mlkemSecret)` via a KDF, because (a) the combined input is 64+ bytes (not 32), and (b) HKDF is the combiner that turns the two halves into a single uniformly-random key. Removing HKDF there would reduce security, not just compute. + +## Validation + +* `TestMLKEMSharedSecretIsAESWrapKey` (lib/ocrypto/mlkem_test.go) extracts the AES-GCM ciphertext from an `mlkem-wrapped` envelope and opens it using `AES-256-GCM(K = sharedSecret)` directly, asserting the recovered plaintext matches the original DEK. This pins the no-KDF contract from both directions (encrypt-side and decrypt-side). +* `TestMLKEMSaltInfoIgnored` (lib/ocrypto/mlkem_test.go) wraps with one `(salt, info)` pair and unwraps with a different pair (and again with `nil, nil`); both must succeed, proving salt/info are no-ops for ML-KEM. +* The existing `TestMLKEM{768,1024}WrapUnwrapRoundTrip`, `TestMLKEM{768,1024}WrapUnwrapWrongKeyFails`, and `TestMLKEM{768,1024}WrapDEKFormats` tests continue to pass. + +## More Information + +* FIPS 203, *Module-Lattice-Based Key-Encapsulation Mechanism Standard*, August 2024: https://nvlpubs.nist.gov/nistpubs/fips/nist.fips.203.pdf +* OpenTDF platform PR #3537 (ML-KEM-768 / ML-KEM-1024 post-quantum encryption support): https://github.com/opentdf/platform/pull/3537 +* Related: `lib/ocrypto/HYBRID_NIST_KEY_WRAPPING.md` (hybrid PQ/T variant, which retains HKDF as the combiner). diff --git a/docs/grpc/index.html b/docs/grpc/index.html index c3c252a786..af3b2301d5 100644 --- a/docs/grpc/index.html +++ b/docs/grpc/index.html @@ -4147,6 +4147,18 @@

Algorithm

+ + ALGORITHM_MLKEM_768 + 20 +

+ + + + ALGORITHM_MLKEM_1024 + 21 +

+ + @@ -4276,6 +4288,18 @@

KasPublicKeyAlgEnum

+ + KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + 20 +

+ + + + KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 + 21 +

+ + diff --git a/docs/openapi/authorization/authorization.openapi.yaml b/docs/openapi/authorization/authorization.openapi.yaml index 5a112b61b6..cb0927c76c 100644 --- a/docs/openapi/authorization/authorization.openapi.yaml +++ b/docs/openapi/authorization/authorization.openapi.yaml @@ -143,6 +143,8 @@ components: - ALGORITHM_HPQT_XWING - ALGORITHM_HPQT_SECP256R1_MLKEM768 - ALGORITHM_HPQT_SECP384R1_MLKEM1024 + - ALGORITHM_MLKEM_768 + - ALGORITHM_MLKEM_1024 description: Supported key algorithms. policy.KasPublicKeyAlgEnum: type: string @@ -157,6 +159,8 @@ components: - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 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 0e03a9e182..e6914f49ba 100644 --- a/docs/openapi/authorization/v2/authorization.openapi.yaml +++ b/docs/openapi/authorization/v2/authorization.openapi.yaml @@ -178,6 +178,8 @@ components: - ALGORITHM_HPQT_XWING - ALGORITHM_HPQT_SECP256R1_MLKEM768 - ALGORITHM_HPQT_SECP384R1_MLKEM1024 + - ALGORITHM_MLKEM_768 + - ALGORITHM_MLKEM_1024 description: Supported key algorithms. policy.KasPublicKeyAlgEnum: type: string @@ -192,6 +194,8 @@ components: - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 policy.SourceType: type: string title: SourceType diff --git a/docs/openapi/policy/actions/actions.openapi.yaml b/docs/openapi/policy/actions/actions.openapi.yaml index da57657292..69a29f3504 100644 --- a/docs/openapi/policy/actions/actions.openapi.yaml +++ b/docs/openapi/policy/actions/actions.openapi.yaml @@ -206,6 +206,8 @@ components: - ALGORITHM_HPQT_XWING - ALGORITHM_HPQT_SECP256R1_MLKEM768 - ALGORITHM_HPQT_SECP384R1_MLKEM1024 + - ALGORITHM_MLKEM_768 + - ALGORITHM_MLKEM_1024 description: Supported key algorithms. policy.AttributeRuleTypeEnum: type: string @@ -235,6 +237,8 @@ components: - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 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 cbdde67a8c..104c4a4702 100644 --- a/docs/openapi/policy/attributes/attributes.openapi.yaml +++ b/docs/openapi/policy/attributes/attributes.openapi.yaml @@ -725,6 +725,8 @@ components: - ALGORITHM_HPQT_XWING - ALGORITHM_HPQT_SECP256R1_MLKEM768 - ALGORITHM_HPQT_SECP384R1_MLKEM1024 + - ALGORITHM_MLKEM_768 + - ALGORITHM_MLKEM_1024 description: Supported key algorithms. policy.AttributeRuleTypeEnum: type: string @@ -754,6 +756,8 @@ components: - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 policy.SortDirection: type: string title: SortDirection diff --git a/docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml b/docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml index 2d803524bb..b8c7b84cad 100644 --- a/docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml +++ b/docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml @@ -526,6 +526,8 @@ components: - ALGORITHM_HPQT_XWING - ALGORITHM_HPQT_SECP256R1_MLKEM768 - ALGORITHM_HPQT_SECP384R1_MLKEM1024 + - ALGORITHM_MLKEM_768 + - ALGORITHM_MLKEM_1024 description: Supported key algorithms. policy.KasPublicKeyAlgEnum: type: string @@ -540,6 +542,8 @@ components: - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 policy.KeyMode: type: string title: KeyMode @@ -1181,7 +1185,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, 6, 7, 8] + this in [1, 2, 3, 4, 5, 6, 7, 8, 20, 21] ``` $ref: '#/components/schemas/policy.Algorithm' @@ -1742,7 +1746,7 @@ components: Filter keys by algorithm The key_algorithm must be one of the defined values.: ``` - this in [0, 1, 2, 3, 4, 5, 6, 7, 8] + this in [0, 1, 2, 3, 4, 5, 6, 7, 8, 20, 21] ``` $ref: '#/components/schemas/policy.Algorithm' @@ -2020,7 +2024,7 @@ components: Required The key_algorithm must be one of the defined values.: ``` - this in [1, 2, 3, 4, 5, 6, 7, 8] + this in [1, 2, 3, 4, 5, 6, 7, 8, 20, 21] ``` $ref: '#/components/schemas/policy.Algorithm' diff --git a/docs/openapi/policy/namespaces/namespaces.openapi.yaml b/docs/openapi/policy/namespaces/namespaces.openapi.yaml index d6f72e3e69..c813ecd93c 100644 --- a/docs/openapi/policy/namespaces/namespaces.openapi.yaml +++ b/docs/openapi/policy/namespaces/namespaces.openapi.yaml @@ -356,6 +356,8 @@ components: - ALGORITHM_HPQT_XWING - ALGORITHM_HPQT_SECP256R1_MLKEM768 - ALGORITHM_HPQT_SECP384R1_MLKEM1024 + - ALGORITHM_MLKEM_768 + - ALGORITHM_MLKEM_1024 description: Supported key algorithms. policy.KasPublicKeyAlgEnum: type: string @@ -370,6 +372,8 @@ components: - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 policy.SortDirection: type: string title: SortDirection diff --git a/docs/openapi/policy/objects.openapi.yaml b/docs/openapi/policy/objects.openapi.yaml index 00630c5163..819df15b55 100644 --- a/docs/openapi/policy/objects.openapi.yaml +++ b/docs/openapi/policy/objects.openapi.yaml @@ -24,6 +24,8 @@ components: - ALGORITHM_HPQT_XWING - ALGORITHM_HPQT_SECP256R1_MLKEM768 - ALGORITHM_HPQT_SECP384R1_MLKEM1024 + - ALGORITHM_MLKEM_768 + - ALGORITHM_MLKEM_1024 description: Supported key algorithms. policy.AttributeRuleTypeEnum: type: string @@ -53,6 +55,8 @@ components: - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 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 562045f797..fe4758bf1a 100644 --- a/docs/openapi/policy/obligations/obligations.openapi.yaml +++ b/docs/openapi/policy/obligations/obligations.openapi.yaml @@ -556,6 +556,8 @@ components: - ALGORITHM_HPQT_XWING - ALGORITHM_HPQT_SECP256R1_MLKEM768 - ALGORITHM_HPQT_SECP384R1_MLKEM1024 + - ALGORITHM_MLKEM_768 + - ALGORITHM_MLKEM_1024 description: Supported key algorithms. policy.AttributeRuleTypeEnum: type: string @@ -585,6 +587,8 @@ components: - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 policy.SortDirection: type: string title: SortDirection diff --git a/docs/openapi/policy/registeredresources/registered_resources.openapi.yaml b/docs/openapi/policy/registeredresources/registered_resources.openapi.yaml index ca41d4841e..84ae4a909b 100644 --- a/docs/openapi/policy/registeredresources/registered_resources.openapi.yaml +++ b/docs/openapi/policy/registeredresources/registered_resources.openapi.yaml @@ -416,6 +416,8 @@ components: - ALGORITHM_HPQT_XWING - ALGORITHM_HPQT_SECP256R1_MLKEM768 - ALGORITHM_HPQT_SECP384R1_MLKEM1024 + - ALGORITHM_MLKEM_768 + - ALGORITHM_MLKEM_1024 description: Supported key algorithms. policy.AttributeRuleTypeEnum: type: string @@ -445,6 +447,8 @@ components: - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 policy.SortDirection: type: string title: SortDirection diff --git a/docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml b/docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml index a11306bc24..f393b21637 100644 --- a/docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml +++ b/docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml @@ -416,6 +416,8 @@ components: - ALGORITHM_HPQT_XWING - ALGORITHM_HPQT_SECP256R1_MLKEM768 - ALGORITHM_HPQT_SECP384R1_MLKEM1024 + - ALGORITHM_MLKEM_768 + - ALGORITHM_MLKEM_1024 description: Supported key algorithms. policy.AttributeRuleTypeEnum: type: string @@ -445,6 +447,8 @@ components: - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 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 4c78ec9f8d..2762d4cffa 100644 --- a/docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml +++ b/docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml @@ -452,6 +452,8 @@ components: - ALGORITHM_HPQT_XWING - ALGORITHM_HPQT_SECP256R1_MLKEM768 - ALGORITHM_HPQT_SECP384R1_MLKEM1024 + - ALGORITHM_MLKEM_768 + - ALGORITHM_MLKEM_1024 description: Supported key algorithms. policy.AttributeRuleTypeEnum: type: string @@ -481,6 +483,8 @@ components: - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 policy.SortDirection: type: string title: SortDirection diff --git a/docs/openapi/policy/unsafe/unsafe.openapi.yaml b/docs/openapi/policy/unsafe/unsafe.openapi.yaml index 09ebdf2e85..0fd9822b2f 100644 --- a/docs/openapi/policy/unsafe/unsafe.openapi.yaml +++ b/docs/openapi/policy/unsafe/unsafe.openapi.yaml @@ -390,6 +390,8 @@ components: - ALGORITHM_HPQT_XWING - ALGORITHM_HPQT_SECP256R1_MLKEM768 - ALGORITHM_HPQT_SECP384R1_MLKEM1024 + - ALGORITHM_MLKEM_768 + - ALGORITHM_MLKEM_1024 description: Supported key algorithms. policy.AttributeRuleTypeEnum: type: string @@ -419,6 +421,8 @@ components: - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 policy.KeyMode: type: string title: KeyMode diff --git a/lib/ocrypto/asym_decryption.go b/lib/ocrypto/asym_decryption.go index 1cbfbfc943..9fa66860ae 100644 --- a/lib/ocrypto/asym_decryption.go +++ b/lib/ocrypto/asym_decryption.go @@ -45,11 +45,20 @@ func FromPrivatePEMWithSalt(privateKeyInPem string, salt, info []byte) (PrivateK } switch block.Type { case PEMBlockXWingPrivateKey: - return NewSaltedXWingDecryptor(block.Bytes, salt, info) + return newKEMDecryptor(xwingKEM{}, block.Bytes, salt, info) case PEMBlockP256MLKEM768PrivateKey: - return NewSaltedP256MLKEM768Decryptor(block.Bytes, salt, info) + return newKEMDecryptor(nistHybridKEM{params: &p256mlkem768Params}, block.Bytes, salt, info) case PEMBlockP384MLKEM1024PrivateKey: - return NewSaltedP384MLKEM1024Decryptor(block.Bytes, salt, info) + return newKEMDecryptor(nistHybridKEM{params: &p384mlkem1024Params}, block.Bytes, salt, info) + } + + switch oid, seed, err := parseKEMPrivatePKCS8(block.Bytes); { + case err == nil: + if k, ok := kemByOID(oid); ok { + return newKEMDecryptor(k, seed, salt, info) + } + case !errors.Is(err, errNotKEM): + return AsymDecryption{}, err } priv, err := x509.ParsePKCS8PrivateKey(block.Bytes) diff --git a/lib/ocrypto/asym_encryption.go b/lib/ocrypto/asym_encryption.go index 2a030c3f32..46794d9716 100644 --- a/lib/ocrypto/asym_encryption.go +++ b/lib/ocrypto/asym_encryption.go @@ -26,6 +26,7 @@ const ( RSA SchemeType = "wrapped" EC SchemeType = "ec-wrapped" Hybrid SchemeType = "hybrid-wrapped" + MLKEM SchemeType = "mlkem-wrapped" ) type PublicKeyEncryptor interface { @@ -76,11 +77,20 @@ func FromPublicPEMWithSalt(publicKeyInPem string, salt, info []byte) (PublicKeyE } switch block.Type { case PEMBlockXWingPublicKey: - return NewXWingEncryptor(block.Bytes, salt, info) + return newKEMEncryptor(xwingKEM{}, block.Bytes, salt, info) case PEMBlockP256MLKEM768PublicKey: - return NewP256MLKEM768Encryptor(block.Bytes, salt, info) + return newKEMEncryptor(nistHybridKEM{params: &p256mlkem768Params}, block.Bytes, salt, info) case PEMBlockP384MLKEM1024PublicKey: - return NewP384MLKEM1024Encryptor(block.Bytes, salt, info) + return newKEMEncryptor(nistHybridKEM{params: &p384mlkem1024Params}, block.Bytes, salt, info) + } + + switch oid, key, err := ParseKEMPublicSPKI(block.Bytes); { + case err == nil: + if k, ok := kemByOID(oid); ok { + return newKEMEncryptor(k, key, salt, info) + } + case !errors.Is(err, errNotKEM): + return nil, err } pub, err := getPublicPart(publicKeyInPem) @@ -237,7 +247,7 @@ func publicKeyInPemFormat(pk any) (string, error) { publicKeyPem := pem.EncodeToMemory( &pem.Block{ - Type: "PUBLIC KEY", + Type: pemBlockPublicKey, Bytes: publicKeyBytes, }, ) diff --git a/lib/ocrypto/benchmark_test.go b/lib/ocrypto/benchmark_test.go index b86b3075e7..eb741838de 100644 --- a/lib/ocrypto/benchmark_test.go +++ b/lib/ocrypto/benchmark_test.go @@ -198,7 +198,7 @@ func BenchmarkWrapDEK(b *testing.B) { if err != nil { b.Fatal(err) } - ss, ct, err := XWingEncapsulate(pubKey) + ss, ct, err := xwingKEM{}.encapsulate(pubKey) if err != nil { b.Fatal(err) } @@ -214,9 +214,9 @@ func BenchmarkWrapDEK(b *testing.B) { if err != nil { b.Fatal(err) } - sinkBytes, errSink = asn1.Marshal(HybridNISTWrappedKey{ - HybridCiphertext: ct, - EncryptedDEK: encDEK, + sinkBytes, errSink = asn1.Marshal(kemEnvelope{ + KEMCiphertext: ct, + EncryptedDEK: encDEK, }) } b.ReportMetric(float64(len(sinkBytes)), "wrapped-bytes") @@ -229,7 +229,7 @@ func BenchmarkWrapDEK(b *testing.B) { if err != nil { b.Fatal(err) } - ss, ct, err := P256MLKEM768Encapsulate(pubKey) + ss, ct, err := nistHybridKEM{params: &p256mlkem768Params}.encapsulate(pubKey) if err != nil { b.Fatal(err) } @@ -245,9 +245,9 @@ func BenchmarkWrapDEK(b *testing.B) { if err != nil { b.Fatal(err) } - sinkBytes, errSink = asn1.Marshal(HybridNISTWrappedKey{ - HybridCiphertext: ct, - EncryptedDEK: encDEK, + sinkBytes, errSink = asn1.Marshal(kemEnvelope{ + KEMCiphertext: ct, + EncryptedDEK: encDEK, }) } b.ReportMetric(float64(len(sinkBytes)), "wrapped-bytes") @@ -260,7 +260,7 @@ func BenchmarkWrapDEK(b *testing.B) { if err != nil { b.Fatal(err) } - ss, ct, err := P384MLKEM1024Encapsulate(pubKey) + ss, ct, err := nistHybridKEM{params: &p384mlkem1024Params}.encapsulate(pubKey) if err != nil { b.Fatal(err) } @@ -276,9 +276,9 @@ func BenchmarkWrapDEK(b *testing.B) { if err != nil { b.Fatal(err) } - sinkBytes, errSink = asn1.Marshal(HybridNISTWrappedKey{ - HybridCiphertext: ct, - EncryptedDEK: encDEK, + sinkBytes, errSink = asn1.Marshal(kemEnvelope{ + KEMCiphertext: ct, + EncryptedDEK: encDEK, }) } b.ReportMetric(float64(len(sinkBytes)), "wrapped-bytes") @@ -536,12 +536,16 @@ func BenchmarkUnwrapDEK(b *testing.B) { } func BenchmarkHybridSubOps(b *testing.B) { + xwingAdapter := xwingKEM{} + p256Adapter := nistHybridKEM{params: &p256mlkem768Params} + p384Adapter := nistHybridKEM{params: &p384mlkem1024Params} + // Setup X-Wing xwingKP, err := NewXWingKeyPair() if err != nil { b.Fatal(err) } - xwingSS, xwingCt, err := XWingEncapsulate(xwingKP.publicKey) + xwingSS, xwingCt, err := xwingAdapter.encapsulate(xwingKP.publicKey) if err != nil { b.Fatal(err) } @@ -551,7 +555,7 @@ func BenchmarkHybridSubOps(b *testing.B) { if err != nil { b.Fatal(err) } - p256SS, p256Ct, err := P256MLKEM768Encapsulate(p256KP.publicKey) + p256SS, p256Ct, err := p256Adapter.encapsulate(p256KP.publicKey) if err != nil { b.Fatal(err) } @@ -561,7 +565,7 @@ func BenchmarkHybridSubOps(b *testing.B) { if err != nil { b.Fatal(err) } - p384SS, p384Ct, err := P384MLKEM1024Encapsulate(p384KP.publicKey) + p384SS, p384Ct, err := p384Adapter.encapsulate(p384KP.publicKey) if err != nil { b.Fatal(err) } @@ -569,19 +573,19 @@ func BenchmarkHybridSubOps(b *testing.B) { salt := defaultTDFSalt() // Pre-derive a wrap key for AES-GCM benchmarks - wrapKey, err := deriveXWingWrapKey(xwingSS, salt, nil) + wrapKey, err := hkdfWrapKey(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) + sinkBytes, sinkBytes, errSink = xwingAdapter.encapsulate(xwingKP.publicKey) } }) b.Run("XWing/HKDF", func(b *testing.B) { for b.Loop() { - sinkBytes, errSink = deriveXWingWrapKey(xwingSS, salt, nil) + sinkBytes, errSink = hkdfWrapKey(xwingSS, salt, nil) } }) b.Run("XWing/AES-GCM-Encrypt", func(b *testing.B) { @@ -594,25 +598,25 @@ func BenchmarkHybridSubOps(b *testing.B) { } }) b.Run("XWing/ASN1-Marshal", func(b *testing.B) { - wrapped := XWingWrappedKey{XWingCiphertext: xwingCt, EncryptedDEK: testDEK} + wrapped := kemEnvelope{KEMCiphertext: xwingCt, EncryptedDEK: testDEK} for b.Loop() { sinkBytes, errSink = asn1.Marshal(wrapped) } }) // P256+MLKEM768 sub-ops - p256WrapKey, err := deriveHybridNISTWrapKey(p256SS, salt, nil) + p256WrapKey, err := hkdfWrapKey(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) + sinkBytes, sinkBytes, errSink = p256Adapter.encapsulate(p256KP.publicKey) } }) b.Run("P256_MLKEM768/HKDF", func(b *testing.B) { for b.Loop() { - sinkBytes, errSink = deriveHybridNISTWrapKey(p256SS, salt, nil) + sinkBytes, errSink = hkdfWrapKey(p256SS, salt, nil) } }) b.Run("P256_MLKEM768/AES-GCM-Encrypt", func(b *testing.B) { @@ -625,25 +629,25 @@ func BenchmarkHybridSubOps(b *testing.B) { } }) b.Run("P256_MLKEM768/ASN1-Marshal", func(b *testing.B) { - wrapped := HybridNISTWrappedKey{HybridCiphertext: p256Ct, EncryptedDEK: testDEK} + wrapped := kemEnvelope{KEMCiphertext: p256Ct, EncryptedDEK: testDEK} for b.Loop() { sinkBytes, errSink = asn1.Marshal(wrapped) } }) // P384+MLKEM1024 sub-ops - p384WrapKey, err := deriveHybridNISTWrapKey(p384SS, salt, nil) + p384WrapKey, err := hkdfWrapKey(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) + sinkBytes, sinkBytes, errSink = p384Adapter.encapsulate(p384KP.publicKey) } }) b.Run("P384_MLKEM1024/HKDF", func(b *testing.B) { for b.Loop() { - sinkBytes, errSink = deriveHybridNISTWrapKey(p384SS, salt, nil) + sinkBytes, errSink = hkdfWrapKey(p384SS, salt, nil) } }) b.Run("P384_MLKEM1024/AES-GCM-Encrypt", func(b *testing.B) { @@ -656,7 +660,7 @@ func BenchmarkHybridSubOps(b *testing.B) { } }) b.Run("P384_MLKEM1024/ASN1-Marshal", func(b *testing.B) { - wrapped := HybridNISTWrappedKey{HybridCiphertext: p384Ct, EncryptedDEK: testDEK} + wrapped := kemEnvelope{KEMCiphertext: p384Ct, EncryptedDEK: testDEK} for b.Loop() { sinkBytes, errSink = asn1.Marshal(wrapped) } diff --git a/lib/ocrypto/ec_key_pair.go b/lib/ocrypto/ec_key_pair.go index 70e30cc8df..11a86d96fb 100644 --- a/lib/ocrypto/ec_key_pair.go +++ b/lib/ocrypto/ec_key_pair.go @@ -4,6 +4,7 @@ import ( "crypto/ecdh" "crypto/ecdsa" "crypto/elliptic" + "crypto/mlkem" "crypto/rand" "crypto/sha256" "crypto/x509" @@ -22,11 +23,13 @@ type ECCMode uint8 type KeyType string const ( - RSA2048Key KeyType = "rsa:2048" - RSA4096Key KeyType = "rsa:4096" - EC256Key KeyType = "ec:secp256r1" - EC384Key KeyType = "ec:secp384r1" - EC521Key KeyType = "ec:secp521r1" + RSA2048Key KeyType = "rsa:2048" + RSA4096Key KeyType = "rsa:4096" + EC256Key KeyType = "ec:secp256r1" + EC384Key KeyType = "ec:secp384r1" + EC521Key KeyType = "ec:secp521r1" + MLKEM768Key KeyType = "mlkem:768" + MLKEM1024Key KeyType = "mlkem:1024" ) // ParseKeyType validates a string as a known KeyType, returning an error for @@ -35,6 +38,7 @@ func ParseKeyType(alg string) (KeyType, error) { switch KeyType(alg) { case RSA2048Key, RSA4096Key, EC256Key, EC384Key, EC521Key, + MLKEM768Key, MLKEM1024Key, HybridXWingKey, HybridSecp256r1MLKEM768Key, HybridSecp384r1MLKEM1024Key: return KeyType(alg), nil default: @@ -79,6 +83,10 @@ func NewKeyPair(kt KeyType) (KeyPair, error) { return NewECKeyPair(mode) case HybridSecp256r1MLKEM768Key, HybridSecp384r1MLKEM1024Key, HybridXWingKey: return NewHybridKeyPair(kt) + case MLKEM768Key: + return NewMLKEMKeyPair() + case MLKEM1024Key: + return NewMLKEM1024KeyPair() default: return nil, fmt.Errorf("unsupported key type: %v", kt) } @@ -88,6 +96,14 @@ type ECKeyPair struct { PrivateKey *ecdsa.PrivateKey } +type MLKEMKeyPair struct { + PrivateKey *mlkem.DecapsulationKey768 +} + +type MLKEM1024KeyPair struct { + PrivateKey *mlkem.DecapsulationKey1024 +} + func IsECKeyType(kt KeyType) bool { switch kt { //nolint:exhaustive // only handle ec types case EC256Key, EC384Key, EC521Key: @@ -106,6 +122,15 @@ func IsRSAKeyType(kt KeyType) bool { } } +func IsMLKEMKeyType(kt KeyType) bool { + switch kt { //nolint:exhaustive // only handle mlkem types + case MLKEM768Key, MLKEM1024Key: + return true + default: + return false + } +} + // GetECCurveFromECCMode return elliptic curve from ecc mode func GetECCurveFromECCMode(mode ECCMode) (elliptic.Curve, error) { var c elliptic.Curve @@ -212,7 +237,7 @@ func (keyPair ECKeyPair) PrivateKeyInPemFormat() (string, error) { privateKeyPem := pem.EncodeToMemory( &pem.Block{ - Type: "PRIVATE KEY", + Type: pemBlockPrivateKey, Bytes: privateKeyBytes, }, ) @@ -232,7 +257,7 @@ func (keyPair ECKeyPair) PublicKeyInPemFormat() (string, error) { publicKeyPem := pem.EncodeToMemory( &pem.Block{ - Type: "PUBLIC KEY", + Type: pemBlockPublicKey, Bytes: publicKeyBytes, }, ) @@ -452,7 +477,7 @@ func ECPrivateKeyInPemFormat(privateKey ecdsa.PrivateKey) (string, error) { privateKeyPem := pem.EncodeToMemory( &pem.Block{ - Type: "PRIVATE KEY", + Type: pemBlockPrivateKey, Bytes: privateKeyBytes, }, ) @@ -468,7 +493,7 @@ func ECPublicKeyInPemFormat(publicKey ecdsa.PublicKey) (string, error) { publicKeyPem := pem.EncodeToMemory( &pem.Block{ - Type: "PUBLIC KEY", + Type: pemBlockPublicKey, Bytes: pkb, }, ) @@ -509,3 +534,77 @@ func GetECKeySize(pemData []byte) (int, error) { func (keyPair ECKeyPair) GetKeyType() KeyType { return EC256Key } + +func NewMLKEMKeyPair() (MLKEMKeyPair, error) { + privateKey, err := mlkem.GenerateKey768() + if err != nil { + return MLKEMKeyPair{}, fmt.Errorf("mlkem.GenerateKey768 failed: %w", err) + } + + return MLKEMKeyPair{PrivateKey: privateKey}, nil +} + +func NewMLKEM1024KeyPair() (MLKEM1024KeyPair, error) { + privateKey, err := mlkem.GenerateKey1024() + if err != nil { + return MLKEM1024KeyPair{}, fmt.Errorf("mlkem.GenerateKey1024 failed: %w", err) + } + + return MLKEM1024KeyPair{PrivateKey: privateKey}, nil +} + +func (keyPair MLKEMKeyPair) PrivateKeyInPemFormat() (string, error) { + if keyPair.PrivateKey == nil { + return "", errors.New("failed to generate PEM formatted private key") + } + + der, err := marshalKEMPrivatePKCS8(OidMLKEM768, keyPair.PrivateKey.Bytes()) + if err != nil { + return "", fmt.Errorf("marshal ML-KEM-768 PKCS#8 failed: %w", err) + } + return string(pem.EncodeToMemory(&pem.Block{Type: pemBlockPrivateKey, Bytes: der})), nil +} + +func (keyPair MLKEMKeyPair) PublicKeyInPemFormat() (string, error) { + if keyPair.PrivateKey == nil { + return "", errors.New("failed to generate PEM formatted public key") + } + + der, err := marshalKEMPublicSPKI(OidMLKEM768, keyPair.PrivateKey.EncapsulationKey().Bytes()) + if err != nil { + return "", fmt.Errorf("marshal ML-KEM-768 SPKI failed: %w", err) + } + return string(pem.EncodeToMemory(&pem.Block{Type: pemBlockPublicKey, Bytes: der})), nil +} + +func (keyPair MLKEMKeyPair) GetKeyType() KeyType { + return MLKEM768Key +} + +func (keyPair MLKEM1024KeyPair) PrivateKeyInPemFormat() (string, error) { + if keyPair.PrivateKey == nil { + return "", errors.New("failed to generate PEM formatted private key") + } + + der, err := marshalKEMPrivatePKCS8(OidMLKEM1024, keyPair.PrivateKey.Bytes()) + if err != nil { + return "", fmt.Errorf("marshal ML-KEM-1024 PKCS#8 failed: %w", err) + } + return string(pem.EncodeToMemory(&pem.Block{Type: pemBlockPrivateKey, Bytes: der})), nil +} + +func (keyPair MLKEM1024KeyPair) PublicKeyInPemFormat() (string, error) { + if keyPair.PrivateKey == nil { + return "", errors.New("failed to generate PEM formatted public key") + } + + der, err := marshalKEMPublicSPKI(OidMLKEM1024, keyPair.PrivateKey.EncapsulationKey().Bytes()) + if err != nil { + return "", fmt.Errorf("marshal ML-KEM-1024 SPKI failed: %w", err) + } + return string(pem.EncodeToMemory(&pem.Block{Type: pemBlockPublicKey, Bytes: der})), nil +} + +func (keyPair MLKEM1024KeyPair) GetKeyType() KeyType { + return MLKEM1024Key +} diff --git a/lib/ocrypto/hybrid_common.go b/lib/ocrypto/hybrid_common.go index 4f4ee05417..bf7385c9ad 100644 --- a/lib/ocrypto/hybrid_common.go +++ b/lib/ocrypto/hybrid_common.go @@ -6,42 +6,44 @@ import ( "fmt" ) -// HybridWrapDEK parses the recipient's hybrid public key PEM, encapsulates -// against it using the scheme implied by ktype, and returns the ASN.1-encoded -// wrapped DEK envelope used in `hybrid-wrapped` manifests. It dispatches across -// both the X-Wing and NIST EC + ML-KEM families so SDK call sites do not need -// to repeat the algorithm switch. +// WrapDEK parses the recipient's KEM public key PEM, encapsulates against it +// using the scheme implied by ktype, and returns the ASN.1-encoded wrapped DEK +// envelope used in `hybrid-wrapped` and `mlkem-wrapped` manifests. It covers +// every KEM family — pure ML-KEM, X-Wing, and the NIST EC + ML-KEM hybrids — +// so SDK call sites do not need to repeat the algorithm switch. // -// The HKDF salt is the default TDF salt; callers that need a non-default salt -// should call the per-scheme `*WrapDEK` helpers directly. -func HybridWrapDEK(ktype KeyType, kasPublicKeyPEM string, dek []byte) ([]byte, error) { - switch ktype { //nolint:exhaustive // only handle hybrid types - case HybridXWingKey: - pubKey, err := XWingPubKeyFromPem([]byte(kasPublicKeyPEM)) - if err != nil { - return nil, fmt.Errorf("X-Wing public key: %w", err) - } - return XWingWrapDEK(pubKey, dek) - case HybridSecp256r1MLKEM768Key: - pubKey, err := P256MLKEM768PubKeyFromPem([]byte(kasPublicKeyPEM)) - if err != nil { - return nil, fmt.Errorf("P-256+ML-KEM-768 public key: %w", err) - } - return P256MLKEM768WrapDEK(pubKey, dek) - case HybridSecp384r1MLKEM1024Key: - pubKey, err := P384MLKEM1024PubKeyFromPem([]byte(kasPublicKeyPEM)) - if err != nil { - return nil, fmt.Errorf("P-384+ML-KEM-1024 public key: %w", err) - } - return P384MLKEM1024WrapDEK(pubKey, dek) - default: - return nil, fmt.Errorf("unsupported hybrid key type: %s", ktype) +// For hybrid PQ/T schemes the HKDF salt is the default TDF salt; callers that +// need a non-default salt should construct an encryptor via +// FromPublicPEMWithSalt instead. Pure ML-KEM ignores salt/info and uses the +// 32-byte Decaps shared secret directly as the AES-GCM wrap key — see +// adr/decisions/2026-06-16-mlkem-direct-key-wrap.md. +func WrapDEK(ktype KeyType, kasPublicKeyPEM string, dek []byte) ([]byte, error) { + if !IsKEMKeyType(ktype) { + return nil, fmt.Errorf("unsupported KEM key type: %s", ktype) + } + enc, err := FromPublicPEM(kasPublicKeyPEM) + if err != nil { + return nil, fmt.Errorf("parse %s public key: %w", ktype, err) } + if got := enc.KeyType(); got != ktype { + return nil, fmt.Errorf("KEM key type mismatch: want %s, got %s", ktype, got) + } + return enc.Encrypt(dek) +} + +// HybridWrapDEK is the legacy entrypoint for hybrid PQ/T wrapping. It now +// delegates to WrapDEK, which covers both hybrid and pure ML-KEM schemes. +// +// Deprecated: Use WrapDEK. +func HybridWrapDEK(ktype KeyType, kasPublicKeyPEM string, dek []byte) ([]byte, error) { + return WrapDEK(ktype, kasPublicKeyPEM, dek) } -// defaultTDFSalt returns the salt used for HKDF derivation in all TDF hybrid -// key wrapping schemes (X-Wing and NIST EC + ML-KEM). Defined here rather than -// in a per-scheme file so that any change applies uniformly across schemes. +// defaultTDFSalt returns the salt used for HKDF derivation in the hybrid +// PQ/T KEM wrapping schemes (X-Wing and NIST EC + ML-KEM) and in ECIES. +// Pure ML-KEM uses the Decaps shared secret directly as the AES-GCM wrap +// key (see adr/decisions/2026-06-16-mlkem-direct-key-wrap.md) and does +// not call this helper. func defaultTDFSalt() []byte { digest := sha256.New() digest.Write([]byte("TDF")) @@ -66,6 +68,24 @@ func rawToPEM(blockType string, raw []byte, expectedSize int) (string, error) { return string(pemBytes), nil } +// decodeSizedPEMBlock decodes a PEM block of the given type and verifies its +// payload is exactly expectedSize bytes long. Used by X-Wing and NIST hybrid +// PEM helpers that carry raw-bytes PEM blobs (pre-SPKI-migration). +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 +} + // cloneOrNil returns a copy of data, or nil if data is empty. func cloneOrNil(data []byte) []byte { if len(data) == 0 { diff --git a/lib/ocrypto/hybrid_nist.go b/lib/ocrypto/hybrid_nist.go index ee0a575ac5..1de0f08b91 100644 --- a/lib/ocrypto/hybrid_nist.go +++ b/lib/ocrypto/hybrid_nist.go @@ -4,12 +4,7 @@ import ( "crypto/ecdh" "crypto/mlkem" "crypto/rand" - "crypto/sha256" - "encoding/asn1" "fmt" - "io" - - "golang.org/x/crypto/hkdf" ) const ( @@ -52,15 +47,6 @@ const ( 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 @@ -105,22 +91,6 @@ type HybridNISTKeyPair struct { 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 @@ -221,299 +191,34 @@ func P384MLKEM1024PrivateKeyFromPem(data []byte) ([]byte, error) { return decodeSizedPEMBlock(data, PEMBlockP384MLKEM1024PrivateKey, P384MLKEM1024PrivateKeySize) } -func NewP256MLKEM768Encryptor(publicKey, salt, info []byte) (*HybridNISTEncryptor, error) { - return newHybridNISTEncryptor(&p256mlkem768Params, publicKey, salt, info) -} - -func NewP384MLKEM1024Encryptor(publicKey, salt, info []byte) (*HybridNISTEncryptor, error) { - return newHybridNISTEncryptor(&p384mlkem1024Params, publicKey, salt, info) -} - -func newHybridNISTEncryptor(p *hybridNISTParams, publicKey, salt, info []byte) (*HybridNISTEncryptor, error) { - expectedSize := p.ecPubSize + p.mlkemPubSize - if len(publicKey) != expectedSize { - return nil, fmt.Errorf("invalid %s public key size: got %d want %d", p.keyType, len(publicKey), expectedSize) - } - return &HybridNISTEncryptor{ - publicKey: append([]byte(nil), publicKey...), - salt: cloneOrNil(salt), - info: cloneOrNil(info), - params: p, - }, nil -} - -func (e *HybridNISTEncryptor) Encrypt(data []byte) ([]byte, error) { - return hybridNISTWrapDEK(e.params, e.publicKey, data, e.salt, e.info) -} - -func (e *HybridNISTEncryptor) PublicKeyInPemFormat() (string, error) { - return rawToPEM(e.params.pubPEMBlock, e.publicKey, e.params.ecPubSize+e.params.mlkemPubSize) -} - -func (e *HybridNISTEncryptor) Type() SchemeType { return Hybrid } -func (e *HybridNISTEncryptor) KeyType() KeyType { return e.params.keyType } -func (e *HybridNISTEncryptor) EphemeralKey() []byte { return nil } - -func (e *HybridNISTEncryptor) Metadata() (map[string]string, error) { - return make(map[string]string), nil -} - -func NewP256MLKEM768Decryptor(privateKey []byte) (*HybridNISTDecryptor, error) { - return NewSaltedP256MLKEM768Decryptor(privateKey, defaultTDFSalt(), nil) -} - -func NewSaltedP256MLKEM768Decryptor(privateKey, salt, info []byte) (*HybridNISTDecryptor, error) { - return newHybridNISTDecryptor(&p256mlkem768Params, privateKey, salt, info) -} - -func NewP384MLKEM1024Decryptor(privateKey []byte) (*HybridNISTDecryptor, error) { - return NewSaltedP384MLKEM1024Decryptor(privateKey, defaultTDFSalt(), nil) -} - -func NewSaltedP384MLKEM1024Decryptor(privateKey, salt, info []byte) (*HybridNISTDecryptor, error) { - return newHybridNISTDecryptor(&p384mlkem1024Params, privateKey, salt, info) -} - -func newHybridNISTDecryptor(p *hybridNISTParams, privateKey, salt, info []byte) (*HybridNISTDecryptor, error) { - expectedSize := p.ecPrivSize + p.mlkemPrivSize - if len(privateKey) != expectedSize { - return nil, fmt.Errorf("invalid %s private key size: got %d want %d", p.keyType, len(privateKey), expectedSize) - } - return &HybridNISTDecryptor{ - privateKey: append([]byte(nil), privateKey...), - salt: cloneOrNil(salt), - info: cloneOrNil(info), - params: p, - }, nil -} - -func (d *HybridNISTDecryptor) Decrypt(data []byte) ([]byte, error) { - return hybridNISTUnwrapDEK(d.params, d.privateKey, data, d.salt, d.info) -} - +// P256MLKEM768WrapDEK wraps a DEK using the P-256 + ML-KEM-768 hybrid scheme. +// +// Deprecated: Use WrapDEK with HybridSecp256r1MLKEM768Key, or construct via +// FromPublicPEM. func P256MLKEM768WrapDEK(publicKeyRaw, dek []byte) ([]byte, error) { - return hybridNISTWrapDEK(&p256mlkem768Params, publicKeyRaw, dek, defaultTDFSalt(), nil) + return wrapDEKWithKEM(nistHybridKEM{params: &p256mlkem768Params}, publicKeyRaw, dek, defaultTDFSalt(), nil) } +// P256MLKEM768UnwrapDEK unwraps an envelope produced by P256MLKEM768WrapDEK +// using the supplied raw P-256 + ML-KEM-768 private key. This is the binary- +// bytes counterpart to FromPrivatePEM; callers that already hold raw key +// material can use it directly without re-encoding to PEM. func P256MLKEM768UnwrapDEK(privateKeyRaw, wrappedDER []byte) ([]byte, error) { - return hybridNISTUnwrapDEK(&p256mlkem768Params, privateKeyRaw, wrappedDER, defaultTDFSalt(), nil) + return unwrapDEKWithKEM(nistHybridKEM{params: &p256mlkem768Params}, privateKeyRaw, wrappedDER, defaultTDFSalt(), nil) } +// P384MLKEM1024WrapDEK wraps a DEK using the P-384 + ML-KEM-1024 hybrid scheme. +// +// Deprecated: Use WrapDEK with HybridSecp384r1MLKEM1024Key, or construct via +// FromPublicPEM. func P384MLKEM1024WrapDEK(publicKeyRaw, dek []byte) ([]byte, error) { - return hybridNISTWrapDEK(&p384mlkem1024Params, publicKeyRaw, dek, defaultTDFSalt(), nil) + return wrapDEKWithKEM(nistHybridKEM{params: &p384mlkem1024Params}, publicKeyRaw, dek, defaultTDFSalt(), nil) } +// P384MLKEM1024UnwrapDEK unwraps an envelope produced by P384MLKEM1024WrapDEK +// using the supplied raw P-384 + ML-KEM-1024 private key. This is the binary- +// bytes counterpart to FromPrivatePEM; callers that already hold raw key +// material can use it directly without re-encoding to PEM. func P384MLKEM1024UnwrapDEK(privateKeyRaw, wrappedDER []byte) ([]byte, error) { - return hybridNISTUnwrapDEK(&p384mlkem1024Params, privateKeyRaw, wrappedDER, defaultTDFSalt(), nil) -} - -// hybridNISTEncapsulate performs hybrid encapsulation: -// 1. Generates an ephemeral EC key and computes ECDH shared secret -// 2. Encapsulates ML-KEM to produce a post-quantum shared secret -// 3. Combines both secrets (ECDH || ML-KEM) -// 4. Builds hybrid ciphertext (ephemeral EC point || ML-KEM ciphertext) -// -// Returns (combinedSecret, hybridCiphertext) without applying KDF or encryption. -func hybridNISTEncapsulate(p *hybridNISTParams, publicKeyRaw []byte) ([]byte, []byte, error) { - expectedPubSize := p.ecPubSize + p.mlkemPubSize - if len(publicKeyRaw) != expectedPubSize { - return nil, nil, fmt.Errorf("invalid %s public key size: got %d want %d", p.keyType, len(publicKeyRaw), expectedPubSize) - } - - ecPubBytes := publicKeyRaw[:p.ecPubSize] - mlkemPubBytes := publicKeyRaw[p.ecPubSize:] - - // ECDH: generate ephemeral key, compute shared secret - ecPub, err := p.curve.NewPublicKey(ecPubBytes) - if err != nil { - return nil, nil, fmt.Errorf("invalid EC public key: %w", err) - } - ephemeral, err := p.curve.GenerateKey(rand.Reader) - if err != nil { - return nil, nil, fmt.Errorf("ECDH ephemeral key generation failed: %w", err) - } - ecdhSecret, err := ephemeral.ECDH(ecPub) - if err != nil { - return nil, nil, fmt.Errorf("ECDH failed: %w", err) - } - ephemeralPub := ephemeral.PublicKey().Bytes() - - // ML-KEM: encapsulate - var mlkemSecret, mlkemCt []byte - switch p.keyType { //nolint:exhaustive // only NIST hybrid types - case HybridSecp256r1MLKEM768Key: - ek, ekErr := mlkem.NewEncapsulationKey768(mlkemPubBytes) - if ekErr != nil { - return nil, nil, fmt.Errorf("mlkem768 encapsulation key: %w", ekErr) - } - mlkemSecret, mlkemCt = ek.Encapsulate() - case HybridSecp384r1MLKEM1024Key: - ek, ekErr := mlkem.NewEncapsulationKey1024(mlkemPubBytes) - if ekErr != nil { - return nil, nil, fmt.Errorf("mlkem1024 encapsulation key: %w", ekErr) - } - mlkemSecret, mlkemCt = ek.Encapsulate() - default: - return nil, nil, fmt.Errorf("unsupported ML-KEM key type: %s", p.keyType) - } - - // Combine secrets: ECDH || ML-KEM - combinedSecret := make([]byte, 0, len(ecdhSecret)+len(mlkemSecret)) - combinedSecret = append(combinedSecret, ecdhSecret...) - combinedSecret = append(combinedSecret, mlkemSecret...) - - // Build hybrid ciphertext: ephemeral EC point || ML-KEM ciphertext - hybridCt := make([]byte, 0, len(ephemeralPub)+len(mlkemCt)) - hybridCt = append(hybridCt, ephemeralPub...) - hybridCt = append(hybridCt, mlkemCt...) - - return combinedSecret, hybridCt, nil -} - -// P256MLKEM768Encapsulate performs P-256 ECDH + ML-KEM-768 hybrid encapsulation. -func P256MLKEM768Encapsulate(publicKeyRaw []byte) ([]byte, []byte, error) { - return hybridNISTEncapsulate(&p256mlkem768Params, publicKeyRaw) -} - -// P384MLKEM1024Encapsulate performs P-384 ECDH + ML-KEM-1024 hybrid encapsulation. -func P384MLKEM1024Encapsulate(publicKeyRaw []byte) ([]byte, []byte, error) { - return hybridNISTEncapsulate(&p384mlkem1024Params, publicKeyRaw) -} - -func hybridNISTWrapDEK(p *hybridNISTParams, publicKeyRaw, dek, salt, info []byte) ([]byte, error) { - combinedSecret, hybridCt, err := hybridNISTEncapsulate(p, publicKeyRaw) - if err != nil { - return nil, err - } - - // Derive AES-256 wrap key via HKDF - wrapKey, err := deriveHybridNISTWrapKey(combinedSecret, salt, info) - if err != nil { - return nil, err - } - - // AES-GCM encrypt DEK - gcm, err := NewAESGcm(wrapKey) - if err != nil { - return nil, fmt.Errorf("NewAESGcm failed: %w", err) - } - encryptedDEK, err := gcm.Encrypt(dek) - if err != nil { - return nil, fmt.Errorf("AES-GCM encrypt failed: %w", err) - } - - wrappedDER, err := asn1.Marshal(HybridNISTWrappedKey{ - HybridCiphertext: hybridCt, - EncryptedDEK: encryptedDEK, - }) - if err != nil { - return nil, fmt.Errorf("asn1.Marshal failed: %w", err) - } - - return wrappedDER, nil -} - -func hybridNISTUnwrapDEK(p *hybridNISTParams, privateKeyRaw, wrappedDER, salt, info []byte) ([]byte, error) { - expectedPrivSize := p.ecPrivSize + p.mlkemPrivSize - if len(privateKeyRaw) != expectedPrivSize { - return nil, fmt.Errorf("invalid %s private key size: got %d want %d", p.keyType, len(privateKeyRaw), expectedPrivSize) - } - - var wrapped HybridNISTWrappedKey - rest, err := asn1.Unmarshal(wrappedDER, &wrapped) - if err != nil { - return nil, fmt.Errorf("asn1.Unmarshal failed: %w", err) - } - if len(rest) != 0 { - return nil, fmt.Errorf("asn1.Unmarshal left %d trailing bytes", len(rest)) - } - - expectedCtSize := p.ecPubSize + p.mlkemCtSize - if len(wrapped.HybridCiphertext) != expectedCtSize { - return nil, fmt.Errorf("invalid %s ciphertext size: got %d want %d", - p.keyType, len(wrapped.HybridCiphertext), expectedCtSize) - } - - // Split hybrid ciphertext - ephemeralPubBytes := wrapped.HybridCiphertext[:p.ecPubSize] - mlkemCtBytes := wrapped.HybridCiphertext[p.ecPubSize:] - - // Split private key - ecPrivBytes := privateKeyRaw[:p.ecPrivSize] - mlkemPrivBytes := privateKeyRaw[p.ecPrivSize:] - - // ECDH: reconstruct shared secret - ecPriv, err := p.curve.NewPrivateKey(ecPrivBytes) - if err != nil { - return nil, fmt.Errorf("invalid EC private key: %w", err) - } - ephemeralPub, err := p.curve.NewPublicKey(ephemeralPubBytes) - if err != nil { - return nil, fmt.Errorf("invalid ephemeral EC public key: %w", err) - } - ecdhSecret, err := ecPriv.ECDH(ephemeralPub) - if err != nil { - return nil, fmt.Errorf("ECDH failed: %w", err) - } - - // ML-KEM: decapsulate. Implicit rejection (FIPS 203 §6.3) means a wrong-key - // ciphertext yields a pseudorandom shared secret without an error here; - // authentication is enforced by the AES-GCM decrypt below. - var mlkemSecret []byte - switch p.keyType { //nolint:exhaustive // only NIST hybrid types - case HybridSecp256r1MLKEM768Key: - dk, dkErr := mlkem.NewDecapsulationKey768(mlkemPrivBytes) - if dkErr != nil { - return nil, fmt.Errorf("mlkem768 decapsulation key: %w", dkErr) - } - mlkemSecret, err = dk.Decapsulate(mlkemCtBytes) - case HybridSecp384r1MLKEM1024Key: - dk, dkErr := mlkem.NewDecapsulationKey1024(mlkemPrivBytes) - if dkErr != nil { - return nil, fmt.Errorf("mlkem1024 decapsulation key: %w", dkErr) - } - mlkemSecret, err = dk.Decapsulate(mlkemCtBytes) - default: - return nil, fmt.Errorf("unsupported ML-KEM key type: %s", p.keyType) - } - if err != nil { - return nil, fmt.Errorf("ML-KEM decapsulate failed: %w", err) - } - - // Combine secrets: ECDH || ML-KEM - combinedSecret := make([]byte, 0, len(ecdhSecret)+len(mlkemSecret)) - combinedSecret = append(combinedSecret, ecdhSecret...) - combinedSecret = append(combinedSecret, mlkemSecret...) - - // Derive AES-256 wrap key via HKDF - wrapKey, err := deriveHybridNISTWrapKey(combinedSecret, salt, info) - if err != nil { - return nil, err - } - - // AES-GCM decrypt DEK - gcm, err := NewAESGcm(wrapKey) - if err != nil { - return nil, fmt.Errorf("NewAESGcm failed: %w", err) - } - plaintext, err := gcm.Decrypt(wrapped.EncryptedDEK) - if err != nil { - return nil, fmt.Errorf("AES-GCM decrypt failed: %w", err) - } - - return plaintext, nil -} - -func deriveHybridNISTWrapKey(combinedSecret, salt, info []byte) ([]byte, error) { - if len(salt) == 0 { - salt = defaultTDFSalt() - } - - hkdfObj := hkdf.New(sha256.New, combinedSecret, salt, info) - derivedKey := make([]byte, hybridNISTWrapKeySize) - if _, err := io.ReadFull(hkdfObj, derivedKey); err != nil { - return nil, fmt.Errorf("hkdf failure: %w", err) - } - - return derivedKey, nil + return unwrapDEKWithKEM(nistHybridKEM{params: &p384mlkem1024Params}, privateKeyRaw, wrappedDER, defaultTDFSalt(), nil) } diff --git a/lib/ocrypto/hybrid_nist_test.go b/lib/ocrypto/hybrid_nist_test.go index e7c9808aae..2a741644f4 100644 --- a/lib/ocrypto/hybrid_nist_test.go +++ b/lib/ocrypto/hybrid_nist_test.go @@ -115,16 +115,16 @@ func TestP384MLKEM1024WrapUnwrapWrongKeyFails(t *testing.T) { assert.ErrorContains(t, err, "AES-GCM decrypt failed") } -func TestHybridNISTWrappedKeyASN1RoundTrip(t *testing.T) { - original := HybridNISTWrappedKey{ - HybridCiphertext: []byte("hybrid-ciphertext-data"), - EncryptedDEK: []byte("encrypted-dek-data"), +func TestHybridNISTEnvelopeASN1RoundTrip(t *testing.T) { + original := kemEnvelope{ + KEMCiphertext: []byte("hybrid-ciphertext-data"), + EncryptedDEK: []byte("encrypted-dek-data"), } der, err := asn1.Marshal(original) require.NoError(t, err) - var decoded HybridNISTWrappedKey + var decoded kemEnvelope rest, err := asn1.Unmarshal(der, &decoded) require.NoError(t, err) assert.Empty(t, rest) @@ -146,23 +146,18 @@ func TestP256MLKEM768PEMDispatch(t *testing.T) { 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()) + assert.Equal(t, Hybrid, encryptor.Type()) + assert.Equal(t, HybridSecp256r1MLKEM768Key, encryptor.KeyType()) + assert.Nil(t, encryptor.EphemeralKey()) - metadata, err := nistEncryptor.Metadata() + metadata, err := encryptor.Metadata() require.NoError(t, err) assert.Empty(t, metadata) - nistDecryptor, ok := decryptor.(*HybridNISTDecryptor) - require.True(t, ok) - - wrapped, err := nistEncryptor.Encrypt([]byte("dispatch-dek")) + wrapped, err := encryptor.Encrypt([]byte("dispatch-dek")) require.NoError(t, err) - plaintext, err := nistDecryptor.Decrypt(wrapped) + plaintext, err := decryptor.Decrypt(wrapped) require.NoError(t, err) assert.Equal(t, []byte("dispatch-dek"), plaintext) } @@ -182,19 +177,14 @@ func TestP384MLKEM1024PEMDispatch(t *testing.T) { 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) + assert.Equal(t, Hybrid, encryptor.Type()) + assert.Equal(t, HybridSecp384r1MLKEM1024Key, encryptor.KeyType()) + assert.Nil(t, encryptor.EphemeralKey()) - wrapped, err := nistEncryptor.Encrypt([]byte("dispatch-dek-384")) + wrapped, err := encryptor.Encrypt([]byte("dispatch-dek-384")) require.NoError(t, err) - plaintext, err := nistDecryptor.Decrypt(wrapped) + plaintext, err := decryptor.Decrypt(wrapped) require.NoError(t, err) assert.Equal(t, []byte("dispatch-dek-384"), plaintext) } @@ -209,7 +199,7 @@ func TestP256MLKEM768Encapsulate(t *testing.T) { pubKeyRaw, err := P256MLKEM768PubKeyFromPem([]byte(pubKey)) require.NoError(t, err) - combinedSecret, hybridCt, err := P256MLKEM768Encapsulate(pubKeyRaw) + combinedSecret, hybridCt, err := nistHybridKEM{params: &p256mlkem768Params}.encapsulate(pubKeyRaw) require.NoError(t, err) assert.NotEmpty(t, combinedSecret) assert.Len(t, hybridCt, P256MLKEM768CiphertextSize) @@ -225,7 +215,7 @@ func TestP384MLKEM1024Encapsulate(t *testing.T) { pubKeyRaw, err := P384MLKEM1024PubKeyFromPem([]byte(pubKey)) require.NoError(t, err) - combinedSecret, hybridCt, err := P384MLKEM1024Encapsulate(pubKeyRaw) + combinedSecret, hybridCt, err := nistHybridKEM{params: &p384mlkem1024Params}.encapsulate(pubKeyRaw) require.NoError(t, err) assert.NotEmpty(t, combinedSecret) assert.Len(t, hybridCt, P384MLKEM1024CiphertextSize) diff --git a/lib/ocrypto/kem.go b/lib/ocrypto/kem.go new file mode 100644 index 0000000000..b34da9f329 --- /dev/null +++ b/lib/ocrypto/kem.go @@ -0,0 +1,532 @@ +package ocrypto + +import ( + "crypto/mlkem" + "crypto/rand" + "crypto/sha256" + "encoding/asn1" + "encoding/pem" + "fmt" + "io" + + "github.com/cloudflare/circl/kem/xwing" + "golang.org/x/crypto/hkdf" +) + +// kem is the post-quantum KEM contract implemented by ML-KEM, X-Wing, and the +// NIST hybrid PQ/T schemes. Unifying behind this single interface collapses the +// `hybrid-wrapped` and `mlkem-wrapped` wrap/unwrap paths into one envelope and +// one AES-GCM call site; per-scheme key-derivation policy is selected by +// wrapKey below. +type kem interface { + keyType() KeyType + scheme() SchemeType + pubSize() int + privSize() int + ctSize() int + encapsulate(pub []byte) (sharedSecret, ciphertext []byte, err error) + decapsulate(priv, ct []byte) (sharedSecret []byte, err error) + // publicKeyPEM returns the PEM serialization for the given raw public key. + // Each adapter handles its own format. After the planned follow-up moves + // X-Wing and the NIST hybrid keys onto standard SPKI PEM blocks this + // per-adapter hook collapses to a single shared helper. + publicKeyPEM(pub []byte) (string, error) + // wrapKey returns the AES-256 key used to seal the DEK from the + // shared secret produced by encapsulate / decapsulate. + // + // ML-KEM returns the 32-byte Decaps output verbatim (no KDF) so that an + // HSM-backed KAS holding the shared secret as a CKK_AES, non-extractable + // object can perform AES-GCM directly. See FIPS 203 §6.3 / §7.3 and + // adr/decisions/2026-06-16-mlkem-direct-key-wrap.md. + // + // Hybrid PQ/T schemes (X-Wing, NIST EC + ML-KEM) concatenate two + // shared-secret halves and still require HKDF-SHA256 over (salt, info) + // for proper combiner hygiene. + wrapKey(sharedSecret, salt, info []byte) ([]byte, error) +} + +// kemEnvelope is the ASN.1 wire format for every KEM-wrapped DEK across +// `hybrid-wrapped` and `mlkem-wrapped` KAOs. It is byte-identical to the three +// legacy structs (MLKEMWrappedKey, XWingWrappedKey, HybridNISTWrappedKey) it +// replaces — same tags, same field order. +type kemEnvelope struct { + KEMCiphertext []byte `asn1:"tag:0"` + EncryptedDEK []byte `asn1:"tag:1"` +} + +// kemWrapKeySize is the AES-256 wrap key length. Pure ML-KEM uses the +// 32-byte Decaps output directly; hybrid PQ/T schemes derive it via +// HKDF-SHA256 over the combined shared secret. +const kemWrapKeySize = 32 + +// kemRegistry maps the SPKI/PKCS#8 OID published for a KEM scheme to a +// constructor that returns a kem adapter bound to that scheme. ML-KEM is the +// only family with standardized OIDs landed today; the planned hybrid PQ/T +// SPKI follow-up adds X-Wing and the two NIST hybrid OIDs by inserting +// registry entries here. +var kemRegistry = map[string]func() kem{ + OidMLKEM768.String(): func() kem { return mlkemKEM{variant: mlkem768} }, + OidMLKEM1024.String(): func() kem { return mlkemKEM{variant: mlkem1024} }, +} + +// kemByOID returns the kem adapter registered for the supplied OID, or false +// if the OID is not a recognised KEM algorithm. +func kemByOID(oid asn1.ObjectIdentifier) (kem, bool) { + ctor, ok := kemRegistry[oid.String()] + if !ok { + return nil, false + } + return ctor(), true +} + +// kemByKeyType returns the kem adapter for the supplied KeyType, covering both +// pure ML-KEM and hybrid PQ/T schemes. This is the entry point for wrap-side +// dispatch where the caller knows the KAS algorithm but has not yet decoded a +// public key. +func kemByKeyType(kt KeyType) (kem, bool) { + switch kt { //nolint:exhaustive // only handle KEM types; other KeyTypes return false + case MLKEM768Key: + return mlkemKEM{variant: mlkem768}, true + case MLKEM1024Key: + return mlkemKEM{variant: mlkem1024}, true + case HybridXWingKey: + return xwingKEM{}, true + case HybridSecp256r1MLKEM768Key: + return nistHybridKEM{params: &p256mlkem768Params}, true + case HybridSecp384r1MLKEM1024Key: + return nistHybridKEM{params: &p384mlkem1024Params}, true + default: + return nil, false + } +} + +// IsKEMKeyType reports whether the supplied KeyType is one of the KEM schemes +// — pure ML-KEM or hybrid PQ/T — handled by the unified wrap/unwrap path. +func IsKEMKeyType(kt KeyType) bool { + _, ok := kemByKeyType(kt) + return ok +} + +// --- mlkemKEM adapter ------------------------------------------------------- + +type mlkemVariant int + +const ( + mlkem768 mlkemVariant = iota + mlkem1024 +) + +type mlkemKEM struct { + variant mlkemVariant +} + +func (m mlkemKEM) keyType() KeyType { + if m.variant == mlkem1024 { + return MLKEM1024Key + } + return MLKEM768Key +} + +func (mlkemKEM) scheme() SchemeType { return MLKEM } + +func (m mlkemKEM) pubSize() int { + if m.variant == mlkem1024 { + return MLKEM1024PublicKeySize + } + return MLKEM768PublicKeySize +} + +func (m mlkemKEM) privSize() int { + if m.variant == mlkem1024 { + return MLKEM1024PrivateKeySize + } + return MLKEM768PrivateKeySize +} + +func (m mlkemKEM) ctSize() int { + if m.variant == mlkem1024 { + return MLKEM1024CiphertextSize + } + return MLKEM768CiphertextSize +} + +func (m mlkemKEM) encapsulate(pub []byte) ([]byte, []byte, error) { + if len(pub) != m.pubSize() { + return nil, nil, fmt.Errorf("invalid %s public key size: got %d want %d", m.keyType(), len(pub), m.pubSize()) + } + if m.variant == mlkem1024 { + ek, err := mlkem.NewEncapsulationKey1024(pub) + if err != nil { + return nil, nil, fmt.Errorf("mlkem.NewEncapsulationKey1024 failed: %w", err) + } + ss, ct := ek.Encapsulate() + return ss, ct, nil + } + ek, err := mlkem.NewEncapsulationKey768(pub) + if err != nil { + return nil, nil, fmt.Errorf("mlkem.NewEncapsulationKey768 failed: %w", err) + } + ss, ct := ek.Encapsulate() + return ss, ct, nil +} + +func (m mlkemKEM) decapsulate(priv, ct []byte) ([]byte, error) { + if len(priv) != m.privSize() { + return nil, fmt.Errorf("invalid %s private key size: got %d want %d", m.keyType(), len(priv), m.privSize()) + } + if m.variant == mlkem1024 { + dk, err := mlkem.NewDecapsulationKey1024(priv) + if err != nil { + return nil, fmt.Errorf("mlkem.NewDecapsulationKey1024 failed: %w", err) + } + ss, err := dk.Decapsulate(ct) + if err != nil { + return nil, fmt.Errorf("mlkem1024 decapsulate failed: %w", err) + } + return ss, nil + } + dk, err := mlkem.NewDecapsulationKey768(priv) + if err != nil { + return nil, fmt.Errorf("mlkem.NewDecapsulationKey768 failed: %w", err) + } + ss, err := dk.Decapsulate(ct) + if err != nil { + return nil, fmt.Errorf("mlkem768 decapsulate failed: %w", err) + } + return ss, nil +} + +func (m mlkemKEM) publicKeyPEM(pub []byte) (string, error) { + oid := OidMLKEM768 + if m.variant == mlkem1024 { + oid = OidMLKEM1024 + } + der, err := marshalKEMPublicSPKI(oid, pub) + if err != nil { + return "", fmt.Errorf("marshal %s SPKI failed: %w", m.keyType(), err) + } + return string(pem.EncodeToMemory(&pem.Block{Type: pemBlockPublicKey, Bytes: der})), nil +} + +// wrapKey returns the ML-KEM Decaps output directly as the AES-256 wrap key. +// +// FIPS 203 §6.3 / §7.3 specify that ML-KEM Decaps emits a uniformly random +// 32-byte shared secret K that is suitable for direct use as a symmetric key, +// and ML-KEM produces a fresh K per encapsulation by construction. salt and +// info are ignored on purpose so that HSM-backed KAS providers that can only +// materialize the shared secret as a non-extractable CKK_AES object (e.g. +// Thales Luna T-Series 7.15.1 in strict-FIPS mode, which rejects HMAC over +// the Decaps output with CKR_ATTRIBUTE_TYPE_INVALID) can still complete +// AES-GCM unwrap without an HKDF step. +func (mlkemKEM) wrapKey(sharedSecret, _ /*salt*/, _ /*info*/ []byte) ([]byte, error) { + if len(sharedSecret) != kemWrapKeySize { + return nil, fmt.Errorf("invalid ML-KEM shared secret size: got %d want %d", len(sharedSecret), kemWrapKeySize) + } + return append([]byte(nil), sharedSecret...), nil +} + +// --- xwingKEM adapter ------------------------------------------------------- + +type xwingKEM struct{} + +func (xwingKEM) keyType() KeyType { return HybridXWingKey } +func (xwingKEM) scheme() SchemeType { return Hybrid } +func (xwingKEM) pubSize() int { return XWingPublicKeySize } +func (xwingKEM) privSize() int { return XWingPrivateKeySize } +func (xwingKEM) ctSize() int { return XWingCiphertextSize } + +func (xwingKEM) encapsulate(pub []byte) ([]byte, []byte, error) { + if len(pub) != XWingPublicKeySize { + return nil, nil, fmt.Errorf("invalid X-Wing public key size: got %d want %d", len(pub), XWingPublicKeySize) + } + ss, ct, err := xwing.Encapsulate(pub, nil) + if err != nil { + return nil, nil, fmt.Errorf("xwing.Encapsulate failed: %w", err) + } + return ss, ct, nil +} + +func (xwingKEM) decapsulate(priv, ct []byte) ([]byte, error) { + if len(priv) != XWingPrivateKeySize { + return nil, fmt.Errorf("invalid X-Wing private key size: got %d want %d", len(priv), XWingPrivateKeySize) + } + return xwing.Decapsulate(ct, priv), nil +} + +func (xwingKEM) publicKeyPEM(pub []byte) (string, error) { + return rawToPEM(PEMBlockXWingPublicKey, pub, XWingPublicKeySize) +} + +// wrapKey derives a 32-byte AES key from the X-Wing shared secret via +// HKDF-SHA256 over (salt, info). +func (xwingKEM) wrapKey(sharedSecret, salt, info []byte) ([]byte, error) { + return hkdfWrapKey(sharedSecret, salt, info) +} + +// --- nistHybridKEM adapter -------------------------------------------------- + +type nistHybridKEM struct { + params *hybridNISTParams +} + +func (h nistHybridKEM) keyType() KeyType { return h.params.keyType } +func (nistHybridKEM) scheme() SchemeType { return Hybrid } +func (h nistHybridKEM) pubSize() int { return h.params.ecPubSize + h.params.mlkemPubSize } +func (h nistHybridKEM) privSize() int { return h.params.ecPrivSize + h.params.mlkemPrivSize } +func (h nistHybridKEM) ctSize() int { return h.params.ecPubSize + h.params.mlkemCtSize } + +// mlkemAdapter returns the ML-KEM half of this hybrid scheme. +func (h nistHybridKEM) mlkemAdapter() mlkemKEM { + if h.params.mlkemPubSize == MLKEM1024PublicKeySize { + return mlkemKEM{variant: mlkem1024} + } + return mlkemKEM{variant: mlkem768} +} + +func (h nistHybridKEM) encapsulate(pub []byte) ([]byte, []byte, error) { + if len(pub) != h.pubSize() { + return nil, nil, fmt.Errorf("invalid %s public key size: got %d want %d", h.keyType(), len(pub), h.pubSize()) + } + ecPubBytes := pub[:h.params.ecPubSize] + mlkemPubBytes := pub[h.params.ecPubSize:] + + ecPub, err := h.params.curve.NewPublicKey(ecPubBytes) + if err != nil { + return nil, nil, fmt.Errorf("invalid EC public key: %w", err) + } + ephemeral, err := h.params.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() + + mlkemSecret, mlkemCt, err := h.mlkemAdapter().encapsulate(mlkemPubBytes) + if err != nil { + return nil, nil, err + } + + combinedSecret := make([]byte, 0, len(ecdhSecret)+len(mlkemSecret)) + combinedSecret = append(combinedSecret, ecdhSecret...) + combinedSecret = append(combinedSecret, mlkemSecret...) + + hybridCt := make([]byte, 0, len(ephemeralPub)+len(mlkemCt)) + hybridCt = append(hybridCt, ephemeralPub...) + hybridCt = append(hybridCt, mlkemCt...) + + return combinedSecret, hybridCt, nil +} + +func (h nistHybridKEM) decapsulate(priv, ct []byte) ([]byte, error) { + if len(priv) != h.privSize() { + return nil, fmt.Errorf("invalid %s private key size: got %d want %d", h.keyType(), len(priv), h.privSize()) + } + if len(ct) != h.ctSize() { + return nil, fmt.Errorf("invalid %s ciphertext size: got %d want %d", h.keyType(), len(ct), h.ctSize()) + } + + ephemeralPubBytes := ct[:h.params.ecPubSize] + mlkemCtBytes := ct[h.params.ecPubSize:] + ecPrivBytes := priv[:h.params.ecPrivSize] + mlkemPrivBytes := priv[h.params.ecPrivSize:] + + ecPriv, err := h.params.curve.NewPrivateKey(ecPrivBytes) + if err != nil { + return nil, fmt.Errorf("invalid EC private key: %w", err) + } + ephemeralPub, err := h.params.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) + } + + // FIPS 203 §6.3 implicit rejection: a wrong-key ciphertext yields a + // pseudorandom shared secret without an error here; authentication is + // enforced by AES-GCM at the wrap layer. + mlkemSecret, err := h.mlkemAdapter().decapsulate(mlkemPrivBytes, mlkemCtBytes) + if err != nil { + return nil, err + } + + combinedSecret := make([]byte, 0, len(ecdhSecret)+len(mlkemSecret)) + combinedSecret = append(combinedSecret, ecdhSecret...) + combinedSecret = append(combinedSecret, mlkemSecret...) + + return combinedSecret, nil +} + +func (h nistHybridKEM) publicKeyPEM(pub []byte) (string, error) { + return rawToPEM(h.params.pubPEMBlock, pub, h.pubSize()) +} + +// wrapKey derives a 32-byte AES key from the concatenated EC+ML-KEM shared +// secret via HKDF-SHA256 over (salt, info). The KDF here is load-bearing: it +// is the combiner that binds the two halves of the hybrid into a single +// uniformly-random wrap key. +func (nistHybridKEM) wrapKey(sharedSecret, salt, info []byte) ([]byte, error) { + return hkdfWrapKey(sharedSecret, salt, info) +} + +// --- wrap / unwrap ---------------------------------------------------------- + +// wrapDEKWithKEM encapsulates against pub, asks the scheme adapter for an +// AES-256 wrap key (HKDF for hybrid PQ/T, direct shared-secret for pure +// ML-KEM), and emits the kemEnvelope ASN.1 DER blob carrying (KEM +// ciphertext, AES-GCM-encrypted DEK). +func wrapDEKWithKEM(k kem, pub, dek, salt, info []byte) ([]byte, error) { + sharedSecret, ciphertext, err := k.encapsulate(pub) + if err != nil { + return nil, err + } + + wrapKey, err := k.wrapKey(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(kemEnvelope{ + KEMCiphertext: ciphertext, + EncryptedDEK: encryptedDEK, + }) + if err != nil { + return nil, fmt.Errorf("asn1.Marshal failed: %w", err) + } + + return wrappedDER, nil +} + +// unwrapDEKWithKEM parses the kemEnvelope DER blob, decapsulates with priv to +// recover the shared secret, asks the scheme adapter for the matching AES-256 +// wrap key, and AES-GCM decrypts the DEK. +func unwrapDEKWithKEM(k kem, priv, der, salt, info []byte) ([]byte, error) { + var env kemEnvelope + rest, err := asn1.Unmarshal(der, &env) + 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(env.KEMCiphertext) != k.ctSize() { + return nil, fmt.Errorf("invalid %s ciphertext size: got %d want %d", k.keyType(), len(env.KEMCiphertext), k.ctSize()) + } + + sharedSecret, err := k.decapsulate(priv, env.KEMCiphertext) + if err != nil { + return nil, err + } + + wrapKey, err := k.wrapKey(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(env.EncryptedDEK) + if err != nil { + return nil, fmt.Errorf("AES-GCM decrypt failed: %w", err) + } + + return plaintext, nil +} + +// hkdfWrapKey derives the AES-256 wrap key used by the hybrid PQ/T KEM +// schemes (X-Wing, NIST EC + ML-KEM). Pure ML-KEM uses the shared secret +// directly and does not call this helper. +func hkdfWrapKey(sharedSecret, salt, info []byte) ([]byte, error) { + if len(salt) == 0 { + salt = defaultTDFSalt() + } + hkdfObj := hkdf.New(sha256.New, sharedSecret, salt, info) + derivedKey := make([]byte, kemWrapKeySize) + if _, err := io.ReadFull(hkdfObj, derivedKey); err != nil { + return nil, fmt.Errorf("hkdf failure: %w", err) + } + return derivedKey, nil +} + +// --- unified encryptor / decryptor ------------------------------------------ + +// kemEncryptor satisfies PublicKeyEncryptor for every KEM family. It replaces +// the per-variant MLKEMEncryptor*, XWingEncryptor, and HybridNISTEncryptor +// types behind the FromPublicPEM factory. +type kemEncryptor struct { + k kem + publicKey []byte + salt []byte + info []byte +} + +func newKEMEncryptor(k kem, publicKey, salt, info []byte) (*kemEncryptor, error) { + if len(publicKey) != k.pubSize() { + return nil, fmt.Errorf("invalid %s public key size: got %d want %d", k.keyType(), len(publicKey), k.pubSize()) + } + return &kemEncryptor{ + k: k, + publicKey: append([]byte(nil), publicKey...), + salt: cloneOrNil(salt), + info: cloneOrNil(info), + }, nil +} + +func (e *kemEncryptor) Encrypt(data []byte) ([]byte, error) { + return wrapDEKWithKEM(e.k, e.publicKey, data, e.salt, e.info) +} + +func (e *kemEncryptor) PublicKeyInPemFormat() (string, error) { + return e.k.publicKeyPEM(e.publicKey) +} + +func (e *kemEncryptor) Type() SchemeType { return e.k.scheme() } +func (e *kemEncryptor) KeyType() KeyType { return e.k.keyType() } +func (e *kemEncryptor) EphemeralKey() []byte { return nil } + +func (e *kemEncryptor) Metadata() (map[string]string, error) { + return make(map[string]string), nil +} + +// kemDecryptor satisfies PrivateKeyDecryptor for every KEM family. It replaces +// the per-variant MLKEMDecryptor*, XWingDecryptor, and HybridNISTDecryptor +// types behind the FromPrivatePEM factory. +type kemDecryptor struct { + k kem + privateKey []byte + salt []byte + info []byte +} + +func newKEMDecryptor(k kem, privateKey, salt, info []byte) (*kemDecryptor, error) { + if len(privateKey) != k.privSize() { + return nil, fmt.Errorf("invalid %s private key size: got %d want %d", k.keyType(), len(privateKey), k.privSize()) + } + return &kemDecryptor{ + k: k, + privateKey: append([]byte(nil), privateKey...), + salt: cloneOrNil(salt), + info: cloneOrNil(info), + }, nil +} + +func (d *kemDecryptor) Decrypt(data []byte) ([]byte, error) { + return unwrapDEKWithKEM(d.k, d.privateKey, data, d.salt, d.info) +} diff --git a/lib/ocrypto/mlkem.go b/lib/ocrypto/mlkem.go new file mode 100644 index 0000000000..436815d059 --- /dev/null +++ b/lib/ocrypto/mlkem.go @@ -0,0 +1,213 @@ +package ocrypto + +import ( + "bytes" + "encoding/asn1" + "encoding/pem" + "errors" + "fmt" +) + +// PEM block types defined by RFC 7468 for SPKI / PKCS#8 envelopes. +const ( + pemBlockPublicKey = "PUBLIC KEY" + pemBlockPrivateKey = "PRIVATE KEY" +) + +// errNotKEM is returned by the generic SPKI / PKCS#8 KEM parsers when the +// supplied DER blob is not a recognised KEM algorithm, signalling the caller +// to fall through to other algorithm parsers. +var errNotKEM = errors.New("not a recognised KEM key") + +const ( + MLKEM768PublicKeySize = 1184 // mlkem768 encapsulation key + MLKEM768PrivateKeySize = 64 // mlkem768 seed (d || z) + MLKEM768CiphertextSize = 1088 // mlkem768 ciphertext + MLKEM1024PublicKeySize = 1568 // mlkem1024 encapsulation key + MLKEM1024PrivateKeySize = 64 // mlkem1024 seed (d || z) + MLKEM1024CiphertextSize = 1568 // mlkem1024 ciphertext +) + +// NIST-assigned OIDs for ML-KEM (FIPS 203). +var ( + OidMLKEM768 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 4, 2} + OidMLKEM1024 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 4, 3} +) + +type kemAlgorithmIdentifier struct { + Algorithm asn1.ObjectIdentifier +} + +type kemSPKI struct { + Algorithm kemAlgorithmIdentifier + PublicKey asn1.BitString +} + +// kemPKCS8 mirrors RFC 5958 OneAsymmetricKey v1. +type kemPKCS8 struct { + Version int + Algorithm kemAlgorithmIdentifier + PrivateKey []byte +} + +const bitsPerByte = 8 + +// marshalKEMPublicSPKI encodes a raw KEM encapsulation key as RFC 5280 +// SubjectPublicKeyInfo using the supplied algorithm OID. +func marshalKEMPublicSPKI(oid asn1.ObjectIdentifier, rawKey []byte) ([]byte, error) { + return asn1.Marshal(kemSPKI{ + Algorithm: kemAlgorithmIdentifier{Algorithm: oid}, + PublicKey: asn1.BitString{Bytes: rawKey, BitLength: len(rawKey) * bitsPerByte}, + }) +} + +// marshalKEMPrivatePKCS8 encodes a raw KEM seed (or private key) as RFC 5958 +// OneAsymmetricKey, with the inner KEM-PrivateKey CHOICE selected as [0] +// IMPLICIT OCTET STRING. +func marshalKEMPrivatePKCS8(oid asn1.ObjectIdentifier, rawSeedOrKey []byte) ([]byte, error) { + inner, err := asn1.MarshalWithParams(rawSeedOrKey, "tag:0,implicit") + if err != nil { + return nil, fmt.Errorf("asn1.MarshalWithParams seed failed: %w", err) + } + return asn1.Marshal(kemPKCS8{ + Version: 0, + Algorithm: kemAlgorithmIdentifier{Algorithm: oid}, + PrivateKey: inner, + }) +} + +// ParseKEMPublicSPKI returns the OID and raw encapsulation key bytes from any +// SPKI DER blob whose AlgorithmIdentifier has no parameters. If the blob is +// not a well-formed parameter-less SPKI structure the sentinel errNotKEM is +// returned so the caller can fall through to other parsers. +func ParseKEMPublicSPKI(der []byte) (asn1.ObjectIdentifier, []byte, error) { + var s kemSPKI + rest, err := asn1.Unmarshal(der, &s) + if err != nil || len(rest) != 0 { + return nil, nil, errNotKEM + } + if s.PublicKey.BitLength%bitsPerByte != 0 { + return nil, nil, errors.New("KEM SPKI bit string is not byte-aligned") + } + return s.Algorithm.Algorithm, s.PublicKey.RightAlign(), nil +} + +// ParseMLKEMPublicSPKI returns the OID and raw encapsulation key bytes from an +// SPKI DER blob if the algorithm is ML-KEM-768 or ML-KEM-1024. +// +// Deprecated: Use ParseKEMPublicSPKI and verify the returned OID against the +// expected ML-KEM variant. +func ParseMLKEMPublicSPKI(der []byte) (asn1.ObjectIdentifier, []byte, error) { + oid, raw, err := ParseKEMPublicSPKI(der) + if err != nil { + return nil, nil, err + } + if !oid.Equal(OidMLKEM768) && !oid.Equal(OidMLKEM1024) { + return nil, nil, errNotKEM + } + return oid, raw, nil +} + +// parseKEMPrivatePKCS8 returns the OID and raw seed bytes from any PKCS#8 DER +// blob whose AlgorithmIdentifier matches a registered KEM scheme and whose +// inner private key is encoded as [0] IMPLICIT OCTET STRING. The sentinel +// errNotKEM is returned for any non-KEM PKCS#8 blob so the caller can fall +// through to other parsers. +func parseKEMPrivatePKCS8(der []byte) (asn1.ObjectIdentifier, []byte, error) { + var p kemPKCS8 + rest, err := asn1.Unmarshal(der, &p) + if err != nil || len(rest) != 0 { + return nil, nil, errNotKEM + } + if _, ok := kemRegistry[p.Algorithm.Algorithm.String()]; !ok { + return nil, nil, errNotKEM + } + + var innerSeed []byte + innerRest, err := asn1.UnmarshalWithParams(p.PrivateKey, &innerSeed, "tag:0,implicit") + if err != nil || len(innerRest) != 0 { + return nil, nil, fmt.Errorf("KEM PKCS#8 inner seed parse failed: %w", err) + } + return p.Algorithm.Algorithm, innerSeed, nil +} + +// normalizeMLKEMPublicKey detects the input format and returns raw key bytes. +// Accepts: raw key (1184/1568 bytes), SPKI DER (1206/1590 bytes), or PEM-wrapped SPKI. +func normalizeMLKEMPublicKey(input []byte, expectedRawSize int, expectedOID asn1.ObjectIdentifier) ([]byte, error) { + if len(input) == expectedRawSize { + return input, nil + } + + if bytes.HasPrefix(input, []byte("-----BEGIN")) { + block, _ := pem.Decode(input) + if block == nil { + return nil, errors.New("failed to decode PEM block") + } + if block.Type != pemBlockPublicKey { + return nil, fmt.Errorf("expected %s PEM block, got %s", pemBlockPublicKey, block.Type) + } + input = block.Bytes + } + + oid, rawKey, err := ParseMLKEMPublicSPKI(input) + if err != nil { + if errors.Is(err, errNotKEM) { + return nil, errors.New("not an ML-KEM key in SPKI format") + } + return nil, fmt.Errorf("failed to parse SPKI: %w", err) + } + + if !oid.Equal(expectedOID) { + return nil, fmt.Errorf("OID mismatch: expected %v, got %v", expectedOID, oid) + } + + if len(rawKey) != expectedRawSize { + return nil, fmt.Errorf("extracted key has wrong size: got %d want %d", len(rawKey), expectedRawSize) + } + + return rawKey, nil +} + +// MLKEM768WrapDEK encapsulates against an ML-KEM-768 public key (raw, SPKI +// DER, or PEM) and returns the ASN.1 DER envelope carrying the KEM ciphertext +// and AES-GCM-wrapped DEK. The ML-KEM Decaps shared secret is used directly +// as the AES-256 wrap key (no HKDF); see adr/decisions/2026-06-16-mlkem-direct-key-wrap.md. +// +// Deprecated: Use WrapDEK with MLKEM768Key, or construct via FromPublicPEM. +func MLKEM768WrapDEK(publicKey, dek []byte) ([]byte, error) { + rawKey, err := normalizeMLKEMPublicKey(publicKey, MLKEM768PublicKeySize, OidMLKEM768) + if err != nil { + return nil, fmt.Errorf("invalid ML-KEM-768 public key: %w", err) + } + return wrapDEKWithKEM(mlkemKEM{variant: mlkem768}, rawKey, dek, nil, nil) +} + +// MLKEM768UnwrapDEK decapsulates the envelope produced by MLKEM768WrapDEK +// using the supplied raw ML-KEM-768 seed. This is the binary-bytes counterpart +// to FromPrivatePEM; callers that already hold a raw seed can use it directly +// without re-encoding to PKCS#8 PEM. +func MLKEM768UnwrapDEK(privateKeyRaw, wrappedDER []byte) ([]byte, error) { + return unwrapDEKWithKEM(mlkemKEM{variant: mlkem768}, privateKeyRaw, wrappedDER, nil, nil) +} + +// MLKEM1024WrapDEK encapsulates against an ML-KEM-1024 public key (raw, SPKI +// DER, or PEM) and returns the ASN.1 DER envelope carrying the KEM ciphertext +// and AES-GCM-wrapped DEK. The ML-KEM Decaps shared secret is used directly +// as the AES-256 wrap key (no HKDF); see adr/decisions/2026-06-16-mlkem-direct-key-wrap.md. +// +// Deprecated: Use WrapDEK with MLKEM1024Key, or construct via FromPublicPEM. +func MLKEM1024WrapDEK(publicKey, dek []byte) ([]byte, error) { + rawKey, err := normalizeMLKEMPublicKey(publicKey, MLKEM1024PublicKeySize, OidMLKEM1024) + if err != nil { + return nil, fmt.Errorf("invalid ML-KEM-1024 public key: %w", err) + } + return wrapDEKWithKEM(mlkemKEM{variant: mlkem1024}, rawKey, dek, nil, nil) +} + +// MLKEM1024UnwrapDEK decapsulates the envelope produced by MLKEM1024WrapDEK +// using the supplied raw ML-KEM-1024 seed. This is the binary-bytes counterpart +// to FromPrivatePEM; callers that already hold a raw seed can use it directly +// without re-encoding to PKCS#8 PEM. +func MLKEM1024UnwrapDEK(privateKeyRaw, wrappedDER []byte) ([]byte, error) { + return unwrapDEKWithKEM(mlkemKEM{variant: mlkem1024}, privateKeyRaw, wrappedDER, nil, nil) +} diff --git a/lib/ocrypto/mlkem_format_test.go b/lib/ocrypto/mlkem_format_test.go new file mode 100644 index 0000000000..0e954eee87 --- /dev/null +++ b/lib/ocrypto/mlkem_format_test.go @@ -0,0 +1,124 @@ +package ocrypto + +import ( + "encoding/pem" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestMLKEM768WrapDEKFormats verifies that MLKEM768WrapDEK accepts raw, SPKI DER, and PEM formats +func TestMLKEM768WrapDEKFormats(t *testing.T) { + keyPair, err := NewMLKEMKeyPair() + require.NoError(t, err) + + dek := []byte("0123456789abcdef0123456789abcdef") + + // Test 1: Raw key (1184 bytes) + rawKey := keyPair.PrivateKey.EncapsulationKey().Bytes() + require.Len(t, rawKey, MLKEM768PublicKeySize) + + wrappedFromRaw, err := MLKEM768WrapDEK(rawKey, dek) + require.NoError(t, err, "Should accept raw key") + + // Test 2: SPKI DER (1206 bytes) + spkiDER, err := marshalKEMPublicSPKI(OidMLKEM768, rawKey) + require.NoError(t, err) + require.Greater(t, len(spkiDER), len(rawKey), "SPKI DER should be larger than raw key") + + wrappedFromSPKI, err := MLKEM768WrapDEK(spkiDER, dek) + require.NoError(t, err, "Should accept SPKI DER") + + // Test 3: PEM-wrapped SPKI (~1686 bytes) + pemBytes := pem.EncodeToMemory(&pem.Block{Type: pemBlockPublicKey, Bytes: spkiDER}) + require.Greater(t, len(pemBytes), len(spkiDER), "PEM should be larger than DER") + + wrappedFromPEM, err := MLKEM768WrapDEK(pemBytes, dek) + require.NoError(t, err, "Should accept PEM-wrapped SPKI") + + // Verify we can unwrap all three (ML-KEM uses randomness, so wrapped results differ each time) + privateKeyBytes := keyPair.PrivateKey.Bytes() + + plaintext1, err := MLKEM768UnwrapDEK(privateKeyBytes, wrappedFromRaw) + require.NoError(t, err, "Should unwrap from raw key wrapping") + assert.Equal(t, dek, plaintext1) + + plaintext2, err := MLKEM768UnwrapDEK(privateKeyBytes, wrappedFromSPKI) + require.NoError(t, err, "Should unwrap from SPKI DER wrapping") + assert.Equal(t, dek, plaintext2) + + plaintext3, err := MLKEM768UnwrapDEK(privateKeyBytes, wrappedFromPEM) + require.NoError(t, err, "Should unwrap from PEM wrapping") + assert.Equal(t, dek, plaintext3) +} + +// TestMLKEM1024WrapDEKFormats verifies that MLKEM1024WrapDEK accepts raw, SPKI DER, and PEM formats +func TestMLKEM1024WrapDEKFormats(t *testing.T) { + keyPair, err := NewMLKEM1024KeyPair() + require.NoError(t, err) + + dek := []byte("0123456789abcdef0123456789abcdef") + + // Test 1: Raw key (1568 bytes) + rawKey := keyPair.PrivateKey.EncapsulationKey().Bytes() + require.Len(t, rawKey, MLKEM1024PublicKeySize) + + wrappedFromRaw, err := MLKEM1024WrapDEK(rawKey, dek) + require.NoError(t, err, "Should accept raw key") + + // Test 2: SPKI DER (1590 bytes) + spkiDER, err := marshalKEMPublicSPKI(OidMLKEM1024, rawKey) + require.NoError(t, err) + require.Greater(t, len(spkiDER), len(rawKey), "SPKI DER should be larger than raw key") + + wrappedFromSPKI, err := MLKEM1024WrapDEK(spkiDER, dek) + require.NoError(t, err, "Should accept SPKI DER") + + // Test 3: PEM-wrapped SPKI (~2206 bytes) + pemBytes := pem.EncodeToMemory(&pem.Block{Type: pemBlockPublicKey, Bytes: spkiDER}) + require.Greater(t, len(pemBytes), len(spkiDER), "PEM should be larger than DER") + + wrappedFromPEM, err := MLKEM1024WrapDEK(pemBytes, dek) + require.NoError(t, err, "Should accept PEM-wrapped SPKI") + + // Verify we can unwrap all three (ML-KEM uses randomness, so wrapped results differ each time) + privateKeyBytes := keyPair.PrivateKey.Bytes() + + plaintext1, err := MLKEM1024UnwrapDEK(privateKeyBytes, wrappedFromRaw) + require.NoError(t, err, "Should unwrap from raw key wrapping") + assert.Equal(t, dek, plaintext1) + + plaintext2, err := MLKEM1024UnwrapDEK(privateKeyBytes, wrappedFromSPKI) + require.NoError(t, err, "Should unwrap from SPKI DER wrapping") + assert.Equal(t, dek, plaintext2) + + plaintext3, err := MLKEM1024UnwrapDEK(privateKeyBytes, wrappedFromPEM) + require.NoError(t, err, "Should unwrap from PEM wrapping") + assert.Equal(t, dek, plaintext3) +} + +// TestMLKEM768WrapDEKInvalidFormats verifies error handling for invalid inputs +func TestMLKEM768WrapDEKInvalidFormats(t *testing.T) { + dek := []byte("0123456789abcdef0123456789abcdef") + + // Wrong size raw key + wrongSizeRaw := make([]byte, 100) + _, err := MLKEM768WrapDEK(wrongSizeRaw, dek) + require.Error(t, err, "Should reject wrong-size raw key") + + // Wrong OID in SPKI + keyPair1024, err := NewMLKEM1024KeyPair() + require.NoError(t, err) + spki1024, err := marshalKEMPublicSPKI(OidMLKEM1024, keyPair1024.PrivateKey.EncapsulationKey().Bytes()) + require.NoError(t, err) + + _, err = MLKEM768WrapDEK(spki1024, dek) + require.Error(t, err, "Should reject ML-KEM-1024 SPKI when expecting ML-KEM-768") + assert.Contains(t, err.Error(), "OID mismatch") + + // Invalid PEM + invalidPEM := []byte("-----BEGIN PUBLIC KEY-----\ninvalid base64\n-----END PUBLIC KEY-----") + _, err = MLKEM768WrapDEK(invalidPEM, dek) + require.Error(t, err, "Should reject invalid PEM") +} diff --git a/lib/ocrypto/mlkem_test.go b/lib/ocrypto/mlkem_test.go new file mode 100644 index 0000000000..f70c245c58 --- /dev/null +++ b/lib/ocrypto/mlkem_test.go @@ -0,0 +1,355 @@ +package ocrypto + +import ( + "encoding/asn1" + "encoding/pem" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestMLKEM768WrapUnwrapRoundTrip(t *testing.T) { + keyPair, err := NewMLKEMKeyPair() + require.NoError(t, err) + + publicKeyBytes := keyPair.PrivateKey.EncapsulationKey().Bytes() + privateKeyBytes := keyPair.PrivateKey.Bytes() + + dek := []byte("0123456789abcdef0123456789abcdef") + wrapped, err := MLKEM768WrapDEK(publicKeyBytes, dek) + require.NoError(t, err) + + plaintext, err := MLKEM768UnwrapDEK(privateKeyBytes, wrapped) + require.NoError(t, err) + assert.Equal(t, dek, plaintext) +} + +func TestMLKEM1024WrapUnwrapRoundTrip(t *testing.T) { + keyPair, err := NewMLKEM1024KeyPair() + require.NoError(t, err) + + publicKeyBytes := keyPair.PrivateKey.EncapsulationKey().Bytes() + privateKeyBytes := keyPair.PrivateKey.Bytes() + + dek := []byte("0123456789abcdef0123456789abcdef") + wrapped, err := MLKEM1024WrapDEK(publicKeyBytes, dek) + require.NoError(t, err) + + plaintext, err := MLKEM1024UnwrapDEK(privateKeyBytes, wrapped) + require.NoError(t, err) + assert.Equal(t, dek, plaintext) +} + +func TestMLKEM768WrapUnwrapWrongKeyFails(t *testing.T) { + keyPair1, err := NewMLKEMKeyPair() + require.NoError(t, err) + keyPair2, err := NewMLKEMKeyPair() + require.NoError(t, err) + + publicKey1 := keyPair1.PrivateKey.EncapsulationKey().Bytes() + privateKey2 := keyPair2.PrivateKey.Bytes() + + wrapped, err := MLKEM768WrapDEK(publicKey1, []byte("top secret dek")) + require.NoError(t, err) + + _, err = MLKEM768UnwrapDEK(privateKey2, wrapped) + require.Error(t, err) + assert.Contains(t, err.Error(), "AES-GCM decrypt failed") +} + +func TestMLKEM1024WrapUnwrapWrongKeyFails(t *testing.T) { + keyPair1, err := NewMLKEM1024KeyPair() + require.NoError(t, err) + keyPair2, err := NewMLKEM1024KeyPair() + require.NoError(t, err) + + publicKey1 := keyPair1.PrivateKey.EncapsulationKey().Bytes() + privateKey2 := keyPair2.PrivateKey.Bytes() + + wrapped, err := MLKEM1024WrapDEK(publicKey1, []byte("top secret dek")) + require.NoError(t, err) + + _, err = MLKEM1024UnwrapDEK(privateKey2, wrapped) + require.Error(t, err) + assert.Contains(t, err.Error(), "AES-GCM decrypt failed") +} + +func TestKEMEnvelopeASN1RoundTrip(t *testing.T) { + original := kemEnvelope{ + KEMCiphertext: []byte("ciphertext"), + EncryptedDEK: []byte("encrypted-dek"), + } + + der, err := asn1.Marshal(original) + require.NoError(t, err) + + var decoded kemEnvelope + rest, err := asn1.Unmarshal(der, &decoded) + require.NoError(t, err) + assert.Empty(t, rest) + assert.Equal(t, original, decoded) +} + +func TestMLKEM768CiphertextSizeValidation(t *testing.T) { + keyPair, err := NewMLKEMKeyPair() + require.NoError(t, err) + + privateKeyBytes := keyPair.PrivateKey.Bytes() + + invalidWrapped := kemEnvelope{ + KEMCiphertext: []byte("too-short"), + EncryptedDEK: []byte("encrypted-dek"), + } + + der, err := asn1.Marshal(invalidWrapped) + require.NoError(t, err) + + _, err = MLKEM768UnwrapDEK(privateKeyBytes, der) + require.Error(t, err) + assert.Contains(t, err.Error(), "ciphertext size") +} + +func TestMLKEM1024CiphertextSizeValidation(t *testing.T) { + keyPair, err := NewMLKEM1024KeyPair() + require.NoError(t, err) + + privateKeyBytes := keyPair.PrivateKey.Bytes() + + invalidWrapped := kemEnvelope{ + KEMCiphertext: []byte("too-short"), + EncryptedDEK: []byte("encrypted-dek"), + } + + der, err := asn1.Marshal(invalidWrapped) + require.NoError(t, err) + + _, err = MLKEM1024UnwrapDEK(privateKeyBytes, der) + require.Error(t, err) + assert.Contains(t, err.Error(), "ciphertext size") +} + +// TestMLKEMSaltInfoIgnored verifies that salt/info passed to the ML-KEM +// encryptor/decryptor are ignored: an envelope produced with one (salt, info) +// pair must unwrap correctly under a different (salt, info) pair, because pure +// ML-KEM uses the Decaps shared secret directly as the AES-GCM wrap key. +func TestMLKEMSaltInfoIgnored(t *testing.T) { + keyPair, err := NewMLKEMKeyPair() + require.NoError(t, err) + + publicKeyBytes := keyPair.PrivateKey.EncapsulationKey().Bytes() + privateKeyBytes := keyPair.PrivateKey.Bytes() + + encryptor, err := newKEMEncryptor(mlkemKEM{variant: mlkem768}, publicKeyBytes, []byte("salt-A"), []byte("info-A")) + require.NoError(t, err) + + // Decrypt with deliberately different salt/info; for ML-KEM both must be no-ops. + decryptor, err := newKEMDecryptor(mlkemKEM{variant: mlkem768}, privateKeyBytes, []byte("salt-B"), []byte("info-B")) + require.NoError(t, err) + + dek := []byte("test-dek-value-123456") + wrapped, err := encryptor.Encrypt(dek) + require.NoError(t, err) + + plaintext, err := decryptor.Decrypt(wrapped) + require.NoError(t, err) + assert.Equal(t, dek, plaintext) + + // Also decrypt with nil salt/info to make the contract explicit. + bareDecryptor, err := newKEMDecryptor(mlkemKEM{variant: mlkem768}, privateKeyBytes, nil, nil) + require.NoError(t, err) + plaintextBare, err := bareDecryptor.Decrypt(wrapped) + require.NoError(t, err) + assert.Equal(t, dek, plaintextBare) +} + +// TestMLKEMSharedSecretIsAESWrapKey verifies that the AES-GCM-encrypted DEK +// inside an ML-KEM envelope can be opened by AES-256-GCM using the raw 32-byte +// shared secret produced by Decaps — i.e. there is no KDF between Decaps and +// the AES-GCM unwrap key. This is the load-bearing assertion for HSM-backed +// KAS providers that can only materialize the Decaps output as a non- +// extractable CKK_AES object. +func TestMLKEMSharedSecretIsAESWrapKey(t *testing.T) { + t.Run("MLKEM768", func(t *testing.T) { + assertSharedSecretIsAESWrapKey(t, mlkemKEM{variant: mlkem768}, MLKEM768CiphertextSize) + }) + t.Run("MLKEM1024", func(t *testing.T) { + assertSharedSecretIsAESWrapKey(t, mlkemKEM{variant: mlkem1024}, MLKEM1024CiphertextSize) + }) +} + +func assertSharedSecretIsAESWrapKey(t *testing.T, k mlkemKEM, expectedCtSize int) { + t.Helper() + + var ( + pubBytes []byte + privBytes []byte + ) + if k.variant == mlkem1024 { + kp, err := NewMLKEM1024KeyPair() + require.NoError(t, err) + pubBytes = kp.PrivateKey.EncapsulationKey().Bytes() + privBytes = kp.PrivateKey.Bytes() + } else { + kp, err := NewMLKEMKeyPair() + require.NoError(t, err) + pubBytes = kp.PrivateKey.EncapsulationKey().Bytes() + privBytes = kp.PrivateKey.Bytes() + } + + dek := []byte("0123456789abcdef0123456789abcdef") // 32-byte DEK + wrappedDER, err := wrapDEKWithKEM(k, pubBytes, dek, nil, nil) + require.NoError(t, err) + + // Parse the envelope to pull out the KEM ciphertext and the + // AES-GCM-wrapped DEK independently of unwrapDEKWithKEM. + var env kemEnvelope + rest, err := asn1.Unmarshal(wrappedDER, &env) + require.NoError(t, err) + require.Empty(t, rest) + require.Len(t, env.KEMCiphertext, expectedCtSize) + + // Reproduce the wrap key the way an HSM-backed KAS would: Decaps then + // straight into AES-256-GCM, no KDF. + sharedSecret, err := k.decapsulate(privBytes, env.KEMCiphertext) + require.NoError(t, err) + require.Len(t, sharedSecret, kemWrapKeySize, "FIPS 203 §6.3/§7.3 mandates a 32-byte shared secret") + + gcm, err := NewAESGcm(sharedSecret) + require.NoError(t, err) + plaintext, err := gcm.Decrypt(env.EncryptedDEK) + require.NoError(t, err) + assert.Equal(t, dek, plaintext, "AES-GCM with sharedSecret-as-key must recover the DEK") + + // Also verify the symmetric direction: an AES-GCM seal under the shared + // secret must be openable by unwrapDEKWithKEM-style code, i.e. the wrap + // key on both sides is exactly the Decaps output. + sealed, err := gcm.Encrypt(dek) + require.NoError(t, err) + roundTrip, err := gcm.Decrypt(sealed) + require.NoError(t, err) + assert.Equal(t, dek, roundTrip) +} + +func TestMLKEMEncryptorImplementsInterface(t *testing.T) { + keyPair, err := NewMLKEMKeyPair() + require.NoError(t, err) + + publicKeyBytes := keyPair.PrivateKey.EncapsulationKey().Bytes() + + encryptor, err := newKEMEncryptor(mlkemKEM{variant: mlkem768}, publicKeyBytes, nil, nil) + require.NoError(t, err) + + assert.Equal(t, MLKEM, encryptor.Type()) + assert.Equal(t, MLKEM768Key, encryptor.KeyType()) + assert.Nil(t, encryptor.EphemeralKey()) + + metadata, err := encryptor.Metadata() + require.NoError(t, err) + assert.Empty(t, metadata) +} + +func TestMLKEM768Encapsulate(t *testing.T) { + keyPair, err := NewMLKEMKeyPair() + require.NoError(t, err) + + publicKeyBytes := keyPair.PrivateKey.EncapsulationKey().Bytes() + + sharedSecret, ciphertext, err := mlkemKEM{variant: mlkem768}.encapsulate(publicKeyBytes) + require.NoError(t, err) + assert.Len(t, sharedSecret, 32) + assert.Len(t, ciphertext, MLKEM768CiphertextSize) +} + +func TestMLKEM1024Encapsulate(t *testing.T) { + keyPair, err := NewMLKEM1024KeyPair() + require.NoError(t, err) + + publicKeyBytes := keyPair.PrivateKey.EncapsulationKey().Bytes() + + sharedSecret, ciphertext, err := mlkemKEM{variant: mlkem1024}.encapsulate(publicKeyBytes) + require.NoError(t, err) + assert.Len(t, sharedSecret, 32) + assert.Len(t, ciphertext, MLKEM1024CiphertextSize) +} + +func TestMLKEM768EncapsulateInvalidKeySize(t *testing.T) { + _, _, err := mlkemKEM{variant: mlkem768}.encapsulate([]byte("too-short")) + require.Error(t, err) + assert.Contains(t, err.Error(), "public key size") +} + +func TestMLKEM1024EncapsulateInvalidKeySize(t *testing.T) { + _, _, err := mlkemKEM{variant: mlkem1024}.encapsulate([]byte("too-short")) + require.Error(t, err) + assert.Contains(t, err.Error(), "public key size") +} + +func TestMLKEM768PEMRoundTrip(t *testing.T) { + keyPair, err := NewMLKEMKeyPair() + require.NoError(t, err) + + pubPEM, err := keyPair.PublicKeyInPemFormat() + require.NoError(t, err) + assert.True(t, strings.HasPrefix(pubPEM, "-----BEGIN PUBLIC KEY-----")) + pubBlock, _ := pem.Decode([]byte(pubPEM)) + require.NotNil(t, pubBlock) + assert.Equal(t, "PUBLIC KEY", pubBlock.Type) + + privPEM, err := keyPair.PrivateKeyInPemFormat() + require.NoError(t, err) + assert.True(t, strings.HasPrefix(privPEM, "-----BEGIN PRIVATE KEY-----")) + privBlock, _ := pem.Decode([]byte(privPEM)) + require.NotNil(t, privBlock) + assert.Equal(t, "PRIVATE KEY", privBlock.Type) + + enc, err := FromPublicPEM(pubPEM) + require.NoError(t, err) + assert.Equal(t, MLKEM, enc.Type()) + assert.Equal(t, MLKEM768Key, enc.KeyType()) + + dek := []byte("ml-kem-768 round-trip data") + wrapped, err := enc.Encrypt(dek) + require.NoError(t, err) + + dec, err := FromPrivatePEM(privPEM) + require.NoError(t, err) + plaintext, err := dec.Decrypt(wrapped) + require.NoError(t, err) + assert.Equal(t, dek, plaintext) +} + +func TestMLKEM1024PEMRoundTrip(t *testing.T) { + keyPair, err := NewMLKEM1024KeyPair() + require.NoError(t, err) + + pubPEM, err := keyPair.PublicKeyInPemFormat() + require.NoError(t, err) + assert.True(t, strings.HasPrefix(pubPEM, "-----BEGIN PUBLIC KEY-----")) + pubBlock, _ := pem.Decode([]byte(pubPEM)) + require.NotNil(t, pubBlock) + assert.Equal(t, "PUBLIC KEY", pubBlock.Type) + + privPEM, err := keyPair.PrivateKeyInPemFormat() + require.NoError(t, err) + assert.True(t, strings.HasPrefix(privPEM, "-----BEGIN PRIVATE KEY-----")) + privBlock, _ := pem.Decode([]byte(privPEM)) + require.NotNil(t, privBlock) + assert.Equal(t, "PRIVATE KEY", privBlock.Type) + + enc, err := FromPublicPEM(pubPEM) + require.NoError(t, err) + assert.Equal(t, MLKEM, enc.Type()) + assert.Equal(t, MLKEM1024Key, enc.KeyType()) + + dek := []byte("ml-kem-1024 round-trip data") + wrapped, err := enc.Encrypt(dek) + require.NoError(t, err) + + dec, err := FromPrivatePEM(privPEM) + require.NoError(t, err) + plaintext, err := dec.Decrypt(wrapped) + require.NoError(t, err) + assert.Equal(t, dek, plaintext) +} diff --git a/lib/ocrypto/rsa_key_pair.go b/lib/ocrypto/rsa_key_pair.go index 914eb8f80c..5294e10c13 100644 --- a/lib/ocrypto/rsa_key_pair.go +++ b/lib/ocrypto/rsa_key_pair.go @@ -41,7 +41,7 @@ func (keyPair RsaKeyPair) PrivateKeyInPemFormat() (string, error) { privateKeyPem := pem.EncodeToMemory( &pem.Block{ - Type: "PRIVATE KEY", + Type: pemBlockPrivateKey, Bytes: privateKeyBytes, }, ) @@ -61,7 +61,7 @@ func (keyPair RsaKeyPair) PublicKeyInPemFormat() (string, error) { publicKeyPem := pem.EncodeToMemory( &pem.Block{ - Type: "PUBLIC KEY", + Type: pemBlockPublicKey, Bytes: publicKeyBytes, }, ) diff --git a/lib/ocrypto/xwing.go b/lib/ocrypto/xwing.go index 1ea9d5ab25..ce8ac47c01 100644 --- a/lib/ocrypto/xwing.go +++ b/lib/ocrypto/xwing.go @@ -2,14 +2,9 @@ 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 ( @@ -23,28 +18,11 @@ const ( 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 { @@ -82,179 +60,18 @@ func XWingPrivateKeyFromPem(data []byte) ([]byte, error) { return decodeSizedPEMBlock(data, PEMBlockXWingPrivateKey, XWingPrivateKeySize) } -func NewXWingEncryptor(publicKey, salt, info []byte) (*XWingEncryptor, error) { - if len(publicKey) != XWingPublicKeySize { - return nil, fmt.Errorf("invalid X-Wing public key size: got %d want %d", len(publicKey), XWingPublicKeySize) - } - - return &XWingEncryptor{ - publicKey: append([]byte(nil), publicKey...), - salt: cloneOrNil(salt), - info: cloneOrNil(info), - }, nil -} - -func (e *XWingEncryptor) Encrypt(data []byte) ([]byte, error) { - return xwingWrapDEK(e.publicKey, data, e.salt, e.info) -} - -func (e *XWingEncryptor) PublicKeyInPemFormat() (string, error) { - return rawToPEM(PEMBlockXWingPublicKey, e.publicKey, XWingPublicKeySize) -} - -func (e *XWingEncryptor) Type() SchemeType { - return Hybrid -} - -func (e *XWingEncryptor) KeyType() KeyType { - return HybridXWingKey -} - -func (e *XWingEncryptor) EphemeralKey() []byte { - return nil -} - -func (e *XWingEncryptor) Metadata() (map[string]string, error) { - return make(map[string]string), nil -} - -func NewXWingDecryptor(privateKey []byte) (*XWingDecryptor, error) { - return NewSaltedXWingDecryptor(privateKey, defaultTDFSalt(), nil) -} - -func NewSaltedXWingDecryptor(privateKey, salt, info []byte) (*XWingDecryptor, error) { - if len(privateKey) != XWingPrivateKeySize { - return nil, fmt.Errorf("invalid X-Wing private key size: got %d want %d", len(privateKey), XWingPrivateKeySize) - } - - return &XWingDecryptor{ - privateKey: append([]byte(nil), privateKey...), - salt: cloneOrNil(salt), - info: cloneOrNil(info), - }, nil -} - -func (d *XWingDecryptor) Decrypt(data []byte) ([]byte, error) { - return xwingUnwrapDEK(d.privateKey, data, d.salt, d.info) -} - +// XWingWrapDEK encapsulates against an X-Wing public key and returns the +// ASN.1 DER envelope carrying the KEM ciphertext and AES-GCM-wrapped DEK. +// +// Deprecated: Use WrapDEK with HybridXWingKey, or construct via FromPublicPEM. func XWingWrapDEK(publicKeyRaw, dek []byte) ([]byte, error) { - return xwingWrapDEK(publicKeyRaw, dek, defaultTDFSalt(), nil) + return wrapDEKWithKEM(xwingKEM{}, publicKeyRaw, dek, defaultTDFSalt(), nil) } +// XWingUnwrapDEK decapsulates the envelope produced by XWingWrapDEK using the +// supplied raw X-Wing private key. This is the binary-bytes counterpart to +// FromPrivatePEM (which works from PEM); callers that already hold raw key +// material can use it directly without re-encoding to PEM. func XWingUnwrapDEK(privateKeyRaw, wrappedDER []byte) ([]byte, error) { - return xwingUnwrapDEK(privateKeyRaw, wrappedDER, defaultTDFSalt(), nil) -} - -// XWingEncapsulate performs the X-Wing KEM encapsulation, returning the shared -// secret and ciphertext without applying KDF or encryption. -func XWingEncapsulate(publicKeyRaw []byte) ([]byte, []byte, error) { - if len(publicKeyRaw) != XWingPublicKeySize { - return nil, nil, fmt.Errorf("invalid X-Wing public key size: got %d want %d", len(publicKeyRaw), XWingPublicKeySize) - } - - sharedSecret, ciphertext, err := xwing.Encapsulate(publicKeyRaw, nil) - if err != nil { - return nil, nil, fmt.Errorf("xwing.Encapsulate failed: %w", err) - } - - return sharedSecret, ciphertext, nil -} - -func xwingWrapDEK(publicKeyRaw, dek, salt, info []byte) ([]byte, error) { - sharedSecret, ciphertext, err := XWingEncapsulate(publicKeyRaw) - if err != nil { - return nil, err - } - - wrapKey, err := deriveXWingWrapKey(sharedSecret, salt, info) - if err != nil { - return nil, err - } - - gcm, err := NewAESGcm(wrapKey) - if err != nil { - return nil, fmt.Errorf("NewAESGcm failed: %w", err) - } - - encryptedDEK, err := gcm.Encrypt(dek) - if err != nil { - return nil, fmt.Errorf("AES-GCM encrypt failed: %w", err) - } - - wrappedDER, err := asn1.Marshal(XWingWrappedKey{ - XWingCiphertext: ciphertext, - EncryptedDEK: encryptedDEK, - }) - if err != nil { - return nil, fmt.Errorf("asn1.Marshal failed: %w", err) - } - - return wrappedDER, nil -} - -func xwingUnwrapDEK(privateKeyRaw, wrappedDER, salt, info []byte) ([]byte, error) { - if len(privateKeyRaw) != XWingPrivateKeySize { - return nil, fmt.Errorf("invalid X-Wing private key size: got %d want %d", len(privateKeyRaw), XWingPrivateKeySize) - } - - var wrappedKey XWingWrappedKey - rest, err := asn1.Unmarshal(wrappedDER, &wrappedKey) - if err != nil { - return nil, fmt.Errorf("asn1.Unmarshal failed: %w", err) - } - if len(rest) != 0 { - return nil, fmt.Errorf("asn1.Unmarshal left %d trailing bytes", len(rest)) - } - if len(wrappedKey.XWingCiphertext) != XWingCiphertextSize { - return nil, fmt.Errorf("invalid X-Wing ciphertext size: got %d want %d", len(wrappedKey.XWingCiphertext), XWingCiphertextSize) - } - - sharedSecret := xwing.Decapsulate(wrappedKey.XWingCiphertext, privateKeyRaw) - - wrapKey, err := deriveXWingWrapKey(sharedSecret, salt, info) - if err != nil { - return nil, err - } - - gcm, err := NewAESGcm(wrapKey) - if err != nil { - return nil, fmt.Errorf("NewAESGcm failed: %w", err) - } - - plaintext, err := gcm.Decrypt(wrappedKey.EncryptedDEK) - if err != nil { - return nil, fmt.Errorf("AES-GCM decrypt failed: %w", err) - } - - return plaintext, nil -} - -func deriveXWingWrapKey(sharedSecret, salt, info []byte) ([]byte, error) { - if len(salt) == 0 { - salt = defaultTDFSalt() - } - - hkdfObj := hkdf.New(sha256.New, sharedSecret, salt, info) - derivedKey := make([]byte, xwing.SharedKeySize) - if _, err := io.ReadFull(hkdfObj, derivedKey); err != nil { - return nil, fmt.Errorf("hkdf failure: %w", err) - } - - return derivedKey, nil -} - -func decodeSizedPEMBlock(data []byte, blockType string, expectedSize int) ([]byte, error) { - block, _ := pem.Decode(data) - if block == nil { - return nil, fmt.Errorf("failed to parse PEM formatted %s", blockType) - } - if block.Type != blockType { - return nil, fmt.Errorf("unexpected PEM block type: got %s want %s", block.Type, blockType) - } - if len(block.Bytes) != expectedSize { - return nil, fmt.Errorf("invalid %s size: got %d want %d", blockType, len(block.Bytes), expectedSize) - } - - return append([]byte(nil), block.Bytes...), nil + return unwrapDEKWithKEM(xwingKEM{}, privateKeyRaw, wrappedDER, defaultTDFSalt(), nil) } diff --git a/lib/ocrypto/xwing_test.go b/lib/ocrypto/xwing_test.go index c0bf783ba9..1e363c93fc 100644 --- a/lib/ocrypto/xwing_test.go +++ b/lib/ocrypto/xwing_test.go @@ -60,16 +60,16 @@ func TestXWingWrapUnwrapWrongKeyFails(t *testing.T) { assert.Contains(t, err.Error(), "AES-GCM decrypt failed") } -func TestXWingWrappedKeyASN1RoundTrip(t *testing.T) { - original := XWingWrappedKey{ - XWingCiphertext: []byte("ciphertext"), - EncryptedDEK: []byte("encrypted-dek"), +func TestXWingEnvelopeASN1RoundTrip(t *testing.T) { + original := kemEnvelope{ + KEMCiphertext: []byte("ciphertext"), + EncryptedDEK: []byte("encrypted-dek"), } der, err := asn1.Marshal(original) require.NoError(t, err) - var decoded XWingWrappedKey + var decoded kemEnvelope rest, err := asn1.Unmarshal(der, &decoded) require.NoError(t, err) assert.Empty(t, rest) @@ -91,23 +91,18 @@ func TestXWingPEMDispatch(t *testing.T) { 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()) + assert.Equal(t, Hybrid, encryptor.Type()) + assert.Equal(t, HybridXWingKey, encryptor.KeyType()) + assert.Nil(t, encryptor.EphemeralKey()) - metadata, err := xwingEncryptor.Metadata() + metadata, err := encryptor.Metadata() require.NoError(t, err) assert.Empty(t, metadata) - xwingDecryptor, ok := decryptor.(*XWingDecryptor) - require.True(t, ok) - - wrapped, err := xwingEncryptor.Encrypt([]byte("dispatch-dek")) + wrapped, err := encryptor.Encrypt([]byte("dispatch-dek")) require.NoError(t, err) - plaintext, err := xwingDecryptor.Decrypt(wrapped) + plaintext, err := decryptor.Decrypt(wrapped) require.NoError(t, err) assert.Equal(t, []byte("dispatch-dek"), plaintext) } @@ -116,14 +111,14 @@ func TestXWingEncapsulate(t *testing.T) { keyPair, err := NewXWingKeyPair() require.NoError(t, err) - sharedSecret, ciphertext, err := XWingEncapsulate(keyPair.publicKey) + sharedSecret, ciphertext, err := xwingKEM{}.encapsulate(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")) + _, _, err := xwingKEM{}.encapsulate([]byte("too-short")) require.Error(t, err) - assert.Contains(t, err.Error(), "invalid X-Wing public key size") + assert.Contains(t, err.Error(), "X-Wing public key size") } diff --git a/otdfctl/cmd/policy/kasKeys.go b/otdfctl/cmd/policy/kasKeys.go index e811486284..fab10c03e6 100644 --- a/otdfctl/cmd/policy/kasKeys.go +++ b/otdfctl/cmd/policy/kasKeys.go @@ -89,6 +89,10 @@ func generateKeyPair(alg policy.Algorithm) (ocrypto.KeyPair, error) { key, err = ocrypto.NewKeyPair(ocrypto.HybridSecp256r1MLKEM768Key) case policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024: key, err = ocrypto.NewKeyPair(ocrypto.HybridSecp384r1MLKEM1024Key) + case policy.Algorithm_ALGORITHM_MLKEM_768: + key, err = ocrypto.NewKeyPair(ocrypto.MLKEM768Key) + case policy.Algorithm_ALGORITHM_MLKEM_1024: + key, err = ocrypto.NewKeyPair(ocrypto.MLKEM1024Key) case policy.Algorithm_ALGORITHM_UNSPECIFIED: fallthrough default: diff --git a/otdfctl/docs/man/policy/kas-registry/key/create.md b/otdfctl/docs/man/policy/kas-registry/key/create.md index 76f5a85af9..166f14dd93 100644 --- a/otdfctl/docs/man/policy/kas-registry/key/create.md +++ b/otdfctl/docs/man/policy/kas-registry/key/create.md @@ -75,6 +75,8 @@ otdfctl policy kas-registry key create --key-id "aws-key" --algorithm "rsa:2048" | `ec:secp256r1` | | `ec:secp384r1` | | `ec:secp521r1` | + | `mlkem:768` | + | `mlkem:1024` | | `hpqt:xwing` | | `hpqt:secp256r1-mlkem768` | | `hpqt:secp384r1-mlkem1024` | diff --git a/otdfctl/docs/man/policy/kas-registry/key/import.md b/otdfctl/docs/man/policy/kas-registry/key/import.md index b08a2ea843..1c209271f6 100644 --- a/otdfctl/docs/man/policy/kas-registry/key/import.md +++ b/otdfctl/docs/man/policy/kas-registry/key/import.md @@ -79,6 +79,8 @@ otdfctl policy kas-registry key import --key-id "imported-key" --algorithm "rsa: | `ec:secp256r1` | | `ec:secp384r1` | | `ec:secp521r1` | + | `mlkem:768` | + | `mlkem:1024` | | `hpqt:xwing` | | `hpqt:secp256r1-mlkem768` | | `hpqt:secp384r1-mlkem1024` | diff --git a/otdfctl/docs/man/policy/kas-registry/key/rotate.md b/otdfctl/docs/man/policy/kas-registry/key/rotate.md index 726d85f74b..29a94bf53e 100644 --- a/otdfctl/docs/man/policy/kas-registry/key/rotate.md +++ b/otdfctl/docs/man/policy/kas-registry/key/rotate.md @@ -88,6 +88,8 @@ otdfctl policy kas-registry key rotate --key "public-key-old" --kas "Secondary K | `ec:secp256r1` | | `ec:secp384r1` | | `ec:secp521r1` | + | `mlkem:768` | + | `mlkem:1024` | | `hpqt:xwing` | | `hpqt:secp256r1-mlkem768` | | `hpqt:secp384r1-mlkem1024` | diff --git a/otdfctl/pkg/cli/sdkHelpers.go b/otdfctl/pkg/cli/sdkHelpers.go index 943fbbc71e..941e406b7e 100644 --- a/otdfctl/pkg/cli/sdkHelpers.go +++ b/otdfctl/pkg/cli/sdkHelpers.go @@ -131,6 +131,10 @@ func KeyAlgToEnum(alg string) (policy.Algorithm, error) { return policy.Algorithm_ALGORITHM_HPQT_SECP256R1_MLKEM768, nil case "hpqt:secp384r1-mlkem1024": return policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024, nil + case "mlkem:768": + return policy.Algorithm_ALGORITHM_MLKEM_768, nil + case "mlkem:1024": + return policy.Algorithm_ALGORITHM_MLKEM_1024, nil default: return policy.Algorithm_ALGORITHM_UNSPECIFIED, errors.New("invalid algorithm") } @@ -154,6 +158,10 @@ func KeyEnumToAlg(enum policy.Algorithm) (string, error) { return "hpqt:secp256r1-mlkem768", nil case policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024: return "hpqt:secp384r1-mlkem1024", nil + case policy.Algorithm_ALGORITHM_MLKEM_768: + return "mlkem:768", nil + case policy.Algorithm_ALGORITHM_MLKEM_1024: + return "mlkem:1024", nil default: return "", errors.New("invalid enum algorithm") } diff --git a/otdfctl/pkg/utils/pemvalidate.go b/otdfctl/pkg/utils/pemvalidate.go index 29fc17f8ef..558af3cd55 100644 --- a/otdfctl/pkg/utils/pemvalidate.go +++ b/otdfctl/pkg/utils/pemvalidate.go @@ -53,6 +53,14 @@ func ValidatePublicKeyPEM(pemBytes []byte, expected policy.Algorithm) error { if enc.KeyType() != ocrypto.HybridSecp384r1MLKEM1024Key { return errors.New("algorithm mismatch: expected hybrid NIST P-384 + ML-KEM-1024") } + case policy.Algorithm_ALGORITHM_MLKEM_768: + if enc.KeyType() != ocrypto.MLKEM768Key { + return errors.New("algorithm mismatch: expected ML-KEM-768") + } + case policy.Algorithm_ALGORITHM_MLKEM_1024: + if enc.KeyType() != ocrypto.MLKEM1024Key { + return errors.New("algorithm mismatch: expected ML-KEM-1024") + } case policy.Algorithm_ALGORITHM_UNSPECIFIED: fallthrough default: 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 c3cd4c7dc2..61fe847fae 100644 --- a/protocol/go/policy/kasregistry/key_access_server_registry.pb.go +++ b/protocol/go/policy/kasregistry/key_access_server_registry.pb.go @@ -4297,560 +4297,562 @@ var file_policy_kasregistry_key_access_server_registry_proto_rawDesc = []byte{ 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, 0xd5, 0x0c, 0x0a, 0x10, 0x43, 0x72, 0x65, 0x61, + 0x69, 0x6f, 0x6e, 0x3a, 0x02, 0x18, 0x01, 0x22, 0xdd, 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, 0xad, 0x01, + 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x12, 0xb5, 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, 0x75, 0xba, 0x48, 0x72, 0xba, 0x01, 0x6f, + 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x42, 0x7d, 0xba, 0x48, 0x7a, 0xba, 0x01, 0x77, 0x0a, 0x15, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x34, 0x54, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, - 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x1a, 0x20, 0x74, + 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x1a, 0x28, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x5b, 0x31, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, - 0x20, 0x34, 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x36, 0x2c, 0x20, 0x37, 0x2c, 0x20, 0x38, 0x5d, 0x52, - 0x0c, 0x6b, 0x65, 0x79, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x93, 0x01, - 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x0f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x65, 0x79, 0x4d, 0x6f, 0x64, - 0x65, 0x42, 0x67, 0xba, 0x48, 0x64, 0xba, 0x01, 0x61, 0x0a, 0x10, 0x6b, 0x65, 0x79, 0x5f, 0x6d, - 0x6f, 0x64, 0x65, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x35, 0x54, 0x68, 0x65, - 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, - 0x65, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x66, - 0x69, 0x6e, 0x65, 0x64, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x28, 0x31, 0x2d, 0x34, - 0x29, 0x2e, 0x1a, 0x16, 0x74, 0x68, 0x69, 0x73, 0x20, 0x3e, 0x3d, 0x20, 0x31, 0x20, 0x26, 0x26, - 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x3c, 0x3d, 0x20, 0x34, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x4d, - 0x6f, 0x64, 0x65, 0x12, 0x42, 0x0a, 0x0e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, - 0x79, 0x5f, 0x63, 0x74, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x43, 0x74, - 0x78, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x0c, 0x70, 0x75, 0x62, 0x6c, 0x69, - 0x63, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x12, 0x3d, 0x0a, 0x0f, 0x70, 0x72, 0x69, 0x76, 0x61, - 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x15, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, - 0x65, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x52, 0x0d, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, - 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, - 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x12, 0x33, 0x0a, 0x08, - 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, - 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x4d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x3a, 0xbb, 0x07, 0xba, 0x48, 0xb7, 0x07, 0x1a, 0x97, 0x03, 0x0a, 0x23, 0x70, 0x72, 0x69, - 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x5f, 0x6f, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, - 0x12, 0xbc, 0x01, 0x54, 0x68, 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, - 0x65, 0x79, 0x20, 0x69, 0x73, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x69, - 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, - 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x52, 0x4f, - 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x20, 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, - 0x44, 0x45, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, 0x52, 0x5f, 0x52, 0x4f, 0x4f, 0x54, - 0x5f, 0x4b, 0x45, 0x59, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, - 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x65, 0x6d, - 0x70, 0x74, 0x79, 0x20, 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, - 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, - 0x54, 0x45, 0x20, 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, - 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x2e, 0x1a, - 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, + 0x20, 0x34, 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x36, 0x2c, 0x20, 0x37, 0x2c, 0x20, 0x38, 0x2c, 0x20, + 0x32, 0x30, 0x2c, 0x20, 0x32, 0x31, 0x5d, 0x52, 0x0c, 0x6b, 0x65, 0x79, 0x41, 0x6c, 0x67, 0x6f, + 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x93, 0x01, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, + 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x2e, 0x4b, 0x65, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x42, 0x67, 0xba, 0x48, 0x64, 0xba, 0x01, + 0x61, 0x0a, 0x10, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x5f, 0x64, 0x65, 0x66, 0x69, + 0x6e, 0x65, 0x64, 0x12, 0x35, 0x54, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, + 0x65, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x73, 0x20, 0x28, 0x31, 0x2d, 0x34, 0x29, 0x2e, 0x1a, 0x16, 0x74, 0x68, 0x69, 0x73, + 0x20, 0x3e, 0x3d, 0x20, 0x31, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x3c, 0x3d, + 0x20, 0x34, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x42, 0x0a, 0x0e, 0x70, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, + 0x01, 0x52, 0x0c, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x12, + 0x3d, 0x0a, 0x0f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, + 0x74, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x2e, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x52, + 0x0d, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x12, 0x2c, + 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, + 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x6c, 0x65, + 0x67, 0x61, 0x63, 0x79, 0x12, 0x33, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, + 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x3a, 0xbb, 0x07, 0xba, 0x48, 0xb7, 0x07, + 0x1a, 0x97, 0x03, 0x0a, 0x23, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, + 0x5f, 0x63, 0x74, 0x78, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x5f, + 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0xbc, 0x01, 0x54, 0x68, 0x65, 0x20, 0x77, + 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x73, 0x20, 0x72, 0x65, + 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, + 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x43, + 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x20, 0x6f, + 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, + 0x44, 0x45, 0x52, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x2e, 0x20, 0x54, 0x68, + 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x6d, 0x75, + 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x69, 0x66, 0x20, 0x6b, + 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, + 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x20, 0x6f, 0x72, 0x20, 0x4b, 0x45, + 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, + 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x2e, 0x1a, 0xb0, 0x01, 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, + 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x20, 0x7c, + 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, + 0x3d, 0x3d, 0x20, 0x32, 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, 0x72, + 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x2e, 0x77, 0x72, + 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x27, 0x29, + 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, + 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x33, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, + 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x34, 0x29, 0x20, + 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, + 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x2e, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, + 0x6b, 0x65, 0x79, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x27, 0x29, 0x1a, 0xf4, 0x02, 0x0a, 0x26, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, - 0x64, 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, 0x86, 0x04, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0xb0, 0x01, 0x0a, 0x0d, 0x6b, 0x65, 0x79, - 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, - 0x74, 0x68, 0x6d, 0x42, 0x78, 0xba, 0x48, 0x75, 0xba, 0x01, 0x72, 0x0a, 0x15, 0x6b, 0x65, 0x79, - 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, - 0x65, 0x64, 0x12, 0x34, 0x54, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, - 0x72, 0x69, 0x74, 0x68, 0x6d, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x6f, 0x6e, - 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, - 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x1a, 0x23, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, - 0x6e, 0x20, 0x5b, 0x30, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x34, - 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x36, 0x2c, 0x20, 0x37, 0x2c, 0x20, 0x38, 0x5d, 0x52, 0x0c, 0x6b, - 0x65, 0x79, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x21, 0x0a, 0x06, 0x6b, - 0x61, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, - 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x05, 0x6b, 0x61, 0x73, 0x49, 0x64, 0x12, 0x24, - 0x0a, 0x08, 0x6b, 0x61, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x48, 0x00, 0x52, 0x07, 0x6b, 0x61, 0x73, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x07, 0x6b, 0x61, 0x73, 0x5f, 0x75, 0x72, 0x69, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0x72, 0x05, 0x10, 0x01, 0x88, 0x01, - 0x01, 0x48, 0x00, 0x52, 0x06, 0x6b, 0x61, 0x73, 0x55, 0x72, 0x69, 0x12, 0x1b, 0x0a, 0x06, 0x6c, - 0x65, 0x67, 0x61, 0x63, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x48, 0x01, 0x52, 0x06, 0x6c, - 0x65, 0x67, 0x61, 0x63, 0x79, 0x88, 0x01, 0x01, 0x12, 0x33, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, - 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3d, 0x0a, - 0x04, 0x73, 0x6f, 0x72, 0x74, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, - 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x42, 0x08, 0xba, 0x48, - 0x05, 0x92, 0x01, 0x02, 0x10, 0x01, 0x52, 0x04, 0x73, 0x6f, 0x72, 0x74, 0x12, 0x26, 0x0a, 0x06, - 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x06, 0x73, 0x65, - 0x61, 0x72, 0x63, 0x68, 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, 0xee, 0x0e, 0x0a, 0x10, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x02, 0x69, - 0x64, 0x12, 0x38, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, - 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x72, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x66, 0x69, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x44, 0x0a, 0x07, 0x6e, - 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x70, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, - 0x79, 0x2e, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x2e, 0x4e, 0x65, 0x77, 0x4b, 0x65, 0x79, 0x52, 0x06, 0x6e, 0x65, 0x77, 0x4b, 0x65, - 0x79, 0x1a, 0xd8, 0x04, 0x0a, 0x06, 0x4e, 0x65, 0x77, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x06, - 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, - 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x12, 0xa6, 0x01, 0x0a, - 0x09, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, - 0x74, 0x68, 0x6d, 0x42, 0x75, 0xba, 0x48, 0x72, 0xba, 0x01, 0x6f, 0x0a, 0x15, 0x6b, 0x65, 0x79, - 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, - 0x65, 0x64, 0x12, 0x34, 0x54, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, - 0x72, 0x69, 0x74, 0x68, 0x6d, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x6f, 0x6e, - 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, - 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x1a, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, - 0x6e, 0x20, 0x5b, 0x31, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x35, - 0x2c, 0x20, 0x36, 0x2c, 0x20, 0x37, 0x2c, 0x20, 0x38, 0x5d, 0x52, 0x09, 0x61, 0x6c, 0x67, 0x6f, - 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x9e, 0x01, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, - 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x2e, 0x4b, 0x65, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x42, 0x72, 0xba, 0x48, 0x6f, 0xba, 0x01, - 0x67, 0x0a, 0x14, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x5f, - 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x39, 0x54, 0x68, 0x65, 0x20, 0x6e, 0x65, 0x77, - 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, - 0x65, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x66, - 0x69, 0x6e, 0x65, 0x64, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x28, 0x31, 0x2d, 0x34, - 0x29, 0x2e, 0x1a, 0x14, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x5b, 0x31, 0x2c, 0x20, - 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x34, 0x5d, 0x82, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x6b, - 0x65, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x42, 0x0a, 0x0e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, - 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, - 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, - 0x79, 0x43, 0x74, 0x78, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x0c, 0x70, 0x75, - 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x12, 0x3d, 0x0a, 0x0f, 0x70, 0x72, - 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x72, 0x69, - 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x52, 0x0d, 0x70, 0x72, 0x69, 0x76, - 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x49, 0x64, 0x12, 0x33, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, - 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4d, 0x75, 0x74, 0x61, 0x62, - 0x6c, 0x65, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x3a, 0xcd, 0x08, 0xba, - 0x48, 0xc9, 0x08, 0x1a, 0xd8, 0x03, 0x0a, 0x23, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, - 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, - 0x6c, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0xcd, 0x01, 0x46, 0x6f, - 0x72, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x6b, 0x65, 0x79, 0x2c, 0x20, 0x74, - 0x68, 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x69, - 0x73, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x69, 0x66, 0x20, 0x6b, 0x65, - 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, - 0x44, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, - 0x45, 0x59, 0x20, 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, + 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, - 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, - 0x79, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x20, - 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, - 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x20, 0x6f, - 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, - 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x2e, 0x1a, 0xe0, 0x01, 0x28, 0x28, - 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x6b, 0x65, 0x79, - 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, - 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, - 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, - 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, - 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x2e, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, - 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x27, 0x29, 0x20, 0x7c, 0x7c, 0x20, - 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x6b, - 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x33, 0x20, 0x7c, 0x7c, 0x20, - 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x6b, 0x65, 0x79, - 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x34, 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, - 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x69, 0x76, - 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x2e, 0x77, 0x72, 0x61, 0x70, - 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x27, 0x29, 0x1a, 0xb5, - 0x03, 0x0a, 0x26, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x5f, 0x69, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x6c, 0x79, - 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0xb9, 0x01, 0x46, 0x6f, 0x72, 0x20, - 0x74, 0x68, 0x65, 0x20, 0x6e, 0x65, 0x77, 0x20, 0x6b, 0x65, 0x79, 0x2c, 0x20, 0x70, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x20, 0x69, 0x64, 0x20, - 0x69, 0x73, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x69, 0x66, 0x20, 0x6b, - 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, - 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, 0x52, 0x5f, 0x52, 0x4f, 0x4f, - 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x20, 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, - 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x2e, 0x20, 0x49, 0x74, 0x20, 0x6d, 0x75, 0x73, - 0x74, 0x20, 0x62, 0x65, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x4b, - 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x52, - 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x4b, 0x45, 0x59, 0x5f, - 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, - 0x4f, 0x4e, 0x4c, 0x59, 0x2e, 0x1a, 0xce, 0x01, 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, - 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, - 0x3d, 0x3d, 0x20, 0x31, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, - 0x5f, 0x6b, 0x65, 0x79, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, - 0x20, 0x34, 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, - 0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x27, 0x29, 0x20, 0x7c, 0x7c, - 0x20, 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x2e, - 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x20, 0x7c, 0x7c, + 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, 0x8f, 0x04, 0x0a, + 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0xb9, 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, 0x80, 0x01, 0xba, 0x48, + 0x7d, 0xba, 0x01, 0x7a, 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, 0x2b, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x5b, 0x30, 0x2c, 0x20, 0x31, + 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x36, 0x2c, + 0x20, 0x37, 0x2c, 0x20, 0x38, 0x2c, 0x20, 0x32, 0x30, 0x2c, 0x20, 0x32, 0x31, 0x5d, 0x52, 0x0c, + 0x6b, 0x65, 0x79, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x21, 0x0a, 0x06, + 0x6b, 0x61, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, + 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x05, 0x6b, 0x61, 0x73, 0x49, 0x64, 0x12, + 0x24, 0x0a, 0x08, 0x6b, 0x61, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x48, 0x00, 0x52, 0x07, 0x6b, 0x61, + 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x07, 0x6b, 0x61, 0x73, 0x5f, 0x75, 0x72, 0x69, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0x72, 0x05, 0x10, 0x01, 0x88, + 0x01, 0x01, 0x48, 0x00, 0x52, 0x06, 0x6b, 0x61, 0x73, 0x55, 0x72, 0x69, 0x12, 0x1b, 0x0a, 0x06, + 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x48, 0x01, 0x52, 0x06, + 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x88, 0x01, 0x01, 0x12, 0x33, 0x0a, 0x0a, 0x70, 0x61, 0x67, + 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3d, + 0x0a, 0x04, 0x73, 0x6f, 0x72, 0x74, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, + 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x42, 0x08, 0xba, + 0x48, 0x05, 0x92, 0x01, 0x02, 0x10, 0x01, 0x52, 0x04, 0x73, 0x6f, 0x72, 0x74, 0x12, 0x26, 0x0a, + 0x06, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x52, 0x06, 0x73, + 0x65, 0x61, 0x72, 0x63, 0x68, 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, 0xf6, 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, 0xe0, 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, 0xae, 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, 0x7d, 0xba, 0x48, 0x7a, 0xba, 0x01, 0x77, 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, 0x28, 0x74, 0x68, 0x69, 0x73, 0x20, + 0x69, 0x6e, 0x20, 0x5b, 0x31, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x34, 0x2c, 0x20, + 0x35, 0x2c, 0x20, 0x36, 0x2c, 0x20, 0x37, 0x2c, 0x20, 0x38, 0x2c, 0x20, 0x32, 0x30, 0x2c, 0x20, + 0x32, 0x31, 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, 0x20, + 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, 0x73, + 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, 0x20, + 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, 0x63, + 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, 0x2a, 0xef, 0x01, 0x0a, 0x18, 0x53, 0x6f, - 0x72, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2c, 0x0a, 0x28, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x4b, - 0x45, 0x59, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x53, 0x53, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, - 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, - 0x45, 0x44, 0x10, 0x00, 0x12, 0x25, 0x0a, 0x21, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x4b, 0x45, 0x59, + 0x52, 0x11, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x4d, 0x61, 0x70, 0x70, 0x69, + 0x6e, 0x67, 0x73, 0x12, 0x4d, 0x0a, 0x0e, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x6d, 0x61, 0x70, + 0x70, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, + 0x2e, 0x4d, 0x61, 0x70, 0x70, 0x65, 0x64, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x52, 0x0d, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, + 0x67, 0x73, 0x22, 0xb8, 0x01, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x4d, 0x61, + 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, + 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x02, 0x69, 0x64, 0x12, 0x38, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, + 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, + 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x33, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x0a, 0x70, 0x61, + 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x13, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x00, 0x22, 0x92, 0x01, + 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0c, 0x6b, 0x65, 0x79, + 0x5f, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4b, 0x65, 0x79, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, + 0x0b, 0x6b, 0x65, 0x79, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x34, 0x0a, 0x0a, + 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x2a, 0xef, 0x01, 0x0a, 0x18, 0x53, 0x6f, 0x72, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x2c, 0x0a, 0x28, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x41, 0x43, 0x43, 0x45, + 0x53, 0x53, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x25, 0x0a, + 0x21, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x53, 0x53, + 0x5f, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x41, + 0x4d, 0x45, 0x10, 0x01, 0x12, 0x24, 0x0a, 0x20, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x53, 0x53, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x53, 0x5f, - 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4e, 0x41, 0x4d, 0x45, 0x10, 0x01, 0x12, 0x24, 0x0a, 0x20, 0x53, - 0x4f, 0x52, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x53, 0x53, 0x5f, 0x53, - 0x45, 0x52, 0x56, 0x45, 0x52, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x52, 0x49, 0x10, - 0x02, 0x12, 0x2b, 0x0a, 0x27, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x41, 0x43, - 0x43, 0x45, 0x53, 0x53, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x53, 0x5f, 0x54, 0x59, 0x50, - 0x45, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x44, 0x5f, 0x41, 0x54, 0x10, 0x03, 0x12, 0x2b, - 0x0a, 0x27, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x53, - 0x53, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x45, 0x52, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, - 0x50, 0x44, 0x41, 0x54, 0x45, 0x44, 0x5f, 0x41, 0x54, 0x10, 0x04, 0x2a, 0x9a, 0x01, 0x0a, 0x0f, - 0x53, 0x6f, 0x72, 0x74, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x22, 0x0a, 0x1e, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x4b, 0x41, 0x53, 0x5f, 0x4b, 0x45, 0x59, 0x53, - 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, - 0x44, 0x10, 0x00, 0x12, 0x1d, 0x0a, 0x19, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x4b, 0x41, 0x53, 0x5f, - 0x4b, 0x45, 0x59, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x49, 0x44, - 0x10, 0x01, 0x12, 0x21, 0x0a, 0x1d, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x4b, 0x41, 0x53, 0x5f, 0x4b, - 0x45, 0x59, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x44, - 0x5f, 0x41, 0x54, 0x10, 0x02, 0x12, 0x21, 0x0a, 0x1d, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x4b, 0x41, - 0x53, 0x5f, 0x4b, 0x45, 0x59, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x50, 0x44, 0x41, - 0x54, 0x45, 0x44, 0x5f, 0x41, 0x54, 0x10, 0x03, 0x32, 0x99, 0x0c, 0x0a, 0x1e, 0x4b, 0x65, 0x79, - 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, - 0x73, 0x74, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x7e, 0x0a, 0x14, 0x4c, - 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x73, 0x12, 0x2f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, - 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, - 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, - 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, - 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x78, 0x0a, 0x12, 0x47, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x52, 0x49, 0x10, 0x02, 0x12, 0x2b, 0x0a, 0x27, 0x53, 0x4f, + 0x52, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x53, 0x53, 0x5f, 0x53, 0x45, + 0x52, 0x56, 0x45, 0x52, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, + 0x45, 0x44, 0x5f, 0x41, 0x54, 0x10, 0x03, 0x12, 0x2b, 0x0a, 0x27, 0x53, 0x4f, 0x52, 0x54, 0x5f, + 0x4b, 0x45, 0x59, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x53, 0x53, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x45, + 0x52, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x44, 0x5f, + 0x41, 0x54, 0x10, 0x04, 0x2a, 0x9a, 0x01, 0x0a, 0x0f, 0x53, 0x6f, 0x72, 0x74, 0x4b, 0x61, 0x73, + 0x4b, 0x65, 0x79, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x22, 0x0a, 0x1e, 0x53, 0x4f, 0x52, 0x54, + 0x5f, 0x4b, 0x41, 0x53, 0x5f, 0x4b, 0x45, 0x59, 0x53, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, + 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1d, 0x0a, 0x19, + 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x4b, 0x41, 0x53, 0x5f, 0x4b, 0x45, 0x59, 0x53, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x49, 0x44, 0x10, 0x01, 0x12, 0x21, 0x0a, 0x1d, 0x53, + 0x4f, 0x52, 0x54, 0x5f, 0x4b, 0x41, 0x53, 0x5f, 0x4b, 0x45, 0x59, 0x53, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x44, 0x5f, 0x41, 0x54, 0x10, 0x02, 0x12, 0x21, + 0x0a, 0x1d, 0x53, 0x4f, 0x52, 0x54, 0x5f, 0x4b, 0x41, 0x53, 0x5f, 0x4b, 0x45, 0x59, 0x53, 0x5f, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x44, 0x5f, 0x41, 0x54, 0x10, + 0x03, 0x32, 0x99, 0x0c, 0x0a, 0x1e, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x12, 0x7e, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x41, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x2f, 0x2e, 0x70, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, + 0x79, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, + 0x72, 0x79, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x03, 0x90, 0x02, 0x01, 0x12, 0x78, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x2d, 0x2e, 0x70, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, + 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x12, 0x2d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, - 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x2e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x7e, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, - 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x30, - 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x72, 0x79, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x31, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7e, 0x0a, 0x15, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, - 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x30, - 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x72, 0x79, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x31, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7e, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4b, - 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x30, - 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x72, 0x79, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x31, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x41, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x90, 0x01, 0x0a, 0x19, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, - 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x61, - 0x6e, 0x74, 0x73, 0x12, 0x34, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, - 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, - 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x47, 0x72, 0x61, 0x6e, - 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x70, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 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, + 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, + 0x74, 0x72, 0x79, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, + 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x51, 0x0a, + 0x06, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x47, 0x65, 0x74, + 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x70, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, + 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x57, 0x0a, 0x08, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x23, 0x2e, 0x70, + 0x6f, 0x6c, 0x69, 0x63, 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, + 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, 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, + 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, } var ( diff --git a/protocol/go/policy/objects.pb.go b/protocol/go/policy/objects.pb.go index 1e386a7531..92175c8323 100644 --- a/protocol/go/policy/objects.pb.go +++ b/protocol/go/policy/objects.pb.go @@ -246,6 +246,8 @@ const ( 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 + KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 KasPublicKeyAlgEnum = 20 + KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 KasPublicKeyAlgEnum = 21 ) // Enum value maps for KasPublicKeyAlgEnum. @@ -260,6 +262,8 @@ var ( 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", + 20: "KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768", + 21: "KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024", } KasPublicKeyAlgEnum_value = map[string]int32{ "KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED": 0, @@ -271,6 +275,8 @@ var ( "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, + "KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768": 20, + "KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024": 21, } ) @@ -314,20 +320,24 @@ const ( Algorithm_ALGORITHM_HPQT_XWING Algorithm = 6 Algorithm_ALGORITHM_HPQT_SECP256R1_MLKEM768 Algorithm = 7 Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024 Algorithm = 8 + Algorithm_ALGORITHM_MLKEM_768 Algorithm = 20 + Algorithm_ALGORITHM_MLKEM_1024 Algorithm = 21 ) // Enum value maps for Algorithm. var ( Algorithm_name = map[int32]string{ - 0: "ALGORITHM_UNSPECIFIED", - 1: "ALGORITHM_RSA_2048", - 2: "ALGORITHM_RSA_4096", - 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", + 0: "ALGORITHM_UNSPECIFIED", + 1: "ALGORITHM_RSA_2048", + 2: "ALGORITHM_RSA_4096", + 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", + 20: "ALGORITHM_MLKEM_768", + 21: "ALGORITHM_MLKEM_1024", } Algorithm_value = map[string]int32{ "ALGORITHM_UNSPECIFIED": 0, @@ -339,6 +349,8 @@ var ( "ALGORITHM_HPQT_XWING": 6, "ALGORITHM_HPQT_SECP256R1_MLKEM768": 7, "ALGORITHM_HPQT_SECP384R1_MLKEM1024": 8, + "ALGORITHM_MLKEM_768": 20, + "ALGORITHM_MLKEM_1024": 21, } ) @@ -3760,7 +3772,7 @@ var file_policy_objects_proto_rawDesc = []byte{ 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x58, 0x54, 0x45, - 0x52, 0x4e, 0x41, 0x4c, 0x10, 0x02, 0x2a, 0x9b, 0x03, 0x0a, 0x13, 0x4b, 0x61, 0x73, 0x50, 0x75, + 0x52, 0x4e, 0x41, 0x4c, 0x10, 0x02, 0x2a, 0xea, 0x03, 0x0a, 0x13, 0x4b, 0x61, 0x73, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x41, 0x6c, 0x67, 0x45, 0x6e, 0x75, 0x6d, 0x12, 0x27, 0x0a, 0x23, 0x4b, 0x41, 0x53, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x41, 0x4c, 0x47, 0x5f, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, @@ -3786,47 +3798,55 @@ var file_policy_objects_proto_rawDesc = []byte{ 0x0a, 0x30, 0x4b, 0x41, 0x53, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x41, 0x4c, 0x47, 0x5f, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x48, 0x50, 0x51, 0x54, 0x5f, 0x53, 0x45, 0x43, 0x50, 0x33, 0x38, 0x34, 0x52, 0x31, 0x5f, 0x4d, 0x4c, 0x4b, 0x45, 0x4d, 0x31, 0x30, - 0x32, 0x34, 0x10, 0x0c, 0x2a, 0x84, 0x02, 0x0a, 0x09, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, - 0x68, 0x6d, 0x12, 0x19, 0x0a, 0x15, 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, - 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x16, 0x0a, - 0x12, 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, 0x52, 0x53, 0x41, 0x5f, 0x32, - 0x30, 0x34, 0x38, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, - 0x48, 0x4d, 0x5f, 0x52, 0x53, 0x41, 0x5f, 0x34, 0x30, 0x39, 0x36, 0x10, 0x02, 0x12, 0x15, 0x0a, - 0x11, 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, 0x45, 0x43, 0x5f, 0x50, 0x32, - 0x35, 0x36, 0x10, 0x03, 0x12, 0x15, 0x0a, 0x11, 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, - 0x4d, 0x5f, 0x45, 0x43, 0x5f, 0x50, 0x33, 0x38, 0x34, 0x10, 0x04, 0x12, 0x15, 0x0a, 0x11, 0x41, - 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, 0x45, 0x43, 0x5f, 0x50, 0x35, 0x32, 0x31, - 0x10, 0x05, 0x12, 0x18, 0x0a, 0x14, 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, - 0x48, 0x50, 0x51, 0x54, 0x5f, 0x58, 0x57, 0x49, 0x4e, 0x47, 0x10, 0x06, 0x12, 0x25, 0x0a, 0x21, - 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, 0x48, 0x50, 0x51, 0x54, 0x5f, 0x53, - 0x45, 0x43, 0x50, 0x32, 0x35, 0x36, 0x52, 0x31, 0x5f, 0x4d, 0x4c, 0x4b, 0x45, 0x4d, 0x37, 0x36, - 0x38, 0x10, 0x07, 0x12, 0x26, 0x0a, 0x22, 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, - 0x5f, 0x48, 0x50, 0x51, 0x54, 0x5f, 0x53, 0x45, 0x43, 0x50, 0x33, 0x38, 0x34, 0x52, 0x31, 0x5f, - 0x4d, 0x4c, 0x4b, 0x45, 0x4d, 0x31, 0x30, 0x32, 0x34, 0x10, 0x08, 0x2a, 0x56, 0x0a, 0x09, 0x4b, - 0x65, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x16, 0x4b, 0x45, 0x59, 0x5f, - 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, - 0x45, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x4b, 0x45, 0x59, 0x5f, 0x53, 0x54, 0x41, 0x54, - 0x55, 0x53, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x4b, - 0x45, 0x59, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x52, 0x4f, 0x54, 0x41, 0x54, 0x45, - 0x44, 0x10, 0x02, 0x2a, 0x94, 0x01, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, - 0x18, 0x0a, 0x14, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, - 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x4b, 0x45, 0x59, - 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x52, 0x4f, 0x4f, - 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x01, 0x12, 0x1e, 0x0a, 0x1a, 0x4b, 0x45, 0x59, 0x5f, 0x4d, - 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, 0x52, 0x5f, 0x52, 0x4f, 0x4f, - 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x4b, 0x45, 0x59, 0x5f, 0x4d, - 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x10, 0x03, 0x12, 0x1c, 0x0a, 0x18, - 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, - 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x04, 0x42, 0x82, 0x01, 0x0a, 0x0a, 0x63, - 0x6f, 0x6d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x42, 0x0c, 0x4f, 0x62, 0x6a, 0x65, 0x63, - 0x74, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x64, 0x66, 0x2f, 0x70, 0x6c, - 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, - 0x67, 0x6f, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0xa2, 0x02, 0x03, 0x50, 0x58, 0x58, 0xaa, - 0x02, 0x06, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0xca, 0x02, 0x06, 0x50, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0xe2, 0x02, 0x12, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x06, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x32, 0x34, 0x10, 0x0c, 0x12, 0x25, 0x0a, 0x21, 0x4b, 0x41, 0x53, 0x5f, 0x50, 0x55, 0x42, 0x4c, + 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x41, 0x4c, 0x47, 0x5f, 0x45, 0x4e, 0x55, 0x4d, 0x5f, + 0x4d, 0x4c, 0x4b, 0x45, 0x4d, 0x5f, 0x37, 0x36, 0x38, 0x10, 0x14, 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, 0x4d, 0x4c, 0x4b, 0x45, 0x4d, 0x5f, 0x31, 0x30, 0x32, + 0x34, 0x10, 0x15, 0x2a, 0xb7, 0x02, 0x0a, 0x09, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, + 0x6d, 0x12, 0x19, 0x0a, 0x15, 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, 0x55, + 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, + 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, 0x52, 0x53, 0x41, 0x5f, 0x32, 0x30, + 0x34, 0x38, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, + 0x4d, 0x5f, 0x52, 0x53, 0x41, 0x5f, 0x34, 0x30, 0x39, 0x36, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, + 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, 0x45, 0x43, 0x5f, 0x50, 0x32, 0x35, + 0x36, 0x10, 0x03, 0x12, 0x15, 0x0a, 0x11, 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, + 0x5f, 0x45, 0x43, 0x5f, 0x50, 0x33, 0x38, 0x34, 0x10, 0x04, 0x12, 0x15, 0x0a, 0x11, 0x41, 0x4c, + 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, 0x45, 0x43, 0x5f, 0x50, 0x35, 0x32, 0x31, 0x10, + 0x05, 0x12, 0x18, 0x0a, 0x14, 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, 0x48, + 0x50, 0x51, 0x54, 0x5f, 0x58, 0x57, 0x49, 0x4e, 0x47, 0x10, 0x06, 0x12, 0x25, 0x0a, 0x21, 0x41, + 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, 0x48, 0x50, 0x51, 0x54, 0x5f, 0x53, 0x45, + 0x43, 0x50, 0x32, 0x35, 0x36, 0x52, 0x31, 0x5f, 0x4d, 0x4c, 0x4b, 0x45, 0x4d, 0x37, 0x36, 0x38, + 0x10, 0x07, 0x12, 0x26, 0x0a, 0x22, 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, + 0x48, 0x50, 0x51, 0x54, 0x5f, 0x53, 0x45, 0x43, 0x50, 0x33, 0x38, 0x34, 0x52, 0x31, 0x5f, 0x4d, + 0x4c, 0x4b, 0x45, 0x4d, 0x31, 0x30, 0x32, 0x34, 0x10, 0x08, 0x12, 0x17, 0x0a, 0x13, 0x41, 0x4c, + 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, 0x4d, 0x4c, 0x4b, 0x45, 0x4d, 0x5f, 0x37, 0x36, + 0x38, 0x10, 0x14, 0x12, 0x18, 0x0a, 0x14, 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, + 0x5f, 0x4d, 0x4c, 0x4b, 0x45, 0x4d, 0x5f, 0x31, 0x30, 0x32, 0x34, 0x10, 0x15, 0x2a, 0x56, 0x0a, + 0x09, 0x4b, 0x65, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x16, 0x4b, 0x45, + 0x59, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, + 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x4b, 0x45, 0x59, 0x5f, 0x53, 0x54, + 0x41, 0x54, 0x55, 0x53, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x01, 0x12, 0x16, 0x0a, + 0x12, 0x4b, 0x45, 0x59, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x52, 0x4f, 0x54, 0x41, + 0x54, 0x45, 0x44, 0x10, 0x02, 0x2a, 0x94, 0x01, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x4d, 0x6f, 0x64, + 0x65, 0x12, 0x18, 0x0a, 0x14, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, + 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x4b, + 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x52, + 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x01, 0x12, 0x1e, 0x0a, 0x1a, 0x4b, 0x45, 0x59, + 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, 0x52, 0x5f, 0x52, + 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x4b, 0x45, 0x59, + 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x10, 0x03, 0x12, 0x1c, + 0x0a, 0x18, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, + 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x04, 0x42, 0x82, 0x01, 0x0a, + 0x0a, 0x63, 0x6f, 0x6d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x42, 0x0c, 0x4f, 0x62, 0x6a, + 0x65, 0x63, 0x74, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2e, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x64, 0x66, 0x2f, + 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, + 0x6c, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0xa2, 0x02, 0x03, 0x50, 0x58, + 0x58, 0xaa, 0x02, 0x06, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0xca, 0x02, 0x06, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0xe2, 0x02, 0x12, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5c, 0x47, 0x50, 0x42, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x06, 0x50, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/sdk/basekey.go b/sdk/basekey.go index 96f43f927c..a445729acd 100644 --- a/sdk/basekey.go +++ b/sdk/basekey.go @@ -46,6 +46,10 @@ func KeyTypeToPolicyAlgorithm(kt ocrypto.KeyType) (policy.Algorithm, error) { return policy.Algorithm_ALGORITHM_HPQT_SECP256R1_MLKEM768, nil case ocrypto.HybridSecp384r1MLKEM1024Key: return policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024, nil + case ocrypto.MLKEM768Key: + return policy.Algorithm_ALGORITHM_MLKEM_768, nil + case ocrypto.MLKEM1024Key: + return policy.Algorithm_ALGORITHM_MLKEM_1024, nil default: return policy.Algorithm_ALGORITHM_UNSPECIFIED, fmt.Errorf("unknown key type: %s", kt) } @@ -69,6 +73,10 @@ func PolicyAlgorithmToKeyType(alg policy.Algorithm) (ocrypto.KeyType, error) { return ocrypto.HybridSecp256r1MLKEM768Key, nil case policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024: return ocrypto.HybridSecp384r1MLKEM1024Key, nil + case policy.Algorithm_ALGORITHM_MLKEM_768: + return ocrypto.MLKEM768Key, nil + case policy.Algorithm_ALGORITHM_MLKEM_1024: + return ocrypto.MLKEM1024Key, nil case policy.Algorithm_ALGORITHM_UNSPECIFIED: fallthrough default: diff --git a/sdk/experimental/tdf/key_access.go b/sdk/experimental/tdf/key_access.go index 3974b06cf4..ad849c739c 100644 --- a/sdk/experimental/tdf/key_access.go +++ b/sdk/experimental/tdf/key_access.go @@ -165,8 +165,8 @@ 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.IsKEMKeyType(ktype) { + return wrapKeyWithKEM(ktype, pubKeyInfo.PEM, symKey) } if ocrypto.IsECKeyType(ktype) { // Handle EC key wrapping @@ -249,10 +249,18 @@ func wrapKeyWithRSA(kasPublicKeyPEM string, symKey []byte) (string, error) { return string(ocrypto.Base64Encode(encryptedKey)), nil } -func wrapKeyWithHybrid(ktype ocrypto.KeyType, kasPublicKeyPEM string, symKey []byte) (string, string, string, error) { - wrappedDER, err := ocrypto.HybridWrapDEK(ktype, kasPublicKeyPEM, symKey) +// wrapKeyWithKEM wraps a DEK with any KEM scheme — pure ML-KEM or hybrid +// (X-Wing, NIST PQ/T). Returns the base64-encoded envelope, the manifest +// scheme name (`hybrid-wrapped` or `mlkem-wrapped`), and an empty ephemeral +// key string (KEMs do not emit one in this profile). +func wrapKeyWithKEM(ktype ocrypto.KeyType, kasPublicKeyPEM string, symKey []byte) (string, string, string, error) { + wrappedDER, err := ocrypto.WrapDEK(ktype, kasPublicKeyPEM, symKey) if err != nil { - return "", "", "", fmt.Errorf("hybrid wrap failed: %w", err) + return "", "", "", fmt.Errorf("kem wrap failed: %w", err) } - return string(ocrypto.Base64Encode(wrappedDER)), "hybrid-wrapped", "", nil + scheme := "hybrid-wrapped" + if ocrypto.IsMLKEMKeyType(ktype) { + scheme = "mlkem-wrapped" + } + return string(ocrypto.Base64Encode(wrappedDER)), scheme, "", nil } diff --git a/sdk/experimental/tdf/keysplit/attributes.go b/sdk/experimental/tdf/keysplit/attributes.go index 27cc1e386f..f21fab3824 100644 --- a/sdk/experimental/tdf/keysplit/attributes.go +++ b/sdk/experimental/tdf/keysplit/attributes.go @@ -213,6 +213,10 @@ func convertAlgEnum2Simple(a policy.KasPublicKeyAlgEnum) policy.Algorithm { 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_MLKEM_768: + return policy.Algorithm_ALGORITHM_MLKEM_768 + case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024: + return policy.Algorithm_ALGORITHM_MLKEM_1024 case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED: return policy.Algorithm_ALGORITHM_UNSPECIFIED default: diff --git a/sdk/granter.go b/sdk/granter.go index ede578cc17..571616efa0 100644 --- a/sdk/granter.go +++ b/sdk/granter.go @@ -292,6 +292,10 @@ func convertAlgEnum2Simple(a policy.KasPublicKeyAlgEnum) policy.Algorithm { 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_MLKEM_768: + return policy.Algorithm_ALGORITHM_MLKEM_768 + case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024: + return policy.Algorithm_ALGORITHM_MLKEM_1024 case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED: return policy.Algorithm_ALGORITHM_UNSPECIFIED default: @@ -484,6 +488,10 @@ func algProto2String(e policy.KasPublicKeyAlgEnum) string { 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_MLKEM_768: + return string(ocrypto.MLKEM768Key) + case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024: + return string(ocrypto.MLKEM1024Key) case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED: return "" } diff --git a/sdk/tdf.go b/sdk/tdf.go index 1d6ef1182c..ce42099603 100644 --- a/sdk/tdf.go +++ b/sdk/tdf.go @@ -44,6 +44,7 @@ const ( kWrapped = "wrapped" kECWrapped = "ec-wrapped" kHybridWrapped = "hybrid-wrapped" + kMLKEMWrapped = "mlkem-wrapped" kKasProtocol = "kas" kSplitKeyType = "split" kGCMCipherAlgorithm = "AES-256-GCM" @@ -676,12 +677,12 @@ func createKeyAccess(kasInfo KASInfo, symKey []byte, policyBinding PolicyBinding ktype := ocrypto.KeyType(kasInfo.Algorithm) switch { - case ocrypto.IsHybridKeyType(ktype): - wrappedKey, err := generateWrapKeyWithHybrid(kasInfo.Algorithm, kasInfo.PublicKey, symKey) + case ocrypto.IsKEMKeyType(ktype): + wrappedKey, scheme, err := generateWrapKeyWithKEM(ktype, kasInfo.PublicKey, symKey) if err != nil { return KeyAccess{}, err } - keyAccess.KeyType = kHybridWrapped + keyAccess.KeyType = scheme keyAccess.WrappedKey = wrappedKey case ocrypto.IsECKeyType(ktype): mode, err := ocrypto.ECKeyTypeToMode(ktype) @@ -770,12 +771,19 @@ func generateWrapKeyWithRSA(publicKey string, symKey []byte) (string, error) { return string(ocrypto.Base64Encode(wrappedKey)), nil } -func generateWrapKeyWithHybrid(algorithm, publicKeyPEM string, symKey []byte) (string, error) { - wrappedDER, err := ocrypto.HybridWrapDEK(ocrypto.KeyType(algorithm), publicKeyPEM, symKey) +// generateWrapKeyWithKEM wraps a DEK with any KEM scheme — pure ML-KEM or +// hybrid (X-Wing, NIST PQ/T). Returns the base64-encoded envelope and the +// wire scheme name (`hybrid-wrapped` or `mlkem-wrapped`) for the manifest. +func generateWrapKeyWithKEM(ktype ocrypto.KeyType, publicKeyPEM string, symKey []byte) (string, string, error) { + wrappedDER, err := ocrypto.WrapDEK(ktype, publicKeyPEM, symKey) if err != nil { - return "", fmt.Errorf("generateWrapKeyWithHybrid: %w", err) + return "", "", fmt.Errorf("generateWrapKeyWithKEM: %w", err) } - return string(ocrypto.Base64Encode(wrappedDER)), nil + scheme := kHybridWrapped + if ocrypto.IsMLKEMKeyType(ktype) { + scheme = kMLKEMWrapped + } + return string(ocrypto.Base64Encode(wrappedDER)), scheme, nil } // create policy object @@ -1247,7 +1255,7 @@ func createRewrapRequest(_ context.Context, r *Reader) (map[string]*kas.Unsigned invalidPolicy = !ok alg, ok = policyBinding["alg"].(string) invalidPolicy = invalidPolicy || !ok - case (PolicyBinding): + case PolicyBinding: hash = policyBinding.Hash alg = policyBinding.Alg default: diff --git a/service/cmd/keygen/main.go b/service/cmd/keygen/main.go index 721191e286..f182339e6e 100644 --- a/service/cmd/keygen/main.go +++ b/service/cmd/keygen/main.go @@ -1,4 +1,4 @@ -// Package main generates hybrid post-quantum KAS key pairs (X-Wing, P256+ML-KEM-768, P384+ML-KEM-1024) +// Package main generates post-quantum KAS key pairs (X-Wing, P256+ML-KEM-768, P384+ML-KEM-1024, ML-KEM-768, ML-KEM-1024) // as PEM files for use with the OpenTDF platform. package main @@ -45,6 +45,18 @@ func main() { privateOut: "kas-p384mlkem1024-private.pem", publicOut: "kas-p384mlkem1024-public.pem", }, + { + name: "ML-KEM-768", + newKeyPair: generateMLKEM768, + privateOut: "kas-mlkem768-private.pem", + publicOut: "kas-mlkem768-public.pem", + }, + { + name: "ML-KEM-1024", + newKeyPair: generateMLKEM1024, + privateOut: "kas-mlkem1024-private.pem", + publicOut: "kas-mlkem1024-public.pem", + }, } for _, s := range specs { @@ -114,3 +126,35 @@ func generateP384MLKEM1024() (string, string, error) { } return priv, pub, nil } + +func generateMLKEM768() (string, string, error) { + kp, err := ocrypto.NewMLKEMKeyPair() + 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 generateMLKEM1024() (string, string, error) { + kp, err := ocrypto.NewMLKEM1024KeyPair() + 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 a33ce486f7..0a8e8d382b 100644 --- a/service/internal/security/basic_manager.go +++ b/service/internal/security/basic_manager.go @@ -82,17 +82,14 @@ func (b *BasicManager) Decrypt(ctx context.Context, keyDetails trust.KeyDetails, return nil, fmt.Errorf("failed to create decryptor from private PEM: %w", err) } - switch keyDetails.Algorithm() { + alg := keyDetails.Algorithm() + switch alg { //nolint:exhaustive // KEM key types are handled by the IsKEMKeyType branch below case ocrypto.RSA2048Key, ocrypto.RSA4096Key: plaintext, err := decrypter.Decrypt(ciphertext) if err != nil { return nil, fmt.Errorf("failed to decrypt with RSA: %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 ocrypto.NewAESProtectedKey(plaintext) case ocrypto.EC256Key, ocrypto.EC384Key, ocrypto.EC521Key: ecPrivKey, err := ocrypto.ECPrivateKeyFromPem(privKey) if err != nil { @@ -106,65 +103,21 @@ func (b *BasicManager) Decrypt(ctx context.Context, keyDetails trust.KeyDetails, if err != nil { return nil, fmt.Errorf("failed to decrypt with ephemeral key: %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.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: + return ocrypto.NewAESProtectedKey(plaintext) + } + + if ocrypto.IsKEMKeyType(alg) { 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) + return nil, fmt.Errorf("ephemeral public key should not be provided for %s decryption", alg) } - 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) + plaintext, err := decrypter.Decrypt(ciphertext) if err != nil { - return nil, fmt.Errorf("failed to create protected key: %w", err) + return nil, fmt.Errorf("failed to decrypt with %s: %w", alg, err) } - return protectedKey, nil + return ocrypto.NewAESProtectedKey(plaintext) } - return nil, fmt.Errorf("unsupported algorithm: %s", keyDetails.Algorithm()) + return nil, fmt.Errorf("unsupported algorithm: %s", alg) } func (b *BasicManager) DeriveKey(ctx context.Context, keyDetails trust.KeyDetails, ephemeralPublicKeyBytes []byte, curve elliptic.Curve) (ocrypto.ProtectedKey, error) { diff --git a/service/internal/security/crypto_provider.go b/service/internal/security/crypto_provider.go index dcbe1ae5a1..4a0b9a28ec 100644 --- a/service/internal/security/crypto_provider.go +++ b/service/internal/security/crypto_provider.go @@ -18,4 +18,8 @@ const ( // Used for hybrid NIST EC + ML-KEM wrapping of the KAO AlgorithmHPQTSecp256r1MLKEM768 = "hpqt:secp256r1-mlkem768" AlgorithmHPQTSecp384r1MLKEM1024 = "hpqt:secp384r1-mlkem1024" + + // Used for encryption with ML-KEM of the KAO + AlgorithmMLKEM768 = "mlkem:768" + AlgorithmMLKEM1024 = "mlkem:1024" ) diff --git a/service/internal/security/in_process_provider.go b/service/internal/security/in_process_provider.go index 7d4b624e22..bf1d99c3c8 100644 --- a/service/internal/security/in_process_provider.go +++ b/service/internal/security/in_process_provider.go @@ -27,6 +27,8 @@ var InProcessSupportedAlgorithms = []ocrypto.KeyType{ ocrypto.HybridXWingKey, ocrypto.HybridSecp256r1MLKEM768Key, ocrypto.HybridSecp384r1MLKEM1024Key, + ocrypto.MLKEM768Key, + ocrypto.MLKEM1024Key, } func convertPEMToJWK(_ string) (string, error) { @@ -96,11 +98,8 @@ 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 + if kemKey, err := k.cryptoProvider.KEMPublicKey(kid); err == nil { + return kemKey, nil } return k.cryptoProvider.ECPublicKey(kid) default: @@ -274,6 +273,12 @@ func (a *InProcessProvider) Decrypt(ctx context.Context, keyDetails trust.KeyDet } return a.cryptoProvider.Decrypt(ctx, trust.KeyIdentifier(kid), ciphertext, nil) + case AlgorithmMLKEM768, AlgorithmMLKEM1024: + if len(ephemeralPublicKey) > 0 { + return nil, errors.New("ephemeral public key should not be provided for ML-KEM decryption") + } + return a.cryptoProvider.Decrypt(ctx, trust.KeyIdentifier(kid), ciphertext, nil) + default: return nil, errors.New("unsupported key algorithm") } @@ -360,9 +365,7 @@ func (a *InProcessProvider) determineKeyType(kid string) (string, error) { return key.Algorithm, nil case StandardECCrypto: return key.Algorithm, nil - case StandardXWingCrypto: - return key.Algorithm, nil - case StandardHybridCrypto: + case StandardKEMCrypto: return key.Algorithm, nil } diff --git a/service/internal/security/in_process_provider_test.go b/service/internal/security/in_process_provider_test.go index 9870ad53bb..e56eca2cfc 100644 --- a/service/internal/security/in_process_provider_test.go +++ b/service/internal/security/in_process_provider_test.go @@ -229,3 +229,29 @@ func TestInProcessProviderDetermineKeyType(t *testing.T) { _, err = provider.determineKeyType("missing") require.Error(t, err) } + +func TestInProcessProviderDetermineKeyTypeMLKEM(t *testing.T) { + cryptoProvider, material := newStandardCryptoWithMLKEMForTest(t) + providerIface := NewSecurityProviderAdapter(cryptoProvider, nil, nil) + provider, ok := providerIface.(*InProcessProvider) + require.True(t, ok) + + keyType, err := provider.determineKeyType(material.mlkem768Kid) + require.NoError(t, err) + assert.Equal(t, AlgorithmMLKEM768, keyType) + + keyType, err = provider.determineKeyType(material.mlkem1024Kid) + require.NoError(t, err) + assert.Equal(t, AlgorithmMLKEM1024, keyType) + + details, err := provider.FindKeyByID(t.Context(), trust.KeyIdentifier(material.mlkem768Kid)) + require.NoError(t, err) + assert.Equal(t, ocrypto.KeyType(AlgorithmMLKEM768), details.Algorithm()) + + details, err = provider.FindKeyByID(t.Context(), trust.KeyIdentifier(material.mlkem1024Kid)) + require.NoError(t, err) + assert.Equal(t, ocrypto.KeyType(AlgorithmMLKEM1024), details.Algorithm()) + + _, err = provider.determineKeyType("missing") + require.Error(t, err) +} diff --git a/service/internal/security/standard_crypto.go b/service/internal/security/standard_crypto.go index 59953a8523..95c2efce28 100644 --- a/service/internal/security/standard_crypto.go +++ b/service/internal/security/standard_crypto.go @@ -67,16 +67,14 @@ type StandardECCrypto struct { sk *ecdh.PrivateKey } -type StandardXWingCrypto struct { +// StandardKEMCrypto holds any KEM-based key (X-Wing, NIST hybrid PQ/T, +// or pure ML-KEM). The decryptor is created at load time so per-call +// dispatch reduces to decryptor.Decrypt(ciphertext). +type StandardKEMCrypto struct { KeyPairInfo - xwingPrivateKeyPem string - xwingPublicKeyPem string -} - -type StandardHybridCrypto struct { - KeyPairInfo - hybridPrivateKeyPem string - hybridPublicKeyPem string + privateKeyPem string + publicKeyPem string + decryptor ocrypto.PrivateKeyDecryptor } // List of keys by identifier @@ -120,7 +118,8 @@ func loadKeys(ks []KeyPairInfo) (*StandardCrypto, error) { keysByAlg := make(map[string]keylist) keysByID := make(keylist) for _, k := range ks { - slog.Info("crypto cfg loading", + slog.Info( + "crypto cfg loading", slog.Any("id", k.KID), slog.Any("alg", k.Algorithm), ) @@ -162,17 +161,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), + case AlgorithmHPQTXWing, + AlgorithmHPQTSecp256r1MLKEM768, AlgorithmHPQTSecp384r1MLKEM1024, + AlgorithmMLKEM768, AlgorithmMLKEM1024: + decryptor, err := ocrypto.FromPrivatePEM(string(privatePEM)) + if err != nil { + return nil, fmt.Errorf("ocrypto.FromPrivatePEM (%s) failed: %w", k.Algorithm, err) + } + return StandardKEMCrypto{ + KeyPairInfo: k, + privateKeyPem: string(privatePEM), + publicKeyPem: string(certPEM), + decryptor: decryptor, }, nil case AlgorithmRSA2048, AlgorithmRSA4096: asymDecryption, err := ocrypto.NewAsymDecryption(string(privatePEM)) @@ -247,7 +247,8 @@ func loadDeprecatedKeys(rsaKeys map[string]StandardKeyInfo, ecKeys map[string]St keysByID[id] = k } for id, kasInfo := range ecKeys { - slog.Info("cfg.ECKeys", + slog.Info( + "cfg.ECKeys", slog.String("id", id), slog.Any("kasInfo", kasInfo), ) @@ -353,40 +354,21 @@ func (s StandardCrypto) ECPublicKey(kid string) (string, error) { return string(pemBytes), nil } -func (s StandardCrypto) XWingPublicKey(kid string) (string, error) { +// KEMPublicKey returns the public-key PEM for any KEM-based key +// (X-Wing, NIST hybrid PQ/T, or pure ML-KEM). +func (s StandardCrypto) KEMPublicKey(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) + return "", fmt.Errorf("no key with id [%s]: %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] + kem, ok := k.(StandardKEMCrypto) if !ok { - return "", fmt.Errorf("no hybrid key with id [%s]: %w", kid, ErrCertNotFound) + return "", fmt.Errorf("key with id [%s] is not a KEM key: %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) + if kem.publicKeyPem == "" { + return "", fmt.Errorf("no public key with id [%s]: %w", kid, ErrCertNotFound) } + return kem.publicKeyPem, nil } func (s StandardCrypto) RSADecrypt(_ crypto.Hash, kid string, _ string, ciphertext []byte) ([]byte, error) { @@ -499,47 +481,14 @@ func (s *StandardCrypto) Decrypt(_ context.Context, keyID trust.KeyIdentifier, c return nil, fmt.Errorf("error decrypting data: %w", err) } - case StandardXWingCrypto: + case StandardKEMCrypto: if len(ephemeralPublicKey) > 0 { - return nil, errors.New("ephemeral public key should not be provided for X-Wing decryption") + return nil, fmt.Errorf("ephemeral public key should not be provided for %s decryption", key.Algorithm) } - privateKey, err := ocrypto.XWingPrivateKeyFromPem([]byte(key.xwingPrivateKeyPem)) + rawKey, err = key.decryptor.Decrypt(ciphertext) 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) + return nil, fmt.Errorf("failed to decrypt with %s: %w", key.Algorithm, err) } default: diff --git a/service/internal/security/test_helpers_test.go b/service/internal/security/test_helpers_test.go index f3736529b7..f15144a1f1 100644 --- a/service/internal/security/test_helpers_test.go +++ b/service/internal/security/test_helpers_test.go @@ -17,6 +17,14 @@ type testKeyMaterial struct { ecKid string ecPrivatePEM string ecPublicPEM string + + mlkem768Kid string + mlkem768PrivatePEM string + mlkem768PublicPEM string + + mlkem1024Kid string + mlkem1024PrivatePEM string + mlkem1024PublicPEM string } func writeTempFile(t *testing.T, dir, name, contents string) string { @@ -83,6 +91,55 @@ func newStandardCryptoForTest(t *testing.T, includeRSA, includeEC bool) (*Standa return crypto, material } +func newStandardCryptoWithMLKEMForTest(t *testing.T) (*StandardCrypto, testKeyMaterial) { + t.Helper() + + dir := t.TempDir() + var keys []KeyPairInfo + var material testKeyMaterial + + kp768, err := ocrypto.NewMLKEMKeyPair() + require.NoError(t, err) + mlkem768Private, err := kp768.PrivateKeyInPemFormat() + require.NoError(t, err) + mlkem768Public, err := kp768.PublicKeyInPemFormat() + require.NoError(t, err) + + material.mlkem768Kid = "mlkem768-test-key" + material.mlkem768PrivatePEM = mlkem768Private + material.mlkem768PublicPEM = mlkem768Public + + keys = append(keys, KeyPairInfo{ + Algorithm: AlgorithmMLKEM768, + KID: material.mlkem768Kid, + Private: writeTempFile(t, dir, "mlkem768-private.pem", mlkem768Private), + Certificate: writeTempFile(t, dir, "mlkem768-public.pem", mlkem768Public), + }) + + kp1024, err := ocrypto.NewMLKEM1024KeyPair() + require.NoError(t, err) + mlkem1024Private, err := kp1024.PrivateKeyInPemFormat() + require.NoError(t, err) + mlkem1024Public, err := kp1024.PublicKeyInPemFormat() + require.NoError(t, err) + + material.mlkem1024Kid = "mlkem1024-test-key" + material.mlkem1024PrivatePEM = mlkem1024Private + material.mlkem1024PublicPEM = mlkem1024Public + + keys = append(keys, KeyPairInfo{ + Algorithm: AlgorithmMLKEM1024, + KID: material.mlkem1024Kid, + Private: writeTempFile(t, dir, "mlkem1024-private.pem", mlkem1024Private), + Certificate: writeTempFile(t, dir, "mlkem1024-public.pem", mlkem1024Public), + }) + + crypto, err := NewStandardCrypto(StandardConfig{Keys: keys}) + require.NoError(t, err) + + return crypto, material +} + func exportProtectedKey(t *testing.T, key ocrypto.ProtectedKey) []byte { t.Helper() raw, err := (&noOpEncapsulator{}).Encapsulate(key) diff --git a/service/kas/access/publicKey.go b/service/kas/access/publicKey.go index d7f381c307..14c6dd8a05 100644 --- a/service/kas/access/publicKey.go +++ b/service/kas/access/publicKey.go @@ -77,7 +77,8 @@ func (p *Provider) LegacyPublicKey(ctx context.Context, req *connect.Request[kas return nil, connect.NewError(connect.CodeInternal, errors.Join(ErrConfig, errors.New("configuration error"))) } case security.AlgorithmRSA2048, security.AlgorithmHPQTXWing, - security.AlgorithmHPQTSecp256r1MLKEM768, security.AlgorithmHPQTSecp384r1MLKEM1024, "": + security.AlgorithmHPQTSecp256r1MLKEM768, security.AlgorithmHPQTSecp384r1MLKEM1024, + security.AlgorithmMLKEM768, security.AlgorithmMLKEM1024, "": // For RSA keys, return the public key in PKCS8 format pem, err = keyDetails.ExportPublicKey(ctx, trust.KeyTypePKCS8) if err != nil { @@ -154,7 +155,9 @@ func (p *Provider) PublicKey(ctx context.Context, req *connect.Request[kaspb.Pub return r(ecPublicKeyPem, kid, err) case security.AlgorithmHPQTXWing, security.AlgorithmHPQTSecp256r1MLKEM768, - security.AlgorithmHPQTSecp384r1MLKEM1024: + security.AlgorithmHPQTSecp384r1MLKEM1024, + security.AlgorithmMLKEM768, + security.AlgorithmMLKEM1024: switch fmt { case "pkcs8", "": publicKeyPEM, err := keyDetails.ExportPublicKey(ctx, trust.KeyTypePKCS8) diff --git a/service/kas/access/rewrap.go b/service/kas/access/rewrap.go index aba7ffa84e..c5e18b34b6 100644 --- a/service/kas/access/rewrap.go +++ b/service/kas/access/rewrap.go @@ -160,7 +160,8 @@ func (p *Provider) parseSRT(ctx context.Context, srt string) (jwt.Token, string, "unable to validate or parse token", slog.Any("error", err), slog.Int("srt_length", len(srt)), - jwkThumbprintAttr(ctxAuth.GetJWKFromContext(ctx, p.Logger))) + jwkThumbprintAttr(ctxAuth.GetJWKFromContext(ctx, p.Logger)), + ) return nil, "", err401("could not parse token") } @@ -187,7 +188,8 @@ func (p *Provider) logSRTValidationFailure(ctx context.Context, token jwt.Token, issuedAt := token.IssuedAt() if !issuedAt.IsZero() { - fields = append(fields, + fields = append( + fields, slog.Time("iat", issuedAt), slog.Duration("iat_delta", issuedAt.Sub(now)), ) @@ -198,7 +200,8 @@ func (p *Provider) logSRTValidationFailure(ctx context.Context, token jwt.Token, expires := token.Expiration() if !expires.IsZero() { - fields = append(fields, + fields = append( + fields, slog.Time("exp", expires), slog.Duration("exp_delta", now.Sub(expires)), ) @@ -209,7 +212,8 @@ func (p *Provider) logSRTValidationFailure(ctx context.Context, token jwt.Token, notBefore := token.NotBefore() if !notBefore.IsZero() { - fields = append(fields, + fields = append( + fields, slog.Time("nbf", notBefore), slog.Duration("nbf_delta", notBefore.Sub(now)), ) @@ -261,7 +265,8 @@ func (p *Provider) verifySRTSignature(ctx context.Context, srt string, dpopJWK j ) if err != nil { if p.Logger != nil { - p.Logger.WarnContext(ctx, + p.Logger.WarnContext( + ctx, "unable to verify request token", slog.Int("srt_length", len(srt)), jwkThumbprintAttr(dpopJWK), @@ -370,7 +375,8 @@ func (p *Provider) extractSRTBody(ctx context.Context, headers http.Header, in * err := protojson.UnmarshalOptions{DiscardUnknown: true}.Unmarshal([]byte(rbString), &requestBody) // if there are no requests then it could be a v1 request if err != nil { - p.Logger.WarnContext(ctx, + p.Logger.WarnContext( + ctx, "invalid SRT", slog.Any("err_v2", err), slog.Int("rb_string_length", len(rbString)), @@ -382,7 +388,8 @@ func (p *Provider) extractSRTBody(ctx context.Context, headers http.Header, in * var errv1 error if requestBody, errv1 = extractAndConvertV1SRTBody([]byte(rbString)); errv1 != nil { - p.Logger.WarnContext(ctx, + p.Logger.WarnContext( + ctx, "invalid SRT", slog.Any("err_v1", errv1), slog.Int("rb_string_length", len(rbString)), @@ -393,7 +400,8 @@ func (p *Provider) extractSRTBody(ctx context.Context, headers http.Header, in * isV1 = true } // TODO: this log is too big and should be reconsidered or removed - p.Logger.DebugContext(ctx, + p.Logger.DebugContext( + ctx, "extracted request body", slog.String("rewrap_body", requestBody.String()), slog.String("rewrap_srt", rbString), @@ -594,7 +602,8 @@ func (p *Provider) Rewrap(ctx context.Context, req *connect.Request[kaspb.Rewrap } kaoResults := *getMapValue(results) if len(kaoResults) != 1 { - p.Logger.WarnContext(ctx, + p.Logger.WarnContext( + ctx, "status 400 due to wrong result set size", slog.Any("kao_results", kaoResults), slog.Any("results", results), @@ -681,7 +690,8 @@ func (p *Provider) verifyRewrapRequests(ctx context.Context, req *kaspb.Unsigned // Get EC key size and convert to mode keySize, err := ocrypto.GetECKeySize([]byte(ephemeralPubKeyPEM)) if err != nil { - p.Logger.WarnContext(ctx, + p.Logger.WarnContext( + ctx, "failed to get EC key size", slog.Any("kao", kao), slog.Any("error", err), @@ -692,7 +702,8 @@ func (p *Provider) verifyRewrapRequests(ctx context.Context, req *kaspb.Unsigned mode, err := ocrypto.ECSizeToMode(keySize) if err != nil { - p.Logger.WarnContext(ctx, + p.Logger.WarnContext( + ctx, "failed to convert key size to mode", slog.Any("kao", kao), slog.Any("error", err), @@ -704,7 +715,8 @@ func (p *Provider) verifyRewrapRequests(ctx context.Context, req *kaspb.Unsigned // Parse the PEM public key block, _ := pem.Decode([]byte(ephemeralPubKeyPEM)) if block == nil { - p.Logger.WarnContext(ctx, + p.Logger.WarnContext( + ctx, "failed to decode PEM block", slog.Any("kao", kao), slog.Any("error", err), @@ -715,7 +727,8 @@ func (p *Provider) verifyRewrapRequests(ctx context.Context, req *kaspb.Unsigned pub, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { - p.Logger.WarnContext(ctx, + p.Logger.WarnContext( + ctx, "failed to parse public key", slog.Any("kao", kao), slog.Any("error", err), @@ -760,6 +773,20 @@ func (p *Provider) verifyRewrapRequests(ctx context.Context, req *kaspb.Unsigned failedKAORewrap(results, kao, err400("bad request")) continue } + case "mlkem-wrapped": + if !p.HybridTDFEnabled && !p.Preview.HybridTDFEnabled { + p.Logger.WarnContext(ctx, "mlkem-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 ML-KEM key", slog.Any("error", err)) + failedKAORewrap(results, kao, err400("bad request")) + continue + } case "wrapped": var kidsToCheck []trust.KeyIdentifier if kao.GetKeyAccessObject().GetKid() != "" { @@ -898,7 +925,8 @@ func (p *Provider) tdf3Rewrap(ctx context.Context, requests []*kaspb.UnsignedRew // Store per-KAO results even on error so tamper signals (e.g. corrupted // policy body → generic "bad request") reach the SDK rather than being // replaced by a top-level "invalid request". - p.Logger.WarnContext(ctx, + p.Logger.WarnContext( + ctx, "rewrap: verifyRewrapRequests failed", slog.String("policy_id", policyID), slog.Any("error", err), @@ -916,7 +944,8 @@ func (p *Provider) tdf3Rewrap(ctx context.Context, requests []*kaspb.UnsignedRew pdpAccessResults, accessErr := p.canAccess(ctx, tok, policies, additionalRewrapContext.Obligations.FulfillableFQNs) if accessErr != nil { - p.Logger.DebugContext(ctx, + p.Logger.DebugContext( + ctx, "tdf3rewrap: cannot access policy", slog.Any("policies", policies), slog.Any("error", accessErr), diff --git a/service/policy/db/grant_mappings.go b/service/policy/db/grant_mappings.go index 7cd8830f92..2d1683c56e 100644 --- a/service/policy/db/grant_mappings.go +++ b/service/policy/db/grant_mappings.go @@ -28,6 +28,10 @@ func mapAlgorithmToKasPublicKeyAlg(alg policy.Algorithm) policy.KasPublicKeyAlgE return policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 case policy.Algorithm_ALGORITHM_HPQT_SECP384R1_MLKEM1024: return policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 + case policy.Algorithm_ALGORITHM_MLKEM_768: + return policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + case policy.Algorithm_ALGORITHM_MLKEM_1024: + return policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 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 86e9fbc9a6..4b40df67cf 100644 --- a/service/policy/kasregistry/key_access_server_registry.proto +++ b/service/policy/kasregistry/key_access_server_registry.proto @@ -436,7 +436,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, 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 + expression: "this in [1, 2, 3, 4, 5, 6, 7, 8, 20, 21]" // 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, ALGORITHM_MLKEM_768, ALGORITHM_MLKEM_1024 }]; // The algorithm to be used for the key // Required KeyMode key_mode = 4 [(buf.validate.field).cel = { @@ -480,7 +480,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, 6, 7, 8]" // Allow unspecified and all supported algorithm values + expression: "this in [0, 1, 2, 3, 4, 5, 6, 7, 8, 20, 21]" // Allow unspecified and all supported algorithm values, including ALGORITHM_MLKEM_768 and ALGORITHM_MLKEM_1024 }]; // Filter keys by algorithm oneof kas_filter { @@ -593,7 +593,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, 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 + expression: "this in [1, 2, 3, 4, 5, 6, 7, 8, 20, 21]" // 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, ALGORITHM_MLKEM_768, ALGORITHM_MLKEM_1024 }]; // Required KeyMode key_mode = 3 [ diff --git a/service/policy/objects.proto b/service/policy/objects.proto index 2bf66902ae..3ad41ad7db 100644 --- a/service/policy/objects.proto +++ b/service/policy/objects.proto @@ -399,6 +399,8 @@ enum 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; + KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 = 20; + KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 = 21; } // Deprecated @@ -572,6 +574,8 @@ enum Algorithm { ALGORITHM_HPQT_XWING = 6; ALGORITHM_HPQT_SECP256R1_MLKEM768 = 7; ALGORITHM_HPQT_SECP384R1_MLKEM1024 = 8; + ALGORITHM_MLKEM_768 = 20; + ALGORITHM_MLKEM_1024 = 21; } // The status of the key diff --git a/test/tdf-roundtrips.bats b/test/tdf-roundtrips.bats index e6f63f4fca..cd16a6c84c 100755 --- a/test/tdf-roundtrips.bats +++ b/test/tdf-roundtrips.bats @@ -89,6 +89,48 @@ printf '%s\n' "$output" | grep "Hello P384+ML-KEM-1024 wrappers!" } +@test "examples: roundtrip Z-TDF with ML-KEM-768 wrapped KAO" { + echo "[INFO] create a tdf3 format file" + run go run ./examples encrypt -o sensitive-with-mlkem768.txt.tdf --autoconfigure=false -A "mlkem:768" "Hello ML-KEM-768 wrappers!" + echo "[INFO] echoing output; if successful, this is just the manifest" + echo "$output" + + echo "[INFO] Validate the manifest lists the expected type in its KAO" + kaotype=$(jq -r '.encryptionInformation.keyAccess[0].type' <<<"${output}") + echo "$kaotype" + [ "$kaotype" = mlkem-wrapped ] + + kid=$(jq -r '.encryptionInformation.keyAccess[0].kid' <<<"${output}") + echo "kao.kid=$kid" + [ "$kid" = m1 ] + + echo "[INFO] decrypting..." + run go run ./examples decrypt sensitive-with-mlkem768.txt.tdf + echo "$output" + printf '%s\n' "$output" | grep "Hello ML-KEM-768 wrappers!" +} + +@test "examples: roundtrip Z-TDF with ML-KEM-1024 wrapped KAO" { + echo "[INFO] create a tdf3 format file" + run go run ./examples encrypt -o sensitive-with-mlkem1024.txt.tdf --autoconfigure=false -A "mlkem:1024" "Hello ML-KEM-1024 wrappers!" + echo "[INFO] echoing output; if successful, this is just the manifest" + echo "$output" + + echo "[INFO] Validate the manifest lists the expected type in its KAO" + kaotype=$(jq -r '.encryptionInformation.keyAccess[0].type' <<<"${output}") + echo "$kaotype" + [ "$kaotype" = mlkem-wrapped ] + + kid=$(jq -r '.encryptionInformation.keyAccess[0].kid' <<<"${output}") + echo "kao.kid=$kid" + [ "$kid" = m2 ] + + echo "[INFO] decrypting..." + run go run ./examples decrypt sensitive-with-mlkem1024.txt.tdf + echo "$output" + printf '%s\n' "$output" | grep "Hello ML-KEM-1024 wrappers!" +} + @test "examples: legacy key support Z-TDF" { echo "[INFO] validating default key is r1" echo "[INFO] default key result: $(grpcurl "localhost:8080" "kas.AccessService/PublicKey")" @@ -272,6 +314,10 @@ services: alg: hpqt:secp256r1-mlkem768 - kid: h2 alg: hpqt:secp384r1-mlkem1024 + - kid: m1 + alg: mlkem:768 + - kid: m2 + alg: mlkem:1024 policy: enabled: true authorization: @@ -331,6 +377,14 @@ server: alg: hpqt:secp384r1-mlkem1024 private: kas-p384mlkem1024-private.pem cert: kas-p384mlkem1024-public.pem + - kid: m1 + alg: mlkem:768 + private: kas-mlkem768-private.pem + cert: kas-mlkem768-public.pem + - kid: m2 + alg: mlkem:1024 + private: kas-mlkem1024-private.pem + cert: kas-mlkem1024-public.pem port: 8080 opa: embedded: true diff --git a/tests-bdd/cukes/utils/utils_genKeys.go b/tests-bdd/cukes/utils/utils_genKeys.go index 3ad0b57599..5a3d344b95 100644 --- a/tests-bdd/cukes/utils/utils_genKeys.go +++ b/tests-bdd/cukes/utils/utils_genKeys.go @@ -207,7 +207,7 @@ 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. +// generateHybridKeys creates post-quantum key pairs: X-Wing, P256+ML-KEM-768, P384+ML-KEM-1024, ML-KEM-768, and ML-KEM-1024. func generateHybridKeys(outputPath string) { specs := []struct { name string @@ -218,6 +218,8 @@ func generateHybridKeys(outputPath 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"}, + {"ML-KEM-768", generateMLKEM768KeyPair, "kas-mlkem768-private.pem", "kas-mlkem768-public.pem"}, + {"ML-KEM-1024", generateMLKEM1024KeyPair, "kas-mlkem1024-private.pem", "kas-mlkem1024-public.pem"}, } for _, s := range specs { @@ -289,3 +291,35 @@ func generateP384MLKEM1024KeyPair() (string, string, error) { } return priv, pub, nil } + +func generateMLKEM768KeyPair() (string, string, error) { + kp, err := ocrypto.NewMLKEMKeyPair() + 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 generateMLKEM1024KeyPair() (string, string, error) { + kp, err := ocrypto.NewMLKEM1024KeyPair() + 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 +}