diff --git a/api/buf.gen.yaml b/api/buf.gen.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..9e37a8ea0c9672424902d23f460d43df168e09fe
--- /dev/null
+++ b/api/buf.gen.yaml
@@ -0,0 +1,22 @@
+version: v1
+plugins:
+  - plugin: buf.build/grpc/go
+    out: go/grpc
+    opt:
+      - paths=source_relative
+  - plugin: buf.build/grpc-ecosystem/gateway
+    out: go/grpc
+    opt:
+      - paths=source_relative
+  - plugin: buf.build/grpc-ecosystem/openapiv2
+    out: openapiv2
+    opt:
+      - allow_merge=true
+      - merge_file_name=ekms_etsi14
+#      - generate_unbound_methods=true
+#      - include_package_in_tags=false
+  # dependencies
+  - plugin: buf.build/protocolbuffers/go
+    out: go/grpc
+    opt:
+      - paths=source_relative
diff --git a/api/buf.work.yaml b/api/buf.work.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..7a18eb0259c681f888c407191a2fad18b9dffec6
--- /dev/null
+++ b/api/buf.work.yaml
@@ -0,0 +1,3 @@
+version: v1
+directories:
+  - proto
\ No newline at end of file
diff --git a/api/go/grpc/etsi/etsi14.pb.go b/api/go/grpc/etsi/etsi14.pb.go
new file mode 100644
index 0000000000000000000000000000000000000000..1bc8d74052db0054351883c43fdc675ff7309785
--- /dev/null
+++ b/api/go/grpc/etsi/etsi14.pb.go
@@ -0,0 +1,1540 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.31.0
+// 	protoc        (unknown)
+// source: etsi/etsi14.proto
+
+package etsi14
+
+import (
+	_ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options"
+	_ "google.golang.org/genproto/googleapis/api/annotations"
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	_ "google.golang.org/protobuf/types/descriptorpb"
+	anypb "google.golang.org/protobuf/types/known/anypb"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type GetStatusRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	KMEHostname  string `protobuf:"bytes,1,opt,name=KME_hostname,json=KMEHostname,proto3" json:"KME_hostname,omitempty"`
+	Slave_SAE_ID string `protobuf:"bytes,2,opt,name=slave_SAE_ID,json=slaveSAEID,proto3" json:"slave_SAE_ID,omitempty"`
+}
+
+func (x *GetStatusRequest) Reset() {
+	*x = GetStatusRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_etsi_etsi14_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *GetStatusRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GetStatusRequest) ProtoMessage() {}
+
+func (x *GetStatusRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_etsi_etsi14_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use GetStatusRequest.ProtoReflect.Descriptor instead.
+func (*GetStatusRequest) Descriptor() ([]byte, []int) {
+	return file_etsi_etsi14_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *GetStatusRequest) GetKMEHostname() string {
+	if x != nil {
+		return x.KMEHostname
+	}
+	return ""
+}
+
+func (x *GetStatusRequest) GetSlave_SAE_ID() string {
+	if x != nil {
+		return x.Slave_SAE_ID
+	}
+	return ""
+}
+
+type GetStatusResponse struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Timestamp int64   `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
+	Status    *Status `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"`
+	Error     *Error  `protobuf:"bytes,3,opt,name=error,proto3" json:"error,omitempty"`
+}
+
+func (x *GetStatusResponse) Reset() {
+	*x = GetStatusResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_etsi_etsi14_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *GetStatusResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GetStatusResponse) ProtoMessage() {}
+
+func (x *GetStatusResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_etsi_etsi14_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use GetStatusResponse.ProtoReflect.Descriptor instead.
+func (*GetStatusResponse) Descriptor() ([]byte, []int) {
+	return file_etsi_etsi14_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *GetStatusResponse) GetTimestamp() int64 {
+	if x != nil {
+		return x.Timestamp
+	}
+	return 0
+}
+
+func (x *GetStatusResponse) GetStatus() *Status {
+	if x != nil {
+		return x.Status
+	}
+	return nil
+}
+
+func (x *GetStatusResponse) GetError() *Error {
+	if x != nil {
+		return x.Error
+	}
+	return nil
+}
+
+type GetKeyRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	KMEHostname  string      `protobuf:"bytes,1,opt,name=KME_hostname,json=KMEHostname,proto3" json:"KME_hostname,omitempty"`
+	Slave_SAE_ID string      `protobuf:"bytes,2,opt,name=slave_SAE_ID,json=slaveSAEID,proto3" json:"slave_SAE_ID,omitempty"`
+	KeyRequest   *KeyRequest `protobuf:"bytes,3,opt,name=key_request,json=keyRequest,proto3" json:"key_request,omitempty"`
+}
+
+func (x *GetKeyRequest) Reset() {
+	*x = GetKeyRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_etsi_etsi14_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *GetKeyRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GetKeyRequest) ProtoMessage() {}
+
+func (x *GetKeyRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_etsi_etsi14_proto_msgTypes[2]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use GetKeyRequest.ProtoReflect.Descriptor instead.
+func (*GetKeyRequest) Descriptor() ([]byte, []int) {
+	return file_etsi_etsi14_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *GetKeyRequest) GetKMEHostname() string {
+	if x != nil {
+		return x.KMEHostname
+	}
+	return ""
+}
+
+func (x *GetKeyRequest) GetSlave_SAE_ID() string {
+	if x != nil {
+		return x.Slave_SAE_ID
+	}
+	return ""
+}
+
+func (x *GetKeyRequest) GetKeyRequest() *KeyRequest {
+	if x != nil {
+		return x.KeyRequest
+	}
+	return nil
+}
+
+type GetKeyResponse struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Timestamp    int64         `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
+	KeyContainer *KeyContainer `protobuf:"bytes,2,opt,name=key_container,json=keyContainer,proto3" json:"key_container,omitempty"`
+	Error        *Error        `protobuf:"bytes,3,opt,name=error,proto3" json:"error,omitempty"`
+}
+
+func (x *GetKeyResponse) Reset() {
+	*x = GetKeyResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_etsi_etsi14_proto_msgTypes[3]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *GetKeyResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GetKeyResponse) ProtoMessage() {}
+
+func (x *GetKeyResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_etsi_etsi14_proto_msgTypes[3]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use GetKeyResponse.ProtoReflect.Descriptor instead.
+func (*GetKeyResponse) Descriptor() ([]byte, []int) {
+	return file_etsi_etsi14_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *GetKeyResponse) GetTimestamp() int64 {
+	if x != nil {
+		return x.Timestamp
+	}
+	return 0
+}
+
+func (x *GetKeyResponse) GetKeyContainer() *KeyContainer {
+	if x != nil {
+		return x.KeyContainer
+	}
+	return nil
+}
+
+func (x *GetKeyResponse) GetError() *Error {
+	if x != nil {
+		return x.Error
+	}
+	return nil
+}
+
+type GetKeyWithIDsRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	KMEHostname   string   `protobuf:"bytes,1,opt,name=KME_hostname,json=KMEHostname,proto3" json:"KME_hostname,omitempty"`
+	Master_SAE_ID string   `protobuf:"bytes,2,opt,name=master_SAE_ID,json=masterSAEID,proto3" json:"master_SAE_ID,omitempty"`
+	Slave_SAE_ID  string   `protobuf:"bytes,3,opt,name=slave_SAE_ID,json=slaveSAEID,proto3" json:"slave_SAE_ID,omitempty"`
+	Key_ID        []*KeyID `protobuf:"bytes,4,rep,name=key_ID,json=keyID,proto3" json:"key_ID,omitempty"`
+}
+
+func (x *GetKeyWithIDsRequest) Reset() {
+	*x = GetKeyWithIDsRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_etsi_etsi14_proto_msgTypes[4]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *GetKeyWithIDsRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GetKeyWithIDsRequest) ProtoMessage() {}
+
+func (x *GetKeyWithIDsRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_etsi_etsi14_proto_msgTypes[4]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use GetKeyWithIDsRequest.ProtoReflect.Descriptor instead.
+func (*GetKeyWithIDsRequest) Descriptor() ([]byte, []int) {
+	return file_etsi_etsi14_proto_rawDescGZIP(), []int{4}
+}
+
+func (x *GetKeyWithIDsRequest) GetKMEHostname() string {
+	if x != nil {
+		return x.KMEHostname
+	}
+	return ""
+}
+
+func (x *GetKeyWithIDsRequest) GetMaster_SAE_ID() string {
+	if x != nil {
+		return x.Master_SAE_ID
+	}
+	return ""
+}
+
+func (x *GetKeyWithIDsRequest) GetSlave_SAE_ID() string {
+	if x != nil {
+		return x.Slave_SAE_ID
+	}
+	return ""
+}
+
+func (x *GetKeyWithIDsRequest) GetKey_ID() []*KeyID {
+	if x != nil {
+		return x.Key_ID
+	}
+	return nil
+}
+
+type GetKeyWithIDsResponse struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Timestamp    int64         `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
+	KeyContainer *KeyContainer `protobuf:"bytes,2,opt,name=key_container,json=keyContainer,proto3" json:"key_container,omitempty"`
+	Error        *Error        `protobuf:"bytes,3,opt,name=error,proto3" json:"error,omitempty"`
+}
+
+func (x *GetKeyWithIDsResponse) Reset() {
+	*x = GetKeyWithIDsResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_etsi_etsi14_proto_msgTypes[5]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *GetKeyWithIDsResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GetKeyWithIDsResponse) ProtoMessage() {}
+
+func (x *GetKeyWithIDsResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_etsi_etsi14_proto_msgTypes[5]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use GetKeyWithIDsResponse.ProtoReflect.Descriptor instead.
+func (*GetKeyWithIDsResponse) Descriptor() ([]byte, []int) {
+	return file_etsi_etsi14_proto_rawDescGZIP(), []int{5}
+}
+
+func (x *GetKeyWithIDsResponse) GetTimestamp() int64 {
+	if x != nil {
+		return x.Timestamp
+	}
+	return 0
+}
+
+func (x *GetKeyWithIDsResponse) GetKeyContainer() *KeyContainer {
+	if x != nil {
+		return x.KeyContainer
+	}
+	return nil
+}
+
+func (x *GetKeyWithIDsResponse) GetError() *Error {
+	if x != nil {
+		return x.Error
+	}
+	return nil
+}
+
+// FIGURE THIS ERROR STUFF OUT
+type Error struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// ErrorCode code = 1;
+	Message string                    `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
+	Details map[string]*ListOfDetails `protobuf:"bytes,2,rep,name=details,proto3" json:"details,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // maybe map<string, string> is enough here?!
+}
+
+func (x *Error) Reset() {
+	*x = Error{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_etsi_etsi14_proto_msgTypes[6]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Error) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Error) ProtoMessage() {}
+
+func (x *Error) ProtoReflect() protoreflect.Message {
+	mi := &file_etsi_etsi14_proto_msgTypes[6]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Error.ProtoReflect.Descriptor instead.
+func (*Error) Descriptor() ([]byte, []int) {
+	return file_etsi_etsi14_proto_rawDescGZIP(), []int{6}
+}
+
+func (x *Error) GetMessage() string {
+	if x != nil {
+		return x.Message
+	}
+	return ""
+}
+
+func (x *Error) GetDetails() map[string]*ListOfDetails {
+	if x != nil {
+		return x.Details
+	}
+	return nil
+}
+
+// Maybe not neccessary
+type ListOfDetails struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Details []string `protobuf:"bytes,1,rep,name=details,proto3" json:"details,omitempty"`
+}
+
+func (x *ListOfDetails) Reset() {
+	*x = ListOfDetails{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_etsi_etsi14_proto_msgTypes[7]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ListOfDetails) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ListOfDetails) ProtoMessage() {}
+
+func (x *ListOfDetails) ProtoReflect() protoreflect.Message {
+	mi := &file_etsi_etsi14_proto_msgTypes[7]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ListOfDetails.ProtoReflect.Descriptor instead.
+func (*ListOfDetails) Descriptor() ([]byte, []int) {
+	return file_etsi_etsi14_proto_rawDescGZIP(), []int{7}
+}
+
+func (x *ListOfDetails) GetDetails() []string {
+	if x != nil {
+		return x.Details
+	}
+	return nil
+}
+
+type Status struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Source_KME_ID    string `protobuf:"bytes,1,opt,name=source_KME_ID,json=sourceKMEID,proto3" json:"source_KME_ID,omitempty"`
+	Target_KME_ID    string `protobuf:"bytes,2,opt,name=target_KME_ID,json=targetKMEID,proto3" json:"target_KME_ID,omitempty"`
+	Master_SAE_ID    string `protobuf:"bytes,3,opt,name=master_SAE_ID,json=masterSAEID,proto3" json:"master_SAE_ID,omitempty"`
+	Slave_SAE_ID     string `protobuf:"bytes,4,opt,name=slave_SAE_ID,json=slaveSAEID,proto3" json:"slave_SAE_ID,omitempty"`
+	KeySize          int64  `protobuf:"varint,5,opt,name=key_size,json=keySize,proto3" json:"key_size,omitempty"`
+	StoredKeyCount   int64  `protobuf:"varint,6,opt,name=stored_key_count,json=storedKeyCount,proto3" json:"stored_key_count,omitempty"`
+	MaxKeyCount      int64  `protobuf:"varint,7,opt,name=max_key_count,json=maxKeyCount,proto3" json:"max_key_count,omitempty"`
+	MaxKeyPerRequest int64  `protobuf:"varint,8,opt,name=max_key_per_request,json=maxKeyPerRequest,proto3" json:"max_key_per_request,omitempty"`
+	MaxKeySize       int64  `protobuf:"varint,9,opt,name=max_key_size,json=maxKeySize,proto3" json:"max_key_size,omitempty"`
+	MinKeySize       int64  `protobuf:"varint,10,opt,name=min_key_size,json=minKeySize,proto3" json:"min_key_size,omitempty"`
+	Max_SAE_IDCount  int64  `protobuf:"varint,11,opt,name=max_SAE_ID_count,json=maxSAEIDCount,proto3" json:"max_SAE_ID_count,omitempty"`
+}
+
+func (x *Status) Reset() {
+	*x = Status{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_etsi_etsi14_proto_msgTypes[8]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Status) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Status) ProtoMessage() {}
+
+func (x *Status) ProtoReflect() protoreflect.Message {
+	mi := &file_etsi_etsi14_proto_msgTypes[8]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Status.ProtoReflect.Descriptor instead.
+func (*Status) Descriptor() ([]byte, []int) {
+	return file_etsi_etsi14_proto_rawDescGZIP(), []int{8}
+}
+
+func (x *Status) GetSource_KME_ID() string {
+	if x != nil {
+		return x.Source_KME_ID
+	}
+	return ""
+}
+
+func (x *Status) GetTarget_KME_ID() string {
+	if x != nil {
+		return x.Target_KME_ID
+	}
+	return ""
+}
+
+func (x *Status) GetMaster_SAE_ID() string {
+	if x != nil {
+		return x.Master_SAE_ID
+	}
+	return ""
+}
+
+func (x *Status) GetSlave_SAE_ID() string {
+	if x != nil {
+		return x.Slave_SAE_ID
+	}
+	return ""
+}
+
+func (x *Status) GetKeySize() int64 {
+	if x != nil {
+		return x.KeySize
+	}
+	return 0
+}
+
+func (x *Status) GetStoredKeyCount() int64 {
+	if x != nil {
+		return x.StoredKeyCount
+	}
+	return 0
+}
+
+func (x *Status) GetMaxKeyCount() int64 {
+	if x != nil {
+		return x.MaxKeyCount
+	}
+	return 0
+}
+
+func (x *Status) GetMaxKeyPerRequest() int64 {
+	if x != nil {
+		return x.MaxKeyPerRequest
+	}
+	return 0
+}
+
+func (x *Status) GetMaxKeySize() int64 {
+	if x != nil {
+		return x.MaxKeySize
+	}
+	return 0
+}
+
+func (x *Status) GetMinKeySize() int64 {
+	if x != nil {
+		return x.MinKeySize
+	}
+	return 0
+}
+
+func (x *Status) GetMax_SAE_IDCount() int64 {
+	if x != nil {
+		return x.Max_SAE_IDCount
+	}
+	return 0
+}
+
+type KeyRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Number                  int64                 `protobuf:"varint,1,opt,name=number,proto3" json:"number,omitempty"`
+	Size                    int64                 `protobuf:"varint,2,opt,name=size,proto3" json:"size,omitempty"`
+	AdditionalSlave_SAE_IDs []string              `protobuf:"bytes,3,rep,name=additional_slave_SAE_IDs,json=additionalSlaveSAEIDs,proto3" json:"additional_slave_SAE_IDs,omitempty"`
+	ExtensionMandatory      []*ExtensionMandatory `protobuf:"bytes,4,rep,name=extension_mandatory,json=extensionMandatory,proto3" json:"extension_mandatory,omitempty"`
+	ExtensionOptional       []*ExtensionOptional  `protobuf:"bytes,5,rep,name=extension_optional,json=extensionOptional,proto3" json:"extension_optional,omitempty"`
+}
+
+func (x *KeyRequest) Reset() {
+	*x = KeyRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_etsi_etsi14_proto_msgTypes[9]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *KeyRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*KeyRequest) ProtoMessage() {}
+
+func (x *KeyRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_etsi_etsi14_proto_msgTypes[9]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use KeyRequest.ProtoReflect.Descriptor instead.
+func (*KeyRequest) Descriptor() ([]byte, []int) {
+	return file_etsi_etsi14_proto_rawDescGZIP(), []int{9}
+}
+
+func (x *KeyRequest) GetNumber() int64 {
+	if x != nil {
+		return x.Number
+	}
+	return 0
+}
+
+func (x *KeyRequest) GetSize() int64 {
+	if x != nil {
+		return x.Size
+	}
+	return 0
+}
+
+func (x *KeyRequest) GetAdditionalSlave_SAE_IDs() []string {
+	if x != nil {
+		return x.AdditionalSlave_SAE_IDs
+	}
+	return nil
+}
+
+func (x *KeyRequest) GetExtensionMandatory() []*ExtensionMandatory {
+	if x != nil {
+		return x.ExtensionMandatory
+	}
+	return nil
+}
+
+func (x *KeyRequest) GetExtensionOptional() []*ExtensionOptional {
+	if x != nil {
+		return x.ExtensionOptional
+	}
+	return nil
+}
+
+// Array of extension parameters specified as name/value pairs that KME shall
+// handle or return an error. Parameter values may be of any type, including objects.
+type ExtensionMandatory struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	ExtensionMandatory map[string]*anypb.Any `protobuf:"bytes,1,rep,name=extension_mandatory,json=extensionMandatory,proto3" json:"extension_mandatory,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
+}
+
+func (x *ExtensionMandatory) Reset() {
+	*x = ExtensionMandatory{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_etsi_etsi14_proto_msgTypes[10]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ExtensionMandatory) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ExtensionMandatory) ProtoMessage() {}
+
+func (x *ExtensionMandatory) ProtoReflect() protoreflect.Message {
+	mi := &file_etsi_etsi14_proto_msgTypes[10]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ExtensionMandatory.ProtoReflect.Descriptor instead.
+func (*ExtensionMandatory) Descriptor() ([]byte, []int) {
+	return file_etsi_etsi14_proto_rawDescGZIP(), []int{10}
+}
+
+func (x *ExtensionMandatory) GetExtensionMandatory() map[string]*anypb.Any {
+	if x != nil {
+		return x.ExtensionMandatory
+	}
+	return nil
+}
+
+// Array of extension parameters specified as name/value pairs that KME may ignore.
+// Parameter values may be of any type, including objects.
+type ExtensionOptional struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	ExtensionOptional map[string]*anypb.Any `protobuf:"bytes,1,rep,name=extension_optional,json=extensionOptional,proto3" json:"extension_optional,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
+}
+
+func (x *ExtensionOptional) Reset() {
+	*x = ExtensionOptional{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_etsi_etsi14_proto_msgTypes[11]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ExtensionOptional) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ExtensionOptional) ProtoMessage() {}
+
+func (x *ExtensionOptional) ProtoReflect() protoreflect.Message {
+	mi := &file_etsi_etsi14_proto_msgTypes[11]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ExtensionOptional.ProtoReflect.Descriptor instead.
+func (*ExtensionOptional) Descriptor() ([]byte, []int) {
+	return file_etsi_etsi14_proto_rawDescGZIP(), []int{11}
+}
+
+func (x *ExtensionOptional) GetExtensionOptional() map[string]*anypb.Any {
+	if x != nil {
+		return x.ExtensionOptional
+	}
+	return nil
+}
+
+type KeyID struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Key_ID          string     `protobuf:"bytes,1,opt,name=key_ID,json=keyID,proto3" json:"key_ID,omitempty"`
+	Key_IDExtension *anypb.Any `protobuf:"bytes,2,opt,name=key_ID_extension,json=keyIDExtension,proto3" json:"key_ID_extension,omitempty"`
+}
+
+func (x *KeyID) Reset() {
+	*x = KeyID{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_etsi_etsi14_proto_msgTypes[12]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *KeyID) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*KeyID) ProtoMessage() {}
+
+func (x *KeyID) ProtoReflect() protoreflect.Message {
+	mi := &file_etsi_etsi14_proto_msgTypes[12]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use KeyID.ProtoReflect.Descriptor instead.
+func (*KeyID) Descriptor() ([]byte, []int) {
+	return file_etsi_etsi14_proto_rawDescGZIP(), []int{12}
+}
+
+func (x *KeyID) GetKey_ID() string {
+	if x != nil {
+		return x.Key_ID
+	}
+	return ""
+}
+
+func (x *KeyID) GetKey_IDExtension() *anypb.Any {
+	if x != nil {
+		return x.Key_IDExtension
+	}
+	return nil
+}
+
+type KeyContainer struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Key                   []*Key     `protobuf:"bytes,1,rep,name=key,proto3" json:"key,omitempty"`
+	KeyContainerExtension *anypb.Any `protobuf:"bytes,2,opt,name=key_container_extension,json=keyContainerExtension,proto3" json:"key_container_extension,omitempty"`
+}
+
+func (x *KeyContainer) Reset() {
+	*x = KeyContainer{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_etsi_etsi14_proto_msgTypes[13]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *KeyContainer) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*KeyContainer) ProtoMessage() {}
+
+func (x *KeyContainer) ProtoReflect() protoreflect.Message {
+	mi := &file_etsi_etsi14_proto_msgTypes[13]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use KeyContainer.ProtoReflect.Descriptor instead.
+func (*KeyContainer) Descriptor() ([]byte, []int) {
+	return file_etsi_etsi14_proto_rawDescGZIP(), []int{13}
+}
+
+func (x *KeyContainer) GetKey() []*Key {
+	if x != nil {
+		return x.Key
+	}
+	return nil
+}
+
+func (x *KeyContainer) GetKeyContainerExtension() *anypb.Any {
+	if x != nil {
+		return x.KeyContainerExtension
+	}
+	return nil
+}
+
+type KeyIDExtension struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+}
+
+func (x *KeyIDExtension) Reset() {
+	*x = KeyIDExtension{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_etsi_etsi14_proto_msgTypes[14]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *KeyIDExtension) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*KeyIDExtension) ProtoMessage() {}
+
+func (x *KeyIDExtension) ProtoReflect() protoreflect.Message {
+	mi := &file_etsi_etsi14_proto_msgTypes[14]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use KeyIDExtension.ProtoReflect.Descriptor instead.
+func (*KeyIDExtension) Descriptor() ([]byte, []int) {
+	return file_etsi_etsi14_proto_rawDescGZIP(), []int{14}
+}
+
+type Key struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Key_ID          string          `protobuf:"bytes,1,opt,name=key_ID,json=keyID,proto3" json:"key_ID,omitempty"`
+	Key_IDExtension *KeyIDExtension `protobuf:"bytes,2,opt,name=key_ID_extension,json=keyIDExtension,proto3" json:"key_ID_extension,omitempty"`
+	Key             string          `protobuf:"bytes,3,opt,name=key,proto3" json:"key,omitempty"`
+	KeyExtension    *anypb.Any      `protobuf:"bytes,4,opt,name=key_extension,json=keyExtension,proto3" json:"key_extension,omitempty"`
+}
+
+func (x *Key) Reset() {
+	*x = Key{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_etsi_etsi14_proto_msgTypes[15]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Key) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Key) ProtoMessage() {}
+
+func (x *Key) ProtoReflect() protoreflect.Message {
+	mi := &file_etsi_etsi14_proto_msgTypes[15]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Key.ProtoReflect.Descriptor instead.
+func (*Key) Descriptor() ([]byte, []int) {
+	return file_etsi_etsi14_proto_rawDescGZIP(), []int{15}
+}
+
+func (x *Key) GetKey_ID() string {
+	if x != nil {
+		return x.Key_ID
+	}
+	return ""
+}
+
+func (x *Key) GetKey_IDExtension() *KeyIDExtension {
+	if x != nil {
+		return x.Key_IDExtension
+	}
+	return nil
+}
+
+func (x *Key) GetKey() string {
+	if x != nil {
+		return x.Key
+	}
+	return ""
+}
+
+func (x *Key) GetKeyExtension() *anypb.Any {
+	if x != nil {
+		return x.KeyExtension
+	}
+	return nil
+}
+
+var File_etsi_etsi14_proto protoreflect.FileDescriptor
+
+var file_etsi_etsi14_proto_rawDesc = []byte{
+	0x0a, 0x11, 0x65, 0x74, 0x73, 0x69, 0x2f, 0x65, 0x74, 0x73, 0x69, 0x31, 0x34, 0x2e, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x64, 0x61, 0x6e, 0x65, 0x74, 0x2e, 0x65, 0x74, 0x73, 0x69, 0x1a,
+	0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f,
+	0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x67,
+	0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64,
+	0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a,
+	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, 0x6e,
+	0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x6e,
+	0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a,
+	0x19, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
+	0x2f, 0x61, 0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x57, 0x0a, 0x10, 0x47, 0x65,
+	0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21,
+	0x0a, 0x0c, 0x4b, 0x4d, 0x45, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x4b, 0x4d, 0x45, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d,
+	0x65, 0x12, 0x20, 0x0a, 0x0c, 0x73, 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x53, 0x41, 0x45, 0x5f, 0x49,
+	0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6c, 0x61, 0x76, 0x65, 0x53, 0x41,
+	0x45, 0x49, 0x44, 0x22, 0x86, 0x01, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75,
+	0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d,
+	0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69,
+	0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,
+	0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x64, 0x61, 0x6e, 0x65, 0x74, 0x2e,
+	0x65, 0x74, 0x73, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61,
+	0x74, 0x75, 0x73, 0x12, 0x27, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01,
+	0x28, 0x0b, 0x32, 0x11, 0x2e, 0x64, 0x61, 0x6e, 0x65, 0x74, 0x2e, 0x65, 0x74, 0x73, 0x69, 0x2e,
+	0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x8d, 0x01, 0x0a,
+	0x0d, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21,
+	0x0a, 0x0c, 0x4b, 0x4d, 0x45, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x4b, 0x4d, 0x45, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d,
+	0x65, 0x12, 0x20, 0x0a, 0x0c, 0x73, 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x53, 0x41, 0x45, 0x5f, 0x49,
+	0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6c, 0x61, 0x76, 0x65, 0x53, 0x41,
+	0x45, 0x49, 0x44, 0x12, 0x37, 0x0a, 0x0b, 0x6b, 0x65, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65,
+	0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x64, 0x61, 0x6e, 0x65, 0x74,
+	0x2e, 0x65, 0x74, 0x73, 0x69, 0x2e, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+	0x52, 0x0a, 0x6b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x96, 0x01, 0x0a,
+	0x0e, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
+	0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x3d, 0x0a,
+	0x0d, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x64, 0x61, 0x6e, 0x65, 0x74, 0x2e, 0x65, 0x74, 0x73,
+	0x69, 0x2e, 0x4b, 0x65, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x0c,
+	0x6b, 0x65, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x27, 0x0a, 0x05,
+	0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x64, 0x61,
+	0x6e, 0x65, 0x74, 0x2e, 0x65, 0x74, 0x73, 0x69, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05,
+	0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xa9, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79,
+	0x57, 0x69, 0x74, 0x68, 0x49, 0x44, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21,
+	0x0a, 0x0c, 0x4b, 0x4d, 0x45, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x4b, 0x4d, 0x45, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d,
+	0x65, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x53, 0x41, 0x45, 0x5f,
+	0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72,
+	0x53, 0x41, 0x45, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0c, 0x73, 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x53,
+	0x41, 0x45, 0x5f, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6c, 0x61,
+	0x76, 0x65, 0x53, 0x41, 0x45, 0x49, 0x44, 0x12, 0x28, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x49,
+	0x44, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x64, 0x61, 0x6e, 0x65, 0x74, 0x2e,
+	0x65, 0x74, 0x73, 0x69, 0x2e, 0x4b, 0x65, 0x79, 0x49, 0x44, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49,
+	0x44, 0x22, 0x9d, 0x01, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x57, 0x69, 0x74, 0x68,
+	0x49, 0x44, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74,
+	0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09,
+	0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x3d, 0x0a, 0x0d, 0x6b, 0x65, 0x79,
+	0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x18, 0x2e, 0x64, 0x61, 0x6e, 0x65, 0x74, 0x2e, 0x65, 0x74, 0x73, 0x69, 0x2e, 0x4b, 0x65,
+	0x79, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x0c, 0x6b, 0x65, 0x79, 0x43,
+	0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x27, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f,
+	0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x64, 0x61, 0x6e, 0x65, 0x74, 0x2e,
+	0x65, 0x74, 0x73, 0x69, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f,
+	0x72, 0x22, 0xb2, 0x01, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x6d,
+	0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65,
+	0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x38, 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73,
+	0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x64, 0x61, 0x6e, 0x65, 0x74, 0x2e, 0x65,
+	0x74, 0x73, 0x69, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c,
+	0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x1a,
+	0x55, 0x0a, 0x0c, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12,
+	0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65,
+	0x79, 0x12, 0x2f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x19, 0x2e, 0x64, 0x61, 0x6e, 0x65, 0x74, 0x2e, 0x65, 0x74, 0x73, 0x69, 0x2e, 0x4c, 0x69,
+	0x73, 0x74, 0x4f, 0x66, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c,
+	0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x29, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x4f, 0x66,
+	0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69,
+	0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c,
+	0x73, 0x22, 0x9b, 0x03, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x22, 0x0a, 0x0d,
+	0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x4b, 0x4d, 0x45, 0x5f, 0x49, 0x44, 0x18, 0x01, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4b, 0x4d, 0x45, 0x49, 0x44,
+	0x12, 0x22, 0x0a, 0x0d, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x4b, 0x4d, 0x45, 0x5f, 0x49,
+	0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4b,
+	0x4d, 0x45, 0x49, 0x44, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x53,
+	0x41, 0x45, 0x5f, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6d, 0x61, 0x73,
+	0x74, 0x65, 0x72, 0x53, 0x41, 0x45, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0c, 0x73, 0x6c, 0x61, 0x76,
+	0x65, 0x5f, 0x53, 0x41, 0x45, 0x5f, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a,
+	0x73, 0x6c, 0x61, 0x76, 0x65, 0x53, 0x41, 0x45, 0x49, 0x44, 0x12, 0x19, 0x0a, 0x08, 0x6b, 0x65,
+	0x79, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6b, 0x65,
+	0x79, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x5f,
+	0x6b, 0x65, 0x79, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52,
+	0x0e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12,
+	0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74,
+	0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x4b, 0x65, 0x79, 0x43, 0x6f,
+	0x75, 0x6e, 0x74, 0x12, 0x2d, 0x0a, 0x13, 0x6d, 0x61, 0x78, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x70,
+	0x65, 0x72, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03,
+	0x52, 0x10, 0x6d, 0x61, 0x78, 0x4b, 0x65, 0x79, 0x50, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65,
+	0x73, 0x74, 0x12, 0x20, 0x0a, 0x0c, 0x6d, 0x61, 0x78, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x73, 0x69,
+	0x7a, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x6d, 0x61, 0x78, 0x4b, 0x65, 0x79,
+	0x53, 0x69, 0x7a, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x6d, 0x69, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x5f,
+	0x73, 0x69, 0x7a, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x6d, 0x69, 0x6e, 0x4b,
+	0x65, 0x79, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x27, 0x0a, 0x10, 0x6d, 0x61, 0x78, 0x5f, 0x53, 0x41,
+	0x45, 0x5f, 0x49, 0x44, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03,
+	0x52, 0x0d, 0x6d, 0x61, 0x78, 0x53, 0x41, 0x45, 0x49, 0x44, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22,
+	0x90, 0x02, 0x0a, 0x0a, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16,
+	0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06,
+	0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x37, 0x0a, 0x18, 0x61, 0x64,
+	0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x73, 0x6c, 0x61, 0x76, 0x65, 0x5f, 0x53,
+	0x41, 0x45, 0x5f, 0x49, 0x44, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x15, 0x61, 0x64,
+	0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x53, 0x6c, 0x61, 0x76, 0x65, 0x53, 0x41, 0x45,
+	0x49, 0x44, 0x73, 0x12, 0x4f, 0x0a, 0x13, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e,
+	0x5f, 0x6d, 0x61, 0x6e, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b,
+	0x32, 0x1e, 0x2e, 0x64, 0x61, 0x6e, 0x65, 0x74, 0x2e, 0x65, 0x74, 0x73, 0x69, 0x2e, 0x45, 0x78,
+	0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x6e, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x79,
+	0x52, 0x12, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x6e, 0x64, 0x61,
+	0x74, 0x6f, 0x72, 0x79, 0x12, 0x4c, 0x0a, 0x12, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f,
+	0x6e, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b,
+	0x32, 0x1d, 0x2e, 0x64, 0x61, 0x6e, 0x65, 0x74, 0x2e, 0x65, 0x74, 0x73, 0x69, 0x2e, 0x45, 0x78,
+	0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x52,
+	0x11, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+	0x61, 0x6c, 0x22, 0xda, 0x01, 0x0a, 0x12, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e,
+	0x4d, 0x61, 0x6e, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x67, 0x0a, 0x13, 0x65, 0x78, 0x74,
+	0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x61, 0x6e, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x79,
+	0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x64, 0x61, 0x6e, 0x65, 0x74, 0x2e, 0x65,
+	0x74, 0x73, 0x69, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x6e,
+	0x64, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e,
+	0x4d, 0x61, 0x6e, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x12,
+	0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4d, 0x61, 0x6e, 0x64, 0x61, 0x74, 0x6f,
+	0x72, 0x79, 0x1a, 0x5b, 0x0a, 0x17, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4d,
+	0x61, 0x6e, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
+	0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
+	0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14,
+	0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
+	0x2e, 0x41, 0x6e, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22,
+	0xd4, 0x01, 0x0a, 0x11, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x74,
+	0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x12, 0x63, 0x0a, 0x12, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69,
+	0x6f, 0x6e, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x03, 0x28,
+	0x0b, 0x32, 0x34, 0x2e, 0x64, 0x61, 0x6e, 0x65, 0x74, 0x2e, 0x65, 0x74, 0x73, 0x69, 0x2e, 0x45,
+	0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c,
+	0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+	0x61, 0x6c, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x11, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69,
+	0x6f, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x1a, 0x5a, 0x0a, 0x16, 0x45, 0x78,
+	0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x45,
+	0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
+	0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c,
+	0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x5e, 0x0a, 0x05, 0x4b, 0x65, 0x79, 0x49, 0x44, 0x12,
+	0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x05, 0x6b, 0x65, 0x79, 0x49, 0x44, 0x12, 0x3e, 0x0a, 0x10, 0x6b, 0x65, 0x79, 0x5f, 0x49, 0x44,
+	0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
+	0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x0e, 0x6b, 0x65, 0x79, 0x49, 0x44, 0x45, 0x78, 0x74,
+	0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x7f, 0x0a, 0x0c, 0x4b, 0x65, 0x79, 0x43, 0x6f, 0x6e,
+	0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20,
+	0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x64, 0x61, 0x6e, 0x65, 0x74, 0x2e, 0x65, 0x74, 0x73, 0x69,
+	0x2e, 0x4b, 0x65, 0x79, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x4c, 0x0a, 0x17, 0x6b, 0x65, 0x79,
+	0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e,
+	0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f,
+	0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79,
+	0x52, 0x15, 0x6b, 0x65, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x45, 0x78,
+	0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x10, 0x0a, 0x0e, 0x4b, 0x65, 0x79, 0x49, 0x44,
+	0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xaf, 0x01, 0x0a, 0x03, 0x4b, 0x65,
+	0x79, 0x12, 0x15, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x44, 0x12, 0x44, 0x0a, 0x10, 0x6b, 0x65, 0x79, 0x5f,
+	0x49, 0x44, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x64, 0x61, 0x6e, 0x65, 0x74, 0x2e, 0x65, 0x74, 0x73, 0x69, 0x2e,
+	0x4b, 0x65, 0x79, 0x49, 0x44, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0e,
+	0x6b, 0x65, 0x79, 0x49, 0x44, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x10,
+	0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79,
+	0x12, 0x39, 0x0a, 0x0d, 0x6b, 0x65, 0x79, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f,
+	0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
+	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x0c, 0x6b,
+	0x65, 0x79, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x32, 0xae, 0x03, 0x0a, 0x0d,
+	0x45, 0x54, 0x53, 0x49, 0x31, 0x34, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x83, 0x01,
+	0x0a, 0x09, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1c, 0x2e, 0x64, 0x61,
+	0x6e, 0x65, 0x74, 0x2e, 0x65, 0x74, 0x73, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74,
+	0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x64, 0x61, 0x6e, 0x65,
+	0x74, 0x2e, 0x65, 0x74, 0x73, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
+	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x39, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x33,
+	0x12, 0x31, 0x2f, 0x7b, 0x4b, 0x4d, 0x45, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65,
+	0x7d, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6b, 0x65, 0x79, 0x73, 0x2f, 0x7b, 0x73,
+	0x6c, 0x61, 0x76, 0x65, 0x5f, 0x53, 0x41, 0x45, 0x5f, 0x49, 0x44, 0x7d, 0x2f, 0x73, 0x74, 0x61,
+	0x74, 0x75, 0x73, 0x12, 0x7f, 0x0a, 0x06, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x19, 0x2e,
+	0x64, 0x61, 0x6e, 0x65, 0x74, 0x2e, 0x65, 0x74, 0x73, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x4b, 0x65,
+	0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x64, 0x61, 0x6e, 0x65, 0x74,
+	0x2e, 0x65, 0x74, 0x73, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70,
+	0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x38, 0x3a, 0x01, 0x2a, 0x22,
+	0x33, 0x2f, 0x7b, 0x4b, 0x4d, 0x45, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x7d,
+	0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6b, 0x65, 0x79, 0x73, 0x2f, 0x7b, 0x73, 0x6c,
+	0x61, 0x76, 0x65, 0x5f, 0x53, 0x41, 0x45, 0x5f, 0x49, 0x44, 0x7d, 0x2f, 0x65, 0x6e, 0x63, 0x5f,
+	0x6b, 0x65, 0x79, 0x73, 0x12, 0x95, 0x01, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x57,
+	0x69, 0x74, 0x68, 0x49, 0x44, 0x73, 0x12, 0x20, 0x2e, 0x64, 0x61, 0x6e, 0x65, 0x74, 0x2e, 0x65,
+	0x74, 0x73, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x57, 0x69, 0x74, 0x68, 0x49, 0x44,
+	0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x64, 0x61, 0x6e, 0x65, 0x74,
+	0x2e, 0x65, 0x74, 0x73, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x57, 0x69, 0x74, 0x68,
+	0x49, 0x44, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3f, 0x82, 0xd3, 0xe4,
+	0x93, 0x02, 0x39, 0x3a, 0x01, 0x2a, 0x22, 0x34, 0x2f, 0x7b, 0x4b, 0x4d, 0x45, 0x5f, 0x68, 0x6f,
+	0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x7d, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6b,
+	0x65, 0x79, 0x73, 0x2f, 0x7b, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x53, 0x41, 0x45, 0x5f,
+	0x49, 0x44, 0x7d, 0x2f, 0x64, 0x65, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x42, 0xc8, 0x01, 0x92,
+	0x41, 0x99, 0x01, 0x12, 0x96, 0x01, 0x0a, 0x16, 0x64, 0x61, 0x6e, 0x65, 0x74, 0x20, 0x45, 0x54,
+	0x53, 0x49, 0x31, 0x34, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x47,
+	0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x20, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x20,
+	0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x61,
+	0x6e, 0x64, 0x20, 0x47, 0x6f, 0x20, 0x63, 0x6f, 0x64, 0x65, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x74,
+	0x68, 0x65, 0x20, 0x45, 0x54, 0x53, 0x49, 0x31, 0x34, 0x20, 0x4b, 0x4d, 0x53, 0x20, 0x49, 0x6e,
+	0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x22, 0x16, 0x0a, 0x14, 0x64, 0x61, 0x6e, 0x65, 0x74,
+	0x20, 0x72, 0x65, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x20, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2a,
+	0x16, 0x0a, 0x14, 0x42, 0x53, 0x44, 0x20, 0x33, 0x2d, 0x43, 0x6c, 0x61, 0x75, 0x73, 0x65, 0x20,
+	0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x32, 0x03, 0x30, 0x2e, 0x31, 0x5a, 0x29, 0x63, 0x6f,
+	0x64, 0x65, 0x2e, 0x66, 0x62, 0x69, 0x2e, 0x68, 0x2d, 0x64, 0x61, 0x2e, 0x64, 0x65, 0x2f, 0x64,
+	0x61, 0x6e, 0x65, 0x74, 0x2f, 0x65, 0x74, 0x73, 0x69, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x6f,
+	0x2f, 0x65, 0x74, 0x73, 0x69, 0x31, 0x34, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_etsi_etsi14_proto_rawDescOnce sync.Once
+	file_etsi_etsi14_proto_rawDescData = file_etsi_etsi14_proto_rawDesc
+)
+
+func file_etsi_etsi14_proto_rawDescGZIP() []byte {
+	file_etsi_etsi14_proto_rawDescOnce.Do(func() {
+		file_etsi_etsi14_proto_rawDescData = protoimpl.X.CompressGZIP(file_etsi_etsi14_proto_rawDescData)
+	})
+	return file_etsi_etsi14_proto_rawDescData
+}
+
+var file_etsi_etsi14_proto_msgTypes = make([]protoimpl.MessageInfo, 19)
+var file_etsi_etsi14_proto_goTypes = []interface{}{
+	(*GetStatusRequest)(nil),      // 0: danet.etsi.GetStatusRequest
+	(*GetStatusResponse)(nil),     // 1: danet.etsi.GetStatusResponse
+	(*GetKeyRequest)(nil),         // 2: danet.etsi.GetKeyRequest
+	(*GetKeyResponse)(nil),        // 3: danet.etsi.GetKeyResponse
+	(*GetKeyWithIDsRequest)(nil),  // 4: danet.etsi.GetKeyWithIDsRequest
+	(*GetKeyWithIDsResponse)(nil), // 5: danet.etsi.GetKeyWithIDsResponse
+	(*Error)(nil),                 // 6: danet.etsi.Error
+	(*ListOfDetails)(nil),         // 7: danet.etsi.ListOfDetails
+	(*Status)(nil),                // 8: danet.etsi.Status
+	(*KeyRequest)(nil),            // 9: danet.etsi.KeyRequest
+	(*ExtensionMandatory)(nil),    // 10: danet.etsi.ExtensionMandatory
+	(*ExtensionOptional)(nil),     // 11: danet.etsi.ExtensionOptional
+	(*KeyID)(nil),                 // 12: danet.etsi.KeyID
+	(*KeyContainer)(nil),          // 13: danet.etsi.KeyContainer
+	(*KeyIDExtension)(nil),        // 14: danet.etsi.KeyIDExtension
+	(*Key)(nil),                   // 15: danet.etsi.Key
+	nil,                           // 16: danet.etsi.Error.DetailsEntry
+	nil,                           // 17: danet.etsi.ExtensionMandatory.ExtensionMandatoryEntry
+	nil,                           // 18: danet.etsi.ExtensionOptional.ExtensionOptionalEntry
+	(*anypb.Any)(nil),             // 19: google.protobuf.Any
+}
+var file_etsi_etsi14_proto_depIdxs = []int32{
+	8,  // 0: danet.etsi.GetStatusResponse.status:type_name -> danet.etsi.Status
+	6,  // 1: danet.etsi.GetStatusResponse.error:type_name -> danet.etsi.Error
+	9,  // 2: danet.etsi.GetKeyRequest.key_request:type_name -> danet.etsi.KeyRequest
+	13, // 3: danet.etsi.GetKeyResponse.key_container:type_name -> danet.etsi.KeyContainer
+	6,  // 4: danet.etsi.GetKeyResponse.error:type_name -> danet.etsi.Error
+	12, // 5: danet.etsi.GetKeyWithIDsRequest.key_ID:type_name -> danet.etsi.KeyID
+	13, // 6: danet.etsi.GetKeyWithIDsResponse.key_container:type_name -> danet.etsi.KeyContainer
+	6,  // 7: danet.etsi.GetKeyWithIDsResponse.error:type_name -> danet.etsi.Error
+	16, // 8: danet.etsi.Error.details:type_name -> danet.etsi.Error.DetailsEntry
+	10, // 9: danet.etsi.KeyRequest.extension_mandatory:type_name -> danet.etsi.ExtensionMandatory
+	11, // 10: danet.etsi.KeyRequest.extension_optional:type_name -> danet.etsi.ExtensionOptional
+	17, // 11: danet.etsi.ExtensionMandatory.extension_mandatory:type_name -> danet.etsi.ExtensionMandatory.ExtensionMandatoryEntry
+	18, // 12: danet.etsi.ExtensionOptional.extension_optional:type_name -> danet.etsi.ExtensionOptional.ExtensionOptionalEntry
+	19, // 13: danet.etsi.KeyID.key_ID_extension:type_name -> google.protobuf.Any
+	15, // 14: danet.etsi.KeyContainer.key:type_name -> danet.etsi.Key
+	19, // 15: danet.etsi.KeyContainer.key_container_extension:type_name -> google.protobuf.Any
+	14, // 16: danet.etsi.Key.key_ID_extension:type_name -> danet.etsi.KeyIDExtension
+	19, // 17: danet.etsi.Key.key_extension:type_name -> google.protobuf.Any
+	7,  // 18: danet.etsi.Error.DetailsEntry.value:type_name -> danet.etsi.ListOfDetails
+	19, // 19: danet.etsi.ExtensionMandatory.ExtensionMandatoryEntry.value:type_name -> google.protobuf.Any
+	19, // 20: danet.etsi.ExtensionOptional.ExtensionOptionalEntry.value:type_name -> google.protobuf.Any
+	0,  // 21: danet.etsi.ETSI14Service.GetStatus:input_type -> danet.etsi.GetStatusRequest
+	2,  // 22: danet.etsi.ETSI14Service.GetKey:input_type -> danet.etsi.GetKeyRequest
+	4,  // 23: danet.etsi.ETSI14Service.GetKeyWithIDs:input_type -> danet.etsi.GetKeyWithIDsRequest
+	1,  // 24: danet.etsi.ETSI14Service.GetStatus:output_type -> danet.etsi.GetStatusResponse
+	3,  // 25: danet.etsi.ETSI14Service.GetKey:output_type -> danet.etsi.GetKeyResponse
+	5,  // 26: danet.etsi.ETSI14Service.GetKeyWithIDs:output_type -> danet.etsi.GetKeyWithIDsResponse
+	24, // [24:27] is the sub-list for method output_type
+	21, // [21:24] is the sub-list for method input_type
+	21, // [21:21] is the sub-list for extension type_name
+	21, // [21:21] is the sub-list for extension extendee
+	0,  // [0:21] is the sub-list for field type_name
+}
+
+func init() { file_etsi_etsi14_proto_init() }
+func file_etsi_etsi14_proto_init() {
+	if File_etsi_etsi14_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_etsi_etsi14_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*GetStatusRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_etsi_etsi14_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*GetStatusResponse); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_etsi_etsi14_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*GetKeyRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_etsi_etsi14_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*GetKeyResponse); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_etsi_etsi14_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*GetKeyWithIDsRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_etsi_etsi14_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*GetKeyWithIDsResponse); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_etsi_etsi14_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Error); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_etsi_etsi14_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ListOfDetails); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_etsi_etsi14_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Status); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_etsi_etsi14_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*KeyRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_etsi_etsi14_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ExtensionMandatory); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_etsi_etsi14_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ExtensionOptional); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_etsi_etsi14_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*KeyID); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_etsi_etsi14_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*KeyContainer); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_etsi_etsi14_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*KeyIDExtension); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_etsi_etsi14_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Key); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_etsi_etsi14_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   19,
+			NumExtensions: 0,
+			NumServices:   1,
+		},
+		GoTypes:           file_etsi_etsi14_proto_goTypes,
+		DependencyIndexes: file_etsi_etsi14_proto_depIdxs,
+		MessageInfos:      file_etsi_etsi14_proto_msgTypes,
+	}.Build()
+	File_etsi_etsi14_proto = out.File
+	file_etsi_etsi14_proto_rawDesc = nil
+	file_etsi_etsi14_proto_goTypes = nil
+	file_etsi_etsi14_proto_depIdxs = nil
+}
diff --git a/api/go/grpc/etsi/etsi14.pb.gw.go b/api/go/grpc/etsi/etsi14.pb.gw.go
new file mode 100644
index 0000000000000000000000000000000000000000..af13502e940115f8bb17e4baa7200bafdea38bb9
--- /dev/null
+++ b/api/go/grpc/etsi/etsi14.pb.gw.go
@@ -0,0 +1,487 @@
+// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
+// source: etsi/etsi14.proto
+
+/*
+Package etsi14 is a reverse proxy.
+
+It translates gRPC into RESTful JSON APIs.
+*/
+package etsi14
+
+import (
+	"context"
+	"io"
+	"net/http"
+
+	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
+	"github.com/grpc-ecosystem/grpc-gateway/v2/utilities"
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/grpclog"
+	"google.golang.org/grpc/metadata"
+	"google.golang.org/grpc/status"
+	"google.golang.org/protobuf/proto"
+)
+
+// Suppress "imported and not used" errors
+var _ codes.Code
+var _ io.Reader
+var _ status.Status
+var _ = runtime.String
+var _ = utilities.NewDoubleArray
+var _ = metadata.Join
+
+func request_ETSI14Service_GetStatus_0(ctx context.Context, marshaler runtime.Marshaler, client ETSI14ServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq GetStatusRequest
+	var metadata runtime.ServerMetadata
+
+	var (
+		val string
+		ok  bool
+		err error
+		_   = err
+	)
+
+	val, ok = pathParams["KME_hostname"]
+	if !ok {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "KME_hostname")
+	}
+
+	protoReq.KMEHostname, err = runtime.String(val)
+	if err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "KME_hostname", err)
+	}
+
+	val, ok = pathParams["slave_SAE_ID"]
+	if !ok {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "slave_SAE_ID")
+	}
+
+	protoReq.Slave_SAE_ID, err = runtime.String(val)
+	if err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "slave_SAE_ID", err)
+	}
+
+	msg, err := client.GetStatus(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_ETSI14Service_GetStatus_0(ctx context.Context, marshaler runtime.Marshaler, server ETSI14ServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq GetStatusRequest
+	var metadata runtime.ServerMetadata
+
+	var (
+		val string
+		ok  bool
+		err error
+		_   = err
+	)
+
+	val, ok = pathParams["KME_hostname"]
+	if !ok {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "KME_hostname")
+	}
+
+	protoReq.KMEHostname, err = runtime.String(val)
+	if err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "KME_hostname", err)
+	}
+
+	val, ok = pathParams["slave_SAE_ID"]
+	if !ok {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "slave_SAE_ID")
+	}
+
+	protoReq.Slave_SAE_ID, err = runtime.String(val)
+	if err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "slave_SAE_ID", err)
+	}
+
+	msg, err := server.GetStatus(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
+func request_ETSI14Service_GetKey_0(ctx context.Context, marshaler runtime.Marshaler, client ETSI14ServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq GetKeyRequest
+	var metadata runtime.ServerMetadata
+
+	newReader, berr := utilities.IOReaderFactory(req.Body)
+	if berr != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
+	}
+	if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	var (
+		val string
+		ok  bool
+		err error
+		_   = err
+	)
+
+	val, ok = pathParams["KME_hostname"]
+	if !ok {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "KME_hostname")
+	}
+
+	protoReq.KMEHostname, err = runtime.String(val)
+	if err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "KME_hostname", err)
+	}
+
+	val, ok = pathParams["slave_SAE_ID"]
+	if !ok {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "slave_SAE_ID")
+	}
+
+	protoReq.Slave_SAE_ID, err = runtime.String(val)
+	if err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "slave_SAE_ID", err)
+	}
+
+	msg, err := client.GetKey(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_ETSI14Service_GetKey_0(ctx context.Context, marshaler runtime.Marshaler, server ETSI14ServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq GetKeyRequest
+	var metadata runtime.ServerMetadata
+
+	newReader, berr := utilities.IOReaderFactory(req.Body)
+	if berr != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
+	}
+	if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	var (
+		val string
+		ok  bool
+		err error
+		_   = err
+	)
+
+	val, ok = pathParams["KME_hostname"]
+	if !ok {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "KME_hostname")
+	}
+
+	protoReq.KMEHostname, err = runtime.String(val)
+	if err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "KME_hostname", err)
+	}
+
+	val, ok = pathParams["slave_SAE_ID"]
+	if !ok {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "slave_SAE_ID")
+	}
+
+	protoReq.Slave_SAE_ID, err = runtime.String(val)
+	if err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "slave_SAE_ID", err)
+	}
+
+	msg, err := server.GetKey(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
+func request_ETSI14Service_GetKeyWithIDs_0(ctx context.Context, marshaler runtime.Marshaler, client ETSI14ServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq GetKeyWithIDsRequest
+	var metadata runtime.ServerMetadata
+
+	newReader, berr := utilities.IOReaderFactory(req.Body)
+	if berr != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
+	}
+	if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	var (
+		val string
+		ok  bool
+		err error
+		_   = err
+	)
+
+	val, ok = pathParams["KME_hostname"]
+	if !ok {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "KME_hostname")
+	}
+
+	protoReq.KMEHostname, err = runtime.String(val)
+	if err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "KME_hostname", err)
+	}
+
+	val, ok = pathParams["master_SAE_ID"]
+	if !ok {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "master_SAE_ID")
+	}
+
+	protoReq.Master_SAE_ID, err = runtime.String(val)
+	if err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "master_SAE_ID", err)
+	}
+
+	msg, err := client.GetKeyWithIDs(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_ETSI14Service_GetKeyWithIDs_0(ctx context.Context, marshaler runtime.Marshaler, server ETSI14ServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq GetKeyWithIDsRequest
+	var metadata runtime.ServerMetadata
+
+	newReader, berr := utilities.IOReaderFactory(req.Body)
+	if berr != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
+	}
+	if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
+	}
+
+	var (
+		val string
+		ok  bool
+		err error
+		_   = err
+	)
+
+	val, ok = pathParams["KME_hostname"]
+	if !ok {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "KME_hostname")
+	}
+
+	protoReq.KMEHostname, err = runtime.String(val)
+	if err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "KME_hostname", err)
+	}
+
+	val, ok = pathParams["master_SAE_ID"]
+	if !ok {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "master_SAE_ID")
+	}
+
+	protoReq.Master_SAE_ID, err = runtime.String(val)
+	if err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "master_SAE_ID", err)
+	}
+
+	msg, err := server.GetKeyWithIDs(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
+// RegisterETSI14ServiceHandlerServer registers the http handlers for service ETSI14Service to "mux".
+// UnaryRPC     :call ETSI14ServiceServer directly.
+// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
+// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterETSI14ServiceHandlerFromEndpoint instead.
+func RegisterETSI14ServiceHandlerServer(ctx context.Context, mux *runtime.ServeMux, server ETSI14ServiceServer) error {
+
+	mux.Handle("GET", pattern_ETSI14Service_GetStatus_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		var stream runtime.ServerTransportStream
+		ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		var err error
+		var annotatedContext context.Context
+		annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/danet.etsi.ETSI14Service/GetStatus", runtime.WithHTTPPathPattern("/{KME_hostname}/api/v1/keys/{slave_SAE_ID}/status"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := local_request_ETSI14Service_GetStatus_0(annotatedContext, inboundMarshaler, server, req, pathParams)
+		md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+		annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
+		if err != nil {
+			runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_ETSI14Service_GetStatus_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("POST", pattern_ETSI14Service_GetKey_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		var stream runtime.ServerTransportStream
+		ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		var err error
+		var annotatedContext context.Context
+		annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/danet.etsi.ETSI14Service/GetKey", runtime.WithHTTPPathPattern("/{KME_hostname}/api/v1/keys/{slave_SAE_ID}/enc_keys"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := local_request_ETSI14Service_GetKey_0(annotatedContext, inboundMarshaler, server, req, pathParams)
+		md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+		annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
+		if err != nil {
+			runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_ETSI14Service_GetKey_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("POST", pattern_ETSI14Service_GetKeyWithIDs_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		var stream runtime.ServerTransportStream
+		ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		var err error
+		var annotatedContext context.Context
+		annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/danet.etsi.ETSI14Service/GetKeyWithIDs", runtime.WithHTTPPathPattern("/{KME_hostname}/api/v1/keys/{master_SAE_ID}/dec_keys"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := local_request_ETSI14Service_GetKeyWithIDs_0(annotatedContext, inboundMarshaler, server, req, pathParams)
+		md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+		annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
+		if err != nil {
+			runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_ETSI14Service_GetKeyWithIDs_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	return nil
+}
+
+// RegisterETSI14ServiceHandlerFromEndpoint is same as RegisterETSI14ServiceHandler but
+// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
+func RegisterETSI14ServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
+	conn, err := grpc.DialContext(ctx, endpoint, opts...)
+	if err != nil {
+		return err
+	}
+	defer func() {
+		if err != nil {
+			if cerr := conn.Close(); cerr != nil {
+				grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
+			}
+			return
+		}
+		go func() {
+			<-ctx.Done()
+			if cerr := conn.Close(); cerr != nil {
+				grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
+			}
+		}()
+	}()
+
+	return RegisterETSI14ServiceHandler(ctx, mux, conn)
+}
+
+// RegisterETSI14ServiceHandler registers the http handlers for service ETSI14Service to "mux".
+// The handlers forward requests to the grpc endpoint over "conn".
+func RegisterETSI14ServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
+	return RegisterETSI14ServiceHandlerClient(ctx, mux, NewETSI14ServiceClient(conn))
+}
+
+// RegisterETSI14ServiceHandlerClient registers the http handlers for service ETSI14Service
+// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "ETSI14ServiceClient".
+// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "ETSI14ServiceClient"
+// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
+// "ETSI14ServiceClient" to call the correct interceptors.
+func RegisterETSI14ServiceHandlerClient(ctx context.Context, mux *runtime.ServeMux, client ETSI14ServiceClient) error {
+
+	mux.Handle("GET", pattern_ETSI14Service_GetStatus_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		var err error
+		var annotatedContext context.Context
+		annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/danet.etsi.ETSI14Service/GetStatus", runtime.WithHTTPPathPattern("/{KME_hostname}/api/v1/keys/{slave_SAE_ID}/status"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_ETSI14Service_GetStatus_0(annotatedContext, inboundMarshaler, client, req, pathParams)
+		annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
+		if err != nil {
+			runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_ETSI14Service_GetStatus_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("POST", pattern_ETSI14Service_GetKey_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		var err error
+		var annotatedContext context.Context
+		annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/danet.etsi.ETSI14Service/GetKey", runtime.WithHTTPPathPattern("/{KME_hostname}/api/v1/keys/{slave_SAE_ID}/enc_keys"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_ETSI14Service_GetKey_0(annotatedContext, inboundMarshaler, client, req, pathParams)
+		annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
+		if err != nil {
+			runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_ETSI14Service_GetKey_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("POST", pattern_ETSI14Service_GetKeyWithIDs_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		var err error
+		var annotatedContext context.Context
+		annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/danet.etsi.ETSI14Service/GetKeyWithIDs", runtime.WithHTTPPathPattern("/{KME_hostname}/api/v1/keys/{master_SAE_ID}/dec_keys"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_ETSI14Service_GetKeyWithIDs_0(annotatedContext, inboundMarshaler, client, req, pathParams)
+		annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
+		if err != nil {
+			runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_ETSI14Service_GetKeyWithIDs_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	return nil
+}
+
+var (
+	pattern_ETSI14Service_GetStatus_0 = runtime.MustPattern(runtime.NewPattern(1, []int{1, 0, 4, 1, 5, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"KME_hostname", "api", "v1", "keys", "slave_SAE_ID", "status"}, ""))
+
+	pattern_ETSI14Service_GetKey_0 = runtime.MustPattern(runtime.NewPattern(1, []int{1, 0, 4, 1, 5, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"KME_hostname", "api", "v1", "keys", "slave_SAE_ID", "enc_keys"}, ""))
+
+	pattern_ETSI14Service_GetKeyWithIDs_0 = runtime.MustPattern(runtime.NewPattern(1, []int{1, 0, 4, 1, 5, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"KME_hostname", "api", "v1", "keys", "master_SAE_ID", "dec_keys"}, ""))
+)
+
+var (
+	forward_ETSI14Service_GetStatus_0 = runtime.ForwardResponseMessage
+
+	forward_ETSI14Service_GetKey_0 = runtime.ForwardResponseMessage
+
+	forward_ETSI14Service_GetKeyWithIDs_0 = runtime.ForwardResponseMessage
+)
diff --git a/api/go/grpc/etsi/etsi14_grpc.pb.go b/api/go/grpc/etsi/etsi14_grpc.pb.go
new file mode 100644
index 0000000000000000000000000000000000000000..3ae7d92d01925e03406a649869642f8128f36e4f
--- /dev/null
+++ b/api/go/grpc/etsi/etsi14_grpc.pb.go
@@ -0,0 +1,211 @@
+// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
+// versions:
+// - protoc-gen-go-grpc v1.3.0
+// - protoc             (unknown)
+// source: etsi/etsi14.proto
+
+package etsi14
+
+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.32.0 or later.
+const _ = grpc.SupportPackageIsVersion7
+
+const (
+	ETSI14Service_GetStatus_FullMethodName     = "/danet.etsi.ETSI14Service/GetStatus"
+	ETSI14Service_GetKey_FullMethodName        = "/danet.etsi.ETSI14Service/GetKey"
+	ETSI14Service_GetKeyWithIDs_FullMethodName = "/danet.etsi.ETSI14Service/GetKeyWithIDs"
+)
+
+// ETSI14ServiceClient is the client API for ETSI14Service 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.
+type ETSI14ServiceClient interface {
+	// Returns Status from a KME to the calling SAE.
+	// Status contains information on keys available to be requested
+	// by a master SAE for a specified slave SAE.
+	GetStatus(ctx context.Context, in *GetStatusRequest, opts ...grpc.CallOption) (*GetStatusResponse, error)
+	// Returns Key container data from the KME to the calling master SAE.
+	// Key container data contains one or more keys. The calling master SAE may supply
+	// Key request data to specify the requirement on Key container data. The slave SAE
+	// specified by the slave_SAE_ID parameter may subsequently request matching keys
+	// from a remote KME using key_ID identifiers from the returned Key container.
+	GetKey(ctx context.Context, in *GetKeyRequest, opts ...grpc.CallOption) (*GetKeyResponse, error)
+	// Returns Key container from the KME to the calling slave SAE.
+	// Key container contains keys matching those previously delivered to a remote master SAE
+	// based on the Key IDs supplied from the remote master SAE in response to its call to Get key.
+	// The KME shall reject the request with a 401 HTTP status code if the SAE ID of the requestor
+	// was not an SAE ID supplied to the "Get key" method each time it was called resulting in the
+	// return of any of the Key IDs being requested.
+	GetKeyWithIDs(ctx context.Context, in *GetKeyWithIDsRequest, opts ...grpc.CallOption) (*GetKeyWithIDsResponse, error)
+}
+
+type eTSI14ServiceClient struct {
+	cc grpc.ClientConnInterface
+}
+
+func NewETSI14ServiceClient(cc grpc.ClientConnInterface) ETSI14ServiceClient {
+	return &eTSI14ServiceClient{cc}
+}
+
+func (c *eTSI14ServiceClient) GetStatus(ctx context.Context, in *GetStatusRequest, opts ...grpc.CallOption) (*GetStatusResponse, error) {
+	out := new(GetStatusResponse)
+	err := c.cc.Invoke(ctx, ETSI14Service_GetStatus_FullMethodName, in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *eTSI14ServiceClient) GetKey(ctx context.Context, in *GetKeyRequest, opts ...grpc.CallOption) (*GetKeyResponse, error) {
+	out := new(GetKeyResponse)
+	err := c.cc.Invoke(ctx, ETSI14Service_GetKey_FullMethodName, in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *eTSI14ServiceClient) GetKeyWithIDs(ctx context.Context, in *GetKeyWithIDsRequest, opts ...grpc.CallOption) (*GetKeyWithIDsResponse, error) {
+	out := new(GetKeyWithIDsResponse)
+	err := c.cc.Invoke(ctx, ETSI14Service_GetKeyWithIDs_FullMethodName, in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+// ETSI14ServiceServer is the server API for ETSI14Service service.
+// All implementations must embed UnimplementedETSI14ServiceServer
+// for forward compatibility
+type ETSI14ServiceServer interface {
+	// Returns Status from a KME to the calling SAE.
+	// Status contains information on keys available to be requested
+	// by a master SAE for a specified slave SAE.
+	GetStatus(context.Context, *GetStatusRequest) (*GetStatusResponse, error)
+	// Returns Key container data from the KME to the calling master SAE.
+	// Key container data contains one or more keys. The calling master SAE may supply
+	// Key request data to specify the requirement on Key container data. The slave SAE
+	// specified by the slave_SAE_ID parameter may subsequently request matching keys
+	// from a remote KME using key_ID identifiers from the returned Key container.
+	GetKey(context.Context, *GetKeyRequest) (*GetKeyResponse, error)
+	// Returns Key container from the KME to the calling slave SAE.
+	// Key container contains keys matching those previously delivered to a remote master SAE
+	// based on the Key IDs supplied from the remote master SAE in response to its call to Get key.
+	// The KME shall reject the request with a 401 HTTP status code if the SAE ID of the requestor
+	// was not an SAE ID supplied to the "Get key" method each time it was called resulting in the
+	// return of any of the Key IDs being requested.
+	GetKeyWithIDs(context.Context, *GetKeyWithIDsRequest) (*GetKeyWithIDsResponse, error)
+	mustEmbedUnimplementedETSI14ServiceServer()
+}
+
+// UnimplementedETSI14ServiceServer must be embedded to have forward compatible implementations.
+type UnimplementedETSI14ServiceServer struct {
+}
+
+func (UnimplementedETSI14ServiceServer) GetStatus(context.Context, *GetStatusRequest) (*GetStatusResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method GetStatus not implemented")
+}
+func (UnimplementedETSI14ServiceServer) GetKey(context.Context, *GetKeyRequest) (*GetKeyResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method GetKey not implemented")
+}
+func (UnimplementedETSI14ServiceServer) GetKeyWithIDs(context.Context, *GetKeyWithIDsRequest) (*GetKeyWithIDsResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method GetKeyWithIDs not implemented")
+}
+func (UnimplementedETSI14ServiceServer) mustEmbedUnimplementedETSI14ServiceServer() {}
+
+// UnsafeETSI14ServiceServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to ETSI14ServiceServer will
+// result in compilation errors.
+type UnsafeETSI14ServiceServer interface {
+	mustEmbedUnimplementedETSI14ServiceServer()
+}
+
+func RegisterETSI14ServiceServer(s grpc.ServiceRegistrar, srv ETSI14ServiceServer) {
+	s.RegisterService(&ETSI14Service_ServiceDesc, srv)
+}
+
+func _ETSI14Service_GetStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(GetStatusRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(ETSI14ServiceServer).GetStatus(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: ETSI14Service_GetStatus_FullMethodName,
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(ETSI14ServiceServer).GetStatus(ctx, req.(*GetStatusRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _ETSI14Service_GetKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(GetKeyRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(ETSI14ServiceServer).GetKey(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: ETSI14Service_GetKey_FullMethodName,
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(ETSI14ServiceServer).GetKey(ctx, req.(*GetKeyRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _ETSI14Service_GetKeyWithIDs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(GetKeyWithIDsRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(ETSI14ServiceServer).GetKeyWithIDs(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: ETSI14Service_GetKeyWithIDs_FullMethodName,
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(ETSI14ServiceServer).GetKeyWithIDs(ctx, req.(*GetKeyWithIDsRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+// ETSI14Service_ServiceDesc is the grpc.ServiceDesc for ETSI14Service service.
+// It's only intended for direct use with grpc.RegisterService,
+// and not to be introspected or modified (even as a copy)
+var ETSI14Service_ServiceDesc = grpc.ServiceDesc{
+	ServiceName: "danet.etsi.ETSI14Service",
+	HandlerType: (*ETSI14ServiceServer)(nil),
+	Methods: []grpc.MethodDesc{
+		{
+			MethodName: "GetStatus",
+			Handler:    _ETSI14Service_GetStatus_Handler,
+		},
+		{
+			MethodName: "GetKey",
+			Handler:    _ETSI14Service_GetKey_Handler,
+		},
+		{
+			MethodName: "GetKeyWithIDs",
+			Handler:    _ETSI14Service_GetKeyWithIDs_Handler,
+		},
+	},
+	Streams:  []grpc.StreamDesc{},
+	Metadata: "etsi/etsi14.proto",
+}
diff --git a/api/openapiv2/ekms_etsi14.swagger.json b/api/openapiv2/ekms_etsi14.swagger.json
new file mode 100644
index 0000000000000000000000000000000000000000..f0cb0616a5321931aa9db07e8e8b3920bdc44312
--- /dev/null
+++ b/api/openapiv2/ekms_etsi14.swagger.json
@@ -0,0 +1,426 @@
+{
+  "swagger": "2.0",
+  "info": {
+    "title": "danet ETSI14 Interface",
+    "description": "Protocol Buffer specifications and Go code for the ETSI14 KMS Interface",
+    "version": "0.1",
+    "contact": {
+      "name": "danet research group"
+    },
+    "license": {
+      "name": "BSD 3-Clause License"
+    }
+  },
+  "tags": [
+    {
+      "name": "ETSI14Service"
+    }
+  ],
+  "consumes": [
+    "application/json"
+  ],
+  "produces": [
+    "application/json"
+  ],
+  "paths": {
+    "/{KMEHostname}/api/v1/keys/{masterSAEID}/dec_keys": {
+      "post": {
+        "summary": "Returns Key container from the KME to the calling slave SAE. \nKey container contains keys matching those previously delivered to a remote master SAE\nbased on the Key IDs supplied from the remote master SAE in response to its call to Get key.\nThe KME shall reject the request with a 401 HTTP status code if the SAE ID of the requestor \nwas not an SAE ID supplied to the \"Get key\" method each time it was called resulting in the\nreturn of any of the Key IDs being requested.",
+        "operationId": "ETSI14Service_GetKeyWithIDs",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/etsiGetKeyWithIDsResponse"
+            }
+          },
+          "default": {
+            "description": "An unexpected error response.",
+            "schema": {
+              "$ref": "#/definitions/googlerpcStatus"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "KMEHostname",
+            "in": "path",
+            "required": true,
+            "type": "string"
+          },
+          {
+            "name": "masterSAEID",
+            "in": "path",
+            "required": true,
+            "type": "string"
+          },
+          {
+            "name": "body",
+            "in": "body",
+            "required": true,
+            "schema": {
+              "type": "object",
+              "properties": {
+                "slaveSAEID": {
+                  "type": "string"
+                },
+                "keyID": {
+                  "type": "array",
+                  "items": {
+                    "type": "object",
+                    "$ref": "#/definitions/etsiKeyID"
+                  }
+                }
+              }
+            }
+          }
+        ],
+        "tags": [
+          "ETSI14Service"
+        ]
+      }
+    },
+    "/{KMEHostname}/api/v1/keys/{slaveSAEID}/enc_keys": {
+      "post": {
+        "summary": "Returns Key container data from the KME to the calling master SAE. \nKey container data contains one or more keys. The calling master SAE may supply \nKey request data to specify the requirement on Key container data. The slave SAE \nspecified by the slave_SAE_ID parameter may subsequently request matching keys \nfrom a remote KME using key_ID identifiers from the returned Key container.",
+        "operationId": "ETSI14Service_GetKey",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/etsiGetKeyResponse"
+            }
+          },
+          "default": {
+            "description": "An unexpected error response.",
+            "schema": {
+              "$ref": "#/definitions/googlerpcStatus"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "KMEHostname",
+            "in": "path",
+            "required": true,
+            "type": "string"
+          },
+          {
+            "name": "slaveSAEID",
+            "in": "path",
+            "required": true,
+            "type": "string"
+          },
+          {
+            "name": "body",
+            "in": "body",
+            "required": true,
+            "schema": {
+              "type": "object",
+              "properties": {
+                "keyRequest": {
+                  "$ref": "#/definitions/etsiKeyRequest"
+                }
+              }
+            }
+          }
+        ],
+        "tags": [
+          "ETSI14Service"
+        ]
+      }
+    },
+    "/{KMEHostname}/api/v1/keys/{slaveSAEID}/status": {
+      "get": {
+        "summary": "Returns Status from a KME to the calling SAE. \nStatus contains information on keys available to be requested \nby a master SAE for a specified slave SAE.",
+        "operationId": "ETSI14Service_GetStatus",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/etsiGetStatusResponse"
+            }
+          },
+          "default": {
+            "description": "An unexpected error response.",
+            "schema": {
+              "$ref": "#/definitions/googlerpcStatus"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "KMEHostname",
+            "in": "path",
+            "required": true,
+            "type": "string"
+          },
+          {
+            "name": "slaveSAEID",
+            "in": "path",
+            "required": true,
+            "type": "string"
+          }
+        ],
+        "tags": [
+          "ETSI14Service"
+        ]
+      }
+    }
+  },
+  "definitions": {
+    "danetetsiStatus": {
+      "type": "object",
+      "properties": {
+        "sourceKMEID": {
+          "type": "string"
+        },
+        "targetKMEID": {
+          "type": "string"
+        },
+        "masterSAEID": {
+          "type": "string"
+        },
+        "slaveSAEID": {
+          "type": "string"
+        },
+        "keySize": {
+          "type": "string",
+          "format": "int64"
+        },
+        "storedKeyCount": {
+          "type": "string",
+          "format": "int64"
+        },
+        "maxKeyCount": {
+          "type": "string",
+          "format": "int64"
+        },
+        "maxKeyPerRequest": {
+          "type": "string",
+          "format": "int64"
+        },
+        "maxKeySize": {
+          "type": "string",
+          "format": "int64"
+        },
+        "minKeySize": {
+          "type": "string",
+          "format": "int64"
+        },
+        "maxSAEIDCount": {
+          "type": "string",
+          "format": "int64"
+        }
+      }
+    },
+    "etsiError": {
+      "type": "object",
+      "properties": {
+        "message": {
+          "type": "string",
+          "title": "ErrorCode code = 1;"
+        },
+        "details": {
+          "type": "object",
+          "additionalProperties": {
+            "$ref": "#/definitions/etsiListOfDetails"
+          },
+          "title": "maybe map\u003cstring, string\u003e is enough here?!"
+        }
+      },
+      "title": "FIGURE THIS ERROR STUFF OUT"
+    },
+    "etsiExtensionMandatory": {
+      "type": "object",
+      "properties": {
+        "extensionMandatory": {
+          "type": "object",
+          "additionalProperties": {
+            "$ref": "#/definitions/protobufAny"
+          }
+        }
+      },
+      "description": "Array of extension parameters specified as name/value pairs that KME shall \nhandle or return an error. Parameter values may be of any type, including objects."
+    },
+    "etsiExtensionOptional": {
+      "type": "object",
+      "properties": {
+        "extensionOptional": {
+          "type": "object",
+          "additionalProperties": {
+            "$ref": "#/definitions/protobufAny"
+          }
+        }
+      },
+      "description": "Array of extension parameters specified as name/value pairs that KME may ignore. \nParameter values may be of any type, including objects."
+    },
+    "etsiGetKeyResponse": {
+      "type": "object",
+      "properties": {
+        "timestamp": {
+          "type": "string",
+          "format": "int64"
+        },
+        "keyContainer": {
+          "$ref": "#/definitions/etsiKeyContainer"
+        },
+        "error": {
+          "$ref": "#/definitions/etsiError"
+        }
+      }
+    },
+    "etsiGetKeyWithIDsResponse": {
+      "type": "object",
+      "properties": {
+        "timestamp": {
+          "type": "string",
+          "format": "int64"
+        },
+        "keyContainer": {
+          "$ref": "#/definitions/etsiKeyContainer"
+        },
+        "error": {
+          "$ref": "#/definitions/etsiError"
+        }
+      }
+    },
+    "etsiGetStatusResponse": {
+      "type": "object",
+      "properties": {
+        "timestamp": {
+          "type": "string",
+          "format": "int64"
+        },
+        "status": {
+          "$ref": "#/definitions/danetetsiStatus"
+        },
+        "error": {
+          "$ref": "#/definitions/etsiError"
+        }
+      }
+    },
+    "etsiKey": {
+      "type": "object",
+      "properties": {
+        "keyID": {
+          "type": "string"
+        },
+        "keyIDExtension": {
+          "$ref": "#/definitions/etsiKeyIDExtension"
+        },
+        "key": {
+          "type": "string"
+        },
+        "keyExtension": {
+          "$ref": "#/definitions/protobufAny"
+        }
+      }
+    },
+    "etsiKeyContainer": {
+      "type": "object",
+      "properties": {
+        "key": {
+          "type": "array",
+          "items": {
+            "type": "object",
+            "$ref": "#/definitions/etsiKey"
+          }
+        },
+        "keyContainerExtension": {
+          "$ref": "#/definitions/protobufAny"
+        }
+      }
+    },
+    "etsiKeyID": {
+      "type": "object",
+      "properties": {
+        "keyID": {
+          "type": "string"
+        },
+        "keyIDExtension": {
+          "$ref": "#/definitions/protobufAny"
+        }
+      }
+    },
+    "etsiKeyIDExtension": {
+      "type": "object",
+      "description": "optional for future use."
+    },
+    "etsiKeyRequest": {
+      "type": "object",
+      "properties": {
+        "number": {
+          "type": "string",
+          "format": "int64"
+        },
+        "size": {
+          "type": "string",
+          "format": "int64"
+        },
+        "additionalSlaveSAEIDs": {
+          "type": "array",
+          "items": {
+            "type": "string"
+          }
+        },
+        "extensionMandatory": {
+          "type": "array",
+          "items": {
+            "type": "object",
+            "$ref": "#/definitions/etsiExtensionMandatory"
+          }
+        },
+        "extensionOptional": {
+          "type": "array",
+          "items": {
+            "type": "object",
+            "$ref": "#/definitions/etsiExtensionOptional"
+          }
+        }
+      }
+    },
+    "etsiListOfDetails": {
+      "type": "object",
+      "properties": {
+        "details": {
+          "type": "array",
+          "items": {
+            "type": "string"
+          }
+        }
+      },
+      "title": "Maybe not neccessary"
+    },
+    "googlerpcStatus": {
+      "type": "object",
+      "properties": {
+        "code": {
+          "type": "integer",
+          "format": "int32"
+        },
+        "message": {
+          "type": "string"
+        },
+        "details": {
+          "type": "array",
+          "items": {
+            "type": "object",
+            "$ref": "#/definitions/protobufAny"
+          }
+        }
+      }
+    },
+    "protobufAny": {
+      "type": "object",
+      "properties": {
+        "@type": {
+          "type": "string",
+          "description": "A URL/resource name that uniquely identifies the type of the serialized\nprotocol buffer message. This string must contain at least\none \"/\" character. The last segment of the URL's path must represent\nthe fully qualified name of the type (as in\n`path/google.protobuf.Duration`). The name should be in a canonical form\n(e.g., leading \".\" is not accepted).\n\nIn practice, teams usually precompile into the binary all types that they\nexpect it to use in the context of Any. However, for URLs which use the\nscheme `http`, `https`, or no scheme, one can optionally set up a type\nserver that maps type URLs to message definitions as follows:\n\n* If no scheme is provided, `https` is assumed.\n* An HTTP GET on the URL must yield a [google.protobuf.Type][]\n  value in binary format, or produce an error.\n* Applications are allowed to cache lookup results based on the\n  URL, or have them precompiled into a binary to avoid any\n  lookup. Therefore, binary compatibility needs to be preserved\n  on changes to types. (Use versioned type names to manage\n  breaking changes.)\n\nNote: this functionality is not currently available in the official\nprotobuf release, and it is not used for type URLs beginning with\ntype.googleapis.com.\n\nSchemes other than `http`, `https` (or the empty scheme) might be\nused with implementation specific semantics."
+        }
+      },
+      "additionalProperties": {},
+      "description": "`Any` contains an arbitrary serialized protocol buffer message along with a\nURL that describes the type of the serialized message.\n\nProtobuf library provides support to pack/unpack Any values in the form\nof utility functions or additional generated methods of the Any type.\n\nExample 1: Pack and unpack a message in C++.\n\n    Foo foo = ...;\n    Any any;\n    any.PackFrom(foo);\n    ...\n    if (any.UnpackTo(\u0026foo)) {\n      ...\n    }\n\nExample 2: Pack and unpack a message in Java.\n\n    Foo foo = ...;\n    Any any = Any.pack(foo);\n    ...\n    if (any.is(Foo.class)) {\n      foo = any.unpack(Foo.class);\n    }\n    // or ...\n    if (any.isSameTypeAs(Foo.getDefaultInstance())) {\n      foo = any.unpack(Foo.getDefaultInstance());\n    }\n\nExample 3: Pack and unpack a message in Python.\n\n    foo = Foo(...)\n    any = Any()\n    any.Pack(foo)\n    ...\n    if any.Is(Foo.DESCRIPTOR):\n      any.Unpack(foo)\n      ...\n\nExample 4: Pack and unpack a message in Go\n\n     foo := \u0026pb.Foo{...}\n     any, err := anypb.New(foo)\n     if err != nil {\n       ...\n     }\n     ...\n     foo := \u0026pb.Foo{}\n     if err := any.UnmarshalTo(foo); err != nil {\n       ...\n     }\n\nThe pack methods provided by protobuf library will by default use\n'type.googleapis.com/full.type.name' as the type URL and the unpack\nmethods only use the fully qualified type name after the last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\nname \"y.z\".\n\nJSON\n\nThe JSON representation of an `Any` value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field `@type` which contains the type URL. Example:\n\n    package google.profile;\n    message Person {\n      string first_name = 1;\n      string last_name = 2;\n    }\n\n    {\n      \"@type\": \"type.googleapis.com/google.profile.Person\",\n      \"firstName\": \u003cstring\u003e,\n      \"lastName\": \u003cstring\u003e\n    }\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\n`value` which holds the custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\n    {\n      \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n      \"value\": \"1.212s\"\n    }"
+    }
+  }
+}
diff --git a/api/proto/buf.lock b/api/proto/buf.lock
new file mode 100644
index 0000000000000000000000000000000000000000..695e713fa461478c6fb1f532dae04280abc8b79b
--- /dev/null
+++ b/api/proto/buf.lock
@@ -0,0 +1,18 @@
+# Generated by buf. DO NOT EDIT.
+version: v1
+deps:
+  - remote: buf.build
+    owner: bufbuild
+    repository: protovalidate
+    commit: 0de7443d03cf41228f8a9790b12b417e
+    digest: shake256:3c0676a73cef06439c107cb9560627354815adbc254976f807d645de7e2c1bf19d0438d5d56d5bc92465377e0d9315951e986fc6ced2871e450534b2b8c953b0
+  - remote: buf.build
+    owner: googleapis
+    repository: googleapis
+    commit: 28151c0d0a1641bf938a7672c500e01d
+    digest: shake256:49215edf8ef57f7863004539deff8834cfb2195113f0b890dd1f67815d9353e28e668019165b9d872395871eeafcbab3ccfdb2b5f11734d3cca95be9e8d139de
+  - remote: buf.build
+    owner: grpc-ecosystem
+    repository: grpc-gateway
+    commit: 048ae6ff94ca4476b3225904b1078fad
+    digest: shake256:e5250bf2d999516c02206d757502b902e406f35c099d0e869dc3e4f923f6870fe0805a9974c27df0695462937eae90cd4d9db90bb9a03489412560baa74a87b6
diff --git a/api/proto/buf.yaml b/api/proto/buf.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..03893b716e1026c8c8dccb35e97dc0fc8329ec02
--- /dev/null
+++ b/api/proto/buf.yaml
@@ -0,0 +1,11 @@
+version: v1
+deps:
+  - buf.build/googleapis/googleapis
+  - buf.build/grpc-ecosystem/grpc-gateway
+  - buf.build/bufbuild/protovalidate
+lint:
+  use:
+    - DEFAULT
+breaking:
+  use:
+    - FILE
diff --git a/api/proto/etsi/etsi14.proto b/api/proto/etsi/etsi14.proto
new file mode 100644
index 0000000000000000000000000000000000000000..1d7363c69e0303788747199db24b96065b959827
--- /dev/null
+++ b/api/proto/etsi/etsi14.proto
@@ -0,0 +1,174 @@
+syntax = "proto3";
+
+package danet.etsi;
+
+import "google/api/annotations.proto";
+import "google/protobuf/descriptor.proto";
+import "protoc-gen-openapiv2/options/annotations.proto";
+import "google/protobuf/any.proto";
+
+option go_package = "code.fbi.h-da.de/danet/etsi/api/go/etsi14";
+
+option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
+	info: {
+		title: "danet ETSI14 Interface";
+    description: "Protocol Buffer specifications and Go code for the ETSI14 KMS Interface"
+		version: "0.1";
+		contact: {
+			name: "danet research group";
+            //TODO: ADD PROPER URL AND MAIL
+			//url: "https://code.fbi.h-da.de/danet/api";
+			//email: "none@example.com";
+		};
+		license: {
+			name: "BSD 3-Clause License";
+			// TODO: ADD PROPER LICENSE LINK
+            //url: "https://code.fbi.h-da.de/danet/api/-/blob/master/LICENSE";
+		};
+  };
+};
+
+service ETSI14Service {
+    // Returns Status from a KME to the calling SAE. 
+    // Status contains information on keys available to be requested 
+    // by a master SAE for a specified slave SAE.
+    rpc GetStatus(GetStatusRequest) returns (GetStatusResponse) {
+        option (google.api.http) = {
+            get: "/{KME_hostname}/api/v1/keys/{slave_SAE_ID}/status"
+        };
+    }
+
+    // Returns Key container data from the KME to the calling master SAE. 
+    // Key container data contains one or more keys. The calling master SAE may supply 
+    // Key request data to specify the requirement on Key container data. The slave SAE 
+    // specified by the slave_SAE_ID parameter may subsequently request matching keys 
+    // from a remote KME using key_ID identifiers from the returned Key container.
+    rpc GetKey(GetKeyRequest) returns (GetKeyResponse) {
+        option (google.api.http) = {
+            post: "/{KME_hostname}/api/v1/keys/{slave_SAE_ID}/enc_keys"
+            body: "*"
+        };
+    }
+
+    // Returns Key container from the KME to the calling slave SAE. 
+    // Key container contains keys matching those previously delivered to a remote master SAE
+    // based on the Key IDs supplied from the remote master SAE in response to its call to Get key.
+    // The KME shall reject the request with a 401 HTTP status code if the SAE ID of the requestor 
+    // was not an SAE ID supplied to the "Get key" method each time it was called resulting in the
+    // return of any of the Key IDs being requested.
+    rpc GetKeyWithIDs(GetKeyWithIDsRequest) returns (GetKeyWithIDsResponse) {
+        option (google.api.http) = {
+            post: "/{KME_hostname}/api/v1/keys/{master_SAE_ID}/dec_keys"
+            body: "*"
+        };
+    }
+}
+
+message GetStatusRequest {
+    string KME_hostname = 1;
+    string slave_SAE_ID = 2;
+}
+
+message GetStatusResponse {
+    int64 timestamp = 1;
+    Status status = 2;
+    Error error = 3;
+}
+
+message GetKeyRequest {
+    string KME_hostname = 1;
+    string slave_SAE_ID = 2;
+    KeyRequest key_request = 3;
+}
+
+message GetKeyResponse {
+    int64 timestamp = 1;
+    KeyContainer key_container = 2;
+    Error error = 3;
+}
+
+message GetKeyWithIDsRequest {
+    string KME_hostname = 1;
+    string master_SAE_ID = 2;
+    string slave_SAE_ID = 3;
+    repeated KeyID key_ID = 4;
+}
+
+message GetKeyWithIDsResponse {
+    int64 timestamp = 1;
+    KeyContainer key_container = 2;
+    Error error = 3;
+}
+
+// FIGURE THIS ERROR STUFF OUT
+message Error {
+   // ErrorCode code = 1;
+    string message = 1;
+    map<string, ListOfDetails> details = 2; // maybe map<string, string> is enough here?!
+    // or also google.protobuf.Any?
+}   
+
+// Maybe not neccessary
+message ListOfDetails {
+    repeated string details = 1;
+}
+
+// // FIGURE THIS ERROR STUFF OUT
+// enum ErrorCode {
+
+// }
+
+message Status {
+    string source_KME_ID = 1;
+    string target_KME_ID = 2;
+    string master_SAE_ID = 3;
+    string slave_SAE_ID = 4;
+    int64 key_size = 5;
+    int64 stored_key_count = 6;
+    int64 max_key_count = 7; 
+    int64 max_key_per_request = 8;
+    int64 max_key_size = 9;
+    int64 min_key_size = 10;
+    int64 max_SAE_ID_count = 11;
+}
+
+message KeyRequest {
+    int64 number = 1;
+    int64 size = 2;
+    repeated string additional_slave_SAE_IDs = 3;
+    repeated ExtensionMandatory extension_mandatory = 4;
+    repeated ExtensionOptional extension_optional = 5;
+}
+
+// Array of extension parameters specified as name/value pairs that KME shall 
+// handle or return an error. Parameter values may be of any type, including objects.
+message ExtensionMandatory {
+    map<string, google.protobuf.Any> extension_mandatory = 1;
+}
+
+// Array of extension parameters specified as name/value pairs that KME may ignore. 
+// Parameter values may be of any type, including objects.
+message ExtensionOptional {
+    map<string, google.protobuf.Any> extension_optional = 1;
+}
+
+message KeyID {
+    string key_ID = 1;
+    google.protobuf.Any key_ID_extension = 2;
+}
+
+message KeyContainer {
+    repeated Key key = 1;
+    google.protobuf.Any key_container_extension = 2;
+}
+
+message KeyIDExtension {
+    // optional for future use.
+}
+
+message Key {
+    string key_ID = 1;
+    KeyIDExtension key_ID_extension = 2;
+    string key = 3;
+    google.protobuf.Any key_extension = 4;
+}
\ No newline at end of file
diff --git a/go.mod b/go.mod
index 77435de56bc0caf1565a8818c6cd2b0faf6b415b..2fb787c9a338fec9db4225df1a67f635c85417d2 100644
--- a/go.mod
+++ b/go.mod
@@ -6,6 +6,7 @@ require (
 	code.fbi.h-da.de/danet/gnmi-target v0.0.0-20231020162329-e5679b7b675b
 	code.fbi.h-da.de/danet/proto-kms v0.0.0-20231010134253-1aaf11da29b9
 	github.com/google/uuid v1.3.1
+	github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0
 	github.com/openconfig/gnmi v0.10.0
 	github.com/openconfig/goyang v1.4.2
 	github.com/openconfig/ygot v0.29.12
@@ -15,6 +16,9 @@ require (
 	github.com/spf13/cobra v1.7.0
 	github.com/spf13/viper v1.17.0
 	golang.org/x/sys v0.13.0
+	google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb
+	google.golang.org/grpc v1.58.2
+	google.golang.org/protobuf v1.31.0
 	gopkg.in/yaml.v3 v3.0.1
 )
 
@@ -45,8 +49,7 @@ require (
 	golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
 	golang.org/x/net v0.15.0 // indirect
 	golang.org/x/text v0.13.0 // indirect
+	google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb // indirect
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect
-	google.golang.org/grpc v1.58.2 // indirect
-	google.golang.org/protobuf v1.31.0 // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
 )
diff --git a/go.sum b/go.sum
index a99f0628d1dd709a00c80803f2ccfe5a5579e6a7..f8010d26e18a945ed0919b2a7f2c99c911374a66 100644
--- a/go.sum
+++ b/go.sum
@@ -136,6 +136,8 @@ github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
 github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
 github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
 github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 h1:RtRsiaGvWxcwd8y3BiRZxsylPT8hLWZ5SPcfI+3IDNk=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0/go.mod h1:TzP6duP4Py2pHLVPPQp42aoYI92+PCrVotyR5e8Vqlk=
 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
@@ -502,6 +504,10 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D
 google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb h1:XFBgcDwm7irdHTbz4Zk2h7Mh+eis4nfJEFQFYzJzuIA=
+google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4=
+google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb h1:lK0oleSc7IQsUxO3U5TjL9DWlsxpEBemh+zpB7IqhWI=
+google.golang.org/genproto/googleapis/api v0.0.0-20230913181813-007df8e322eb/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 h1:N3bU/SQDCDyD6R528GJ/PwW9KjYcJA3dgyH+MovAkIM=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
@@ -538,8 +544,8 @@ google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs
 google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
 gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=