diff --git a/kms/client.go b/kms/client.go index 80548c2..68ce171 100644 --- a/kms/client.go +++ b/kms/client.go @@ -5,6 +5,7 @@ package kms import ( + "bytes" "context" "crypto/rand" "crypto/tls" @@ -149,16 +150,11 @@ func (c *Client) NodeStatus(ctx context.Context, endpoint string, _ *NodeStatusR return nil, readError(resp) } - var data pb.NodeStatusResponse + var data NodeStatusResponse if err := readResponse(resp, &data); err != nil { return nil, err } - - var stat NodeStatusResponse - if err := stat.UnmarshalPB(&data); err != nil { - return nil, err - } - return &stat, nil + return &data, nil } // Status returns status information about the entire KMS cluster. The @@ -192,16 +188,161 @@ func (c *Client) Status(ctx context.Context, _ *StatusRequest) (*StatusResponse, return nil, readError(resp) } - var data pb.StatusResponse + var data StatusResponse + if err := readResponse(resp, &data); err != nil { + return nil, err + } + return &data, nil +} + +// Encrypt encrypts a message with the key within the given enclave. +// +// It returns ErrEnclaveNotFound if no such enclave exists and +// ErrKeyNotFound if no such key exists. +func (c *Client) Encrypt(ctx context.Context, enclave, key string, req *EncryptRequest) (*EncryptResponse, error) { + const ( + Method = http.MethodPut + Path = api.PathSecretKeyEncrypt + StatusOK = http.StatusOK + ContentType = headers.ContentTypeAppAny // accept JSON or protobuf + ) + + body, err := pb.Marshal(req) + if err != nil { + return nil, err + } + + url, err := c.lb.URL(Path, key) + if err != nil { + return nil, err + } + r, err := http.NewRequestWithContext(ctx, Method, url, bytes.NewReader(body)) + if err != nil { + return nil, err + } + r.Header.Set(headers.Accept, ContentType) + r.Header.Set(headers.Enclave, enclave) + + resp, err := c.client.Do(r) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != StatusOK { + return nil, readError(resp) + } + + var data EncryptResponse + if err := readResponse(resp, &data); err != nil { + return nil, err + } + return &data, nil +} + +// Decrypt decrypts a ciphertext with the key within the given enclave. +// +// It returns ErrEnclaveNotFound if no such enclave exists and +// ErrKeyNotFound if no such key exists. +func (c *Client) Decrypt(ctx context.Context, enclave, key string, req *DecryptRequest) (*DecryptResponse, error) { + const ( + Method = http.MethodPut + Path = api.PathSecretKeyDecrypt + StatusOK = http.StatusOK + ContentType = headers.ContentTypeAppAny // accept JSON or protobuf + ) + + body, err := pb.Marshal(req) + if err != nil { + return nil, err + } + + url, err := c.lb.URL(Path, key) + if err != nil { + return nil, err + } + r, err := http.NewRequestWithContext(ctx, Method, url, bytes.NewReader(body)) + if err != nil { + return nil, err + } + r.Header.Set(headers.Accept, ContentType) + r.Header.Set(headers.Enclave, enclave) + + resp, err := c.client.Do(r) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != StatusOK { + return nil, readError(resp) + } + + var data DecryptResponse if err := readResponse(resp, &data); err != nil { return nil, err } + return &data, nil +} - var stat StatusResponse - if err := stat.UnmarshalPB(&data); err != nil { +// GenerateKey generates a new unique data encryption key. The returned +// GenerateKeyResponse contains a plaintext data encryption key with +// the requested length and a ciphertext version. The ciphertext +// is the plaintext data encryption key encrypted with the key within +// the given enclave. +// +// Applications should use, but never store, the plaintext data encryption +// key for cryptographic operations and remember the ciphertext version of +// the data encryption key. For example, encrypt a file with the plaintext +// data encryption key and store the ciphertext version of data encryption +// key alongside the encrypted file. +// The plaintext data encryption key can be obtained by decrypting the +// ciphertext data encryption key using Decrypt. +// +// Applications should also persist the key version that is used to prepare +// for future key rotation. +// +// It returns ErrEnclaveNotFound if no such enclave exists and +// ErrKeyNotFound if no such key exists. +func (c *Client) GenerateKey(ctx context.Context, enclave, key string, req *GenerateKeyRequest) (*GenerateKeyResponse, error) { + const ( + Method = http.MethodPut + Path = api.PathSecretKeyGenerate + StatusOK = http.StatusOK + ContentType = headers.ContentTypeAppAny // accept JSON or protobuf + ) + + body, err := pb.Marshal(req) + if err != nil { + return nil, err + } + + url, err := c.lb.URL(Path, key) + if err != nil { + return nil, err + } + r, err := http.NewRequestWithContext(ctx, Method, url, bytes.NewReader(body)) + if err != nil { + return nil, err + } + r.Header.Set(headers.Accept, ContentType) + r.Header.Set(headers.Enclave, enclave) + + resp, err := c.client.Do(r) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != StatusOK { + return nil, readError(resp) + } + + var data GenerateKeyResponse + if err := readResponse(resp, &data); err != nil { return nil, err } - return &stat, nil + return &data, nil } // httpsURL turns the endpoint into an HTTPS endpoint. diff --git a/kms/error.go b/kms/error.go index 3d2b029..585a6ca 100644 --- a/kms/error.go +++ b/kms/error.go @@ -27,6 +27,14 @@ var ( // an enclave that does not exist. For example, when trying to // create a key in a non-existing enclave. ErrEnclaveNotFound = Error{http.StatusNotFound, "enclave does not exist"} + + // ErrKeyExists is returned when trying to create a key in an + // enclave that already contains a key with the same name. + ErrKeyExists = Error{http.StatusConflict, "key already exists"} + + // ErrKeyNotFound is returned when trying to use a key that + // that does not exist. + ErrKeyNotFound = Error{http.StatusNotFound, "key does not exist"} ) // Error is a KMS API error. diff --git a/kms/internal/api/api.go b/kms/internal/api/api.go index 22db5b2..fa81ee4 100644 --- a/kms/internal/api/api.go +++ b/kms/internal/api/api.go @@ -7,5 +7,9 @@ package api const ( PathStatus = "/v1/status" + PathSecretKeyGenerate = "/v1/key/generate/" + PathSecretKeyEncrypt = "/v1/key/encrypt/" + PathSecretKeyDecrypt = "/v1/key/decrypt/" + PathClusterStatus = "/v1/cluster/status" ) diff --git a/kms/protobuf/proto.go b/kms/protobuf/proto.go index 46b869f..d870cb9 100644 --- a/kms/protobuf/proto.go +++ b/kms/protobuf/proto.go @@ -7,10 +7,81 @@ package protobuf import ( "time" + "google.golang.org/protobuf/proto" pbd "google.golang.org/protobuf/types/known/durationpb" pbt "google.golang.org/protobuf/types/known/timestamppb" ) +// Marshaler is an interface implemented by types that +// know how to marshal themselves into their protobuf +// representation T. +type Marshaler[T proto.Message] interface { + MarshalPB(T) error +} + +// Unmarshaler is an interface implemented by types that +// know how to unmarshal themselves from their protobuf +// representation T. +type Unmarshaler[T proto.Message] interface { + UnmarshalPB(T) error +} + +// Marshal returns v's protobuf binary data by first converting +// v into its protobuf representation type M and then marshaling +// M into the protobuf wire format. +func Marshal[M any, P Pointer[M], T Marshaler[P]](v T) ([]byte, error) { + var m M + if err := v.MarshalPB(&m); err != nil { + return nil, err + } + + var p P = &m + return proto.Marshal(p) +} + +// Unmarshal unmarshales v from b by first decoding b into v's +// protobuf representation M before converting M to v. It returns +// an error if b is not a valid protobuf representation of v. +func Unmarshal[M any, P Pointer[M], T Unmarshaler[P]](b []byte, v T) error { + var m M + var p P = &m + if err := proto.Unmarshal(b, p); err != nil { + return err + } + return v.UnmarshalPB(p) +} + +// Pointer is a type constraint used to express that some +// type P is a pointer of some other type T such that: +// +// var t T +// var p P = &t +// +// This proposition is useful when unmarshaling data into types +// without additional dynamic dispatch or heap allocations. +// +// A generic function that wants to use the default value of +// some type T but also wants to call pointer receiver methods +// on instances of T has to have two type parameters: +// +// func foo[T any, P pointer[T]]() { +// var t T +// var p P = &t +// } +// +// This functionality cannot be achieved with a single type +// parameter because: +// +// func foo[T proto.Message]() { +// var t T // compiles but t is nil if T is a pointer type +// var t2 T = *new(T) // compiles but t2 is nil if T is a pointer type +// var t3 = T{} // compiler error - e.g. T may be a pointer type +// } +type Pointer[M any] interface { + proto.Message + *M // Anything implementing Pointer must also be a pointer type of M +} + // Time returns a new protobuf timestamp from the given t. func Time(t time.Time) *pbt.Timestamp { return pbt.New(t) } diff --git a/kms/protobuf/request.pb.go b/kms/protobuf/request.pb.go new file mode 100644 index 0000000..1cfff27 --- /dev/null +++ b/kms/protobuf/request.pb.go @@ -0,0 +1,368 @@ +// Copyright 2023 - MinIO, Inc. All rights reserved. +// Use of this source code is governed by the AGPLv3 +// license that can be found in the LICENSE file. + +// Generate the Go protobuf code by running the protobuf compiler +// from the repository root: +// +// $ protoc -I=./kms/protobuf --go_out=. ./kms/protobuf/*.proto + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc v4.25.1 +// source: request.proto + +package protobuf + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type EncryptRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Version identifies the key version within the key ring used to encrypt + // the plaintext. If not present, the latest key version is used. + Version *uint32 `protobuf:"varint,1,opt,name=Version,json=version,proto3,oneof" json:"Version,omitempty"` + // Plaintext is the plain message that is encrypted. + Plaintext []byte `protobuf:"bytes,2,opt,name=Plaintext,json=plaintext,proto3" json:"Plaintext,omitempty"` + // AssociatedData is additional data that is not encrypted but crypto. bound + // to the ciphertext. The same associated data must be provided when decrypting + // the ciphertext. + // + // Associated data should describe the context of the plaintext data. For example, + // the name of the file that gets encrypted. + AssociatedData []byte `protobuf:"bytes,3,opt,name=AssociatedData,json=associated_data,proto3" json:"AssociatedData,omitempty"` +} + +func (x *EncryptRequest) Reset() { + *x = EncryptRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_request_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EncryptRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EncryptRequest) ProtoMessage() {} + +func (x *EncryptRequest) ProtoReflect() protoreflect.Message { + mi := &file_request_proto_msgTypes[0] + 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 EncryptRequest.ProtoReflect.Descriptor instead. +func (*EncryptRequest) Descriptor() ([]byte, []int) { + return file_request_proto_rawDescGZIP(), []int{0} +} + +func (x *EncryptRequest) GetVersion() uint32 { + if x != nil && x.Version != nil { + return *x.Version + } + return 0 +} + +func (x *EncryptRequest) GetPlaintext() []byte { + if x != nil { + return x.Plaintext + } + return nil +} + +func (x *EncryptRequest) GetAssociatedData() []byte { + if x != nil { + return x.AssociatedData + } + return nil +} + +type GenerateKeyRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Version identifies the key version within the key ring used to generate + // the data encryption key. If not present, the latest key version is used. + Version *uint32 `protobuf:"varint,1,opt,name=Version,json=version,proto3,oneof" json:"Version,omitempty"` + // AssociatedData is additional data that is not encrypted but crypto. bound + // to the ciphertext of the data encryption key. The same associated data must + // be provided when decrypting the ciphertext. + // + // Associated data should describe the context within the data encryption key + // is used. For example, the name of the file that gets encrypted with the + // data encryption key. + AssociatedData []byte `protobuf:"bytes,2,opt,name=AssociatedData,json=associated_data,proto3" json:"AssociatedData,omitempty"` + // Length is the length of the generated plaintext data encryption key in bytes. + // At most 1024 (8192 bits). If not present, defaults to 32 (256 bits). + Length *uint32 `protobuf:"varint,3,opt,name=Length,json=length,proto3,oneof" json:"Length,omitempty"` +} + +func (x *GenerateKeyRequest) Reset() { + *x = GenerateKeyRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_request_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GenerateKeyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GenerateKeyRequest) ProtoMessage() {} + +func (x *GenerateKeyRequest) ProtoReflect() protoreflect.Message { + mi := &file_request_proto_msgTypes[1] + 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 GenerateKeyRequest.ProtoReflect.Descriptor instead. +func (*GenerateKeyRequest) Descriptor() ([]byte, []int) { + return file_request_proto_rawDescGZIP(), []int{1} +} + +func (x *GenerateKeyRequest) GetVersion() uint32 { + if x != nil && x.Version != nil { + return *x.Version + } + return 0 +} + +func (x *GenerateKeyRequest) GetAssociatedData() []byte { + if x != nil { + return x.AssociatedData + } + return nil +} + +func (x *GenerateKeyRequest) GetLength() uint32 { + if x != nil && x.Length != nil { + return *x.Length + } + return 0 +} + +type DecryptRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Version identifies the key version within the key ring that should be + // used to decrypt the ciphertext. + Version uint32 `protobuf:"varint,1,opt,name=Version,json=version,proto3" json:"Version,omitempty"` + // Ciphertext is the encrypted message that gets decrypted. + Ciphertext []byte `protobuf:"bytes,2,opt,name=Ciphertext,json=ciphertext,proto3" json:"Ciphertext,omitempty"` + // AssociatedData is additional data that has been crypto. bound to the + // ciphertext. + AssociatedData []byte `protobuf:"bytes,3,opt,name=AssociatedData,json=associated_data,proto3" json:"AssociatedData,omitempty"` +} + +func (x *DecryptRequest) Reset() { + *x = DecryptRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_request_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DecryptRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DecryptRequest) ProtoMessage() {} + +func (x *DecryptRequest) ProtoReflect() protoreflect.Message { + mi := &file_request_proto_msgTypes[2] + 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 DecryptRequest.ProtoReflect.Descriptor instead. +func (*DecryptRequest) Descriptor() ([]byte, []int) { + return file_request_proto_rawDescGZIP(), []int{2} +} + +func (x *DecryptRequest) GetVersion() uint32 { + if x != nil { + return x.Version + } + return 0 +} + +func (x *DecryptRequest) GetCiphertext() []byte { + if x != nil { + return x.Ciphertext + } + return nil +} + +func (x *DecryptRequest) GetAssociatedData() []byte { + if x != nil { + return x.AssociatedData + } + return nil +} + +var File_request_proto protoreflect.FileDescriptor + +var file_request_proto_rawDesc = []byte{ + 0x0a, 0x0d, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x09, 0x6d, 0x69, 0x6e, 0x69, 0x6f, 0x2e, 0x6b, 0x6d, 0x73, 0x22, 0x82, 0x01, 0x0a, 0x0e, 0x45, + 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, + 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, + 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x1c, 0x0a, 0x09, + 0x50, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x09, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x12, 0x27, 0x0a, 0x0e, 0x41, 0x73, + 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x0f, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x64, + 0x61, 0x74, 0x61, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, + 0x90, 0x01, 0x0a, 0x12, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x27, 0x0a, 0x0e, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, + 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x61, + 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1b, + 0x0a, 0x06, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x01, + 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x4c, 0x65, 0x6e, 0x67, + 0x74, 0x68, 0x22, 0x73, 0x0a, 0x0e, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, + 0x0a, 0x0a, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x12, 0x27, + 0x0a, 0x0e, 0x41, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69, 0x61, 0x74, + 0x65, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x42, 0x0e, 0x5a, 0x0c, 0x6b, 0x6d, 0x73, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_request_proto_rawDescOnce sync.Once + file_request_proto_rawDescData = file_request_proto_rawDesc +) + +func file_request_proto_rawDescGZIP() []byte { + file_request_proto_rawDescOnce.Do(func() { + file_request_proto_rawDescData = protoimpl.X.CompressGZIP(file_request_proto_rawDescData) + }) + return file_request_proto_rawDescData +} + +var file_request_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_request_proto_goTypes = []interface{}{ + (*EncryptRequest)(nil), // 0: minio.kms.EncryptRequest + (*GenerateKeyRequest)(nil), // 1: minio.kms.GenerateKeyRequest + (*DecryptRequest)(nil), // 2: minio.kms.DecryptRequest +} +var file_request_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_request_proto_init() } +func file_request_proto_init() { + if File_request_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_request_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EncryptRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_request_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GenerateKeyRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_request_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DecryptRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_request_proto_msgTypes[0].OneofWrappers = []interface{}{} + file_request_proto_msgTypes[1].OneofWrappers = []interface{}{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_request_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_request_proto_goTypes, + DependencyIndexes: file_request_proto_depIdxs, + MessageInfos: file_request_proto_msgTypes, + }.Build() + File_request_proto = out.File + file_request_proto_rawDesc = nil + file_request_proto_goTypes = nil + file_request_proto_depIdxs = nil +} diff --git a/kms/protobuf/request.proto b/kms/protobuf/request.proto new file mode 100644 index 0000000..e7b9d7a --- /dev/null +++ b/kms/protobuf/request.proto @@ -0,0 +1,63 @@ +// Copyright 2023 - MinIO, Inc. All rights reserved. +// Use of this source code is governed by the AGPLv3 +// license that can be found in the LICENSE file. + +// Generate the Go protobuf code by running the protobuf compiler +// from the repository root: +// +// $ protoc -I=./kms/protobuf --go_out=. ./kms/protobuf/*.proto + +syntax = "proto3"; + +package minio.kms; + +option go_package = "kms/protobuf"; + +message EncryptRequest { + // Version identifies the key version within the key ring used to encrypt + // the plaintext. If not present, the latest key version is used. + optional uint32 Version = 1 [ json_name = "version" ]; + + // Plaintext is the plain message that is encrypted. + bytes Plaintext = 2 [ json_name = "plaintext" ]; + + // AssociatedData is additional data that is not encrypted but crypto. bound + // to the ciphertext. The same associated data must be provided when decrypting + // the ciphertext. + // + // Associated data should describe the context of the plaintext data. For example, + // the name of the file that gets encrypted. + bytes AssociatedData = 3 [ json_name = "associated_data" ]; +} + +message GenerateKeyRequest { + // Version identifies the key version within the key ring used to generate + // the data encryption key. If not present, the latest key version is used. + optional uint32 Version = 1 [ json_name = "version" ]; + + // AssociatedData is additional data that is not encrypted but crypto. bound + // to the ciphertext of the data encryption key. The same associated data must + // be provided when decrypting the ciphertext. + // + // Associated data should describe the context within the data encryption key + // is used. For example, the name of the file that gets encrypted with the + // data encryption key. + bytes AssociatedData = 2 [ json_name = "associated_data" ]; + + // Length is the length of the generated plaintext data encryption key in bytes. + // At most 1024 (8192 bits). If not present, defaults to 32 (256 bits). + optional uint32 Length = 3 [ json_name = "length"]; +} + +message DecryptRequest { + // Version identifies the key version within the key ring that should be + // used to decrypt the ciphertext. + uint32 Version = 1 [ json_name = "version" ]; + + // Ciphertext is the encrypted message that gets decrypted. + bytes Ciphertext = 2 [ json_name = "ciphertext" ]; + + // AssociatedData is additional data that has been crypto. bound to the + // ciphertext. + bytes AssociatedData = 3 [ json_name = "associated_data" ]; +} diff --git a/kms/protobuf/response.pb.go b/kms/protobuf/response.pb.go index ca5b192..3a43382 100644 --- a/kms/protobuf/response.pb.go +++ b/kms/protobuf/response.pb.go @@ -19,7 +19,6 @@ import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" durationpb "google.golang.org/protobuf/types/known/durationpb" - _ "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" sync "sync" ) @@ -83,23 +82,68 @@ type NodeStatusResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Version string `protobuf:"bytes,1,opt,name=Version,json=version,proto3" json:"Version,omitempty"` - Addr string `protobuf:"bytes,2,opt,name=Addr,json=address,proto3" json:"Addr,omitempty"` - State string `protobuf:"bytes,3,opt,name=State,json=state,proto3" json:"State,omitempty"` - Commit uint64 `protobuf:"varint,4,opt,name=Commit,json=commit,proto3" json:"Commit,omitempty"` - Nodes map[uint32]string `protobuf:"bytes,5,rep,name=Nodes,json=nodes,proto3" json:"Nodes,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - ID uint32 `protobuf:"varint,6,opt,name=ID,json=node_id,proto3" json:"ID,omitempty"` - LeaderID int64 `protobuf:"zigzag64,7,opt,name=LeaderID,json=leader_id,proto3" json:"LeaderID,omitempty"` - LastHeartbeat *durationpb.Duration `protobuf:"bytes,8,opt,name=LastHeartbeat,json=last_heartbeat,proto3" json:"LastHeartbeat,omitempty"` - HeartbeatInterval *durationpb.Duration `protobuf:"bytes,9,opt,name=HeartbeatInterval,json=heartbeat_interval,proto3" json:"HeartbeatInterval,omitempty"` - ElectionTimeout *durationpb.Duration `protobuf:"bytes,10,opt,name=ElectionTimeout,json=election_timeout,proto3" json:"ElectionTimeout,omitempty"` - UpTime *durationpb.Duration `protobuf:"bytes,11,opt,name=UpTime,json=sys_uptime,proto3" json:"UpTime,omitempty"` - OS string `protobuf:"bytes,12,opt,name=OS,json=sys_os,proto3" json:"OS,omitempty"` - Arch string `protobuf:"bytes,13,opt,name=Arch,json=sys_cpu_arch,proto3" json:"Arch,omitempty"` - CPUs uint32 `protobuf:"varint,14,opt,name=CPUs,json=sys_cpu_num,proto3" json:"CPUs,omitempty"` - UsableCPUs uint32 `protobuf:"varint,15,opt,name=UsableCPUs,json=sys_cpu_used,proto3" json:"UsableCPUs,omitempty"` - HeapMemInUse uint64 `protobuf:"varint,16,opt,name=HeapMemInUse,json=sys_mem_heap_used,proto3" json:"HeapMemInUse,omitempty"` - StackMemInUse uint64 `protobuf:"varint,17,opt,name=StackMemInUse,json=sys_mem_stack_used,proto3" json:"StackMemInUse,omitempty"` + // Version is the version of the KMS server. It's the timestamp of + // the latest commit formatted as 'yyyy-mm-ddThh-mm-ssZ'. For example, + // "2023-12-01T16-06-52Z" + Version string `protobuf:"bytes,1,opt,name=Version,json=version,proto3" json:"Version,omitempty"` + // APIVersion is the API version supported by the KMS server. + // For example, "v1". + APIVersion string `protobuf:"bytes,2,opt,name=APIVersion,json=api_version,proto3" json:"APIVersion,omitempty"` + // Addr is the KMS server address as 'host' or 'host:port'. + Addr string `protobuf:"bytes,3,opt,name=Addr,json=address,proto3" json:"Addr,omitempty"` + // Role is the current role the KMS server node has within the cluster. + // Either, "Leader", "Follower" or "Candidate". + Role string `protobuf:"bytes,4,opt,name=Role,json=role,proto3" json:"Role,omitempty"` + // Commit is the number of state changes applied to this KMS server. + Commit uint64 `protobuf:"varint,5,opt,name=Commit,json=commit,proto3" json:"Commit,omitempty"` + // Nodes is a list of KMS server nodes within the KMS cluster as a map + // of node IDs to KMS server addresses of the form 'host' or 'host:port'. + Nodes map[uint32]string `protobuf:"bytes,6,rep,name=Nodes,json=nodes,proto3" json:"Nodes,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // ID is the node ID of this KMS server. It only changes if the node + // joins a cluster. + ID uint32 `protobuf:"varint,7,opt,name=ID,json=node_id,proto3" json:"ID,omitempty"` + // LeaderID is the ID of the current cluster leader or negative if + // the cluster has no leader. + LeaderID int64 `protobuf:"zigzag64,8,opt,name=LeaderID,json=leader_id,proto3" json:"LeaderID,omitempty"` + // LastHeartbeat is the duration since the KMS server has sent or received + // a heartbeat. As long as there is a cluster leader, it should be lower + // than the ElectionTimeout. + LastHeartbeat *durationpb.Duration `protobuf:"bytes,9,opt,name=LastHeartbeat,json=last_heartbeat,proto3" json:"LastHeartbeat,omitempty"` + // HeartbeatInterval defines the frequency in which this KMS server, as cluster + // leader, sends heartbeats to its follower nodes. All nodes within a cluster + // should use the same heartbeat interval. + HeartbeatInterval *durationpb.Duration `protobuf:"bytes,10,opt,name=HeartbeatInterval,json=heartbeat_interval,proto3" json:"HeartbeatInterval,omitempty"` + // ElectionTimeout defines how long a KMS server node waits for heartbeats before + // it considers the cluster leaders as down and starts a leader election to become + // the cluster leader itself. + // Each cluster node should have a slightly different election timeout to avoid + // spliting votes. Typically, base election timeout + random jitter. The average + // or base election timeout of all cluster nodes should be balanced with the + // HeartbeatInterval to prevent nodes from starting elections even though a leader + // is present. A reasonable default may be ElectionTimeout = 3 * HeartbeatInterval. + ElectionTimeout *durationpb.Duration `protobuf:"bytes,11,opt,name=ElectionTimeout,json=election_timeout,proto3" json:"ElectionTimeout,omitempty"` + // UpTime is the amount of time the KMS server is up and running. + UpTime *durationpb.Duration `protobuf:"bytes,12,opt,name=UpTime,json=sys_uptime,proto3" json:"UpTime,omitempty"` + // OS identifies the operating system the KMS server is running on. + // For example, "linux" or "darwin". + OS string `protobuf:"bytes,13,opt,name=OS,json=sys_os,proto3" json:"OS,omitempty"` + // Arch is the CPU architecture of the KMS server. For example, "amd64". + Arch string `protobuf:"bytes,14,opt,name=Arch,json=sys_cpu_arch,proto3" json:"Arch,omitempty"` + // CPUs is the number of logical CPUs that can execite the KMS server process. + // However, the KMS server may not use all of these CPUs. It might be limited + // to fewer CPUs. + CPUs uint32 `protobuf:"varint,15,opt,name=CPUs,json=sys_cpu_num,proto3" json:"CPUs,omitempty"` + // UsableCPUs is the number of CPUs actually used by the KMS server process. + // Unless the KMS server has been limited to fewer CPUs, equal to CPUs field. + UsableCPUs uint32 `protobuf:"varint,16,opt,name=UsableCPUs,json=sys_cpu_used,proto3" json:"UsableCPUs,omitempty"` + // HeapMemInUse is the amount of heap memory currently occupied by the KMS server. + // The total amount of memory used by the KMS server process is HeapMemInUse + + // StackMemInUse. + HeapMemInUse uint64 `protobuf:"varint,17,opt,name=HeapMemInUse,json=sys_mem_heap_used,proto3" json:"HeapMemInUse,omitempty"` + // StackMemInUse is the amount of stack memory currently occupied by the KMS server. + // The total amount of memory used by the KMS server process is HeapMemInUse + + // StackMemInUse. + StackMemInUse uint64 `protobuf:"varint,18,opt,name=StackMemInUse,json=sys_mem_stack_used,proto3" json:"StackMemInUse,omitempty"` } func (x *NodeStatusResponse) Reset() { @@ -141,6 +185,13 @@ func (x *NodeStatusResponse) GetVersion() string { return "" } +func (x *NodeStatusResponse) GetAPIVersion() string { + if x != nil { + return x.APIVersion + } + return "" +} + func (x *NodeStatusResponse) GetAddr() string { if x != nil { return x.Addr @@ -148,9 +199,9 @@ func (x *NodeStatusResponse) GetAddr() string { return "" } -func (x *NodeStatusResponse) GetState() string { +func (x *NodeStatusResponse) GetRole() string { if x != nil { - return x.State + return x.Role } return "" } @@ -308,86 +359,275 @@ func (x *StatusResponse) GetNodesDown() map[uint32]string { return nil } +type EncryptResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Version identifies the particular key within a key ring used to encrypt + // the message. + Version uint32 `protobuf:"varint,1,opt,name=Version,json=version,proto3" json:"Version,omitempty"` + // Ciphertext is the encrypted message. + Ciphertext []byte `protobuf:"bytes,2,opt,name=Ciphertext,json=ciphertext,proto3" json:"Ciphertext,omitempty"` +} + +func (x *EncryptResponse) Reset() { + *x = EncryptResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_response_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EncryptResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EncryptResponse) ProtoMessage() {} + +func (x *EncryptResponse) ProtoReflect() protoreflect.Message { + mi := &file_response_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 EncryptResponse.ProtoReflect.Descriptor instead. +func (*EncryptResponse) Descriptor() ([]byte, []int) { + return file_response_proto_rawDescGZIP(), []int{3} +} + +func (x *EncryptResponse) GetVersion() uint32 { + if x != nil { + return x.Version + } + return 0 +} + +func (x *EncryptResponse) GetCiphertext() []byte { + if x != nil { + return x.Ciphertext + } + return nil +} + +type DecryptResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Plaintext is the decrypted message. + Plaintext []byte `protobuf:"bytes,1,opt,name=Plaintext,json=plaintext,proto3" json:"Plaintext,omitempty"` +} + +func (x *DecryptResponse) Reset() { + *x = DecryptResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_response_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DecryptResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DecryptResponse) ProtoMessage() {} + +func (x *DecryptResponse) ProtoReflect() protoreflect.Message { + mi := &file_response_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 DecryptResponse.ProtoReflect.Descriptor instead. +func (*DecryptResponse) Descriptor() ([]byte, []int) { + return file_response_proto_rawDescGZIP(), []int{4} +} + +func (x *DecryptResponse) GetPlaintext() []byte { + if x != nil { + return x.Plaintext + } + return nil +} + +type GenerateKeyResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Version identifies the particular key within a key ring used to generate + // and encrypt this data encryption key. + Version uint32 `protobuf:"varint,1,opt,name=Version,json=version,proto3" json:"Version,omitempty"` + // Plaintext is the plain data encryption key. It may be used by clients to + // perform crypto. operations. + Plaintext []byte `protobuf:"bytes,2,opt,name=Plaintext,json=plaintext,proto3" json:"Plaintext,omitempty"` + // Ciphertext is the encrypted data encryption key. Clients should store it + // to obtain the plain data encryption key in the future again. + Ciphertext []byte `protobuf:"bytes,3,opt,name=Ciphertext,json=ciphertext,proto3" json:"Ciphertext,omitempty"` +} + +func (x *GenerateKeyResponse) Reset() { + *x = GenerateKeyResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_response_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GenerateKeyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GenerateKeyResponse) ProtoMessage() {} + +func (x *GenerateKeyResponse) ProtoReflect() protoreflect.Message { + mi := &file_response_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 GenerateKeyResponse.ProtoReflect.Descriptor instead. +func (*GenerateKeyResponse) Descriptor() ([]byte, []int) { + return file_response_proto_rawDescGZIP(), []int{5} +} + +func (x *GenerateKeyResponse) GetVersion() uint32 { + if x != nil { + return x.Version + } + return 0 +} + +func (x *GenerateKeyResponse) GetPlaintext() []byte { + if x != nil { + return x.Plaintext + } + return nil +} + +func (x *GenerateKeyResponse) GetCiphertext() []byte { + if x != nil { + return x.Ciphertext + } + return nil +} + var File_response_proto protoreflect.FileDescriptor var file_response_proto_rawDesc = []byte{ 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x12, 0x09, 0x6d, 0x69, 0x6e, 0x69, 0x6f, 0x2e, 0x6b, 0x6d, 0x73, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x27, 0x0a, 0x0b, - 0x45, 0x72, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xe9, 0x05, 0x0a, 0x12, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, - 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x15, 0x0a, 0x04, 0x41, 0x64, 0x64, 0x72, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x14, 0x0a, - 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, - 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x12, 0x3e, 0x0a, 0x05, 0x4e, - 0x6f, 0x64, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x6d, 0x69, 0x6e, - 0x69, 0x6f, 0x2e, 0x6b, 0x6d, 0x73, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x13, 0x0a, 0x02, 0x49, - 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, - 0x12, 0x1b, 0x0a, 0x08, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x49, 0x44, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x12, 0x52, 0x09, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x12, 0x40, 0x0a, - 0x0d, 0x4c, 0x61, 0x73, 0x74, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x0e, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x12, - 0x48, 0x0a, 0x11, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x49, 0x6e, 0x74, 0x65, - 0x72, 0x76, 0x61, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x12, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, - 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x44, 0x0a, 0x0f, 0x45, 0x6c, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x0a, 0x20, 0x01, + 0x12, 0x09, 0x6d, 0x69, 0x6e, 0x69, 0x6f, 0x2e, 0x6b, 0x6d, 0x73, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x27, 0x0a, 0x0b, 0x45, + 0x72, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x22, 0x88, 0x06, 0x0a, 0x12, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x56, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0a, 0x41, 0x50, 0x49, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x70, 0x69, 0x5f, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x15, 0x0a, 0x04, 0x41, 0x64, 0x64, 0x72, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, + 0x04, 0x52, 0x6f, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x6f, 0x6c, + 0x65, 0x12, 0x16, 0x0a, 0x06, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x12, 0x3e, 0x0a, 0x05, 0x4e, 0x6f, 0x64, + 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x6d, 0x69, 0x6e, 0x69, 0x6f, + 0x2e, 0x6b, 0x6d, 0x73, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x13, 0x0a, 0x02, 0x49, 0x44, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x12, 0x1b, + 0x0a, 0x08, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x49, 0x44, 0x18, 0x08, 0x20, 0x01, 0x28, 0x12, + 0x52, 0x09, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x12, 0x40, 0x0a, 0x0d, 0x4c, + 0x61, 0x73, 0x74, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x65, - 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, - 0x35, 0x0a, 0x06, 0x55, 0x70, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x73, 0x79, 0x73, 0x5f, - 0x75, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x02, 0x4f, 0x53, 0x18, 0x0c, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x73, 0x79, 0x73, 0x5f, 0x6f, 0x73, 0x12, 0x1a, 0x0a, 0x04, 0x41, 0x72, - 0x63, 0x68, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x79, 0x73, 0x5f, 0x63, 0x70, - 0x75, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x12, 0x19, 0x0a, 0x04, 0x43, 0x50, 0x55, 0x73, 0x18, 0x0e, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x73, 0x79, 0x73, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x6e, 0x75, - 0x6d, 0x12, 0x20, 0x0a, 0x0a, 0x55, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x50, 0x55, 0x73, 0x18, - 0x0f, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x73, 0x79, 0x73, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x75, - 0x73, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x70, 0x4d, 0x65, 0x6d, 0x49, 0x6e, - 0x55, 0x73, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x73, 0x79, 0x73, 0x5f, 0x6d, - 0x65, 0x6d, 0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x12, 0x29, 0x0a, 0x0d, - 0x53, 0x74, 0x61, 0x63, 0x6b, 0x4d, 0x65, 0x6d, 0x49, 0x6e, 0x55, 0x73, 0x65, 0x18, 0x11, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x12, 0x73, 0x79, 0x73, 0x5f, 0x6d, 0x65, 0x6d, 0x5f, 0x73, 0x74, 0x61, - 0x63, 0x6b, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x1a, 0x38, 0x0a, 0x0a, 0x4e, 0x6f, 0x64, 0x65, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x22, 0xb5, 0x02, 0x0a, 0x0e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x55, 0x70, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x6d, 0x69, 0x6e, 0x69, 0x6f, 0x2e, 0x6b, 0x6d, - 0x73, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x55, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6e, - 0x6f, 0x64, 0x65, 0x73, 0x5f, 0x75, 0x70, 0x12, 0x47, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x73, - 0x44, 0x6f, 0x77, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x6d, 0x69, 0x6e, - 0x69, 0x6f, 0x2e, 0x6b, 0x6d, 0x73, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x44, 0x6f, 0x77, 0x6e, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x5f, 0x64, 0x6f, 0x77, 0x6e, - 0x1a, 0x59, 0x0a, 0x0c, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x55, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x33, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1d, 0x2e, 0x6d, 0x69, 0x6e, 0x69, 0x6f, 0x2e, 0x6b, 0x6d, 0x73, 0x2e, 0x4e, 0x6f, - 0x64, 0x65, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3c, 0x0a, 0x0e, 0x4e, - 0x6f, 0x64, 0x65, 0x73, 0x44, 0x6f, 0x77, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, - 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, - 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x0e, 0x5a, 0x0c, 0x6b, 0x6d, 0x73, - 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x6c, + 0x61, 0x73, 0x74, 0x5f, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x12, 0x48, 0x0a, + 0x11, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, + 0x61, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x12, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x5f, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x44, 0x0a, 0x0f, 0x45, 0x6c, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x65, 0x6c, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x35, 0x0a, + 0x06, 0x55, 0x70, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x73, 0x79, 0x73, 0x5f, 0x75, 0x70, + 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x02, 0x4f, 0x53, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x73, 0x79, 0x73, 0x5f, 0x6f, 0x73, 0x12, 0x1a, 0x0a, 0x04, 0x41, 0x72, 0x63, 0x68, + 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x79, 0x73, 0x5f, 0x63, 0x70, 0x75, 0x5f, + 0x61, 0x72, 0x63, 0x68, 0x12, 0x19, 0x0a, 0x04, 0x43, 0x50, 0x55, 0x73, 0x18, 0x0f, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x0b, 0x73, 0x79, 0x73, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x6e, 0x75, 0x6d, 0x12, + 0x20, 0x0a, 0x0a, 0x55, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x50, 0x55, 0x73, 0x18, 0x10, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x73, 0x79, 0x73, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x75, 0x73, 0x65, + 0x64, 0x12, 0x27, 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x70, 0x4d, 0x65, 0x6d, 0x49, 0x6e, 0x55, 0x73, + 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x73, 0x79, 0x73, 0x5f, 0x6d, 0x65, 0x6d, + 0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x12, 0x29, 0x0a, 0x0d, 0x53, 0x74, + 0x61, 0x63, 0x6b, 0x4d, 0x65, 0x6d, 0x49, 0x6e, 0x55, 0x73, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x12, 0x73, 0x79, 0x73, 0x5f, 0x6d, 0x65, 0x6d, 0x5f, 0x73, 0x74, 0x61, 0x63, 0x6b, + 0x5f, 0x75, 0x73, 0x65, 0x64, 0x1a, 0x38, 0x0a, 0x0a, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, + 0xb5, 0x02, 0x0a, 0x0e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x41, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x55, 0x70, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x6d, 0x69, 0x6e, 0x69, 0x6f, 0x2e, 0x6b, 0x6d, 0x73, 0x2e, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4e, + 0x6f, 0x64, 0x65, 0x73, 0x55, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6e, 0x6f, 0x64, + 0x65, 0x73, 0x5f, 0x75, 0x70, 0x12, 0x47, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x44, 0x6f, + 0x77, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x6d, 0x69, 0x6e, 0x69, 0x6f, + 0x2e, 0x6b, 0x6d, 0x73, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x44, 0x6f, 0x77, 0x6e, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x0a, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x1a, 0x59, + 0x0a, 0x0c, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x55, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x33, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1d, 0x2e, 0x6d, 0x69, 0x6e, 0x69, 0x6f, 0x2e, 0x6b, 0x6d, 0x73, 0x2e, 0x4e, 0x6f, 0x64, 0x65, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3c, 0x0a, 0x0e, 0x4e, 0x6f, 0x64, + 0x65, 0x73, 0x44, 0x6f, 0x77, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x4b, 0x0a, 0x0f, 0x45, 0x6e, 0x63, 0x72, 0x79, + 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, + 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, + 0x74, 0x65, 0x78, 0x74, 0x22, 0x2f, 0x0a, 0x0f, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x50, 0x6c, 0x61, 0x69, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x6c, 0x61, 0x69, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x6d, 0x0a, 0x13, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, + 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x50, 0x6c, 0x61, 0x69, 0x6e, 0x74, + 0x65, 0x78, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x6c, 0x61, 0x69, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, + 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, + 0x74, 0x65, 0x78, 0x74, 0x42, 0x0e, 0x5a, 0x0c, 0x6b, 0x6d, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -402,24 +642,27 @@ func file_response_proto_rawDescGZIP() []byte { return file_response_proto_rawDescData } -var file_response_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_response_proto_msgTypes = make([]protoimpl.MessageInfo, 9) var file_response_proto_goTypes = []interface{}{ (*ErrResponse)(nil), // 0: minio.kms.ErrResponse (*NodeStatusResponse)(nil), // 1: minio.kms.NodeStatusResponse (*StatusResponse)(nil), // 2: minio.kms.StatusResponse - nil, // 3: minio.kms.NodeStatusResponse.NodesEntry - nil, // 4: minio.kms.StatusResponse.NodesUpEntry - nil, // 5: minio.kms.StatusResponse.NodesDownEntry - (*durationpb.Duration)(nil), // 6: google.protobuf.Duration + (*EncryptResponse)(nil), // 3: minio.kms.EncryptResponse + (*DecryptResponse)(nil), // 4: minio.kms.DecryptResponse + (*GenerateKeyResponse)(nil), // 5: minio.kms.GenerateKeyResponse + nil, // 6: minio.kms.NodeStatusResponse.NodesEntry + nil, // 7: minio.kms.StatusResponse.NodesUpEntry + nil, // 8: minio.kms.StatusResponse.NodesDownEntry + (*durationpb.Duration)(nil), // 9: google.protobuf.Duration } var file_response_proto_depIdxs = []int32{ - 3, // 0: minio.kms.NodeStatusResponse.Nodes:type_name -> minio.kms.NodeStatusResponse.NodesEntry - 6, // 1: minio.kms.NodeStatusResponse.LastHeartbeat:type_name -> google.protobuf.Duration - 6, // 2: minio.kms.NodeStatusResponse.HeartbeatInterval:type_name -> google.protobuf.Duration - 6, // 3: minio.kms.NodeStatusResponse.ElectionTimeout:type_name -> google.protobuf.Duration - 6, // 4: minio.kms.NodeStatusResponse.UpTime:type_name -> google.protobuf.Duration - 4, // 5: minio.kms.StatusResponse.nodesUp:type_name -> minio.kms.StatusResponse.NodesUpEntry - 5, // 6: minio.kms.StatusResponse.nodesDown:type_name -> minio.kms.StatusResponse.NodesDownEntry + 6, // 0: minio.kms.NodeStatusResponse.Nodes:type_name -> minio.kms.NodeStatusResponse.NodesEntry + 9, // 1: minio.kms.NodeStatusResponse.LastHeartbeat:type_name -> google.protobuf.Duration + 9, // 2: minio.kms.NodeStatusResponse.HeartbeatInterval:type_name -> google.protobuf.Duration + 9, // 3: minio.kms.NodeStatusResponse.ElectionTimeout:type_name -> google.protobuf.Duration + 9, // 4: minio.kms.NodeStatusResponse.UpTime:type_name -> google.protobuf.Duration + 7, // 5: minio.kms.StatusResponse.nodesUp:type_name -> minio.kms.StatusResponse.NodesUpEntry + 8, // 6: minio.kms.StatusResponse.nodesDown:type_name -> minio.kms.StatusResponse.NodesDownEntry 1, // 7: minio.kms.StatusResponse.NodesUpEntry.value:type_name -> minio.kms.NodeStatusResponse 8, // [8:8] is the sub-list for method output_type 8, // [8:8] is the sub-list for method input_type @@ -470,6 +713,42 @@ func file_response_proto_init() { return nil } } + file_response_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EncryptResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_response_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DecryptResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_response_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GenerateKeyResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -477,7 +756,7 @@ func file_response_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_response_proto_rawDesc, NumEnums: 0, - NumMessages: 6, + NumMessages: 9, NumExtensions: 0, NumServices: 0, }, diff --git a/kms/protobuf/response.proto b/kms/protobuf/response.proto index 7ef938a..72abc06 100644 --- a/kms/protobuf/response.proto +++ b/kms/protobuf/response.proto @@ -2,7 +2,6 @@ // Use of this source code is governed by the AGPLv3 // license that can be found in the LICENSE file. - // Generate the Go protobuf code by running the protobuf compiler // from the repository root: // @@ -12,36 +11,126 @@ syntax = "proto3"; package minio.kms; -import "google/protobuf/timestamp.proto"; import "google/protobuf/duration.proto"; option go_package = "kms/protobuf"; message ErrResponse { - string Message = 1 [json_name="message" ]; + string Message = 1 [json_name="message" ]; } message NodeStatusResponse { - string Version = 1 [ json_name="version" ]; - string Addr = 2 [ json_name="address" ]; - string State = 3 [ json_name="state" ]; - uint64 Commit = 4 [ json_name="commit" ]; - map Nodes = 5 [ json_name="nodes" ]; - uint32 ID = 6 [ json_name="node_id" ]; - sint64 LeaderID = 7 [ json_name="leader_id" ]; - google.protobuf.Duration LastHeartbeat = 8 [ json_name="last_heartbeat" ]; - google.protobuf.Duration HeartbeatInterval = 9 [ json_name="heartbeat_interval" ]; - google.protobuf.Duration ElectionTimeout = 10 [ json_name="election_timeout" ]; - google.protobuf.Duration UpTime = 11 [ json_name="sys_uptime" ]; - string OS = 12 [ json_name="sys_os" ]; - string Arch = 13 [ json_name="sys_cpu_arch" ]; - uint32 CPUs = 14 [ json_name="sys_cpu_num" ]; - uint32 UsableCPUs = 15 [ json_name="sys_cpu_used" ]; - uint64 HeapMemInUse = 16 [ json_name="sys_mem_heap_used" ]; - uint64 StackMemInUse = 17 [ json_name="sys_mem_stack_used" ]; + // Version is the version of the KMS server. It's the timestamp of + // the latest commit formatted as 'yyyy-mm-ddThh-mm-ssZ'. For example, + // "2023-12-01T16-06-52Z" + string Version = 1 [ json_name="version" ]; + + // APIVersion is the API version supported by the KMS server. + // For example, "v1". + string APIVersion = 2 [ json_name="api_version" ]; + + // Addr is the KMS server address as 'host' or 'host:port'. + string Addr = 3 [ json_name="address" ]; + + // Role is the current role the KMS server node has within the cluster. + // Either, "Leader", "Follower" or "Candidate". + string Role = 4 [ json_name="role" ]; + + // Commit is the number of state changes applied to this KMS server. + uint64 Commit = 5 [ json_name="commit" ]; + + // Nodes is a list of KMS server nodes within the KMS cluster as a map + // of node IDs to KMS server addresses of the form 'host' or 'host:port'. + map Nodes = 6 [ json_name="nodes" ]; + + // ID is the node ID of this KMS server. It only changes if the node + // joins a cluster. + uint32 ID = 7 [ json_name="node_id" ]; + + // LeaderID is the ID of the current cluster leader or negative if + // the cluster has no leader. + sint64 LeaderID = 8 [ json_name="leader_id" ]; + + // LastHeartbeat is the duration since the KMS server has sent or received + // a heartbeat. As long as there is a cluster leader, it should be lower + // than the ElectionTimeout. + google.protobuf.Duration LastHeartbeat = 9 [ json_name="last_heartbeat" ]; + + // HeartbeatInterval defines the frequency in which this KMS server, as cluster + // leader, sends heartbeats to its follower nodes. All nodes within a cluster + // should use the same heartbeat interval. + google.protobuf.Duration HeartbeatInterval = 10 [ json_name="heartbeat_interval" ]; + + // ElectionTimeout defines how long a KMS server node waits for heartbeats before + // it considers the cluster leaders as down and starts a leader election to become + // the cluster leader itself. + // Each cluster node should have a slightly different election timeout to avoid + // spliting votes. Typically, base election timeout + random jitter. The average + // or base election timeout of all cluster nodes should be balanced with the + // HeartbeatInterval to prevent nodes from starting elections even though a leader + // is present. A reasonable default may be ElectionTimeout = 3 * HeartbeatInterval. + google.protobuf.Duration ElectionTimeout = 11 [ json_name="election_timeout" ]; + + // UpTime is the amount of time the KMS server is up and running. + google.protobuf.Duration UpTime = 12 [ json_name="sys_uptime" ]; + + // OS identifies the operating system the KMS server is running on. + // For example, "linux" or "darwin". + string OS = 13 [ json_name="sys_os" ]; + + // Arch is the CPU architecture of the KMS server. For example, "amd64". + string Arch = 14 [ json_name="sys_cpu_arch" ]; + + // CPUs is the number of logical CPUs that can execite the KMS server process. + // However, the KMS server may not use all of these CPUs. It might be limited + // to fewer CPUs. + uint32 CPUs = 15 [ json_name="sys_cpu_num" ]; + + // UsableCPUs is the number of CPUs actually used by the KMS server process. + // Unless the KMS server has been limited to fewer CPUs, equal to CPUs field. + uint32 UsableCPUs = 16 [ json_name="sys_cpu_used" ]; + + // HeapMemInUse is the amount of heap memory currently occupied by the KMS server. + // The total amount of memory used by the KMS server process is HeapMemInUse + + // StackMemInUse. + uint64 HeapMemInUse = 17 [ json_name="sys_mem_heap_used" ]; + + // StackMemInUse is the amount of stack memory currently occupied by the KMS server. + // The total amount of memory used by the KMS server process is HeapMemInUse + + // StackMemInUse. + uint64 StackMemInUse = 18 [ json_name="sys_mem_stack_used" ]; } message StatusResponse { map nodesUp = 1 [ json_name="nodes_up" ]; map nodesDown = 2 [ json_name="nodes_down" ]; } + +message EncryptResponse { + // Version identifies the particular key within a key ring used to encrypt + // the message. + uint32 Version = 1 [ json_name = "version" ]; + + // Ciphertext is the encrypted message. + bytes Ciphertext = 2 [ json_name = "ciphertext" ]; +} + +message DecryptResponse { + // Plaintext is the decrypted message. + bytes Plaintext = 1 [ json_name = "plaintext" ]; +} + +message GenerateKeyResponse { + // Version identifies the particular key within a key ring used to generate + // and encrypt this data encryption key. + uint32 Version = 1 [ json_name = "version" ]; + + // Plaintext is the plain data encryption key. It may be used by clients to + // perform crypto. operations. + bytes Plaintext = 2 [ json_name = "plaintext"]; + + // Ciphertext is the encrypted data encryption key. Clients should store it + // to obtain the plain data encryption key in the future again. + bytes Ciphertext = 3 [ json_name = "ciphertext" ]; +} + diff --git a/kms/request.go b/kms/request.go index e013f28..fe48973 100644 --- a/kms/request.go +++ b/kms/request.go @@ -4,6 +4,8 @@ package kms +import pb "github.com/minio/kms-go/kms/protobuf" + // NodeStatusRequest contains options for fetching status // information for one particular KMS cluster node. type NodeStatusRequest struct{} @@ -11,3 +13,97 @@ type NodeStatusRequest struct{} // StatusRequest contains options for fetching KMS cluster // status information. type StatusRequest struct{} + +// EncryptRequest contains a plaintext message that should be encrypted and +// associated data that is crypto. bound to the resulting ciphertext. +type EncryptRequest struct { + // Plaintext is the plain message that is encrypted. + Plaintext []byte + + // AssociatedData is additional data that is not encrypted but crypto. bound + // to the ciphertext. The same associated data must be provided when decrypting + // the ciphertext. + // + // Associated data should describe the context of the plaintext data. For example, + // the name of the file that gets encrypted. + AssociatedData []byte +} + +// MarshalPB converts the EncryptRequest into its protobuf representation. +func (r *EncryptRequest) MarshalPB(v *pb.EncryptRequest) error { + v.Plaintext = r.Plaintext + v.AssociatedData = r.AssociatedData + return nil +} + +// UnmarshalPB initializes the EncryptRequest from its protobuf representation. +func (r *EncryptRequest) UnmarshalPB(v *pb.EncryptRequest) error { + r.Plaintext = v.Plaintext + r.AssociatedData = v.AssociatedData + return nil +} + +// GenerateKeyRequest contains options for generating a new unique data encryption key. +type GenerateKeyRequest struct { + // AssociatedData is additional data that is not encrypted but crypto. bound + // to the ciphertext of the data encryption key. The same associated data must + // be provided when decrypting the ciphertext. + // + // Associated data should describe the context within the data encryption key + // is used. For example, the name of the file that gets encrypted with the + // data encryption key. + AssociateData []byte + + // Length is an optional length of the generated plaintext data encryption key + // in bytes. At most 1024 (8192 bits). If <= 0, defaults to 32 (256 bits). + Length int +} + +// MarshalPB converts the GenerateKeyRequest into its protobuf representation. +func (r *GenerateKeyRequest) MarshalPB(v *pb.GenerateKeyRequest) error { + v.AssociatedData = r.AssociateData + if r.Length > 0 { + v.Length = new(uint32) + *v.Length = uint32(r.Length) + } + return nil +} + +// UnmarshalPB initializes the GenerateKeyRequest from its protobuf representation. +func (r *GenerateKeyRequest) UnmarshalPB(v *pb.GenerateKeyRequest) error { + r.AssociateData = v.AssociatedData + if v.Length != nil { + r.Length = int(*v.Length) + } + return nil +} + +// DecryptRequest contains a ciphertext message that should be decrypted. +type DecryptRequest struct { + // Version identifies the key version within the key ring that should be + // used to decrypt the ciphertext. + Version int + + // Ciphertext is the encrypted message that is decrypted. + Ciphertext []byte + + // AssociatedData is additional data that has been crypto. bound to the + // ciphertext. + AssociateData []byte +} + +// MarshalPB converts the DecryptKeyRequest into its protobuf representation. +func (r *DecryptRequest) MarshalPB(v *pb.DecryptRequest) error { + v.Version = uint32(r.Version) + v.Ciphertext = r.Ciphertext + v.AssociatedData = r.AssociateData + return nil +} + +// UnmarshalPB initializes the DecryptKeyRequest from its protobuf representation. +func (r *DecryptRequest) UnmarshalPB(v *pb.DecryptRequest) error { + r.Version = int(v.Version) + r.Ciphertext = v.Ciphertext + r.AssociateData = v.AssociatedData + return nil +} diff --git a/kms/response.go b/kms/response.go index af85ddf..68ae117 100644 --- a/kms/response.go +++ b/kms/response.go @@ -21,7 +21,7 @@ import ( // readBesponse assumes that the response body is limited // to a reasonable size. It returns an error if it cannot // determine the response content length before decoding. -func readResponse(r *http.Response, v proto.Message) error { +func readResponse[M any, P pb.Pointer[M], T pb.Unmarshaler[P]](r *http.Response, v T) error { if r.ContentLength < 0 { return Error{http.StatusLengthRequired, "request content length is negative"} } @@ -32,39 +32,113 @@ func readResponse(r *http.Response, v proto.Message) error { return err } + var m M + var p P = &m if r.Header.Get(headers.ContentType) == headers.ContentTypeBinary { - return proto.Unmarshal(buf, v) + if err := proto.Unmarshal(buf, p); err != nil { + return err + } + } else { + if err := protojson.Unmarshal(buf, p); err != nil { + return err + } } - return protojson.Unmarshal(buf, v) + return v.UnmarshalPB(p) } // NodeStatusResponse contains status information about a single // KMS cluster node. type NodeStatusResponse struct { - Version string - Endpoint string - State string - Commit uint64 - Nodes map[int]string - ID int - LeaderID int - LastHeartbeat time.Duration + // Version is the version of the KMS server. It's the timestamp of + // the latest commit formatted as 'yyyy-mm-ddThh-mm-ssZ'. For example, + // "2023-12-01T16-06-52Z" + Version string + + // APIVersion is the API version supported by the KMS server. + // For example, "v1". + APIVersion string + + // Endpoint is the KMS server endpoint as 'host' or 'host:port'. + Endpoint string + + // Role is the current role the KMS server node has within the cluster. + // Either, "Leader", "Follower" or "Candidate". + Role string + + // Commit is the number of state changes applied to this KMS server. + Commit uint64 + + // Nodes is a list of KMS server nodes within the KMS cluster as a map + // of node IDs to KMS server addresses of the form 'host' or 'host:port'. + Nodes map[int]string + + // ID is the node ID of this KMS server. It only changes if the node + // joins a cluster. + ID int + + // LeaderID is the ID of the current cluster leader or negative if + // the cluster has no leader. + LeaderID int + + // LastHeartbeat is the duration since the KMS server has sent or received + // a heartbeat. As long as there is a cluster leader, it should be lower + // than the ElectionTimeout. + LastHeartbeat time.Duration + + // HeartbeatInterval defines the frequency in which this KMS server, as cluster + // leader, sends heartbeats to its follower nodes. All nodes within a cluster + // should use the same heartbeat interval. HeartbeatInterval time.Duration - ElectionTimeout time.Duration - UpTime time.Duration - OS string - CPUArch string - CPUs uint - UsableCPUs uint - HeapMemInUse uint64 - StackMemInUse uint64 + + // ElectionTimeout defines how long a KMS server node waits for heartbeats before + // it considers the cluster leaders as down and starts a leader election to become + // the cluster leader itself. + // + // Each cluster node should have a slightly different election timeout to avoid + // spliting votes. Typically, base election timeout + random jitter. The average + // or base election timeout of all cluster nodes should be balanced with the + // HeartbeatInterval to prevent nodes from starting elections even though a leader + // is present. A reasonable default may be: + // + // ElectionTimeout = 3 * HeartbeatInterval. + ElectionTimeout time.Duration + + // UpTime is the amount of time the KMS server is up and running. + UpTime time.Duration + + // OS identifies the operating system the KMS server is running on. + // For example, "linux" or "darwin". + OS string + + // CPUArch is the CPU architecture of the KMS server. For example, "amd64". + CPUArch string + + // CPUs is the number of logical CPUs that can execite the KMS server process. + // However, the KMS server may not use all of these CPUs. It might be limited + // to fewer CPUs. + CPUs uint + + // UsableCPUs is the number of CPUs actually used by the KMS server process. + // Unless the KMS server has been limited to fewer CPUs, equal to CPUs field. + UsableCPUs uint + + // HeapMemInUse is the amount of heap memory currently occupied by the KMS server. + // The total amount of memory used by the KMS server process is HeapMemInUse + + // StackMemInUse. + HeapMemInUse uint64 + + // StackMemInUse is the amount of stack memory currently occupied by the KMS server. + // The total amount of memory used by the KMS server process is HeapMemInUse + + // StackMemInUse. + StackMemInUse uint64 } // MarshalPB converts the NodeStatusResponse into its protobuf representation. func (s *NodeStatusResponse) MarshalPB(v *pb.NodeStatusResponse) error { v.Version = s.Version + v.APIVersion = s.APIVersion v.Addr = s.Endpoint - v.State = s.State + v.Role = s.Role v.Commit = s.Commit v.Nodes = make(map[uint32]string, len(s.Nodes)) for id, node := range s.Nodes { @@ -87,8 +161,9 @@ func (s *NodeStatusResponse) MarshalPB(v *pb.NodeStatusResponse) error { // UnmarshalPB initializes the NodeStatusResponse from its protobuf representation. func (s *NodeStatusResponse) UnmarshalPB(v *pb.NodeStatusResponse) error { s.Version = v.Version + s.APIVersion = v.APIVersion s.Endpoint = v.Addr - s.State = v.State + s.Role = v.Role s.Commit = v.Commit s.Nodes = make(map[int]string, len(v.Nodes)) for id, node := range v.Nodes { @@ -159,3 +234,80 @@ func (s *StatusResponse) UnmarshalPB(v *pb.StatusResponse) error { } return nil } + +// EncryptResponse contains the ciphertext of an encrypted message +// and the key version used to encrypt the message. +type EncryptResponse struct { + // Version identifies the particular key within a key ring used to encrypt + // the message. + Version int + + // Ciphertext is the encrypted message. + Ciphertext []byte +} + +// MarshalPB converts the EncryptResponse into its protobuf representation. +func (r *EncryptResponse) MarshalPB(v *pb.EncryptResponse) error { + v.Version = uint32(r.Version) + v.Ciphertext = r.Ciphertext + return nil +} + +// UnmarshalPB initializes the EncryptResponse from its protobuf representation. +func (r *EncryptResponse) UnmarshalPB(v *pb.EncryptResponse) error { + r.Version = int(v.Version) + r.Ciphertext = v.Ciphertext + return nil +} + +// DecryptResponse contains the decrypted plaintext message. +type DecryptResponse struct { + // Plaintext is the decrypted message. + Plaintext []byte +} + +// MarshalPB converts the DecryptResponse into its protobuf representation. +func (r *DecryptResponse) MarshalPB(v *pb.DecryptResponse) error { + v.Plaintext = r.Plaintext + return nil +} + +// UnmarshalPB initializes the DecryptResponse from its protobuf representation. +func (r *DecryptResponse) UnmarshalPB(v *pb.DecryptResponse) error { + r.Plaintext = v.Plaintext + return nil +} + +// GenerateKeyResponse contains data encryption key that consists of a plaintext +// data encryption key and an encrypted ciphertext. Applications should use, but +// never store, the plaintext data encryption key for crypto. operations and store +// the ciphertext and key version. +type GenerateKeyResponse struct { + // Version identifies the particular key within a key ring used to generate + // and encrypt this data encryption key. + Version int + + // Plaintext is the plain data encryption key. It may be used by applications + // to perform crypto. operations. + Plaintext []byte + + // Ciphertext is the encrypted data encryption key. Applications should store + // it to obtain the plain data encryption key in the future again. + Ciphertext []byte +} + +// MarshalPB converts the GenerateKeyResponse into its protobuf representation. +func (r *GenerateKeyResponse) MarshalPB(v *pb.GenerateKeyResponse) error { + v.Version = uint32(r.Version) + v.Plaintext = r.Plaintext + v.Ciphertext = r.Ciphertext + return nil +} + +// UnmarshalPB initializes the GenerateKeyResponse from its protobuf representation. +func (r *GenerateKeyResponse) UnmarshalPB(v *pb.GenerateKeyResponse) error { + r.Version = int(v.Version) + r.Plaintext = v.Plaintext + r.Ciphertext = v.Ciphertext + return nil +}