diff --git a/docs/grpc/index.html b/docs/grpc/index.html index 30670c641..dea63f4b0 100644 --- a/docs/grpc/index.html +++ b/docs/grpc/index.html @@ -438,10 +438,30 @@

Table of Contents

MInfoResponse +
  • + MKeyAccess +
  • + +
  • + MKeyAccessRewrapResult +
  • + +
  • + MKeyAccessRewrapResult.MetadataEntry +
  • +
  • MLegacyPublicKeyRequest
  • +
  • + MPolicyBinding +
  • + +
  • + MPolicyRewrapResult +
  • +
  • MPublicKeyRequest
  • @@ -462,6 +482,22 @@

    Table of Contents

    MRewrapResponse.MetadataEntry +
  • + MUnsignedRewrapRequest +
  • + +
  • + MUnsignedRewrapRequest.WithKeyAccessObject +
  • + +
  • + MUnsignedRewrapRequest.WithPolicy +
  • + +
  • + MUnsignedRewrapRequest.WithPolicyRequest +
  • + @@ -3203,6 +3239,169 @@

    InfoResponse

    +

    KeyAccess

    +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeLabelDescription
    encrypted_metadatastring

    policy_bindingPolicyBinding

    protocolstring

    key_typestring

    kas_urlstring

    kidstring

    split_idstring

    wrapped_keybytes

    headerbytes

    header is only used for NanoTDFs

    + + + + + +

    KeyAccessRewrapResult

    +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeLabelDescription
    metadataKeyAccessRewrapResult.MetadataEntryrepeated

    key_access_object_idstring

    statusstring

    kas_wrapped_keybytes

    errorstring

    + + + + + +

    KeyAccessRewrapResult.MetadataEntry

    +

    + + + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeLabelDescription
    keystring

    valuegoogle.protobuf.Value

    + + + + +

    LegacyPublicKeyRequest

    @@ -3227,6 +3426,68 @@

    LegacyPublicKeyRequest

    +

    PolicyBinding

    +

    + + + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeLabelDescription
    algorithmstring

    hashstring

    + + + + + +

    PolicyRewrapResult

    +

    + + + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeLabelDescription
    policy_idstring

    resultsKeyAccessRewrapResultrepeated

    + + + + +

    PublicKeyRequest

    @@ -3334,14 +3595,14 @@

    RewrapResponse

    metadata RewrapResponse.MetadataEntry repeated -

    +

    Deprecated.

    entity_wrapped_key bytes -

    +

    Deprecated.

    @@ -3355,13 +3616,51 @@

    RewrapResponse

    schema_version string -

    +

    Deprecated.

    + + + + responses + PolicyRewrapResult + repeated +

    New Rewrap API changes

    + + +

    Fields with deprecated option

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameOption
    metadata

    true

    entity_wrapped_key

    true

    schema_version

    true

    + + @@ -3396,6 +3695,137 @@

    RewrapResponse.MetadataEntry

    +

    UnsignedRewrapRequest

    +

    + + + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeLabelDescription
    client_public_keystring

    requestsUnsignedRewrapRequest.WithPolicyRequestrepeated

    + + + + + +

    UnsignedRewrapRequest.WithKeyAccessObject

    +

    + + + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeLabelDescription
    key_access_object_idstring

    key_access_objectKeyAccess

    + + + + + +

    UnsignedRewrapRequest.WithPolicy

    +

    + + + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeLabelDescription
    idstring

    bodystring

    + + + + + +

    UnsignedRewrapRequest.WithPolicyRequest

    +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeLabelDescription
    key_access_objectsUnsignedRewrapRequest.WithKeyAccessObjectrepeated

    policyUnsignedRewrapRequest.WithPolicy

    algorithmstring

    + + + + + diff --git a/docs/openapi/kas/kas.swagger.json b/docs/openapi/kas/kas.swagger.json index 485207629..d422537e3 100644 --- a/docs/openapi/kas/kas.swagger.json +++ b/docs/openapi/kas/kas.swagger.json @@ -130,6 +130,43 @@ } }, "definitions": { + "kasKeyAccessRewrapResult": { + "type": "object", + "properties": { + "metadata": { + "type": "object", + "additionalProperties": {} + }, + "keyAccessObjectId": { + "type": "string" + }, + "status": { + "type": "string" + }, + "kasWrappedKey": { + "type": "string", + "format": "byte" + }, + "error": { + "type": "string" + } + } + }, + "kasPolicyRewrapResult": { + "type": "object", + "properties": { + "policyId": { + "type": "string" + }, + "results": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/kasKeyAccessRewrapResult" + } + } + } + }, "kasPublicKeyResponse": { "type": "object", "properties": { @@ -165,6 +202,14 @@ }, "schemaVersion": { "type": "string" + }, + "responses": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/kasPolicyRewrapResult" + }, + "title": "New Rewrap API changes" } } }, diff --git a/examples/cmd/benchmark.go b/examples/cmd/benchmark.go index 3909cb1ff..b61dff2ee 100644 --- a/examples/cmd/benchmark.go +++ b/examples/cmd/benchmark.go @@ -204,7 +204,10 @@ func runBenchmark(cmd *cobra.Command, args []string) error { } totalTime := time.Since(startTime) - averageLatency := totalDuration / time.Duration(successCount) + var averageLatency time.Duration + if successCount > 0 { + averageLatency = totalDuration / time.Duration(successCount) + } throughput := float64(successCount) / totalTime.Seconds() // Print results @@ -214,7 +217,9 @@ func runBenchmark(cmd *cobra.Command, args []string) error { cmd.Printf("Failed Requests: %d\n", errorCount) cmd.Printf("Concurrent Requests: %d\n", config.ConcurrentRequests) cmd.Printf("Total Time: %s\n", totalTime) - cmd.Printf("Average Latency: %s\n", averageLatency) + if successCount > 0 { + cmd.Printf("Average Latency: %s\n", averageLatency) + } cmd.Printf("Throughput: %.2f requests/second\n", throughput) if errorCount > 0 { diff --git a/examples/cmd/benchmark_bulk.go b/examples/cmd/benchmark_bulk.go new file mode 100644 index 000000000..c989142c7 --- /dev/null +++ b/examples/cmd/benchmark_bulk.go @@ -0,0 +1,169 @@ +package cmd + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "os" + "strings" + "time" + + "github.com/opentdf/platform/sdk" + "github.com/spf13/cobra" +) + +func init() { + benchmarkCmd := &cobra.Command{ + Use: "benchmark-bulk", + Short: "OpenTDF benchmark tool", + Long: `A OpenTDF benchmark tool to measure Bulk Rewrap.`, + RunE: runBenchmarkBulk, + } + + benchmarkCmd.Flags().IntVar(&config.RequestCount, "count", 100, "Total number of requests") + benchmarkCmd.Flags().Var(&config.TDFFormat, "tdf", "TDF format (tdf3 or nanotdf)") + ExamplesCmd.AddCommand(benchmarkCmd) +} + +func runBenchmarkBulk(cmd *cobra.Command, args []string) error { + in := strings.NewReader("Hello, World!") + + // Create new offline client + client, err := newSDK() + if err != nil { + return err + } + + out := os.Stdout + if outputName != "-" { + out, err = os.Create("sensitive.txt.tdf") + if err != nil { + return err + } + } + defer func() { + if outputName != "-" { + out.Close() + } + }() + + dataAttributes := []string{"https://example.com/attr/attr1/value/value1"} + if config.TDFFormat == NanoTDF { + nanoTDFConfig, err := client.NewNanoTDFConfig() + if err != nil { + return err + } + nanoTDFConfig.SetAttributes(dataAttributes) + nanoTDFConfig.EnableECDSAPolicyBinding() + err = nanoTDFConfig.SetKasURL(fmt.Sprintf("http://%s/kas", "localhost:8080")) + if err != nil { + return err + } + + _, err = client.CreateNanoTDF(out, in, *nanoTDFConfig) + if err != nil { + return err + } + + if outputName != "-" { + err = cat(cmd, outputName) + if err != nil { + return err + } + } + } else { + tdf, err := + client.CreateTDF( + out, in, + sdk.WithDataAttributes(dataAttributes...), + sdk.WithKasInformation( + sdk.KASInfo{ + URL: fmt.Sprintf("http://%s", "localhost:8080"), + PublicKey: "", + }), + sdk.WithAutoconfigure(false)) + if err != nil { + return err + } + + manifestJSON, err := json.MarshalIndent(tdf.Manifest(), "", " ") + if err != nil { + return err + } + cmd.Println(string(manifestJSON)) + } + + var errors []error + var requestFailure error + + // Function to perform the operation + operation := func() { + file, err := os.Open("sensitive.txt.tdf") + if err != nil { + requestFailure = fmt.Errorf("file open error: %v", err) + return + } + defer file.Close() + cipher, _ := io.ReadAll(file) + + file.Seek(0, 0) + format := sdk.Nano + var bulkTdfs []*sdk.BulkTDF + if config.TDFFormat == "tdf3" { + format = sdk.Standard + } + for i := 0; i < config.RequestCount; i++ { + bulkTdfs = append(bulkTdfs, &sdk.BulkTDF{Reader: bytes.NewReader(cipher), Writer: io.Discard}) + } + err = client.BulkDecrypt(context.Background(), sdk.WithTDFs(bulkTdfs...), sdk.WithTDFType(format)) + if err != nil { + if errList, ok := sdk.FromBulkErrors(err); ok { + errors = errList + } else { + requestFailure = err + } + } + + } + + // Start the benchmark + startTime := time.Now() + operation() + totalTime := time.Since(startTime) + + // Count errors and collect error messages + errorCount := 0 + successCount := 0 + if requestFailure != nil { + errorCount = config.RequestCount + errors = append(errors, requestFailure) + } else { + errorCount = len(errors) + successCount = config.RequestCount - errorCount + } + throughput := float64(successCount) / totalTime.Seconds() + + errorMsgs := make(map[string]int) + for _, err := range errors { + errorMsgs[err.Error()] += 1 + } + + // Print results + cmd.Printf("\nBenchmark Results:\n") + cmd.Printf("Total Decrypts: %d\n", config.RequestCount) + cmd.Printf("Successful Decrypts: %d\n", successCount) + cmd.Printf("Failed Decrypts: %d\n", errorCount) + cmd.Printf("Total Time: %s\n", totalTime) + cmd.Printf("Throughput: %.2f requests/second\n", throughput) + + if errorCount > 0 { + cmd.Printf("\nError Summary:\n") + for errMsg, count := range errorMsgs { + cmd.Printf("%s: %d occurrences\n", errMsg, count) + } + } + + return nil +} diff --git a/protocol/go/kas/kas.pb.go b/protocol/go/kas/kas.pb.go index eee98539a..7451da9c9 100644 --- a/protocol/go/kas/kas.pb.go +++ b/protocol/go/kas/kas.pb.go @@ -157,6 +157,228 @@ func (x *LegacyPublicKeyRequest) GetAlgorithm() string { return "" } +type PolicyBinding struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Algorithm string `protobuf:"bytes,1,opt,name=algorithm,json=alg,proto3" json:"algorithm,omitempty"` + Hash string `protobuf:"bytes,2,opt,name=hash,proto3" json:"hash,omitempty"` +} + +func (x *PolicyBinding) Reset() { + *x = PolicyBinding{} + if protoimpl.UnsafeEnabled { + mi := &file_kas_kas_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PolicyBinding) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PolicyBinding) ProtoMessage() {} + +func (x *PolicyBinding) ProtoReflect() protoreflect.Message { + mi := &file_kas_kas_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PolicyBinding.ProtoReflect.Descriptor instead. +func (*PolicyBinding) Descriptor() ([]byte, []int) { + return file_kas_kas_proto_rawDescGZIP(), []int{3} +} + +func (x *PolicyBinding) GetAlgorithm() string { + if x != nil { + return x.Algorithm + } + return "" +} + +func (x *PolicyBinding) GetHash() string { + if x != nil { + return x.Hash + } + return "" +} + +type KeyAccess struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + EncryptedMetadata string `protobuf:"bytes,1,opt,name=encrypted_metadata,json=encryptedMetadata,proto3" json:"encrypted_metadata,omitempty"` + PolicyBinding *PolicyBinding `protobuf:"bytes,2,opt,name=policy_binding,json=policyBinding,proto3" json:"policy_binding,omitempty"` + Protocol string `protobuf:"bytes,3,opt,name=protocol,proto3" json:"protocol,omitempty"` + KeyType string `protobuf:"bytes,4,opt,name=key_type,json=type,proto3" json:"key_type,omitempty"` + KasUrl string `protobuf:"bytes,5,opt,name=kas_url,json=url,proto3" json:"kas_url,omitempty"` + Kid string `protobuf:"bytes,6,opt,name=kid,proto3" json:"kid,omitempty"` + SplitId string `protobuf:"bytes,7,opt,name=split_id,json=sid,proto3" json:"split_id,omitempty"` + WrappedKey []byte `protobuf:"bytes,8,opt,name=wrapped_key,json=wrappedKey,proto3" json:"wrapped_key,omitempty"` + // header is only used for NanoTDFs + Header []byte `protobuf:"bytes,9,opt,name=header,proto3" json:"header,omitempty"` +} + +func (x *KeyAccess) Reset() { + *x = KeyAccess{} + if protoimpl.UnsafeEnabled { + mi := &file_kas_kas_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *KeyAccess) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*KeyAccess) ProtoMessage() {} + +func (x *KeyAccess) ProtoReflect() protoreflect.Message { + mi := &file_kas_kas_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use KeyAccess.ProtoReflect.Descriptor instead. +func (*KeyAccess) Descriptor() ([]byte, []int) { + return file_kas_kas_proto_rawDescGZIP(), []int{4} +} + +func (x *KeyAccess) GetEncryptedMetadata() string { + if x != nil { + return x.EncryptedMetadata + } + return "" +} + +func (x *KeyAccess) GetPolicyBinding() *PolicyBinding { + if x != nil { + return x.PolicyBinding + } + return nil +} + +func (x *KeyAccess) GetProtocol() string { + if x != nil { + return x.Protocol + } + return "" +} + +func (x *KeyAccess) GetKeyType() string { + if x != nil { + return x.KeyType + } + return "" +} + +func (x *KeyAccess) GetKasUrl() string { + if x != nil { + return x.KasUrl + } + return "" +} + +func (x *KeyAccess) GetKid() string { + if x != nil { + return x.Kid + } + return "" +} + +func (x *KeyAccess) GetSplitId() string { + if x != nil { + return x.SplitId + } + return "" +} + +func (x *KeyAccess) GetWrappedKey() []byte { + if x != nil { + return x.WrappedKey + } + return nil +} + +func (x *KeyAccess) GetHeader() []byte { + if x != nil { + return x.Header + } + return nil +} + +type UnsignedRewrapRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ClientPublicKey string `protobuf:"bytes,1,opt,name=client_public_key,json=clientPublicKey,proto3" json:"client_public_key,omitempty"` + Requests []*UnsignedRewrapRequest_WithPolicyRequest `protobuf:"bytes,2,rep,name=requests,proto3" json:"requests,omitempty"` +} + +func (x *UnsignedRewrapRequest) Reset() { + *x = UnsignedRewrapRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_kas_kas_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UnsignedRewrapRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UnsignedRewrapRequest) ProtoMessage() {} + +func (x *UnsignedRewrapRequest) ProtoReflect() protoreflect.Message { + mi := &file_kas_kas_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UnsignedRewrapRequest.ProtoReflect.Descriptor instead. +func (*UnsignedRewrapRequest) Descriptor() ([]byte, []int) { + return file_kas_kas_proto_rawDescGZIP(), []int{5} +} + +func (x *UnsignedRewrapRequest) GetClientPublicKey() string { + if x != nil { + return x.ClientPublicKey + } + return "" +} + +func (x *UnsignedRewrapRequest) GetRequests() []*UnsignedRewrapRequest_WithPolicyRequest { + if x != nil { + return x.Requests + } + return nil +} + type PublicKeyRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -170,7 +392,7 @@ type PublicKeyRequest struct { func (x *PublicKeyRequest) Reset() { *x = PublicKeyRequest{} if protoimpl.UnsafeEnabled { - mi := &file_kas_kas_proto_msgTypes[3] + mi := &file_kas_kas_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -183,7 +405,7 @@ func (x *PublicKeyRequest) String() string { func (*PublicKeyRequest) ProtoMessage() {} func (x *PublicKeyRequest) ProtoReflect() protoreflect.Message { - mi := &file_kas_kas_proto_msgTypes[3] + mi := &file_kas_kas_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -196,7 +418,7 @@ func (x *PublicKeyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PublicKeyRequest.ProtoReflect.Descriptor instead. func (*PublicKeyRequest) Descriptor() ([]byte, []int) { - return file_kas_kas_proto_rawDescGZIP(), []int{3} + return file_kas_kas_proto_rawDescGZIP(), []int{6} } func (x *PublicKeyRequest) GetAlgorithm() string { @@ -232,7 +454,7 @@ type PublicKeyResponse struct { func (x *PublicKeyResponse) Reset() { *x = PublicKeyResponse{} if protoimpl.UnsafeEnabled { - mi := &file_kas_kas_proto_msgTypes[4] + mi := &file_kas_kas_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -245,7 +467,7 @@ func (x *PublicKeyResponse) String() string { func (*PublicKeyResponse) ProtoMessage() {} func (x *PublicKeyResponse) ProtoReflect() protoreflect.Message { - mi := &file_kas_kas_proto_msgTypes[4] + mi := &file_kas_kas_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -258,7 +480,7 @@ func (x *PublicKeyResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use PublicKeyResponse.ProtoReflect.Descriptor instead. func (*PublicKeyResponse) Descriptor() ([]byte, []int) { - return file_kas_kas_proto_rawDescGZIP(), []int{4} + return file_kas_kas_proto_rawDescGZIP(), []int{7} } func (x *PublicKeyResponse) GetPublicKey() string { @@ -286,7 +508,7 @@ type RewrapRequest struct { func (x *RewrapRequest) Reset() { *x = RewrapRequest{} if protoimpl.UnsafeEnabled { - mi := &file_kas_kas_proto_msgTypes[5] + mi := &file_kas_kas_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -299,7 +521,7 @@ func (x *RewrapRequest) String() string { func (*RewrapRequest) ProtoMessage() {} func (x *RewrapRequest) ProtoReflect() protoreflect.Message { - mi := &file_kas_kas_proto_msgTypes[5] + mi := &file_kas_kas_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -312,7 +534,7 @@ func (x *RewrapRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RewrapRequest.ProtoReflect.Descriptor instead. func (*RewrapRequest) Descriptor() ([]byte, []int) { - return file_kas_kas_proto_rawDescGZIP(), []int{5} + return file_kas_kas_proto_rawDescGZIP(), []int{8} } func (x *RewrapRequest) GetSignedRequestToken() string { @@ -322,21 +544,186 @@ func (x *RewrapRequest) GetSignedRequestToken() string { return "" } +type KeyAccessRewrapResult struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Metadata map[string]*structpb.Value `protobuf:"bytes,1,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + KeyAccessObjectId string `protobuf:"bytes,2,opt,name=key_access_object_id,json=keyAccessObjectId,proto3" json:"key_access_object_id,omitempty"` + Status string `protobuf:"bytes,3,opt,name=status,proto3" json:"status,omitempty"` + // Types that are assignable to Result: + // + // *KeyAccessRewrapResult_KasWrappedKey + // *KeyAccessRewrapResult_Error + Result isKeyAccessRewrapResult_Result `protobuf_oneof:"result"` +} + +func (x *KeyAccessRewrapResult) Reset() { + *x = KeyAccessRewrapResult{} + if protoimpl.UnsafeEnabled { + mi := &file_kas_kas_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *KeyAccessRewrapResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*KeyAccessRewrapResult) ProtoMessage() {} + +func (x *KeyAccessRewrapResult) ProtoReflect() protoreflect.Message { + mi := &file_kas_kas_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use KeyAccessRewrapResult.ProtoReflect.Descriptor instead. +func (*KeyAccessRewrapResult) Descriptor() ([]byte, []int) { + return file_kas_kas_proto_rawDescGZIP(), []int{9} +} + +func (x *KeyAccessRewrapResult) GetMetadata() map[string]*structpb.Value { + if x != nil { + return x.Metadata + } + return nil +} + +func (x *KeyAccessRewrapResult) GetKeyAccessObjectId() string { + if x != nil { + return x.KeyAccessObjectId + } + return "" +} + +func (x *KeyAccessRewrapResult) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (m *KeyAccessRewrapResult) GetResult() isKeyAccessRewrapResult_Result { + if m != nil { + return m.Result + } + return nil +} + +func (x *KeyAccessRewrapResult) GetKasWrappedKey() []byte { + if x, ok := x.GetResult().(*KeyAccessRewrapResult_KasWrappedKey); ok { + return x.KasWrappedKey + } + return nil +} + +func (x *KeyAccessRewrapResult) GetError() string { + if x, ok := x.GetResult().(*KeyAccessRewrapResult_Error); ok { + return x.Error + } + return "" +} + +type isKeyAccessRewrapResult_Result interface { + isKeyAccessRewrapResult_Result() +} + +type KeyAccessRewrapResult_KasWrappedKey struct { + KasWrappedKey []byte `protobuf:"bytes,4,opt,name=kas_wrapped_key,json=kasWrappedKey,proto3,oneof"` +} + +type KeyAccessRewrapResult_Error struct { + Error string `protobuf:"bytes,5,opt,name=error,proto3,oneof"` +} + +func (*KeyAccessRewrapResult_KasWrappedKey) isKeyAccessRewrapResult_Result() {} + +func (*KeyAccessRewrapResult_Error) isKeyAccessRewrapResult_Result() {} + +type PolicyRewrapResult struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PolicyId string `protobuf:"bytes,1,opt,name=policy_id,json=policyId,proto3" json:"policy_id,omitempty"` + Results []*KeyAccessRewrapResult `protobuf:"bytes,2,rep,name=results,proto3" json:"results,omitempty"` +} + +func (x *PolicyRewrapResult) Reset() { + *x = PolicyRewrapResult{} + if protoimpl.UnsafeEnabled { + mi := &file_kas_kas_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PolicyRewrapResult) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PolicyRewrapResult) ProtoMessage() {} + +func (x *PolicyRewrapResult) ProtoReflect() protoreflect.Message { + mi := &file_kas_kas_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PolicyRewrapResult.ProtoReflect.Descriptor instead. +func (*PolicyRewrapResult) Descriptor() ([]byte, []int) { + return file_kas_kas_proto_rawDescGZIP(), []int{10} +} + +func (x *PolicyRewrapResult) GetPolicyId() string { + if x != nil { + return x.PolicyId + } + return "" +} + +func (x *PolicyRewrapResult) GetResults() []*KeyAccessRewrapResult { + if x != nil { + return x.Results + } + return nil +} + type RewrapResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Metadata map[string]*structpb.Value `protobuf:"bytes,1,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - EntityWrappedKey []byte `protobuf:"bytes,2,opt,name=entity_wrapped_key,json=entityWrappedKey,proto3" json:"entity_wrapped_key,omitempty"` - SessionPublicKey string `protobuf:"bytes,3,opt,name=session_public_key,json=sessionPublicKey,proto3" json:"session_public_key,omitempty"` - SchemaVersion string `protobuf:"bytes,4,opt,name=schema_version,json=schemaVersion,proto3" json:"schema_version,omitempty"` + // Deprecated: Marked as deprecated in kas/kas.proto. + Metadata map[string]*structpb.Value `protobuf:"bytes,1,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // Deprecated: Marked as deprecated in kas/kas.proto. + EntityWrappedKey []byte `protobuf:"bytes,2,opt,name=entity_wrapped_key,json=entityWrappedKey,proto3" json:"entity_wrapped_key,omitempty"` + SessionPublicKey string `protobuf:"bytes,3,opt,name=session_public_key,json=sessionPublicKey,proto3" json:"session_public_key,omitempty"` + // Deprecated: Marked as deprecated in kas/kas.proto. + SchemaVersion string `protobuf:"bytes,4,opt,name=schema_version,json=schemaVersion,proto3" json:"schema_version,omitempty"` + // New Rewrap API changes + Responses []*PolicyRewrapResult `protobuf:"bytes,5,rep,name=responses,proto3" json:"responses,omitempty"` } func (x *RewrapResponse) Reset() { *x = RewrapResponse{} if protoimpl.UnsafeEnabled { - mi := &file_kas_kas_proto_msgTypes[6] + mi := &file_kas_kas_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -349,7 +736,7 @@ func (x *RewrapResponse) String() string { func (*RewrapResponse) ProtoMessage() {} func (x *RewrapResponse) ProtoReflect() protoreflect.Message { - mi := &file_kas_kas_proto_msgTypes[6] + mi := &file_kas_kas_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -362,9 +749,10 @@ func (x *RewrapResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RewrapResponse.ProtoReflect.Descriptor instead. func (*RewrapResponse) Descriptor() ([]byte, []int) { - return file_kas_kas_proto_rawDescGZIP(), []int{6} + return file_kas_kas_proto_rawDescGZIP(), []int{11} } +// Deprecated: Marked as deprecated in kas/kas.proto. func (x *RewrapResponse) GetMetadata() map[string]*structpb.Value { if x != nil { return x.Metadata @@ -372,6 +760,7 @@ func (x *RewrapResponse) GetMetadata() map[string]*structpb.Value { return nil } +// Deprecated: Marked as deprecated in kas/kas.proto. func (x *RewrapResponse) GetEntityWrappedKey() []byte { if x != nil { return x.EntityWrappedKey @@ -386,6 +775,7 @@ func (x *RewrapResponse) GetSessionPublicKey() string { return "" } +// Deprecated: Marked as deprecated in kas/kas.proto. func (x *RewrapResponse) GetSchemaVersion() string { if x != nil { return x.SchemaVersion @@ -393,6 +783,186 @@ func (x *RewrapResponse) GetSchemaVersion() string { return "" } +func (x *RewrapResponse) GetResponses() []*PolicyRewrapResult { + if x != nil { + return x.Responses + } + return nil +} + +type UnsignedRewrapRequest_WithPolicy struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Body string `protobuf:"bytes,2,opt,name=body,proto3" json:"body,omitempty"` +} + +func (x *UnsignedRewrapRequest_WithPolicy) Reset() { + *x = UnsignedRewrapRequest_WithPolicy{} + if protoimpl.UnsafeEnabled { + mi := &file_kas_kas_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UnsignedRewrapRequest_WithPolicy) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UnsignedRewrapRequest_WithPolicy) ProtoMessage() {} + +func (x *UnsignedRewrapRequest_WithPolicy) ProtoReflect() protoreflect.Message { + mi := &file_kas_kas_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UnsignedRewrapRequest_WithPolicy.ProtoReflect.Descriptor instead. +func (*UnsignedRewrapRequest_WithPolicy) Descriptor() ([]byte, []int) { + return file_kas_kas_proto_rawDescGZIP(), []int{5, 0} +} + +func (x *UnsignedRewrapRequest_WithPolicy) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *UnsignedRewrapRequest_WithPolicy) GetBody() string { + if x != nil { + return x.Body + } + return "" +} + +type UnsignedRewrapRequest_WithKeyAccessObject struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + KeyAccessObjectId string `protobuf:"bytes,1,opt,name=key_access_object_id,json=keyAccessObjectId,proto3" json:"key_access_object_id,omitempty"` + KeyAccessObject *KeyAccess `protobuf:"bytes,2,opt,name=key_access_object,json=keyAccessObject,proto3" json:"key_access_object,omitempty"` +} + +func (x *UnsignedRewrapRequest_WithKeyAccessObject) Reset() { + *x = UnsignedRewrapRequest_WithKeyAccessObject{} + if protoimpl.UnsafeEnabled { + mi := &file_kas_kas_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UnsignedRewrapRequest_WithKeyAccessObject) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UnsignedRewrapRequest_WithKeyAccessObject) ProtoMessage() {} + +func (x *UnsignedRewrapRequest_WithKeyAccessObject) ProtoReflect() protoreflect.Message { + mi := &file_kas_kas_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UnsignedRewrapRequest_WithKeyAccessObject.ProtoReflect.Descriptor instead. +func (*UnsignedRewrapRequest_WithKeyAccessObject) Descriptor() ([]byte, []int) { + return file_kas_kas_proto_rawDescGZIP(), []int{5, 1} +} + +func (x *UnsignedRewrapRequest_WithKeyAccessObject) GetKeyAccessObjectId() string { + if x != nil { + return x.KeyAccessObjectId + } + return "" +} + +func (x *UnsignedRewrapRequest_WithKeyAccessObject) GetKeyAccessObject() *KeyAccess { + if x != nil { + return x.KeyAccessObject + } + return nil +} + +type UnsignedRewrapRequest_WithPolicyRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + KeyAccessObjects []*UnsignedRewrapRequest_WithKeyAccessObject `protobuf:"bytes,1,rep,name=key_access_objects,json=keyAccessObjects,proto3" json:"key_access_objects,omitempty"` + Policy *UnsignedRewrapRequest_WithPolicy `protobuf:"bytes,2,opt,name=policy,proto3" json:"policy,omitempty"` + Algorithm string `protobuf:"bytes,3,opt,name=algorithm,proto3" json:"algorithm,omitempty"` +} + +func (x *UnsignedRewrapRequest_WithPolicyRequest) Reset() { + *x = UnsignedRewrapRequest_WithPolicyRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_kas_kas_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UnsignedRewrapRequest_WithPolicyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UnsignedRewrapRequest_WithPolicyRequest) ProtoMessage() {} + +func (x *UnsignedRewrapRequest_WithPolicyRequest) ProtoReflect() protoreflect.Message { + mi := &file_kas_kas_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UnsignedRewrapRequest_WithPolicyRequest.ProtoReflect.Descriptor instead. +func (*UnsignedRewrapRequest_WithPolicyRequest) Descriptor() ([]byte, []int) { + return file_kas_kas_proto_rawDescGZIP(), []int{5, 2} +} + +func (x *UnsignedRewrapRequest_WithPolicyRequest) GetKeyAccessObjects() []*UnsignedRewrapRequest_WithKeyAccessObject { + if x != nil { + return x.KeyAccessObjects + } + return nil +} + +func (x *UnsignedRewrapRequest_WithPolicyRequest) GetPolicy() *UnsignedRewrapRequest_WithPolicy { + if x != nil { + return x.Policy + } + return nil +} + +func (x *UnsignedRewrapRequest_WithPolicyRequest) GetAlgorithm() string { + if x != nil { + return x.Algorithm + } + return "" +} + var File_kas_kas_proto protoreflect.FileDescriptor var file_kas_kas_proto_rawDesc = []byte{ @@ -413,82 +983,169 @@ var file_kas_kas_proto_rawDesc = []byte{ 0x61, 0x63, 0x79, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, - 0x6d, 0x22, 0xb1, 0x01, 0x0a, 0x10, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x51, 0x0a, 0x09, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, - 0x74, 0x68, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x33, 0x92, 0x41, 0x30, 0x32, 0x2e, - 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x72, - 0x73, 0x61, 0x3a, 0x3c, 0x6b, 0x65, 0x79, 0x73, 0x69, 0x7a, 0x65, 0x3e, 0x20, 0x6f, 0x72, 0x20, - 0x65, 0x63, 0x3a, 0x3c, 0x63, 0x75, 0x72, 0x76, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x3e, 0x52, 0x09, - 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x26, 0x0a, 0x03, 0x66, 0x6d, 0x74, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x14, 0x92, 0x41, 0x11, 0x32, 0x0f, 0x72, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x52, 0x03, 0x66, 0x6d, - 0x74, 0x12, 0x22, 0x0a, 0x01, 0x76, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x14, 0x92, 0x41, - 0x11, 0x32, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x52, 0x01, 0x76, 0x22, 0x44, 0x0a, 0x11, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, - 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, - 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x69, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x69, 0x64, 0x22, 0x4f, 0x0a, 0x0d, 0x52, - 0x65, 0x77, 0x72, 0x61, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x30, 0x0a, 0x14, - 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x74, - 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x73, 0x69, 0x67, 0x6e, - 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x4a, 0x04, - 0x08, 0x02, 0x10, 0x03, 0x52, 0x06, 0x62, 0x65, 0x61, 0x72, 0x65, 0x72, 0x22, 0xa7, 0x02, 0x0a, - 0x0e, 0x52, 0x65, 0x77, 0x72, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x3d, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x21, 0x2e, 0x6b, 0x61, 0x73, 0x2e, 0x52, 0x65, 0x77, 0x72, 0x61, 0x70, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2c, + 0x6d, 0x22, 0x3b, 0x0a, 0x0d, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x42, 0x69, 0x6e, 0x64, 0x69, + 0x6e, 0x67, 0x12, 0x16, 0x0a, 0x09, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x6c, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, + 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x22, 0xa1, + 0x02, 0x0a, 0x09, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x2d, 0x0a, 0x12, + 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, + 0x74, 0x65, 0x64, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x39, 0x0a, 0x0e, 0x70, + 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x62, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6b, 0x61, 0x73, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x42, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x0d, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x42, + 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6c, 0x12, 0x16, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x07, 0x6b, 0x61, + 0x73, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x69, 0x64, 0x12, 0x15, 0x0a, 0x08, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x73, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x77, 0x72, 0x61, + 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, + 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x22, 0x95, 0x04, 0x0a, 0x15, 0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x52, + 0x65, 0x77, 0x72, 0x61, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2a, 0x0a, 0x11, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x50, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x48, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x6b, 0x61, 0x73, + 0x2e, 0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x52, 0x65, 0x77, 0x72, 0x61, 0x70, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x50, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x73, 0x1a, 0x30, 0x0a, 0x0a, 0x57, 0x69, 0x74, 0x68, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, + 0x12, 0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x62, 0x6f, 0x64, 0x79, 0x1a, 0x82, 0x01, 0x0a, 0x13, 0x57, 0x69, 0x74, 0x68, 0x4b, 0x65, 0x79, + 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x2f, 0x0a, 0x14, + 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, + 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x6b, 0x65, 0x79, 0x41, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x12, 0x3a, 0x0a, + 0x11, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6f, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6b, 0x61, 0x73, 0x2e, 0x4b, + 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x0f, 0x6b, 0x65, 0x79, 0x41, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x1a, 0xce, 0x01, 0x0a, 0x11, 0x57, 0x69, + 0x74, 0x68, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x5c, 0x0a, 0x12, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x6b, 0x61, + 0x73, 0x2e, 0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x52, 0x65, 0x77, 0x72, 0x61, 0x70, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x41, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x10, 0x6b, 0x65, 0x79, + 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x3d, 0x0a, + 0x06, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, + 0x6b, 0x61, 0x73, 0x2e, 0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x52, 0x65, 0x77, 0x72, + 0x61, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x57, 0x69, 0x74, 0x68, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x52, 0x06, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x1c, 0x0a, 0x09, + 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x22, 0xb1, 0x01, 0x0a, 0x10, 0x50, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x51, 0x0a, 0x09, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x33, 0x92, 0x41, 0x30, 0x32, 0x2e, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, + 0x68, 0x6d, 0x20, 0x74, 0x79, 0x70, 0x65, 0x20, 0x72, 0x73, 0x61, 0x3a, 0x3c, 0x6b, 0x65, 0x79, + 0x73, 0x69, 0x7a, 0x65, 0x3e, 0x20, 0x6f, 0x72, 0x20, 0x65, 0x63, 0x3a, 0x3c, 0x63, 0x75, 0x72, + 0x76, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x3e, 0x52, 0x09, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, + 0x68, 0x6d, 0x12, 0x26, 0x0a, 0x03, 0x66, 0x6d, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x14, 0x92, 0x41, 0x11, 0x32, 0x0f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x20, 0x66, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x52, 0x03, 0x66, 0x6d, 0x74, 0x12, 0x22, 0x0a, 0x01, 0x76, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x14, 0x92, 0x41, 0x11, 0x32, 0x0f, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x01, 0x76, 0x22, 0x44, + 0x0a, 0x11, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, + 0x65, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x6b, 0x69, 0x64, 0x22, 0x4f, 0x0a, 0x0d, 0x52, 0x65, 0x77, 0x72, 0x61, 0x70, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x12, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x52, 0x06, 0x62, + 0x65, 0x61, 0x72, 0x65, 0x72, 0x22, 0xc7, 0x02, 0x0a, 0x15, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x52, 0x65, 0x77, 0x72, 0x61, 0x70, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, + 0x44, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x28, 0x2e, 0x6b, 0x61, 0x73, 0x2e, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x52, 0x65, 0x77, 0x72, 0x61, 0x70, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2e, 0x4d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2f, 0x0a, 0x14, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x11, 0x6b, 0x65, 0x79, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4f, 0x62, + 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x28, + 0x0a, 0x0f, 0x6b, 0x61, 0x73, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, + 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x0d, 0x6b, 0x61, 0x73, 0x57, 0x72, + 0x61, 0x70, 0x70, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x1a, 0x53, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, + 0x67, 0x0a, 0x12, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x77, 0x72, 0x61, 0x70, 0x52, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x49, 0x64, 0x12, 0x34, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6b, 0x61, 0x73, 0x2e, 0x4b, 0x65, 0x79, 0x41, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x52, 0x65, 0x77, 0x72, 0x61, 0x70, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, + 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0xea, 0x02, 0x0a, 0x0e, 0x52, 0x65, 0x77, + 0x72, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x08, 0x6d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, + 0x6b, 0x61, 0x73, 0x2e, 0x52, 0x65, 0x77, 0x72, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x42, 0x02, 0x18, 0x01, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x30, 0x0a, 0x12, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, - 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x65, 0x6e, 0x74, 0x69, - 0x74, 0x79, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x12, - 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, - 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x63, - 0x68, 0x65, 0x6d, 0x61, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0d, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x1a, 0x53, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x32, 0xce, 0x02, 0x0a, 0x0d, 0x41, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x69, 0x0a, 0x09, 0x50, 0x75, 0x62, 0x6c, - 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x15, 0x2e, 0x6b, 0x61, 0x73, 0x2e, 0x50, 0x75, 0x62, 0x6c, - 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6b, - 0x61, 0x73, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2d, 0x92, 0x41, 0x09, 0x4a, 0x07, 0x0a, 0x03, 0x32, 0x30, 0x30, - 0x12, 0x00, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x6b, 0x61, 0x73, 0x2f, 0x76, - 0x32, 0x2f, 0x6b, 0x61, 0x73, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, - 0x90, 0x02, 0x01, 0x12, 0x78, 0x0a, 0x0f, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x50, 0x75, 0x62, - 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x1b, 0x2e, 0x6b, 0x61, 0x73, 0x2e, 0x4c, 0x65, 0x67, - 0x61, 0x63, 0x79, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x22, 0x2a, 0x92, 0x41, 0x09, 0x4a, 0x07, 0x0a, 0x03, 0x32, 0x30, 0x30, 0x12, 0x00, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x6b, 0x61, 0x73, 0x2f, 0x6b, 0x61, 0x73, 0x5f, - 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x90, 0x02, 0x01, 0x12, 0x58, 0x0a, - 0x06, 0x52, 0x65, 0x77, 0x72, 0x61, 0x70, 0x12, 0x12, 0x2e, 0x6b, 0x61, 0x73, 0x2e, 0x52, 0x65, - 0x77, 0x72, 0x61, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6b, 0x61, - 0x73, 0x2e, 0x52, 0x65, 0x77, 0x72, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x25, 0x92, 0x41, 0x09, 0x4a, 0x07, 0x0a, 0x03, 0x32, 0x30, 0x30, 0x12, 0x00, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x13, 0x3a, 0x01, 0x2a, 0x22, 0x0e, 0x2f, 0x6b, 0x61, 0x73, 0x2f, 0x76, 0x32, - 0x2f, 0x72, 0x65, 0x77, 0x72, 0x61, 0x70, 0x42, 0xe2, 0x01, 0x92, 0x41, 0x73, 0x12, 0x71, 0x0a, - 0x1a, 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x44, 0x46, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x41, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2a, 0x4c, 0x0a, 0x12, 0x42, - 0x53, 0x44, 0x20, 0x33, 0x2d, 0x43, 0x6c, 0x61, 0x75, 0x73, 0x65, 0x20, 0x43, 0x6c, 0x65, 0x61, - 0x72, 0x12, 0x36, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x64, 0x66, 0x2f, 0x62, 0x61, - 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x62, 0x6c, 0x6f, 0x62, 0x2f, 0x6d, 0x61, 0x73, 0x74, 0x65, - 0x72, 0x2f, 0x4c, 0x49, 0x43, 0x45, 0x4e, 0x53, 0x45, 0x32, 0x05, 0x31, 0x2e, 0x35, 0x2e, 0x30, - 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x2e, 0x6b, 0x61, 0x73, 0x42, 0x08, 0x4b, 0x61, 0x73, 0x50, 0x72, - 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2b, 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, 0x6b, - 0x61, 0x73, 0xa2, 0x02, 0x03, 0x4b, 0x58, 0x58, 0xaa, 0x02, 0x03, 0x4b, 0x61, 0x73, 0xca, 0x02, - 0x03, 0x4b, 0x61, 0x73, 0xe2, 0x02, 0x0f, 0x4b, 0x61, 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x03, 0x4b, 0x61, 0x73, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x02, 0x18, 0x01, 0x52, 0x10, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x57, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x4b, 0x65, 0x79, + 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x29, + 0x0a, 0x0e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0d, 0x73, 0x63, 0x68, 0x65, + 0x6d, 0x61, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x35, 0x0a, 0x09, 0x72, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6b, + 0x61, 0x73, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x77, 0x72, 0x61, 0x70, 0x52, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, + 0x1a, 0x53, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x32, 0xce, 0x02, 0x0a, 0x0d, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x69, 0x0a, 0x09, 0x50, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x4b, 0x65, 0x79, 0x12, 0x15, 0x2e, 0x6b, 0x61, 0x73, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x6b, 0x61, + 0x73, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x2d, 0x92, 0x41, 0x09, 0x4a, 0x07, 0x0a, 0x03, 0x32, 0x30, 0x30, 0x12, + 0x00, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x12, 0x16, 0x2f, 0x6b, 0x61, 0x73, 0x2f, 0x76, 0x32, + 0x2f, 0x6b, 0x61, 0x73, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x90, + 0x02, 0x01, 0x12, 0x78, 0x0a, 0x0f, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x50, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x1b, 0x2e, 0x6b, 0x61, 0x73, 0x2e, 0x4c, 0x65, 0x67, 0x61, + 0x63, 0x79, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x22, 0x2a, 0x92, 0x41, 0x09, 0x4a, 0x07, 0x0a, 0x03, 0x32, 0x30, 0x30, 0x12, 0x00, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x15, 0x12, 0x13, 0x2f, 0x6b, 0x61, 0x73, 0x2f, 0x6b, 0x61, 0x73, 0x5f, 0x70, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x90, 0x02, 0x01, 0x12, 0x58, 0x0a, 0x06, + 0x52, 0x65, 0x77, 0x72, 0x61, 0x70, 0x12, 0x12, 0x2e, 0x6b, 0x61, 0x73, 0x2e, 0x52, 0x65, 0x77, + 0x72, 0x61, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6b, 0x61, 0x73, + 0x2e, 0x52, 0x65, 0x77, 0x72, 0x61, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x25, 0x92, 0x41, 0x09, 0x4a, 0x07, 0x0a, 0x03, 0x32, 0x30, 0x30, 0x12, 0x00, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x13, 0x3a, 0x01, 0x2a, 0x22, 0x0e, 0x2f, 0x6b, 0x61, 0x73, 0x2f, 0x76, 0x32, 0x2f, + 0x72, 0x65, 0x77, 0x72, 0x61, 0x70, 0x42, 0xe2, 0x01, 0x92, 0x41, 0x73, 0x12, 0x71, 0x0a, 0x1a, + 0x4f, 0x70, 0x65, 0x6e, 0x54, 0x44, 0x46, 0x20, 0x4b, 0x65, 0x79, 0x20, 0x41, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x20, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2a, 0x4c, 0x0a, 0x12, 0x42, 0x53, + 0x44, 0x20, 0x33, 0x2d, 0x43, 0x6c, 0x61, 0x75, 0x73, 0x65, 0x20, 0x43, 0x6c, 0x65, 0x61, 0x72, + 0x12, 0x36, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x64, 0x66, 0x2f, 0x62, 0x61, 0x63, + 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x62, 0x6c, 0x6f, 0x62, 0x2f, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, + 0x2f, 0x4c, 0x49, 0x43, 0x45, 0x4e, 0x53, 0x45, 0x32, 0x05, 0x31, 0x2e, 0x35, 0x2e, 0x30, 0x0a, + 0x07, 0x63, 0x6f, 0x6d, 0x2e, 0x6b, 0x61, 0x73, 0x42, 0x08, 0x4b, 0x61, 0x73, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2b, 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, 0x6b, 0x61, + 0x73, 0xa2, 0x02, 0x03, 0x4b, 0x58, 0x58, 0xaa, 0x02, 0x03, 0x4b, 0x61, 0x73, 0xca, 0x02, 0x03, + 0x4b, 0x61, 0x73, 0xe2, 0x02, 0x0f, 0x4b, 0x61, 0x73, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x03, 0x4b, 0x61, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( @@ -503,33 +1160,51 @@ func file_kas_kas_proto_rawDescGZIP() []byte { return file_kas_kas_proto_rawDescData } -var file_kas_kas_proto_msgTypes = make([]protoimpl.MessageInfo, 8) +var file_kas_kas_proto_msgTypes = make([]protoimpl.MessageInfo, 17) var file_kas_kas_proto_goTypes = []interface{}{ - (*InfoRequest)(nil), // 0: kas.InfoRequest - (*InfoResponse)(nil), // 1: kas.InfoResponse - (*LegacyPublicKeyRequest)(nil), // 2: kas.LegacyPublicKeyRequest - (*PublicKeyRequest)(nil), // 3: kas.PublicKeyRequest - (*PublicKeyResponse)(nil), // 4: kas.PublicKeyResponse - (*RewrapRequest)(nil), // 5: kas.RewrapRequest - (*RewrapResponse)(nil), // 6: kas.RewrapResponse - nil, // 7: kas.RewrapResponse.MetadataEntry - (*structpb.Value)(nil), // 8: google.protobuf.Value - (*wrapperspb.StringValue)(nil), // 9: google.protobuf.StringValue + (*InfoRequest)(nil), // 0: kas.InfoRequest + (*InfoResponse)(nil), // 1: kas.InfoResponse + (*LegacyPublicKeyRequest)(nil), // 2: kas.LegacyPublicKeyRequest + (*PolicyBinding)(nil), // 3: kas.PolicyBinding + (*KeyAccess)(nil), // 4: kas.KeyAccess + (*UnsignedRewrapRequest)(nil), // 5: kas.UnsignedRewrapRequest + (*PublicKeyRequest)(nil), // 6: kas.PublicKeyRequest + (*PublicKeyResponse)(nil), // 7: kas.PublicKeyResponse + (*RewrapRequest)(nil), // 8: kas.RewrapRequest + (*KeyAccessRewrapResult)(nil), // 9: kas.KeyAccessRewrapResult + (*PolicyRewrapResult)(nil), // 10: kas.PolicyRewrapResult + (*RewrapResponse)(nil), // 11: kas.RewrapResponse + (*UnsignedRewrapRequest_WithPolicy)(nil), // 12: kas.UnsignedRewrapRequest.WithPolicy + (*UnsignedRewrapRequest_WithKeyAccessObject)(nil), // 13: kas.UnsignedRewrapRequest.WithKeyAccessObject + (*UnsignedRewrapRequest_WithPolicyRequest)(nil), // 14: kas.UnsignedRewrapRequest.WithPolicyRequest + nil, // 15: kas.KeyAccessRewrapResult.MetadataEntry + nil, // 16: kas.RewrapResponse.MetadataEntry + (*structpb.Value)(nil), // 17: google.protobuf.Value + (*wrapperspb.StringValue)(nil), // 18: google.protobuf.StringValue } var file_kas_kas_proto_depIdxs = []int32{ - 7, // 0: kas.RewrapResponse.metadata:type_name -> kas.RewrapResponse.MetadataEntry - 8, // 1: kas.RewrapResponse.MetadataEntry.value:type_name -> google.protobuf.Value - 3, // 2: kas.AccessService.PublicKey:input_type -> kas.PublicKeyRequest - 2, // 3: kas.AccessService.LegacyPublicKey:input_type -> kas.LegacyPublicKeyRequest - 5, // 4: kas.AccessService.Rewrap:input_type -> kas.RewrapRequest - 4, // 5: kas.AccessService.PublicKey:output_type -> kas.PublicKeyResponse - 9, // 6: kas.AccessService.LegacyPublicKey:output_type -> google.protobuf.StringValue - 6, // 7: kas.AccessService.Rewrap:output_type -> kas.RewrapResponse - 5, // [5:8] is the sub-list for method output_type - 2, // [2:5] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name + 3, // 0: kas.KeyAccess.policy_binding:type_name -> kas.PolicyBinding + 14, // 1: kas.UnsignedRewrapRequest.requests:type_name -> kas.UnsignedRewrapRequest.WithPolicyRequest + 15, // 2: kas.KeyAccessRewrapResult.metadata:type_name -> kas.KeyAccessRewrapResult.MetadataEntry + 9, // 3: kas.PolicyRewrapResult.results:type_name -> kas.KeyAccessRewrapResult + 16, // 4: kas.RewrapResponse.metadata:type_name -> kas.RewrapResponse.MetadataEntry + 10, // 5: kas.RewrapResponse.responses:type_name -> kas.PolicyRewrapResult + 4, // 6: kas.UnsignedRewrapRequest.WithKeyAccessObject.key_access_object:type_name -> kas.KeyAccess + 13, // 7: kas.UnsignedRewrapRequest.WithPolicyRequest.key_access_objects:type_name -> kas.UnsignedRewrapRequest.WithKeyAccessObject + 12, // 8: kas.UnsignedRewrapRequest.WithPolicyRequest.policy:type_name -> kas.UnsignedRewrapRequest.WithPolicy + 17, // 9: kas.KeyAccessRewrapResult.MetadataEntry.value:type_name -> google.protobuf.Value + 17, // 10: kas.RewrapResponse.MetadataEntry.value:type_name -> google.protobuf.Value + 6, // 11: kas.AccessService.PublicKey:input_type -> kas.PublicKeyRequest + 2, // 12: kas.AccessService.LegacyPublicKey:input_type -> kas.LegacyPublicKeyRequest + 8, // 13: kas.AccessService.Rewrap:input_type -> kas.RewrapRequest + 7, // 14: kas.AccessService.PublicKey:output_type -> kas.PublicKeyResponse + 18, // 15: kas.AccessService.LegacyPublicKey:output_type -> google.protobuf.StringValue + 11, // 16: kas.AccessService.Rewrap:output_type -> kas.RewrapResponse + 14, // [14:17] is the sub-list for method output_type + 11, // [11:14] is the sub-list for method input_type + 11, // [11:11] is the sub-list for extension type_name + 11, // [11:11] is the sub-list for extension extendee + 0, // [0:11] is the sub-list for field type_name } func init() { file_kas_kas_proto_init() } @@ -575,7 +1250,7 @@ func file_kas_kas_proto_init() { } } file_kas_kas_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PublicKeyRequest); i { + switch v := v.(*PolicyBinding); i { case 0: return &v.state case 1: @@ -587,7 +1262,7 @@ func file_kas_kas_proto_init() { } } file_kas_kas_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PublicKeyResponse); i { + switch v := v.(*KeyAccess); i { case 0: return &v.state case 1: @@ -599,7 +1274,7 @@ func file_kas_kas_proto_init() { } } file_kas_kas_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RewrapRequest); i { + switch v := v.(*UnsignedRewrapRequest); i { case 0: return &v.state case 1: @@ -611,6 +1286,66 @@ func file_kas_kas_proto_init() { } } file_kas_kas_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PublicKeyRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_kas_kas_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PublicKeyResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_kas_kas_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RewrapRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_kas_kas_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*KeyAccessRewrapResult); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_kas_kas_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PolicyRewrapResult); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_kas_kas_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RewrapResponse); i { case 0: return &v.state @@ -622,6 +1357,46 @@ func file_kas_kas_proto_init() { return nil } } + file_kas_kas_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UnsignedRewrapRequest_WithPolicy); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_kas_kas_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UnsignedRewrapRequest_WithKeyAccessObject); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_kas_kas_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UnsignedRewrapRequest_WithPolicyRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_kas_kas_proto_msgTypes[9].OneofWrappers = []interface{}{ + (*KeyAccessRewrapResult_KasWrappedKey)(nil), + (*KeyAccessRewrapResult_Error)(nil), } type x struct{} out := protoimpl.TypeBuilder{ @@ -629,7 +1404,7 @@ func file_kas_kas_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_kas_kas_proto_rawDesc, NumEnums: 0, - NumMessages: 8, + NumMessages: 17, NumExtensions: 0, NumServices: 1, }, diff --git a/sdk/bulk.go b/sdk/bulk.go new file mode 100644 index 000000000..f878ec27f --- /dev/null +++ b/sdk/bulk.go @@ -0,0 +1,154 @@ +package sdk + +import ( + "context" + "errors" + "fmt" + "io" + + "github.com/opentdf/platform/protocol/go/kas" +) + +// BulkTDF: Reader is TDF Content. Writer writes encrypted data. Error is the error that occurs if decrypting fails. +type BulkTDF struct { + Reader io.ReadSeeker + Writer io.Writer + Error error +} + +type BulkDecryptRequest struct { + TDFs []*BulkTDF + TDFType TdfType +} + +// BulkErrors List of Errors that Failed during Bulk Decryption +type BulkErrors []error + +func (b BulkErrors) Unwrap() []error { + return b +} + +func (b BulkErrors) Error() string { + return fmt.Sprintf("Some TDFs could not be Decrypted: %s", errors.Join(b...).Error()) +} + +// FromBulkErrors Returns List of Decrypt Failures and true if is decryption failures +func FromBulkErrors(err error) ([]error, bool) { + var list BulkErrors + ok := errors.As(err, &list) + return list, ok +} + +type BulkDecryptOption func(request *BulkDecryptRequest) + +// WithTDFs Adds Lists of TDFs to be decrypted +func WithTDFs(tdfs ...*BulkTDF) BulkDecryptOption { + return func(request *BulkDecryptRequest) { + request.appendTDFs(tdfs...) + } +} + +// WithTDFType Type of TDFs to be decrypted +func WithTDFType(tdfType TdfType) BulkDecryptOption { + return func(request *BulkDecryptRequest) { + request.TDFType = tdfType + } +} + +func createBulkRewrapRequest(options ...BulkDecryptOption) *BulkDecryptRequest { + req := &BulkDecryptRequest{} + for _, opt := range options { + opt(req) + } + return req +} + +func (s SDK) createDecryptor(tdf *BulkTDF, tdfType TdfType) (decryptor, error) { + switch tdfType { + case Nano: + decryptor := createNanoTDFDecryptHandler(tdf.Reader, tdf.Writer) + return decryptor, nil + case Standard: + return s.createTDF3DecryptHandler(tdf.Writer, tdf.Reader) + case Invalid: + } + return nil, fmt.Errorf("unknown tdf type: %s", tdfType) +} + +// BulkDecrypt Decrypts a list of BulkTDF and if a partial failure of TDFs unable to be decrypted, BulkErrors would be returned. +func (s SDK) BulkDecrypt(ctx context.Context, opts ...BulkDecryptOption) error { + bulkReq := createBulkRewrapRequest(opts...) + kasRewrapRequests := make(map[string][]*kas.UnsignedRewrapRequest_WithPolicyRequest) + tdfDecryptors := make(map[string]decryptor) + policyTDF := make(map[string]*BulkTDF) + + for i, tdf := range bulkReq.TDFs { + policyID := fmt.Sprintf("policy-%d", i) + decryptor, err := s.createDecryptor(tdf, bulkReq.TDFType) + if err != nil { + tdf.Error = err + continue + } + + req, err := decryptor.CreateRewrapRequest(ctx) + if err != nil { + tdf.Error = err + continue + } + tdfDecryptors[policyID] = decryptor + policyTDF[policyID] = tdf + for kasURL, r := range req { + r.Policy.Id = policyID + kasRewrapRequests[kasURL] = append(kasRewrapRequests[kasURL], r) + } + } + + kasClient := newKASClient(s.dialOptions, s.tokenSource, s.kasSessionKey) + allRewrapResp := make(map[string][]kaoResult) + var err error + for _, rewrapRequests := range kasRewrapRequests { + var rewrapResp map[string][]kaoResult + switch bulkReq.TDFType { + case Nano: + rewrapResp, err = kasClient.nanoUnwrap(ctx, rewrapRequests...) + case Standard, Invalid: + rewrapResp, err = kasClient.unwrap(ctx, rewrapRequests...) + } + + for id, res := range rewrapResp { + allRewrapResp[id] = append(allRewrapResp[id], res...) + } + } + if err != nil { + return fmt.Errorf("bulk rewrap failed: %w", err) + } + + var errList []error + for id, tdf := range policyTDF { + kaoRes, ok := allRewrapResp[id] + if !ok { + tdf.Error = fmt.Errorf("rewrap did not create a response for this TDF") + errList = append(errList, tdf.Error) + continue + } + decryptor := tdfDecryptors[id] + if _, err = decryptor.Decrypt(ctx, kaoRes); err != nil { + tdf.Error = err + errList = append(errList, tdf.Error) + continue + } + } + + if len(errList) != 0 { + return BulkErrors(errList) + } + + return nil +} + +func (b *BulkDecryptRequest) appendTDFs(tdfs ...*BulkTDF) { + b.TDFs = append( + b.TDFs, + tdfs..., + ) +} diff --git a/sdk/kas_client.go b/sdk/kas_client.go index 5d1fe065f..e74e125d0 100644 --- a/sdk/kas_client.go +++ b/sdk/kas_client.go @@ -2,12 +2,14 @@ package sdk import ( "context" - "encoding/json" + "errors" "fmt" "net" "net/url" "time" + "google.golang.org/protobuf/encoding/protojson" + "github.com/lestrrat-go/jwx/v2/jwk" "github.com/lestrrat-go/jwx/v2/jwt" "github.com/opentdf/platform/lib/ocrypto" @@ -20,25 +22,21 @@ const ( secondsPerMinute = 60 ) -type RequestBody struct { - KeyAccess `json:"keyAccess"` - ClientPublicKey string `json:"clientPublicKey"` - Policy string `json:"policy"` -} - type KASClient struct { accessTokenSource auth.AccessTokenSource dialOptions []grpc.DialOption sessionKey *ocrypto.RsaKeyPair } -// once the backend moves over we should use the same type that the golang backend uses here -type rewrapRequestBody struct { - KeyAccess KeyAccess `json:"keyAccess"` - Policy string `json:"policy,omitempty"` - Algorithm string `json:"algorithm,omitempty"` - ClientPublicKey string `json:"clientPublicKey"` - SchemaVersion string `json:"schemaVersion,omitempty"` +type kaoResult struct { + SymmetricKey []byte + Error error + KeyAccessObjectID string +} + +type decryptor interface { + CreateRewrapRequest(ctx context.Context) (map[string]*kas.UnsignedRewrapRequest_WithPolicyRequest, error) + Decrypt(ctx context.Context, results []kaoResult) (uint32, error) } func newKASClient(dialOptions []grpc.DialOption, accessTokenSource auth.AccessTokenSource, sessionKey *ocrypto.RsaKeyPair) *KASClient { @@ -50,12 +48,12 @@ func newKASClient(dialOptions []grpc.DialOption, accessTokenSource auth.AccessTo } // there is no connection caching as of now -func (k *KASClient) makeRewrapRequest(ctx context.Context, keyAccess KeyAccess, policy string) (*kas.RewrapResponse, error) { - rewrapRequest, err := k.getRewrapRequest(keyAccess, policy) +func (k *KASClient) makeRewrapRequest(ctx context.Context, requests []*kas.UnsignedRewrapRequest_WithPolicyRequest, pubKey string) (*kas.RewrapResponse, error) { + rewrapRequest, err := k.getRewrapRequest(requests, pubKey) if err != nil { return nil, err } - grpcAddress, err := getGRPCAddress(keyAccess.KasURL) + grpcAddress, err := getGRPCAddress(requests[0].GetKeyAccessObjects()[0].GetKeyAccessObject().GetKasUrl()) if err != nil { return nil, err } @@ -76,148 +74,106 @@ func (k *KASClient) makeRewrapRequest(ctx context.Context, keyAccess KeyAccess, return response, nil } -func (k *KASClient) unwrap(ctx context.Context, keyAccess KeyAccess, policy string) ([]byte, error) { - response, err := k.makeRewrapRequest(ctx, keyAccess, policy) - if err != nil { - return nil, fmt.Errorf("error making rewrap request to kas: %w", err) - } - - if k.sessionKey == nil { - return nil, fmt.Errorf("session key is nil") - } - clientPrivateKey, err := k.sessionKey.PrivateKeyInPemFormat() - if err != nil { - return nil, fmt.Errorf("ocrypto.PrivateKeyInPemFormat failed: %w", err) - } - - asymDecryption, err := ocrypto.NewAsymDecryption(clientPrivateKey) +func (k *KASClient) nanoUnwrap(ctx context.Context, requests ...*kas.UnsignedRewrapRequest_WithPolicyRequest) (map[string][]kaoResult, error) { + keypair, err := ocrypto.NewECKeyPair(ocrypto.ECCModeSecp256r1) if err != nil { - return nil, fmt.Errorf("ocrypto.NewAsymDecryption failed: %w", err) + return nil, fmt.Errorf("ocrypto.NewECKeyPair failed :%w", err) } - key, err := asymDecryption.Decrypt(response.GetEntityWrappedKey()) + publicKeyAsPem, err := keypair.PublicKeyInPemFormat() if err != nil { - return nil, fmt.Errorf("error decrypting payload from KAS: %w", err) - } - - return key, nil -} - -func (k *KASClient) getNanoTDFRewrapRequest(header string, kasURL string, pubKey string) (*kas.RewrapRequest, error) { - kAccess := keyAccess{ - Header: header, - KeyAccessType: "remote", - URL: kasURL, - Protocol: "kas", - } - - requestBody := requestBody{ - Algorithm: "ec:secp256r1", - KeyAccess: kAccess, - ClientPublicKey: pubKey, + return nil, fmt.Errorf("ocrypto.NewECKeyPair.PublicKeyInPemFormat failed :%w", err) } - requestBodyJSON, err := json.Marshal(requestBody) + privateKeyAsPem, err := keypair.PrivateKeyInPemFormat() if err != nil { - return nil, fmt.Errorf("Error marshaling request body: %w", err) + return nil, fmt.Errorf("ocrypto.NewECKeyPair.PrivateKeyInPemFormat failed :%w", err) } - - now := time.Now() - tok, err := jwt.NewBuilder(). - Claim("requestBody", string(requestBodyJSON)). - IssuedAt(now). - Expiration(now.Add(secondsPerMinute * time.Second)). - Build() + response, err := k.makeRewrapRequest(ctx, requests, publicKeyAsPem) if err != nil { - return nil, fmt.Errorf("failed to create jwt: %w", err) + return nil, err } - signedToken, err := k.accessTokenSource.MakeToken(func(key jwk.Key) ([]byte, error) { - signed, err := jwt.Sign(tok, jwt.WithKey(key.Algorithm(), key)) - if err != nil { - return nil, fmt.Errorf("error signing DPoP token: %w", err) - } - - return signed, nil - }) + sessionKey, err := ocrypto.ComputeECDHKey([]byte(privateKeyAsPem), []byte(response.GetSessionPublicKey())) if err != nil { - return nil, fmt.Errorf("failed to sign the token: %w", err) + return nil, fmt.Errorf("ocrypto.ComputeECDHKey failed :%w", err) } - rewrapRequest := kas.RewrapRequest{ - SignedRequestToken: string(signedToken), - } - return &rewrapRequest, nil -} - -func (k *KASClient) makeNanoTDFRewrapRequest(ctx context.Context, header string, kasURL string, pubKey string) (*kas.RewrapResponse, error) { - rewrapRequest, err := k.getNanoTDFRewrapRequest(header, kasURL, pubKey) - if err != nil { - return nil, err - } - grpcAddress, err := getGRPCAddress(kasURL) + sessionKey, err = ocrypto.CalculateHKDF(versionSalt(), sessionKey) if err != nil { - return nil, err + return nil, fmt.Errorf("ocrypto.CalculateHKDF failed:%w", err) } - conn, err := grpc.NewClient(grpcAddress, k.dialOptions...) + aesGcm, err := ocrypto.NewAESGcm(sessionKey) if err != nil { - return nil, fmt.Errorf("error connecting to kas: %w", err) + return nil, fmt.Errorf("ocrypto.NewAESGcm failed:%w", err) } - defer conn.Close() - - serviceClient := kas.NewAccessServiceClient(conn) - response, err := serviceClient.Rewrap(ctx, rewrapRequest) - if err != nil { - return nil, fmt.Errorf("error making rewrap request: %w", err) + policyResults := make(map[string][]kaoResult) + for _, results := range response.GetResponses() { + var kaoKeys []kaoResult + for _, kao := range results.GetResults() { + if kao.GetStatus() == "permit" { + wrappedKey := kao.GetKasWrappedKey() + key, err := aesGcm.Decrypt(wrappedKey) + if err != nil { + kaoKeys = append(kaoKeys, kaoResult{KeyAccessObjectID: kao.GetKeyAccessObjectId(), Error: err}) + } else { + kaoKeys = append(kaoKeys, kaoResult{KeyAccessObjectID: kao.GetKeyAccessObjectId(), SymmetricKey: key}) + } + } else { + kaoKeys = append(kaoKeys, kaoResult{KeyAccessObjectID: kao.GetKeyAccessObjectId(), Error: errors.New(kao.GetError())}) + } + } + policyResults[results.GetPolicyId()] = kaoKeys } - return response, nil + return policyResults, nil } -func (k *KASClient) unwrapNanoTDF(ctx context.Context, header string, kasURL string) ([]byte, error) { - keypair, err := ocrypto.NewECKeyPair(ocrypto.ECCModeSecp256r1) - if err != nil { - return nil, fmt.Errorf("ocrypto.NewECKeyPair failed :%w", err) - } - - publicKeyAsPem, err := keypair.PublicKeyInPemFormat() - if err != nil { - return nil, fmt.Errorf("ocrypto.NewECKeyPair.PublicKeyInPemFormat failed :%w", err) - } - - privateKeyAsPem, err := keypair.PrivateKeyInPemFormat() - if err != nil { - return nil, fmt.Errorf("ocrypto.NewECKeyPair.PrivateKeyInPemFormat failed :%w", err) +func (k *KASClient) unwrap(ctx context.Context, requests ...*kas.UnsignedRewrapRequest_WithPolicyRequest) (map[string][]kaoResult, error) { + if k.sessionKey == nil { + return nil, fmt.Errorf("session key is nil") } - - response, err := k.makeNanoTDFRewrapRequest(ctx, header, kasURL, publicKeyAsPem) + pubKey, err := k.sessionKey.PublicKeyInPemFormat() if err != nil { - return nil, fmt.Errorf("error making nano rewrap request to kas: %w", err) + return nil, fmt.Errorf("ocrypto.PublicKeyInPermFormat failed: %w", err) } - - sessionKey, err := ocrypto.ComputeECDHKey([]byte(privateKeyAsPem), []byte(response.GetSessionPublicKey())) + response, err := k.makeRewrapRequest(ctx, requests, pubKey) if err != nil { - return nil, fmt.Errorf("ocrypto.ComputeECDHKey failed :%w", err) + return nil, fmt.Errorf("error making rewrap request to kas: %w", err) } - sessionKey, err = ocrypto.CalculateHKDF(versionSalt(), sessionKey) + clientPrivateKey, err := k.sessionKey.PrivateKeyInPemFormat() if err != nil { - return nil, fmt.Errorf("ocrypto.CalculateHKDF failed:%w", err) + return nil, fmt.Errorf("ocrypto.PrivateKeyInPemFormat failed: %w", err) } - aesGcm, err := ocrypto.NewAESGcm(sessionKey) + asymDecryption, err := ocrypto.NewAsymDecryption(clientPrivateKey) if err != nil { - return nil, fmt.Errorf("ocrypto.NewAESGcm failed:%w", err) + return nil, fmt.Errorf("ocrypto.NewAsymDecryption failed: %w", err) } - symmetricKey, err := aesGcm.Decrypt(response.GetEntityWrappedKey()) - if err != nil { - return nil, fmt.Errorf("AesGcm.Decrypt failed:%w", err) + policyResults := make(map[string][]kaoResult) + for _, results := range response.GetResponses() { + var kaoKeys []kaoResult + for _, kao := range results.GetResults() { + if kao.GetStatus() == "permit" { + wrappedKey := kao.GetKasWrappedKey() + key, err := asymDecryption.Decrypt(wrappedKey) + if err != nil { + kaoKeys = append(kaoKeys, kaoResult{KeyAccessObjectID: kao.GetKeyAccessObjectId(), Error: err}) + } else { + kaoKeys = append(kaoKeys, kaoResult{KeyAccessObjectID: kao.GetKeyAccessObjectId(), SymmetricKey: key}) + } + } else { + kaoKeys = append(kaoKeys, kaoResult{KeyAccessObjectID: kao.GetKeyAccessObjectId(), Error: errors.New(kao.GetError())}) + } + } + policyResults[results.GetPolicyId()] = kaoKeys } - return symmetricKey, nil + return policyResults, nil } func getGRPCAddress(kasURL string) (string, error) { @@ -240,23 +196,13 @@ func getGRPCAddress(kasURL string) (string, error) { return net.JoinHostPort(parsedURL.Hostname(), port), nil } -func (k *KASClient) getRewrapRequest(keyAccess KeyAccess, policy string) (*kas.RewrapRequest, error) { - // check if the session key is nil if not return an error - if k.sessionKey == nil { - return nil, fmt.Errorf("session key is nil") - } - - clientPublicKey, err := k.sessionKey.PublicKeyInPemFormat() - if err != nil { - return nil, fmt.Errorf("ocrypto.PublicKeyInPemFormat failed: %w", err) +func (k *KASClient) getRewrapRequest(reqs []*kas.UnsignedRewrapRequest_WithPolicyRequest, pubKey string) (*kas.RewrapRequest, error) { + requestBody := &kas.UnsignedRewrapRequest{ + ClientPublicKey: pubKey, + Requests: reqs, } - requestBody := rewrapRequestBody{ - Policy: policy, - KeyAccess: keyAccess, - ClientPublicKey: clientPublicKey, - } - requestBodyJSON, err := json.Marshal(requestBody) + requestBodyJSON, err := protojson.Marshal(requestBody) if err != nil { return nil, fmt.Errorf("Error marshaling request body: %w", err) } diff --git a/sdk/kas_client_test.go b/sdk/kas_client_test.go index dfdb1f5a7..a45f32119 100644 --- a/sdk/kas_client_test.go +++ b/sdk/kas_client_test.go @@ -2,7 +2,6 @@ package sdk import ( "context" - "encoding/json" "net/http" "testing" @@ -10,11 +9,13 @@ import ( "github.com/lestrrat-go/jwx/v2/jwk" "github.com/lestrrat-go/jwx/v2/jwt" "github.com/opentdf/platform/lib/ocrypto" + kaspb "github.com/opentdf/platform/protocol/go/kas" "github.com/opentdf/platform/protocol/go/policy" "github.com/opentdf/platform/sdk/auth" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc" + "google.golang.org/protobuf/encoding/protojson" ) type FakeAccessTokenSource struct { @@ -26,6 +27,7 @@ type FakeAccessTokenSource struct { func (fake FakeAccessTokenSource) AccessToken(context.Context, *http.Client) (auth.AccessToken, error) { return auth.AccessToken(fake.accessToken), nil } + func (fake FakeAccessTokenSource) MakeToken(tokenMaker func(jwk.Key) ([]byte, error)) ([]byte, error) { return tokenMaker(fake.dpopKey) } @@ -57,22 +59,34 @@ func TestCreatingRequest(t *testing.T) { require.NoError(t, err, "error creating RSA Key") client := newKASClient(dialOption, tokenSource, &kasKey) + require.NoError(t, err) - keyAccess := KeyAccess{ - KeyType: "type1", - KasURL: "https://kas.example.org", - Protocol: "protocol one", - WrappedKey: "wrapped", - PolicyBinding: PolicyBinding{ - Alg: "HS256", - Hash: "somehash", + keyAccess := []*kaspb.UnsignedRewrapRequest_WithPolicyRequest{ + { + KeyAccessObjects: []*kaspb.UnsignedRewrapRequest_WithKeyAccessObject{ + { + KeyAccessObject: &kaspb.KeyAccess{ + KeyType: "type1", + KasUrl: "https://kas.example.org", + Protocol: "protocol one", + WrappedKey: []byte("wrapped"), + PolicyBinding: &kaspb.PolicyBinding{ + Hash: "somehash", + Algorithm: "HS256", + }, + EncryptedMetadata: "encrypted", + }, + }, + }, }, - EncryptedMetadata: "encrypted", } + kp, err := ocrypto.NewRSAKeyPair(1024) + require.NoError(t, err, "failed to make pub key") + pubkey, err := kp.PublicKeyInPemFormat() + require.NoError(t, err, "failed to make pub key") - req, err := client.getRewrapRequest(keyAccess, "a policy") + req, err := client.getRewrapRequest(keyAccess, pubkey) require.NoError(t, err, "failed to create a rewrap request") - if req.GetSignedRequestToken() == "" { t.Fatalf("didn't produce a signed request token") } @@ -85,29 +99,24 @@ func TestCreatingRequest(t *testing.T) { rb, ok := tok.Get("requestBody") require.True(t, ok, "didn't contain a request body") requestBodyJSON, _ := rb.(string) - var requestBody map[string]interface{} + var requestBody kaspb.UnsignedRewrapRequest - require.NoError(t, json.Unmarshal([]byte(requestBodyJSON), &requestBody), "error unmarshaling request body") + require.NoError(t, protojson.Unmarshal([]byte(requestBodyJSON), &requestBody), "error unmarshaling request body") - cpk, ok := requestBody["clientPublicKey"].(string) - require.True(t, ok) - - _, err = ocrypto.NewAsymEncryption(cpk) + _, err = ocrypto.NewAsymEncryption(requestBody.GetClientPublicKey()) require.NoError(t, err, "NewAsymEncryption failed, incorrect public key include") - assert.Equal(t, "a policy", requestBody["policy"]) - - requestKeyAccess, ok := requestBody["keyAccess"].(map[string]interface{}) - require.True(t, ok) - policyBinding, ok := requestKeyAccess["policyBinding"].(map[string]interface{}) - require.True(t, ok) - - assert.Equal(t, "https://kas.example.org", requestKeyAccess["url"], "incorrect kasURL") - assert.Equal(t, "protocol one", requestKeyAccess["protocol"], "incorrect protocol") - assert.Equal(t, "wrapped", requestKeyAccess["wrappedKey"], "incorrect wrapped key") - assert.Equal(t, "HS256", policyBinding["alg"], "incorrect policy binding") - assert.Equal(t, "somehash", policyBinding["hash"], "incorrect policy binding") - assert.Equal(t, "encrypted", requestKeyAccess["encryptedMetadata"], "incorrect encrypted metadata") + require.Len(t, requestBody.GetRequests(), 1) + require.Len(t, requestBody.GetRequests()[0].GetKeyAccessObjects(), 1) + kao := requestBody.GetRequests()[0].GetKeyAccessObjects()[0] + policyBinding := kao.GetKeyAccessObject().GetPolicyBinding() + + assert.Equal(t, "https://kas.example.org", kao.GetKeyAccessObject().GetKasUrl(), "incorrect kasURL") + assert.Equal(t, "protocol one", kao.GetKeyAccessObject().GetProtocol(), "incorrect protocol") + assert.Equal(t, []byte("wrapped"), kao.GetKeyAccessObject().GetWrappedKey(), "incorrect wrapped key") + assert.Equal(t, "HS256", policyBinding.GetAlgorithm(), "incorrect policy binding") + assert.Equal(t, "somehash", policyBinding.GetHash(), "incorrect policy binding") + assert.Equal(t, "encrypted", kao.GetKeyAccessObject().GetEncryptedMetadata(), "incorrect encrypted metadata") } func Test_StoreKASKeys(t *testing.T) { diff --git a/sdk/nanotdf.go b/sdk/nanotdf.go index cf735f9bd..601068af2 100644 --- a/sdk/nanotdf.go +++ b/sdk/nanotdf.go @@ -14,6 +14,8 @@ import ( "sync" "time" + "github.com/opentdf/platform/protocol/go/kas" + "github.com/opentdf/platform/lib/ocrypto" ) @@ -899,44 +901,78 @@ func (s SDK) CreateNanoTDF(writer io.Writer, reader io.Reader, config NanoTDFCon // NanoTDF Decrypt // ============================================================================================================ -// ReadNanoTDF - read the nano tdf and return the decrypted data from it -func (s SDK) ReadNanoTDF(writer io.Writer, reader io.ReadSeeker) (uint32, error) { - return s.ReadNanoTDFContext(context.Background(), writer, reader) +type NanoTDFDecryptHandler struct { + reader io.ReadSeeker + writer io.Writer + + header NanoTDFHeader + headerBuf []byte } -// ReadNanoTDFContext - allows cancelling the reader -func (s SDK) ReadNanoTDFContext(ctx context.Context, writer io.Writer, reader io.ReadSeeker) (uint32, error) { - header, headerSize, err := NewNanoTDFHeaderFromReader(reader) - if err != nil { - return 0, err +func createNanoTDFDecryptHandler(reader io.ReadSeeker, writer io.Writer) *NanoTDFDecryptHandler { + return &NanoTDFDecryptHandler{ + reader: reader, + writer: writer, } +} - _, err = reader.Seek(0, io.SeekStart) +func (n *NanoTDFDecryptHandler) getRawHeader() []byte { + return n.headerBuf +} + +func (n *NanoTDFDecryptHandler) CreateRewrapRequest(_ context.Context) (map[string]*kas.UnsignedRewrapRequest_WithPolicyRequest, error) { + var err error + var headerSize uint32 + n.header, headerSize, err = NewNanoTDFHeaderFromReader(n.reader) if err != nil { - return 0, fmt.Errorf("readSeeker.Seek failed: %w", err) + return nil, err + } + _, err = n.reader.Seek(0, io.SeekStart) + if err != nil { + return nil, fmt.Errorf("readSeeker.Seek failed: %w", err) } headerBuf := make([]byte, headerSize) - _, err = reader.Read(headerBuf) + _, err = n.reader.Read(headerBuf) if err != nil { - return 0, fmt.Errorf("readSeeker.Seek failed: %w", err) + return nil, fmt.Errorf("readSeeker.Seek failed: %w", err) } - - kasURL, err := header.kasURL.GetURL() + kasURL, err := n.header.kasURL.GetURL() if err != nil { - return 0, fmt.Errorf("readSeeker.Seek failed: %w", err) + return nil, err + } + + req := &kas.UnsignedRewrapRequest_WithPolicyRequest{ + KeyAccessObjects: []*kas.UnsignedRewrapRequest_WithKeyAccessObject{ + { + KeyAccessObjectId: "kao-0", + KeyAccessObject: &kas.KeyAccess{KasUrl: kasURL, Header: headerBuf}, + }, + }, + Policy: &kas.UnsignedRewrapRequest_WithPolicy{ + Id: "policy", + }, + Algorithm: "ec:secp256r1", + } + return map[string]*kas.UnsignedRewrapRequest_WithPolicyRequest{kasURL: req}, nil +} + +func (n *NanoTDFDecryptHandler) Decrypt(_ context.Context, result []kaoResult) (uint32, error) { + var err error + if len(result) != 1 { + return 0, fmt.Errorf("improper result from kas") } - symmetricKey, err := s.getNanoRewrapKey(ctx, headerBuf, kasURL) - if err != nil { - return 0, err + if result[0].Error != nil { + return 0, result[0].Error } + key := result[0].SymmetricKey const ( kPayloadLoadLengthBufLength = 4 ) payloadLengthBuf := make([]byte, kPayloadLoadLengthBufLength) - _, err = reader.Read(payloadLengthBuf[1:]) + _, err = n.reader.Read(payloadLengthBuf[1:]) if err != nil { return 0, fmt.Errorf(" io.Reader.Read failed :%w", err) @@ -946,12 +982,12 @@ func (s SDK) ReadNanoTDFContext(ctx context.Context, writer io.Writer, reader io slog.Debug("ReadNanoTDF", slog.Uint64("payloadLength", uint64(payloadLength))) cipherDate := make([]byte, payloadLength) - _, err = reader.Read(cipherDate) + _, err = n.reader.Read(cipherDate) if err != nil { return 0, fmt.Errorf("readSeeker.Seek failed: %w", err) } - aesGcm, err := ocrypto.NewAESGcm(symmetricKey) + aesGcm, err := ocrypto.NewAESGcm(key) if err != nil { return 0, fmt.Errorf("ocrypto.NewAESGcm failed:%w", err) } @@ -962,7 +998,7 @@ func (s SDK) ReadNanoTDFContext(ctx context.Context, writer io.Writer, reader io iv := cipherDate[:kNanoTDFIvSize] ivPadded = append(ivPadded, iv...) - tagSize, err := SizeOfAuthTagForCipher(header.sigCfg.cipher) + tagSize, err := SizeOfAuthTagForCipher(n.header.sigCfg.cipher) if err != nil { return 0, fmt.Errorf("SizeOfAuthTagForCipher failed:%w", err) } @@ -972,7 +1008,7 @@ func (s SDK) ReadNanoTDFContext(ctx context.Context, writer io.Writer, reader io return 0, err } - writeLen, err := writer.Write(decryptedData) + writeLen, err := n.writer.Write(decryptedData) if err != nil { return 0, err } @@ -980,37 +1016,56 @@ func (s SDK) ReadNanoTDFContext(ctx context.Context, writer io.Writer, reader io return uint32(writeLen), nil } -func (s SDK) getNanoRewrapKey(ctx context.Context, header []byte, kasURL string) ([]byte, error) { +// ReadNanoTDF - read the nano tdf and return the decrypted data from it +func (s SDK) ReadNanoTDF(writer io.Writer, reader io.ReadSeeker) (uint32, error) { + return s.ReadNanoTDFContext(context.Background(), writer, reader) +} + +// ReadNanoTDFContext - allows cancelling the reader +func (s SDK) ReadNanoTDFContext(ctx context.Context, writer io.Writer, reader io.ReadSeeker) (uint32, error) { + handler := createNanoTDFDecryptHandler(reader, writer) + + symmetricKey, err := s.getNanoRewrapKey(ctx, handler) + if err != nil { + return 0, err + } + return handler.Decrypt(ctx, []kaoResult{{SymmetricKey: symmetricKey}}) +} + +func (s SDK) getNanoRewrapKey(ctx context.Context, decryptor *NanoTDFDecryptHandler) ([]byte, error) { + req, err := decryptor.CreateRewrapRequest(ctx) + if err != nil { + return nil, err + } + if s.collectionStore != nil { - if key, found := s.collectionStore.get(header); found { + if key, found := s.collectionStore.get(decryptor.getRawHeader()); found { return key, nil } } - encodedHeader := ocrypto.Base64Encode(header) client := newKASClient(s.dialOptions, s.tokenSource, nil) + kasURL, err := decryptor.header.kasURL.GetURL() + if err != nil { + return nil, err + } - symmetricKey, err := client.unwrapNanoTDF(ctx, string(encodedHeader), kasURL) + policyResult, err := client.nanoUnwrap(ctx, req[kasURL]) if err != nil { - return nil, fmt.Errorf("readSeeker.Seek failed: %w", err) + return nil, fmt.Errorf("rewrap failed: %w", err) } - if s.collectionStore != nil { - s.collectionStore.store(header, symmetricKey) + result, ok := policyResult["policy"] + if !ok || len(result) != 1 { + return nil, fmt.Errorf("policy was not found in rewrap response") + } + if result[0].Error != nil { + return nil, result[0].Error } - return symmetricKey, nil -} - -type requestBody struct { - Algorithm string `json:"algorithm,omitempty"` - KeyAccess keyAccess `json:"keyAccess"` - ClientPublicKey string `json:"clientPublicKey"` -} -type keyAccess struct { - Header string `json:"header"` - KeyAccessType string `json:"type"` - URL string `json:"url"` - Protocol string `json:"protocol"` + if s.collectionStore != nil { + s.collectionStore.store(decryptor.getRawHeader(), result[0].SymmetricKey) + } + return result[0].SymmetricKey, nil } func versionSalt() []byte { diff --git a/sdk/tdf.go b/sdk/tdf.go index 50fada4b7..420d786f2 100644 --- a/sdk/tdf.go +++ b/sdk/tdf.go @@ -9,8 +9,11 @@ import ( "fmt" "io" "math" + "strconv" "strings" + "github.com/opentdf/platform/protocol/go/kas" + "github.com/google/uuid" "github.com/opentdf/platform/lib/ocrypto" "github.com/opentdf/platform/sdk/auth" @@ -72,11 +75,45 @@ type TDFObject struct { payloadKey [kKeySize]byte } +type tdf3DecryptHandler struct { + writer io.Writer + reader *Reader +} + +func (r *tdf3DecryptHandler) Decrypt(ctx context.Context, results []kaoResult) (uint32, error) { + err := r.reader.buildKey(ctx, results) + if err != nil { + return 0, err + } + data, err := io.ReadAll(r.reader) + if err != nil { + return 0, err + } + + n, err := r.writer.Write(data) + return uint32(n), err +} + +func (r *tdf3DecryptHandler) CreateRewrapRequest(ctx context.Context) (map[string]*kas.UnsignedRewrapRequest_WithPolicyRequest, error) { + return createRewrapRequest(ctx, r.reader) +} + +func (s SDK) createTDF3DecryptHandler(writer io.Writer, reader io.ReadSeeker) (*tdf3DecryptHandler, error) { + tdfReader, err := s.LoadTDF(reader) + if err != nil { + return nil, err + } + + return &tdf3DecryptHandler{ + reader: tdfReader, + writer: writer, + }, nil +} + func (t TDFObject) Size() int64 { return t.size } -// CreateTDF reads plain text from the given reader and saves it to the writer, subject to the given options func (s SDK) CreateTDF(writer io.Writer, reader io.ReadSeeker, opts ...TDFOption) (*TDFObject, error) { return s.CreateTDFContext(context.Background(), writer, reader, opts...) } @@ -209,8 +246,7 @@ func (s SDK) CreateTDFContext(ctx context.Context, writer io.Writer, reader io.R EncryptedSize: int64(len(cipherData)), } - tdfObject.manifest.EncryptionInformation.IntegrityInformation.Segments = - append(tdfObject.manifest.EncryptionInformation.IntegrityInformation.Segments, segmentInfo) + tdfObject.manifest.EncryptionInformation.IntegrityInformation.Segments = append(tdfObject.manifest.EncryptionInformation.IntegrityInformation.Segments, segmentInfo) totalSegments-- readPos += readSize @@ -274,7 +310,7 @@ func (s SDK) CreateTDFContext(ctx context.Context, writer io.Writer, reader io.R encoded := ocrypto.Base64Encode([]byte(completeHashBuilder.String())) - var assertionSigningKey = AssertionKey{} + assertionSigningKey := AssertionKey{} // Set default to HS256 and payload key assertionSigningKey.Alg = AssertionKeyAlgHS256 @@ -633,8 +669,8 @@ func (r *Reader) ReadAt(buf []byte, offset int64) (int, error) { //nolint:funlen } defaultSegmentSize := r.manifest.EncryptionInformation.IntegrityInformation.DefaultSegmentSize - var start = math.Floor(float64(offset) / float64(defaultSegmentSize)) - var end = math.Ceil(float64(offset+int64(len(buf))) / float64(defaultSegmentSize)) + start := math.Floor(float64(offset) / float64(defaultSegmentSize)) + end := math.Ceil(float64(offset+int64(len(buf))) / float64(defaultSegmentSize)) firstSegment := int64(start) lastSegment := int64(end) @@ -766,7 +802,7 @@ func (r *Reader) DataAttributes() ([]string, error) { /* *WARNING:* Using this function is unsafe since KAS will no longer be able to prevent access to the key. -Retrieve the payload key, either from performing an unwrap or from a previous unwrap, +Retrieve the payload key, either from performing an buildKey or from a previous buildKey, and write it to a user buffer. OUTPUTS: @@ -784,27 +820,92 @@ func (r *Reader) UnsafePayloadKeyRetrieval() ([]byte, error) { return r.payloadKey, nil } -// Unwraps the payload key, if possible, using the access service -func (r *Reader) doPayloadKeyUnwrap(ctx context.Context) error { //nolint:gocognit // Better readability keeping it as is +func createRewrapRequest(_ context.Context, r *Reader) (map[string]*kas.UnsignedRewrapRequest_WithPolicyRequest, error) { + kasReqs := make(map[string]*kas.UnsignedRewrapRequest_WithPolicyRequest) + for i, kao := range r.manifest.EncryptionInformation.KeyAccessObjs { + kaoID := fmt.Sprintf("kao-%d", i) + key, err := ocrypto.Base64Decode([]byte(kao.WrappedKey)) + if err != nil { + return nil, fmt.Errorf("could not decode wrapper key: %w", err) + } + var alg string + var hash string + invalidPolicy := false + switch policyBinding := kao.PolicyBinding.(type) { + case string: + hash = policyBinding + case map[string]interface{}: + var ok bool + hash, ok = policyBinding["hash"].(string) + invalidPolicy = !ok + alg, ok = policyBinding["alg"].(string) + invalidPolicy = invalidPolicy || !ok + case (PolicyBinding): + hash = policyBinding.Hash + alg = policyBinding.Alg + default: + invalidPolicy = true + } + if invalidPolicy { + return nil, fmt.Errorf("invalid policy object: %s", kao.PolicyBinding) + } + kaoReq := &kas.UnsignedRewrapRequest_WithKeyAccessObject{ + KeyAccessObjectId: kaoID, + KeyAccessObject: &kas.KeyAccess{ + KeyType: kao.KeyType, + KasUrl: kao.KasURL, + Kid: kao.KID, + Protocol: kao.Protocol, + PolicyBinding: &kas.PolicyBinding{ + Hash: hash, + Algorithm: alg, + }, + SplitId: kao.SplitID, + WrappedKey: key, + }, + } + if req, ok := kasReqs[kao.KasURL]; ok { + req.KeyAccessObjects = append(req.KeyAccessObjects, kaoReq) + } else { + rewrapReq := kas.UnsignedRewrapRequest_WithPolicyRequest{ + Policy: &kas.UnsignedRewrapRequest_WithPolicy{ + Body: r.manifest.EncryptionInformation.Policy, + Id: "policy", + }, + KeyAccessObjects: []*kas.UnsignedRewrapRequest_WithKeyAccessObject{kaoReq}, + } + kasReqs[kao.KasURL] = &rewrapReq + } + } + + return kasReqs, nil +} + +func getIdx(kaoID string) int { + idx, _ := strconv.Atoi(strings.Split(kaoID, "-")[1]) + return idx +} + +func (r *Reader) buildKey(_ context.Context, results []kaoResult) error { var unencryptedMetadata []byte var payloadKey [kKeySize]byte knownSplits := make(map[string]bool) foundSplits := make(map[string]bool) skippedSplits := make(map[keySplitStep]error) - for _, keyAccessObj := range r.manifest.EncryptionInformation.KeyAccessObjs { - client := newKASClient(r.dialOptions, r.tokenSource, &r.kasSessionKey) - + for _, kaoRes := range results { + idx := getIdx(kaoRes.KeyAccessObjectID) + keyAccessObj := r.manifest.KeyAccessObjs[idx] ss := keySplitStep{KAS: keyAccessObj.KasURL, SplitID: keyAccessObj.SplitID} - var err error - var wrappedKey []byte + wrappedKey := kaoRes.SymmetricKey + err := kaoRes.Error knownSplits[ss.SplitID] = true if foundSplits[ss.SplitID] { // already found continue } - wrappedKey, err = client.unwrap(ctx, keyAccessObj, r.manifest.EncryptionInformation.Policy) + if err != nil { errToReturn := fmt.Errorf("kao unwrap failed for split %v: %w", ss, err) if strings.Contains(err.Error(), codes.InvalidArgument.String()) { @@ -956,6 +1057,40 @@ func (r *Reader) doPayloadKeyUnwrap(ctx context.Context) error { //nolint:gocogn return nil } +// Unwraps the payload key, if possible, using the access service +func (r *Reader) doPayloadKeyUnwrap(ctx context.Context) error { //nolint:gocognit // Better readability keeping it as is + kasClient := newKASClient(r.dialOptions, r.tokenSource, &r.kasSessionKey) + + var kaoResults []kaoResult + reqFail := func(err error, req *kas.UnsignedRewrapRequest_WithPolicyRequest) { + for _, kao := range req.GetKeyAccessObjects() { + kaoResults = append(kaoResults, kaoResult{ + KeyAccessObjectID: kao.GetKeyAccessObjectId(), + Error: err, + }) + } + } + + reqs, err := createRewrapRequest(ctx, r) + if err != nil { + return err + } + for _, req := range reqs { + policyRes, err := kasClient.unwrap(ctx, req) + if err != nil { + reqFail(err, req) + } + result, ok := policyRes["policy"] + if !ok { + err = fmt.Errorf("could not find policy in rewrap response") + reqFail(err, req) + } + kaoResults = append(kaoResults, result...) + } + + return r.buildKey(ctx, kaoResults) +} + // calculateSignature calculate signature of data of the given algorithm. func calculateSignature(data []byte, secret []byte, alg IntegrityAlgorithm) (string, error) { if alg == HS256 { diff --git a/sdk/tdf_test.go b/sdk/tdf_test.go index dd2bce58d..2c55de0e1 100644 --- a/sdk/tdf_test.go +++ b/sdk/tdf_test.go @@ -6,7 +6,6 @@ import ( "crypto/rand" "crypto/rsa" "crypto/sha256" - "encoding/json" "fmt" "io" "log/slog" @@ -18,6 +17,8 @@ import ( "testing" "time" + "google.golang.org/protobuf/encoding/protojson" + "github.com/lestrrat-go/jwx/v2/jwt" "github.com/opentdf/platform/lib/ocrypto" kaspb "github.com/opentdf/platform/protocol/go/kas" @@ -1215,7 +1216,7 @@ func (s *TDFSuite) testDecryptWithReader(sdk *SDK, tdfFile, decryptedTdfFileName r, err := sdk.LoadTDF(readSeeker) s.Require().NoError(err) - ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(60*time.Millisecond)) + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(300*time.Minute)) defer cancel() err = r.Init(ctx) s.Require().NoError(err) @@ -1427,40 +1428,53 @@ func (f *FakeKas) Rewrap(_ context.Context, in *kaspb.RewrapRequest) (*kaspb.Rew if !ok { return nil, fmt.Errorf("requestBody not a string") } - entityWrappedKey := f.getRewrappedKey(requestBodyStr) + result := f.getRewrapResponse(requestBodyStr) - return &kaspb.RewrapResponse{EntityWrappedKey: entityWrappedKey}, nil + return result, nil } func (f *FakeKas) PublicKey(_ context.Context, _ *kaspb.PublicKeyRequest) (*kaspb.PublicKeyResponse, error) { return &kaspb.PublicKeyResponse{PublicKey: f.KASInfo.PublicKey, Kid: f.KID}, nil } -func (f *FakeKas) getRewrappedKey(rewrapRequest string) []byte { - bodyData := RequestBody{} - err := json.Unmarshal([]byte(rewrapRequest), &bodyData) +func (f *FakeKas) getRewrapResponse(rewrapRequest string) *kaspb.RewrapResponse { + bodyData := kaspb.UnsignedRewrapRequest{} + err := protojson.Unmarshal([]byte(rewrapRequest), &bodyData) f.s.Require().NoError(err, "json.Unmarshal failed") + resp := &kaspb.RewrapResponse{} + + for _, req := range bodyData.GetRequests() { + results := &kaspb.PolicyRewrapResult{PolicyId: req.GetPolicy().GetId()} + resp.Responses = append(resp.Responses, results) + for _, kaoReq := range req.GetKeyAccessObjects() { + kao := kaoReq.GetKeyAccessObject() + wrappedKey := kaoReq.GetKeyAccessObject().GetWrappedKey() + + kasPrivateKey := strings.ReplaceAll(f.privateKey, "\n\t", "\n") + if kao.GetKid() != "" && kao.GetKid() != f.KID { + // old kid + lk, ok := f.legakeys[kaoReq.GetKeyAccessObject().GetKid()] + f.s.Require().True(ok, "unable to find key [%s]", kao.GetKid()) + kasPrivateKey = strings.ReplaceAll(lk.private, "\n\t", "\n") + } - wrappedKey, err := ocrypto.Base64Decode([]byte(bodyData.WrappedKey)) - f.s.Require().NoError(err, "ocrypto.Base64Decode failed") - - kasPrivateKey := strings.ReplaceAll(f.privateKey, "\n\t", "\n") - if bodyData.KID != "" && bodyData.KID != f.KID { - // old kid - lk, ok := f.legakeys[bodyData.KID] - f.s.Require().True(ok, "unable to find key [%s]", bodyData.KID) - kasPrivateKey = strings.ReplaceAll(lk.private, "\n\t", "\n") + asymDecrypt, err := ocrypto.NewAsymDecryption(kasPrivateKey) + f.s.Require().NoError(err, "ocrypto.NewAsymDecryption failed") + symmetricKey, err := asymDecrypt.Decrypt(wrappedKey) + f.s.Require().NoError(err, "ocrypto.Decrypt failed") + asymEncrypt, err := ocrypto.NewAsymEncryption(bodyData.GetClientPublicKey()) + f.s.Require().NoError(err, "ocrypto.NewAsymEncryption failed") + entityWrappedKey, err := asymEncrypt.Encrypt(symmetricKey) + f.s.Require().NoError(err, "ocrypto.encrypt failed") + kaoResult := &kaspb.KeyAccessRewrapResult{ + Result: &kaspb.KeyAccessRewrapResult_KasWrappedKey{KasWrappedKey: entityWrappedKey}, + Status: "permit", + KeyAccessObjectId: kaoReq.GetKeyAccessObjectId(), + } + results.Results = append(results.Results, kaoResult) + } } - - asymDecrypt, err := ocrypto.NewAsymDecryption(kasPrivateKey) - f.s.Require().NoError(err, "ocrypto.NewAsymDecryption failed") - symmetricKey, err := asymDecrypt.Decrypt(wrappedKey) - f.s.Require().NoError(err, "ocrypto.Decrypt failed") - asymEncrypt, err := ocrypto.NewAsymEncryption(bodyData.ClientPublicKey) - f.s.Require().NoError(err, "ocrypto.NewAsymEncryption failed") - entityWrappedKey, err := asymEncrypt.Encrypt(symmetricKey) - f.s.Require().NoError(err, "ocrypto.encrypt failed") - return entityWrappedKey + return resp } func (s *TDFSuite) checkIdentical(file, checksum string) bool { diff --git a/service/kas/access/accessPdp.go b/service/kas/access/accessPdp.go index 04e7553cf..8bdfb87e7 100644 --- a/service/kas/access/accessPdp.go +++ b/service/kas/access/accessPdp.go @@ -3,6 +3,7 @@ package access import ( "context" "errors" + "fmt" "github.com/opentdf/platform/protocol/go/authorization" "github.com/opentdf/platform/protocol/go/policy" @@ -14,30 +15,51 @@ const ( ErrDecisionCountUnexpected = Error("authorization decision count unexpected") ) -func (p *Provider) canAccess(ctx context.Context, token *authorization.Token, policy Policy) (bool, error) { - if len(policy.Body.Dissem) > 0 { - // TODO: Move dissems check to the getdecisions endpoint - p.Logger.Error("Dissems check is not enabled in v2 platform kas") - } - if len(policy.Body.DataAttributes) > 0 { - attrAccess, err := p.checkAttributes(ctx, policy.Body.DataAttributes, token) - if err != nil { - return false, err +type PDPAccessResult struct { + Access bool + Error error + Policy *Policy +} + +func (p *Provider) canAccess(ctx context.Context, token *authorization.Token, policies []*Policy) ([]PDPAccessResult, error) { + var res []PDPAccessResult + var rasList []*authorization.ResourceAttribute + idPolicyMap := make(map[string]*Policy) + for i, policy := range policies { + if len(policy.Body.Dissem) > 0 { + // TODO: Move dissems check to the getdecisions endpoint + p.Logger.Error("Dissems check is not enabled in v2 platform kas") + } + if len(policy.Body.DataAttributes) > 0 { + id := fmt.Sprintf("rewrap-%d", i) + ras := &authorization.ResourceAttribute{ResourceAttributesId: id} + for _, attr := range policy.Body.DataAttributes { + ras.AttributeValueFqns = append(ras.AttributeValueFqns, attr.URI) + } + rasList = append(rasList, ras) + idPolicyMap[id] = policy + } else { + res = append(res, PDPAccessResult{Access: true, Policy: policy}) } - return attrAccess, nil } - // if no dissem and no attributes then allow - return true, nil -} -func (p *Provider) checkAttributes(ctx context.Context, dataAttrs []Attribute, ent *authorization.Token) (bool, error) { - ras := []*authorization.ResourceAttribute{{ - AttributeValueFqns: make([]string, 0), - }} + dr, err := p.checkAttributes(ctx, rasList, token) - for _, attr := range dataAttrs { - ras[0].AttributeValueFqns = append(ras[0].GetAttributeValueFqns(), attr.URI) + if err != nil { + return nil, err } + for _, resp := range dr.GetDecisionResponses() { + policy, ok := idPolicyMap[resp.GetResourceAttributesId()] + if !ok { // this really should not happen + continue + } + res = append(res, PDPAccessResult{Policy: policy, Access: resp.GetDecision() == authorization.DecisionResponse_DECISION_PERMIT}) + } + + return res, nil +} + +func (p *Provider) checkAttributes(ctx context.Context, ras []*authorization.ResourceAttribute, ent *authorization.Token) (*authorization.GetDecisionsByTokenResponse, error) { in := authorization.GetDecisionsByTokenRequest{ DecisionRequests: []*authorization.TokenDecisionRequest{ { @@ -52,14 +74,7 @@ func (p *Provider) checkAttributes(ctx context.Context, dataAttrs []Attribute, e dr, err := p.SDK.Authorization.GetDecisionsByToken(ctx, &in) if err != nil { p.Logger.ErrorContext(ctx, "Error received from GetDecisionsByToken", "err", err) - return false, errors.Join(ErrDecisionUnexpected, err) - } - if len(dr.GetDecisionResponses()) != 1 { - p.Logger.ErrorContext(ctx, ErrDecisionCountUnexpected.Error(), "count", len(dr.GetDecisionResponses())) - return false, ErrDecisionCountUnexpected - } - if dr.GetDecisionResponses()[0].GetDecision() == authorization.DecisionResponse_DECISION_PERMIT { - return true, nil + return nil, errors.Join(ErrDecisionUnexpected, err) } - return false, nil + return dr, nil } diff --git a/service/kas/access/attribute.go b/service/kas/access/attribute.go index 75e9c7113..27c088d90 100644 --- a/service/kas/access/attribute.go +++ b/service/kas/access/attribute.go @@ -4,8 +4,6 @@ import ( "crypto" ) -const schemaVersion = "1.1.0" - type Attribute struct { URI string `json:"attribute"` // attribute PublicKey crypto.PublicKey `json:"pubKey"` // pubKey diff --git a/service/kas/access/policy_test.go b/service/kas/access/policy_test.go deleted file mode 100644 index 09bb5922e..000000000 --- a/service/kas/access/policy_test.go +++ /dev/null @@ -1 +0,0 @@ -package access diff --git a/service/kas/access/rewrap.go b/service/kas/access/rewrap.go index ea4e53821..d57a34cdb 100644 --- a/service/kas/access/rewrap.go +++ b/service/kas/access/rewrap.go @@ -17,7 +17,6 @@ import ( "fmt" "log/slog" "net/http" - "strings" "time" "connectrpc.com/connect" @@ -27,6 +26,7 @@ import ( "github.com/opentdf/platform/lib/ocrypto" "github.com/opentdf/platform/protocol/go/authorization" "go.opentelemetry.io/otel/trace" + "google.golang.org/protobuf/encoding/protojson" kaspb "github.com/opentdf/platform/protocol/go/kas" "github.com/opentdf/platform/sdk" @@ -38,6 +38,13 @@ import ( "google.golang.org/grpc/status" ) +const ( + kTDF3Algorithm = "rsa:2048" + kNanoAlgorithm = "ec:secp256r1" + kFailedStatus = "fail" + kPermitStatus = "permit" +) + type SignedRequestBody struct { RequestBody string `json:"requestBody"` } @@ -58,6 +65,14 @@ type entityInfo struct { Token string `json:"-"` } +type kaoResult struct { + ID string + Key []byte + Error error +} + +type policyKAOResults map[string]map[string]kaoResult + const ( kNanoTDFGMACLength = 8 ErrUser = Error("request error") @@ -121,7 +136,47 @@ func justRequestBody(ctx context.Context, token jwt.Token, logger logger.Logger) return rbString, nil } -func extractSRTBody(ctx context.Context, headers http.Header, in *kaspb.RewrapRequest, logger logger.Logger) (*RequestBody, error) { +func extractAndConvertV1SRTBody(body []byte) (kaspb.UnsignedRewrapRequest, error) { + var requestBody RequestBody + if err := json.Unmarshal(body, &requestBody); err != nil { + return kaspb.UnsignedRewrapRequest{}, err + } + + kao := requestBody.KeyAccess + // ignore errors, maybe nanoTDF + binding, _ := extractPolicyBinding(kao.PolicyBinding) + + reqs := []*kaspb.UnsignedRewrapRequest_WithPolicyRequest{ + { + KeyAccessObjects: []*kaspb.UnsignedRewrapRequest_WithKeyAccessObject{ + {KeyAccessObjectId: "kao-0", KeyAccessObject: &kaspb.KeyAccess{ + EncryptedMetadata: kao.EncryptedMetadata, + PolicyBinding: &kaspb.PolicyBinding{Hash: binding, Algorithm: kao.Algorithm}, + Protocol: kao.Protocol, + KeyType: kao.Type, + KasUrl: kao.URL, + Kid: kao.KID, + SplitId: kao.SID, + WrappedKey: kao.WrappedKey, + Header: kao.Header, + }}, + }, + Algorithm: requestBody.Algorithm, + Policy: &kaspb.UnsignedRewrapRequest_WithPolicy{ + Id: "policy-1", + Body: requestBody.Policy, + }, + }, + } + + return kaspb.UnsignedRewrapRequest{ + ClientPublicKey: requestBody.ClientPublicKey, + Requests: reqs, + }, nil +} + +func extractSRTBody(ctx context.Context, headers http.Header, in *kaspb.RewrapRequest, logger logger.Logger) (*kaspb.UnsignedRewrapRequest, bool, error) { + isV1 := false // First load legacy method for verifying SRT if vpk, ok := headers["X-Virtrupubkey"]; ok && len(vpk) == 1 { logger.InfoContext(ctx, "Legacy Client: Processing X-Virtrupubkey") @@ -142,79 +197,63 @@ func extractSRTBody(ctx context.Context, headers http.Header, in *kaspb.RewrapRe rbString, err = noverify(ctx, srt, logger) if err != nil { logger.ErrorContext(ctx, "unable to load RSA verifier", "err", err) - return nil, err + return nil, false, err } } else { // verify and validate the request token var err error rbString, err = verifySRT(ctx, srt, dpopJWK, logger) if err != nil { - return nil, err + return nil, false, err } } - var requestBody RequestBody - err = json.Unmarshal([]byte(rbString), &requestBody) - if err != nil { - logger.WarnContext(ctx, "invalid request body") - return nil, err400("invalid request body") + var requestBody kaspb.UnsignedRewrapRequest + err = protojson.Unmarshal([]byte(rbString), &requestBody) + // if there are no requests then it could be a v1 request + if err != nil || len(requestBody.GetRequests()) == 0 { + logger.WarnContext(ctx, "invalid request body! checking v1 SRT") + requestBody, err = extractAndConvertV1SRTBody([]byte(rbString)) + if err != nil { + return nil, false, err400("invalid request body") + } + isV1 = true } - logger.DebugContext(ctx, "extracted request body", slog.Any("requestBody", requestBody)) + logger.DebugContext(ctx, "extracted request body", slog.Any("requestBody", requestBody.String())) - logger.DebugContext(ctx, "extract public key", "requestBody.ClientPublicKey", requestBody.ClientPublicKey) - block, _ := pem.Decode([]byte(requestBody.ClientPublicKey)) + logger.DebugContext(ctx, "extract public key", "requestBody.ClientPublicKey", requestBody.GetClientPublicKey()) + block, _ := pem.Decode([]byte(requestBody.GetClientPublicKey())) if block == nil { logger.WarnContext(ctx, "missing clientPublicKey") - return nil, err400("clientPublicKey failure") + return nil, isV1, err400("clientPublicKey failure") } // Try to parse the clientPublicKey clientPublicKey, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { logger.WarnContext(ctx, "failure to parse clientPublicKey", "err", err) - return nil, err400("clientPublicKey parse failure") + return nil, isV1, err400("clientPublicKey parse failure") } // Check to make sure the clientPublicKey is a supported key type - switch publicKey := clientPublicKey.(type) { + switch clientPublicKey.(type) { case *rsa.PublicKey: - requestBody.PublicKey = publicKey - return &requestBody, nil + return &requestBody, isV1, nil case *ecdsa.PublicKey: - requestBody.PublicKey = publicKey - return &requestBody, nil + return &requestBody, isV1, nil default: logger.WarnContext(ctx, fmt.Sprintf("clientPublicKey not a supported key, was [%T]", clientPublicKey)) - return nil, err400("clientPublicKey unsupported type") - } -} - -func extractPolicyBinding(policyBinding interface{}) (string, error) { - switch v := policyBinding.(type) { - case string: - return v, nil - case map[string]interface{}: - if hash, ok := v["hash"].(string); ok { - return hash, nil - } - return "", fmt.Errorf("invalid policy binding object, missing 'hash' field") - default: - return "", fmt.Errorf("unsupported policy binding type") + return nil, isV1, err400("clientPublicKey unsupported type") } } -func verifyAndParsePolicy(ctx context.Context, requestBody *RequestBody, k []byte, logger logger.Logger) (*Policy, error) { - actualHMAC, err := generateHMACDigest(ctx, []byte(requestBody.Policy), k, logger) +func verifyPolicyBinding(ctx context.Context, policy []byte, kao *kaspb.UnsignedRewrapRequest_WithKeyAccessObject, symKey []byte, logger logger.Logger) error { + actualHMAC, err := generateHMACDigest(ctx, policy, symKey, logger) if err != nil { logger.WarnContext(ctx, "unable to generate policy hmac", "err", err) - return nil, err400("bad request") - } - - policyBinding, err := extractPolicyBinding(requestBody.KeyAccess.PolicyBinding) - if err != nil { - logger.WarnContext(ctx, "invalid policy binding", "err", err) - return nil, err400("bad request") + return err400("bad request") } + policyBinding := kao.GetKeyAccessObject().GetPolicyBinding().GetHash() expectedHMAC := make([]byte, base64.StdEncoding.DecodedLen(len(policyBinding))) n, err := base64.StdEncoding.Decode(expectedHMAC, []byte(policyBinding)) if err == nil { @@ -223,25 +262,28 @@ func verifyAndParsePolicy(ctx context.Context, requestBody *RequestBody, k []byt expectedHMAC = expectedHMAC[:n] if err != nil { logger.WarnContext(ctx, "invalid policy binding", "err", err) - return nil, err400("bad request") + return err400("bad request") } if !hmac.Equal(actualHMAC, expectedHMAC) { logger.WarnContext(ctx, "policy hmac mismatch", "policyBinding", policyBinding) - return nil, err400("bad request") + return err400("bad request") } - sDecPolicy, err := base64.StdEncoding.DecodeString(requestBody.Policy) - if err != nil { - logger.WarnContext(ctx, "unable to decode policy", "err", err) - return nil, err400("bad request") - } - decoder := json.NewDecoder(strings.NewReader(string(sDecPolicy))) - var policy Policy - err = decoder.Decode(&policy) - if err != nil { - logger.WarnContext(ctx, "unable to decode policy", "err", err) - return nil, err400("bad request") + + return nil +} + +func extractPolicyBinding(policyBinding interface{}) (string, error) { + switch v := policyBinding.(type) { + case string: + return v, nil + case map[string]interface{}: + if hash, ok := v["hash"].(string); ok { + return hash, nil + } + return "", fmt.Errorf("invalid policy binding object, missing 'hash' field") + default: + return "", fmt.Errorf("unsupported policy binding type") } - return &policy, nil } func getEntityInfo(ctx context.Context, logger *logger.Logger) (*entityInfo, error) { @@ -268,11 +310,51 @@ func getEntityInfo(ctx context.Context, logger *logger.Logger) (*entityInfo, err return info, nil } +func failedKAORewrap(res map[string]kaoResult, kao *kaspb.UnsignedRewrapRequest_WithKeyAccessObject, err error) { + res[kao.GetKeyAccessObjectId()] = kaoResult{ + ID: kao.GetKeyAccessObjectId(), + Error: err, + } +} + +func addResultsToResponse(response *kaspb.RewrapResponse, result policyKAOResults) { + for policyID, policyMap := range result { + policyResults := &kaspb.PolicyRewrapResult{ + PolicyId: policyID, + } + for kaoID, kaoRes := range policyMap { + kaoResult := &kaspb.KeyAccessRewrapResult{ + KeyAccessObjectId: kaoID, + } + switch { + case kaoRes.Error != nil: + kaoResult.Status = kFailedStatus + kaoResult.Result = &kaspb.KeyAccessRewrapResult_Error{Error: kaoRes.Error.Error()} + case kaoRes.Key != nil: + kaoResult.Status = kPermitStatus + kaoResult.Result = &kaspb.KeyAccessRewrapResult_KasWrappedKey{KasWrappedKey: kaoRes.Key} + default: + kaoResult.Status = kFailedStatus + kaoResult.Result = &kaspb.KeyAccessRewrapResult_Error{Error: "kao not processed by kas"} + } + policyResults.Results = append(policyResults.Results, kaoResult) + } + response.Responses = append(response.Responses, policyResults) + } +} + +func getMapValue[Map ~map[K]V, K comparable, V any](m Map) *V { + for _, v := range m { + return &v + } + return nil +} + func (p *Provider) Rewrap(ctx context.Context, req *connect.Request[kaspb.RewrapRequest]) (*connect.Response[kaspb.RewrapResponse], error) { in := req.Msg p.Logger.DebugContext(ctx, "REWRAP") - body, err := extractSRTBody(ctx, req.Header(), in, *p.Logger) + body, isV1, err := extractSRTBody(ctx, req.Header(), in, *p.Logger) if err != nil { p.Logger.DebugContext(ctx, "unverifiable srt", "err", err) return nil, err @@ -284,224 +366,364 @@ func (p *Provider) Rewrap(ctx context.Context, req *connect.Request[kaspb.Rewrap return nil, err } - if body.Algorithm == "" { - p.Logger.DebugContext(ctx, "default rewrap algorithm") - body.Algorithm = "rsa:2048" - } + resp := &kaspb.RewrapResponse{} - if body.Algorithm == "ec:secp256r1" { - rsp, err := p.nanoTDFRewrap(ctx, body, entityInfo) - if err != nil { - p.Logger.ErrorContext(ctx, "rewrap nano", "err", err) + var nanoReqs []*kaspb.UnsignedRewrapRequest_WithPolicyRequest + var tdf3Reqs []*kaspb.UnsignedRewrapRequest_WithPolicyRequest + for _, req := range body.GetRequests() { + switch { + case req.GetAlgorithm() == kNanoAlgorithm: + nanoReqs = append(nanoReqs, req) + case req.GetAlgorithm() == "": + req.Algorithm = kTDF3Algorithm + tdf3Reqs = append(tdf3Reqs, req) + default: + tdf3Reqs = append(tdf3Reqs, req) } - p.Logger.DebugContext(ctx, "rewrap nano", "rsp", rsp) - return connect.NewResponse(rsp), err } - rsp, err := p.tdf3Rewrap(ctx, body, entityInfo) - if err != nil { - p.Logger.ErrorContext(ctx, "rewrap tdf3", "err", err) + var results policyKAOResults + if len(tdf3Reqs) > 0 { + results = p.tdf3Rewrap(ctx, tdf3Reqs, body.GetClientPublicKey(), entityInfo) + addResultsToResponse(resp, results) + } else { + resp.SessionPublicKey, results = p.nanoTDFRewrap(ctx, nanoReqs, body.GetClientPublicKey(), entityInfo) + addResultsToResponse(resp, results) } - return connect.NewResponse(rsp), err + + if isV1 { + if len(results) != 1 { + return nil, fmt.Errorf("invalid request") + } + kaoResults := *getMapValue(results) + if len(kaoResults) != 1 { + return nil, fmt.Errorf("invalid request") + } + kao := *getMapValue(kaoResults) + + if kao.Error != nil { + return nil, kao.Error + } + resp.EntityWrappedKey = kao.Key //nolint:staticcheck // deprecated but keeping behavior for backwards compatibility + } + + return connect.NewResponse(resp), err } -func (p *Provider) tdf3Rewrap(ctx context.Context, body *RequestBody, entity *entityInfo) (*kaspb.RewrapResponse, error) { - if p.Tracer != nil { - var span trace.Span - ctx, span = p.Tracer.Start(ctx, "rewrap-tdf3") - defer span.End() +func (p *Provider) verifyRewrapRequests(ctx context.Context, req *kaspb.UnsignedRewrapRequest_WithPolicyRequest) (*Policy, map[string]kaoResult, error) { + results := make(map[string]kaoResult) + anyValidKAOs := false + p.Logger.DebugContext(ctx, "extracting policy", "requestBody.policy", req.GetPolicy()) + sDecPolicy, policyErr := base64.StdEncoding.DecodeString(req.GetPolicy().GetBody()) + policy := &Policy{} + if policyErr == nil { + policyErr = json.Unmarshal(sDecPolicy, policy) } - var kidsToCheck []string - if body.KeyAccess.KID != "" { - kidsToCheck = []string{body.KeyAccess.KID} - } else { - p.Logger.InfoContext(ctx, "kid free kao") - for _, k := range p.KASConfig.Keyring { - if k.Algorithm == security.AlgorithmRSA2048 && k.Legacy { - kidsToCheck = append(kidsToCheck, k.KID) + for _, kao := range req.GetKeyAccessObjects() { + if policyErr != nil { + failedKAORewrap(results, kao, err400("bad request")) + continue + } + var kidsToCheck []string + if kao.GetKeyAccessObject().GetKid() != "" { + kidsToCheck = []string{kao.GetKeyAccessObject().GetKid()} + } else { + p.Logger.InfoContext(ctx, "kid free kao") + for _, k := range p.KASConfig.Keyring { + if k.Algorithm == security.AlgorithmRSA2048 && k.Legacy { + kidsToCheck = append(kidsToCheck, k.KID) + } + } + if len(kidsToCheck) == 0 { + p.Logger.WarnContext(ctx, "failure to find legacy kids for rsa") + failedKAORewrap(results, kao, err400("bad request")) + continue } } - if len(kidsToCheck) == 0 { - p.Logger.WarnContext(ctx, "failure to find legacy kids for rsa") - return nil, err400("bad request") + + symKey, err := p.CryptoProvider.RSADecrypt(crypto.SHA1, kidsToCheck[0], "", kao.GetKeyAccessObject().GetWrappedKey()) + for _, kid := range kidsToCheck[1:] { + p.Logger.WarnContext(ctx, "continue paging through legacy KIDs for kid free kao", "err", err) + if err == nil { + break + } + symKey, err = p.CryptoProvider.RSADecrypt(crypto.SHA1, kid, "", kao.GetKeyAccessObject().GetWrappedKey()) } - } - symmetricKey, err := p.CryptoProvider.RSADecrypt(crypto.SHA1, kidsToCheck[0], "", body.KeyAccess.WrappedKey) - for _, kid := range kidsToCheck[1:] { - p.Logger.WarnContext(ctx, "continue paging through legacy KIDs for kid free kao", "err", err) - if err == nil { - break + if err != nil { + p.Logger.WarnContext(ctx, "failure to decrypt dek", "err", err) + failedKAORewrap(results, kao, err400("bad request")) + continue } - symmetricKey, err = p.CryptoProvider.RSADecrypt(crypto.SHA1, kid, "", body.KeyAccess.WrappedKey) - } - if err != nil { - p.Logger.WarnContext(ctx, "failure to decrypt dek", "err", err) - return nil, err400("bad request") - } - p.Logger.DebugContext(ctx, "verifying policy binding", "requestBody.policy", body.Policy) - policy, err := verifyAndParsePolicy(ctx, body, symmetricKey, *p.Logger) - if err != nil { - return nil, err - } + err = verifyPolicyBinding(ctx, []byte(req.GetPolicy().GetBody()), kao, symKey, *p.Logger) + if err != nil { + failedKAORewrap(results, kao, err) + continue + } + results[kao.GetKeyAccessObjectId()] = kaoResult{ + ID: kao.GetKeyAccessObjectId(), + Key: symKey, + } - p.Logger.DebugContext(ctx, "extracting policy", "requestBody.policy", body.Policy) - // changed use the entities in the token to get the decisions - tok := &authorization.Token{ - Id: "rewrap-tok", - Jwt: entity.Token, + anyValidKAOs = true } - access, err := p.canAccess(ctx, tok, *policy) + if policyErr != nil { + return nil, results, policyErr + } - // Audit the TDF3 Rewrap - kasPolicy := ConvertToAuditKasPolicy(*policy) + if !anyValidKAOs { + p.Logger.WarnContext(ctx, "no valid KAOs found") + return policy, results, fmt.Errorf("no valid KAOs") + } - policyBinding, _ := extractPolicyBinding(body.KeyAccess.PolicyBinding) + return policy, results, nil +} - auditEventParams := audit.RewrapAuditEventParams{ - Policy: kasPolicy, - IsSuccess: access, - TDFFormat: "tdf3", - Algorithm: body.Algorithm, - PolicyBinding: policyBinding, +func (p *Provider) tdf3Rewrap(ctx context.Context, requests []*kaspb.UnsignedRewrapRequest_WithPolicyRequest, clientPublicKey string, entity *entityInfo) policyKAOResults { + if p.Tracer != nil { + var span trace.Span + ctx, span = p.Tracer.Start(ctx, "rewrap-tdf3") + defer span.End() } - if err != nil { - p.Logger.WarnContext(ctx, "Could not perform access decision!", "err", err) - p.Logger.Audit.RewrapFailure(ctx, auditEventParams) - return nil, err403("forbidden") + results := make(policyKAOResults) + var policies []*Policy + policyReqs := make(map[*Policy]*kaspb.UnsignedRewrapRequest_WithPolicyRequest) + for _, req := range requests { + policy, kaoResults, err := p.verifyRewrapRequests(ctx, req) + results[req.GetPolicy().GetId()] = kaoResults + if err != nil { + continue + } + policies = append(policies, policy) + policyReqs[policy] = req } - if !access { - p.Logger.Audit.RewrapFailure(ctx, auditEventParams) - return nil, err403("forbidden") + tok := &authorization.Token{ + Id: "rewrap-token", + Jwt: entity.Token, + } + pdpAccessResults, accessErr := p.canAccess(ctx, tok, policies) + if accessErr != nil { + failAllKaos(requests, results, err403("could not perform access")) + return results } - asymEncrypt, err := ocrypto.NewAsymEncryption(body.ClientPublicKey) + asymEncrypt, err := ocrypto.NewAsymEncryption(clientPublicKey) if err != nil { p.Logger.WarnContext(ctx, "ocrypto.NewAsymEncryption:", "err", err) } - rewrappedKey, err := asymEncrypt.Encrypt(symmetricKey) - if err != nil { - p.Logger.WarnContext(ctx, "rewrap: ocrypto.AsymEncryption.encrypt failed", "err", err, "clientPublicKey", &body.ClientPublicKey) - p.Logger.Audit.RewrapFailure(ctx, auditEventParams) - return nil, err400("bad key for rewrap") - } + for _, pdpAccess := range pdpAccessResults { + policy := pdpAccess.Policy + req, ok := policyReqs[policy] + kaoResults := results[req.GetPolicy().GetId()] + if !ok { // this should not happen + continue + } + access := pdpAccess.Access - p.Logger.Audit.RewrapSuccess(ctx, auditEventParams) - return &kaspb.RewrapResponse{ - EntityWrappedKey: rewrappedKey, - SessionPublicKey: "", - SchemaVersion: schemaVersion, - }, nil + // Audit the TDF3 Rewrap + kasPolicy := ConvertToAuditKasPolicy(*policy) + + for _, kao := range req.GetKeyAccessObjects() { + kaoRes := kaoResults[kao.GetKeyAccessObjectId()] + if kaoRes.Error != nil { + continue + } + + policyBinding := kao.GetKeyAccessObject().GetPolicyBinding().GetHash() + auditEventParams := audit.RewrapAuditEventParams{ + Policy: kasPolicy, + IsSuccess: access, + TDFFormat: "tdf3", + Algorithm: req.GetAlgorithm(), + PolicyBinding: policyBinding, + } + + if !access { + p.Logger.Audit.RewrapFailure(ctx, auditEventParams) + failedKAORewrap(kaoResults, kao, err403("forbidden")) + continue + } + + rewrappedKey, err := asymEncrypt.Encrypt(kaoRes.Key) + if err != nil { + p.Logger.WarnContext(ctx, "rewrap: ocrypto.AsymEncryption.encrypt failed", "err", err, "clientPublicKey", clientPublicKey) + p.Logger.Audit.RewrapFailure(ctx, auditEventParams) + failedKAORewrap(kaoResults, kao, err400("bad key for rewrap")) + continue + } + kaoResults[kao.GetKeyAccessObjectId()] = kaoResult{ + ID: kao.GetKeyAccessObjectId(), + Key: rewrappedKey, + } + + p.Logger.Audit.RewrapSuccess(ctx, auditEventParams) + } + } + return results } -func (p *Provider) nanoTDFRewrap(ctx context.Context, body *RequestBody, entity *entityInfo) (*kaspb.RewrapResponse, error) { +func (p *Provider) nanoTDFRewrap(ctx context.Context, requests []*kaspb.UnsignedRewrapRequest_WithPolicyRequest, clientPublicKey string, entity *entityInfo) (string, policyKAOResults) { + results := make(policyKAOResults) if p.Tracer != nil { var span trace.Span ctx, span = p.Tracer.Start(ctx, "rewrap-nanotdf") defer span.End() } - headerReader := bytes.NewReader(body.KeyAccess.Header) + var policies []*Policy + policyReqs := make(map[*Policy]*kaspb.UnsignedRewrapRequest_WithPolicyRequest) - header, _, err := sdk.NewNanoTDFHeaderFromReader(headerReader) - if err != nil { - return nil, fmt.Errorf("failed to parse NanoTDF header: %w", err) - } - // Lookup KID from nano header - kid, err := header.GetKasURL().GetIdentifier() - if err != nil { - p.Logger.DebugContext(ctx, "nanoTDFRewrap GetIdentifier", "kid", kid, "err", err) - // legacy nano with KID - kid, err = p.lookupKid(ctx, security.AlgorithmECP256R1) - if err != nil { - p.Logger.ErrorContext(ctx, "failure to find default kid for ec", "err", err) - return nil, err400("bad request") + for _, req := range requests { + policy, kaoResults := p.verifyNanoRewrapRequests(ctx, req) + results[req.GetPolicy().GetId()] = kaoResults + if policy != nil { + policies = append(policies, policy) + policyReqs[policy] = req } - p.Logger.DebugContext(ctx, "nanoTDFRewrap lookupKid", "kid", kid) } - p.Logger.DebugContext(ctx, "nanoTDFRewrap", "kid", kid) - ecCurve, err := header.ECCurve() - if err != nil { - return nil, fmt.Errorf("ECCurve failed: %w", err) + // do the access check + tok := &authorization.Token{ + Id: "rewrap-tok", + Jwt: entity.Token, } - symmetricKey, err := p.CryptoProvider.GenerateNanoTDFSymmetricKey(kid, header.EphemeralKey, ecCurve) - if err != nil { - return nil, fmt.Errorf("failed to generate symmetric key: %w", err) + pdpAccessResults, accessErr := p.canAccess(ctx, tok, policies) + if accessErr != nil { + failAllKaos(requests, results, err403("could not perform access")) + return "", results } - // extract the policy - policy, err := extractNanoPolicy(symmetricKey, header) + privateKeyHandle, publicKeyHandle, err := p.CryptoProvider.GenerateEphemeralKasKeys() if err != nil { - return nil, fmt.Errorf("Error extracting policy: %w", err) + failAllKaos(requests, results, fmt.Errorf("failed to generate keypair: %w", err)) + return "", results } - - // check the policy binding - verify, err := header.VerifyPolicyBinding() + sessionKey, err := p.CryptoProvider.GenerateNanoTDFSessionKey(privateKeyHandle, []byte(clientPublicKey)) if err != nil { - return nil, fmt.Errorf("failed to verify policy binding: %w", err) + p.Logger.DebugContext(ctx, "GenerateNanoTDFSessionKey", "err", err) + failAllKaos(requests, results, fmt.Errorf("failed to generate session key: %w", err)) + return "", results } - if !verify { - return nil, fmt.Errorf("policy binding verification failed") - } + for _, pdpAccess := range pdpAccessResults { + policy := pdpAccess.Policy + req, ok := policyReqs[policy] + if !ok { // this should not happen + continue + } + kaoResults := results[req.GetPolicy().GetId()] + access := pdpAccess.Access - // do the access check - tok := &authorization.Token{ - Id: "rewrap-tok", - Jwt: entity.Token, - } + // Audit the Nano Rewrap + kasPolicy := ConvertToAuditKasPolicy(*policy) - access, err := p.canAccess(ctx, tok, *policy) + for _, kao := range req.GetKeyAccessObjects() { + kaoInfo := kaoResults[kao.GetKeyAccessObjectId()] + if kaoInfo.Error != nil { + continue + } - // Audit the rewrap - kasPolicy := ConvertToAuditKasPolicy(*policy) - auditEventParams := audit.RewrapAuditEventParams{ - Policy: kasPolicy, - TDFFormat: "nano", - Algorithm: body.Algorithm, - } + auditEventParams := audit.RewrapAuditEventParams{ + Policy: kasPolicy, + IsSuccess: access, + TDFFormat: "Nano", + Algorithm: req.GetAlgorithm(), + } - if err != nil { - p.Logger.WarnContext(ctx, "Could not perform access decision!", "err", err) - p.Logger.Audit.RewrapFailure(ctx, auditEventParams) - return nil, err403("forbidden") - } + if !access { + p.Logger.Audit.RewrapFailure(ctx, auditEventParams) + failedKAORewrap(kaoResults, kao, err403("forbidden")) + continue + } + cipherText, err := wrapKeyAES(sessionKey, kaoInfo.Key) + if err != nil { + p.Logger.Audit.RewrapFailure(ctx, auditEventParams) + failedKAORewrap(kaoResults, kao, err403("forbidden")) + continue + } - if !access { - p.Logger.WarnContext(ctx, "Access Denied; no reason given") - p.Logger.Audit.RewrapFailure(ctx, auditEventParams) - return nil, err403("forbidden") - } + kaoResults[kao.GetKeyAccessObjectId()] = kaoResult{ + ID: kao.GetKeyAccessObjectId(), + Key: cipherText, + } - privateKeyHandle, publicKeyHandle, err := p.CryptoProvider.GenerateEphemeralKasKeys() - if err != nil { - p.Logger.Audit.RewrapFailure(ctx, auditEventParams) - return nil, fmt.Errorf("failed to generate keypair: %w", err) - } - sessionKey, err := p.CryptoProvider.GenerateNanoTDFSessionKey(privateKeyHandle, []byte(body.ClientPublicKey)) - if err != nil { - p.Logger.Audit.RewrapFailure(ctx, auditEventParams) - return nil, fmt.Errorf("failed to generate session key: %w", err) + p.Logger.Audit.RewrapSuccess(ctx, auditEventParams) + } } + return string(publicKeyHandle), results +} - cipherText, err := wrapKeyAES(sessionKey, symmetricKey) - if err != nil { - p.Logger.Audit.RewrapFailure(ctx, auditEventParams) - return nil, fmt.Errorf("failed to encrypt key: %w", err) - } +func (p *Provider) verifyNanoRewrapRequests(ctx context.Context, req *kaspb.UnsignedRewrapRequest_WithPolicyRequest) (*Policy, map[string]kaoResult) { + results := make(map[string]kaoResult) - p.Logger.Audit.RewrapSuccess(ctx, auditEventParams) + for _, kao := range req.GetKeyAccessObjects() { + // there should never be multiple KAOs in policy + if len(req.GetKeyAccessObjects()) != 1 { + failedKAORewrap(results, kao, err400("NanoTDFs should not have multiple KAOs per Policy")) + continue + } - return &kaspb.RewrapResponse{ - EntityWrappedKey: cipherText, - SessionPublicKey: string(publicKeyHandle), - SchemaVersion: schemaVersion, - }, nil + headerReader := bytes.NewReader(kao.GetKeyAccessObject().GetHeader()) + header, _, err := sdk.NewNanoTDFHeaderFromReader(headerReader) + if err != nil { + failedKAORewrap(results, kao, fmt.Errorf("failed to parse NanoTDF header: %w", err)) + return nil, results + } + // Lookup KID from nano header + kid, err := header.GetKasURL().GetIdentifier() + if err != nil { + p.Logger.DebugContext(ctx, "nanoTDFRewrap GetIdentifier", "kid", kid, "err", err) + // legacy nano with KID + kid, err = p.lookupKid(ctx, security.AlgorithmECP256R1) + if err != nil { + p.Logger.ErrorContext(ctx, "failure to find default kid for ec", "err", err) + failedKAORewrap(results, kao, err400("bad request")) + continue + } + p.Logger.DebugContext(ctx, "nanoTDFRewrap lookupKid", "kid", kid) + } + p.Logger.DebugContext(ctx, "nanoTDFRewrap", "kid", kid) + ecCurve, err := header.ECCurve() + if err != nil { + failedKAORewrap(results, kao, fmt.Errorf("ECCurve failed: %w", err)) + return nil, results + } + + symmetricKey, err := p.CryptoProvider.GenerateNanoTDFSymmetricKey(kid, header.EphemeralKey, ecCurve) + if err != nil { + failedKAORewrap(results, kao, fmt.Errorf("failed to generate symmetric key: %w", err)) + return nil, results + } + + // extract the policy + policy, err := extractNanoPolicy(symmetricKey, header) + if err != nil { + failedKAORewrap(results, kao, fmt.Errorf("Error extracting policy: %w", err)) + return nil, results + } + + // check the policy binding + verify, err := header.VerifyPolicyBinding() + if err != nil { + failedKAORewrap(results, kao, fmt.Errorf("failed to verify policy binding: %w", err)) + return nil, results + } + + if !verify { + failedKAORewrap(results, kao, fmt.Errorf("policy binding verification failed")) + return nil, results + } + results[kao.GetKeyAccessObjectId()] = kaoResult{ + ID: kao.GetKeyAccessObjectId(), + Key: symmetricKey, + } + return policy, results + } + return nil, results } func extractNanoPolicy(symmetricKey []byte, header sdk.NanoTDFHeader) (*Policy, error) { @@ -545,3 +767,11 @@ func wrapKeyAES(sessionKey, dek []byte) ([]byte, error) { return cipherText, nil } + +func failAllKaos(reqs []*kaspb.UnsignedRewrapRequest_WithPolicyRequest, results policyKAOResults, err error) { + for _, req := range reqs { + for _, kao := range req.GetKeyAccessObjects() { + failedKAORewrap(results[req.GetPolicy().GetId()], kao, err) + } + } +} diff --git a/service/kas/access/rewrap_test.go b/service/kas/access/rewrap_test.go index a9eff6e97..14ad8b620 100644 --- a/service/kas/access/rewrap_test.go +++ b/service/kas/access/rewrap_test.go @@ -12,6 +12,8 @@ import ( "net/http" "testing" + "google.golang.org/protobuf/encoding/protojson" + "github.com/lestrrat-go/jwx/v2/jwa" "github.com/lestrrat-go/jwx/v2/jwk" "github.com/lestrrat-go/jwx/v2/jws" @@ -199,7 +201,7 @@ type PolicyBinding struct { Hash string `json:"hash"` } -func keyAccessWrappedRaw(t *testing.T, policyBindingAsString bool) KeyAccess { +func keyAccessWrappedRaw(t *testing.T, policyBindingAsString bool) kaspb.UnsignedRewrapRequest_WithKeyAccessObject { policyBytes := fauxPolicyBytes(t) asym, err := ocrypto.NewAsymEncryption(rsaPublicAlt) require.NoError(t, err, "rewrap: NewAsymEncryption failed") @@ -213,23 +215,29 @@ func keyAccessWrappedRaw(t *testing.T, policyBindingAsString bool) KeyAccess { dst := make([]byte, hex.EncodedLen(len(bindingBytes))) hex.Encode(dst, bindingBytes) - var policyBinding interface{} + var policyBinding *kaspb.PolicyBinding if policyBindingAsString { - policyBinding = base64.StdEncoding.EncodeToString(dst) - } else { - policyBinding = PolicyBinding{ - Alg: "HS256", + policyBinding = &kaspb.PolicyBinding{ Hash: base64.StdEncoding.EncodeToString(dst), } + } else { + policyBinding = &kaspb.PolicyBinding{ + Algorithm: "HS256", + Hash: base64.StdEncoding.EncodeToString(dst), + } } + require.NoError(t, err) - return KeyAccess{ - Type: "wrapped", - URL: "http://127.0.0.1:4000", - Protocol: "kas", - WrappedKey: []byte(base64.StdEncoding.EncodeToString(wrappedKey)), - PolicyBinding: policyBinding, + return kaspb.UnsignedRewrapRequest_WithKeyAccessObject{ + KeyAccessObjectId: "123", + KeyAccessObject: &kaspb.KeyAccess{ + KeyType: "wrapped", + KasUrl: "http://127.0.0.1:4000", + Protocol: "kas", + WrappedKey: []byte(base64.StdEncoding.EncodeToString(wrappedKey)), + PolicyBinding: policyBinding, + }, } } @@ -276,13 +284,25 @@ func jwtWrongKey(t *testing.T) []byte { return signedMockJWT(t, entityPrivateKey(t)) } +func makeRewrapRequests(t *testing.T, policy []byte, bindingAsString bool) []*kaspb.UnsignedRewrapRequest_WithPolicyRequest { + kaoReq := keyAccessWrappedRaw(t, bindingAsString) + return []*kaspb.UnsignedRewrapRequest_WithPolicyRequest{ + { + KeyAccessObjects: []*kaspb.UnsignedRewrapRequest_WithKeyAccessObject{&kaoReq}, + Policy: &kaspb.UnsignedRewrapRequest_WithPolicy{ + Id: "123", + Body: string(policy), + }, + }, + } +} + func makeRewrapBody(t *testing.T, policy []byte, policyBindingAsString bool) []byte { - mockBody := RequestBody{ - KeyAccess: keyAccessWrappedRaw(t, policyBindingAsString), - Policy: string(policy), + mockBody := &kaspb.UnsignedRewrapRequest{ + Requests: makeRewrapRequests(t, policy, policyBindingAsString), ClientPublicKey: rsaPublicAlt, } - bodyData, err := json.Marshal(mockBody) + bodyData, err := protojson.Marshal(mockBody) require.NoError(t, err) tok := jwt.New() @@ -336,7 +356,7 @@ func TestParseAndVerifyRequest(t *testing.T) { logger := logger.CreateTestLogger() - verified, err := extractSRTBody( + verified, _, err := extractSRTBody( ctx, http.Header{}, &kaspb.RewrapRequest{ @@ -347,14 +367,15 @@ func TestParseAndVerifyRequest(t *testing.T) { if tt.goodDPoP { require.NoError(t, err, "failed to parse srt=[%s], tok=[%s]", tt.body, bearer) require.NotNil(t, verified, "unable to load request body") - require.NotNil(t, verified.ClientPublicKey, "unable to load public key") - - policy, err := verifyAndParsePolicy(context.Background(), verified, []byte(plainKey), *logger) - if !tt.shouldError { - require.NoError(t, err, "failed to verify policy body=[%v]", tt.body) - assert.Len(t, policy.Body.DataAttributes, 2, "incorrect policy body=[%v]", policy.Body) - } else { - require.Error(t, err, "failed to fail policy body=[%v]", tt.body) + require.NotNil(t, verified.GetClientPublicKey(), "unable to load public key") + + for _, req := range verified.GetRequests() { + err := verifyPolicyBinding(context.Background(), []byte(req.GetPolicy().GetBody()), req.GetKeyAccessObjects()[0], []byte(plainKey), *logger) + if !tt.shouldError { + require.NoError(t, err, "failed to verify policy body=[%v]", tt.body) + } else { + require.Error(t, err, "failed to fail policy body=[%v]", tt.body) + } } } else { require.Error(t, err, "failed to fail srt=[%s], tok=[%s]", tt.body, bearer) @@ -375,7 +396,7 @@ func Test_SignedRequestBody_When_Bad_Signature_Expect_Failure(t *testing.T) { md := metadata.New(map[string]string{"token": string(jwtWrongKey(t))}) ctx = metadata.NewIncomingContext(ctx, md) - verified, err := extractSRTBody( + verified, _, err := extractSRTBody( ctx, http.Header{}, &kaspb.RewrapRequest{ diff --git a/service/kas/kas.proto b/service/kas/kas.proto index 8f0528d05..312540360 100644 --- a/service/kas/kas.proto +++ b/service/kas/kas.proto @@ -31,6 +31,43 @@ message LegacyPublicKeyRequest { string algorithm = 1; } +message PolicyBinding { + string algorithm = 1 [json_name = "alg"]; + string hash = 2; +} + +message KeyAccess { + string encrypted_metadata = 1; + PolicyBinding policy_binding = 2; + string protocol = 3; + string key_type = 4 [json_name = "type"]; + string kas_url = 5 [json_name = "url"]; + string kid = 6; + string split_id = 7 [json_name = "sid"]; + bytes wrapped_key = 8; + // header is only used for NanoTDFs + bytes header = 9; +} + +message UnsignedRewrapRequest { + message WithPolicy { + string id = 1; + string body = 2; + } + message WithKeyAccessObject { + string key_access_object_id = 1; + KeyAccess key_access_object = 2; + } + + message WithPolicyRequest { + repeated WithKeyAccessObject key_access_objects = 1; + WithPolicy policy = 2; + string algorithm = 3; + } + + string client_public_key = 1; + repeated WithPolicyRequest requests = 2; +} message PublicKeyRequest { string algorithm = 1 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "algorithm type rsa: or ec:"}]; string fmt = 2 [(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {description: "response format"}]; @@ -48,11 +85,29 @@ message RewrapRequest { string signed_request_token = 1; } -message RewrapResponse { + +message KeyAccessRewrapResult { map metadata = 1; - bytes entity_wrapped_key = 2; + string key_access_object_id = 2; + string status = 3; + oneof result { + bytes kas_wrapped_key = 4; + string error = 5; + } +} + +message PolicyRewrapResult { + string policy_id = 1; + repeated KeyAccessRewrapResult results = 2; +} + +message RewrapResponse { + map metadata = 1 [deprecated = true]; + bytes entity_wrapped_key = 2 [deprecated = true]; string session_public_key = 3; - string schema_version = 4; + string schema_version = 4 [deprecated = true]; + // New Rewrap API changes + repeated PolicyRewrapResult responses = 5; } // Get app info from the root path diff --git a/service/rttests/rt_test.go b/service/rttests/rt_test.go index 49a43737b..f911fcfc4 100644 --- a/service/rttests/rt_test.go +++ b/service/rttests/rt_test.go @@ -123,11 +123,13 @@ func (s *RoundtripSuite) SetupSuite() { } func (s *RoundtripSuite) Tests() { + var passNames []string // success tests for i, attributes := range successAttributeSets { n := fmt.Sprintf("success roundtrip %d", i) s.Run(n, func() { filename := fmt.Sprintf("test-success-%d.tdf", i) + passNames = append(passNames, filename) plaintext := "Running a roundtrip test!" err := encrypt(s.client, s.TestConfig, plaintext, attributes, filename) s.Require().NoError(err) @@ -136,11 +138,13 @@ func (s *RoundtripSuite) Tests() { }) } + var failNames []string // failure tests for i, attributes := range failureAttributeSets { n := fmt.Sprintf("failure roundtrip %d", i) s.Run(n, func() { filename := fmt.Sprintf("test-failure-%d.tdf", i) + failNames = append(failNames, filename) plaintext := "Running a roundtrip test!" err := encrypt(s.client, s.TestConfig, plaintext, attributes, filename) s.Require().NoError(err) @@ -148,6 +152,11 @@ func (s *RoundtripSuite) Tests() { s.ErrorContains(err, "PermissionDenied") }) } + + // bulk tests + s.Run("bulk test", func() { + s.Require().NoError(bulk(s.client, passNames, failNames, "Running a roundtrip test!")) + }) } func (s *RoundtripSuite) CreateTestData() error { @@ -372,3 +381,53 @@ func decrypt(client *sdk.SDK, tdfFile string, plaintext string) error { return nil } + +func bulk(client *sdk.SDK, tdfSuccess []string, tdfFail []string, plaintext string) error { + var passTDF []*sdk.BulkTDF + for _, fileName := range tdfSuccess { + file, err := os.Open(fileName) + if err != nil { + return err + } + + defer file.Close() + + buf := new(strings.Builder) + passTDF = append(passTDF, &sdk.BulkTDF{Writer: buf, Reader: file}) + } + + var failTDF []*sdk.BulkTDF + for _, fileName := range tdfFail { + file, err := os.Open(fileName) + if err != nil { + return err + } + + defer file.Close() + + buf := new(strings.Builder) + failTDF = append(failTDF, &sdk.BulkTDF{Writer: buf, Reader: file}) + } + + _ = client.BulkDecrypt(context.Background(), sdk.WithTDFs(passTDF...), sdk.WithTDFs(failTDF...), sdk.WithTDFType(sdk.Standard)) + for _, tdf := range passTDF { + builder, ok := tdf.Writer.(*strings.Builder) + if !ok { + return fmt.Errorf("bad writer") + } + + if tdf.Error != nil { + return tdf.Error + } + if builder.String() != plaintext { + return fmt.Errorf("bulk did not equal plaintext") + } + } + for _, tdf := range failTDF { + if tdf.Error == nil { + return fmt.Errorf("no expected err") + } + } + + return nil +}