diff --git a/cmd/metacrypt/server.go b/cmd/metacrypt/server.go index 9c4f4ad..e44ee70 100644 --- a/cmd/metacrypt/server.go +++ b/cmd/metacrypt/server.go @@ -16,6 +16,7 @@ import ( "git.wntrmute.dev/kyle/metacrypt/internal/db" "git.wntrmute.dev/kyle/metacrypt/internal/engine" "git.wntrmute.dev/kyle/metacrypt/internal/engine/ca" + "git.wntrmute.dev/kyle/metacrypt/internal/engine/transit" "git.wntrmute.dev/kyle/metacrypt/internal/grpcserver" "git.wntrmute.dev/kyle/metacrypt/internal/policy" "git.wntrmute.dev/kyle/metacrypt/internal/seal" @@ -74,6 +75,7 @@ func runServer(cmd *cobra.Command, args []string) error { policyEngine := policy.NewEngine(b) engineRegistry := engine.NewRegistry(b, logger) engineRegistry.RegisterFactory(engine.EngineTypeCA, ca.NewCAEngine) + engineRegistry.RegisterFactory(engine.EngineTypeTransit, transit.NewTransitEngine) srv := server.New(cfg, sealMgr, authenticator, policyEngine, engineRegistry, logger, version) grpcSrv := grpcserver.New(cfg, sealMgr, authenticator, policyEngine, engineRegistry, logger) diff --git a/gen/metacrypt/v2/transit.pb.go b/gen/metacrypt/v2/transit.pb.go new file mode 100644 index 0000000..8bfec8a --- /dev/null +++ b/gen/metacrypt/v2/transit.pb.go @@ -0,0 +1,2197 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc v3.20.3 +// source: proto/metacrypt/v2/transit.proto + +package metacryptv2 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +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 CreateTransitKeyRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Mount string `protobuf:"bytes,1,opt,name=mount,proto3" json:"mount,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + // type is the key algorithm: aes256-gcm, chacha20-poly, ed25519, + // ecdsa-p256, ecdsa-p384, hmac-sha256, hmac-sha512. + // Defaults to aes256-gcm if empty. + Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateTransitKeyRequest) Reset() { + *x = CreateTransitKeyRequest{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateTransitKeyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateTransitKeyRequest) ProtoMessage() {} + +func (x *CreateTransitKeyRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateTransitKeyRequest.ProtoReflect.Descriptor instead. +func (*CreateTransitKeyRequest) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{0} +} + +func (x *CreateTransitKeyRequest) GetMount() string { + if x != nil { + return x.Mount + } + return "" +} + +func (x *CreateTransitKeyRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *CreateTransitKeyRequest) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +type CreateTransitKeyResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` + Version int32 `protobuf:"varint,3,opt,name=version,proto3" json:"version,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateTransitKeyResponse) Reset() { + *x = CreateTransitKeyResponse{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateTransitKeyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateTransitKeyResponse) ProtoMessage() {} + +func (x *CreateTransitKeyResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateTransitKeyResponse.ProtoReflect.Descriptor instead. +func (*CreateTransitKeyResponse) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{1} +} + +func (x *CreateTransitKeyResponse) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *CreateTransitKeyResponse) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *CreateTransitKeyResponse) GetVersion() int32 { + if x != nil { + return x.Version + } + return 0 +} + +type DeleteTransitKeyRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Mount string `protobuf:"bytes,1,opt,name=mount,proto3" json:"mount,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeleteTransitKeyRequest) Reset() { + *x = DeleteTransitKeyRequest{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteTransitKeyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteTransitKeyRequest) ProtoMessage() {} + +func (x *DeleteTransitKeyRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteTransitKeyRequest.ProtoReflect.Descriptor instead. +func (*DeleteTransitKeyRequest) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{2} +} + +func (x *DeleteTransitKeyRequest) GetMount() string { + if x != nil { + return x.Mount + } + return "" +} + +func (x *DeleteTransitKeyRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type DeleteTransitKeyResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeleteTransitKeyResponse) Reset() { + *x = DeleteTransitKeyResponse{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteTransitKeyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteTransitKeyResponse) ProtoMessage() {} + +func (x *DeleteTransitKeyResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteTransitKeyResponse.ProtoReflect.Descriptor instead. +func (*DeleteTransitKeyResponse) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{3} +} + +type GetTransitKeyRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Mount string `protobuf:"bytes,1,opt,name=mount,proto3" json:"mount,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetTransitKeyRequest) Reset() { + *x = GetTransitKeyRequest{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetTransitKeyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetTransitKeyRequest) ProtoMessage() {} + +func (x *GetTransitKeyRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetTransitKeyRequest.ProtoReflect.Descriptor instead. +func (*GetTransitKeyRequest) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{4} +} + +func (x *GetTransitKeyRequest) GetMount() string { + if x != nil { + return x.Mount + } + return "" +} + +func (x *GetTransitKeyRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type GetTransitKeyResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` + CurrentVersion int32 `protobuf:"varint,3,opt,name=current_version,json=currentVersion,proto3" json:"current_version,omitempty"` + MinDecryptionVersion int32 `protobuf:"varint,4,opt,name=min_decryption_version,json=minDecryptionVersion,proto3" json:"min_decryption_version,omitempty"` + AllowDeletion bool `protobuf:"varint,5,opt,name=allow_deletion,json=allowDeletion,proto3" json:"allow_deletion,omitempty"` + Versions []int32 `protobuf:"varint,6,rep,packed,name=versions,proto3" json:"versions,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetTransitKeyResponse) Reset() { + *x = GetTransitKeyResponse{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetTransitKeyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetTransitKeyResponse) ProtoMessage() {} + +func (x *GetTransitKeyResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetTransitKeyResponse.ProtoReflect.Descriptor instead. +func (*GetTransitKeyResponse) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{5} +} + +func (x *GetTransitKeyResponse) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *GetTransitKeyResponse) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *GetTransitKeyResponse) GetCurrentVersion() int32 { + if x != nil { + return x.CurrentVersion + } + return 0 +} + +func (x *GetTransitKeyResponse) GetMinDecryptionVersion() int32 { + if x != nil { + return x.MinDecryptionVersion + } + return 0 +} + +func (x *GetTransitKeyResponse) GetAllowDeletion() bool { + if x != nil { + return x.AllowDeletion + } + return false +} + +func (x *GetTransitKeyResponse) GetVersions() []int32 { + if x != nil { + return x.Versions + } + return nil +} + +type ListTransitKeysRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Mount string `protobuf:"bytes,1,opt,name=mount,proto3" json:"mount,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListTransitKeysRequest) Reset() { + *x = ListTransitKeysRequest{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListTransitKeysRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListTransitKeysRequest) ProtoMessage() {} + +func (x *ListTransitKeysRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListTransitKeysRequest.ProtoReflect.Descriptor instead. +func (*ListTransitKeysRequest) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{6} +} + +func (x *ListTransitKeysRequest) GetMount() string { + if x != nil { + return x.Mount + } + return "" +} + +type ListTransitKeysResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Keys []string `protobuf:"bytes,1,rep,name=keys,proto3" json:"keys,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListTransitKeysResponse) Reset() { + *x = ListTransitKeysResponse{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListTransitKeysResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListTransitKeysResponse) ProtoMessage() {} + +func (x *ListTransitKeysResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListTransitKeysResponse.ProtoReflect.Descriptor instead. +func (*ListTransitKeysResponse) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{7} +} + +func (x *ListTransitKeysResponse) GetKeys() []string { + if x != nil { + return x.Keys + } + return nil +} + +type RotateTransitKeyRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Mount string `protobuf:"bytes,1,opt,name=mount,proto3" json:"mount,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RotateTransitKeyRequest) Reset() { + *x = RotateTransitKeyRequest{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RotateTransitKeyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RotateTransitKeyRequest) ProtoMessage() {} + +func (x *RotateTransitKeyRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RotateTransitKeyRequest.ProtoReflect.Descriptor instead. +func (*RotateTransitKeyRequest) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{8} +} + +func (x *RotateTransitKeyRequest) GetMount() string { + if x != nil { + return x.Mount + } + return "" +} + +func (x *RotateTransitKeyRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type RotateTransitKeyResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Version int32 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RotateTransitKeyResponse) Reset() { + *x = RotateTransitKeyResponse{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RotateTransitKeyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RotateTransitKeyResponse) ProtoMessage() {} + +func (x *RotateTransitKeyResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RotateTransitKeyResponse.ProtoReflect.Descriptor instead. +func (*RotateTransitKeyResponse) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{9} +} + +func (x *RotateTransitKeyResponse) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *RotateTransitKeyResponse) GetVersion() int32 { + if x != nil { + return x.Version + } + return 0 +} + +type UpdateTransitKeyConfigRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Mount string `protobuf:"bytes,1,opt,name=mount,proto3" json:"mount,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + MinDecryptionVersion int32 `protobuf:"varint,3,opt,name=min_decryption_version,json=minDecryptionVersion,proto3" json:"min_decryption_version,omitempty"` + AllowDeletion bool `protobuf:"varint,4,opt,name=allow_deletion,json=allowDeletion,proto3" json:"allow_deletion,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateTransitKeyConfigRequest) Reset() { + *x = UpdateTransitKeyConfigRequest{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateTransitKeyConfigRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateTransitKeyConfigRequest) ProtoMessage() {} + +func (x *UpdateTransitKeyConfigRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[10] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateTransitKeyConfigRequest.ProtoReflect.Descriptor instead. +func (*UpdateTransitKeyConfigRequest) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{10} +} + +func (x *UpdateTransitKeyConfigRequest) GetMount() string { + if x != nil { + return x.Mount + } + return "" +} + +func (x *UpdateTransitKeyConfigRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *UpdateTransitKeyConfigRequest) GetMinDecryptionVersion() int32 { + if x != nil { + return x.MinDecryptionVersion + } + return 0 +} + +func (x *UpdateTransitKeyConfigRequest) GetAllowDeletion() bool { + if x != nil { + return x.AllowDeletion + } + return false +} + +type UpdateTransitKeyConfigResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateTransitKeyConfigResponse) Reset() { + *x = UpdateTransitKeyConfigResponse{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateTransitKeyConfigResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateTransitKeyConfigResponse) ProtoMessage() {} + +func (x *UpdateTransitKeyConfigResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[11] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateTransitKeyConfigResponse.ProtoReflect.Descriptor instead. +func (*UpdateTransitKeyConfigResponse) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{11} +} + +type TrimTransitKeyRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Mount string `protobuf:"bytes,1,opt,name=mount,proto3" json:"mount,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TrimTransitKeyRequest) Reset() { + *x = TrimTransitKeyRequest{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TrimTransitKeyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TrimTransitKeyRequest) ProtoMessage() {} + +func (x *TrimTransitKeyRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[12] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TrimTransitKeyRequest.ProtoReflect.Descriptor instead. +func (*TrimTransitKeyRequest) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{12} +} + +func (x *TrimTransitKeyRequest) GetMount() string { + if x != nil { + return x.Mount + } + return "" +} + +func (x *TrimTransitKeyRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type TrimTransitKeyResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Trimmed int32 `protobuf:"varint,1,opt,name=trimmed,proto3" json:"trimmed,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TrimTransitKeyResponse) Reset() { + *x = TrimTransitKeyResponse{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TrimTransitKeyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TrimTransitKeyResponse) ProtoMessage() {} + +func (x *TrimTransitKeyResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[13] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TrimTransitKeyResponse.ProtoReflect.Descriptor instead. +func (*TrimTransitKeyResponse) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{13} +} + +func (x *TrimTransitKeyResponse) GetTrimmed() int32 { + if x != nil { + return x.Trimmed + } + return 0 +} + +type TransitEncryptRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Mount string `protobuf:"bytes,1,opt,name=mount,proto3" json:"mount,omitempty"` + Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` + // plaintext is base64-encoded data to encrypt. + Plaintext string `protobuf:"bytes,3,opt,name=plaintext,proto3" json:"plaintext,omitempty"` + // context is optional base64-encoded additional authenticated data (AAD). + Context string `protobuf:"bytes,4,opt,name=context,proto3" json:"context,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TransitEncryptRequest) Reset() { + *x = TransitEncryptRequest{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TransitEncryptRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TransitEncryptRequest) ProtoMessage() {} + +func (x *TransitEncryptRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[14] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TransitEncryptRequest.ProtoReflect.Descriptor instead. +func (*TransitEncryptRequest) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{14} +} + +func (x *TransitEncryptRequest) GetMount() string { + if x != nil { + return x.Mount + } + return "" +} + +func (x *TransitEncryptRequest) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *TransitEncryptRequest) GetPlaintext() string { + if x != nil { + return x.Plaintext + } + return "" +} + +func (x *TransitEncryptRequest) GetContext() string { + if x != nil { + return x.Context + } + return "" +} + +type TransitEncryptResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // ciphertext in format "metacrypt:v{version}:{base64(nonce+ciphertext+tag)}" + Ciphertext string `protobuf:"bytes,1,opt,name=ciphertext,proto3" json:"ciphertext,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TransitEncryptResponse) Reset() { + *x = TransitEncryptResponse{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TransitEncryptResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TransitEncryptResponse) ProtoMessage() {} + +func (x *TransitEncryptResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[15] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TransitEncryptResponse.ProtoReflect.Descriptor instead. +func (*TransitEncryptResponse) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{15} +} + +func (x *TransitEncryptResponse) GetCiphertext() string { + if x != nil { + return x.Ciphertext + } + return "" +} + +type TransitDecryptRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Mount string `protobuf:"bytes,1,opt,name=mount,proto3" json:"mount,omitempty"` + Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` + Ciphertext string `protobuf:"bytes,3,opt,name=ciphertext,proto3" json:"ciphertext,omitempty"` + Context string `protobuf:"bytes,4,opt,name=context,proto3" json:"context,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TransitDecryptRequest) Reset() { + *x = TransitDecryptRequest{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TransitDecryptRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TransitDecryptRequest) ProtoMessage() {} + +func (x *TransitDecryptRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[16] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TransitDecryptRequest.ProtoReflect.Descriptor instead. +func (*TransitDecryptRequest) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{16} +} + +func (x *TransitDecryptRequest) GetMount() string { + if x != nil { + return x.Mount + } + return "" +} + +func (x *TransitDecryptRequest) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *TransitDecryptRequest) GetCiphertext() string { + if x != nil { + return x.Ciphertext + } + return "" +} + +func (x *TransitDecryptRequest) GetContext() string { + if x != nil { + return x.Context + } + return "" +} + +type TransitDecryptResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // plaintext is base64-encoded decrypted data. + Plaintext string `protobuf:"bytes,1,opt,name=plaintext,proto3" json:"plaintext,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TransitDecryptResponse) Reset() { + *x = TransitDecryptResponse{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TransitDecryptResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TransitDecryptResponse) ProtoMessage() {} + +func (x *TransitDecryptResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[17] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TransitDecryptResponse.ProtoReflect.Descriptor instead. +func (*TransitDecryptResponse) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{17} +} + +func (x *TransitDecryptResponse) GetPlaintext() string { + if x != nil { + return x.Plaintext + } + return "" +} + +type TransitRewrapRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Mount string `protobuf:"bytes,1,opt,name=mount,proto3" json:"mount,omitempty"` + Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` + Ciphertext string `protobuf:"bytes,3,opt,name=ciphertext,proto3" json:"ciphertext,omitempty"` + Context string `protobuf:"bytes,4,opt,name=context,proto3" json:"context,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TransitRewrapRequest) Reset() { + *x = TransitRewrapRequest{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TransitRewrapRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TransitRewrapRequest) ProtoMessage() {} + +func (x *TransitRewrapRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[18] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TransitRewrapRequest.ProtoReflect.Descriptor instead. +func (*TransitRewrapRequest) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{18} +} + +func (x *TransitRewrapRequest) GetMount() string { + if x != nil { + return x.Mount + } + return "" +} + +func (x *TransitRewrapRequest) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *TransitRewrapRequest) GetCiphertext() string { + if x != nil { + return x.Ciphertext + } + return "" +} + +func (x *TransitRewrapRequest) GetContext() string { + if x != nil { + return x.Context + } + return "" +} + +type TransitRewrapResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Ciphertext string `protobuf:"bytes,1,opt,name=ciphertext,proto3" json:"ciphertext,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TransitRewrapResponse) Reset() { + *x = TransitRewrapResponse{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TransitRewrapResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TransitRewrapResponse) ProtoMessage() {} + +func (x *TransitRewrapResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[19] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TransitRewrapResponse.ProtoReflect.Descriptor instead. +func (*TransitRewrapResponse) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{19} +} + +func (x *TransitRewrapResponse) GetCiphertext() string { + if x != nil { + return x.Ciphertext + } + return "" +} + +type TransitBatchItem struct { + state protoimpl.MessageState `protogen:"open.v1"` + Plaintext string `protobuf:"bytes,1,opt,name=plaintext,proto3" json:"plaintext,omitempty"` + Ciphertext string `protobuf:"bytes,2,opt,name=ciphertext,proto3" json:"ciphertext,omitempty"` + Context string `protobuf:"bytes,3,opt,name=context,proto3" json:"context,omitempty"` + Reference string `protobuf:"bytes,4,opt,name=reference,proto3" json:"reference,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TransitBatchItem) Reset() { + *x = TransitBatchItem{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TransitBatchItem) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TransitBatchItem) ProtoMessage() {} + +func (x *TransitBatchItem) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[20] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TransitBatchItem.ProtoReflect.Descriptor instead. +func (*TransitBatchItem) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{20} +} + +func (x *TransitBatchItem) GetPlaintext() string { + if x != nil { + return x.Plaintext + } + return "" +} + +func (x *TransitBatchItem) GetCiphertext() string { + if x != nil { + return x.Ciphertext + } + return "" +} + +func (x *TransitBatchItem) GetContext() string { + if x != nil { + return x.Context + } + return "" +} + +func (x *TransitBatchItem) GetReference() string { + if x != nil { + return x.Reference + } + return "" +} + +type TransitBatchResultItem struct { + state protoimpl.MessageState `protogen:"open.v1"` + Plaintext string `protobuf:"bytes,1,opt,name=plaintext,proto3" json:"plaintext,omitempty"` + Ciphertext string `protobuf:"bytes,2,opt,name=ciphertext,proto3" json:"ciphertext,omitempty"` + Reference string `protobuf:"bytes,3,opt,name=reference,proto3" json:"reference,omitempty"` + Error string `protobuf:"bytes,4,opt,name=error,proto3" json:"error,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TransitBatchResultItem) Reset() { + *x = TransitBatchResultItem{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TransitBatchResultItem) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TransitBatchResultItem) ProtoMessage() {} + +func (x *TransitBatchResultItem) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[21] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TransitBatchResultItem.ProtoReflect.Descriptor instead. +func (*TransitBatchResultItem) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{21} +} + +func (x *TransitBatchResultItem) GetPlaintext() string { + if x != nil { + return x.Plaintext + } + return "" +} + +func (x *TransitBatchResultItem) GetCiphertext() string { + if x != nil { + return x.Ciphertext + } + return "" +} + +func (x *TransitBatchResultItem) GetReference() string { + if x != nil { + return x.Reference + } + return "" +} + +func (x *TransitBatchResultItem) GetError() string { + if x != nil { + return x.Error + } + return "" +} + +type TransitBatchEncryptRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Mount string `protobuf:"bytes,1,opt,name=mount,proto3" json:"mount,omitempty"` + Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` + Items []*TransitBatchItem `protobuf:"bytes,3,rep,name=items,proto3" json:"items,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TransitBatchEncryptRequest) Reset() { + *x = TransitBatchEncryptRequest{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TransitBatchEncryptRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TransitBatchEncryptRequest) ProtoMessage() {} + +func (x *TransitBatchEncryptRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[22] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TransitBatchEncryptRequest.ProtoReflect.Descriptor instead. +func (*TransitBatchEncryptRequest) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{22} +} + +func (x *TransitBatchEncryptRequest) GetMount() string { + if x != nil { + return x.Mount + } + return "" +} + +func (x *TransitBatchEncryptRequest) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *TransitBatchEncryptRequest) GetItems() []*TransitBatchItem { + if x != nil { + return x.Items + } + return nil +} + +type TransitBatchDecryptRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Mount string `protobuf:"bytes,1,opt,name=mount,proto3" json:"mount,omitempty"` + Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` + Items []*TransitBatchItem `protobuf:"bytes,3,rep,name=items,proto3" json:"items,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TransitBatchDecryptRequest) Reset() { + *x = TransitBatchDecryptRequest{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TransitBatchDecryptRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TransitBatchDecryptRequest) ProtoMessage() {} + +func (x *TransitBatchDecryptRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[23] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TransitBatchDecryptRequest.ProtoReflect.Descriptor instead. +func (*TransitBatchDecryptRequest) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{23} +} + +func (x *TransitBatchDecryptRequest) GetMount() string { + if x != nil { + return x.Mount + } + return "" +} + +func (x *TransitBatchDecryptRequest) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *TransitBatchDecryptRequest) GetItems() []*TransitBatchItem { + if x != nil { + return x.Items + } + return nil +} + +type TransitBatchRewrapRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Mount string `protobuf:"bytes,1,opt,name=mount,proto3" json:"mount,omitempty"` + Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` + Items []*TransitBatchItem `protobuf:"bytes,3,rep,name=items,proto3" json:"items,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TransitBatchRewrapRequest) Reset() { + *x = TransitBatchRewrapRequest{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TransitBatchRewrapRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TransitBatchRewrapRequest) ProtoMessage() {} + +func (x *TransitBatchRewrapRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[24] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TransitBatchRewrapRequest.ProtoReflect.Descriptor instead. +func (*TransitBatchRewrapRequest) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{24} +} + +func (x *TransitBatchRewrapRequest) GetMount() string { + if x != nil { + return x.Mount + } + return "" +} + +func (x *TransitBatchRewrapRequest) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *TransitBatchRewrapRequest) GetItems() []*TransitBatchItem { + if x != nil { + return x.Items + } + return nil +} + +type TransitBatchResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Results []*TransitBatchResultItem `protobuf:"bytes,1,rep,name=results,proto3" json:"results,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TransitBatchResponse) Reset() { + *x = TransitBatchResponse{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TransitBatchResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TransitBatchResponse) ProtoMessage() {} + +func (x *TransitBatchResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[25] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TransitBatchResponse.ProtoReflect.Descriptor instead. +func (*TransitBatchResponse) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{25} +} + +func (x *TransitBatchResponse) GetResults() []*TransitBatchResultItem { + if x != nil { + return x.Results + } + return nil +} + +type TransitSignRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Mount string `protobuf:"bytes,1,opt,name=mount,proto3" json:"mount,omitempty"` + Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` + // input is base64-encoded data to sign. + Input string `protobuf:"bytes,3,opt,name=input,proto3" json:"input,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TransitSignRequest) Reset() { + *x = TransitSignRequest{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TransitSignRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TransitSignRequest) ProtoMessage() {} + +func (x *TransitSignRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[26] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TransitSignRequest.ProtoReflect.Descriptor instead. +func (*TransitSignRequest) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{26} +} + +func (x *TransitSignRequest) GetMount() string { + if x != nil { + return x.Mount + } + return "" +} + +func (x *TransitSignRequest) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *TransitSignRequest) GetInput() string { + if x != nil { + return x.Input + } + return "" +} + +type TransitSignResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // signature in format "metacrypt:v{version}:{base64(signature_bytes)}" + Signature string `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TransitSignResponse) Reset() { + *x = TransitSignResponse{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[27] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TransitSignResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TransitSignResponse) ProtoMessage() {} + +func (x *TransitSignResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[27] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TransitSignResponse.ProtoReflect.Descriptor instead. +func (*TransitSignResponse) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{27} +} + +func (x *TransitSignResponse) GetSignature() string { + if x != nil { + return x.Signature + } + return "" +} + +type TransitVerifyRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Mount string `protobuf:"bytes,1,opt,name=mount,proto3" json:"mount,omitempty"` + Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` + Input string `protobuf:"bytes,3,opt,name=input,proto3" json:"input,omitempty"` + Signature string `protobuf:"bytes,4,opt,name=signature,proto3" json:"signature,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TransitVerifyRequest) Reset() { + *x = TransitVerifyRequest{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TransitVerifyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TransitVerifyRequest) ProtoMessage() {} + +func (x *TransitVerifyRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[28] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TransitVerifyRequest.ProtoReflect.Descriptor instead. +func (*TransitVerifyRequest) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{28} +} + +func (x *TransitVerifyRequest) GetMount() string { + if x != nil { + return x.Mount + } + return "" +} + +func (x *TransitVerifyRequest) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *TransitVerifyRequest) GetInput() string { + if x != nil { + return x.Input + } + return "" +} + +func (x *TransitVerifyRequest) GetSignature() string { + if x != nil { + return x.Signature + } + return "" +} + +type TransitVerifyResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Valid bool `protobuf:"varint,1,opt,name=valid,proto3" json:"valid,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TransitVerifyResponse) Reset() { + *x = TransitVerifyResponse{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TransitVerifyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TransitVerifyResponse) ProtoMessage() {} + +func (x *TransitVerifyResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[29] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TransitVerifyResponse.ProtoReflect.Descriptor instead. +func (*TransitVerifyResponse) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{29} +} + +func (x *TransitVerifyResponse) GetValid() bool { + if x != nil { + return x.Valid + } + return false +} + +type TransitHmacRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Mount string `protobuf:"bytes,1,opt,name=mount,proto3" json:"mount,omitempty"` + Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` + // input is base64-encoded data to HMAC. + Input string `protobuf:"bytes,3,opt,name=input,proto3" json:"input,omitempty"` + // hmac, if set, switches to verify mode. + Hmac string `protobuf:"bytes,4,opt,name=hmac,proto3" json:"hmac,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TransitHmacRequest) Reset() { + *x = TransitHmacRequest{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[30] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TransitHmacRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TransitHmacRequest) ProtoMessage() {} + +func (x *TransitHmacRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[30] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TransitHmacRequest.ProtoReflect.Descriptor instead. +func (*TransitHmacRequest) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{30} +} + +func (x *TransitHmacRequest) GetMount() string { + if x != nil { + return x.Mount + } + return "" +} + +func (x *TransitHmacRequest) GetKey() string { + if x != nil { + return x.Key + } + return "" +} + +func (x *TransitHmacRequest) GetInput() string { + if x != nil { + return x.Input + } + return "" +} + +func (x *TransitHmacRequest) GetHmac() string { + if x != nil { + return x.Hmac + } + return "" +} + +type TransitHmacResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // hmac is set in compute mode. + Hmac string `protobuf:"bytes,1,opt,name=hmac,proto3" json:"hmac,omitempty"` + // valid is set in verify mode. + Valid bool `protobuf:"varint,2,opt,name=valid,proto3" json:"valid,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TransitHmacResponse) Reset() { + *x = TransitHmacResponse{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[31] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TransitHmacResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TransitHmacResponse) ProtoMessage() {} + +func (x *TransitHmacResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[31] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TransitHmacResponse.ProtoReflect.Descriptor instead. +func (*TransitHmacResponse) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{31} +} + +func (x *TransitHmacResponse) GetHmac() string { + if x != nil { + return x.Hmac + } + return "" +} + +func (x *TransitHmacResponse) GetValid() bool { + if x != nil { + return x.Valid + } + return false +} + +type GetTransitPublicKeyRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Mount string `protobuf:"bytes,1,opt,name=mount,proto3" json:"mount,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Version int32 `protobuf:"varint,3,opt,name=version,proto3" json:"version,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetTransitPublicKeyRequest) Reset() { + *x = GetTransitPublicKeyRequest{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[32] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetTransitPublicKeyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetTransitPublicKeyRequest) ProtoMessage() {} + +func (x *GetTransitPublicKeyRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[32] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetTransitPublicKeyRequest.ProtoReflect.Descriptor instead. +func (*GetTransitPublicKeyRequest) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{32} +} + +func (x *GetTransitPublicKeyRequest) GetMount() string { + if x != nil { + return x.Mount + } + return "" +} + +func (x *GetTransitPublicKeyRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *GetTransitPublicKeyRequest) GetVersion() int32 { + if x != nil { + return x.Version + } + return 0 +} + +type GetTransitPublicKeyResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // public_key is base64-encoded PKIX DER public key. + PublicKey string `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` + Version int32 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"` + Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetTransitPublicKeyResponse) Reset() { + *x = GetTransitPublicKeyResponse{} + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[33] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetTransitPublicKeyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetTransitPublicKeyResponse) ProtoMessage() {} + +func (x *GetTransitPublicKeyResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_metacrypt_v2_transit_proto_msgTypes[33] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetTransitPublicKeyResponse.ProtoReflect.Descriptor instead. +func (*GetTransitPublicKeyResponse) Descriptor() ([]byte, []int) { + return file_proto_metacrypt_v2_transit_proto_rawDescGZIP(), []int{33} +} + +func (x *GetTransitPublicKeyResponse) GetPublicKey() string { + if x != nil { + return x.PublicKey + } + return "" +} + +func (x *GetTransitPublicKeyResponse) GetVersion() int32 { + if x != nil { + return x.Version + } + return 0 +} + +func (x *GetTransitPublicKeyResponse) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +var File_proto_metacrypt_v2_transit_proto protoreflect.FileDescriptor + +const file_proto_metacrypt_v2_transit_proto_rawDesc = "" + + "\n" + + " proto/metacrypt/v2/transit.proto\x12\fmetacrypt.v2\"W\n" + + "\x17CreateTransitKeyRequest\x12\x14\n" + + "\x05mount\x18\x01 \x01(\tR\x05mount\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\x12\x12\n" + + "\x04type\x18\x03 \x01(\tR\x04type\"\\\n" + + "\x18CreateTransitKeyResponse\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n" + + "\x04type\x18\x02 \x01(\tR\x04type\x12\x18\n" + + "\aversion\x18\x03 \x01(\x05R\aversion\"C\n" + + "\x17DeleteTransitKeyRequest\x12\x14\n" + + "\x05mount\x18\x01 \x01(\tR\x05mount\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\"\x1a\n" + + "\x18DeleteTransitKeyResponse\"@\n" + + "\x14GetTransitKeyRequest\x12\x14\n" + + "\x05mount\x18\x01 \x01(\tR\x05mount\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\"\xe1\x01\n" + + "\x15GetTransitKeyResponse\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n" + + "\x04type\x18\x02 \x01(\tR\x04type\x12'\n" + + "\x0fcurrent_version\x18\x03 \x01(\x05R\x0ecurrentVersion\x124\n" + + "\x16min_decryption_version\x18\x04 \x01(\x05R\x14minDecryptionVersion\x12%\n" + + "\x0eallow_deletion\x18\x05 \x01(\bR\rallowDeletion\x12\x1a\n" + + "\bversions\x18\x06 \x03(\x05R\bversions\".\n" + + "\x16ListTransitKeysRequest\x12\x14\n" + + "\x05mount\x18\x01 \x01(\tR\x05mount\"-\n" + + "\x17ListTransitKeysResponse\x12\x12\n" + + "\x04keys\x18\x01 \x03(\tR\x04keys\"C\n" + + "\x17RotateTransitKeyRequest\x12\x14\n" + + "\x05mount\x18\x01 \x01(\tR\x05mount\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\"H\n" + + "\x18RotateTransitKeyResponse\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n" + + "\aversion\x18\x02 \x01(\x05R\aversion\"\xa6\x01\n" + + "\x1dUpdateTransitKeyConfigRequest\x12\x14\n" + + "\x05mount\x18\x01 \x01(\tR\x05mount\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\x124\n" + + "\x16min_decryption_version\x18\x03 \x01(\x05R\x14minDecryptionVersion\x12%\n" + + "\x0eallow_deletion\x18\x04 \x01(\bR\rallowDeletion\" \n" + + "\x1eUpdateTransitKeyConfigResponse\"A\n" + + "\x15TrimTransitKeyRequest\x12\x14\n" + + "\x05mount\x18\x01 \x01(\tR\x05mount\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\"2\n" + + "\x16TrimTransitKeyResponse\x12\x18\n" + + "\atrimmed\x18\x01 \x01(\x05R\atrimmed\"w\n" + + "\x15TransitEncryptRequest\x12\x14\n" + + "\x05mount\x18\x01 \x01(\tR\x05mount\x12\x10\n" + + "\x03key\x18\x02 \x01(\tR\x03key\x12\x1c\n" + + "\tplaintext\x18\x03 \x01(\tR\tplaintext\x12\x18\n" + + "\acontext\x18\x04 \x01(\tR\acontext\"8\n" + + "\x16TransitEncryptResponse\x12\x1e\n" + + "\n" + + "ciphertext\x18\x01 \x01(\tR\n" + + "ciphertext\"y\n" + + "\x15TransitDecryptRequest\x12\x14\n" + + "\x05mount\x18\x01 \x01(\tR\x05mount\x12\x10\n" + + "\x03key\x18\x02 \x01(\tR\x03key\x12\x1e\n" + + "\n" + + "ciphertext\x18\x03 \x01(\tR\n" + + "ciphertext\x12\x18\n" + + "\acontext\x18\x04 \x01(\tR\acontext\"6\n" + + "\x16TransitDecryptResponse\x12\x1c\n" + + "\tplaintext\x18\x01 \x01(\tR\tplaintext\"x\n" + + "\x14TransitRewrapRequest\x12\x14\n" + + "\x05mount\x18\x01 \x01(\tR\x05mount\x12\x10\n" + + "\x03key\x18\x02 \x01(\tR\x03key\x12\x1e\n" + + "\n" + + "ciphertext\x18\x03 \x01(\tR\n" + + "ciphertext\x12\x18\n" + + "\acontext\x18\x04 \x01(\tR\acontext\"7\n" + + "\x15TransitRewrapResponse\x12\x1e\n" + + "\n" + + "ciphertext\x18\x01 \x01(\tR\n" + + "ciphertext\"\x88\x01\n" + + "\x10TransitBatchItem\x12\x1c\n" + + "\tplaintext\x18\x01 \x01(\tR\tplaintext\x12\x1e\n" + + "\n" + + "ciphertext\x18\x02 \x01(\tR\n" + + "ciphertext\x12\x18\n" + + "\acontext\x18\x03 \x01(\tR\acontext\x12\x1c\n" + + "\treference\x18\x04 \x01(\tR\treference\"\x8a\x01\n" + + "\x16TransitBatchResultItem\x12\x1c\n" + + "\tplaintext\x18\x01 \x01(\tR\tplaintext\x12\x1e\n" + + "\n" + + "ciphertext\x18\x02 \x01(\tR\n" + + "ciphertext\x12\x1c\n" + + "\treference\x18\x03 \x01(\tR\treference\x12\x14\n" + + "\x05error\x18\x04 \x01(\tR\x05error\"z\n" + + "\x1aTransitBatchEncryptRequest\x12\x14\n" + + "\x05mount\x18\x01 \x01(\tR\x05mount\x12\x10\n" + + "\x03key\x18\x02 \x01(\tR\x03key\x124\n" + + "\x05items\x18\x03 \x03(\v2\x1e.metacrypt.v2.TransitBatchItemR\x05items\"z\n" + + "\x1aTransitBatchDecryptRequest\x12\x14\n" + + "\x05mount\x18\x01 \x01(\tR\x05mount\x12\x10\n" + + "\x03key\x18\x02 \x01(\tR\x03key\x124\n" + + "\x05items\x18\x03 \x03(\v2\x1e.metacrypt.v2.TransitBatchItemR\x05items\"y\n" + + "\x19TransitBatchRewrapRequest\x12\x14\n" + + "\x05mount\x18\x01 \x01(\tR\x05mount\x12\x10\n" + + "\x03key\x18\x02 \x01(\tR\x03key\x124\n" + + "\x05items\x18\x03 \x03(\v2\x1e.metacrypt.v2.TransitBatchItemR\x05items\"V\n" + + "\x14TransitBatchResponse\x12>\n" + + "\aresults\x18\x01 \x03(\v2$.metacrypt.v2.TransitBatchResultItemR\aresults\"R\n" + + "\x12TransitSignRequest\x12\x14\n" + + "\x05mount\x18\x01 \x01(\tR\x05mount\x12\x10\n" + + "\x03key\x18\x02 \x01(\tR\x03key\x12\x14\n" + + "\x05input\x18\x03 \x01(\tR\x05input\"3\n" + + "\x13TransitSignResponse\x12\x1c\n" + + "\tsignature\x18\x01 \x01(\tR\tsignature\"r\n" + + "\x14TransitVerifyRequest\x12\x14\n" + + "\x05mount\x18\x01 \x01(\tR\x05mount\x12\x10\n" + + "\x03key\x18\x02 \x01(\tR\x03key\x12\x14\n" + + "\x05input\x18\x03 \x01(\tR\x05input\x12\x1c\n" + + "\tsignature\x18\x04 \x01(\tR\tsignature\"-\n" + + "\x15TransitVerifyResponse\x12\x14\n" + + "\x05valid\x18\x01 \x01(\bR\x05valid\"f\n" + + "\x12TransitHmacRequest\x12\x14\n" + + "\x05mount\x18\x01 \x01(\tR\x05mount\x12\x10\n" + + "\x03key\x18\x02 \x01(\tR\x03key\x12\x14\n" + + "\x05input\x18\x03 \x01(\tR\x05input\x12\x12\n" + + "\x04hmac\x18\x04 \x01(\tR\x04hmac\"?\n" + + "\x13TransitHmacResponse\x12\x12\n" + + "\x04hmac\x18\x01 \x01(\tR\x04hmac\x12\x14\n" + + "\x05valid\x18\x02 \x01(\bR\x05valid\"`\n" + + "\x1aGetTransitPublicKeyRequest\x12\x14\n" + + "\x05mount\x18\x01 \x01(\tR\x05mount\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\x12\x18\n" + + "\aversion\x18\x03 \x01(\x05R\aversion\"j\n" + + "\x1bGetTransitPublicKeyResponse\x12\x1d\n" + + "\n" + + "public_key\x18\x01 \x01(\tR\tpublicKey\x12\x18\n" + + "\aversion\x18\x02 \x01(\x05R\aversion\x12\x12\n" + + "\x04type\x18\x03 \x01(\tR\x04type2\xfd\v\n" + + "\x0eTransitService\x12Z\n" + + "\tCreateKey\x12%.metacrypt.v2.CreateTransitKeyRequest\x1a&.metacrypt.v2.CreateTransitKeyResponse\x12Z\n" + + "\tDeleteKey\x12%.metacrypt.v2.DeleteTransitKeyRequest\x1a&.metacrypt.v2.DeleteTransitKeyResponse\x12Q\n" + + "\x06GetKey\x12\".metacrypt.v2.GetTransitKeyRequest\x1a#.metacrypt.v2.GetTransitKeyResponse\x12W\n" + + "\bListKeys\x12$.metacrypt.v2.ListTransitKeysRequest\x1a%.metacrypt.v2.ListTransitKeysResponse\x12Z\n" + + "\tRotateKey\x12%.metacrypt.v2.RotateTransitKeyRequest\x1a&.metacrypt.v2.RotateTransitKeyResponse\x12l\n" + + "\x0fUpdateKeyConfig\x12+.metacrypt.v2.UpdateTransitKeyConfigRequest\x1a,.metacrypt.v2.UpdateTransitKeyConfigResponse\x12T\n" + + "\aTrimKey\x12#.metacrypt.v2.TrimTransitKeyRequest\x1a$.metacrypt.v2.TrimTransitKeyResponse\x12T\n" + + "\aEncrypt\x12#.metacrypt.v2.TransitEncryptRequest\x1a$.metacrypt.v2.TransitEncryptResponse\x12T\n" + + "\aDecrypt\x12#.metacrypt.v2.TransitDecryptRequest\x1a$.metacrypt.v2.TransitDecryptResponse\x12Q\n" + + "\x06Rewrap\x12\".metacrypt.v2.TransitRewrapRequest\x1a#.metacrypt.v2.TransitRewrapResponse\x12\\\n" + + "\fBatchEncrypt\x12(.metacrypt.v2.TransitBatchEncryptRequest\x1a\".metacrypt.v2.TransitBatchResponse\x12\\\n" + + "\fBatchDecrypt\x12(.metacrypt.v2.TransitBatchDecryptRequest\x1a\".metacrypt.v2.TransitBatchResponse\x12Z\n" + + "\vBatchRewrap\x12'.metacrypt.v2.TransitBatchRewrapRequest\x1a\".metacrypt.v2.TransitBatchResponse\x12K\n" + + "\x04Sign\x12 .metacrypt.v2.TransitSignRequest\x1a!.metacrypt.v2.TransitSignResponse\x12Q\n" + + "\x06Verify\x12\".metacrypt.v2.TransitVerifyRequest\x1a#.metacrypt.v2.TransitVerifyResponse\x12K\n" + + "\x04Hmac\x12 .metacrypt.v2.TransitHmacRequest\x1a!.metacrypt.v2.TransitHmacResponse\x12c\n" + + "\fGetPublicKey\x12(.metacrypt.v2.GetTransitPublicKeyRequest\x1a).metacrypt.v2.GetTransitPublicKeyResponseB>Z metacrypt.v2.TransitBatchItem + 20, // 1: metacrypt.v2.TransitBatchDecryptRequest.items:type_name -> metacrypt.v2.TransitBatchItem + 20, // 2: metacrypt.v2.TransitBatchRewrapRequest.items:type_name -> metacrypt.v2.TransitBatchItem + 21, // 3: metacrypt.v2.TransitBatchResponse.results:type_name -> metacrypt.v2.TransitBatchResultItem + 0, // 4: metacrypt.v2.TransitService.CreateKey:input_type -> metacrypt.v2.CreateTransitKeyRequest + 2, // 5: metacrypt.v2.TransitService.DeleteKey:input_type -> metacrypt.v2.DeleteTransitKeyRequest + 4, // 6: metacrypt.v2.TransitService.GetKey:input_type -> metacrypt.v2.GetTransitKeyRequest + 6, // 7: metacrypt.v2.TransitService.ListKeys:input_type -> metacrypt.v2.ListTransitKeysRequest + 8, // 8: metacrypt.v2.TransitService.RotateKey:input_type -> metacrypt.v2.RotateTransitKeyRequest + 10, // 9: metacrypt.v2.TransitService.UpdateKeyConfig:input_type -> metacrypt.v2.UpdateTransitKeyConfigRequest + 12, // 10: metacrypt.v2.TransitService.TrimKey:input_type -> metacrypt.v2.TrimTransitKeyRequest + 14, // 11: metacrypt.v2.TransitService.Encrypt:input_type -> metacrypt.v2.TransitEncryptRequest + 16, // 12: metacrypt.v2.TransitService.Decrypt:input_type -> metacrypt.v2.TransitDecryptRequest + 18, // 13: metacrypt.v2.TransitService.Rewrap:input_type -> metacrypt.v2.TransitRewrapRequest + 22, // 14: metacrypt.v2.TransitService.BatchEncrypt:input_type -> metacrypt.v2.TransitBatchEncryptRequest + 23, // 15: metacrypt.v2.TransitService.BatchDecrypt:input_type -> metacrypt.v2.TransitBatchDecryptRequest + 24, // 16: metacrypt.v2.TransitService.BatchRewrap:input_type -> metacrypt.v2.TransitBatchRewrapRequest + 26, // 17: metacrypt.v2.TransitService.Sign:input_type -> metacrypt.v2.TransitSignRequest + 28, // 18: metacrypt.v2.TransitService.Verify:input_type -> metacrypt.v2.TransitVerifyRequest + 30, // 19: metacrypt.v2.TransitService.Hmac:input_type -> metacrypt.v2.TransitHmacRequest + 32, // 20: metacrypt.v2.TransitService.GetPublicKey:input_type -> metacrypt.v2.GetTransitPublicKeyRequest + 1, // 21: metacrypt.v2.TransitService.CreateKey:output_type -> metacrypt.v2.CreateTransitKeyResponse + 3, // 22: metacrypt.v2.TransitService.DeleteKey:output_type -> metacrypt.v2.DeleteTransitKeyResponse + 5, // 23: metacrypt.v2.TransitService.GetKey:output_type -> metacrypt.v2.GetTransitKeyResponse + 7, // 24: metacrypt.v2.TransitService.ListKeys:output_type -> metacrypt.v2.ListTransitKeysResponse + 9, // 25: metacrypt.v2.TransitService.RotateKey:output_type -> metacrypt.v2.RotateTransitKeyResponse + 11, // 26: metacrypt.v2.TransitService.UpdateKeyConfig:output_type -> metacrypt.v2.UpdateTransitKeyConfigResponse + 13, // 27: metacrypt.v2.TransitService.TrimKey:output_type -> metacrypt.v2.TrimTransitKeyResponse + 15, // 28: metacrypt.v2.TransitService.Encrypt:output_type -> metacrypt.v2.TransitEncryptResponse + 17, // 29: metacrypt.v2.TransitService.Decrypt:output_type -> metacrypt.v2.TransitDecryptResponse + 19, // 30: metacrypt.v2.TransitService.Rewrap:output_type -> metacrypt.v2.TransitRewrapResponse + 25, // 31: metacrypt.v2.TransitService.BatchEncrypt:output_type -> metacrypt.v2.TransitBatchResponse + 25, // 32: metacrypt.v2.TransitService.BatchDecrypt:output_type -> metacrypt.v2.TransitBatchResponse + 25, // 33: metacrypt.v2.TransitService.BatchRewrap:output_type -> metacrypt.v2.TransitBatchResponse + 27, // 34: metacrypt.v2.TransitService.Sign:output_type -> metacrypt.v2.TransitSignResponse + 29, // 35: metacrypt.v2.TransitService.Verify:output_type -> metacrypt.v2.TransitVerifyResponse + 31, // 36: metacrypt.v2.TransitService.Hmac:output_type -> metacrypt.v2.TransitHmacResponse + 33, // 37: metacrypt.v2.TransitService.GetPublicKey:output_type -> metacrypt.v2.GetTransitPublicKeyResponse + 21, // [21:38] is the sub-list for method output_type + 4, // [4:21] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name +} + +func init() { file_proto_metacrypt_v2_transit_proto_init() } +func file_proto_metacrypt_v2_transit_proto_init() { + if File_proto_metacrypt_v2_transit_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_metacrypt_v2_transit_proto_rawDesc), len(file_proto_metacrypt_v2_transit_proto_rawDesc)), + NumEnums: 0, + NumMessages: 34, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_proto_metacrypt_v2_transit_proto_goTypes, + DependencyIndexes: file_proto_metacrypt_v2_transit_proto_depIdxs, + MessageInfos: file_proto_metacrypt_v2_transit_proto_msgTypes, + }.Build() + File_proto_metacrypt_v2_transit_proto = out.File + file_proto_metacrypt_v2_transit_proto_goTypes = nil + file_proto_metacrypt_v2_transit_proto_depIdxs = nil +} diff --git a/gen/metacrypt/v2/transit_grpc.pb.go b/gen/metacrypt/v2/transit_grpc.pb.go new file mode 100644 index 0000000..27beafe --- /dev/null +++ b/gen/metacrypt/v2/transit_grpc.pb.go @@ -0,0 +1,777 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.6.1 +// - protoc v3.20.3 +// source: proto/metacrypt/v2/transit.proto + +package metacryptv2 + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + TransitService_CreateKey_FullMethodName = "/metacrypt.v2.TransitService/CreateKey" + TransitService_DeleteKey_FullMethodName = "/metacrypt.v2.TransitService/DeleteKey" + TransitService_GetKey_FullMethodName = "/metacrypt.v2.TransitService/GetKey" + TransitService_ListKeys_FullMethodName = "/metacrypt.v2.TransitService/ListKeys" + TransitService_RotateKey_FullMethodName = "/metacrypt.v2.TransitService/RotateKey" + TransitService_UpdateKeyConfig_FullMethodName = "/metacrypt.v2.TransitService/UpdateKeyConfig" + TransitService_TrimKey_FullMethodName = "/metacrypt.v2.TransitService/TrimKey" + TransitService_Encrypt_FullMethodName = "/metacrypt.v2.TransitService/Encrypt" + TransitService_Decrypt_FullMethodName = "/metacrypt.v2.TransitService/Decrypt" + TransitService_Rewrap_FullMethodName = "/metacrypt.v2.TransitService/Rewrap" + TransitService_BatchEncrypt_FullMethodName = "/metacrypt.v2.TransitService/BatchEncrypt" + TransitService_BatchDecrypt_FullMethodName = "/metacrypt.v2.TransitService/BatchDecrypt" + TransitService_BatchRewrap_FullMethodName = "/metacrypt.v2.TransitService/BatchRewrap" + TransitService_Sign_FullMethodName = "/metacrypt.v2.TransitService/Sign" + TransitService_Verify_FullMethodName = "/metacrypt.v2.TransitService/Verify" + TransitService_Hmac_FullMethodName = "/metacrypt.v2.TransitService/Hmac" + TransitService_GetPublicKey_FullMethodName = "/metacrypt.v2.TransitService/GetPublicKey" +) + +// TransitServiceClient is the client API for TransitService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// TransitService provides typed, authenticated access to transit engine +// operations: symmetric encryption, signing, HMAC, and versioned key +// management. All RPCs require the service to be unsealed. +type TransitServiceClient interface { + // CreateKey creates a new named encryption key. Admin only. + CreateKey(ctx context.Context, in *CreateTransitKeyRequest, opts ...grpc.CallOption) (*CreateTransitKeyResponse, error) + // DeleteKey permanently removes a named key. Admin only. + // Only succeeds if allow_deletion is true on the key config. + DeleteKey(ctx context.Context, in *DeleteTransitKeyRequest, opts ...grpc.CallOption) (*DeleteTransitKeyResponse, error) + // GetKey returns metadata for a named key (no raw material). Auth required. + GetKey(ctx context.Context, in *GetTransitKeyRequest, opts ...grpc.CallOption) (*GetTransitKeyResponse, error) + // ListKeys returns the names of all configured keys. Auth required. + ListKeys(ctx context.Context, in *ListTransitKeysRequest, opts ...grpc.CallOption) (*ListTransitKeysResponse, error) + // RotateKey creates a new version of the named key. Admin only. + RotateKey(ctx context.Context, in *RotateTransitKeyRequest, opts ...grpc.CallOption) (*RotateTransitKeyResponse, error) + // UpdateKeyConfig updates key configuration (e.g. min_decryption_version). + // Admin only. + UpdateKeyConfig(ctx context.Context, in *UpdateTransitKeyConfigRequest, opts ...grpc.CallOption) (*UpdateTransitKeyConfigResponse, error) + // TrimKey deletes versions below min_decryption_version. Admin only. + TrimKey(ctx context.Context, in *TrimTransitKeyRequest, opts ...grpc.CallOption) (*TrimTransitKeyResponse, error) + // Encrypt encrypts plaintext with the latest key version. Auth required. + Encrypt(ctx context.Context, in *TransitEncryptRequest, opts ...grpc.CallOption) (*TransitEncryptResponse, error) + // Decrypt decrypts ciphertext. Auth required. + Decrypt(ctx context.Context, in *TransitDecryptRequest, opts ...grpc.CallOption) (*TransitDecryptResponse, error) + // Rewrap re-encrypts ciphertext with the latest key version without + // exposing plaintext. Auth required. + Rewrap(ctx context.Context, in *TransitRewrapRequest, opts ...grpc.CallOption) (*TransitRewrapResponse, error) + // BatchEncrypt encrypts multiple items in a single request. Auth required. + BatchEncrypt(ctx context.Context, in *TransitBatchEncryptRequest, opts ...grpc.CallOption) (*TransitBatchResponse, error) + // BatchDecrypt decrypts multiple items in a single request. Auth required. + BatchDecrypt(ctx context.Context, in *TransitBatchDecryptRequest, opts ...grpc.CallOption) (*TransitBatchResponse, error) + // BatchRewrap re-encrypts multiple items in a single request. Auth required. + BatchRewrap(ctx context.Context, in *TransitBatchRewrapRequest, opts ...grpc.CallOption) (*TransitBatchResponse, error) + // Sign signs input data with an asymmetric key. Auth required. + Sign(ctx context.Context, in *TransitSignRequest, opts ...grpc.CallOption) (*TransitSignResponse, error) + // Verify verifies a signature against input data. Auth required. + Verify(ctx context.Context, in *TransitVerifyRequest, opts ...grpc.CallOption) (*TransitVerifyResponse, error) + // Hmac computes or verifies an HMAC. Auth required. + Hmac(ctx context.Context, in *TransitHmacRequest, opts ...grpc.CallOption) (*TransitHmacResponse, error) + // GetPublicKey returns the public key for an asymmetric key. Auth required. + GetPublicKey(ctx context.Context, in *GetTransitPublicKeyRequest, opts ...grpc.CallOption) (*GetTransitPublicKeyResponse, error) +} + +type transitServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewTransitServiceClient(cc grpc.ClientConnInterface) TransitServiceClient { + return &transitServiceClient{cc} +} + +func (c *transitServiceClient) CreateKey(ctx context.Context, in *CreateTransitKeyRequest, opts ...grpc.CallOption) (*CreateTransitKeyResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CreateTransitKeyResponse) + err := c.cc.Invoke(ctx, TransitService_CreateKey_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *transitServiceClient) DeleteKey(ctx context.Context, in *DeleteTransitKeyRequest, opts ...grpc.CallOption) (*DeleteTransitKeyResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(DeleteTransitKeyResponse) + err := c.cc.Invoke(ctx, TransitService_DeleteKey_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *transitServiceClient) GetKey(ctx context.Context, in *GetTransitKeyRequest, opts ...grpc.CallOption) (*GetTransitKeyResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetTransitKeyResponse) + err := c.cc.Invoke(ctx, TransitService_GetKey_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *transitServiceClient) ListKeys(ctx context.Context, in *ListTransitKeysRequest, opts ...grpc.CallOption) (*ListTransitKeysResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListTransitKeysResponse) + err := c.cc.Invoke(ctx, TransitService_ListKeys_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *transitServiceClient) RotateKey(ctx context.Context, in *RotateTransitKeyRequest, opts ...grpc.CallOption) (*RotateTransitKeyResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(RotateTransitKeyResponse) + err := c.cc.Invoke(ctx, TransitService_RotateKey_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *transitServiceClient) UpdateKeyConfig(ctx context.Context, in *UpdateTransitKeyConfigRequest, opts ...grpc.CallOption) (*UpdateTransitKeyConfigResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(UpdateTransitKeyConfigResponse) + err := c.cc.Invoke(ctx, TransitService_UpdateKeyConfig_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *transitServiceClient) TrimKey(ctx context.Context, in *TrimTransitKeyRequest, opts ...grpc.CallOption) (*TrimTransitKeyResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(TrimTransitKeyResponse) + err := c.cc.Invoke(ctx, TransitService_TrimKey_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *transitServiceClient) Encrypt(ctx context.Context, in *TransitEncryptRequest, opts ...grpc.CallOption) (*TransitEncryptResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(TransitEncryptResponse) + err := c.cc.Invoke(ctx, TransitService_Encrypt_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *transitServiceClient) Decrypt(ctx context.Context, in *TransitDecryptRequest, opts ...grpc.CallOption) (*TransitDecryptResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(TransitDecryptResponse) + err := c.cc.Invoke(ctx, TransitService_Decrypt_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *transitServiceClient) Rewrap(ctx context.Context, in *TransitRewrapRequest, opts ...grpc.CallOption) (*TransitRewrapResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(TransitRewrapResponse) + err := c.cc.Invoke(ctx, TransitService_Rewrap_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *transitServiceClient) BatchEncrypt(ctx context.Context, in *TransitBatchEncryptRequest, opts ...grpc.CallOption) (*TransitBatchResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(TransitBatchResponse) + err := c.cc.Invoke(ctx, TransitService_BatchEncrypt_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *transitServiceClient) BatchDecrypt(ctx context.Context, in *TransitBatchDecryptRequest, opts ...grpc.CallOption) (*TransitBatchResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(TransitBatchResponse) + err := c.cc.Invoke(ctx, TransitService_BatchDecrypt_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *transitServiceClient) BatchRewrap(ctx context.Context, in *TransitBatchRewrapRequest, opts ...grpc.CallOption) (*TransitBatchResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(TransitBatchResponse) + err := c.cc.Invoke(ctx, TransitService_BatchRewrap_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *transitServiceClient) Sign(ctx context.Context, in *TransitSignRequest, opts ...grpc.CallOption) (*TransitSignResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(TransitSignResponse) + err := c.cc.Invoke(ctx, TransitService_Sign_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *transitServiceClient) Verify(ctx context.Context, in *TransitVerifyRequest, opts ...grpc.CallOption) (*TransitVerifyResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(TransitVerifyResponse) + err := c.cc.Invoke(ctx, TransitService_Verify_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *transitServiceClient) Hmac(ctx context.Context, in *TransitHmacRequest, opts ...grpc.CallOption) (*TransitHmacResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(TransitHmacResponse) + err := c.cc.Invoke(ctx, TransitService_Hmac_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *transitServiceClient) GetPublicKey(ctx context.Context, in *GetTransitPublicKeyRequest, opts ...grpc.CallOption) (*GetTransitPublicKeyResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetTransitPublicKeyResponse) + err := c.cc.Invoke(ctx, TransitService_GetPublicKey_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// TransitServiceServer is the server API for TransitService service. +// All implementations must embed UnimplementedTransitServiceServer +// for forward compatibility. +// +// TransitService provides typed, authenticated access to transit engine +// operations: symmetric encryption, signing, HMAC, and versioned key +// management. All RPCs require the service to be unsealed. +type TransitServiceServer interface { + // CreateKey creates a new named encryption key. Admin only. + CreateKey(context.Context, *CreateTransitKeyRequest) (*CreateTransitKeyResponse, error) + // DeleteKey permanently removes a named key. Admin only. + // Only succeeds if allow_deletion is true on the key config. + DeleteKey(context.Context, *DeleteTransitKeyRequest) (*DeleteTransitKeyResponse, error) + // GetKey returns metadata for a named key (no raw material). Auth required. + GetKey(context.Context, *GetTransitKeyRequest) (*GetTransitKeyResponse, error) + // ListKeys returns the names of all configured keys. Auth required. + ListKeys(context.Context, *ListTransitKeysRequest) (*ListTransitKeysResponse, error) + // RotateKey creates a new version of the named key. Admin only. + RotateKey(context.Context, *RotateTransitKeyRequest) (*RotateTransitKeyResponse, error) + // UpdateKeyConfig updates key configuration (e.g. min_decryption_version). + // Admin only. + UpdateKeyConfig(context.Context, *UpdateTransitKeyConfigRequest) (*UpdateTransitKeyConfigResponse, error) + // TrimKey deletes versions below min_decryption_version. Admin only. + TrimKey(context.Context, *TrimTransitKeyRequest) (*TrimTransitKeyResponse, error) + // Encrypt encrypts plaintext with the latest key version. Auth required. + Encrypt(context.Context, *TransitEncryptRequest) (*TransitEncryptResponse, error) + // Decrypt decrypts ciphertext. Auth required. + Decrypt(context.Context, *TransitDecryptRequest) (*TransitDecryptResponse, error) + // Rewrap re-encrypts ciphertext with the latest key version without + // exposing plaintext. Auth required. + Rewrap(context.Context, *TransitRewrapRequest) (*TransitRewrapResponse, error) + // BatchEncrypt encrypts multiple items in a single request. Auth required. + BatchEncrypt(context.Context, *TransitBatchEncryptRequest) (*TransitBatchResponse, error) + // BatchDecrypt decrypts multiple items in a single request. Auth required. + BatchDecrypt(context.Context, *TransitBatchDecryptRequest) (*TransitBatchResponse, error) + // BatchRewrap re-encrypts multiple items in a single request. Auth required. + BatchRewrap(context.Context, *TransitBatchRewrapRequest) (*TransitBatchResponse, error) + // Sign signs input data with an asymmetric key. Auth required. + Sign(context.Context, *TransitSignRequest) (*TransitSignResponse, error) + // Verify verifies a signature against input data. Auth required. + Verify(context.Context, *TransitVerifyRequest) (*TransitVerifyResponse, error) + // Hmac computes or verifies an HMAC. Auth required. + Hmac(context.Context, *TransitHmacRequest) (*TransitHmacResponse, error) + // GetPublicKey returns the public key for an asymmetric key. Auth required. + GetPublicKey(context.Context, *GetTransitPublicKeyRequest) (*GetTransitPublicKeyResponse, error) + mustEmbedUnimplementedTransitServiceServer() +} + +// UnimplementedTransitServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedTransitServiceServer struct{} + +func (UnimplementedTransitServiceServer) CreateKey(context.Context, *CreateTransitKeyRequest) (*CreateTransitKeyResponse, error) { + return nil, status.Error(codes.Unimplemented, "method CreateKey not implemented") +} +func (UnimplementedTransitServiceServer) DeleteKey(context.Context, *DeleteTransitKeyRequest) (*DeleteTransitKeyResponse, error) { + return nil, status.Error(codes.Unimplemented, "method DeleteKey not implemented") +} +func (UnimplementedTransitServiceServer) GetKey(context.Context, *GetTransitKeyRequest) (*GetTransitKeyResponse, error) { + return nil, status.Error(codes.Unimplemented, "method GetKey not implemented") +} +func (UnimplementedTransitServiceServer) ListKeys(context.Context, *ListTransitKeysRequest) (*ListTransitKeysResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ListKeys not implemented") +} +func (UnimplementedTransitServiceServer) RotateKey(context.Context, *RotateTransitKeyRequest) (*RotateTransitKeyResponse, error) { + return nil, status.Error(codes.Unimplemented, "method RotateKey not implemented") +} +func (UnimplementedTransitServiceServer) UpdateKeyConfig(context.Context, *UpdateTransitKeyConfigRequest) (*UpdateTransitKeyConfigResponse, error) { + return nil, status.Error(codes.Unimplemented, "method UpdateKeyConfig not implemented") +} +func (UnimplementedTransitServiceServer) TrimKey(context.Context, *TrimTransitKeyRequest) (*TrimTransitKeyResponse, error) { + return nil, status.Error(codes.Unimplemented, "method TrimKey not implemented") +} +func (UnimplementedTransitServiceServer) Encrypt(context.Context, *TransitEncryptRequest) (*TransitEncryptResponse, error) { + return nil, status.Error(codes.Unimplemented, "method Encrypt not implemented") +} +func (UnimplementedTransitServiceServer) Decrypt(context.Context, *TransitDecryptRequest) (*TransitDecryptResponse, error) { + return nil, status.Error(codes.Unimplemented, "method Decrypt not implemented") +} +func (UnimplementedTransitServiceServer) Rewrap(context.Context, *TransitRewrapRequest) (*TransitRewrapResponse, error) { + return nil, status.Error(codes.Unimplemented, "method Rewrap not implemented") +} +func (UnimplementedTransitServiceServer) BatchEncrypt(context.Context, *TransitBatchEncryptRequest) (*TransitBatchResponse, error) { + return nil, status.Error(codes.Unimplemented, "method BatchEncrypt not implemented") +} +func (UnimplementedTransitServiceServer) BatchDecrypt(context.Context, *TransitBatchDecryptRequest) (*TransitBatchResponse, error) { + return nil, status.Error(codes.Unimplemented, "method BatchDecrypt not implemented") +} +func (UnimplementedTransitServiceServer) BatchRewrap(context.Context, *TransitBatchRewrapRequest) (*TransitBatchResponse, error) { + return nil, status.Error(codes.Unimplemented, "method BatchRewrap not implemented") +} +func (UnimplementedTransitServiceServer) Sign(context.Context, *TransitSignRequest) (*TransitSignResponse, error) { + return nil, status.Error(codes.Unimplemented, "method Sign not implemented") +} +func (UnimplementedTransitServiceServer) Verify(context.Context, *TransitVerifyRequest) (*TransitVerifyResponse, error) { + return nil, status.Error(codes.Unimplemented, "method Verify not implemented") +} +func (UnimplementedTransitServiceServer) Hmac(context.Context, *TransitHmacRequest) (*TransitHmacResponse, error) { + return nil, status.Error(codes.Unimplemented, "method Hmac not implemented") +} +func (UnimplementedTransitServiceServer) GetPublicKey(context.Context, *GetTransitPublicKeyRequest) (*GetTransitPublicKeyResponse, error) { + return nil, status.Error(codes.Unimplemented, "method GetPublicKey not implemented") +} +func (UnimplementedTransitServiceServer) mustEmbedUnimplementedTransitServiceServer() {} +func (UnimplementedTransitServiceServer) testEmbeddedByValue() {} + +// UnsafeTransitServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to TransitServiceServer will +// result in compilation errors. +type UnsafeTransitServiceServer interface { + mustEmbedUnimplementedTransitServiceServer() +} + +func RegisterTransitServiceServer(s grpc.ServiceRegistrar, srv TransitServiceServer) { + // If the following call panics, it indicates UnimplementedTransitServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&TransitService_ServiceDesc, srv) +} + +func _TransitService_CreateKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateTransitKeyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TransitServiceServer).CreateKey(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TransitService_CreateKey_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TransitServiceServer).CreateKey(ctx, req.(*CreateTransitKeyRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TransitService_DeleteKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteTransitKeyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TransitServiceServer).DeleteKey(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TransitService_DeleteKey_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TransitServiceServer).DeleteKey(ctx, req.(*DeleteTransitKeyRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TransitService_GetKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetTransitKeyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TransitServiceServer).GetKey(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TransitService_GetKey_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TransitServiceServer).GetKey(ctx, req.(*GetTransitKeyRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TransitService_ListKeys_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListTransitKeysRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TransitServiceServer).ListKeys(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TransitService_ListKeys_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TransitServiceServer).ListKeys(ctx, req.(*ListTransitKeysRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TransitService_RotateKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RotateTransitKeyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TransitServiceServer).RotateKey(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TransitService_RotateKey_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TransitServiceServer).RotateKey(ctx, req.(*RotateTransitKeyRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TransitService_UpdateKeyConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateTransitKeyConfigRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TransitServiceServer).UpdateKeyConfig(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TransitService_UpdateKeyConfig_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TransitServiceServer).UpdateKeyConfig(ctx, req.(*UpdateTransitKeyConfigRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TransitService_TrimKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TrimTransitKeyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TransitServiceServer).TrimKey(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TransitService_TrimKey_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TransitServiceServer).TrimKey(ctx, req.(*TrimTransitKeyRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TransitService_Encrypt_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TransitEncryptRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TransitServiceServer).Encrypt(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TransitService_Encrypt_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TransitServiceServer).Encrypt(ctx, req.(*TransitEncryptRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TransitService_Decrypt_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TransitDecryptRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TransitServiceServer).Decrypt(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TransitService_Decrypt_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TransitServiceServer).Decrypt(ctx, req.(*TransitDecryptRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TransitService_Rewrap_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TransitRewrapRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TransitServiceServer).Rewrap(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TransitService_Rewrap_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TransitServiceServer).Rewrap(ctx, req.(*TransitRewrapRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TransitService_BatchEncrypt_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TransitBatchEncryptRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TransitServiceServer).BatchEncrypt(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TransitService_BatchEncrypt_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TransitServiceServer).BatchEncrypt(ctx, req.(*TransitBatchEncryptRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TransitService_BatchDecrypt_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TransitBatchDecryptRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TransitServiceServer).BatchDecrypt(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TransitService_BatchDecrypt_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TransitServiceServer).BatchDecrypt(ctx, req.(*TransitBatchDecryptRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TransitService_BatchRewrap_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TransitBatchRewrapRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TransitServiceServer).BatchRewrap(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TransitService_BatchRewrap_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TransitServiceServer).BatchRewrap(ctx, req.(*TransitBatchRewrapRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TransitService_Sign_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TransitSignRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TransitServiceServer).Sign(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TransitService_Sign_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TransitServiceServer).Sign(ctx, req.(*TransitSignRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TransitService_Verify_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TransitVerifyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TransitServiceServer).Verify(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TransitService_Verify_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TransitServiceServer).Verify(ctx, req.(*TransitVerifyRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TransitService_Hmac_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TransitHmacRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TransitServiceServer).Hmac(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TransitService_Hmac_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TransitServiceServer).Hmac(ctx, req.(*TransitHmacRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _TransitService_GetPublicKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetTransitPublicKeyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(TransitServiceServer).GetPublicKey(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: TransitService_GetPublicKey_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(TransitServiceServer).GetPublicKey(ctx, req.(*GetTransitPublicKeyRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// TransitService_ServiceDesc is the grpc.ServiceDesc for TransitService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var TransitService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "metacrypt.v2.TransitService", + HandlerType: (*TransitServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "CreateKey", + Handler: _TransitService_CreateKey_Handler, + }, + { + MethodName: "DeleteKey", + Handler: _TransitService_DeleteKey_Handler, + }, + { + MethodName: "GetKey", + Handler: _TransitService_GetKey_Handler, + }, + { + MethodName: "ListKeys", + Handler: _TransitService_ListKeys_Handler, + }, + { + MethodName: "RotateKey", + Handler: _TransitService_RotateKey_Handler, + }, + { + MethodName: "UpdateKeyConfig", + Handler: _TransitService_UpdateKeyConfig_Handler, + }, + { + MethodName: "TrimKey", + Handler: _TransitService_TrimKey_Handler, + }, + { + MethodName: "Encrypt", + Handler: _TransitService_Encrypt_Handler, + }, + { + MethodName: "Decrypt", + Handler: _TransitService_Decrypt_Handler, + }, + { + MethodName: "Rewrap", + Handler: _TransitService_Rewrap_Handler, + }, + { + MethodName: "BatchEncrypt", + Handler: _TransitService_BatchEncrypt_Handler, + }, + { + MethodName: "BatchDecrypt", + Handler: _TransitService_BatchDecrypt_Handler, + }, + { + MethodName: "BatchRewrap", + Handler: _TransitService_BatchRewrap_Handler, + }, + { + MethodName: "Sign", + Handler: _TransitService_Sign_Handler, + }, + { + MethodName: "Verify", + Handler: _TransitService_Verify_Handler, + }, + { + MethodName: "Hmac", + Handler: _TransitService_Hmac_Handler, + }, + { + MethodName: "GetPublicKey", + Handler: _TransitService_GetPublicKey_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "proto/metacrypt/v2/transit.proto", +} diff --git a/internal/engine/transit/transit.go b/internal/engine/transit/transit.go new file mode 100644 index 0000000..415d6ca --- /dev/null +++ b/internal/engine/transit/transit.go @@ -0,0 +1,1602 @@ +// Package transit implements the transit encryption engine for symmetric +// encryption, signing, and HMAC operations with versioned key management. +package transit + +import ( + "context" + "crypto" + "crypto/aes" + "crypto/cipher" + "crypto/ecdsa" + "crypto/ed25519" + "crypto/elliptic" + "crypto/hmac" + "crypto/rand" + "crypto/sha256" + "crypto/sha512" + "crypto/x509" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "hash" + "sort" + "strconv" + "strings" + "sync" + + "golang.org/x/crypto/chacha20poly1305" + + "git.wntrmute.dev/kyle/metacrypt/internal/barrier" + mcrypto "git.wntrmute.dev/kyle/metacrypt/internal/crypto" + "git.wntrmute.dev/kyle/metacrypt/internal/engine" +) + +const maxBatchSize = 500 + +var ( + ErrSealed = errors.New("transit: engine is sealed") + ErrKeyNotFound = errors.New("transit: key not found") + ErrKeyExists = errors.New("transit: key already exists") + ErrForbidden = errors.New("transit: forbidden") + ErrUnauthorized = errors.New("transit: authentication required") + ErrDeletionDenied = errors.New("transit: deletion not allowed") + ErrInvalidKeyType = errors.New("transit: invalid key type") + ErrUnsupportedOp = errors.New("transit: unsupported operation for key type") + ErrDecryptVersion = errors.New("transit: ciphertext version below minimum decryption version") + ErrInvalidFormat = errors.New("transit: invalid ciphertext format") + ErrBatchTooLarge = errors.New("transit: batch size exceeds maximum") + ErrInvalidMinVer = errors.New("transit: min_decryption_version can only increase and cannot exceed current version") +) + +// keyVersion holds a single version of key material. +type keyVersion struct { + version int + key []byte // symmetric key material + privKey crypto.PrivateKey // asymmetric (nil for symmetric) + pubKey crypto.PublicKey // asymmetric (nil for symmetric) +} + +// keyState holds in-memory state for a loaded key. +type keyState struct { + config *KeyConfig + versions map[int]*keyVersion +} + +// TransitEngine implements the transit encryption engine. +type TransitEngine struct { + barrier barrier.Barrier + config *TransitConfig + keys map[string]*keyState + mountPath string + mu sync.RWMutex +} + +// NewTransitEngine creates a new transit engine instance. +func NewTransitEngine() engine.Engine { + return &TransitEngine{ + keys: make(map[string]*keyState), + } +} + +func (e *TransitEngine) Type() engine.EngineType { + return engine.EngineTypeTransit +} + +// Initialize sets up the transit engine for first use. +func (e *TransitEngine) Initialize(ctx context.Context, b barrier.Barrier, mountPath string, config map[string]interface{}) error { + e.mu.Lock() + defer e.mu.Unlock() + + e.barrier = b + e.mountPath = mountPath + + cfg := &TransitConfig{} + if config != nil { + if v, ok := config["max_key_versions"]; ok { + switch val := v.(type) { + case float64: + cfg.MaxKeyVersions = int(val) + case int: + cfg.MaxKeyVersions = val + } + } + } + e.config = cfg + + configData, err := json.Marshal(cfg) + if err != nil { + return fmt.Errorf("transit: marshal config: %w", err) + } + if err := b.Put(ctx, mountPath+"config.json", configData); err != nil { + return fmt.Errorf("transit: store config: %w", err) + } + + e.keys = make(map[string]*keyState) + return nil +} + +// Unseal loads the transit state from the barrier into memory. +func (e *TransitEngine) Unseal(ctx context.Context, b barrier.Barrier, mountPath string) error { + e.mu.Lock() + defer e.mu.Unlock() + + e.barrier = b + e.mountPath = mountPath + + // Load config. + configData, err := b.Get(ctx, mountPath+"config.json") + if err != nil { + return fmt.Errorf("transit: load config: %w", err) + } + var cfg TransitConfig + if err := json.Unmarshal(configData, &cfg); err != nil { + return fmt.Errorf("transit: parse config: %w", err) + } + e.config = &cfg + e.keys = make(map[string]*keyState) + + // Load all keys. + keyPaths, err := b.List(ctx, mountPath+"keys/") + if err != nil { + return nil // no keys yet + } + + // Collect unique key names from paths like "mykey/config.json", "mykey/v1.key". + keyNames := make(map[string]bool) + for _, p := range keyPaths { + parts := strings.SplitN(p, "/", 2) + if len(parts) > 0 && parts[0] != "" { + keyNames[parts[0]] = true + } + } + + for name := range keyNames { + ks, err := e.loadKey(ctx, b, mountPath, name) + if err != nil { + return fmt.Errorf("transit: load key %q: %w", name, err) + } + e.keys[name] = ks + } + + return nil +} + +func (e *TransitEngine) loadKey(ctx context.Context, b barrier.Barrier, mountPath, name string) (*keyState, error) { + prefix := mountPath + "keys/" + name + "/" + + configData, err := b.Get(ctx, prefix+"config.json") + if err != nil { + return nil, fmt.Errorf("load config: %w", err) + } + var cfg KeyConfig + if err := json.Unmarshal(configData, &cfg); err != nil { + return nil, fmt.Errorf("parse config: %w", err) + } + + ks := &keyState{ + config: &cfg, + versions: make(map[int]*keyVersion), + } + + // Load all versions. + for v := 1; v <= cfg.CurrentVersion; v++ { + kv, err := e.loadKeyVersion(ctx, b, prefix, &cfg, v) + if err != nil { + // Version may have been trimmed; skip. + continue + } + ks.versions[v] = kv + } + + return ks, nil +} + +func (e *TransitEngine) loadKeyVersion(ctx context.Context, b barrier.Barrier, prefix string, cfg *KeyConfig, version int) (*keyVersion, error) { + path := fmt.Sprintf("%sv%d.key", prefix, version) + data, err := b.Get(ctx, path) + if err != nil { + return nil, err + } + + kv := &keyVersion{version: version} + + switch cfg.Type { + case "aes256-gcm", "chacha20-poly", "hmac-sha256", "hmac-sha512": + kv.key = data + case "ed25519": + privKey := ed25519.PrivateKey(data) + kv.key = data + kv.privKey = privKey + kv.pubKey = privKey.Public() + case "ecdsa-p256", "ecdsa-p384": + privKey, err := x509.ParsePKCS8PrivateKey(data) + if err != nil { + return nil, fmt.Errorf("parse PKCS8 key: %w", err) + } + ecKey, ok := privKey.(*ecdsa.PrivateKey) + if !ok { + return nil, fmt.Errorf("expected ECDSA key, got %T", privKey) + } + kv.privKey = ecKey + kv.pubKey = &ecKey.PublicKey + default: + return nil, fmt.Errorf("unknown key type: %s", cfg.Type) + } + + return kv, nil +} + +// Seal zeroizes all in-memory key material. +func (e *TransitEngine) Seal() error { + e.mu.Lock() + defer e.mu.Unlock() + + for name, ks := range e.keys { + for _, kv := range ks.versions { + if kv.key != nil { + mcrypto.Zeroize(kv.key) + } + zeroizeKey(kv.privKey) + } + delete(e.keys, name) + } + e.keys = nil + e.config = nil + + return nil +} + +// HandleRequest dispatches transit operations. +func (e *TransitEngine) HandleRequest(ctx context.Context, req *engine.Request) (*engine.Response, error) { + switch req.Operation { + case "create-key": + return e.handleCreateKey(ctx, req) + case "delete-key": + return e.handleDeleteKey(ctx, req) + case "get-key": + return e.handleGetKey(ctx, req) + case "list-keys": + return e.handleListKeys(ctx, req) + case "rotate-key": + return e.handleRotateKey(ctx, req) + case "update-key-config": + return e.handleUpdateKeyConfig(ctx, req) + case "trim-key": + return e.handleTrimKey(ctx, req) + case "encrypt": + return e.handleEncrypt(ctx, req) + case "decrypt": + return e.handleDecrypt(ctx, req) + case "rewrap": + return e.handleRewrap(ctx, req) + case "batch-encrypt": + return e.handleBatchEncrypt(ctx, req) + case "batch-decrypt": + return e.handleBatchDecrypt(ctx, req) + case "batch-rewrap": + return e.handleBatchRewrap(ctx, req) + case "sign": + return e.handleSign(ctx, req) + case "verify": + return e.handleVerify(ctx, req) + case "hmac": + return e.handleHMAC(ctx, req) + case "get-public-key": + return e.handleGetPublicKey(ctx, req) + default: + return nil, fmt.Errorf("transit: unknown operation: %s", req.Operation) + } +} + +// --- Authorization helpers --- + +func (e *TransitEngine) requireAdmin(req *engine.Request) error { + if req.CallerInfo == nil { + return ErrUnauthorized + } + if !req.CallerInfo.IsAdmin { + return ErrForbidden + } + return nil +} + +func (e *TransitEngine) requireUser(req *engine.Request) error { + if req.CallerInfo == nil { + return ErrUnauthorized + } + if !req.CallerInfo.IsUser() { + return ErrForbidden + } + return nil +} + +func (e *TransitEngine) requireUserWithPolicy(req *engine.Request, keyName string) error { + if req.CallerInfo == nil { + return ErrUnauthorized + } + if req.CallerInfo.IsAdmin { + return nil + } + if !req.CallerInfo.IsUser() { + return ErrForbidden + } + + // Check policy for the specific key. + if req.CheckPolicy != nil { + resource := fmt.Sprintf("transit/%s/key/%s", e.mountName(), keyName) + action := operationToAction(req.Operation) + effect, matched := req.CheckPolicy(resource, action) + if matched { + if effect == "allow" { + return nil + } + return ErrForbidden + } + } + + // Default: users can access transit operations without explicit policy. + return nil +} + +func operationToAction(op string) string { + switch op { + case "get-key", "list-keys", "get-public-key": + return "read" + case "decrypt", "rewrap", "batch-decrypt", "batch-rewrap": + return "decrypt" + default: + return "write" + } +} + +// mountName extracts the user-facing mount name from the mount path. +func (e *TransitEngine) mountName() string { + parts := strings.Split(strings.TrimSuffix(e.mountPath, "/"), "/") + if len(parts) >= 3 { + return parts[2] + } + return "" +} + +func (e *TransitEngine) sealed() bool { + return e.config == nil +} + +// --- Key Management Operations --- + +func (e *TransitEngine) handleCreateKey(ctx context.Context, req *engine.Request) (*engine.Response, error) { + if err := e.requireAdmin(req); err != nil { + return nil, err + } + + e.mu.Lock() + defer e.mu.Unlock() + + if e.sealed() { + return nil, ErrSealed + } + + name, _ := req.Data["name"].(string) + keyType, _ := req.Data["type"].(string) + if name == "" { + return nil, fmt.Errorf("transit: name is required") + } + if keyType == "" { + keyType = "aes256-gcm" + } + + if !isValidKeyType(keyType) { + return nil, ErrInvalidKeyType + } + + if _, exists := e.keys[name]; exists { + return nil, ErrKeyExists + } + + // Generate key version 1. + kv, err := generateKeyVersion(keyType, 1) + if err != nil { + return nil, fmt.Errorf("transit: generate key: %w", err) + } + + cfg := &KeyConfig{ + Name: name, + Type: keyType, + CurrentVersion: 1, + MinDecryptionVersion: 1, + AllowDeletion: false, + } + + // Store config and key. + prefix := e.mountPath + "keys/" + name + "/" + if err := e.storeKeyConfig(ctx, prefix, cfg); err != nil { + return nil, err + } + if err := e.storeKeyVersion(ctx, prefix, cfg, kv); err != nil { + return nil, err + } + + e.keys[name] = &keyState{ + config: cfg, + versions: map[int]*keyVersion{1: kv}, + } + + return &engine.Response{ + Data: map[string]interface{}{ + "name": name, + "type": keyType, + "version": 1, + }, + }, nil +} + +func (e *TransitEngine) handleDeleteKey(ctx context.Context, req *engine.Request) (*engine.Response, error) { + if err := e.requireAdmin(req); err != nil { + return nil, err + } + + e.mu.Lock() + defer e.mu.Unlock() + + if e.sealed() { + return nil, ErrSealed + } + + name, _ := req.Data["name"].(string) + if name == "" { + return nil, fmt.Errorf("transit: name is required") + } + + ks, ok := e.keys[name] + if !ok { + return nil, ErrKeyNotFound + } + + if !ks.config.AllowDeletion { + return nil, ErrDeletionDenied + } + + // Delete all versions and config from barrier. + prefix := e.mountPath + "keys/" + name + "/" + for v := range ks.versions { + path := fmt.Sprintf("%sv%d.key", prefix, v) + _ = e.barrier.Delete(ctx, path) + } + _ = e.barrier.Delete(ctx, prefix+"config.json") + + // Zeroize in-memory material. + for _, kv := range ks.versions { + if kv.key != nil { + mcrypto.Zeroize(kv.key) + } + zeroizeKey(kv.privKey) + } + delete(e.keys, name) + + return &engine.Response{ + Data: map[string]interface{}{"ok": true}, + }, nil +} + +func (e *TransitEngine) handleGetKey(_ context.Context, req *engine.Request) (*engine.Response, error) { + if err := e.requireUser(req); err != nil { + return nil, err + } + + e.mu.RLock() + defer e.mu.RUnlock() + + if e.sealed() { + return nil, ErrSealed + } + + name, _ := req.Data["name"].(string) + if name == "" { + return nil, fmt.Errorf("transit: name is required") + } + + ks, ok := e.keys[name] + if !ok { + return nil, ErrKeyNotFound + } + + versions := make([]int, 0, len(ks.versions)) + for v := range ks.versions { + versions = append(versions, v) + } + sort.Ints(versions) + + return &engine.Response{ + Data: map[string]interface{}{ + "name": ks.config.Name, + "type": ks.config.Type, + "current_version": ks.config.CurrentVersion, + "min_decryption_version": ks.config.MinDecryptionVersion, + "allow_deletion": ks.config.AllowDeletion, + "versions": versions, + }, + }, nil +} + +func (e *TransitEngine) handleListKeys(_ context.Context, req *engine.Request) (*engine.Response, error) { + if err := e.requireUser(req); err != nil { + return nil, err + } + + e.mu.RLock() + defer e.mu.RUnlock() + + if e.sealed() { + return nil, ErrSealed + } + + keys := make([]string, 0, len(e.keys)) + for name := range e.keys { + keys = append(keys, name) + } + sort.Strings(keys) + + return &engine.Response{ + Data: map[string]interface{}{"keys": keys}, + }, nil +} + +func (e *TransitEngine) handleRotateKey(ctx context.Context, req *engine.Request) (*engine.Response, error) { + if err := e.requireAdmin(req); err != nil { + return nil, err + } + + e.mu.Lock() + defer e.mu.Unlock() + + if e.sealed() { + return nil, ErrSealed + } + + name, _ := req.Data["name"].(string) + if name == "" { + return nil, fmt.Errorf("transit: name is required") + } + + ks, ok := e.keys[name] + if !ok { + return nil, ErrKeyNotFound + } + + newVersion := ks.config.CurrentVersion + 1 + kv, err := generateKeyVersion(ks.config.Type, newVersion) + if err != nil { + return nil, fmt.Errorf("transit: generate key version: %w", err) + } + + ks.config.CurrentVersion = newVersion + prefix := e.mountPath + "keys/" + name + "/" + if err := e.storeKeyConfig(ctx, prefix, ks.config); err != nil { + return nil, err + } + if err := e.storeKeyVersion(ctx, prefix, ks.config, kv); err != nil { + return nil, err + } + + ks.versions[newVersion] = kv + + // Prune old versions if max_key_versions is set. + if e.config.MaxKeyVersions > 0 && len(ks.versions) > e.config.MaxKeyVersions { + e.pruneVersions(ctx, ks, prefix) + } + + return &engine.Response{ + Data: map[string]interface{}{ + "name": name, + "version": newVersion, + }, + }, nil +} + +func (e *TransitEngine) pruneVersions(ctx context.Context, ks *keyState, prefix string) { + versions := make([]int, 0, len(ks.versions)) + for v := range ks.versions { + versions = append(versions, v) + } + sort.Ints(versions) + + for len(versions) > e.config.MaxKeyVersions { + v := versions[0] + if v >= ks.config.MinDecryptionVersion { + break + } + path := fmt.Sprintf("%sv%d.key", prefix, v) + _ = e.barrier.Delete(ctx, path) + if kv, ok := ks.versions[v]; ok { + if kv.key != nil { + mcrypto.Zeroize(kv.key) + } + zeroizeKey(kv.privKey) + } + delete(ks.versions, v) + versions = versions[1:] + } +} + +func (e *TransitEngine) handleUpdateKeyConfig(ctx context.Context, req *engine.Request) (*engine.Response, error) { + if err := e.requireAdmin(req); err != nil { + return nil, err + } + + e.mu.Lock() + defer e.mu.Unlock() + + if e.sealed() { + return nil, ErrSealed + } + + name, _ := req.Data["name"].(string) + if name == "" { + return nil, fmt.Errorf("transit: name is required") + } + + ks, ok := e.keys[name] + if !ok { + return nil, ErrKeyNotFound + } + + if v, ok := req.Data["min_decryption_version"]; ok { + newMin := toInt(v) + if newMin < ks.config.MinDecryptionVersion || newMin > ks.config.CurrentVersion { + return nil, ErrInvalidMinVer + } + ks.config.MinDecryptionVersion = newMin + } + + if v, ok := req.Data["allow_deletion"]; ok { + if b, ok := v.(bool); ok { + ks.config.AllowDeletion = b + } + } + + prefix := e.mountPath + "keys/" + name + "/" + if err := e.storeKeyConfig(ctx, prefix, ks.config); err != nil { + return nil, err + } + + return &engine.Response{ + Data: map[string]interface{}{"ok": true}, + }, nil +} + +func (e *TransitEngine) handleTrimKey(ctx context.Context, req *engine.Request) (*engine.Response, error) { + if err := e.requireAdmin(req); err != nil { + return nil, err + } + + e.mu.Lock() + defer e.mu.Unlock() + + if e.sealed() { + return nil, ErrSealed + } + + name, _ := req.Data["name"].(string) + if name == "" { + return nil, fmt.Errorf("transit: name is required") + } + + ks, ok := e.keys[name] + if !ok { + return nil, ErrKeyNotFound + } + + prefix := e.mountPath + "keys/" + name + "/" + trimmed := 0 + for v, kv := range ks.versions { + if v < ks.config.MinDecryptionVersion { + path := fmt.Sprintf("%sv%d.key", prefix, v) + _ = e.barrier.Delete(ctx, path) + if kv.key != nil { + mcrypto.Zeroize(kv.key) + } + zeroizeKey(kv.privKey) + delete(ks.versions, v) + trimmed++ + } + } + + return &engine.Response{ + Data: map[string]interface{}{ + "trimmed": trimmed, + }, + }, nil +} + +// --- Crypto Operations --- + +func (e *TransitEngine) handleEncrypt(_ context.Context, req *engine.Request) (*engine.Response, error) { + keyName, _ := req.Data["key"].(string) + if keyName == "" { + keyName, _ = req.Data["name"].(string) + } + if err := e.requireUserWithPolicy(req, keyName); err != nil { + return nil, err + } + + e.mu.RLock() + defer e.mu.RUnlock() + + if e.sealed() { + return nil, ErrSealed + } + + if keyName == "" { + return nil, fmt.Errorf("transit: key name is required") + } + + plaintextB64, _ := req.Data["plaintext"].(string) + contextB64, _ := req.Data["context"].(string) + + ciphertext, err := e.encryptWithKey(keyName, plaintextB64, contextB64) + if err != nil { + return nil, err + } + + return &engine.Response{ + Data: map[string]interface{}{"ciphertext": ciphertext}, + }, nil +} + +func (e *TransitEngine) encryptWithKey(keyName, plaintextB64, contextB64 string) (string, error) { + ks, ok := e.keys[keyName] + if !ok { + return "", ErrKeyNotFound + } + + if !isSymmetric(ks.config.Type) { + return "", ErrUnsupportedOp + } + + plaintext, err := base64.StdEncoding.DecodeString(plaintextB64) + if err != nil { + return "", fmt.Errorf("transit: invalid base64 plaintext: %w", err) + } + + var aad []byte + if contextB64 != "" { + aad, err = base64.StdEncoding.DecodeString(contextB64) + if err != nil { + return "", fmt.Errorf("transit: invalid base64 context: %w", err) + } + } + + currentVersion := ks.config.CurrentVersion + kv, ok := ks.versions[currentVersion] + if !ok { + return "", fmt.Errorf("transit: current key version %d not found", currentVersion) + } + + encrypted, err := encryptData(ks.config.Type, kv.key, plaintext, aad) + if err != nil { + return "", err + } + + return formatCiphertext(currentVersion, encrypted), nil +} + +func (e *TransitEngine) handleDecrypt(_ context.Context, req *engine.Request) (*engine.Response, error) { + keyName, _ := req.Data["key"].(string) + if keyName == "" { + keyName, _ = req.Data["name"].(string) + } + if err := e.requireUserWithPolicy(req, keyName); err != nil { + return nil, err + } + + e.mu.RLock() + defer e.mu.RUnlock() + + if e.sealed() { + return nil, ErrSealed + } + + if keyName == "" { + return nil, fmt.Errorf("transit: key name is required") + } + + ciphertextStr, _ := req.Data["ciphertext"].(string) + contextB64, _ := req.Data["context"].(string) + + plaintext, err := e.decryptWithKey(keyName, ciphertextStr, contextB64) + if err != nil { + return nil, err + } + + return &engine.Response{ + Data: map[string]interface{}{"plaintext": base64.StdEncoding.EncodeToString(plaintext)}, + }, nil +} + +func (e *TransitEngine) decryptWithKey(keyName, ciphertextStr, contextB64 string) ([]byte, error) { + ks, ok := e.keys[keyName] + if !ok { + return nil, ErrKeyNotFound + } + + if !isSymmetric(ks.config.Type) { + return nil, ErrUnsupportedOp + } + + version, data, err := parseCiphertext(ciphertextStr) + if err != nil { + return nil, err + } + + if version < ks.config.MinDecryptionVersion { + return nil, ErrDecryptVersion + } + + kv, ok := ks.versions[version] + if !ok { + return nil, fmt.Errorf("transit: key version %d not found", version) + } + + var aad []byte + if contextB64 != "" { + aad, err = base64.StdEncoding.DecodeString(contextB64) + if err != nil { + return nil, fmt.Errorf("transit: invalid base64 context: %w", err) + } + } + + return decryptData(ks.config.Type, kv.key, data, aad) +} + +func (e *TransitEngine) handleRewrap(_ context.Context, req *engine.Request) (*engine.Response, error) { + keyName, _ := req.Data["key"].(string) + if keyName == "" { + keyName, _ = req.Data["name"].(string) + } + if err := e.requireUserWithPolicy(req, keyName); err != nil { + return nil, err + } + + e.mu.RLock() + defer e.mu.RUnlock() + + if e.sealed() { + return nil, ErrSealed + } + + if keyName == "" { + return nil, fmt.Errorf("transit: key name is required") + } + + ciphertextStr, _ := req.Data["ciphertext"].(string) + contextB64, _ := req.Data["context"].(string) + + // Decrypt with old version. + plaintext, err := e.decryptWithKey(keyName, ciphertextStr, contextB64) + if err != nil { + return nil, err + } + + // Re-encrypt with latest version (reuse the decoded plaintext as raw bytes). + plaintextB64 := base64.StdEncoding.EncodeToString(plaintext) + newCiphertext, err := e.encryptWithKey(keyName, plaintextB64, contextB64) + if err != nil { + return nil, err + } + + return &engine.Response{ + Data: map[string]interface{}{"ciphertext": newCiphertext}, + }, nil +} + +// --- Batch Operations --- + +type batchItem struct { + Plaintext string `json:"plaintext"` + Ciphertext string `json:"ciphertext"` + Context string `json:"context"` + Reference string `json:"reference"` +} + +type batchResult struct { + Plaintext string `json:"plaintext,omitempty"` + Ciphertext string `json:"ciphertext,omitempty"` + Reference string `json:"reference,omitempty"` + Error string `json:"error,omitempty"` +} + +func (e *TransitEngine) handleBatchEncrypt(_ context.Context, req *engine.Request) (*engine.Response, error) { + keyName, _ := req.Data["key"].(string) + if keyName == "" { + keyName, _ = req.Data["name"].(string) + } + if err := e.requireUserWithPolicy(req, keyName); err != nil { + return nil, err + } + + e.mu.RLock() + defer e.mu.RUnlock() + + if e.sealed() { + return nil, ErrSealed + } + + items, err := extractBatchItems(req.Data["items"]) + if err != nil { + return nil, err + } + if len(items) > maxBatchSize { + return nil, ErrBatchTooLarge + } + + results := make([]interface{}, len(items)) + for i, item := range items { + ct, err := e.encryptWithKey(keyName, item.Plaintext, item.Context) + r := batchResult{Reference: item.Reference} + if err != nil { + r.Error = err.Error() + } else { + r.Ciphertext = ct + } + results[i] = r + } + + return &engine.Response{ + Data: map[string]interface{}{"results": results}, + }, nil +} + +func (e *TransitEngine) handleBatchDecrypt(_ context.Context, req *engine.Request) (*engine.Response, error) { + keyName, _ := req.Data["key"].(string) + if keyName == "" { + keyName, _ = req.Data["name"].(string) + } + if err := e.requireUserWithPolicy(req, keyName); err != nil { + return nil, err + } + + e.mu.RLock() + defer e.mu.RUnlock() + + if e.sealed() { + return nil, ErrSealed + } + + items, err := extractBatchItems(req.Data["items"]) + if err != nil { + return nil, err + } + if len(items) > maxBatchSize { + return nil, ErrBatchTooLarge + } + + results := make([]interface{}, len(items)) + for i, item := range items { + pt, err := e.decryptWithKey(keyName, item.Ciphertext, item.Context) + r := batchResult{Reference: item.Reference} + if err != nil { + r.Error = err.Error() + } else { + r.Plaintext = base64.StdEncoding.EncodeToString(pt) + } + results[i] = r + } + + return &engine.Response{ + Data: map[string]interface{}{"results": results}, + }, nil +} + +func (e *TransitEngine) handleBatchRewrap(_ context.Context, req *engine.Request) (*engine.Response, error) { + keyName, _ := req.Data["key"].(string) + if keyName == "" { + keyName, _ = req.Data["name"].(string) + } + if err := e.requireUserWithPolicy(req, keyName); err != nil { + return nil, err + } + + e.mu.RLock() + defer e.mu.RUnlock() + + if e.sealed() { + return nil, ErrSealed + } + + items, err := extractBatchItems(req.Data["items"]) + if err != nil { + return nil, err + } + if len(items) > maxBatchSize { + return nil, ErrBatchTooLarge + } + + results := make([]interface{}, len(items)) + for i, item := range items { + r := batchResult{Reference: item.Reference} + // Decrypt with old version. + pt, err := e.decryptWithKey(keyName, item.Ciphertext, item.Context) + if err != nil { + r.Error = err.Error() + results[i] = r + continue + } + // Re-encrypt with latest version. + ptB64 := base64.StdEncoding.EncodeToString(pt) + ct, err := e.encryptWithKey(keyName, ptB64, item.Context) + if err != nil { + r.Error = err.Error() + } else { + r.Ciphertext = ct + } + results[i] = r + } + + return &engine.Response{ + Data: map[string]interface{}{"results": results}, + }, nil +} + +// --- Sign/Verify Operations --- + +func (e *TransitEngine) handleSign(_ context.Context, req *engine.Request) (*engine.Response, error) { + keyName, _ := req.Data["key"].(string) + if keyName == "" { + keyName, _ = req.Data["name"].(string) + } + if err := e.requireUserWithPolicy(req, keyName); err != nil { + return nil, err + } + + e.mu.RLock() + defer e.mu.RUnlock() + + if e.sealed() { + return nil, ErrSealed + } + + if keyName == "" { + return nil, fmt.Errorf("transit: key name is required") + } + + ks, ok := e.keys[keyName] + if !ok { + return nil, ErrKeyNotFound + } + + if !isAsymmetric(ks.config.Type) { + return nil, ErrUnsupportedOp + } + + inputB64, _ := req.Data["input"].(string) + input, err := base64.StdEncoding.DecodeString(inputB64) + if err != nil { + return nil, fmt.Errorf("transit: invalid base64 input: %w", err) + } + + currentVersion := ks.config.CurrentVersion + kv, ok := ks.versions[currentVersion] + if !ok { + return nil, fmt.Errorf("transit: current key version %d not found", currentVersion) + } + + var sig []byte + switch ks.config.Type { + case "ed25519": + edKey, ok := kv.privKey.(ed25519.PrivateKey) + if !ok { + return nil, fmt.Errorf("transit: expected ed25519 key") + } + sig = ed25519.Sign(edKey, input) + case "ecdsa-p256": + ecKey, ok := kv.privKey.(*ecdsa.PrivateKey) + if !ok { + return nil, fmt.Errorf("transit: expected ECDSA key") + } + h := sha256.Sum256(input) + sig, err = ecdsa.SignASN1(rand.Reader, ecKey, h[:]) + if err != nil { + return nil, fmt.Errorf("transit: sign: %w", err) + } + case "ecdsa-p384": + ecKey, ok := kv.privKey.(*ecdsa.PrivateKey) + if !ok { + return nil, fmt.Errorf("transit: expected ECDSA key") + } + h := sha512.Sum384(input) + sig, err = ecdsa.SignASN1(rand.Reader, ecKey, h[:]) + if err != nil { + return nil, fmt.Errorf("transit: sign: %w", err) + } + default: + return nil, ErrUnsupportedOp + } + + return &engine.Response{ + Data: map[string]interface{}{ + "signature": formatSignature(currentVersion, sig), + }, + }, nil +} + +func (e *TransitEngine) handleVerify(_ context.Context, req *engine.Request) (*engine.Response, error) { + keyName, _ := req.Data["key"].(string) + if keyName == "" { + keyName, _ = req.Data["name"].(string) + } + if err := e.requireUserWithPolicy(req, keyName); err != nil { + return nil, err + } + + e.mu.RLock() + defer e.mu.RUnlock() + + if e.sealed() { + return nil, ErrSealed + } + + if keyName == "" { + return nil, fmt.Errorf("transit: key name is required") + } + + ks, ok := e.keys[keyName] + if !ok { + return nil, ErrKeyNotFound + } + + if !isAsymmetric(ks.config.Type) { + return nil, ErrUnsupportedOp + } + + inputB64, _ := req.Data["input"].(string) + input, err := base64.StdEncoding.DecodeString(inputB64) + if err != nil { + return nil, fmt.Errorf("transit: invalid base64 input: %w", err) + } + + signatureStr, _ := req.Data["signature"].(string) + version, sigBytes, err := parseVersionedData(signatureStr) + if err != nil { + return nil, fmt.Errorf("transit: invalid signature format: %w", err) + } + + kv, ok := ks.versions[version] + if !ok { + return nil, fmt.Errorf("transit: key version %d not found", version) + } + + valid := false + switch ks.config.Type { + case "ed25519": + edPub, ok := kv.pubKey.(ed25519.PublicKey) + if !ok { + return nil, fmt.Errorf("transit: expected ed25519 public key") + } + valid = ed25519.Verify(edPub, input, sigBytes) + case "ecdsa-p256": + ecPub, ok := kv.pubKey.(*ecdsa.PublicKey) + if !ok { + return nil, fmt.Errorf("transit: expected ECDSA public key") + } + h := sha256.Sum256(input) + valid = ecdsa.VerifyASN1(ecPub, h[:], sigBytes) + case "ecdsa-p384": + ecPub, ok := kv.pubKey.(*ecdsa.PublicKey) + if !ok { + return nil, fmt.Errorf("transit: expected ECDSA public key") + } + h := sha512.Sum384(input) + valid = ecdsa.VerifyASN1(ecPub, h[:], sigBytes) + default: + return nil, ErrUnsupportedOp + } + + return &engine.Response{ + Data: map[string]interface{}{"valid": valid}, + }, nil +} + +// --- HMAC Operation --- + +func (e *TransitEngine) handleHMAC(_ context.Context, req *engine.Request) (*engine.Response, error) { + keyName, _ := req.Data["key"].(string) + if keyName == "" { + keyName, _ = req.Data["name"].(string) + } + if err := e.requireUserWithPolicy(req, keyName); err != nil { + return nil, err + } + + e.mu.RLock() + defer e.mu.RUnlock() + + if e.sealed() { + return nil, ErrSealed + } + + if keyName == "" { + return nil, fmt.Errorf("transit: key name is required") + } + + ks, ok := e.keys[keyName] + if !ok { + return nil, ErrKeyNotFound + } + + if !isHMAC(ks.config.Type) { + return nil, ErrUnsupportedOp + } + + inputB64, _ := req.Data["input"].(string) + input, err := base64.StdEncoding.DecodeString(inputB64) + if err != nil { + return nil, fmt.Errorf("transit: invalid base64 input: %w", err) + } + + // Verify mode: if hmac is provided, verify it. + if hmacStr, ok := req.Data["hmac"].(string); ok && hmacStr != "" { + version, macBytes, err := parseVersionedData(hmacStr) + if err != nil { + return nil, fmt.Errorf("transit: invalid hmac format: %w", err) + } + + kv, ok := ks.versions[version] + if !ok { + return nil, fmt.Errorf("transit: key version %d not found", version) + } + + expected := computeHMAC(ks.config.Type, kv.key, input) + valid := hmac.Equal(macBytes, expected) + + return &engine.Response{ + Data: map[string]interface{}{"valid": valid}, + }, nil + } + + // Compute mode. + currentVersion := ks.config.CurrentVersion + kv, ok := ks.versions[currentVersion] + if !ok { + return nil, fmt.Errorf("transit: current key version %d not found", currentVersion) + } + + mac := computeHMAC(ks.config.Type, kv.key, input) + return &engine.Response{ + Data: map[string]interface{}{ + "hmac": formatHMAC(currentVersion, mac), + }, + }, nil +} + +// --- Get Public Key --- + +func (e *TransitEngine) handleGetPublicKey(_ context.Context, req *engine.Request) (*engine.Response, error) { + if err := e.requireUser(req); err != nil { + return nil, err + } + + e.mu.RLock() + defer e.mu.RUnlock() + + if e.sealed() { + return nil, ErrSealed + } + + keyName, _ := req.Data["name"].(string) + if keyName == "" { + return nil, fmt.Errorf("transit: name is required") + } + + ks, ok := e.keys[keyName] + if !ok { + return nil, ErrKeyNotFound + } + + if !isAsymmetric(ks.config.Type) { + return nil, ErrUnsupportedOp + } + + version := ks.config.CurrentVersion + if v, ok := req.Data["version"]; ok { + version = toInt(v) + } + + kv, ok := ks.versions[version] + if !ok { + return nil, fmt.Errorf("transit: key version %d not found", version) + } + + pubKeyBytes, err := x509.MarshalPKIXPublicKey(kv.pubKey) + if err != nil { + return nil, fmt.Errorf("transit: marshal public key: %w", err) + } + + return &engine.Response{ + Data: map[string]interface{}{ + "public_key": base64.StdEncoding.EncodeToString(pubKeyBytes), + "version": version, + "type": ks.config.Type, + }, + }, nil +} + +// --- Storage helpers --- + +func (e *TransitEngine) storeKeyConfig(ctx context.Context, prefix string, cfg *KeyConfig) error { + data, err := json.Marshal(cfg) + if err != nil { + return fmt.Errorf("transit: marshal key config: %w", err) + } + return e.barrier.Put(ctx, prefix+"config.json", data) +} + +func (e *TransitEngine) storeKeyVersion(ctx context.Context, prefix string, cfg *KeyConfig, kv *keyVersion) error { + path := fmt.Sprintf("%sv%d.key", prefix, kv.version) + + var data []byte + switch cfg.Type { + case "aes256-gcm", "chacha20-poly", "hmac-sha256", "hmac-sha512": + data = kv.key + case "ed25519": + data = kv.key // raw 64-byte private key + case "ecdsa-p256", "ecdsa-p384": + var err error + data, err = x509.MarshalPKCS8PrivateKey(kv.privKey) + if err != nil { + return fmt.Errorf("transit: marshal PKCS8 key: %w", err) + } + default: + return fmt.Errorf("transit: unknown key type: %s", cfg.Type) + } + + return e.barrier.Put(ctx, path, data) +} + +// --- Key generation --- + +func generateKeyVersion(keyType string, version int) (*keyVersion, error) { + kv := &keyVersion{version: version} + + switch keyType { + case "aes256-gcm": + key := make([]byte, 32) + if _, err := rand.Read(key); err != nil { + return nil, err + } + kv.key = key + case "chacha20-poly": + key := make([]byte, 32) + if _, err := rand.Read(key); err != nil { + return nil, err + } + kv.key = key + case "ed25519": + _, privKey, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + return nil, err + } + kv.key = []byte(privKey) + kv.privKey = privKey + kv.pubKey = privKey.Public() + case "ecdsa-p256": + privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return nil, err + } + kv.privKey = privKey + kv.pubKey = &privKey.PublicKey + case "ecdsa-p384": + privKey, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) + if err != nil { + return nil, err + } + kv.privKey = privKey + kv.pubKey = &privKey.PublicKey + case "hmac-sha256": + key := make([]byte, 32) + if _, err := rand.Read(key); err != nil { + return nil, err + } + kv.key = key + case "hmac-sha512": + key := make([]byte, 64) + if _, err := rand.Read(key); err != nil { + return nil, err + } + kv.key = key + default: + return nil, fmt.Errorf("unknown key type: %s", keyType) + } + + return kv, nil +} + +// --- Encryption/Decryption helpers --- + +func encryptData(keyType string, key, plaintext, aad []byte) ([]byte, error) { + aead, err := newAEAD(keyType, key) + if err != nil { + return nil, err + } + + nonce := make([]byte, aead.NonceSize()) + if _, err := rand.Read(nonce); err != nil { + return nil, fmt.Errorf("transit: generate nonce: %w", err) + } + + ciphertext := aead.Seal(nil, nonce, plaintext, aad) + + // Format: nonce + ciphertext (includes tag) + result := make([]byte, len(nonce)+len(ciphertext)) + copy(result, nonce) + copy(result[len(nonce):], ciphertext) + return result, nil +} + +func decryptData(keyType string, key, data, aad []byte) ([]byte, error) { + aead, err := newAEAD(keyType, key) + if err != nil { + return nil, err + } + + nonceSize := aead.NonceSize() + if len(data) < nonceSize { + return nil, ErrInvalidFormat + } + + nonce := data[:nonceSize] + ciphertext := data[nonceSize:] + + plaintext, err := aead.Open(nil, nonce, ciphertext, aad) + if err != nil { + return nil, fmt.Errorf("transit: decryption failed: %w", err) + } + return plaintext, nil +} + +func newAEAD(keyType string, key []byte) (cipher.AEAD, error) { + switch keyType { + case "aes256-gcm": + block, err := aes.NewCipher(key) + if err != nil { + return nil, fmt.Errorf("transit: new AES cipher: %w", err) + } + return cipher.NewGCM(block) + case "chacha20-poly": + return chacha20poly1305.NewX(key) + default: + return nil, fmt.Errorf("transit: unsupported encryption type: %s", keyType) + } +} + +// --- HMAC helpers --- + +func computeHMAC(keyType string, key, input []byte) []byte { + var h func() hash.Hash + switch keyType { + case "hmac-sha256": + h = sha256.New + case "hmac-sha512": + h = sha512.New + default: + return nil + } + mac := hmac.New(h, key) + mac.Write(input) + return mac.Sum(nil) +} + +// --- Format helpers --- + +func formatCiphertext(version int, data []byte) string { + return fmt.Sprintf("metacrypt:v%d:%s", version, base64.StdEncoding.EncodeToString(data)) +} + +func formatSignature(version int, sig []byte) string { + return fmt.Sprintf("metacrypt:v%d:%s", version, base64.StdEncoding.EncodeToString(sig)) +} + +func formatHMAC(version int, mac []byte) string { + return fmt.Sprintf("metacrypt:v%d:%s", version, base64.StdEncoding.EncodeToString(mac)) +} + +func parseCiphertext(s string) (int, []byte, error) { + return parseVersionedData(s) +} + +func parseVersionedData(s string) (int, []byte, error) { + parts := strings.SplitN(s, ":", 3) + if len(parts) != 3 || parts[0] != "metacrypt" { + return 0, nil, ErrInvalidFormat + } + + if !strings.HasPrefix(parts[1], "v") { + return 0, nil, ErrInvalidFormat + } + + version, err := strconv.Atoi(parts[1][1:]) + if err != nil { + return 0, nil, ErrInvalidFormat + } + + data, err := base64.StdEncoding.DecodeString(parts[2]) + if err != nil { + return 0, nil, fmt.Errorf("transit: invalid base64: %w", err) + } + + return version, data, nil +} + +// --- Type helpers --- + +func isValidKeyType(t string) bool { + switch t { + case "aes256-gcm", "chacha20-poly", "ed25519", "ecdsa-p256", "ecdsa-p384", "hmac-sha256", "hmac-sha512": + return true + } + return false +} + +func isSymmetric(t string) bool { + return t == "aes256-gcm" || t == "chacha20-poly" +} + +func isAsymmetric(t string) bool { + return t == "ed25519" || t == "ecdsa-p256" || t == "ecdsa-p384" +} + +func isHMAC(t string) bool { + return t == "hmac-sha256" || t == "hmac-sha512" +} + +// --- Utility --- + +func toInt(v interface{}) int { + switch val := v.(type) { + case float64: + return int(val) + case int: + return val + case int64: + return int(val) + case json.Number: + n, _ := val.Int64() + return int(n) + } + return 0 +} + +func extractBatchItems(v interface{}) ([]batchItem, error) { + raw, ok := v.([]interface{}) + if !ok { + return nil, fmt.Errorf("transit: items must be an array") + } + + items := make([]batchItem, len(raw)) + for i, r := range raw { + m, ok := r.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("transit: item %d is not an object", i) + } + items[i].Plaintext, _ = m["plaintext"].(string) + items[i].Ciphertext, _ = m["ciphertext"].(string) + items[i].Context, _ = m["context"].(string) + items[i].Reference, _ = m["reference"].(string) + } + return items, nil +} + +func zeroizeKey(key crypto.PrivateKey) { + if key == nil { + return + } + switch k := key.(type) { + case *ecdsa.PrivateKey: + k.D.SetInt64(0) + case ed25519.PrivateKey: + for i := range k { + k[i] = 0 + } + } +} diff --git a/internal/engine/transit/transit_test.go b/internal/engine/transit/transit_test.go new file mode 100644 index 0000000..66a281b --- /dev/null +++ b/internal/engine/transit/transit_test.go @@ -0,0 +1,1025 @@ +package transit + +import ( + "context" + "encoding/base64" + "strings" + "sync" + "testing" + + "git.wntrmute.dev/kyle/metacrypt/internal/barrier" + "git.wntrmute.dev/kyle/metacrypt/internal/engine" +) + +// memBarrier is an in-memory barrier for testing. +type memBarrier struct { + data map[string][]byte + mu sync.RWMutex +} + +func newMemBarrier() *memBarrier { + return &memBarrier{data: make(map[string][]byte)} +} + +func (m *memBarrier) Unseal(_ []byte) error { return nil } +func (m *memBarrier) Seal() error { return nil } +func (m *memBarrier) IsSealed() bool { return false } + +func (m *memBarrier) Get(_ context.Context, path string) ([]byte, error) { + m.mu.RLock() + defer m.mu.RUnlock() + v, ok := m.data[path] + if !ok { + return nil, barrier.ErrNotFound + } + cp := make([]byte, len(v)) + copy(cp, v) + return cp, nil +} + +func (m *memBarrier) Put(_ context.Context, path string, value []byte) error { + m.mu.Lock() + defer m.mu.Unlock() + cp := make([]byte, len(value)) + copy(cp, value) + m.data[path] = cp + return nil +} + +func (m *memBarrier) Delete(_ context.Context, path string) error { + m.mu.Lock() + defer m.mu.Unlock() + delete(m.data, path) + return nil +} + +func (m *memBarrier) List(_ context.Context, prefix string) ([]string, error) { + m.mu.RLock() + defer m.mu.RUnlock() + var paths []string + for k := range m.data { + if strings.HasPrefix(k, prefix) { + paths = append(paths, strings.TrimPrefix(k, prefix)) + } + } + return paths, nil +} + +func adminCaller() *engine.CallerInfo { + return &engine.CallerInfo{Username: "admin", Roles: []string{"admin"}, IsAdmin: true} +} + +func userCaller() *engine.CallerInfo { + return &engine.CallerInfo{Username: "user", Roles: []string{"user"}, IsAdmin: false} +} + +func guestCaller() *engine.CallerInfo { + return &engine.CallerInfo{Username: "guest", Roles: []string{"guest"}, IsAdmin: false} +} + +func setupEngine(t *testing.T) (*TransitEngine, *memBarrier) { + t.Helper() + b := newMemBarrier() + eng := NewTransitEngine() + ctx := context.Background() + mountPath := "engine/transit/test/" + + err := eng.Initialize(ctx, b, mountPath, nil) + if err != nil { + t.Fatalf("Initialize: %v", err) + } + + te := eng.(*TransitEngine) + return te, b +} + +func setupEngineWithUnseal(t *testing.T) (*TransitEngine, *memBarrier) { + t.Helper() + te, b := setupEngine(t) + + // Seal and unseal to test the full lifecycle. + if err := te.Seal(); err != nil { + t.Fatalf("Seal: %v", err) + } + + eng2 := NewTransitEngine() + ctx := context.Background() + if err := eng2.Unseal(ctx, b, "engine/transit/test/"); err != nil { + t.Fatalf("Unseal: %v", err) + } + return eng2.(*TransitEngine), b +} + +func createKey(t *testing.T, te *TransitEngine, name, keyType string) { + t.Helper() + ctx := context.Background() + _, err := te.HandleRequest(ctx, &engine.Request{ + Operation: "create-key", + CallerInfo: adminCaller(), + Data: map[string]interface{}{ + "name": name, + "type": keyType, + }, + }) + if err != nil { + t.Fatalf("create-key %s: %v", name, err) + } +} + +func TestInitializeAndUnseal(t *testing.T) { + te, b := setupEngine(t) + + // Create a key so unseal has something to load. + createKey(t, te, "mykey", "aes256-gcm") + + if err := te.Seal(); err != nil { + t.Fatalf("Seal: %v", err) + } + + eng2 := NewTransitEngine() + if err := eng2.Unseal(context.Background(), b, "engine/transit/test/"); err != nil { + t.Fatalf("Unseal: %v", err) + } + + te2 := eng2.(*TransitEngine) + if _, ok := te2.keys["mykey"]; !ok { + t.Fatal("expected key 'mykey' after unseal") + } +} + +func TestCreateKeyAllTypes(t *testing.T) { + types := []string{"aes256-gcm", "chacha20-poly", "ed25519", "ecdsa-p256", "ecdsa-p384", "hmac-sha256", "hmac-sha512"} + for _, kt := range types { + t.Run(kt, func(t *testing.T) { + te, _ := setupEngine(t) + createKey(t, te, "key-"+kt, kt) + + ks, ok := te.keys["key-"+kt] + if !ok { + t.Fatal("key not created") + } + if ks.config.CurrentVersion != 1 { + t.Fatalf("expected version 1, got %d", ks.config.CurrentVersion) + } + if ks.config.MinDecryptionVersion != 1 { + t.Fatalf("expected min_decryption_version 1, got %d", ks.config.MinDecryptionVersion) + } + }) + } +} + +func TestEncryptDecryptAES(t *testing.T) { + te, _ := setupEngine(t) + createKey(t, te, "aes-key", "aes256-gcm") + ctx := context.Background() + + plaintext := base64.StdEncoding.EncodeToString([]byte("hello world")) + + // Encrypt. + resp, err := te.HandleRequest(ctx, &engine.Request{ + Operation: "encrypt", + CallerInfo: userCaller(), + Data: map[string]interface{}{ + "key": "aes-key", + "plaintext": plaintext, + }, + }) + if err != nil { + t.Fatalf("encrypt: %v", err) + } + + ct, _ := resp.Data["ciphertext"].(string) + if !strings.HasPrefix(ct, "metacrypt:v1:") { + t.Fatalf("unexpected ciphertext format: %s", ct) + } + + // Decrypt. + resp, err = te.HandleRequest(ctx, &engine.Request{ + Operation: "decrypt", + CallerInfo: userCaller(), + Data: map[string]interface{}{ + "key": "aes-key", + "ciphertext": ct, + }, + }) + if err != nil { + t.Fatalf("decrypt: %v", err) + } + + ptB64, _ := resp.Data["plaintext"].(string) + decoded, _ := base64.StdEncoding.DecodeString(ptB64) + if string(decoded) != "hello world" { + t.Fatalf("expected 'hello world', got %q", string(decoded)) + } +} + +func TestEncryptDecryptChaCha(t *testing.T) { + te, _ := setupEngine(t) + createKey(t, te, "chacha-key", "chacha20-poly") + ctx := context.Background() + + plaintext := base64.StdEncoding.EncodeToString([]byte("secret data")) + + resp, err := te.HandleRequest(ctx, &engine.Request{ + Operation: "encrypt", + CallerInfo: userCaller(), + Data: map[string]interface{}{"key": "chacha-key", "plaintext": plaintext}, + }) + if err != nil { + t.Fatalf("encrypt: %v", err) + } + + ct, _ := resp.Data["ciphertext"].(string) + if !strings.HasPrefix(ct, "metacrypt:v1:") { + t.Fatalf("unexpected ciphertext format: %s", ct) + } + + resp, err = te.HandleRequest(ctx, &engine.Request{ + Operation: "decrypt", + CallerInfo: userCaller(), + Data: map[string]interface{}{"key": "chacha-key", "ciphertext": ct}, + }) + if err != nil { + t.Fatalf("decrypt: %v", err) + } + + ptB64, _ := resp.Data["plaintext"].(string) + decoded, _ := base64.StdEncoding.DecodeString(ptB64) + if string(decoded) != "secret data" { + t.Fatalf("expected 'secret data', got %q", string(decoded)) + } +} + +func TestEncryptWithContext(t *testing.T) { + te, _ := setupEngine(t) + createKey(t, te, "ctx-key", "aes256-gcm") + ctx := context.Background() + + plaintext := base64.StdEncoding.EncodeToString([]byte("context test")) + aadB64 := base64.StdEncoding.EncodeToString([]byte("my-context")) + + resp, err := te.HandleRequest(ctx, &engine.Request{ + Operation: "encrypt", + CallerInfo: userCaller(), + Data: map[string]interface{}{"key": "ctx-key", "plaintext": plaintext, "context": aadB64}, + }) + if err != nil { + t.Fatalf("encrypt: %v", err) + } + ct, _ := resp.Data["ciphertext"].(string) + + // Decrypt with correct context. + resp, err = te.HandleRequest(ctx, &engine.Request{ + Operation: "decrypt", + CallerInfo: userCaller(), + Data: map[string]interface{}{"key": "ctx-key", "ciphertext": ct, "context": aadB64}, + }) + if err != nil { + t.Fatalf("decrypt: %v", err) + } + + // Decrypt with wrong context should fail. + wrongCtx := base64.StdEncoding.EncodeToString([]byte("wrong-context")) + _, err = te.HandleRequest(ctx, &engine.Request{ + Operation: "decrypt", + CallerInfo: userCaller(), + Data: map[string]interface{}{"key": "ctx-key", "ciphertext": ct, "context": wrongCtx}, + }) + if err == nil { + t.Fatal("expected decrypt to fail with wrong context") + } +} + +func TestKeyRotationAndDecrypt(t *testing.T) { + te, _ := setupEngine(t) + createKey(t, te, "rot-key", "aes256-gcm") + ctx := context.Background() + + plaintext := base64.StdEncoding.EncodeToString([]byte("rotate me")) + + // Encrypt with v1. + resp, err := te.HandleRequest(ctx, &engine.Request{ + Operation: "encrypt", + CallerInfo: userCaller(), + Data: map[string]interface{}{"key": "rot-key", "plaintext": plaintext}, + }) + if err != nil { + t.Fatalf("encrypt v1: %v", err) + } + ctV1, _ := resp.Data["ciphertext"].(string) + + // Rotate. + _, err = te.HandleRequest(ctx, &engine.Request{ + Operation: "rotate-key", + CallerInfo: adminCaller(), + Data: map[string]interface{}{"name": "rot-key"}, + }) + if err != nil { + t.Fatalf("rotate: %v", err) + } + + // Encrypt with v2. + resp, err = te.HandleRequest(ctx, &engine.Request{ + Operation: "encrypt", + CallerInfo: userCaller(), + Data: map[string]interface{}{"key": "rot-key", "plaintext": plaintext}, + }) + if err != nil { + t.Fatalf("encrypt v2: %v", err) + } + ctV2, _ := resp.Data["ciphertext"].(string) + + if !strings.HasPrefix(ctV2, "metacrypt:v2:") { + t.Fatalf("expected v2 ciphertext, got %s", ctV2) + } + + // Both ciphertexts should decrypt. + for _, ct := range []string{ctV1, ctV2} { + resp, err = te.HandleRequest(ctx, &engine.Request{ + Operation: "decrypt", + CallerInfo: userCaller(), + Data: map[string]interface{}{"key": "rot-key", "ciphertext": ct}, + }) + if err != nil { + t.Fatalf("decrypt: %v", err) + } + ptB64, _ := resp.Data["plaintext"].(string) + decoded, _ := base64.StdEncoding.DecodeString(ptB64) + if string(decoded) != "rotate me" { + t.Fatalf("expected 'rotate me', got %q", string(decoded)) + } + } +} + +func TestUpdateKeyConfig(t *testing.T) { + te, _ := setupEngine(t) + createKey(t, te, "cfg-key", "aes256-gcm") + ctx := context.Background() + + plaintext := base64.StdEncoding.EncodeToString([]byte("old data")) + + // Encrypt with v1. + resp, err := te.HandleRequest(ctx, &engine.Request{ + Operation: "encrypt", + CallerInfo: userCaller(), + Data: map[string]interface{}{"key": "cfg-key", "plaintext": plaintext}, + }) + if err != nil { + t.Fatalf("encrypt: %v", err) + } + ctV1, _ := resp.Data["ciphertext"].(string) + + // Rotate to v2. + _, err = te.HandleRequest(ctx, &engine.Request{ + Operation: "rotate-key", + CallerInfo: adminCaller(), + Data: map[string]interface{}{"name": "cfg-key"}, + }) + if err != nil { + t.Fatalf("rotate: %v", err) + } + + // Advance min_decryption_version to 2. + _, err = te.HandleRequest(ctx, &engine.Request{ + Operation: "update-key-config", + CallerInfo: adminCaller(), + Data: map[string]interface{}{"name": "cfg-key", "min_decryption_version": float64(2)}, + }) + if err != nil { + t.Fatalf("update-key-config: %v", err) + } + + // v1 ciphertext should be rejected. + _, err = te.HandleRequest(ctx, &engine.Request{ + Operation: "decrypt", + CallerInfo: userCaller(), + Data: map[string]interface{}{"key": "cfg-key", "ciphertext": ctV1}, + }) + if err == nil { + t.Fatal("expected decrypt to fail for v1 ciphertext") + } + + // Cannot decrease min_decryption_version. + _, err = te.HandleRequest(ctx, &engine.Request{ + Operation: "update-key-config", + CallerInfo: adminCaller(), + Data: map[string]interface{}{"name": "cfg-key", "min_decryption_version": float64(1)}, + }) + if err == nil { + t.Fatal("expected error decreasing min_decryption_version") + } + + // Cannot exceed current version. + _, err = te.HandleRequest(ctx, &engine.Request{ + Operation: "update-key-config", + CallerInfo: adminCaller(), + Data: map[string]interface{}{"name": "cfg-key", "min_decryption_version": float64(99)}, + }) + if err == nil { + t.Fatal("expected error exceeding current version") + } +} + +func TestTrimKey(t *testing.T) { + te, _ := setupEngine(t) + createKey(t, te, "trim-key", "aes256-gcm") + ctx := context.Background() + + // Rotate to v2, v3. + for i := 0; i < 2; i++ { + _, err := te.HandleRequest(ctx, &engine.Request{ + Operation: "rotate-key", + CallerInfo: adminCaller(), + Data: map[string]interface{}{"name": "trim-key"}, + }) + if err != nil { + t.Fatalf("rotate: %v", err) + } + } + + // Set min_decryption_version to 2. + _, err := te.HandleRequest(ctx, &engine.Request{ + Operation: "update-key-config", + CallerInfo: adminCaller(), + Data: map[string]interface{}{"name": "trim-key", "min_decryption_version": float64(2)}, + }) + if err != nil { + t.Fatalf("update-key-config: %v", err) + } + + // Trim. + resp, err := te.HandleRequest(ctx, &engine.Request{ + Operation: "trim-key", + CallerInfo: adminCaller(), + Data: map[string]interface{}{"name": "trim-key"}, + }) + if err != nil { + t.Fatalf("trim-key: %v", err) + } + + trimmed, _ := resp.Data["trimmed"].(int) + if trimmed != 1 { + t.Fatalf("expected 1 trimmed, got %d", trimmed) + } + + // Version 1 should be gone. + if _, ok := te.keys["trim-key"].versions[1]; ok { + t.Fatal("version 1 should have been trimmed") + } + if _, ok := te.keys["trim-key"].versions[2]; !ok { + t.Fatal("version 2 should still exist") + } +} + +func TestSignVerifyEd25519(t *testing.T) { + te, _ := setupEngine(t) + createKey(t, te, "ed-key", "ed25519") + ctx := context.Background() + + input := base64.StdEncoding.EncodeToString([]byte("sign this")) + + resp, err := te.HandleRequest(ctx, &engine.Request{ + Operation: "sign", + CallerInfo: userCaller(), + Data: map[string]interface{}{"key": "ed-key", "input": input}, + }) + if err != nil { + t.Fatalf("sign: %v", err) + } + sig, _ := resp.Data["signature"].(string) + if !strings.HasPrefix(sig, "metacrypt:v1:") { + t.Fatalf("unexpected signature format: %s", sig) + } + + // Verify. + resp, err = te.HandleRequest(ctx, &engine.Request{ + Operation: "verify", + CallerInfo: userCaller(), + Data: map[string]interface{}{"key": "ed-key", "input": input, "signature": sig}, + }) + if err != nil { + t.Fatalf("verify: %v", err) + } + valid, _ := resp.Data["valid"].(bool) + if !valid { + t.Fatal("expected valid signature") + } + + // Wrong input should fail verification. + wrongInput := base64.StdEncoding.EncodeToString([]byte("wrong data")) + resp, err = te.HandleRequest(ctx, &engine.Request{ + Operation: "verify", + CallerInfo: userCaller(), + Data: map[string]interface{}{"key": "ed-key", "input": wrongInput, "signature": sig}, + }) + if err != nil { + t.Fatalf("verify: %v", err) + } + valid, _ = resp.Data["valid"].(bool) + if valid { + t.Fatal("expected invalid signature for wrong input") + } +} + +func TestSignVerifyECDSA(t *testing.T) { + for _, keyType := range []string{"ecdsa-p256", "ecdsa-p384"} { + t.Run(keyType, func(t *testing.T) { + te, _ := setupEngine(t) + createKey(t, te, "ec-key", keyType) + ctx := context.Background() + + input := base64.StdEncoding.EncodeToString([]byte("ecdsa test")) + + resp, err := te.HandleRequest(ctx, &engine.Request{ + Operation: "sign", + CallerInfo: userCaller(), + Data: map[string]interface{}{"key": "ec-key", "input": input}, + }) + if err != nil { + t.Fatalf("sign: %v", err) + } + sig, _ := resp.Data["signature"].(string) + + resp, err = te.HandleRequest(ctx, &engine.Request{ + Operation: "verify", + CallerInfo: userCaller(), + Data: map[string]interface{}{"key": "ec-key", "input": input, "signature": sig}, + }) + if err != nil { + t.Fatalf("verify: %v", err) + } + valid, _ := resp.Data["valid"].(bool) + if !valid { + t.Fatal("expected valid signature") + } + }) + } +} + +func TestSignRejectsSymmetricAndHMAC(t *testing.T) { + for _, keyType := range []string{"aes256-gcm", "hmac-sha256"} { + t.Run(keyType, func(t *testing.T) { + te, _ := setupEngine(t) + createKey(t, te, "sym-key", keyType) + ctx := context.Background() + + input := base64.StdEncoding.EncodeToString([]byte("test")) + _, err := te.HandleRequest(ctx, &engine.Request{ + Operation: "sign", + CallerInfo: userCaller(), + Data: map[string]interface{}{"key": "sym-key", "input": input}, + }) + if err == nil { + t.Fatal("expected error signing with non-asymmetric key") + } + }) + } +} + +func TestHMACComputeAndVerify(t *testing.T) { + for _, keyType := range []string{"hmac-sha256", "hmac-sha512"} { + t.Run(keyType, func(t *testing.T) { + te, _ := setupEngine(t) + createKey(t, te, "hmac-key", keyType) + ctx := context.Background() + + input := base64.StdEncoding.EncodeToString([]byte("hmac me")) + + // Compute. + resp, err := te.HandleRequest(ctx, &engine.Request{ + Operation: "hmac", + CallerInfo: userCaller(), + Data: map[string]interface{}{"key": "hmac-key", "input": input}, + }) + if err != nil { + t.Fatalf("hmac compute: %v", err) + } + hmacStr, _ := resp.Data["hmac"].(string) + if !strings.HasPrefix(hmacStr, "metacrypt:v1:") { + t.Fatalf("unexpected hmac format: %s", hmacStr) + } + + // Verify. + resp, err = te.HandleRequest(ctx, &engine.Request{ + Operation: "hmac", + CallerInfo: userCaller(), + Data: map[string]interface{}{"key": "hmac-key", "input": input, "hmac": hmacStr}, + }) + if err != nil { + t.Fatalf("hmac verify: %v", err) + } + valid, _ := resp.Data["valid"].(bool) + if !valid { + t.Fatal("expected valid HMAC") + } + + // Wrong input should fail. + wrongInput := base64.StdEncoding.EncodeToString([]byte("wrong data")) + resp, err = te.HandleRequest(ctx, &engine.Request{ + Operation: "hmac", + CallerInfo: userCaller(), + Data: map[string]interface{}{"key": "hmac-key", "input": wrongInput, "hmac": hmacStr}, + }) + if err != nil { + t.Fatalf("hmac verify wrong: %v", err) + } + valid, _ = resp.Data["valid"].(bool) + if valid { + t.Fatal("expected invalid HMAC for wrong input") + } + }) + } +} + +func TestBatchEncryptDecrypt(t *testing.T) { + te, _ := setupEngine(t) + createKey(t, te, "batch-key", "aes256-gcm") + ctx := context.Background() + + items := []interface{}{ + map[string]interface{}{ + "plaintext": base64.StdEncoding.EncodeToString([]byte("item1")), + "reference": "ref1", + }, + map[string]interface{}{ + "plaintext": base64.StdEncoding.EncodeToString([]byte("item2")), + "reference": "ref2", + }, + } + + // Batch encrypt. + resp, err := te.HandleRequest(ctx, &engine.Request{ + Operation: "batch-encrypt", + CallerInfo: userCaller(), + Data: map[string]interface{}{"key": "batch-key", "items": items}, + }) + if err != nil { + t.Fatalf("batch-encrypt: %v", err) + } + + results, _ := resp.Data["results"].([]interface{}) + if len(results) != 2 { + t.Fatalf("expected 2 results, got %d", len(results)) + } + + // Build decrypt items. + decryptItems := make([]interface{}, len(results)) + for i, r := range results { + br, ok := r.(batchResult) + if !ok { + t.Fatalf("expected batchResult, got %T", r) + } + if br.Error != "" { + t.Fatalf("batch encrypt item %d error: %s", i, br.Error) + } + decryptItems[i] = map[string]interface{}{ + "ciphertext": br.Ciphertext, + "reference": br.Reference, + } + } + + // Batch decrypt. + resp, err = te.HandleRequest(ctx, &engine.Request{ + Operation: "batch-decrypt", + CallerInfo: userCaller(), + Data: map[string]interface{}{"key": "batch-key", "items": decryptItems}, + }) + if err != nil { + t.Fatalf("batch-decrypt: %v", err) + } + + results, _ = resp.Data["results"].([]interface{}) + expected := []string{"item1", "item2"} + for i, r := range results { + br, ok := r.(batchResult) + if !ok { + t.Fatalf("expected batchResult, got %T", r) + } + if br.Error != "" { + t.Fatalf("batch decrypt item %d error: %s", i, br.Error) + } + decoded, _ := base64.StdEncoding.DecodeString(br.Plaintext) + if string(decoded) != expected[i] { + t.Fatalf("item %d: expected %q, got %q", i, expected[i], string(decoded)) + } + } +} + +func TestBatchPartialErrors(t *testing.T) { + te, _ := setupEngine(t) + createKey(t, te, "batch-err-key", "aes256-gcm") + ctx := context.Background() + + items := []interface{}{ + map[string]interface{}{ + "ciphertext": "metacrypt:v1:invalidbase64!!!", + "reference": "bad", + }, + map[string]interface{}{ + "ciphertext": "metacrypt:v1:" + base64.StdEncoding.EncodeToString([]byte("not-valid-ciphertext")), + "reference": "also-bad", + }, + } + + resp, err := te.HandleRequest(ctx, &engine.Request{ + Operation: "batch-decrypt", + CallerInfo: userCaller(), + Data: map[string]interface{}{"key": "batch-err-key", "items": items}, + }) + if err != nil { + t.Fatalf("batch-decrypt: %v", err) + } + + results, _ := resp.Data["results"].([]interface{}) + if len(results) != 2 { + t.Fatalf("expected 2 results, got %d", len(results)) + } + for i, r := range results { + br, ok := r.(batchResult) + if !ok { + t.Fatalf("expected batchResult, got %T", r) + } + if br.Error == "" { + t.Fatalf("item %d: expected error", i) + } + } +} + +func TestRewrap(t *testing.T) { + te, _ := setupEngine(t) + createKey(t, te, "rewrap-key", "aes256-gcm") + ctx := context.Background() + + plaintext := base64.StdEncoding.EncodeToString([]byte("rewrap me")) + + // Encrypt with v1. + resp, err := te.HandleRequest(ctx, &engine.Request{ + Operation: "encrypt", + CallerInfo: userCaller(), + Data: map[string]interface{}{"key": "rewrap-key", "plaintext": plaintext}, + }) + if err != nil { + t.Fatalf("encrypt: %v", err) + } + ctV1, _ := resp.Data["ciphertext"].(string) + + // Rotate. + _, err = te.HandleRequest(ctx, &engine.Request{ + Operation: "rotate-key", + CallerInfo: adminCaller(), + Data: map[string]interface{}{"name": "rewrap-key"}, + }) + if err != nil { + t.Fatalf("rotate: %v", err) + } + + // Rewrap. + resp, err = te.HandleRequest(ctx, &engine.Request{ + Operation: "rewrap", + CallerInfo: userCaller(), + Data: map[string]interface{}{"key": "rewrap-key", "ciphertext": ctV1}, + }) + if err != nil { + t.Fatalf("rewrap: %v", err) + } + ctV2, _ := resp.Data["ciphertext"].(string) + if !strings.HasPrefix(ctV2, "metacrypt:v2:") { + t.Fatalf("expected v2 ciphertext after rewrap, got %s", ctV2) + } + + // Decrypt rewrapped ciphertext. + resp, err = te.HandleRequest(ctx, &engine.Request{ + Operation: "decrypt", + CallerInfo: userCaller(), + Data: map[string]interface{}{"key": "rewrap-key", "ciphertext": ctV2}, + }) + if err != nil { + t.Fatalf("decrypt rewrapped: %v", err) + } + ptB64, _ := resp.Data["plaintext"].(string) + decoded, _ := base64.StdEncoding.DecodeString(ptB64) + if string(decoded) != "rewrap me" { + t.Fatalf("expected 'rewrap me', got %q", string(decoded)) + } +} + +func TestAuthEnforcement(t *testing.T) { + te, _ := setupEngine(t) + ctx := context.Background() + + // Admin-only operations should fail for users. + _, err := te.HandleRequest(ctx, &engine.Request{ + Operation: "create-key", + CallerInfo: userCaller(), + Data: map[string]interface{}{"name": "test", "type": "aes256-gcm"}, + }) + if err == nil { + t.Fatal("expected create-key to fail for user") + } + + // Admin-only operations should fail for guests. + _, err = te.HandleRequest(ctx, &engine.Request{ + Operation: "create-key", + CallerInfo: guestCaller(), + Data: map[string]interface{}{"name": "test", "type": "aes256-gcm"}, + }) + if err == nil { + t.Fatal("expected create-key to fail for guest") + } + + // User operations should fail for guests. + createKey(t, te, "auth-key", "aes256-gcm") + + _, err = te.HandleRequest(ctx, &engine.Request{ + Operation: "list-keys", + CallerInfo: guestCaller(), + }) + if err == nil { + t.Fatal("expected list-keys to fail for guest") + } + + // Encrypt should fail for guest. + _, err = te.HandleRequest(ctx, &engine.Request{ + Operation: "encrypt", + CallerInfo: guestCaller(), + Data: map[string]interface{}{"key": "auth-key", "plaintext": base64.StdEncoding.EncodeToString([]byte("test"))}, + }) + if err == nil { + t.Fatal("expected encrypt to fail for guest") + } + + // Unauthenticated should fail. + _, err = te.HandleRequest(ctx, &engine.Request{ + Operation: "create-key", + CallerInfo: nil, + Data: map[string]interface{}{"name": "test", "type": "aes256-gcm"}, + }) + if err == nil { + t.Fatal("expected create-key to fail without auth") + } +} + +func TestDeleteKeyWithAndWithoutAllowDeletion(t *testing.T) { + te, _ := setupEngine(t) + ctx := context.Background() + + createKey(t, te, "nodelete-key", "aes256-gcm") + + // Should fail: allow_deletion is false. + _, err := te.HandleRequest(ctx, &engine.Request{ + Operation: "delete-key", + CallerInfo: adminCaller(), + Data: map[string]interface{}{"name": "nodelete-key"}, + }) + if err == nil { + t.Fatal("expected delete to fail without allow_deletion") + } + + // Enable deletion. + _, err = te.HandleRequest(ctx, &engine.Request{ + Operation: "update-key-config", + CallerInfo: adminCaller(), + Data: map[string]interface{}{"name": "nodelete-key", "allow_deletion": true}, + }) + if err != nil { + t.Fatalf("update-key-config: %v", err) + } + + // Should succeed now. + _, err = te.HandleRequest(ctx, &engine.Request{ + Operation: "delete-key", + CallerInfo: adminCaller(), + Data: map[string]interface{}{"name": "nodelete-key"}, + }) + if err != nil { + t.Fatalf("delete-key: %v", err) + } + + if _, ok := te.keys["nodelete-key"]; ok { + t.Fatal("key should have been deleted") + } +} + +func TestGetPublicKey(t *testing.T) { + te, _ := setupEngine(t) + createKey(t, te, "pubkey", "ed25519") + ctx := context.Background() + + resp, err := te.HandleRequest(ctx, &engine.Request{ + Operation: "get-public-key", + CallerInfo: userCaller(), + Data: map[string]interface{}{"name": "pubkey"}, + }) + if err != nil { + t.Fatalf("get-public-key: %v", err) + } + pk, _ := resp.Data["public_key"].(string) + if pk == "" { + t.Fatal("expected non-empty public key") + } + ver, _ := resp.Data["version"].(int) + if ver != 1 { + t.Fatalf("expected version 1, got %d", ver) + } +} + +func TestGetPublicKeyRejectsSymmetric(t *testing.T) { + te, _ := setupEngine(t) + createKey(t, te, "sym", "aes256-gcm") + ctx := context.Background() + + _, err := te.HandleRequest(ctx, &engine.Request{ + Operation: "get-public-key", + CallerInfo: userCaller(), + Data: map[string]interface{}{"name": "sym"}, + }) + if err == nil { + t.Fatal("expected error getting public key for symmetric key") + } +} + +func TestBatchRewrap(t *testing.T) { + te, _ := setupEngine(t) + createKey(t, te, "brwrap-key", "aes256-gcm") + ctx := context.Background() + + pt1 := base64.StdEncoding.EncodeToString([]byte("item1")) + pt2 := base64.StdEncoding.EncodeToString([]byte("item2")) + + // Encrypt two items. + var cts []string + for _, pt := range []string{pt1, pt2} { + resp, _ := te.HandleRequest(ctx, &engine.Request{ + Operation: "encrypt", + CallerInfo: userCaller(), + Data: map[string]interface{}{"key": "brwrap-key", "plaintext": pt}, + }) + ct, _ := resp.Data["ciphertext"].(string) + cts = append(cts, ct) + } + + // Rotate. + te.HandleRequest(ctx, &engine.Request{ + Operation: "rotate-key", + CallerInfo: adminCaller(), + Data: map[string]interface{}{"name": "brwrap-key"}, + }) + + // Batch rewrap. + items := []interface{}{ + map[string]interface{}{"ciphertext": cts[0], "reference": "r1"}, + map[string]interface{}{"ciphertext": cts[1], "reference": "r2"}, + } + + resp, err := te.HandleRequest(ctx, &engine.Request{ + Operation: "batch-rewrap", + CallerInfo: userCaller(), + Data: map[string]interface{}{"key": "brwrap-key", "items": items}, + }) + if err != nil { + t.Fatalf("batch-rewrap: %v", err) + } + + results, _ := resp.Data["results"].([]interface{}) + for i, r := range results { + br, ok := r.(batchResult) + if !ok { + t.Fatalf("expected batchResult, got %T", r) + } + if br.Error != "" { + t.Fatalf("item %d error: %s", i, br.Error) + } + if !strings.HasPrefix(br.Ciphertext, "metacrypt:v2:") { + t.Fatalf("item %d: expected v2 ciphertext, got %s", i, br.Ciphertext) + } + } +} + +func TestDuplicateKeyCreation(t *testing.T) { + te, _ := setupEngine(t) + createKey(t, te, "dup-key", "aes256-gcm") + ctx := context.Background() + + _, err := te.HandleRequest(ctx, &engine.Request{ + Operation: "create-key", + CallerInfo: adminCaller(), + Data: map[string]interface{}{"name": "dup-key", "type": "aes256-gcm"}, + }) + if err == nil { + t.Fatal("expected error creating duplicate key") + } +} + +func TestInvalidKeyType(t *testing.T) { + te, _ := setupEngine(t) + ctx := context.Background() + + _, err := te.HandleRequest(ctx, &engine.Request{ + Operation: "create-key", + CallerInfo: adminCaller(), + Data: map[string]interface{}{"name": "bad", "type": "invalid-type"}, + }) + if err == nil { + t.Fatal("expected error for invalid key type") + } +} diff --git a/internal/engine/transit/types.go b/internal/engine/transit/types.go new file mode 100644 index 0000000..f1c7a2b --- /dev/null +++ b/internal/engine/transit/types.go @@ -0,0 +1,15 @@ +package transit + +// TransitConfig is the transit engine configuration stored in the barrier. +type TransitConfig struct { + MaxKeyVersions int `json:"max_key_versions"` +} + +// KeyConfig is per-key configuration stored in the barrier. +type KeyConfig struct { + Name string `json:"name"` + Type string `json:"type"` // aes256-gcm, chacha20-poly, ed25519, ecdsa-p256, ecdsa-p384, hmac-sha256, hmac-sha512 + CurrentVersion int `json:"current_version"` + MinDecryptionVersion int `json:"min_decryption_version"` + AllowDeletion bool `json:"allow_deletion"` +} diff --git a/internal/grpcserver/server.go b/internal/grpcserver/server.go index 5cc93f9..bb6e39f 100644 --- a/internal/grpcserver/server.go +++ b/internal/grpcserver/server.go @@ -82,6 +82,7 @@ func (s *GRPCServer) Start() error { pb.RegisterCAServiceServer(s.srv, &caServer{s: s}) pb.RegisterPolicyServiceServer(s.srv, &policyServer{s: s}) pb.RegisterACMEServiceServer(s.srv, &acmeServer{s: s}) + pb.RegisterTransitServiceServer(s.srv, &transitServer{s: s}) lis, err := net.Listen("tcp", s.cfg.Server.GRPCAddr) if err != nil { @@ -136,7 +137,24 @@ func sealRequiredMethods() map[string]bool { "/metacrypt.v2.ACMEService/CreateEAB": true, "/metacrypt.v2.ACMEService/SetConfig": true, "/metacrypt.v2.ACMEService/ListAccounts": true, - "/metacrypt.v2.ACMEService/ListOrders": true, + "/metacrypt.v2.ACMEService/ListOrders": true, + "/metacrypt.v2.TransitService/CreateKey": true, + "/metacrypt.v2.TransitService/DeleteKey": true, + "/metacrypt.v2.TransitService/GetKey": true, + "/metacrypt.v2.TransitService/ListKeys": true, + "/metacrypt.v2.TransitService/RotateKey": true, + "/metacrypt.v2.TransitService/UpdateKeyConfig": true, + "/metacrypt.v2.TransitService/TrimKey": true, + "/metacrypt.v2.TransitService/Encrypt": true, + "/metacrypt.v2.TransitService/Decrypt": true, + "/metacrypt.v2.TransitService/Rewrap": true, + "/metacrypt.v2.TransitService/BatchEncrypt": true, + "/metacrypt.v2.TransitService/BatchDecrypt": true, + "/metacrypt.v2.TransitService/BatchRewrap": true, + "/metacrypt.v2.TransitService/Sign": true, + "/metacrypt.v2.TransitService/Verify": true, + "/metacrypt.v2.TransitService/Hmac": true, + "/metacrypt.v2.TransitService/GetPublicKey": true, } } @@ -166,7 +184,24 @@ func authRequiredMethods() map[string]bool { "/metacrypt.v2.ACMEService/CreateEAB": true, "/metacrypt.v2.ACMEService/SetConfig": true, "/metacrypt.v2.ACMEService/ListAccounts": true, - "/metacrypt.v2.ACMEService/ListOrders": true, + "/metacrypt.v2.ACMEService/ListOrders": true, + "/metacrypt.v2.TransitService/CreateKey": true, + "/metacrypt.v2.TransitService/DeleteKey": true, + "/metacrypt.v2.TransitService/GetKey": true, + "/metacrypt.v2.TransitService/ListKeys": true, + "/metacrypt.v2.TransitService/RotateKey": true, + "/metacrypt.v2.TransitService/UpdateKeyConfig": true, + "/metacrypt.v2.TransitService/TrimKey": true, + "/metacrypt.v2.TransitService/Encrypt": true, + "/metacrypt.v2.TransitService/Decrypt": true, + "/metacrypt.v2.TransitService/Rewrap": true, + "/metacrypt.v2.TransitService/BatchEncrypt": true, + "/metacrypt.v2.TransitService/BatchDecrypt": true, + "/metacrypt.v2.TransitService/BatchRewrap": true, + "/metacrypt.v2.TransitService/Sign": true, + "/metacrypt.v2.TransitService/Verify": true, + "/metacrypt.v2.TransitService/Hmac": true, + "/metacrypt.v2.TransitService/GetPublicKey": true, } } @@ -185,6 +220,11 @@ func adminRequiredMethods() map[string]bool { "/metacrypt.v2.PolicyService/DeletePolicy": true, "/metacrypt.v2.ACMEService/SetConfig": true, "/metacrypt.v2.ACMEService/ListAccounts": true, - "/metacrypt.v2.ACMEService/ListOrders": true, + "/metacrypt.v2.ACMEService/ListOrders": true, + "/metacrypt.v2.TransitService/CreateKey": true, + "/metacrypt.v2.TransitService/DeleteKey": true, + "/metacrypt.v2.TransitService/RotateKey": true, + "/metacrypt.v2.TransitService/UpdateKeyConfig": true, + "/metacrypt.v2.TransitService/TrimKey": true, } } diff --git a/internal/grpcserver/transit.go b/internal/grpcserver/transit.go new file mode 100644 index 0000000..f5fc2f3 --- /dev/null +++ b/internal/grpcserver/transit.go @@ -0,0 +1,486 @@ +package grpcserver + +import ( + "context" + "errors" + "strings" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + pb "git.wntrmute.dev/kyle/metacrypt/gen/metacrypt/v2" + "git.wntrmute.dev/kyle/metacrypt/internal/engine" + "git.wntrmute.dev/kyle/metacrypt/internal/engine/transit" + "git.wntrmute.dev/kyle/metacrypt/internal/policy" +) + +type transitServer struct { + pb.UnimplementedTransitServiceServer + s *GRPCServer +} + +func (ts *transitServer) transitHandleRequest(ctx context.Context, mount, operation string, req *engine.Request) (*engine.Response, error) { + resp, err := ts.s.engines.HandleRequest(ctx, mount, req) + if err != nil { + st := codes.Internal + switch { + case errors.Is(err, engine.ErrMountNotFound): + st = codes.NotFound + case errors.Is(err, transit.ErrKeyNotFound): + st = codes.NotFound + case errors.Is(err, transit.ErrKeyExists): + st = codes.AlreadyExists + case errors.Is(err, transit.ErrUnauthorized): + st = codes.Unauthenticated + case errors.Is(err, transit.ErrForbidden): + st = codes.PermissionDenied + case errors.Is(err, transit.ErrDeletionDenied): + st = codes.FailedPrecondition + case errors.Is(err, transit.ErrUnsupportedOp): + st = codes.InvalidArgument + case errors.Is(err, transit.ErrDecryptVersion): + st = codes.FailedPrecondition + case errors.Is(err, transit.ErrInvalidFormat): + st = codes.InvalidArgument + case errors.Is(err, transit.ErrBatchTooLarge): + st = codes.InvalidArgument + case errors.Is(err, transit.ErrInvalidMinVer): + st = codes.InvalidArgument + case strings.Contains(err.Error(), "not found"): + st = codes.NotFound + case strings.Contains(err.Error(), "forbidden"): + st = codes.PermissionDenied + } + ts.s.logger.Error("grpc: transit "+operation, "mount", mount, "error", err) + return nil, status.Error(st, err.Error()) + } + return resp, nil +} + +func (ts *transitServer) callerInfo(ctx context.Context) *engine.CallerInfo { + ti := tokenInfoFromContext(ctx) + if ti == nil { + return nil + } + return &engine.CallerInfo{ + Username: ti.Username, + Roles: ti.Roles, + IsAdmin: ti.IsAdmin, + } +} + +func (ts *transitServer) policyChecker(ctx context.Context) engine.PolicyChecker { + caller := ts.callerInfo(ctx) + if caller == nil { + return nil + } + return func(resource, action string) (string, bool) { + pReq := &policy.Request{ + Username: caller.Username, + Roles: caller.Roles, + Resource: resource, + Action: action, + } + effect, matched, err := ts.s.policy.Match(ctx, pReq) + if err != nil { + return string(policy.EffectDeny), false + } + return string(effect), matched + } +} + +func (ts *transitServer) CreateKey(ctx context.Context, req *pb.CreateTransitKeyRequest) (*pb.CreateTransitKeyResponse, error) { + if req.Mount == "" || req.Name == "" { + return nil, status.Error(codes.InvalidArgument, "mount and name are required") + } + resp, err := ts.transitHandleRequest(ctx, req.Mount, "create-key", &engine.Request{ + Operation: "create-key", + CallerInfo: ts.callerInfo(ctx), + Data: map[string]interface{}{ + "name": req.Name, + "type": req.Type, + }, + }) + if err != nil { + return nil, err + } + name, _ := resp.Data["name"].(string) + keyType, _ := resp.Data["type"].(string) + version, _ := resp.Data["version"].(int) + ts.s.logger.Info("audit: transit key created", "mount", req.Mount, "key", name, "type", keyType, "username", callerUsername(ctx)) + return &pb.CreateTransitKeyResponse{Name: name, Type: keyType, Version: int32(version)}, nil +} + +func (ts *transitServer) DeleteKey(ctx context.Context, req *pb.DeleteTransitKeyRequest) (*pb.DeleteTransitKeyResponse, error) { + if req.Mount == "" || req.Name == "" { + return nil, status.Error(codes.InvalidArgument, "mount and name are required") + } + _, err := ts.transitHandleRequest(ctx, req.Mount, "delete-key", &engine.Request{ + Operation: "delete-key", + CallerInfo: ts.callerInfo(ctx), + Data: map[string]interface{}{"name": req.Name}, + }) + if err != nil { + return nil, err + } + ts.s.logger.Info("audit: transit key deleted", "mount", req.Mount, "key", req.Name, "username", callerUsername(ctx)) + return &pb.DeleteTransitKeyResponse{}, nil +} + +func (ts *transitServer) GetKey(ctx context.Context, req *pb.GetTransitKeyRequest) (*pb.GetTransitKeyResponse, error) { + if req.Mount == "" || req.Name == "" { + return nil, status.Error(codes.InvalidArgument, "mount and name are required") + } + resp, err := ts.transitHandleRequest(ctx, req.Mount, "get-key", &engine.Request{ + Operation: "get-key", + CallerInfo: ts.callerInfo(ctx), + Data: map[string]interface{}{"name": req.Name}, + }) + if err != nil { + return nil, err + } + name, _ := resp.Data["name"].(string) + keyType, _ := resp.Data["type"].(string) + currentVersion, _ := resp.Data["current_version"].(int) + minDecryptionVersion, _ := resp.Data["min_decryption_version"].(int) + allowDeletion, _ := resp.Data["allow_deletion"].(bool) + rawVersions, _ := resp.Data["versions"].([]int) + versions := make([]int32, len(rawVersions)) + for i, v := range rawVersions { + versions[i] = int32(v) + } + return &pb.GetTransitKeyResponse{ + Name: name, + Type: keyType, + CurrentVersion: int32(currentVersion), + MinDecryptionVersion: int32(minDecryptionVersion), + AllowDeletion: allowDeletion, + Versions: versions, + }, nil +} + +func (ts *transitServer) ListKeys(ctx context.Context, req *pb.ListTransitKeysRequest) (*pb.ListTransitKeysResponse, error) { + if req.Mount == "" { + return nil, status.Error(codes.InvalidArgument, "mount is required") + } + resp, err := ts.transitHandleRequest(ctx, req.Mount, "list-keys", &engine.Request{ + Operation: "list-keys", + CallerInfo: ts.callerInfo(ctx), + }) + if err != nil { + return nil, err + } + keys := toStringSliceFromInterface(resp.Data["keys"]) + return &pb.ListTransitKeysResponse{Keys: keys}, nil +} + +func (ts *transitServer) RotateKey(ctx context.Context, req *pb.RotateTransitKeyRequest) (*pb.RotateTransitKeyResponse, error) { + if req.Mount == "" || req.Name == "" { + return nil, status.Error(codes.InvalidArgument, "mount and name are required") + } + resp, err := ts.transitHandleRequest(ctx, req.Mount, "rotate-key", &engine.Request{ + Operation: "rotate-key", + CallerInfo: ts.callerInfo(ctx), + Data: map[string]interface{}{"name": req.Name}, + }) + if err != nil { + return nil, err + } + name, _ := resp.Data["name"].(string) + version, _ := resp.Data["version"].(int) + ts.s.logger.Info("audit: transit key rotated", "mount", req.Mount, "key", name, "version", version, "username", callerUsername(ctx)) + return &pb.RotateTransitKeyResponse{Name: name, Version: int32(version)}, nil +} + +func (ts *transitServer) UpdateKeyConfig(ctx context.Context, req *pb.UpdateTransitKeyConfigRequest) (*pb.UpdateTransitKeyConfigResponse, error) { + if req.Mount == "" || req.Name == "" { + return nil, status.Error(codes.InvalidArgument, "mount and name are required") + } + data := map[string]interface{}{"name": req.Name} + if req.MinDecryptionVersion != 0 { + data["min_decryption_version"] = float64(req.MinDecryptionVersion) + } + data["allow_deletion"] = req.AllowDeletion + + _, err := ts.transitHandleRequest(ctx, req.Mount, "update-key-config", &engine.Request{ + Operation: "update-key-config", + CallerInfo: ts.callerInfo(ctx), + Data: data, + }) + if err != nil { + return nil, err + } + return &pb.UpdateTransitKeyConfigResponse{}, nil +} + +func (ts *transitServer) TrimKey(ctx context.Context, req *pb.TrimTransitKeyRequest) (*pb.TrimTransitKeyResponse, error) { + if req.Mount == "" || req.Name == "" { + return nil, status.Error(codes.InvalidArgument, "mount and name are required") + } + resp, err := ts.transitHandleRequest(ctx, req.Mount, "trim-key", &engine.Request{ + Operation: "trim-key", + CallerInfo: ts.callerInfo(ctx), + Data: map[string]interface{}{"name": req.Name}, + }) + if err != nil { + return nil, err + } + trimmed, _ := resp.Data["trimmed"].(int) + return &pb.TrimTransitKeyResponse{Trimmed: int32(trimmed)}, nil +} + +func (ts *transitServer) Encrypt(ctx context.Context, req *pb.TransitEncryptRequest) (*pb.TransitEncryptResponse, error) { + if req.Mount == "" || req.Key == "" { + return nil, status.Error(codes.InvalidArgument, "mount and key are required") + } + data := map[string]interface{}{ + "key": req.Key, + "plaintext": req.Plaintext, + } + if req.Context != "" { + data["context"] = req.Context + } + resp, err := ts.transitHandleRequest(ctx, req.Mount, "encrypt", &engine.Request{ + Operation: "encrypt", + CallerInfo: ts.callerInfo(ctx), + CheckPolicy: ts.policyChecker(ctx), + Data: data, + }) + if err != nil { + return nil, err + } + ct, _ := resp.Data["ciphertext"].(string) + return &pb.TransitEncryptResponse{Ciphertext: ct}, nil +} + +func (ts *transitServer) Decrypt(ctx context.Context, req *pb.TransitDecryptRequest) (*pb.TransitDecryptResponse, error) { + if req.Mount == "" || req.Key == "" { + return nil, status.Error(codes.InvalidArgument, "mount and key are required") + } + data := map[string]interface{}{ + "key": req.Key, + "ciphertext": req.Ciphertext, + } + if req.Context != "" { + data["context"] = req.Context + } + resp, err := ts.transitHandleRequest(ctx, req.Mount, "decrypt", &engine.Request{ + Operation: "decrypt", + CallerInfo: ts.callerInfo(ctx), + CheckPolicy: ts.policyChecker(ctx), + Data: data, + }) + if err != nil { + return nil, err + } + pt, _ := resp.Data["plaintext"].(string) + return &pb.TransitDecryptResponse{Plaintext: pt}, nil +} + +func (ts *transitServer) Rewrap(ctx context.Context, req *pb.TransitRewrapRequest) (*pb.TransitRewrapResponse, error) { + if req.Mount == "" || req.Key == "" { + return nil, status.Error(codes.InvalidArgument, "mount and key are required") + } + data := map[string]interface{}{ + "key": req.Key, + "ciphertext": req.Ciphertext, + } + if req.Context != "" { + data["context"] = req.Context + } + resp, err := ts.transitHandleRequest(ctx, req.Mount, "rewrap", &engine.Request{ + Operation: "rewrap", + CallerInfo: ts.callerInfo(ctx), + CheckPolicy: ts.policyChecker(ctx), + Data: data, + }) + if err != nil { + return nil, err + } + ct, _ := resp.Data["ciphertext"].(string) + return &pb.TransitRewrapResponse{Ciphertext: ct}, nil +} + +func (ts *transitServer) BatchEncrypt(ctx context.Context, req *pb.TransitBatchEncryptRequest) (*pb.TransitBatchResponse, error) { + if req.Mount == "" || req.Key == "" { + return nil, status.Error(codes.InvalidArgument, "mount and key are required") + } + items := protoItemsToInterface(req.Items) + resp, err := ts.transitHandleRequest(ctx, req.Mount, "batch-encrypt", &engine.Request{ + Operation: "batch-encrypt", + CallerInfo: ts.callerInfo(ctx), + CheckPolicy: ts.policyChecker(ctx), + Data: map[string]interface{}{"key": req.Key, "items": items}, + }) + if err != nil { + return nil, err + } + return toBatchResponse(resp), nil +} + +func (ts *transitServer) BatchDecrypt(ctx context.Context, req *pb.TransitBatchDecryptRequest) (*pb.TransitBatchResponse, error) { + if req.Mount == "" || req.Key == "" { + return nil, status.Error(codes.InvalidArgument, "mount and key are required") + } + items := protoItemsToInterface(req.Items) + resp, err := ts.transitHandleRequest(ctx, req.Mount, "batch-decrypt", &engine.Request{ + Operation: "batch-decrypt", + CallerInfo: ts.callerInfo(ctx), + CheckPolicy: ts.policyChecker(ctx), + Data: map[string]interface{}{"key": req.Key, "items": items}, + }) + if err != nil { + return nil, err + } + return toBatchResponse(resp), nil +} + +func (ts *transitServer) BatchRewrap(ctx context.Context, req *pb.TransitBatchRewrapRequest) (*pb.TransitBatchResponse, error) { + if req.Mount == "" || req.Key == "" { + return nil, status.Error(codes.InvalidArgument, "mount and key are required") + } + items := protoItemsToInterface(req.Items) + resp, err := ts.transitHandleRequest(ctx, req.Mount, "batch-rewrap", &engine.Request{ + Operation: "batch-rewrap", + CallerInfo: ts.callerInfo(ctx), + CheckPolicy: ts.policyChecker(ctx), + Data: map[string]interface{}{"key": req.Key, "items": items}, + }) + if err != nil { + return nil, err + } + return toBatchResponse(resp), nil +} + +func (ts *transitServer) Sign(ctx context.Context, req *pb.TransitSignRequest) (*pb.TransitSignResponse, error) { + if req.Mount == "" || req.Key == "" { + return nil, status.Error(codes.InvalidArgument, "mount and key are required") + } + resp, err := ts.transitHandleRequest(ctx, req.Mount, "sign", &engine.Request{ + Operation: "sign", + CallerInfo: ts.callerInfo(ctx), + CheckPolicy: ts.policyChecker(ctx), + Data: map[string]interface{}{"key": req.Key, "input": req.Input}, + }) + if err != nil { + return nil, err + } + sig, _ := resp.Data["signature"].(string) + return &pb.TransitSignResponse{Signature: sig}, nil +} + +func (ts *transitServer) Verify(ctx context.Context, req *pb.TransitVerifyRequest) (*pb.TransitVerifyResponse, error) { + if req.Mount == "" || req.Key == "" { + return nil, status.Error(codes.InvalidArgument, "mount and key are required") + } + resp, err := ts.transitHandleRequest(ctx, req.Mount, "verify", &engine.Request{ + Operation: "verify", + CallerInfo: ts.callerInfo(ctx), + CheckPolicy: ts.policyChecker(ctx), + Data: map[string]interface{}{ + "key": req.Key, + "input": req.Input, + "signature": req.Signature, + }, + }) + if err != nil { + return nil, err + } + valid, _ := resp.Data["valid"].(bool) + return &pb.TransitVerifyResponse{Valid: valid}, nil +} + +func (ts *transitServer) Hmac(ctx context.Context, req *pb.TransitHmacRequest) (*pb.TransitHmacResponse, error) { + if req.Mount == "" || req.Key == "" { + return nil, status.Error(codes.InvalidArgument, "mount and key are required") + } + data := map[string]interface{}{ + "key": req.Key, + "input": req.Input, + } + if req.Hmac != "" { + data["hmac"] = req.Hmac + } + resp, err := ts.transitHandleRequest(ctx, req.Mount, "hmac", &engine.Request{ + Operation: "hmac", + CallerInfo: ts.callerInfo(ctx), + CheckPolicy: ts.policyChecker(ctx), + Data: data, + }) + if err != nil { + return nil, err + } + hmacStr, _ := resp.Data["hmac"].(string) + valid, _ := resp.Data["valid"].(bool) + return &pb.TransitHmacResponse{Hmac: hmacStr, Valid: valid}, nil +} + +func (ts *transitServer) GetPublicKey(ctx context.Context, req *pb.GetTransitPublicKeyRequest) (*pb.GetTransitPublicKeyResponse, error) { + if req.Mount == "" || req.Name == "" { + return nil, status.Error(codes.InvalidArgument, "mount and name are required") + } + data := map[string]interface{}{"name": req.Name} + if req.Version != 0 { + data["version"] = float64(req.Version) + } + resp, err := ts.transitHandleRequest(ctx, req.Mount, "get-public-key", &engine.Request{ + Operation: "get-public-key", + CallerInfo: ts.callerInfo(ctx), + Data: data, + }) + if err != nil { + return nil, err + } + pk, _ := resp.Data["public_key"].(string) + version, _ := resp.Data["version"].(int) + keyType, _ := resp.Data["type"].(string) + return &pb.GetTransitPublicKeyResponse{ + PublicKey: pk, + Version: int32(version), + Type: keyType, + }, nil +} + +// --- helpers --- + +func protoItemsToInterface(items []*pb.TransitBatchItem) []interface{} { + out := make([]interface{}, len(items)) + for i, item := range items { + m := map[string]interface{}{} + if item.Plaintext != "" { + m["plaintext"] = item.Plaintext + } + if item.Ciphertext != "" { + m["ciphertext"] = item.Ciphertext + } + if item.Context != "" { + m["context"] = item.Context + } + if item.Reference != "" { + m["reference"] = item.Reference + } + out[i] = m + } + return out +} + +func toBatchResponse(resp *engine.Response) *pb.TransitBatchResponse { + raw, _ := resp.Data["results"].([]interface{}) + results := make([]*pb.TransitBatchResultItem, 0, len(raw)) + for _, item := range raw { + switch r := item.(type) { + case map[string]interface{}: + pt, _ := r["plaintext"].(string) + ct, _ := r["ciphertext"].(string) + ref, _ := r["reference"].(string) + errStr, _ := r["error"].(string) + results = append(results, &pb.TransitBatchResultItem{ + Plaintext: pt, + Ciphertext: ct, + Reference: ref, + Error: errStr, + }) + } + } + return &pb.TransitBatchResponse{Results: results} +} diff --git a/internal/server/routes.go b/internal/server/routes.go index 5822d54..e818362 100644 --- a/internal/server/routes.go +++ b/internal/server/routes.go @@ -47,6 +47,26 @@ func (s *Server) registerRoutes(r chi.Router) { r.HandleFunc("/v1/policy/rules", s.requireAuth(s.handlePolicyRules)) r.HandleFunc("/v1/policy/rule", s.requireAuth(s.handlePolicyRule)) + + // Transit engine routes. + r.Post("/v1/transit/{mount}/keys", s.requireAdmin(s.handleTransitCreateKey)) + r.Get("/v1/transit/{mount}/keys", s.requireAuth(s.handleTransitListKeys)) + r.Get("/v1/transit/{mount}/keys/{name}", s.requireAuth(s.handleTransitGetKey)) + r.Delete("/v1/transit/{mount}/keys/{name}", s.requireAdmin(s.handleTransitDeleteKey)) + r.Post("/v1/transit/{mount}/keys/{name}/rotate", s.requireAdmin(s.handleTransitRotateKey)) + r.Post("/v1/transit/{mount}/keys/{name}/config", s.requireAdmin(s.handleTransitUpdateKeyConfig)) + r.Post("/v1/transit/{mount}/keys/{name}/trim", s.requireAdmin(s.handleTransitTrimKey)) + r.Post("/v1/transit/{mount}/encrypt/{key}", s.requireAuth(s.handleTransitEncrypt)) + r.Post("/v1/transit/{mount}/decrypt/{key}", s.requireAuth(s.handleTransitDecrypt)) + r.Post("/v1/transit/{mount}/rewrap/{key}", s.requireAuth(s.handleTransitRewrap)) + r.Post("/v1/transit/{mount}/batch/encrypt/{key}", s.requireAuth(s.handleTransitBatchEncrypt)) + r.Post("/v1/transit/{mount}/batch/decrypt/{key}", s.requireAuth(s.handleTransitBatchDecrypt)) + r.Post("/v1/transit/{mount}/batch/rewrap/{key}", s.requireAuth(s.handleTransitBatchRewrap)) + r.Post("/v1/transit/{mount}/sign/{key}", s.requireAuth(s.handleTransitSign)) + r.Post("/v1/transit/{mount}/verify/{key}", s.requireAuth(s.handleTransitVerify)) + r.Post("/v1/transit/{mount}/hmac/{key}", s.requireAuth(s.handleTransitHmac)) + r.Get("/v1/transit/{mount}/keys/{name}/public-key", s.requireAuth(s.handleTransitGetPublicKey)) + s.registerACMERoutes(r) } @@ -608,11 +628,270 @@ func (s *Server) getCAEngine(mountName string) (*ca.CAEngine, error) { return caEng, nil } +// --- Transit Engine Handlers --- + +func (s *Server) transitRequest(w http.ResponseWriter, r *http.Request, mount, operation string, data map[string]interface{}) { + info := TokenInfoFromContext(r.Context()) + + policyChecker := func(resource, action string) (string, bool) { + pReq := &policy.Request{ + Username: info.Username, + Roles: info.Roles, + Resource: resource, + Action: action, + } + eff, matched, pErr := s.policy.Match(r.Context(), pReq) + if pErr != nil { + return string(policy.EffectDeny), false + } + return string(eff), matched + } + + resp, err := s.engines.HandleRequest(r.Context(), mount, &engine.Request{ + Operation: operation, + CallerInfo: &engine.CallerInfo{Username: info.Username, Roles: info.Roles, IsAdmin: info.IsAdmin}, + CheckPolicy: policyChecker, + Data: data, + }) + if err != nil { + status := http.StatusInternalServerError + switch { + case errors.Is(err, engine.ErrMountNotFound): + status = http.StatusNotFound + case strings.Contains(err.Error(), "forbidden"): + status = http.StatusForbidden + case strings.Contains(err.Error(), "authentication required"): + status = http.StatusUnauthorized + case strings.Contains(err.Error(), "not found"): + status = http.StatusNotFound + case strings.Contains(err.Error(), "not allowed"): + status = http.StatusForbidden + case strings.Contains(err.Error(), "unsupported"): + status = http.StatusBadRequest + case strings.Contains(err.Error(), "invalid"): + status = http.StatusBadRequest + } + http.Error(w, `{"error":"`+err.Error()+`"}`, status) + return + } + writeJSON(w, http.StatusOK, resp.Data) +} + +func (s *Server) handleTransitCreateKey(w http.ResponseWriter, r *http.Request) { + mount := chi.URLParam(r, "mount") + var req struct { + Name string `json:"name"` + Type string `json:"type"` + } + if err := readJSON(r, &req); err != nil { + http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest) + return + } + s.transitRequest(w, r, mount, "create-key", map[string]interface{}{"name": req.Name, "type": req.Type}) +} + +func (s *Server) handleTransitDeleteKey(w http.ResponseWriter, r *http.Request) { + mount := chi.URLParam(r, "mount") + name := chi.URLParam(r, "name") + s.transitRequest(w, r, mount, "delete-key", map[string]interface{}{"name": name}) +} + +func (s *Server) handleTransitGetKey(w http.ResponseWriter, r *http.Request) { + mount := chi.URLParam(r, "mount") + name := chi.URLParam(r, "name") + s.transitRequest(w, r, mount, "get-key", map[string]interface{}{"name": name}) +} + +func (s *Server) handleTransitListKeys(w http.ResponseWriter, r *http.Request) { + mount := chi.URLParam(r, "mount") + s.transitRequest(w, r, mount, "list-keys", nil) +} + +func (s *Server) handleTransitRotateKey(w http.ResponseWriter, r *http.Request) { + mount := chi.URLParam(r, "mount") + name := chi.URLParam(r, "name") + s.transitRequest(w, r, mount, "rotate-key", map[string]interface{}{"name": name}) +} + +func (s *Server) handleTransitUpdateKeyConfig(w http.ResponseWriter, r *http.Request) { + mount := chi.URLParam(r, "mount") + name := chi.URLParam(r, "name") + var req struct { + MinDecryptionVersion *float64 `json:"min_decryption_version"` + AllowDeletion *bool `json:"allow_deletion"` + } + if err := readJSON(r, &req); err != nil { + http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest) + return + } + data := map[string]interface{}{"name": name} + if req.MinDecryptionVersion != nil { + data["min_decryption_version"] = *req.MinDecryptionVersion + } + if req.AllowDeletion != nil { + data["allow_deletion"] = *req.AllowDeletion + } + s.transitRequest(w, r, mount, "update-key-config", data) +} + +func (s *Server) handleTransitTrimKey(w http.ResponseWriter, r *http.Request) { + mount := chi.URLParam(r, "mount") + name := chi.URLParam(r, "name") + s.transitRequest(w, r, mount, "trim-key", map[string]interface{}{"name": name}) +} + +func (s *Server) handleTransitEncrypt(w http.ResponseWriter, r *http.Request) { + mount := chi.URLParam(r, "mount") + key := chi.URLParam(r, "key") + var req struct { + Plaintext string `json:"plaintext"` + Context string `json:"context"` + } + if err := readJSON(r, &req); err != nil { + http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest) + return + } + data := map[string]interface{}{"key": key, "plaintext": req.Plaintext} + if req.Context != "" { + data["context"] = req.Context + } + s.transitRequest(w, r, mount, "encrypt", data) +} + +func (s *Server) handleTransitDecrypt(w http.ResponseWriter, r *http.Request) { + mount := chi.URLParam(r, "mount") + key := chi.URLParam(r, "key") + var req struct { + Ciphertext string `json:"ciphertext"` + Context string `json:"context"` + } + if err := readJSON(r, &req); err != nil { + http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest) + return + } + data := map[string]interface{}{"key": key, "ciphertext": req.Ciphertext} + if req.Context != "" { + data["context"] = req.Context + } + s.transitRequest(w, r, mount, "decrypt", data) +} + +func (s *Server) handleTransitRewrap(w http.ResponseWriter, r *http.Request) { + mount := chi.URLParam(r, "mount") + key := chi.URLParam(r, "key") + var req struct { + Ciphertext string `json:"ciphertext"` + Context string `json:"context"` + } + if err := readJSON(r, &req); err != nil { + http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest) + return + } + data := map[string]interface{}{"key": key, "ciphertext": req.Ciphertext} + if req.Context != "" { + data["context"] = req.Context + } + s.transitRequest(w, r, mount, "rewrap", data) +} + +func (s *Server) handleTransitBatchEncrypt(w http.ResponseWriter, r *http.Request) { + mount := chi.URLParam(r, "mount") + key := chi.URLParam(r, "key") + var req struct { + Items []interface{} `json:"items"` + } + if err := readJSON(r, &req); err != nil { + http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest) + return + } + s.transitRequest(w, r, mount, "batch-encrypt", map[string]interface{}{"key": key, "items": req.Items}) +} + +func (s *Server) handleTransitBatchDecrypt(w http.ResponseWriter, r *http.Request) { + mount := chi.URLParam(r, "mount") + key := chi.URLParam(r, "key") + var req struct { + Items []interface{} `json:"items"` + } + if err := readJSON(r, &req); err != nil { + http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest) + return + } + s.transitRequest(w, r, mount, "batch-decrypt", map[string]interface{}{"key": key, "items": req.Items}) +} + +func (s *Server) handleTransitBatchRewrap(w http.ResponseWriter, r *http.Request) { + mount := chi.URLParam(r, "mount") + key := chi.URLParam(r, "key") + var req struct { + Items []interface{} `json:"items"` + } + if err := readJSON(r, &req); err != nil { + http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest) + return + } + s.transitRequest(w, r, mount, "batch-rewrap", map[string]interface{}{"key": key, "items": req.Items}) +} + +func (s *Server) handleTransitSign(w http.ResponseWriter, r *http.Request) { + mount := chi.URLParam(r, "mount") + key := chi.URLParam(r, "key") + var req struct { + Input string `json:"input"` + } + if err := readJSON(r, &req); err != nil { + http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest) + return + } + s.transitRequest(w, r, mount, "sign", map[string]interface{}{"key": key, "input": req.Input}) +} + +func (s *Server) handleTransitVerify(w http.ResponseWriter, r *http.Request) { + mount := chi.URLParam(r, "mount") + key := chi.URLParam(r, "key") + var req struct { + Input string `json:"input"` + Signature string `json:"signature"` + } + if err := readJSON(r, &req); err != nil { + http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest) + return + } + s.transitRequest(w, r, mount, "verify", map[string]interface{}{"key": key, "input": req.Input, "signature": req.Signature}) +} + +func (s *Server) handleTransitHmac(w http.ResponseWriter, r *http.Request) { + mount := chi.URLParam(r, "mount") + key := chi.URLParam(r, "key") + var req struct { + Input string `json:"input"` + HMAC string `json:"hmac"` + } + if err := readJSON(r, &req); err != nil { + http.Error(w, `{"error":"invalid request"}`, http.StatusBadRequest) + return + } + data := map[string]interface{}{"key": key, "input": req.Input} + if req.HMAC != "" { + data["hmac"] = req.HMAC + } + s.transitRequest(w, r, mount, "hmac", data) +} + +func (s *Server) handleTransitGetPublicKey(w http.ResponseWriter, r *http.Request) { + mount := chi.URLParam(r, "mount") + name := chi.URLParam(r, "name") + s.transitRequest(w, r, mount, "get-public-key", map[string]interface{}{"name": name}) +} + // operationAction maps an engine operation name to a policy action ("read" or "write"). func operationAction(op string) string { switch op { - case "list-issuers", "list-certs", "get-cert", "get-root", "get-chain", "get-issuer": + case "list-issuers", "list-certs", "get-cert", "get-root", "get-chain", "get-issuer", + "list-keys", "get-key", "get-public-key": return "read" + case "decrypt", "rewrap", "batch-decrypt", "batch-rewrap": + return "decrypt" default: return "write" } diff --git a/proto/metacrypt/v2/transit.proto b/proto/metacrypt/v2/transit.proto new file mode 100644 index 0000000..4e2ae32 --- /dev/null +++ b/proto/metacrypt/v2/transit.proto @@ -0,0 +1,291 @@ +syntax = "proto3"; + +package metacrypt.v2; + +option go_package = "git.wntrmute.dev/kyle/metacrypt/gen/metacrypt/v2;metacryptv2"; + +// TransitService provides typed, authenticated access to transit engine +// operations: symmetric encryption, signing, HMAC, and versioned key +// management. All RPCs require the service to be unsealed. +service TransitService { + // CreateKey creates a new named encryption key. Admin only. + rpc CreateKey(CreateTransitKeyRequest) returns (CreateTransitKeyResponse); + + // DeleteKey permanently removes a named key. Admin only. + // Only succeeds if allow_deletion is true on the key config. + rpc DeleteKey(DeleteTransitKeyRequest) returns (DeleteTransitKeyResponse); + + // GetKey returns metadata for a named key (no raw material). Auth required. + rpc GetKey(GetTransitKeyRequest) returns (GetTransitKeyResponse); + + // ListKeys returns the names of all configured keys. Auth required. + rpc ListKeys(ListTransitKeysRequest) returns (ListTransitKeysResponse); + + // RotateKey creates a new version of the named key. Admin only. + rpc RotateKey(RotateTransitKeyRequest) returns (RotateTransitKeyResponse); + + // UpdateKeyConfig updates key configuration (e.g. min_decryption_version). + // Admin only. + rpc UpdateKeyConfig(UpdateTransitKeyConfigRequest) returns (UpdateTransitKeyConfigResponse); + + // TrimKey deletes versions below min_decryption_version. Admin only. + rpc TrimKey(TrimTransitKeyRequest) returns (TrimTransitKeyResponse); + + // Encrypt encrypts plaintext with the latest key version. Auth required. + rpc Encrypt(TransitEncryptRequest) returns (TransitEncryptResponse); + + // Decrypt decrypts ciphertext. Auth required. + rpc Decrypt(TransitDecryptRequest) returns (TransitDecryptResponse); + + // Rewrap re-encrypts ciphertext with the latest key version without + // exposing plaintext. Auth required. + rpc Rewrap(TransitRewrapRequest) returns (TransitRewrapResponse); + + // BatchEncrypt encrypts multiple items in a single request. Auth required. + rpc BatchEncrypt(TransitBatchEncryptRequest) returns (TransitBatchResponse); + + // BatchDecrypt decrypts multiple items in a single request. Auth required. + rpc BatchDecrypt(TransitBatchDecryptRequest) returns (TransitBatchResponse); + + // BatchRewrap re-encrypts multiple items in a single request. Auth required. + rpc BatchRewrap(TransitBatchRewrapRequest) returns (TransitBatchResponse); + + // Sign signs input data with an asymmetric key. Auth required. + rpc Sign(TransitSignRequest) returns (TransitSignResponse); + + // Verify verifies a signature against input data. Auth required. + rpc Verify(TransitVerifyRequest) returns (TransitVerifyResponse); + + // Hmac computes or verifies an HMAC. Auth required. + rpc Hmac(TransitHmacRequest) returns (TransitHmacResponse); + + // GetPublicKey returns the public key for an asymmetric key. Auth required. + rpc GetPublicKey(GetTransitPublicKeyRequest) returns (GetTransitPublicKeyResponse); +} + +// --- CreateKey --- + +message CreateTransitKeyRequest { + string mount = 1; + string name = 2; + // type is the key algorithm: aes256-gcm, chacha20-poly, ed25519, + // ecdsa-p256, ecdsa-p384, hmac-sha256, hmac-sha512. + // Defaults to aes256-gcm if empty. + string type = 3; +} + +message CreateTransitKeyResponse { + string name = 1; + string type = 2; + int32 version = 3; +} + +// --- DeleteKey --- + +message DeleteTransitKeyRequest { + string mount = 1; + string name = 2; +} + +message DeleteTransitKeyResponse {} + +// --- GetKey --- + +message GetTransitKeyRequest { + string mount = 1; + string name = 2; +} + +message GetTransitKeyResponse { + string name = 1; + string type = 2; + int32 current_version = 3; + int32 min_decryption_version = 4; + bool allow_deletion = 5; + repeated int32 versions = 6; +} + +// --- ListKeys --- + +message ListTransitKeysRequest { + string mount = 1; +} + +message ListTransitKeysResponse { + repeated string keys = 1; +} + +// --- RotateKey --- + +message RotateTransitKeyRequest { + string mount = 1; + string name = 2; +} + +message RotateTransitKeyResponse { + string name = 1; + int32 version = 2; +} + +// --- UpdateKeyConfig --- + +message UpdateTransitKeyConfigRequest { + string mount = 1; + string name = 2; + int32 min_decryption_version = 3; + bool allow_deletion = 4; +} + +message UpdateTransitKeyConfigResponse {} + +// --- TrimKey --- + +message TrimTransitKeyRequest { + string mount = 1; + string name = 2; +} + +message TrimTransitKeyResponse { + int32 trimmed = 1; +} + +// --- Encrypt --- + +message TransitEncryptRequest { + string mount = 1; + string key = 2; + // plaintext is base64-encoded data to encrypt. + string plaintext = 3; + // context is optional base64-encoded additional authenticated data (AAD). + string context = 4; +} + +message TransitEncryptResponse { + // ciphertext in format "metacrypt:v{version}:{base64(nonce+ciphertext+tag)}" + string ciphertext = 1; +} + +// --- Decrypt --- + +message TransitDecryptRequest { + string mount = 1; + string key = 2; + string ciphertext = 3; + string context = 4; +} + +message TransitDecryptResponse { + // plaintext is base64-encoded decrypted data. + string plaintext = 1; +} + +// --- Rewrap --- + +message TransitRewrapRequest { + string mount = 1; + string key = 2; + string ciphertext = 3; + string context = 4; +} + +message TransitRewrapResponse { + string ciphertext = 1; +} + +// --- Batch --- + +message TransitBatchItem { + string plaintext = 1; + string ciphertext = 2; + string context = 3; + string reference = 4; +} + +message TransitBatchResultItem { + string plaintext = 1; + string ciphertext = 2; + string reference = 3; + string error = 4; +} + +message TransitBatchEncryptRequest { + string mount = 1; + string key = 2; + repeated TransitBatchItem items = 3; +} + +message TransitBatchDecryptRequest { + string mount = 1; + string key = 2; + repeated TransitBatchItem items = 3; +} + +message TransitBatchRewrapRequest { + string mount = 1; + string key = 2; + repeated TransitBatchItem items = 3; +} + +message TransitBatchResponse { + repeated TransitBatchResultItem results = 1; +} + +// --- Sign --- + +message TransitSignRequest { + string mount = 1; + string key = 2; + // input is base64-encoded data to sign. + string input = 3; +} + +message TransitSignResponse { + // signature in format "metacrypt:v{version}:{base64(signature_bytes)}" + string signature = 1; +} + +// --- Verify --- + +message TransitVerifyRequest { + string mount = 1; + string key = 2; + string input = 3; + string signature = 4; +} + +message TransitVerifyResponse { + bool valid = 1; +} + +// --- HMAC --- + +message TransitHmacRequest { + string mount = 1; + string key = 2; + // input is base64-encoded data to HMAC. + string input = 3; + // hmac, if set, switches to verify mode. + string hmac = 4; +} + +message TransitHmacResponse { + // hmac is set in compute mode. + string hmac = 1; + // valid is set in verify mode. + bool valid = 2; +} + +// --- GetPublicKey --- + +message GetTransitPublicKeyRequest { + string mount = 1; + string name = 2; + int32 version = 3; +} + +message GetTransitPublicKeyResponse { + // public_key is base64-encoded PKIX DER public key. + string public_key = 1; + int32 version = 2; + string type = 3; +}