diff --git a/api/client_test.go b/api/client_test.go index 01dff2c7cc9aec25427124273f00a2f7f7706e4a..56179e68a0da3c946754eb1684bc1c61f0929d86 100644 --- a/api/client_test.go +++ b/api/client_test.go @@ -11,6 +11,7 @@ import ( "gitlab.com/elixxir/client/cmixproto" "gitlab.com/elixxir/client/globals" "gitlab.com/elixxir/client/io" + "gitlab.com/elixxir/client/io/keyExchange" "gitlab.com/elixxir/client/keyStore" "gitlab.com/elixxir/client/parse" "gitlab.com/elixxir/client/storage" @@ -129,7 +130,7 @@ func TestNewClient(t *testing.T) { func TestParse(t *testing.T) { ms := parse.Message{} ms.Body = []byte{0, 1, 2} - ms.MessageType = int32(cmixproto.Type_NO_TYPE) + ms.MessageType = int32(keyExchange.Type_NO_TYPE) ms.Receiver = &id.ZeroUser ms.Sender = &id.ZeroUser diff --git a/api/mockserver.go b/api/mockserver.go index 5405c8277631c7bb60c26ca5f99ae16790e970e1..9d0de5000345256ffa40f738169e258657d000ea 100644 --- a/api/mockserver.go +++ b/api/mockserver.go @@ -11,8 +11,8 @@ import ( "encoding/json" "fmt" "github.com/pkg/errors" - "gitlab.com/elixxir/client/cmixproto" "gitlab.com/elixxir/client/globals" + "gitlab.com/elixxir/client/io/keyExchange" "gitlab.com/elixxir/client/parse" pb "gitlab.com/elixxir/comms/mixmessages" "gitlab.com/elixxir/crypto/cyclic" @@ -55,7 +55,7 @@ func (m APIMessage) GetPayload() []byte { } func (m APIMessage) GetMessageType() int32 { - return int32(cmixproto.Type_NO_TYPE) + return int32(keyExchange.Type_NO_TYPE) } func (m APIMessage) GetCryptoType() parse.CryptoType { diff --git a/bindings/client_test.go b/bindings/client_test.go index 90c0a01ba26efcb7431a1f894d2c3f95039c4a91..71975246acd7ebabb21c3c55538b3f338118a994 100644 --- a/bindings/client_test.go +++ b/bindings/client_test.go @@ -13,9 +13,9 @@ import ( "encoding/base64" "encoding/json" "fmt" - "gitlab.com/elixxir/client/cmixproto" "gitlab.com/elixxir/client/globals" "gitlab.com/elixxir/client/io" + "gitlab.com/elixxir/client/io/keyExchange" "gitlab.com/elixxir/client/parse" "gitlab.com/elixxir/client/storage" "gitlab.com/elixxir/client/user" @@ -493,7 +493,7 @@ func TestListen(t *testing.T) { } listener := MockListener(false) - client.Listen(id.ZeroUser[:], int32(cmixproto.Type_NO_TYPE), &listener) + client.Listen(id.ZeroUser[:], int32(keyExchange.Type_NO_TYPE), &listener) client.client.GetSwitchboard().Speak(&parse.Message{ TypedBody: parse.TypedBody{ MessageType: 0, @@ -542,7 +542,7 @@ func TestStopListening(t *testing.T) { } listener := MockListener(false) - handle, err := client.Listen(id.ZeroUser[:], int32(cmixproto.Type_NO_TYPE), &listener) + handle, err := client.Listen(id.ZeroUser[:], int32(keyExchange.Type_NO_TYPE), &listener) if err != nil { t.Fatal(err) } @@ -582,7 +582,7 @@ func TestSetLogOutput(t *testing.T) { func TestParse(t *testing.T) { ms := parse.Message{} ms.Body = []byte{0, 1, 2} - ms.MessageType = int32(cmixproto.Type_NO_TYPE) + ms.MessageType = int32(keyExchange.Type_NO_TYPE) ms.Receiver = &id.ZeroUser ms.Sender = &id.ZeroUser diff --git a/bots/bots.go b/bots/bots.go index 8c49607787b9644cb884a35c98039d27e0a2046a..2af12fcee1cb7c33833a1f126e30ef03a6a7d066 100644 --- a/bots/bots.go +++ b/bots/bots.go @@ -4,6 +4,7 @@ import ( "gitlab.com/elixxir/client/cmixproto" "gitlab.com/elixxir/client/globals" "gitlab.com/elixxir/client/io" + "gitlab.com/elixxir/client/io/keyExchange" "gitlab.com/elixxir/client/parse" "gitlab.com/elixxir/client/storage" "gitlab.com/elixxir/client/user" @@ -81,13 +82,13 @@ func InitBots(s user.Session, s2 storage.Session, m io.Communications, l := m.GetSwitchboard() - l.Register(&id.UDB, int32(cmixproto.Type_UDB_PUSH_KEY_RESPONSE), + l.Register(&id.UDB, int32(keyExchange.Type_UDB_PUSH_KEY_RESPONSE), &pushKeyResponseListener) - l.Register(&id.UDB, int32(cmixproto.Type_UDB_GET_KEY_RESPONSE), + l.Register(&id.UDB, int32(keyExchange.Type_UDB_GET_KEY_RESPONSE), &getKeyResponseListener) - l.Register(&id.UDB, int32(cmixproto.Type_UDB_REGISTER_RESPONSE), + l.Register(&id.UDB, int32(keyExchange.Type_UDB_REGISTER_RESPONSE), ®isterResponseListener) - l.Register(&id.UDB, int32(cmixproto.Type_UDB_SEARCH_RESPONSE), + l.Register(&id.UDB, int32(keyExchange.Type_UDB_SEARCH_RESPONSE), &searchResponseListener) l.Register(&id.ZeroUser, int32(cmixproto.Type_NICKNAME_REQUEST), &nicknameRequestListener) diff --git a/bots/userDiscovery.go b/bots/userDiscovery.go index 74db2a4557429aa245052b9da78d39646c76ab71..bc474d92bf9594c67e1354160d5faa7d44975d26 100644 --- a/bots/userDiscovery.go +++ b/bots/userDiscovery.go @@ -13,8 +13,8 @@ import ( "encoding/base64" "fmt" "github.com/pkg/errors" - "gitlab.com/elixxir/client/cmixproto" "gitlab.com/elixxir/client/globals" + "gitlab.com/elixxir/client/io/keyExchange" "gitlab.com/elixxir/client/parse" "gitlab.com/elixxir/client/storage" "gitlab.com/elixxir/crypto/hash" @@ -77,7 +77,7 @@ func Register(valueType, value string, publicKey []byte, regStatus func(int), ti //send the user information to udb msgBody := parse.Pack(&parse.TypedBody{ - MessageType: int32(cmixproto.Type_UDB_REGISTER), + MessageType: int32(keyExchange.Type_UDB_REGISTER), Body: []byte(fmt.Sprintf("%s %s %s", valueType, value, keyFP)), }) @@ -131,7 +131,7 @@ func Search(valueType, value string, searchStatus func(int), timeout time.Durati searchStatus(globals.UDB_SEARCH_LOOK) msgBody := parse.Pack(&parse.TypedBody{ - MessageType: int32(cmixproto.Type_UDB_SEARCH), + MessageType: int32(keyExchange.Type_UDB_SEARCH), Body: []byte(fmt.Sprintf("%s %s", valueType, value)), }) err = sendCommand(&id.UDB, msgBody) @@ -170,7 +170,7 @@ func Search(valueType, value string, searchStatus func(int), timeout time.Durati // Get the full key and decode it msgBody = parse.Pack(&parse.TypedBody{ - MessageType: int32(cmixproto.Type_UDB_GET_KEY), + MessageType: int32(keyExchange.Type_UDB_GET_KEY), Body: []byte(keyFP), }) err = sendCommand(&id.UDB, msgBody) @@ -270,7 +270,7 @@ func pushKey(keyFP string, publicKey []byte) error { pushKeyMsg := fmt.Sprintf("%s %s", keyFP, publicKeyString) return sendCommand(&id.UDB, parse.Pack(&parse.TypedBody{ - MessageType: int32(cmixproto.Type_UDB_PUSH_KEY), + MessageType: int32(keyExchange.Type_UDB_PUSH_KEY), Body: []byte(pushKeyMsg), })) } diff --git a/cmd/root.go b/cmd/root.go index 8f6413d7fc4c26baf7336c95e789e0418bc8a1b7..928473fbe031959be6e6da372a6b98c3e0921c89 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -19,6 +19,7 @@ import ( "gitlab.com/elixxir/client/cmixproto" "gitlab.com/elixxir/client/globals" "gitlab.com/elixxir/client/io" + "gitlab.com/elixxir/client/io/keyExchange" "gitlab.com/elixxir/client/parse" "gitlab.com/elixxir/client/user" "gitlab.com/elixxir/client/userRegistry" @@ -414,11 +415,11 @@ var rootCmd = &cobra.Command{ // the integration test // Normal text messages text := TextListener{} - client.Listen(&id.ZeroUser, int32(cmixproto.Type_TEXT_MESSAGE), + client.Listen(&id.ZeroUser, int32(keyExchange.Type_TEXT_MESSAGE), &text) // All other messages fallback := FallbackListener{} - client.Listen(&id.ZeroUser, int32(cmixproto.Type_NO_TYPE), + client.Listen(&id.ZeroUser, int32(keyExchange.Type_NO_TYPE), &fallback) // Log the user in, for now using the first gateway specified @@ -513,7 +514,7 @@ var rootCmd = &cobra.Command{ err := client.Send(&parse.Message{ Sender: userID, TypedBody: parse.TypedBody{ - MessageType: int32(cmixproto.Type_TEXT_MESSAGE), + MessageType: int32(keyExchange.Type_TEXT_MESSAGE), Body: wireOut, }, InferredType: cryptoType, diff --git a/cmixproto/generate.sh b/cmixproto/generate.sh deleted file mode 100755 index 06e52ef847ad9a007c2ed67b9a3c9fce9cc68230..0000000000000000000000000000000000000000 --- a/cmixproto/generate.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -protoc --go_out=. types.proto diff --git a/cmixproto/types.pb.go b/cmixproto/types.pb.go deleted file mode 100644 index bfd3993996f5e065b82d61c18b785904f598ebdb..0000000000000000000000000000000000000000 --- a/cmixproto/types.pb.go +++ /dev/null @@ -1,304 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Copyright © 2018 Privategrity Corporation / -// / -// All rights reserved. / -//////////////////////////////////////////////////////////////////////////////// - -// Call ./generate.sh to generate the protocol buffer code - -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.25.0 -// protoc (unknown) -// source: types.proto - -package cmixproto - -import ( - proto "github.com/golang/protobuf/proto" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -// This is a compile-time assertion that a sufficiently up-to-date version -// of the legacy proto package is being used. -const _ = proto.ProtoPackageIsVersion4 - -type Type int32 - -const ( - // Used as a wildcard for listeners to listen to all existing types. - // Think of it as "No type in particular" - Type_NO_TYPE Type = 0 - // See proto buf documentation below - Type_TEXT_MESSAGE Type = 1 - // Second field is the key data itself. This should be 2048 bits long - // (according to the message length that our prime allows) and is - // base64-encoded. - Type_UDB_PUSH_KEY Type = 10 - // The push key response message is a string. If the key push was a - // success, the UDB should respond with a message that starts with "PUSHKEY - // COMPLETE", followed by the fingerprint of the key that was pushed. - // If the response doesn't begin with "PUSHKEY COMPLETE", the message is - // an error message and should be shown to the user. - Type_UDB_PUSH_KEY_RESPONSE Type = 11 - // The get key message includes a single string field with the key - // fingerprint of the key that needs gettin'. This is the same fingerprint - // you would have pushed. - Type_UDB_GET_KEY Type = 12 - // The get key response message is a string. The first space-separated - // field should always be "GETKEY". The second field is the fingerprint of - // the key. The third field is "NOTFOUND" if the UDB didn't find the key, - // or the key itself, encoded in base64, otherwise. - Type_UDB_GET_KEY_RESPONSE Type = 13 - // To wit: The first argument in the list of space-separated fields is - // the type of the registration. Currently the only allowed type is - // "EMAIL". The second argument is the value of the type you're registering - // with. In all currently acceptable registration types, this would be an - // email address. If you could register with your phone, it would be your - // phone number, and so on. Then, the key fingerprint of the user's key is - // the third argument. To register successfully, you must have already - // pushed the key with that fingerprint. - Type_UDB_REGISTER Type = 14 - // The registration response is just a string. It will be either an error - // message to show to the user, or the message "REGISTRATION COMPLETE" if - // registration was successful. - Type_UDB_REGISTER_RESPONSE Type = 15 - // The search message is just another space separated list. The first field - // will contain the type of registered user you're searching for, namely - // "EMAIL". The second field with contain the value of that type that - // you're searching for. - Type_UDB_SEARCH Type = 16 - // The search response is a list of fields. The first is always "SEARCH". - // The second is always the value that the user searched for. The third is - // "FOUND" or "NOTFOUND" depending on whether the UDB found the user. If - // the user was FOUND, the last field will contain their key fingerprint, - // which you can use with GET_KEY to get the keys you need to talk with - // that user. Otherwise, this fourth field won't exist. - Type_UDB_SEARCH_RESPONSE Type = 17 - // End to End Rekey message types - // Trigger a rekey, this message is used locally in client only - Type_REKEY_TRIGGER Type = 30 - // Rekey confirmation message. Sent by partner to confirm completion of a rekey - Type_REKEY_CONFIRM Type = 31 -) - -// Enum value maps for Type. -var ( - Type_name = map[int32]string{ - 0: "NO_TYPE", - 1: "TEXT_MESSAGE", - 10: "UDB_PUSH_KEY", - 11: "UDB_PUSH_KEY_RESPONSE", - 12: "UDB_GET_KEY", - 13: "UDB_GET_KEY_RESPONSE", - 14: "UDB_REGISTER", - 15: "UDB_REGISTER_RESPONSE", - 16: "UDB_SEARCH", - 17: "UDB_SEARCH_RESPONSE", - 30: "REKEY_TRIGGER", - 31: "REKEY_CONFIRM", - } - Type_value = map[string]int32{ - "NO_TYPE": 0, - "TEXT_MESSAGE": 1, - "UDB_PUSH_KEY": 10, - "UDB_PUSH_KEY_RESPONSE": 11, - "UDB_GET_KEY": 12, - "UDB_GET_KEY_RESPONSE": 13, - "UDB_REGISTER": 14, - "UDB_REGISTER_RESPONSE": 15, - "UDB_SEARCH": 16, - "UDB_SEARCH_RESPONSE": 17, - "REKEY_TRIGGER": 30, - "REKEY_CONFIRM": 31, - } -) - -func (x Type) Enum() *Type { - p := new(Type) - *p = x - return p -} - -func (x Type) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (Type) Descriptor() protoreflect.EnumDescriptor { - return file_types_proto_enumTypes[0].Descriptor() -} - -func (Type) Type() protoreflect.EnumType { - return &file_types_proto_enumTypes[0] -} - -func (x Type) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use Type.Descriptor instead. -func (Type) EnumDescriptor() ([]byte, []int) { - return file_types_proto_rawDescGZIP(), []int{0} -} - -type RekeyTrigger struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // PublicKey used in the registration - PublicKey []byte `protobuf:"bytes,1,opt,name=publicKey,proto3" json:"publicKey,omitempty"` - // ID of the session used to create this session - SessionID []byte `protobuf:"bytes,2,opt,name=SessionID,json=sessionID,proto3" json:"SessionID,omitempty"` -} - -func (x *RekeyTrigger) Reset() { - *x = RekeyTrigger{} - if protoimpl.UnsafeEnabled { - mi := &file_types_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RekeyTrigger) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RekeyTrigger) ProtoMessage() {} - -func (x *RekeyTrigger) ProtoReflect() protoreflect.Message { - mi := &file_types_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 RekeyTrigger.ProtoReflect.Descriptor instead. -func (*RekeyTrigger) Descriptor() ([]byte, []int) { - return file_types_proto_rawDescGZIP(), []int{0} -} - -func (x *RekeyTrigger) GetPublicKey() []byte { - if x != nil { - return x.PublicKey - } - return nil -} - -func (x *RekeyTrigger) GetSessionID() []byte { - if x != nil { - return x.SessionID - } - return nil -} - -var File_types_proto protoreflect.FileDescriptor - -var file_types_proto_rawDesc = []byte{ - 0x0a, 0x0b, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x70, - 0x61, 0x72, 0x73, 0x65, 0x22, 0x4a, 0x0a, 0x0c, 0x52, 0x65, 0x6b, 0x65, 0x79, 0x54, 0x72, 0x69, - 0x67, 0x67, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, - 0x65, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x44, - 0x2a, 0xf9, 0x01, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x4e, 0x4f, 0x5f, - 0x54, 0x59, 0x50, 0x45, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x54, 0x45, 0x58, 0x54, 0x5f, 0x4d, - 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x55, 0x44, 0x42, 0x5f, - 0x50, 0x55, 0x53, 0x48, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x0a, 0x12, 0x19, 0x0a, 0x15, 0x55, 0x44, - 0x42, 0x5f, 0x50, 0x55, 0x53, 0x48, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x52, 0x45, 0x53, 0x50, 0x4f, - 0x4e, 0x53, 0x45, 0x10, 0x0b, 0x12, 0x0f, 0x0a, 0x0b, 0x55, 0x44, 0x42, 0x5f, 0x47, 0x45, 0x54, - 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x0c, 0x12, 0x18, 0x0a, 0x14, 0x55, 0x44, 0x42, 0x5f, 0x47, 0x45, - 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45, 0x10, 0x0d, - 0x12, 0x10, 0x0a, 0x0c, 0x55, 0x44, 0x42, 0x5f, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, - 0x10, 0x0e, 0x12, 0x19, 0x0a, 0x15, 0x55, 0x44, 0x42, 0x5f, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, - 0x45, 0x52, 0x5f, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45, 0x10, 0x0f, 0x12, 0x0e, 0x0a, - 0x0a, 0x55, 0x44, 0x42, 0x5f, 0x53, 0x45, 0x41, 0x52, 0x43, 0x48, 0x10, 0x10, 0x12, 0x17, 0x0a, - 0x13, 0x55, 0x44, 0x42, 0x5f, 0x53, 0x45, 0x41, 0x52, 0x43, 0x48, 0x5f, 0x52, 0x45, 0x53, 0x50, - 0x4f, 0x4e, 0x53, 0x45, 0x10, 0x11, 0x12, 0x11, 0x0a, 0x0d, 0x52, 0x45, 0x4b, 0x45, 0x59, 0x5f, - 0x54, 0x52, 0x49, 0x47, 0x47, 0x45, 0x52, 0x10, 0x1e, 0x12, 0x11, 0x0a, 0x0d, 0x52, 0x45, 0x4b, - 0x45, 0x59, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x52, 0x4d, 0x10, 0x1f, 0x42, 0x0b, 0x5a, 0x09, - 0x63, 0x6d, 0x69, 0x78, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, -} - -var ( - file_types_proto_rawDescOnce sync.Once - file_types_proto_rawDescData = file_types_proto_rawDesc -) - -func file_types_proto_rawDescGZIP() []byte { - file_types_proto_rawDescOnce.Do(func() { - file_types_proto_rawDescData = protoimpl.X.CompressGZIP(file_types_proto_rawDescData) - }) - return file_types_proto_rawDescData -} - -var file_types_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_types_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_types_proto_goTypes = []interface{}{ - (Type)(0), // 0: parse.Type - (*RekeyTrigger)(nil), // 1: parse.RekeyTrigger -} -var file_types_proto_depIdxs = []int32{ - 0, // [0:0] is the sub-list for method output_type - 0, // [0:0] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name -} - -func init() { file_types_proto_init() } -func file_types_proto_init() { - if File_types_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_types_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RekeyTrigger); 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_types_proto_rawDesc, - NumEnums: 1, - NumMessages: 1, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_types_proto_goTypes, - DependencyIndexes: file_types_proto_depIdxs, - EnumInfos: file_types_proto_enumTypes, - MessageInfos: file_types_proto_msgTypes, - }.Build() - File_types_proto = out.File - file_types_proto_rawDesc = nil - file_types_proto_goTypes = nil - file_types_proto_depIdxs = nil -} diff --git a/cmixproto/types.proto b/cmixproto/types.proto deleted file mode 100644 index 57b5796e13641843d454bd8179882535bc87f0db..0000000000000000000000000000000000000000 --- a/cmixproto/types.proto +++ /dev/null @@ -1,114 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Copyright © 2018 Privategrity Corporation / -// / -// All rights reserved. / -//////////////////////////////////////////////////////////////////////////////// - -// Call ./generate.sh to generate the protocol buffer code - -syntax = "proto3"; - -package parse; -option go_package = "cmixproto"; - -enum Type { - // Used as a wildcard for listeners to listen to all existing types. - // Think of it as "No type in particular" - NO_TYPE = 0; - - - // See proto buf documentation below - TEXT_MESSAGE = 1; - - // None of the UDB message types are proto bufs because I haven't had time - // to migrate UDB fully to the new systems yet. - - // I was considering migrating these types to proto bufs to make them more - // compact for transmission, but you would have to compress them to even - // have a chance of fitting the whole key in one Cmix message. In any case, - // I don't think the benefit is there for the time investment. - - // The prefixes of the UDB response messages are made redundant by the - // message types in this very enumeration, so at some point we can remove - // them from the UDB code that generates the responses. - - - // The push key message includes two string fields, separated by a space. - - // First field is the key fingerprint, which the UDB uses as an key into - // the map of, uhh, the keys. This can be any string that doesn't have a - // space in it. - - // Second field is the key data itself. This should be 2048 bits long - // (according to the message length that our prime allows) and is - // base64-encoded. - UDB_PUSH_KEY = 10; - // The push key response message is a string. If the key push was a - // success, the UDB should respond with a message that starts with "PUSHKEY - // COMPLETE", followed by the fingerprint of the key that was pushed. - // If the response doesn't begin with "PUSHKEY COMPLETE", the message is - // an error message and should be shown to the user. - UDB_PUSH_KEY_RESPONSE = 11; - // The get key message includes a single string field with the key - // fingerprint of the key that needs gettin'. This is the same fingerprint - // you would have pushed. - UDB_GET_KEY = 12; - // The get key response message is a string. The first space-separated - // field should always be "GETKEY". The second field is the fingerprint of - // the key. The third field is "NOTFOUND" if the UDB didn't find the key, - // or the key itself, encoded in base64, otherwise. - UDB_GET_KEY_RESPONSE = 13; - // The register message is unchanged from the OG UDB code, except that - // the REGISTER command in front has been replaced with the type string - // corresponding to this entry in the enumeration. - - // To wit: The first argument in the list of space-separated fields is - // the type of the registration. Currently the only allowed type is - // "EMAIL". The second argument is the value of the type you're registering - // with. In all currently acceptable registration types, this would be an - // email address. If you could register with your phone, it would be your - // phone number, and so on. Then, the key fingerprint of the user's key is - // the third argument. To register successfully, you must have already - // pushed the key with that fingerprint. - UDB_REGISTER = 14; - // The registration response is just a string. It will be either an error - // message to show to the user, or the message "REGISTRATION COMPLETE" if - // registration was successful. - UDB_REGISTER_RESPONSE = 15; - // The search message is just another space separated list. The first field - // will contain the type of registered user you're searching for, namely - // "EMAIL". The second field with contain the value of that type that - // you're searching for. - UDB_SEARCH = 16; - // The search response is a list of fields. The first is always "SEARCH". - // The second is always the value that the user searched for. The third is - // "FOUND" or "NOTFOUND" depending on whether the UDB found the user. If - // the user was FOUND, the last field will contain their key fingerprint, - // which you can use with GET_KEY to get the keys you need to talk with - // that user. Otherwise, this fourth field won't exist. - UDB_SEARCH_RESPONSE = 17; - - - // The client sends payment transaction messages to the payment bot to - // fund compound coins with seed coins. In the current implementation, - // there's one compound that gets funded that's from the payee. This comes - // across in a PAYMENT_INVOICE. And there's a second compound that contains - // the change from the seeds that the payer is using to fund the invoice. - // The rest are the seeds that are the source of the payment. - - // All of the seeds and compounds are in an ordered list, and they get - // categorized and processed on the payment bot. - - // End to End Rekey message types - // Trigger a rekey, this message is used locally in client only - REKEY_TRIGGER = 30; - // Rekey confirmation message. Sent by partner to confirm completion of a rekey - REKEY_CONFIRM = 31; -} - -message RekeyTrigger { - // PublicKey used in the registration - bytes publicKey = 1; - // ID of the session used to create this session - bytes SessionID = 2; -} diff --git a/context/context.go b/context/context.go index 0d50cdcd2c966f8743285d68f43232db2bd9ad93..c9de4b8781eed4f47c21c994bbae81027fe0f977 100644 --- a/context/context.go +++ b/context/context.go @@ -1,8 +1,8 @@ package context import ( + "gitlab.com/elixxir/client/context/switchboard" "gitlab.com/elixxir/client/storage" - "gitlab.com/elixxir/primitives/switchboard" ) type Context struct { diff --git a/context/message/encryptionType.go b/context/message/encryptionType.go new file mode 100644 index 0000000000000000000000000000000000000000..5afdd855a60f68dbf89a2e331edc05c3c7907384 --- /dev/null +++ b/context/message/encryptionType.go @@ -0,0 +1,8 @@ +package message + +type EncryptionType uint8 + +const ( + None EncryptionType = 0 + E2E EncryptionType = 1 +) diff --git a/context/message/receiveMessage.go b/context/message/receiveMessage.go new file mode 100644 index 0000000000000000000000000000000000000000..71fbca259e9e7d341fce76bce8968f79a5c03d6f --- /dev/null +++ b/context/message/receiveMessage.go @@ -0,0 +1,15 @@ +package message + +import ( + "gitlab.com/xx_network/primitives/id" + "time" +) + +type Receive struct { + Recipient *id.ID + Payload []byte + MessageType Type + Sender *id.ID + Timestamp time.Time + Encryption EncryptionType +} diff --git a/context/message/message.go b/context/message/sendMessage.go similarity index 71% rename from context/message/message.go rename to context/message/sendMessage.go index 604c5c162664dcfd6c6c93e865aa886d3faf9074..1e639669f607ba0b9dbc26918bca3e6b0617d5b9 100644 --- a/context/message/message.go +++ b/context/message/sendMessage.go @@ -2,8 +2,8 @@ package message import "gitlab.com/xx_network/primitives/id" -type Message struct { +type Send struct { Recipient *id.ID Payload []byte - MessageType int32 + MessageType Type } diff --git a/context/message/type.go b/context/message/type.go new file mode 100644 index 0000000000000000000000000000000000000000..a36645e3182f3dd1ee3bae408cc5233d95943441 --- /dev/null +++ b/context/message/type.go @@ -0,0 +1,97 @@ +package message + +const TypeLen = 4 + +type Type uint32 + +const ( + // Used as a wildcard for listeners to listen to all existing types. + // Think of it as "No type in particular" + NoType Type = 0 + + //General text message, contains human readable text + Text Type = 1 + + // None of the UDB message types are proto bufs because I haven't had time + // to migrate UDB fully to the new systems yet. + + // I was considering migrating these types to proto bufs to make them more + // compact for transmission, but you would have to compress them to even + // have a chance of fitting the whole key in one Cmix message. In any case, + // I don't think the benefit is there for the time investment. + + // The prefixes of the UDB response messages are made redundant by the + // message types in this very enumeration, so at some point we can remove + // them from the UDB code that generates the responses. + + // The push key message includes two string fields, separated by a space. + + // First field is the key fingerprint, which the UDB uses as an key into + // the map of, uhh, the keys. This can be any string that doesn't have a + // space in it. + + // Second field is the key data itself. This should be 2048 bits long + // (according to the message length that our prime allows) and is + // base64-encoded. + UdbPushKey = 10; + // The push key response message is a string. If the key push was a + // success, the UDB should respond with a message that starts with "PUSHKEY + // COMPLETE", followed by the fingerprint of the key that was pushed. + // If the response doesn't begin with "PUSHKEY COMPLETE", the message is + // an error message and should be shown to the user. + UdbPushKeyResponse = 11; + // The get key message includes a single string field with the key + // fingerprint of the key that needs gettin'. This is the same fingerprint + // you would have pushed. + UdbGetKey = 12; + // The get key response message is a string. The first space-separated + // field should always be "GETKEY". The second field is the fingerprint of + // the key. The third field is "NOTFOUND" if the UDB didn't find the key, + // or the key itself, encoded in base64, otherwise. + UdbGetKeyResponse = 13; + // The register message is unchanged from the OG UDB code, except that + // the REGISTER command in front has been replaced with the type string + // corresponding to this entry in the enumeration. + + // To wit: The first argument in the list of space-separated fields is + // the type of the registration. Currently the only allowed type is + // "EMAIL". The second argument is the value of the type you're registering + // with. In all currently acceptable registration types, this would be an + // email address. If you could register with your phone, it would be your + // phone number, and so on. Then, the key fingerprint of the user's key is + // the third argument. To register successfully, you must have already + // pushed the key with that fingerprint. + UdbRegister = 14; + // The registration response is just a string. It will be either an error + // message to show to the user, or the message "REGISTRATION COMPLETE" if + // registration was successful. + UdbRegisterResponse = 15; + // The search message is just another space separated list. The first field + // will contain the type of registered user you're searching for, namely + // "EMAIL". The second field with contain the value of that type that + // you're searching for. + UdbSearch = 16; + // The search response is a list of fields. The first is always "SEARCH". + // The second is always the value that the user searched for. The third is + // "FOUND" or "NOTFOUND" depending on whether the UDB found the user. If + // the user was FOUND, the last field will contain their key fingerprint, + // which you can use with GET_KEY to get the keys you need to talk with + // that user. Otherwise, this fourth field won't exist. + UdbSearchResponse = 17; + + // The client sends payment transaction messages to the payment bot to + // fund compound coins with seed coins. In the current implementation, + // there's one compound that gets funded that's from the payee. This comes + // across in a PAYMENT_INVOICE. And there's a second compound that contains + // the change from the seeds that the payer is using to fund the invoice. + // The rest are the seeds that are the source of the payment. + + // All of the seeds and compounds are in an ordered list, and they get + // categorized and processed on the payment bot. + + // End to End Rekey message types + // Trigger a rekey, this message is used locally in client only + KeyExchangeTrigger = 30; + // Rekey confirmation message. Sent by partner to confirm completion of a rekey + KeyExchangeConfirm = 31; +) diff --git a/context/networkManager.go b/context/networkManager.go index 2287265152da53b01f6af7c6323da8f418db5e14..b585b946e668e780b9b5ea4832d62f9c967ab439 100644 --- a/context/networkManager.go +++ b/context/networkManager.go @@ -10,8 +10,8 @@ import ( ) type NetworkManager interface { - SendE2E(m message.Message, e2eP params.E2E, cmixP params.CMIX) ([]id.Round, error) - SendUnsafe(m message.Message) ([]id.Round, error) + SendE2E(m message.Send, e2eP params.E2E, cmixP params.CMIX) ([]id.Round, error) + SendUnsafe(m message.Send) ([]id.Round, error) SendCMIX(message format.Message) (id.Round, error) GetInstance() *network.Instance Stoppable() stoppable.Stoppable diff --git a/context/switchboard/any.go b/context/switchboard/any.go new file mode 100644 index 0000000000000000000000000000000000000000..aef95e3780a5a917606cb0352a9bb44460977ea0 --- /dev/null +++ b/context/switchboard/any.go @@ -0,0 +1,14 @@ +package switchboard + +import ( + "gitlab.com/elixxir/client/context/message" + "gitlab.com/xx_network/primitives/id" +) + +// ID to respond to any message type +const AnyType = message.NoType + +//ID to respond to any user +func AnyUser() *id.ID { + return &id.ZeroUser +} diff --git a/context/switchboard/any_test.go b/context/switchboard/any_test.go new file mode 100644 index 0000000000000000000000000000000000000000..1f31bccd885ee97d19213812fb2b0cbfc1fd6a2a --- /dev/null +++ b/context/switchboard/any_test.go @@ -0,0 +1,14 @@ +package switchboard + +import ( + "gitlab.com/xx_network/primitives/id" + "testing" +) + +//tests that AnyUser returns the correct user +func TestAnyUser(t *testing.T) { + au := AnyUser() + if !au.Cmp(&id.ZeroUser) { + t.Errorf("Wrong user returned from AnyUser") + } +} diff --git a/context/switchboard/byID.go b/context/switchboard/byID.go new file mode 100644 index 0000000000000000000000000000000000000000..ad7197452e80db9220a589f5c4a880caf15532e4 --- /dev/null +++ b/context/switchboard/byID.go @@ -0,0 +1,63 @@ +package switchboard + +import ( + "github.com/golang-collections/collections/set" + "gitlab.com/xx_network/primitives/id" +) + +type byId struct { + list map[id.ID]*set.Set + generic *set.Set +} + +// builds a new byID structure +// registers an empty ID and the designated zero ID as generic +func newById() *byId { + bi := &byId{ + list: make(map[id.ID]*set.Set), + generic: set.New(), + } + + //make the zero IDs, which are defined as any, all point to the generic + bi.list[*AnyUser()] = bi.generic + bi.list[id.ID{}] = bi.generic + + return bi +} + +// returns a set associated with the passed ID unioned with the generic return +func (bi *byId) Get(uid *id.ID) *set.Set { + lookup, ok := bi.list[*uid] + if !ok { + return bi.generic + } else { + return lookup.Union(bi.generic) + } +} + +// adds a listener to a set for the given ID. Creates a new set to add it to if +// the set does not exist +func (bi *byId) Add(uid *id.ID, l Listener) *set.Set { + s, ok := bi.list[*uid] + if !ok { + s = set.New(l) + bi.list[*uid] = s + } else { + s.Insert(l) + } + + return s +} + +// Removes the passed listener from the set for UserID and +// deletes the set if it is empty if the ID is not a generic one +func (bi *byId) Remove(uid *id.ID, l Listener) { + s, ok := bi.list[*uid] + if ok { + s.Remove(l) + + if s.Len() == 0 && !uid.Cmp(AnyUser()) && !uid.Cmp(&id.ID{}) { + delete(bi.list, *uid) + } + } +} diff --git a/context/switchboard/byID_test.go b/context/switchboard/byID_test.go new file mode 100644 index 0000000000000000000000000000000000000000..683a68f0a8ab3a10d989ba3425d87f9451e6c94b --- /dev/null +++ b/context/switchboard/byID_test.go @@ -0,0 +1,308 @@ +package switchboard + +import ( + "github.com/golang-collections/collections/set" + "gitlab.com/xx_network/primitives/id" + "testing" +) + +// tests the newByID functions forms a properly constructed byId +func TestById_newById(t *testing.T) { + nbi := newById() + + if nbi.list == nil { + t.Errorf("No list created") + } + + if nbi.generic == nil { + t.Errorf("No generic created") + } + + if nbi.generic != nbi.list[id.ZeroUser] { + t.Errorf("zero user not registered as generic") + } + + if nbi.generic != nbi.list[id.ID{}] { + t.Errorf("zero id not registered as generic") + } +} + +// tests that when nothing has been added an empty set is returned +func TestById_Get_Empty(t *testing.T) { + nbi := newById() + + uid := id.NewIdFromUInt(42, id.User, t) + + s := nbi.Get(uid) + + if s.Len() != 0 { + t.Errorf("Should not have returned a set") + } +} + +//tests that getting a set for a specific ID returns that set +func TestById_Get_Selected(t *testing.T) { + nbi := newById() + + uid := id.NewIdFromUInt(42, id.User, t) + + set1 := set.New(0) + + nbi.list[*uid] = set1 + + s := nbi.Get(uid) + + if s.Len() == 0 { + t.Errorf("Should have returned a set") + } + + if !s.SubsetOf(set1) || !set1.SubsetOf(s) { + t.Errorf("Wrong set returned") + } +} + +// tests that when getting a specific ID which is not there returns the generic +// set if is present +func TestById_Get_Generic(t *testing.T) { + nbi := newById() + + uid := id.NewIdFromUInt(42, id.User, t) + + nbi.generic.Insert(0) + + s := nbi.Get(uid) + + if s.Len() == 0 { + t.Errorf("Should have returned a set") + } + + if !s.SubsetOf(nbi.generic) || !nbi.generic.SubsetOf(s) { + t.Errorf("Wrong set returned") + } +} + +// tests that when getting a specific ID is there and there are elements +// in the generic that the union of the two is returned +func TestById_Get_GenericSelected(t *testing.T) { + nbi := newById() + + uid := id.NewIdFromUInt(42, id.User, t) + + set1 := set.New(0) + + nbi.list[*uid] = set1 + + nbi.generic.Insert(1) + + s := nbi.Get(uid) + + if s.Len() == 0 { + t.Errorf("Should have returned a set") + } + + setUnion := set1.Union(nbi.generic) + + if !s.SubsetOf(setUnion) || !setUnion.SubsetOf(s) { + t.Errorf("Wrong set returned") + } +} + +// Tests that when adding to a set which does not exist, the set is created +func TestById_Add_New(t *testing.T) { + nbi := newById() + + uid := id.NewIdFromUInt(42, id.User, t) + + l := &funcListener{} + + nbi.Add(uid, l) + + s := nbi.list[*uid] + + if s.Len() != 1 { + t.Errorf("Should a set of the wrong size") + } + + if !s.Has(l) { + t.Errorf("Wrong set returned") + } +} + +// Tests that when adding to a set which does exist, the set is retained and +// added to +func TestById_Add_Old(t *testing.T) { + nbi := newById() + + uid := id.NewIdFromUInt(42, id.User, t) + + l1 := &funcListener{} + l2 := &funcListener{} + + set1 := set.New(l1) + + nbi.list[*uid] = set1 + + nbi.Add(uid, l2) + + s := nbi.list[*uid] + + if s.Len() != 2 { + t.Errorf("Should have returned a set") + } + + if !s.Has(l1) { + t.Errorf("Set does not include the initial listener") + } + + if !s.Has(l2) { + t.Errorf("Set does not include the new listener") + } +} + +// Tests that when adding to a generic ID, the listener is added to the +// generic set +func TestById_Add_Generic(t *testing.T) { + nbi := newById() + + l1 := &funcListener{} + l2 := &funcListener{} + + nbi.Add(&id.ID{}, l1) + nbi.Add(AnyUser(), l2) + + s := nbi.generic + + if s.Len() != 2 { + t.Errorf("Should have returned a set of size 2") + } + + if !s.Has(l1) { + t.Errorf("Set does not include the ZeroUser listener") + } + + if !s.Has(l2) { + t.Errorf("Set does not include the empty user listener") + } +} + +// Tests that removing a listener from a set with multiple listeners removes the +// listener but maintains the set +func TestById_Remove_ManyInSet(t *testing.T) { + nbi := newById() + + uid := id.NewIdFromUInt(42, id.User, t) + + l1 := &funcListener{} + l2 := &funcListener{} + + set1 := set.New(l1, l2) + + nbi.list[*uid] = set1 + + nbi.Remove(uid, l1) + + if _, ok := nbi.list[*uid]; !ok { + t.Errorf("Set removed when it should not have been") + } + + if set1.Len() != 1 { + t.Errorf("Set is incorrect length after the remove call: %v", + set1.Len()) + } + + if set1.Has(l1) { + t.Errorf("Listener 1 still in set, it should not be") + } + + if !set1.Has(l2) { + t.Errorf("Listener 2 not still in set, it should be") + } + +} + +// Tests that removing a listener from a set with a single listener removes the +// listener and the set +func TestById_Remove_SingleInSet(t *testing.T) { + nbi := newById() + + uid := id.NewIdFromUInt(42, id.User, t) + + l1 := &funcListener{} + + set1 := set.New(l1) + + nbi.list[*uid] = set1 + + nbi.Remove(uid, l1) + + if _, ok := nbi.list[*uid]; ok { + t.Errorf("Set not removed when it should have been") + } + + if set1.Len() != 0 { + t.Errorf("Set is incorrect length after the remove call: %v", + set1.Len()) + } + + if set1.Has(l1) { + t.Errorf("Listener 1 still in set, it should not be") + } +} + +// Tests that removing a listener from a set with a single listener removes the +// listener and not the set when the ID iz ZeroUser +func TestById_Remove_SingleInSet_ZeroUser(t *testing.T) { + nbi := newById() + + uid := &id.ZeroUser + + l1 := &funcListener{} + + set1 := set.New(l1) + + nbi.list[*uid] = set1 + + nbi.Remove(uid, l1) + + if _, ok := nbi.list[*uid]; !ok { + t.Errorf("Set removed when it should not have been") + } + + if set1.Len() != 0 { + t.Errorf("Set is incorrect length after the remove call: %v", + set1.Len()) + } + + if set1.Has(l1) { + t.Errorf("Listener 1 still in set, it should not be") + } +} + +// Tests that removing a listener from a set with a single listener removes the +// listener and not the set when the ID iz ZeroUser +func TestById_Remove_SingleInSet_EmptyUser(t *testing.T) { + nbi := newById() + + uid := &id.ID{} + + l1 := &funcListener{} + + set1 := set.New(l1) + + nbi.list[*uid] = set1 + + nbi.Remove(uid, l1) + + if _, ok := nbi.list[*uid]; !ok { + t.Errorf("Set removed when it should not have been") + } + + if set1.Len() != 0 { + t.Errorf("Set is incorrect length after the remove call: %v", + set1.Len()) + } + + if set1.Has(l1) { + t.Errorf("Listener 1 still in set, it should not be") + } +} diff --git a/context/switchboard/byType.go b/context/switchboard/byType.go new file mode 100644 index 0000000000000000000000000000000000000000..f9a7c6a273115c24499e15388a868459c1319e27 --- /dev/null +++ b/context/switchboard/byType.go @@ -0,0 +1,64 @@ +package switchboard + +import ( + "github.com/golang-collections/collections/set" + "gitlab.com/elixxir/client/context/message" +) + +type byType struct { + list map[message.Type]*set.Set + generic *set.Set +} + +// builds a new byType structure +// registers an AnyType as generic +func newByType() *byType { + bt := &byType{ + list: make(map[message.Type]*set.Set), + generic: set.New(), + } + + // make the zero messages, which are defined as AnyType, + // point to the generic + bt.list[AnyType] = bt.generic + + return bt +} + +// returns a set associated with the passed messageType unioned with the +// generic return +func (bt *byType) Get(messageType message.Type) *set.Set { + lookup, ok := bt.list[messageType] + if !ok { + return bt.generic + } else { + return lookup.Union(bt.generic) + } +} + +// adds a listener to a set for the given messageType. Creates a new set to add +// it to if the set does not exist +func (bt *byType) Add(messageType message.Type, r Listener) *set.Set { + s, ok := bt.list[messageType] + if !ok { + s = set.New(r) + bt.list[messageType] = s + } else { + s.Insert(r) + } + + return s +} + +// Removes the passed listener from the set for messageType and +// deletes the set if it is empty and the type is not AnyType +func (bt *byType) Remove(mt message.Type, l Listener) { + s, ok := bt.list[mt] + if ok { + s.Remove(l) + + if s.Len() == 0 && mt != AnyType { + delete(bt.list, mt) + } + } +} diff --git a/context/switchboard/byType_test.go b/context/switchboard/byType_test.go new file mode 100644 index 0000000000000000000000000000000000000000..fb385195f1cf92a7babea8362af6d438d621aeb6 --- /dev/null +++ b/context/switchboard/byType_test.go @@ -0,0 +1,226 @@ +package switchboard + +import ( + "github.com/golang-collections/collections/set" + "gitlab.com/elixxir/client/context/message" + "testing" +) + +func TestByType_newByType(t *testing.T) { + nbt := newByType() + + if nbt.list == nil { + t.Errorf("No list created") + } + + if nbt.generic == nil { + t.Errorf("No generic created") + } + + if nbt.generic != nbt.list[0] { + t.Errorf("zero message type not registered as generic") + } + +} + +func TestByType_Get_Empty(t *testing.T) { + nbt := newByType() + + s := nbt.Get(42) + + if s.Len() != 0 { + t.Errorf("Should not have returned a set") + } +} + +func TestByType_Get_Selected(t *testing.T) { + nbt := newByType() + + m := message.Type(42) + + set1 := set.New(0) + + nbt.list[m] = set1 + + s := nbt.Get(m) + + if s.Len() == 0 { + t.Errorf("Should have returned a set") + } + + if !s.SubsetOf(set1) || !set1.SubsetOf(s) { + t.Errorf("Wrong set returned") + } +} + +func TestByType_Get_Generic(t *testing.T) { + nbt := newByType() + + m := message.Type(42) + + nbt.generic.Insert(0) + + s := nbt.Get(m) + + if s.Len() == 0 { + t.Errorf("Should have returned a set") + } + + if !s.SubsetOf(nbt.generic) || !nbt.generic.SubsetOf(s) { + t.Errorf("Wrong set returned") + } +} + +func TestByType_Get_GenericSelected(t *testing.T) { + nbt := newByType() + + m := message.Type(42) + + nbt.generic.Insert(1) + + set1 := set.New(0) + + nbt.list[m] = set1 + + s := nbt.Get(m) + + if s.Len() == 0 { + t.Errorf("Should have returned a set") + } + + setUnion := set1.Union(nbt.generic) + + if !s.SubsetOf(setUnion) || !setUnion.SubsetOf(s) { + t.Errorf("Wrong set returned") + } +} + +// Tests that when adding to a set which does not exist, the set is created +func TestByType_Add_New(t *testing.T) { + nbt := newByType() + + m := message.Type(42) + + l := &funcListener{} + + nbt.Add(m, l) + + s := nbt.list[m] + + if s.Len() != 1 { + t.Errorf("Should a set of the wrong size") + } + + if !s.Has(l) { + t.Errorf("Wrong set returned") + } +} + +// Tests that when adding to a set which does exist, the set is retained and +// added to +func TestByType_Add_Old(t *testing.T) { + nbt := newByType() + + m := message.Type(42) + + l1 := &funcListener{} + l2 := &funcListener{} + + set1 := set.New(l1) + + nbt.list[m] = set1 + + nbt.Add(m, l2) + + s := nbt.list[m] + + if s.Len() != 2 { + t.Errorf("Should have returned a set") + } + + if !s.Has(l1) { + t.Errorf("Set does not include the initial listener") + } + + if !s.Has(l2) { + t.Errorf("Set does not include the new listener") + } +} + +// Tests that when adding to a generic ID, the listener is added to the +// generic set +func TestByType_Add_Generic(t *testing.T) { + nbt := newByType() + + l1 := &funcListener{} + + nbt.Add(AnyType, l1) + + s := nbt.generic + + if s.Len() != 1 { + t.Errorf("Should have returned a set of size 2") + } + + if !s.Has(l1) { + t.Errorf("Set does not include the ZeroUser listener") + } +} + +// Tests that removing a listener from a set with a single listener removes the +// listener and the set +func TestByType_Remove_SingleInSet(t *testing.T) { + nbt := newByType() + + m := message.Type(42) + + l1 := &funcListener{} + + set1 := set.New(l1) + + nbt.list[m] = set1 + + nbt.Remove(m, l1) + + if _, ok := nbt.list[m]; ok { + t.Errorf("Set not removed when it should have been") + } + + if set1.Len() != 0 { + t.Errorf("Set is incorrect length after the remove call: %v", + set1.Len()) + } + + if set1.Has(l1) { + t.Errorf("Listener 1 still in set, it should not be") + } +} + +// Tests that removing a listener from a set with a single listener removes the +// listener and not the set when the ID iz ZeroUser +func TestByType_Remove_SingleInSet_AnyType(t *testing.T) { + nbt := newByType() + + m := AnyType + + l1 := &funcListener{} + + set1 := set.New(l1) + + nbt.list[m] = set1 + + nbt.Remove(m, l1) + + if _, ok := nbt.list[m]; !ok { + t.Errorf("Set removed when it should not have been") + } + + if set1.Len() != 0 { + t.Errorf("Set is incorrect length after the remove call: %v", + set1.Len()) + } + + if set1.Has(l1) { + t.Errorf("Listener 1 still in set, it should not be") + } +} diff --git a/context/switchboard/listener.go b/context/switchboard/listener.go new file mode 100644 index 0000000000000000000000000000000000000000..abd6bea71c77d6c7e3afc7c3920ebe0bc381e9e4 --- /dev/null +++ b/context/switchboard/listener.go @@ -0,0 +1,112 @@ +//////////////////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the LICENSE file // +//////////////////////////////////////////////////////////////////////////////////////////// + +package switchboard + +import ( + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/client/context/message" + "gitlab.com/xx_network/primitives/id" +) + +//interface for a listener adhere to +type Listener interface { + // the Hear function is called to exercise the listener, passing in the + // data as an item + Hear(item message.Receive) + // Returns a name, used for debugging + Name() string +} + +// This function type defines callbacks that get passed when the listener is +// listened to. It will always be called in its own goroutine. It may be called +// multiple times simultaneously +type ListenerFunc func(item message.Receive) + +// id object returned when a listener is created and is used to delete it from +// the system +type ListenerID struct { + userID *id.ID + messageType message.Type + listener Listener +} + +//getter for userID +func (lid ListenerID) GetUserID() *id.ID { + return lid.userID +} + +//getter for message type +func (lid ListenerID) GetMessageType() message.Type { + return lid.messageType +} + +//getter for name +func (lid ListenerID) GetName() string { + return lid.listener.Name() +} + +/*internal listener implementations*/ + +//listener based off of a function +type funcListener struct { + listener ListenerFunc + name string +} + +// creates a new FuncListener Adhereing to the listener interface out of the +// passed function and name, returns a pointer to the result +func newFuncListener(listener ListenerFunc, name string) *funcListener { + return &funcListener{ + listener: listener, + name: name, + } +} + +// Adheres to the Hear function of the listener interface, calls the internal +// function with the passed item +func (fl *funcListener) Hear(item message.Receive) { + fl.listener(item) +} + +// Adheres to the Name function of the listener interface, returns a name. +// used for debugging +func (fl *funcListener) Name() string { + return fl.name +} + +//listener based off of a channel +type chanListener struct { + listener chan message.Receive + name string +} + +// creates a new ChanListener Adhereing to the listener interface out of the +// passed channel and name, returns a pointer to the result +func newChanListener(listener chan message.Receive, name string) *chanListener { + return &chanListener{ + listener: listener, + name: name, + } +} + +// Adheres to the Hear function of the listener interface, calls the passed the +// heard item across the channel. Drops the item if it cannot put it into the +// channel immediately +func (cl *chanListener) Hear(item message.Receive) { + select { + case cl.listener <- item: + default: + jww.WARN.Printf("Switchboard failed to speak on channel "+ + "listener %s", cl.name) + } +} + +// Adheres to the Name function of the listener interface, returns a name. +// used for debugging +func (cl *chanListener) Name() string { + return cl.name +} diff --git a/context/switchboard/listener_test.go b/context/switchboard/listener_test.go new file mode 100644 index 0000000000000000000000000000000000000000..295310bfbcd34fa8f62f1bbada9ad2c40c75f321 --- /dev/null +++ b/context/switchboard/listener_test.go @@ -0,0 +1,164 @@ +//////////////////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the LICENSE file // +//////////////////////////////////////////////////////////////////////////////////////////// + +package switchboard + +import ( + "gitlab.com/elixxir/client/context/message" + "gitlab.com/xx_network/primitives/id" + "reflect" + "testing" + "time" +) + +//verify func listener adheres to the listener interface +var _ Listener = &funcListener{} + +//verify chan listener adheres to the listener interface +var _ Listener = &chanListener{} + +//test listenerID returns the userID +func TestListenerID_GetUserID(t *testing.T) { + lid := ListenerID{ + userID: id.NewIdFromUInt(42, id.User, t), + messageType: 42, + listener: nil, + } + + if !lid.GetUserID().Cmp(lid.userID) { + t.Errorf("Returned userID does not match") + } +} + +//test listenerID returns the messageType +func TestListenerID_GetMessageType(t *testing.T) { + lid := ListenerID{ + userID: id.NewIdFromUInt(42, id.User, t), + messageType: 42, + listener: nil, + } + + if lid.GetMessageType() != lid.messageType { + t.Errorf("Returned message type does not match") + } +} + +//test listenerID returns the name +func TestListenerID_GetName(t *testing.T) { + name := "test" + + lid := ListenerID{ + userID: id.NewIdFromUInt(42, id.User, t), + messageType: 42, + listener: newFuncListener(nil, name), + } + + if lid.GetName() != name { + t.Errorf("Returned name type does not match") + } +} + +//tests new function listener creates the funcListener properly +func TestNewFuncListener(t *testing.T) { + f := func(item message.Receive) {} + name := "test" + listener := newFuncListener(f, name) + + if listener.listener == nil { + t.Errorf("function is wrong") + } + + if listener.name != name { + t.Errorf("name is wrong") + } +} + +//tests FuncListener Hear works +func TestFuncListener_Hear(t *testing.T) { + m := message.Receive{ + Payload: []byte{0, 1, 2, 3}, + Sender: id.NewIdFromUInt(42, id.User, t), + MessageType: 69, + } + + heard := make(chan message.Receive, 1) + + f := func(item message.Receive) { + heard <- item + } + + listener := newFuncListener(f, "test") + + listener.Hear(m) + + select { + case item := <-heard: + if !reflect.DeepEqual(item, m) { + t.Errorf("Heard message did not match") + } + case <-time.After(5 * time.Millisecond): + t.Errorf("Did not hear") + } +} + +// Test FuncListener returns the correct name +func TestFuncListener_Name(t *testing.T) { + name := "test" + listener := newFuncListener(nil, name) + + if listener.Name() != name { + t.Errorf("Name did not match") + } +} + +//tests new chan listener creates the chanListener properly +func TestNewChanListener(t *testing.T) { + c := make(chan message.Receive) + name := "test" + listener := newChanListener(c, name) + + if listener.listener == nil { + t.Errorf("function is wrong") + } + + if listener.name != name { + t.Errorf("name is wrong") + } +} + +//tests ChanListener Hear works +func TestChanListener_Hear(t *testing.T) { + m := message.Receive{ + Payload: []byte{0, 1, 2, 3}, + Sender: id.NewIdFromUInt(42, id.User, t), + MessageType: 69, + } + + heard := make(chan message.Receive, 1) + + listener := newChanListener(heard, "test") + + listener.Hear(m) + + select { + case item := <-heard: + if !reflect.DeepEqual(item, m) { + t.Errorf("Heard message did not match") + } + case <-time.After(5 * time.Millisecond): + t.Errorf("Did not hear") + } +} + +// Test FuncListener returns the correct name +func TestChanListener_Name(t *testing.T) { + name := "test" + listener := newChanListener(nil, name) + + if listener.Name() != name { + t.Errorf("Name did not match") + } +} diff --git a/context/switchboard/switchboard.go b/context/switchboard/switchboard.go new file mode 100644 index 0000000000000000000000000000000000000000..0053686c3a57bcd8c8ec69c08a7d9fd4f3e11045 --- /dev/null +++ b/context/switchboard/switchboard.go @@ -0,0 +1,166 @@ +package switchboard + +import ( + "github.com/golang-collections/collections/set" + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/client/context/message" + "gitlab.com/xx_network/primitives/id" + "sync" +) + +type Switchboard struct { + id *byId + messageType *byType + + mux sync.RWMutex +} + +// New generates and returns a new switchboard object. +func New() *Switchboard { + return &Switchboard{ + id: newById(), + messageType: newByType(), + } +} + +// Registers a new listener. Returns the ID of the new listener. +// Keep this around if you want to be able to delete the listener later. +// +// name is used for debug printing and not checked for uniqueness +// +// user: 0 for all, or any user ID to listen for messages from a particular +// user. 0 can be id.ZeroUser or id.ZeroID +// messageType: 0 for all, or any message type to listen for messages of that +// type. 0 can be switchboard.AnyType +// newListener: something implementing the Listener interface. Do not +// pass nil to this. +// +// If a message matches multiple listeners, all of them will hear the message. +func (sw *Switchboard) RegisterListener(user *id.ID, messageType message.Type, + newListener Listener) ListenerID { + + // check the input data is valid + if user == nil { + jww.FATAL.Panicf("cannot register listener to nil user") + } + + if newListener == nil { + jww.FATAL.Panicf("cannot register nil listener") + } + + //register the listener by both ID and messageType + sw.mux.Lock() + + sw.id.Add(user, newListener) + sw.messageType.Add(messageType, newListener) + + sw.mux.Unlock() + + //return a ListenerID so it can be unregistered in the future + return ListenerID{ + userID: user, + messageType: messageType, + listener: newListener, + } +} + +// Registers a new listener built around the passed function. +// Returns the ID of the new listener. +// Keep this around if you want to be able to delete the listener later. +// +// name is used for debug printing and not checked for uniqueness +// +// user: 0 for all, or any user ID to listen for messages from a particular +// user. 0 can be id.ZeroUser or id.ZeroID +// messageType: 0 for all, or any message type to listen for messages of that +// type. 0 can be switchboard.AnyType +// newListener: a function implementing the ListenerFunc function type. +// Do not pass nil to this. +// +// If a message matches multiple listeners, all of them will hear the message. +func (sw *Switchboard) RegisterFunc(name string, user *id.ID, + messageType message.Type, newListener ListenerFunc) ListenerID { + // check that the input data is valid + if newListener == nil { + jww.FATAL.Panicf("cannot register function listener '%s' "+ + "with nil func", name) + } + + // generate a funcListener object adhering to the listener interface + fl := newFuncListener(newListener, name) + + //register the listener and return the result + return sw.RegisterListener(user, messageType, fl) +} + +// Registers a new listener built around the passed channel. +// Returns the ID of the new listener. +// Keep this around if you want to be able to delete the listener later. +// +// name is used for debug printing and not checked for uniqueness +// +// user: 0 for all, or any user ID to listen for messages from a particular +// user. 0 can be id.ZeroUser or id.ZeroID +// messageType: 0 for all, or any message type to listen for messages of that +// type. 0 can be switchboard.AnyType +// newListener: an item channel. +// Do not pass nil to this. +// +// If a message matches multiple listeners, all of them will hear the message. +func (sw *Switchboard) RegisterChannel(name string, user *id.ID, + messageType message.Type, newListener chan message.Receive) ListenerID { + // check that the input data is valid + if newListener == nil { + jww.FATAL.Panicf("cannot register channel listener '%s' with"+ + " nil channel", name) + } + + // generate a chanListener object adhering to the listener interface + cl := newChanListener(newListener, name) + + //register the listener and return the result + return sw.RegisterListener(user, messageType, cl) +} + +// Speak broadcasts a message to the appropriate listeners. +// each is spoken to in their own goroutine +func (sw *Switchboard) Speak(item message.Receive) { + sw.mux.RLock() + defer sw.mux.RUnlock() + + // Matching listeners: include those that match all criteria perfectly, as + // well as those that do not care about certain criteria + matches := sw.matchListeners(item) + + //Execute hear on all matched listeners in a new goroutine + matches.Do(func(i interface{}) { + r := i.(Listener) + go r.Hear(item) + }) + + // print to log if nothing was heard + if matches.Len() == 0 { + jww.ERROR.Printf( + "Message of type %v from user %q didn't match any listeners in"+ + " the map", item.MessageType, item.Sender) + } +} + +// Unregister removes the listener with the specified ID so it will no longer +// get called +func (sw *Switchboard) Unregister(listenerID ListenerID) { + sw.mux.Lock() + + sw.id.Remove(listenerID.userID, listenerID.listener) + sw.messageType.Remove(listenerID.messageType, listenerID.listener) + + sw.mux.Unlock() +} + +// finds all listeners who match the items sender or ID, or have those fields +// as generic +func (sw *Switchboard) matchListeners(item message.Receive) *set.Set { + idSet := sw.id.Get(item.Sender) + typeSet := sw.messageType.Get(item.MessageType) + return idSet.Intersection(typeSet) +} diff --git a/context/switchboard/switchboard_test.go b/context/switchboard/switchboard_test.go new file mode 100644 index 0000000000000000000000000000000000000000..5c54d2906a3a6558faa5123a7d9c2c36b9c05c64 --- /dev/null +++ b/context/switchboard/switchboard_test.go @@ -0,0 +1,343 @@ +package switchboard + +import ( + "gitlab.com/elixxir/client/context/message" + "gitlab.com/xx_network/primitives/id" + "strings" + "testing" + "time" +) + +// tests that New create a correctly structured switchboard +func TestNew(t *testing.T) { + sw := New() + + if sw.id == nil { + t.Errorf("did not create an id map") + } + + if sw.messageType == nil { + t.Errorf("did not create a messageType map") + } +} + +//Tests that register listener handles errors properly +func TestSwitchboard_RegisterListener_Error_NilUserID(t *testing.T) { + defer func() { + if r := recover(); r != nil && !strings.Contains(r.(string), + "cannot register listener to nil user") { + t.Errorf("A nil userID caused the wrong error: %s", r) + } + }() + + sw := New() + sw.RegisterListener(nil, 0, &funcListener{}) + + t.Errorf("A nil userID should have caused an panic") +} + +//Tests that register listener handles errors properly +func TestSwitchboard_RegisterListener_Error_NilListener(t *testing.T) { + defer func() { + if r := recover(); r != nil && !strings.Contains(r.(string), + "cannot register nil listener") { + t.Errorf("A nil listener caused the wrong error: %s", r) + } + }() + + sw := New() + sw.RegisterListener(id.NewIdFromUInt(42, id.User, t), 0, nil) + + t.Errorf("A nil listener should have caused an error") +} + +//Tests that RegisterListener properly registers the listeners +func TestSwitchboard_RegisterListener(t *testing.T) { + sw := New() + + l := &funcListener{} + + uid := id.NewIdFromUInt(42, id.User, t) + + mt := message.Type(69) + + lid := sw.RegisterListener(uid, mt, l) + + if lid.messageType != mt { + t.Errorf("ListenerID message type is wrong") + } + + if !lid.userID.Cmp(uid) { + t.Errorf("ListenerID userID is wrong") + } + + if lid.listener != l { + t.Errorf("ListenerID listener is wrong") + } + + //check that the listener is registered in the appropriate location + setID := sw.id.Get(uid) + + if !setID.Has(l) { + t.Errorf("Listener is not registered by ID") + } + + setType := sw.messageType.Get(mt) + + if !setType.Has(l) { + t.Errorf("Listener is not registered by Message Type") + } + +} + +//Tests that register funcListener handles errors properly +func TestSwitchboard_RegisterFunc_Error_NilUserID(t *testing.T) { + defer func() { + if r := recover(); r != nil && !strings.Contains(r.(string), + "cannot register listener to nil user") { + t.Errorf("A nil userID caused the wrong error: %s", r) + } + }() + + sw := New() + sw.RegisterFunc("test", nil, 0, func(receive message.Receive) {}) + + t.Errorf("A nil user ID should have caused an error") +} + +//Tests that register funcListener handles errors properly +func TestSwitchboard_RegisterFunc_Error_NilFunc(t *testing.T) { + defer func() { + if r := recover(); r != nil && !strings.Contains(r.(string), + "cannot register function listener 'test' with nil func") { + t.Errorf("A nil func caused the wrong error: %s", r) + } + }() + + sw := New() + sw.RegisterFunc("test", id.NewIdFromUInt(42, id.User, t), 0, nil) + + t.Errorf("A nil listener func should have caused an error") +} + +//Tests that RegisterFunc properly registers the listeners +func TestSwitchboard_RegisterFunc(t *testing.T) { + sw := New() + + heard := false + + l := func(receive message.Receive) { heard = true } + + uid := id.NewIdFromUInt(42, id.User, t) + + mt := message.Type(69) + + lid := sw.RegisterFunc("test", uid, mt, l) + + if lid.messageType != mt { + t.Errorf("ListenerID message type is wrong") + } + + if !lid.userID.Cmp(uid) { + t.Errorf("ListenerID userID is wrong") + } + + //check that the listener is registered in the appropriate location + setID := sw.id.Get(uid) + + if !setID.Has(lid.listener) { + t.Errorf("Listener is not registered by ID") + } + + setType := sw.messageType.Get(mt) + + if !setType.Has(lid.listener) { + t.Errorf("Listener is not registered by Message Type") + } + + lid.listener.Hear(message.Receive{}) + if !heard { + t.Errorf("Func listener not registered correctly") + } +} + +//Tests that register chanListener handles errors properly +func TestSwitchboard_RegisterChan_Error_NilUser(t *testing.T) { + defer func() { + if r := recover(); r != nil && !strings.Contains(r.(string), + "cannot register listener to nil user") { + t.Errorf("A nil user ID caused the wrong error: %s", r) + } + }() + sw := New() + sw.RegisterChannel("test", nil, 0, + make(chan message.Receive)) + + t.Errorf("A nil userID should have caused an error") +} + +//Tests that register chanListener handles errors properly +func TestSwitchboard_RegisterChan_Error_NilChan(t *testing.T) { + defer func() { + if r := recover(); r != nil && !strings.Contains(r.(string), + "cannot register channel listener 'test' with nil channel") { + t.Errorf("A nil channel caused the wrong error: %s", r) + } + }() + sw := New() + sw.RegisterChannel("test", &id.ID{}, 0, nil) + + t.Errorf("A nil channel func should have caused an error") +} + +//Tests that RegisterChan properly registers the listeners +func TestSwitchboard_RegisterChan(t *testing.T) { + sw := New() + + ch := make(chan message.Receive, 1) + + uid := id.NewIdFromUInt(42, id.User, t) + + mt := message.Type(69) + + lid := sw.RegisterChannel("test", uid, mt, ch) + + //check the returns + if lid.messageType != mt { + t.Errorf("ListenerID message type is wrong") + } + + if !lid.userID.Cmp(uid) { + t.Errorf("ListenerID userID is wrong") + } + + //check that the listener is registered in the appropriate location + setID := sw.id.Get(uid) + + if !setID.Has(lid.listener) { + t.Errorf("Listener is not registered by ID") + } + + setType := sw.messageType.Get(mt) + + if !setType.Has(lid.listener) { + t.Errorf("Listener is not registered by Message Type") + } + + lid.listener.Hear(message.Receive{}) + select { + case <-ch: + case <-time.After(5 * time.Millisecond): + t.Errorf("Chan listener not registered correctly") + } +} + +//tests all combinations of hits and misses for speak +func TestSwitchboard_Speak(t *testing.T) { + + uids := []*id.ID{{}, AnyUser(), id.NewIdFromUInt(42, id.User, t), id.NewIdFromUInt(69, id.User, t)} + mts := []message.Type{AnyType, 42, 69} + + for _, uidReg := range uids { + for _, mtReg := range mts { + + //create the registrations + sw := New() + ch1 := make(chan message.Receive, 1) + ch2 := make(chan message.Receive, 1) + + sw.RegisterChannel("test", uidReg, mtReg, ch1) + sw.RegisterChannel("test", uidReg, mtReg, ch2) + + //send every possible message + for _, uid := range uids { + for _, mt := range mts { + if uid.Cmp(&id.ID{}) || mt == AnyType { + continue + } + + m := message.Receive{ + Payload: []byte{0, 1, 2, 3}, + Sender: uid, + MessageType: mt, + } + + sw.Speak(m) + + shouldHear := (m.Sender.Cmp(uidReg) || + uidReg.Cmp(&id.ID{}) || uidReg.Cmp(AnyUser())) && + (m.MessageType == mtReg || mtReg == AnyType) + + var heard1 bool + + select { + case <-ch1: + heard1 = true + case <-time.After(5 * time.Millisecond): + heard1 = false + } + + if shouldHear != heard1 { + t.Errorf("Correct operation not recorded "+ + "for listener 1: Expected: %v, Occured: %v", + shouldHear, heard1) + } + + var heard2 bool + + select { + case <-ch2: + heard2 = true + case <-time.After(5 * time.Millisecond): + heard2 = false + } + + if shouldHear != heard2 { + t.Errorf("Correct operation not recorded "+ + "for listener 2: Expected: %v, Occured: %v", + shouldHear, heard2) + } + } + } + } + } +} + +//tests that Unregister removes the listener and only the listener +func TestSwitchboard_Unregister(t *testing.T) { + sw := New() + + uid := id.NewIdFromUInt(42, id.User, t) + mt := message.Type(69) + + l := func(receive message.Receive) {} + + lid1 := sw.RegisterFunc("a", uid, mt, l) + + lid2 := sw.RegisterFunc("a", uid, mt, l) + + sw.Unregister(lid1) + + //get sets to check + setID := sw.id.Get(uid) + setType := sw.messageType.Get(mt) + + //check that the removed listener is not registered + if setID.Has(lid1.listener) { + t.Errorf("Removed Listener is registered by ID, should not be") + } + + if setType.Has(lid1.listener) { + t.Errorf("Removed Listener not registered by Message Type, " + + "should not be") + } + + //check that the not removed listener is still registered + if !setID.Has(lid2.listener) { + t.Errorf("Remaining Listener is not registered by ID") + } + + if !setType.Has(lid2.listener) { + t.Errorf("Remaining Listener is not registered by Message Type") + } +} diff --git a/crypto/decrypt.go b/crypto/decrypt.go deleted file mode 100644 index 9004d5aa028585d9ff05dd7894f6d9d80041ea32..0000000000000000000000000000000000000000 --- a/crypto/decrypt.go +++ /dev/null @@ -1,77 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Copyright © 2019 Privategrity Corporation / -// / -// All rights reserved. / -//////////////////////////////////////////////////////////////////////////////// - -package crypto - -import ( - "github.com/pkg/errors" - "gitlab.com/elixxir/crypto/cyclic" - "gitlab.com/elixxir/crypto/e2e" - "gitlab.com/elixxir/crypto/hash" - "gitlab.com/elixxir/primitives/format" -) - -// E2EDecrypt uses the E2E key to decrypt the message -// It returns an error in case of HMAC verification failure -// or in case of a decryption error (related to padding) -// If it succeeds, it modifies the passed message -func E2EDecrypt(grp *cyclic.Group, key *cyclic.Int, - msg *format.Message) error { - // First thing to do is check MAC - if !hash.VerifyHMAC(msg.Contents.Get(), msg.GetMAC(), key.Bytes()) { - return errors.New("HMAC verification failed for E2E message") - } - var iv [e2e.AESBlockSize]byte - fp := msg.GetKeyFP() - copy(iv[:], fp[:e2e.AESBlockSize]) - // decrypt the timestamp in the associated data - decryptedTimestamp, err := e2e.DecryptAES256WithIV( - key.Bytes(), iv, msg.GetTimestamp()) - if err != nil { - return errors.New("Timestamp decryption failed for E2E message: " + err.Error()) - } - // TODO deserialize this somewhere along the line and provide methods - // to mobile developers on the bindings to interact with the timestamps - decryptedTimestamp = append(decryptedTimestamp, 0) - msg.SetTimestamp(decryptedTimestamp) - // Decrypt e2e - decryptedPayload, err := e2e.Decrypt(grp, key, msg.Contents.Get()) - - if err != nil { - return errors.New("Failed to decrypt E2E message: " + err.Error()) - } - msg.Contents.SetRightAligned(decryptedPayload) - return nil -} - -// E2EDecryptUnsafe uses the E2E key to decrypt the message -// It returns an error in case of HMAC verification failure -// It doesn't expect the payload to be padded -// If it succeeds, it modifies the passed message -func E2EDecryptUnsafe(grp *cyclic.Group, key *cyclic.Int, - msg *format.Message) error { - // First thing to do is check MAC - if !hash.VerifyHMAC(msg.Contents.Get(), msg.GetMAC(), key.Bytes()) { - return errors.New("HMAC verification failed for E2E message") - } - var iv [e2e.AESBlockSize]byte - fp := msg.GetKeyFP() - copy(iv[:], fp[:e2e.AESBlockSize]) - // decrypt the timestamp in the associated data - decryptedTimestamp, err := e2e.DecryptAES256WithIV( - key.Bytes(), iv, msg.GetTimestamp()) - if err != nil { - return errors.New("Timestamp decryption failed for E2E message: " + err.Error()) - } - // TODO deserialize this somewhere along the line and provide methods - // to mobile developers on the bindings to interact with the timestamps - decryptedTimestamp = append(decryptedTimestamp, 0) - msg.SetTimestamp(decryptedTimestamp) - // Decrypt e2e - decryptedPayload := e2e.DecryptUnsafe(grp, key, msg.Contents.Get()) - msg.Contents.Set(decryptedPayload) - return nil -} diff --git a/crypto/encrypt.go b/crypto/encrypt.go deleted file mode 100644 index bb3fb592f4f6dfc89b5dc9a0a8af16378b0d5b67..0000000000000000000000000000000000000000 --- a/crypto/encrypt.go +++ /dev/null @@ -1,126 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Copyright © 2019 Privategrity Corporation / -// / -// All rights reserved. / -//////////////////////////////////////////////////////////////////////////////// - -package crypto - -import ( - "gitlab.com/elixxir/client/globals" - "gitlab.com/elixxir/client/storage" - "gitlab.com/elixxir/client/user" - "gitlab.com/elixxir/crypto/cmix" - "gitlab.com/elixxir/crypto/cyclic" - "gitlab.com/elixxir/crypto/e2e" - "gitlab.com/elixxir/crypto/hash" - "gitlab.com/elixxir/primitives/format" - "gitlab.com/xx_network/comms/connect" -) - -// TODO: REMOVE ME -var SessionV2 *storage.Session - -// CMIX Encrypt performs the encryption -// of the msg to a team of nodes -// It returns a new msg -func CMIXEncrypt(session user.Session, topology *connect.Circuit, salt []byte, - msg *format.Message) (*format.Message, [][]byte) { - // Generate the encryption key - nodeKeys, err := SessionV2.GetNodeKeysFromCircuit(topology) - if err != nil { - globals.Log.FATAL.Panicf("could not get nodeKeys: %+v", err) - } - - baseKeys := make([]*cyclic.Int, len(nodeKeys)) - for i, key := range nodeKeys { - globals.Log.WARN.Printf("NodeKey for %d: %v", i, key.TransmissionKey) - baseKeys[i] = key.TransmissionKey - } - - userData, err := SessionV2.GetUserData() - - if err != nil { - globals.Log.FATAL.Panicf("could not get userData: %+v", err) - } - - ecrMsg := cmix.ClientEncrypt(userData.CmixGrp, msg, salt, baseKeys) - - h, err := hash.NewCMixHash() - if err != nil { - globals.Log.ERROR.Printf("Cound not get hash for KMAC generation: %+v", h) - } - - KMAC := cmix.GenerateKMACs(salt, baseKeys, h) - - return ecrMsg, KMAC -} - -// E2EEncrypt uses the E2E key to encrypt msg -// to its intended recipient -// It also properly populates the associated data -// It modifies the passed msg instead of returning a new one -func E2EEncrypt(grp *cyclic.Group, - key *cyclic.Int, keyFP format.Fingerprint, - msg *format.Message) { - msg.SetKeyFP(keyFP) - - // Encrypt the timestamp using key - // Timestamp bytes were previously stored - // and GO only uses 15 bytes, so use those - var iv [e2e.AESBlockSize]byte - copy(iv[:], keyFP[:e2e.AESBlockSize]) - encryptedTimestamp, err := - e2e.EncryptAES256WithIV(key.Bytes(), iv, - msg.GetTimestamp()[:15]) - if err != nil { - panic(err) - } - msg.SetTimestamp(encryptedTimestamp) - - // E2E encrypt the msg - encPayload, err := e2e.Encrypt(grp, key, msg.Contents.GetRightAligned()) - if err != nil { - globals.Log.ERROR.Panicf(err.Error()) - } - msg.Contents.Set(encPayload) - - // MAC is HMAC(key, ciphertext) - // Currently, the MAC doesn't include any of the associated data - MAC := hash.CreateHMAC(encPayload, key.Bytes()) - msg.SetMAC(MAC) -} - -// E2EEncryptUnsafe uses the E2E key to encrypt msg -// to its intended recipient -// It doesn't apply padding to the payload, so it can be unsafe -// if the payload is small -// It also properly populates the associated data -// It modifies the passed msg instead of returning a new one -func E2EEncryptUnsafe(grp *cyclic.Group, - key *cyclic.Int, keyFP format.Fingerprint, - msg *format.Message) { - msg.SetKeyFP(keyFP) - - // Encrypt the timestamp using key - // Timestamp bytes were previously stored - // and GO only uses 15 bytes, so use those - var iv [e2e.AESBlockSize]byte - copy(iv[:], keyFP[:e2e.AESBlockSize]) - encryptedTimestamp, err := - e2e.EncryptAES256WithIV(key.Bytes(), iv, - msg.GetTimestamp()[:15]) - if err != nil { - panic(err) - } - msg.SetTimestamp(encryptedTimestamp) - - // E2E encrypt the msg - encPayload := e2e.EncryptUnsafe(grp, key, msg.Contents.Get()) - msg.Contents.Set(encPayload) - - // MAC is HMAC(key, ciphertext) - // Currently, the MAC doesn't include any of the associated data - MAC := hash.CreateHMAC(encPayload, key.Bytes()) - msg.SetMAC(MAC) -} diff --git a/crypto/encryptdecrypt_test.go b/crypto/encryptdecrypt_test.go deleted file mode 100644 index 0ed8c376f6be5e2b9d8171e72fa4661c551ab380..0000000000000000000000000000000000000000 --- a/crypto/encryptdecrypt_test.go +++ /dev/null @@ -1,411 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Copyright © 2019 Privategrity Corporation / -// / -// All rights reserved. / -//////////////////////////////////////////////////////////////////////////////// - -package crypto - -import ( - "bytes" - "encoding/binary" - "fmt" - "gitlab.com/elixxir/client/storage" - user2 "gitlab.com/elixxir/client/storage/user" - "gitlab.com/elixxir/client/user" - "gitlab.com/elixxir/client/userRegistry" - pb "gitlab.com/elixxir/comms/mixmessages" - "gitlab.com/elixxir/crypto/cmix" - "gitlab.com/elixxir/crypto/cyclic" - "gitlab.com/elixxir/crypto/hash" - "gitlab.com/elixxir/crypto/large" - "gitlab.com/elixxir/primitives/format" - "gitlab.com/xx_network/comms/connect" - "gitlab.com/xx_network/primitives/id" - "golang.org/x/crypto/blake2b" - "os" - "testing" - "time" -) - -const numNodes = 5 - -var salt = []byte( - "fdecfa52a8ad1688dbfa7d16df74ebf27e535903c469cefc007ebbe1ee895064") - -var session user.Session -var serverPayloadAKey *cyclic.Int -var serverPayloadBKey *cyclic.Int - -var topology *connect.Circuit - -func setup() { - - cmixGrp, e2eGrp := getGroups() - - userRegistry.InitUserRegistry(cmixGrp) - - UID := new(id.ID) - binary.BigEndian.PutUint64(UID[:], 18) - UID.SetType(id.User) - u, ok := userRegistry.Users.GetUser(UID) - if !ok { - panic("Didn't get user 18 from registry") - } - - var nodeSlice []*id.ID - - //build topology - for i := 0; i < numNodes; i++ { - nodeId := new(id.ID) - nodeId[0] = byte(i) - nodeId.SetType(id.Node) - nodeSlice = append(nodeSlice, nodeId) - } - - topology = connect.NewCircuit(nodeSlice) - - tempKey := cmixGrp.NewInt(1) - serverPayloadAKey = cmixGrp.NewInt(1) - serverPayloadBKey = cmixGrp.NewInt(1) - - h, _ := blake2b.New256(nil) - - session = user.NewSession(nil, "password") - - SessionV2, _ = storage.Init(".ekvcryptotest", "password") - - userData := &user2.UserData{ - ThisUser: u, - CmixGrp: cmixGrp, - E2EGrp: e2eGrp, - } - SessionV2.CommitUserData(userData) - - for i := 0; i < numNodes; i++ { - - nk := user.NodeKeys{} - - h.Reset() - h.Write(salt) - - nk.TransmissionKey = cmixGrp.NewInt(int64(2 + i)) - cmix.NodeKeyGen(cmixGrp, salt, nk.TransmissionKey, tempKey) - cmixGrp.Mul(serverPayloadAKey, tempKey, serverPayloadAKey) - - cmix.NodeKeyGen(cmixGrp, h.Sum(nil), nk.TransmissionKey, tempKey) - cmixGrp.Mul(serverPayloadBKey, tempKey, serverPayloadBKey) - - SessionV2.PushNodeKey(topology.GetNodeAtIndex(i), nk) - fmt.Printf("Saved NodeKey: %s, %v", topology.GetNodeAtIndex(i), - nk.TransmissionKey) - - } - -} - -func TestMain(m *testing.M) { - setup() - os.Exit(m.Run()) -} - -func TestFullEncryptDecrypt(t *testing.T) { - cmixGrp, e2eGrp := getGroups() - - sender := id.NewIdFromUInt(38, id.User, t) - recipient := id.NewIdFromUInt(29, id.User, t) - msg := format.NewMessage() - msg.SetRecipient(recipient) - msgPayload := []byte("help me, i'm stuck in an" + - " EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory") - // Normally, msgPayload would be the right length due to padding - //msgPayload = append(msgPayload, make([]byte, - // format.ContentsLen-len(msgPayload)-format.PadMinLen)...) - msg.Contents.SetRightAligned(msgPayload) - now := time.Now() - nowBytes, _ := now.MarshalBinary() - // Normally, nowBytes would be the right length due to AES encryption - nowBytes = append(nowBytes, make([]byte, format.TimestampLen-len(nowBytes))...) - msg.SetTimestamp(nowBytes) - - key := e2eGrp.NewInt(42) - h, _ := hash.NewCMixHash() - h.Write(key.Bytes()) - fp := format.Fingerprint{} - copy(fp[:], h.Sum(nil)) - - // E2E Encryption - E2EEncrypt(e2eGrp, key, fp, msg) - - // CMIX Encryption - encMsg, _ := CMIXEncrypt(session, topology, salt, msg) - - // Server will decrypt payload (which is OK because the payload is now e2e) - // This block imitates what the server does during the realtime - payloadA := cmixGrp.NewIntFromBytes(encMsg.GetPayloadA()) - payloadB := cmixGrp.NewIntFromBytes(encMsg.GetPayloadB()) - // Multiply payloadA and associated data by serverPayloadBkey - cmixGrp.Mul(payloadA, serverPayloadAKey, payloadA) - // Multiply payloadB data only by serverPayloadAkey - cmixGrp.Mul(payloadB, serverPayloadBKey, payloadB) - - decMsg := format.NewMessage() - decMsg.SetPayloadA(payloadA.LeftpadBytes(uint64(format.PayloadLen))) - decMsg.SetDecryptedPayloadB(payloadB.LeftpadBytes(uint64(format.PayloadLen))) - - // E2E Decryption - err := E2EDecrypt(e2eGrp, key, decMsg) - - if err != nil { - t.Errorf("E2EDecrypt returned error: %v", err.Error()) - } - - decryptedRecipient, err := decMsg.GetRecipient() - if err != nil { - t.Fatal(err) - } - - if !decryptedRecipient.Cmp(recipient) { - t.Errorf("Recipient differed from expected: Got %q, expected %q", - decryptedRecipient, sender) - } - if !bytes.Equal(decMsg.Contents.GetRightAligned(), msgPayload) { - t.Errorf("Decrypted payload differed from expected: Got %q, "+ - "expected %q", decMsg.Contents.Get(), msgPayload) - } -} - -// E2E unsafe functions should only be used when the payload -// to be sent occupies the whole payload structure, i.e. 256 bytes -func TestFullEncryptDecrypt_Unsafe(t *testing.T) { - cmixGrp, e2eGrp := getGroups() - sender := id.NewIdFromUInt(38, id.User, t) - recipient := id.NewIdFromUInt(29, id.User, t) - msg := format.NewMessage() - msg.SetRecipient(recipient) - msgPayload := []byte( - " EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory" + - " EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory" + - " EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory" + - " EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory" + - " EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory" + - " EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory" + - " EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory") - msg.Contents.Set(msgPayload[:format.ContentsLen]) - - msg.SetTimestamp(make([]byte, 16)) - - key := e2eGrp.NewInt(42) - h, _ := hash.NewCMixHash() - h.Write(key.Bytes()) - fp := format.Fingerprint{} - copy(fp[:], h.Sum(nil)) - - // E2E Encryption without padding - E2EEncryptUnsafe(e2eGrp, key, fp, msg) - - // CMIX Encryption - encMsg, _ := CMIXEncrypt(session, topology, salt, msg) - - // Server will decrypt payload (which is OK because the payload is now e2e) - // This block imitates what the server does during the realtime - var encryptedNet *pb.Slot - { - payload := cmixGrp.NewIntFromBytes(encMsg.GetPayloadA()) - assocData := cmixGrp.NewIntFromBytes(encMsg.GetPayloadB()) - // Multiply payload and associated data by transmission key only - cmixGrp.Mul(payload, serverPayloadAKey, payload) - // Multiply associated data only by transmission key - cmixGrp.Mul(assocData, serverPayloadBKey, assocData) - encryptedNet = &pb.Slot{ - SenderID: sender.Bytes(), - Salt: salt, - PayloadA: payload.LeftpadBytes(uint64(format.PayloadLen)), - PayloadB: assocData.LeftpadBytes(uint64(format.PayloadLen)), - } - } - - decMsg := format.NewMessage() - decMsg.SetPayloadA(encryptedNet.PayloadA) - decMsg.SetDecryptedPayloadB(encryptedNet.PayloadB) - - // E2E Decryption - err := E2EDecryptUnsafe(e2eGrp, key, decMsg) - - if err != nil { - t.Errorf("E2EDecryptUnsafe returned error: %v", err.Error()) - } - - decryptedRecipient, err := decMsg.GetRecipient() - if err != nil { - t.Fatal(err) - } - - if !decryptedRecipient.Cmp(recipient) { - t.Errorf("Recipient differed from expected: Got %q, expected %q", - decryptedRecipient, sender) - } - if !bytes.Equal(decMsg.Contents.Get(), msgPayload[:format.ContentsLen]) { - t.Errorf("Decrypted payload differed from expected: Got %q, "+ - "expected %q", decMsg.Contents.Get(), msgPayload[:format.ContentsLen]) - } -} - -// Test that E2EEncrypt panics if the payload is too big (can't be padded) -func TestE2EEncrypt_Panic(t *testing.T) { - _, e2eGrp := getGroups() - recipient := id.NewIdFromUInt(29, id.User, t) - msg := format.NewMessage() - msg.SetRecipient(recipient) - msgPayload := []byte("help me, i'm stuck in an" + - " EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory" + - " EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory" + - " EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory" + - " EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory" + - " EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory" + - " EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory") - msgPayload = msgPayload[:format.ContentsLen] - msg.Contents.Set(msgPayload) - msg.SetTimestamp(make([]byte, 16)) - - key := e2eGrp.NewInt(42) - h, _ := hash.NewCMixHash() - h.Write(key.Bytes()) - fp := format.Fingerprint{} - copy(fp[:], h.Sum(nil)) - - defer func() { - if r := recover(); r == nil { - t.Errorf("E2EEncrypt should panic on payload too large") - } - }() - - // E2E Encryption Panics - E2EEncrypt(e2eGrp, key, fp, msg) -} - -// Test that E2EDecrypt and E2EDecryptUnsafe handle errors correctly -func TestE2EDecrypt_Errors(t *testing.T) { - _, e2eGrp := getGroups() - recipient := id.NewIdFromUInt(29, id.User, t) - msg := format.NewMessage() - msg.SetRecipient(recipient) - msgPayload := []byte("help me, i'm stuck in an EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory ") - msg.Contents.SetRightAligned(msgPayload) - msg.SetTimestamp(make([]byte, 16)) - - key := e2eGrp.NewInt(42) - h, _ := hash.NewCMixHash() - h.Write(key.Bytes()) - fp := format.Fingerprint{} - copy(fp[:], h.Sum(nil)) - - // E2E Encryption - E2EEncrypt(e2eGrp, key, fp, msg) - - // Copy message - badMsg := format.NewMessage() - badMsg.SetPayloadA(msg.GetPayloadA()) - badMsg.SetPayloadB(msg.GetPayloadB()) - - // Corrupt MAC to make decryption fail - badMsg.SetMAC([]byte("sakfaskfajskasfkkaskfanjffffjnaf")) - - // E2E Decryption returns error - err := E2EDecrypt(e2eGrp, key, badMsg) - - if err == nil { - t.Errorf("E2EDecrypt should have returned error") - } else { - t.Logf("E2EDecrypt error: %v", err.Error()) - } - - // Unsafe E2E Decryption returns error - err = E2EDecryptUnsafe(e2eGrp, key, badMsg) - - if err == nil { - t.Errorf("E2EDecryptUnsafe should have returned error") - } else { - t.Logf("E2EDecryptUnsafe error: %v", err.Error()) - } - - // Set correct MAC again - badMsg.SetMAC(msg.GetMAC()) - - // Corrupt timestamp to make decryption fail - badMsg.SetTimestamp([]byte("ABCDEF1234567890")) - - // E2E Decryption returns error - err = E2EDecrypt(e2eGrp, key, badMsg) - - if err == nil { - t.Errorf("E2EDecrypt should have returned error") - } else { - t.Logf("E2EDecrypt error: %v", err.Error()) - } - - // Unsafe E2E Decryption returns error - err = E2EDecryptUnsafe(e2eGrp, key, badMsg) - - if err == nil { - t.Errorf("E2EDecryptUnsafe should have returned error") - } else { - t.Logf("E2EDecryptUnsafe error: %v", err.Error()) - } - - // Set correct Timestamp again - badMsg.SetTimestamp(msg.GetTimestamp()) - - // Corrupt payload to make decryption fail - badMsg.Contents.SetRightAligned([]byte( - "sakomnsfjeiknheuijhgfyaistuajhfaiuojfkhufijsahufiaij")) - - // Calculate new MAC to avoid failing on that verification again - newMAC := hash.CreateHMAC(badMsg.Contents.Get(), key.Bytes()) - badMsg.SetMAC(newMAC) - - // E2E Decryption returns error - err = E2EDecrypt(e2eGrp, key, badMsg) - - if err == nil { - t.Errorf("E2EDecrypt should have returned error") - } else { - t.Logf("E2EDecrypt error: %v", err.Error()) - } -} - -func getGroups() (*cyclic.Group, *cyclic.Group) { - - cmixGrp := cyclic.NewGroup( - large.NewIntFromString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"+ - "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"+ - "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"+ - "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"+ - "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"+ - "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"+ - "83655D23DCA3AD961C62F356208552BB9ED529077096966D"+ - "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"+ - "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"+ - "DE2BCBF6955817183995497CEA956AE515D2261898FA0510"+ - "15728E5A8AACAA68FFFFFFFFFFFFFFFF", 16), - large.NewIntFromString("2", 16)) - - e2eGrp := cyclic.NewGroup( - large.NewIntFromString("E2EE983D031DC1DB6F1A7A67DF0E9A8E5561DB8E8D49413394C049B"+ - "7A8ACCEDC298708F121951D9CF920EC5D146727AA4AE535B0922C688B55B3DD2AE"+ - "DF6C01C94764DAB937935AA83BE36E67760713AB44A6337C20E7861575E745D31F"+ - "8B9E9AD8412118C62A3E2E29DF46B0864D0C951C394A5CBBDC6ADC718DD2A3E041"+ - "023DBB5AB23EBB4742DE9C1687B5B34FA48C3521632C4A530E8FFB1BC51DADDF45"+ - "3B0B2717C2BC6669ED76B4BDD5C9FF558E88F26E5785302BEDBCA23EAC5ACE9209"+ - "6EE8A60642FB61E8F3D24990B8CB12EE448EEF78E184C7242DD161C7738F32BF29"+ - "A841698978825B4111B4BC3E1E198455095958333D776D8B2BEEED3A1A1A221A6E"+ - "37E664A64B83981C46FFDDC1A45E3D5211AAF8BFBC072768C4F50D7D7803D2D4F2"+ - "78DE8014A47323631D7E064DE81C0C6BFA43EF0E6998860F1390B5D3FEACAF1696"+ - "015CB79C3F9C2D93D961120CD0E5F12CBB687EAB045241F96789C38E89D796138E"+ - "6319BE62E35D87B1048CA28BE389B575E994DCA755471584A09EC723742DC35873"+ - "847AEF49F66E43873", 16), - large.NewIntFromString("2", 16)) - - return cmixGrp, e2eGrp - -} diff --git a/io/collate/format.go b/io/collate/format.go new file mode 100644 index 0000000000000000000000000000000000000000..65c1e2681357c5f2f8c31c1123b19d12d9cbcfd6 --- /dev/null +++ b/io/collate/format.go @@ -0,0 +1,6 @@ +package collate + +import ( + "encoding/binary" + "gitlab.com/elixxir/client/context/message" +) diff --git a/io/keyExchange/generate.sh b/io/keyExchange/generate.sh new file mode 100644 index 0000000000000000000000000000000000000000..08904d8b1a8ffa53f9247b4c5338fb948d65dc51 --- /dev/null +++ b/io/keyExchange/generate.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +protoc --go_out=. xchange.proto diff --git a/io/keyExchange/init.go b/io/keyExchange/init.go index 7159d57803c07649e28612d4bf6b6dc9372593ae..854eb34f7b3ab26e2f84c3ec314baacae5b51774 100644 --- a/io/keyExchange/init.go +++ b/io/keyExchange/init.go @@ -1,15 +1,37 @@ package keyExchange import ( + "github.com/golang/protobuf/proto" + jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/context" + "gitlab.com/elixxir/client/context/message" "gitlab.com/elixxir/client/context/stoppable" - "gitlab.com/elixxir/primitives/switchboard" + "gitlab.com/elixxir/client/storage/e2e" + "gitlab.com/xx_network/primitives/id" ) +const keyExchangeTriggerName = "KeyExchangeTrigger" + func Init(ctx *context.Context) stoppable.Stoppable { //register the rekey request thread - rekeyRequestCh := make(chan switchboard.Item, 10) - ctx.Switchboard.RegisterChannel() + rekeyRequestCh := make(chan message.Receive, 10) + ctx.Switchboard.RegisterChannel(keyExchangeTriggerName, &id.ID{}, + message.KeyExchangeTrigger, rekeyRequestCh) + + triggerStop := stoppable.NewSingle(keyExchangeTriggerName) + go func() { + for true { + select { + case <-triggerStop.Quit(): + return + case request := <-rekeyRequestCh: + ctx.Session.request.Sender + } + } + () + } } + + diff --git a/io/keyExchange/rekey.go b/io/keyExchange/rekey.go index ea1dfcd8d96682032429d1fd6f95bc9aa3eafa28..5c3fcdf4bb500ea13b3d46d8bed12a2232865329 100644 --- a/io/keyExchange/rekey.go +++ b/io/keyExchange/rekey.go @@ -4,7 +4,6 @@ import ( "github.com/golang/protobuf/proto" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" - "gitlab.com/elixxir/client/cmixproto" "gitlab.com/elixxir/client/context" "gitlab.com/elixxir/client/context/message" "gitlab.com/elixxir/client/context/params" @@ -30,7 +29,7 @@ func CheckKeyExchanges(ctx *context.Context, manager *e2e.Manager) { // session while the latter on an extand func trigger(ctx *context.Context, manager *e2e.Manager, session *e2e.Session) { var negotiatingSession *e2e.Session - switch session.ConfirmationStatus() { + switch session.NegotiationStatus() { // If the passed session is triggering a negotiation on a new session to // replace itself, then create the session case e2e.NewSessionTriggered: @@ -44,7 +43,7 @@ func trigger(ctx *context.Context, manager *e2e.Manager, session *e2e.Session) { negotiatingSession = session default: jww.FATAL.Panicf("Session %s provided invalid e2e "+ - "negotiating status: %s", session, session.ConfirmationStatus()) + "negotiating status: %s", session, session.NegotiationStatus()) } // send the rekey notification to the partner @@ -65,22 +64,22 @@ func negotiate(ctx *context.Context, session *e2e.Session) error { e2eStore.GetGroup()) //build the payload - payload, err := proto.Marshal(&cmixproto.RekeyTrigger{ + payload, err := proto.Marshal(&RekeyTrigger{ PublicKey: pubKey.Bytes(), - SessionID: session.GetTrigger().Bytes(), + SessionID: session.GetTrigger().Marshal(), }) //If the payload cannot be marshaled, panic if err != nil { jww.FATAL.Printf("Failed to marshal payload for Key "+ - "Negotation with %s", session.GetPartner()) + "Negotation Trigger with %s", session.GetPartner()) } //send session - m := message.Message{ + m := message.Send{ Recipient: session.GetPartner(), Payload: payload, - MessageType: int32(cmixproto.Type_REKEY_TRIGGER), + MessageType: message.KeyExchangeTrigger, } //send the message under the key exchange @@ -108,23 +107,18 @@ func negotiate(ctx *context.Context, session *e2e.Session) error { states.COMPLETED, states.FAILED) } - //Start the thread which will handle the outcome of the send - go trackNegotiationResult(sendResults, len(rounds), session) -} - -func trackNegotiationResult(resultsCh chan ds.EventReturn, numResults int, session *e2e.Session) { - success, numTimeOut, numRoundFail := utility.TrackResults(resultsCh, numResults) + //Wait until the result tracking responds + success, numTimeOut, numRoundFail := utility.TrackResults(sendResults, len(rounds)) // If a single partition of the Key Negotiation request does not // transmit, the partner cannot read the result. Log the error and set // the session as unconfirmed so it will re-trigger the negotiation if !success { - jww.ERROR.Printf("Key Negotiation for %s failed to "+ + session.SetNegotiationStatus(e2e.Unconfirmed) + return errors.Errorf("Key Negotiation for %s failed to "+ "transmit %v/%v paritions: %v round failures, %v timeouts", - session, numRoundFail+numTimeOut, numResults, numRoundFail, + session, numRoundFail+numTimeOut, len(rounds), numRoundFail, numTimeOut) - session.SetNegotiationStatus(e2e.Unconfirmed) - return } // otherwise, the transmission is a success and this should be denoted @@ -132,5 +126,8 @@ func trackNegotiationResult(resultsCh chan ds.EventReturn, numResults int, sessi jww.INFO.Printf("Key Negotiation transmission for %s sucesfull", session) session.SetNegotiationStatus(e2e.Sent) + + return nil } + diff --git a/io/keyExchange/trigger.go b/io/keyExchange/trigger.go new file mode 100644 index 0000000000000000000000000000000000000000..06dd4a682d240aef49e00f899a0f7098710e3219 --- /dev/null +++ b/io/keyExchange/trigger.go @@ -0,0 +1,110 @@ +package keyExchange + +import ( + "github.com/golang/protobuf/proto" + "github.com/pkg/errors" + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/client/context" + "gitlab.com/elixxir/client/context/message" + "gitlab.com/elixxir/client/context/params" + "gitlab.com/elixxir/client/storage/e2e" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/xx_network/primitives/id" +) + +func handleTrigger(ctx *context.Context, request message.Receive) { + //ensure the message was encrypted properly + if request.Encryption != message.E2E { + jww.ERROR.Printf("Received non-e2e encrypted Key Exchange "+ + "Trigger from partner %s", request.Sender) + return + } + + //Get the partner + partner, err := ctx.Session.E2e().GetPartner(request.Sender) + if err != nil { + jww.ERROR.Printf("Received Key Exchange Trigger with unknown "+ + "partner %s", request.Sender) + return + } + + //unmarshal the message + oldSessionID, PartnerPublicKey, err := unmarshalKeyExchangeTrigger( + ctx.Session.E2e().GetGroup(), request.Payload) + if err != nil { + jww.ERROR.Printf("Failed to unmarshal Key Exchange Trigger with "+ + "partner %s: %s", request.Sender, err) + return + } + + //get the old session which triggered the exchange + oldSession := partner.GetSendSession(oldSessionID) + if oldSession == nil { + jww.ERROR.Printf("Failed to find parent session %s for Key "+ + "Exchange Trigger from partner %s: %s", oldSession, request.Sender, + err) + return + } + + //create the new session + newSession, duplicate := partner.NewReceiveSession(PartnerPublicKey, + e2e.GetDefaultSessionParams(), oldSession) + // new session being nil means the session was a duplicate. This is possible + // in edge cases where the partner crashes during operation. The session + // creation in this case ignores the new session, but the confirmation + // message is still sent so the partner will know the session is confirmed + if duplicate { + jww.INFO.Printf("New session from Key Exchange Trigger to "+ + "create session %s for partner %s is a duplicate, request ignored", + newSession.GetID(), request.Sender) + } + + //Send the Confirmation Message + //build the payload + payload, err := proto.Marshal(&RekeyConfirm{ + SessionID: newSession.GetTrigger().Marshal(), + }) + + //If the payload cannot be marshaled, panic + if err != nil { + jww.FATAL.Printf("Failed to marshal payload for Key "+ + "Negotation Confirmation with %s", newSession.GetPartner()) + } + + //build the message + m := message.Send{ + Recipient: newSession.GetPartner(), + Payload: payload, + MessageType: message.KeyExchangeConfirm, + } + + //send the message under the key exchange + e2eParams := params.GetDefaultE2E() + cmixParams := params.GetDefaultCMIX() + + rounds, err := ctx.Manager.SendE2E(m, e2eParams, cmixParams) + +} + +func unmarshalKeyExchangeTrigger(grp *cyclic.Group, payload []byte) (e2e.SessionID, + *cyclic.Int, error) { + + msg := &RekeyTrigger{} + if err := proto.Unmarshal(payload, msg); err != nil { + return e2e.SessionID{}, nil, errors.Errorf("Failed to "+ + "unmarshal payload: %s", err) + } + + oldSessionID := e2e.SessionID{} + if err := oldSessionID.Unmarshal(msg.SessionID); err != nil { + return e2e.SessionID{}, nil, errors.Errorf("Failed to unmarshal"+ + " sessionID: %s", err) + } + + if !grp.BytesInside(msg.PublicKey) { + return e2e.SessionID{}, nil, errors.Errorf("Public key not in e2e group; PublicKey %v", + msg.PublicKey) + } + + return oldSessionID, grp.NewIntFromBytes(msg.PublicKey), nil +} diff --git a/io/keyExchange/xchange.pb.go b/io/keyExchange/xchange.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..c7923f4730105260369b5a07635df6b6edae9d2d --- /dev/null +++ b/io/keyExchange/xchange.pb.go @@ -0,0 +1,231 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2018 Privategrity Corporation / +// / +// All rights reserved. / +//////////////////////////////////////////////////////////////////////////////// + +// Call ./generate.sh to generate the protocol buffer code + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.25.0 +// protoc (unknown) +// source: xchange.proto + +package keyExchange + +import ( + proto "github.com/golang/protobuf/proto" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 + +type RekeyTrigger struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // PublicKey used in the registration + PublicKey []byte `protobuf:"bytes,1,opt,name=publicKey,proto3" json:"publicKey,omitempty"` + // ID of the session used to create this session + SessionID []byte `protobuf:"bytes,2,opt,name=sessionID,proto3" json:"sessionID,omitempty"` +} + +func (x *RekeyTrigger) Reset() { + *x = RekeyTrigger{} + if protoimpl.UnsafeEnabled { + mi := &file_xchange_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RekeyTrigger) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RekeyTrigger) ProtoMessage() {} + +func (x *RekeyTrigger) ProtoReflect() protoreflect.Message { + mi := &file_xchange_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 RekeyTrigger.ProtoReflect.Descriptor instead. +func (*RekeyTrigger) Descriptor() ([]byte, []int) { + return file_xchange_proto_rawDescGZIP(), []int{0} +} + +func (x *RekeyTrigger) GetPublicKey() []byte { + if x != nil { + return x.PublicKey + } + return nil +} + +func (x *RekeyTrigger) GetSessionID() []byte { + if x != nil { + return x.SessionID + } + return nil +} + +type RekeyConfirm struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // ID of the session created + SessionID []byte `protobuf:"bytes,1,opt,name=sessionID,proto3" json:"sessionID,omitempty"` +} + +func (x *RekeyConfirm) Reset() { + *x = RekeyConfirm{} + if protoimpl.UnsafeEnabled { + mi := &file_xchange_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RekeyConfirm) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RekeyConfirm) ProtoMessage() {} + +func (x *RekeyConfirm) ProtoReflect() protoreflect.Message { + mi := &file_xchange_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 RekeyConfirm.ProtoReflect.Descriptor instead. +func (*RekeyConfirm) Descriptor() ([]byte, []int) { + return file_xchange_proto_rawDescGZIP(), []int{1} +} + +func (x *RekeyConfirm) GetSessionID() []byte { + if x != nil { + return x.SessionID + } + return nil +} + +var File_xchange_proto protoreflect.FileDescriptor + +var file_xchange_proto_rawDesc = []byte{ + 0x0a, 0x0d, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, 0x22, 0x4a, 0x0a, 0x0c, 0x52, 0x65, 0x6b, 0x65, 0x79, 0x54, + 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x4b, 0x65, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, + 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x49, 0x44, 0x22, 0x2c, 0x0a, 0x0c, 0x52, 0x65, 0x6b, 0x65, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x72, 0x6d, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x44, + 0x42, 0x0d, 0x5a, 0x0b, 0x6b, 0x65, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_xchange_proto_rawDescOnce sync.Once + file_xchange_proto_rawDescData = file_xchange_proto_rawDesc +) + +func file_xchange_proto_rawDescGZIP() []byte { + file_xchange_proto_rawDescOnce.Do(func() { + file_xchange_proto_rawDescData = protoimpl.X.CompressGZIP(file_xchange_proto_rawDescData) + }) + return file_xchange_proto_rawDescData +} + +var file_xchange_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_xchange_proto_goTypes = []interface{}{ + (*RekeyTrigger)(nil), // 0: parse.RekeyTrigger + (*RekeyConfirm)(nil), // 1: parse.RekeyConfirm +} +var file_xchange_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_xchange_proto_init() } +func file_xchange_proto_init() { + if File_xchange_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_xchange_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RekeyTrigger); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_xchange_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RekeyConfirm); 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_xchange_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_xchange_proto_goTypes, + DependencyIndexes: file_xchange_proto_depIdxs, + MessageInfos: file_xchange_proto_msgTypes, + }.Build() + File_xchange_proto = out.File + file_xchange_proto_rawDesc = nil + file_xchange_proto_goTypes = nil + file_xchange_proto_depIdxs = nil +} diff --git a/io/keyExchange/xchange.proto b/io/keyExchange/xchange.proto new file mode 100644 index 0000000000000000000000000000000000000000..76dac5e960f539e95d56a56624c6f7eaff34cd5d --- /dev/null +++ b/io/keyExchange/xchange.proto @@ -0,0 +1,24 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2018 Privategrity Corporation / +// / +// All rights reserved. / +//////////////////////////////////////////////////////////////////////////////// + +// Call ./generate.sh to generate the protocol buffer code + +syntax = "proto3"; + +package parse; +option go_package = "keyExchange"; + +message RekeyTrigger { + // PublicKey used in the registration + bytes publicKey = 1; + // ID of the session used to create this session + bytes sessionID = 2; +} + +message RekeyConfirm { + // ID of the session created + bytes sessionID = 1; +} diff --git a/io/receive.go b/io/receive.go index fcfa34d229f8573cf97061927670fce7e58b3ade..af0d66fb53c84f81386965a58247d3b3d192ccc0 100644 --- a/io/receive.go +++ b/io/receive.go @@ -9,9 +9,9 @@ package io import ( "fmt" "github.com/pkg/errors" - "gitlab.com/elixxir/client/cmixproto" "gitlab.com/elixxir/client/crypto" "gitlab.com/elixxir/client/globals" + "gitlab.com/elixxir/client/io/keyExchange" "gitlab.com/elixxir/client/parse" "gitlab.com/elixxir/client/storage" "gitlab.com/elixxir/client/user" @@ -176,7 +176,7 @@ func handleE2EReceiving(session user.Session, switchb *switchboard.Switchboard, rekeyMsg := &parse.Message{ Sender: partner, TypedBody: parse.TypedBody{ - MessageType: int32(cmixproto.Type_NO_TYPE), + MessageType: int32(keyExchange.Type_NO_TYPE), Body: partnerPubKey, }, InferredType: parse.Rekey, diff --git a/io/send.go b/io/send.go index 2a43bd387a091de5b0630cef7cdb2ef63ac4a278..80aa9d19a473c2aa469816a4ff4075ac9da97197 100644 --- a/io/send.go +++ b/io/send.go @@ -9,9 +9,9 @@ package io import ( "fmt" "github.com/pkg/errors" - "gitlab.com/elixxir/client/cmixproto" "gitlab.com/elixxir/client/crypto" "gitlab.com/elixxir/client/globals" + "gitlab.com/elixxir/client/io/keyExchange" "gitlab.com/elixxir/client/keyStore" "gitlab.com/elixxir/client/parse" "gitlab.com/elixxir/client/user" @@ -264,7 +264,7 @@ func handleE2ESending(session user.Session, switchb *switchboard.Switchboard, rekeyMsg := &parse.Message{ Sender: userData.ThisUser.User, TypedBody: parse.TypedBody{ - MessageType: int32(cmixproto.Type_REKEY_TRIGGER), + MessageType: int32(keyExchange.Type_REKEY_TRIGGER), Body: []byte{}, }, InferredType: parse.None, diff --git a/keyStore/action.go b/keyStore/action.go deleted file mode 100644 index 52cdf7e389bf22821c4ca8ba6981f0cee82c7987..0000000000000000000000000000000000000000 --- a/keyStore/action.go +++ /dev/null @@ -1,25 +0,0 @@ -package keyStore - -type Action uint8 - -const ( - None Action = iota - Rekey - Purge - Deleted -) - -func (a Action) String() string { - var ret string - switch a { - case None: - ret = "None" - case Rekey: - ret = "Rekey" - case Purge: - ret = "Purge" - case Deleted: - ret = "Deleted" - } - return ret -} diff --git a/keyStore/action_test.go b/keyStore/action_test.go deleted file mode 100644 index 76463d97e5281e3654b4f5be8834a045e5bec2fa..0000000000000000000000000000000000000000 --- a/keyStore/action_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package keyStore - -import "testing" - -// Test all outputs of String for coverage -func TestAction_String(t *testing.T) { - expectedStr := "None" - action := None - if action.String() != expectedStr { - t.Errorf("String returned %s, expected %s", - action.String(), - expectedStr) - } - - expectedStr = "Rekey" - action = Rekey - if action.String() != expectedStr { - t.Errorf("String returned %s, expected %s", - action.String(), - expectedStr) - } - - expectedStr = "Purge" - action = Purge - if action.String() != expectedStr { - t.Errorf("String returned %s, expected %s", - action.String(), - expectedStr) - } - - expectedStr = "Deleted" - action = Deleted - if action.String() != expectedStr { - t.Errorf("String returned %s, expected %s", - action.String(), - expectedStr) - } -} diff --git a/keyStore/e2eKey.go b/keyStore/e2eKey.go deleted file mode 100644 index 3c6226477ab86bd3bc34d30786eea663abe00e29..0000000000000000000000000000000000000000 --- a/keyStore/e2eKey.go +++ /dev/null @@ -1,50 +0,0 @@ -package keyStore - -import ( - "gitlab.com/elixxir/client/parse" - "gitlab.com/elixxir/crypto/cyclic" - "gitlab.com/elixxir/crypto/hash" - "gitlab.com/elixxir/primitives/format" -) - -type E2EKey struct { - // Link to Manager - manager *KeyManager - - // Key to be used - key *cyclic.Int - - // Designation of crypto type - outer parse.CryptoType - - // keyNum is needed by Key Manager - // to keep track of which receiving keys - // have been used - keyNum uint32 -} - -// Get key manager -func (e2ekey *E2EKey) GetManager() *KeyManager { - return e2ekey.manager -} - -// Get key value (cyclic.Int) -func (e2ekey *E2EKey) GetKey() *cyclic.Int { - return e2ekey.key -} - -// Get key type, E2E or Rekey -func (e2ekey *E2EKey) GetOuterType() parse.CryptoType { - return e2ekey.outer -} - -// Generate key fingerprint -// NOTE: This function is not a getter, -// it returns a new byte array on each call -func (e2ekey *E2EKey) KeyFingerprint() format.Fingerprint { - h, _ := hash.NewCMixHash() - h.Write(e2ekey.key.Bytes()) - fp := format.Fingerprint{} - copy(fp[:], h.Sum(nil)) - return fp -} diff --git a/keyStore/e2eKey_test.go b/keyStore/e2eKey_test.go deleted file mode 100644 index 19fa0b083b1a321eebe8428d56915d5913b883c7..0000000000000000000000000000000000000000 --- a/keyStore/e2eKey_test.go +++ /dev/null @@ -1,23 +0,0 @@ -package keyStore - -import ( - "bytes" - "encoding/hex" - "testing" -) - -// Test key fingerprint for consistency -func TestE2EKey_KeyFingerprint(t *testing.T) { - grp := initGroup() - key := new(E2EKey) - key.key = grp.NewInt(42) - keyFP := key.KeyFingerprint() - expectedFP, _ := hex.DecodeString( - "395a122eb1402bf256d86e3fa44764cf" + - "9acc559017a00b2b9ee12498e73ef2b5") - - if !bytes.Equal(keyFP[:], expectedFP) { - t.Errorf("Key Fingerprint value is wrong. Expected %x"+ - ", got %x", expectedFP, keyFP[:]) - } -} diff --git a/keyStore/keyManager.go b/keyStore/keyManager.go deleted file mode 100644 index 92ce0f9fba6249bb73420c3ef824ea721faab53e..0000000000000000000000000000000000000000 --- a/keyStore/keyManager.go +++ /dev/null @@ -1,592 +0,0 @@ -package keyStore - -import ( - "bytes" - "encoding/binary" - "encoding/gob" - "gitlab.com/elixxir/client/globals" - "gitlab.com/elixxir/client/parse" - "gitlab.com/elixxir/crypto/cyclic" - //"gitlab.com/elixxir/crypto/e2e" - "gitlab.com/elixxir/primitives/format" - "gitlab.com/xx_network/primitives/id" - "sync/atomic" -) - -// The KeyManager keeps track of all keys used in a single E2E -// uni-directional relationship between the user and a partner -// It tracks usage of send Keys and ReKeys in an atomic sendState -// OR -// It tracks usage of receiving Keys and ReKeys in lists of -// atomic "dirty bit" states -// It also owns the send Keys and ReKeys stacks of keys -// OR lists of receiving Keys and ReKeys fingerprints -// All Key Managers can be stored in the session object, and -// can be GOB encoded/decoded, preserving the state -// When the GOB Decode is successful, GenerateKeys can be called -// on the KeyManager to generate all keys that have not been used -type KeyManager struct { - // Underlying key - baseKey *cyclic.Int - // Own Private Key - privKey *cyclic.Int - // Partner Public Key - pubKey *cyclic.Int - - // Designates end-to-end partner - partner *id.ID - - // True if key manager tracks send keys, false if receive keys - sendOrRecv bool - - // State of Sending Keys and Rekeys, formatted as follows: - // Bits - // | 63 | 62 - 56 | 55 - 40 | 39 - 32 | 31 - 0 | - // | deleted | empty | rekey count | empty | key count | - // | 1 bit | 7 bits | 16 bits | 8 bits | 32 bits | - sendState *uint64 - - // Value of the counter at which a rekey is triggered - ttl uint16 - - // Total number of Keys - numKeys uint32 - // Total number of Rekey keys - numReKeys uint16 - - // Received Keys dirty bits - // Each bit represents a single Receiving Key - recvKeysState [numStates]*uint64 - // Received ReKeys dirty bits - // Each bit represents a single Receiving ReKey - recvReKeysState [numReStates]*uint64 - - // Send Keys Stack - sendKeys *KeyStack - // Send ReKeys Stack - sendReKeys *KeyStack - // Receive Keys fingerprint list - recvKeysFingerprint []format.Fingerprint - // Receive ReKeys fingerprint list - recvReKeysFingerprint []format.Fingerprint -} - -// Creates a new KeyManager to manage E2E Keys between user and partner -// Receives the baseKey, privKey, pubKey, partner userID, numKeys, ttl and numReKeys -// All internal states are forced to 0 for safety purposes -func NewManager(baseKey *cyclic.Int, - privKey *cyclic.Int, pubKey *cyclic.Int, - partner *id.ID, sendOrRecv bool, - numKeys uint32, ttl uint16, numReKeys uint16) *KeyManager { - - km := new(KeyManager) - km.baseKey = baseKey - km.privKey = privKey - km.pubKey = pubKey - km.partner = partner - km.sendOrRecv = sendOrRecv - km.sendState = new(uint64) - *km.sendState = 0 - km.ttl = ttl - km.numKeys = numKeys - km.numReKeys = numReKeys - for i := range km.recvKeysState { - km.recvKeysState[i] = new(uint64) - *km.recvKeysState[i] = 0 - } - for i := range km.recvReKeysState { - km.recvReKeysState[i] = new(uint64) - *km.recvReKeysState[i] = 0 - } - return km -} - -// Get the base key from the Key Manager -func (km *KeyManager) GetBaseKey() *cyclic.Int { - return km.baseKey -} - -// Get the private key from the Key Manager -func (km *KeyManager) GetPrivKey() *cyclic.Int { - return km.privKey -} - -// Get the public key from the Key Manager -func (km *KeyManager) GetPubKey() *cyclic.Int { - return km.pubKey -} - -// Get the partner ID from the Key Manager -func (km *KeyManager) GetPartner() *id.ID { - return km.partner -} - -// Constants needed for access to sendState -// Bits -// | 63 | 62 - 56 | 55 - 40 | 39 - 32 | 31 - 0 | -// | deleted | empty | rekey count | empty | key count | -// | 1 bit | 7 bits | 16 bits | 8 bits | 32 bits | -const ( - // Delete is most significant bit - stateDeleteMask uint64 = 0x8000000000000000 - // Key Counter is lowest 32 bits - stateKeyMask uint64 = 0x00000000FFFFFFFF - // ReKey Counter is bits 55 to 40 (0 indexed) - stateReKeyMask uint64 = 0x00FFFF0000000000 - // ReKey Counter shift value is 40 - stateReKeyShift uint64 = 40 - // Delete Increment is 1 shifted by 63 bits - stateDeleteIncr uint64 = 1 << 63 - // Key Counter increment is 1 - stateKeyIncr uint64 = 1 - // ReKey Counter increment is 1 << 40 - stateReKeyIncr uint64 = 1 << stateReKeyShift -) - -// Check if a Rekey should be triggered -// Extract the Key counter from state and then -// compare to passed val -func checkRekey(state uint64, val uint16) bool { - keyCounter := uint32(state & stateKeyMask) - return keyCounter >= uint32(val) -} - -// Check if a Purge should be triggered -// Extract the ReKey counter from state and then -// compare to passed val -func checkPurge(state uint64, val uint16) bool { - reKeyCounter := uint16((state & stateReKeyMask) >> stateReKeyShift) - return reKeyCounter >= val -} - -// UpdateState atomically updates internal state -// of key manager for send Keys or ReKeys -// Once the number of used keys reaches the TTL value -// a Rekey Action is returned -// Once the number of used ReKeys reaches the the NumReKeys -// value, a Purge Action is returned, and the Key Manager -// can be destroyed -// When a Purge is returned, the state topmost bit is set, -// indicating that the KeyManager is now Deleted -// This means that if the caller doesn't destroy it -// right away, any further send Keys obtained from the -// global key map will have the action set to Deleted -// which can be used to trigger an error -func (km *KeyManager) updateState(rekey bool) Action { - var stateIncr uint64 - // Choose the correct increment according to key type - if rekey { - stateIncr = stateReKeyIncr - } else { - stateIncr = stateKeyIncr - } - - // Atomically increment the state and save result - result := atomic.AddUint64(km.sendState, stateIncr) - - // Check if KeyManager is in Deleted state - if result&stateDeleteMask != 0 { - return Deleted - } - - // Check if result should trigger a Purge - if rekey && checkPurge(result, km.numReKeys) { - // set delete bit - atomic.AddUint64(km.sendState, stateDeleteIncr) - return Purge - // Check if result should trigger a Rekey - } else if !rekey && checkRekey(result, km.ttl) { - return Rekey - } - return None -} - -// UpdateRecvState atomically updates internal -// receiving state of key manager -// It sets the correct bit of state index based on keyNum -// and rekey -// The keyNum is used to select the correct state from the array -// Since each state is an uint64, keyNum / 64 determines the index -// and keyNum % 64 determines the bit that needs to be set -// Rekey is used to select which state array to update: -// recvReKeysState or recvKeysState -// The state is atomically updated by adding a value of 1 shifted -// to the determined bit -func (km *KeyManager) updateRecvState(rekey bool, keyNum uint32) { - stateIdx := keyNum / 64 - stateBit := uint64(1 << (keyNum % 64)) - - if rekey { - atomic.AddUint64(km.recvReKeysState[stateIdx], stateBit) - } else { - atomic.AddUint64(km.recvKeysState[stateIdx], stateBit) - } -} - -// Return true if bit specified by keyNum is set, meaning -// that a particular key or reKey has been used -// The keyNum is used to select the correct state from the array -// Since each state is an uint64, keyNum / 64 determines the index -// and keyNum % 64 determines the bit that needs to be read -// Rekey is used to select which state array to update: -// recvReKeysState or recvKeysState -// The state is atomically loaded and then the bit mask is applied -// to check if the value is 0 or different -func (km *KeyManager) checkRecvStateBit(rekey bool, keyNum uint32) bool { - stateIdx := keyNum / 64 - stateBit := uint64(1 << (keyNum % 64)) - - var state uint64 - if rekey { - state = atomic.LoadUint64(km.recvReKeysState[stateIdx]) - } else { - state = atomic.LoadUint64(km.recvKeysState[stateIdx]) - } - - return (state & stateBit) != 0 -} - -// GenerateKeys will generate all previously unused keys based on -// KeyManager states -// Sending Keys and ReKeys are generated and then pushed to a stack, -// meaning that they are used in a LIFO manner. -// This makes it easier to generate all send keys from a pre-existing state -// as the number of unused keys will be simply numKeys - usedKeys -// where usedKeys is extracted from the KeyManager state -// Receiving Keys and ReKeys are generated in order, but there is no -// guarantee that they will be used in order, this is why KeyManager -// keeps a list of fingerprint for all receiving keys -// When generating receiving keys from pre-existing state, all bits -// from receiving states are checked, and if the bit is set ("dirty") -// the key is not added to the Reception Keys map and fingerprint list -// This way, this function can be used to generate all keys when a new -// E2E relationship is established, and also to generate all previously -// unused keys based on KeyManager state, when reloading an user session -// The function returns modifications that need to be independently made to the keystore. -func (km *KeyManager) GenerateKeys(grp *cyclic.Group, userID *id.ID) []*E2EKey { - var recE2EKeys []*E2EKey - - if km.sendOrRecv { - // Calculate how many unused send keys are needed - usedSendKeys := uint32(*km.sendState & stateKeyMask) - numGenSendKeys := uint(km.numKeys - usedSendKeys) - usedSendReKeys := uint16((*km.sendState & stateReKeyMask) >> stateReKeyShift) - numGenSendReKeys := uint(km.numReKeys - usedSendReKeys) - - // Generate numGenSendKeys send keys - sendKeys := make([]*cyclic.Int, numGenSendKeys) - // Generate numGenSendReKeys send reKeys - sendReKeys := make([]*cyclic.Int, numGenSendReKeys) - - // Create Send Keys Stack on keyManager - km.sendKeys = NewKeyStack() - - // Create send E2E Keys and add to stack - for _, key := range sendKeys { - e2ekey := new(E2EKey) - e2ekey.key = key - e2ekey.manager = km - e2ekey.outer = parse.E2E - km.sendKeys.Push(e2ekey) - } - - // Create Send ReKeys Stack on keyManager - km.sendReKeys = NewKeyStack() - - // Create send E2E ReKeys and add to stack - for _, key := range sendReKeys { - e2ekey := new(E2EKey) - e2ekey.key = key - e2ekey.manager = km - e2ekey.outer = parse.Rekey - km.sendReKeys.Push(e2ekey) - } - - } else { - // For receiving keys, generate all, and then only add to the map - // the unused ones based on recvStates - // Generate numKeys recv keys - recvKeys := make([]*cyclic.Int, 5) - // Generate numReKeys recv reKeys - recvReKeys := make([]*cyclic.Int, 5) - - // Create Receive E2E Keys and put them into the E2eKeys obbj to return into the parent - // Skip keys that were already used as per recvStates - km.recvKeysFingerprint = make([]format.Fingerprint, 0) - for i, key := range recvKeys { - if !km.checkRecvStateBit(false, uint32(i)) { - e2ekey := new(E2EKey) - e2ekey.key = key - e2ekey.manager = km - e2ekey.outer = parse.E2E - e2ekey.keyNum = uint32(i) - recE2EKeys = append(recE2EKeys, e2ekey) - keyFP := e2ekey.KeyFingerprint() - km.recvKeysFingerprint = append(km.recvKeysFingerprint, keyFP) - } - } - - // Create Receive E2E reKeys and add them into the E2ERekeys variable to return back to parent - // while keeping a list of the fingerprints - km.recvReKeysFingerprint = make([]format.Fingerprint, 0) - for i, key := range recvReKeys { - if !km.checkRecvStateBit(true, uint32(i)) { - e2ekey := new(E2EKey) - e2ekey.key = key - e2ekey.manager = km - e2ekey.outer = parse.Rekey - e2ekey.keyNum = uint32(i) - recE2EKeys = append(recE2EKeys, e2ekey) - keyFP := e2ekey.KeyFingerprint() - km.recvReKeysFingerprint = append(km.recvReKeysFingerprint, keyFP) - } - } - } - return recE2EKeys -} - -// Pops first key from Send KeyStack of KeyManager -// Atomically updates Key Manager Sending state -// Returns *E2EKey and KeyAction -func (km *KeyManager) PopKey() (*E2EKey, Action) { - // Pop key - e2eKey := km.sendKeys.Pop() - // Update Key Manager State - action := km.updateState(false) - return e2eKey, action -} - -// Pops first rekey from Send ReKeyStack of KeyManager -// Atomically updates Key Manager Sending state -// Returns *E2EKey and KeyAction -func (km *KeyManager) PopRekey() (*E2EKey, Action) { - // Pop key - e2eKey := km.sendReKeys.Pop() - // Update Key Manager State - action := km.updateState(true) - return e2eKey, action -} - -// If the KeyManager is a sending one, destroy -// will remove it from KeyStore map and then destroy it's key stacks -// If it is a receiving one, destroy will remove it -// from KeyStore map and then remove all keys from receiving key -// map -func (km *KeyManager) Destroy(ks *KeyStore) { - if km.sendOrRecv { - // Remove KeyManager from KeyStore - ks.DeleteSendManager(km.partner) - // Delete KeyStacks - km.sendKeys.Delete() - km.sendReKeys.Delete() - } else { - globals.Log.WARN.Println("This function no longer handles deleting of reception keys.") - } - - // Hopefully when the function returns there - // will be no keys referencing this Manager left, - // so it will be garbage collected -} - -// GobEncode the KeyManager so that it can be saved in -// the session file -func (km *KeyManager) GobEncode() ([]byte, error) { - // Anonymous structure that flattens nested structures - s := struct { - Partner []byte - SendOrRecv []byte - State []byte - TTL []byte - NumKeys []byte - NumReKeys []byte - RecvKeyState []byte - RecvReKeyState []byte - BaseKey []byte - PrivKey []byte - PubKey []byte - }{ - km.partner.Bytes(), - make([]byte, 1), - make([]byte, 8), - make([]byte, 2), - make([]byte, 4), - make([]byte, 2), - make([]byte, 8*numStates), - make([]byte, 8*numReStates), - make([]byte, 0), - make([]byte, 0), - make([]byte, 0), - } - - // Set send or receive - if km.sendOrRecv { - s.SendOrRecv[0] = 0xFF - } else { - s.SendOrRecv[0] = 0x00 - } - - // Convert all internal uints to bytes - binary.BigEndian.PutUint64(s.State, *km.sendState) - binary.BigEndian.PutUint16(s.TTL, km.ttl) - binary.BigEndian.PutUint32(s.NumKeys, km.numKeys) - binary.BigEndian.PutUint16(s.NumReKeys, km.numReKeys) - for i := 0; i < int(numStates); i++ { - binary.BigEndian.PutUint64( - s.RecvKeyState[i*8:(i+1)*8], - *km.recvKeysState[i]) - } - for i := 0; i < int(numReStates); i++ { - binary.BigEndian.PutUint64( - s.RecvReKeyState[i*8:(i+1)*8], - *km.recvReKeysState[i]) - } - - // GobEncode baseKey - keyBytes, err := km.baseKey.GobEncode() - - if err != nil { - return nil, err - } - - // Add baseKey to struct - s.BaseKey = append(s.BaseKey, keyBytes...) - - // GobEncode privKey - if km.privKey != nil { - keyBytes, err = km.privKey.GobEncode() - - if err != nil { - return nil, err - } - } - - // Add privKey to struct - s.PrivKey = append(s.BaseKey, keyBytes...) - - // GobEncode pubKey - if km.pubKey != nil { - keyBytes, err = km.pubKey.GobEncode() - - if err != nil { - return nil, err - } - } - - // Add pubKey to struct - s.PubKey = append(s.BaseKey, keyBytes...) - - var buf bytes.Buffer - - // Create new encoder that will transmit the buffer - enc := gob.NewEncoder(&buf) - - // Transmit the data - err = enc.Encode(s) - - if err != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -// GobDecode bytes into a new Key Manager -// It can be used to get Key Managers from the -// store session file -// GenerateKeys should then be run so that all -// key maps are restored properly -func (km *KeyManager) GobDecode(in []byte) error { - // Anonymous structure that flattens nested structures - s := struct { - Partner []byte - SendOrRecv []byte - State []byte - TTL []byte - NumKeys []byte - NumReKeys []byte - RecvKeyState []byte - RecvReKeyState []byte - BaseKey []byte - PrivKey []byte - PubKey []byte - }{ - make([]byte, 32), - make([]byte, 1), - make([]byte, 8), - make([]byte, 2), - make([]byte, 4), - make([]byte, 2), - make([]byte, 8*numStates), - make([]byte, 8*numReStates), - []byte{}, - []byte{}, - []byte{}, - } - - var buf bytes.Buffer - - // Write bytes to the buffer - buf.Write(in) - - // Create new decoder that reads from the buffer - dec := gob.NewDecoder(&buf) - - // Receive and decode data - err := dec.Decode(&s) - - if err != nil { - return err - } - - partner, err := id.Unmarshal(s.Partner) - if err != nil { - return err - } - km.partner = partner - - // Convert decoded bytes and put into key manager structure - km.baseKey = new(cyclic.Int) - err = km.baseKey.GobDecode(s.BaseKey) - - if err != nil { - return err - } - - km.privKey = new(cyclic.Int) - err = km.privKey.GobDecode(s.PrivKey) - - if err != nil { - return err - } - - km.pubKey = new(cyclic.Int) - err = km.pubKey.GobDecode(s.PubKey) - - if err != nil { - return err - } - - if s.SendOrRecv[0] == 0xFF { - km.sendOrRecv = true - } else { - km.sendOrRecv = false - } - - km.sendState = new(uint64) - *km.sendState = binary.BigEndian.Uint64(s.State) - km.ttl = binary.BigEndian.Uint16(s.TTL) - km.numKeys = binary.BigEndian.Uint32(s.NumKeys) - km.numReKeys = binary.BigEndian.Uint16(s.NumReKeys) - for i := 0; i < int(numStates); i++ { - km.recvKeysState[i] = new(uint64) - *km.recvKeysState[i] = binary.BigEndian.Uint64( - s.RecvKeyState[i*8 : (i+1)*8]) - } - for i := 0; i < int(numReStates); i++ { - km.recvReKeysState[i] = new(uint64) - *km.recvReKeysState[i] = binary.BigEndian.Uint64( - s.RecvReKeyState[i*8 : (i+1)*8]) - } - - return nil -} diff --git a/keyStore/keyManager_test.go b/keyStore/keyManager_test.go deleted file mode 100644 index 1163b9185333e980f035c7b4497f2bcf7a5cfe9c..0000000000000000000000000000000000000000 --- a/keyStore/keyManager_test.go +++ /dev/null @@ -1,671 +0,0 @@ -package keyStore - -import ( - "bytes" - "encoding/base64" - "encoding/gob" - "github.com/pkg/errors" - "gitlab.com/elixxir/crypto/cyclic" - "gitlab.com/elixxir/crypto/e2e" - "gitlab.com/elixxir/crypto/large" - "gitlab.com/xx_network/primitives/id" - "testing" -) - -// initGroup sets up the cryptographic constants for cMix -func initGroup() *cyclic.Group { - - base := 16 - - pString := "9DB6FB5951B66BB6FE1E140F1D2CE5502374161FD6538DF1648218642F0B5C48" + - "C8F7A41AADFA187324B87674FA1822B00F1ECF8136943D7C55757264E5A1A44F" + - "FE012E9936E00C1D3E9310B01C7D179805D3058B2A9F4BB6F9716BFE6117C6B5" + - "B3CC4D9BE341104AD4A80AD6C94E005F4B993E14F091EB51743BF33050C38DE2" + - "35567E1B34C3D6A5C0CEAA1A0F368213C3D19843D0B4B09DCB9FC72D39C8DE41" + - "F1BF14D4BB4563CA28371621CAD3324B6A2D392145BEBFAC748805236F5CA2FE" + - "92B871CD8F9C36D3292B5509CA8CAA77A2ADFC7BFD77DDA6F71125A7456FEA15" + - "3E433256A2261C6A06ED3693797E7995FAD5AABBCFBE3EDA2741E375404AE25B" - - gString := "5C7FF6B06F8F143FE8288433493E4769C4D988ACE5BE25A0E24809670716C613" + - "D7B0CEE6932F8FAA7C44D2CB24523DA53FBE4F6EC3595892D1AA58C4328A06C4" + - "6A15662E7EAA703A1DECF8BBB2D05DBE2EB956C142A338661D10461C0D135472" + - "085057F3494309FFA73C611F78B32ADBB5740C361C9F35BE90997DB2014E2EF5" + - "AA61782F52ABEB8BD6432C4DD097BC5423B285DAFB60DC364E8161F4A2A35ACA" + - "3A10B1C4D203CC76A470A33AFDCBDD92959859ABD8B56E1725252D78EAC66E71" + - "BA9AE3F1DD2487199874393CD4D832186800654760E1E34C09E4D155179F9EC0" + - "DC4473F996BDCE6EED1CABED8B6F116F7AD9CF505DF0F998E34AB27514B0FFE7" - - p := large.NewIntFromString(pString, base) - g := large.NewIntFromString(gString, base) - - grp := cyclic.NewGroup(p, g) - - return grp -} - -// Test creation of KeyManager -func TestKeyManager_New(t *testing.T) { - grp := cyclic.NewGroup(large.NewInt(107), large.NewInt(2)) - baseKey := grp.NewInt(57) - partner := id.NewIdFromUInt(14, id.User, t) - - km := NewManager(baseKey, nil, nil, - partner, true, 12, 10, 10) - - if km == nil { - t.Errorf("NewManager returned nil") - } -} - -// Test KeyManager base key getter -func TestKeyManager_GetBaseKey(t *testing.T) { - grp := cyclic.NewGroup(large.NewInt(107), large.NewInt(2)) - baseKey := grp.NewInt(57) - privKey := grp.NewInt(5) - pubKey := grp.NewInt(42) - partner := id.NewIdFromUInt(14, id.User, t) - - km := NewManager(baseKey, privKey, pubKey, - partner, true, 12, 10, 10) - - result := km.GetBaseKey() - - if result.Cmp(baseKey) != 0 { - t.Errorf("GetBaseKey returned wrong value, "+ - "expected: %s, got: %s", - privKey.Text(10), result.Text(10)) - } -} - -// Test KeyManager private key getter -func TestKeyManager_GetPrivKey(t *testing.T) { - grp := cyclic.NewGroup(large.NewInt(107), large.NewInt(2)) - baseKey := grp.NewInt(57) - privKey := grp.NewInt(5) - pubKey := grp.NewInt(42) - partner := id.NewIdFromUInt(14, id.User, t) - - km := NewManager(baseKey, privKey, pubKey, - partner, true, 12, 10, 10) - - result := km.GetPrivKey() - - if result.Cmp(privKey) != 0 { - t.Errorf("GetPrivKey returned wrong value, "+ - "expected: %s, got: %s", - privKey.Text(10), result.Text(10)) - } -} - -// Test KeyManager public key getter -func TestKeyManager_GetPubKey(t *testing.T) { - grp := cyclic.NewGroup(large.NewInt(107), large.NewInt(2)) - baseKey := grp.NewInt(57) - privKey := grp.NewInt(5) - pubKey := grp.NewInt(42) - partner := id.NewIdFromUInt(14, id.User, t) - - km := NewManager(baseKey, privKey, pubKey, - partner, true, 12, 10, 10) - - result := km.GetPubKey() - - if result.Cmp(pubKey) != 0 { - t.Errorf("GetPubKey returned wrong value, "+ - "expected: %s, got: %s", - pubKey.Text(10), result.Text(10)) - } -} - -// Test KeyManager partner getter -func TestKeyManager_GetPartner(t *testing.T) { - grp := cyclic.NewGroup(large.NewInt(107), large.NewInt(2)) - baseKey := grp.NewInt(57) - privKey := grp.NewInt(5) - pubKey := grp.NewInt(42) - partner := id.NewIdFromUInt(14, id.User, t) - - km := NewManager(baseKey, privKey, pubKey, - partner, true, 12, 10, 10) - - result := km.GetPartner() - - if *result != *partner { - t.Errorf("GetPartner returned wrong value, "+ - "expected: %s, got: %s", - *partner, *result) - } -} - -// Test rekey trigger -func TestKeyManager_Rekey(t *testing.T) { - grp := cyclic.NewGroup(large.NewInt(107), large.NewInt(2)) - baseKey := grp.NewInt(57) - partner := id.NewIdFromUInt(14, id.User, t) - - km := NewManager(baseKey, nil, nil, - partner, true, 12, 10, 10) - - var action Action - for i := 0; i < 9; i++ { - action = km.updateState(false) - if action != None { - t.Errorf("Expected 'None' action, got %s instead", - action) - } - } - - action = km.updateState(false) - if action != Rekey { - t.Errorf("Expected 'Rekey' action, got %s instead", - action) - } -} - -// Test purge trigger -func TestKeyManager_Purge(t *testing.T) { - grp := cyclic.NewGroup(large.NewInt(107), large.NewInt(2)) - baseKey := grp.NewInt(57) - partner := id.NewIdFromUInt(14, id.User, t) - - km := NewManager(baseKey, nil, nil, - partner, true, 12, 10, 10) - - var action Action - for i := 0; i < 9; i++ { - action = km.updateState(true) - if action != None { - t.Errorf("Expected 'None' action, got %s instead", - action) - } - } - - action = km.updateState(true) - if action != Purge { - t.Errorf("Expected 'Purge' action, got %s instead", - action) - } - - // Confirm that state is now deleted - action = km.updateState(false) - if action != Deleted { - t.Errorf("Expected 'Deleted' action, got %s instead", - action) - } -} - -// Test receive state update -func TestKeyManager_UpdateRecvState(t *testing.T) { - grp := cyclic.NewGroup(large.NewInt(107), large.NewInt(2)) - baseKey := grp.NewInt(57) - partner := id.NewIdFromUInt(14, id.User, t) - - km := NewManager(baseKey, nil, nil, - partner, false, 12, 10, 10) - - expectedVal := uint64(0x0010000001000008) - // Mark some keys as used and confirm expected value - km.updateRecvState(false, 3) - km.updateRecvState(false, 24) - km.updateRecvState(false, 52) - - if *km.recvKeysState[0] != expectedVal { - t.Errorf("UpdateRecvState failed for Key, expected"+ - " %d, got %d", expectedVal, *km.recvKeysState[0]) - } - - expectedVal = uint64(0x0000080000040020) - // Mark some Rekeys as used and confirm expected value - km.updateRecvState(true, 5) - km.updateRecvState(true, 18) - km.updateRecvState(true, 43) - - if *km.recvReKeysState[0] != expectedVal { - t.Errorf("UpdateRecvState failed for ReKey, expected"+ - " %d, got %d", expectedVal, *km.recvReKeysState[0]) - } -} - -// Test KeyManager Key Generation -func TestKeyManager_GenerateKeys(t *testing.T) { - grp := initGroup() - baseKey := grp.NewInt(57) - partner := id.NewIdFromUInt(14, id.User, t) - userID := id.NewIdFromUInt(18, id.User, t) - - ks := NewStore() - kmSend := NewManager(baseKey, nil, nil, - partner, true, 12, 10, 10) - - // Generate Send Keys - kmSend.GenerateKeys(grp, userID) - ks.AddSendManager(kmSend) - - kmRecv := NewManager(baseKey, nil, nil, - partner, false, 12, 10, 10) - - // Generate Receive Keys - e2ekeys := kmRecv.GenerateKeys(grp, userID) - ks.AddRecvManager(kmRecv) - ks.AddReceiveKeysByFingerprint(e2ekeys) - - // Confirm Send KeyManager is stored correctly in KeyStore map - retKM := ks.GetSendManager(partner) - if retKM != kmSend { - t.Errorf("KeyManager stored in KeyStore is not the same") - } - - // Confirm keys can be correctly pop'ed from KeyManager - actual, action := retKM.PopKey() - - if actual == nil { - t.Errorf("KeyManager returned nil when poping key") - } else if action != None { - t.Errorf("Expected 'None' action, got %s instead", - action) - } - - actual, action = retKM.PopRekey() - - if actual == nil { - t.Errorf("KeyManager returned nil when poping rekey") - } else if action != None { - t.Errorf("Expected 'None' action, got %s instead", - action) - } - - // Confirm Receive Keys can be obtained from KeyStore - actual = ks.GetRecvKey(kmRecv.recvKeysFingerprint[4]) - if actual == nil { - t.Errorf("ReceptionKeys Map returned nil for Key") - } - - actual = ks.GetRecvKey(e2ekeys[8].KeyFingerprint()) - - if actual == nil { - t.Errorf("ReceptionKeys Map returned nil for ReKey") - } -} - -// Test KeyManager destroy -func TestKeyManager_Destroy(t *testing.T) { - grp := initGroup() - baseKey := grp.NewInt(57) - partner := id.NewIdFromUInt(14, id.User, t) - userID := id.NewIdFromUInt(18, id.User, t) - - ks := NewStore() - km := NewManager(baseKey, nil, nil, - partner, true, 12, 10, 10) - - // Generate Send Keys - km.GenerateKeys(grp, userID) - ks.AddSendManager(km) - - km2 := NewManager(baseKey, nil, nil, - partner, false, 12, 10, 10) - - // Generate Receive Keys - e2ekeys := km2.GenerateKeys(grp, userID) - // TODO add ks keys here - ks.AddRecvManager(km2) - ks.AddReceiveKeysByFingerprint(e2ekeys) - - // Confirm Send KeyManager is stored correctly in KeyStore map - retKM := ks.GetSendManager(partner) - if retKM != km { - t.Errorf("KeyManager stored in KeyStore is not the same") - } - - // Confirm keys can be correctly pop'ed from KeyManager - actual, action := retKM.PopKey() - - if actual == nil { - t.Errorf("KeyManager returned nil when poping key") - } else if action != None { - t.Errorf("Expected 'None' action, got %s instead", - action) - } - - actual, action = retKM.PopRekey() - - if actual == nil { - t.Errorf("KeyManager returned nil when poping rekey") - } else if action != None { - t.Errorf("Expected 'None' action, got %s instead", - action) - } - - // Confirm Receive Keys can be obtained from KeyStore - actual = ks.GetRecvKey(km2.recvKeysFingerprint[4]) - - if actual == nil { - t.Errorf("ReceptionKeys Map returned nil for Key") - } - - actual = ks.GetRecvKey(km2.recvReKeysFingerprint[8]) - if actual == nil { - t.Errorf("ReceptionKeys Map returned nil for ReKey") - } - - // Destroy KeyManager and confirm KeyManager is gone from map - km.Destroy(ks) - - retKM = ks.GetSendManager(partner) - if retKM != nil { - t.Errorf("KeyManager was not properly removed from KeyStore") - } - -} - -// Test GOB Encode/Decode of KeyManager -// and do a simple comparison after -func TestKeyManager_GobSimple(t *testing.T) { - grp := initGroup() - baseKey := grp.NewInt(57) - privKey := grp.NewInt(5) - pubKey := grp.NewInt(42) - partner := id.NewIdFromUInt(14, id.User, t) - - var byteBuf bytes.Buffer - - enc := gob.NewEncoder(&byteBuf) - dec := gob.NewDecoder(&byteBuf) - - km := NewManager(baseKey, privKey, pubKey, - partner, true, 12, 10, 10) - - err := enc.Encode(km) - - if err != nil { - t.Errorf("Error GOB Encoding KeyManager: %s", err) - } - - outKm := &KeyManager{} - - err = dec.Decode(&outKm) - - if err != nil { - t.Errorf("Error GOB Decoding KeyManager: %s", err) - } - - if km.baseKey.Cmp(outKm.baseKey) != 0 { - t.Errorf("GobEncoder/GobDecoder failed on BaseKey, "+ - "Expected: %v; Recieved: %v ", - km.baseKey.TextVerbose(10, 12), - outKm.baseKey.TextVerbose(10, 12)) - } - - if *km.partner != *outKm.partner { - t.Errorf("GobEncoder/GobDecoder failed on Partner, "+ - "Expected: %v; Recieved: %v ", - *km.partner, - *outKm.partner) - } - - if *km.sendState != *outKm.sendState { - t.Errorf("GobEncoder/GobDecoder failed on State, "+ - "Expected: %v; Recieved: %v ", - *km.sendState, - *outKm.sendState) - } - - if km.ttl != outKm.ttl { - t.Errorf("GobEncoder/GobDecoder failed on TTL, "+ - "Expected: %v; Recieved: %v ", - km.ttl, - outKm.ttl) - } - - if km.numKeys != outKm.numKeys { - t.Errorf("GobEncoder/GobDecoder failed on NumKeys, "+ - "Expected: %v; Recieved: %v ", - km.numKeys, - outKm.numKeys) - } - - if km.numReKeys != outKm.numReKeys { - t.Errorf("GobEncoder/GobDecoder failed on NumReKeys, "+ - "Expected: %v; Recieved: %v ", - km.numReKeys, - outKm.numReKeys) - } - - for i := 0; i < int(numStates); i++ { - if *km.recvKeysState[i] != *outKm.recvKeysState[i] { - t.Errorf("GobEncoder/GobDecoder failed on RecvKeysState[%d], "+ - "Expected: %v; Recieved: %v ", - i, - *km.recvKeysState[i], - *outKm.recvKeysState[i]) - } - } - - for i := 0; i < int(numReStates); i++ { - if *km.recvReKeysState[i] != *outKm.recvReKeysState[i] { - t.Errorf("GobEncoder/GobDecoder failed on RecvReKeysState[%d], "+ - "Expected: %v; Recieved: %v ", - i, - *km.recvReKeysState[i], - *outKm.recvReKeysState[i]) - } - } -} - -// Tests that GobDecode() for Key Manager throws an error for a -// malformed byte array -func TestKeyManager_GobDecodeError(t *testing.T) { - km := KeyManager{} - err := km.GobDecode([]byte{}) - - if err.Error() != "EOF" { - t.Errorf("GobDecode() did not produce the expected error\n\treceived: %v"+ - "\n\texpected: %v", err, errors.New("EOF")) - } -} - -// Test that key maps are reconstructed correctly after -// Key Manager GOB Encode/Decode -func TestKeyManager_Gob(t *testing.T) { - grp := initGroup() - baseKey := grp.NewInt(57) - privKey := grp.NewInt(5) - pubKey := grp.NewInt(42) - partner := id.NewIdFromUInt(14, id.User, t) - userID := id.NewIdFromUInt(18, id.User, t) - - ks := NewStore() - km := NewManager(baseKey, privKey, pubKey, - partner, true, 12, 10, 10) - - // Generate Send Keys - km.GenerateKeys(grp, userID) - ks.AddSendManager(km) - - km2 := NewManager(baseKey, privKey, pubKey, - partner, false, 12, 10, 10) - - // Generate Receive Keys - e2ekeys := km2.GenerateKeys(grp, userID) - ks.AddRecvManager(km2) - ks.AddReceiveKeysByFingerprint(e2ekeys) - - // Generate keys here to have a way to compare after - sendKeys := e2e.DeriveKeys(grp, baseKey, userID, uint(km.numKeys)) - sendReKeys := e2e.DeriveEmergencyKeys(grp, baseKey, userID, uint(km.numReKeys)) - recvKeys := e2e.DeriveKeys(grp, baseKey, partner, uint(km.numKeys)) - recvReKeys := e2e.DeriveEmergencyKeys(grp, baseKey, partner, uint(km.numReKeys)) - - var expectedKeyMap = make(map[string]bool) - - for _, key := range sendKeys { - expectedKeyMap[base64.StdEncoding.EncodeToString(key.Bytes())] = true - } - - for _, key := range sendReKeys { - expectedKeyMap[base64.StdEncoding.EncodeToString(key.Bytes())] = true - } - - for _, key := range recvKeys { - expectedKeyMap[base64.StdEncoding.EncodeToString(key.Bytes())] = true - } - - for _, key := range recvReKeys { - expectedKeyMap[base64.StdEncoding.EncodeToString(key.Bytes())] = true - } - - // Use some send keys and mark on expected map as used - retKM := ks.GetSendManager(partner) - if retKM != km { - t.Errorf("KeyManager stored in KeyStore is not the same") - } - key, _ := retKM.PopKey() - expectedKeyMap[base64.StdEncoding.EncodeToString(key.key.Bytes())] = false - key, _ = retKM.PopKey() - expectedKeyMap[base64.StdEncoding.EncodeToString(key.key.Bytes())] = false - key, _ = retKM.PopKey() - expectedKeyMap[base64.StdEncoding.EncodeToString(key.key.Bytes())] = false - usedSendKeys := 3 - - key, _ = retKM.PopRekey() - expectedKeyMap[base64.StdEncoding.EncodeToString(key.key.Bytes())] = false - key, _ = retKM.PopRekey() - expectedKeyMap[base64.StdEncoding.EncodeToString(key.key.Bytes())] = false - usedSendReKeys := 2 - - // Use some receive keys and mark on expected map as used - key = ks.GetRecvKey(km2.recvKeysFingerprint[3]) - expectedKeyMap[base64.StdEncoding.EncodeToString(key.key.Bytes())] = false - key = ks.GetRecvKey(km2.recvKeysFingerprint[8]) - expectedKeyMap[base64.StdEncoding.EncodeToString(key.key.Bytes())] = false - key = ks.GetRecvKey(km2.recvKeysFingerprint[6]) - expectedKeyMap[base64.StdEncoding.EncodeToString(key.key.Bytes())] = false - key = ks.GetRecvKey(km2.recvKeysFingerprint[1]) - expectedKeyMap[base64.StdEncoding.EncodeToString(key.key.Bytes())] = false - usedRecvKeys := 4 - - key = ks.GetRecvKey(km2.recvReKeysFingerprint[4]) - expectedKeyMap[base64.StdEncoding.EncodeToString(key.key.Bytes())] = false - usedRecvReKeys := 1 - - // Now GOB Encode Key Manager - var byteBuf bytes.Buffer - - enc := gob.NewEncoder(&byteBuf) - dec := gob.NewDecoder(&byteBuf) - - err := enc.Encode(km) - - if err != nil { - t.Errorf("Error GOB Encoding KeyManager: %s", err) - } - - // Destroy KeyManager and confirm KeyManager is gone from map - km.Destroy(ks) - - retKM = ks.GetSendManager(partner) - if retKM != nil { - t.Errorf("KeyManager was not properly removed from KeyStore") - } - - // GOB Decode Key Manager - sendKm := &KeyManager{} - err = dec.Decode(&sendKm) - - if err != nil { - t.Errorf("Error GOB Decoding KeyManager: %s", err) - } - - err = enc.Encode(km2) - - if err != nil { - t.Errorf("Error GOB Encoding KeyManager2: %s", err) - } - - // Destroy Key Manager (and maps) and confirm no more receive keys exist - km2.Destroy(ks) - - // GOB Decode Key Manager2 - outKm2 := &KeyManager{} - err = dec.Decode(&outKm2) - - if err != nil { - t.Errorf("Error GOB Decoding KeyManager2: %s", err) - } - - // Generate Keys from decoded Key Managers - e2ekeys = sendKm.GenerateKeys(grp, userID) - ks.AddSendManager(sendKm) - //ks.AddReceiveKeysByFingerprint(e2ekeys) - - e2ekeys = outKm2.GenerateKeys(grp, userID) - ks.AddRecvManager(km) - ks.AddReceiveKeysByFingerprint(e2ekeys) - - // Confirm maps are the same as before delete - - // First, check that len of send Stacks matches expected - if sendKm.sendKeys.keys.Len() != int(sendKm.numKeys)-usedSendKeys { - t.Errorf("SendKeys Stack contains more keys than expected after decode."+ - " Expected: %d, Got: %d", - int(sendKm.numKeys)-usedSendKeys, - sendKm.sendKeys.keys.Len()) - } - - if sendKm.sendReKeys.keys.Len() != int(sendKm.numReKeys)-usedSendReKeys { - t.Errorf("SendReKeys Stack contains more keys than expected after decode."+ - " Expected: %d, Got: %d", - int(sendKm.numReKeys)-usedSendReKeys, - sendKm.sendReKeys.keys.Len()) - } - - // Now confirm that all send keys are in the expected map - retKM = ks.GetSendManager(partner) - for i := 0; i < int(sendKm.numKeys)-usedSendKeys; i++ { - key, _ := retKM.PopKey() - if expectedKeyMap[base64.StdEncoding.EncodeToString(key.key.Bytes())] != true { - t.Errorf("SendKey %v was used or didn't exist before", - key.KeyFingerprint()) - } - } - - for i := 0; i < int(sendKm.numReKeys)-usedSendReKeys; i++ { - key, _ := retKM.PopRekey() - if expectedKeyMap[base64.StdEncoding.EncodeToString(key.key.Bytes())] != true { - t.Errorf("SendReKey %v was used or didn't exist before", - key.KeyFingerprint()) - } - } - - // Check that len of fingerprint lists matches expected - if len(outKm2.recvKeysFingerprint) != int(outKm2.numKeys)-usedRecvKeys { - t.Errorf("ReceiveKeys list contains more keys than expected after decode."+ - " Expected: %d, Got: %d", - int(outKm2.numKeys)-usedRecvKeys, - len(outKm2.recvKeysFingerprint)) - } - - if len(outKm2.recvReKeysFingerprint) != int(outKm2.numReKeys)-usedRecvReKeys { - t.Errorf("ReceiveReKeys list contains more keys than expected after decode."+ - " Expected: %d, Got: %d", - int(outKm2.numReKeys)-usedRecvReKeys, - len(outKm2.recvReKeysFingerprint)) - } - - // Now confirm that all receiving keys are in the expected map - for i := 0; i < int(outKm2.numKeys)-usedRecvKeys; i++ { - key := ks.GetRecvKey(outKm2.recvKeysFingerprint[i]) - if expectedKeyMap[base64.StdEncoding.EncodeToString(key.key.Bytes())] != true { - t.Errorf("ReceiveKey %v was used or didn't exist before", - key.KeyFingerprint()) - } - } - - for i := 0; i < int(outKm2.numReKeys)-usedRecvReKeys; i++ { - key := ks.GetRecvKey(outKm2.recvReKeysFingerprint[i]) - if expectedKeyMap[base64.StdEncoding.EncodeToString(key.key.Bytes())] != true { - t.Errorf("ReceiveReKey %v was used or didn't exist before", - key.KeyFingerprint()) - } - } -} diff --git a/keyStore/keyParams.go b/keyStore/keyParams.go deleted file mode 100644 index 7af8bae62e12c51a7ca573d99fc39dfd3081551b..0000000000000000000000000000000000000000 --- a/keyStore/keyParams.go +++ /dev/null @@ -1,30 +0,0 @@ -package keyStore - -import "gitlab.com/elixxir/crypto/e2e" - -// DEFAULT KEY GENERATION PARAMETERS -// Hardcoded limits for keys -// With 16 receiving states we can hold -// 16*64=1024 dirty bits for receiving keys -// With that limit, and setting maxKeys to 800, -// we need a Threshold of 224, and a scalar -// smaller than 1.28 to ensure we never generate -// more than 1024 keys -// With 1 receiving states for ReKeys we can hold -// 64 Rekeys -const ( - numStates uint16 = 16 - numReStates uint16 = 1 - minKeys uint16 = 500 - maxKeys uint16 = 800 - ttlScalar float64 = 1.2 // generate 20% extra keys - threshold uint16 = 224 - numReKeys uint16 = 64 -) - -type KeyParams struct { - MinKeys uint16 - MaxKeys uint16 - NumRekeys uint16 - e2e.TTLParams -} diff --git a/keyStore/keyStack.go b/keyStore/keyStack.go deleted file mode 100644 index 43bf3ae0d3f67dc5d6b96e8a63e74dc6fc17fc7f..0000000000000000000000000000000000000000 --- a/keyStore/keyStack.go +++ /dev/null @@ -1,63 +0,0 @@ -package keyStore - -import ( - "github.com/golang-collections/collections/stack" - "gitlab.com/elixxir/client/globals" - "sync" -) - -// KeyStack contains a stack of E2E keys (or rekeys) -// Also has a mutex for access control -type KeyStack struct { - // List of Keys used for sending - // When a key is used it is deleted (pop'ed) - keys *stack.Stack - // Lock - sync.Mutex -} - -// Create a new KeyStack -// It creates the internal stack.Stack object -func NewKeyStack() *KeyStack { - ks := new(KeyStack) - ks.keys = stack.New() - return ks -} - -// Push an E2EKey into the stack -func (ks *KeyStack) Push(key *E2EKey) { - ks.keys.Push(key) -} - -// Returns the top key on the stack -// Internally holds the lock when -// running Pop on the internal stack.Stack object -func (ks *KeyStack) Pop() *E2EKey { - var key *E2EKey - - // Get the key - ks.Lock() - keyFace := ks.keys.Pop() - ks.Unlock() - - // Check if the key exists and panic otherwise - if keyFace == nil { - globals.Log.WARN.Printf("E2E key stack is empty!") - key = nil - } else { - key = keyFace.(*E2EKey) - } - - return key -} - -// Deletes all keys from stack, i.e., pops all -// Internally holds the lock -func (ks *KeyStack) Delete() { - ks.Lock() - defer ks.Unlock() - length := ks.keys.Len() - for i := 0; i < length; i++ { - ks.keys.Pop() - } -} diff --git a/keyStore/keyStack_test.go b/keyStore/keyStack_test.go deleted file mode 100644 index caecf8635b716de4ae7bf6639d8ba8264f34b35c..0000000000000000000000000000000000000000 --- a/keyStore/keyStack_test.go +++ /dev/null @@ -1,135 +0,0 @@ -package keyStore - -import ( - "gitlab.com/elixxir/client/parse" - "gitlab.com/elixxir/crypto/cyclic" - "gitlab.com/elixxir/crypto/large" - "testing" - "time" -) - -// Helper function to compare E2E Keys -func E2EKeyCmp(a, b *E2EKey) bool { - if a.GetManager() != b.GetManager() { - return false - } - if a.GetOuterType() != b.GetOuterType() { - return false - } - if a.GetKey().Cmp(b.GetKey()) != 0 { - return false - } - return true -} - -// Test KeyStack creation and push/pop -func TestKeyStack(t *testing.T) { - ks := NewKeyStack() - grp := cyclic.NewGroup(large.NewInt(107), large.NewInt(2)) - expectedKeys := make([]*E2EKey, 100) - - for i := 0; i < 100; i++ { - key := new(E2EKey) - key.outer = parse.E2E - key.key = grp.NewInt(int64(i + 2)) - key.manager = nil - expectedKeys[99-i] = key - ks.Push(key) - } - - for i := 0; i < 100; i++ { - actual := ks.Pop() - if !E2EKeyCmp(actual, expectedKeys[i]) { - t.Errorf("Pop'd key doesn't match with expected") - } - } -} - -// Test that KeyStack panics on pop if empty -func TestKeyStack_Panic(t *testing.T) { - ks := NewKeyStack() - grp := cyclic.NewGroup(large.NewInt(107), large.NewInt(2)) - expectedKeys := make([]*E2EKey, 10) - - for i := 0; i < 10; i++ { - key := new(E2EKey) - key.outer = parse.E2E - key.key = grp.NewInt(int64(i + 2)) - key.manager = nil - expectedKeys[9-i] = key - ks.Push(key) - } - - defer func() { - if r := recover(); r == nil { - t.Errorf("Pop should panic when stack is empty") - } - }() - - for i := 0; i < 11; i++ { - actual := ks.Pop() - if !E2EKeyCmp(actual, expectedKeys[i]) { - t.Errorf("Pop'd key doesn't match with expected") - } - } -} - -// Test that delete correctly empties stack -func TestKeyStack_Delete(t *testing.T) { - ks := NewKeyStack() - grp := cyclic.NewGroup(large.NewInt(107), large.NewInt(2)) - expectedKeys := make([]*E2EKey, 100) - - for i := 0; i < 100; i++ { - key := new(E2EKey) - key.outer = parse.E2E - key.key = grp.NewInt(int64(i + 2)) - key.manager = nil - expectedKeys[99-i] = key - ks.Push(key) - } - - for i := 0; i < 50; i++ { - actual := ks.Pop() - if !E2EKeyCmp(actual, expectedKeys[i]) { - t.Errorf("Pop'd key doesn't match with expected") - } - } - - ks.Delete() - - k4 := ks.Pop() - if k4 != nil { - t.Errorf("Pop should return nil when stack is empty") - } -} - -// Test concurrent access -func TestKeyStack_Concurrent(t *testing.T) { - ks := NewKeyStack() - grp := cyclic.NewGroup(large.NewInt(107), large.NewInt(2)) - expectedKeys := make([]*E2EKey, 100) - - for i := 0; i < 100; i++ { - key := new(E2EKey) - key.outer = parse.E2E - key.key = grp.NewInt(int64(i + 2)) - key.manager = nil - expectedKeys[99-i] = key - ks.Push(key) - } - - for i := 0; i < 100; i++ { - go func() { - ks.Pop() - }() - } - - // wait for goroutines - time.Sleep(500 * time.Millisecond) - - k4 := ks.Pop() - if k4 != nil { - t.Errorf("Pop should return nil when stack is empty") - } -} diff --git a/keyStore/keyStore.go b/keyStore/keyStore.go deleted file mode 100644 index 8efba3a8ad03fd2ea32005d82318fdd7b1481ee4..0000000000000000000000000000000000000000 --- a/keyStore/keyStore.go +++ /dev/null @@ -1,353 +0,0 @@ -package keyStore - -import ( - "bytes" - "encoding/gob" - "github.com/pkg/errors" - "gitlab.com/elixxir/client/parse" - "gitlab.com/elixxir/crypto/cyclic" - "gitlab.com/elixxir/crypto/e2e" - "gitlab.com/elixxir/primitives/format" - "gitlab.com/xx_network/primitives/id" - "sync" -) - -// Local types in order to implement functions that -// return real types instead of interfaces -type keyManMap sync.Map -type inKeyMap sync.Map - -// Stores a KeyManager entry for given user -func (m *keyManMap) Store(user *id.ID, km *KeyManager) { - (*sync.Map)(m).Store(*user, km) -} - -// Loads a KeyManager entry for given user -func (m *keyManMap) Load(user *id.ID) *KeyManager { - val, ok := (*sync.Map)(m).Load(*user) - if !ok { - return nil - } else { - return val.(*KeyManager) - } -} - -// Deletes a KeyManager entry for given user -func (m *keyManMap) Delete(user *id.ID) { - (*sync.Map)(m).Delete(*user) -} - -// Internal helper function to get a list of all values -// contained in a KeyManMap -func (m *keyManMap) values() []*KeyManager { - valueList := make([]*KeyManager, 0) - (*sync.Map)(m).Range(func(key, value interface{}) bool { - valueList = append(valueList, value.(*KeyManager)) - return true - }) - return valueList -} - -// Internal helper function to get a list of all keys -// contained in a KeyManMap -func (m *keyManMap) keys() []id.ID { - keyList := make([]id.ID, 0) - (*sync.Map)(m).Range(func(key, value interface{}) bool { - keyList = append(keyList, key.(id.ID)) - return true - }) - return keyList -} - -// Stores an *E2EKey for given fingerprint -func (m *inKeyMap) Store(fingerprint format.Fingerprint, key *E2EKey) { - (*sync.Map)(m).Store(fingerprint, key) -} - -// Pops key for given fingerprint, i.e, -// returns and deletes it from the map -// Atomically updates Key Manager Receiving state -// Returns nil if not found -func (m *inKeyMap) Pop(fingerprint format.Fingerprint) *E2EKey { - val, ok := (*sync.Map)(m).Load(fingerprint) - - var key *E2EKey - if !ok { - return nil - } else { - key = val.(*E2EKey) - } - // Delete key from map - m.Delete(fingerprint) - // Update Key Manager Receiving State - key.GetManager().updateRecvState( - key.GetOuterType() == parse.Rekey, - key.keyNum) - return key -} - -// Deletes a key for given fingerprint -func (m *inKeyMap) Delete(fingerprint format.Fingerprint) { - (*sync.Map)(m).Delete(fingerprint) -} - -// Deletes keys from a given list of fingerprints -func (m *inKeyMap) DeleteList(fingerprints []format.Fingerprint) { - for _, fp := range fingerprints { - m.Delete(fp) - } -} - -// KeyStore contains the E2E key -// and Key Managers maps -// Send keys are obtained directly from the Key Manager -// which is looked up in the sendKeyManagers map -// Receiving keys are lookup up by fingerprint on -// receptionKeys map -// RecvKeyManagers map is needed in order to maintain -// active Key Managers when the session is stored/loaded -// It is not a sync.map since it won't be accessed -// very often -// It still contains a lock for multithreaded access -type KeyStore struct { - // Key generation parameters - params *KeyParams - - // Transmission Keys map - // Maps id.ID to *KeyManager - sendKeyManagers *keyManMap - - // Reception Keys map - // Maps format.Fingerprint to *E2EKey - receptionKeys *inKeyMap - - // Reception Key Managers map - recvKeyManagers map[id.ID]*ReceptionKeyManagerBuffer - - lock sync.Mutex -} - -func NewStore() *KeyStore { - ks := new(KeyStore) - ks.params = &KeyParams{ - MinKeys: minKeys, - MaxKeys: maxKeys, - NumRekeys: numReKeys, - TTLParams: e2e.TTLParams{ - TTLScalar: ttlScalar, - MinNumKeys: threshold, - }, - } - ks.sendKeyManagers = new(keyManMap) - ks.receptionKeys = new(inKeyMap) - ks.recvKeyManagers = make(map[id.ID]*ReceptionKeyManagerBuffer) - return ks -} - -func (ks *KeyStore) DeleteContactKeys(id *id.ID) error { - ks.lock.Lock() - defer ks.lock.Unlock() - - rkmb, ok := ks.recvKeyManagers[*id] - if ok { - for _, manager := range rkmb.managers { - if manager != nil { - keys := manager.recvKeysFingerprint - rekeys := manager.recvReKeysFingerprint - ks.receptionKeys.DeleteList(append(keys, rekeys...)) - } - } - } else { - return errors.Errorf("User with id %+v not in map of key managers", id) - } - delete(ks.recvKeyManagers, *id) - ks.sendKeyManagers.Delete(id) - return nil -} - -// Get Key generation parameters from KeyStore -func (ks *KeyStore) GetKeyParams() *KeyParams { - return ks.params -} - -// Add a Send KeyManager to respective map in KeyStore -func (ks *KeyStore) AddSendManager(km *KeyManager) { - ks.sendKeyManagers.Store(km.GetPartner(), km) -} - -// Get a Send KeyManager from respective map in KeyStore -// based on partner ID -func (ks *KeyStore) GetSendManager(partner *id.ID) *KeyManager { - return ks.sendKeyManagers.Load(partner) -} - -// GetPartners returns the list of partners we have keys for -func (ks *KeyStore) GetPartners() []id.ID { - return ks.sendKeyManagers.keys() -} - -// Delete a Send KeyManager from respective map in KeyStore -// based on partner ID -func (ks *KeyStore) DeleteSendManager(partner *id.ID) { - ks.sendKeyManagers.Delete(partner) -} - -// Add a Receiving E2EKey to the correct KeyStore map -// based on its fingerprint -func (ks *KeyStore) AddRecvKey(fingerprint format.Fingerprint, - key *E2EKey) { - ks.receptionKeys.Store(fingerprint, key) -} - -// Get the Receiving Key stored in correct KeyStore map -// based on the given fingerprint -func (ks *KeyStore) GetRecvKey(fingerprint format.Fingerprint) *E2EKey { - return ks.receptionKeys.Pop(fingerprint) -} - -// Add a Receive KeyManager to respective map in KeyStore -func (ks *KeyStore) AddRecvManager(km *KeyManager) { - ks.lock.Lock() - defer ks.lock.Unlock() - - //ks.recvKeyManagers = km - keys, ok := ks.recvKeyManagers[*km.partner] - - if ok { - toBeDeleted := keys.push(km) - ks.DeleteReceiveKeysByFingerprint(toBeDeleted) - } else { - newBuffer := NewReceptionKeyManagerBuffer() - newBuffer.push(km) - ks.recvKeyManagers[*km.partner] = newBuffer - } -} - -// Gets the Key manager at the current location on the ReceptionKeyManagerBuffer -// based on partner ID -func (ks *KeyStore) GetRecvManager(partner *id.ID) *KeyManager { - ks.lock.Lock() - defer ks.lock.Unlock() - return ks.recvKeyManagers[*partner].getCurrentReceptionKeyManager() -} - -// Delete a Receive KeyManager based on partner ID from respective map in KeyStore -func (ks *KeyStore) DeleteRecvManager(partner *id.ID) { - ks.lock.Lock() - defer ks.lock.Unlock() - delete(ks.recvKeyManagers, *partner) -} - -// GobEncode the KeyStore -func (ks *KeyStore) GobEncode() ([]byte, error) { - var buf bytes.Buffer - - // Create new encoder that will transmit the buffer - enc := gob.NewEncoder(&buf) - - // Transmit the Key Parameters - err := enc.Encode(ks.params) - - if err != nil { - return nil, err - } - - // Transmit the Send Key Managers - kmList := ks.sendKeyManagers.values() - err = enc.Encode(kmList) - - if err != nil { - return nil, err - } - - // Transmit the Receive Key Managers - err = enc.Encode(ks.recvKeyManagers) - - if err != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -// GobDecode the KeyStore from bytes -// NOTE: ReconstructKeys must be called after GobDecoding a KeyStore -func (ks *KeyStore) GobDecode(in []byte) error { - var buf bytes.Buffer - - // Write bytes to the buffer - buf.Write(in) - - // Create new decoder that reads from the buffer - dec := gob.NewDecoder(&buf) - - // Decode Key Parameters - err := dec.Decode(&ks.params) - - if err != nil { - return err - } - - // Decode Key Managers List - var kmList []*KeyManager - err = dec.Decode(&kmList) - - if err != nil { - return err - } - - // Decode Recv Key Managers map - err = dec.Decode(&ks.recvKeyManagers) - - if err != nil { - return err - } - - // Reconstruct Send Key Manager map - ks.sendKeyManagers = new(keyManMap) - ks.receptionKeys = new(inKeyMap) - for _, km := range kmList { - ks.AddSendManager(km) - } - - return nil -} - -// ReconstructKeys loops through all key managers and -// calls GenerateKeys on each of them, in order to rebuild -// the key maps -func (ks *KeyStore) ReconstructKeys(grp *cyclic.Group, userID *id.ID) { - - kmList := ks.sendKeyManagers.values() - for _, km := range kmList { - km.GenerateKeys(grp, userID) - ks.AddSendManager(km) - } - - for _, kmb := range ks.recvKeyManagers { - for _, km := range kmb.managers { - if km != nil { - e2eKeys := km.GenerateKeys(grp, userID) - ks.AddReceiveKeysByFingerprint(e2eKeys) - } - } - } -} - -func (ks *KeyStore) DeleteReceiveKeysByFingerprint(toBeDeleted []format.Fingerprint) { - if len(toBeDeleted) != 0 { - ks.receptionKeys.DeleteList(toBeDeleted) - } -} - -func (ks *KeyStore) AddReceiveKeysByFingerprint(newKeys []*E2EKey) { - for _, key := range newKeys { - ks.AddRecvKey(key.KeyFingerprint(), key) - } -} - -// Delete multiple Receiving E2EKeys from the correct KeyStore map -// based on a list of fingerprints -func (ks *KeyStore) DeleteRecvKeyList(fingerprints []format.Fingerprint) { - ks.receptionKeys.DeleteList(fingerprints) -} diff --git a/keyStore/keyStore_test.go b/keyStore/keyStore_test.go deleted file mode 100644 index 53891da174ab1d284892c676f166e591e96a6b08..0000000000000000000000000000000000000000 --- a/keyStore/keyStore_test.go +++ /dev/null @@ -1,164 +0,0 @@ -package keyStore - -import ( - "bytes" - "encoding/gob" - "github.com/pkg/errors" - "gitlab.com/elixxir/primitives/format" - "gitlab.com/xx_network/primitives/id" - "testing" -) - -// Test GetKeyParams and confirm default params are correct -func TestKeyStore_GetKeyParams(t *testing.T) { - ks := NewStore() - - params := ks.GetKeyParams() - - if params.MinKeys != minKeys { - t.Errorf("KeyParams: MinKeys mismatch, expected %d, "+ - "got %d", minKeys, params.MinKeys) - } else if params.MaxKeys != maxKeys { - t.Errorf("KeyParams: MaxKeys mismatch, expected %d, "+ - "got %d", maxKeys, params.MaxKeys) - } else if params.NumRekeys != numReKeys { - t.Errorf("KeyParams: NumRekeys mismatch, expected %d, "+ - "got %d", numReKeys, params.NumRekeys) - } else if params.TTLScalar != ttlScalar { - t.Errorf("KeyParams: TTLScalar mismatch, expected %f, "+ - "got %f", ttlScalar, params.TTLScalar) - } else if params.MinNumKeys != threshold { - t.Errorf("KeyParams: MinNumKeys mismatch, expected %d, "+ - "got %d", threshold, params.MinNumKeys) - } -} - -// Test GOB Encode/Decode of KeyStore -// and compare if all keys match originals -func TestKeyStore_Gob(t *testing.T) { - grp := initGroup() - baseKey := grp.NewInt(57) - privKey := grp.NewInt(5) - pubKey := grp.NewInt(42) - partner := id.NewIdFromUInt(14, id.User, t) - userID := id.NewIdFromUInt(18, id.User, t) - - ks := NewStore() - km := NewManager(baseKey, privKey, pubKey, - partner, true, 12, 10, 10) - - // Generate Send Keys - e2ekeys := km.GenerateKeys(grp, userID) - ks.AddSendManager(km) - - km2 := NewManager(baseKey, privKey, pubKey, - partner, false, 12, 10, 10) - - // Generate Receive Keys - e2ekeys = km2.GenerateKeys(grp, userID) - ks.AddReceiveKeysByFingerprint(e2ekeys) - ks.AddRecvManager(km2) - - // Now that some KeyManagers are in the keystore, Gob Encode it - var byteBuf bytes.Buffer - - enc := gob.NewEncoder(&byteBuf) - dec := gob.NewDecoder(&byteBuf) - - err := enc.Encode(ks) - - if err != nil { - t.Errorf("Error GOB Encoding KeyStore: %s", err) - } - - outKs := &KeyStore{} - - err = dec.Decode(&outKs) - - if err != nil { - t.Errorf("Error GOB Decoding KeyStore: %s", err) - } - - // Need to reconstruct keys after decoding - outKs.ReconstructKeys(grp, userID) - - // Get KeyManagers and compare keys - outKm := outKs.GetSendManager(partner) - - for i := 0; i < 12; i++ { - origKey, _ := km.PopKey() - actualKey, _ := outKm.PopKey() - - if origKey.GetOuterType() != actualKey.GetOuterType() { - t.Errorf("Send Key type mistmatch after GOB Encode/Decode") - } else if origKey.key.Cmp(actualKey.key) != 0 { - t.Errorf("Send Key mistmatch after GOB Encode/Decode") - } - } - - for i := 0; i < 10; i++ { - origKey, _ := km.PopRekey() - actualKey, _ := outKm.PopRekey() - - if origKey.GetOuterType() != actualKey.GetOuterType() { - t.Errorf("Send Key type mistmatch after GOB Encode/Decode") - } else if origKey.key.Cmp(actualKey.key) != 0 { - t.Errorf("Send Key mistmatch after GOB Encode/Decode") - } - } -} - -// Tests that GobDecode() for Key Store throws an error for a -// malformed byte array -func TestKeyStore_GobDecodeErrors(t *testing.T) { - ksTest := KeyStore{} - err := ksTest.GobDecode([]byte{}) - - if err.Error() != "EOF" { - //if !reflect.DeepEqual(err, errors.New("EOF")) { - t.Errorf("GobDecode() did not produce the expected error\n\treceived: %v"+ - "\n\texpected: %v", err, errors.New("EOF")) - } -} - -func TestKeyStore_DeleteContactKeys(t *testing.T) { - grp := initGroup() - baseKey := grp.NewInt(57) - privKey := grp.NewInt(5) - pubKey := grp.NewInt(42) - partner := id.NewIdFromUInt(14, id.User, t) - userID := id.NewIdFromUInt(18, id.User, t) - - ks := NewStore() - km := NewManager(baseKey, privKey, pubKey, - partner, true, 12, 10, 10) - - // Generate Send Keys - e2ekeys := km.GenerateKeys(grp, userID) - km.recvReKeysFingerprint = []format.Fingerprint{*format.NewFingerprint([]byte("testtesttesttesttesttesttesttest"))} - km.recvKeysFingerprint = []format.Fingerprint{*format.NewFingerprint([]byte("testtesttesttesttesttesttesttest"))} - ks.AddSendManager(km) - rkmb := NewReceptionKeyManagerBuffer() - - km2 := NewManager(baseKey, privKey, pubKey, - partner, false, 12, 10, 10) - - // Generate Receive Keys - e2ekeys = km2.GenerateKeys(grp, userID) - ks.AddReceiveKeysByFingerprint(e2ekeys) - km2.recvReKeysFingerprint = []format.Fingerprint{*format.NewFingerprint([]byte("testtesttesttesttesttesttesttest"))} - km2.recvKeysFingerprint = []format.Fingerprint{*format.NewFingerprint([]byte("testtesttesttesttesttesttesttest"))} - ks.AddRecvManager(km2) - - rkmb.managers[0] = km - rkmb.managers[1] = km2 - rkmb.managers[2] = km2 - rkmb.managers[3] = km2 - rkmb.managers[4] = km2 - ks.recvKeyManagers[*partner] = rkmb - - err := ks.DeleteContactKeys(partner) - if err != nil { - t.Errorf("Failed to delete contact keys: %+v", err) - } -} diff --git a/keyStore/recieveKeyManagerBuffer.go b/keyStore/recieveKeyManagerBuffer.go deleted file mode 100644 index 334b5c795b7dc48e8583a31676918a0353a56cef..0000000000000000000000000000000000000000 --- a/keyStore/recieveKeyManagerBuffer.go +++ /dev/null @@ -1,127 +0,0 @@ -package keyStore - -import ( - "bytes" - "encoding/gob" - "fmt" - "github.com/pkg/errors" - "gitlab.com/elixxir/primitives/format" -) - -const ReceptionKeyManagerBufferLength = 5 - -//This creates a circular buffer and initializes all the keymanagers to be nil at location zero. -func NewReceptionKeyManagerBuffer() *ReceptionKeyManagerBuffer { - newBuffer := ReceptionKeyManagerBuffer{} - newBuffer.loc = 0 - return &newBuffer -} - -type ReceptionKeyManagerBuffer struct { - managers [ReceptionKeyManagerBufferLength]*KeyManager - loc int -} - -// Push takes in a new keymanager obj, and adds it into our circular buffer of keymanagers, -// the keymanager obj passed in overwrites the keymanager in the buffer, and we have to return the existing -// keymanager if there is one back ot the parent so that the deletion can be handled. -func (rkmb *ReceptionKeyManagerBuffer) push(km *KeyManager) []format.Fingerprint { - deadkm := &KeyManager{} - deadkm = nil - if rkmb.managers[0] != nil { - //Don't increment location if location 0 is empty first time around - rkmb.loc = (rkmb.loc + 1) % ReceptionKeyManagerBufferLength - deadkm = rkmb.managers[rkmb.loc] - } else { - - } - - rkmb.managers[rkmb.loc] = km - - if deadkm == nil { - return []format.Fingerprint{} - } else { - - return append(deadkm.recvKeysFingerprint, deadkm.recvReKeysFingerprint...) - - } -} - -func (rkmb *ReceptionKeyManagerBuffer) getCurrentReceptionKeyManager() *KeyManager { - return rkmb.managers[rkmb.loc] -} - -func (rkmb *ReceptionKeyManagerBuffer) getCurrentLoc() int { - return rkmb.loc -} - -func (rkmb *ReceptionKeyManagerBuffer) getReceptionKeyManagerAtLoc(n int) *KeyManager { - return rkmb.managers[n%ReceptionKeyManagerBufferLength] -} - -func (rkmb *ReceptionKeyManagerBuffer) GobEncode() ([]byte, error) { - - //get rid of nils for encoding - var bufferSlice []*KeyManager - - for i := 0; i < len(rkmb.managers); i++ { - j := (rkmb.loc + i) % len(rkmb.managers) - if rkmb.managers[j] != nil { - bufferSlice = append(bufferSlice, rkmb.managers[j]) - } - - } - - anon := struct { - Managers []*KeyManager - Loc int - }{ - bufferSlice, - rkmb.loc, - } - - var encodeBytes bytes.Buffer - - enc := gob.NewEncoder(&encodeBytes) - - err := enc.Encode(anon) - - if err != nil { - err = errors.New(fmt.Sprintf("Could not encode Reception Keymanager Buffer: %s", - err.Error())) - return nil, err - } - return encodeBytes.Bytes(), nil - -} - -func (rkmb *ReceptionKeyManagerBuffer) GobDecode(in []byte) error { - - anon := struct { - Managers []*KeyManager - Loc int - }{} - - var buf bytes.Buffer - - // Write bytes to the buffer - buf.Write(in) - - dec := gob.NewDecoder(&buf) - - err := dec.Decode(&anon) - - if err != nil { - err = errors.New(fmt.Sprintf("Could not Decode Reception Keymanager Buffer: %s", err.Error())) - return err - } - - rkmb.loc = anon.Loc - - for i := 0; i < len(anon.Managers); i++ { - j := (anon.Loc + i) % len(rkmb.managers) - rkmb.managers[j] = anon.Managers[i] - } - - return nil -} diff --git a/keyStore/recieveKeyManagerBuffer_test.go b/keyStore/recieveKeyManagerBuffer_test.go deleted file mode 100644 index e4680eeb8a451b31ea42950a094eb78e5754b578..0000000000000000000000000000000000000000 --- a/keyStore/recieveKeyManagerBuffer_test.go +++ /dev/null @@ -1,140 +0,0 @@ -package keyStore - -import ( - "bytes" - "encoding/gob" - "gitlab.com/xx_network/primitives/id" - "testing" -) - -// Test that the buffer is recieving objects and that it is in fact circular -func TestPush(t *testing.T) { - aBuffer := NewReceptionKeyManagerBuffer() - - grp := initGroup() - baseKey := grp.NewInt(57) - partner := id.NewIdFromUInt(14, id.User, t) - userID := id.NewIdFromUInt(18, id.User, t) - - //Generate twice the amount of keymanagers so we can test the circularness of the buffer as well - kmArray := []KeyManager{} - for i := 0; i < ReceptionKeyManagerBufferLength*2; i++ { - newKm := *NewManager(baseKey, nil, nil, - partner, false, 12, 10, 10) - - newKm.GenerateKeys(grp, userID) - kmArray = append(kmArray, newKm) - - toDelete := aBuffer.push(&newKm) - println("delete %v", toDelete) - if i < ReceptionKeyManagerBufferLength { - if len(toDelete) != 0 { - //ERROR should have something - t.Errorf("Error Nothing Should Be Returned to be deleted since" + - " keybuffer should be filling up from empty state") - } - - if &newKm != aBuffer.getCurrentReceptionKeyManager() { - t.Errorf("Error incorrect Keymanager receieved from buffer.") - } - - } else { - if len(toDelete) == 0 { - t.Errorf("Error not returning old keymanager to properly be disposed of") - } - - if &newKm != aBuffer.getCurrentReceptionKeyManager() { - t.Errorf("Error incorrect Keymanager receieved from buffer after its been filled up.") - } - } - - } - - if &kmArray[0] == &kmArray[1] { - t.Errorf("Error tests fail because we are not creating a new Keymanager") - } - -} - -//test that loc is always circular and outputted value is what is expected -func TestReceptionKeyManagerBuffer_getCurrentLoc(t *testing.T) { - aBuffer := NewReceptionKeyManagerBuffer() - - if aBuffer.getCurrentLoc() != 0 { - // Error location is not initialized as zero - t.Errorf("Error ReceptionKeyManagerBuffer Loc not initialized to zero") - } - - for i := 0; i < ReceptionKeyManagerBufferLength*2; i++ { - - aBuffer.push(&KeyManager{}) - - if aBuffer.getCurrentLoc() != aBuffer.loc { - //error mismatch between actual loc and returned loc - t.Errorf("Error ReceptionKeyManagerBuffer Loc mismatch with Getfunction") - } - - if aBuffer.loc > ReceptionKeyManagerBufferLength || aBuffer.loc < 0 { - //Error Buffer Out of bounds - t.Errorf("Error ReceptionKeyManagerBuffer Loc out of bounds error") - } - - if aBuffer.loc != (i % ReceptionKeyManagerBufferLength) { - //Error location is not circular - - t.Errorf("Error ReceptionKeyManagerBuffer Loc is not circular") - } - } - -} - -func TestReceptionKeyManagerBuffer_getCurrentReceptionKeyManager(t *testing.T) { - aBuffer := NewReceptionKeyManagerBuffer() - testManager := &KeyManager{} - aBuffer.push(testManager) - - if aBuffer.getCurrentReceptionKeyManager() != testManager { - t.Errorf("Error this is not the same manager pushed in.") - } -} - -func TestNewReceptionKeyManagerBuffer(t *testing.T) { - aBuffer := NewReceptionKeyManagerBuffer() - - if aBuffer == nil { - t.Errorf("Error creating new reception keymanager buffer returning nil") - } -} - -func TestReceptionKeyManagerBuffer_Gob(t *testing.T) { - aBuffer := NewReceptionKeyManagerBuffer() - grp := initGroup() - baseKey := grp.NewInt(57) - partner := id.NewIdFromUInt(14, id.User, t) - userID := id.NewIdFromUInt(18, id.User, t) - - newKm := *NewManager(baseKey, nil, - nil, partner, - false, 12, 10, 10) - - newKm.GenerateKeys(grp, userID) - - aBuffer.push(&newKm) - - var byteBuf bytes.Buffer - - enc := gob.NewEncoder(&byteBuf) - dec := gob.NewDecoder(&byteBuf) - - err := enc.Encode(aBuffer) - - if err != nil { - t.Errorf("Failed to encode GOB KeyManagerBuffer: %s", err) - } - - newBuffer := NewReceptionKeyManagerBuffer() - err = dec.Decode(&newBuffer) - if err != nil { - t.Errorf("Failed to decode GOB KeyManagerBuffer: %s", err) - } -} diff --git a/keyStore/rekeyManager.go b/keyStore/rekeyManager.go deleted file mode 100644 index 64e87ef466dfde92e449a94b1a152d9c1d78f5b5..0000000000000000000000000000000000000000 --- a/keyStore/rekeyManager.go +++ /dev/null @@ -1,80 +0,0 @@ -package keyStore - -import ( - "gitlab.com/elixxir/crypto/cyclic" - "gitlab.com/xx_network/primitives/id" - "sync" -) - -type RekeyContext struct { - BaseKey *cyclic.Int - PrivKey *cyclic.Int - PubKey *cyclic.Int -} - -type RekeyKeys struct { - CurrPrivKey *cyclic.Int - CurrPubKey *cyclic.Int - NewPrivKey *cyclic.Int - NewPubKey *cyclic.Int -} - -func (k *RekeyKeys) RotateKeysIfReady() { - if k.NewPrivKey != nil && k.NewPubKey != nil { - k.CurrPrivKey = k.NewPrivKey - k.CurrPubKey = k.NewPubKey - k.NewPrivKey = nil - k.NewPubKey = nil - } -} - -type RekeyManager struct { - Ctxs map[id.ID]*RekeyContext - Keys map[id.ID]*RekeyKeys - lock sync.Mutex -} - -func NewRekeyManager() *RekeyManager { - return &RekeyManager{ - Ctxs: make(map[id.ID]*RekeyContext), - Keys: make(map[id.ID]*RekeyKeys), - } -} - -func (rkm *RekeyManager) AddCtx(partner *id.ID, - ctx *RekeyContext) { - rkm.lock.Lock() - defer rkm.lock.Unlock() - rkm.Ctxs[*partner] = ctx -} - -func (rkm *RekeyManager) GetCtx(partner *id.ID) *RekeyContext { - rkm.lock.Lock() - defer rkm.lock.Unlock() - return rkm.Ctxs[*partner] -} - -func (rkm *RekeyManager) DeleteCtx(partner *id.ID) { - rkm.lock.Lock() - defer rkm.lock.Unlock() - delete(rkm.Ctxs, *partner) -} - -func (rkm *RekeyManager) AddKeys(partner *id.ID, - keys *RekeyKeys) { - rkm.lock.Lock() - defer rkm.lock.Unlock() - rkm.Keys[*partner] = keys -} - -func (rkm *RekeyManager) GetKeys(partner *id.ID) *RekeyKeys { - rkm.lock.Lock() - defer rkm.lock.Unlock() - return rkm.Keys[*partner] -} - -func (rkm *RekeyManager) DeleteKeys(partner *id.ID) { - rkm.lock.Lock() - defer rkm.lock.Unlock() - delete(rkm.Keys, *partner) -} diff --git a/keyStore/rekeyManager_test.go b/keyStore/rekeyManager_test.go deleted file mode 100644 index a41a5ed5a2289b9ee3e80d817a76f298c6e29922..0000000000000000000000000000000000000000 --- a/keyStore/rekeyManager_test.go +++ /dev/null @@ -1,137 +0,0 @@ -package keyStore - -import ( - "gitlab.com/elixxir/crypto/cyclic" - "gitlab.com/elixxir/crypto/large" - "gitlab.com/xx_network/primitives/id" - "testing" -) - -// Test creation of RekeyManager -func TestRekeyManager_New(t *testing.T) { - rkm := NewRekeyManager() - - if rkm == nil { - t.Errorf("NewRekeyManager returned nil") - } -} - -// Test all Ctx related functions of RekeyManager -func TestRekeyManager_Ctx(t *testing.T) { - grp := cyclic.NewGroup(large.NewInt(107), large.NewInt(2)) - baseKey := grp.NewInt(57) - privKey := grp.NewInt(5) - pubKey := grp.NewInt(42) - partner := id.NewIdFromUInt(14, id.User, t) - userID := id.NewIdFromUInt(18, id.User, t) - rkm := NewRekeyManager() - - val := &RekeyContext{ - BaseKey: baseKey, - PrivKey: privKey, - PubKey: pubKey, - } - - // Add RekeyContext to map - rkm.AddCtx(partner, val) - - // Confirm different partner returns nil - actual := rkm.GetCtx(userID) - - if actual != nil { - t.Errorf("GetCtx returned something but expected nil") - } - - // Get added value and compare - actual = rkm.GetCtx(partner) - - if actual == nil { - t.Errorf("GetCtx returned nil") - } else if actual.BaseKey.Cmp(baseKey) != 0 { - t.Errorf("BaseKey doesn't match for RekeyContext added to Contexts map") - } else if actual.PrivKey.Cmp(privKey) != 0 { - t.Errorf("PrivKey doesn't match for RekeyContext added to Contexts map") - } else if actual.PubKey.Cmp(pubKey) != 0 { - t.Errorf("PubKey doesn't match for RekeyContext added to Contexts map") - } - - // Delete value and confirm it's gone - rkm.DeleteCtx(partner) - - actual = rkm.GetCtx(partner) - - if actual != nil { - t.Errorf("GetCtx returned something but expected nil after deletion") - } -} - -// Test all Keys related functions of RekeyManager -func TestRekeyManager_Keys(t *testing.T) { - grp := cyclic.NewGroup(large.NewInt(107), large.NewInt(2)) - privKey := grp.NewInt(5) - pubKey := grp.NewInt(42) - partner := id.NewIdFromUInt(14, id.User, t) - userID := id.NewIdFromUInt(18, id.User, t) - rkm := NewRekeyManager() - - val := &RekeyKeys{ - CurrPrivKey: privKey, - CurrPubKey: pubKey, - } - - // Add RekeyKeys to map - rkm.AddKeys(partner, val) - - // Confirm different partner returns nil - actual := rkm.GetKeys(userID) - - if actual != nil { - t.Errorf("GetNodeKeys returned something but expected nil") - } - - // Get added value and compare - actual = rkm.GetKeys(partner) - - if actual == nil { - t.Errorf("GetNodeKeys returned nil") - } else if actual.CurrPrivKey.Cmp(privKey) != 0 { - t.Errorf("CurrPrivKey doesn't match for RekeyKeys added to Keys map") - } else if actual.CurrPubKey.Cmp(pubKey) != 0 { - t.Errorf("CurrPubKey doesn't match for RekeyKeys added to Keys map") - } - - // Delete value and confirm it's gone - rkm.DeleteKeys(partner) - - actual = rkm.GetKeys(partner) - - if actual != nil { - t.Errorf("GetNodeKeys returned something but expected nil after deletion") - } - - // Confirm RekeyKeys behavior of key rotation - newPrivKey := grp.NewInt(7) - newPubKey := grp.NewInt(91) - - // Add new PrivKey - val.NewPrivKey = newPrivKey - - // Call rotate and confirm nothing changes - val.RotateKeysIfReady() - - if val.CurrPrivKey.Cmp(privKey) != 0 { - t.Errorf("CurrPrivKey doesn't match for RekeyKeys after adding new PrivateKey") - } else if val.CurrPubKey.Cmp(pubKey) != 0 { - t.Errorf("CurrPubKey doesn't match for RekeyKeys after adding new PrivateKey") - } - - // Add new PubKey, rotate, and confirm keys change - val.NewPubKey = newPubKey - val.RotateKeysIfReady() - - if val.CurrPrivKey.Cmp(newPrivKey) != 0 { - t.Errorf("CurrPrivKey doesn't match for RekeyKeys after key rotation") - } else if val.CurrPubKey.Cmp(newPubKey) != 0 { - t.Errorf("CurrPubKey doesn't match for RekeyKeys after key rotation") - } -} diff --git a/rekey/rekey.go b/rekey/rekey.go index fe5997a6db20a46ff737128ed5d542e606725e94..09554a0fe0370b1cde586fc75f85c6bae094448a 100644 --- a/rekey/rekey.go +++ b/rekey/rekey.go @@ -3,9 +3,9 @@ package rekey import ( "bytes" "fmt" - "gitlab.com/elixxir/client/cmixproto" "gitlab.com/elixxir/client/globals" "gitlab.com/elixxir/client/io" + "gitlab.com/elixxir/client/io/keyExchange" "gitlab.com/elixxir/client/keyStore" "gitlab.com/elixxir/client/parse" "gitlab.com/elixxir/client/storage" @@ -112,7 +112,7 @@ func InitRekey(s user.Session, s2 storage.Session, m io.Communications, } l.Register(userData.ThisUser.User, - int32(cmixproto.Type_REKEY_TRIGGER), + int32(keyExchange.Type_REKEY_TRIGGER), &rekeyTriggerList) // TODO(nen) Wouldn't it be possible to register these listeners based // solely on the inner type? maybe the switchboard can rebroadcast @@ -120,10 +120,10 @@ func InitRekey(s user.Session, s2 storage.Session, m io.Communications, // possible // in short, switchboard should be the package that includes outer l.Register(&id.ZeroUser, - int32(cmixproto.Type_NO_TYPE), + int32(keyExchange.Type_NO_TYPE), &rekeyList) l.Register(&id.ZeroUser, - int32(cmixproto.Type_REKEY_CONFIRM), + int32(keyExchange.Type_REKEY_CONFIRM), &rekeyConfirmList) } @@ -280,7 +280,7 @@ func rekeyProcess(rt rekeyType, partner *id.ID, data []byte) error { h.Write(ctx.BaseKey.Bytes()) baseKeyHash := h.Sum(nil) msg := parse.Pack(&parse.TypedBody{ - MessageType: int32(cmixproto.Type_REKEY_CONFIRM), + MessageType: int32(keyExchange.Type_REKEY_CONFIRM), Body: baseKeyHash, }) return comms.SendMessage(session, topology, partner, parse.None, msg, transmissionHost) diff --git a/rekey/rekey_test.go b/rekey/rekey_test.go index 6ed7ffc7515941af7817041bb613e95b9781f221..119ad6f5c2538c1da4729cda647b67fc19445a3c 100644 --- a/rekey/rekey_test.go +++ b/rekey/rekey_test.go @@ -4,8 +4,8 @@ import ( "bytes" "encoding/binary" "fmt" - "gitlab.com/elixxir/client/cmixproto" "gitlab.com/elixxir/client/globals" + "gitlab.com/elixxir/client/io/keyExchange" "gitlab.com/elixxir/client/keyStore" "gitlab.com/elixxir/client/parse" "gitlab.com/elixxir/client/storage" @@ -170,7 +170,7 @@ func TestRekeyTrigger(t *testing.T) { msg := &parse.Message{ Sender: userData.ThisUser.User, TypedBody: parse.TypedBody{ - MessageType: int32(cmixproto.Type_REKEY_TRIGGER), + MessageType: int32(keyExchange.Type_REKEY_TRIGGER), Body: partnerPubKey.Bytes(), }, InferredType: parse.None, @@ -203,7 +203,7 @@ func TestRekeyTrigger(t *testing.T) { msg = &parse.Message{ Sender: userData.ThisUser.User, TypedBody: parse.TypedBody{ - MessageType: int32(cmixproto.Type_REKEY_TRIGGER), + MessageType: int32(keyExchange.Type_REKEY_TRIGGER), Body: partnerPubKey.Bytes(), }, InferredType: parse.None, @@ -229,7 +229,7 @@ func TestRekeyConfirm(t *testing.T) { msg := &parse.Message{ Sender: partnerID, TypedBody: parse.TypedBody{ - MessageType: int32(cmixproto.Type_REKEY_CONFIRM), + MessageType: int32(keyExchange.Type_REKEY_CONFIRM), Body: baseKey.Bytes(), }, InferredType: parse.None, @@ -248,7 +248,7 @@ func TestRekeyConfirm(t *testing.T) { msg = &parse.Message{ Sender: partnerID, TypedBody: parse.TypedBody{ - MessageType: int32(cmixproto.Type_REKEY_CONFIRM), + MessageType: int32(keyExchange.Type_REKEY_CONFIRM), Body: h.Sum(nil), }, InferredType: parse.None, @@ -274,7 +274,7 @@ func TestRekeyConfirm(t *testing.T) { msg = &parse.Message{ Sender: partnerID, TypedBody: parse.TypedBody{ - MessageType: int32(cmixproto.Type_REKEY_CONFIRM), + MessageType: int32(keyExchange.Type_REKEY_CONFIRM), Body: h.Sum(nil), }, InferredType: parse.None, @@ -303,7 +303,7 @@ func TestRekey(t *testing.T) { msg := &parse.Message{ Sender: partnerID, TypedBody: parse.TypedBody{ - MessageType: int32(cmixproto.Type_NO_TYPE), + MessageType: int32(keyExchange.Type_NO_TYPE), Body: pubKey.Bytes(), }, InferredType: parse.Rekey, @@ -364,7 +364,7 @@ func TestRekey_Errors(t *testing.T) { msg := &parse.Message{ Sender: userData.ThisUser.User, TypedBody: parse.TypedBody{ - MessageType: int32(cmixproto.Type_REKEY_TRIGGER), + MessageType: int32(keyExchange.Type_REKEY_TRIGGER), Body: partnerPubKey.Bytes(), }, InferredType: parse.None, @@ -381,7 +381,7 @@ func TestRekey_Errors(t *testing.T) { msg = &parse.Message{ Sender: partnerID, TypedBody: parse.TypedBody{ - MessageType: int32(cmixproto.Type_NO_TYPE), + MessageType: int32(keyExchange.Type_NO_TYPE), Body: []byte{}, }, InferredType: parse.Rekey, diff --git a/storage/cmix/store_test.go b/storage/cmix/store_test.go index 957ad418535d708cf87a424840531b8d9af9a4dc..49a00033697f42bdf0cf6eab5fd37331de7c25a8 100644 --- a/storage/cmix/store_test.go +++ b/storage/cmix/store_test.go @@ -43,21 +43,13 @@ func TestStore_AddRemove(t *testing.T) { nodeId := id.NewIdFromString("test", id.Node, t) key := testStore.grp.NewInt(5) - err := testStore.Add(nodeId, key) - if err != nil { - t.Errorf("Unable to add node key: %+v", err) - return - } + testStore.Add(nodeId, key) if _, exists := testStore.nodes[*nodeId]; !exists { t.Errorf("Failed to add node key") return } - err = testStore.Remove(nodeId) - if err != nil { - t.Errorf("Unable to remove node key: %+v", err) - return - } + testStore.Remove(nodeId) if _, exists := testStore.nodes[*nodeId]; exists { t.Errorf("Failed to remove node key") return @@ -66,8 +58,6 @@ func TestStore_AddRemove(t *testing.T) { // Missing keys path func TestStore_GetRoundKeys_Missing(t *testing.T) { - var err error - // Set up the circuit numIds := 10 nodeIds := make([]*id.ID, numIds) @@ -77,10 +67,7 @@ func TestStore_GetRoundKeys_Missing(t *testing.T) { // Only add every other node so there are missing nodes if i%2 == 0 { - err = testStore.Add(nodeIds[i], key) - if err != nil { - t.Errorf("Unable to add node key: %+v", err) - } + testStore.Add(nodeIds[i], key) } } diff --git a/storage/collate/messagePart.go b/storage/collate/messagePart.go new file mode 100644 index 0000000000000000000000000000000000000000..8a0686cf7ea81f4d3d5d2d51383ff32cd0bab099 --- /dev/null +++ b/storage/collate/messagePart.go @@ -0,0 +1,70 @@ +package collate + +import ( + "encoding/binary" + "gitlab.com/elixxir/client/context/message" +) + +const typeLen = message.TypeLen +const idLen = 4 +const partLen = 1 +const numPartsLen = 1 +const headerLen = typeLen + idLen + partLen + numPartsLen + +type messagePart struct { + Data []byte + Type []byte + Id []byte + Part []byte + NumParts []byte + Contents []byte +} + +func newMessage(mt message.Type, id uint32, part uint8, numParts uint8, contents []byte) messagePart { + data := make([]byte, len(contents)+headerLen) + + m := Unmarshal(data) + + binary.BigEndian.PutUint32(m.Type, uint32(mt)) + binary.BigEndian.PutUint32(m.Id, id) + m.Part[0] = part + m.NumParts[0] = numParts + copy(m.Contents, contents) + return m +} + +func Unmarshal(data []byte) messagePart { + m := messagePart{ + Data: data, + Type: data[:typeLen], + Id: data[typeLen : typeLen+idLen], + Part: data[typeLen+idLen : typeLen+idLen+partLen], + NumParts: data[typeLen+idLen+partLen : typeLen+idLen+partLen+numPartsLen], + Contents: data[typeLen+idLen+partLen+numPartsLen:], + } + return m +} + +func (m messagePart) GetType() message.Type { + return message.Type(binary.BigEndian.Uint32(m.Type)) +} + +func (m messagePart) GetID() uint32 { + return binary.BigEndian.Uint32(m.Id) +} + +func (m messagePart) GetPart() uint8 { + return m.Part[0] +} + +func (m messagePart) GetNumParts() uint8 { + return m.NumParts[0] +} + +func (m messagePart) GetContents() []byte { + return m.Contents +} + +func (m messagePart) Marshal() []byte { + return m.Data +} diff --git a/storage/collate/multiPartMessage.go b/storage/collate/multiPartMessage.go new file mode 100644 index 0000000000000000000000000000000000000000..6dfc534230961925d2fabce7f8e03f6ad31cfa12 --- /dev/null +++ b/storage/collate/multiPartMessage.go @@ -0,0 +1,16 @@ +package collate + +import ( + "gitlab.com/elixxir/client/context/message" + "time" +) + +type multiPartMessage struct { + messageID uint64 + numParts uint8 + presentParts uint8 + timestamp time.Time + messageType message.Type + + parts [][]byte +} diff --git a/storage/collate/store.go b/storage/collate/store.go new file mode 100644 index 0000000000000000000000000000000000000000..faf31b2c5330ac94d4c8c2d64da6428a7690b8de --- /dev/null +++ b/storage/collate/store.go @@ -0,0 +1,19 @@ +package collate + +import ( + "crypto/md5" + "encoding/binary" + "gitlab.com/xx_network/primitives/id" +) + +type multiPartID [16]byte + +type Store struct { + multiparts map[multiPartID]multiPartMessage +} + +func getMultiPartID(partner *id.ID, messageID uint64) multiPartID { + b := make([]byte, 8) + binary.BigEndian.PutUint64(b, messageID) + return md5.Sum(append(partner[:], b...)) +} diff --git a/storage/conversation/partner.go b/storage/conversation/partner.go new file mode 100644 index 0000000000000000000000000000000000000000..bae8aa98123df248b1a85d19499a48afc917b71e --- /dev/null +++ b/storage/conversation/partner.go @@ -0,0 +1,174 @@ +package conversation + +import ( + "encoding/json" + "github.com/pkg/errors" + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/client/storage/versioned" + "gitlab.com/xx_network/primitives/id" + "math" + "strings" + "sync" + "time" +) + +const conversationKeyPrefix = "conversation" +const currentConversationVersion = 0 +const bottomRegion = math.MaxUint32 / 4 +const topRegion = bottomRegion * 3 + +type Conversation struct { + // Public & stored data + lastReceivedID uint32 + numReceivedRevolutions uint32 + nextSentID uint64 + + // Private, unstored data + partner *id.ID + kv *versioned.KV + mux sync.Mutex +} + +type conversationDisk struct { + // Public & stored data + LastReceivedID uint32 + NumReceivedRevolutions uint32 + NextSendID uint64 +} + +// Returns the Conversation if it can be found, otherwise returns a new partner +func LoadOrMakeConversation(kv *versioned.KV, partner *id.ID) *Conversation { + + c, err := loadConversation(kv, partner) + if err != nil && !strings.Contains(err.Error(), "Failed to Load conversation") { + jww.FATAL.Panicf("Failed to loadOrMakeConversation: %s", err) + } + + if c == nil { + c = &Conversation{ + lastReceivedID: 0, + numReceivedRevolutions: 0, + nextSentID: 0, + partner: partner, + kv: kv, + } + if err = c.save(); err != nil { + jww.FATAL.Panicf("Failed to save new conversation: %s", err) + } + } + + return c +} + +// Finds the full 64 bit message ID and updates the internal last message ID if +// the new ID is newer +func (c *Conversation) ProcessReceivedMessageID(mid uint32, timestamp time.Time) uint64 { + c.mux.Lock() + defer c.mux.Unlock() + var high uint32 + switch cmp(c.lastReceivedID, mid) { + case 1: + c.numReceivedRevolutions++ + c.lastReceivedID = mid + if err := c.save(); err != nil { + jww.FATAL.Panicf("Failed to save after updating Last "+ + "Received ID in a conversation: %s", err) + } + high = c.numReceivedRevolutions + case 0: + high = c.numReceivedRevolutions + case -1: + high = c.numReceivedRevolutions - 1 + } + + return (uint64(high) << 16) | uint64(mid) +} + +func cmp(a, b uint32) int { + if a > topRegion && b < bottomRegion { + return 1 + } else if a < bottomRegion && b > topRegion { + return -1 + } + return 0 +} + +//returns the next sendID in both full and truncated formats +func (c *Conversation) GetNextSendID() (uint64, uint32) { + c.mux.Lock() + old := c.nextSentID + c.nextSentID++ + if err := c.save(); err != nil { + jww.FATAL.Panicf("Failed to save after incrementing the sendID: "+ + "%s", err) + } + defer c.mux.Unlock() + return old, uint32(old & 0x00000000FFFFFFFF) +} + +func loadConversation(kv *versioned.KV, partner *id.ID) (*Conversation, error) { + key := makeConversationKey(partner) + + obj, err := kv.Get(key) + if err != nil { + return nil, errors.WithMessage(err, "Failed to Load conversation") + } + + c := &Conversation{ + partner: partner, + kv: kv, + } + + if err = c.unmarshal(obj.Data); err != nil { + return nil, errors.WithMessage(err, "Failed to Load conversation") + } + + return c, nil +} + +func (c *Conversation) save() error { + data, err := c.marshal() + if err != nil { + return err + } + + obj := versioned.Object{ + Version: currentConversationVersion, + Timestamp: time.Now(), + Data: data, + } + + key := makeConversationKey(c.partner) + return c.kv.Set(key, &obj) +} + +func (c *Conversation) unmarshal(b []byte) error { + cd := conversationDisk{} + + if err := json.Unmarshal(b, &cd); err != nil { + return errors.Wrap(err, "Failed to Unmarshal Conversation") + } + + c.lastReceivedID = cd.LastReceivedID + c.numReceivedRevolutions = cd.NumReceivedRevolutions + c.nextSentID = cd.NextSendID + + return nil +} + +func (c *Conversation) marshal() ([]byte, error) { + cd := conversationDisk{} + cd.LastReceivedID = c.lastReceivedID + cd.NumReceivedRevolutions = c.numReceivedRevolutions + cd.NextSendID = c.nextSentID + + b, err := json.Marshal(&cd) + if err != nil { + return nil, errors.Wrap(err, "Failed to unmarshal conversation") + } + return b, nil +} + +func makeConversationKey(partner *id.ID) string { + return conversationKeyPrefix + ":" + partner.String() +} diff --git a/storage/conversation/store.go b/storage/conversation/store.go new file mode 100644 index 0000000000000000000000000000000000000000..be33d73f464c6e40dd027dc928129217d8d2f250 --- /dev/null +++ b/storage/conversation/store.go @@ -0,0 +1,39 @@ +package conversation + +import ( + "gitlab.com/elixxir/client/storage/versioned" + "gitlab.com/xx_network/primitives/id" + "sync" +) + +type Store struct { + loadedConversations map[id.ID]*Conversation + kv *versioned.KV + mux sync.RWMutex +} + +//Returns a new conversation store made off of the KV +func NewStore(kv *versioned.KV) *Store { + return &Store{ + loadedConversations: make(map[id.ID]*Conversation), + kv: kv, + } +} + +// Gets the conversation with the partner from ram if it is there, otherwise +// loads it from disk +func (s *Store) Get(partner *id.ID) *Conversation { + s.mux.RLock() + c, ok := s.loadedConversations[*partner] + s.mux.RUnlock() + if !ok { + s.mux.Lock() + c, ok = s.loadedConversations[*partner] + if !ok { + c = LoadOrMakeConversation(s.kv, partner) + s.loadedConversations[*partner] = c + } + s.mux.Unlock() + } + return c +} diff --git a/storage/e2e/manager.go b/storage/e2e/manager.go index a0938315b443c74d1f103212876a31914497b59c..c4dd2ce21437fb2095a57aa7749daad0d28dc15d 100644 --- a/storage/e2e/manager.go +++ b/storage/e2e/manager.go @@ -6,6 +6,7 @@ import ( "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/xx_network/primitives/id" jww "github.com/spf13/jwalterweatherman" + dh "gitlab.com/elixxir/crypto/diffieHellman" ) type Manager struct { @@ -28,11 +29,13 @@ func newManager(ctx *context, partnerID *id.ID, myPrivKey *cyclic.Int, m.send = NewSessionBuff(m, "send") m.receive = NewSessionBuff(m, "receive") - sendSession := newSession(m, myPrivKey, partnerPubKey, sendParams, Send, SessionID{}) + sendSession := newSession(m, myPrivKey, partnerPubKey, nil, + sendParams, Send, SessionID{}) m.send.AddSession(sendSession) - receiveSession := newSession(m, myPrivKey, partnerPubKey, receiveParams, Receive, SessionID{}) + receiveSession := newSession(m, myPrivKey, partnerPubKey, nil, + receiveParams, Receive, SessionID{}) m.receive.AddSession(receiveSession) @@ -73,17 +76,28 @@ func (m *Manager) GetPartnerID() *id.ID { // creates a new receive session using the latest private key this user has sent // and the new public key received from the partner. -func (m *Manager) NewReceiveSession(partnerPubKey *cyclic.Int, params SessionParams, trigger SessionID) *Session { - //find your last confirmed private key - myPrivKey := m.send.GetNewestRekeyableSession().GetMyPrivKey() +// If the session already exists, it will not be overwritten and the extant +// session will be returned, with the bool set to true denoting a duplicate. +// This is so duplicate key exchange triggering can be supported +func (m *Manager) NewReceiveSession(partnerPubKey *cyclic.Int, params SessionParams, + trigger *Session) (*Session, bool) { + + //check if the session already exists + baseKey := dh.GenerateSessionKey(trigger.myPrivKey, partnerPubKey, m.ctx.grp) + sessionID := getSessionIDFromBaseKey(baseKey) + + if s := m.receive.GetByID(sessionID); s != nil { + return s, true + } - //create the session - session := newSession(m, myPrivKey, partnerPubKey, params, Receive, trigger) + //create the session but do not save + session := newSession(m, trigger.myPrivKey, partnerPubKey, baseKey, params, Receive, + trigger.GetID()) //add the session to the buffer m.receive.AddSession(session) - return session + return session, false } // creates a new receive session using the latest public key received from the @@ -95,7 +109,8 @@ func (m *Manager) NewSendSession(myPrivKey *cyclic.Int, params SessionParams, tr partnerPubKey := m.receive.GetNewestRekeyableSession().partnerPubKey //create the session - session := newSession(m, myPrivKey, partnerPubKey, params, Send, trigger) + session := newSession(m, myPrivKey, partnerPubKey, nil, + params, Send, trigger) //add the session to the send session buffer and return m.send.AddSession(session) @@ -104,7 +119,7 @@ func (m *Manager) NewSendSession(myPrivKey *cyclic.Int, params SessionParams, tr } // gets the correct session to send with depending on the type of send -func (m *Manager) GetSendingSession(st params.SendType) *Session { +func (m *Manager) GetSessionForSending(st params.SendType) *Session { switch st { case params.Standard: return m.send.GetSessionForSending() @@ -118,6 +133,16 @@ func (m *Manager) GetSendingSession(st params.SendType) *Session { return nil } +// gets the send session of the passed ID. Returns nil if no session is found +func (m *Manager) GetSendSession(sessionID SessionID) *Session { + return m.send.GetByID(sessionID) +} + +// gets the receive session of the passed ID. Returns nil if no session is found +func (m *Manager) GetReceiveSession(sessionID SessionID) *Session { + return m.receive.GetByID(sessionID) +} + // Confirms a send session is known about by the partner func (m *Manager) Confirm(sid SessionID) error { return m.send.Confirm(sid) diff --git a/storage/e2e/session.go b/storage/e2e/session.go index f5d313170b05f75a1f25c69832989daada34bf64..43446a9867e3e9ae0cccf0e2c8211c7830a020cc 100644 --- a/storage/e2e/session.go +++ b/storage/e2e/session.go @@ -39,7 +39,6 @@ type Session struct { // if a receive session trigger SessionID - //denotes if the other party has confirmed this key negotiationStatus Negotiation @@ -81,22 +80,21 @@ type SessionDisk struct { /*CONSTRUCTORS*/ //Generator which creates all keys and structures -func newSession(manager *Manager, myPrivKey *cyclic.Int, - partnerPubKey *cyclic.Int, params SessionParams, t SessionType, - trigger SessionID) *Session { +func newSession(manager *Manager, myPrivKey, partnerPubKey, baseKey *cyclic.Int, + params SessionParams, t SessionType, trigger SessionID) *Session { confirmation := Unconfirmed if t == Receive { confirmation = Confirmed } - session := &Session{ params: params, manager: manager, t: t, myPrivKey: myPrivKey, partnerPubKey: partnerPubKey, + baseKey: baseKey, negotiationStatus: confirmation, trigger: trigger, } @@ -201,16 +199,21 @@ func (s *Session) GetTrigger() SessionID { return s.trigger } -//Blake2B hash of base key used for storage -func (s *Session) GetID() SessionID { +//underlying definition of session id +func getSessionIDFromBaseKey(baseKey *cyclic.Int) SessionID { // no lock is needed because this cannot be edited sid := SessionID{} h, _ := hash.NewCMixHash() - h.Write(s.baseKey.Bytes()) + h.Write(baseKey.Bytes()) copy(sid[:], h.Sum(nil)) return sid } +//Blake2B hash of base key used for storage +func (s *Session) GetID() SessionID { + return getSessionIDFromBaseKey(s.baseKey) +} + // returns the ID of the partner for this session func (s *Session) GetPartner() *id.ID { return s.manager.partner @@ -263,14 +266,12 @@ func (s *Session) unmarshal(b []byte) error { s.ttl = sd.TTL copy(s.trigger[:], sd.Trigger) - statesKey := makeStateVectorKey(keyEKVPrefix, s.GetID()) s.keyState, err = loadStateVector(s.manager.ctx, statesKey) if err != nil { return err } - return nil } @@ -322,6 +323,7 @@ func (s *Session) Status() Status { // Sets the negotiation status, this tracks the state of the key negotiation, // only certain movements are allowed // Unconfirmed <--> Sending --> Sent --> Confirmed <--> NewSessionTriggered --> NewSessionCreated +// --------------> // // Saves the session unless the status is sending so that on reload the rekey // will be redone if it was in the process of sending @@ -335,7 +337,7 @@ var legalStateChanges = [][]bool{ {false, false, false, false, false, false}, {true, false, true, true, false, false}, {false, false, false, true, false, false}, - {false, false, false, false, false, false}, + {false, false, false, false, true, false}, {false, false, false, true, false, true}, {false, false, false, false, false, false}, } @@ -431,7 +433,7 @@ func (s *Session) triggerNegotiation() bool { } // checks if the session has been confirmed -func (s *Session) ConfirmationStatus() Negotiation { +func (s *Session) NegotiationStatus() Negotiation { s.mux.RLock() defer s.mux.RUnlock() return s.negotiationStatus @@ -439,7 +441,7 @@ func (s *Session) ConfirmationStatus() Negotiation { // checks if the session has been confirmed func (s *Session) IsConfirmed() bool { - c := s.ConfirmationStatus() + c := s.NegotiationStatus() return c >= Confirmed } @@ -464,8 +466,10 @@ func (s *Session) generate() { csprng.NewSystemRNG()) } - // compute the base key - s.baseKey = dh.GenerateSessionKey(s.myPrivKey, s.partnerPubKey, grp) + // compute the base key if it is not already there + if s.baseKey != nil { + s.baseKey = dh.GenerateSessionKey(s.myPrivKey, s.partnerPubKey, grp) + } //generate ttl and keying info keysTTL, numKeys := e2e.GenerateKeyTTL(s.baseKey.GetLargeInt(), diff --git a/storage/e2e/sessionID.go b/storage/e2e/sessionID.go index c5837c2cc9ad5688d12a83b5888ed4e1c41982b6..a1e31bd25a22493e6dcd289c58429edc9edd405f 100644 --- a/storage/e2e/sessionID.go +++ b/storage/e2e/sessionID.go @@ -1,10 +1,15 @@ package e2e -import "encoding/base64" +import ( + "encoding/base64" + "github.com/pkg/errors" +) -type SessionID [32]byte +const sessionIDLen = 32 -func (sid SessionID) Bytes() []byte { +type SessionID [sessionIDLen]byte + +func (sid SessionID) Marshal() []byte { return sid[:] } @@ -12,7 +17,19 @@ func (sid SessionID) String() string { return base64.StdEncoding.EncodeToString(sid[:]) } +func (sid SessionID) Unmarshal(b []byte) error { + if len(b) != sessionIDLen { + return errors.New("SessionID of invalid length received") + } + + copy(sid[:], b) + return nil +} + + //builds the func makeSessionKey(sid SessionID) string { return sid.String() } + + diff --git a/storage/e2e/session_test.go b/storage/e2e/session_test.go index 9c7f69a76e4de260ce7ebd70594465cdc8641fd8..7c6ceab532164ad2cfdfebecc015fb4dcd0e4adb 100644 --- a/storage/e2e/session_test.go +++ b/storage/e2e/session_test.go @@ -372,7 +372,7 @@ func TestSession_GetBaseKey(t *testing.T) { func TestSession_GetID(t *testing.T) { s, _ := makeTestSession(t) id := s.GetID() - if len(id.Bytes()) == 0 { + if len(id.Marshal()) == 0 { t.Error("Zero length for session ID!") } } diff --git a/storage/regStatus.go b/storage/regStatus.go index b065e450a1389b513a33df004123b38f62bc2e54..2df2672ca81fcc51a457e21bd39ed9e1812f0f14 100644 --- a/storage/regStatus.go +++ b/storage/regStatus.go @@ -11,7 +11,6 @@ import ( "fmt" "github.com/pkg/errors" "gitlab.com/elixxir/client/storage/versioned" - "os" "time" ) @@ -55,18 +54,32 @@ func (rs RegistrationStatus) marshalBinary() []byte { return b } -// loads the registration status from disk. If the status cannot be found, it -// defaults to Not Started -func (s *Session) loadOrCreateRegStatus() error { +// creates a new registration status and stores it +func (s *Session) newRegStatus() error { + s.regStatus = NotStarted + + now := time.Now() + + obj := versioned.Object{ + Version: currentRegistrationStatusVersion, + Timestamp: now, + Data: s.regStatus.marshalBinary(), + } + + err := s.Set(registrationStatusKey, &obj) + if err != nil { + return errors.WithMessagef(err, "Failed to store new "+ + "registration status") + } + + return nil +} + +// loads registration status from disk. +func (s *Session) loadRegStatus() error { obj, err := s.Get(registrationStatusKey) if err != nil { - if os.IsNotExist(err) { - // set at not started but do not save until it is updated - s.regStatus = NotStarted - return nil - } else { - return errors.WithMessagef(err, "Failed to load registration status") - } + return errors.WithMessage(err, "Failed to load registration status") } s.regStatus = regStatusUnmarshalBinary(obj.Data) return nil diff --git a/storage/session.go b/storage/session.go index 497df93cca1c392f9ec10d3d08b753b52d952046..0d1ad47e245ae512af686c785753da9bad890f08 100644 --- a/storage/session.go +++ b/storage/session.go @@ -12,6 +12,7 @@ import ( "github.com/pkg/errors" "gitlab.com/elixxir/client/globals" "gitlab.com/elixxir/client/storage/cmix" + "gitlab.com/elixxir/client/storage/conversation" "gitlab.com/elixxir/client/storage/e2e" "gitlab.com/elixxir/client/storage/user" "gitlab.com/elixxir/client/storage/versioned" @@ -31,15 +32,14 @@ type Session struct { regStatus RegistrationStatus //sub-stores - e2e *e2e.Store - cmix *cmix.Store - user *user.User - - loaded bool + e2e *e2e.Store + cmix *cmix.Store + user *user.User + conversations *conversation.Store } // Initialize a new Session object -func Init(baseDir, password string) (*Session, error) { +func initStore(baseDir, password string) (*Session, error) { fs, err := ekv.NewFilestore(baseDir, password) var s *Session if err != nil { @@ -49,82 +49,77 @@ func Init(baseDir, password string) (*Session, error) { s = &Session{ kv: versioned.NewKV(fs), - loaded: false, - } - - err = s.loadOrCreateRegStatus() - if err != nil { - return nil, errors.WithMessage(err, - "Failed to load or create registration status") } return s, nil } // Creates new UserData in the session -func (s *Session) Create(uid *id.ID, salt []byte, rsaKey *rsa.PrivateKey, +func New(baseDir, password string, uid *id.ID, salt []byte, rsaKey *rsa.PrivateKey, isPrecanned bool, cmixDHPrivKey, e2eDHPrivKey *cyclic.Int, cmixGrp, - e2eGrp *cyclic.Group) error { - s.mux.Lock() - defer s.mux.Unlock() - if s.loaded { - return errors.New("Cannot create a session which already has one loaded") + e2eGrp *cyclic.Group) (*Session, error) { + + s, err := initStore(baseDir, password) + if err != nil { + return nil, errors.WithMessage(err, "Failed to create session") } - var err error + err = s.newRegStatus() + if err != nil { + return nil, errors.WithMessage(err, + "Create new session") + } s.user, err = user.NewUser(s.kv, uid, salt, rsaKey, isPrecanned) if err != nil { - return errors.WithMessage(err, "Failed to create Session due "+ - "to failed user creation") + return nil, errors.WithMessage(err, "Failed to create session") } s.cmix, err = cmix.NewStore(cmixGrp, s.kv, cmixDHPrivKey) if err != nil { - return errors.WithMessage(err, "Failed to create Session due "+ - "to failed cmix keystore creation") + return nil, errors.WithMessage(err, "Failed to create session") } s.e2e, err = e2e.NewStore(e2eGrp, s.kv, e2eDHPrivKey) if err != nil { - return errors.WithMessage(err, "Failed to create Session due "+ - "to failed e2e keystore creation") + return nil, errors.WithMessage(err, "Failed to create session") } - s.loaded = true - return nil + s.conversations = conversation.NewStore(s.kv) + + return s, nil } // Loads existing user data into the session -func (s *Session) Load() error { - s.mux.Lock() - defer s.mux.Unlock() - if s.loaded { - return errors.New("Cannot load a session which already has one loaded") +func Load(baseDir, password string) (*Session, error) { + s, err := initStore(baseDir, password) + if err != nil { + return nil, errors.WithMessage(err, "Failed to load Session") } - var err error + err = s.loadRegStatus() + if err != nil { + return nil, errors.WithMessage(err, "Failed to load Session") + } s.user, err = user.LoadUser(s.kv) if err != nil { - return errors.WithMessage(err, "Failed to load Session due "+ - "to failure to load user") + return nil, errors.WithMessage(err, "Failed to load Session") } s.cmix, err = cmix.LoadStore(s.kv) if err != nil { - return errors.WithMessage(err, "Failed to load Session due "+ - "to failure to load cmix keystore") + return nil, errors.WithMessage(err, "Failed to load Session") } s.e2e, err = e2e.LoadStore(s.kv) if err != nil { - return errors.WithMessage(err, "Failed to load Session due "+ - "to failure to load e2e keystore") + return nil, errors.WithMessage(err, "Failed to load Session") } - s.loaded = true - return nil + s.conversations = conversation.NewStore(s.kv) + + return s, nil } func (s *Session) User() *user.User { @@ -145,6 +140,12 @@ func (s *Session) E2e() *e2e.Store { return s.e2e } +func (s *Session) Conversations() *conversation.Store { + s.mux.RLock() + defer s.mux.RUnlock() + return s.conversations +} + // Get an object from the session func (s *Session) Get(key string) (*versioned.Object, error) { return s.kv.Get(key) diff --git a/storage/user/cryptographic.go b/storage/user/cryptographic.go index bc7eb730a3789295bdf661e1e44564e41fbb6f68..fe5d9b59ab979d6e5df20117ebdb43a129f6df31 100644 --- a/storage/user/cryptographic.go +++ b/storage/user/cryptographic.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/gob" "github.com/pkg/errors" + jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/storage/versioned" "gitlab.com/xx_network/crypto/signature/rsa" "gitlab.com/xx_network/primitives/id" @@ -21,13 +22,7 @@ type CryptographicIdentity struct { } func newCryptographicIdentity(uid *id.ID, salt []byte, rsaKey *rsa.PrivateKey, - isPrecanned bool, kv *versioned.KV) (*CryptographicIdentity, error) { - - _, err := kv.Get(cryptographicIdentityKey) - if err == nil { - return nil, errors.New("cannot create cryptographic identity " + - "when one already exists") - } + isPrecanned bool, kv *versioned.KV) *CryptographicIdentity { ci := &CryptographicIdentity{ userID: uid, @@ -36,7 +31,12 @@ func newCryptographicIdentity(uid *id.ID, salt []byte, rsaKey *rsa.PrivateKey, isPrecanned: isPrecanned, } - return ci, ci.save(kv) + if err := ci.save(kv); err != nil { + jww.FATAL.Panicf("Failed to store the new Cryptographic"+ + " Identity: %s", err) + } + + return ci } func loadCryptographicIdentity(kv *versioned.KV) (*CryptographicIdentity, error) { diff --git a/storage/user/regValidationSig.go b/storage/user/regValidationSig.go index 89593f40391c2a2d00f002c2b06071f2d7c9b5e4..0a736904d6d0fc7890795eae6d3391bba6634c4c 100644 --- a/storage/user/regValidationSig.go +++ b/storage/user/regValidationSig.go @@ -2,6 +2,7 @@ package user import ( "github.com/pkg/errors" + jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/storage/versioned" "time" ) @@ -29,13 +30,13 @@ func (u *User) loadRegistrationValidationSignature() { // Sets the Registration Validation Signature if it is not set and stores it in // the ekv -func (u *User) SetRegistrationValidationSignature(b []byte) error { +func (u *User) SetRegistrationValidationSignature(b []byte) { u.rvsMux.Lock() defer u.rvsMux.Unlock() //check if the signature already exists if u.regValidationSig != nil { - return errors.New("cannot overwrite existing Registration Validation Signature") + jww.FATAL.Panicf("cannot overwrite existing Registration Validation Signature") } obj := &versioned.Object{ @@ -46,11 +47,9 @@ func (u *User) SetRegistrationValidationSignature(b []byte) error { err := u.kv.Set(regValidationSigKey, obj) if err != nil { - return errors.WithMessage(err, "Failed to store the "+ - "Registration Validation Signature") + jww.FATAL.Panicf("Failed to store the Registration Validation "+ + "Signature: %s", err) } u.regValidationSig = b - - return nil } diff --git a/storage/user/user.go b/storage/user/user.go index 34e6f2e96b20a363b94db77700fb0dd26db1da67..7abd5f2e2962e4ec84be4d141c53472c27d9b135 100644 --- a/storage/user/user.go +++ b/storage/user/user.go @@ -24,11 +24,7 @@ type User struct { func NewUser(kv *versioned.KV, uid *id.ID, salt []byte, rsaKey *rsa.PrivateKey, isPrecanned bool) (*User, error) { - ci, err := newCryptographicIdentity(uid, salt, rsaKey, isPrecanned, kv) - if err != nil { - return nil, errors.WithMessage(err, "Failed to create user "+ - "due to failure to create cryptographic identity") - } + ci := newCryptographicIdentity(uid, salt, rsaKey, isPrecanned, kv) return &User{ci: ci, kv: kv}, nil } diff --git a/storage/user/username.go b/storage/user/username.go index 5d2052812009b02061ab52a1a0e377d433fd71a0..9127eae18731431b14cfaeec3149f5eb4d79e6b0 100644 --- a/storage/user/username.go +++ b/storage/user/username.go @@ -2,6 +2,7 @@ package user import ( "github.com/pkg/errors" + jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/storage/versioned" "time" ) @@ -33,7 +34,7 @@ func (u *User) SetUsername(username string) error { err := u.kv.Set(usernameKey, obj) if err != nil { - return errors.WithMessage(err, "Failed to store the username") + jww.FATAL.Panicf("Failed to store the username: %s", err) } u.username = username