From 0a9764361ea5db5279291b6532a483bf4021f5f9 Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Mon, 1 Jun 2026 13:33:20 -0400 Subject: [PATCH 01/23] feat(policy,crypto): add ML-KEM 768/1024 algorithm support Add pure ML-KEM (Module-Lattice-Based Key-Encapsulation Mechanism) support alongside existing hybrid post-quantum algorithms. Changes: - Add ALGORITHM_MLKEM_768 and ALGORITHM_MLKEM_1024 to proto enums - Add ML-KEM key types (mlkem:768, mlkem:1024) to ocrypto library - Implement MLKEMKeyPair and MLKEM1024KeyPair with key generation - Update proto validation to accept ML-KEM algorithms - Regenerate all protocol buffers and OpenAPI docs This is a partial implementation. Remaining work includes: - Add ML-KEM encryption/decryption to asym_encryption.go - Add ML-KEM support to SDK and service layers - Add corresponding tests Co-Authored-By: Claude Sonnet 4.5 --- docs/grpc/index.html | 24 + .../authorization/authorization.openapi.yaml | 450 ++++--- .../v2/authorization.openapi.yaml | 542 ++++---- docs/openapi/common/common.openapi.yaml | 38 +- docs/openapi/entity/entity.openapi.yaml | 88 +- .../entity_resolution.openapi.yaml | 284 ++--- .../v2/entity_resolution.openapi.yaml | 294 ++--- docs/openapi/kas/kas.openapi.yaml | 265 ++-- .../policy/actions/actions.openapi.yaml | 501 ++++---- .../policy/attributes/attributes.openapi.yaml | 759 ++++++------ .../key_access_server_registry.openapi.yaml | 1102 +++++++++-------- .../keymanagement/key_management.openapi.yaml | 223 ++-- .../policy/namespaces/namespaces.openapi.yaml | 441 +++---- docs/openapi/policy/objects.openapi.yaml | 265 ++-- .../obligations/obligations.openapi.yaml | 655 +++++----- .../registered_resources.openapi.yaml | 634 +++++----- .../resource_mapping.openapi.yaml | 528 ++++---- docs/openapi/policy/selectors.openapi.yaml | 24 +- .../subject_mapping.openapi.yaml | 575 ++++----- .../openapi/policy/unsafe/unsafe.openapi.yaml | 530 ++++---- .../wellknown_configuration.openapi.yaml | 144 +-- lib/ocrypto/ec_key_pair.go | 108 +- .../key_access_server_registry.pb.go | 401 +++--- protocol/go/policy/objects.pb.go | 122 +- .../key_access_server_registry.proto | 4 +- service/policy/objects.proto | 4 + 26 files changed, 4581 insertions(+), 4424 deletions(-) diff --git a/docs/grpc/index.html b/docs/grpc/index.html index a59d73dd56..ded15f7de3 100644 --- a/docs/grpc/index.html +++ b/docs/grpc/index.html @@ -4134,6 +4134,18 @@

Algorithm

+ + ALGORITHM_MLKEM_768 + 20 +

+ + + + ALGORITHM_MLKEM_1024 + 21 +

+ + @@ -4263,6 +4275,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 1f3648bae8..cb0927c76c 100644 --- a/docs/openapi/authorization/authorization.openapi.yaml +++ b/docs/openapi/authorization/authorization.openapi.yaml @@ -109,6 +109,69 @@ paths: $ref: '#/components/schemas/authorization.GetEntitlementsResponse' components: schemas: + authorization.DecisionResponse.Decision: + type: string + title: Decision + enum: + - DECISION_UNSPECIFIED + - DECISION_DENY + - DECISION_PERMIT + authorization.Entity.Category: + type: string + title: Category + enum: + - CATEGORY_UNSPECIFIED + - CATEGORY_SUBJECT + - CATEGORY_ENVIRONMENT + policy.Action.StandardAction: + type: string + title: StandardAction + enum: + - STANDARD_ACTION_UNSPECIFIED + - STANDARD_ACTION_DECRYPT + - STANDARD_ACTION_TRANSMIT + policy.Algorithm: + type: string + title: Algorithm + enum: + - ALGORITHM_UNSPECIFIED + - 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 + description: Supported key algorithms. + policy.KasPublicKeyAlgEnum: + type: string + title: KasPublicKeyAlgEnum + enum: + - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED + - KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048 + - KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 + policy.SourceType: + type: string + title: SourceType + enum: + - SOURCE_TYPE_UNSPECIFIED + - SOURCE_TYPE_INTERNAL + - SOURCE_TYPE_EXTERNAL + description: |- + Describes whether this kas is managed by the organization or if they imported + the kas information from an external party. These two modes are necessary in order + to encrypt a tdf dek with an external parties kas public key. authorization.DecisionRequest: type: object properties: @@ -133,6 +196,7 @@ components: Example Request Get Decisions to answer the question - Do Bob (represented by entity chain ec1) and Alice (represented by entity chain ec2) have TRANSMIT authorization for 2 resources; resource1 (attr-set-1) defined by attributes foo:bar resource2 (attr-set-2) defined by attribute foo:bar, color:red ? + { "actions": [ { @@ -204,11 +268,13 @@ components: Example response for a Decision Request - Do Bob (represented by entity chain ec1) and Alice (represented by entity chain ec2) have TRANSMIT authorization for 2 resources; resource1 (attr-set-1) defined by attributes foo:bar resource2 (attr-set-2) defined by attribute foo:bar, color:red ? + Results: - bob has permitted authorization to transmit for a resource defined by attr-set-1 attributes and has a watermark obligation - bob has denied authorization to transmit a for a resource defined by attr-set-2 attributes - alice has permitted authorization to transmit for a resource defined by attr-set-1 attributes - alice has denied authorization to transmit a for a resource defined by attr-set-2 attributes + { "entityChainId": "ec1", "resourceAttributesId": "attr-set-1", @@ -232,92 +298,70 @@ components: "resourceAttributesId": "attr-set-2", "decision": "DECISION_DENY" } - authorization.DecisionResponse.Decision: - type: string - title: Decision - enum: - - DECISION_UNSPECIFIED - - DECISION_DENY - - DECISION_PERMIT authorization.Entity: type: object - allOf: + oneOf: - properties: - id: - type: string - title: id - description: ephemeral id for tracking between request and response - category: - title: category - $ref: '#/components/schemas/authorization.Entity.Category' - - oneOf: - - type: object - properties: - claims: - title: claims - $ref: '#/components/schemas/google.protobuf.Any' + claims: title: claims - required: - - claims - - type: object - properties: - clientId: - type: string - title: client_id + $ref: '#/components/schemas/google.protobuf.Any' + title: claims + required: + - claims + - properties: + clientId: + type: string title: client_id - required: - - clientId - - type: object - properties: - custom: - title: custom - $ref: '#/components/schemas/authorization.EntityCustom' + title: client_id + required: + - clientId + - properties: + custom: title: custom - required: - - custom - - type: object - properties: - emailAddress: - type: string - title: email_address - description: one of the entity options must be set + $ref: '#/components/schemas/authorization.EntityCustom' + title: custom + required: + - custom + - properties: + emailAddress: + type: string title: email_address - required: - - emailAddress - - type: object - properties: - remoteClaimsUrl: - type: string - title: remote_claims_url + description: one of the entity options must be set + title: email_address + required: + - emailAddress + - properties: + remoteClaimsUrl: + type: string title: remote_claims_url - required: - - remoteClaimsUrl - - type: object - properties: - userName: - type: string - title: user_name + title: remote_claims_url + required: + - remoteClaimsUrl + - properties: + userName: + type: string title: user_name - required: - - userName - - type: object - properties: - uuid: - type: string - title: uuid + title: user_name + required: + - userName + - properties: + uuid: + type: string title: uuid - required: - - uuid + title: uuid + required: + - uuid + properties: + id: + type: string + title: id + description: ephemeral id for tracking between request and response + category: + title: category + $ref: '#/components/schemas/authorization.Entity.Category' title: Entity additionalProperties: false description: PE (Person Entity) or NPE (Non-Person Entity) - authorization.Entity.Category: - type: string - title: Category - enum: - - CATEGORY_UNSPECIFIED - - CATEGORY_SUBJECT - - CATEGORY_ENVIRONMENT authorization.EntityChain: type: object properties: @@ -405,22 +449,22 @@ components: title: entities description: list of requested entities scope: - oneOf: - - $ref: '#/components/schemas/authorization.ResourceAttribute' - - type: "null" title: scope description: optional attribute fqn as a scope + nullable: true + $ref: '#/components/schemas/authorization.ResourceAttribute' withComprehensiveHierarchy: - type: - - boolean - - "null" + type: boolean title: with_comprehensive_hierarchy description: optional parameter to return a full list of entitlements - returns lower hierarchy attributes + nullable: true title: GetEntitlementsRequest additionalProperties: false description: |- Request to get entitlements for one or more entities for an optional attribute scope + Example: Get entitlements for bob and alice (both represented using an email address + { "entities": [ { @@ -451,6 +495,7 @@ components: additionalProperties: false description: |- Example Response for a request of : Get entitlements for bob and alice (both represented using an email address + { "entitlements": [ { @@ -522,6 +567,7 @@ components: Example Request Get Decisions by Token to answer the question - Do Bob and client1 (represented by token tok1) and Alice and client2 (represented by token tok2) have TRANSMIT authorization for 2 resources; resource1 (attr-set-1) defined by attributes foo:bar resource2 (attr-set-2) defined by attribute foo:bar, color:red ? + { "actions": [ { @@ -584,75 +630,6 @@ components: title: value title: LabelsEntry additionalProperties: false - connect-protocol-version: - type: number - title: Connect-Protocol-Version - enum: - - 1 - description: Define the version of the Connect protocol - const: 1 - connect-timeout-header: - type: number - title: Connect-Timeout-Ms - description: Define the timeout, in ms - connect.error: - type: object - properties: - code: - type: string - examples: - - not_found - enum: - - canceled - - unknown - - invalid_argument - - deadline_exceeded - - not_found - - already_exists - - permission_denied - - resource_exhausted - - failed_precondition - - aborted - - out_of_range - - unimplemented - - internal - - unavailable - - data_loss - - unauthenticated - description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. - message: - type: string - description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. - details: - type: array - items: - $ref: '#/components/schemas/connect.error_details.Any' - description: A list of messages that carry the error details. There is no limit on the number of messages. - title: Connect Error - additionalProperties: true - description: 'Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation' - connect.error_details.Any: - type: object - properties: - type: - type: string - description: 'A URL that acts as a globally unique identifier for the type of the serialized message. For example: `type.googleapis.com/google.rpc.ErrorInfo`. This is used to determine the schema of the data in the `value` field and is the discriminator for the `debug` field.' - value: - type: string - format: binary - description: The Protobuf message, serialized as bytes and base64-encoded. The specific message type is identified by the `type` field. - debug: - oneOf: - - type: object - title: Any - additionalProperties: true - description: Detailed error information. - discriminator: - propertyName: type - title: Debug - description: Deserialized error detail payload. The 'type' field indicates the schema. This field is for easier debugging and should not be relied upon for application logic. - additionalProperties: true - description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message, with an additional debug field for ConnectRPC error details. google.protobuf.Any: type: object properties: @@ -661,6 +638,9 @@ components: value: type: string format: binary + debug: + type: object + additionalProperties: true additionalProperties: true description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message. google.protobuf.BoolValue: @@ -675,8 +655,8 @@ components: google.protobuf.Timestamp: type: string examples: - - "2023-01-15T01:30:15.01Z" - - "2024-12-25T12:00:00Z" + - 1s + - 1.000340012s format: date-time description: |- A Timestamp represents a point in time independent of any time zone or local @@ -770,65 +750,41 @@ components: ) to obtain a formatter capable of generating timestamps in this format. policy.Action: type: object - allOf: + oneOf: - properties: - id: - type: string - title: id - description: Generated uuid in database - name: + custom: type: string - title: name - namespace: - title: namespace - description: Namespace context for this action - $ref: '#/components/schemas/policy.Namespace' - metadata: - title: metadata - $ref: '#/components/schemas/common.Metadata' - - oneOf: - - type: object - properties: - custom: - type: string - title: custom - description: Deprecated title: custom - required: - - custom - - type: object - properties: - standard: - title: standard - description: Deprecated - $ref: '#/components/schemas/policy.Action.StandardAction' + description: Deprecated + title: custom + required: + - custom + - properties: + standard: title: standard - required: - - standard + description: Deprecated + $ref: '#/components/schemas/policy.Action.StandardAction' + title: standard + required: + - standard + properties: + id: + type: string + title: id + description: Generated uuid in database + name: + type: string + title: name + namespace: + title: namespace + description: Namespace context for this action + $ref: '#/components/schemas/policy.Namespace' + metadata: + title: metadata + $ref: '#/components/schemas/common.Metadata' title: Action additionalProperties: false description: An action an entity can take - policy.Action.StandardAction: - type: string - title: StandardAction - enum: - - STANDARD_ACTION_UNSPECIFIED - - STANDARD_ACTION_DECRYPT - - STANDARD_ACTION_TRANSMIT - policy.Algorithm: - type: string - title: Algorithm - enum: - - ALGORITHM_UNSPECIFIED - - 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 - description: Supported key algorithms. policy.KasPublicKey: type: object properties: @@ -847,7 +803,7 @@ components: alg: not: enum: - - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED + - 0 title: alg description: |- A known algorithm type with any additional parameters encoded. @@ -859,19 +815,6 @@ components: description: |- Deprecated A KAS public key and some associated metadata for further identifcation - policy.KasPublicKeyAlgEnum: - type: string - title: KasPublicKeyAlgEnum - enum: - - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED - - KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048 - - KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 policy.KasPublicKeySet: type: object properties: @@ -894,9 +837,13 @@ components: uri: type: string title: uri - description: | + description: |+ Address of a KAS instance - uri_format // URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes. + URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes.: + ``` + this.matches('^https?://[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?)*(:[0-9]+)?(/.*)?$') + ``` + publicKey: title: public_key description: 'Deprecated: KAS can have multiple key pairs' @@ -964,8 +911,7 @@ components: policy.PublicKey: type: object oneOf: - - type: object - properties: + - properties: cached: title: cached description: public key with additional information. Current preferred version @@ -973,14 +919,17 @@ components: title: cached required: - cached - - type: object - properties: + - properties: remote: type: string title: remote - description: | + description: |+ kas public key url - optional since can also be retrieved via public key - uri_format // URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes. + URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes.: + ``` + this.matches('^https://[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?)*(/.*)?$') + ``` + title: remote required: - remote @@ -1018,17 +967,50 @@ components: title: pem title: SimpleKasPublicKey additionalProperties: false - policy.SourceType: - type: string - title: SourceType + connect-protocol-version: + type: number + title: Connect-Protocol-Version enum: - - SOURCE_TYPE_UNSPECIFIED - - SOURCE_TYPE_INTERNAL - - SOURCE_TYPE_EXTERNAL - description: |- - Describes whether this kas is managed by the organization or if they imported - the kas information from an external party. These two modes are necessary in order - to encrypt a tdf dek with an external parties kas public key. + - 1 + description: Define the version of the Connect protocol + const: 1 + connect-timeout-header: + type: number + title: Connect-Timeout-Ms + description: Define the timeout, in ms + connect.error: + type: object + properties: + code: + type: string + examples: + - not_found + enum: + - canceled + - unknown + - invalid_argument + - deadline_exceeded + - not_found + - already_exists + - permission_denied + - resource_exhausted + - failed_precondition + - aborted + - out_of_range + - unimplemented + - internal + - unavailable + - data_loss + - unauthenticated + description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. + message: + type: string + description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. + detail: + $ref: '#/components/schemas/google.protobuf.Any' + title: Connect Error + additionalProperties: true + description: 'Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation' security: [] tags: - name: authorization.AuthorizationService diff --git a/docs/openapi/authorization/v2/authorization.openapi.yaml b/docs/openapi/authorization/v2/authorization.openapi.yaml index b4dbfa42d6..aa97661424 100644 --- a/docs/openapi/authorization/v2/authorization.openapi.yaml +++ b/docs/openapi/authorization/v2/authorization.openapi.yaml @@ -37,12 +37,12 @@ paths: application/json: schema: $ref: '#/components/schemas/authorization.v2.GetDecisionResponse' - /authorization.v2.AuthorizationService/GetDecisionBulk: + /authorization.v2.AuthorizationService/GetDecisionMultiResource: post: tags: - authorization.v2.AuthorizationService - summary: GetDecisionBulk - operationId: authorization.v2.AuthorizationService.GetDecisionBulk + summary: GetDecisionMultiResource + operationId: authorization.v2.AuthorizationService.GetDecisionMultiResource parameters: - name: Connect-Protocol-Version in: header @@ -57,7 +57,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/authorization.v2.GetDecisionBulkRequest' + $ref: '#/components/schemas/authorization.v2.GetDecisionMultiResourceRequest' required: true responses: default: @@ -71,13 +71,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/authorization.v2.GetDecisionBulkResponse' - /authorization.v2.AuthorizationService/GetDecisionMultiResource: + $ref: '#/components/schemas/authorization.v2.GetDecisionMultiResourceResponse' + /authorization.v2.AuthorizationService/GetDecisionBulk: post: tags: - authorization.v2.AuthorizationService - summary: GetDecisionMultiResource - operationId: authorization.v2.AuthorizationService.GetDecisionMultiResource + summary: GetDecisionBulk + operationId: authorization.v2.AuthorizationService.GetDecisionBulk parameters: - name: Connect-Protocol-Version in: header @@ -92,7 +92,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/authorization.v2.GetDecisionMultiResourceRequest' + $ref: '#/components/schemas/authorization.v2.GetDecisionBulkRequest' required: true responses: default: @@ -106,7 +106,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/authorization.v2.GetDecisionMultiResourceResponse' + $ref: '#/components/schemas/authorization.v2.GetDecisionBulkResponse' /authorization.v2.AuthorizationService/GetEntitlements: post: tags: @@ -151,6 +151,62 @@ components: - DECISION_UNSPECIFIED - DECISION_DENY - DECISION_PERMIT + entity.Entity.Category: + type: string + title: Category + enum: + - CATEGORY_UNSPECIFIED + - CATEGORY_SUBJECT + - CATEGORY_ENVIRONMENT + policy.Action.StandardAction: + type: string + title: StandardAction + enum: + - STANDARD_ACTION_UNSPECIFIED + - STANDARD_ACTION_DECRYPT + - STANDARD_ACTION_TRANSMIT + policy.Algorithm: + type: string + title: Algorithm + enum: + - ALGORITHM_UNSPECIFIED + - 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 + description: Supported key algorithms. + policy.KasPublicKeyAlgEnum: + type: string + title: KasPublicKeyAlgEnum + enum: + - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED + - KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048 + - KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 + policy.SourceType: + type: string + title: SourceType + enum: + - SOURCE_TYPE_UNSPECIFIED + - SOURCE_TYPE_INTERNAL + - SOURCE_TYPE_EXTERNAL + description: |- + Describes whether this kas is managed by the organization or if they imported + the kas information from an external party. These two modes are necessary in order + to encrypt a tdf dek with an external parties kas public key. authorization.v2.EntityEntitlements: type: object properties: @@ -191,19 +247,21 @@ components: authorization.v2.EntityIdentifier: type: object oneOf: - - type: object - properties: + - properties: entityChain: title: entity_chain - description: | + description: |+ chain of one or more entities and at most 10 - entity_chain_required // entities must be provided and between 1 and 10 in count + entities must be provided and between 1 and 10 in count: + ``` + has(this.entities) && this.entities.size() > 0 && this.entities.size() <= 10 + ``` + $ref: '#/components/schemas/entity.EntityChain' title: entity_chain required: - entityChain - - type: object - properties: + - properties: registeredResourceValueFqn: type: string title: registered_resource_value_fqn @@ -215,24 +273,30 @@ components: title: registered_resource_value_fqn required: - registeredResourceValueFqn - - type: object - properties: + - properties: token: title: token - description: | + description: |+ access token (JWT), which is used to create an entity chain (comprising one or more entities) - token_required // token must be provided + token must be provided: + ``` + has(this.jwt) && this.jwt.size() > 0 + ``` + $ref: '#/components/schemas/entity.Token' title: token required: - token - - type: object - properties: + - properties: withRequestToken: title: with_request_token - description: | + description: |+ derive the entity from the request's authorization access token JWT, rather than passing in the body - with_request_token_must_be_true // with_request_token must be true when set + with_request_token must be true when set: + ``` + this == true + ``` + $ref: '#/components/schemas/google.protobuf.BoolValue' title: with_request_token required: @@ -292,19 +356,27 @@ components: type: array items: type: string - description: | - obligation_value_fqns_valid // if provided, fulfillable_obligation_fqns must be between 1 and 50 in count with all valid FQNs + description: |+ + if provided, fulfillable_obligation_fqns must be between 1 and 50 in count with all valid FQNs: + ``` + this.size() == 0 || (this.size() <= 50 && this.all(item, item.isUri())) + ``` + title: fulfillable_obligation_fqns - description: | + description: |+ obligations (fully qualified values) the requester is capable of fulfilling i.e. https:///obl//value/ - obligation_value_fqns_valid // if provided, fulfillable_obligation_fqns must be between 1 and 50 in count with all valid FQNs + if provided, fulfillable_obligation_fqns must be between 1 and 50 in count with all valid FQNs: + ``` + this.size() == 0 || (this.size() <= 50 && this.all(item, item.isUri())) + ``` + title: GetDecisionMultiResourceRequest required: - entityIdentifier - action additionalProperties: false - description: | + description: |+ Can the identified entity/entities access? 1. one entity reference (actor) 2. one action @@ -313,7 +385,11 @@ components: If entitled, checks obligation policy: fulfillable obligations must satisfy all triggered. Note: this is a more performant bulk request for multiple resource decisions, up to 1000 per request - get_decision_multi_request.action_name_required // action.name must be provided + action.name must be provided: + ``` + has(this.action.name) + ``` + authorization.v2.GetDecisionMultiResourceResponse: type: object properties: @@ -347,27 +423,39 @@ components: type: array items: type: string - description: | - obligation_value_fqns_valid // if provided, fulfillable_obligation_fqns must be between 1 and 50 in count with all valid FQNs + description: |+ + if provided, fulfillable_obligation_fqns must be between 1 and 50 in count with all valid FQNs: + ``` + this.size() == 0 || (this.size() <= 50 && this.all(item, item.isUri())) + ``` + title: fulfillable_obligation_fqns - description: | + description: |+ obligations (fully qualified values) the requester is capable of fulfilling i.e. https:///obl//value/ - obligation_value_fqns_valid // if provided, fulfillable_obligation_fqns must be between 1 and 50 in count with all valid FQNs + if provided, fulfillable_obligation_fqns must be between 1 and 50 in count with all valid FQNs: + ``` + this.size() == 0 || (this.size() <= 50 && this.all(item, item.isUri())) + ``` + title: GetDecisionRequest required: - entityIdentifier - action - resource additionalProperties: false - description: | + description: |+ Can the identified entity/entities access? 1. one entity reference (actor) 2. one action 3. one resource If entitled, checks obligation policy: fulfillable obligations must satisfy all triggered. - get_decision_request.action_name_required // action.name must be provided + action.name must be provided: + ``` + has(this.action.name) + ``` + authorization.v2.GetDecisionResponse: type: object properties: @@ -385,13 +473,12 @@ components: description: an entity must be identified for entitlement decisioning $ref: '#/components/schemas/authorization.v2.EntityIdentifier' withComprehensiveHierarchy: - type: - - boolean - - "null" + type: boolean title: with_comprehensive_hierarchy description: |- optional parameter to return all entitled values for attribute definitions with hierarchy rules, propagating down the hierarchical values instead of returning solely the value that is directly entitled + nullable: true title: GetEntitlementsRequest required: - entityIdentifier @@ -413,35 +500,36 @@ components: additionalProperties: false authorization.v2.Resource: type: object - allOf: + oneOf: - properties: - ephemeralId: - type: string - title: ephemeral_id - description: ephemeral id for tracking between request and response - - oneOf: - - type: object - properties: - attributeValues: - title: attribute_values - description: | - a set of attribute value FQNs, such as those on a TDF, between 1 and 20 in count - attribute_values_required // if provided, resource.attribute_values must be between 1 and 20 in count with all valid FQNs - $ref: '#/components/schemas/authorization.v2.Resource.AttributeValues' + attributeValues: title: attribute_values - required: - - attributeValues - - type: object - properties: - registeredResourceValueFqn: - type: string - title: registered_resource_value_fqn - minLength: 1 - format: uri - description: fully qualified name of the registered resource value stored in platform policy + description: |+ + a set of attribute value FQNs, such as those on a TDF, between 1 and 20 in count + if provided, resource.attribute_values must be between 1 and 20 in count with all valid FQNs: + ``` + this.fqns.size() > 0 && this.fqns.size() <= 20 && this.fqns.all(item, item.isUri()) + ``` + + $ref: '#/components/schemas/authorization.v2.Resource.AttributeValues' + title: attribute_values + required: + - attributeValues + - properties: + registeredResourceValueFqn: + type: string title: registered_resource_value_fqn - required: - - registeredResourceValueFqn + minLength: 1 + format: uri + description: fully qualified name of the registered resource value stored in platform policy + title: registered_resource_value_fqn + required: + - registeredResourceValueFqn + properties: + ephemeralId: + type: string + title: ephemeral_id + description: ephemeral id for tracking between request and response title: Resource additionalProperties: false description: Either a set of attribute values (such as those on a TDF) or a registered resource value @@ -508,130 +596,49 @@ components: title: value title: LabelsEntry additionalProperties: false - connect-protocol-version: - type: number - title: Connect-Protocol-Version - enum: - - 1 - description: Define the version of the Connect protocol - const: 1 - connect-timeout-header: - type: number - title: Connect-Timeout-Ms - description: Define the timeout, in ms - connect.error: - type: object - properties: - code: - type: string - examples: - - not_found - enum: - - canceled - - unknown - - invalid_argument - - deadline_exceeded - - not_found - - already_exists - - permission_denied - - resource_exhausted - - failed_precondition - - aborted - - out_of_range - - unimplemented - - internal - - unavailable - - data_loss - - unauthenticated - description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. - message: - type: string - description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. - details: - type: array - items: - $ref: '#/components/schemas/connect.error_details.Any' - description: A list of messages that carry the error details. There is no limit on the number of messages. - title: Connect Error - additionalProperties: true - description: 'Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation' - connect.error_details.Any: - type: object - properties: - type: - type: string - description: 'A URL that acts as a globally unique identifier for the type of the serialized message. For example: `type.googleapis.com/google.rpc.ErrorInfo`. This is used to determine the schema of the data in the `value` field and is the discriminator for the `debug` field.' - value: - type: string - format: binary - description: The Protobuf message, serialized as bytes and base64-encoded. The specific message type is identified by the `type` field. - debug: - oneOf: - - type: object - title: Any - additionalProperties: true - description: Detailed error information. - discriminator: - propertyName: type - title: Debug - description: Deserialized error detail payload. The 'type' field indicates the schema. This field is for easier debugging and should not be relied upon for application logic. - additionalProperties: true - description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message, with an additional debug field for ConnectRPC error details. entity.Entity: type: object - allOf: + oneOf: - properties: - ephemeralId: - type: string - title: ephemeral_id - description: ephemeral id for tracking between request and response - category: - title: category - $ref: '#/components/schemas/entity.Entity.Category' - - oneOf: - - type: object - properties: - claims: - title: claims - description: used by ERS claims mode - $ref: '#/components/schemas/google.protobuf.Any' + claims: title: claims - required: - - claims - - type: object - properties: - clientId: - type: string - title: client_id + description: used by ERS claims mode + $ref: '#/components/schemas/google.protobuf.Any' + title: claims + required: + - claims + - properties: + clientId: + type: string title: client_id - required: - - clientId - - type: object - properties: - emailAddress: - type: string - title: email_address + title: client_id + required: + - clientId + - properties: + emailAddress: + type: string title: email_address - required: - - emailAddress - - type: object - properties: - userName: - type: string - title: user_name + title: email_address + required: + - emailAddress + - properties: + userName: + type: string title: user_name - required: - - userName + title: user_name + required: + - userName + properties: + ephemeralId: + type: string + title: ephemeral_id + description: ephemeral id for tracking between request and response + category: + title: category + $ref: '#/components/schemas/entity.Entity.Category' title: Entity additionalProperties: false description: PE (Person Entity) or NPE (Non-Person Entity) - entity.Entity.Category: - type: string - title: Category - enum: - - CATEGORY_UNSPECIFIED - - CATEGORY_SUBJECT - - CATEGORY_ENVIRONMENT entity.EntityChain: type: object properties: @@ -670,6 +677,9 @@ components: value: type: string format: binary + debug: + type: object + additionalProperties: true additionalProperties: true description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message. google.protobuf.BoolValue: @@ -684,8 +694,8 @@ components: google.protobuf.Timestamp: type: string examples: - - "2023-01-15T01:30:15.01Z" - - "2024-12-25T12:00:00Z" + - 1s + - 1.000340012s format: date-time description: |- A Timestamp represents a point in time independent of any time zone or local @@ -779,65 +789,41 @@ components: ) to obtain a formatter capable of generating timestamps in this format. policy.Action: type: object - allOf: + oneOf: - properties: - id: + custom: type: string - title: id - description: Generated uuid in database - name: - type: string - title: name - namespace: - title: namespace - description: Namespace context for this action - $ref: '#/components/schemas/policy.Namespace' - metadata: - title: metadata - $ref: '#/components/schemas/common.Metadata' - - oneOf: - - type: object - properties: - custom: - type: string - title: custom - description: Deprecated title: custom - required: - - custom - - type: object - properties: - standard: - title: standard - description: Deprecated - $ref: '#/components/schemas/policy.Action.StandardAction' + description: Deprecated + title: custom + required: + - custom + - properties: + standard: title: standard - required: - - standard + description: Deprecated + $ref: '#/components/schemas/policy.Action.StandardAction' + title: standard + required: + - standard + properties: + id: + type: string + title: id + description: Generated uuid in database + name: + type: string + title: name + namespace: + title: namespace + description: Namespace context for this action + $ref: '#/components/schemas/policy.Namespace' + metadata: + title: metadata + $ref: '#/components/schemas/common.Metadata' title: Action additionalProperties: false description: An action an entity can take - policy.Action.StandardAction: - type: string - title: StandardAction - enum: - - STANDARD_ACTION_UNSPECIFIED - - STANDARD_ACTION_DECRYPT - - STANDARD_ACTION_TRANSMIT - policy.Algorithm: - type: string - title: Algorithm - enum: - - ALGORITHM_UNSPECIFIED - - 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 - description: Supported key algorithms. policy.KasPublicKey: type: object properties: @@ -856,7 +842,7 @@ components: alg: not: enum: - - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED + - 0 title: alg description: |- A known algorithm type with any additional parameters encoded. @@ -868,19 +854,6 @@ components: description: |- Deprecated A KAS public key and some associated metadata for further identifcation - policy.KasPublicKeyAlgEnum: - type: string - title: KasPublicKeyAlgEnum - enum: - - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED - - KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048 - - KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 policy.KasPublicKeySet: type: object properties: @@ -903,9 +876,13 @@ components: uri: type: string title: uri - description: | + description: |+ Address of a KAS instance - uri_format // URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes. + URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes.: + ``` + this.matches('^https?://[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?)*(:[0-9]+)?(/.*)?$') + ``` + publicKey: title: public_key description: 'Deprecated: KAS can have multiple key pairs' @@ -973,8 +950,7 @@ components: policy.PublicKey: type: object oneOf: - - type: object - properties: + - properties: cached: title: cached description: public key with additional information. Current preferred version @@ -982,14 +958,17 @@ components: title: cached required: - cached - - type: object - properties: + - properties: remote: type: string title: remote - description: | + description: |+ kas public key url - optional since can also be retrieved via public key - uri_format // URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes. + URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes.: + ``` + this.matches('^https://[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?)*(/.*)?$') + ``` + title: remote required: - remote @@ -1027,17 +1006,50 @@ components: title: pem title: SimpleKasPublicKey additionalProperties: false - policy.SourceType: - type: string - title: SourceType + connect-protocol-version: + type: number + title: Connect-Protocol-Version enum: - - SOURCE_TYPE_UNSPECIFIED - - SOURCE_TYPE_INTERNAL - - SOURCE_TYPE_EXTERNAL - description: |- - Describes whether this kas is managed by the organization or if they imported - the kas information from an external party. These two modes are necessary in order - to encrypt a tdf dek with an external parties kas public key. + - 1 + description: Define the version of the Connect protocol + const: 1 + connect-timeout-header: + type: number + title: Connect-Timeout-Ms + description: Define the timeout, in ms + connect.error: + type: object + properties: + code: + type: string + examples: + - not_found + enum: + - canceled + - unknown + - invalid_argument + - deadline_exceeded + - not_found + - already_exists + - permission_denied + - resource_exhausted + - failed_precondition + - aborted + - out_of_range + - unimplemented + - internal + - unavailable + - data_loss + - unauthenticated + description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. + message: + type: string + description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. + detail: + $ref: '#/components/schemas/google.protobuf.Any' + title: Connect Error + additionalProperties: true + description: 'Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation' security: [] tags: - name: authorization.v2.AuthorizationService diff --git a/docs/openapi/common/common.openapi.yaml b/docs/openapi/common/common.openapi.yaml index 093091b6a7..0d1e60b152 100644 --- a/docs/openapi/common/common.openapi.yaml +++ b/docs/openapi/common/common.openapi.yaml @@ -13,14 +13,15 @@ components: - ACTIVE_STATE_ENUM_INACTIVE - ACTIVE_STATE_ENUM_ANY description: 'buflint ENUM_VALUE_PREFIX: to make sure that C++ scoping rules aren''t violated when users add new enum values to an enum in a given package' + common.MetadataUpdateEnum: + type: string + title: MetadataUpdateEnum + enum: + - METADATA_UPDATE_ENUM_UNSPECIFIED + - METADATA_UPDATE_ENUM_EXTEND + - METADATA_UPDATE_ENUM_REPLACE common.IdFqnIdentifier: type: object - allOf: - - oneOf: - - required: - - id - - required: - - fqn properties: id: type: string @@ -35,12 +36,6 @@ components: additionalProperties: false common.IdNameIdentifier: type: object - allOf: - - oneOf: - - required: - - id - - required: - - name properties: id: type: string @@ -51,8 +46,12 @@ components: title: name maxLength: 253 minLength: 1 - description: | - name_format // Name must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored name will be normalized to lower case. + description: |+ + Name must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored name will be normalized to lower case.: + ``` + this.matches('^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$') + ``` + title: IdNameIdentifier additionalProperties: false common.Metadata: @@ -110,18 +109,11 @@ components: title: value title: LabelsEntry additionalProperties: false - common.MetadataUpdateEnum: - type: string - title: MetadataUpdateEnum - enum: - - METADATA_UPDATE_ENUM_UNSPECIFIED - - METADATA_UPDATE_ENUM_EXTEND - - METADATA_UPDATE_ENUM_REPLACE google.protobuf.Timestamp: type: string examples: - - "2023-01-15T01:30:15.01Z" - - "2024-12-25T12:00:00Z" + - 1s + - 1.000340012s format: date-time description: |- A Timestamp represents a point in time independent of any time zone or local diff --git a/docs/openapi/entity/entity.openapi.yaml b/docs/openapi/entity/entity.openapi.yaml index 388fb1f7a3..6484c863a0 100644 --- a/docs/openapi/entity/entity.openapi.yaml +++ b/docs/openapi/entity/entity.openapi.yaml @@ -4,61 +4,56 @@ info: paths: {} components: schemas: + entity.Entity.Category: + type: string + title: Category + enum: + - CATEGORY_UNSPECIFIED + - CATEGORY_SUBJECT + - CATEGORY_ENVIRONMENT entity.Entity: type: object - allOf: + oneOf: - properties: - ephemeralId: - type: string - title: ephemeral_id - description: ephemeral id for tracking between request and response - category: - title: category - $ref: '#/components/schemas/entity.Entity.Category' - - oneOf: - - type: object - properties: - claims: - title: claims - description: used by ERS claims mode - $ref: '#/components/schemas/google.protobuf.Any' + claims: title: claims - required: - - claims - - type: object - properties: - clientId: - type: string - title: client_id + description: used by ERS claims mode + $ref: '#/components/schemas/google.protobuf.Any' + title: claims + required: + - claims + - properties: + clientId: + type: string title: client_id - required: - - clientId - - type: object - properties: - emailAddress: - type: string - title: email_address + title: client_id + required: + - clientId + - properties: + emailAddress: + type: string title: email_address - required: - - emailAddress - - type: object - properties: - userName: - type: string - title: user_name + title: email_address + required: + - emailAddress + - properties: + userName: + type: string title: user_name - required: - - userName + title: user_name + required: + - userName + properties: + ephemeralId: + type: string + title: ephemeral_id + description: ephemeral id for tracking between request and response + category: + title: category + $ref: '#/components/schemas/entity.Entity.Category' title: Entity additionalProperties: false description: PE (Person Entity) or NPE (Non-Person Entity) - entity.Entity.Category: - type: string - title: Category - enum: - - CATEGORY_UNSPECIFIED - - CATEGORY_SUBJECT - - CATEGORY_ENVIRONMENT entity.EntityChain: type: object properties: @@ -97,6 +92,9 @@ components: value: type: string format: binary + debug: + type: object + additionalProperties: true additionalProperties: true description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message. security: [] diff --git a/docs/openapi/entityresolution/entity_resolution.openapi.yaml b/docs/openapi/entityresolution/entity_resolution.openapi.yaml index 6a57e3b749..ac5e8021f4 100644 --- a/docs/openapi/entityresolution/entity_resolution.openapi.yaml +++ b/docs/openapi/entityresolution/entity_resolution.openapi.yaml @@ -2,13 +2,13 @@ openapi: 3.1.0 info: title: entityresolution paths: - /entityresolution.EntityResolutionService/CreateEntityChainFromJwt: + /entityresolution.EntityResolutionService/ResolveEntities: post: tags: - entityresolution.EntityResolutionService - summary: CreateEntityChainFromJwt - description: 'Deprecated: use v2 CreateEntityChainsFromTokens instead' - operationId: entityresolution.EntityResolutionService.CreateEntityChainFromJwt + summary: ResolveEntities + description: 'Deprecated: use v2 ResolveEntities instead' + operationId: entityresolution.EntityResolutionService.ResolveEntities parameters: - name: Connect-Protocol-Version in: header @@ -23,7 +23,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/entityresolution.CreateEntityChainFromJwtRequest' + $ref: '#/components/schemas/entityresolution.ResolveEntitiesRequest' required: true responses: default: @@ -37,14 +37,14 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/entityresolution.CreateEntityChainFromJwtResponse' - /entityresolution.EntityResolutionService/ResolveEntities: + $ref: '#/components/schemas/entityresolution.ResolveEntitiesResponse' + /entityresolution.EntityResolutionService/CreateEntityChainFromJwt: post: tags: - entityresolution.EntityResolutionService - summary: ResolveEntities - description: 'Deprecated: use v2 ResolveEntities instead' - operationId: entityresolution.EntityResolutionService.ResolveEntities + summary: CreateEntityChainFromJwt + description: 'Deprecated: use v2 CreateEntityChainsFromTokens instead' + operationId: entityresolution.EntityResolutionService.CreateEntityChainFromJwt parameters: - name: Connect-Protocol-Version in: header @@ -59,7 +59,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/entityresolution.ResolveEntitiesRequest' + $ref: '#/components/schemas/entityresolution.CreateEntityChainFromJwtRequest' required: true responses: default: @@ -73,88 +73,90 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/entityresolution.ResolveEntitiesResponse' + $ref: '#/components/schemas/entityresolution.CreateEntityChainFromJwtResponse' components: schemas: + authorization.Entity.Category: + type: string + title: Category + enum: + - CATEGORY_UNSPECIFIED + - CATEGORY_SUBJECT + - CATEGORY_ENVIRONMENT + google.protobuf.NullValue: + type: string + title: NullValue + enum: + - NULL_VALUE + description: |- + `NullValue` is a singleton enumeration to represent the null value for the + `Value` type union. + + The JSON representation for `NullValue` is JSON `null`. authorization.Entity: type: object - allOf: + oneOf: - properties: - id: - type: string - title: id - description: ephemeral id for tracking between request and response - category: - title: category - $ref: '#/components/schemas/authorization.Entity.Category' - - oneOf: - - type: object - properties: - claims: - title: claims - $ref: '#/components/schemas/google.protobuf.Any' + claims: title: claims - required: - - claims - - type: object - properties: - clientId: - type: string - title: client_id + $ref: '#/components/schemas/google.protobuf.Any' + title: claims + required: + - claims + - properties: + clientId: + type: string title: client_id - required: - - clientId - - type: object - properties: - custom: - title: custom - $ref: '#/components/schemas/authorization.EntityCustom' + title: client_id + required: + - clientId + - properties: + custom: title: custom - required: - - custom - - type: object - properties: - emailAddress: - type: string - title: email_address - description: one of the entity options must be set + $ref: '#/components/schemas/authorization.EntityCustom' + title: custom + required: + - custom + - properties: + emailAddress: + type: string title: email_address - required: - - emailAddress - - type: object - properties: - remoteClaimsUrl: - type: string - title: remote_claims_url + description: one of the entity options must be set + title: email_address + required: + - emailAddress + - properties: + remoteClaimsUrl: + type: string title: remote_claims_url - required: - - remoteClaimsUrl - - type: object - properties: - userName: - type: string - title: user_name + title: remote_claims_url + required: + - remoteClaimsUrl + - properties: + userName: + type: string title: user_name - required: - - userName - - type: object - properties: - uuid: - type: string - title: uuid + title: user_name + required: + - userName + - properties: + uuid: + type: string title: uuid - required: - - uuid + title: uuid + required: + - uuid + properties: + id: + type: string + title: id + description: ephemeral id for tracking between request and response + category: + title: category + $ref: '#/components/schemas/authorization.Entity.Category' title: Entity additionalProperties: false description: PE (Person Entity) or NPE (Non-Person Entity) - authorization.Entity.Category: - type: string - title: Category - enum: - - CATEGORY_UNSPECIFIED - - CATEGORY_SUBJECT - - CATEGORY_ENVIRONMENT authorization.EntityChain: type: object properties: @@ -192,75 +194,6 @@ components: description: the token title: Token additionalProperties: false - connect-protocol-version: - type: number - title: Connect-Protocol-Version - enum: - - 1 - description: Define the version of the Connect protocol - const: 1 - connect-timeout-header: - type: number - title: Connect-Timeout-Ms - description: Define the timeout, in ms - connect.error: - type: object - properties: - code: - type: string - examples: - - not_found - enum: - - canceled - - unknown - - invalid_argument - - deadline_exceeded - - not_found - - already_exists - - permission_denied - - resource_exhausted - - failed_precondition - - aborted - - out_of_range - - unimplemented - - internal - - unavailable - - data_loss - - unauthenticated - description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. - message: - type: string - description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. - details: - type: array - items: - $ref: '#/components/schemas/connect.error_details.Any' - description: A list of messages that carry the error details. There is no limit on the number of messages. - title: Connect Error - additionalProperties: true - description: 'Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation' - connect.error_details.Any: - type: object - properties: - type: - type: string - description: 'A URL that acts as a globally unique identifier for the type of the serialized message. For example: `type.googleapis.com/google.rpc.ErrorInfo`. This is used to determine the schema of the data in the `value` field and is the discriminator for the `debug` field.' - value: - type: string - format: binary - description: The Protobuf message, serialized as bytes and base64-encoded. The specific message type is identified by the `type` field. - debug: - oneOf: - - type: object - title: Any - additionalProperties: true - description: Detailed error information. - discriminator: - propertyName: type - title: Debug - description: Deserialized error detail payload. The 'type' field indicates the schema. This field is for easier debugging and should not be relied upon for application logic. - additionalProperties: true - description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message, with an additional debug field for ConnectRPC error details. entityresolution.CreateEntityChainFromJwtRequest: type: object properties: @@ -402,6 +335,9 @@ components: value: type: string format: binary + debug: + type: object + additionalProperties: true additionalProperties: true description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message. google.protobuf.ListValue: @@ -419,16 +355,6 @@ components: `ListValue` is a wrapper around a repeated field of values. The JSON representation for `ListValue` is JSON array. - google.protobuf.NullValue: - type: string - title: NullValue - enum: - - NULL_VALUE - description: |- - `NullValue` is a singleton enumeration to represent the null value for the - `Value` type union. - - The JSON representation for `NullValue` is JSON `null`. google.protobuf.Struct: type: object additionalProperties: @@ -469,6 +395,50 @@ components: variants. Absence of any variant indicates an error. The JSON representation for `Value` is JSON value. + connect-protocol-version: + type: number + title: Connect-Protocol-Version + enum: + - 1 + description: Define the version of the Connect protocol + const: 1 + connect-timeout-header: + type: number + title: Connect-Timeout-Ms + description: Define the timeout, in ms + connect.error: + type: object + properties: + code: + type: string + examples: + - not_found + enum: + - canceled + - unknown + - invalid_argument + - deadline_exceeded + - not_found + - already_exists + - permission_denied + - resource_exhausted + - failed_precondition + - aborted + - out_of_range + - unimplemented + - internal + - unavailable + - data_loss + - unauthenticated + description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. + message: + type: string + description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. + detail: + $ref: '#/components/schemas/google.protobuf.Any' + title: Connect Error + additionalProperties: true + description: 'Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation' security: [] tags: - name: entityresolution.EntityResolutionService diff --git a/docs/openapi/entityresolution/v2/entity_resolution.openapi.yaml b/docs/openapi/entityresolution/v2/entity_resolution.openapi.yaml index 52baa90875..b8e661c75a 100644 --- a/docs/openapi/entityresolution/v2/entity_resolution.openapi.yaml +++ b/docs/openapi/entityresolution/v2/entity_resolution.openapi.yaml @@ -2,12 +2,12 @@ openapi: 3.1.0 info: title: entityresolution.v2 paths: - /entityresolution.v2.EntityResolutionService/CreateEntityChainsFromTokens: + /entityresolution.v2.EntityResolutionService/ResolveEntities: post: tags: - entityresolution.v2.EntityResolutionService - summary: CreateEntityChainsFromTokens - operationId: entityresolution.v2.EntityResolutionService.CreateEntityChainsFromTokens + summary: ResolveEntities + operationId: entityresolution.v2.EntityResolutionService.ResolveEntities parameters: - name: Connect-Protocol-Version in: header @@ -22,7 +22,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/entityresolution.v2.CreateEntityChainsFromTokensRequest' + $ref: '#/components/schemas/entityresolution.v2.ResolveEntitiesRequest' required: true responses: default: @@ -36,13 +36,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/entityresolution.v2.CreateEntityChainsFromTokensResponse' - /entityresolution.v2.EntityResolutionService/ResolveEntities: + $ref: '#/components/schemas/entityresolution.v2.ResolveEntitiesResponse' + /entityresolution.v2.EntityResolutionService/CreateEntityChainsFromTokens: post: tags: - entityresolution.v2.EntityResolutionService - summary: ResolveEntities - operationId: entityresolution.v2.EntityResolutionService.ResolveEntities + summary: CreateEntityChainsFromTokens + operationId: entityresolution.v2.EntityResolutionService.CreateEntityChainsFromTokens parameters: - name: Connect-Protocol-Version in: header @@ -57,7 +57,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/entityresolution.v2.ResolveEntitiesRequest' + $ref: '#/components/schemas/entityresolution.v2.CreateEntityChainsFromTokensRequest' required: true responses: default: @@ -71,40 +71,58 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/entityresolution.v2.ResolveEntitiesResponse' + $ref: '#/components/schemas/entityresolution.v2.CreateEntityChainsFromTokensResponse' components: schemas: + entity.Entity.Category: + type: string + title: Category + enum: + - CATEGORY_UNSPECIFIED + - CATEGORY_SUBJECT + - CATEGORY_ENVIRONMENT + google.protobuf.NullValue: + type: string + title: NullValue + enum: + - NULL_VALUE + description: |- + `NullValue` is a singleton enumeration to represent the null value for the + `Value` type union. + + The JSON representation for `NullValue` is JSON `null`. authorization.v2.Resource: type: object - allOf: + oneOf: - properties: - ephemeralId: - type: string - title: ephemeral_id - description: ephemeral id for tracking between request and response - - oneOf: - - type: object - properties: - attributeValues: - title: attribute_values - description: | - a set of attribute value FQNs, such as those on a TDF, between 1 and 20 in count - attribute_values_required // if provided, resource.attribute_values must be between 1 and 20 in count with all valid FQNs - $ref: '#/components/schemas/authorization.v2.Resource.AttributeValues' + attributeValues: title: attribute_values - required: - - attributeValues - - type: object - properties: - registeredResourceValueFqn: - type: string - title: registered_resource_value_fqn - minLength: 1 - format: uri - description: fully qualified name of the registered resource value stored in platform policy + description: |+ + a set of attribute value FQNs, such as those on a TDF, between 1 and 20 in count + if provided, resource.attribute_values must be between 1 and 20 in count with all valid FQNs: + ``` + this.fqns.size() > 0 && this.fqns.size() <= 20 && this.fqns.all(item, item.isUri()) + ``` + + $ref: '#/components/schemas/authorization.v2.Resource.AttributeValues' + title: attribute_values + required: + - attributeValues + - properties: + registeredResourceValueFqn: + type: string title: registered_resource_value_fqn - required: - - registeredResourceValueFqn + minLength: 1 + format: uri + description: fully qualified name of the registered resource value stored in platform policy + title: registered_resource_value_fqn + required: + - registeredResourceValueFqn + properties: + ephemeralId: + type: string + title: ephemeral_id + description: ephemeral id for tracking between request and response title: Resource additionalProperties: false description: Either a set of attribute values (such as those on a TDF) or a registered resource value @@ -118,130 +136,49 @@ components: title: fqns title: AttributeValues additionalProperties: false - connect-protocol-version: - type: number - title: Connect-Protocol-Version - enum: - - 1 - description: Define the version of the Connect protocol - const: 1 - connect-timeout-header: - type: number - title: Connect-Timeout-Ms - description: Define the timeout, in ms - connect.error: - type: object - properties: - code: - type: string - examples: - - not_found - enum: - - canceled - - unknown - - invalid_argument - - deadline_exceeded - - not_found - - already_exists - - permission_denied - - resource_exhausted - - failed_precondition - - aborted - - out_of_range - - unimplemented - - internal - - unavailable - - data_loss - - unauthenticated - description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. - message: - type: string - description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. - details: - type: array - items: - $ref: '#/components/schemas/connect.error_details.Any' - description: A list of messages that carry the error details. There is no limit on the number of messages. - title: Connect Error - additionalProperties: true - description: 'Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation' - connect.error_details.Any: - type: object - properties: - type: - type: string - description: 'A URL that acts as a globally unique identifier for the type of the serialized message. For example: `type.googleapis.com/google.rpc.ErrorInfo`. This is used to determine the schema of the data in the `value` field and is the discriminator for the `debug` field.' - value: - type: string - format: binary - description: The Protobuf message, serialized as bytes and base64-encoded. The specific message type is identified by the `type` field. - debug: - oneOf: - - type: object - title: Any - additionalProperties: true - description: Detailed error information. - discriminator: - propertyName: type - title: Debug - description: Deserialized error detail payload. The 'type' field indicates the schema. This field is for easier debugging and should not be relied upon for application logic. - additionalProperties: true - description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message, with an additional debug field for ConnectRPC error details. entity.Entity: type: object - allOf: + oneOf: - properties: - ephemeralId: - type: string - title: ephemeral_id - description: ephemeral id for tracking between request and response - category: - title: category - $ref: '#/components/schemas/entity.Entity.Category' - - oneOf: - - type: object - properties: - claims: - title: claims - description: used by ERS claims mode - $ref: '#/components/schemas/google.protobuf.Any' + claims: title: claims - required: - - claims - - type: object - properties: - clientId: - type: string - title: client_id + description: used by ERS claims mode + $ref: '#/components/schemas/google.protobuf.Any' + title: claims + required: + - claims + - properties: + clientId: + type: string title: client_id - required: - - clientId - - type: object - properties: - emailAddress: - type: string - title: email_address + title: client_id + required: + - clientId + - properties: + emailAddress: + type: string title: email_address - required: - - emailAddress - - type: object - properties: - userName: - type: string - title: user_name + title: email_address + required: + - emailAddress + - properties: + userName: + type: string title: user_name - required: - - userName + title: user_name + required: + - userName + properties: + ephemeralId: + type: string + title: ephemeral_id + description: ephemeral id for tracking between request and response + category: + title: category + $ref: '#/components/schemas/entity.Entity.Category' title: Entity additionalProperties: false description: PE (Person Entity) or NPE (Non-Person Entity) - entity.Entity.Category: - type: string - title: Category - enum: - - CATEGORY_UNSPECIFIED - - CATEGORY_SUBJECT - - CATEGORY_ENVIRONMENT entity.EntityChain: type: object properties: @@ -385,6 +322,9 @@ components: value: type: string format: binary + debug: + type: object + additionalProperties: true additionalProperties: true description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message. google.protobuf.ListValue: @@ -402,16 +342,6 @@ components: `ListValue` is a wrapper around a repeated field of values. The JSON representation for `ListValue` is JSON array. - google.protobuf.NullValue: - type: string - title: NullValue - enum: - - NULL_VALUE - description: |- - `NullValue` is a singleton enumeration to represent the null value for the - `Value` type union. - - The JSON representation for `NullValue` is JSON `null`. google.protobuf.Struct: type: object additionalProperties: @@ -452,6 +382,50 @@ components: variants. Absence of any variant indicates an error. The JSON representation for `Value` is JSON value. + connect-protocol-version: + type: number + title: Connect-Protocol-Version + enum: + - 1 + description: Define the version of the Connect protocol + const: 1 + connect-timeout-header: + type: number + title: Connect-Timeout-Ms + description: Define the timeout, in ms + connect.error: + type: object + properties: + code: + type: string + examples: + - not_found + enum: + - canceled + - unknown + - invalid_argument + - deadline_exceeded + - not_found + - already_exists + - permission_denied + - resource_exhausted + - failed_precondition + - aborted + - out_of_range + - unimplemented + - internal + - unavailable + - data_loss + - unauthenticated + description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. + message: + type: string + description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. + detail: + $ref: '#/components/schemas/google.protobuf.Any' + title: Connect Error + additionalProperties: true + description: 'Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation' security: [] tags: - name: entityresolution.v2.EntityResolutionService diff --git a/docs/openapi/kas/kas.openapi.yaml b/docs/openapi/kas/kas.openapi.yaml index 147aea0ddf..a357028153 100644 --- a/docs/openapi/kas/kas.openapi.yaml +++ b/docs/openapi/kas/kas.openapi.yaml @@ -2,16 +2,12 @@ openapi: 3.1.0 info: title: kas paths: - /kas.AccessService/LegacyPublicKey: + /kas.AccessService/PublicKey: post: tags: - kas.AccessService - summary: Endpoint intended for gRPC Gateway's REST endpoint to provide v1 compatibility with older TDF clients - description: |- - This endpoint is not recommended for use in new applications, prefer the v2 endpoint ('PublicKey') instead. - - buf:lint:ignore RPC_RESPONSE_STANDARD_NAME - operationId: kas.AccessService.LegacyPublicKey + summary: PublicKey + operationId: kas.AccessService.PublicKey parameters: - name: Connect-Protocol-Version in: header @@ -26,7 +22,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/kas.LegacyPublicKeyRequest' + $ref: '#/components/schemas/kas.PublicKeyRequest' required: true responses: default: @@ -40,14 +36,19 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/google.protobuf.StringValue' - deprecated: true - /kas.AccessService/PublicKey: + $ref: '#/components/schemas/kas.PublicKeyResponse' + /kas.AccessService/LegacyPublicKey: post: tags: - kas.AccessService - summary: PublicKey - operationId: kas.AccessService.PublicKey + summary: LegacyPublicKey + description: |- + Endpoint intended for gRPC Gateway's REST endpoint to provide v1 compatibility with older TDF clients + + This endpoint is not recommended for use in new applications, prefer the v2 endpoint ('PublicKey') instead. + + buf:lint:ignore RPC_RESPONSE_STANDARD_NAME + operationId: kas.AccessService.LegacyPublicKey parameters: - name: Connect-Protocol-Version in: header @@ -62,7 +63,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/kas.PublicKeyRequest' + $ref: '#/components/schemas/kas.LegacyPublicKeyRequest' required: true responses: default: @@ -76,7 +77,8 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/kas.PublicKeyResponse' + $ref: '#/components/schemas/google.protobuf.StringValue' + deprecated: true /kas.AccessService/Rewrap: post: tags: @@ -114,75 +116,16 @@ paths: $ref: '#/components/schemas/kas.RewrapResponse' components: schemas: - connect-protocol-version: - type: number - title: Connect-Protocol-Version + google.protobuf.NullValue: + type: string + title: NullValue enum: - - 1 - description: Define the version of the Connect protocol - const: 1 - connect-timeout-header: - type: number - title: Connect-Timeout-Ms - description: Define the timeout, in ms - connect.error: - type: object - properties: - code: - type: string - examples: - - not_found - enum: - - canceled - - unknown - - invalid_argument - - deadline_exceeded - - not_found - - already_exists - - permission_denied - - resource_exhausted - - failed_precondition - - aborted - - out_of_range - - unimplemented - - internal - - unavailable - - data_loss - - unauthenticated - description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. - message: - type: string - description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. - details: - type: array - items: - $ref: '#/components/schemas/connect.error_details.Any' - description: A list of messages that carry the error details. There is no limit on the number of messages. - title: Connect Error - additionalProperties: true - description: 'Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation' - connect.error_details.Any: - type: object - properties: - type: - type: string - description: 'A URL that acts as a globally unique identifier for the type of the serialized message. For example: `type.googleapis.com/google.rpc.ErrorInfo`. This is used to determine the schema of the data in the `value` field and is the discriminator for the `debug` field.' - value: - type: string - format: binary - description: The Protobuf message, serialized as bytes and base64-encoded. The specific message type is identified by the `type` field. - debug: - oneOf: - - type: object - title: Any - additionalProperties: true - description: Detailed error information. - discriminator: - propertyName: type - title: Debug - description: Deserialized error detail payload. The 'type' field indicates the schema. This field is for easier debugging and should not be relied upon for application logic. - additionalProperties: true - description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message, with an additional debug field for ConnectRPC error details. + - NULL_VALUE + description: |- + `NullValue` is a singleton enumeration to represent the null value for the + `Value` type union. + + The JSON representation for `NullValue` is JSON `null`. google.protobuf.ListValue: type: object properties: @@ -198,16 +141,6 @@ components: `ListValue` is a wrapper around a repeated field of values. The JSON representation for `ListValue` is JSON array. - google.protobuf.NullValue: - type: string - title: NullValue - enum: - - NULL_VALUE - description: |- - `NullValue` is a singleton enumeration to represent the null value for the - `Value` type union. - - The JSON representation for `NullValue` is JSON `null`. google.protobuf.StringValue: type: string description: |- @@ -357,57 +290,54 @@ components: description: Key Access Object containing cryptographic material and metadata for TDF decryption kas.KeyAccessRewrapResult: type: object - allOf: + oneOf: - properties: - metadata: - type: object - title: metadata - additionalProperties: - title: value - $ref: '#/components/schemas/google.protobuf.Value' - description: |- - Metadata associated with this KAO result (e.g., required obligations) - Optional: May contain obligation requirements or other policy metadata - Common keys: "X-Required-Obligations" with array of obligation FQNs - keyAccessObjectId: + error: type: string - title: key_access_object_id + title: error description: |- - Identifier matching the key_access_object_id from the request - Required: Always matches the ID from UnsignedRewrapRequest_WithKeyAccessObject - status: + Error message when rewrap failed + Present when status="fail" + Human-readable description of the failure reason + title: error + required: + - error + - properties: + kasWrappedKey: type: string - title: status - description: |- - Status of the rewrap operation for this KAO - Required: Always - Values: "permit" (success), "fail" (failure) - - oneOf: - - type: object - properties: - error: - type: string - title: error - description: |- - Error message when rewrap failed - Present when status="fail" - Human-readable description of the failure reason - title: error - required: - - error - - type: object - properties: - kasWrappedKey: - type: string - title: kas_wrapped_key - format: byte - description: |- - Successfully rewrapped key encrypted with the session key - Present when status="permit" - Contains the DEK encrypted with the ephemeral session key title: kas_wrapped_key - required: - - kasWrappedKey + format: byte + description: |- + Successfully rewrapped key encrypted with the session key + Present when status="permit" + Contains the DEK encrypted with the ephemeral session key + title: kas_wrapped_key + required: + - kasWrappedKey + properties: + metadata: + type: object + title: metadata + additionalProperties: + title: value + $ref: '#/components/schemas/google.protobuf.Value' + description: |- + Metadata associated with this KAO result (e.g., required obligations) + Optional: May contain obligation requirements or other policy metadata + Common keys: "X-Required-Obligations" with array of obligation FQNs + keyAccessObjectId: + type: string + title: key_access_object_id + description: |- + Identifier matching the key_access_object_id from the request + Required: Always matches the ID from UnsignedRewrapRequest_WithKeyAccessObject + status: + type: string + title: status + description: |- + Status of the rewrap operation for this KAO + Required: Always + Values: "permit" (success), "fail" (failure) title: KeyAccessRewrapResult additionalProperties: false description: Result of a key access object rewrap operation @@ -689,6 +619,63 @@ components: title: WithPolicyRequest additionalProperties: false description: Request grouping policy with associated key access objects + connect-protocol-version: + type: number + title: Connect-Protocol-Version + enum: + - 1 + description: Define the version of the Connect protocol + const: 1 + connect-timeout-header: + type: number + title: Connect-Timeout-Ms + description: Define the timeout, in ms + connect.error: + type: object + properties: + code: + type: string + examples: + - not_found + enum: + - canceled + - unknown + - invalid_argument + - deadline_exceeded + - not_found + - already_exists + - permission_denied + - resource_exhausted + - failed_precondition + - aborted + - out_of_range + - unimplemented + - internal + - unavailable + - data_loss + - unauthenticated + description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. + message: + type: string + description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. + detail: + $ref: '#/components/schemas/google.protobuf.Any' + title: Connect Error + additionalProperties: true + description: 'Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation' + google.protobuf.Any: + type: object + properties: + type: + type: string + value: + type: string + format: binary + debug: + type: object + additionalProperties: true + additionalProperties: true + description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message. security: [] tags: - name: kas.AccessService diff --git a/docs/openapi/policy/actions/actions.openapi.yaml b/docs/openapi/policy/actions/actions.openapi.yaml index b5c6f2c12b..e0c19399fb 100644 --- a/docs/openapi/policy/actions/actions.openapi.yaml +++ b/docs/openapi/policy/actions/actions.openapi.yaml @@ -2,12 +2,12 @@ openapi: 3.1.0 info: title: policy.actions paths: - /policy.actions.ActionService/CreateAction: + /policy.actions.ActionService/GetAction: post: tags: - policy.actions.ActionService - summary: CreateAction - operationId: policy.actions.ActionService.CreateAction + summary: GetAction + operationId: policy.actions.ActionService.GetAction parameters: - name: Connect-Protocol-Version in: header @@ -22,7 +22,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.actions.CreateActionRequest' + $ref: '#/components/schemas/policy.actions.GetActionRequest' required: true responses: default: @@ -36,13 +36,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.actions.CreateActionResponse' - /policy.actions.ActionService/DeleteAction: + $ref: '#/components/schemas/policy.actions.GetActionResponse' + /policy.actions.ActionService/ListActions: post: tags: - policy.actions.ActionService - summary: DeleteAction - operationId: policy.actions.ActionService.DeleteAction + summary: ListActions + operationId: policy.actions.ActionService.ListActions parameters: - name: Connect-Protocol-Version in: header @@ -57,7 +57,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.actions.DeleteActionRequest' + $ref: '#/components/schemas/policy.actions.ListActionsRequest' required: true responses: default: @@ -71,13 +71,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.actions.DeleteActionResponse' - /policy.actions.ActionService/GetAction: + $ref: '#/components/schemas/policy.actions.ListActionsResponse' + /policy.actions.ActionService/CreateAction: post: tags: - policy.actions.ActionService - summary: GetAction - operationId: policy.actions.ActionService.GetAction + summary: CreateAction + operationId: policy.actions.ActionService.CreateAction parameters: - name: Connect-Protocol-Version in: header @@ -92,7 +92,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.actions.GetActionRequest' + $ref: '#/components/schemas/policy.actions.CreateActionRequest' required: true responses: default: @@ -106,13 +106,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.actions.GetActionResponse' - /policy.actions.ActionService/ListActions: + $ref: '#/components/schemas/policy.actions.CreateActionResponse' + /policy.actions.ActionService/UpdateAction: post: tags: - policy.actions.ActionService - summary: ListActions - operationId: policy.actions.ActionService.ListActions + summary: UpdateAction + operationId: policy.actions.ActionService.UpdateAction parameters: - name: Connect-Protocol-Version in: header @@ -127,7 +127,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.actions.ListActionsRequest' + $ref: '#/components/schemas/policy.actions.UpdateActionRequest' required: true responses: default: @@ -141,13 +141,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.actions.ListActionsResponse' - /policy.actions.ActionService/UpdateAction: + $ref: '#/components/schemas/policy.actions.UpdateActionResponse' + /policy.actions.ActionService/DeleteAction: post: tags: - policy.actions.ActionService - summary: UpdateAction - operationId: policy.actions.ActionService.UpdateAction + summary: DeleteAction + operationId: policy.actions.ActionService.DeleteAction parameters: - name: Connect-Protocol-Version in: header @@ -162,7 +162,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.actions.UpdateActionRequest' + $ref: '#/components/schemas/policy.actions.DeleteActionRequest' required: true responses: default: @@ -176,9 +176,88 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.actions.UpdateActionResponse' + $ref: '#/components/schemas/policy.actions.DeleteActionResponse' components: schemas: + common.MetadataUpdateEnum: + type: string + title: MetadataUpdateEnum + enum: + - METADATA_UPDATE_ENUM_UNSPECIFIED + - METADATA_UPDATE_ENUM_EXTEND + - METADATA_UPDATE_ENUM_REPLACE + policy.Action.StandardAction: + type: string + title: StandardAction + enum: + - STANDARD_ACTION_UNSPECIFIED + - STANDARD_ACTION_DECRYPT + - STANDARD_ACTION_TRANSMIT + policy.Algorithm: + type: string + title: Algorithm + enum: + - ALGORITHM_UNSPECIFIED + - 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 + description: Supported key algorithms. + policy.AttributeRuleTypeEnum: + type: string + title: AttributeRuleTypeEnum + enum: + - ATTRIBUTE_RULE_TYPE_ENUM_UNSPECIFIED + - ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF + - ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF + - ATTRIBUTE_RULE_TYPE_ENUM_HIERARCHY + policy.ConditionBooleanTypeEnum: + type: string + title: ConditionBooleanTypeEnum + enum: + - CONDITION_BOOLEAN_TYPE_ENUM_UNSPECIFIED + - CONDITION_BOOLEAN_TYPE_ENUM_AND + - CONDITION_BOOLEAN_TYPE_ENUM_OR + policy.KasPublicKeyAlgEnum: + type: string + title: KasPublicKeyAlgEnum + enum: + - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED + - KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048 + - KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 + policy.SourceType: + type: string + title: SourceType + enum: + - SOURCE_TYPE_UNSPECIFIED + - SOURCE_TYPE_INTERNAL + - SOURCE_TYPE_EXTERNAL + description: |- + Describes whether this kas is managed by the organization or if they imported + the kas information from an external party. These two modes are necessary in order + to encrypt a tdf dek with an external parties kas public key. + policy.SubjectMappingOperatorEnum: + type: string + title: SubjectMappingOperatorEnum + enum: + - SUBJECT_MAPPING_OPERATOR_ENUM_UNSPECIFIED + - SUBJECT_MAPPING_OPERATOR_ENUM_IN + - SUBJECT_MAPPING_OPERATOR_ENUM_NOT_IN + - SUBJECT_MAPPING_OPERATOR_ENUM_IN_CONTAINS common.Metadata: type: object properties: @@ -234,82 +313,6 @@ components: title: value title: LabelsEntry additionalProperties: false - common.MetadataUpdateEnum: - type: string - title: MetadataUpdateEnum - enum: - - METADATA_UPDATE_ENUM_UNSPECIFIED - - METADATA_UPDATE_ENUM_EXTEND - - METADATA_UPDATE_ENUM_REPLACE - connect-protocol-version: - type: number - title: Connect-Protocol-Version - enum: - - 1 - description: Define the version of the Connect protocol - const: 1 - connect-timeout-header: - type: number - title: Connect-Timeout-Ms - description: Define the timeout, in ms - connect.error: - type: object - properties: - code: - type: string - examples: - - not_found - enum: - - canceled - - unknown - - invalid_argument - - deadline_exceeded - - not_found - - already_exists - - permission_denied - - resource_exhausted - - failed_precondition - - aborted - - out_of_range - - unimplemented - - internal - - unavailable - - data_loss - - unauthenticated - description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. - message: - type: string - description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. - details: - type: array - items: - $ref: '#/components/schemas/connect.error_details.Any' - description: A list of messages that carry the error details. There is no limit on the number of messages. - title: Connect Error - additionalProperties: true - description: 'Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation' - connect.error_details.Any: - type: object - properties: - type: - type: string - description: 'A URL that acts as a globally unique identifier for the type of the serialized message. For example: `type.googleapis.com/google.rpc.ErrorInfo`. This is used to determine the schema of the data in the `value` field and is the discriminator for the `debug` field.' - value: - type: string - format: binary - description: The Protobuf message, serialized as bytes and base64-encoded. The specific message type is identified by the `type` field. - debug: - oneOf: - - type: object - title: Any - additionalProperties: true - description: Detailed error information. - discriminator: - propertyName: type - title: Debug - description: Deserialized error detail payload. The 'type' field indicates the schema. This field is for easier debugging and should not be relied upon for application logic. - additionalProperties: true - description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message, with an additional debug field for ConnectRPC error details. google.protobuf.BoolValue: type: boolean description: |- @@ -322,8 +325,8 @@ components: google.protobuf.Timestamp: type: string examples: - - "2023-01-15T01:30:15.01Z" - - "2024-12-25T12:00:00Z" + - 1s + - 1.000340012s format: date-time description: |- A Timestamp represents a point in time independent of any time zone or local @@ -417,65 +420,41 @@ components: ) to obtain a formatter capable of generating timestamps in this format. policy.Action: type: object - allOf: + oneOf: - properties: - id: - type: string - title: id - description: Generated uuid in database - name: + custom: type: string - title: name - namespace: - title: namespace - description: Namespace context for this action - $ref: '#/components/schemas/policy.Namespace' - metadata: - title: metadata - $ref: '#/components/schemas/common.Metadata' - - oneOf: - - type: object - properties: - custom: - type: string - title: custom - description: Deprecated title: custom - required: - - custom - - type: object - properties: - standard: - title: standard - description: Deprecated - $ref: '#/components/schemas/policy.Action.StandardAction' + description: Deprecated + title: custom + required: + - custom + - properties: + standard: title: standard - required: - - standard + description: Deprecated + $ref: '#/components/schemas/policy.Action.StandardAction' + title: standard + required: + - standard + properties: + id: + type: string + title: id + description: Generated uuid in database + name: + type: string + title: name + namespace: + title: namespace + description: Namespace context for this action + $ref: '#/components/schemas/policy.Namespace' + metadata: + title: metadata + $ref: '#/components/schemas/common.Metadata' title: Action additionalProperties: false description: An action an entity can take - policy.Action.StandardAction: - type: string - title: StandardAction - enum: - - STANDARD_ACTION_UNSPECIFIED - - STANDARD_ACTION_DECRYPT - - STANDARD_ACTION_TRANSMIT - policy.Algorithm: - type: string - title: Algorithm - enum: - - ALGORITHM_UNSPECIFIED - - 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 - description: Supported key algorithms. policy.Attribute: type: object properties: @@ -532,14 +511,6 @@ components: required: - rule additionalProperties: false - policy.AttributeRuleTypeEnum: - type: string - title: AttributeRuleTypeEnum - enum: - - ATTRIBUTE_RULE_TYPE_ENUM_UNSPECIFIED - - ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF - - ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF - - ATTRIBUTE_RULE_TYPE_ENUM_HIERARCHY policy.Condition: type: object properties: @@ -557,6 +528,7 @@ components: type: array items: type: string + minItems: 1 title: subject_external_values minItems: 1 description: |- @@ -572,13 +544,6 @@ components: * A Condition defines a rule of - policy.ConditionBooleanTypeEnum: - type: string - title: ConditionBooleanTypeEnum - enum: - - CONDITION_BOOLEAN_TYPE_ENUM_UNSPECIFIED - - CONDITION_BOOLEAN_TYPE_ENUM_AND - - CONDITION_BOOLEAN_TYPE_ENUM_OR policy.ConditionGroup: type: object properties: @@ -615,7 +580,7 @@ components: alg: not: enum: - - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED + - 0 title: alg description: |- A known algorithm type with any additional parameters encoded. @@ -627,19 +592,6 @@ components: description: |- Deprecated A KAS public key and some associated metadata for further identifcation - policy.KasPublicKeyAlgEnum: - type: string - title: KasPublicKeyAlgEnum - enum: - - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED - - KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048 - - KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 policy.KasPublicKeySet: type: object properties: @@ -662,9 +614,13 @@ components: uri: type: string title: uri - description: | + description: |+ Address of a KAS instance - uri_format // URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes. + URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes.: + ``` + this.matches('^https?://[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?)*(:[0-9]+)?(/.*)?$') + ``` + publicKey: title: public_key description: 'Deprecated: KAS can have multiple key pairs' @@ -862,8 +818,7 @@ components: policy.PublicKey: type: object oneOf: - - type: object - properties: + - properties: cached: title: cached description: public key with additional information. Current preferred version @@ -871,14 +826,17 @@ components: title: cached required: - cached - - type: object - properties: + - properties: remote: type: string title: remote - description: | + description: |+ kas public key url - optional since can also be retrieved via public key - uri_format // URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes. + URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes.: + ``` + this.matches('^https://[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?)*(/.*)?$') + ``` + title: remote required: - remote @@ -986,17 +944,6 @@ components: title: pem title: SimpleKasPublicKey additionalProperties: false - policy.SourceType: - type: string - title: SourceType - enum: - - SOURCE_TYPE_UNSPECIFIED - - SOURCE_TYPE_INTERNAL - - SOURCE_TYPE_EXTERNAL - description: |- - Describes whether this kas is managed by the organization or if they imported - the kas information from an external party. These two modes are necessary in order - to encrypt a tdf dek with an external parties kas public key. policy.SubjectConditionSet: type: object properties: @@ -1062,14 +1009,6 @@ components: description: |- Subject Mapping: A Policy assigning Subject Set(s) to a permitted attribute value + action(s) combination - policy.SubjectMappingOperatorEnum: - type: string - title: SubjectMappingOperatorEnum - enum: - - SUBJECT_MAPPING_OPERATOR_ENUM_UNSPECIFIED - - SUBJECT_MAPPING_OPERATOR_ENUM_IN - - SUBJECT_MAPPING_OPERATOR_ENUM_NOT_IN - - SUBJECT_MAPPING_OPERATOR_ENUM_IN_CONTAINS policy.SubjectSet: type: object properties: @@ -1143,9 +1082,13 @@ components: type: string title: name maxLength: 253 - description: | + description: |+ Required - action_name_format // Action name must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored action name will be normalized to lower case. + Action name must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored action name will be normalized to lower case.: + ``` + this.matches('^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$') + ``` + namespaceId: type: string title: namespace_id @@ -1201,44 +1144,45 @@ components: additionalProperties: false policy.actions.GetActionRequest: type: object - allOf: + oneOf: - properties: - namespaceId: + id: type: string - title: namespace_id + title: id format: uuid - description: |- - Optional namespace ID to scope name-based lookup. - If omitted for name-based lookup, action search is limited to legacy (namespace_id = NULL) actions. - namespaceFqn: + title: id + required: + - id + - properties: + name: type: string - title: namespace_fqn - minLength: 1 - format: uri - description: |- - Optional namespace FQN to scope name-based lookup. - If omitted for name-based lookup, action search is limited to legacy (namespace_id = NULL) actions. - - oneOf: - - type: object - properties: - id: - type: string - title: id - format: uuid - title: id - required: - - id - - type: object - properties: - name: - type: string - title: name - maxLength: 253 - description: | - action_name_format // Action name must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored action name will be normalized to lower case. title: name - required: - - name + maxLength: 253 + description: |+ + Action name must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored action name will be normalized to lower case.: + ``` + this.matches('^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$') + ``` + + title: name + required: + - name + properties: + namespaceId: + type: string + title: namespace_id + format: uuid + description: |- + Optional namespace ID to scope name-based lookup. + If omitted for name-based lookup, action search is limited to legacy (namespace_id = NULL) actions. + namespaceFqn: + type: string + title: namespace_fqn + minLength: 1 + format: uri + description: |- + Optional namespace FQN to scope name-based lookup. + If omitted for name-based lookup, action search is limited to legacy (namespace_id = NULL) actions. title: GetActionRequest additionalProperties: false policy.actions.GetActionResponse: @@ -1305,10 +1249,14 @@ components: type: string title: name maxLength: 253 - description: | + description: |+ Optional Custom actions only: replaces the existing action name - action_name_format // Action name must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored action name will be normalized to lower case. + Action name must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored action name will be normalized to lower case.: + ``` + size(this) == 0 || this.matches('^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$') + ``` + metadata: title: metadata description: Common metadata @@ -1329,6 +1277,63 @@ components: $ref: '#/components/schemas/policy.Action' title: UpdateActionResponse additionalProperties: false + connect-protocol-version: + type: number + title: Connect-Protocol-Version + enum: + - 1 + description: Define the version of the Connect protocol + const: 1 + connect-timeout-header: + type: number + title: Connect-Timeout-Ms + description: Define the timeout, in ms + connect.error: + type: object + properties: + code: + type: string + examples: + - not_found + enum: + - canceled + - unknown + - invalid_argument + - deadline_exceeded + - not_found + - already_exists + - permission_denied + - resource_exhausted + - failed_precondition + - aborted + - out_of_range + - unimplemented + - internal + - unavailable + - data_loss + - unauthenticated + description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. + message: + type: string + description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. + detail: + $ref: '#/components/schemas/google.protobuf.Any' + title: Connect Error + additionalProperties: true + description: 'Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation' + google.protobuf.Any: + type: object + properties: + type: + type: string + value: + type: string + format: binary + debug: + type: object + additionalProperties: true + additionalProperties: true + description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message. security: [] tags: - name: policy.actions.ActionService diff --git a/docs/openapi/policy/attributes/attributes.openapi.yaml b/docs/openapi/policy/attributes/attributes.openapi.yaml index bb6c0978fe..44af6fba24 100644 --- a/docs/openapi/policy/attributes/attributes.openapi.yaml +++ b/docs/openapi/policy/attributes/attributes.openapi.yaml @@ -2,13 +2,16 @@ openapi: 3.1.0 info: title: policy.attributes paths: - /policy.attributes.AttributesService/AssignKeyAccessServerToAttribute: + /policy.attributes.AttributesService/ListAttributes: post: tags: - policy.attributes.AttributesService - summary: AssignKeyAccessServerToAttribute - description: 'Deprecated: utilize AssignPublicKeyToAttribute' - operationId: policy.attributes.AttributesService.AssignKeyAccessServerToAttribute + summary: ListAttributes + description: |- + --------------------------------------* + Attribute RPCs + --------------------------------------- + operationId: policy.attributes.AttributesService.ListAttributes parameters: - name: Connect-Protocol-Version in: header @@ -23,7 +26,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.attributes.AssignKeyAccessServerToAttributeRequest' + $ref: '#/components/schemas/policy.attributes.ListAttributesRequest' required: true responses: default: @@ -37,15 +40,16 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.attributes.AssignKeyAccessServerToAttributeResponse' - deprecated: true - /policy.attributes.AttributesService/AssignKeyAccessServerToValue: + $ref: '#/components/schemas/policy.attributes.ListAttributesResponse' + /policy.attributes.AttributesService/ListAttributeValues: post: tags: - policy.attributes.AttributesService - summary: AssignKeyAccessServerToValue - description: 'Deprecated: utilize AssignPublicKeyToValue' - operationId: policy.attributes.AttributesService.AssignKeyAccessServerToValue + summary: ListAttributeValues + description: |- + Deprecated + Use GetAttribute + operationId: policy.attributes.AttributesService.ListAttributeValues parameters: - name: Connect-Protocol-Version in: header @@ -60,7 +64,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.attributes.AssignKeyAccessServerToValueRequest' + $ref: '#/components/schemas/policy.attributes.ListAttributeValuesRequest' required: true responses: default: @@ -74,14 +78,14 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.attributes.AssignKeyAccessServerToValueResponse' + $ref: '#/components/schemas/policy.attributes.ListAttributeValuesResponse' deprecated: true - /policy.attributes.AttributesService/AssignPublicKeyToAttribute: + /policy.attributes.AttributesService/GetAttribute: post: tags: - policy.attributes.AttributesService - summary: AssignPublicKeyToAttribute - operationId: policy.attributes.AttributesService.AssignPublicKeyToAttribute + summary: GetAttribute + operationId: policy.attributes.AttributesService.GetAttribute parameters: - name: Connect-Protocol-Version in: header @@ -96,7 +100,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.attributes.AssignPublicKeyToAttributeRequest' + $ref: '#/components/schemas/policy.attributes.GetAttributeRequest' required: true responses: default: @@ -110,13 +114,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.attributes.AssignPublicKeyToAttributeResponse' - /policy.attributes.AttributesService/AssignPublicKeyToValue: + $ref: '#/components/schemas/policy.attributes.GetAttributeResponse' + /policy.attributes.AttributesService/GetAttributeValuesByFqns: post: tags: - policy.attributes.AttributesService - summary: AssignPublicKeyToValue - operationId: policy.attributes.AttributesService.AssignPublicKeyToValue + summary: GetAttributeValuesByFqns + operationId: policy.attributes.AttributesService.GetAttributeValuesByFqns parameters: - name: Connect-Protocol-Version in: header @@ -131,7 +135,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.attributes.AssignPublicKeyToValueRequest' + $ref: '#/components/schemas/policy.attributes.GetAttributeValuesByFqnsRequest' required: true responses: default: @@ -145,7 +149,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.attributes.AssignPublicKeyToValueResponse' + $ref: '#/components/schemas/policy.attributes.GetAttributeValuesByFqnsResponse' /policy.attributes.AttributesService/CreateAttribute: post: tags: @@ -181,12 +185,12 @@ paths: application/json: schema: $ref: '#/components/schemas/policy.attributes.CreateAttributeResponse' - /policy.attributes.AttributesService/CreateAttributeValue: + /policy.attributes.AttributesService/UpdateAttribute: post: tags: - policy.attributes.AttributesService - summary: CreateAttributeValue - operationId: policy.attributes.AttributesService.CreateAttributeValue + summary: UpdateAttribute + operationId: policy.attributes.AttributesService.UpdateAttribute parameters: - name: Connect-Protocol-Version in: header @@ -201,7 +205,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.attributes.CreateAttributeValueRequest' + $ref: '#/components/schemas/policy.attributes.UpdateAttributeRequest' required: true responses: default: @@ -215,7 +219,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.attributes.CreateAttributeValueResponse' + $ref: '#/components/schemas/policy.attributes.UpdateAttributeResponse' /policy.attributes.AttributesService/DeactivateAttribute: post: tags: @@ -251,12 +255,16 @@ paths: application/json: schema: $ref: '#/components/schemas/policy.attributes.DeactivateAttributeResponse' - /policy.attributes.AttributesService/DeactivateAttributeValue: + /policy.attributes.AttributesService/GetAttributeValue: post: tags: - policy.attributes.AttributesService - summary: DeactivateAttributeValue - operationId: policy.attributes.AttributesService.DeactivateAttributeValue + summary: GetAttributeValue + description: |- + --------------------------------------* + Value RPCs + --------------------------------------- + operationId: policy.attributes.AttributesService.GetAttributeValue parameters: - name: Connect-Protocol-Version in: header @@ -271,7 +279,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.attributes.DeactivateAttributeValueRequest' + $ref: '#/components/schemas/policy.attributes.GetAttributeValueRequest' required: true responses: default: @@ -285,13 +293,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.attributes.DeactivateAttributeValueResponse' - /policy.attributes.AttributesService/GetAttribute: + $ref: '#/components/schemas/policy.attributes.GetAttributeValueResponse' + /policy.attributes.AttributesService/CreateAttributeValue: post: tags: - policy.attributes.AttributesService - summary: GetAttribute - operationId: policy.attributes.AttributesService.GetAttribute + summary: CreateAttributeValue + operationId: policy.attributes.AttributesService.CreateAttributeValue parameters: - name: Connect-Protocol-Version in: header @@ -306,7 +314,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.attributes.GetAttributeRequest' + $ref: '#/components/schemas/policy.attributes.CreateAttributeValueRequest' required: true responses: default: @@ -320,17 +328,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.attributes.GetAttributeResponse' - /policy.attributes.AttributesService/GetAttributeValue: + $ref: '#/components/schemas/policy.attributes.CreateAttributeValueResponse' + /policy.attributes.AttributesService/UpdateAttributeValue: post: tags: - policy.attributes.AttributesService - summary: GetAttributeValue - description: |- - --------------------------------------* - Value RPCs - --------------------------------------- - operationId: policy.attributes.AttributesService.GetAttributeValue + summary: UpdateAttributeValue + operationId: policy.attributes.AttributesService.UpdateAttributeValue parameters: - name: Connect-Protocol-Version in: header @@ -345,7 +349,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.attributes.GetAttributeValueRequest' + $ref: '#/components/schemas/policy.attributes.UpdateAttributeValueRequest' required: true responses: default: @@ -359,13 +363,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.attributes.GetAttributeValueResponse' - /policy.attributes.AttributesService/GetAttributeValuesByFqns: + $ref: '#/components/schemas/policy.attributes.UpdateAttributeValueResponse' + /policy.attributes.AttributesService/DeactivateAttributeValue: post: tags: - policy.attributes.AttributesService - summary: GetAttributeValuesByFqns - operationId: policy.attributes.AttributesService.GetAttributeValuesByFqns + summary: DeactivateAttributeValue + operationId: policy.attributes.AttributesService.DeactivateAttributeValue parameters: - name: Connect-Protocol-Version in: header @@ -380,7 +384,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.attributes.GetAttributeValuesByFqnsRequest' + $ref: '#/components/schemas/policy.attributes.DeactivateAttributeValueRequest' required: true responses: default: @@ -394,16 +398,14 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.attributes.GetAttributeValuesByFqnsResponse' - /policy.attributes.AttributesService/ListAttributeValues: + $ref: '#/components/schemas/policy.attributes.DeactivateAttributeValueResponse' + /policy.attributes.AttributesService/AssignKeyAccessServerToAttribute: post: tags: - policy.attributes.AttributesService - summary: ListAttributeValues - description: |- - Deprecated - Use GetAttribute - operationId: policy.attributes.AttributesService.ListAttributeValues + summary: AssignKeyAccessServerToAttribute + description: 'Deprecated: utilize AssignPublicKeyToAttribute' + operationId: policy.attributes.AttributesService.AssignKeyAccessServerToAttribute parameters: - name: Connect-Protocol-Version in: header @@ -418,7 +420,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.attributes.ListAttributeValuesRequest' + $ref: '#/components/schemas/policy.attributes.AssignKeyAccessServerToAttributeRequest' required: true responses: default: @@ -432,18 +434,15 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.attributes.ListAttributeValuesResponse' + $ref: '#/components/schemas/policy.attributes.AssignKeyAccessServerToAttributeResponse' deprecated: true - /policy.attributes.AttributesService/ListAttributes: + /policy.attributes.AttributesService/RemoveKeyAccessServerFromAttribute: post: tags: - policy.attributes.AttributesService - summary: ListAttributes - description: |- - --------------------------------------* - Attribute RPCs - --------------------------------------- - operationId: policy.attributes.AttributesService.ListAttributes + summary: RemoveKeyAccessServerFromAttribute + description: 'Deprecated: utilize RemovePublicKeyFromAttribute' + operationId: policy.attributes.AttributesService.RemoveKeyAccessServerFromAttribute parameters: - name: Connect-Protocol-Version in: header @@ -458,7 +457,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.attributes.ListAttributesRequest' + $ref: '#/components/schemas/policy.attributes.RemoveKeyAccessServerFromAttributeRequest' required: true responses: default: @@ -472,14 +471,15 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.attributes.ListAttributesResponse' - /policy.attributes.AttributesService/RemoveKeyAccessServerFromAttribute: + $ref: '#/components/schemas/policy.attributes.RemoveKeyAccessServerFromAttributeResponse' + deprecated: true + /policy.attributes.AttributesService/AssignKeyAccessServerToValue: post: tags: - policy.attributes.AttributesService - summary: RemoveKeyAccessServerFromAttribute - description: 'Deprecated: utilize RemovePublicKeyFromAttribute' - operationId: policy.attributes.AttributesService.RemoveKeyAccessServerFromAttribute + summary: AssignKeyAccessServerToValue + description: 'Deprecated: utilize AssignPublicKeyToValue' + operationId: policy.attributes.AttributesService.AssignKeyAccessServerToValue parameters: - name: Connect-Protocol-Version in: header @@ -494,7 +494,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.attributes.RemoveKeyAccessServerFromAttributeRequest' + $ref: '#/components/schemas/policy.attributes.AssignKeyAccessServerToValueRequest' required: true responses: default: @@ -508,7 +508,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.attributes.RemoveKeyAccessServerFromAttributeResponse' + $ref: '#/components/schemas/policy.attributes.AssignKeyAccessServerToValueResponse' deprecated: true /policy.attributes.AttributesService/RemoveKeyAccessServerFromValue: post: @@ -547,12 +547,12 @@ paths: schema: $ref: '#/components/schemas/policy.attributes.RemoveKeyAccessServerFromValueResponse' deprecated: true - /policy.attributes.AttributesService/RemovePublicKeyFromAttribute: + /policy.attributes.AttributesService/AssignPublicKeyToAttribute: post: tags: - policy.attributes.AttributesService - summary: RemovePublicKeyFromAttribute - operationId: policy.attributes.AttributesService.RemovePublicKeyFromAttribute + summary: AssignPublicKeyToAttribute + operationId: policy.attributes.AttributesService.AssignPublicKeyToAttribute parameters: - name: Connect-Protocol-Version in: header @@ -567,7 +567,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.attributes.RemovePublicKeyFromAttributeRequest' + $ref: '#/components/schemas/policy.attributes.AssignPublicKeyToAttributeRequest' required: true responses: default: @@ -581,13 +581,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.attributes.RemovePublicKeyFromAttributeResponse' - /policy.attributes.AttributesService/RemovePublicKeyFromValue: + $ref: '#/components/schemas/policy.attributes.AssignPublicKeyToAttributeResponse' + /policy.attributes.AttributesService/RemovePublicKeyFromAttribute: post: tags: - policy.attributes.AttributesService - summary: RemovePublicKeyFromValue - operationId: policy.attributes.AttributesService.RemovePublicKeyFromValue + summary: RemovePublicKeyFromAttribute + operationId: policy.attributes.AttributesService.RemovePublicKeyFromAttribute parameters: - name: Connect-Protocol-Version in: header @@ -602,7 +602,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.attributes.RemovePublicKeyFromValueRequest' + $ref: '#/components/schemas/policy.attributes.RemovePublicKeyFromAttributeRequest' required: true responses: default: @@ -616,13 +616,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.attributes.RemovePublicKeyFromValueResponse' - /policy.attributes.AttributesService/UpdateAttribute: + $ref: '#/components/schemas/policy.attributes.RemovePublicKeyFromAttributeResponse' + /policy.attributes.AttributesService/AssignPublicKeyToValue: post: tags: - policy.attributes.AttributesService - summary: UpdateAttribute - operationId: policy.attributes.AttributesService.UpdateAttribute + summary: AssignPublicKeyToValue + operationId: policy.attributes.AttributesService.AssignPublicKeyToValue parameters: - name: Connect-Protocol-Version in: header @@ -637,7 +637,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.attributes.UpdateAttributeRequest' + $ref: '#/components/schemas/policy.attributes.AssignPublicKeyToValueRequest' required: true responses: default: @@ -651,13 +651,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.attributes.UpdateAttributeResponse' - /policy.attributes.AttributesService/UpdateAttributeValue: + $ref: '#/components/schemas/policy.attributes.AssignPublicKeyToValueResponse' + /policy.attributes.AttributesService/RemovePublicKeyFromValue: post: tags: - policy.attributes.AttributesService - summary: UpdateAttributeValue - operationId: policy.attributes.AttributesService.UpdateAttributeValue + summary: RemovePublicKeyFromValue + operationId: policy.attributes.AttributesService.RemovePublicKeyFromValue parameters: - name: Connect-Protocol-Version in: header @@ -672,7 +672,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.attributes.UpdateAttributeValueRequest' + $ref: '#/components/schemas/policy.attributes.RemovePublicKeyFromValueRequest' required: true responses: default: @@ -686,7 +686,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.attributes.UpdateAttributeValueResponse' + $ref: '#/components/schemas/policy.attributes.RemovePublicKeyFromValueResponse' components: schemas: common.ActiveStateEnum: @@ -698,14 +698,107 @@ components: - ACTIVE_STATE_ENUM_INACTIVE - ACTIVE_STATE_ENUM_ANY description: 'buflint ENUM_VALUE_PREFIX: to make sure that C++ scoping rules aren''t violated when users add new enum values to an enum in a given package' + common.MetadataUpdateEnum: + type: string + title: MetadataUpdateEnum + enum: + - METADATA_UPDATE_ENUM_UNSPECIFIED + - METADATA_UPDATE_ENUM_EXTEND + - METADATA_UPDATE_ENUM_REPLACE + policy.Action.StandardAction: + type: string + title: StandardAction + enum: + - STANDARD_ACTION_UNSPECIFIED + - STANDARD_ACTION_DECRYPT + - STANDARD_ACTION_TRANSMIT + policy.Algorithm: + type: string + title: Algorithm + enum: + - ALGORITHM_UNSPECIFIED + - 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 + description: Supported key algorithms. + policy.AttributeRuleTypeEnum: + type: string + title: AttributeRuleTypeEnum + enum: + - ATTRIBUTE_RULE_TYPE_ENUM_UNSPECIFIED + - ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF + - ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF + - ATTRIBUTE_RULE_TYPE_ENUM_HIERARCHY + policy.ConditionBooleanTypeEnum: + type: string + title: ConditionBooleanTypeEnum + enum: + - CONDITION_BOOLEAN_TYPE_ENUM_UNSPECIFIED + - CONDITION_BOOLEAN_TYPE_ENUM_AND + - CONDITION_BOOLEAN_TYPE_ENUM_OR + policy.KasPublicKeyAlgEnum: + type: string + title: KasPublicKeyAlgEnum + enum: + - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED + - KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048 + - KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 + policy.SortDirection: + type: string + title: SortDirection + enum: + - SORT_DIRECTION_UNSPECIFIED + - SORT_DIRECTION_ASC + - SORT_DIRECTION_DESC + description: |- + Sorting direction shared across list APIs. + When the 'sort' field is omitted or the chosen sort 'field' is UNSPECIFIED, + the endpoint's request message defines the default ordering; see the + specific List* request docs. + policy.SourceType: + type: string + title: SourceType + enum: + - SOURCE_TYPE_UNSPECIFIED + - SOURCE_TYPE_INTERNAL + - SOURCE_TYPE_EXTERNAL + description: |- + Describes whether this kas is managed by the organization or if they imported + the kas information from an external party. These two modes are necessary in order + to encrypt a tdf dek with an external parties kas public key. + policy.SubjectMappingOperatorEnum: + type: string + title: SubjectMappingOperatorEnum + enum: + - SUBJECT_MAPPING_OPERATOR_ENUM_UNSPECIFIED + - SUBJECT_MAPPING_OPERATOR_ENUM_IN + - SUBJECT_MAPPING_OPERATOR_ENUM_NOT_IN + - SUBJECT_MAPPING_OPERATOR_ENUM_IN_CONTAINS + policy.attributes.SortAttributesType: + type: string + title: SortAttributesType + enum: + - SORT_ATTRIBUTES_TYPE_UNSPECIFIED + - SORT_ATTRIBUTES_TYPE_NAME + - SORT_ATTRIBUTES_TYPE_CREATED_AT + - SORT_ATTRIBUTES_TYPE_UPDATED_AT common.IdFqnIdentifier: type: object - allOf: - - oneOf: - - required: - - id - - required: - - fqn properties: id: type: string @@ -720,12 +813,6 @@ components: additionalProperties: false common.IdNameIdentifier: type: object - allOf: - - oneOf: - - required: - - id - - required: - - name properties: id: type: string @@ -736,8 +823,12 @@ components: title: name maxLength: 253 minLength: 1 - description: | - name_format // Name must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored name will be normalized to lower case. + description: |+ + Name must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored name will be normalized to lower case.: + ``` + this.matches('^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$') + ``` + title: IdNameIdentifier additionalProperties: false common.Metadata: @@ -795,82 +886,6 @@ components: title: value title: LabelsEntry additionalProperties: false - common.MetadataUpdateEnum: - type: string - title: MetadataUpdateEnum - enum: - - METADATA_UPDATE_ENUM_UNSPECIFIED - - METADATA_UPDATE_ENUM_EXTEND - - METADATA_UPDATE_ENUM_REPLACE - connect-protocol-version: - type: number - title: Connect-Protocol-Version - enum: - - 1 - description: Define the version of the Connect protocol - const: 1 - connect-timeout-header: - type: number - title: Connect-Timeout-Ms - description: Define the timeout, in ms - connect.error: - type: object - properties: - code: - type: string - examples: - - not_found - enum: - - canceled - - unknown - - invalid_argument - - deadline_exceeded - - not_found - - already_exists - - permission_denied - - resource_exhausted - - failed_precondition - - aborted - - out_of_range - - unimplemented - - internal - - unavailable - - data_loss - - unauthenticated - description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. - message: - type: string - description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. - details: - type: array - items: - $ref: '#/components/schemas/connect.error_details.Any' - description: A list of messages that carry the error details. There is no limit on the number of messages. - title: Connect Error - additionalProperties: true - description: 'Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation' - connect.error_details.Any: - type: object - properties: - type: - type: string - description: 'A URL that acts as a globally unique identifier for the type of the serialized message. For example: `type.googleapis.com/google.rpc.ErrorInfo`. This is used to determine the schema of the data in the `value` field and is the discriminator for the `debug` field.' - value: - type: string - format: binary - description: The Protobuf message, serialized as bytes and base64-encoded. The specific message type is identified by the `type` field. - debug: - oneOf: - - type: object - title: Any - additionalProperties: true - description: Detailed error information. - discriminator: - propertyName: type - title: Debug - description: Deserialized error detail payload. The 'type' field indicates the schema. This field is for easier debugging and should not be relied upon for application logic. - additionalProperties: true - description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message, with an additional debug field for ConnectRPC error details. google.protobuf.BoolValue: type: boolean description: |- @@ -883,8 +898,8 @@ components: google.protobuf.Timestamp: type: string examples: - - "2023-01-15T01:30:15.01Z" - - "2024-12-25T12:00:00Z" + - 1s + - 1.000340012s format: date-time description: |- A Timestamp represents a point in time independent of any time zone or local @@ -978,65 +993,41 @@ components: ) to obtain a formatter capable of generating timestamps in this format. policy.Action: type: object - allOf: + oneOf: - properties: - id: - type: string - title: id - description: Generated uuid in database - name: + custom: type: string - title: name - namespace: - title: namespace - description: Namespace context for this action - $ref: '#/components/schemas/policy.Namespace' - metadata: - title: metadata - $ref: '#/components/schemas/common.Metadata' - - oneOf: - - type: object - properties: - custom: - type: string - title: custom - description: Deprecated title: custom - required: - - custom - - type: object - properties: - standard: - title: standard - description: Deprecated - $ref: '#/components/schemas/policy.Action.StandardAction' + description: Deprecated + title: custom + required: + - custom + - properties: + standard: title: standard - required: - - standard + description: Deprecated + $ref: '#/components/schemas/policy.Action.StandardAction' + title: standard + required: + - standard + properties: + id: + type: string + title: id + description: Generated uuid in database + name: + type: string + title: name + namespace: + title: namespace + description: Namespace context for this action + $ref: '#/components/schemas/policy.Namespace' + metadata: + title: metadata + $ref: '#/components/schemas/common.Metadata' title: Action additionalProperties: false description: An action an entity can take - policy.Action.StandardAction: - type: string - title: StandardAction - enum: - - STANDARD_ACTION_UNSPECIFIED - - STANDARD_ACTION_DECRYPT - - STANDARD_ACTION_TRANSMIT - policy.Algorithm: - type: string - title: Algorithm - enum: - - ALGORITHM_UNSPECIFIED - - 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 - description: Supported key algorithms. policy.Attribute: type: object properties: @@ -1093,14 +1084,6 @@ components: required: - rule additionalProperties: false - policy.AttributeRuleTypeEnum: - type: string - title: AttributeRuleTypeEnum - enum: - - ATTRIBUTE_RULE_TYPE_ENUM_UNSPECIFIED - - ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF - - ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF - - ATTRIBUTE_RULE_TYPE_ENUM_HIERARCHY policy.Condition: type: object properties: @@ -1118,6 +1101,7 @@ components: type: array items: type: string + minItems: 1 title: subject_external_values minItems: 1 description: |- @@ -1133,13 +1117,6 @@ components: * A Condition defines a rule of - policy.ConditionBooleanTypeEnum: - type: string - title: ConditionBooleanTypeEnum - enum: - - CONDITION_BOOLEAN_TYPE_ENUM_UNSPECIFIED - - CONDITION_BOOLEAN_TYPE_ENUM_AND - - CONDITION_BOOLEAN_TYPE_ENUM_OR policy.ConditionGroup: type: object properties: @@ -1176,7 +1153,7 @@ components: alg: not: enum: - - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED + - 0 title: alg description: |- A known algorithm type with any additional parameters encoded. @@ -1188,19 +1165,6 @@ components: description: |- Deprecated A KAS public key and some associated metadata for further identifcation - policy.KasPublicKeyAlgEnum: - type: string - title: KasPublicKeyAlgEnum - enum: - - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED - - KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048 - - KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 policy.KasPublicKeySet: type: object properties: @@ -1223,9 +1187,13 @@ components: uri: type: string title: uri - description: | + description: |+ Address of a KAS instance - uri_format // URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes. + URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes.: + ``` + this.matches('^https?://[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?)*(:[0-9]+)?(/.*)?$') + ``` + publicKey: title: public_key description: 'Deprecated: KAS can have multiple key pairs' @@ -1423,8 +1391,7 @@ components: policy.PublicKey: type: object oneOf: - - type: object - properties: + - properties: cached: title: cached description: public key with additional information. Current preferred version @@ -1432,14 +1399,17 @@ components: title: cached required: - cached - - type: object - properties: + - properties: remote: type: string title: remote - description: | + description: |+ kas public key url - optional since can also be retrieved via public key - uri_format // URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes. + URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes.: + ``` + this.matches('^https://[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?)*(/.*)?$') + ``` + title: remote required: - remote @@ -1547,29 +1517,6 @@ components: title: pem title: SimpleKasPublicKey additionalProperties: false - policy.SortDirection: - type: string - title: SortDirection - enum: - - SORT_DIRECTION_UNSPECIFIED - - SORT_DIRECTION_ASC - - SORT_DIRECTION_DESC - description: |- - Sorting direction shared across list APIs. - When the 'sort' field is omitted or the chosen sort 'field' is UNSPECIFIED, - the endpoint's request message defines the default ordering; see the - specific List* request docs. - policy.SourceType: - type: string - title: SourceType - enum: - - SOURCE_TYPE_UNSPECIFIED - - SOURCE_TYPE_INTERNAL - - SOURCE_TYPE_EXTERNAL - description: |- - Describes whether this kas is managed by the organization or if they imported - the kas information from an external party. These two modes are necessary in order - to encrypt a tdf dek with an external parties kas public key. policy.SubjectConditionSet: type: object properties: @@ -1635,14 +1582,6 @@ components: description: |- Subject Mapping: A Policy assigning Subject Set(s) to a permitted attribute value + action(s) combination - policy.SubjectMappingOperatorEnum: - type: string - title: SubjectMappingOperatorEnum - enum: - - SUBJECT_MAPPING_OPERATOR_ENUM_UNSPECIFIED - - SUBJECT_MAPPING_OPERATOR_ENUM_IN - - SUBJECT_MAPPING_OPERATOR_ENUM_NOT_IN - - SUBJECT_MAPPING_OPERATOR_ENUM_IN_CONTAINS policy.SubjectSet: type: object properties: @@ -1866,9 +1805,13 @@ components: type: string title: name maxLength: 253 - description: | + description: |+ Required - attribute_name_format // Attribute name must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored attribute name will be normalized to lower case. + Attribute name must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored attribute name will be normalized to lower case.: + ``` + this.matches('^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$') + ``` + rule: title: rule description: Required @@ -1879,6 +1822,7 @@ components: type: string maxLength: 253 pattern: ^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$ + uniqueItems: true title: values uniqueItems: true description: |- @@ -1924,9 +1868,13 @@ components: type: string title: value maxLength: 253 - description: | + description: |+ Required - attribute_value_format // Attribute value must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored attribute value will be normalized to lower case. + Attribute value must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored attribute value will be normalized to lower case.: + ``` + this.matches('^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$') + ``` + obligationTriggers: type: array items: @@ -1991,40 +1939,45 @@ components: additionalProperties: false policy.attributes.GetAttributeRequest: type: object - allOf: + oneOf: - properties: - id: + attributeId: type: string - title: id - format: uuid - description: 'Deprecated: utilize identifier' - deprecated: true - - oneOf: - - type: object - properties: - attributeId: - type: string - title: attribute_id - format: uuid - description: 'option (buf.validate.oneof).required = true; // TODO: enable this when we remove the deprecated field' title: attribute_id - required: - - attributeId - - type: object - properties: - fqn: - type: string - title: fqn - minLength: 1 - format: uri + format: uuid + description: 'option (buf.validate.oneof).required = true; // TODO: enable this when we remove the deprecated field' + title: attribute_id + required: + - attributeId + - properties: + fqn: + type: string title: fqn - required: - - fqn + minLength: 1 + format: uri + title: fqn + required: + - fqn + properties: + id: + type: string + title: id + format: uuid + description: 'Deprecated: utilize identifier' + deprecated: true title: GetAttributeRequest additionalProperties: false - description: | - exclusive_fields // Either use deprecated 'id' field or one of 'attribute_id' or 'fqn', but not both - required_fields // Either id or one of attribute_id or fqn must be set + description: |+ + Either use deprecated 'id' field or one of 'attribute_id' or 'fqn', but not both: + ``` + !(has(this.id) && (has(this.attribute_id) || has(this.fqn))) + ``` + + Either id or one of attribute_id or fqn must be set: + ``` + has(this.id) || has(this.attribute_id) || has(this.fqn) + ``` + policy.attributes.GetAttributeResponse: type: object properties: @@ -2035,43 +1988,48 @@ components: additionalProperties: false policy.attributes.GetAttributeValueRequest: type: object - allOf: + oneOf: - properties: - id: + fqn: type: string - title: id - format: uuid - description: 'Deprecated: utilize identifier' - deprecated: true - - oneOf: - - type: object - properties: - fqn: - type: string - title: fqn - minLength: 1 - format: uri title: fqn - required: - - fqn - - type: object - properties: - valueId: - type: string - title: value_id - format: uuid - description: 'option (buf.validate.oneof).required = true; // TODO: enable this when we remove the deprecated field' + minLength: 1 + format: uri + title: fqn + required: + - fqn + - properties: + valueId: + type: string title: value_id - required: - - valueId + format: uuid + description: 'option (buf.validate.oneof).required = true; // TODO: enable this when we remove the deprecated field' + title: value_id + required: + - valueId + properties: + id: + type: string + title: id + format: uuid + description: 'Deprecated: utilize identifier' + deprecated: true title: GetAttributeValueRequest additionalProperties: false - description: | + description: |+ / / Value RPC messages / - exclusive_fields // Either use deprecated 'id' field or one of 'value_id' or 'fqn', but not both - required_fields // Either id or one of value_id or fqn must be set + Either use deprecated 'id' field or one of 'value_id' or 'fqn', but not both: + ``` + !(has(this.id) && (has(this.value_id) || has(this.fqn))) + ``` + + Either id or one of value_id or fqn must be set: + ``` + has(this.id) || has(this.value_id) || has(this.fqn) + ``` + policy.attributes.GetAttributeValueResponse: type: object properties: @@ -2087,6 +2045,8 @@ components: type: array items: type: string + maxItems: 250 + minItems: 1 title: fqns maxItems: 250 minItems: 1 @@ -2284,14 +2244,6 @@ components: $ref: '#/components/schemas/policy.attributes.ValueKey' title: RemovePublicKeyFromValueResponse additionalProperties: false - policy.attributes.SortAttributesType: - type: string - title: SortAttributesType - enum: - - SORT_ATTRIBUTES_TYPE_UNSPECIFIED - - SORT_ATTRIBUTES_TYPE_NAME - - SORT_ATTRIBUTES_TYPE_CREATED_AT - - SORT_ATTRIBUTES_TYPE_UPDATED_AT policy.attributes.UpdateAttributeRequest: type: object properties: @@ -2377,6 +2329,63 @@ components: description: Required title: ValueKeyAccessServer additionalProperties: false + connect-protocol-version: + type: number + title: Connect-Protocol-Version + enum: + - 1 + description: Define the version of the Connect protocol + const: 1 + connect-timeout-header: + type: number + title: Connect-Timeout-Ms + description: Define the timeout, in ms + connect.error: + type: object + properties: + code: + type: string + examples: + - not_found + enum: + - canceled + - unknown + - invalid_argument + - deadline_exceeded + - not_found + - already_exists + - permission_denied + - resource_exhausted + - failed_precondition + - aborted + - out_of_range + - unimplemented + - internal + - unavailable + - data_loss + - unauthenticated + description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. + message: + type: string + description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. + detail: + $ref: '#/components/schemas/google.protobuf.Any' + title: Connect Error + additionalProperties: true + description: 'Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation' + google.protobuf.Any: + type: object + properties: + type: + type: string + value: + type: string + format: binary + debug: + type: object + additionalProperties: true + additionalProperties: true + description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message. security: [] tags: - name: policy.attributes.AttributesService 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 8d9f785054..17a1e669c5 100644 --- a/docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml +++ b/docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml @@ -2,15 +2,12 @@ openapi: 3.1.0 info: title: policy.kasregistry paths: - /policy.kasregistry.KeyAccessServerRegistryService/CreateKey: + /policy.kasregistry.KeyAccessServerRegistryService/ListKeyAccessServers: post: tags: - policy.kasregistry.KeyAccessServerRegistryService - summary: CreateKey - description: |- - KAS Key Management - Request to create a new key in the Key Access Service. - operationId: policy.kasregistry.KeyAccessServerRegistryService.CreateKey + summary: ListKeyAccessServers + operationId: policy.kasregistry.KeyAccessServerRegistryService.ListKeyAccessServers parameters: - name: Connect-Protocol-Version in: header @@ -25,7 +22,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.kasregistry.CreateKeyRequest' + $ref: '#/components/schemas/policy.kasregistry.ListKeyAccessServersRequest' required: true responses: default: @@ -39,13 +36,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.kasregistry.CreateKeyResponse' - /policy.kasregistry.KeyAccessServerRegistryService/CreateKeyAccessServer: + $ref: '#/components/schemas/policy.kasregistry.ListKeyAccessServersResponse' + /policy.kasregistry.KeyAccessServerRegistryService/GetKeyAccessServer: post: tags: - policy.kasregistry.KeyAccessServerRegistryService - summary: CreateKeyAccessServer - operationId: policy.kasregistry.KeyAccessServerRegistryService.CreateKeyAccessServer + summary: GetKeyAccessServer + operationId: policy.kasregistry.KeyAccessServerRegistryService.GetKeyAccessServer parameters: - name: Connect-Protocol-Version in: header @@ -60,7 +57,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.kasregistry.CreateKeyAccessServerRequest' + $ref: '#/components/schemas/policy.kasregistry.GetKeyAccessServerRequest' required: true responses: default: @@ -74,13 +71,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.kasregistry.CreateKeyAccessServerResponse' - /policy.kasregistry.KeyAccessServerRegistryService/DeleteKeyAccessServer: + $ref: '#/components/schemas/policy.kasregistry.GetKeyAccessServerResponse' + /policy.kasregistry.KeyAccessServerRegistryService/CreateKeyAccessServer: post: tags: - policy.kasregistry.KeyAccessServerRegistryService - summary: DeleteKeyAccessServer - operationId: policy.kasregistry.KeyAccessServerRegistryService.DeleteKeyAccessServer + summary: CreateKeyAccessServer + operationId: policy.kasregistry.KeyAccessServerRegistryService.CreateKeyAccessServer parameters: - name: Connect-Protocol-Version in: header @@ -95,7 +92,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.kasregistry.DeleteKeyAccessServerRequest' + $ref: '#/components/schemas/policy.kasregistry.CreateKeyAccessServerRequest' required: true responses: default: @@ -109,14 +106,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.kasregistry.DeleteKeyAccessServerResponse' - /policy.kasregistry.KeyAccessServerRegistryService/GetBaseKey: + $ref: '#/components/schemas/policy.kasregistry.CreateKeyAccessServerResponse' + /policy.kasregistry.KeyAccessServerRegistryService/UpdateKeyAccessServer: post: tags: - policy.kasregistry.KeyAccessServerRegistryService - summary: GetBaseKey - description: Get Default kas keys - operationId: policy.kasregistry.KeyAccessServerRegistryService.GetBaseKey + summary: UpdateKeyAccessServer + operationId: policy.kasregistry.KeyAccessServerRegistryService.UpdateKeyAccessServer parameters: - name: Connect-Protocol-Version in: header @@ -131,7 +127,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.kasregistry.GetBaseKeyRequest' + $ref: '#/components/schemas/policy.kasregistry.UpdateKeyAccessServerRequest' required: true responses: default: @@ -145,14 +141,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.kasregistry.GetBaseKeyResponse' - /policy.kasregistry.KeyAccessServerRegistryService/GetKey: + $ref: '#/components/schemas/policy.kasregistry.UpdateKeyAccessServerResponse' + /policy.kasregistry.KeyAccessServerRegistryService/DeleteKeyAccessServer: post: tags: - policy.kasregistry.KeyAccessServerRegistryService - summary: GetKey - description: Request to retrieve a key from the Key Access Service. - operationId: policy.kasregistry.KeyAccessServerRegistryService.GetKey + summary: DeleteKeyAccessServer + operationId: policy.kasregistry.KeyAccessServerRegistryService.DeleteKeyAccessServer parameters: - name: Connect-Protocol-Version in: header @@ -167,7 +162,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.kasregistry.GetKeyRequest' + $ref: '#/components/schemas/policy.kasregistry.DeleteKeyAccessServerRequest' required: true responses: default: @@ -181,13 +176,14 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.kasregistry.GetKeyResponse' - /policy.kasregistry.KeyAccessServerRegistryService/GetKeyAccessServer: + $ref: '#/components/schemas/policy.kasregistry.DeleteKeyAccessServerResponse' + /policy.kasregistry.KeyAccessServerRegistryService/ListKeyAccessServerGrants: post: tags: - policy.kasregistry.KeyAccessServerRegistryService - summary: GetKeyAccessServer - operationId: policy.kasregistry.KeyAccessServerRegistryService.GetKeyAccessServer + summary: ListKeyAccessServerGrants + description: Deprecated + operationId: policy.kasregistry.KeyAccessServerRegistryService.ListKeyAccessServerGrants parameters: - name: Connect-Protocol-Version in: header @@ -202,7 +198,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.kasregistry.GetKeyAccessServerRequest' + $ref: '#/components/schemas/policy.kasregistry.ListKeyAccessServerGrantsRequest' required: true responses: default: @@ -216,14 +212,17 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.kasregistry.GetKeyAccessServerResponse' - /policy.kasregistry.KeyAccessServerRegistryService/ListKeyAccessServerGrants: + $ref: '#/components/schemas/policy.kasregistry.ListKeyAccessServerGrantsResponse' + deprecated: true + /policy.kasregistry.KeyAccessServerRegistryService/CreateKey: post: tags: - policy.kasregistry.KeyAccessServerRegistryService - summary: ListKeyAccessServerGrants - description: Deprecated - operationId: policy.kasregistry.KeyAccessServerRegistryService.ListKeyAccessServerGrants + summary: CreateKey + description: |- + KAS Key Management + Request to create a new key in the Key Access Service. + operationId: policy.kasregistry.KeyAccessServerRegistryService.CreateKey parameters: - name: Connect-Protocol-Version in: header @@ -238,7 +237,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.kasregistry.ListKeyAccessServerGrantsRequest' + $ref: '#/components/schemas/policy.kasregistry.CreateKeyRequest' required: true responses: default: @@ -252,14 +251,14 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.kasregistry.ListKeyAccessServerGrantsResponse' - deprecated: true - /policy.kasregistry.KeyAccessServerRegistryService/ListKeyAccessServers: + $ref: '#/components/schemas/policy.kasregistry.CreateKeyResponse' + /policy.kasregistry.KeyAccessServerRegistryService/GetKey: post: tags: - policy.kasregistry.KeyAccessServerRegistryService - summary: ListKeyAccessServers - operationId: policy.kasregistry.KeyAccessServerRegistryService.ListKeyAccessServers + summary: GetKey + description: Request to retrieve a key from the Key Access Service. + operationId: policy.kasregistry.KeyAccessServerRegistryService.GetKey parameters: - name: Connect-Protocol-Version in: header @@ -274,7 +273,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.kasregistry.ListKeyAccessServersRequest' + $ref: '#/components/schemas/policy.kasregistry.GetKeyRequest' required: true responses: default: @@ -288,14 +287,14 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.kasregistry.ListKeyAccessServersResponse' - /policy.kasregistry.KeyAccessServerRegistryService/ListKeyMappings: + $ref: '#/components/schemas/policy.kasregistry.GetKeyResponse' + /policy.kasregistry.KeyAccessServerRegistryService/ListKeys: post: tags: - policy.kasregistry.KeyAccessServerRegistryService - summary: ListKeyMappings - description: Request to list key mappings in the Key Access Service. - operationId: policy.kasregistry.KeyAccessServerRegistryService.ListKeyMappings + summary: ListKeys + description: Request to list keys in the Key Access Service. + operationId: policy.kasregistry.KeyAccessServerRegistryService.ListKeys parameters: - name: Connect-Protocol-Version in: header @@ -310,7 +309,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.kasregistry.ListKeyMappingsRequest' + $ref: '#/components/schemas/policy.kasregistry.ListKeysRequest' required: true responses: default: @@ -324,14 +323,14 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.kasregistry.ListKeyMappingsResponse' - /policy.kasregistry.KeyAccessServerRegistryService/ListKeys: + $ref: '#/components/schemas/policy.kasregistry.ListKeysResponse' + /policy.kasregistry.KeyAccessServerRegistryService/UpdateKey: post: tags: - policy.kasregistry.KeyAccessServerRegistryService - summary: ListKeys - description: Request to list keys in the Key Access Service. - operationId: policy.kasregistry.KeyAccessServerRegistryService.ListKeys + summary: UpdateKey + description: Request to update a key in the Key Access Service. + operationId: policy.kasregistry.KeyAccessServerRegistryService.UpdateKey parameters: - name: Connect-Protocol-Version in: header @@ -346,7 +345,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.kasregistry.ListKeysRequest' + $ref: '#/components/schemas/policy.kasregistry.UpdateKeyRequest' required: true responses: default: @@ -360,7 +359,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.kasregistry.ListKeysResponse' + $ref: '#/components/schemas/policy.kasregistry.UpdateKeyResponse' /policy.kasregistry.KeyAccessServerRegistryService/RotateKey: post: tags: @@ -433,13 +432,13 @@ paths: application/json: schema: $ref: '#/components/schemas/policy.kasregistry.SetBaseKeyResponse' - /policy.kasregistry.KeyAccessServerRegistryService/UpdateKey: + /policy.kasregistry.KeyAccessServerRegistryService/GetBaseKey: post: tags: - policy.kasregistry.KeyAccessServerRegistryService - summary: UpdateKey - description: Request to update a key in the Key Access Service. - operationId: policy.kasregistry.KeyAccessServerRegistryService.UpdateKey + summary: GetBaseKey + description: Get Default kas keys + operationId: policy.kasregistry.KeyAccessServerRegistryService.GetBaseKey parameters: - name: Connect-Protocol-Version in: header @@ -454,7 +453,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.kasregistry.UpdateKeyRequest' + $ref: '#/components/schemas/policy.kasregistry.GetBaseKeyRequest' required: true responses: default: @@ -468,13 +467,14 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.kasregistry.UpdateKeyResponse' - /policy.kasregistry.KeyAccessServerRegistryService/UpdateKeyAccessServer: + $ref: '#/components/schemas/policy.kasregistry.GetBaseKeyResponse' + /policy.kasregistry.KeyAccessServerRegistryService/ListKeyMappings: post: tags: - policy.kasregistry.KeyAccessServerRegistryService - summary: UpdateKeyAccessServer - operationId: policy.kasregistry.KeyAccessServerRegistryService.UpdateKeyAccessServer + summary: ListKeyMappings + description: Request to list key mappings in the Key Access Service. + operationId: policy.kasregistry.KeyAccessServerRegistryService.ListKeyMappings parameters: - name: Connect-Protocol-Version in: header @@ -489,7 +489,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.kasregistry.UpdateKeyAccessServerRequest' + $ref: '#/components/schemas/policy.kasregistry.ListKeyMappingsRequest' required: true responses: default: @@ -503,9 +503,105 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.kasregistry.UpdateKeyAccessServerResponse' + $ref: '#/components/schemas/policy.kasregistry.ListKeyMappingsResponse' components: schemas: + common.MetadataUpdateEnum: + type: string + title: MetadataUpdateEnum + enum: + - METADATA_UPDATE_ENUM_UNSPECIFIED + - METADATA_UPDATE_ENUM_EXTEND + - METADATA_UPDATE_ENUM_REPLACE + policy.Algorithm: + type: string + title: Algorithm + enum: + - ALGORITHM_UNSPECIFIED + - 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 + description: Supported key algorithms. + policy.KasPublicKeyAlgEnum: + type: string + title: KasPublicKeyAlgEnum + enum: + - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED + - KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048 + - KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 + policy.KeyMode: + type: string + title: KeyMode + enum: + - KEY_MODE_UNSPECIFIED + - KEY_MODE_CONFIG_ROOT_KEY + - KEY_MODE_PROVIDER_ROOT_KEY + - KEY_MODE_REMOTE + - KEY_MODE_PUBLIC_KEY_ONLY + description: Describes the management and operational mode of a cryptographic key. + policy.KeyStatus: + type: string + title: KeyStatus + enum: + - KEY_STATUS_UNSPECIFIED + - KEY_STATUS_ACTIVE + - KEY_STATUS_ROTATED + description: The status of the key + policy.SortDirection: + type: string + title: SortDirection + enum: + - SORT_DIRECTION_UNSPECIFIED + - SORT_DIRECTION_ASC + - SORT_DIRECTION_DESC + description: |- + Sorting direction shared across list APIs. + When the 'sort' field is omitted or the chosen sort 'field' is UNSPECIFIED, + the endpoint's request message defines the default ordering; see the + specific List* request docs. + policy.SourceType: + type: string + title: SourceType + enum: + - SOURCE_TYPE_UNSPECIFIED + - SOURCE_TYPE_INTERNAL + - SOURCE_TYPE_EXTERNAL + description: |- + Describes whether this kas is managed by the organization or if they imported + the kas information from an external party. These two modes are necessary in order + to encrypt a tdf dek with an external parties kas public key. + policy.kasregistry.SortKasKeysType: + type: string + title: SortKasKeysType + enum: + - SORT_KAS_KEYS_TYPE_UNSPECIFIED + - SORT_KAS_KEYS_TYPE_KEY_ID + - SORT_KAS_KEYS_TYPE_CREATED_AT + - SORT_KAS_KEYS_TYPE_UPDATED_AT + policy.kasregistry.SortKeyAccessServersType: + type: string + title: SortKeyAccessServersType + enum: + - SORT_KEY_ACCESS_SERVERS_TYPE_UNSPECIFIED + - SORT_KEY_ACCESS_SERVERS_TYPE_NAME + - SORT_KEY_ACCESS_SERVERS_TYPE_URI + - SORT_KEY_ACCESS_SERVERS_TYPE_CREATED_AT + - SORT_KEY_ACCESS_SERVERS_TYPE_UPDATED_AT common.Metadata: type: object properties: @@ -561,82 +657,6 @@ components: title: value title: LabelsEntry additionalProperties: false - common.MetadataUpdateEnum: - type: string - title: MetadataUpdateEnum - enum: - - METADATA_UPDATE_ENUM_UNSPECIFIED - - METADATA_UPDATE_ENUM_EXTEND - - METADATA_UPDATE_ENUM_REPLACE - connect-protocol-version: - type: number - title: Connect-Protocol-Version - enum: - - 1 - description: Define the version of the Connect protocol - const: 1 - connect-timeout-header: - type: number - title: Connect-Timeout-Ms - description: Define the timeout, in ms - connect.error: - type: object - properties: - code: - type: string - examples: - - not_found - enum: - - canceled - - unknown - - invalid_argument - - deadline_exceeded - - not_found - - already_exists - - permission_denied - - resource_exhausted - - failed_precondition - - aborted - - out_of_range - - unimplemented - - internal - - unavailable - - data_loss - - unauthenticated - description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. - message: - type: string - description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. - details: - type: array - items: - $ref: '#/components/schemas/connect.error_details.Any' - description: A list of messages that carry the error details. There is no limit on the number of messages. - title: Connect Error - additionalProperties: true - description: 'Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation' - connect.error_details.Any: - type: object - properties: - type: - type: string - description: 'A URL that acts as a globally unique identifier for the type of the serialized message. For example: `type.googleapis.com/google.rpc.ErrorInfo`. This is used to determine the schema of the data in the `value` field and is the discriminator for the `debug` field.' - value: - type: string - format: binary - description: The Protobuf message, serialized as bytes and base64-encoded. The specific message type is identified by the `type` field. - debug: - oneOf: - - type: object - title: Any - additionalProperties: true - description: Detailed error information. - discriminator: - propertyName: type - title: Debug - description: Deserialized error detail payload. The 'type' field indicates the schema. This field is for easier debugging and should not be relied upon for application logic. - additionalProperties: true - description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message, with an additional debug field for ConnectRPC error details. google.protobuf.BoolValue: type: boolean description: |- @@ -649,8 +669,8 @@ components: google.protobuf.Timestamp: type: string examples: - - "2023-01-15T01:30:15.01Z" - - "2024-12-25T12:00:00Z" + - 1s + - 1.000340012s format: date-time description: |- A Timestamp represents a point in time independent of any time zone or local @@ -742,20 +762,6 @@ components: the Joda Time's [`ISODateTimeFormat.dateTime()`]( http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime() ) to obtain a formatter capable of generating timestamps in this format. - policy.Algorithm: - type: string - title: Algorithm - enum: - - ALGORITHM_UNSPECIFIED - - 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 - description: Supported key algorithms. policy.AsymmetricKey: type: object properties: @@ -833,7 +839,7 @@ components: alg: not: enum: - - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED + - 0 title: alg description: |- A known algorithm type with any additional parameters encoded. @@ -845,19 +851,6 @@ components: description: |- Deprecated A KAS public key and some associated metadata for further identifcation - policy.KasPublicKeyAlgEnum: - type: string - title: KasPublicKeyAlgEnum - enum: - - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED - - KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048 - - KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 policy.KasPublicKeySet: type: object properties: @@ -905,9 +898,13 @@ components: uri: type: string title: uri - description: | + description: |+ Address of a KAS instance - uri_format // URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes. + URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes.: + ``` + this.matches('^https?://[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?)*(:[0-9]+)?(/.*)?$') + ``` + publicKey: title: public_key description: 'Deprecated: KAS can have multiple key pairs' @@ -935,16 +932,6 @@ components: title: KeyAccessServer additionalProperties: false description: Key Access Server Registry - policy.KeyMode: - type: string - title: KeyMode - enum: - - KEY_MODE_UNSPECIFIED - - KEY_MODE_CONFIG_ROOT_KEY - - KEY_MODE_PROVIDER_ROOT_KEY - - KEY_MODE_REMOTE - - KEY_MODE_PUBLIC_KEY_ONLY - description: Describes the management and operational mode of a cryptographic key. policy.KeyProviderConfig: type: object properties: @@ -967,14 +954,6 @@ components: $ref: '#/components/schemas/common.Metadata' title: KeyProviderConfig additionalProperties: false - policy.KeyStatus: - type: string - title: KeyStatus - enum: - - KEY_STATUS_UNSPECIFIED - - KEY_STATUS_ACTIVE - - KEY_STATUS_ROTATED - description: The status of the key policy.PageRequest: type: object properties: @@ -1034,8 +1013,7 @@ components: policy.PublicKey: type: object oneOf: - - type: object - properties: + - properties: cached: title: cached description: public key with additional information. Current preferred version @@ -1043,14 +1021,17 @@ components: title: cached required: - cached - - type: object - properties: + - properties: remote: type: string title: remote - description: | + description: |+ kas public key url - optional since can also be retrieved via public key - uri_format // URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes. + URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes.: + ``` + this.matches('^https://[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?)*(/.*)?$') + ``` + title: remote required: - remote @@ -1097,30 +1078,7 @@ components: type: string title: pem title: SimpleKasPublicKey - additionalProperties: false - policy.SortDirection: - type: string - title: SortDirection - enum: - - SORT_DIRECTION_UNSPECIFIED - - SORT_DIRECTION_ASC - - SORT_DIRECTION_DESC - description: |- - Sorting direction shared across list APIs. - When the 'sort' field is omitted or the chosen sort 'field' is UNSPECIFIED, - the endpoint's request message defines the default ordering; see the - specific List* request docs. - policy.SourceType: - type: string - title: SourceType - enum: - - SOURCE_TYPE_UNSPECIFIED - - SOURCE_TYPE_INTERNAL - - SOURCE_TYPE_EXTERNAL - description: |- - Describes whether this kas is managed by the organization or if they imported - the kas information from an external party. These two modes are necessary in order - to encrypt a tdf dek with an external parties kas public key. + additionalProperties: false policy.kasregistry.ActivatePublicKeyRequest: type: object properties: @@ -1158,9 +1116,13 @@ components: uri: type: string title: uri - description: | + description: |+ Required - uri_format // URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes. + URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes.: + ``` + this.isUri() + ``` + publicKey: title: public_key description: Deprecated @@ -1173,9 +1135,13 @@ components: type: string title: name maxLength: 253 - description: | + description: |+ Optional - kas_name_format // Registered KAS name must be an alphanumeric string, allowing hyphens, and underscores but not as the first or last character. The stored KAS name will be normalized to lower case. + Registered KAS name must be an alphanumeric string, allowing hyphens, and underscores but not as the first or last character. The stored KAS name will be normalized to lower case.: + ``` + size(this) > 0 ? this.matches('^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$') : true + ``` + metadata: title: metadata description: Common metadata @@ -1205,15 +1171,23 @@ components: description: Required A user-defined identifier for the key keyAlgorithm: title: key_algorithm - description: | + description: |+ Required The algorithm to be used for the key - key_algorithm_defined // The key_algorithm must be one of the defined values. + The key_algorithm must be one of the defined values.: + ``` + this in [1, 2, 3, 4, 5, 6, 7, 8, 20, 21] + ``` + $ref: '#/components/schemas/policy.Algorithm' keyMode: title: key_mode - description: | + description: |+ Required The mode of the key (e.g., local or external) - key_mode_defined // The key_mode must be one of the defined values (1-4). + The key_mode must be one of the defined values (1-4).: + ``` + this >= 1 && this <= 4 + ``` + $ref: '#/components/schemas/policy.KeyMode' publicKeyCtx: title: public_key_ctx @@ -1239,11 +1213,23 @@ components: required: - publicKeyCtx additionalProperties: false - description: | + description: |+ Create a new asymmetric key for the specified Key Access Server (KAS) - private_key_ctx_for_public_key_only // private_key_ctx must not be set if key_mode is KEY_MODE_PUBLIC_KEY_ONLY. - private_key_ctx_optionally_required // The wrapped_key is required if key_mode is KEY_MODE_CONFIG_ROOT_KEY or KEY_MODE_PROVIDER_ROOT_KEY. The wrapped_key must be empty if key_mode is KEY_MODE_REMOTE or KEY_MODE_PUBLIC_KEY_ONLY. - provider_config_id_optionally_required // Provider config id is required if key_mode is KEY_MODE_PROVIDER_ROOT_KEY or KEY_MODE_REMOTE. It must be empty for KEY_MODE_CONFIG_ROOT_KEY and KEY_MODE_PUBLIC_KEY_ONLY. + The wrapped_key is required if key_mode is KEY_MODE_CONFIG_ROOT_KEY or KEY_MODE_PROVIDER_ROOT_KEY. The wrapped_key must be empty if key_mode is KEY_MODE_REMOTE or KEY_MODE_PUBLIC_KEY_ONLY.: + ``` + ((this.key_mode == 1 || this.key_mode == 2) && this.private_key_ctx.wrapped_key != '') || ((this.key_mode == 3 || this.key_mode == 4) && this.private_key_ctx.wrapped_key == '') + ``` + + Provider config id is required if key_mode is KEY_MODE_PROVIDER_ROOT_KEY or KEY_MODE_REMOTE. It must be empty for KEY_MODE_CONFIG_ROOT_KEY and KEY_MODE_PUBLIC_KEY_ONLY.: + ``` + ((this.key_mode == 1 || this.key_mode == 4) && this.provider_config_id == '') || ((this.key_mode == 2 || this.key_mode == 3) && this.provider_config_id != '') + ``` + + private_key_ctx must not be set if key_mode is KEY_MODE_PUBLIC_KEY_ONLY.: + ``` + !(this.key_mode == 4 && has(this.private_key_ctx)) + ``` + policy.kasregistry.CreateKeyResponse: type: object properties: @@ -1332,49 +1318,53 @@ components: additionalProperties: false policy.kasregistry.GetKeyAccessServerRequest: type: object - allOf: + oneOf: - properties: - id: + kasId: type: string - title: id - format: uuid - description: Deprecated - deprecated: true - - oneOf: - - type: object - properties: - kasId: - type: string - title: kas_id - format: uuid - description: 'option (buf.validate.oneof).required = true; // TODO: enable this when we remove the deprecated field' title: kas_id - required: - - kasId - - type: object - properties: - name: - type: string - title: name - minLength: 1 + format: uuid + description: 'option (buf.validate.oneof).required = true; // TODO: enable this when we remove the deprecated field' + title: kas_id + required: + - kasId + - properties: + name: + type: string title: name - required: - - name - - type: object - properties: - uri: - type: string - title: uri - minLength: 1 - format: uri + minLength: 1 + title: name + required: + - name + - properties: + uri: + type: string title: uri - required: - - uri + minLength: 1 + format: uri + title: uri + required: + - uri + properties: + id: + type: string + title: id + format: uuid + description: Deprecated + deprecated: true title: GetKeyAccessServerRequest additionalProperties: false - description: | - exclusive_fields // Either use deprecated 'id' field or one of 'kas_id' or 'uri', but not both - required_fields // Either id or one of kas_id or uri must be set + description: |+ + Either use deprecated 'id' field or one of 'kas_id' or 'uri', but not both: + ``` + !(has(this.id) && (has(this.kas_id) || has(this.uri) || has(this.name))) + ``` + + Either id or one of kas_id or uri must be set: + ``` + has(this.id) || has(this.kas_id) || has(this.uri) || has(this.name) + ``` + policy.kasregistry.GetKeyAccessServerResponse: type: object properties: @@ -1386,8 +1376,7 @@ components: policy.kasregistry.GetKeyRequest: type: object oneOf: - - type: object - properties: + - properties: id: type: string title: id @@ -1396,8 +1385,7 @@ components: title: id required: - id - - type: object - properties: + - properties: key: title: key $ref: '#/components/schemas/policy.kasregistry.KasKeyIdentifier' @@ -1420,8 +1408,7 @@ components: policy.kasregistry.GetPublicKeyRequest: type: object oneOf: - - type: object - properties: + - properties: id: type: string title: id @@ -1453,42 +1440,38 @@ components: description: Can be namespace, attribute definition, or value policy.kasregistry.KasKeyIdentifier: type: object - allOf: + oneOf: - properties: - kid: + kasId: type: string - title: kid - minLength: 1 - description: Required Key ID of the key in question - - oneOf: - - type: object - properties: - kasId: - type: string - title: kas_id - format: uuid title: kas_id - required: - - kasId - - type: object - properties: - name: - type: string - title: name - minLength: 1 + format: uuid + title: kas_id + required: + - kasId + - properties: + name: + type: string title: name - required: - - name - - type: object - properties: - uri: - type: string - title: uri - minLength: 1 - format: uri + minLength: 1 + title: name + required: + - name + - properties: + uri: + type: string title: uri - required: - - uri + minLength: 1 + format: uri + title: uri + required: + - uri + properties: + kid: + type: string + title: kid + minLength: 1 + description: Required Key ID of the key in question title: KasKeyIdentifier additionalProperties: false description: Nested message for specifying the active key using KAS ID and Key ID @@ -1573,31 +1556,43 @@ components: kasId: type: string title: kas_id - description: | + description: |+ Optional Filter LIST by ID of a registered Key Access Server. If neither is provided, grants from all registered KASs to policy attribute objects are returned. - optional_uuid_format // Optional field must be a valid UUID + Optional field must be a valid UUID: + ``` + size(this) == 0 || this.matches('[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}') + ``` + kasUri: type: string title: kas_uri - description: | + description: |+ Optional Filter LIST by URI of a registered Key Access Server. If none is provided, grants from all registered KASs to policy attribute objects are returned. - optional_uri_format // Optional URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes. + Optional URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes.: + ``` + size(this) == 0 || this.isUri() + ``` + kasName: type: string title: kas_name maxLength: 253 - description: | + description: |+ Optional Filter LIST by name of a registered Key Access Server. If none are provided, grants from all registered KASs to policy attribute objects are returned. - kas_name_format // Registered KAS name must be an alphanumeric string, allowing hyphens, and underscores but not as the first or last character. The stored KAS name will be normalized to lower case. + Registered KAS name must be an alphanumeric string, allowing hyphens, and underscores but not as the first or last character. The stored KAS name will be normalized to lower case.: + ``` + size(this) == 0 || this.matches('^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$') + ``` + pagination: title: pagination description: Optional @@ -1660,31 +1655,28 @@ components: additionalProperties: false policy.kasregistry.ListKeyMappingsRequest: type: object - allOf: + oneOf: - properties: - pagination: - title: pagination - description: Pagination request for the list of keys - $ref: '#/components/schemas/policy.PageRequest' - - oneOf: - - type: object - properties: - id: - type: string - title: id - format: uuid - description: The unique identifier of the key to retrieve + id: + type: string title: id - required: - - id - - type: object - properties: - key: - title: key - $ref: '#/components/schemas/policy.kasregistry.KasKeyIdentifier' + format: uuid + description: The unique identifier of the key to retrieve + title: id + required: + - id + - properties: + key: title: key - required: - - key + $ref: '#/components/schemas/policy.kasregistry.KasKeyIdentifier' + title: key + required: + - key + properties: + pagination: + title: pagination + description: Pagination request for the list of keys + $ref: '#/components/schemas/policy.PageRequest' title: ListKeyMappingsRequest additionalProperties: false policy.kasregistry.ListKeyMappingsResponse: @@ -1704,68 +1696,67 @@ components: additionalProperties: false policy.kasregistry.ListKeysRequest: type: object - allOf: + oneOf: - properties: - keyAlgorithm: - title: key_algorithm - description: | - Filter keys by algorithm - key_algorithm_defined // The key_algorithm must be one of the defined values. - $ref: '#/components/schemas/policy.Algorithm' - legacy: - type: - - boolean - - "null" - title: legacy - description: Optional Filter for legacy keys - pagination: - title: pagination - description: Optional Pagination request for the list of keys - $ref: '#/components/schemas/policy.PageRequest' - sort: - type: array - items: - $ref: '#/components/schemas/policy.kasregistry.KasKeysSort' - title: sort - maxItems: 1 - description: |- - Optional - CONSTRAINT: max 1 item - Sort defaults: - - direction UNSPECIFIED defaults to DESC for the specified field - - field UNSPECIFIED defaults to created_at with the specified direction - - both UNSPECIFIED or sort omitted defaults to created_at DESC - - oneOf: - - type: object - properties: - kasId: - type: string - title: kas_id - format: uuid - description: Filter keys by the KAS ID + kasId: + type: string title: kas_id - required: - - kasId - - type: object - properties: - kasName: - type: string - title: kas_name - minLength: 1 - description: Filter keys by the KAS name + format: uuid + description: Filter keys by the KAS ID + title: kas_id + required: + - kasId + - properties: + kasName: + type: string title: kas_name - required: - - kasName - - type: object - properties: - kasUri: - type: string - title: kas_uri - minLength: 1 - format: uri - description: Filter keys by the KAS URI + minLength: 1 + description: Filter keys by the KAS name + title: kas_name + required: + - kasName + - properties: + kasUri: + type: string title: kas_uri - required: - - kasUri + minLength: 1 + format: uri + description: Filter keys by the KAS URI + title: kas_uri + required: + - kasUri + properties: + keyAlgorithm: + title: key_algorithm + description: |+ + 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] + ``` + + $ref: '#/components/schemas/policy.Algorithm' + legacy: + type: boolean + title: legacy + description: Optional Filter for legacy keys + nullable: true + pagination: + title: pagination + description: Optional Pagination request for the list of keys + $ref: '#/components/schemas/policy.PageRequest' + sort: + type: array + items: + $ref: '#/components/schemas/policy.kasregistry.KasKeysSort' + title: sort + maxItems: 1 + description: |- + Optional - CONSTRAINT: max 1 item + Sort defaults: + - direction UNSPECIFIED defaults to DESC for the specified field + - field UNSPECIFIED defaults to created_at with the specified direction + - both UNSPECIFIED or sort omitted defaults to created_at DESC title: ListKeysRequest additionalProperties: false description: List all asymmetric keys managed by a specific Key Access Server or with a given algorithm @@ -1787,49 +1778,45 @@ components: description: Response to a ListKeysRequest, containing the list of asymmetric keys and pagination information policy.kasregistry.ListPublicKeyMappingRequest: type: object - allOf: + oneOf: - properties: - publicKeyId: + kasId: type: string - title: public_key_id + title: kas_id format: uuid - description: Optional Public Key ID - pagination: - title: pagination description: Optional - $ref: '#/components/schemas/policy.PageRequest' - - oneOf: - - type: object - properties: - kasId: - type: string - title: kas_id - format: uuid - description: Optional - title: kas_id - required: - - kasId - - type: object - properties: - kasName: - type: string - title: kas_name - minLength: 1 - description: Optional + title: kas_id + required: + - kasId + - properties: + kasName: + type: string title: kas_name - required: - - kasName - - type: object - properties: - kasUri: - type: string - title: kas_uri - minLength: 1 - format: uri - description: Optional + minLength: 1 + description: Optional + title: kas_name + required: + - kasName + - properties: + kasUri: + type: string title: kas_uri - required: - - kasUri + minLength: 1 + format: uri + description: Optional + title: kas_uri + required: + - kasUri + properties: + publicKeyId: + type: string + title: public_key_id + format: uuid + description: Optional Public Key ID + pagination: + title: pagination + description: Optional + $ref: '#/components/schemas/policy.PageRequest' title: ListPublicKeyMappingRequest additionalProperties: false policy.kasregistry.ListPublicKeyMappingResponse: @@ -1900,44 +1887,40 @@ components: additionalProperties: false policy.kasregistry.ListPublicKeysRequest: type: object - allOf: + oneOf: - properties: - pagination: - title: pagination - description: Optional - $ref: '#/components/schemas/policy.PageRequest' - - oneOf: - - type: object - properties: - kasId: - type: string - title: kas_id - format: uuid - description: Optional + kasId: + type: string title: kas_id - required: - - kasId - - type: object - properties: - kasName: - type: string - title: kas_name - minLength: 1 - description: Optional + format: uuid + description: Optional + title: kas_id + required: + - kasId + - properties: + kasName: + type: string title: kas_name - required: - - kasName - - type: object - properties: - kasUri: - type: string - title: kas_uri - minLength: 1 - format: uri - description: Optional + minLength: 1 + description: Optional + title: kas_name + required: + - kasName + - properties: + kasUri: + type: string title: kas_uri - required: - - kasUri + minLength: 1 + format: uri + description: Optional + title: kas_uri + required: + - kasUri + properties: + pagination: + title: pagination + description: Optional + $ref: '#/components/schemas/policy.PageRequest' title: ListPublicKeysRequest additionalProperties: false policy.kasregistry.ListPublicKeysResponse: @@ -1968,38 +1951,47 @@ components: additionalProperties: false policy.kasregistry.RotateKeyRequest: type: object - allOf: + oneOf: - properties: - newKey: - title: new_key - description: Information about the new key to be rotated in - $ref: '#/components/schemas/policy.kasregistry.RotateKeyRequest.NewKey' - - oneOf: - - type: object - properties: - id: - type: string - title: id - format: uuid - description: Current Active Key UUID + id: + type: string title: id - required: - - id - - type: object - properties: - key: - title: key - description: Alternative way to specify the active key using KAS ID and Key ID - $ref: '#/components/schemas/policy.kasregistry.KasKeyIdentifier' + format: uuid + description: Current Active Key UUID + title: id + required: + - id + - properties: + key: title: key - required: - - key + description: Alternative way to specify the active key using KAS ID and Key ID + $ref: '#/components/schemas/policy.kasregistry.KasKeyIdentifier' + title: key + required: + - key + properties: + newKey: + title: new_key + description: Information about the new key to be rotated in + $ref: '#/components/schemas/policy.kasregistry.RotateKeyRequest.NewKey' title: RotateKeyRequest additionalProperties: false - description: | - private_key_ctx_for_public_key_only // private_key_ctx must not be set if key_mode is KEY_MODE_PUBLIC_KEY_ONLY. - private_key_ctx_optionally_required // For the new key, the wrapped_key is required if key_mode is KEY_MODE_CONFIG_ROOT_KEY or KEY_MODE_PROVIDER_ROOT_KEY. The wrapped_key must be empty if key_mode is KEY_MODE_REMOTE or KEY_MODE_PUBLIC_KEY_ONLY. - provider_config_id_optionally_required // For the new key, provider config id is required if key_mode is KEY_MODE_PROVIDER_ROOT_KEY or KEY_MODE_REMOTE. It must be empty for KEY_MODE_CONFIG_ROOT_KEY and KEY_MODE_PUBLIC_KEY_ONLY. + description: |+ + For the new key, the wrapped_key is required if key_mode is KEY_MODE_CONFIG_ROOT_KEY or KEY_MODE_PROVIDER_ROOT_KEY. The wrapped_key must be empty if key_mode is KEY_MODE_REMOTE or KEY_MODE_PUBLIC_KEY_ONLY.: + ``` + ((this.new_key.key_mode == 1 || this.new_key.key_mode == 2) && this.new_key.private_key_ctx.wrapped_key != '') || ((this.new_key.key_mode == 3 || this.new_key.key_mode == 4) && this.new_key.private_key_ctx.wrapped_key == '') + ``` + + For the new key, provider config id is required if key_mode is KEY_MODE_PROVIDER_ROOT_KEY or KEY_MODE_REMOTE. It must be empty for KEY_MODE_CONFIG_ROOT_KEY and KEY_MODE_PUBLIC_KEY_ONLY.: + ``` + ((this.new_key.key_mode == 1 || this.new_key.key_mode == 4) && this.new_key.provider_config_id == '') || ((this.new_key.key_mode == 2 || this.new_key.key_mode == 3) && this.new_key.provider_config_id != '') + ``` + + private_key_ctx must not be set if key_mode is KEY_MODE_PUBLIC_KEY_ONLY.: + ``` + !(this.new_key.key_mode == 4 && has(this.new_key.private_key_ctx)) + ``` + policy.kasregistry.RotateKeyRequest.NewKey: type: object properties: @@ -2010,15 +2002,23 @@ components: description: Required algorithm: title: algorithm - description: | + description: |+ Required - key_algorithm_defined // The key_algorithm must be one of the defined values. + The key_algorithm must be one of the defined values.: + ``` + this in [1, 2, 3, 4, 5, 6, 7, 8, 20, 21] + ``` + $ref: '#/components/schemas/policy.Algorithm' keyMode: title: key_mode - description: | + description: |+ Required - new_key_mode_defined // The new key_mode must be one of the defined values (1-4). + The new key_mode must be one of the defined values (1-4).: + ``` + this in [1, 2, 3, 4] + ``` + $ref: '#/components/schemas/policy.KeyMode' publicKeyCtx: title: public_key_ctx @@ -2083,8 +2083,7 @@ components: policy.kasregistry.SetBaseKeyRequest: type: object oneOf: - - type: object - properties: + - properties: id: type: string title: id @@ -2093,8 +2092,7 @@ components: title: id required: - id - - type: object - properties: + - properties: key: title: key description: Alternative way to specify the key using KAS ID and Key ID @@ -2120,23 +2118,6 @@ components: $ref: '#/components/schemas/policy.SimpleKasKey' title: SetBaseKeyResponse additionalProperties: false - policy.kasregistry.SortKasKeysType: - type: string - title: SortKasKeysType - enum: - - SORT_KAS_KEYS_TYPE_UNSPECIFIED - - SORT_KAS_KEYS_TYPE_KEY_ID - - SORT_KAS_KEYS_TYPE_CREATED_AT - - SORT_KAS_KEYS_TYPE_UPDATED_AT - policy.kasregistry.SortKeyAccessServersType: - type: string - title: SortKeyAccessServersType - enum: - - SORT_KEY_ACCESS_SERVERS_TYPE_UNSPECIFIED - - SORT_KEY_ACCESS_SERVERS_TYPE_NAME - - SORT_KEY_ACCESS_SERVERS_TYPE_URI - - SORT_KEY_ACCESS_SERVERS_TYPE_CREATED_AT - - SORT_KEY_ACCESS_SERVERS_TYPE_UPDATED_AT policy.kasregistry.UpdateKeyAccessServerRequest: type: object properties: @@ -2148,9 +2129,13 @@ components: uri: type: string title: uri - description: | + description: |+ Optional - optional_uri_format // Optional URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes. + Optional URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes.: + ``` + size(this) == 0 || this.isUri() + ``` + publicKey: title: public_key description: |- @@ -2170,9 +2155,13 @@ components: type: string title: name maxLength: 253 - description: | + description: |+ Optional - kas_name_format // Registered KAS name must be an alphanumeric string, allowing hyphens, and underscores but not as the first or last character. The stored KAS name will be normalized to lower case. + Registered KAS name must be an alphanumeric string, allowing hyphens, and underscores but not as the first or last character. The stored KAS name will be normalized to lower case.: + ``` + size(this) == 0 || this.matches('^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$') + ``` + metadata: title: metadata description: |- @@ -2212,9 +2201,13 @@ components: $ref: '#/components/schemas/common.MetadataUpdateEnum' title: UpdateKeyRequest additionalProperties: false - description: | + description: |+ Update an existing asymmetric key in the Key Management System - metadata_update_behavior // Metadata update behavior must be either APPEND or REPLACE, when updating metadata. + Metadata update behavior must be either APPEND or REPLACE, when updating metadata.: + ``` + ((!has(this.metadata)) || (has(this.metadata) && this.metadata_update_behavior != 0)) + ``` + policy.kasregistry.UpdateKeyResponse: type: object properties: @@ -2252,6 +2245,63 @@ components: $ref: '#/components/schemas/policy.Key' title: UpdatePublicKeyResponse additionalProperties: false + connect-protocol-version: + type: number + title: Connect-Protocol-Version + enum: + - 1 + description: Define the version of the Connect protocol + const: 1 + connect-timeout-header: + type: number + title: Connect-Timeout-Ms + description: Define the timeout, in ms + connect.error: + type: object + properties: + code: + type: string + examples: + - not_found + enum: + - canceled + - unknown + - invalid_argument + - deadline_exceeded + - not_found + - already_exists + - permission_denied + - resource_exhausted + - failed_precondition + - aborted + - out_of_range + - unimplemented + - internal + - unavailable + - data_loss + - unauthenticated + description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. + message: + type: string + description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. + detail: + $ref: '#/components/schemas/google.protobuf.Any' + title: Connect Error + additionalProperties: true + description: 'Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation' + google.protobuf.Any: + type: object + properties: + type: + type: string + value: + type: string + format: binary + debug: + type: object + additionalProperties: true + additionalProperties: true + description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message. security: [] tags: - name: policy.kasregistry.KeyAccessServerRegistryService diff --git a/docs/openapi/policy/keymanagement/key_management.openapi.yaml b/docs/openapi/policy/keymanagement/key_management.openapi.yaml index 94f70d9b92..61a3e433ba 100644 --- a/docs/openapi/policy/keymanagement/key_management.openapi.yaml +++ b/docs/openapi/policy/keymanagement/key_management.openapi.yaml @@ -40,12 +40,12 @@ paths: application/json: schema: $ref: '#/components/schemas/policy.keymanagement.CreateProviderConfigResponse' - /policy.keymanagement.KeyManagementService/DeleteProviderConfig: + /policy.keymanagement.KeyManagementService/GetProviderConfig: post: tags: - policy.keymanagement.KeyManagementService - summary: DeleteProviderConfig - operationId: policy.keymanagement.KeyManagementService.DeleteProviderConfig + summary: GetProviderConfig + operationId: policy.keymanagement.KeyManagementService.GetProviderConfig parameters: - name: Connect-Protocol-Version in: header @@ -60,7 +60,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.keymanagement.DeleteProviderConfigRequest' + $ref: '#/components/schemas/policy.keymanagement.GetProviderConfigRequest' required: true responses: default: @@ -74,13 +74,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.keymanagement.DeleteProviderConfigResponse' - /policy.keymanagement.KeyManagementService/GetProviderConfig: + $ref: '#/components/schemas/policy.keymanagement.GetProviderConfigResponse' + /policy.keymanagement.KeyManagementService/ListProviderConfigs: post: tags: - policy.keymanagement.KeyManagementService - summary: GetProviderConfig - operationId: policy.keymanagement.KeyManagementService.GetProviderConfig + summary: ListProviderConfigs + operationId: policy.keymanagement.KeyManagementService.ListProviderConfigs parameters: - name: Connect-Protocol-Version in: header @@ -95,7 +95,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.keymanagement.GetProviderConfigRequest' + $ref: '#/components/schemas/policy.keymanagement.ListProviderConfigsRequest' required: true responses: default: @@ -109,13 +109,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.keymanagement.GetProviderConfigResponse' - /policy.keymanagement.KeyManagementService/ListProviderConfigs: + $ref: '#/components/schemas/policy.keymanagement.ListProviderConfigsResponse' + /policy.keymanagement.KeyManagementService/UpdateProviderConfig: post: tags: - policy.keymanagement.KeyManagementService - summary: ListProviderConfigs - operationId: policy.keymanagement.KeyManagementService.ListProviderConfigs + summary: UpdateProviderConfig + operationId: policy.keymanagement.KeyManagementService.UpdateProviderConfig parameters: - name: Connect-Protocol-Version in: header @@ -130,7 +130,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.keymanagement.ListProviderConfigsRequest' + $ref: '#/components/schemas/policy.keymanagement.UpdateProviderConfigRequest' required: true responses: default: @@ -144,13 +144,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.keymanagement.ListProviderConfigsResponse' - /policy.keymanagement.KeyManagementService/UpdateProviderConfig: + $ref: '#/components/schemas/policy.keymanagement.UpdateProviderConfigResponse' + /policy.keymanagement.KeyManagementService/DeleteProviderConfig: post: tags: - policy.keymanagement.KeyManagementService - summary: UpdateProviderConfig - operationId: policy.keymanagement.KeyManagementService.UpdateProviderConfig + summary: DeleteProviderConfig + operationId: policy.keymanagement.KeyManagementService.DeleteProviderConfig parameters: - name: Connect-Protocol-Version in: header @@ -165,7 +165,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.keymanagement.UpdateProviderConfigRequest' + $ref: '#/components/schemas/policy.keymanagement.DeleteProviderConfigRequest' required: true responses: default: @@ -179,9 +179,16 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.keymanagement.UpdateProviderConfigResponse' + $ref: '#/components/schemas/policy.keymanagement.DeleteProviderConfigResponse' components: schemas: + common.MetadataUpdateEnum: + type: string + title: MetadataUpdateEnum + enum: + - METADATA_UPDATE_ENUM_UNSPECIFIED + - METADATA_UPDATE_ENUM_EXTEND + - METADATA_UPDATE_ENUM_REPLACE common.Metadata: type: object properties: @@ -237,87 +244,11 @@ components: title: value title: LabelsEntry additionalProperties: false - common.MetadataUpdateEnum: - type: string - title: MetadataUpdateEnum - enum: - - METADATA_UPDATE_ENUM_UNSPECIFIED - - METADATA_UPDATE_ENUM_EXTEND - - METADATA_UPDATE_ENUM_REPLACE - connect-protocol-version: - type: number - title: Connect-Protocol-Version - enum: - - 1 - description: Define the version of the Connect protocol - const: 1 - connect-timeout-header: - type: number - title: Connect-Timeout-Ms - description: Define the timeout, in ms - connect.error: - type: object - properties: - code: - type: string - examples: - - not_found - enum: - - canceled - - unknown - - invalid_argument - - deadline_exceeded - - not_found - - already_exists - - permission_denied - - resource_exhausted - - failed_precondition - - aborted - - out_of_range - - unimplemented - - internal - - unavailable - - data_loss - - unauthenticated - description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. - message: - type: string - description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. - details: - type: array - items: - $ref: '#/components/schemas/connect.error_details.Any' - description: A list of messages that carry the error details. There is no limit on the number of messages. - title: Connect Error - additionalProperties: true - description: 'Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation' - connect.error_details.Any: - type: object - properties: - type: - type: string - description: 'A URL that acts as a globally unique identifier for the type of the serialized message. For example: `type.googleapis.com/google.rpc.ErrorInfo`. This is used to determine the schema of the data in the `value` field and is the discriminator for the `debug` field.' - value: - type: string - format: binary - description: The Protobuf message, serialized as bytes and base64-encoded. The specific message type is identified by the `type` field. - debug: - oneOf: - - type: object - title: Any - additionalProperties: true - description: Detailed error information. - discriminator: - propertyName: type - title: Debug - description: Deserialized error detail payload. The 'type' field indicates the schema. This field is for easier debugging and should not be relied upon for application logic. - additionalProperties: true - description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message, with an additional debug field for ConnectRPC error details. google.protobuf.Timestamp: type: string examples: - - "2023-01-15T01:30:15.01Z" - - "2024-12-25T12:00:00Z" + - 1s + - 1.000340012s format: date-time description: |- A Timestamp represents a point in time independent of any time zone or local @@ -535,31 +466,28 @@ components: additionalProperties: false policy.keymanagement.GetProviderConfigRequest: type: object - allOf: + oneOf: - properties: - manager: + id: type: string - title: manager - description: Optional - filter by manager type when searching by name - - oneOf: - - type: object - properties: - id: - type: string - title: id - format: uuid title: id - required: - - id - - type: object - properties: - name: - type: string - title: name - minLength: 1 + format: uuid + title: id + required: + - id + - properties: + name: + type: string title: name - required: - - name + minLength: 1 + title: name + required: + - name + properties: + manager: + type: string + title: manager + description: Optional - filter by manager type when searching by name title: GetProviderConfigRequest additionalProperties: false policy.keymanagement.GetProviderConfigResponse: @@ -632,6 +560,63 @@ components: $ref: '#/components/schemas/policy.KeyProviderConfig' title: UpdateProviderConfigResponse additionalProperties: false + connect-protocol-version: + type: number + title: Connect-Protocol-Version + enum: + - 1 + description: Define the version of the Connect protocol + const: 1 + connect-timeout-header: + type: number + title: Connect-Timeout-Ms + description: Define the timeout, in ms + connect.error: + type: object + properties: + code: + type: string + examples: + - not_found + enum: + - canceled + - unknown + - invalid_argument + - deadline_exceeded + - not_found + - already_exists + - permission_denied + - resource_exhausted + - failed_precondition + - aborted + - out_of_range + - unimplemented + - internal + - unavailable + - data_loss + - unauthenticated + description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. + message: + type: string + description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. + detail: + $ref: '#/components/schemas/google.protobuf.Any' + title: Connect Error + additionalProperties: true + description: 'Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation' + google.protobuf.Any: + type: object + properties: + type: + type: string + value: + type: string + format: binary + debug: + type: object + additionalProperties: true + additionalProperties: true + description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message. security: [] tags: - name: policy.keymanagement.KeyManagementService diff --git a/docs/openapi/policy/namespaces/namespaces.openapi.yaml b/docs/openapi/policy/namespaces/namespaces.openapi.yaml index 506217b193..e0e1971f35 100644 --- a/docs/openapi/policy/namespaces/namespaces.openapi.yaml +++ b/docs/openapi/policy/namespaces/namespaces.openapi.yaml @@ -2,13 +2,12 @@ openapi: 3.1.0 info: title: policy.namespaces paths: - /policy.namespaces.NamespaceService/AssignKeyAccessServerToNamespace: + /policy.namespaces.NamespaceService/GetNamespace: post: tags: - policy.namespaces.NamespaceService - summary: AssignKeyAccessServerToNamespace - description: 'Deprecated: utilize AssignPublicKeyToNamespace' - operationId: policy.namespaces.NamespaceService.AssignKeyAccessServerToNamespace + summary: GetNamespace + operationId: policy.namespaces.NamespaceService.GetNamespace parameters: - name: Connect-Protocol-Version in: header @@ -23,7 +22,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.namespaces.AssignKeyAccessServerToNamespaceRequest' + $ref: '#/components/schemas/policy.namespaces.GetNamespaceRequest' required: true responses: default: @@ -37,18 +36,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.namespaces.AssignKeyAccessServerToNamespaceResponse' - deprecated: true - /policy.namespaces.NamespaceService/AssignPublicKeyToNamespace: + $ref: '#/components/schemas/policy.namespaces.GetNamespaceResponse' + /policy.namespaces.NamespaceService/ListNamespaces: post: tags: - policy.namespaces.NamespaceService - summary: AssignPublicKeyToNamespace - description: |- - --------------------------------------* - Namespace <> Key RPCs - --------------------------------------- - operationId: policy.namespaces.NamespaceService.AssignPublicKeyToNamespace + summary: ListNamespaces + operationId: policy.namespaces.NamespaceService.ListNamespaces parameters: - name: Connect-Protocol-Version in: header @@ -63,7 +57,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.namespaces.AssignPublicKeyToNamespaceRequest' + $ref: '#/components/schemas/policy.namespaces.ListNamespacesRequest' required: true responses: default: @@ -77,7 +71,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.namespaces.AssignPublicKeyToNamespaceResponse' + $ref: '#/components/schemas/policy.namespaces.ListNamespacesResponse' /policy.namespaces.NamespaceService/CreateNamespace: post: tags: @@ -113,12 +107,12 @@ paths: application/json: schema: $ref: '#/components/schemas/policy.namespaces.CreateNamespaceResponse' - /policy.namespaces.NamespaceService/DeactivateNamespace: + /policy.namespaces.NamespaceService/UpdateNamespace: post: tags: - policy.namespaces.NamespaceService - summary: DeactivateNamespace - operationId: policy.namespaces.NamespaceService.DeactivateNamespace + summary: UpdateNamespace + operationId: policy.namespaces.NamespaceService.UpdateNamespace parameters: - name: Connect-Protocol-Version in: header @@ -133,7 +127,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.namespaces.DeactivateNamespaceRequest' + $ref: '#/components/schemas/policy.namespaces.UpdateNamespaceRequest' required: true responses: default: @@ -147,13 +141,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.namespaces.DeactivateNamespaceResponse' - /policy.namespaces.NamespaceService/GetNamespace: + $ref: '#/components/schemas/policy.namespaces.UpdateNamespaceResponse' + /policy.namespaces.NamespaceService/DeactivateNamespace: post: tags: - policy.namespaces.NamespaceService - summary: GetNamespace - operationId: policy.namespaces.NamespaceService.GetNamespace + summary: DeactivateNamespace + operationId: policy.namespaces.NamespaceService.DeactivateNamespace parameters: - name: Connect-Protocol-Version in: header @@ -168,7 +162,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.namespaces.GetNamespaceRequest' + $ref: '#/components/schemas/policy.namespaces.DeactivateNamespaceRequest' required: true responses: default: @@ -182,13 +176,14 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.namespaces.GetNamespaceResponse' - /policy.namespaces.NamespaceService/ListNamespaces: + $ref: '#/components/schemas/policy.namespaces.DeactivateNamespaceResponse' + /policy.namespaces.NamespaceService/AssignKeyAccessServerToNamespace: post: tags: - policy.namespaces.NamespaceService - summary: ListNamespaces - operationId: policy.namespaces.NamespaceService.ListNamespaces + summary: AssignKeyAccessServerToNamespace + description: 'Deprecated: utilize AssignPublicKeyToNamespace' + operationId: policy.namespaces.NamespaceService.AssignKeyAccessServerToNamespace parameters: - name: Connect-Protocol-Version in: header @@ -203,7 +198,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.namespaces.ListNamespacesRequest' + $ref: '#/components/schemas/policy.namespaces.AssignKeyAccessServerToNamespaceRequest' required: true responses: default: @@ -217,7 +212,8 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.namespaces.ListNamespacesResponse' + $ref: '#/components/schemas/policy.namespaces.AssignKeyAccessServerToNamespaceResponse' + deprecated: true /policy.namespaces.NamespaceService/RemoveKeyAccessServerFromNamespace: post: tags: @@ -255,12 +251,16 @@ paths: schema: $ref: '#/components/schemas/policy.namespaces.RemoveKeyAccessServerFromNamespaceResponse' deprecated: true - /policy.namespaces.NamespaceService/RemovePublicKeyFromNamespace: + /policy.namespaces.NamespaceService/AssignPublicKeyToNamespace: post: tags: - policy.namespaces.NamespaceService - summary: RemovePublicKeyFromNamespace - operationId: policy.namespaces.NamespaceService.RemovePublicKeyFromNamespace + summary: AssignPublicKeyToNamespace + description: |- + --------------------------------------* + Namespace <> Key RPCs + --------------------------------------- + operationId: policy.namespaces.NamespaceService.AssignPublicKeyToNamespace parameters: - name: Connect-Protocol-Version in: header @@ -275,7 +275,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.namespaces.RemovePublicKeyFromNamespaceRequest' + $ref: '#/components/schemas/policy.namespaces.AssignPublicKeyToNamespaceRequest' required: true responses: default: @@ -289,13 +289,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.namespaces.RemovePublicKeyFromNamespaceResponse' - /policy.namespaces.NamespaceService/UpdateNamespace: + $ref: '#/components/schemas/policy.namespaces.AssignPublicKeyToNamespaceResponse' + /policy.namespaces.NamespaceService/RemovePublicKeyFromNamespace: post: tags: - policy.namespaces.NamespaceService - summary: UpdateNamespace - operationId: policy.namespaces.NamespaceService.UpdateNamespace + summary: RemovePublicKeyFromNamespace + operationId: policy.namespaces.NamespaceService.RemovePublicKeyFromNamespace parameters: - name: Connect-Protocol-Version in: header @@ -310,7 +310,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.namespaces.UpdateNamespaceRequest' + $ref: '#/components/schemas/policy.namespaces.RemovePublicKeyFromNamespaceRequest' required: true responses: default: @@ -324,7 +324,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.namespaces.UpdateNamespaceResponse' + $ref: '#/components/schemas/policy.namespaces.RemovePublicKeyFromNamespaceResponse' components: schemas: common.ActiveStateEnum: @@ -336,6 +336,76 @@ components: - ACTIVE_STATE_ENUM_INACTIVE - ACTIVE_STATE_ENUM_ANY description: 'buflint ENUM_VALUE_PREFIX: to make sure that C++ scoping rules aren''t violated when users add new enum values to an enum in a given package' + common.MetadataUpdateEnum: + type: string + title: MetadataUpdateEnum + enum: + - METADATA_UPDATE_ENUM_UNSPECIFIED + - METADATA_UPDATE_ENUM_EXTEND + - METADATA_UPDATE_ENUM_REPLACE + policy.Algorithm: + type: string + title: Algorithm + enum: + - ALGORITHM_UNSPECIFIED + - 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 + description: Supported key algorithms. + policy.KasPublicKeyAlgEnum: + type: string + title: KasPublicKeyAlgEnum + enum: + - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED + - KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048 + - KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 + policy.SortDirection: + type: string + title: SortDirection + enum: + - SORT_DIRECTION_UNSPECIFIED + - SORT_DIRECTION_ASC + - SORT_DIRECTION_DESC + description: |- + Sorting direction shared across list APIs. + When the 'sort' field is omitted or the chosen sort 'field' is UNSPECIFIED, + the endpoint's request message defines the default ordering; see the + specific List* request docs. + policy.SourceType: + type: string + title: SourceType + enum: + - SOURCE_TYPE_UNSPECIFIED + - SOURCE_TYPE_INTERNAL + - SOURCE_TYPE_EXTERNAL + description: |- + Describes whether this kas is managed by the organization or if they imported + the kas information from an external party. These two modes are necessary in order + to encrypt a tdf dek with an external parties kas public key. + policy.namespaces.SortNamespacesType: + type: string + title: SortNamespacesType + enum: + - SORT_NAMESPACES_TYPE_UNSPECIFIED + - SORT_NAMESPACES_TYPE_NAME + - SORT_NAMESPACES_TYPE_FQN + - SORT_NAMESPACES_TYPE_CREATED_AT + - SORT_NAMESPACES_TYPE_UPDATED_AT common.Metadata: type: object properties: @@ -391,82 +461,6 @@ components: title: value title: LabelsEntry additionalProperties: false - common.MetadataUpdateEnum: - type: string - title: MetadataUpdateEnum - enum: - - METADATA_UPDATE_ENUM_UNSPECIFIED - - METADATA_UPDATE_ENUM_EXTEND - - METADATA_UPDATE_ENUM_REPLACE - connect-protocol-version: - type: number - title: Connect-Protocol-Version - enum: - - 1 - description: Define the version of the Connect protocol - const: 1 - connect-timeout-header: - type: number - title: Connect-Timeout-Ms - description: Define the timeout, in ms - connect.error: - type: object - properties: - code: - type: string - examples: - - not_found - enum: - - canceled - - unknown - - invalid_argument - - deadline_exceeded - - not_found - - already_exists - - permission_denied - - resource_exhausted - - failed_precondition - - aborted - - out_of_range - - unimplemented - - internal - - unavailable - - data_loss - - unauthenticated - description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. - message: - type: string - description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. - details: - type: array - items: - $ref: '#/components/schemas/connect.error_details.Any' - description: A list of messages that carry the error details. There is no limit on the number of messages. - title: Connect Error - additionalProperties: true - description: 'Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation' - connect.error_details.Any: - type: object - properties: - type: - type: string - description: 'A URL that acts as a globally unique identifier for the type of the serialized message. For example: `type.googleapis.com/google.rpc.ErrorInfo`. This is used to determine the schema of the data in the `value` field and is the discriminator for the `debug` field.' - value: - type: string - format: binary - description: The Protobuf message, serialized as bytes and base64-encoded. The specific message type is identified by the `type` field. - debug: - oneOf: - - type: object - title: Any - additionalProperties: true - description: Detailed error information. - discriminator: - propertyName: type - title: Debug - description: Deserialized error detail payload. The 'type' field indicates the schema. This field is for easier debugging and should not be relied upon for application logic. - additionalProperties: true - description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message, with an additional debug field for ConnectRPC error details. google.protobuf.BoolValue: type: boolean description: |- @@ -479,8 +473,8 @@ components: google.protobuf.Timestamp: type: string examples: - - "2023-01-15T01:30:15.01Z" - - "2024-12-25T12:00:00Z" + - 1s + - 1.000340012s format: date-time description: |- A Timestamp represents a point in time independent of any time zone or local @@ -572,20 +566,6 @@ components: the Joda Time's [`ISODateTimeFormat.dateTime()`]( http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime() ) to obtain a formatter capable of generating timestamps in this format. - policy.Algorithm: - type: string - title: Algorithm - enum: - - ALGORITHM_UNSPECIFIED - - 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 - description: Supported key algorithms. policy.KasPublicKey: type: object properties: @@ -604,7 +584,7 @@ components: alg: not: enum: - - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED + - 0 title: alg description: |- A known algorithm type with any additional parameters encoded. @@ -616,19 +596,6 @@ components: description: |- Deprecated A KAS public key and some associated metadata for further identifcation - policy.KasPublicKeyAlgEnum: - type: string - title: KasPublicKeyAlgEnum - enum: - - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED - - KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048 - - KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 policy.KasPublicKeySet: type: object properties: @@ -651,9 +618,13 @@ components: uri: type: string title: uri - description: | + description: |+ Address of a KAS instance - uri_format // URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes. + URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes.: + ``` + this.matches('^https?://[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?)*(:[0-9]+)?(/.*)?$') + ``` + publicKey: title: public_key description: 'Deprecated: KAS can have multiple key pairs' @@ -763,8 +734,7 @@ components: policy.PublicKey: type: object oneOf: - - type: object - properties: + - properties: cached: title: cached description: public key with additional information. Current preferred version @@ -772,14 +742,17 @@ components: title: cached required: - cached - - type: object - properties: + - properties: remote: type: string title: remote - description: | + description: |+ kas public key url - optional since can also be retrieved via public key - uri_format // URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes. + URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes.: + ``` + this.matches('^https://[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?)*(/.*)?$') + ``` + title: remote required: - remote @@ -817,29 +790,6 @@ components: title: pem title: SimpleKasPublicKey additionalProperties: false - policy.SortDirection: - type: string - title: SortDirection - enum: - - SORT_DIRECTION_UNSPECIFIED - - SORT_DIRECTION_ASC - - SORT_DIRECTION_DESC - description: |- - Sorting direction shared across list APIs. - When the 'sort' field is omitted or the chosen sort 'field' is UNSPECIFIED, - the endpoint's request message defines the default ordering; see the - specific List* request docs. - policy.SourceType: - type: string - title: SourceType - enum: - - SOURCE_TYPE_UNSPECIFIED - - SOURCE_TYPE_INTERNAL - - SOURCE_TYPE_EXTERNAL - description: |- - Describes whether this kas is managed by the organization or if they imported - the kas information from an external party. These two modes are necessary in order - to encrypt a tdf dek with an external parties kas public key. policy.namespaces.AssignKeyAccessServerToNamespaceRequest: type: object properties: @@ -883,9 +833,13 @@ components: type: string title: name maxLength: 253 - description: | + description: |+ Required - namespace_format // Namespace must be a valid hostname. It should include at least one dot, with each segment (label) starting and ending with an alphanumeric character. Each label must be 1 to 63 characters long, allowing hyphens but not as the first or last character. The top-level domain (the last segment after the final dot) must consist of at least two alphabetic characters. The stored namespace will be normalized to lower case. + Namespace must be a valid hostname. It should include at least one dot, with each segment (label) starting and ending with an alphanumeric character. Each label must be 1 to 63 characters long, allowing hyphens but not as the first or last character. The top-level domain (the last segment after the final dot) must consist of at least two alphabetic characters. The stored namespace will be normalized to lower case.: + ``` + this.matches('^([a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,}$') + ``` + metadata: title: metadata description: Optional @@ -918,40 +872,45 @@ components: additionalProperties: false policy.namespaces.GetNamespaceRequest: type: object - allOf: + oneOf: - properties: - id: + fqn: type: string - title: id - format: uuid - description: Deprecated - deprecated: true - - oneOf: - - type: object - properties: - fqn: - type: string - title: fqn - minLength: 1 - format: uri title: fqn - required: - - fqn - - type: object - properties: - namespaceId: - type: string - title: namespace_id - format: uuid - description: 'option (buf.validate.oneof).required = true; // TODO: enable this when we remove the deprecated field' + minLength: 1 + format: uri + title: fqn + required: + - fqn + - properties: + namespaceId: + type: string title: namespace_id - required: - - namespaceId + format: uuid + description: 'option (buf.validate.oneof).required = true; // TODO: enable this when we remove the deprecated field' + title: namespace_id + required: + - namespaceId + properties: + id: + type: string + title: id + format: uuid + description: Deprecated + deprecated: true title: GetNamespaceRequest additionalProperties: false - description: | - exclusive_fields // Either use deprecated 'id' field or one of 'namespace_id' or 'fqn', but not both - required_fields // Either id or one of namespace_id or fqn must be set + description: |+ + Either use deprecated 'id' field or one of 'namespace_id' or 'fqn', but not both: + ``` + !(has(this.id) && (has(this.namespace_id) || has(this.fqn))) + ``` + + Either id or one of namespace_id or fqn must be set: + ``` + has(this.id) || has(this.namespace_id) || has(this.fqn) + ``` + policy.namespaces.GetNamespaceResponse: type: object properties: @@ -1080,15 +1039,6 @@ components: $ref: '#/components/schemas/policy.namespaces.NamespaceKey' title: RemovePublicKeyFromNamespaceResponse additionalProperties: false - policy.namespaces.SortNamespacesType: - type: string - title: SortNamespacesType - enum: - - SORT_NAMESPACES_TYPE_UNSPECIFIED - - SORT_NAMESPACES_TYPE_NAME - - SORT_NAMESPACES_TYPE_FQN - - SORT_NAMESPACES_TYPE_CREATED_AT - - SORT_NAMESPACES_TYPE_UPDATED_AT policy.namespaces.UpdateNamespaceRequest: type: object properties: @@ -1114,6 +1064,63 @@ components: $ref: '#/components/schemas/policy.Namespace' title: UpdateNamespaceResponse additionalProperties: false + connect-protocol-version: + type: number + title: Connect-Protocol-Version + enum: + - 1 + description: Define the version of the Connect protocol + const: 1 + connect-timeout-header: + type: number + title: Connect-Timeout-Ms + description: Define the timeout, in ms + connect.error: + type: object + properties: + code: + type: string + examples: + - not_found + enum: + - canceled + - unknown + - invalid_argument + - deadline_exceeded + - not_found + - already_exists + - permission_denied + - resource_exhausted + - failed_precondition + - aborted + - out_of_range + - unimplemented + - internal + - unavailable + - data_loss + - unauthenticated + description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. + message: + type: string + description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. + detail: + $ref: '#/components/schemas/google.protobuf.Any' + title: Connect Error + additionalProperties: true + description: 'Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation' + google.protobuf.Any: + type: object + properties: + type: + type: string + value: + type: string + format: binary + debug: + type: object + additionalProperties: true + additionalProperties: true + description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message. security: [] tags: - name: policy.namespaces.NamespaceService diff --git a/docs/openapi/policy/objects.openapi.yaml b/docs/openapi/policy/objects.openapi.yaml index 6bef650b76..4efc2c3e93 100644 --- a/docs/openapi/policy/objects.openapi.yaml +++ b/docs/openapi/policy/objects.openapi.yaml @@ -4,6 +4,96 @@ info: paths: {} components: schemas: + policy.Action.StandardAction: + type: string + title: StandardAction + enum: + - STANDARD_ACTION_UNSPECIFIED + - STANDARD_ACTION_DECRYPT + - STANDARD_ACTION_TRANSMIT + policy.Algorithm: + type: string + title: Algorithm + enum: + - ALGORITHM_UNSPECIFIED + - 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 + description: Supported key algorithms. + policy.AttributeRuleTypeEnum: + type: string + title: AttributeRuleTypeEnum + enum: + - ATTRIBUTE_RULE_TYPE_ENUM_UNSPECIFIED + - ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF + - ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF + - ATTRIBUTE_RULE_TYPE_ENUM_HIERARCHY + policy.ConditionBooleanTypeEnum: + type: string + title: ConditionBooleanTypeEnum + enum: + - CONDITION_BOOLEAN_TYPE_ENUM_UNSPECIFIED + - CONDITION_BOOLEAN_TYPE_ENUM_AND + - CONDITION_BOOLEAN_TYPE_ENUM_OR + policy.KasPublicKeyAlgEnum: + type: string + title: KasPublicKeyAlgEnum + enum: + - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED + - KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048 + - KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 + policy.KeyMode: + type: string + title: KeyMode + enum: + - KEY_MODE_UNSPECIFIED + - KEY_MODE_CONFIG_ROOT_KEY + - KEY_MODE_PROVIDER_ROOT_KEY + - KEY_MODE_REMOTE + - KEY_MODE_PUBLIC_KEY_ONLY + description: Describes the management and operational mode of a cryptographic key. + policy.KeyStatus: + type: string + title: KeyStatus + enum: + - KEY_STATUS_UNSPECIFIED + - KEY_STATUS_ACTIVE + - KEY_STATUS_ROTATED + description: The status of the key + policy.SourceType: + type: string + title: SourceType + enum: + - SOURCE_TYPE_UNSPECIFIED + - SOURCE_TYPE_INTERNAL + - SOURCE_TYPE_EXTERNAL + description: |- + Describes whether this kas is managed by the organization or if they imported + the kas information from an external party. These two modes are necessary in order + to encrypt a tdf dek with an external parties kas public key. + policy.SubjectMappingOperatorEnum: + type: string + title: SubjectMappingOperatorEnum + enum: + - SUBJECT_MAPPING_OPERATOR_ENUM_UNSPECIFIED + - SUBJECT_MAPPING_OPERATOR_ENUM_IN + - SUBJECT_MAPPING_OPERATOR_ENUM_NOT_IN + - SUBJECT_MAPPING_OPERATOR_ENUM_IN_CONTAINS common.Metadata: type: object properties: @@ -48,8 +138,8 @@ components: google.protobuf.Timestamp: type: string examples: - - "2023-01-15T01:30:15.01Z" - - "2024-12-25T12:00:00Z" + - 1s + - 1.000340012s format: date-time description: |- A Timestamp represents a point in time independent of any time zone or local @@ -143,65 +233,41 @@ components: ) to obtain a formatter capable of generating timestamps in this format. policy.Action: type: object - allOf: + oneOf: - properties: - id: - type: string - title: id - description: Generated uuid in database - name: + custom: type: string - title: name - namespace: - title: namespace - description: Namespace context for this action - $ref: '#/components/schemas/policy.Namespace' - metadata: - title: metadata - $ref: '#/components/schemas/common.Metadata' - - oneOf: - - type: object - properties: - custom: - type: string - title: custom - description: Deprecated title: custom - required: - - custom - - type: object - properties: - standard: - title: standard - description: Deprecated - $ref: '#/components/schemas/policy.Action.StandardAction' + description: Deprecated + title: custom + required: + - custom + - properties: + standard: title: standard - required: - - standard + description: Deprecated + $ref: '#/components/schemas/policy.Action.StandardAction' + title: standard + required: + - standard + properties: + id: + type: string + title: id + description: Generated uuid in database + name: + type: string + title: name + namespace: + title: namespace + description: Namespace context for this action + $ref: '#/components/schemas/policy.Namespace' + metadata: + title: metadata + $ref: '#/components/schemas/common.Metadata' title: Action additionalProperties: false description: An action an entity can take - policy.Action.StandardAction: - type: string - title: StandardAction - enum: - - STANDARD_ACTION_UNSPECIFIED - - STANDARD_ACTION_DECRYPT - - STANDARD_ACTION_TRANSMIT - policy.Algorithm: - type: string - title: Algorithm - enum: - - ALGORITHM_UNSPECIFIED - - 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 - description: Supported key algorithms. policy.AsymmetricKey: type: object properties: @@ -303,14 +369,6 @@ components: required: - rule additionalProperties: false - policy.AttributeRuleTypeEnum: - type: string - title: AttributeRuleTypeEnum - enum: - - ATTRIBUTE_RULE_TYPE_ENUM_UNSPECIFIED - - ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF - - ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF - - ATTRIBUTE_RULE_TYPE_ENUM_HIERARCHY policy.Condition: type: object properties: @@ -328,6 +386,7 @@ components: type: array items: type: string + minItems: 1 title: subject_external_values minItems: 1 description: |- @@ -343,13 +402,6 @@ components: * A Condition defines a rule of - policy.ConditionBooleanTypeEnum: - type: string - title: ConditionBooleanTypeEnum - enum: - - CONDITION_BOOLEAN_TYPE_ENUM_UNSPECIFIED - - CONDITION_BOOLEAN_TYPE_ENUM_AND - - CONDITION_BOOLEAN_TYPE_ENUM_OR policy.ConditionGroup: type: object properties: @@ -400,7 +452,7 @@ components: alg: not: enum: - - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED + - 0 title: alg description: |- A known algorithm type with any additional parameters encoded. @@ -412,19 +464,6 @@ components: description: |- Deprecated A KAS public key and some associated metadata for further identifcation - policy.KasPublicKeyAlgEnum: - type: string - title: KasPublicKeyAlgEnum - enum: - - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED - - KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048 - - KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 policy.KasPublicKeySet: type: object properties: @@ -472,9 +511,13 @@ components: uri: type: string title: uri - description: | + description: |+ Address of a KAS instance - uri_format // URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes. + URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes.: + ``` + this.matches('^https?://[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?)*(:[0-9]+)?(/.*)?$') + ``` + publicKey: title: public_key description: 'Deprecated: KAS can have multiple key pairs' @@ -502,16 +545,6 @@ components: title: KeyAccessServer additionalProperties: false description: Key Access Server Registry - policy.KeyMode: - type: string - title: KeyMode - enum: - - KEY_MODE_UNSPECIFIED - - KEY_MODE_CONFIG_ROOT_KEY - - KEY_MODE_PROVIDER_ROOT_KEY - - KEY_MODE_REMOTE - - KEY_MODE_PUBLIC_KEY_ONLY - description: Describes the management and operational mode of a cryptographic key. policy.KeyProviderConfig: type: object properties: @@ -534,14 +567,6 @@ components: $ref: '#/components/schemas/common.Metadata' title: KeyProviderConfig additionalProperties: false - policy.KeyStatus: - type: string - title: KeyStatus - enum: - - KEY_STATUS_UNSPECIFIED - - KEY_STATUS_ACTIVE - - KEY_STATUS_ROTATED - description: The status of the key policy.Namespace: type: object properties: @@ -684,8 +709,7 @@ components: policy.PublicKey: type: object oneOf: - - type: object - properties: + - properties: cached: title: cached description: public key with additional information. Current preferred version @@ -693,14 +717,17 @@ components: title: cached required: - cached - - type: object - properties: + - properties: remote: type: string title: remote - description: | + description: |+ kas public key url - optional since can also be retrieved via public key - uri_format // URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes. + URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes.: + ``` + this.matches('^https://[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?)*(/.*)?$') + ``` + title: remote required: - remote @@ -885,17 +912,6 @@ components: title: pem title: SimpleKasPublicKey additionalProperties: false - policy.SourceType: - type: string - title: SourceType - enum: - - SOURCE_TYPE_UNSPECIFIED - - SOURCE_TYPE_INTERNAL - - SOURCE_TYPE_EXTERNAL - description: |- - Describes whether this kas is managed by the organization or if they imported - the kas information from an external party. These two modes are necessary in order - to encrypt a tdf dek with an external parties kas public key. policy.SubjectConditionSet: type: object properties: @@ -961,14 +977,6 @@ components: description: |- Subject Mapping: A Policy assigning Subject Set(s) to a permitted attribute value + action(s) combination - policy.SubjectMappingOperatorEnum: - type: string - title: SubjectMappingOperatorEnum - enum: - - SUBJECT_MAPPING_OPERATOR_ENUM_UNSPECIFIED - - SUBJECT_MAPPING_OPERATOR_ENUM_IN - - SUBJECT_MAPPING_OPERATOR_ENUM_NOT_IN - - SUBJECT_MAPPING_OPERATOR_ENUM_IN_CONTAINS policy.SubjectProperty: type: object properties: @@ -989,6 +997,7 @@ components: authoritative source such as an IDP (Identity Provider) or User Store. Examples include such ADFS/LDAP, OKTA, etc. For now, a valid property must contain both a selector expression & a resulting value. + The external_selector_value is a specifier to select a value from a flattened external representation of an Entity (such as from idP/LDAP), and the external_value is the value selected by the external_selector_value on that diff --git a/docs/openapi/policy/obligations/obligations.openapi.yaml b/docs/openapi/policy/obligations/obligations.openapi.yaml index 946d6ce813..850d19ae0c 100644 --- a/docs/openapi/policy/obligations/obligations.openapi.yaml +++ b/docs/openapi/policy/obligations/obligations.openapi.yaml @@ -2,12 +2,12 @@ openapi: 3.1.0 info: title: policy.obligations paths: - /policy.obligations.Service/AddObligationTrigger: + /policy.obligations.Service/ListObligations: post: tags: - policy.obligations.Service - summary: AddObligationTrigger - operationId: policy.obligations.Service.AddObligationTrigger + summary: ListObligations + operationId: policy.obligations.Service.ListObligations parameters: - name: Connect-Protocol-Version in: header @@ -22,7 +22,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.obligations.AddObligationTriggerRequest' + $ref: '#/components/schemas/policy.obligations.ListObligationsRequest' required: true responses: default: @@ -36,13 +36,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.obligations.AddObligationTriggerResponse' - /policy.obligations.Service/CreateObligation: + $ref: '#/components/schemas/policy.obligations.ListObligationsResponse' + /policy.obligations.Service/GetObligation: post: tags: - policy.obligations.Service - summary: CreateObligation - operationId: policy.obligations.Service.CreateObligation + summary: GetObligation + operationId: policy.obligations.Service.GetObligation parameters: - name: Connect-Protocol-Version in: header @@ -57,7 +57,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.obligations.CreateObligationRequest' + $ref: '#/components/schemas/policy.obligations.GetObligationRequest' required: true responses: default: @@ -71,13 +71,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.obligations.CreateObligationResponse' - /policy.obligations.Service/CreateObligationValue: + $ref: '#/components/schemas/policy.obligations.GetObligationResponse' + /policy.obligations.Service/GetObligationsByFQNs: post: tags: - policy.obligations.Service - summary: CreateObligationValue - operationId: policy.obligations.Service.CreateObligationValue + summary: GetObligationsByFQNs + operationId: policy.obligations.Service.GetObligationsByFQNs parameters: - name: Connect-Protocol-Version in: header @@ -92,7 +92,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.obligations.CreateObligationValueRequest' + $ref: '#/components/schemas/policy.obligations.GetObligationsByFQNsRequest' required: true responses: default: @@ -106,13 +106,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.obligations.CreateObligationValueResponse' - /policy.obligations.Service/DeleteObligation: + $ref: '#/components/schemas/policy.obligations.GetObligationsByFQNsResponse' + /policy.obligations.Service/CreateObligation: post: tags: - policy.obligations.Service - summary: DeleteObligation - operationId: policy.obligations.Service.DeleteObligation + summary: CreateObligation + operationId: policy.obligations.Service.CreateObligation parameters: - name: Connect-Protocol-Version in: header @@ -127,7 +127,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.obligations.DeleteObligationRequest' + $ref: '#/components/schemas/policy.obligations.CreateObligationRequest' required: true responses: default: @@ -141,13 +141,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.obligations.DeleteObligationResponse' - /policy.obligations.Service/DeleteObligationValue: + $ref: '#/components/schemas/policy.obligations.CreateObligationResponse' + /policy.obligations.Service/UpdateObligation: post: tags: - policy.obligations.Service - summary: DeleteObligationValue - operationId: policy.obligations.Service.DeleteObligationValue + summary: UpdateObligation + operationId: policy.obligations.Service.UpdateObligation parameters: - name: Connect-Protocol-Version in: header @@ -162,7 +162,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.obligations.DeleteObligationValueRequest' + $ref: '#/components/schemas/policy.obligations.UpdateObligationRequest' required: true responses: default: @@ -176,13 +176,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.obligations.DeleteObligationValueResponse' - /policy.obligations.Service/GetObligation: + $ref: '#/components/schemas/policy.obligations.UpdateObligationResponse' + /policy.obligations.Service/DeleteObligation: post: tags: - policy.obligations.Service - summary: GetObligation - operationId: policy.obligations.Service.GetObligation + summary: DeleteObligation + operationId: policy.obligations.Service.DeleteObligation parameters: - name: Connect-Protocol-Version in: header @@ -197,7 +197,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.obligations.GetObligationRequest' + $ref: '#/components/schemas/policy.obligations.DeleteObligationRequest' required: true responses: default: @@ -211,13 +211,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.obligations.GetObligationResponse' - /policy.obligations.Service/GetObligationTrigger: + $ref: '#/components/schemas/policy.obligations.DeleteObligationResponse' + /policy.obligations.Service/GetObligationValue: post: tags: - policy.obligations.Service - summary: GetObligationTrigger - operationId: policy.obligations.Service.GetObligationTrigger + summary: GetObligationValue + operationId: policy.obligations.Service.GetObligationValue parameters: - name: Connect-Protocol-Version in: header @@ -232,7 +232,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.obligations.GetObligationTriggerRequest' + $ref: '#/components/schemas/policy.obligations.GetObligationValueRequest' required: true responses: default: @@ -246,13 +246,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.obligations.GetObligationTriggerResponse' - /policy.obligations.Service/GetObligationValue: + $ref: '#/components/schemas/policy.obligations.GetObligationValueResponse' + /policy.obligations.Service/GetObligationValuesByFQNs: post: tags: - policy.obligations.Service - summary: GetObligationValue - operationId: policy.obligations.Service.GetObligationValue + summary: GetObligationValuesByFQNs + operationId: policy.obligations.Service.GetObligationValuesByFQNs parameters: - name: Connect-Protocol-Version in: header @@ -267,7 +267,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.obligations.GetObligationValueRequest' + $ref: '#/components/schemas/policy.obligations.GetObligationValuesByFQNsRequest' required: true responses: default: @@ -281,13 +281,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.obligations.GetObligationValueResponse' - /policy.obligations.Service/GetObligationValuesByFQNs: + $ref: '#/components/schemas/policy.obligations.GetObligationValuesByFQNsResponse' + /policy.obligations.Service/CreateObligationValue: post: tags: - policy.obligations.Service - summary: GetObligationValuesByFQNs - operationId: policy.obligations.Service.GetObligationValuesByFQNs + summary: CreateObligationValue + operationId: policy.obligations.Service.CreateObligationValue parameters: - name: Connect-Protocol-Version in: header @@ -302,7 +302,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.obligations.GetObligationValuesByFQNsRequest' + $ref: '#/components/schemas/policy.obligations.CreateObligationValueRequest' required: true responses: default: @@ -316,13 +316,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.obligations.GetObligationValuesByFQNsResponse' - /policy.obligations.Service/GetObligationsByFQNs: + $ref: '#/components/schemas/policy.obligations.CreateObligationValueResponse' + /policy.obligations.Service/UpdateObligationValue: post: tags: - policy.obligations.Service - summary: GetObligationsByFQNs - operationId: policy.obligations.Service.GetObligationsByFQNs + summary: UpdateObligationValue + operationId: policy.obligations.Service.UpdateObligationValue parameters: - name: Connect-Protocol-Version in: header @@ -337,7 +337,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.obligations.GetObligationsByFQNsRequest' + $ref: '#/components/schemas/policy.obligations.UpdateObligationValueRequest' required: true responses: default: @@ -351,13 +351,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.obligations.GetObligationsByFQNsResponse' - /policy.obligations.Service/ListObligationTriggers: + $ref: '#/components/schemas/policy.obligations.UpdateObligationValueResponse' + /policy.obligations.Service/DeleteObligationValue: post: tags: - policy.obligations.Service - summary: ListObligationTriggers - operationId: policy.obligations.Service.ListObligationTriggers + summary: DeleteObligationValue + operationId: policy.obligations.Service.DeleteObligationValue parameters: - name: Connect-Protocol-Version in: header @@ -372,7 +372,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.obligations.ListObligationTriggersRequest' + $ref: '#/components/schemas/policy.obligations.DeleteObligationValueRequest' required: true responses: default: @@ -386,13 +386,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.obligations.ListObligationTriggersResponse' - /policy.obligations.Service/ListObligations: + $ref: '#/components/schemas/policy.obligations.DeleteObligationValueResponse' + /policy.obligations.Service/GetObligationTrigger: post: tags: - policy.obligations.Service - summary: ListObligations - operationId: policy.obligations.Service.ListObligations + summary: GetObligationTrigger + operationId: policy.obligations.Service.GetObligationTrigger parameters: - name: Connect-Protocol-Version in: header @@ -407,7 +407,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.obligations.ListObligationsRequest' + $ref: '#/components/schemas/policy.obligations.GetObligationTriggerRequest' required: true responses: default: @@ -421,13 +421,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.obligations.ListObligationsResponse' - /policy.obligations.Service/RemoveObligationTrigger: + $ref: '#/components/schemas/policy.obligations.GetObligationTriggerResponse' + /policy.obligations.Service/AddObligationTrigger: post: tags: - policy.obligations.Service - summary: RemoveObligationTrigger - operationId: policy.obligations.Service.RemoveObligationTrigger + summary: AddObligationTrigger + operationId: policy.obligations.Service.AddObligationTrigger parameters: - name: Connect-Protocol-Version in: header @@ -442,7 +442,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.obligations.RemoveObligationTriggerRequest' + $ref: '#/components/schemas/policy.obligations.AddObligationTriggerRequest' required: true responses: default: @@ -456,13 +456,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.obligations.RemoveObligationTriggerResponse' - /policy.obligations.Service/UpdateObligation: + $ref: '#/components/schemas/policy.obligations.AddObligationTriggerResponse' + /policy.obligations.Service/RemoveObligationTrigger: post: tags: - policy.obligations.Service - summary: UpdateObligation - operationId: policy.obligations.Service.UpdateObligation + summary: RemoveObligationTrigger + operationId: policy.obligations.Service.RemoveObligationTrigger parameters: - name: Connect-Protocol-Version in: header @@ -477,7 +477,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.obligations.UpdateObligationRequest' + $ref: '#/components/schemas/policy.obligations.RemoveObligationTriggerRequest' required: true responses: default: @@ -491,13 +491,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.obligations.UpdateObligationResponse' - /policy.obligations.Service/UpdateObligationValue: + $ref: '#/components/schemas/policy.obligations.RemoveObligationTriggerResponse' + /policy.obligations.Service/ListObligationTriggers: post: tags: - policy.obligations.Service - summary: UpdateObligationValue - operationId: policy.obligations.Service.UpdateObligationValue + summary: ListObligationTriggers + operationId: policy.obligations.Service.ListObligationTriggers parameters: - name: Connect-Protocol-Version in: header @@ -512,7 +512,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.obligations.UpdateObligationValueRequest' + $ref: '#/components/schemas/policy.obligations.ListObligationTriggersRequest' required: true responses: default: @@ -526,17 +526,111 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.obligations.UpdateObligationValueResponse' + $ref: '#/components/schemas/policy.obligations.ListObligationTriggersResponse' components: schemas: + common.MetadataUpdateEnum: + type: string + title: MetadataUpdateEnum + enum: + - METADATA_UPDATE_ENUM_UNSPECIFIED + - METADATA_UPDATE_ENUM_EXTEND + - METADATA_UPDATE_ENUM_REPLACE + policy.Action.StandardAction: + type: string + title: StandardAction + enum: + - STANDARD_ACTION_UNSPECIFIED + - STANDARD_ACTION_DECRYPT + - STANDARD_ACTION_TRANSMIT + policy.Algorithm: + type: string + title: Algorithm + enum: + - ALGORITHM_UNSPECIFIED + - 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 + description: Supported key algorithms. + policy.AttributeRuleTypeEnum: + type: string + title: AttributeRuleTypeEnum + enum: + - ATTRIBUTE_RULE_TYPE_ENUM_UNSPECIFIED + - ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF + - ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF + - ATTRIBUTE_RULE_TYPE_ENUM_HIERARCHY + policy.ConditionBooleanTypeEnum: + type: string + title: ConditionBooleanTypeEnum + enum: + - CONDITION_BOOLEAN_TYPE_ENUM_UNSPECIFIED + - CONDITION_BOOLEAN_TYPE_ENUM_AND + - CONDITION_BOOLEAN_TYPE_ENUM_OR + policy.KasPublicKeyAlgEnum: + type: string + title: KasPublicKeyAlgEnum + enum: + - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED + - KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048 + - KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 + policy.SortDirection: + type: string + title: SortDirection + enum: + - SORT_DIRECTION_UNSPECIFIED + - SORT_DIRECTION_ASC + - SORT_DIRECTION_DESC + description: |- + Sorting direction shared across list APIs. + When the 'sort' field is omitted or the chosen sort 'field' is UNSPECIFIED, + the endpoint's request message defines the default ordering; see the + specific List* request docs. + policy.SourceType: + type: string + title: SourceType + enum: + - SOURCE_TYPE_UNSPECIFIED + - SOURCE_TYPE_INTERNAL + - SOURCE_TYPE_EXTERNAL + description: |- + Describes whether this kas is managed by the organization or if they imported + the kas information from an external party. These two modes are necessary in order + to encrypt a tdf dek with an external parties kas public key. + policy.SubjectMappingOperatorEnum: + type: string + title: SubjectMappingOperatorEnum + enum: + - SUBJECT_MAPPING_OPERATOR_ENUM_UNSPECIFIED + - SUBJECT_MAPPING_OPERATOR_ENUM_IN + - SUBJECT_MAPPING_OPERATOR_ENUM_NOT_IN + - SUBJECT_MAPPING_OPERATOR_ENUM_IN_CONTAINS + policy.obligations.SortObligationsType: + type: string + title: SortObligationsType + enum: + - SORT_OBLIGATIONS_TYPE_UNSPECIFIED + - SORT_OBLIGATIONS_TYPE_NAME + - SORT_OBLIGATIONS_TYPE_FQN + - SORT_OBLIGATIONS_TYPE_CREATED_AT + - SORT_OBLIGATIONS_TYPE_UPDATED_AT common.IdFqnIdentifier: type: object - allOf: - - oneOf: - - required: - - id - - required: - - fqn properties: id: type: string @@ -551,12 +645,6 @@ components: additionalProperties: false common.IdNameIdentifier: type: object - allOf: - - oneOf: - - required: - - id - - required: - - name properties: id: type: string @@ -567,8 +655,12 @@ components: title: name maxLength: 253 minLength: 1 - description: | - name_format // Name must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored name will be normalized to lower case. + description: |+ + Name must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored name will be normalized to lower case.: + ``` + this.matches('^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$') + ``` + title: IdNameIdentifier additionalProperties: false common.Metadata: @@ -626,82 +718,6 @@ components: title: value title: LabelsEntry additionalProperties: false - common.MetadataUpdateEnum: - type: string - title: MetadataUpdateEnum - enum: - - METADATA_UPDATE_ENUM_UNSPECIFIED - - METADATA_UPDATE_ENUM_EXTEND - - METADATA_UPDATE_ENUM_REPLACE - connect-protocol-version: - type: number - title: Connect-Protocol-Version - enum: - - 1 - description: Define the version of the Connect protocol - const: 1 - connect-timeout-header: - type: number - title: Connect-Timeout-Ms - description: Define the timeout, in ms - connect.error: - type: object - properties: - code: - type: string - examples: - - not_found - enum: - - canceled - - unknown - - invalid_argument - - deadline_exceeded - - not_found - - already_exists - - permission_denied - - resource_exhausted - - failed_precondition - - aborted - - out_of_range - - unimplemented - - internal - - unavailable - - data_loss - - unauthenticated - description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. - message: - type: string - description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. - details: - type: array - items: - $ref: '#/components/schemas/connect.error_details.Any' - description: A list of messages that carry the error details. There is no limit on the number of messages. - title: Connect Error - additionalProperties: true - description: 'Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation' - connect.error_details.Any: - type: object - properties: - type: - type: string - description: 'A URL that acts as a globally unique identifier for the type of the serialized message. For example: `type.googleapis.com/google.rpc.ErrorInfo`. This is used to determine the schema of the data in the `value` field and is the discriminator for the `debug` field.' - value: - type: string - format: binary - description: The Protobuf message, serialized as bytes and base64-encoded. The specific message type is identified by the `type` field. - debug: - oneOf: - - type: object - title: Any - additionalProperties: true - description: Detailed error information. - discriminator: - propertyName: type - title: Debug - description: Deserialized error detail payload. The 'type' field indicates the schema. This field is for easier debugging and should not be relied upon for application logic. - additionalProperties: true - description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message, with an additional debug field for ConnectRPC error details. google.protobuf.BoolValue: type: boolean description: |- @@ -714,8 +730,8 @@ components: google.protobuf.Timestamp: type: string examples: - - "2023-01-15T01:30:15.01Z" - - "2024-12-25T12:00:00Z" + - 1s + - 1.000340012s format: date-time description: |- A Timestamp represents a point in time independent of any time zone or local @@ -809,65 +825,41 @@ components: ) to obtain a formatter capable of generating timestamps in this format. policy.Action: type: object - allOf: + oneOf: - properties: - id: - type: string - title: id - description: Generated uuid in database - name: + custom: type: string - title: name - namespace: - title: namespace - description: Namespace context for this action - $ref: '#/components/schemas/policy.Namespace' - metadata: - title: metadata - $ref: '#/components/schemas/common.Metadata' - - oneOf: - - type: object - properties: - custom: - type: string - title: custom - description: Deprecated title: custom - required: - - custom - - type: object - properties: - standard: - title: standard - description: Deprecated - $ref: '#/components/schemas/policy.Action.StandardAction' + description: Deprecated + title: custom + required: + - custom + - properties: + standard: title: standard - required: - - standard + description: Deprecated + $ref: '#/components/schemas/policy.Action.StandardAction' + title: standard + required: + - standard + properties: + id: + type: string + title: id + description: Generated uuid in database + name: + type: string + title: name + namespace: + title: namespace + description: Namespace context for this action + $ref: '#/components/schemas/policy.Namespace' + metadata: + title: metadata + $ref: '#/components/schemas/common.Metadata' title: Action additionalProperties: false description: An action an entity can take - policy.Action.StandardAction: - type: string - title: StandardAction - enum: - - STANDARD_ACTION_UNSPECIFIED - - STANDARD_ACTION_DECRYPT - - STANDARD_ACTION_TRANSMIT - policy.Algorithm: - type: string - title: Algorithm - enum: - - ALGORITHM_UNSPECIFIED - - 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 - description: Supported key algorithms. policy.Attribute: type: object properties: @@ -924,14 +916,6 @@ components: required: - rule additionalProperties: false - policy.AttributeRuleTypeEnum: - type: string - title: AttributeRuleTypeEnum - enum: - - ATTRIBUTE_RULE_TYPE_ENUM_UNSPECIFIED - - ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF - - ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF - - ATTRIBUTE_RULE_TYPE_ENUM_HIERARCHY policy.Condition: type: object properties: @@ -949,6 +933,7 @@ components: type: array items: type: string + minItems: 1 title: subject_external_values minItems: 1 description: |- @@ -964,13 +949,6 @@ components: * A Condition defines a rule of - policy.ConditionBooleanTypeEnum: - type: string - title: ConditionBooleanTypeEnum - enum: - - CONDITION_BOOLEAN_TYPE_ENUM_UNSPECIFIED - - CONDITION_BOOLEAN_TYPE_ENUM_AND - - CONDITION_BOOLEAN_TYPE_ENUM_OR policy.ConditionGroup: type: object properties: @@ -1007,7 +985,7 @@ components: alg: not: enum: - - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED + - 0 title: alg description: |- A known algorithm type with any additional parameters encoded. @@ -1019,19 +997,6 @@ components: description: |- Deprecated A KAS public key and some associated metadata for further identifcation - policy.KasPublicKeyAlgEnum: - type: string - title: KasPublicKeyAlgEnum - enum: - - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED - - KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048 - - KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 policy.KasPublicKeySet: type: object properties: @@ -1054,9 +1019,13 @@ components: uri: type: string title: uri - description: | + description: |+ Address of a KAS instance - uri_format // URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes. + URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes.: + ``` + this.matches('^https?://[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?)*(:[0-9]+)?(/.*)?$') + ``` + publicKey: title: public_key description: 'Deprecated: KAS can have multiple key pairs' @@ -1254,8 +1223,7 @@ components: policy.PublicKey: type: object oneOf: - - type: object - properties: + - properties: cached: title: cached description: public key with additional information. Current preferred version @@ -1263,14 +1231,17 @@ components: title: cached required: - cached - - type: object - properties: + - properties: remote: type: string title: remote - description: | + description: |+ kas public key url - optional since can also be retrieved via public key - uri_format // URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes. + URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes.: + ``` + this.matches('^https://[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?)*(/.*)?$') + ``` + title: remote required: - remote @@ -1378,29 +1349,6 @@ components: title: pem title: SimpleKasPublicKey additionalProperties: false - policy.SortDirection: - type: string - title: SortDirection - enum: - - SORT_DIRECTION_UNSPECIFIED - - SORT_DIRECTION_ASC - - SORT_DIRECTION_DESC - description: |- - Sorting direction shared across list APIs. - When the 'sort' field is omitted or the chosen sort 'field' is UNSPECIFIED, - the endpoint's request message defines the default ordering; see the - specific List* request docs. - policy.SourceType: - type: string - title: SourceType - enum: - - SOURCE_TYPE_UNSPECIFIED - - SOURCE_TYPE_INTERNAL - - SOURCE_TYPE_EXTERNAL - description: |- - Describes whether this kas is managed by the organization or if they imported - the kas information from an external party. These two modes are necessary in order - to encrypt a tdf dek with an external parties kas public key. policy.SubjectConditionSet: type: object properties: @@ -1466,14 +1414,6 @@ components: description: |- Subject Mapping: A Policy assigning Subject Set(s) to a permitted attribute value + action(s) combination - policy.SubjectMappingOperatorEnum: - type: string - title: SubjectMappingOperatorEnum - enum: - - SUBJECT_MAPPING_OPERATOR_ENUM_UNSPECIFIED - - SUBJECT_MAPPING_OPERATOR_ENUM_IN - - SUBJECT_MAPPING_OPERATOR_ENUM_NOT_IN - - SUBJECT_MAPPING_OPERATOR_ENUM_IN_CONTAINS policy.SubjectSet: type: object properties: @@ -1587,12 +1527,6 @@ components: additionalProperties: false policy.obligations.CreateObligationRequest: type: object - allOf: - - oneOf: - - required: - - namespaceId - - required: - - namespaceFqn properties: namespaceId: type: string @@ -1607,14 +1541,19 @@ components: type: string title: name maxLength: 253 - description: | - obligation_name_format // Obligation name must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored name will be normalized to lower case. + description: |+ + Obligation name must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored name will be normalized to lower case.: + ``` + this.matches('^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$') + ``` + values: type: array items: type: string maxLength: 253 pattern: ^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$ + uniqueItems: true title: values uniqueItems: true description: Optional @@ -1638,12 +1577,6 @@ components: additionalProperties: false policy.obligations.CreateObligationValueRequest: type: object - allOf: - - oneOf: - - required: - - obligationId - - required: - - obligationFqn properties: obligationId: type: string @@ -1658,8 +1591,12 @@ components: type: string title: value maxLength: 253 - description: | - obligation_value_format // Obligation value must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored value will be normalized to lower case. + description: |+ + Obligation value must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored value will be normalized to lower case.: + ``` + this.matches('^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$') + ``` + triggers: type: array items: @@ -1688,12 +1625,6 @@ components: additionalProperties: false policy.obligations.DeleteObligationRequest: type: object - allOf: - - oneOf: - - required: - - id - - required: - - fqn properties: id: type: string @@ -1716,12 +1647,6 @@ components: additionalProperties: false policy.obligations.DeleteObligationValueRequest: type: object - allOf: - - oneOf: - - required: - - id - - required: - - fqn properties: id: type: string @@ -1744,12 +1669,6 @@ components: additionalProperties: false policy.obligations.GetObligationRequest: type: object - allOf: - - oneOf: - - required: - - id - - required: - - fqn properties: id: type: string @@ -1791,12 +1710,6 @@ components: additionalProperties: false policy.obligations.GetObligationValueRequest: type: object - allOf: - - oneOf: - - required: - - id - - required: - - fqn properties: id: type: string @@ -1827,6 +1740,9 @@ components: type: string minLength: 1 format: uri + maxItems: 250 + minItems: 1 + uniqueItems: true title: fqns maxItems: 250 minItems: 1 @@ -1864,6 +1780,9 @@ components: type: string minLength: 1 format: uri + maxItems: 250 + minItems: 1 + uniqueItems: true title: fqns maxItems: 250 minItems: 1 @@ -1995,15 +1914,6 @@ components: $ref: '#/components/schemas/policy.ObligationTrigger' title: RemoveObligationTriggerResponse additionalProperties: false - policy.obligations.SortObligationsType: - type: string - title: SortObligationsType - enum: - - SORT_OBLIGATIONS_TYPE_UNSPECIFIED - - SORT_OBLIGATIONS_TYPE_NAME - - SORT_OBLIGATIONS_TYPE_FQN - - SORT_OBLIGATIONS_TYPE_CREATED_AT - - SORT_OBLIGATIONS_TYPE_UPDATED_AT policy.obligations.UpdateObligationRequest: type: object properties: @@ -2016,9 +1926,13 @@ components: type: string title: name maxLength: 253 - description: | + description: |+ Optional - obligation_name_format // Obligation name must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored name will be normalized to lower case. + Obligation name must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored name will be normalized to lower case.: + ``` + size(this) > 0 ? this.matches('^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$') : true + ``` + metadata: title: metadata $ref: '#/components/schemas/common.MetadataMutable' @@ -2047,9 +1961,13 @@ components: type: string title: value maxLength: 253 - description: | + description: |+ Optional - obligation_value_format // Obligation value must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored value will be normalized to lower case. + Obligation value must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored value will be normalized to lower case.: + ``` + size(this) > 0 ? this.matches('^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$') : true + ``` + triggers: type: array items: @@ -2097,6 +2015,63 @@ components: - action - attributeValue additionalProperties: false + connect-protocol-version: + type: number + title: Connect-Protocol-Version + enum: + - 1 + description: Define the version of the Connect protocol + const: 1 + connect-timeout-header: + type: number + title: Connect-Timeout-Ms + description: Define the timeout, in ms + connect.error: + type: object + properties: + code: + type: string + examples: + - not_found + enum: + - canceled + - unknown + - invalid_argument + - deadline_exceeded + - not_found + - already_exists + - permission_denied + - resource_exhausted + - failed_precondition + - aborted + - out_of_range + - unimplemented + - internal + - unavailable + - data_loss + - unauthenticated + description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. + message: + type: string + description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. + detail: + $ref: '#/components/schemas/google.protobuf.Any' + title: Connect Error + additionalProperties: true + description: 'Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation' + google.protobuf.Any: + type: object + properties: + type: + type: string + value: + type: string + format: binary + debug: + type: object + additionalProperties: true + additionalProperties: true + description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message. security: [] tags: - name: policy.obligations.Service diff --git a/docs/openapi/policy/registeredresources/registered_resources.openapi.yaml b/docs/openapi/policy/registeredresources/registered_resources.openapi.yaml index c22280f626..1b7d02a2b3 100644 --- a/docs/openapi/policy/registeredresources/registered_resources.openapi.yaml +++ b/docs/openapi/policy/registeredresources/registered_resources.openapi.yaml @@ -37,12 +37,12 @@ paths: application/json: schema: $ref: '#/components/schemas/policy.registeredresources.CreateRegisteredResourceResponse' - /policy.registeredresources.RegisteredResourcesService/CreateRegisteredResourceValue: + /policy.registeredresources.RegisteredResourcesService/GetRegisteredResource: post: tags: - policy.registeredresources.RegisteredResourcesService - summary: CreateRegisteredResourceValue - operationId: policy.registeredresources.RegisteredResourcesService.CreateRegisteredResourceValue + summary: GetRegisteredResource + operationId: policy.registeredresources.RegisteredResourcesService.GetRegisteredResource parameters: - name: Connect-Protocol-Version in: header @@ -57,7 +57,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.registeredresources.CreateRegisteredResourceValueRequest' + $ref: '#/components/schemas/policy.registeredresources.GetRegisteredResourceRequest' required: true responses: default: @@ -71,13 +71,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.registeredresources.CreateRegisteredResourceValueResponse' - /policy.registeredresources.RegisteredResourcesService/DeleteRegisteredResource: + $ref: '#/components/schemas/policy.registeredresources.GetRegisteredResourceResponse' + /policy.registeredresources.RegisteredResourcesService/ListRegisteredResources: post: tags: - policy.registeredresources.RegisteredResourcesService - summary: DeleteRegisteredResource - operationId: policy.registeredresources.RegisteredResourcesService.DeleteRegisteredResource + summary: ListRegisteredResources + operationId: policy.registeredresources.RegisteredResourcesService.ListRegisteredResources parameters: - name: Connect-Protocol-Version in: header @@ -92,7 +92,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.registeredresources.DeleteRegisteredResourceRequest' + $ref: '#/components/schemas/policy.registeredresources.ListRegisteredResourcesRequest' required: true responses: default: @@ -106,13 +106,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.registeredresources.DeleteRegisteredResourceResponse' - /policy.registeredresources.RegisteredResourcesService/DeleteRegisteredResourceValue: + $ref: '#/components/schemas/policy.registeredresources.ListRegisteredResourcesResponse' + /policy.registeredresources.RegisteredResourcesService/UpdateRegisteredResource: post: tags: - policy.registeredresources.RegisteredResourcesService - summary: DeleteRegisteredResourceValue - operationId: policy.registeredresources.RegisteredResourcesService.DeleteRegisteredResourceValue + summary: UpdateRegisteredResource + operationId: policy.registeredresources.RegisteredResourcesService.UpdateRegisteredResource parameters: - name: Connect-Protocol-Version in: header @@ -127,7 +127,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.registeredresources.DeleteRegisteredResourceValueRequest' + $ref: '#/components/schemas/policy.registeredresources.UpdateRegisteredResourceRequest' required: true responses: default: @@ -141,13 +141,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.registeredresources.DeleteRegisteredResourceValueResponse' - /policy.registeredresources.RegisteredResourcesService/GetRegisteredResource: + $ref: '#/components/schemas/policy.registeredresources.UpdateRegisteredResourceResponse' + /policy.registeredresources.RegisteredResourcesService/DeleteRegisteredResource: post: tags: - policy.registeredresources.RegisteredResourcesService - summary: GetRegisteredResource - operationId: policy.registeredresources.RegisteredResourcesService.GetRegisteredResource + summary: DeleteRegisteredResource + operationId: policy.registeredresources.RegisteredResourcesService.DeleteRegisteredResource parameters: - name: Connect-Protocol-Version in: header @@ -162,7 +162,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.registeredresources.GetRegisteredResourceRequest' + $ref: '#/components/schemas/policy.registeredresources.DeleteRegisteredResourceRequest' required: true responses: default: @@ -176,13 +176,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.registeredresources.GetRegisteredResourceResponse' - /policy.registeredresources.RegisteredResourcesService/GetRegisteredResourceValue: + $ref: '#/components/schemas/policy.registeredresources.DeleteRegisteredResourceResponse' + /policy.registeredresources.RegisteredResourcesService/CreateRegisteredResourceValue: post: tags: - policy.registeredresources.RegisteredResourcesService - summary: GetRegisteredResourceValue - operationId: policy.registeredresources.RegisteredResourcesService.GetRegisteredResourceValue + summary: CreateRegisteredResourceValue + operationId: policy.registeredresources.RegisteredResourcesService.CreateRegisteredResourceValue parameters: - name: Connect-Protocol-Version in: header @@ -197,7 +197,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.registeredresources.GetRegisteredResourceValueRequest' + $ref: '#/components/schemas/policy.registeredresources.CreateRegisteredResourceValueRequest' required: true responses: default: @@ -211,13 +211,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.registeredresources.GetRegisteredResourceValueResponse' - /policy.registeredresources.RegisteredResourcesService/GetRegisteredResourceValuesByFQNs: + $ref: '#/components/schemas/policy.registeredresources.CreateRegisteredResourceValueResponse' + /policy.registeredresources.RegisteredResourcesService/GetRegisteredResourceValue: post: tags: - policy.registeredresources.RegisteredResourcesService - summary: GetRegisteredResourceValuesByFQNs - operationId: policy.registeredresources.RegisteredResourcesService.GetRegisteredResourceValuesByFQNs + summary: GetRegisteredResourceValue + operationId: policy.registeredresources.RegisteredResourcesService.GetRegisteredResourceValue parameters: - name: Connect-Protocol-Version in: header @@ -232,7 +232,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.registeredresources.GetRegisteredResourceValuesByFQNsRequest' + $ref: '#/components/schemas/policy.registeredresources.GetRegisteredResourceValueRequest' required: true responses: default: @@ -246,13 +246,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.registeredresources.GetRegisteredResourceValuesByFQNsResponse' - /policy.registeredresources.RegisteredResourcesService/ListRegisteredResourceValues: + $ref: '#/components/schemas/policy.registeredresources.GetRegisteredResourceValueResponse' + /policy.registeredresources.RegisteredResourcesService/GetRegisteredResourceValuesByFQNs: post: tags: - policy.registeredresources.RegisteredResourcesService - summary: ListRegisteredResourceValues - operationId: policy.registeredresources.RegisteredResourcesService.ListRegisteredResourceValues + summary: GetRegisteredResourceValuesByFQNs + operationId: policy.registeredresources.RegisteredResourcesService.GetRegisteredResourceValuesByFQNs parameters: - name: Connect-Protocol-Version in: header @@ -267,7 +267,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.registeredresources.ListRegisteredResourceValuesRequest' + $ref: '#/components/schemas/policy.registeredresources.GetRegisteredResourceValuesByFQNsRequest' required: true responses: default: @@ -281,13 +281,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.registeredresources.ListRegisteredResourceValuesResponse' - /policy.registeredresources.RegisteredResourcesService/ListRegisteredResources: + $ref: '#/components/schemas/policy.registeredresources.GetRegisteredResourceValuesByFQNsResponse' + /policy.registeredresources.RegisteredResourcesService/ListRegisteredResourceValues: post: tags: - policy.registeredresources.RegisteredResourcesService - summary: ListRegisteredResources - operationId: policy.registeredresources.RegisteredResourcesService.ListRegisteredResources + summary: ListRegisteredResourceValues + operationId: policy.registeredresources.RegisteredResourcesService.ListRegisteredResourceValues parameters: - name: Connect-Protocol-Version in: header @@ -302,7 +302,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.registeredresources.ListRegisteredResourcesRequest' + $ref: '#/components/schemas/policy.registeredresources.ListRegisteredResourceValuesRequest' required: true responses: default: @@ -316,13 +316,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.registeredresources.ListRegisteredResourcesResponse' - /policy.registeredresources.RegisteredResourcesService/UpdateRegisteredResource: + $ref: '#/components/schemas/policy.registeredresources.ListRegisteredResourceValuesResponse' + /policy.registeredresources.RegisteredResourcesService/UpdateRegisteredResourceValue: post: tags: - policy.registeredresources.RegisteredResourcesService - summary: UpdateRegisteredResource - operationId: policy.registeredresources.RegisteredResourcesService.UpdateRegisteredResource + summary: UpdateRegisteredResourceValue + operationId: policy.registeredresources.RegisteredResourcesService.UpdateRegisteredResourceValue parameters: - name: Connect-Protocol-Version in: header @@ -337,7 +337,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.registeredresources.UpdateRegisteredResourceRequest' + $ref: '#/components/schemas/policy.registeredresources.UpdateRegisteredResourceValueRequest' required: true responses: default: @@ -351,13 +351,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.registeredresources.UpdateRegisteredResourceResponse' - /policy.registeredresources.RegisteredResourcesService/UpdateRegisteredResourceValue: + $ref: '#/components/schemas/policy.registeredresources.UpdateRegisteredResourceValueResponse' + /policy.registeredresources.RegisteredResourcesService/DeleteRegisteredResourceValue: post: tags: - policy.registeredresources.RegisteredResourcesService - summary: UpdateRegisteredResourceValue - operationId: policy.registeredresources.RegisteredResourcesService.UpdateRegisteredResourceValue + summary: DeleteRegisteredResourceValue + operationId: policy.registeredresources.RegisteredResourcesService.DeleteRegisteredResourceValue parameters: - name: Connect-Protocol-Version in: header @@ -372,7 +372,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.registeredresources.UpdateRegisteredResourceValueRequest' + $ref: '#/components/schemas/policy.registeredresources.DeleteRegisteredResourceValueRequest' required: true responses: default: @@ -386,9 +386,108 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.registeredresources.UpdateRegisteredResourceValueResponse' + $ref: '#/components/schemas/policy.registeredresources.DeleteRegisteredResourceValueResponse' components: schemas: + common.MetadataUpdateEnum: + type: string + title: MetadataUpdateEnum + enum: + - METADATA_UPDATE_ENUM_UNSPECIFIED + - METADATA_UPDATE_ENUM_EXTEND + - METADATA_UPDATE_ENUM_REPLACE + policy.Action.StandardAction: + type: string + title: StandardAction + enum: + - STANDARD_ACTION_UNSPECIFIED + - STANDARD_ACTION_DECRYPT + - STANDARD_ACTION_TRANSMIT + policy.Algorithm: + type: string + title: Algorithm + enum: + - ALGORITHM_UNSPECIFIED + - 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 + description: Supported key algorithms. + policy.AttributeRuleTypeEnum: + type: string + title: AttributeRuleTypeEnum + enum: + - ATTRIBUTE_RULE_TYPE_ENUM_UNSPECIFIED + - ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF + - ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF + - ATTRIBUTE_RULE_TYPE_ENUM_HIERARCHY + policy.ConditionBooleanTypeEnum: + type: string + title: ConditionBooleanTypeEnum + enum: + - CONDITION_BOOLEAN_TYPE_ENUM_UNSPECIFIED + - CONDITION_BOOLEAN_TYPE_ENUM_AND + - CONDITION_BOOLEAN_TYPE_ENUM_OR + policy.KasPublicKeyAlgEnum: + type: string + title: KasPublicKeyAlgEnum + enum: + - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED + - KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048 + - KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 + policy.SortDirection: + type: string + title: SortDirection + enum: + - SORT_DIRECTION_UNSPECIFIED + - SORT_DIRECTION_ASC + - SORT_DIRECTION_DESC + description: |- + Sorting direction shared across list APIs. + When the 'sort' field is omitted or the chosen sort 'field' is UNSPECIFIED, + the endpoint's request message defines the default ordering; see the + specific List* request docs. + policy.SourceType: + type: string + title: SourceType + enum: + - SOURCE_TYPE_UNSPECIFIED + - SOURCE_TYPE_INTERNAL + - SOURCE_TYPE_EXTERNAL + description: |- + Describes whether this kas is managed by the organization or if they imported + the kas information from an external party. These two modes are necessary in order + to encrypt a tdf dek with an external parties kas public key. + policy.SubjectMappingOperatorEnum: + type: string + title: SubjectMappingOperatorEnum + enum: + - SUBJECT_MAPPING_OPERATOR_ENUM_UNSPECIFIED + - SUBJECT_MAPPING_OPERATOR_ENUM_IN + - SUBJECT_MAPPING_OPERATOR_ENUM_NOT_IN + - SUBJECT_MAPPING_OPERATOR_ENUM_IN_CONTAINS + policy.registeredresources.SortRegisteredResourcesType: + type: string + title: SortRegisteredResourcesType + enum: + - SORT_REGISTERED_RESOURCES_TYPE_UNSPECIFIED + - SORT_REGISTERED_RESOURCES_TYPE_NAME + - SORT_REGISTERED_RESOURCES_TYPE_CREATED_AT + - SORT_REGISTERED_RESOURCES_TYPE_UPDATED_AT common.Metadata: type: object properties: @@ -444,82 +543,6 @@ components: title: value title: LabelsEntry additionalProperties: false - common.MetadataUpdateEnum: - type: string - title: MetadataUpdateEnum - enum: - - METADATA_UPDATE_ENUM_UNSPECIFIED - - METADATA_UPDATE_ENUM_EXTEND - - METADATA_UPDATE_ENUM_REPLACE - connect-protocol-version: - type: number - title: Connect-Protocol-Version - enum: - - 1 - description: Define the version of the Connect protocol - const: 1 - connect-timeout-header: - type: number - title: Connect-Timeout-Ms - description: Define the timeout, in ms - connect.error: - type: object - properties: - code: - type: string - examples: - - not_found - enum: - - canceled - - unknown - - invalid_argument - - deadline_exceeded - - not_found - - already_exists - - permission_denied - - resource_exhausted - - failed_precondition - - aborted - - out_of_range - - unimplemented - - internal - - unavailable - - data_loss - - unauthenticated - description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. - message: - type: string - description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. - details: - type: array - items: - $ref: '#/components/schemas/connect.error_details.Any' - description: A list of messages that carry the error details. There is no limit on the number of messages. - title: Connect Error - additionalProperties: true - description: 'Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation' - connect.error_details.Any: - type: object - properties: - type: - type: string - description: 'A URL that acts as a globally unique identifier for the type of the serialized message. For example: `type.googleapis.com/google.rpc.ErrorInfo`. This is used to determine the schema of the data in the `value` field and is the discriminator for the `debug` field.' - value: - type: string - format: binary - description: The Protobuf message, serialized as bytes and base64-encoded. The specific message type is identified by the `type` field. - debug: - oneOf: - - type: object - title: Any - additionalProperties: true - description: Detailed error information. - discriminator: - propertyName: type - title: Debug - description: Deserialized error detail payload. The 'type' field indicates the schema. This field is for easier debugging and should not be relied upon for application logic. - additionalProperties: true - description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message, with an additional debug field for ConnectRPC error details. google.protobuf.BoolValue: type: boolean description: |- @@ -532,8 +555,8 @@ components: google.protobuf.Timestamp: type: string examples: - - "2023-01-15T01:30:15.01Z" - - "2024-12-25T12:00:00Z" + - 1s + - 1.000340012s format: date-time description: |- A Timestamp represents a point in time independent of any time zone or local @@ -627,65 +650,41 @@ components: ) to obtain a formatter capable of generating timestamps in this format. policy.Action: type: object - allOf: + oneOf: - properties: - id: - type: string - title: id - description: Generated uuid in database - name: + custom: type: string - title: name - namespace: - title: namespace - description: Namespace context for this action - $ref: '#/components/schemas/policy.Namespace' - metadata: - title: metadata - $ref: '#/components/schemas/common.Metadata' - - oneOf: - - type: object - properties: - custom: - type: string - title: custom - description: Deprecated title: custom - required: - - custom - - type: object - properties: - standard: - title: standard - description: Deprecated - $ref: '#/components/schemas/policy.Action.StandardAction' + description: Deprecated + title: custom + required: + - custom + - properties: + standard: title: standard - required: - - standard + description: Deprecated + $ref: '#/components/schemas/policy.Action.StandardAction' + title: standard + required: + - standard + properties: + id: + type: string + title: id + description: Generated uuid in database + name: + type: string + title: name + namespace: + title: namespace + description: Namespace context for this action + $ref: '#/components/schemas/policy.Namespace' + metadata: + title: metadata + $ref: '#/components/schemas/common.Metadata' title: Action additionalProperties: false description: An action an entity can take - policy.Action.StandardAction: - type: string - title: StandardAction - enum: - - STANDARD_ACTION_UNSPECIFIED - - STANDARD_ACTION_DECRYPT - - STANDARD_ACTION_TRANSMIT - policy.Algorithm: - type: string - title: Algorithm - enum: - - ALGORITHM_UNSPECIFIED - - 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 - description: Supported key algorithms. policy.Attribute: type: object properties: @@ -742,14 +741,6 @@ components: required: - rule additionalProperties: false - policy.AttributeRuleTypeEnum: - type: string - title: AttributeRuleTypeEnum - enum: - - ATTRIBUTE_RULE_TYPE_ENUM_UNSPECIFIED - - ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF - - ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF - - ATTRIBUTE_RULE_TYPE_ENUM_HIERARCHY policy.Condition: type: object properties: @@ -767,6 +758,7 @@ components: type: array items: type: string + minItems: 1 title: subject_external_values minItems: 1 description: |- @@ -782,13 +774,6 @@ components: * A Condition defines a rule of - policy.ConditionBooleanTypeEnum: - type: string - title: ConditionBooleanTypeEnum - enum: - - CONDITION_BOOLEAN_TYPE_ENUM_UNSPECIFIED - - CONDITION_BOOLEAN_TYPE_ENUM_AND - - CONDITION_BOOLEAN_TYPE_ENUM_OR policy.ConditionGroup: type: object properties: @@ -825,7 +810,7 @@ components: alg: not: enum: - - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED + - 0 title: alg description: |- A known algorithm type with any additional parameters encoded. @@ -837,19 +822,6 @@ components: description: |- Deprecated A KAS public key and some associated metadata for further identifcation - policy.KasPublicKeyAlgEnum: - type: string - title: KasPublicKeyAlgEnum - enum: - - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED - - KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048 - - KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 policy.KasPublicKeySet: type: object properties: @@ -872,9 +844,13 @@ components: uri: type: string title: uri - description: | + description: |+ Address of a KAS instance - uri_format // URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes. + URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes.: + ``` + this.matches('^https?://[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?)*(:[0-9]+)?(/.*)?$') + ``` + publicKey: title: public_key description: 'Deprecated: KAS can have multiple key pairs' @@ -1072,8 +1048,7 @@ components: policy.PublicKey: type: object oneOf: - - type: object - properties: + - properties: cached: title: cached description: public key with additional information. Current preferred version @@ -1081,14 +1056,17 @@ components: title: cached required: - cached - - type: object - properties: + - properties: remote: type: string title: remote - description: | + description: |+ kas public key url - optional since can also be retrieved via public key - uri_format // URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes. + URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes.: + ``` + this.matches('^https://[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?)*(/.*)?$') + ``` + title: remote required: - remote @@ -1263,29 +1241,6 @@ components: title: pem title: SimpleKasPublicKey additionalProperties: false - policy.SortDirection: - type: string - title: SortDirection - enum: - - SORT_DIRECTION_UNSPECIFIED - - SORT_DIRECTION_ASC - - SORT_DIRECTION_DESC - description: |- - Sorting direction shared across list APIs. - When the 'sort' field is omitted or the chosen sort 'field' is UNSPECIFIED, - the endpoint's request message defines the default ordering; see the - specific List* request docs. - policy.SourceType: - type: string - title: SourceType - enum: - - SOURCE_TYPE_UNSPECIFIED - - SOURCE_TYPE_INTERNAL - - SOURCE_TYPE_EXTERNAL - description: |- - Describes whether this kas is managed by the organization or if they imported - the kas information from an external party. These two modes are necessary in order - to encrypt a tdf dek with an external parties kas public key. policy.SubjectConditionSet: type: object properties: @@ -1351,14 +1306,6 @@ components: description: |- Subject Mapping: A Policy assigning Subject Set(s) to a permitted attribute value + action(s) combination - policy.SubjectMappingOperatorEnum: - type: string - title: SubjectMappingOperatorEnum - enum: - - SUBJECT_MAPPING_OPERATOR_ENUM_UNSPECIFIED - - SUBJECT_MAPPING_OPERATOR_ENUM_IN - - SUBJECT_MAPPING_OPERATOR_ENUM_NOT_IN - - SUBJECT_MAPPING_OPERATOR_ENUM_IN_CONTAINS policy.SubjectSet: type: object properties: @@ -1429,8 +1376,7 @@ components: type: object allOf: - oneOf: - - type: object - properties: + - properties: actionId: type: string title: action_id @@ -1438,20 +1384,22 @@ components: title: action_id required: - actionId - - type: object - properties: + - properties: actionName: type: string title: action_name maxLength: 253 - description: | - action_name_format // Action name must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored action name will be normalized to lower case. + description: |+ + Action name must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored action name will be normalized to lower case.: + ``` + this.matches('^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$') + ``` + title: action_name required: - actionName - oneOf: - - type: object - properties: + - properties: attributeValueFqn: type: string title: attribute_value_fqn @@ -1460,8 +1408,7 @@ components: title: attribute_value_fqn required: - attributeValueFqn - - type: object - properties: + - properties: attributeValueId: type: string title: attribute_value_id @@ -1478,15 +1425,20 @@ components: type: string title: name maxLength: 253 - description: | + description: |+ Required - rr_name_format // Registered Resource Name must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored name will be normalized to lower case. + Registered Resource Name must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored name will be normalized to lower case.: + ``` + this.matches('^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$') + ``` + values: type: array items: type: string maxLength: 253 pattern: ^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$ + uniqueItems: true title: values uniqueItems: true description: |- @@ -1532,9 +1484,13 @@ components: type: string title: value maxLength: 253 - description: | + description: |+ Required - rr_value_format // Registered Resource Value must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored value will be normalized to lower case. + Registered Resource Value must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored value will be normalized to lower case.: + ``` + this.matches('^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$') + ``` + actionAttributeValues: type: array items: @@ -1600,38 +1556,39 @@ components: additionalProperties: false policy.registeredresources.GetRegisteredResourceRequest: type: object - allOf: + oneOf: - properties: - namespaceFqn: - type: string - title: namespace_fqn - minLength: 1 - format: uri - namespaceId: + id: type: string - title: namespace_id - format: uuid - - oneOf: - - type: object - properties: - id: - type: string - title: id - format: uuid title: id - required: - - id - - type: object - properties: - name: - type: string - title: name - maxLength: 253 - description: | - rr_name_format // Registered Resource Name must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored name will be normalized to lower case. + format: uuid + title: id + required: + - id + - properties: + name: + type: string title: name - required: - - name + maxLength: 253 + description: |+ + Registered Resource Name must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored name will be normalized to lower case.: + ``` + size(this) > 0 ? this.matches('^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$') : true + ``` + + title: name + required: + - name + properties: + namespaceFqn: + type: string + title: namespace_fqn + minLength: 1 + format: uri + namespaceId: + type: string + title: namespace_id + format: uuid title: GetRegisteredResourceRequest additionalProperties: false policy.registeredresources.GetRegisteredResourceResponse: @@ -1645,8 +1602,7 @@ components: policy.registeredresources.GetRegisteredResourceValueRequest: type: object oneOf: - - type: object - properties: + - properties: fqn: type: string title: fqn @@ -1655,8 +1611,7 @@ components: title: fqn required: - fqn - - type: object - properties: + - properties: id: type: string title: id @@ -1683,6 +1638,8 @@ components: type: string minLength: 1 format: uri + minItems: 1 + uniqueItems: true title: fqns minItems: 1 uniqueItems: true @@ -1717,9 +1674,13 @@ components: resourceId: type: string title: resource_id - description: | + description: |+ Optional - optional_uuid_format // Optional field must be a valid UUID + Optional field must be a valid UUID: + ``` + size(this) == 0 || this.matches('[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}') + ``` + pagination: title: pagination description: Optional @@ -1793,14 +1754,6 @@ components: $ref: '#/components/schemas/policy.SortDirection' title: RegisteredResourcesSort additionalProperties: false - policy.registeredresources.SortRegisteredResourcesType: - type: string - title: SortRegisteredResourcesType - enum: - - SORT_REGISTERED_RESOURCES_TYPE_UNSPECIFIED - - SORT_REGISTERED_RESOURCES_TYPE_NAME - - SORT_REGISTERED_RESOURCES_TYPE_CREATED_AT - - SORT_REGISTERED_RESOURCES_TYPE_UPDATED_AT policy.registeredresources.UpdateRegisteredResourceRequest: type: object properties: @@ -1813,9 +1766,13 @@ components: type: string title: name maxLength: 253 - description: | + description: |+ Optional - rr_name_format // Registered Resource Name must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored name will be normalized to lower case. + Registered Resource Name must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored name will be normalized to lower case.: + ``` + size(this) > 0 ? this.matches('^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$') : true + ``` + metadata: title: metadata description: |- @@ -1847,9 +1804,13 @@ components: type: string title: value maxLength: 253 - description: | + description: |+ Optional - rr_value_format // Registered Resource Value must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored value will be normalized to lower case. + Registered Resource Value must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored value will be normalized to lower case.: + ``` + size(this) > 0 ? this.matches('^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$') : true + ``` + actionAttributeValues: type: array items: @@ -1877,6 +1838,63 @@ components: $ref: '#/components/schemas/policy.RegisteredResourceValue' title: UpdateRegisteredResourceValueResponse additionalProperties: false + connect-protocol-version: + type: number + title: Connect-Protocol-Version + enum: + - 1 + description: Define the version of the Connect protocol + const: 1 + connect-timeout-header: + type: number + title: Connect-Timeout-Ms + description: Define the timeout, in ms + connect.error: + type: object + properties: + code: + type: string + examples: + - not_found + enum: + - canceled + - unknown + - invalid_argument + - deadline_exceeded + - not_found + - already_exists + - permission_denied + - resource_exhausted + - failed_precondition + - aborted + - out_of_range + - unimplemented + - internal + - unavailable + - data_loss + - unauthenticated + description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. + message: + type: string + description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. + detail: + $ref: '#/components/schemas/google.protobuf.Any' + title: Connect Error + additionalProperties: true + description: 'Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation' + google.protobuf.Any: + type: object + properties: + type: + type: string + value: + type: string + format: binary + debug: + type: object + additionalProperties: true + additionalProperties: true + description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message. security: [] tags: - name: policy.registeredresources.RegisteredResourcesService diff --git a/docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml b/docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml index 7928a38a98..1a19e70f06 100644 --- a/docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml +++ b/docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml @@ -2,12 +2,12 @@ openapi: 3.1.0 info: title: policy.resourcemapping paths: - /policy.resourcemapping.ResourceMappingService/CreateResourceMapping: + /policy.resourcemapping.ResourceMappingService/ListResourceMappingGroups: post: tags: - policy.resourcemapping.ResourceMappingService - summary: CreateResourceMapping - operationId: policy.resourcemapping.ResourceMappingService.CreateResourceMapping + summary: ListResourceMappingGroups + operationId: policy.resourcemapping.ResourceMappingService.ListResourceMappingGroups parameters: - name: Connect-Protocol-Version in: header @@ -22,7 +22,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.resourcemapping.CreateResourceMappingRequest' + $ref: '#/components/schemas/policy.resourcemapping.ListResourceMappingGroupsRequest' required: true responses: default: @@ -36,13 +36,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.resourcemapping.CreateResourceMappingResponse' - /policy.resourcemapping.ResourceMappingService/CreateResourceMappingGroup: + $ref: '#/components/schemas/policy.resourcemapping.ListResourceMappingGroupsResponse' + /policy.resourcemapping.ResourceMappingService/GetResourceMappingGroup: post: tags: - policy.resourcemapping.ResourceMappingService - summary: CreateResourceMappingGroup - operationId: policy.resourcemapping.ResourceMappingService.CreateResourceMappingGroup + summary: GetResourceMappingGroup + operationId: policy.resourcemapping.ResourceMappingService.GetResourceMappingGroup parameters: - name: Connect-Protocol-Version in: header @@ -57,7 +57,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.resourcemapping.CreateResourceMappingGroupRequest' + $ref: '#/components/schemas/policy.resourcemapping.GetResourceMappingGroupRequest' required: true responses: default: @@ -71,13 +71,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.resourcemapping.CreateResourceMappingGroupResponse' - /policy.resourcemapping.ResourceMappingService/DeleteResourceMapping: + $ref: '#/components/schemas/policy.resourcemapping.GetResourceMappingGroupResponse' + /policy.resourcemapping.ResourceMappingService/CreateResourceMappingGroup: post: tags: - policy.resourcemapping.ResourceMappingService - summary: DeleteResourceMapping - operationId: policy.resourcemapping.ResourceMappingService.DeleteResourceMapping + summary: CreateResourceMappingGroup + operationId: policy.resourcemapping.ResourceMappingService.CreateResourceMappingGroup parameters: - name: Connect-Protocol-Version in: header @@ -92,7 +92,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.resourcemapping.DeleteResourceMappingRequest' + $ref: '#/components/schemas/policy.resourcemapping.CreateResourceMappingGroupRequest' required: true responses: default: @@ -106,13 +106,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.resourcemapping.DeleteResourceMappingResponse' - /policy.resourcemapping.ResourceMappingService/DeleteResourceMappingGroup: + $ref: '#/components/schemas/policy.resourcemapping.CreateResourceMappingGroupResponse' + /policy.resourcemapping.ResourceMappingService/UpdateResourceMappingGroup: post: tags: - policy.resourcemapping.ResourceMappingService - summary: DeleteResourceMappingGroup - operationId: policy.resourcemapping.ResourceMappingService.DeleteResourceMappingGroup + summary: UpdateResourceMappingGroup + operationId: policy.resourcemapping.ResourceMappingService.UpdateResourceMappingGroup parameters: - name: Connect-Protocol-Version in: header @@ -127,7 +127,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.resourcemapping.DeleteResourceMappingGroupRequest' + $ref: '#/components/schemas/policy.resourcemapping.UpdateResourceMappingGroupRequest' required: true responses: default: @@ -141,13 +141,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.resourcemapping.DeleteResourceMappingGroupResponse' - /policy.resourcemapping.ResourceMappingService/GetResourceMapping: + $ref: '#/components/schemas/policy.resourcemapping.UpdateResourceMappingGroupResponse' + /policy.resourcemapping.ResourceMappingService/DeleteResourceMappingGroup: post: tags: - policy.resourcemapping.ResourceMappingService - summary: GetResourceMapping - operationId: policy.resourcemapping.ResourceMappingService.GetResourceMapping + summary: DeleteResourceMappingGroup + operationId: policy.resourcemapping.ResourceMappingService.DeleteResourceMappingGroup parameters: - name: Connect-Protocol-Version in: header @@ -162,7 +162,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.resourcemapping.GetResourceMappingRequest' + $ref: '#/components/schemas/policy.resourcemapping.DeleteResourceMappingGroupRequest' required: true responses: default: @@ -176,13 +176,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.resourcemapping.GetResourceMappingResponse' - /policy.resourcemapping.ResourceMappingService/GetResourceMappingGroup: + $ref: '#/components/schemas/policy.resourcemapping.DeleteResourceMappingGroupResponse' + /policy.resourcemapping.ResourceMappingService/ListResourceMappings: post: tags: - policy.resourcemapping.ResourceMappingService - summary: GetResourceMappingGroup - operationId: policy.resourcemapping.ResourceMappingService.GetResourceMappingGroup + summary: ListResourceMappings + operationId: policy.resourcemapping.ResourceMappingService.ListResourceMappings parameters: - name: Connect-Protocol-Version in: header @@ -197,7 +197,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.resourcemapping.GetResourceMappingGroupRequest' + $ref: '#/components/schemas/policy.resourcemapping.ListResourceMappingsRequest' required: true responses: default: @@ -211,13 +211,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.resourcemapping.GetResourceMappingGroupResponse' - /policy.resourcemapping.ResourceMappingService/ListResourceMappingGroups: + $ref: '#/components/schemas/policy.resourcemapping.ListResourceMappingsResponse' + /policy.resourcemapping.ResourceMappingService/ListResourceMappingsByGroupFqns: post: tags: - policy.resourcemapping.ResourceMappingService - summary: ListResourceMappingGroups - operationId: policy.resourcemapping.ResourceMappingService.ListResourceMappingGroups + summary: ListResourceMappingsByGroupFqns + operationId: policy.resourcemapping.ResourceMappingService.ListResourceMappingsByGroupFqns parameters: - name: Connect-Protocol-Version in: header @@ -232,7 +232,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.resourcemapping.ListResourceMappingGroupsRequest' + $ref: '#/components/schemas/policy.resourcemapping.ListResourceMappingsByGroupFqnsRequest' required: true responses: default: @@ -246,13 +246,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.resourcemapping.ListResourceMappingGroupsResponse' - /policy.resourcemapping.ResourceMappingService/ListResourceMappings: + $ref: '#/components/schemas/policy.resourcemapping.ListResourceMappingsByGroupFqnsResponse' + /policy.resourcemapping.ResourceMappingService/GetResourceMapping: post: tags: - policy.resourcemapping.ResourceMappingService - summary: ListResourceMappings - operationId: policy.resourcemapping.ResourceMappingService.ListResourceMappings + summary: GetResourceMapping + operationId: policy.resourcemapping.ResourceMappingService.GetResourceMapping parameters: - name: Connect-Protocol-Version in: header @@ -267,7 +267,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.resourcemapping.ListResourceMappingsRequest' + $ref: '#/components/schemas/policy.resourcemapping.GetResourceMappingRequest' required: true responses: default: @@ -281,13 +281,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.resourcemapping.ListResourceMappingsResponse' - /policy.resourcemapping.ResourceMappingService/ListResourceMappingsByGroupFqns: + $ref: '#/components/schemas/policy.resourcemapping.GetResourceMappingResponse' + /policy.resourcemapping.ResourceMappingService/CreateResourceMapping: post: tags: - policy.resourcemapping.ResourceMappingService - summary: ListResourceMappingsByGroupFqns - operationId: policy.resourcemapping.ResourceMappingService.ListResourceMappingsByGroupFqns + summary: CreateResourceMapping + operationId: policy.resourcemapping.ResourceMappingService.CreateResourceMapping parameters: - name: Connect-Protocol-Version in: header @@ -302,7 +302,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.resourcemapping.ListResourceMappingsByGroupFqnsRequest' + $ref: '#/components/schemas/policy.resourcemapping.CreateResourceMappingRequest' required: true responses: default: @@ -316,7 +316,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.resourcemapping.ListResourceMappingsByGroupFqnsResponse' + $ref: '#/components/schemas/policy.resourcemapping.CreateResourceMappingResponse' /policy.resourcemapping.ResourceMappingService/UpdateResourceMapping: post: tags: @@ -352,12 +352,12 @@ paths: application/json: schema: $ref: '#/components/schemas/policy.resourcemapping.UpdateResourceMappingResponse' - /policy.resourcemapping.ResourceMappingService/UpdateResourceMappingGroup: + /policy.resourcemapping.ResourceMappingService/DeleteResourceMapping: post: tags: - policy.resourcemapping.ResourceMappingService - summary: UpdateResourceMappingGroup - operationId: policy.resourcemapping.ResourceMappingService.UpdateResourceMappingGroup + summary: DeleteResourceMapping + operationId: policy.resourcemapping.ResourceMappingService.DeleteResourceMapping parameters: - name: Connect-Protocol-Version in: header @@ -372,7 +372,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.resourcemapping.UpdateResourceMappingGroupRequest' + $ref: '#/components/schemas/policy.resourcemapping.DeleteResourceMappingRequest' required: true responses: default: @@ -386,9 +386,88 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.resourcemapping.UpdateResourceMappingGroupResponse' + $ref: '#/components/schemas/policy.resourcemapping.DeleteResourceMappingResponse' components: schemas: + common.MetadataUpdateEnum: + type: string + title: MetadataUpdateEnum + enum: + - METADATA_UPDATE_ENUM_UNSPECIFIED + - METADATA_UPDATE_ENUM_EXTEND + - METADATA_UPDATE_ENUM_REPLACE + policy.Action.StandardAction: + type: string + title: StandardAction + enum: + - STANDARD_ACTION_UNSPECIFIED + - STANDARD_ACTION_DECRYPT + - STANDARD_ACTION_TRANSMIT + policy.Algorithm: + type: string + title: Algorithm + enum: + - ALGORITHM_UNSPECIFIED + - 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 + description: Supported key algorithms. + policy.AttributeRuleTypeEnum: + type: string + title: AttributeRuleTypeEnum + enum: + - ATTRIBUTE_RULE_TYPE_ENUM_UNSPECIFIED + - ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF + - ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF + - ATTRIBUTE_RULE_TYPE_ENUM_HIERARCHY + policy.ConditionBooleanTypeEnum: + type: string + title: ConditionBooleanTypeEnum + enum: + - CONDITION_BOOLEAN_TYPE_ENUM_UNSPECIFIED + - CONDITION_BOOLEAN_TYPE_ENUM_AND + - CONDITION_BOOLEAN_TYPE_ENUM_OR + policy.KasPublicKeyAlgEnum: + type: string + title: KasPublicKeyAlgEnum + enum: + - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED + - KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048 + - KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 + policy.SourceType: + type: string + title: SourceType + enum: + - SOURCE_TYPE_UNSPECIFIED + - SOURCE_TYPE_INTERNAL + - SOURCE_TYPE_EXTERNAL + description: |- + Describes whether this kas is managed by the organization or if they imported + the kas information from an external party. These two modes are necessary in order + to encrypt a tdf dek with an external parties kas public key. + policy.SubjectMappingOperatorEnum: + type: string + title: SubjectMappingOperatorEnum + enum: + - SUBJECT_MAPPING_OPERATOR_ENUM_UNSPECIFIED + - SUBJECT_MAPPING_OPERATOR_ENUM_IN + - SUBJECT_MAPPING_OPERATOR_ENUM_NOT_IN + - SUBJECT_MAPPING_OPERATOR_ENUM_IN_CONTAINS common.Metadata: type: object properties: @@ -444,82 +523,6 @@ components: title: value title: LabelsEntry additionalProperties: false - common.MetadataUpdateEnum: - type: string - title: MetadataUpdateEnum - enum: - - METADATA_UPDATE_ENUM_UNSPECIFIED - - METADATA_UPDATE_ENUM_EXTEND - - METADATA_UPDATE_ENUM_REPLACE - connect-protocol-version: - type: number - title: Connect-Protocol-Version - enum: - - 1 - description: Define the version of the Connect protocol - const: 1 - connect-timeout-header: - type: number - title: Connect-Timeout-Ms - description: Define the timeout, in ms - connect.error: - type: object - properties: - code: - type: string - examples: - - not_found - enum: - - canceled - - unknown - - invalid_argument - - deadline_exceeded - - not_found - - already_exists - - permission_denied - - resource_exhausted - - failed_precondition - - aborted - - out_of_range - - unimplemented - - internal - - unavailable - - data_loss - - unauthenticated - description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. - message: - type: string - description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. - details: - type: array - items: - $ref: '#/components/schemas/connect.error_details.Any' - description: A list of messages that carry the error details. There is no limit on the number of messages. - title: Connect Error - additionalProperties: true - description: 'Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation' - connect.error_details.Any: - type: object - properties: - type: - type: string - description: 'A URL that acts as a globally unique identifier for the type of the serialized message. For example: `type.googleapis.com/google.rpc.ErrorInfo`. This is used to determine the schema of the data in the `value` field and is the discriminator for the `debug` field.' - value: - type: string - format: binary - description: The Protobuf message, serialized as bytes and base64-encoded. The specific message type is identified by the `type` field. - debug: - oneOf: - - type: object - title: Any - additionalProperties: true - description: Detailed error information. - discriminator: - propertyName: type - title: Debug - description: Deserialized error detail payload. The 'type' field indicates the schema. This field is for easier debugging and should not be relied upon for application logic. - additionalProperties: true - description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message, with an additional debug field for ConnectRPC error details. google.protobuf.BoolValue: type: boolean description: |- @@ -532,8 +535,8 @@ components: google.protobuf.Timestamp: type: string examples: - - "2023-01-15T01:30:15.01Z" - - "2024-12-25T12:00:00Z" + - 1s + - 1.000340012s format: date-time description: |- A Timestamp represents a point in time independent of any time zone or local @@ -627,65 +630,41 @@ components: ) to obtain a formatter capable of generating timestamps in this format. policy.Action: type: object - allOf: + oneOf: - properties: - id: - type: string - title: id - description: Generated uuid in database - name: + custom: type: string - title: name - namespace: - title: namespace - description: Namespace context for this action - $ref: '#/components/schemas/policy.Namespace' - metadata: - title: metadata - $ref: '#/components/schemas/common.Metadata' - - oneOf: - - type: object - properties: - custom: - type: string - title: custom - description: Deprecated title: custom - required: - - custom - - type: object - properties: - standard: - title: standard - description: Deprecated - $ref: '#/components/schemas/policy.Action.StandardAction' + description: Deprecated + title: custom + required: + - custom + - properties: + standard: title: standard - required: - - standard + description: Deprecated + $ref: '#/components/schemas/policy.Action.StandardAction' + title: standard + required: + - standard + properties: + id: + type: string + title: id + description: Generated uuid in database + name: + type: string + title: name + namespace: + title: namespace + description: Namespace context for this action + $ref: '#/components/schemas/policy.Namespace' + metadata: + title: metadata + $ref: '#/components/schemas/common.Metadata' title: Action additionalProperties: false description: An action an entity can take - policy.Action.StandardAction: - type: string - title: StandardAction - enum: - - STANDARD_ACTION_UNSPECIFIED - - STANDARD_ACTION_DECRYPT - - STANDARD_ACTION_TRANSMIT - policy.Algorithm: - type: string - title: Algorithm - enum: - - ALGORITHM_UNSPECIFIED - - 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 - description: Supported key algorithms. policy.Attribute: type: object properties: @@ -742,14 +721,6 @@ components: required: - rule additionalProperties: false - policy.AttributeRuleTypeEnum: - type: string - title: AttributeRuleTypeEnum - enum: - - ATTRIBUTE_RULE_TYPE_ENUM_UNSPECIFIED - - ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF - - ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF - - ATTRIBUTE_RULE_TYPE_ENUM_HIERARCHY policy.Condition: type: object properties: @@ -767,6 +738,7 @@ components: type: array items: type: string + minItems: 1 title: subject_external_values minItems: 1 description: |- @@ -782,13 +754,6 @@ components: * A Condition defines a rule of - policy.ConditionBooleanTypeEnum: - type: string - title: ConditionBooleanTypeEnum - enum: - - CONDITION_BOOLEAN_TYPE_ENUM_UNSPECIFIED - - CONDITION_BOOLEAN_TYPE_ENUM_AND - - CONDITION_BOOLEAN_TYPE_ENUM_OR policy.ConditionGroup: type: object properties: @@ -825,7 +790,7 @@ components: alg: not: enum: - - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED + - 0 title: alg description: |- A known algorithm type with any additional parameters encoded. @@ -837,19 +802,6 @@ components: description: |- Deprecated A KAS public key and some associated metadata for further identifcation - policy.KasPublicKeyAlgEnum: - type: string - title: KasPublicKeyAlgEnum - enum: - - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED - - KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048 - - KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 policy.KasPublicKeySet: type: object properties: @@ -872,9 +824,13 @@ components: uri: type: string title: uri - description: | + description: |+ Address of a KAS instance - uri_format // URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes. + URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes.: + ``` + this.matches('^https?://[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?)*(:[0-9]+)?(/.*)?$') + ``` + publicKey: title: public_key description: 'Deprecated: KAS can have multiple key pairs' @@ -1072,8 +1028,7 @@ components: policy.PublicKey: type: object oneOf: - - type: object - properties: + - properties: cached: title: cached description: public key with additional information. Current preferred version @@ -1081,14 +1036,17 @@ components: title: cached required: - cached - - type: object - properties: + - properties: remote: type: string title: remote - description: | + description: |+ kas public key url - optional since can also be retrieved via public key - uri_format // URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes. + URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes.: + ``` + this.matches('^https://[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?)*(/.*)?$') + ``` + title: remote required: - remote @@ -1196,17 +1154,6 @@ components: title: pem title: SimpleKasPublicKey additionalProperties: false - policy.SourceType: - type: string - title: SourceType - enum: - - SOURCE_TYPE_UNSPECIFIED - - SOURCE_TYPE_INTERNAL - - SOURCE_TYPE_EXTERNAL - description: |- - Describes whether this kas is managed by the organization or if they imported - the kas information from an external party. These two modes are necessary in order - to encrypt a tdf dek with an external parties kas public key. policy.SubjectConditionSet: type: object properties: @@ -1272,14 +1219,6 @@ components: description: |- Subject Mapping: A Policy assigning Subject Set(s) to a permitted attribute value + action(s) combination - policy.SubjectMappingOperatorEnum: - type: string - title: SubjectMappingOperatorEnum - enum: - - SUBJECT_MAPPING_OPERATOR_ENUM_UNSPECIFIED - - SUBJECT_MAPPING_OPERATOR_ENUM_IN - - SUBJECT_MAPPING_OPERATOR_ENUM_NOT_IN - - SUBJECT_MAPPING_OPERATOR_ENUM_IN_CONTAINS policy.SubjectSet: type: object properties: @@ -1386,6 +1325,8 @@ components: type: array items: type: string + maxItems: 1000 + minItems: 1 title: terms maxItems: 1000 minItems: 1 @@ -1393,9 +1334,13 @@ components: groupId: type: string title: group_id - description: | + description: |+ Optional - optional_uuid_format // Optional field must be a valid UUID + Optional field must be a valid UUID: + ``` + size(this) == 0 || this.matches('[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}') + ``` + metadata: title: metadata description: Optional @@ -1488,9 +1433,13 @@ components: namespaceId: type: string title: namespace_id - description: | + description: |+ Optional - optional_uuid_format // Optional field must be a valid UUID + Optional field must be a valid UUID: + ``` + size(this) == 0 || this.matches('[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}') + ``` + pagination: title: pagination description: Optional @@ -1517,6 +1466,7 @@ components: type: array items: type: string + minItems: 1 title: fqns minItems: 1 description: |- @@ -1552,9 +1502,13 @@ components: groupId: type: string title: group_id - description: | + description: |+ Optional - optional_uuid_format // Optional field must be a valid UUID + Optional field must be a valid UUID: + ``` + size(this) == 0 || this.matches('[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}') + ``` + pagination: title: pagination description: Optional @@ -1598,16 +1552,24 @@ components: namespaceId: type: string title: namespace_id - description: | + description: |+ Optional - optional_uuid_format // Optional field must be a valid UUID + Optional field must be a valid UUID: + ``` + size(this) == 0 || this.matches('[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}') + ``` + name: type: string title: name maxLength: 253 - description: | + description: |+ Optional - optional_name_format // Optional field must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored group name will be normalized to lower case. + Optional field must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored group name will be normalized to lower case.: + ``` + size(this) == 0 || this.matches('^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$') + ``` + metadata: title: metadata description: Common metadata @@ -1636,22 +1598,31 @@ components: attributeValueId: type: string title: attribute_value_id - description: | + description: |+ Optional - optional_uuid_format // Optional field must be a valid UUID + Optional field must be a valid UUID: + ``` + size(this) == 0 || this.matches('[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}') + ``` + terms: type: array items: type: string + maxItems: 1000 title: terms maxItems: 1000 description: Optional groupId: type: string title: group_id - description: | + description: |+ Optional - optional_uuid_format // Optional field must be a valid UUID + Optional field must be a valid UUID: + ``` + size(this) == 0 || this.matches('[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}') + ``` + metadata: title: metadata description: |- @@ -1671,6 +1642,63 @@ components: $ref: '#/components/schemas/policy.ResourceMapping' title: UpdateResourceMappingResponse additionalProperties: false + connect-protocol-version: + type: number + title: Connect-Protocol-Version + enum: + - 1 + description: Define the version of the Connect protocol + const: 1 + connect-timeout-header: + type: number + title: Connect-Timeout-Ms + description: Define the timeout, in ms + connect.error: + type: object + properties: + code: + type: string + examples: + - not_found + enum: + - canceled + - unknown + - invalid_argument + - deadline_exceeded + - not_found + - already_exists + - permission_denied + - resource_exhausted + - failed_precondition + - aborted + - out_of_range + - unimplemented + - internal + - unavailable + - data_loss + - unauthenticated + description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. + message: + type: string + description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. + detail: + $ref: '#/components/schemas/google.protobuf.Any' + title: Connect Error + additionalProperties: true + description: 'Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation' + google.protobuf.Any: + type: object + properties: + type: + type: string + value: + type: string + format: binary + debug: + type: object + additionalProperties: true + additionalProperties: true + description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message. security: [] tags: - name: policy.resourcemapping.ResourceMappingService diff --git a/docs/openapi/policy/selectors.openapi.yaml b/docs/openapi/policy/selectors.openapi.yaml index 65cc330d4a..8b885a5a41 100644 --- a/docs/openapi/policy/selectors.openapi.yaml +++ b/docs/openapi/policy/selectors.openapi.yaml @@ -4,6 +4,18 @@ info: paths: {} components: schemas: + policy.SortDirection: + type: string + title: SortDirection + enum: + - SORT_DIRECTION_UNSPECIFIED + - SORT_DIRECTION_ASC + - SORT_DIRECTION_DESC + description: |- + Sorting direction shared across list APIs. + When the 'sort' field is omitted or the chosen sort 'field' is UNSPECIFIED, + the endpoint's request message defines the default ordering; see the + specific List* request docs. policy.AttributeDefinitionSelector: type: object properties: @@ -152,16 +164,4 @@ components: description: Total count of entire list title: PageResponse additionalProperties: false - policy.SortDirection: - type: string - title: SortDirection - enum: - - SORT_DIRECTION_UNSPECIFIED - - SORT_DIRECTION_ASC - - SORT_DIRECTION_DESC - description: |- - Sorting direction shared across list APIs. - When the 'sort' field is omitted or the chosen sort 'field' is UNSPECIFIED, - the endpoint's request message defines the default ordering; see the - specific List* request docs. security: [] diff --git a/docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml b/docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml index 10e2ebeb84..4aa3f7eed8 100644 --- a/docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml +++ b/docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml @@ -2,12 +2,13 @@ openapi: 3.1.0 info: title: policy.subjectmapping paths: - /policy.subjectmapping.SubjectMappingService/CreateSubjectConditionSet: + /policy.subjectmapping.SubjectMappingService/MatchSubjectMappings: post: tags: - policy.subjectmapping.SubjectMappingService - summary: CreateSubjectConditionSet - operationId: policy.subjectmapping.SubjectMappingService.CreateSubjectConditionSet + summary: MatchSubjectMappings + description: Find matching Subject Mappings for a given Subject + operationId: policy.subjectmapping.SubjectMappingService.MatchSubjectMappings parameters: - name: Connect-Protocol-Version in: header @@ -22,7 +23,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.subjectmapping.CreateSubjectConditionSetRequest' + $ref: '#/components/schemas/policy.subjectmapping.MatchSubjectMappingsRequest' required: true responses: default: @@ -36,13 +37,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.subjectmapping.CreateSubjectConditionSetResponse' - /policy.subjectmapping.SubjectMappingService/CreateSubjectMapping: + $ref: '#/components/schemas/policy.subjectmapping.MatchSubjectMappingsResponse' + /policy.subjectmapping.SubjectMappingService/ListSubjectMappings: post: tags: - policy.subjectmapping.SubjectMappingService - summary: CreateSubjectMapping - operationId: policy.subjectmapping.SubjectMappingService.CreateSubjectMapping + summary: ListSubjectMappings + operationId: policy.subjectmapping.SubjectMappingService.ListSubjectMappings parameters: - name: Connect-Protocol-Version in: header @@ -57,7 +58,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.subjectmapping.CreateSubjectMappingRequest' + $ref: '#/components/schemas/policy.subjectmapping.ListSubjectMappingsRequest' required: true responses: default: @@ -71,13 +72,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.subjectmapping.CreateSubjectMappingResponse' - /policy.subjectmapping.SubjectMappingService/DeleteAllUnmappedSubjectConditionSets: + $ref: '#/components/schemas/policy.subjectmapping.ListSubjectMappingsResponse' + /policy.subjectmapping.SubjectMappingService/GetSubjectMapping: post: tags: - policy.subjectmapping.SubjectMappingService - summary: DeleteAllUnmappedSubjectConditionSets - operationId: policy.subjectmapping.SubjectMappingService.DeleteAllUnmappedSubjectConditionSets + summary: GetSubjectMapping + operationId: policy.subjectmapping.SubjectMappingService.GetSubjectMapping parameters: - name: Connect-Protocol-Version in: header @@ -92,7 +93,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.subjectmapping.DeleteAllUnmappedSubjectConditionSetsRequest' + $ref: '#/components/schemas/policy.subjectmapping.GetSubjectMappingRequest' required: true responses: default: @@ -106,13 +107,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.subjectmapping.DeleteAllUnmappedSubjectConditionSetsResponse' - /policy.subjectmapping.SubjectMappingService/DeleteSubjectConditionSet: + $ref: '#/components/schemas/policy.subjectmapping.GetSubjectMappingResponse' + /policy.subjectmapping.SubjectMappingService/CreateSubjectMapping: post: tags: - policy.subjectmapping.SubjectMappingService - summary: DeleteSubjectConditionSet - operationId: policy.subjectmapping.SubjectMappingService.DeleteSubjectConditionSet + summary: CreateSubjectMapping + operationId: policy.subjectmapping.SubjectMappingService.CreateSubjectMapping parameters: - name: Connect-Protocol-Version in: header @@ -127,7 +128,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.subjectmapping.DeleteSubjectConditionSetRequest' + $ref: '#/components/schemas/policy.subjectmapping.CreateSubjectMappingRequest' required: true responses: default: @@ -141,13 +142,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.subjectmapping.DeleteSubjectConditionSetResponse' - /policy.subjectmapping.SubjectMappingService/DeleteSubjectMapping: + $ref: '#/components/schemas/policy.subjectmapping.CreateSubjectMappingResponse' + /policy.subjectmapping.SubjectMappingService/UpdateSubjectMapping: post: tags: - policy.subjectmapping.SubjectMappingService - summary: DeleteSubjectMapping - operationId: policy.subjectmapping.SubjectMappingService.DeleteSubjectMapping + summary: UpdateSubjectMapping + operationId: policy.subjectmapping.SubjectMappingService.UpdateSubjectMapping parameters: - name: Connect-Protocol-Version in: header @@ -162,7 +163,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.subjectmapping.DeleteSubjectMappingRequest' + $ref: '#/components/schemas/policy.subjectmapping.UpdateSubjectMappingRequest' required: true responses: default: @@ -176,13 +177,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.subjectmapping.DeleteSubjectMappingResponse' - /policy.subjectmapping.SubjectMappingService/GetSubjectConditionSet: + $ref: '#/components/schemas/policy.subjectmapping.UpdateSubjectMappingResponse' + /policy.subjectmapping.SubjectMappingService/DeleteSubjectMapping: post: tags: - policy.subjectmapping.SubjectMappingService - summary: GetSubjectConditionSet - operationId: policy.subjectmapping.SubjectMappingService.GetSubjectConditionSet + summary: DeleteSubjectMapping + operationId: policy.subjectmapping.SubjectMappingService.DeleteSubjectMapping parameters: - name: Connect-Protocol-Version in: header @@ -197,7 +198,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.subjectmapping.GetSubjectConditionSetRequest' + $ref: '#/components/schemas/policy.subjectmapping.DeleteSubjectMappingRequest' required: true responses: default: @@ -211,13 +212,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.subjectmapping.GetSubjectConditionSetResponse' - /policy.subjectmapping.SubjectMappingService/GetSubjectMapping: + $ref: '#/components/schemas/policy.subjectmapping.DeleteSubjectMappingResponse' + /policy.subjectmapping.SubjectMappingService/ListSubjectConditionSets: post: tags: - policy.subjectmapping.SubjectMappingService - summary: GetSubjectMapping - operationId: policy.subjectmapping.SubjectMappingService.GetSubjectMapping + summary: ListSubjectConditionSets + operationId: policy.subjectmapping.SubjectMappingService.ListSubjectConditionSets parameters: - name: Connect-Protocol-Version in: header @@ -232,7 +233,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.subjectmapping.GetSubjectMappingRequest' + $ref: '#/components/schemas/policy.subjectmapping.ListSubjectConditionSetsRequest' required: true responses: default: @@ -246,13 +247,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.subjectmapping.GetSubjectMappingResponse' - /policy.subjectmapping.SubjectMappingService/ListSubjectConditionSets: + $ref: '#/components/schemas/policy.subjectmapping.ListSubjectConditionSetsResponse' + /policy.subjectmapping.SubjectMappingService/GetSubjectConditionSet: post: tags: - policy.subjectmapping.SubjectMappingService - summary: ListSubjectConditionSets - operationId: policy.subjectmapping.SubjectMappingService.ListSubjectConditionSets + summary: GetSubjectConditionSet + operationId: policy.subjectmapping.SubjectMappingService.GetSubjectConditionSet parameters: - name: Connect-Protocol-Version in: header @@ -267,7 +268,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.subjectmapping.ListSubjectConditionSetsRequest' + $ref: '#/components/schemas/policy.subjectmapping.GetSubjectConditionSetRequest' required: true responses: default: @@ -281,13 +282,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.subjectmapping.ListSubjectConditionSetsResponse' - /policy.subjectmapping.SubjectMappingService/ListSubjectMappings: + $ref: '#/components/schemas/policy.subjectmapping.GetSubjectConditionSetResponse' + /policy.subjectmapping.SubjectMappingService/CreateSubjectConditionSet: post: tags: - policy.subjectmapping.SubjectMappingService - summary: ListSubjectMappings - operationId: policy.subjectmapping.SubjectMappingService.ListSubjectMappings + summary: CreateSubjectConditionSet + operationId: policy.subjectmapping.SubjectMappingService.CreateSubjectConditionSet parameters: - name: Connect-Protocol-Version in: header @@ -302,7 +303,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.subjectmapping.ListSubjectMappingsRequest' + $ref: '#/components/schemas/policy.subjectmapping.CreateSubjectConditionSetRequest' required: true responses: default: @@ -316,14 +317,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.subjectmapping.ListSubjectMappingsResponse' - /policy.subjectmapping.SubjectMappingService/MatchSubjectMappings: + $ref: '#/components/schemas/policy.subjectmapping.CreateSubjectConditionSetResponse' + /policy.subjectmapping.SubjectMappingService/UpdateSubjectConditionSet: post: tags: - policy.subjectmapping.SubjectMappingService - summary: MatchSubjectMappings - description: Find matching Subject Mappings for a given Subject - operationId: policy.subjectmapping.SubjectMappingService.MatchSubjectMappings + summary: UpdateSubjectConditionSet + operationId: policy.subjectmapping.SubjectMappingService.UpdateSubjectConditionSet parameters: - name: Connect-Protocol-Version in: header @@ -338,7 +338,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.subjectmapping.MatchSubjectMappingsRequest' + $ref: '#/components/schemas/policy.subjectmapping.UpdateSubjectConditionSetRequest' required: true responses: default: @@ -352,13 +352,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.subjectmapping.MatchSubjectMappingsResponse' - /policy.subjectmapping.SubjectMappingService/UpdateSubjectConditionSet: + $ref: '#/components/schemas/policy.subjectmapping.UpdateSubjectConditionSetResponse' + /policy.subjectmapping.SubjectMappingService/DeleteSubjectConditionSet: post: tags: - policy.subjectmapping.SubjectMappingService - summary: UpdateSubjectConditionSet - operationId: policy.subjectmapping.SubjectMappingService.UpdateSubjectConditionSet + summary: DeleteSubjectConditionSet + operationId: policy.subjectmapping.SubjectMappingService.DeleteSubjectConditionSet parameters: - name: Connect-Protocol-Version in: header @@ -373,7 +373,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.subjectmapping.UpdateSubjectConditionSetRequest' + $ref: '#/components/schemas/policy.subjectmapping.DeleteSubjectConditionSetRequest' required: true responses: default: @@ -387,13 +387,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.subjectmapping.UpdateSubjectConditionSetResponse' - /policy.subjectmapping.SubjectMappingService/UpdateSubjectMapping: + $ref: '#/components/schemas/policy.subjectmapping.DeleteSubjectConditionSetResponse' + /policy.subjectmapping.SubjectMappingService/DeleteAllUnmappedSubjectConditionSets: post: tags: - policy.subjectmapping.SubjectMappingService - summary: UpdateSubjectMapping - operationId: policy.subjectmapping.SubjectMappingService.UpdateSubjectMapping + summary: DeleteAllUnmappedSubjectConditionSets + operationId: policy.subjectmapping.SubjectMappingService.DeleteAllUnmappedSubjectConditionSets parameters: - name: Connect-Protocol-Version in: header @@ -408,7 +408,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.subjectmapping.UpdateSubjectMappingRequest' + $ref: '#/components/schemas/policy.subjectmapping.DeleteAllUnmappedSubjectConditionSetsRequest' required: true responses: default: @@ -422,9 +422,114 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.subjectmapping.UpdateSubjectMappingResponse' + $ref: '#/components/schemas/policy.subjectmapping.DeleteAllUnmappedSubjectConditionSetsResponse' components: schemas: + common.MetadataUpdateEnum: + type: string + title: MetadataUpdateEnum + enum: + - METADATA_UPDATE_ENUM_UNSPECIFIED + - METADATA_UPDATE_ENUM_EXTEND + - METADATA_UPDATE_ENUM_REPLACE + policy.Action.StandardAction: + type: string + title: StandardAction + enum: + - STANDARD_ACTION_UNSPECIFIED + - STANDARD_ACTION_DECRYPT + - STANDARD_ACTION_TRANSMIT + policy.Algorithm: + type: string + title: Algorithm + enum: + - ALGORITHM_UNSPECIFIED + - 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 + description: Supported key algorithms. + policy.AttributeRuleTypeEnum: + type: string + title: AttributeRuleTypeEnum + enum: + - ATTRIBUTE_RULE_TYPE_ENUM_UNSPECIFIED + - ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF + - ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF + - ATTRIBUTE_RULE_TYPE_ENUM_HIERARCHY + policy.ConditionBooleanTypeEnum: + type: string + title: ConditionBooleanTypeEnum + enum: + - CONDITION_BOOLEAN_TYPE_ENUM_UNSPECIFIED + - CONDITION_BOOLEAN_TYPE_ENUM_AND + - CONDITION_BOOLEAN_TYPE_ENUM_OR + policy.KasPublicKeyAlgEnum: + type: string + title: KasPublicKeyAlgEnum + enum: + - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED + - KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048 + - KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 + policy.SortDirection: + type: string + title: SortDirection + enum: + - SORT_DIRECTION_UNSPECIFIED + - SORT_DIRECTION_ASC + - SORT_DIRECTION_DESC + description: |- + Sorting direction shared across list APIs. + When the 'sort' field is omitted or the chosen sort 'field' is UNSPECIFIED, + the endpoint's request message defines the default ordering; see the + specific List* request docs. + policy.SourceType: + type: string + title: SourceType + enum: + - SOURCE_TYPE_UNSPECIFIED + - SOURCE_TYPE_INTERNAL + - SOURCE_TYPE_EXTERNAL + description: |- + Describes whether this kas is managed by the organization or if they imported + the kas information from an external party. These two modes are necessary in order + to encrypt a tdf dek with an external parties kas public key. + policy.SubjectMappingOperatorEnum: + type: string + title: SubjectMappingOperatorEnum + enum: + - SUBJECT_MAPPING_OPERATOR_ENUM_UNSPECIFIED + - SUBJECT_MAPPING_OPERATOR_ENUM_IN + - SUBJECT_MAPPING_OPERATOR_ENUM_NOT_IN + - SUBJECT_MAPPING_OPERATOR_ENUM_IN_CONTAINS + policy.subjectmapping.SortSubjectConditionSetsType: + type: string + title: SortSubjectConditionSetsType + enum: + - SORT_SUBJECT_CONDITION_SETS_TYPE_UNSPECIFIED + - SORT_SUBJECT_CONDITION_SETS_TYPE_CREATED_AT + - SORT_SUBJECT_CONDITION_SETS_TYPE_UPDATED_AT + policy.subjectmapping.SortSubjectMappingsType: + type: string + title: SortSubjectMappingsType + enum: + - SORT_SUBJECT_MAPPINGS_TYPE_UNSPECIFIED + - SORT_SUBJECT_MAPPINGS_TYPE_CREATED_AT + - SORT_SUBJECT_MAPPINGS_TYPE_UPDATED_AT common.Metadata: type: object properties: @@ -480,82 +585,6 @@ components: title: value title: LabelsEntry additionalProperties: false - common.MetadataUpdateEnum: - type: string - title: MetadataUpdateEnum - enum: - - METADATA_UPDATE_ENUM_UNSPECIFIED - - METADATA_UPDATE_ENUM_EXTEND - - METADATA_UPDATE_ENUM_REPLACE - connect-protocol-version: - type: number - title: Connect-Protocol-Version - enum: - - 1 - description: Define the version of the Connect protocol - const: 1 - connect-timeout-header: - type: number - title: Connect-Timeout-Ms - description: Define the timeout, in ms - connect.error: - type: object - properties: - code: - type: string - examples: - - not_found - enum: - - canceled - - unknown - - invalid_argument - - deadline_exceeded - - not_found - - already_exists - - permission_denied - - resource_exhausted - - failed_precondition - - aborted - - out_of_range - - unimplemented - - internal - - unavailable - - data_loss - - unauthenticated - description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. - message: - type: string - description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. - details: - type: array - items: - $ref: '#/components/schemas/connect.error_details.Any' - description: A list of messages that carry the error details. There is no limit on the number of messages. - title: Connect Error - additionalProperties: true - description: 'Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation' - connect.error_details.Any: - type: object - properties: - type: - type: string - description: 'A URL that acts as a globally unique identifier for the type of the serialized message. For example: `type.googleapis.com/google.rpc.ErrorInfo`. This is used to determine the schema of the data in the `value` field and is the discriminator for the `debug` field.' - value: - type: string - format: binary - description: The Protobuf message, serialized as bytes and base64-encoded. The specific message type is identified by the `type` field. - debug: - oneOf: - - type: object - title: Any - additionalProperties: true - description: Detailed error information. - discriminator: - propertyName: type - title: Debug - description: Deserialized error detail payload. The 'type' field indicates the schema. This field is for easier debugging and should not be relied upon for application logic. - additionalProperties: true - description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message, with an additional debug field for ConnectRPC error details. google.protobuf.BoolValue: type: boolean description: |- @@ -568,8 +597,8 @@ components: google.protobuf.Timestamp: type: string examples: - - "2023-01-15T01:30:15.01Z" - - "2024-12-25T12:00:00Z" + - 1s + - 1.000340012s format: date-time description: |- A Timestamp represents a point in time independent of any time zone or local @@ -663,65 +692,41 @@ components: ) to obtain a formatter capable of generating timestamps in this format. policy.Action: type: object - allOf: + oneOf: - properties: - id: - type: string - title: id - description: Generated uuid in database - name: + custom: type: string - title: name - namespace: - title: namespace - description: Namespace context for this action - $ref: '#/components/schemas/policy.Namespace' - metadata: - title: metadata - $ref: '#/components/schemas/common.Metadata' - - oneOf: - - type: object - properties: - custom: - type: string - title: custom - description: Deprecated title: custom - required: - - custom - - type: object - properties: - standard: - title: standard - description: Deprecated - $ref: '#/components/schemas/policy.Action.StandardAction' + description: Deprecated + title: custom + required: + - custom + - properties: + standard: title: standard - required: - - standard + description: Deprecated + $ref: '#/components/schemas/policy.Action.StandardAction' + title: standard + required: + - standard + properties: + id: + type: string + title: id + description: Generated uuid in database + name: + type: string + title: name + namespace: + title: namespace + description: Namespace context for this action + $ref: '#/components/schemas/policy.Namespace' + metadata: + title: metadata + $ref: '#/components/schemas/common.Metadata' title: Action additionalProperties: false description: An action an entity can take - policy.Action.StandardAction: - type: string - title: StandardAction - enum: - - STANDARD_ACTION_UNSPECIFIED - - STANDARD_ACTION_DECRYPT - - STANDARD_ACTION_TRANSMIT - policy.Algorithm: - type: string - title: Algorithm - enum: - - ALGORITHM_UNSPECIFIED - - 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 - description: Supported key algorithms. policy.Attribute: type: object properties: @@ -778,14 +783,6 @@ components: required: - rule additionalProperties: false - policy.AttributeRuleTypeEnum: - type: string - title: AttributeRuleTypeEnum - enum: - - ATTRIBUTE_RULE_TYPE_ENUM_UNSPECIFIED - - ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF - - ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF - - ATTRIBUTE_RULE_TYPE_ENUM_HIERARCHY policy.Condition: type: object properties: @@ -803,6 +800,7 @@ components: type: array items: type: string + minItems: 1 title: subject_external_values minItems: 1 description: |- @@ -818,13 +816,6 @@ components: * A Condition defines a rule of - policy.ConditionBooleanTypeEnum: - type: string - title: ConditionBooleanTypeEnum - enum: - - CONDITION_BOOLEAN_TYPE_ENUM_UNSPECIFIED - - CONDITION_BOOLEAN_TYPE_ENUM_AND - - CONDITION_BOOLEAN_TYPE_ENUM_OR policy.ConditionGroup: type: object properties: @@ -861,7 +852,7 @@ components: alg: not: enum: - - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED + - 0 title: alg description: |- A known algorithm type with any additional parameters encoded. @@ -873,19 +864,6 @@ components: description: |- Deprecated A KAS public key and some associated metadata for further identifcation - policy.KasPublicKeyAlgEnum: - type: string - title: KasPublicKeyAlgEnum - enum: - - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED - - KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048 - - KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 policy.KasPublicKeySet: type: object properties: @@ -908,9 +886,13 @@ components: uri: type: string title: uri - description: | + description: |+ Address of a KAS instance - uri_format // URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes. + URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes.: + ``` + this.matches('^https?://[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?)*(:[0-9]+)?(/.*)?$') + ``` + publicKey: title: public_key description: 'Deprecated: KAS can have multiple key pairs' @@ -1108,8 +1090,7 @@ components: policy.PublicKey: type: object oneOf: - - type: object - properties: + - properties: cached: title: cached description: public key with additional information. Current preferred version @@ -1117,14 +1098,17 @@ components: title: cached required: - cached - - type: object - properties: + - properties: remote: type: string title: remote - description: | + description: |+ kas public key url - optional since can also be retrieved via public key - uri_format // URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes. + URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes.: + ``` + this.matches('^https://[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?)*(/.*)?$') + ``` + title: remote required: - remote @@ -1232,29 +1216,6 @@ components: title: pem title: SimpleKasPublicKey additionalProperties: false - policy.SortDirection: - type: string - title: SortDirection - enum: - - SORT_DIRECTION_UNSPECIFIED - - SORT_DIRECTION_ASC - - SORT_DIRECTION_DESC - description: |- - Sorting direction shared across list APIs. - When the 'sort' field is omitted or the chosen sort 'field' is UNSPECIFIED, - the endpoint's request message defines the default ordering; see the - specific List* request docs. - policy.SourceType: - type: string - title: SourceType - enum: - - SOURCE_TYPE_UNSPECIFIED - - SOURCE_TYPE_INTERNAL - - SOURCE_TYPE_EXTERNAL - description: |- - Describes whether this kas is managed by the organization or if they imported - the kas information from an external party. These two modes are necessary in order - to encrypt a tdf dek with an external parties kas public key. policy.SubjectConditionSet: type: object properties: @@ -1320,14 +1281,6 @@ components: description: |- Subject Mapping: A Policy assigning Subject Set(s) to a permitted attribute value + action(s) combination - policy.SubjectMappingOperatorEnum: - type: string - title: SubjectMappingOperatorEnum - enum: - - SUBJECT_MAPPING_OPERATOR_ENUM_UNSPECIFIED - - SUBJECT_MAPPING_OPERATOR_ENUM_IN - - SUBJECT_MAPPING_OPERATOR_ENUM_NOT_IN - - SUBJECT_MAPPING_OPERATOR_ENUM_IN_CONTAINS policy.SubjectProperty: type: object properties: @@ -1348,6 +1301,7 @@ components: authoritative source such as an IDP (Identity Provider) or User Store. Examples include such ADFS/LDAP, OKTA, etc. For now, a valid property must contain both a selector expression & a resulting value. + The external_selector_value is a specifier to select a value from a flattened external representation of an Entity (such as from idP/LDAP), and the external_value is the value selected by the external_selector_value on that @@ -1461,17 +1415,25 @@ components: $ref: '#/components/schemas/policy.Action' title: actions minItems: 1 - description: | + description: |+ Required The actions permitted by subjects in this mapping - action_name_or_id_not_empty // Action name or ID must not be empty if provided + Action name or ID must not be empty if provided: + ``` + this.all(item, item.name != '' || item.id != '') + ``` + existingSubjectConditionSetId: type: string title: existing_subject_condition_set_id - description: | + description: |+ Either of the following: Reuse existing SubjectConditionSet (NOTE: prioritized over new_subject_condition_set) - optional_uuid_format // Optional field must be a valid UUID + Optional field must be a valid UUID: + ``` + size(this) == 0 || this.matches('[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}') + ``` + newSubjectConditionSet: title: new_subject_condition_set description: 'Create new SubjectConditionSet (NOTE: ignored if existing_subject_condition_set_id is provided)' @@ -1708,20 +1670,6 @@ components: title: subject_mappings title: MatchSubjectMappingsResponse additionalProperties: false - policy.subjectmapping.SortSubjectConditionSetsType: - type: string - title: SortSubjectConditionSetsType - enum: - - SORT_SUBJECT_CONDITION_SETS_TYPE_UNSPECIFIED - - SORT_SUBJECT_CONDITION_SETS_TYPE_CREATED_AT - - SORT_SUBJECT_CONDITION_SETS_TYPE_UPDATED_AT - policy.subjectmapping.SortSubjectMappingsType: - type: string - title: SortSubjectMappingsType - enum: - - SORT_SUBJECT_MAPPINGS_TYPE_UNSPECIFIED - - SORT_SUBJECT_MAPPINGS_TYPE_CREATED_AT - - SORT_SUBJECT_MAPPINGS_TYPE_UPDATED_AT policy.subjectmapping.SubjectConditionSetCreate: type: object properties: @@ -1807,19 +1755,27 @@ components: subjectConditionSetId: type: string title: subject_condition_set_id - description: | + description: |+ Optional Replaces the existing SubjectConditionSet id with a new one - optional_uuid_format // Optional field must be a valid UUID + Optional field must be a valid UUID: + ``` + size(this) == 0 || this.matches('[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}') + ``` + actions: type: array items: $ref: '#/components/schemas/policy.Action' title: actions - description: | + description: |+ Optional Replaces entire list of actions permitted by subjects - action_name_or_id_not_empty // Action name or ID must not be empty if provided + Action name or ID must not be empty if provided: + ``` + this.size() == 0 || this.all(item, item.name != '' || item.id != '') + ``` + metadata: title: metadata description: Common metadata @@ -1838,6 +1794,63 @@ components: $ref: '#/components/schemas/policy.SubjectMapping' title: UpdateSubjectMappingResponse additionalProperties: false + connect-protocol-version: + type: number + title: Connect-Protocol-Version + enum: + - 1 + description: Define the version of the Connect protocol + const: 1 + connect-timeout-header: + type: number + title: Connect-Timeout-Ms + description: Define the timeout, in ms + connect.error: + type: object + properties: + code: + type: string + examples: + - not_found + enum: + - canceled + - unknown + - invalid_argument + - deadline_exceeded + - not_found + - already_exists + - permission_denied + - resource_exhausted + - failed_precondition + - aborted + - out_of_range + - unimplemented + - internal + - unavailable + - data_loss + - unauthenticated + description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. + message: + type: string + description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. + detail: + $ref: '#/components/schemas/google.protobuf.Any' + title: Connect Error + additionalProperties: true + description: 'Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation' + google.protobuf.Any: + type: object + properties: + type: + type: string + value: + type: string + format: binary + debug: + type: object + additionalProperties: true + additionalProperties: true + description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message. security: [] tags: - name: policy.subjectmapping.SubjectMappingService diff --git a/docs/openapi/policy/unsafe/unsafe.openapi.yaml b/docs/openapi/policy/unsafe/unsafe.openapi.yaml index 168c60f735..a234bc2f20 100644 --- a/docs/openapi/policy/unsafe/unsafe.openapi.yaml +++ b/docs/openapi/policy/unsafe/unsafe.openapi.yaml @@ -2,12 +2,16 @@ openapi: 3.1.0 info: title: policy.unsafe paths: - /policy.unsafe.UnsafeService/UnsafeDeleteAttribute: + /policy.unsafe.UnsafeService/UnsafeUpdateNamespace: post: tags: - policy.unsafe.UnsafeService - summary: UnsafeDeleteAttribute - operationId: policy.unsafe.UnsafeService.UnsafeDeleteAttribute + summary: UnsafeUpdateNamespace + description: |- + --------------------------------------* + Namespace RPCs + --------------------------------------- + operationId: policy.unsafe.UnsafeService.UnsafeUpdateNamespace parameters: - name: Connect-Protocol-Version in: header @@ -22,7 +26,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.unsafe.UnsafeDeleteAttributeRequest' + $ref: '#/components/schemas/policy.unsafe.UnsafeUpdateNamespaceRequest' required: true responses: default: @@ -36,13 +40,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.unsafe.UnsafeDeleteAttributeResponse' - /policy.unsafe.UnsafeService/UnsafeDeleteAttributeValue: + $ref: '#/components/schemas/policy.unsafe.UnsafeUpdateNamespaceResponse' + /policy.unsafe.UnsafeService/UnsafeReactivateNamespace: post: tags: - policy.unsafe.UnsafeService - summary: UnsafeDeleteAttributeValue - operationId: policy.unsafe.UnsafeService.UnsafeDeleteAttributeValue + summary: UnsafeReactivateNamespace + operationId: policy.unsafe.UnsafeService.UnsafeReactivateNamespace parameters: - name: Connect-Protocol-Version in: header @@ -57,7 +61,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.unsafe.UnsafeDeleteAttributeValueRequest' + $ref: '#/components/schemas/policy.unsafe.UnsafeReactivateNamespaceRequest' required: true responses: default: @@ -71,17 +75,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.unsafe.UnsafeDeleteAttributeValueResponse' - /policy.unsafe.UnsafeService/UnsafeDeleteKasKey: + $ref: '#/components/schemas/policy.unsafe.UnsafeReactivateNamespaceResponse' + /policy.unsafe.UnsafeService/UnsafeDeleteNamespace: post: tags: - policy.unsafe.UnsafeService - summary: UnsafeDeleteKasKey - description: |- - --------------------------------------* - Kas Key RPCs - --------------------------------------- - operationId: policy.unsafe.UnsafeService.UnsafeDeleteKasKey + summary: UnsafeDeleteNamespace + operationId: policy.unsafe.UnsafeService.UnsafeDeleteNamespace parameters: - name: Connect-Protocol-Version in: header @@ -96,7 +96,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.unsafe.UnsafeDeleteKasKeyRequest' + $ref: '#/components/schemas/policy.unsafe.UnsafeDeleteNamespaceRequest' required: true responses: default: @@ -110,13 +110,17 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.unsafe.UnsafeDeleteKasKeyResponse' - /policy.unsafe.UnsafeService/UnsafeDeleteNamespace: + $ref: '#/components/schemas/policy.unsafe.UnsafeDeleteNamespaceResponse' + /policy.unsafe.UnsafeService/UnsafeUpdateAttribute: post: tags: - policy.unsafe.UnsafeService - summary: UnsafeDeleteNamespace - operationId: policy.unsafe.UnsafeService.UnsafeDeleteNamespace + summary: UnsafeUpdateAttribute + description: |- + --------------------------------------* + Attribute RPCs + --------------------------------------- + operationId: policy.unsafe.UnsafeService.UnsafeUpdateAttribute parameters: - name: Connect-Protocol-Version in: header @@ -131,7 +135,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.unsafe.UnsafeDeleteNamespaceRequest' + $ref: '#/components/schemas/policy.unsafe.UnsafeUpdateAttributeRequest' required: true responses: default: @@ -145,7 +149,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.unsafe.UnsafeDeleteNamespaceResponse' + $ref: '#/components/schemas/policy.unsafe.UnsafeUpdateAttributeResponse' /policy.unsafe.UnsafeService/UnsafeReactivateAttribute: post: tags: @@ -181,12 +185,12 @@ paths: application/json: schema: $ref: '#/components/schemas/policy.unsafe.UnsafeReactivateAttributeResponse' - /policy.unsafe.UnsafeService/UnsafeReactivateAttributeValue: + /policy.unsafe.UnsafeService/UnsafeDeleteAttribute: post: tags: - policy.unsafe.UnsafeService - summary: UnsafeReactivateAttributeValue - operationId: policy.unsafe.UnsafeService.UnsafeReactivateAttributeValue + summary: UnsafeDeleteAttribute + operationId: policy.unsafe.UnsafeService.UnsafeDeleteAttribute parameters: - name: Connect-Protocol-Version in: header @@ -201,7 +205,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.unsafe.UnsafeReactivateAttributeValueRequest' + $ref: '#/components/schemas/policy.unsafe.UnsafeDeleteAttributeRequest' required: true responses: default: @@ -215,13 +219,17 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.unsafe.UnsafeReactivateAttributeValueResponse' - /policy.unsafe.UnsafeService/UnsafeReactivateNamespace: + $ref: '#/components/schemas/policy.unsafe.UnsafeDeleteAttributeResponse' + /policy.unsafe.UnsafeService/UnsafeUpdateAttributeValue: post: tags: - policy.unsafe.UnsafeService - summary: UnsafeReactivateNamespace - operationId: policy.unsafe.UnsafeService.UnsafeReactivateNamespace + summary: UnsafeUpdateAttributeValue + description: |- + --------------------------------------* + Value RPCs + --------------------------------------- + operationId: policy.unsafe.UnsafeService.UnsafeUpdateAttributeValue parameters: - name: Connect-Protocol-Version in: header @@ -236,7 +244,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.unsafe.UnsafeReactivateNamespaceRequest' + $ref: '#/components/schemas/policy.unsafe.UnsafeUpdateAttributeValueRequest' required: true responses: default: @@ -250,17 +258,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.unsafe.UnsafeReactivateNamespaceResponse' - /policy.unsafe.UnsafeService/UnsafeUpdateAttribute: + $ref: '#/components/schemas/policy.unsafe.UnsafeUpdateAttributeValueResponse' + /policy.unsafe.UnsafeService/UnsafeReactivateAttributeValue: post: tags: - policy.unsafe.UnsafeService - summary: UnsafeUpdateAttribute - description: |- - --------------------------------------* - Attribute RPCs - --------------------------------------- - operationId: policy.unsafe.UnsafeService.UnsafeUpdateAttribute + summary: UnsafeReactivateAttributeValue + operationId: policy.unsafe.UnsafeService.UnsafeReactivateAttributeValue parameters: - name: Connect-Protocol-Version in: header @@ -275,7 +279,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.unsafe.UnsafeUpdateAttributeRequest' + $ref: '#/components/schemas/policy.unsafe.UnsafeReactivateAttributeValueRequest' required: true responses: default: @@ -289,17 +293,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.unsafe.UnsafeUpdateAttributeResponse' - /policy.unsafe.UnsafeService/UnsafeUpdateAttributeValue: + $ref: '#/components/schemas/policy.unsafe.UnsafeReactivateAttributeValueResponse' + /policy.unsafe.UnsafeService/UnsafeDeleteAttributeValue: post: tags: - policy.unsafe.UnsafeService - summary: UnsafeUpdateAttributeValue - description: |- - --------------------------------------* - Value RPCs - --------------------------------------- - operationId: policy.unsafe.UnsafeService.UnsafeUpdateAttributeValue + summary: UnsafeDeleteAttributeValue + operationId: policy.unsafe.UnsafeService.UnsafeDeleteAttributeValue parameters: - name: Connect-Protocol-Version in: header @@ -314,7 +314,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.unsafe.UnsafeUpdateAttributeValueRequest' + $ref: '#/components/schemas/policy.unsafe.UnsafeDeleteAttributeValueRequest' required: true responses: default: @@ -328,17 +328,17 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.unsafe.UnsafeUpdateAttributeValueResponse' - /policy.unsafe.UnsafeService/UnsafeUpdateNamespace: + $ref: '#/components/schemas/policy.unsafe.UnsafeDeleteAttributeValueResponse' + /policy.unsafe.UnsafeService/UnsafeDeleteKasKey: post: tags: - policy.unsafe.UnsafeService - summary: UnsafeUpdateNamespace + summary: UnsafeDeleteKasKey description: |- --------------------------------------* - Namespace RPCs + Kas Key RPCs --------------------------------------- - operationId: policy.unsafe.UnsafeService.UnsafeUpdateNamespace + operationId: policy.unsafe.UnsafeService.UnsafeDeleteKasKey parameters: - name: Connect-Protocol-Version in: header @@ -353,7 +353,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.unsafe.UnsafeUpdateNamespaceRequest' + $ref: '#/components/schemas/policy.unsafe.UnsafeDeleteKasKeyRequest' required: true responses: default: @@ -367,9 +367,99 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/policy.unsafe.UnsafeUpdateNamespaceResponse' + $ref: '#/components/schemas/policy.unsafe.UnsafeDeleteKasKeyResponse' components: schemas: + policy.Action.StandardAction: + type: string + title: StandardAction + enum: + - STANDARD_ACTION_UNSPECIFIED + - STANDARD_ACTION_DECRYPT + - STANDARD_ACTION_TRANSMIT + policy.Algorithm: + type: string + title: Algorithm + enum: + - ALGORITHM_UNSPECIFIED + - 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 + description: Supported key algorithms. + policy.AttributeRuleTypeEnum: + type: string + title: AttributeRuleTypeEnum + enum: + - ATTRIBUTE_RULE_TYPE_ENUM_UNSPECIFIED + - ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF + - ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF + - ATTRIBUTE_RULE_TYPE_ENUM_HIERARCHY + policy.ConditionBooleanTypeEnum: + type: string + title: ConditionBooleanTypeEnum + enum: + - CONDITION_BOOLEAN_TYPE_ENUM_UNSPECIFIED + - CONDITION_BOOLEAN_TYPE_ENUM_AND + - CONDITION_BOOLEAN_TYPE_ENUM_OR + policy.KasPublicKeyAlgEnum: + type: string + title: KasPublicKeyAlgEnum + enum: + - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED + - KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048 + - KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 + - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 + - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 + policy.KeyMode: + type: string + title: KeyMode + enum: + - KEY_MODE_UNSPECIFIED + - KEY_MODE_CONFIG_ROOT_KEY + - KEY_MODE_PROVIDER_ROOT_KEY + - KEY_MODE_REMOTE + - KEY_MODE_PUBLIC_KEY_ONLY + description: Describes the management and operational mode of a cryptographic key. + policy.KeyStatus: + type: string + title: KeyStatus + enum: + - KEY_STATUS_UNSPECIFIED + - KEY_STATUS_ACTIVE + - KEY_STATUS_ROTATED + description: The status of the key + policy.SourceType: + type: string + title: SourceType + enum: + - SOURCE_TYPE_UNSPECIFIED + - SOURCE_TYPE_INTERNAL + - SOURCE_TYPE_EXTERNAL + description: |- + Describes whether this kas is managed by the organization or if they imported + the kas information from an external party. These two modes are necessary in order + to encrypt a tdf dek with an external parties kas public key. + policy.SubjectMappingOperatorEnum: + type: string + title: SubjectMappingOperatorEnum + enum: + - SUBJECT_MAPPING_OPERATOR_ENUM_UNSPECIFIED + - SUBJECT_MAPPING_OPERATOR_ENUM_IN + - SUBJECT_MAPPING_OPERATOR_ENUM_NOT_IN + - SUBJECT_MAPPING_OPERATOR_ENUM_IN_CONTAINS common.Metadata: type: object properties: @@ -402,75 +492,6 @@ components: title: value title: LabelsEntry additionalProperties: false - connect-protocol-version: - type: number - title: Connect-Protocol-Version - enum: - - 1 - description: Define the version of the Connect protocol - const: 1 - connect-timeout-header: - type: number - title: Connect-Timeout-Ms - description: Define the timeout, in ms - connect.error: - type: object - properties: - code: - type: string - examples: - - not_found - enum: - - canceled - - unknown - - invalid_argument - - deadline_exceeded - - not_found - - already_exists - - permission_denied - - resource_exhausted - - failed_precondition - - aborted - - out_of_range - - unimplemented - - internal - - unavailable - - data_loss - - unauthenticated - description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. - message: - type: string - description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. - details: - type: array - items: - $ref: '#/components/schemas/connect.error_details.Any' - description: A list of messages that carry the error details. There is no limit on the number of messages. - title: Connect Error - additionalProperties: true - description: 'Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation' - connect.error_details.Any: - type: object - properties: - type: - type: string - description: 'A URL that acts as a globally unique identifier for the type of the serialized message. For example: `type.googleapis.com/google.rpc.ErrorInfo`. This is used to determine the schema of the data in the `value` field and is the discriminator for the `debug` field.' - value: - type: string - format: binary - description: The Protobuf message, serialized as bytes and base64-encoded. The specific message type is identified by the `type` field. - debug: - oneOf: - - type: object - title: Any - additionalProperties: true - description: Detailed error information. - discriminator: - propertyName: type - title: Debug - description: Deserialized error detail payload. The 'type' field indicates the schema. This field is for easier debugging and should not be relied upon for application logic. - additionalProperties: true - description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message, with an additional debug field for ConnectRPC error details. google.protobuf.BoolValue: type: boolean description: |- @@ -483,8 +504,8 @@ components: google.protobuf.Timestamp: type: string examples: - - "2023-01-15T01:30:15.01Z" - - "2024-12-25T12:00:00Z" + - 1s + - 1.000340012s format: date-time description: |- A Timestamp represents a point in time independent of any time zone or local @@ -578,65 +599,41 @@ components: ) to obtain a formatter capable of generating timestamps in this format. policy.Action: type: object - allOf: + oneOf: - properties: - id: + custom: type: string - title: id - description: Generated uuid in database - name: - type: string - title: name - namespace: - title: namespace - description: Namespace context for this action - $ref: '#/components/schemas/policy.Namespace' - metadata: - title: metadata - $ref: '#/components/schemas/common.Metadata' - - oneOf: - - type: object - properties: - custom: - type: string - title: custom - description: Deprecated title: custom - required: - - custom - - type: object - properties: - standard: - title: standard - description: Deprecated - $ref: '#/components/schemas/policy.Action.StandardAction' + description: Deprecated + title: custom + required: + - custom + - properties: + standard: title: standard - required: - - standard + description: Deprecated + $ref: '#/components/schemas/policy.Action.StandardAction' + title: standard + required: + - standard + properties: + id: + type: string + title: id + description: Generated uuid in database + name: + type: string + title: name + namespace: + title: namespace + description: Namespace context for this action + $ref: '#/components/schemas/policy.Namespace' + metadata: + title: metadata + $ref: '#/components/schemas/common.Metadata' title: Action additionalProperties: false description: An action an entity can take - policy.Action.StandardAction: - type: string - title: StandardAction - enum: - - STANDARD_ACTION_UNSPECIFIED - - STANDARD_ACTION_DECRYPT - - STANDARD_ACTION_TRANSMIT - policy.Algorithm: - type: string - title: Algorithm - enum: - - ALGORITHM_UNSPECIFIED - - 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 - description: Supported key algorithms. policy.AsymmetricKey: type: object properties: @@ -738,14 +735,6 @@ components: required: - rule additionalProperties: false - policy.AttributeRuleTypeEnum: - type: string - title: AttributeRuleTypeEnum - enum: - - ATTRIBUTE_RULE_TYPE_ENUM_UNSPECIFIED - - ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF - - ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF - - ATTRIBUTE_RULE_TYPE_ENUM_HIERARCHY policy.Condition: type: object properties: @@ -763,6 +752,7 @@ components: type: array items: type: string + minItems: 1 title: subject_external_values minItems: 1 description: |- @@ -778,13 +768,6 @@ components: * A Condition defines a rule of - policy.ConditionBooleanTypeEnum: - type: string - title: ConditionBooleanTypeEnum - enum: - - CONDITION_BOOLEAN_TYPE_ENUM_UNSPECIFIED - - CONDITION_BOOLEAN_TYPE_ENUM_AND - - CONDITION_BOOLEAN_TYPE_ENUM_OR policy.ConditionGroup: type: object properties: @@ -835,7 +818,7 @@ components: alg: not: enum: - - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED + - 0 title: alg description: |- A known algorithm type with any additional parameters encoded. @@ -847,19 +830,6 @@ components: description: |- Deprecated A KAS public key and some associated metadata for further identifcation - policy.KasPublicKeyAlgEnum: - type: string - title: KasPublicKeyAlgEnum - enum: - - KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED - - KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048 - - KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 - - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_XWING - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP256R1_MLKEM768 - - KAS_PUBLIC_KEY_ALG_ENUM_HPQT_SECP384R1_MLKEM1024 policy.KasPublicKeySet: type: object properties: @@ -882,9 +852,13 @@ components: uri: type: string title: uri - description: | + description: |+ Address of a KAS instance - uri_format // URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes. + URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes.: + ``` + this.matches('^https?://[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?)*(:[0-9]+)?(/.*)?$') + ``` + publicKey: title: public_key description: 'Deprecated: KAS can have multiple key pairs' @@ -912,16 +886,6 @@ components: title: KeyAccessServer additionalProperties: false description: Key Access Server Registry - policy.KeyMode: - type: string - title: KeyMode - enum: - - KEY_MODE_UNSPECIFIED - - KEY_MODE_CONFIG_ROOT_KEY - - KEY_MODE_PROVIDER_ROOT_KEY - - KEY_MODE_REMOTE - - KEY_MODE_PUBLIC_KEY_ONLY - description: Describes the management and operational mode of a cryptographic key. policy.KeyProviderConfig: type: object properties: @@ -944,14 +908,6 @@ components: $ref: '#/components/schemas/common.Metadata' title: KeyProviderConfig additionalProperties: false - policy.KeyStatus: - type: string - title: KeyStatus - enum: - - KEY_STATUS_UNSPECIFIED - - KEY_STATUS_ACTIVE - - KEY_STATUS_ROTATED - description: The status of the key policy.Namespace: type: object properties: @@ -1094,8 +1050,7 @@ components: policy.PublicKey: type: object oneOf: - - type: object - properties: + - properties: cached: title: cached description: public key with additional information. Current preferred version @@ -1103,14 +1058,17 @@ components: title: cached required: - cached - - type: object - properties: + - properties: remote: type: string title: remote - description: | + description: |+ kas public key url - optional since can also be retrieved via public key - uri_format // URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes. + URI must be a valid URL (e.g., 'https://demo.com/') followed by additional segments. Each segment must start and end with an alphanumeric character, can contain hyphens, alphanumeric characters, and slashes.: + ``` + this.matches('^https://[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?(\\.[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?)*(/.*)?$') + ``` + title: remote required: - remote @@ -1228,17 +1186,6 @@ components: title: pem title: SimpleKasPublicKey additionalProperties: false - policy.SourceType: - type: string - title: SourceType - enum: - - SOURCE_TYPE_UNSPECIFIED - - SOURCE_TYPE_INTERNAL - - SOURCE_TYPE_EXTERNAL - description: |- - Describes whether this kas is managed by the organization or if they imported - the kas information from an external party. These two modes are necessary in order - to encrypt a tdf dek with an external parties kas public key. policy.SubjectConditionSet: type: object properties: @@ -1304,14 +1251,6 @@ components: description: |- Subject Mapping: A Policy assigning Subject Set(s) to a permitted attribute value + action(s) combination - policy.SubjectMappingOperatorEnum: - type: string - title: SubjectMappingOperatorEnum - enum: - - SUBJECT_MAPPING_OPERATOR_ENUM_UNSPECIFIED - - SUBJECT_MAPPING_OPERATOR_ENUM_IN - - SUBJECT_MAPPING_OPERATOR_ENUM_NOT_IN - - SUBJECT_MAPPING_OPERATOR_ENUM_IN_CONTAINS policy.SubjectSet: type: object properties: @@ -1590,11 +1529,15 @@ components: type: string title: name maxLength: 253 - description: | + description: |+ Optional WARNING!! Updating the name of an Attribute will retroactively alter access to existing TDFs of the old and new Attribute name. - attribute_name_format // Attribute name must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored attribute name will be normalized to lower case. + Attribute name must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored attribute name will be normalized to lower case.: + ``` + size(this) > 0 ? this.matches('^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$') : true + ``` + rule: title: rule description: |- @@ -1647,9 +1590,13 @@ components: type: string title: value maxLength: 253 - description: | + description: |+ Required - value_format // Attribute Value must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored attribute value will be normalized to lower case. + Attribute Value must be an alphanumeric string, allowing hyphens and underscores but not as the first or last character. The stored attribute value will be normalized to lower case.: + ``` + this.matches('^[a-zA-Z0-9](?:[a-zA-Z0-9_-]*[a-zA-Z0-9])?$') + ``` + title: UnsafeUpdateAttributeValueRequest additionalProperties: false description: |- @@ -1675,9 +1622,13 @@ components: type: string title: name maxLength: 253 - description: | + description: |+ Required - namespace_name_format // Namespace must be a valid hostname. It should include at least one dot, with each segment (label) starting and ending with an alphanumeric character. Each label must be 1 to 63 characters long, allowing hyphens but not as the first or last character. The top-level domain (the last segment after the final dot) must consist of at least two alphabetic characters. The stored namespace will be normalized to lower case. + Namespace must be a valid hostname. It should include at least one dot, with each segment (label) starting and ending with an alphanumeric character. Each label must be 1 to 63 characters long, allowing hyphens but not as the first or last character. The top-level domain (the last segment after the final dot) must consist of at least two alphabetic characters. The stored namespace will be normalized to lower case.: + ``` + this.matches('^([a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,}$') + ``` + title: UnsafeUpdateNamespaceRequest additionalProperties: false description: |- @@ -1692,6 +1643,63 @@ components: $ref: '#/components/schemas/policy.Namespace' title: UnsafeUpdateNamespaceResponse additionalProperties: false + connect-protocol-version: + type: number + title: Connect-Protocol-Version + enum: + - 1 + description: Define the version of the Connect protocol + const: 1 + connect-timeout-header: + type: number + title: Connect-Timeout-Ms + description: Define the timeout, in ms + connect.error: + type: object + properties: + code: + type: string + examples: + - not_found + enum: + - canceled + - unknown + - invalid_argument + - deadline_exceeded + - not_found + - already_exists + - permission_denied + - resource_exhausted + - failed_precondition + - aborted + - out_of_range + - unimplemented + - internal + - unavailable + - data_loss + - unauthenticated + description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. + message: + type: string + description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. + detail: + $ref: '#/components/schemas/google.protobuf.Any' + title: Connect Error + additionalProperties: true + description: 'Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation' + google.protobuf.Any: + type: object + properties: + type: + type: string + value: + type: string + format: binary + debug: + type: object + additionalProperties: true + additionalProperties: true + description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message. security: [] tags: - name: policy.unsafe.UnsafeService diff --git a/docs/openapi/wellknownconfiguration/wellknown_configuration.openapi.yaml b/docs/openapi/wellknownconfiguration/wellknown_configuration.openapi.yaml index 203cf0b27e..ad18ddfa64 100644 --- a/docs/openapi/wellknownconfiguration/wellknown_configuration.openapi.yaml +++ b/docs/openapi/wellknownconfiguration/wellknown_configuration.openapi.yaml @@ -39,75 +39,16 @@ paths: $ref: '#/components/schemas/wellknownconfiguration.GetWellKnownConfigurationResponse' components: schemas: - connect-protocol-version: - type: number - title: Connect-Protocol-Version + google.protobuf.NullValue: + type: string + title: NullValue enum: - - 1 - description: Define the version of the Connect protocol - const: 1 - connect-timeout-header: - type: number - title: Connect-Timeout-Ms - description: Define the timeout, in ms - connect.error: - type: object - properties: - code: - type: string - examples: - - not_found - enum: - - canceled - - unknown - - invalid_argument - - deadline_exceeded - - not_found - - already_exists - - permission_denied - - resource_exhausted - - failed_precondition - - aborted - - out_of_range - - unimplemented - - internal - - unavailable - - data_loss - - unauthenticated - description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. - message: - type: string - description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. - details: - type: array - items: - $ref: '#/components/schemas/connect.error_details.Any' - description: A list of messages that carry the error details. There is no limit on the number of messages. - title: Connect Error - additionalProperties: true - description: 'Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation' - connect.error_details.Any: - type: object - properties: - type: - type: string - description: 'A URL that acts as a globally unique identifier for the type of the serialized message. For example: `type.googleapis.com/google.rpc.ErrorInfo`. This is used to determine the schema of the data in the `value` field and is the discriminator for the `debug` field.' - value: - type: string - format: binary - description: The Protobuf message, serialized as bytes and base64-encoded. The specific message type is identified by the `type` field. - debug: - oneOf: - - type: object - title: Any - additionalProperties: true - description: Detailed error information. - discriminator: - propertyName: type - title: Debug - description: Deserialized error detail payload. The 'type' field indicates the schema. This field is for easier debugging and should not be relied upon for application logic. - additionalProperties: true - description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message, with an additional debug field for ConnectRPC error details. + - NULL_VALUE + description: |- + `NullValue` is a singleton enumeration to represent the null value for the + `Value` type union. + + The JSON representation for `NullValue` is JSON `null`. google.protobuf.ListValue: type: object properties: @@ -123,16 +64,6 @@ components: `ListValue` is a wrapper around a repeated field of values. The JSON representation for `ListValue` is JSON array. - google.protobuf.NullValue: - type: string - title: NullValue - enum: - - NULL_VALUE - description: |- - `NullValue` is a singleton enumeration to represent the null value for the - `Value` type union. - - The JSON representation for `NullValue` is JSON `null`. google.protobuf.Struct: type: object additionalProperties: @@ -207,6 +138,63 @@ components: $ref: '#/components/schemas/google.protobuf.Struct' title: ConfigurationEntry additionalProperties: false + connect-protocol-version: + type: number + title: Connect-Protocol-Version + enum: + - 1 + description: Define the version of the Connect protocol + const: 1 + connect-timeout-header: + type: number + title: Connect-Timeout-Ms + description: Define the timeout, in ms + connect.error: + type: object + properties: + code: + type: string + examples: + - not_found + enum: + - canceled + - unknown + - invalid_argument + - deadline_exceeded + - not_found + - already_exists + - permission_denied + - resource_exhausted + - failed_precondition + - aborted + - out_of_range + - unimplemented + - internal + - unavailable + - data_loss + - unauthenticated + description: The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. + message: + type: string + description: A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. + detail: + $ref: '#/components/schemas/google.protobuf.Any' + title: Connect Error + additionalProperties: true + description: 'Error type returned by Connect: https://connectrpc.com/docs/go/errors/#http-representation' + google.protobuf.Any: + type: object + properties: + type: + type: string + value: + type: string + format: binary + debug: + type: object + additionalProperties: true + additionalProperties: true + description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message. security: [] tags: - name: wellknownconfiguration.WellKnownService diff --git a/lib/ocrypto/ec_key_pair.go b/lib/ocrypto/ec_key_pair.go index 70e30cc8df..e0b9aefecd 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: @@ -509,3 +525,85 @@ 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") + } + + privateKeyPEM := pem.EncodeToMemory( + &pem.Block{ + Type: "MLKEM DECAPSULATION KEY", + Bytes: keyPair.PrivateKey.Bytes(), + }, + ) + return string(privateKeyPEM), nil +} + +func (keyPair MLKEMKeyPair) PublicKeyInPemFormat() (string, error) { + if keyPair.PrivateKey == nil { + return "", errors.New("failed to generate PEM formatted public key") + } + + publicKeyPEM := pem.EncodeToMemory( + &pem.Block{ + Type: "MLKEM ENCAPSULATOR", + Bytes: keyPair.PrivateKey.EncapsulationKey().Bytes(), + }, + ) + return string(publicKeyPEM), 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") + } + + privateKeyPEM := pem.EncodeToMemory( + &pem.Block{ + Type: "MLKEM DECAPSULATION KEY", + Bytes: keyPair.PrivateKey.Bytes(), + }, + ) + return string(privateKeyPEM), nil +} + +func (keyPair MLKEM1024KeyPair) PublicKeyInPemFormat() (string, error) { + if keyPair.PrivateKey == nil { + return "", errors.New("failed to generate PEM formatted public key") + } + + publicKeyPEM := pem.EncodeToMemory( + &pem.Block{ + Type: "MLKEM ENCAPSULATOR", + Bytes: keyPair.PrivateKey.EncapsulationKey().Bytes(), + }, + ) + return string(publicKeyPEM), nil +} + +func (keyPair MLKEM1024KeyPair) GetKeyType() KeyType { + return MLKEM1024Key +} 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 5bb3c2a08b..676874d83c 100644 --- a/protocol/go/policy/kasregistry/key_access_server_registry.pb.go +++ b/protocol/go/policy/kasregistry/key_access_server_registry.pb.go @@ -4277,223 +4277,224 @@ var file_policy_kasregistry_key_access_server_registry_proto_rawDesc = []byte{ 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x02, 0x18, 0x01, 0x22, - 0xd5, 0x0c, 0x0a, 0x10, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, + 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, 0x0a, 0x0d, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, + 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, 0x0a, 0x15, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, + 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, 0x68, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x5b, + 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, + 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, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x61, 0x6c, 0x6c, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0xa8, 0x01, + 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x20, + 0x69, 0x64, 0x20, 0x69, 0x73, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x69, + 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, + 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, 0x52, 0x5f, + 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x20, 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, + 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x2e, 0x20, 0x49, 0x74, 0x20, + 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x66, 0x6f, + 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, + 0x47, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x4b, + 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, + 0x45, 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x2e, 0x1a, 0x9e, 0x01, 0x28, 0x28, 0x74, 0x68, 0x69, + 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x20, + 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, + 0x20, 0x3d, 0x3d, 0x20, 0x34, 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, - 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x5f, 0x72, 0x65, 0x71, - 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0xa8, 0x01, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x20, 0x69, 0x64, 0x20, 0x69, 0x73, 0x20, 0x72, 0x65, - 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, - 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, - 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, 0x52, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, - 0x20, 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, 0x4d, - 0x4f, 0x54, 0x45, 0x2e, 0x20, 0x49, 0x74, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, - 0x65, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, - 0x44, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, - 0x45, 0x59, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, - 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x2e, - 0x1a, 0x9e, 0x01, 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, - 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, - 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x34, 0x29, 0x20, 0x26, - 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x27, 0x29, - 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, - 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, - 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x33, 0x29, 0x20, - 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x27, - 0x29, 0x1a, 0xa3, 0x01, 0x0a, 0x23, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, - 0x79, 0x5f, 0x63, 0x74, 0x78, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, - 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x12, 0x48, 0x70, 0x72, 0x69, 0x76, 0x61, - 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, - 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x74, 0x20, 0x69, 0x66, 0x20, 0x6b, 0x65, - 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, - 0x44, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x4e, - 0x4c, 0x59, 0x2e, 0x1a, 0x32, 0x21, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, - 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x34, 0x20, 0x26, 0x26, 0x20, 0x68, 0x61, 0x73, - 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, - 0x79, 0x5f, 0x63, 0x74, 0x78, 0x29, 0x29, 0x22, 0x3c, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x07, - 0x6b, 0x61, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, - 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x52, 0x06, 0x6b, - 0x61, 0x73, 0x4b, 0x65, 0x79, 0x22, 0x7a, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x02, - 0x69, 0x64, 0x12, 0x38, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x24, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, - 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x66, 0x69, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x42, 0x13, 0x0a, 0x0a, - 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, - 0x01, 0x22, 0x39, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x07, 0x6b, 0x61, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x61, - 0x73, 0x4b, 0x65, 0x79, 0x52, 0x06, 0x6b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x22, 0xde, 0x03, 0x0a, - 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0xb0, 0x01, 0x0a, 0x0d, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, - 0x68, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x2e, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x42, 0x78, 0xba, 0x48, 0x75, - 0xba, 0x01, 0x72, 0x0a, 0x15, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, - 0x68, 0x6d, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x34, 0x54, 0x68, 0x65, 0x20, - 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x20, 0x6d, 0x75, - 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, - 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, - 0x1a, 0x23, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x5b, 0x30, 0x2c, 0x20, 0x31, 0x2c, - 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x36, 0x2c, 0x20, - 0x37, 0x2c, 0x20, 0x38, 0x5d, 0x52, 0x0c, 0x6b, 0x65, 0x79, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, - 0x74, 0x68, 0x6d, 0x12, 0x21, 0x0a, 0x06, 0x6b, 0x61, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, - 0x05, 0x6b, 0x61, 0x73, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x08, 0x6b, 0x61, 0x73, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, - 0x01, 0x48, 0x00, 0x52, 0x07, 0x6b, 0x61, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x07, - 0x6b, 0x61, 0x73, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, - 0x48, 0x07, 0x72, 0x05, 0x10, 0x01, 0x88, 0x01, 0x01, 0x48, 0x00, 0x52, 0x06, 0x6b, 0x61, 0x73, - 0x55, 0x72, 0x69, 0x12, 0x1b, 0x0a, 0x06, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x08, 0x48, 0x01, 0x52, 0x06, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x88, 0x01, 0x01, - 0x12, 0x33, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x61, - 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3d, 0x0a, 0x04, 0x73, 0x6f, 0x72, 0x74, 0x18, 0x0b, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, - 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x73, - 0x53, 0x6f, 0x72, 0x74, 0x42, 0x08, 0xba, 0x48, 0x05, 0x92, 0x01, 0x02, 0x10, 0x01, 0x52, 0x04, - 0x73, 0x6f, 0x72, 0x74, 0x42, 0x0c, 0x0a, 0x0a, 0x6b, 0x61, 0x73, 0x5f, 0x66, 0x69, 0x6c, 0x74, - 0x65, 0x72, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x22, 0x73, 0x0a, - 0x10, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x29, 0x0a, 0x08, 0x6b, 0x61, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x61, 0x73, - 0x4b, 0x65, 0x79, 0x52, 0x07, 0x6b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x34, 0x0a, 0x0a, - 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x22, 0x86, 0x03, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x02, 0x69, - 0x64, 0x12, 0x33, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x64, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x4d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x08, 0x6d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x54, 0x0a, 0x18, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, - 0x6f, 0x72, 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, + 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, 0xde, 0x03, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0xb0, 0x01, 0x0a, 0x0d, 0x6b, 0x65, 0x79, + 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, - 0x74, 0x68, 0x6d, 0x42, 0x75, 0xba, 0x48, 0x72, 0xba, 0x01, 0x6f, 0x0a, 0x15, 0x6b, 0x65, 0x79, + 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, 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, + 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x1a, 0x23, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, + 0x6e, 0x20, 0x5b, 0x30, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x34, + 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x36, 0x2c, 0x20, 0x37, 0x2c, 0x20, 0x38, 0x5d, 0x52, 0x0c, 0x6b, + 0x65, 0x79, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x21, 0x0a, 0x06, 0x6b, + 0x61, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, + 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x05, 0x6b, 0x61, 0x73, 0x49, 0x64, 0x12, 0x24, + 0x0a, 0x08, 0x6b, 0x61, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x48, 0x00, 0x52, 0x07, 0x6b, 0x61, 0x73, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x07, 0x6b, 0x61, 0x73, 0x5f, 0x75, 0x72, 0x69, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0x72, 0x05, 0x10, 0x01, 0x88, 0x01, + 0x01, 0x48, 0x00, 0x52, 0x06, 0x6b, 0x61, 0x73, 0x55, 0x72, 0x69, 0x12, 0x1b, 0x0a, 0x06, 0x6c, + 0x65, 0x67, 0x61, 0x63, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x48, 0x01, 0x52, 0x06, 0x6c, + 0x65, 0x67, 0x61, 0x63, 0x79, 0x88, 0x01, 0x01, 0x12, 0x33, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, + 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3d, 0x0a, + 0x04, 0x73, 0x6f, 0x72, 0x74, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, + 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x42, 0x08, 0xba, 0x48, + 0x05, 0x92, 0x01, 0x02, 0x10, 0x01, 0x52, 0x04, 0x73, 0x6f, 0x72, 0x74, 0x42, 0x0c, 0x0a, 0x0a, + 0x6b, 0x61, 0x73, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6c, + 0x65, 0x67, 0x61, 0x63, 0x79, 0x22, 0x73, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x08, 0x6b, 0x61, 0x73, + 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x52, 0x07, 0x6b, 0x61, 0x73, + 0x4b, 0x65, 0x79, 0x73, 0x12, 0x34, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0a, + 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x86, 0x03, 0x0a, 0x10, 0x55, + 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x18, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, + 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0x33, 0x0a, 0x08, 0x6d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, + 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4d, 0x75, 0x74, + 0x61, 0x62, 0x6c, 0x65, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x54, + 0x0a, 0x18, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x5f, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 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, diff --git a/protocol/go/policy/objects.pb.go b/protocol/go/policy/objects.pb.go index b9eda84a37..bc9962ba32 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, } ) @@ -3746,7 +3758,7 @@ var file_policy_objects_proto_rawDesc = []byte{ 0x00, 0x12, 0x18, 0x0a, 0x14, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x58, 0x54, 0x45, 0x52, - 0x4e, 0x41, 0x4c, 0x10, 0x02, 0x2a, 0x9b, 0x03, 0x0a, 0x13, 0x4b, 0x61, 0x73, 0x50, 0x75, 0x62, + 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, 0x49, @@ -3772,47 +3784,55 @@ var file_policy_objects_proto_rawDesc = []byte{ 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, + 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/service/policy/kasregistry/key_access_server_registry.proto b/service/policy/kasregistry/key_access_server_registry.proto index 5d0e805f7e..61fec5965e 100644 --- a/service/policy/kasregistry/key_access_server_registry.proto +++ b/service/policy/kasregistry/key_access_server_registry.proto @@ -433,7 +433,7 @@ message CreateKeyRequest { Algorithm key_algorithm = 3 [(buf.validate.field).cel = { id: "key_algorithm_defined" message: "The key_algorithm must be one of the defined values." - expression: "this in [1, 2, 3, 4, 5, 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 = { @@ -587,7 +587,7 @@ message RotateKeyRequest { Algorithm algorithm = 2 [(buf.validate.field).cel = { id: "key_algorithm_defined" message: "The key_algorithm must be one of the defined values." - expression: "this in [1, 2, 3, 4, 5, 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 3e6ee4d794..323b30a281 100644 --- a/service/policy/objects.proto +++ b/service/policy/objects.proto @@ -394,6 +394,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 @@ -567,6 +569,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 From 6d7dc711526da27ec9bc72a82961125a697fde56 Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Wed, 27 May 2026 15:04:12 -0400 Subject: [PATCH 02/23] feat(crypto): add ML-KEM encryption and decryption Implement complete ML-KEM 768/1024 encryption and decryption support in the ocrypto library. Encryption changes (asym_encryption.go): - Add MLKEM SchemeType constant - Add MLKEMEncryptor768 and MLKEMEncryptor1024 types - Implement newMLKEM768/newMLKEM1024 constructors - Handle mlkem.EncapsulationKey in FromPublicPEMWithSalt - Implement all PublicKeyEncryptor interface methods - Use AES-GCM with shared secret from KEM encapsulation Decryption changes (asym_decryption.go): - Add DecryptWithEphemeralKey method to interface - Add MLKEMDecryptor768 and MLKEMDecryptor1024 types - Handle "MLKEM DECAPSULATION KEY" PEM blocks - Implement Decrypt and DecryptWithEphemeralKey methods - Use AES-GCM with shared secret from KEM decapsulation Also updated existing decryptors: - Add DecryptWithEphemeralKey to AsymDecryption (RSA) - Add DecryptWithEphemeralKey to ECDecryptor (ECDH) - Add DecryptWithEphemeralKey to XWingDecryptor (hybrid) - Add DecryptWithEphemeralKey to HybridNISTDecryptor (hybrid) All lib/ocrypto tests pass. Co-Authored-By: Claude Sonnet 4.5 --- lib/ocrypto/asym_decryption.go | 113 ++++++++++++++++++++++++++++++++ lib/ocrypto/asym_encryption.go | 114 +++++++++++++++++++++++++++++++++ lib/ocrypto/hybrid_nist.go | 8 +++ lib/ocrypto/xwing.go | 8 +++ 4 files changed, 243 insertions(+) diff --git a/lib/ocrypto/asym_decryption.go b/lib/ocrypto/asym_decryption.go index 1cbfbfc943..e241ed391c 100644 --- a/lib/ocrypto/asym_decryption.go +++ b/lib/ocrypto/asym_decryption.go @@ -7,6 +7,7 @@ import ( "crypto/ecdh" "crypto/ecdsa" "crypto/elliptic" + "crypto/mlkem" "crypto/rsa" "crypto/sha256" "crypto/x509" @@ -26,6 +27,17 @@ type AsymDecryption struct { type PrivateKeyDecryptor interface { // Decrypt decrypts ciphertext with private key. Decrypt(data []byte) ([]byte, error) + + // DecryptWithEphemeralKey decrypts ciphertext using additional sender material. + DecryptWithEphemeralKey(data, ephemeral []byte) ([]byte, error) +} + +type MLKEMDecryptor768 struct { + decap *mlkem.DecapsulationKey768 +} + +type MLKEMDecryptor1024 struct { + decap *mlkem.DecapsulationKey1024 } // FromPrivatePEM creates and returns a new AsymDecryption. @@ -52,6 +64,18 @@ func FromPrivatePEMWithSalt(privateKeyInPem string, salt, info []byte) (PrivateK return NewSaltedP384MLKEM1024Decryptor(block.Bytes, salt, info) } + if block.Type == "MLKEM DECAPSULATION KEY" { + decap768, err := mlkem.NewDecapsulationKey768(block.Bytes) + if err == nil { + return &MLKEMDecryptor768{decap: decap768}, nil + } + decap1024, err1024 := mlkem.NewDecapsulationKey1024(block.Bytes) + if err1024 != nil { + return nil, fmt.Errorf("mlkem.NewDecapsulationKey1024 failed after mlkem.NewDecapsulationKey768 failed: %w / %w", err, err1024) + } + return &MLKEMDecryptor1024{decap: decap1024}, nil + } + priv, err := x509.ParsePKCS8PrivateKey(block.Bytes) switch { case err == nil: @@ -117,6 +141,13 @@ func (asymDecryption AsymDecryption) Decrypt(data []byte) ([]byte, error) { return bytes, nil } +func (asymDecryption AsymDecryption) DecryptWithEphemeralKey(data, ephemeral []byte) ([]byte, error) { + if len(ephemeral) > 0 { + return nil, errors.New("ephemeral key is not supported for RSA decryption") + } + return asymDecryption.Decrypt(data) +} + type ECDecryptor struct { sk *ecdh.PrivateKey salt []byte @@ -214,3 +245,85 @@ func convCurve(c ecdh.Curve) elliptic.Curve { return nil } } + +func (d MLKEMDecryptor768) Decrypt(_ []byte) ([]byte, error) { + return nil, errors.New("ciphertext encapsulation is required for ML-KEM decryption") +} + +func (d MLKEMDecryptor1024) Decrypt(_ []byte) ([]byte, error) { + return nil, errors.New("ciphertext encapsulation is required for ML-KEM decryption") +} + +func (d MLKEMDecryptor768) DecryptWithEphemeralKey(data, ephemeral []byte) ([]byte, error) { + if d.decap == nil { + return nil, errors.New("mlkem decapsulation key is nil") + } + if len(ephemeral) == 0 { + return nil, errors.New("ciphertext encapsulation is required for ML-KEM decryption") + } + + sharedSecret, err := d.decap.Decapsulate(ephemeral) + if err != nil { + return nil, fmt.Errorf("mlkem.Decapsulate failed: %w", err) + } + + block, err := aes.NewCipher(sharedSecret) + if err != nil { + return nil, fmt.Errorf("aes.NewCipher failure: %w", err) + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, fmt.Errorf("cipher.NewGCM failure: %w", err) + } + + nonceSize := gcm.NonceSize() + if len(data) < nonceSize { + return nil, errors.New("ciphertext too short") + } + + nonce, ciphertext := data[:nonceSize], data[nonceSize:] + plaintext, err := gcm.Open(nil, nonce, ciphertext, nil) + if err != nil { + return nil, fmt.Errorf("gcm.Open failure: %w", err) + } + + return plaintext, nil +} + +func (d MLKEMDecryptor1024) DecryptWithEphemeralKey(data, ephemeral []byte) ([]byte, error) { + if d.decap == nil { + return nil, errors.New("mlkem decapsulation key is nil") + } + if len(ephemeral) == 0 { + return nil, errors.New("ciphertext encapsulation is required for ML-KEM decryption") + } + + sharedSecret, err := d.decap.Decapsulate(ephemeral) + if err != nil { + return nil, fmt.Errorf("mlkem.Decapsulate failed: %w", err) + } + + block, err := aes.NewCipher(sharedSecret) + if err != nil { + return nil, fmt.Errorf("aes.NewCipher failure: %w", err) + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, fmt.Errorf("cipher.NewGCM failure: %w", err) + } + + nonceSize := gcm.NonceSize() + if len(data) < nonceSize { + return nil, errors.New("ciphertext too short") + } + + nonce, ciphertext := data[:nonceSize], data[nonceSize:] + plaintext, err := gcm.Open(nil, nonce, ciphertext, nil) + if err != nil { + return nil, fmt.Errorf("gcm.Open failure: %w", err) + } + + return plaintext, nil +} diff --git a/lib/ocrypto/asym_encryption.go b/lib/ocrypto/asym_encryption.go index 2a030c3f32..919913348b 100644 --- a/lib/ocrypto/asym_encryption.go +++ b/lib/ocrypto/asym_encryption.go @@ -5,6 +5,7 @@ import ( "crypto/cipher" "crypto/ecdh" "crypto/ecdsa" + "crypto/mlkem" "crypto/rand" "crypto/rsa" "crypto/sha1" //nolint:gosec // used for padding which is safe @@ -26,6 +27,7 @@ const ( RSA SchemeType = "wrapped" EC SchemeType = "ec-wrapped" Hybrid SchemeType = "hybrid-wrapped" + MLKEM SchemeType = "mlkem-wrapped" ) type PublicKeyEncryptor interface { @@ -60,6 +62,18 @@ type ECEncryptor struct { info []byte } +type MLKEMEncryptor768 struct { + pub *mlkem.EncapsulationKey768 + cipherText []byte + sharedSecret []byte +} + +type MLKEMEncryptor1024 struct { + pub *mlkem.EncapsulationKey1024 + cipherText []byte + sharedSecret []byte +} + func FromPublicPEM(publicKeyInPem string) (PublicKeyEncryptor, error) { // TK Move salt and info out of library, into API option functions digest := sha256.New() @@ -99,6 +113,10 @@ func FromPublicPEMWithSalt(publicKeyInPem string, salt, info []byte) (PublicKeyE return newECIES(e, salt, info) case *ecdh.PublicKey: return newECIES(pub, salt, info) + case *mlkem.EncapsulationKey768: + return newMLKEM768(pub), nil + case *mlkem.EncapsulationKey1024: + return newMLKEM1024(pub), nil default: break } @@ -111,6 +129,16 @@ func newECIES(pub *ecdh.PublicKey, salt, info []byte) (ECEncryptor, error) { return ECEncryptor{pub, ek, salt, info}, err } +func newMLKEM768(pub *mlkem.EncapsulationKey768) *MLKEMEncryptor768 { + sharedSecret, cipherText := pub.Encapsulate() + return &MLKEMEncryptor768{pub: pub, cipherText: cipherText, sharedSecret: sharedSecret} +} + +func newMLKEM1024(pub *mlkem.EncapsulationKey1024) *MLKEMEncryptor1024 { + sharedSecret, cipherText := pub.Encapsulate() + return &MLKEMEncryptor1024{pub: pub, cipherText: cipherText, sharedSecret: sharedSecret} +} + // NewAsymEncryption creates and returns a new AsymEncryption. // // Deprecated: Use FromPublicPEM instead. @@ -282,3 +310,89 @@ func (e ECEncryptor) Encrypt(data []byte) ([]byte, error) { func (e ECEncryptor) PublicKeyInPemFormat() (string, error) { return publicKeyInPemFormat(e.ek.Public()) } + +// MLKEMEncryptor768 methods + +func (e MLKEMEncryptor768) Type() SchemeType { + return MLKEM +} + +func (e MLKEMEncryptor768) KeyType() KeyType { + return MLKEM768Key +} + +func (e MLKEMEncryptor768) EphemeralKey() []byte { + return e.cipherText +} + +func (e MLKEMEncryptor768) Metadata() (map[string]string, error) { + return map[string]string{ + "ephemeralKey": string(e.cipherText), + }, nil +} + +func (e MLKEMEncryptor768) Encrypt(data []byte) ([]byte, error) { + block, err := aes.NewCipher(e.sharedSecret) + if err != nil { + return nil, fmt.Errorf("aes.NewCipher failed: %w", err) + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, fmt.Errorf("cipher.NewGCM failed: %w", err) + } + + nonce := make([]byte, gcm.NonceSize()) + if _, err := io.ReadFull(rand.Reader, nonce); err != nil { + return nil, fmt.Errorf("nonce generation failed: %w", err) + } + + return gcm.Seal(nonce, nonce, data, nil), nil +} + +func (e MLKEMEncryptor768) PublicKeyInPemFormat() (string, error) { + return "", errors.New("public key PEM not available for ML-KEM encryptor") +} + +// MLKEMEncryptor1024 methods + +func (e MLKEMEncryptor1024) Type() SchemeType { + return MLKEM +} + +func (e MLKEMEncryptor1024) KeyType() KeyType { + return MLKEM1024Key +} + +func (e MLKEMEncryptor1024) EphemeralKey() []byte { + return e.cipherText +} + +func (e MLKEMEncryptor1024) Metadata() (map[string]string, error) { + return map[string]string{ + "ephemeralKey": string(e.cipherText), + }, nil +} + +func (e MLKEMEncryptor1024) Encrypt(data []byte) ([]byte, error) { + block, err := aes.NewCipher(e.sharedSecret) + if err != nil { + return nil, fmt.Errorf("aes.NewCipher failed: %w", err) + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, fmt.Errorf("cipher.NewGCM failed: %w", err) + } + + nonce := make([]byte, gcm.NonceSize()) + if _, err := io.ReadFull(rand.Reader, nonce); err != nil { + return nil, fmt.Errorf("nonce generation failed: %w", err) + } + + return gcm.Seal(nonce, nonce, data, nil), nil +} + +func (e MLKEMEncryptor1024) PublicKeyInPemFormat() (string, error) { + return "", errors.New("public key PEM not available for ML-KEM encryptor") +} diff --git a/lib/ocrypto/hybrid_nist.go b/lib/ocrypto/hybrid_nist.go index ee0a575ac5..223100de70 100644 --- a/lib/ocrypto/hybrid_nist.go +++ b/lib/ocrypto/hybrid_nist.go @@ -6,6 +6,7 @@ import ( "crypto/rand" "crypto/sha256" "encoding/asn1" + "errors" "fmt" "io" @@ -517,3 +518,10 @@ func deriveHybridNISTWrapKey(combinedSecret, salt, info []byte) ([]byte, error) return derivedKey, nil } + +func (d *HybridNISTDecryptor) DecryptWithEphemeralKey(data, ephemeral []byte) ([]byte, error) { + if len(ephemeral) > 0 { + return nil, errors.New("ephemeral key should not be provided for hybrid NIST decryption") + } + return d.Decrypt(data) +} diff --git a/lib/ocrypto/xwing.go b/lib/ocrypto/xwing.go index 1ea9d5ab25..1c4a57a89c 100644 --- a/lib/ocrypto/xwing.go +++ b/lib/ocrypto/xwing.go @@ -5,6 +5,7 @@ import ( "crypto/sha256" "encoding/asn1" "encoding/pem" + "errors" "fmt" "io" @@ -258,3 +259,10 @@ func decodeSizedPEMBlock(data []byte, blockType string, expectedSize int) ([]byt return append([]byte(nil), block.Bytes...), nil } + +func (d *XWingDecryptor) DecryptWithEphemeralKey(data, ephemeral []byte) ([]byte, error) { + if len(ephemeral) > 0 { + return nil, errors.New("ephemeral key should not be provided for X-Wing decryption") + } + return d.Decrypt(data) +} From 25fbf552e8343fd5f10c24e5880ca3fa7ac5a3b0 Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Wed, 27 May 2026 15:48:19 -0400 Subject: [PATCH 03/23] feat(kas): add ml-kem support to service layer Add ML-KEM-768 and ML-KEM-1024 support to the KAS service layer: - Add AlgorithmMLKEM768 and AlgorithmMLKEM1024 constants - Add ML-KEM decryption support to BasicManager - Add StandardMLKEMCrypto type for ML-KEM key management - Add MLKEMPublicKey method for public key export - Add ML-KEM case to StandardCrypto.Decrypt method Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: Dave Mihalcik --- service/internal/security/basic_manager.go | 10 +++++ service/internal/security/crypto_provider.go | 4 ++ service/internal/security/standard_crypto.go | 42 ++++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/service/internal/security/basic_manager.go b/service/internal/security/basic_manager.go index 139ae7ce31..79608a20a5 100644 --- a/service/internal/security/basic_manager.go +++ b/service/internal/security/basic_manager.go @@ -148,6 +148,16 @@ func (b *BasicManager) Decrypt(ctx context.Context, keyDetails trust.KeyDetails, return nil, fmt.Errorf("failed to create protected key: %w", err) } return protectedKey, nil + case ocrypto.MLKEM768Key, ocrypto.MLKEM1024Key: + plaintext, err := decrypter.DecryptWithEphemeralKey(ciphertext, ephemeralPublicKey) + if err != nil { + return nil, fmt.Errorf("failed to decrypt with ML-KEM: %w", err) + } + protectedKey, err := ocrypto.NewAESProtectedKey(plaintext) + if err != nil { + return nil, fmt.Errorf("failed to create protected key: %w", err) + } + return protectedKey, nil } return nil, fmt.Errorf("unsupported algorithm: %s", keyDetails.Algorithm()) diff --git a/service/internal/security/crypto_provider.go b/service/internal/security/crypto_provider.go index 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/standard_crypto.go b/service/internal/security/standard_crypto.go index 59953a8523..5752331fd0 100644 --- a/service/internal/security/standard_crypto.go +++ b/service/internal/security/standard_crypto.go @@ -79,6 +79,12 @@ type StandardHybridCrypto struct { hybridPublicKeyPem string } +type StandardMLKEMCrypto struct { + KeyPairInfo + mlkemPrivateKeyPem string + mlkemPublicKeyPem string +} + // List of keys by identifier type keylist map[string]any @@ -174,6 +180,12 @@ func loadKey(k KeyPairInfo) (any, error) { hybridPrivateKeyPem: string(privatePEM), hybridPublicKeyPem: string(certPEM), }, nil + case AlgorithmMLKEM768, AlgorithmMLKEM1024: + return StandardMLKEMCrypto{ + KeyPairInfo: k, + mlkemPrivateKeyPem: string(privatePEM), + mlkemPublicKeyPem: string(certPEM), + }, nil case AlgorithmRSA2048, AlgorithmRSA4096: asymDecryption, err := ocrypto.NewAsymDecryption(string(privatePEM)) if err != nil { @@ -389,6 +401,21 @@ func (s StandardCrypto) HybridPublicKey(kid string) (string, error) { } } +func (s StandardCrypto) MLKEMPublicKey(kid string) (string, error) { + k, ok := s.keysByID[kid] + if !ok { + return "", fmt.Errorf("no mlkem key with id [%s]: %w", kid, ErrCertNotFound) + } + mlkem, ok := k.(StandardMLKEMCrypto) + if !ok { + return "", fmt.Errorf("key with id [%s] is not an ML-KEM key: %w", kid, ErrCertNotFound) + } + if mlkem.mlkemPublicKeyPem == "" { + return "", fmt.Errorf("no ML-KEM public key with id [%s]: %w", kid, ErrCertNotFound) + } + return mlkem.mlkemPublicKeyPem, nil +} + func (s StandardCrypto) RSADecrypt(_ crypto.Hash, kid string, _ string, ciphertext []byte) ([]byte, error) { k, ok := s.keysByID[kid] if !ok { @@ -542,6 +569,21 @@ func (s *StandardCrypto) Decrypt(_ context.Context, keyID trust.KeyIdentifier, c return nil, fmt.Errorf("unsupported hybrid algorithm [%s]", key.Algorithm) } + case StandardMLKEMCrypto: + if len(ephemeralPublicKey) == 0 { + return nil, errors.New("ephemeral public key (ciphertext) is required for ML-KEM decryption") + } + + decryptor, err := ocrypto.FromPrivatePEM(key.mlkemPrivateKeyPem) + if err != nil { + return nil, fmt.Errorf("failed to create ML-KEM decryptor from PEM: %w", err) + } + + rawKey, err = decryptor.DecryptWithEphemeralKey(ciphertext, ephemeralPublicKey) + if err != nil { + return nil, fmt.Errorf("failed to decrypt with ML-KEM: %w", err) + } + default: return nil, fmt.Errorf("unsupported key type for key ID [%s]", kid) } From c6b3a8abf38f0a3e5c874b4161d80a7db4c73452 Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Wed, 27 May 2026 15:52:21 -0400 Subject: [PATCH 04/23] feat(kas): extend ml-kem support across service layer Complete ML-KEM integration across the service layer: - Add ML-KEM algorithms to InProcessProvider key listing and decryption - Add ML-KEM support to public key export in KAS access layer - Add ML-KEM algorithm mappings in policy grant_mappings Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: Dave Mihalcik --- service/internal/security/in_process_provider.go | 8 +++++++- service/kas/access/publicKey.go | 7 +++++-- service/policy/db/grant_mappings.go | 4 ++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/service/internal/security/in_process_provider.go b/service/internal/security/in_process_provider.go index 0b9249f4aa..b228725a3b 100644 --- a/service/internal/security/in_process_provider.go +++ b/service/internal/security/in_process_provider.go @@ -199,7 +199,7 @@ func (a *InProcessProvider) ListKeysWith(ctx context.Context, opts trust.ListKey var keys []trust.KeyDetails // Try to find keys for known algorithms - for _, alg := range []string{AlgorithmRSA2048, AlgorithmRSA4096, AlgorithmECP256R1, AlgorithmHPQTXWing, AlgorithmHPQTSecp256r1MLKEM768, AlgorithmHPQTSecp384r1MLKEM1024} { + for _, alg := range []string{AlgorithmRSA2048, AlgorithmRSA4096, AlgorithmECP256R1, AlgorithmHPQTXWing, AlgorithmHPQTSecp256r1MLKEM768, AlgorithmHPQTSecp384r1MLKEM1024, AlgorithmMLKEM768, AlgorithmMLKEM1024} { if kids, err := a.cryptoProvider.ListKIDsByAlgorithm(alg); err == nil && len(kids) > 0 { for _, kid := range kids { if opts.LegacyOnly && !a.legacyKeys[kid] { @@ -261,6 +261,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 (ciphertext) is required for ML-KEM decryption") + } + return a.cryptoProvider.Decrypt(ctx, trust.KeyIdentifier(kid), ciphertext, ephemeralPublicKey) + default: return nil, errors.New("unsupported key algorithm") } 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/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: From 7b405c2f03fcaa5c848c3b366de36e6454d89f40 Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Wed, 27 May 2026 15:59:15 -0400 Subject: [PATCH 05/23] feat(sdk): add ml-kem algorithm support Add ML-KEM-768 and ML-KEM-1024 algorithm support to SDK: - Add ML-KEM cases to KeyTypeToPolicyAlgorithm conversion - Add ML-KEM cases to PolicyAlgorithmToKeyType conversion - Add ML-KEM enum mappings in convertAlgEnum2Simple - Add ML-KEM string mappings in algProto2String Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: Dave Mihalcik --- sdk/basekey.go | 8 ++++++++ sdk/granter.go | 8 ++++++++ 2 files changed, 16 insertions(+) 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/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 "" } From 43aee1ec90ca7b84fbc1129a51e1c93717320cf4 Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Wed, 27 May 2026 16:04:50 -0400 Subject: [PATCH 06/23] feat(otdfctl,sdk): add ml-kem support to CLI and experimental SDK Add ML-KEM-768 and ML-KEM-1024 support to otdfctl and experimental SDK: - Add ML-KEM key generation in kasKeys command - Add "mlkem:768" and "mlkem:1024" string mappings in CLI helpers - Add ML-KEM validation in PEM validator - Add ML-KEM enum conversions in experimental keysplit SDK Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: Dave Mihalcik --- otdfctl/cmd/policy/kasKeys.go | 4 ++++ otdfctl/pkg/cli/sdkHelpers.go | 8 ++++++++ otdfctl/pkg/utils/pemvalidate.go | 8 ++++++++ sdk/experimental/tdf/keysplit/attributes.go | 4 ++++ 4 files changed, 24 insertions(+) diff --git a/otdfctl/cmd/policy/kasKeys.go b/otdfctl/cmd/policy/kasKeys.go index 14f70c55ee..e56cc5aa22 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/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/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: From 8787388fa81be013d8788b2a574f6ec886a168e5 Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Wed, 27 May 2026 16:09:21 -0400 Subject: [PATCH 07/23] feat(keygen,tests): add ml-kem key generation support Add ML-KEM-768 and ML-KEM-1024 key generation: - Add ML-KEM key generation to service keygen command - Add ML-KEM key generation to BDD test utilities - Generate kas-mlkem768-private.pem and kas-mlkem768-public.pem - Generate kas-mlkem1024-private.pem and kas-mlkem1024-public.pem Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: Dave Mihalcik --- service/cmd/keygen/main.go | 46 +++++++++++++++++++++++++- tests-bdd/cukes/utils/utils_genKeys.go | 36 +++++++++++++++++++- 2 files changed, 80 insertions(+), 2 deletions(-) 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/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 +} From 9611cd81fd14d6f27fc7012559994df6dbd47fbd Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Wed, 27 May 2026 16:23:15 -0400 Subject: [PATCH 08/23] test(tdf-roundtrips): add ML-KEM-768 and ML-KEM-1024 roundtrip tests Add pure post-quantum encryption tests for ML-KEM-768 and ML-KEM-1024 algorithms. Tests validate KAO type (mlkem-wrapped), KID assignment (m1, m2), and successful encrypt/decrypt roundtrips. Also updates KAS config to include ML-KEM keys. Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: Dave Mihalcik --- test/tdf-roundtrips.bats | 54 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) 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 From 22aeb8fce6ca9b5c36281874cc2c22d086276c53 Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Thu, 28 May 2026 08:16:43 -0400 Subject: [PATCH 09/23] refactor(ocrypto): adopt X-Wing pattern for ML-KEM implementation Refactors plain ML-KEM (768/1024) to use ASN.1 DER encoding and HKDF key derivation, matching the X-Wing and Hybrid NIST patterns. Stores ML-KEM ciphertext concatenated with wrapped DEK in ASN.1 structure instead of overloading ephemeralKey metadata. - Add lib/ocrypto/mlkem.go with X-Wing-style wrap/unwrap functions - Add comprehensive test coverage in mlkem_test.go - Preserve backwards compatibility by renaming old types to Legacy variants - Update otdfctl documentation to include mlkem:768 and mlkem:1024 algorithms Co-Authored-By: Claude Sonnet 4.5 --- lib/ocrypto/asym_decryption.go | 16 +- lib/ocrypto/asym_encryption.go | 36 +- lib/ocrypto/mlkem.go | 405 ++++++++++++++++++ lib/ocrypto/mlkem_test.go | 222 ++++++++++ .../man/policy/kas-registry/key/create.md | 2 + .../man/policy/kas-registry/key/import.md | 2 + .../man/policy/kas-registry/key/rotate.md | 2 + 7 files changed, 659 insertions(+), 26 deletions(-) create mode 100644 lib/ocrypto/mlkem.go create mode 100644 lib/ocrypto/mlkem_test.go diff --git a/lib/ocrypto/asym_decryption.go b/lib/ocrypto/asym_decryption.go index e241ed391c..3f10f520cb 100644 --- a/lib/ocrypto/asym_decryption.go +++ b/lib/ocrypto/asym_decryption.go @@ -32,11 +32,11 @@ type PrivateKeyDecryptor interface { DecryptWithEphemeralKey(data, ephemeral []byte) ([]byte, error) } -type MLKEMDecryptor768 struct { +type MLKEMDecryptor768Legacy struct { decap *mlkem.DecapsulationKey768 } -type MLKEMDecryptor1024 struct { +type MLKEMDecryptor1024Legacy struct { decap *mlkem.DecapsulationKey1024 } @@ -67,13 +67,13 @@ func FromPrivatePEMWithSalt(privateKeyInPem string, salt, info []byte) (PrivateK if block.Type == "MLKEM DECAPSULATION KEY" { decap768, err := mlkem.NewDecapsulationKey768(block.Bytes) if err == nil { - return &MLKEMDecryptor768{decap: decap768}, nil + return &MLKEMDecryptor768Legacy{decap: decap768}, nil } decap1024, err1024 := mlkem.NewDecapsulationKey1024(block.Bytes) if err1024 != nil { return nil, fmt.Errorf("mlkem.NewDecapsulationKey1024 failed after mlkem.NewDecapsulationKey768 failed: %w / %w", err, err1024) } - return &MLKEMDecryptor1024{decap: decap1024}, nil + return &MLKEMDecryptor1024Legacy{decap: decap1024}, nil } priv, err := x509.ParsePKCS8PrivateKey(block.Bytes) @@ -246,15 +246,15 @@ func convCurve(c ecdh.Curve) elliptic.Curve { } } -func (d MLKEMDecryptor768) Decrypt(_ []byte) ([]byte, error) { +func (d MLKEMDecryptor768Legacy) Decrypt(_ []byte) ([]byte, error) { return nil, errors.New("ciphertext encapsulation is required for ML-KEM decryption") } -func (d MLKEMDecryptor1024) Decrypt(_ []byte) ([]byte, error) { +func (d MLKEMDecryptor1024Legacy) Decrypt(_ []byte) ([]byte, error) { return nil, errors.New("ciphertext encapsulation is required for ML-KEM decryption") } -func (d MLKEMDecryptor768) DecryptWithEphemeralKey(data, ephemeral []byte) ([]byte, error) { +func (d MLKEMDecryptor768Legacy) DecryptWithEphemeralKey(data, ephemeral []byte) ([]byte, error) { if d.decap == nil { return nil, errors.New("mlkem decapsulation key is nil") } @@ -291,7 +291,7 @@ func (d MLKEMDecryptor768) DecryptWithEphemeralKey(data, ephemeral []byte) ([]by return plaintext, nil } -func (d MLKEMDecryptor1024) DecryptWithEphemeralKey(data, ephemeral []byte) ([]byte, error) { +func (d MLKEMDecryptor1024Legacy) DecryptWithEphemeralKey(data, ephemeral []byte) ([]byte, error) { if d.decap == nil { return nil, errors.New("mlkem decapsulation key is nil") } diff --git a/lib/ocrypto/asym_encryption.go b/lib/ocrypto/asym_encryption.go index 919913348b..417dc41b62 100644 --- a/lib/ocrypto/asym_encryption.go +++ b/lib/ocrypto/asym_encryption.go @@ -62,13 +62,13 @@ type ECEncryptor struct { info []byte } -type MLKEMEncryptor768 struct { +type MLKEMEncryptor768Legacy struct { pub *mlkem.EncapsulationKey768 cipherText []byte sharedSecret []byte } -type MLKEMEncryptor1024 struct { +type MLKEMEncryptor1024Legacy struct { pub *mlkem.EncapsulationKey1024 cipherText []byte sharedSecret []byte @@ -129,14 +129,14 @@ func newECIES(pub *ecdh.PublicKey, salt, info []byte) (ECEncryptor, error) { return ECEncryptor{pub, ek, salt, info}, err } -func newMLKEM768(pub *mlkem.EncapsulationKey768) *MLKEMEncryptor768 { +func newMLKEM768(pub *mlkem.EncapsulationKey768) *MLKEMEncryptor768Legacy { sharedSecret, cipherText := pub.Encapsulate() - return &MLKEMEncryptor768{pub: pub, cipherText: cipherText, sharedSecret: sharedSecret} + return &MLKEMEncryptor768Legacy{pub: pub, cipherText: cipherText, sharedSecret: sharedSecret} } -func newMLKEM1024(pub *mlkem.EncapsulationKey1024) *MLKEMEncryptor1024 { +func newMLKEM1024(pub *mlkem.EncapsulationKey1024) *MLKEMEncryptor1024Legacy { sharedSecret, cipherText := pub.Encapsulate() - return &MLKEMEncryptor1024{pub: pub, cipherText: cipherText, sharedSecret: sharedSecret} + return &MLKEMEncryptor1024Legacy{pub: pub, cipherText: cipherText, sharedSecret: sharedSecret} } // NewAsymEncryption creates and returns a new AsymEncryption. @@ -313,25 +313,25 @@ func (e ECEncryptor) PublicKeyInPemFormat() (string, error) { // MLKEMEncryptor768 methods -func (e MLKEMEncryptor768) Type() SchemeType { +func (e MLKEMEncryptor768Legacy) Type() SchemeType { return MLKEM } -func (e MLKEMEncryptor768) KeyType() KeyType { +func (e MLKEMEncryptor768Legacy) KeyType() KeyType { return MLKEM768Key } -func (e MLKEMEncryptor768) EphemeralKey() []byte { +func (e MLKEMEncryptor768Legacy) EphemeralKey() []byte { return e.cipherText } -func (e MLKEMEncryptor768) Metadata() (map[string]string, error) { +func (e MLKEMEncryptor768Legacy) Metadata() (map[string]string, error) { return map[string]string{ "ephemeralKey": string(e.cipherText), }, nil } -func (e MLKEMEncryptor768) Encrypt(data []byte) ([]byte, error) { +func (e MLKEMEncryptor768Legacy) Encrypt(data []byte) ([]byte, error) { block, err := aes.NewCipher(e.sharedSecret) if err != nil { return nil, fmt.Errorf("aes.NewCipher failed: %w", err) @@ -350,31 +350,31 @@ func (e MLKEMEncryptor768) Encrypt(data []byte) ([]byte, error) { return gcm.Seal(nonce, nonce, data, nil), nil } -func (e MLKEMEncryptor768) PublicKeyInPemFormat() (string, error) { +func (e MLKEMEncryptor768Legacy) PublicKeyInPemFormat() (string, error) { return "", errors.New("public key PEM not available for ML-KEM encryptor") } // MLKEMEncryptor1024 methods -func (e MLKEMEncryptor1024) Type() SchemeType { +func (e MLKEMEncryptor1024Legacy) Type() SchemeType { return MLKEM } -func (e MLKEMEncryptor1024) KeyType() KeyType { +func (e MLKEMEncryptor1024Legacy) KeyType() KeyType { return MLKEM1024Key } -func (e MLKEMEncryptor1024) EphemeralKey() []byte { +func (e MLKEMEncryptor1024Legacy) EphemeralKey() []byte { return e.cipherText } -func (e MLKEMEncryptor1024) Metadata() (map[string]string, error) { +func (e MLKEMEncryptor1024Legacy) Metadata() (map[string]string, error) { return map[string]string{ "ephemeralKey": string(e.cipherText), }, nil } -func (e MLKEMEncryptor1024) Encrypt(data []byte) ([]byte, error) { +func (e MLKEMEncryptor1024Legacy) Encrypt(data []byte) ([]byte, error) { block, err := aes.NewCipher(e.sharedSecret) if err != nil { return nil, fmt.Errorf("aes.NewCipher failed: %w", err) @@ -393,6 +393,6 @@ func (e MLKEMEncryptor1024) Encrypt(data []byte) ([]byte, error) { return gcm.Seal(nonce, nonce, data, nil), nil } -func (e MLKEMEncryptor1024) PublicKeyInPemFormat() (string, error) { +func (e MLKEMEncryptor1024Legacy) PublicKeyInPemFormat() (string, error) { return "", errors.New("public key PEM not available for ML-KEM encryptor") } diff --git a/lib/ocrypto/mlkem.go b/lib/ocrypto/mlkem.go new file mode 100644 index 0000000000..e64288dfa3 --- /dev/null +++ b/lib/ocrypto/mlkem.go @@ -0,0 +1,405 @@ +package ocrypto + +import ( + "crypto/mlkem" + "crypto/sha256" + "encoding/asn1" + "encoding/pem" + "errors" + "fmt" + "io" + + "golang.org/x/crypto/hkdf" +) + +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 + + mlkemWrapKeySize = 32 // AES-256 key size for wrap key derivation +) + +type MLKEMWrappedKey struct { + MLKEMCiphertext []byte `asn1:"tag:0"` + EncryptedDEK []byte `asn1:"tag:1"` +} + +type MLKEMEncryptor768 struct { + publicKey []byte + salt []byte + info []byte +} + +type MLKEMDecryptor768 struct { + privateKey []byte + salt []byte + info []byte +} + +type MLKEMEncryptor1024 struct { + publicKey []byte + salt []byte + info []byte +} + +type MLKEMDecryptor1024 struct { + privateKey []byte + salt []byte + info []byte +} + +func NewMLKEM768Encryptor(publicKey, salt, info []byte) (*MLKEMEncryptor768, error) { + if len(publicKey) != MLKEM768PublicKeySize { + return nil, fmt.Errorf("invalid ML-KEM-768 public key size: got %d want %d", len(publicKey), MLKEM768PublicKeySize) + } + + return &MLKEMEncryptor768{ + publicKey: append([]byte(nil), publicKey...), + salt: cloneOrNil(salt), + info: cloneOrNil(info), + }, nil +} + +func (e *MLKEMEncryptor768) Encrypt(data []byte) ([]byte, error) { + return mlkem768WrapDEK(e.publicKey, data, e.salt, e.info) +} + +func (e *MLKEMEncryptor768) PublicKeyInPemFormat() (string, error) { + pemBlock := &pem.Block{ + Type: "MLKEM ENCAPSULATOR", + Bytes: e.publicKey, + } + return string(pem.EncodeToMemory(pemBlock)), nil +} + +func (e *MLKEMEncryptor768) Type() SchemeType { + return MLKEM +} + +func (e *MLKEMEncryptor768) KeyType() KeyType { + return MLKEM768Key +} + +func (e *MLKEMEncryptor768) EphemeralKey() []byte { + return nil +} + +func (e *MLKEMEncryptor768) Metadata() (map[string]string, error) { + return make(map[string]string), nil +} + +func NewMLKEM768Decryptor(privateKey []byte) (*MLKEMDecryptor768, error) { + return NewSaltedMLKEM768Decryptor(privateKey, defaultTDFSalt(), nil) +} + +func NewSaltedMLKEM768Decryptor(privateKey, salt, info []byte) (*MLKEMDecryptor768, error) { + if len(privateKey) != MLKEM768PrivateKeySize { + return nil, fmt.Errorf("invalid ML-KEM-768 private key size: got %d want %d", len(privateKey), MLKEM768PrivateKeySize) + } + + return &MLKEMDecryptor768{ + privateKey: append([]byte(nil), privateKey...), + salt: cloneOrNil(salt), + info: cloneOrNil(info), + }, nil +} + +func (d *MLKEMDecryptor768) Decrypt(data []byte) ([]byte, error) { + return mlkem768UnwrapDEK(d.privateKey, data, d.salt, d.info) +} + +func (d *MLKEMDecryptor768) DecryptWithEphemeralKey(data, ephemeral []byte) ([]byte, error) { + if len(ephemeral) > 0 { + return nil, errors.New("ephemeral key should not be provided for ML-KEM-768 decryption") + } + return d.Decrypt(data) +} + +func NewMLKEM1024Encryptor(publicKey, salt, info []byte) (*MLKEMEncryptor1024, error) { + if len(publicKey) != MLKEM1024PublicKeySize { + return nil, fmt.Errorf("invalid ML-KEM-1024 public key size: got %d want %d", len(publicKey), MLKEM1024PublicKeySize) + } + + return &MLKEMEncryptor1024{ + publicKey: append([]byte(nil), publicKey...), + salt: cloneOrNil(salt), + info: cloneOrNil(info), + }, nil +} + +func (e *MLKEMEncryptor1024) Encrypt(data []byte) ([]byte, error) { + return mlkem1024WrapDEK(e.publicKey, data, e.salt, e.info) +} + +func (e *MLKEMEncryptor1024) PublicKeyInPemFormat() (string, error) { + pemBlock := &pem.Block{ + Type: "MLKEM ENCAPSULATOR", + Bytes: e.publicKey, + } + return string(pem.EncodeToMemory(pemBlock)), nil +} + +func (e *MLKEMEncryptor1024) Type() SchemeType { + return MLKEM +} + +func (e *MLKEMEncryptor1024) KeyType() KeyType { + return MLKEM1024Key +} + +func (e *MLKEMEncryptor1024) EphemeralKey() []byte { + return nil +} + +func (e *MLKEMEncryptor1024) Metadata() (map[string]string, error) { + return make(map[string]string), nil +} + +func NewMLKEM1024Decryptor(privateKey []byte) (*MLKEMDecryptor1024, error) { + return NewSaltedMLKEM1024Decryptor(privateKey, defaultTDFSalt(), nil) +} + +func NewSaltedMLKEM1024Decryptor(privateKey, salt, info []byte) (*MLKEMDecryptor1024, error) { + if len(privateKey) != MLKEM1024PrivateKeySize { + return nil, fmt.Errorf("invalid ML-KEM-1024 private key size: got %d want %d", len(privateKey), MLKEM1024PrivateKeySize) + } + + return &MLKEMDecryptor1024{ + privateKey: append([]byte(nil), privateKey...), + salt: cloneOrNil(salt), + info: cloneOrNil(info), + }, nil +} + +func (d *MLKEMDecryptor1024) Decrypt(data []byte) ([]byte, error) { + return mlkem1024UnwrapDEK(d.privateKey, data, d.salt, d.info) +} + +func (d *MLKEMDecryptor1024) DecryptWithEphemeralKey(data, ephemeral []byte) ([]byte, error) { + if len(ephemeral) > 0 { + return nil, errors.New("ephemeral key should not be provided for ML-KEM-1024 decryption") + } + return d.Decrypt(data) +} + +func MLKEM768WrapDEK(publicKeyRaw, dek []byte) ([]byte, error) { + return mlkem768WrapDEK(publicKeyRaw, dek, defaultTDFSalt(), nil) +} + +func MLKEM768UnwrapDEK(privateKeyRaw, wrappedDER []byte) ([]byte, error) { + return mlkem768UnwrapDEK(privateKeyRaw, wrappedDER, defaultTDFSalt(), nil) +} + +func MLKEM1024WrapDEK(publicKeyRaw, dek []byte) ([]byte, error) { + return mlkem1024WrapDEK(publicKeyRaw, dek, defaultTDFSalt(), nil) +} + +func MLKEM1024UnwrapDEK(privateKeyRaw, wrappedDER []byte) ([]byte, error) { + return mlkem1024UnwrapDEK(privateKeyRaw, wrappedDER, defaultTDFSalt(), nil) +} + +// MLKEM768Encapsulate performs ML-KEM-768 encapsulation, returning the shared +// secret and ciphertext without applying KDF or encryption. +func MLKEM768Encapsulate(publicKeyRaw []byte) ([]byte, []byte, error) { + if len(publicKeyRaw) != MLKEM768PublicKeySize { + return nil, nil, fmt.Errorf("invalid ML-KEM-768 public key size: got %d want %d", len(publicKeyRaw), MLKEM768PublicKeySize) + } + + ek, err := mlkem.NewEncapsulationKey768(publicKeyRaw) + if err != nil { + return nil, nil, fmt.Errorf("mlkem.NewEncapsulationKey768 failed: %w", err) + } + + sharedSecret, ciphertext := ek.Encapsulate() + + return sharedSecret, ciphertext, nil +} + +// MLKEM1024Encapsulate performs ML-KEM-1024 encapsulation, returning the shared +// secret and ciphertext without applying KDF or encryption. +func MLKEM1024Encapsulate(publicKeyRaw []byte) ([]byte, []byte, error) { + if len(publicKeyRaw) != MLKEM1024PublicKeySize { + return nil, nil, fmt.Errorf("invalid ML-KEM-1024 public key size: got %d want %d", len(publicKeyRaw), MLKEM1024PublicKeySize) + } + + ek, err := mlkem.NewEncapsulationKey1024(publicKeyRaw) + if err != nil { + return nil, nil, fmt.Errorf("mlkem.NewEncapsulationKey1024 failed: %w", err) + } + + sharedSecret, ciphertext := ek.Encapsulate() + + return sharedSecret, ciphertext, nil +} + +func mlkem768WrapDEK(publicKeyRaw, dek, salt, info []byte) ([]byte, error) { + sharedSecret, ciphertext, err := MLKEM768Encapsulate(publicKeyRaw) + if err != nil { + return nil, err + } + + wrapKey, err := deriveMLKEMWrapKey(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(MLKEMWrappedKey{ + MLKEMCiphertext: ciphertext, + EncryptedDEK: encryptedDEK, + }) + if err != nil { + return nil, fmt.Errorf("asn1.Marshal failed: %w", err) + } + + return wrappedDER, nil +} + +func mlkem768UnwrapDEK(privateKeyRaw, wrappedDER, salt, info []byte) ([]byte, error) { + if len(privateKeyRaw) != MLKEM768PrivateKeySize { + return nil, fmt.Errorf("invalid ML-KEM-768 private key size: got %d want %d", len(privateKeyRaw), MLKEM768PrivateKeySize) + } + + var wrappedKey MLKEMWrappedKey + 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.MLKEMCiphertext) != MLKEM768CiphertextSize { + return nil, fmt.Errorf("invalid ML-KEM-768 ciphertext size: got %d want %d", len(wrappedKey.MLKEMCiphertext), MLKEM768CiphertextSize) + } + + dk, err := mlkem.NewDecapsulationKey768(privateKeyRaw) + if err != nil { + return nil, fmt.Errorf("mlkem.NewDecapsulationKey768 failed: %w", err) + } + + sharedSecret, err := dk.Decapsulate(wrappedKey.MLKEMCiphertext) + if err != nil { + return nil, fmt.Errorf("mlkem768 decapsulate failed: %w", err) + } + + wrapKey, err := deriveMLKEMWrapKey(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 mlkem1024WrapDEK(publicKeyRaw, dek, salt, info []byte) ([]byte, error) { + sharedSecret, ciphertext, err := MLKEM1024Encapsulate(publicKeyRaw) + if err != nil { + return nil, err + } + + wrapKey, err := deriveMLKEMWrapKey(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(MLKEMWrappedKey{ + MLKEMCiphertext: ciphertext, + EncryptedDEK: encryptedDEK, + }) + if err != nil { + return nil, fmt.Errorf("asn1.Marshal failed: %w", err) + } + + return wrappedDER, nil +} + +func mlkem1024UnwrapDEK(privateKeyRaw, wrappedDER, salt, info []byte) ([]byte, error) { + if len(privateKeyRaw) != MLKEM1024PrivateKeySize { + return nil, fmt.Errorf("invalid ML-KEM-1024 private key size: got %d want %d", len(privateKeyRaw), MLKEM1024PrivateKeySize) + } + + var wrappedKey MLKEMWrappedKey + 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.MLKEMCiphertext) != MLKEM1024CiphertextSize { + return nil, fmt.Errorf("invalid ML-KEM-1024 ciphertext size: got %d want %d", len(wrappedKey.MLKEMCiphertext), MLKEM1024CiphertextSize) + } + + dk, err := mlkem.NewDecapsulationKey1024(privateKeyRaw) + if err != nil { + return nil, fmt.Errorf("mlkem.NewDecapsulationKey1024 failed: %w", err) + } + + sharedSecret, err := dk.Decapsulate(wrappedKey.MLKEMCiphertext) + if err != nil { + return nil, fmt.Errorf("mlkem1024 decapsulate failed: %w", err) + } + + wrapKey, err := deriveMLKEMWrapKey(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 deriveMLKEMWrapKey(sharedSecret, salt, info []byte) ([]byte, error) { + if len(salt) == 0 { + salt = defaultTDFSalt() + } + + hkdfObj := hkdf.New(sha256.New, sharedSecret, salt, info) + derivedKey := make([]byte, mlkemWrapKeySize) + if _, err := io.ReadFull(hkdfObj, derivedKey); err != nil { + return nil, fmt.Errorf("hkdf failure: %w", err) + } + + return derivedKey, nil +} diff --git a/lib/ocrypto/mlkem_test.go b/lib/ocrypto/mlkem_test.go new file mode 100644 index 0000000000..5450d920e7 --- /dev/null +++ b/lib/ocrypto/mlkem_test.go @@ -0,0 +1,222 @@ +package ocrypto + +import ( + "encoding/asn1" + "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 TestMLKEMWrappedKeyASN1RoundTrip(t *testing.T) { + original := MLKEMWrappedKey{ + MLKEMCiphertext: []byte("ciphertext"), + EncryptedDEK: []byte("encrypted-dek"), + } + + der, err := asn1.Marshal(original) + require.NoError(t, err) + + var decoded MLKEMWrappedKey + 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 := MLKEMWrappedKey{ + MLKEMCiphertext: []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(), "invalid ML-KEM-768 ciphertext size") +} + +func TestMLKEM1024CiphertextSizeValidation(t *testing.T) { + keyPair, err := NewMLKEM1024KeyPair() + require.NoError(t, err) + + privateKeyBytes := keyPair.PrivateKey.Bytes() + + invalidWrapped := MLKEMWrappedKey{ + MLKEMCiphertext: []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(), "invalid ML-KEM-1024 ciphertext size") +} + +func TestMLKEMCustomSaltInfo(t *testing.T) { + keyPair, err := NewMLKEMKeyPair() + require.NoError(t, err) + + publicKeyBytes := keyPair.PrivateKey.EncapsulationKey().Bytes() + privateKeyBytes := keyPair.PrivateKey.Bytes() + + customSalt := []byte("custom-salt-value") + customInfo := []byte("custom-info-value") + + encryptor, err := NewMLKEM768Encryptor(publicKeyBytes, customSalt, customInfo) + require.NoError(t, err) + + decryptor, err := NewSaltedMLKEM768Decryptor(privateKeyBytes, customSalt, customInfo) + 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) +} + +func TestMLKEMEncryptorImplementsInterface(t *testing.T) { + keyPair, err := NewMLKEMKeyPair() + require.NoError(t, err) + + publicKeyBytes := keyPair.PrivateKey.EncapsulationKey().Bytes() + + encryptor, err := NewMLKEM768Encryptor(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 TestMLKEMDecryptorImplementsInterface(t *testing.T) { + keyPair, err := NewMLKEMKeyPair() + require.NoError(t, err) + + privateKeyBytes := keyPair.PrivateKey.Bytes() + + decryptor, err := NewMLKEM768Decryptor(privateKeyBytes) + require.NoError(t, err) + + _, err = decryptor.DecryptWithEphemeralKey([]byte("data"), []byte("ephemeral")) + require.Error(t, err) + assert.Contains(t, err.Error(), "ephemeral key should not be provided") +} + +func TestMLKEM768Encapsulate(t *testing.T) { + keyPair, err := NewMLKEMKeyPair() + require.NoError(t, err) + + publicKeyBytes := keyPair.PrivateKey.EncapsulationKey().Bytes() + + sharedSecret, ciphertext, err := MLKEM768Encapsulate(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 := MLKEM1024Encapsulate(publicKeyBytes) + require.NoError(t, err) + assert.Len(t, sharedSecret, 32) + assert.Len(t, ciphertext, MLKEM1024CiphertextSize) +} + +func TestMLKEM768EncapsulateInvalidKeySize(t *testing.T) { + _, _, err := MLKEM768Encapsulate([]byte("too-short")) + require.Error(t, err) + assert.Contains(t, err.Error(), "invalid ML-KEM-768 public key size") +} + +func TestMLKEM1024EncapsulateInvalidKeySize(t *testing.T) { + _, _, err := MLKEM1024Encapsulate([]byte("too-short")) + require.Error(t, err) + assert.Contains(t, err.Error(), "invalid ML-KEM-1024 public key size") +} 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` | From 00a1a36dd10e496bfb82393a51c2a535495ad04c Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Thu, 28 May 2026 08:35:01 -0400 Subject: [PATCH 10/23] refactor(ocrypto): remove DecryptWithEphemeralKey from interface and Legacy ML-KEM types Remove DecryptWithEphemeralKey method from PrivateKeyDecryptor interface, making it EC-specific. Only ECDecryptor needs ephemeral keys for ECDH. Remove Legacy ML-KEM types and implementations: - MLKEMEncryptor768Legacy, MLKEMEncryptor1024Legacy - MLKEMDecryptor768Legacy, MLKEMDecryptor1024Legacy - Old "MLKEM DECAPSULATION KEY" PEM handling - Helper functions newMLKEM768(), newMLKEM1024() Add PEM block constants for new ML-KEM implementation: - PEMBlockMLKEM768PublicKey, PEMBlockMLKEM768PrivateKey - PEMBlockMLKEM1024PublicKey, PEMBlockMLKEM1024PrivateKey Remove DecryptWithEphemeralKey from all non-EC decryptors: - MLKEMDecryptor768, MLKEMDecryptor1024 - XWingDecryptor, HybridNISTDecryptor - AsymDecryption (RSA) Update service layer to use Decrypt() for ML-KEM instead of DecryptWithEphemeralKey. EC decryption continues to use the EC-specific DecryptWithEphemeralKey method. Breaking changes: - Old ML-KEM PEM format ("MLKEM DECAPSULATION KEY") no longer supported - Callers must use concrete ECDecryptor type for DecryptWithEphemeralKey Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: Dave Mihalcik --- lib/ocrypto/asym_decryption.go | 117 +------------------ lib/ocrypto/asym_encryption.go | 117 +------------------ lib/ocrypto/hybrid_nist.go | 8 -- lib/ocrypto/mlkem.go | 20 +--- lib/ocrypto/mlkem_test.go | 14 --- lib/ocrypto/xwing.go | 8 -- service/internal/security/basic_manager.go | 5 +- service/internal/security/standard_crypto.go | 6 +- 8 files changed, 20 insertions(+), 275 deletions(-) diff --git a/lib/ocrypto/asym_decryption.go b/lib/ocrypto/asym_decryption.go index 3f10f520cb..6656c92be9 100644 --- a/lib/ocrypto/asym_decryption.go +++ b/lib/ocrypto/asym_decryption.go @@ -7,7 +7,6 @@ import ( "crypto/ecdh" "crypto/ecdsa" "crypto/elliptic" - "crypto/mlkem" "crypto/rsa" "crypto/sha256" "crypto/x509" @@ -27,17 +26,6 @@ type AsymDecryption struct { type PrivateKeyDecryptor interface { // Decrypt decrypts ciphertext with private key. Decrypt(data []byte) ([]byte, error) - - // DecryptWithEphemeralKey decrypts ciphertext using additional sender material. - DecryptWithEphemeralKey(data, ephemeral []byte) ([]byte, error) -} - -type MLKEMDecryptor768Legacy struct { - decap *mlkem.DecapsulationKey768 -} - -type MLKEMDecryptor1024Legacy struct { - decap *mlkem.DecapsulationKey1024 } // FromPrivatePEM creates and returns a new AsymDecryption. @@ -62,18 +50,10 @@ func FromPrivatePEMWithSalt(privateKeyInPem string, salt, info []byte) (PrivateK return NewSaltedP256MLKEM768Decryptor(block.Bytes, salt, info) case PEMBlockP384MLKEM1024PrivateKey: return NewSaltedP384MLKEM1024Decryptor(block.Bytes, salt, info) - } - - if block.Type == "MLKEM DECAPSULATION KEY" { - decap768, err := mlkem.NewDecapsulationKey768(block.Bytes) - if err == nil { - return &MLKEMDecryptor768Legacy{decap: decap768}, nil - } - decap1024, err1024 := mlkem.NewDecapsulationKey1024(block.Bytes) - if err1024 != nil { - return nil, fmt.Errorf("mlkem.NewDecapsulationKey1024 failed after mlkem.NewDecapsulationKey768 failed: %w / %w", err, err1024) - } - return &MLKEMDecryptor1024Legacy{decap: decap1024}, nil + case PEMBlockMLKEM768PrivateKey: + return NewSaltedMLKEM768Decryptor(block.Bytes, salt, info) + case PEMBlockMLKEM1024PrivateKey: + return NewSaltedMLKEM1024Decryptor(block.Bytes, salt, info) } priv, err := x509.ParsePKCS8PrivateKey(block.Bytes) @@ -141,13 +121,6 @@ func (asymDecryption AsymDecryption) Decrypt(data []byte) ([]byte, error) { return bytes, nil } -func (asymDecryption AsymDecryption) DecryptWithEphemeralKey(data, ephemeral []byte) ([]byte, error) { - if len(ephemeral) > 0 { - return nil, errors.New("ephemeral key is not supported for RSA decryption") - } - return asymDecryption.Decrypt(data) -} - type ECDecryptor struct { sk *ecdh.PrivateKey salt []byte @@ -245,85 +218,3 @@ func convCurve(c ecdh.Curve) elliptic.Curve { return nil } } - -func (d MLKEMDecryptor768Legacy) Decrypt(_ []byte) ([]byte, error) { - return nil, errors.New("ciphertext encapsulation is required for ML-KEM decryption") -} - -func (d MLKEMDecryptor1024Legacy) Decrypt(_ []byte) ([]byte, error) { - return nil, errors.New("ciphertext encapsulation is required for ML-KEM decryption") -} - -func (d MLKEMDecryptor768Legacy) DecryptWithEphemeralKey(data, ephemeral []byte) ([]byte, error) { - if d.decap == nil { - return nil, errors.New("mlkem decapsulation key is nil") - } - if len(ephemeral) == 0 { - return nil, errors.New("ciphertext encapsulation is required for ML-KEM decryption") - } - - sharedSecret, err := d.decap.Decapsulate(ephemeral) - if err != nil { - return nil, fmt.Errorf("mlkem.Decapsulate failed: %w", err) - } - - block, err := aes.NewCipher(sharedSecret) - if err != nil { - return nil, fmt.Errorf("aes.NewCipher failure: %w", err) - } - - gcm, err := cipher.NewGCM(block) - if err != nil { - return nil, fmt.Errorf("cipher.NewGCM failure: %w", err) - } - - nonceSize := gcm.NonceSize() - if len(data) < nonceSize { - return nil, errors.New("ciphertext too short") - } - - nonce, ciphertext := data[:nonceSize], data[nonceSize:] - plaintext, err := gcm.Open(nil, nonce, ciphertext, nil) - if err != nil { - return nil, fmt.Errorf("gcm.Open failure: %w", err) - } - - return plaintext, nil -} - -func (d MLKEMDecryptor1024Legacy) DecryptWithEphemeralKey(data, ephemeral []byte) ([]byte, error) { - if d.decap == nil { - return nil, errors.New("mlkem decapsulation key is nil") - } - if len(ephemeral) == 0 { - return nil, errors.New("ciphertext encapsulation is required for ML-KEM decryption") - } - - sharedSecret, err := d.decap.Decapsulate(ephemeral) - if err != nil { - return nil, fmt.Errorf("mlkem.Decapsulate failed: %w", err) - } - - block, err := aes.NewCipher(sharedSecret) - if err != nil { - return nil, fmt.Errorf("aes.NewCipher failure: %w", err) - } - - gcm, err := cipher.NewGCM(block) - if err != nil { - return nil, fmt.Errorf("cipher.NewGCM failure: %w", err) - } - - nonceSize := gcm.NonceSize() - if len(data) < nonceSize { - return nil, errors.New("ciphertext too short") - } - - nonce, ciphertext := data[:nonceSize], data[nonceSize:] - plaintext, err := gcm.Open(nil, nonce, ciphertext, nil) - if err != nil { - return nil, fmt.Errorf("gcm.Open failure: %w", err) - } - - return plaintext, nil -} diff --git a/lib/ocrypto/asym_encryption.go b/lib/ocrypto/asym_encryption.go index 417dc41b62..e3529084b3 100644 --- a/lib/ocrypto/asym_encryption.go +++ b/lib/ocrypto/asym_encryption.go @@ -5,7 +5,6 @@ import ( "crypto/cipher" "crypto/ecdh" "crypto/ecdsa" - "crypto/mlkem" "crypto/rand" "crypto/rsa" "crypto/sha1" //nolint:gosec // used for padding which is safe @@ -62,18 +61,6 @@ type ECEncryptor struct { info []byte } -type MLKEMEncryptor768Legacy struct { - pub *mlkem.EncapsulationKey768 - cipherText []byte - sharedSecret []byte -} - -type MLKEMEncryptor1024Legacy struct { - pub *mlkem.EncapsulationKey1024 - cipherText []byte - sharedSecret []byte -} - func FromPublicPEM(publicKeyInPem string) (PublicKeyEncryptor, error) { // TK Move salt and info out of library, into API option functions digest := sha256.New() @@ -95,6 +82,10 @@ func FromPublicPEMWithSalt(publicKeyInPem string, salt, info []byte) (PublicKeyE return NewP256MLKEM768Encryptor(block.Bytes, salt, info) case PEMBlockP384MLKEM1024PublicKey: return NewP384MLKEM1024Encryptor(block.Bytes, salt, info) + case PEMBlockMLKEM768PublicKey: + return NewMLKEM768Encryptor(block.Bytes, salt, info) + case PEMBlockMLKEM1024PublicKey: + return NewMLKEM1024Encryptor(block.Bytes, salt, info) } pub, err := getPublicPart(publicKeyInPem) @@ -113,10 +104,6 @@ func FromPublicPEMWithSalt(publicKeyInPem string, salt, info []byte) (PublicKeyE return newECIES(e, salt, info) case *ecdh.PublicKey: return newECIES(pub, salt, info) - case *mlkem.EncapsulationKey768: - return newMLKEM768(pub), nil - case *mlkem.EncapsulationKey1024: - return newMLKEM1024(pub), nil default: break } @@ -129,16 +116,6 @@ func newECIES(pub *ecdh.PublicKey, salt, info []byte) (ECEncryptor, error) { return ECEncryptor{pub, ek, salt, info}, err } -func newMLKEM768(pub *mlkem.EncapsulationKey768) *MLKEMEncryptor768Legacy { - sharedSecret, cipherText := pub.Encapsulate() - return &MLKEMEncryptor768Legacy{pub: pub, cipherText: cipherText, sharedSecret: sharedSecret} -} - -func newMLKEM1024(pub *mlkem.EncapsulationKey1024) *MLKEMEncryptor1024Legacy { - sharedSecret, cipherText := pub.Encapsulate() - return &MLKEMEncryptor1024Legacy{pub: pub, cipherText: cipherText, sharedSecret: sharedSecret} -} - // NewAsymEncryption creates and returns a new AsymEncryption. // // Deprecated: Use FromPublicPEM instead. @@ -310,89 +287,3 @@ func (e ECEncryptor) Encrypt(data []byte) ([]byte, error) { func (e ECEncryptor) PublicKeyInPemFormat() (string, error) { return publicKeyInPemFormat(e.ek.Public()) } - -// MLKEMEncryptor768 methods - -func (e MLKEMEncryptor768Legacy) Type() SchemeType { - return MLKEM -} - -func (e MLKEMEncryptor768Legacy) KeyType() KeyType { - return MLKEM768Key -} - -func (e MLKEMEncryptor768Legacy) EphemeralKey() []byte { - return e.cipherText -} - -func (e MLKEMEncryptor768Legacy) Metadata() (map[string]string, error) { - return map[string]string{ - "ephemeralKey": string(e.cipherText), - }, nil -} - -func (e MLKEMEncryptor768Legacy) Encrypt(data []byte) ([]byte, error) { - block, err := aes.NewCipher(e.sharedSecret) - if err != nil { - return nil, fmt.Errorf("aes.NewCipher failed: %w", err) - } - - gcm, err := cipher.NewGCM(block) - if err != nil { - return nil, fmt.Errorf("cipher.NewGCM failed: %w", err) - } - - nonce := make([]byte, gcm.NonceSize()) - if _, err := io.ReadFull(rand.Reader, nonce); err != nil { - return nil, fmt.Errorf("nonce generation failed: %w", err) - } - - return gcm.Seal(nonce, nonce, data, nil), nil -} - -func (e MLKEMEncryptor768Legacy) PublicKeyInPemFormat() (string, error) { - return "", errors.New("public key PEM not available for ML-KEM encryptor") -} - -// MLKEMEncryptor1024 methods - -func (e MLKEMEncryptor1024Legacy) Type() SchemeType { - return MLKEM -} - -func (e MLKEMEncryptor1024Legacy) KeyType() KeyType { - return MLKEM1024Key -} - -func (e MLKEMEncryptor1024Legacy) EphemeralKey() []byte { - return e.cipherText -} - -func (e MLKEMEncryptor1024Legacy) Metadata() (map[string]string, error) { - return map[string]string{ - "ephemeralKey": string(e.cipherText), - }, nil -} - -func (e MLKEMEncryptor1024Legacy) Encrypt(data []byte) ([]byte, error) { - block, err := aes.NewCipher(e.sharedSecret) - if err != nil { - return nil, fmt.Errorf("aes.NewCipher failed: %w", err) - } - - gcm, err := cipher.NewGCM(block) - if err != nil { - return nil, fmt.Errorf("cipher.NewGCM failed: %w", err) - } - - nonce := make([]byte, gcm.NonceSize()) - if _, err := io.ReadFull(rand.Reader, nonce); err != nil { - return nil, fmt.Errorf("nonce generation failed: %w", err) - } - - return gcm.Seal(nonce, nonce, data, nil), nil -} - -func (e MLKEMEncryptor1024Legacy) PublicKeyInPemFormat() (string, error) { - return "", errors.New("public key PEM not available for ML-KEM encryptor") -} diff --git a/lib/ocrypto/hybrid_nist.go b/lib/ocrypto/hybrid_nist.go index 223100de70..ee0a575ac5 100644 --- a/lib/ocrypto/hybrid_nist.go +++ b/lib/ocrypto/hybrid_nist.go @@ -6,7 +6,6 @@ import ( "crypto/rand" "crypto/sha256" "encoding/asn1" - "errors" "fmt" "io" @@ -518,10 +517,3 @@ func deriveHybridNISTWrapKey(combinedSecret, salt, info []byte) ([]byte, error) return derivedKey, nil } - -func (d *HybridNISTDecryptor) DecryptWithEphemeralKey(data, ephemeral []byte) ([]byte, error) { - if len(ephemeral) > 0 { - return nil, errors.New("ephemeral key should not be provided for hybrid NIST decryption") - } - return d.Decrypt(data) -} diff --git a/lib/ocrypto/mlkem.go b/lib/ocrypto/mlkem.go index e64288dfa3..3df647aaa1 100644 --- a/lib/ocrypto/mlkem.go +++ b/lib/ocrypto/mlkem.go @@ -5,7 +5,6 @@ import ( "crypto/sha256" "encoding/asn1" "encoding/pem" - "errors" "fmt" "io" @@ -21,6 +20,11 @@ const ( MLKEM1024CiphertextSize = 1568 // mlkem1024 ciphertext mlkemWrapKeySize = 32 // AES-256 key size for wrap key derivation + + PEMBlockMLKEM768PublicKey = "MLKEM768 PUBLIC KEY" + PEMBlockMLKEM768PrivateKey = "MLKEM768 PRIVATE KEY" + PEMBlockMLKEM1024PublicKey = "MLKEM1024 PUBLIC KEY" + PEMBlockMLKEM1024PrivateKey = "MLKEM1024 PRIVATE KEY" ) type MLKEMWrappedKey struct { @@ -112,13 +116,6 @@ func (d *MLKEMDecryptor768) Decrypt(data []byte) ([]byte, error) { return mlkem768UnwrapDEK(d.privateKey, data, d.salt, d.info) } -func (d *MLKEMDecryptor768) DecryptWithEphemeralKey(data, ephemeral []byte) ([]byte, error) { - if len(ephemeral) > 0 { - return nil, errors.New("ephemeral key should not be provided for ML-KEM-768 decryption") - } - return d.Decrypt(data) -} - func NewMLKEM1024Encryptor(publicKey, salt, info []byte) (*MLKEMEncryptor1024, error) { if len(publicKey) != MLKEM1024PublicKeySize { return nil, fmt.Errorf("invalid ML-KEM-1024 public key size: got %d want %d", len(publicKey), MLKEM1024PublicKeySize) @@ -179,13 +176,6 @@ func (d *MLKEMDecryptor1024) Decrypt(data []byte) ([]byte, error) { return mlkem1024UnwrapDEK(d.privateKey, data, d.salt, d.info) } -func (d *MLKEMDecryptor1024) DecryptWithEphemeralKey(data, ephemeral []byte) ([]byte, error) { - if len(ephemeral) > 0 { - return nil, errors.New("ephemeral key should not be provided for ML-KEM-1024 decryption") - } - return d.Decrypt(data) -} - func MLKEM768WrapDEK(publicKeyRaw, dek []byte) ([]byte, error) { return mlkem768WrapDEK(publicKeyRaw, dek, defaultTDFSalt(), nil) } diff --git a/lib/ocrypto/mlkem_test.go b/lib/ocrypto/mlkem_test.go index 5450d920e7..bc85f72dad 100644 --- a/lib/ocrypto/mlkem_test.go +++ b/lib/ocrypto/mlkem_test.go @@ -171,20 +171,6 @@ func TestMLKEMEncryptorImplementsInterface(t *testing.T) { assert.Empty(t, metadata) } -func TestMLKEMDecryptorImplementsInterface(t *testing.T) { - keyPair, err := NewMLKEMKeyPair() - require.NoError(t, err) - - privateKeyBytes := keyPair.PrivateKey.Bytes() - - decryptor, err := NewMLKEM768Decryptor(privateKeyBytes) - require.NoError(t, err) - - _, err = decryptor.DecryptWithEphemeralKey([]byte("data"), []byte("ephemeral")) - require.Error(t, err) - assert.Contains(t, err.Error(), "ephemeral key should not be provided") -} - func TestMLKEM768Encapsulate(t *testing.T) { keyPair, err := NewMLKEMKeyPair() require.NoError(t, err) diff --git a/lib/ocrypto/xwing.go b/lib/ocrypto/xwing.go index 1c4a57a89c..1ea9d5ab25 100644 --- a/lib/ocrypto/xwing.go +++ b/lib/ocrypto/xwing.go @@ -5,7 +5,6 @@ import ( "crypto/sha256" "encoding/asn1" "encoding/pem" - "errors" "fmt" "io" @@ -259,10 +258,3 @@ func decodeSizedPEMBlock(data []byte, blockType string, expectedSize int) ([]byt return append([]byte(nil), block.Bytes...), nil } - -func (d *XWingDecryptor) DecryptWithEphemeralKey(data, ephemeral []byte) ([]byte, error) { - if len(ephemeral) > 0 { - return nil, errors.New("ephemeral key should not be provided for X-Wing decryption") - } - return d.Decrypt(data) -} diff --git a/service/internal/security/basic_manager.go b/service/internal/security/basic_manager.go index 79608a20a5..7de5df14f3 100644 --- a/service/internal/security/basic_manager.go +++ b/service/internal/security/basic_manager.go @@ -149,7 +149,10 @@ func (b *BasicManager) Decrypt(ctx context.Context, keyDetails trust.KeyDetails, } return protectedKey, nil case ocrypto.MLKEM768Key, ocrypto.MLKEM1024Key: - plaintext, err := decrypter.DecryptWithEphemeralKey(ciphertext, ephemeralPublicKey) + if len(ephemeralPublicKey) > 0 { + return nil, errors.New("ephemeral public key should not be provided for ML-KEM decryption") + } + plaintext, err := decrypter.Decrypt(ciphertext) if err != nil { return nil, fmt.Errorf("failed to decrypt with ML-KEM: %w", err) } diff --git a/service/internal/security/standard_crypto.go b/service/internal/security/standard_crypto.go index 5752331fd0..74cdaf47c9 100644 --- a/service/internal/security/standard_crypto.go +++ b/service/internal/security/standard_crypto.go @@ -570,8 +570,8 @@ func (s *StandardCrypto) Decrypt(_ context.Context, keyID trust.KeyIdentifier, c } case StandardMLKEMCrypto: - if len(ephemeralPublicKey) == 0 { - return nil, errors.New("ephemeral public key (ciphertext) is required for ML-KEM decryption") + if len(ephemeralPublicKey) > 0 { + return nil, errors.New("ephemeral public key should not be provided for ML-KEM decryption") } decryptor, err := ocrypto.FromPrivatePEM(key.mlkemPrivateKeyPem) @@ -579,7 +579,7 @@ func (s *StandardCrypto) Decrypt(_ context.Context, keyID trust.KeyIdentifier, c return nil, fmt.Errorf("failed to create ML-KEM decryptor from PEM: %w", err) } - rawKey, err = decryptor.DecryptWithEphemeralKey(ciphertext, ephemeralPublicKey) + rawKey, err = decryptor.Decrypt(ciphertext) if err != nil { return nil, fmt.Errorf("failed to decrypt with ML-KEM: %w", err) } From cb61c2db45bae4a2192c60612c5e570baeb716f5 Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Thu, 28 May 2026 08:46:55 -0400 Subject: [PATCH 11/23] fix(security): handle StandardMLKEMCrypto in determineKeyType ML-KEM keys loaded through InProcessProvider failed FindKeyByID and Decrypt with "could not determine key type" because the type switch in determineKeyType was missing the StandardMLKEMCrypto case. Add the case and a regression test exercising both mlkem:768 and mlkem:1024 through determineKeyType and FindKeyByID. Co-Authored-By: Claude Opus 4.7 Signed-off-by: Dave Mihalcik --- .../internal/security/in_process_provider.go | 5 +- .../security/in_process_provider_test.go | 23 ++++++++ .../internal/security/test_helpers_test.go | 57 +++++++++++++++++++ 3 files changed, 84 insertions(+), 1 deletion(-) diff --git a/service/internal/security/in_process_provider.go b/service/internal/security/in_process_provider.go index b228725a3b..7fe2f9c49b 100644 --- a/service/internal/security/in_process_provider.go +++ b/service/internal/security/in_process_provider.go @@ -214,7 +214,8 @@ func (a *InProcessProvider) ListKeysWith(ctx context.Context, opts trust.ListKey } } else if err != nil { if a.logger != nil { - a.logger.WarnContext(ctx, + a.logger.WarnContext( + ctx, "failed to list keys by algorithm", slog.String("algorithm", alg), slog.Any("error", err), @@ -357,6 +358,8 @@ func (a *InProcessProvider) determineKeyType(kid string) (string, error) { return key.Algorithm, nil case StandardHybridCrypto: return key.Algorithm, nil + case StandardMLKEMCrypto: + return key.Algorithm, nil } return "", errors.New("could not determine key type") diff --git a/service/internal/security/in_process_provider_test.go b/service/internal/security/in_process_provider_test.go index 9870ad53bb..277a4348ea 100644 --- a/service/internal/security/in_process_provider_test.go +++ b/service/internal/security/in_process_provider_test.go @@ -229,3 +229,26 @@ 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()) +} 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) From a160c1c450fb374cfafee0a7fa4cc74b597cd45b Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Thu, 28 May 2026 11:47:02 -0400 Subject: [PATCH 12/23] perf(security): pre-parse ML-KEM private key in loadKey Cache the ML-KEM PrivateKeyDecryptor on StandardMLKEMCrypto during loadKey instead of re-parsing the PEM on every Decrypt call. Mirrors the existing RSA pattern. Also fixes a latent bug in ocrypto.MLKEMKeyPair PEM writers: they emitted "MLKEM DECAPSULATION KEY" / "MLKEM ENCAPSULATOR" block types, but commit 40d10ce3 removed parser support for those headers. Updated the writers to use the PEMBlockMLKEM{768,1024}{Private,Public}Key constants the parser now recognizes. Signed-off-by: Dave Mihalcik --- lib/ocrypto/ec_key_pair.go | 8 ++++---- service/internal/security/standard_crypto.go | 19 +++++++++++-------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/lib/ocrypto/ec_key_pair.go b/lib/ocrypto/ec_key_pair.go index e0b9aefecd..ccac3d7dff 100644 --- a/lib/ocrypto/ec_key_pair.go +++ b/lib/ocrypto/ec_key_pair.go @@ -551,7 +551,7 @@ func (keyPair MLKEMKeyPair) PrivateKeyInPemFormat() (string, error) { privateKeyPEM := pem.EncodeToMemory( &pem.Block{ - Type: "MLKEM DECAPSULATION KEY", + Type: PEMBlockMLKEM768PrivateKey, Bytes: keyPair.PrivateKey.Bytes(), }, ) @@ -565,7 +565,7 @@ func (keyPair MLKEMKeyPair) PublicKeyInPemFormat() (string, error) { publicKeyPEM := pem.EncodeToMemory( &pem.Block{ - Type: "MLKEM ENCAPSULATOR", + Type: PEMBlockMLKEM768PublicKey, Bytes: keyPair.PrivateKey.EncapsulationKey().Bytes(), }, ) @@ -583,7 +583,7 @@ func (keyPair MLKEM1024KeyPair) PrivateKeyInPemFormat() (string, error) { privateKeyPEM := pem.EncodeToMemory( &pem.Block{ - Type: "MLKEM DECAPSULATION KEY", + Type: PEMBlockMLKEM1024PrivateKey, Bytes: keyPair.PrivateKey.Bytes(), }, ) @@ -597,7 +597,7 @@ func (keyPair MLKEM1024KeyPair) PublicKeyInPemFormat() (string, error) { publicKeyPEM := pem.EncodeToMemory( &pem.Block{ - Type: "MLKEM ENCAPSULATOR", + Type: PEMBlockMLKEM1024PublicKey, Bytes: keyPair.PrivateKey.EncapsulationKey().Bytes(), }, ) diff --git a/service/internal/security/standard_crypto.go b/service/internal/security/standard_crypto.go index 74cdaf47c9..6a574eac69 100644 --- a/service/internal/security/standard_crypto.go +++ b/service/internal/security/standard_crypto.go @@ -83,6 +83,7 @@ type StandardMLKEMCrypto struct { KeyPairInfo mlkemPrivateKeyPem string mlkemPublicKeyPem string + decryptor ocrypto.PrivateKeyDecryptor } // List of keys by identifier @@ -126,7 +127,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), ) @@ -181,10 +183,15 @@ func loadKey(k KeyPairInfo) (any, error) { hybridPublicKeyPem: string(certPEM), }, nil case AlgorithmMLKEM768, AlgorithmMLKEM1024: + decryptor, err := ocrypto.FromPrivatePEM(string(privatePEM)) + if err != nil { + return nil, fmt.Errorf("ocrypto.FromPrivatePEM (ML-KEM) failed: %w", err) + } return StandardMLKEMCrypto{ KeyPairInfo: k, mlkemPrivateKeyPem: string(privatePEM), mlkemPublicKeyPem: string(certPEM), + decryptor: decryptor, }, nil case AlgorithmRSA2048, AlgorithmRSA4096: asymDecryption, err := ocrypto.NewAsymDecryption(string(privatePEM)) @@ -259,7 +266,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), ) @@ -574,12 +582,7 @@ func (s *StandardCrypto) Decrypt(_ context.Context, keyID trust.KeyIdentifier, c return nil, errors.New("ephemeral public key should not be provided for ML-KEM decryption") } - decryptor, err := ocrypto.FromPrivatePEM(key.mlkemPrivateKeyPem) - if err != nil { - return nil, fmt.Errorf("failed to create ML-KEM decryptor from PEM: %w", err) - } - - rawKey, err = decryptor.Decrypt(ciphertext) + rawKey, err = key.decryptor.Decrypt(ciphertext) if err != nil { return nil, fmt.Errorf("failed to decrypt with ML-KEM: %w", err) } From fdb7030a858fd1a4ee2686b994af29b7a1fb1da7 Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Thu, 28 May 2026 11:48:15 -0400 Subject: [PATCH 13/23] feat(kasregistry): allow ML-KEM algorithms in ListKeysRequest filter The key_algorithm CEL validation on ListKeysRequest only accepted algorithm values 0-8, which excluded the post-quantum ML-KEM-768 (20) and ML-KEM-1024 (21) values. Filtering by these algorithms returned a validation error even though the server could store and serve them. RotateKeyRequest.NewKey already includes 20 and 21 in its allowed set. Signed-off-by: Dave Mihalcik --- .../key_access_server_registry.openapi.yaml | 2 +- .../key_access_server_registry.pb.go | 841 +++++++++--------- .../key_access_server_registry.proto | 2 +- 3 files changed, 423 insertions(+), 422 deletions(-) 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 17a1e669c5..1c516a37c5 100644 --- a/docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml +++ b/docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml @@ -1732,7 +1732,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' 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 676874d83c..ed58169736 100644 --- a/protocol/go/policy/kasregistry/key_access_server_registry.pb.go +++ b/protocol/go/policy/kasregistry/key_access_server_registry.pb.go @@ -4394,441 +4394,442 @@ var file_policy_kasregistry_key_access_server_registry_proto_rawDesc = []byte{ 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, 0xde, 0x03, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0xb0, 0x01, 0x0a, 0x0d, 0x6b, 0x65, 0x79, + 0x73, 0x4b, 0x65, 0x79, 0x22, 0xe7, 0x03, 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, 0x78, 0xba, 0x48, 0x75, 0xba, 0x01, 0x72, 0x0a, 0x15, 0x6b, 0x65, 0x79, - 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, - 0x65, 0x64, 0x12, 0x34, 0x54, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, - 0x72, 0x69, 0x74, 0x68, 0x6d, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x6f, 0x6e, - 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, - 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x1a, 0x23, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, - 0x6e, 0x20, 0x5b, 0x30, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x34, - 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x36, 0x2c, 0x20, 0x37, 0x2c, 0x20, 0x38, 0x5d, 0x52, 0x0c, 0x6b, - 0x65, 0x79, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x21, 0x0a, 0x06, 0x6b, - 0x61, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, - 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x05, 0x6b, 0x61, 0x73, 0x49, 0x64, 0x12, 0x24, - 0x0a, 0x08, 0x6b, 0x61, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x48, 0x00, 0x52, 0x07, 0x6b, 0x61, 0x73, - 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x07, 0x6b, 0x61, 0x73, 0x5f, 0x75, 0x72, 0x69, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0x72, 0x05, 0x10, 0x01, 0x88, 0x01, - 0x01, 0x48, 0x00, 0x52, 0x06, 0x6b, 0x61, 0x73, 0x55, 0x72, 0x69, 0x12, 0x1b, 0x0a, 0x06, 0x6c, - 0x65, 0x67, 0x61, 0x63, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x48, 0x01, 0x52, 0x06, 0x6c, - 0x65, 0x67, 0x61, 0x63, 0x79, 0x88, 0x01, 0x01, 0x12, 0x33, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, - 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, - 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3d, 0x0a, - 0x04, 0x73, 0x6f, 0x72, 0x74, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, - 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x73, 0x53, 0x6f, 0x72, 0x74, 0x42, 0x08, 0xba, 0x48, - 0x05, 0x92, 0x01, 0x02, 0x10, 0x01, 0x52, 0x04, 0x73, 0x6f, 0x72, 0x74, 0x42, 0x0c, 0x0a, 0x0a, - 0x6b, 0x61, 0x73, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6c, - 0x65, 0x67, 0x61, 0x63, 0x79, 0x22, 0x73, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x08, 0x6b, 0x61, 0x73, - 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x52, 0x07, 0x6b, 0x61, 0x73, - 0x4b, 0x65, 0x79, 0x73, 0x12, 0x34, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0a, - 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x86, 0x03, 0x0a, 0x10, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x18, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, - 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0x33, 0x0a, 0x08, 0x6d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, - 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4d, 0x75, 0x74, - 0x61, 0x62, 0x6c, 0x65, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x54, - 0x0a, 0x18, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x5f, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 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, 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, + 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, 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/service/policy/kasregistry/key_access_server_registry.proto b/service/policy/kasregistry/key_access_server_registry.proto index 61fec5965e..4bf9a40f12 100644 --- a/service/policy/kasregistry/key_access_server_registry.proto +++ b/service/policy/kasregistry/key_access_server_registry.proto @@ -477,7 +477,7 @@ message ListKeysRequest { Algorithm key_algorithm = 1 [(buf.validate.field).cel = { id: "key_algorithm_defined" message: "The key_algorithm must be one of the defined values." - expression: "this in [0, 1, 2, 3, 4, 5, 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 { From 53a89590e9babdee7e177051fb06bf99a04e1dc3 Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Thu, 28 May 2026 12:21:45 -0400 Subject: [PATCH 14/23] fix(security): export ML-KEM public keys from InProcessProvider ExportPublicKey fell through RSA/Hybrid/XWing to ECPublicKey, so pure ML-KEM keys returned ErrCertNotFound and KAS PublicKey requests for mlkem:768/mlkem:1024 failed with not_found. Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: Dave Mihalcik --- service/internal/security/in_process_provider.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/service/internal/security/in_process_provider.go b/service/internal/security/in_process_provider.go index 7fe2f9c49b..1875af78bb 100644 --- a/service/internal/security/in_process_provider.go +++ b/service/internal/security/in_process_provider.go @@ -90,6 +90,9 @@ func (k *KeyDetailsAdapter) ExportPublicKey(_ context.Context, format trust.KeyT if xwingKey, err := k.cryptoProvider.XWingPublicKey(kid); err == nil { return xwingKey, nil } + if mlkemKey, err := k.cryptoProvider.MLKEMPublicKey(kid); err == nil { + return mlkemKey, nil + } return k.cryptoProvider.ECPublicKey(kid) default: return "", ErrCertNotFound From 337716f496c068379a06cf2a81c28ad07c1c9b62 Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Thu, 28 May 2026 15:51:59 -0400 Subject: [PATCH 15/23] fix(security): allow pure ML-KEM rewrap through InProcessProvider InProcessProvider.Decrypt rejected empty ephemeralPublicKey for ML-KEM, but StandardCrypto.Decrypt and BasicManager.Decrypt both reject non-empty values. The KEM ciphertext is the encapsulation; there is no separate ephemeral key. Invert the check to match the HPQT case above and pass nil downstream. Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: Dave Mihalcik --- service/internal/security/in_process_provider.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/service/internal/security/in_process_provider.go b/service/internal/security/in_process_provider.go index 1875af78bb..51b92b3227 100644 --- a/service/internal/security/in_process_provider.go +++ b/service/internal/security/in_process_provider.go @@ -266,10 +266,10 @@ 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 (ciphertext) is required for ML-KEM decryption") + 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, ephemeralPublicKey) + return a.cryptoProvider.Decrypt(ctx, trust.KeyIdentifier(kid), ciphertext, nil) default: return nil, errors.New("unsupported key algorithm") From 2678fec697be25bd911a9e4bf08d8938f345a4b8 Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Thu, 28 May 2026 15:52:51 -0400 Subject: [PATCH 16/23] fix(ocrypto): emit canonical PEM block types from ML-KEM encryptors MLKEMEncryptor768/1024.PublicKeyInPemFormat emitted "MLKEM ENCAPSULATOR", but FromPublicPEMWithSalt dispatches on PEMBlockMLKEM768PublicKey / PEMBlockMLKEM1024PublicKey, breaking PEM round-trip. Use the canonical constants to match the X-Wing pattern and the existing MLKEMKeyPair serialization in ec_key_pair.go. Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: Dave Mihalcik --- lib/ocrypto/mlkem.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ocrypto/mlkem.go b/lib/ocrypto/mlkem.go index 3df647aaa1..e87150aebf 100644 --- a/lib/ocrypto/mlkem.go +++ b/lib/ocrypto/mlkem.go @@ -74,7 +74,7 @@ func (e *MLKEMEncryptor768) Encrypt(data []byte) ([]byte, error) { func (e *MLKEMEncryptor768) PublicKeyInPemFormat() (string, error) { pemBlock := &pem.Block{ - Type: "MLKEM ENCAPSULATOR", + Type: PEMBlockMLKEM768PublicKey, Bytes: e.publicKey, } return string(pem.EncodeToMemory(pemBlock)), nil @@ -134,7 +134,7 @@ func (e *MLKEMEncryptor1024) Encrypt(data []byte) ([]byte, error) { func (e *MLKEMEncryptor1024) PublicKeyInPemFormat() (string, error) { pemBlock := &pem.Block{ - Type: "MLKEM ENCAPSULATOR", + Type: PEMBlockMLKEM1024PublicKey, Bytes: e.publicKey, } return string(pem.EncodeToMemory(pemBlock)), nil From 67678391052fbd96f45481f3aa90d589703b471d Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Thu, 28 May 2026 15:53:42 -0400 Subject: [PATCH 17/23] test(security): cover missing-kid case in ML-KEM determineKeyType Mirrors the negative assertion already in TestInProcessProviderDetermineKeyType. Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: Dave Mihalcik --- service/internal/security/in_process_provider_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/service/internal/security/in_process_provider_test.go b/service/internal/security/in_process_provider_test.go index 277a4348ea..e56eca2cfc 100644 --- a/service/internal/security/in_process_provider_test.go +++ b/service/internal/security/in_process_provider_test.go @@ -251,4 +251,7 @@ func TestInProcessProviderDetermineKeyTypeMLKEM(t *testing.T) { 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) } From 135a0efc831254d03cd61fdeecfc9b4d90d3d82c Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Fri, 29 May 2026 09:32:55 -0400 Subject: [PATCH 18/23] refactor(ocrypto): encode pure ML-KEM keys as SPKI/PKCS#8 with NIST OIDs Replace the custom "MLKEM768 PUBLIC KEY" / "MLKEM1024 PRIVATE KEY" PEM labels (raw key bytes with no ASN.1 envelope) with standard "PUBLIC KEY" and "PRIVATE KEY" labels carrying RFC 5280 SubjectPublicKeyInfo and RFC 5958 OneAsymmetricKey, with the algorithm conveyed by NIST OIDs 2.16.840.1.101.3.4.4.2 (ML-KEM-768) and 2.16.840.1.101.3.4.4.3 (ML-KEM-1024). The private-key PKCS#8 inner CHOICE uses [0] IMPLICIT OCTET STRING (seed form, 64 bytes) per draft-ietf-lamps-kyber-certificates. The encoding is hand-rolled rather than via crypto/x509 because stdlib ML-KEM support in MarshalPKIXPublicKey / MarshalPKCS8PrivateKey landed in Go 1.26 and this module pins go 1.25. FromPublicPEMWithSalt / FromPrivatePEMWithSalt now peek at the OID after PEM decode and route ML-KEM blobs to the existing encryptor/decryptor constructors. Non-ML-KEM blobs fall through to the existing RSA/EC parsers unchanged. The hybrid SECP256R1-MLKEM768, SECP384R1-MLKEM1024, and X-Wing schemes keep their custom PEM labels for now; conformance to IETF composite-KEM and X-Wing drafts is tracked under DSPX-3396 and will land in a separate PR off main. Co-Authored-By: Claude Opus 4.7 Signed-off-by: Dave Mihalcik --- lib/ocrypto/asym_decryption.go | 13 +++- lib/ocrypto/asym_encryption.go | 15 ++-- lib/ocrypto/ec_key_pair.go | 56 ++++++-------- lib/ocrypto/mlkem.go | 130 ++++++++++++++++++++++++++++++--- lib/ocrypto/mlkem_test.go | 70 ++++++++++++++++++ lib/ocrypto/rsa_key_pair.go | 4 +- 6 files changed, 233 insertions(+), 55 deletions(-) diff --git a/lib/ocrypto/asym_decryption.go b/lib/ocrypto/asym_decryption.go index 6656c92be9..93e2b158b0 100644 --- a/lib/ocrypto/asym_decryption.go +++ b/lib/ocrypto/asym_decryption.go @@ -50,10 +50,15 @@ func FromPrivatePEMWithSalt(privateKeyInPem string, salt, info []byte) (PrivateK return NewSaltedP256MLKEM768Decryptor(block.Bytes, salt, info) case PEMBlockP384MLKEM1024PrivateKey: return NewSaltedP384MLKEM1024Decryptor(block.Bytes, salt, info) - case PEMBlockMLKEM768PrivateKey: - return NewSaltedMLKEM768Decryptor(block.Bytes, salt, info) - case PEMBlockMLKEM1024PrivateKey: - return NewSaltedMLKEM1024Decryptor(block.Bytes, salt, info) + } + + switch oid, seed, err := parseMLKEMPrivatePKCS8(block.Bytes); { + case err == nil && oid.Equal(oidMLKEM768): + return NewSaltedMLKEM768Decryptor(seed, salt, info) + case err == nil && oid.Equal(oidMLKEM1024): + return NewSaltedMLKEM1024Decryptor(seed, salt, info) + case err != nil && !errors.Is(err, errNotMLKEM): + return AsymDecryption{}, err } priv, err := x509.ParsePKCS8PrivateKey(block.Bytes) diff --git a/lib/ocrypto/asym_encryption.go b/lib/ocrypto/asym_encryption.go index e3529084b3..dd90543df2 100644 --- a/lib/ocrypto/asym_encryption.go +++ b/lib/ocrypto/asym_encryption.go @@ -82,10 +82,15 @@ func FromPublicPEMWithSalt(publicKeyInPem string, salt, info []byte) (PublicKeyE return NewP256MLKEM768Encryptor(block.Bytes, salt, info) case PEMBlockP384MLKEM1024PublicKey: return NewP384MLKEM1024Encryptor(block.Bytes, salt, info) - case PEMBlockMLKEM768PublicKey: - return NewMLKEM768Encryptor(block.Bytes, salt, info) - case PEMBlockMLKEM1024PublicKey: - return NewMLKEM1024Encryptor(block.Bytes, salt, info) + } + + switch oid, key, err := parseMLKEMPublicSPKI(block.Bytes); { + case err == nil && oid.Equal(oidMLKEM768): + return NewMLKEM768Encryptor(key, salt, info) + case err == nil && oid.Equal(oidMLKEM1024): + return NewMLKEM1024Encryptor(key, salt, info) + case err != nil && !errors.Is(err, errNotMLKEM): + return nil, err } pub, err := getPublicPart(publicKeyInPem) @@ -242,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/ec_key_pair.go b/lib/ocrypto/ec_key_pair.go index ccac3d7dff..d28ad46799 100644 --- a/lib/ocrypto/ec_key_pair.go +++ b/lib/ocrypto/ec_key_pair.go @@ -228,7 +228,7 @@ func (keyPair ECKeyPair) PrivateKeyInPemFormat() (string, error) { privateKeyPem := pem.EncodeToMemory( &pem.Block{ - Type: "PRIVATE KEY", + Type: pemBlockPrivateKey, Bytes: privateKeyBytes, }, ) @@ -248,7 +248,7 @@ func (keyPair ECKeyPair) PublicKeyInPemFormat() (string, error) { publicKeyPem := pem.EncodeToMemory( &pem.Block{ - Type: "PUBLIC KEY", + Type: pemBlockPublicKey, Bytes: publicKeyBytes, }, ) @@ -468,7 +468,7 @@ func ECPrivateKeyInPemFormat(privateKey ecdsa.PrivateKey) (string, error) { privateKeyPem := pem.EncodeToMemory( &pem.Block{ - Type: "PRIVATE KEY", + Type: pemBlockPrivateKey, Bytes: privateKeyBytes, }, ) @@ -484,7 +484,7 @@ func ECPublicKeyInPemFormat(publicKey ecdsa.PublicKey) (string, error) { publicKeyPem := pem.EncodeToMemory( &pem.Block{ - Type: "PUBLIC KEY", + Type: pemBlockPublicKey, Bytes: pkb, }, ) @@ -549,13 +549,11 @@ func (keyPair MLKEMKeyPair) PrivateKeyInPemFormat() (string, error) { return "", errors.New("failed to generate PEM formatted private key") } - privateKeyPEM := pem.EncodeToMemory( - &pem.Block{ - Type: PEMBlockMLKEM768PrivateKey, - Bytes: keyPair.PrivateKey.Bytes(), - }, - ) - return string(privateKeyPEM), nil + der, err := marshalMLKEMPrivatePKCS8(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) { @@ -563,13 +561,11 @@ func (keyPair MLKEMKeyPair) PublicKeyInPemFormat() (string, error) { return "", errors.New("failed to generate PEM formatted public key") } - publicKeyPEM := pem.EncodeToMemory( - &pem.Block{ - Type: PEMBlockMLKEM768PublicKey, - Bytes: keyPair.PrivateKey.EncapsulationKey().Bytes(), - }, - ) - return string(publicKeyPEM), nil + der, err := marshalMLKEMPublicSPKI(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 { @@ -581,13 +577,11 @@ func (keyPair MLKEM1024KeyPair) PrivateKeyInPemFormat() (string, error) { return "", errors.New("failed to generate PEM formatted private key") } - privateKeyPEM := pem.EncodeToMemory( - &pem.Block{ - Type: PEMBlockMLKEM1024PrivateKey, - Bytes: keyPair.PrivateKey.Bytes(), - }, - ) - return string(privateKeyPEM), nil + der, err := marshalMLKEMPrivatePKCS8(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) { @@ -595,13 +589,11 @@ func (keyPair MLKEM1024KeyPair) PublicKeyInPemFormat() (string, error) { return "", errors.New("failed to generate PEM formatted public key") } - publicKeyPEM := pem.EncodeToMemory( - &pem.Block{ - Type: PEMBlockMLKEM1024PublicKey, - Bytes: keyPair.PrivateKey.EncapsulationKey().Bytes(), - }, - ) - return string(publicKeyPEM), nil + der, err := marshalMLKEMPublicSPKI(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 { diff --git a/lib/ocrypto/mlkem.go b/lib/ocrypto/mlkem.go index e87150aebf..aca3f9b610 100644 --- a/lib/ocrypto/mlkem.go +++ b/lib/ocrypto/mlkem.go @@ -5,12 +5,24 @@ import ( "crypto/sha256" "encoding/asn1" "encoding/pem" + "errors" "fmt" "io" "golang.org/x/crypto/hkdf" ) +// PEM block types defined by RFC 7468 for SPKI / PKCS#8 envelopes. +const ( + pemBlockPublicKey = "PUBLIC KEY" + pemBlockPrivateKey = "PRIVATE KEY" +) + +// errNotMLKEM is returned by the ML-KEM SPKI / PKCS#8 parsers when the supplied +// DER blob is not an ML-KEM key, signalling the caller to fall through to +// other algorithm parsers. +var errNotMLKEM = errors.New("not an ML-KEM key") + const ( MLKEM768PublicKeySize = 1184 // mlkem768 encapsulation key MLKEM768PrivateKeySize = 64 // mlkem768 seed (d || z) @@ -20,13 +32,107 @@ const ( MLKEM1024CiphertextSize = 1568 // mlkem1024 ciphertext mlkemWrapKeySize = 32 // AES-256 key size for wrap key derivation +) - PEMBlockMLKEM768PublicKey = "MLKEM768 PUBLIC KEY" - PEMBlockMLKEM768PrivateKey = "MLKEM768 PRIVATE KEY" - PEMBlockMLKEM1024PublicKey = "MLKEM1024 PUBLIC KEY" - PEMBlockMLKEM1024PrivateKey = "MLKEM1024 PRIVATE KEY" +// 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 mlkemAlgorithmIdentifier struct { + Algorithm asn1.ObjectIdentifier +} + +type mlkemSPKI struct { + Algorithm mlkemAlgorithmIdentifier + PublicKey asn1.BitString +} + +// mlkemPKCS8 mirrors RFC 5958 OneAsymmetricKey v1. +type mlkemPKCS8 struct { + Version int + Algorithm mlkemAlgorithmIdentifier + PrivateKey []byte +} + +const bitsPerByte = 8 + +// marshalMLKEMPublicSPKI encodes a raw ML-KEM encapsulation key as RFC 5280 SubjectPublicKeyInfo. +func marshalMLKEMPublicSPKI(oid asn1.ObjectIdentifier, rawKey []byte) ([]byte, error) { + return asn1.Marshal(mlkemSPKI{ + Algorithm: mlkemAlgorithmIdentifier{Algorithm: oid}, + PublicKey: asn1.BitString{Bytes: rawKey, BitLength: len(rawKey) * bitsPerByte}, + }) +} + +// marshalMLKEMPrivatePKCS8 encodes the ML-KEM seed as RFC 5958 OneAsymmetricKey, +// with the inner ML-KEM-PrivateKey CHOICE selected as [0] IMPLICIT OCTET STRING (seed). +func marshalMLKEMPrivatePKCS8(oid asn1.ObjectIdentifier, seed []byte) ([]byte, error) { + inner, err := asn1.MarshalWithParams(seed, "tag:0,implicit") + if err != nil { + return nil, fmt.Errorf("asn1.MarshalWithParams seed failed: %w", err) + } + return asn1.Marshal(mlkemPKCS8{ + Version: 0, + Algorithm: mlkemAlgorithmIdentifier{Algorithm: oid}, + PrivateKey: inner, + }) +} + +// 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. If the blob is +// not ML-KEM the sentinel errNotMLKEM is returned so the caller can fall +// through to other parsers. +func parseMLKEMPublicSPKI(der []byte) (asn1.ObjectIdentifier, []byte, error) { + var s mlkemSPKI + rest, err := asn1.Unmarshal(der, &s) + if err != nil || len(rest) != 0 { + return nil, nil, errNotMLKEM + } + var oid asn1.ObjectIdentifier + switch { + case s.Algorithm.Algorithm.Equal(oidMLKEM768): + oid = oidMLKEM768 + case s.Algorithm.Algorithm.Equal(oidMLKEM1024): + oid = oidMLKEM1024 + default: + return nil, nil, errNotMLKEM + } + if s.PublicKey.BitLength%bitsPerByte != 0 { + return nil, nil, errors.New("ML-KEM SPKI bit string is not byte-aligned") + } + return oid, s.PublicKey.RightAlign(), nil +} + +// parseMLKEMPrivatePKCS8 returns the OID and raw seed bytes from a PKCS#8 DER +// blob if the algorithm is ML-KEM-768 or ML-KEM-1024. If the blob is not +// ML-KEM the sentinel errNotMLKEM is returned so the caller can fall through +// to other parsers. +func parseMLKEMPrivatePKCS8(der []byte) (asn1.ObjectIdentifier, []byte, error) { + var p mlkemPKCS8 + rest, err := asn1.Unmarshal(der, &p) + if err != nil || len(rest) != 0 { + return nil, nil, errNotMLKEM + } + var oid asn1.ObjectIdentifier + switch { + case p.Algorithm.Algorithm.Equal(oidMLKEM768): + oid = oidMLKEM768 + case p.Algorithm.Algorithm.Equal(oidMLKEM1024): + oid = oidMLKEM1024 + default: + return nil, nil, errNotMLKEM + } + + var innerSeed []byte + innerRest, err := asn1.UnmarshalWithParams(p.PrivateKey, &innerSeed, "tag:0,implicit") + if err != nil || len(innerRest) != 0 { + return nil, nil, fmt.Errorf("ML-KEM PKCS#8 inner seed parse failed: %w", err) + } + return oid, innerSeed, nil +} + type MLKEMWrappedKey struct { MLKEMCiphertext []byte `asn1:"tag:0"` EncryptedDEK []byte `asn1:"tag:1"` @@ -73,11 +179,11 @@ func (e *MLKEMEncryptor768) Encrypt(data []byte) ([]byte, error) { } func (e *MLKEMEncryptor768) PublicKeyInPemFormat() (string, error) { - pemBlock := &pem.Block{ - Type: PEMBlockMLKEM768PublicKey, - Bytes: e.publicKey, + der, err := marshalMLKEMPublicSPKI(oidMLKEM768, e.publicKey) + if err != nil { + return "", fmt.Errorf("marshal ML-KEM-768 SPKI failed: %w", err) } - return string(pem.EncodeToMemory(pemBlock)), nil + return string(pem.EncodeToMemory(&pem.Block{Type: pemBlockPublicKey, Bytes: der})), nil } func (e *MLKEMEncryptor768) Type() SchemeType { @@ -133,11 +239,11 @@ func (e *MLKEMEncryptor1024) Encrypt(data []byte) ([]byte, error) { } func (e *MLKEMEncryptor1024) PublicKeyInPemFormat() (string, error) { - pemBlock := &pem.Block{ - Type: PEMBlockMLKEM1024PublicKey, - Bytes: e.publicKey, + der, err := marshalMLKEMPublicSPKI(oidMLKEM1024, e.publicKey) + if err != nil { + return "", fmt.Errorf("marshal ML-KEM-1024 SPKI failed: %w", err) } - return string(pem.EncodeToMemory(pemBlock)), nil + return string(pem.EncodeToMemory(&pem.Block{Type: pemBlockPublicKey, Bytes: der})), nil } func (e *MLKEMEncryptor1024) Type() SchemeType { diff --git a/lib/ocrypto/mlkem_test.go b/lib/ocrypto/mlkem_test.go index bc85f72dad..185c568cc8 100644 --- a/lib/ocrypto/mlkem_test.go +++ b/lib/ocrypto/mlkem_test.go @@ -2,6 +2,8 @@ package ocrypto import ( "encoding/asn1" + "encoding/pem" + "strings" "testing" "github.com/stretchr/testify/assert" @@ -206,3 +208,71 @@ func TestMLKEM1024EncapsulateInvalidKeySize(t *testing.T) { require.Error(t, err) assert.Contains(t, err.Error(), "invalid ML-KEM-1024 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, }, ) From 7ed2955dc9980408e146ac2565c80fff8f82c67a Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Fri, 29 May 2026 15:15:45 -0400 Subject: [PATCH 19/23] feat(sdk): emit mlkem-wrapped type for pure ML-KEM KAOs Add support for generating KAOs with type=mlkem-wrapped when using pure ML-KEM wrapping keys (MLKEM768, MLKEM1024), while maintaining backwards compatibility for reading type=wrapped KAOs. Changes: - Add IsMLKEMKeyType() helper in lib/ocrypto/ec_key_pair.go - Add kMLKEMWrapped constant and ML-KEM case to createKeyAccess() in sdk/tdf.go - Implement generateWrapKeyWithMLKEM() in sdk/tdf.go - Add ML-KEM handling to wrapKeyWithPublicKey() in sdk/experimental/tdf/key_access.go - Implement wrapKeyWithMLKEM() in sdk/experimental/tdf/key_access.go This ensures integration tests pass which expect mlkem-wrapped type for pure ML-KEM keys, while type=wrapped continues for RSA keys. Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: Dave Mihalcik --- lib/ocrypto/ec_key_pair.go | 9 +++++++++ sdk/experimental/tdf/key_access.go | 24 ++++++++++++++++++++++ sdk/tdf.go | 32 +++++++++++++++++++++++++++++- 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/lib/ocrypto/ec_key_pair.go b/lib/ocrypto/ec_key_pair.go index d28ad46799..d55d5f266c 100644 --- a/lib/ocrypto/ec_key_pair.go +++ b/lib/ocrypto/ec_key_pair.go @@ -122,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 diff --git a/sdk/experimental/tdf/key_access.go b/sdk/experimental/tdf/key_access.go index 3974b06cf4..ec26aa6692 100644 --- a/sdk/experimental/tdf/key_access.go +++ b/sdk/experimental/tdf/key_access.go @@ -172,6 +172,10 @@ func wrapKeyWithPublicKey(symKey []byte, pubKeyInfo keysplit.KASPublicKey) (stri // Handle EC key wrapping return wrapKeyWithEC(ktype, pubKeyInfo.PEM, symKey) } + if ocrypto.IsMLKEMKeyType(ktype) { + // Handle ML-KEM key wrapping + return wrapKeyWithMLKEM(ktype, pubKeyInfo.PEM, symKey) + } // Handle RSA key wrapping wrapped, err := wrapKeyWithRSA(pubKeyInfo.PEM, symKey) return wrapped, "wrapped", "", err @@ -256,3 +260,23 @@ func wrapKeyWithHybrid(ktype ocrypto.KeyType, kasPublicKeyPEM string, symKey []b } return string(ocrypto.Base64Encode(wrappedDER)), "hybrid-wrapped", "", nil } + +func wrapKeyWithMLKEM(ktype ocrypto.KeyType, kasPublicKeyPEM string, symKey []byte) (string, string, string, error) { + var wrappedDER []byte + var err error + + switch ktype { + case ocrypto.MLKEM768Key: + wrappedDER, err = ocrypto.MLKEM768WrapDEK([]byte(kasPublicKeyPEM), symKey) + case ocrypto.MLKEM1024Key: + wrappedDER, err = ocrypto.MLKEM1024WrapDEK([]byte(kasPublicKeyPEM), symKey) + default: + return "", "", "", fmt.Errorf("unsupported ML-KEM key type: %s", ktype) + } + + if err != nil { + return "", "", "", fmt.Errorf("mlkem wrap failed: %w", err) + } + + return string(ocrypto.Base64Encode(wrappedDER)), "mlkem-wrapped", "", nil +} diff --git a/sdk/tdf.go b/sdk/tdf.go index 1d6ef1182c..bcee2846f1 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" @@ -695,6 +696,13 @@ func createKeyAccess(kasInfo KASInfo, symKey []byte, policyBinding PolicyBinding keyAccess.KeyType = kECWrapped keyAccess.WrappedKey = wrappedKeyInfo.wrappedKey keyAccess.EphemeralPublicKey = wrappedKeyInfo.publicKey + case ocrypto.IsMLKEMKeyType(ktype): + wrappedKey, err := generateWrapKeyWithMLKEM(kasInfo.Algorithm, kasInfo.PublicKey, symKey) + if err != nil { + return KeyAccess{}, err + } + keyAccess.KeyType = kMLKEMWrapped + keyAccess.WrappedKey = wrappedKey default: wrappedKey, err := generateWrapKeyWithRSA(kasInfo.PublicKey, symKey) if err != nil { @@ -778,6 +786,28 @@ func generateWrapKeyWithHybrid(algorithm, publicKeyPEM string, symKey []byte) (s return string(ocrypto.Base64Encode(wrappedDER)), nil } +func generateWrapKeyWithMLKEM(algorithm, publicKeyPEM string, symKey []byte) (string, error) { + ktype := ocrypto.KeyType(algorithm) + + var wrappedDER []byte + var err error + + switch ktype { + case ocrypto.MLKEM768Key: + wrappedDER, err = ocrypto.MLKEM768WrapDEK([]byte(publicKeyPEM), symKey) + case ocrypto.MLKEM1024Key: + wrappedDER, err = ocrypto.MLKEM1024WrapDEK([]byte(publicKeyPEM), symKey) + default: + return "", fmt.Errorf("unsupported ML-KEM key type: %s", algorithm) + } + + if err != nil { + return "", fmt.Errorf("generateWrapKeyWithMLKEM: %w", err) + } + + return string(ocrypto.Base64Encode(wrappedDER)), nil +} + // create policy object func createPolicyObject(attributes []AttributeValueFQN) (PolicyObject, error) { uuidObj, err := uuid.NewUUID() @@ -1247,7 +1277,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: From 0d98f53a2bd7c4ed02b4865802277ff71a39491b Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Mon, 1 Jun 2026 16:57:20 -0400 Subject: [PATCH 20/23] feat(ocrypto): add format auto-detection for ML-KEM public keys Make MLKEM768WrapDEK and MLKEM1024WrapDEK accept multiple input formats: - Raw key bytes (1184/1568 bytes) - fast path - SPKI DER (1206/1590 bytes) - PEM-wrapped SPKI (~1686/~2206 bytes) This fixes the issue introduced in 6a7480dc where KAS started returning SPKI-encoded PEM keys but callers expected raw bytes. Instead of requiring all callers to decode manually, the crypto library now handles format detection transparently. Changes: - Add normalizeMLKEMPublicKey() helper for format detection - Export ParseMLKEMPublicSPKI() and OidMLKEM768/OidMLKEM1024 constants - Update all internal references to use exported names - Add comprehensive tests for format handling Benefits: - Backward compatible (raw keys still work) - Simpler callers (no manual PEM decoding needed) - Better encapsulation (format logic in crypto library) - Future-proof (handles new formats automatically) Co-Authored-By: Claude Sonnet 4.5 --- lib/ocrypto/asym_decryption.go | 4 +- lib/ocrypto/asym_encryption.go | 6 +- lib/ocrypto/ec_key_pair.go | 8 +- lib/ocrypto/mlkem.go | 90 +++++++++++++++++----- lib/ocrypto/mlkem_format_test.go | 124 +++++++++++++++++++++++++++++++ 5 files changed, 205 insertions(+), 27 deletions(-) create mode 100644 lib/ocrypto/mlkem_format_test.go diff --git a/lib/ocrypto/asym_decryption.go b/lib/ocrypto/asym_decryption.go index 93e2b158b0..a5bb5ce155 100644 --- a/lib/ocrypto/asym_decryption.go +++ b/lib/ocrypto/asym_decryption.go @@ -53,9 +53,9 @@ func FromPrivatePEMWithSalt(privateKeyInPem string, salt, info []byte) (PrivateK } switch oid, seed, err := parseMLKEMPrivatePKCS8(block.Bytes); { - case err == nil && oid.Equal(oidMLKEM768): + case err == nil && oid.Equal(OidMLKEM768): return NewSaltedMLKEM768Decryptor(seed, salt, info) - case err == nil && oid.Equal(oidMLKEM1024): + case err == nil && oid.Equal(OidMLKEM1024): return NewSaltedMLKEM1024Decryptor(seed, salt, info) case err != nil && !errors.Is(err, errNotMLKEM): return AsymDecryption{}, err diff --git a/lib/ocrypto/asym_encryption.go b/lib/ocrypto/asym_encryption.go index dd90543df2..86ee385b96 100644 --- a/lib/ocrypto/asym_encryption.go +++ b/lib/ocrypto/asym_encryption.go @@ -84,10 +84,10 @@ func FromPublicPEMWithSalt(publicKeyInPem string, salt, info []byte) (PublicKeyE return NewP384MLKEM1024Encryptor(block.Bytes, salt, info) } - switch oid, key, err := parseMLKEMPublicSPKI(block.Bytes); { - case err == nil && oid.Equal(oidMLKEM768): + switch oid, key, err := ParseMLKEMPublicSPKI(block.Bytes); { + case err == nil && oid.Equal(OidMLKEM768): return NewMLKEM768Encryptor(key, salt, info) - case err == nil && oid.Equal(oidMLKEM1024): + case err == nil && oid.Equal(OidMLKEM1024): return NewMLKEM1024Encryptor(key, salt, info) case err != nil && !errors.Is(err, errNotMLKEM): return nil, err diff --git a/lib/ocrypto/ec_key_pair.go b/lib/ocrypto/ec_key_pair.go index d55d5f266c..976089c3c3 100644 --- a/lib/ocrypto/ec_key_pair.go +++ b/lib/ocrypto/ec_key_pair.go @@ -558,7 +558,7 @@ func (keyPair MLKEMKeyPair) PrivateKeyInPemFormat() (string, error) { return "", errors.New("failed to generate PEM formatted private key") } - der, err := marshalMLKEMPrivatePKCS8(oidMLKEM768, keyPair.PrivateKey.Bytes()) + der, err := marshalMLKEMPrivatePKCS8(OidMLKEM768, keyPair.PrivateKey.Bytes()) if err != nil { return "", fmt.Errorf("marshal ML-KEM-768 PKCS#8 failed: %w", err) } @@ -570,7 +570,7 @@ func (keyPair MLKEMKeyPair) PublicKeyInPemFormat() (string, error) { return "", errors.New("failed to generate PEM formatted public key") } - der, err := marshalMLKEMPublicSPKI(oidMLKEM768, keyPair.PrivateKey.EncapsulationKey().Bytes()) + der, err := marshalMLKEMPublicSPKI(OidMLKEM768, keyPair.PrivateKey.EncapsulationKey().Bytes()) if err != nil { return "", fmt.Errorf("marshal ML-KEM-768 SPKI failed: %w", err) } @@ -586,7 +586,7 @@ func (keyPair MLKEM1024KeyPair) PrivateKeyInPemFormat() (string, error) { return "", errors.New("failed to generate PEM formatted private key") } - der, err := marshalMLKEMPrivatePKCS8(oidMLKEM1024, keyPair.PrivateKey.Bytes()) + der, err := marshalMLKEMPrivatePKCS8(OidMLKEM1024, keyPair.PrivateKey.Bytes()) if err != nil { return "", fmt.Errorf("marshal ML-KEM-1024 PKCS#8 failed: %w", err) } @@ -598,7 +598,7 @@ func (keyPair MLKEM1024KeyPair) PublicKeyInPemFormat() (string, error) { return "", errors.New("failed to generate PEM formatted public key") } - der, err := marshalMLKEMPublicSPKI(oidMLKEM1024, keyPair.PrivateKey.EncapsulationKey().Bytes()) + der, err := marshalMLKEMPublicSPKI(OidMLKEM1024, keyPair.PrivateKey.EncapsulationKey().Bytes()) if err != nil { return "", fmt.Errorf("marshal ML-KEM-1024 SPKI failed: %w", err) } diff --git a/lib/ocrypto/mlkem.go b/lib/ocrypto/mlkem.go index aca3f9b610..a2264ad83f 100644 --- a/lib/ocrypto/mlkem.go +++ b/lib/ocrypto/mlkem.go @@ -1,6 +1,7 @@ package ocrypto import ( + "bytes" "crypto/mlkem" "crypto/sha256" "encoding/asn1" @@ -36,8 +37,8 @@ const ( // 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} + 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 mlkemAlgorithmIdentifier struct { @@ -80,11 +81,11 @@ func marshalMLKEMPrivatePKCS8(oid asn1.ObjectIdentifier, seed []byte) ([]byte, e }) } -// parseMLKEMPublicSPKI returns the OID and raw encapsulation key bytes from an +// 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. If the blob is // not ML-KEM the sentinel errNotMLKEM is returned so the caller can fall // through to other parsers. -func parseMLKEMPublicSPKI(der []byte) (asn1.ObjectIdentifier, []byte, error) { +func ParseMLKEMPublicSPKI(der []byte) (asn1.ObjectIdentifier, []byte, error) { var s mlkemSPKI rest, err := asn1.Unmarshal(der, &s) if err != nil || len(rest) != 0 { @@ -92,10 +93,10 @@ func parseMLKEMPublicSPKI(der []byte) (asn1.ObjectIdentifier, []byte, error) { } var oid asn1.ObjectIdentifier switch { - case s.Algorithm.Algorithm.Equal(oidMLKEM768): - oid = oidMLKEM768 - case s.Algorithm.Algorithm.Equal(oidMLKEM1024): - oid = oidMLKEM1024 + case s.Algorithm.Algorithm.Equal(OidMLKEM768): + oid = OidMLKEM768 + case s.Algorithm.Algorithm.Equal(OidMLKEM1024): + oid = OidMLKEM1024 default: return nil, nil, errNotMLKEM } @@ -117,10 +118,10 @@ func parseMLKEMPrivatePKCS8(der []byte) (asn1.ObjectIdentifier, []byte, error) { } var oid asn1.ObjectIdentifier switch { - case p.Algorithm.Algorithm.Equal(oidMLKEM768): - oid = oidMLKEM768 - case p.Algorithm.Algorithm.Equal(oidMLKEM1024): - oid = oidMLKEM1024 + case p.Algorithm.Algorithm.Equal(OidMLKEM768): + oid = OidMLKEM768 + case p.Algorithm.Algorithm.Equal(OidMLKEM1024): + oid = OidMLKEM1024 default: return nil, nil, errNotMLKEM } @@ -179,7 +180,7 @@ func (e *MLKEMEncryptor768) Encrypt(data []byte) ([]byte, error) { } func (e *MLKEMEncryptor768) PublicKeyInPemFormat() (string, error) { - der, err := marshalMLKEMPublicSPKI(oidMLKEM768, e.publicKey) + der, err := marshalMLKEMPublicSPKI(OidMLKEM768, e.publicKey) if err != nil { return "", fmt.Errorf("marshal ML-KEM-768 SPKI failed: %w", err) } @@ -239,7 +240,7 @@ func (e *MLKEMEncryptor1024) Encrypt(data []byte) ([]byte, error) { } func (e *MLKEMEncryptor1024) PublicKeyInPemFormat() (string, error) { - der, err := marshalMLKEMPublicSPKI(oidMLKEM1024, e.publicKey) + der, err := marshalMLKEMPublicSPKI(OidMLKEM1024, e.publicKey) if err != nil { return "", fmt.Errorf("marshal ML-KEM-1024 SPKI failed: %w", err) } @@ -282,16 +283,69 @@ func (d *MLKEMDecryptor1024) Decrypt(data []byte) ([]byte, error) { return mlkem1024UnwrapDEK(d.privateKey, data, d.salt, d.info) } -func MLKEM768WrapDEK(publicKeyRaw, dek []byte) ([]byte, error) { - return mlkem768WrapDEK(publicKeyRaw, dek, defaultTDFSalt(), 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) { + // Fast path: already raw? + if len(input) == expectedRawSize { + return input, nil + } + + // Check for PEM format + 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) + } + // Continue with DER bytes + input = block.Bytes + } + + // Try parsing as SPKI DER + oid, rawKey, err := ParseMLKEMPublicSPKI(input) + if err != nil { + if errors.Is(err, errNotMLKEM) { + return nil, errors.New("not an ML-KEM key in SPKI format") + } + return nil, fmt.Errorf("failed to parse SPKI: %w", err) + } + + // Verify OID matches expected variant + if !oid.Equal(expectedOID) { + return nil, fmt.Errorf("OID mismatch: expected %v, got %v", expectedOID, oid) + } + + // Verify extracted key is correct size + if len(rawKey) != expectedRawSize { + return nil, fmt.Errorf("extracted key has wrong size: got %d want %d", len(rawKey), expectedRawSize) + } + + return rawKey, nil +} + +func MLKEM768WrapDEK(publicKey, dek []byte) ([]byte, error) { + // Normalize input to raw key bytes (handles raw, SPKI DER, or PEM) + rawKey, err := normalizeMLKEMPublicKey(publicKey, MLKEM768PublicKeySize, OidMLKEM768) + if err != nil { + return nil, fmt.Errorf("invalid ML-KEM-768 public key: %w", err) + } + return mlkem768WrapDEK(rawKey, dek, defaultTDFSalt(), nil) } func MLKEM768UnwrapDEK(privateKeyRaw, wrappedDER []byte) ([]byte, error) { return mlkem768UnwrapDEK(privateKeyRaw, wrappedDER, defaultTDFSalt(), nil) } -func MLKEM1024WrapDEK(publicKeyRaw, dek []byte) ([]byte, error) { - return mlkem1024WrapDEK(publicKeyRaw, dek, defaultTDFSalt(), nil) +func MLKEM1024WrapDEK(publicKey, dek []byte) ([]byte, error) { + // Normalize input to raw key bytes (handles raw, SPKI DER, or PEM) + rawKey, err := normalizeMLKEMPublicKey(publicKey, MLKEM1024PublicKeySize, OidMLKEM1024) + if err != nil { + return nil, fmt.Errorf("invalid ML-KEM-1024 public key: %w", err) + } + return mlkem1024WrapDEK(rawKey, dek, defaultTDFSalt(), nil) } func MLKEM1024UnwrapDEK(privateKeyRaw, wrappedDER []byte) ([]byte, error) { diff --git a/lib/ocrypto/mlkem_format_test.go b/lib/ocrypto/mlkem_format_test.go new file mode 100644 index 0000000000..13b74d39f7 --- /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 := marshalMLKEMPublicSPKI(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 := marshalMLKEMPublicSPKI(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 := marshalMLKEMPublicSPKI(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") +} From 0d0662c27b16f5b7d1c41c11da4651705c11e928 Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Tue, 2 Jun 2026 10:16:08 -0400 Subject: [PATCH 21/23] feat(kas): add mlkem-wrapped case to rewrap handler Adds support for pure ML-KEM key agreement objects in the KAS rewrap handler. The SDK now emits type="mlkem-wrapped" for MLKEM768/MLKEM1024 KAOs, and this change adds the corresponding case to handle decryption. Follows the hybrid-wrapped pattern: uses HybridTDFEnabled flag, no ephemeral key processing, and generic error messages per security guidelines. Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: Dave Mihalcik --- service/kas/access/rewrap.go | 59 +++++++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 15 deletions(-) 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), From f174b77dff458518a367ca530e036ead9c8c527a Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Wed, 3 Jun 2026 08:48:52 -0400 Subject: [PATCH 22/23] fixup exhaustive nolint lines --- sdk/experimental/tdf/key_access.go | 2 +- sdk/tdf.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/experimental/tdf/key_access.go b/sdk/experimental/tdf/key_access.go index ec26aa6692..534aa39b6a 100644 --- a/sdk/experimental/tdf/key_access.go +++ b/sdk/experimental/tdf/key_access.go @@ -265,7 +265,7 @@ func wrapKeyWithMLKEM(ktype ocrypto.KeyType, kasPublicKeyPEM string, symKey []by var wrappedDER []byte var err error - switch ktype { + switch ktype { //nolint:exhaustive // only handle mlkem types case ocrypto.MLKEM768Key: wrappedDER, err = ocrypto.MLKEM768WrapDEK([]byte(kasPublicKeyPEM), symKey) case ocrypto.MLKEM1024Key: diff --git a/sdk/tdf.go b/sdk/tdf.go index bcee2846f1..2aea39637e 100644 --- a/sdk/tdf.go +++ b/sdk/tdf.go @@ -792,7 +792,7 @@ func generateWrapKeyWithMLKEM(algorithm, publicKeyPEM string, symKey []byte) (st var wrappedDER []byte var err error - switch ktype { + switch ktype { //nolint:exhaustive // only handle mlkem types case ocrypto.MLKEM768Key: wrappedDER, err = ocrypto.MLKEM768WrapDEK([]byte(publicKeyPEM), symKey) case ocrypto.MLKEM1024Key: From 8c6e15eff52266be802f614236a47e366fb8ad44 Mon Sep 17 00:00:00 2001 From: Dave Mihalcik Date: Wed, 3 Jun 2026 13:17:31 -0400 Subject: [PATCH 23/23] refactor(ocrypto): unify ML-KEM, X-Wing, and NIST hybrid wrap paths Collapse the three near-identical KEM wrap/unwrap implementations behind one OID-keyed registry and a single internal kem contract. ML-KEM-768, ML-KEM-1024, X-Wing, P-256+ML-KEM-768, and P-384+ML-KEM-1024 now share one envelope type, one wrap function, one unwrap function, and one encryptor/decryptor pair routed through FromPublicPEM / FromPrivatePEM. Service and SDK callers shrink accordingly: StandardXWingCrypto, StandardHybridCrypto, and StandardMLKEMCrypto fold into a single StandardKEMCrypto; the per-algorithm wrap dispatch in sdk/tdf.go and sdk/experimental/tdf/key_access.go collapses to one IsKEMKeyType branch calling ocrypto.WrapDEK. Wire formats are preserved byte-for-byte (hybrid-wrapped, mlkem-wrapped). The OID registry leaves the planned hybrid-PEM-to-SPKI follow-up as a near-zero change: three OID constants plus three registry entries. Co-Authored-By: Claude Opus 4.7 Signed-off-by: Dave Mihalcik --- lib/ocrypto/asym_decryption.go | 18 +- lib/ocrypto/asym_encryption.go | 18 +- lib/ocrypto/benchmark_test.go | 58 +- lib/ocrypto/ec_key_pair.go | 8 +- lib/ocrypto/hybrid_common.go | 77 +-- lib/ocrypto/hybrid_nist.go | 335 +----------- lib/ocrypto/hybrid_nist_test.go | 46 +- lib/ocrypto/kem.go | 482 +++++++++++++++++ lib/ocrypto/mlkem.go | 508 +++--------------- lib/ocrypto/mlkem_format_test.go | 6 +- lib/ocrypto/mlkem_test.go | 44 +- lib/ocrypto/xwing.go | 203 +------ lib/ocrypto/xwing_test.go | 33 +- sdk/experimental/tdf/key_access.go | 42 +- sdk/tdf.go | 48 +- service/internal/security/basic_manager.go | 88 +-- .../internal/security/in_process_provider.go | 16 +- service/internal/security/standard_crypto.go | 152 +----- 18 files changed, 822 insertions(+), 1360 deletions(-) create mode 100644 lib/ocrypto/kem.go diff --git a/lib/ocrypto/asym_decryption.go b/lib/ocrypto/asym_decryption.go index a5bb5ce155..9fa66860ae 100644 --- a/lib/ocrypto/asym_decryption.go +++ b/lib/ocrypto/asym_decryption.go @@ -45,19 +45,19 @@ 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 := parseMLKEMPrivatePKCS8(block.Bytes); { - case err == nil && oid.Equal(OidMLKEM768): - return NewSaltedMLKEM768Decryptor(seed, salt, info) - case err == nil && oid.Equal(OidMLKEM1024): - return NewSaltedMLKEM1024Decryptor(seed, salt, info) - case err != nil && !errors.Is(err, errNotMLKEM): + 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 } diff --git a/lib/ocrypto/asym_encryption.go b/lib/ocrypto/asym_encryption.go index 86ee385b96..46794d9716 100644 --- a/lib/ocrypto/asym_encryption.go +++ b/lib/ocrypto/asym_encryption.go @@ -77,19 +77,19 @@ 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 := ParseMLKEMPublicSPKI(block.Bytes); { - case err == nil && oid.Equal(OidMLKEM768): - return NewMLKEM768Encryptor(key, salt, info) - case err == nil && oid.Equal(OidMLKEM1024): - return NewMLKEM1024Encryptor(key, salt, info) - case err != nil && !errors.Is(err, errNotMLKEM): + 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 } diff --git a/lib/ocrypto/benchmark_test.go b/lib/ocrypto/benchmark_test.go index b86b3075e7..31a53d1651 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 := deriveKEMWrapKey(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 = deriveKEMWrapKey(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 := deriveKEMWrapKey(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 = deriveKEMWrapKey(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 := deriveKEMWrapKey(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 = deriveKEMWrapKey(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 976089c3c3..11a86d96fb 100644 --- a/lib/ocrypto/ec_key_pair.go +++ b/lib/ocrypto/ec_key_pair.go @@ -558,7 +558,7 @@ func (keyPair MLKEMKeyPair) PrivateKeyInPemFormat() (string, error) { return "", errors.New("failed to generate PEM formatted private key") } - der, err := marshalMLKEMPrivatePKCS8(OidMLKEM768, keyPair.PrivateKey.Bytes()) + der, err := marshalKEMPrivatePKCS8(OidMLKEM768, keyPair.PrivateKey.Bytes()) if err != nil { return "", fmt.Errorf("marshal ML-KEM-768 PKCS#8 failed: %w", err) } @@ -570,7 +570,7 @@ func (keyPair MLKEMKeyPair) PublicKeyInPemFormat() (string, error) { return "", errors.New("failed to generate PEM formatted public key") } - der, err := marshalMLKEMPublicSPKI(OidMLKEM768, keyPair.PrivateKey.EncapsulationKey().Bytes()) + der, err := marshalKEMPublicSPKI(OidMLKEM768, keyPair.PrivateKey.EncapsulationKey().Bytes()) if err != nil { return "", fmt.Errorf("marshal ML-KEM-768 SPKI failed: %w", err) } @@ -586,7 +586,7 @@ func (keyPair MLKEM1024KeyPair) PrivateKeyInPemFormat() (string, error) { return "", errors.New("failed to generate PEM formatted private key") } - der, err := marshalMLKEMPrivatePKCS8(OidMLKEM1024, keyPair.PrivateKey.Bytes()) + der, err := marshalKEMPrivatePKCS8(OidMLKEM1024, keyPair.PrivateKey.Bytes()) if err != nil { return "", fmt.Errorf("marshal ML-KEM-1024 PKCS#8 failed: %w", err) } @@ -598,7 +598,7 @@ func (keyPair MLKEM1024KeyPair) PublicKeyInPemFormat() (string, error) { return "", errors.New("failed to generate PEM formatted public key") } - der, err := marshalMLKEMPublicSPKI(OidMLKEM1024, keyPair.PrivateKey.EncapsulationKey().Bytes()) + der, err := marshalKEMPublicSPKI(OidMLKEM1024, keyPair.PrivateKey.EncapsulationKey().Bytes()) if err != nil { return "", fmt.Errorf("marshal ML-KEM-1024 SPKI failed: %w", err) } diff --git a/lib/ocrypto/hybrid_common.go b/lib/ocrypto/hybrid_common.go index 4f4ee05417..f919c107e2 100644 --- a/lib/ocrypto/hybrid_common.go +++ b/lib/ocrypto/hybrid_common.go @@ -6,42 +6,39 @@ 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) +// should construct an encryptor via FromPublicPEMWithSalt instead. +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 all TDF KEM key +// wrapping schemes (pure ML-KEM, X-Wing, and NIST EC + ML-KEM). Defined here +// rather than in a per-scheme file so any change applies uniformly. func defaultTDFSalt() []byte { digest := sha256.New() digest.Write([]byte("TDF")) @@ -66,6 +63,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..d80b57120d --- /dev/null +++ b/lib/ocrypto/kem.go @@ -0,0 +1,482 @@ +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, one +// HKDF derivation, and one AES-GCM encryption call site. +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) +} + +// 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 derived via HKDF. +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 +} + +// --- 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) +} + +// --- 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()) +} + +// --- wrap / unwrap ---------------------------------------------------------- + +// wrapDEKWithKEM encapsulates against pub, derives an AES-256 wrap key via +// HKDF-SHA256 over (sharedSecret, salt, info), 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 := deriveKEMWrapKey(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, derives the same 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 := deriveKEMWrapKey(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 +} + +func deriveKEMWrapKey(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 index a2264ad83f..d879694cda 100644 --- a/lib/ocrypto/mlkem.go +++ b/lib/ocrypto/mlkem.go @@ -2,15 +2,10 @@ package ocrypto import ( "bytes" - "crypto/mlkem" - "crypto/sha256" "encoding/asn1" "encoding/pem" "errors" "fmt" - "io" - - "golang.org/x/crypto/hkdf" ) // PEM block types defined by RFC 7468 for SPKI / PKCS#8 envelopes. @@ -19,10 +14,10 @@ const ( pemBlockPrivateKey = "PRIVATE KEY" ) -// errNotMLKEM is returned by the ML-KEM SPKI / PKCS#8 parsers when the supplied -// DER blob is not an ML-KEM key, signalling the caller to fall through to -// other algorithm parsers. -var errNotMLKEM = errors.New("not an ML-KEM 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 @@ -31,8 +26,6 @@ const ( MLKEM1024PublicKeySize = 1568 // mlkem1024 encapsulation key MLKEM1024PrivateKeySize = 64 // mlkem1024 seed (d || z) MLKEM1024CiphertextSize = 1568 // mlkem1024 ciphertext - - mlkemWrapKeySize = 32 // AES-256 key size for wrap key derivation ) // NIST-assigned OIDs for ML-KEM (FIPS 203). @@ -41,257 +34,110 @@ var ( OidMLKEM1024 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 4, 3} ) -type mlkemAlgorithmIdentifier struct { +type kemAlgorithmIdentifier struct { Algorithm asn1.ObjectIdentifier } -type mlkemSPKI struct { - Algorithm mlkemAlgorithmIdentifier +type kemSPKI struct { + Algorithm kemAlgorithmIdentifier PublicKey asn1.BitString } -// mlkemPKCS8 mirrors RFC 5958 OneAsymmetricKey v1. -type mlkemPKCS8 struct { +// kemPKCS8 mirrors RFC 5958 OneAsymmetricKey v1. +type kemPKCS8 struct { Version int - Algorithm mlkemAlgorithmIdentifier + Algorithm kemAlgorithmIdentifier PrivateKey []byte } const bitsPerByte = 8 -// marshalMLKEMPublicSPKI encodes a raw ML-KEM encapsulation key as RFC 5280 SubjectPublicKeyInfo. -func marshalMLKEMPublicSPKI(oid asn1.ObjectIdentifier, rawKey []byte) ([]byte, error) { - return asn1.Marshal(mlkemSPKI{ - Algorithm: mlkemAlgorithmIdentifier{Algorithm: oid}, +// 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}, }) } -// marshalMLKEMPrivatePKCS8 encodes the ML-KEM seed as RFC 5958 OneAsymmetricKey, -// with the inner ML-KEM-PrivateKey CHOICE selected as [0] IMPLICIT OCTET STRING (seed). -func marshalMLKEMPrivatePKCS8(oid asn1.ObjectIdentifier, seed []byte) ([]byte, error) { - inner, err := asn1.MarshalWithParams(seed, "tag:0,implicit") +// 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(mlkemPKCS8{ + return asn1.Marshal(kemPKCS8{ Version: 0, - Algorithm: mlkemAlgorithmIdentifier{Algorithm: oid}, + Algorithm: kemAlgorithmIdentifier{Algorithm: oid}, PrivateKey: inner, }) } -// 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. If the blob is -// not ML-KEM the sentinel errNotMLKEM is returned so the caller can fall -// through to other parsers. -func ParseMLKEMPublicSPKI(der []byte) (asn1.ObjectIdentifier, []byte, error) { - var s mlkemSPKI +// 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, errNotMLKEM - } - var oid asn1.ObjectIdentifier - switch { - case s.Algorithm.Algorithm.Equal(OidMLKEM768): - oid = OidMLKEM768 - case s.Algorithm.Algorithm.Equal(OidMLKEM1024): - oid = OidMLKEM1024 - default: - return nil, nil, errNotMLKEM + return nil, nil, errNotKEM } if s.PublicKey.BitLength%bitsPerByte != 0 { - return nil, nil, errors.New("ML-KEM SPKI bit string is not byte-aligned") - } - return oid, s.PublicKey.RightAlign(), nil -} - -// parseMLKEMPrivatePKCS8 returns the OID and raw seed bytes from a PKCS#8 DER -// blob if the algorithm is ML-KEM-768 or ML-KEM-1024. If the blob is not -// ML-KEM the sentinel errNotMLKEM is returned so the caller can fall through -// to other parsers. -func parseMLKEMPrivatePKCS8(der []byte) (asn1.ObjectIdentifier, []byte, error) { - var p mlkemPKCS8 - rest, err := asn1.Unmarshal(der, &p) - if err != nil || len(rest) != 0 { - return nil, nil, errNotMLKEM - } - var oid asn1.ObjectIdentifier - switch { - case p.Algorithm.Algorithm.Equal(OidMLKEM768): - oid = OidMLKEM768 - case p.Algorithm.Algorithm.Equal(OidMLKEM1024): - oid = OidMLKEM1024 - default: - return nil, nil, errNotMLKEM - } - - var innerSeed []byte - innerRest, err := asn1.UnmarshalWithParams(p.PrivateKey, &innerSeed, "tag:0,implicit") - if err != nil || len(innerRest) != 0 { - return nil, nil, fmt.Errorf("ML-KEM PKCS#8 inner seed parse failed: %w", err) - } - return oid, innerSeed, nil -} - -type MLKEMWrappedKey struct { - MLKEMCiphertext []byte `asn1:"tag:0"` - EncryptedDEK []byte `asn1:"tag:1"` -} - -type MLKEMEncryptor768 struct { - publicKey []byte - salt []byte - info []byte -} - -type MLKEMDecryptor768 struct { - privateKey []byte - salt []byte - info []byte -} - -type MLKEMEncryptor1024 struct { - publicKey []byte - salt []byte - info []byte -} - -type MLKEMDecryptor1024 struct { - privateKey []byte - salt []byte - info []byte -} - -func NewMLKEM768Encryptor(publicKey, salt, info []byte) (*MLKEMEncryptor768, error) { - if len(publicKey) != MLKEM768PublicKeySize { - return nil, fmt.Errorf("invalid ML-KEM-768 public key size: got %d want %d", len(publicKey), MLKEM768PublicKeySize) + return nil, nil, errors.New("KEM SPKI bit string is not byte-aligned") } - - return &MLKEMEncryptor768{ - publicKey: append([]byte(nil), publicKey...), - salt: cloneOrNil(salt), - info: cloneOrNil(info), - }, nil -} - -func (e *MLKEMEncryptor768) Encrypt(data []byte) ([]byte, error) { - return mlkem768WrapDEK(e.publicKey, data, e.salt, e.info) + return s.Algorithm.Algorithm, s.PublicKey.RightAlign(), nil } -func (e *MLKEMEncryptor768) PublicKeyInPemFormat() (string, error) { - der, err := marshalMLKEMPublicSPKI(OidMLKEM768, e.publicKey) +// 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 "", fmt.Errorf("marshal ML-KEM-768 SPKI failed: %w", err) + return nil, nil, err } - return string(pem.EncodeToMemory(&pem.Block{Type: pemBlockPublicKey, Bytes: der})), nil -} - -func (e *MLKEMEncryptor768) Type() SchemeType { - return MLKEM -} - -func (e *MLKEMEncryptor768) KeyType() KeyType { - return MLKEM768Key -} - -func (e *MLKEMEncryptor768) EphemeralKey() []byte { - return nil -} - -func (e *MLKEMEncryptor768) Metadata() (map[string]string, error) { - return make(map[string]string), nil -} - -func NewMLKEM768Decryptor(privateKey []byte) (*MLKEMDecryptor768, error) { - return NewSaltedMLKEM768Decryptor(privateKey, defaultTDFSalt(), nil) -} - -func NewSaltedMLKEM768Decryptor(privateKey, salt, info []byte) (*MLKEMDecryptor768, error) { - if len(privateKey) != MLKEM768PrivateKeySize { - return nil, fmt.Errorf("invalid ML-KEM-768 private key size: got %d want %d", len(privateKey), MLKEM768PrivateKeySize) + if !oid.Equal(OidMLKEM768) && !oid.Equal(OidMLKEM1024) { + return nil, nil, errNotKEM } - - return &MLKEMDecryptor768{ - privateKey: append([]byte(nil), privateKey...), - salt: cloneOrNil(salt), - info: cloneOrNil(info), - }, nil -} - -func (d *MLKEMDecryptor768) Decrypt(data []byte) ([]byte, error) { - return mlkem768UnwrapDEK(d.privateKey, data, d.salt, d.info) + return oid, raw, nil } -func NewMLKEM1024Encryptor(publicKey, salt, info []byte) (*MLKEMEncryptor1024, error) { - if len(publicKey) != MLKEM1024PublicKeySize { - return nil, fmt.Errorf("invalid ML-KEM-1024 public key size: got %d want %d", len(publicKey), MLKEM1024PublicKeySize) +// 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 } - - return &MLKEMEncryptor1024{ - publicKey: append([]byte(nil), publicKey...), - salt: cloneOrNil(salt), - info: cloneOrNil(info), - }, nil -} - -func (e *MLKEMEncryptor1024) Encrypt(data []byte) ([]byte, error) { - return mlkem1024WrapDEK(e.publicKey, data, e.salt, e.info) -} - -func (e *MLKEMEncryptor1024) PublicKeyInPemFormat() (string, error) { - der, err := marshalMLKEMPublicSPKI(OidMLKEM1024, e.publicKey) - if err != nil { - return "", fmt.Errorf("marshal ML-KEM-1024 SPKI failed: %w", err) + if _, ok := kemRegistry[p.Algorithm.Algorithm.String()]; !ok { + return nil, nil, errNotKEM } - return string(pem.EncodeToMemory(&pem.Block{Type: pemBlockPublicKey, Bytes: der})), nil -} - -func (e *MLKEMEncryptor1024) Type() SchemeType { - return MLKEM -} - -func (e *MLKEMEncryptor1024) KeyType() KeyType { - return MLKEM1024Key -} -func (e *MLKEMEncryptor1024) EphemeralKey() []byte { - return nil -} - -func (e *MLKEMEncryptor1024) Metadata() (map[string]string, error) { - return make(map[string]string), nil -} - -func NewMLKEM1024Decryptor(privateKey []byte) (*MLKEMDecryptor1024, error) { - return NewSaltedMLKEM1024Decryptor(privateKey, defaultTDFSalt(), nil) -} - -func NewSaltedMLKEM1024Decryptor(privateKey, salt, info []byte) (*MLKEMDecryptor1024, error) { - if len(privateKey) != MLKEM1024PrivateKeySize { - return nil, fmt.Errorf("invalid ML-KEM-1024 private key size: got %d want %d", len(privateKey), MLKEM1024PrivateKeySize) + 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 &MLKEMDecryptor1024{ - privateKey: append([]byte(nil), privateKey...), - salt: cloneOrNil(salt), - info: cloneOrNil(info), - }, nil -} - -func (d *MLKEMDecryptor1024) Decrypt(data []byte) ([]byte, error) { - return mlkem1024UnwrapDEK(d.privateKey, data, d.salt, d.info) + 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) { - // Fast path: already raw? if len(input) == expectedRawSize { return input, nil } - // Check for PEM format if bytes.HasPrefix(input, []byte("-----BEGIN")) { block, _ := pem.Decode(input) if block == nil { @@ -300,25 +146,21 @@ func normalizeMLKEMPublicKey(input []byte, expectedRawSize int, expectedOID asn1 if block.Type != pemBlockPublicKey { return nil, fmt.Errorf("expected %s PEM block, got %s", pemBlockPublicKey, block.Type) } - // Continue with DER bytes input = block.Bytes } - // Try parsing as SPKI DER oid, rawKey, err := ParseMLKEMPublicSPKI(input) if err != nil { - if errors.Is(err, errNotMLKEM) { + 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) } - // Verify OID matches expected variant if !oid.Equal(expectedOID) { return nil, fmt.Errorf("OID mismatch: expected %v, got %v", expectedOID, oid) } - // Verify extracted key is correct size if len(rawKey) != expectedRawSize { return nil, fmt.Errorf("extracted key has wrong size: got %d want %d", len(rawKey), expectedRawSize) } @@ -326,230 +168,44 @@ func normalizeMLKEMPublicKey(input []byte, expectedRawSize int, expectedOID asn1 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. +// +// Deprecated: Use WrapDEK with MLKEM768Key, or construct via FromPublicPEM. func MLKEM768WrapDEK(publicKey, dek []byte) ([]byte, error) { - // Normalize input to raw key bytes (handles raw, SPKI DER, or PEM) rawKey, err := normalizeMLKEMPublicKey(publicKey, MLKEM768PublicKeySize, OidMLKEM768) if err != nil { return nil, fmt.Errorf("invalid ML-KEM-768 public key: %w", err) } - return mlkem768WrapDEK(rawKey, dek, defaultTDFSalt(), nil) + return wrapDEKWithKEM(mlkemKEM{variant: mlkem768}, rawKey, dek, defaultTDFSalt(), 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 mlkem768UnwrapDEK(privateKeyRaw, wrappedDER, defaultTDFSalt(), nil) + return unwrapDEKWithKEM(mlkemKEM{variant: mlkem768}, privateKeyRaw, wrappedDER, defaultTDFSalt(), 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. +// +// Deprecated: Use WrapDEK with MLKEM1024Key, or construct via FromPublicPEM. func MLKEM1024WrapDEK(publicKey, dek []byte) ([]byte, error) { - // Normalize input to raw key bytes (handles raw, SPKI DER, or PEM) rawKey, err := normalizeMLKEMPublicKey(publicKey, MLKEM1024PublicKeySize, OidMLKEM1024) if err != nil { return nil, fmt.Errorf("invalid ML-KEM-1024 public key: %w", err) } - return mlkem1024WrapDEK(rawKey, dek, defaultTDFSalt(), nil) + return wrapDEKWithKEM(mlkemKEM{variant: mlkem1024}, rawKey, dek, defaultTDFSalt(), 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 mlkem1024UnwrapDEK(privateKeyRaw, wrappedDER, defaultTDFSalt(), nil) -} - -// MLKEM768Encapsulate performs ML-KEM-768 encapsulation, returning the shared -// secret and ciphertext without applying KDF or encryption. -func MLKEM768Encapsulate(publicKeyRaw []byte) ([]byte, []byte, error) { - if len(publicKeyRaw) != MLKEM768PublicKeySize { - return nil, nil, fmt.Errorf("invalid ML-KEM-768 public key size: got %d want %d", len(publicKeyRaw), MLKEM768PublicKeySize) - } - - ek, err := mlkem.NewEncapsulationKey768(publicKeyRaw) - if err != nil { - return nil, nil, fmt.Errorf("mlkem.NewEncapsulationKey768 failed: %w", err) - } - - sharedSecret, ciphertext := ek.Encapsulate() - - return sharedSecret, ciphertext, nil -} - -// MLKEM1024Encapsulate performs ML-KEM-1024 encapsulation, returning the shared -// secret and ciphertext without applying KDF or encryption. -func MLKEM1024Encapsulate(publicKeyRaw []byte) ([]byte, []byte, error) { - if len(publicKeyRaw) != MLKEM1024PublicKeySize { - return nil, nil, fmt.Errorf("invalid ML-KEM-1024 public key size: got %d want %d", len(publicKeyRaw), MLKEM1024PublicKeySize) - } - - ek, err := mlkem.NewEncapsulationKey1024(publicKeyRaw) - if err != nil { - return nil, nil, fmt.Errorf("mlkem.NewEncapsulationKey1024 failed: %w", err) - } - - sharedSecret, ciphertext := ek.Encapsulate() - - return sharedSecret, ciphertext, nil -} - -func mlkem768WrapDEK(publicKeyRaw, dek, salt, info []byte) ([]byte, error) { - sharedSecret, ciphertext, err := MLKEM768Encapsulate(publicKeyRaw) - if err != nil { - return nil, err - } - - wrapKey, err := deriveMLKEMWrapKey(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(MLKEMWrappedKey{ - MLKEMCiphertext: ciphertext, - EncryptedDEK: encryptedDEK, - }) - if err != nil { - return nil, fmt.Errorf("asn1.Marshal failed: %w", err) - } - - return wrappedDER, nil -} - -func mlkem768UnwrapDEK(privateKeyRaw, wrappedDER, salt, info []byte) ([]byte, error) { - if len(privateKeyRaw) != MLKEM768PrivateKeySize { - return nil, fmt.Errorf("invalid ML-KEM-768 private key size: got %d want %d", len(privateKeyRaw), MLKEM768PrivateKeySize) - } - - var wrappedKey MLKEMWrappedKey - 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.MLKEMCiphertext) != MLKEM768CiphertextSize { - return nil, fmt.Errorf("invalid ML-KEM-768 ciphertext size: got %d want %d", len(wrappedKey.MLKEMCiphertext), MLKEM768CiphertextSize) - } - - dk, err := mlkem.NewDecapsulationKey768(privateKeyRaw) - if err != nil { - return nil, fmt.Errorf("mlkem.NewDecapsulationKey768 failed: %w", err) - } - - sharedSecret, err := dk.Decapsulate(wrappedKey.MLKEMCiphertext) - if err != nil { - return nil, fmt.Errorf("mlkem768 decapsulate failed: %w", err) - } - - wrapKey, err := deriveMLKEMWrapKey(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 mlkem1024WrapDEK(publicKeyRaw, dek, salt, info []byte) ([]byte, error) { - sharedSecret, ciphertext, err := MLKEM1024Encapsulate(publicKeyRaw) - if err != nil { - return nil, err - } - - wrapKey, err := deriveMLKEMWrapKey(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(MLKEMWrappedKey{ - MLKEMCiphertext: ciphertext, - EncryptedDEK: encryptedDEK, - }) - if err != nil { - return nil, fmt.Errorf("asn1.Marshal failed: %w", err) - } - - return wrappedDER, nil -} - -func mlkem1024UnwrapDEK(privateKeyRaw, wrappedDER, salt, info []byte) ([]byte, error) { - if len(privateKeyRaw) != MLKEM1024PrivateKeySize { - return nil, fmt.Errorf("invalid ML-KEM-1024 private key size: got %d want %d", len(privateKeyRaw), MLKEM1024PrivateKeySize) - } - - var wrappedKey MLKEMWrappedKey - 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.MLKEMCiphertext) != MLKEM1024CiphertextSize { - return nil, fmt.Errorf("invalid ML-KEM-1024 ciphertext size: got %d want %d", len(wrappedKey.MLKEMCiphertext), MLKEM1024CiphertextSize) - } - - dk, err := mlkem.NewDecapsulationKey1024(privateKeyRaw) - if err != nil { - return nil, fmt.Errorf("mlkem.NewDecapsulationKey1024 failed: %w", err) - } - - sharedSecret, err := dk.Decapsulate(wrappedKey.MLKEMCiphertext) - if err != nil { - return nil, fmt.Errorf("mlkem1024 decapsulate failed: %w", err) - } - - wrapKey, err := deriveMLKEMWrapKey(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 deriveMLKEMWrapKey(sharedSecret, salt, info []byte) ([]byte, error) { - if len(salt) == 0 { - salt = defaultTDFSalt() - } - - hkdfObj := hkdf.New(sha256.New, sharedSecret, salt, info) - derivedKey := make([]byte, mlkemWrapKeySize) - if _, err := io.ReadFull(hkdfObj, derivedKey); err != nil { - return nil, fmt.Errorf("hkdf failure: %w", err) - } - - return derivedKey, nil + return unwrapDEKWithKEM(mlkemKEM{variant: mlkem1024}, privateKeyRaw, wrappedDER, defaultTDFSalt(), nil) } diff --git a/lib/ocrypto/mlkem_format_test.go b/lib/ocrypto/mlkem_format_test.go index 13b74d39f7..0e954eee87 100644 --- a/lib/ocrypto/mlkem_format_test.go +++ b/lib/ocrypto/mlkem_format_test.go @@ -23,7 +23,7 @@ func TestMLKEM768WrapDEKFormats(t *testing.T) { require.NoError(t, err, "Should accept raw key") // Test 2: SPKI DER (1206 bytes) - spkiDER, err := marshalMLKEMPublicSPKI(OidMLKEM768, rawKey) + spkiDER, err := marshalKEMPublicSPKI(OidMLKEM768, rawKey) require.NoError(t, err) require.Greater(t, len(spkiDER), len(rawKey), "SPKI DER should be larger than raw key") @@ -68,7 +68,7 @@ func TestMLKEM1024WrapDEKFormats(t *testing.T) { require.NoError(t, err, "Should accept raw key") // Test 2: SPKI DER (1590 bytes) - spkiDER, err := marshalMLKEMPublicSPKI(OidMLKEM1024, rawKey) + spkiDER, err := marshalKEMPublicSPKI(OidMLKEM1024, rawKey) require.NoError(t, err) require.Greater(t, len(spkiDER), len(rawKey), "SPKI DER should be larger than raw key") @@ -110,7 +110,7 @@ func TestMLKEM768WrapDEKInvalidFormats(t *testing.T) { // Wrong OID in SPKI keyPair1024, err := NewMLKEM1024KeyPair() require.NoError(t, err) - spki1024, err := marshalMLKEMPublicSPKI(OidMLKEM1024, keyPair1024.PrivateKey.EncapsulationKey().Bytes()) + spki1024, err := marshalKEMPublicSPKI(OidMLKEM1024, keyPair1024.PrivateKey.EncapsulationKey().Bytes()) require.NoError(t, err) _, err = MLKEM768WrapDEK(spki1024, dek) diff --git a/lib/ocrypto/mlkem_test.go b/lib/ocrypto/mlkem_test.go index 185c568cc8..47045b3059 100644 --- a/lib/ocrypto/mlkem_test.go +++ b/lib/ocrypto/mlkem_test.go @@ -76,16 +76,16 @@ func TestMLKEM1024WrapUnwrapWrongKeyFails(t *testing.T) { assert.Contains(t, err.Error(), "AES-GCM decrypt failed") } -func TestMLKEMWrappedKeyASN1RoundTrip(t *testing.T) { - original := MLKEMWrappedKey{ - MLKEMCiphertext: []byte("ciphertext"), - EncryptedDEK: []byte("encrypted-dek"), +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 MLKEMWrappedKey + var decoded kemEnvelope rest, err := asn1.Unmarshal(der, &decoded) require.NoError(t, err) assert.Empty(t, rest) @@ -98,9 +98,9 @@ func TestMLKEM768CiphertextSizeValidation(t *testing.T) { privateKeyBytes := keyPair.PrivateKey.Bytes() - invalidWrapped := MLKEMWrappedKey{ - MLKEMCiphertext: []byte("too-short"), - EncryptedDEK: []byte("encrypted-dek"), + invalidWrapped := kemEnvelope{ + KEMCiphertext: []byte("too-short"), + EncryptedDEK: []byte("encrypted-dek"), } der, err := asn1.Marshal(invalidWrapped) @@ -108,7 +108,7 @@ func TestMLKEM768CiphertextSizeValidation(t *testing.T) { _, err = MLKEM768UnwrapDEK(privateKeyBytes, der) require.Error(t, err) - assert.Contains(t, err.Error(), "invalid ML-KEM-768 ciphertext size") + assert.Contains(t, err.Error(), "ciphertext size") } func TestMLKEM1024CiphertextSizeValidation(t *testing.T) { @@ -117,9 +117,9 @@ func TestMLKEM1024CiphertextSizeValidation(t *testing.T) { privateKeyBytes := keyPair.PrivateKey.Bytes() - invalidWrapped := MLKEMWrappedKey{ - MLKEMCiphertext: []byte("too-short"), - EncryptedDEK: []byte("encrypted-dek"), + invalidWrapped := kemEnvelope{ + KEMCiphertext: []byte("too-short"), + EncryptedDEK: []byte("encrypted-dek"), } der, err := asn1.Marshal(invalidWrapped) @@ -127,7 +127,7 @@ func TestMLKEM1024CiphertextSizeValidation(t *testing.T) { _, err = MLKEM1024UnwrapDEK(privateKeyBytes, der) require.Error(t, err) - assert.Contains(t, err.Error(), "invalid ML-KEM-1024 ciphertext size") + assert.Contains(t, err.Error(), "ciphertext size") } func TestMLKEMCustomSaltInfo(t *testing.T) { @@ -140,10 +140,10 @@ func TestMLKEMCustomSaltInfo(t *testing.T) { customSalt := []byte("custom-salt-value") customInfo := []byte("custom-info-value") - encryptor, err := NewMLKEM768Encryptor(publicKeyBytes, customSalt, customInfo) + encryptor, err := newKEMEncryptor(mlkemKEM{variant: mlkem768}, publicKeyBytes, customSalt, customInfo) require.NoError(t, err) - decryptor, err := NewSaltedMLKEM768Decryptor(privateKeyBytes, customSalt, customInfo) + decryptor, err := newKEMDecryptor(mlkemKEM{variant: mlkem768}, privateKeyBytes, customSalt, customInfo) require.NoError(t, err) dek := []byte("test-dek-value-123456") @@ -161,7 +161,7 @@ func TestMLKEMEncryptorImplementsInterface(t *testing.T) { publicKeyBytes := keyPair.PrivateKey.EncapsulationKey().Bytes() - encryptor, err := NewMLKEM768Encryptor(publicKeyBytes, nil, nil) + encryptor, err := newKEMEncryptor(mlkemKEM{variant: mlkem768}, publicKeyBytes, nil, nil) require.NoError(t, err) assert.Equal(t, MLKEM, encryptor.Type()) @@ -179,7 +179,7 @@ func TestMLKEM768Encapsulate(t *testing.T) { publicKeyBytes := keyPair.PrivateKey.EncapsulationKey().Bytes() - sharedSecret, ciphertext, err := MLKEM768Encapsulate(publicKeyBytes) + sharedSecret, ciphertext, err := mlkemKEM{variant: mlkem768}.encapsulate(publicKeyBytes) require.NoError(t, err) assert.Len(t, sharedSecret, 32) assert.Len(t, ciphertext, MLKEM768CiphertextSize) @@ -191,22 +191,22 @@ func TestMLKEM1024Encapsulate(t *testing.T) { publicKeyBytes := keyPair.PrivateKey.EncapsulationKey().Bytes() - sharedSecret, ciphertext, err := MLKEM1024Encapsulate(publicKeyBytes) + 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 := MLKEM768Encapsulate([]byte("too-short")) + _, _, err := mlkemKEM{variant: mlkem768}.encapsulate([]byte("too-short")) require.Error(t, err) - assert.Contains(t, err.Error(), "invalid ML-KEM-768 public key size") + assert.Contains(t, err.Error(), "public key size") } func TestMLKEM1024EncapsulateInvalidKeySize(t *testing.T) { - _, _, err := MLKEM1024Encapsulate([]byte("too-short")) + _, _, err := mlkemKEM{variant: mlkem1024}.encapsulate([]byte("too-short")) require.Error(t, err) - assert.Contains(t, err.Error(), "invalid ML-KEM-1024 public key size") + assert.Contains(t, err.Error(), "public key size") } func TestMLKEM768PEMRoundTrip(t *testing.T) { 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/sdk/experimental/tdf/key_access.go b/sdk/experimental/tdf/key_access.go index 534aa39b6a..ad849c739c 100644 --- a/sdk/experimental/tdf/key_access.go +++ b/sdk/experimental/tdf/key_access.go @@ -165,17 +165,13 @@ 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 return wrapKeyWithEC(ktype, pubKeyInfo.PEM, symKey) } - if ocrypto.IsMLKEMKeyType(ktype) { - // Handle ML-KEM key wrapping - return wrapKeyWithMLKEM(ktype, pubKeyInfo.PEM, symKey) - } // Handle RSA key wrapping wrapped, err := wrapKeyWithRSA(pubKeyInfo.PEM, symKey) return wrapped, "wrapped", "", err @@ -253,30 +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 string(ocrypto.Base64Encode(wrappedDER)), "hybrid-wrapped", "", nil -} - -func wrapKeyWithMLKEM(ktype ocrypto.KeyType, kasPublicKeyPEM string, symKey []byte) (string, string, string, error) { - var wrappedDER []byte - var err error - - switch ktype { //nolint:exhaustive // only handle mlkem types - case ocrypto.MLKEM768Key: - wrappedDER, err = ocrypto.MLKEM768WrapDEK([]byte(kasPublicKeyPEM), symKey) - case ocrypto.MLKEM1024Key: - wrappedDER, err = ocrypto.MLKEM1024WrapDEK([]byte(kasPublicKeyPEM), symKey) - default: - return "", "", "", fmt.Errorf("unsupported ML-KEM key type: %s", ktype) + return "", "", "", fmt.Errorf("kem wrap failed: %w", err) } - - if err != nil { - return "", "", "", fmt.Errorf("mlkem wrap failed: %w", err) + scheme := "hybrid-wrapped" + if ocrypto.IsMLKEMKeyType(ktype) { + scheme = "mlkem-wrapped" } - - return string(ocrypto.Base64Encode(wrappedDER)), "mlkem-wrapped", "", nil + return string(ocrypto.Base64Encode(wrappedDER)), scheme, "", nil } diff --git a/sdk/tdf.go b/sdk/tdf.go index 2aea39637e..ce42099603 100644 --- a/sdk/tdf.go +++ b/sdk/tdf.go @@ -677,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) @@ -696,13 +696,6 @@ func createKeyAccess(kasInfo KASInfo, symKey []byte, policyBinding PolicyBinding keyAccess.KeyType = kECWrapped keyAccess.WrappedKey = wrappedKeyInfo.wrappedKey keyAccess.EphemeralPublicKey = wrappedKeyInfo.publicKey - case ocrypto.IsMLKEMKeyType(ktype): - wrappedKey, err := generateWrapKeyWithMLKEM(kasInfo.Algorithm, kasInfo.PublicKey, symKey) - if err != nil { - return KeyAccess{}, err - } - keyAccess.KeyType = kMLKEMWrapped - keyAccess.WrappedKey = wrappedKey default: wrappedKey, err := generateWrapKeyWithRSA(kasInfo.PublicKey, symKey) if err != nil { @@ -778,34 +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 string(ocrypto.Base64Encode(wrappedDER)), nil -} - -func generateWrapKeyWithMLKEM(algorithm, publicKeyPEM string, symKey []byte) (string, error) { - ktype := ocrypto.KeyType(algorithm) - - var wrappedDER []byte - var err error - - switch ktype { //nolint:exhaustive // only handle mlkem types - case ocrypto.MLKEM768Key: - wrappedDER, err = ocrypto.MLKEM768WrapDEK([]byte(publicKeyPEM), symKey) - case ocrypto.MLKEM1024Key: - wrappedDER, err = ocrypto.MLKEM1024WrapDEK([]byte(publicKeyPEM), symKey) - default: - return "", fmt.Errorf("unsupported ML-KEM key type: %s", algorithm) + return "", "", fmt.Errorf("generateWrapKeyWithKEM: %w", err) } - - if err != nil { - return "", fmt.Errorf("generateWrapKeyWithMLKEM: %w", err) + scheme := kHybridWrapped + if ocrypto.IsMLKEMKeyType(ktype) { + scheme = kMLKEMWrapped } - - return string(ocrypto.Base64Encode(wrappedDER)), nil + return string(ocrypto.Base64Encode(wrappedDER)), scheme, nil } // create policy object diff --git a/service/internal/security/basic_manager.go b/service/internal/security/basic_manager.go index 7de5df14f3..648cf0bf0f 100644 --- a/service/internal/security/basic_manager.go +++ b/service/internal/security/basic_manager.go @@ -68,17 +68,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 { @@ -92,78 +89,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: - if len(ephemeralPublicKey) > 0 { - return nil, errors.New("ephemeral public key should not be provided for hybrid decryption") - } - privKeyBytes, err := ocrypto.P384MLKEM1024PrivateKeyFromPem(privKey) - if err != nil { - return nil, fmt.Errorf("failed to parse P384-MLKEM1024 private key from PEM: %w", err) - } - plaintext, err := ocrypto.P384MLKEM1024UnwrapDEK(privKeyBytes, ciphertext) - if err != nil { - return nil, fmt.Errorf("failed to decrypt with P384-MLKEM1024: %w", err) - } - protectedKey, err := ocrypto.NewAESProtectedKey(plaintext) - if err != nil { - return nil, fmt.Errorf("failed to create protected key: %w", err) - } - return protectedKey, nil - case ocrypto.MLKEM768Key, ocrypto.MLKEM1024Key: + return ocrypto.NewAESProtectedKey(plaintext) + } + + if ocrypto.IsKEMKeyType(alg) { if len(ephemeralPublicKey) > 0 { - return nil, errors.New("ephemeral public key should not be provided for ML-KEM decryption") + return nil, fmt.Errorf("ephemeral public key should not be provided for %s decryption", alg) } plaintext, err := decrypter.Decrypt(ciphertext) if err != nil { - return nil, fmt.Errorf("failed to decrypt with ML-KEM: %w", err) - } - protectedKey, err := ocrypto.NewAESProtectedKey(plaintext) - 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) { @@ -247,7 +187,8 @@ func (b *BasicManager) unwrap(ctx context.Context, kid string, wrappedKey string if privKeyBytes, ok := privKey.([]byte); ok { return privKeyBytes, nil } - b.l.ErrorContext(ctx, + b.l.ErrorContext( + ctx, "private key in cache is not of type []byte", slog.String("kid", kid), slog.Any("type", fmt.Sprintf("%T", privKey)), @@ -283,7 +224,8 @@ func (b *BasicManager) unwrap(ctx context.Context, kid string, wrappedKey string if cacheEnabled { if err := b.cache.Set(ctx, kid, privKey, nil); err != nil { - b.l.ErrorContext(ctx, + b.l.ErrorContext( + ctx, "failed to cache private key", slog.String("kid", kid), slog.Any("error", err), diff --git a/service/internal/security/in_process_provider.go b/service/internal/security/in_process_provider.go index 51b92b3227..6e2e9c840d 100644 --- a/service/internal/security/in_process_provider.go +++ b/service/internal/security/in_process_provider.go @@ -84,14 +84,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 mlkemKey, err := k.cryptoProvider.MLKEMPublicKey(kid); err == nil { - return mlkemKey, nil + if kemKey, err := k.cryptoProvider.KEMPublicKey(kid); err == nil { + return kemKey, nil } return k.cryptoProvider.ECPublicKey(kid) default: @@ -357,11 +351,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: - return key.Algorithm, nil - case StandardMLKEMCrypto: + case StandardKEMCrypto: return key.Algorithm, nil } diff --git a/service/internal/security/standard_crypto.go b/service/internal/security/standard_crypto.go index 6a574eac69..95c2efce28 100644 --- a/service/internal/security/standard_crypto.go +++ b/service/internal/security/standard_crypto.go @@ -67,23 +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 -} - -type StandardMLKEMCrypto struct { - KeyPairInfo - mlkemPrivateKeyPem string - mlkemPublicKeyPem string - decryptor ocrypto.PrivateKeyDecryptor + privateKeyPem string + publicKeyPem string + decryptor ocrypto.PrivateKeyDecryptor } // List of keys by identifier @@ -170,28 +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), - }, nil - case AlgorithmMLKEM768, AlgorithmMLKEM1024: + case AlgorithmHPQTXWing, + AlgorithmHPQTSecp256r1MLKEM768, AlgorithmHPQTSecp384r1MLKEM1024, + AlgorithmMLKEM768, AlgorithmMLKEM1024: decryptor, err := ocrypto.FromPrivatePEM(string(privatePEM)) if err != nil { - return nil, fmt.Errorf("ocrypto.FromPrivatePEM (ML-KEM) failed: %w", err) + return nil, fmt.Errorf("ocrypto.FromPrivatePEM (%s) failed: %w", k.Algorithm, err) } - return StandardMLKEMCrypto{ - KeyPairInfo: k, - mlkemPrivateKeyPem: string(privatePEM), - mlkemPublicKeyPem: string(certPEM), - decryptor: decryptor, + return StandardKEMCrypto{ + KeyPairInfo: k, + privateKeyPem: string(privatePEM), + publicKeyPem: string(certPEM), + decryptor: decryptor, }, nil case AlgorithmRSA2048, AlgorithmRSA4096: asymDecryption, err := ocrypto.NewAsymDecryption(string(privatePEM)) @@ -373,55 +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) - } - if xw.xwingPublicKeyPem == "" { - return "", fmt.Errorf("no X-Wing public key with id [%s]: %w", kid, ErrCertNotFound) + return "", fmt.Errorf("no 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) } -} - -func (s StandardCrypto) MLKEMPublicKey(kid string) (string, error) { - k, ok := s.keysByID[kid] - if !ok { - return "", fmt.Errorf("no mlkem key with id [%s]: %w", kid, ErrCertNotFound) - } - mlkem, ok := k.(StandardMLKEMCrypto) - if !ok { - return "", fmt.Errorf("key with id [%s] is not an ML-KEM key: %w", kid, ErrCertNotFound) - } - if mlkem.mlkemPublicKeyPem == "" { - return "", fmt.Errorf("no ML-KEM public key with id [%s]: %w", kid, ErrCertNotFound) - } - return mlkem.mlkemPublicKeyPem, nil + return kem.publicKeyPem, nil } func (s StandardCrypto) RSADecrypt(_ crypto.Hash, kid string, _ string, ciphertext []byte) ([]byte, error) { @@ -534,57 +481,14 @@ func (s *StandardCrypto) Decrypt(_ context.Context, keyID trust.KeyIdentifier, c return nil, fmt.Errorf("error decrypting data: %w", err) } - case StandardXWingCrypto: - if len(ephemeralPublicKey) > 0 { - return nil, errors.New("ephemeral public key should not be provided for X-Wing decryption") - } - - privateKey, err := ocrypto.XWingPrivateKeyFromPem([]byte(key.xwingPrivateKeyPem)) - if err != nil { - return nil, fmt.Errorf("failed to parse X-Wing private key: %w", err) - } - - rawKey, err = ocrypto.XWingUnwrapDEK(privateKey, ciphertext) - if err != nil { - return nil, fmt.Errorf("failed to decrypt with X-Wing: %w", err) - } - - case StandardHybridCrypto: - if len(ephemeralPublicKey) > 0 { - return nil, errors.New("ephemeral public key should not be provided for hybrid decryption") - } - - switch key.Algorithm { - case AlgorithmHPQTSecp256r1MLKEM768: - privateKey, err := ocrypto.P256MLKEM768PrivateKeyFromPem([]byte(key.hybridPrivateKeyPem)) - if err != nil { - return nil, fmt.Errorf("failed to parse P256-MLKEM768 private key: %w", err) - } - rawKey, err = ocrypto.P256MLKEM768UnwrapDEK(privateKey, ciphertext) - if err != nil { - return nil, fmt.Errorf("failed to decrypt with P256-MLKEM768: %w", err) - } - case AlgorithmHPQTSecp384r1MLKEM1024: - privateKey, err := ocrypto.P384MLKEM1024PrivateKeyFromPem([]byte(key.hybridPrivateKeyPem)) - if err != nil { - return nil, fmt.Errorf("failed to parse P384-MLKEM1024 private key: %w", err) - } - rawKey, err = ocrypto.P384MLKEM1024UnwrapDEK(privateKey, ciphertext) - if err != nil { - return nil, fmt.Errorf("failed to decrypt with P384-MLKEM1024: %w", err) - } - default: - return nil, fmt.Errorf("unsupported hybrid algorithm [%s]", key.Algorithm) - } - - case StandardMLKEMCrypto: + case StandardKEMCrypto: if len(ephemeralPublicKey) > 0 { - return nil, errors.New("ephemeral public key should not be provided for ML-KEM decryption") + return nil, fmt.Errorf("ephemeral public key should not be provided for %s decryption", key.Algorithm) } rawKey, err = key.decryptor.Decrypt(ciphertext) if err != nil { - return nil, fmt.Errorf("failed to decrypt with ML-KEM: %w", err) + return nil, fmt.Errorf("failed to decrypt with %s: %w", key.Algorithm, err) } default: