diff --git a/bindings/notifications.go b/bindings/notifications.go index 7f393cf7271c253cf5e07771f9f570d89b69a3de..62cf55faea8eb127dddf116303a2153c41c16906 100644 --- a/bindings/notifications.go +++ b/bindings/notifications.go @@ -50,7 +50,7 @@ func (mnfmr *ManyNotificationForMeReport) Len() int { // NotificationsForMe Check if a notification received is for me // It returns a NotificationForMeReport which contains a ForMe bool stating if it is for the caller, -// a Type, and a source. These are as follows: +// a Tag, and a source. These are as follows: // TYPE SOURCE DESCRIPTION // "default" recipient user ID A message with no association // "request" sender user ID A channel request has been received diff --git a/event/event_test.go b/event/event_test.go index 8be16a67ee3526b619907214953b277c8cc6226f..c960f1374b134f5635984a3571e154fb4e901291 100644 --- a/event/event_test.go +++ b/event/event_test.go @@ -35,7 +35,7 @@ func TestEventReporting(t *testing.T) { // Send a few events evtMgr.Report(10, "Hi", "TypityType", "I'm an event") - evtMgr.Report(1, "Hi", "TypeII", "Type II errors are the worst") + evtMgr.Report(1, "Hi", "TypeII", "Tag II errors are the worst") evtMgr.Report(20, "Hi", "TypityType3", "eventy details") evtMgr.Report(22, "Hi", "TypityType4", "I'm an event 2") @@ -68,7 +68,7 @@ func TestEventReporting(t *testing.T) { evtMgr.UnregisterEventCallback("test") // Send more events evtMgr.Report(10, "Hi", "TypityType", "I'm an event") - evtMgr.Report(1, "Hi", "TypeII", "Type II errors are the worst") + evtMgr.Report(1, "Hi", "TypeII", "Tag II errors are the worst") evtMgr.Report(20, "Hi", "TypityType3", "eventy details") evtMgr.Report(22, "Hi", "TypityType4", "I'm an event 2") diff --git a/fileTransfer/ftMessages.pb.go b/fileTransfer/ftMessages.pb.go index 4bc094d03ea4cd8e3776298f8c216972c286e45e..0663a60be474e3988c9efc4173ae74413da3257a 100644 --- a/fileTransfer/ftMessages.pb.go +++ b/fileTransfer/ftMessages.pb.go @@ -35,7 +35,7 @@ type NewFileTransfer struct { unknownFields protoimpl.UnknownFields FileName string `protobuf:"bytes,1,opt,name=fileName,proto3" json:"fileName,omitempty"` // Name of the file; max 48 characters - FileType string `protobuf:"bytes,2,opt,name=fileType,proto3" json:"fileType,omitempty"` // Type of file; max 8 characters + FileType string `protobuf:"bytes,2,opt,name=fileType,proto3" json:"fileType,omitempty"` // Tag of file; max 8 characters TransferKey []byte `protobuf:"bytes,3,opt,name=transferKey,proto3" json:"transferKey,omitempty"` // 256 bit encryption key to identify the transfer TransferMac []byte `protobuf:"bytes,4,opt,name=transferMac,proto3" json:"transferMac,omitempty"` // 256 bit MAC of the entire file NumParts uint32 `protobuf:"varint,5,opt,name=numParts,proto3" json:"numParts,omitempty"` // Number of file parts diff --git a/go.mod b/go.mod index 534fe473edd20876006fca0143f2e7a05240f0ff..101ca34e6f5c908e2573a5d837cc41d95b3ac97c 100644 --- a/go.mod +++ b/go.mod @@ -13,9 +13,9 @@ require ( github.com/spf13/viper v1.7.1 gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228 gitlab.com/elixxir/comms v0.0.4-0.20220308183624-c2183e687a03 - gitlab.com/elixxir/crypto v0.0.7-0.20220325215559-7489d68d7714 + gitlab.com/elixxir/crypto v0.0.7-0.20220328164108-c72388181116 gitlab.com/elixxir/ekv v0.1.6 - gitlab.com/elixxir/primitives v0.0.3-0.20220222212109-d412a6e46623 + gitlab.com/elixxir/primitives v0.0.3-0.20220325212708-5e2a7a385db7 gitlab.com/xx_network/comms v0.0.4-0.20220311192415-d95fe8906580 gitlab.com/xx_network/crypto v0.0.5-0.20220222212031-750f7e8a01f4 gitlab.com/xx_network/primitives v0.0.4-0.20220222211843-901fa4a2d72b diff --git a/go.sum b/go.sum index 8815f9a6d0b520caec5adad3455796ca70c3c58d..59dc6cb4ac92695cb2347125b6c174328fc3b3ed 100644 --- a/go.sum +++ b/go.sum @@ -281,6 +281,12 @@ gitlab.com/elixxir/crypto v0.0.7-0.20220309234716-1ba339865787 h1:+qmsWov412+Yn7 gitlab.com/elixxir/crypto v0.0.7-0.20220309234716-1ba339865787/go.mod h1:tD6XjtQh87T2nKZL5I/pYPck5M2wLpkZ1Oz7H/LqO10= gitlab.com/elixxir/crypto v0.0.7-0.20220325215559-7489d68d7714 h1:epnov8zyFWod14MUNtGHSbZCVSkZjN4NvoiBs1TgEV8= gitlab.com/elixxir/crypto v0.0.7-0.20220325215559-7489d68d7714/go.mod h1:tD6XjtQh87T2nKZL5I/pYPck5M2wLpkZ1Oz7H/LqO10= +gitlab.com/elixxir/crypto v0.0.7-0.20220325224306-705ce59288bb h1:WdlmG+KPaM2Pjo1EFiFFPYEVSMV64Di1CitQnXGWBOQ= +gitlab.com/elixxir/crypto v0.0.7-0.20220325224306-705ce59288bb/go.mod h1:tD6XjtQh87T2nKZL5I/pYPck5M2wLpkZ1Oz7H/LqO10= +gitlab.com/elixxir/crypto v0.0.7-0.20220328163237-3bdc3e1369ca h1:jPhotwqZFJYHR4LFitMSvkYtlYH9One+yTsEIoBwQCw= +gitlab.com/elixxir/crypto v0.0.7-0.20220328163237-3bdc3e1369ca/go.mod h1:tD6XjtQh87T2nKZL5I/pYPck5M2wLpkZ1Oz7H/LqO10= +gitlab.com/elixxir/crypto v0.0.7-0.20220328164108-c72388181116 h1:HvMO//NbadCiKGI10cdW98WimYf0YIudzQeUznYAgRQ= +gitlab.com/elixxir/crypto v0.0.7-0.20220328164108-c72388181116/go.mod h1:tD6XjtQh87T2nKZL5I/pYPck5M2wLpkZ1Oz7H/LqO10= gitlab.com/elixxir/ekv v0.1.6 h1:M2hUSNhH/ChxDd+s8xBqSEKgoPtmE6hOEBqQ73KbN6A= gitlab.com/elixxir/ekv v0.1.6/go.mod h1:e6WPUt97taFZe5PFLPb1Dupk7tqmDCTQu1kkstqJvw4= gitlab.com/elixxir/primitives v0.0.0-20200731184040-494269b53b4d/go.mod h1:OQgUZq7SjnE0b+8+iIAT2eqQF+2IFHn73tOo+aV11mg= @@ -289,6 +295,8 @@ gitlab.com/elixxir/primitives v0.0.0-20200804182913-788f47bded40/go.mod h1:tzdFF gitlab.com/elixxir/primitives v0.0.1/go.mod h1:kNp47yPqja2lHSiS4DddTvFpB/4D9dB2YKnw5c+LJCE= gitlab.com/elixxir/primitives v0.0.3-0.20220222212109-d412a6e46623 h1:NzJ06KdJd3fVJee0QvGhNr3CO+Ki8Ea1PeakZsm+rZM= gitlab.com/elixxir/primitives v0.0.3-0.20220222212109-d412a6e46623/go.mod h1:MtFIyJUQn9P7djzVlBpEYkPNnnWFTjZvw89swoXY+QM= +gitlab.com/elixxir/primitives v0.0.3-0.20220325212708-5e2a7a385db7 h1:2yIEkxkPJboftkk/xiCONVP/FSl7Y3zOPpekvQ2dhO0= +gitlab.com/elixxir/primitives v0.0.3-0.20220325212708-5e2a7a385db7/go.mod h1:MtFIyJUQn9P7djzVlBpEYkPNnnWFTjZvw89swoXY+QM= gitlab.com/xx_network/comms v0.0.0-20200805174823-841427dd5023/go.mod h1:owEcxTRl7gsoM8c3RQ5KAm5GstxrJp5tn+6JfQ4z5Hw= gitlab.com/xx_network/comms v0.0.4-0.20220223205228-7c4974139569/go.mod h1:isHnwem0v4rTcwwHP455FhVlFyPcHkHiVz+N3s/uCSI= gitlab.com/xx_network/comms v0.0.4-0.20220311192415-d95fe8906580 h1:IV0gDwdTxtCpc9Vkx7IeSStSqvG+0ZpF57X+OhTQDIM= diff --git a/interfaces/networkManager.go b/interfaces/networkManager.go index e39af97f8e2380b6aced6dcbd01ad7ec0d612b9e..4914bfb4fa04dd5273ea5bfb775ae1e3450b33ba 100644 --- a/interfaces/networkManager.go +++ b/interfaces/networkManager.go @@ -219,26 +219,6 @@ type NetworkManager interface { type Preimage [32]byte -type Trigger struct { - Preimage - Type string - Source []byte -} - -type TriggerTracker func(triggers []Trigger) - -type MessageProcessor interface { - // Process decrypts and hands off the message to its internal down - // stream message processing system. - // CRITICAL: Fingerprints should never be used twice. Process must - // denote, in long term storage, usage of a fingerprint and that - // fingerprint must not be added again during application load. - // It is a security vulnerability to reuse a fingerprint. It leaks - // privacy and can lead to compromise of message contents and integrity. - Process(message format.Message, receptionID EphemeralIdentity, - round *mixmessages.RoundInfo) -} - type ClientErrorReport func(source, message, trace string) //type Ratchet interface { diff --git a/storage/utility/cmixMessageBuffer.go b/network/cmixMessageBuffer.go similarity index 92% rename from storage/utility/cmixMessageBuffer.go rename to network/cmixMessageBuffer.go index e072f05d4bf1dbc7fab4ba5371c32ba786f63860..a70733ba44e7981be4a6fae8329a52c9227a7a52 100644 --- a/storage/utility/cmixMessageBuffer.go +++ b/network/cmixMessageBuffer.go @@ -5,12 +5,13 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package utility +package network import ( "encoding/json" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/client/storage/utility" "gitlab.com/elixxir/client/storage/versioned" "gitlab.com/elixxir/primitives/format" "gitlab.com/xx_network/primitives/id" @@ -25,6 +26,7 @@ type cmixMessageHandler struct{} type storedMessage struct { Msg []byte Recipient []byte + Params []byte } func (sm storedMessage) Marshal() []byte { @@ -79,12 +81,12 @@ func (cmh *cmixMessageHandler) DeleteMessage(kv *versioned.KV, key string) error } // HashMessage generates a hash of the message. -func (cmh *cmixMessageHandler) HashMessage(m interface{}) MessageHash { +func (cmh *cmixMessageHandler) HashMessage(m interface{}) utility.MessageHash { h, _ := blake2b.New256(nil) h.Write(m.(storedMessage).Marshal()) - var messageHash MessageHash + var messageHash utility.MessageHash copy(messageHash[:], h.Sum(nil)) return messageHash @@ -93,11 +95,11 @@ func (cmh *cmixMessageHandler) HashMessage(m interface{}) MessageHash { // CmixMessageBuffer wraps the message buffer to store and load raw cmix // messages. type CmixMessageBuffer struct { - mb *MessageBuffer + mb *utility.MessageBuffer } func NewCmixMessageBuffer(kv *versioned.KV, key string) (*CmixMessageBuffer, error) { - mb, err := NewMessageBuffer(kv, &cmixMessageHandler{}, key) + mb, err := utility.NewMessageBuffer(kv, &cmixMessageHandler{}, key) if err != nil { return nil, err } @@ -106,7 +108,7 @@ func NewCmixMessageBuffer(kv *versioned.KV, key string) (*CmixMessageBuffer, err } func LoadCmixMessageBuffer(kv *versioned.KV, key string) (*CmixMessageBuffer, error) { - mb, err := LoadMessageBuffer(kv, &cmixMessageHandler{}, key) + mb, err := utility.LoadMessageBuffer(kv, &cmixMessageHandler{}, key) if err != nil { return nil, err } diff --git a/storage/utility/cmixMessageBuffer_test.go b/network/cmixMessageBuffer_test.go similarity index 94% rename from storage/utility/cmixMessageBuffer_test.go rename to network/cmixMessageBuffer_test.go index 48be053ea5b1ad01c571f6bd0c5cc26a9420fb7d..c8377dd9bf8fafc0d76cb95ac6508e21c52977cf 100644 --- a/storage/utility/cmixMessageBuffer_test.go +++ b/network/cmixMessageBuffer_test.go @@ -5,10 +5,11 @@ // LICENSE file // /////////////////////////////////////////////////////////////////////////////// -package utility +package network import ( "bytes" + "gitlab.com/elixxir/client/storage/utility" "gitlab.com/elixxir/client/storage/versioned" "gitlab.com/elixxir/ekv" "gitlab.com/elixxir/primitives/format" @@ -31,7 +32,7 @@ func TestCmixMessageHandler_SaveMessage(t *testing.T) { Msg: testMsgs[i].Marshal(), Recipient: ids[i].Marshal(), } - key := MakeStoredMessageKey("testKey", cmh.HashMessage(msg)) + key := utility.MakeStoredMessageKey("testKey", cmh.HashMessage(msg)) // Save message err := cmh.SaveMessage(kv, msg, key) @@ -67,7 +68,7 @@ func TestCmixMessageHandler_LoadMessage(t *testing.T) { Msg: testMsgs[i].Marshal(), Recipient: ids[i].Marshal(), } - key := MakeStoredMessageKey("testKey", cmh.HashMessage(msg)) + key := utility.MakeStoredMessageKey("testKey", cmh.HashMessage(msg)) // Save message if err := cmh.SaveMessage(kv, msg, key); err != nil { @@ -157,10 +158,10 @@ func TestCmixMessageBuffer_Smoke(t *testing.T) { // makeTestCmixMessages creates a list of messages with random data and the // expected map after they are added to the buffer. -func makeTestCmixMessages(n int) ([]format.Message, []*id.ID, map[MessageHash]struct{}) { +func makeTestCmixMessages(n int) ([]format.Message, []*id.ID, map[utility.MessageHash]struct{}) { cmh := &cmixMessageHandler{} prng := rand.New(rand.NewSource(netTime.Now().UnixNano())) - mh := map[MessageHash]struct{}{} + mh := map[utility.MessageHash]struct{}{} msgs := make([]format.Message, n) ids := make([]*id.ID, n) for i := range msgs { diff --git a/network/critical.go b/network/critical.go new file mode 100644 index 0000000000000000000000000000000000000000..d838135bd4fe947b2a01a74a72b947e4b589d6c6 --- /dev/null +++ b/network/critical.go @@ -0,0 +1,13 @@ +package network + +import ( + "gitlab.com/elixxir/client/network/health" +) + +type Manager struct { + storage *CmixMessageBuffer + trigger chan bool + hm *health.Monitor +} + +func NewManager() diff --git a/network/interface.go b/network/interface.go index 0c48d254657515a5895c697343cdb981951ddabe..dd5b12290ac83e5d73b0e580fb5aa9824afcd5bb 100644 --- a/network/interface.go +++ b/network/interface.go @@ -1,7 +1,7 @@ package network import ( - "gitlab.com/elixxir/client/network/identity/receptionID" + "gitlab.com/elixxir/client/network/message" "gitlab.com/elixxir/client/stoppable" "gitlab.com/elixxir/comms/mixmessages" "gitlab.com/elixxir/comms/network" @@ -22,18 +22,55 @@ type Manager interface { /*===Sending==============================================================*/ + // GetMaxMessageLength returns the max message size for the current network + GetMaxMessageLength() int + // SendCMIX sends a "raw" CMIX message payload to the provided recipient. // Returns the round ID of the round the payload was sent or an error // if it fails. + // This does not have end to end encryption on it and is used exclusively as a + // send for higher order cryptographic protocols. Do not use unless implementing + // a protocol on top. + // recipient - cMix ID of the recipient + // fingerprint - Key Fingerprint. 256 bit field to store a 255 bit + // fingerprint, highest order bit must be 0 (panic otherwise). If your + // system does not use key fingerprints, this must be random bits. + // service - Reception Service. The backup way for a client to identify + // messages on receipt via trial hashing and to identify notifications. + // If unused, use messages.RandomService to fill the field with random data + // payload - Contents of the message. Cannot exceed the payload size for a + // cMix message (panic otherwise). + // mac - 256 bit field to store a 255 bit mac, highest order bit must be 0 + // (panic otherwise). If used, fill with random bits. + // Will return an error if the network is unhealthy or if it fails to send + // (along with the reason). Blocks until successful send or err. + // WARNING: Do not roll your own crypto SendCMIX(message format.Message, recipient *id.ID, p CMIXParams) ( id.Round, ephemeral.Id, error) - // SendManyCMIX sends many "raw" cMix message payloads to each of the provided - // recipients. Used to send messages in group chats. Metadata is NOT as well - // protected with this call and can leak data about yourself. Should be - // replaced with multiple uses of SendCmix in most cases. Returns the round - // ID of the round the payload was sent or an error if it fails. - // WARNING: Potentially Unsafe + // SendManyCMIX sends many "raw" CMIX message payloads to the provided + // recipients all in the same round. + // Returns the round ID of the round the payloads was sent or an error + // if it fails. + // This does not have end to end encryption on it and is used exclusively as a + // send for higher order cryptographic protocols. Do not use unless implementing + // a protocol on top. + // Due to sending multiple payloads, this leaks more metadata than a standard + // cmix send and should be in general avoided. + // recipient - cMix ID of the recipient + // fingerprint - Key Fingerprint. 256 bit field to store a 255 bit + // fingerprint, highest order bit must be 0 (panic otherwise). If your + // system does not use key fingerprints, this must be random bits. + // service - Reception Service. The backup way for a client to identify + // messages on receipt via trial hashing and to identify notifications. + // If unused, use messages.RandomService to fill the field with random data + // payload - Contents of the message. Cannot exceed the payload size for a + // cMix message (panic otherwise). + // mac - 256 bit field to store a 255 bit mac, highest order bit must be 0 + // (panic otherwise). If used, fill with random bits. + // Will return an error if the network is unhealthy or if it fails to send + // (along with the reason). Blocks until successful send or err. + // WARNING: Do not roll your own crypto SendManyCMIX(messages []TargetedCmixMessage, p CMIXParams) ( id.Round, []ephemeral.Id, error) @@ -60,7 +97,7 @@ type Manager interface { // AddFingerprint - Adds a fingerprint which will be handled by a // specific processor for messages received by the given identity AddFingerprint(identity *id.ID, fingerprint format.Fingerprint, - mp MessageProcessor) error + mp message.Processor) error // DeleteFingerprint deletes a single fingerprint associated with the given // identity if it exists @@ -70,11 +107,11 @@ type Manager interface { // identity if it exists DeleteClientFingerprints(identity *id.ID) - /* trigger - predefined hash based tags appended to all cMix messages + /* Service - predefined hash based tags appended to all cMix messages which, though trial hashing, are used to determine if a message applies to this client - Triggers are used for 2 purposes - They can be processed by the + Services are used for 2 purposes - They can be processed by the notifications system, or can be used to implement custom non fingerprint processing of payloads. I.E. key negotiation, broadcast negotiation @@ -85,41 +122,38 @@ type Manager interface { messages are for the client on reception (which is normally hidden due to collision between ephemeral IDs. - Due to the extra overhead of trial hashing, triggers are processed after fingerprints. - If a fingerprint match occurs on the message, triggers will not be handled. + Due to the extra overhead of trial hashing, services are processed after + fingerprints. If a fingerprint match occurs on the message, services will + not be handled. - Triggers are address to the session. When starting a new client, all triggers must be - re-added before StartNetworkFollower is called. + Services are address to the session. When starting a new client, all + services must be re-added before StartNetworkFollower is called. */ - // AddTrigger - Adds a trigger which can call a message handing function or - // be used for notifications. Multiple triggers can be registered for the - // same preimage. + // AddService adds a service which can call a message handing function or be + // used for notifications. In general a single service can only be registered + // for the same identifier/tag pair. // preimage - the preimage which is triggered on - // type - a descriptive string of the trigger. Generally used in notifications + // type - a descriptive string of the service. Generally used in notifications // source - a byte buffer of related data. Generally used in notifications. // Example: Sender ID - AddTrigger(identity *id.ID, newTrigger Trigger, response MessageProcessor) + AddService(AddService *id.ID, newService message.Service, response message.Processor) - // DeleteTrigger - If only a single response is associated with the - // preimage, the entire preimage is removed. If there is more than one - // response, only the given response is removed if nil is passed in for - // response, all triggers for the preimage will be removed - DeleteTrigger(identity *id.ID, preimage Preimage, response MessageProcessor) error + // DeleteService - If only a single response is associated with the preimage, + // the entire preimage is removed. If there is more than one response, only the + // given response is removed. If nil is passed in for response, all triggers for + // the preimage will be removed. + DeleteService(clientID *id.ID, toDelete message.Service, processor message.Processor) - // DeleteClientTriggers - deletes all triggers assoseated with the given identity - DeleteClientTriggers(identity *id.ID) + // DeleteClientService deletes the mapping associated with an ID. + DeleteClientService(clientID *id.ID) - // TrackTriggers - Registers a callback which will get called every time triggers change. + // TrackServices - Registers a callback which will get called every time a + // service is added or removed. // It will receive the triggers list every time it is modified. // Will only get callbacks while the Network Follower is running. // Multiple trackTriggers can be registered - TrackTriggers(TriggerTracker) - - - //Dropped Messages Pickup - RegisterDroppedMessagesPickup(response MessageProcessor) - DenoteReception(msgId uint) + TrackServices(tracker message.ServicesTracker) /* In inProcess */ // it is possible to receive a message over cMix before the fingerprints or @@ -228,26 +262,4 @@ type Manager interface { GetVerboseRounds() string } -type Preimage [32]byte - -type Trigger struct { - Preimage - Type string - Source []byte -} - -type TriggerTracker func(triggers []Trigger) - -type MessageProcessor interface { - // Process decrypts and hands off the message to its internal down - // stream message processing system. - // CRITICAL: Fingerprints should never be used twice. Process must - // denote, in long term storage, usage of a fingerprint and that - // fingerprint must not be added again during application load. - // It is a security vulnerability to reuse a fingerprint. It leaks - // privacy and can lead to compromise of message contents and integrity. - Process(message format.Message, receptionID receptionID.EphemeralIdentity, - round *mixmessages.RoundInfo) -} - type ClientErrorReport func(source, message, trace string) diff --git a/network/manager.go b/network/manager.go index 5624c916d64a30a98082aab9657b8ead5eba19db..fd4cbc25b647a4fa287195881d72a8574a9b5e47 100644 --- a/network/manager.go +++ b/network/manager.go @@ -27,6 +27,7 @@ import ( "gitlab.com/elixxir/comms/client" commNetwork "gitlab.com/elixxir/comms/network" "gitlab.com/elixxir/crypto/fastRNG" + "gitlab.com/elixxir/primitives/format" "gitlab.com/xx_network/primitives/id" "gitlab.com/xx_network/primitives/ndf" "math" @@ -80,6 +81,9 @@ type manager struct { // Event reporting api events event.Manager + + //storage of the max message length + maxMsgLen int } // NewManager builds a new reception manager object using inputted key fields @@ -94,6 +98,8 @@ func NewManager(params Params, comms *client.Comms, session storage.Session, " client network manager") } + tmpMsg := format.NewMessage(session.GetCmixGroup().GetP().ByteLen()) + tracker := uint64(0) earliest := uint64(0) // create manager object @@ -107,6 +113,7 @@ func NewManager(params Params, comms *client.Comms, session storage.Session, rng: rng, comms: comms, instance: instance, + maxMsgLen: tmpMsg.ContentsSize(), } m.UpdateAddressSpace(18) @@ -224,3 +231,8 @@ func (m *manager) GetVerboseRounds() string { func (m *manager) SetFakeEarliestRound(rnd id.Round) { atomic.StoreUint64(m.earliestRound, uint64(rnd)) } + +// GetMaxMessageLength returns the maximum length of a cmix message +func (m *manager) GetMaxMessageLength() int { + return m.maxMsgLen +} diff --git a/network/message/bundle.go b/network/message/bundle.go index b0ab15c22115fb8004dd7cb3154c9c206505b71c..a3fe68d783b106475ca9d79f77ac56234918c9e5 100644 --- a/network/message/bundle.go +++ b/network/message/bundle.go @@ -8,7 +8,7 @@ package message import ( - "gitlab.com/elixxir/client/interfaces" + "gitlab.com/elixxir/client/network/identity/receptionID" pb "gitlab.com/elixxir/comms/mixmessages" "gitlab.com/elixxir/primitives/format" "gitlab.com/xx_network/primitives/id" @@ -19,5 +19,5 @@ type Bundle struct { RoundInfo *pb.RoundInfo Messages []format.Message Finish func() - Identity interfaces.EphemeralIdentity + Identity receptionID.EphemeralIdentity } diff --git a/network/message/fingerprints.go b/network/message/fingerprints.go index 91fa906d4d1ff8cd0ebd0e760219c5fa493b7ee2..01b814470f85b3d1abe3fa2078ea8af0aa1de171 100644 --- a/network/message/fingerprints.go +++ b/network/message/fingerprints.go @@ -11,7 +11,6 @@ import ( "sync" "github.com/pkg/errors" - "gitlab.com/elixxir/client/interfaces" "gitlab.com/elixxir/primitives/format" "gitlab.com/xx_network/primitives/id" ) @@ -19,14 +18,14 @@ import ( // FingerprintsManager is a thread-safe map, mapping format.Fingerprint's to // a Handler object. type FingerprintsManager struct { - fpMap map[id.ID]map[format.Fingerprint]interfaces.MessageProcessor + fpMap map[id.ID]map[format.Fingerprint]Processor sync.Mutex } // newFingerprints is a constructor function for the Fingerprints tracker. func newFingerprints() *FingerprintsManager { return &FingerprintsManager{ - fpMap: make(map[id.ID]map[format.Fingerprint]interfaces.MessageProcessor), + fpMap: make(map[id.ID]map[format.Fingerprint]Processor), } } @@ -36,7 +35,7 @@ func newFingerprints() *FingerprintsManager { // vulnerability. func (f *FingerprintsManager) pop(clientID *id.ID, fingerprint format.Fingerprint) ( - interfaces.MessageProcessor, bool) { + Processor, bool) { f.Lock() defer f.Unlock() cid := *clientID @@ -58,7 +57,7 @@ func (f *FingerprintsManager) pop(clientID *id.ID, // value. If there is already an entry for this fingerprint, the // method returns with no write operation. func (f *FingerprintsManager) AddFingerprint(clientID *id.ID, - fingerprint format.Fingerprint, mp interfaces.MessageProcessor) error { + fingerprint format.Fingerprint, mp Processor) error { f.Lock() defer f.Unlock() @@ -66,7 +65,7 @@ func (f *FingerprintsManager) AddFingerprint(clientID *id.ID, if _, exists := f.fpMap[cid]; !exists { f.fpMap[cid] = make( - map[format.Fingerprint]interfaces.MessageProcessor) + map[format.Fingerprint]Processor) } if _, exists := f.fpMap[cid][fingerprint]; exists { diff --git a/network/message/handler.go b/network/message/handler.go index 0496f71ab601a77f65df25812874b46c8c6fec9d..625a43b2dbe62bb0e20c75058611b98756f49a2a 100644 --- a/network/message/handler.go +++ b/network/message/handler.go @@ -10,10 +10,8 @@ package message import ( "fmt" jww "github.com/spf13/jwalterweatherman" - "gitlab.com/elixxir/client/interfaces" "gitlab.com/elixxir/client/interfaces/preimage" "gitlab.com/elixxir/client/stoppable" - fingerprint2 "gitlab.com/elixxir/crypto/fingerprint" "gitlab.com/elixxir/primitives/format" "gitlab.com/xx_network/primitives/netTime" "sync" @@ -65,21 +63,19 @@ func (p *handler) handleMessage(ecrMsg format.Message, bundle Bundle) bool { identity := bundle.Identity round := bundle.RoundInfo - var receptionID interfaces.EphemeralIdentity - // If we have a fingerprint, process it. if proc, exists := p.pop(identity.Source, fingerprint); exists { - proc.Process(ecrMsg, receptionID, round) + proc.Process(ecrMsg, identity, round) return true } triggers, exists := p.get(identity.Source, ecrMsg.GetSIH(), ecrMsg.GetContents()) if exists { for _, t := range triggers { - go t.Process(ecrMsg, receptionID, round) + go t.Process(ecrMsg, identity, round) } if len(triggers) == 0 { - jww.ERROR.Printf("empty trigger list for %s", + jww.ERROR.Printf("empty service list for %s", ecrMsg.GetSIH()) // get preimage } return true @@ -91,17 +87,10 @@ func (p *handler) handleMessage(ecrMsg format.Message, bundle Bundle) bool { // ecrMsgContents, preimage.MakeDefault(identity.Source)) } - if jww.GetLogThreshold() == jww.LevelTrace { - expectedFP := fingerprint2.IdentityFP(ecrMsg.GetContents(), - preimage.MakeDefault(identity.Source)) - jww.TRACE.Printf("Message for %d (%s) failed identity "+ - "check: %v (expected-default) vs %v (received)", - identity.EphId, - identity.Source, expectedFP, ecrMsg.GetSIH()) - } - im := fmt.Sprintf("Garbled/RAW Message: keyFP: %v, round: %d"+ + im := fmt.Sprintf("Message cannot be identify: keyFP: %v, round: %d"+ "msgDigest: %s, not determined to be for client", ecrMsg.GetKeyFP(), bundle.Round, ecrMsg.Digest()) + jww.TRACE.Printf(im) p.events.Report(1, "MessageReception", "Garbled", im) return false } diff --git a/network/message/pickup.go b/network/message/pickup.go index d86aa1a2f70464511dcfd3275ad439cdb267094e..7ad3173ad24b736500e67895df3f571ded08c1b0 100644 --- a/network/message/pickup.go +++ b/network/message/pickup.go @@ -9,7 +9,6 @@ package message import ( "gitlab.com/elixxir/client/event" - "gitlab.com/elixxir/client/interfaces" "gitlab.com/elixxir/client/storage/versioned" "gitlab.com/elixxir/primitives/format" "gitlab.com/xx_network/primitives/id" @@ -29,15 +28,15 @@ type Handler interface { CheckInProgressMessages() // Fingerprints - AddFingerprint(clientID *id.ID, fingerprint format.Fingerprint, mp interfaces.MessageProcessor) error + AddFingerprint(clientID *id.ID, fingerprint format.Fingerprint, mp Processor) error DeleteFingerprint(clientID *id.ID, fingerprint format.Fingerprint) DeleteClientFingerprints(clientID *id.ID) // Triggers - AddTrigger(clientID *id.ID, newTrigger interfaces.Trigger, response interfaces.MessageProcessor) - DeleteTrigger(clientID *id.ID, preimage interfaces.Preimage, response interfaces.MessageProcessor) error - DeleteClientTriggers(clientID *id.ID) - TrackTriggers(triggerTracker interfaces.TriggerTracker) + AddService(clientID *id.ID, newService Service, response Processor) + DeleteService(clientID *id.ID, toDelete Service, response Processor) + DeleteClientService(clientID *id.ID) + TrackServices(triggerTracker ServicesTracker) } type handler struct { @@ -51,7 +50,7 @@ type handler struct { events event.Manager FingerprintsManager - TriggersManager + ServicesManager } func NewHandler(param Params, kv *versioned.KV, events event.Manager) Handler { @@ -70,7 +69,7 @@ func NewHandler(param Params, kv *versioned.KV, events event.Manager) Handler { } m.FingerprintsManager = *newFingerprints() - m.TriggersManager = *NewTriggers() + m.ServicesManager = *NewServices() return &m } diff --git a/network/message/processor.go b/network/message/processor.go new file mode 100644 index 0000000000000000000000000000000000000000..f4aa5e3202615e3ed8f9328b8bcd44a3be3f2ade --- /dev/null +++ b/network/message/processor.go @@ -0,0 +1,19 @@ +package message + +import ( + "gitlab.com/elixxir/client/network/identity/receptionID" + "gitlab.com/elixxir/comms/mixmessages" + "gitlab.com/elixxir/primitives/format" +) + +type Processor interface { + // Process decrypts and hands off the message to its internal down + // stream message processing system. + // CRITICAL: Fingerprints should never be used twice. Process must + // denote, in long term storage, usage of a fingerprint and that + // fingerprint must not be added again during application load. + // It is a security vulnerability to reuse a fingerprint. It leaks + // privacy and can lead to compromise of message contents and integrity. + Process(message format.Message, receptionID receptionID.EphemeralIdentity, + round *mixmessages.RoundInfo) +} diff --git a/network/message/serviceGenerators.go b/network/message/serviceGenerators.go new file mode 100644 index 0000000000000000000000000000000000000000..c2e6c791e2787babf3238e76ef0896301e0b6fbd --- /dev/null +++ b/network/message/serviceGenerators.go @@ -0,0 +1,37 @@ +package message + +import ( + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/crypto/sih" + "gitlab.com/xx_network/crypto/csprng" + "gitlab.com/xx_network/primitives/id" +) + +// GetDefaultService is used to generate a default service. All identities +// will respond to their default service, but it lacks privacy because it +// uses the public ID as the key. Used for initial reach out in some protocols, +// otherwise should not be used +func GetDefaultService(recipient *id.ID) Service { + jww.WARN.Printf("Generating Default Service for %s - "+ + "may not be private", recipient) + return Service{ + Identifier: recipient[:], + Tag: sih.Default, + Source: recipient[:], + } +} + +// GetRandomService is used to make a servivce for cMix sending when no +// service is needed. It fills the Identifier with random, bits in order to +// preserve privacy +func GetRandomService(rng csprng.Source) Service { + identifier := make([]byte, 32) + if _, err := rng.Read(identifier); err != nil { + jww.FATAL.Panicf("Failed to generate random data: %+v", err) + } + return Service{ + Identifier: identifier, + Tag: "Random", + Source: identifier, + } +} diff --git a/network/message/serviceInterface.go b/network/message/serviceInterface.go new file mode 100644 index 0000000000000000000000000000000000000000..7c9e79d9027ae9362e38f5bdbac77864bb778b0c --- /dev/null +++ b/network/message/serviceInterface.go @@ -0,0 +1,62 @@ +package message + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "gitlab.com/elixxir/crypto/sih" +) + +type Service struct { + Identifier []byte + Tag string + Source []byte //optional metadata field, only used on reception + + //private field for lazy evaluation of preimage + //Nil denotes not yet evaluated + lazyPreimage *sih.Preimage +} + +func (si Service) Hash(contents []byte) []byte { + preimage := si.preimage() + return sih.Hash(preimage, contents) +} + +func (si Service) HashFromMessageHash(messageHash []byte) []byte { + preimage := si.preimage() + return sih.HashFromMessageHash(preimage, messageHash) +} + +func (si Service) preimage() sih.Preimage { + // calculate + if si.lazyPreimage == nil { + p := sih.MakePreimage(si.Identifier, si.Tag) + si.lazyPreimage = &p + } + + return *si.lazyPreimage +} + +func (si Service) ForMe(contents, hash []byte) bool { + return sih.ForMe(si.preimage(), contents, hash) +} + +func (si Service) ForMeFromMessageHash(messageHash, hash []byte) bool { + return sih.ForMeFromMessageHash(si.preimage(), messageHash, hash) +} + +func (si Service) MarshalJSON() ([]byte, error) { + return json.Marshal(&si) +} + +func (si Service) UnmarshalJSON(b []byte) error { + return json.Unmarshal(b, &si) +} + +func (si Service) String() string { + p := si.preimage() + return fmt.Sprintf("Tag: %s, Identifier: %s, source: %s, "+ + "preimage:%s", si.Tag, base64.StdEncoding.EncodeToString(si.Identifier), + base64.StdEncoding.EncodeToString(si.Source), + base64.StdEncoding.EncodeToString(p[:])) +} diff --git a/network/message/serviceTracker.go b/network/message/serviceTracker.go new file mode 100644 index 0000000000000000000000000000000000000000..8ae97c829f3218a3d6c60532dadde1e4e0412fe7 --- /dev/null +++ b/network/message/serviceTracker.go @@ -0,0 +1,72 @@ +package message + +import ( + "encoding/json" + "gitlab.com/xx_network/primitives/id" +) + +type ServicesTracker func(ServiceList) + +// TrackServices adds a service tracker to be triggered when a nee service +// as added. Generally used for notificatiosn to use this system to identify a +// received message +func (sm *ServicesManager) TrackServices(tracker ServicesTracker) { + if tracker == nil { + return + } + sm.Lock() + defer sm.Unlock() + + sm.trackers = append(sm.trackers, tracker) +} + +// triggerServiceTracking triggers the tracking of services. +// Is it called when a service is added or removed. +func (sm *ServicesManager) triggerServiceTracking() { + if len(sm.trackers) == 0 { + return + } + + services := make(ServiceList) + for uid, tmap := range sm.tmap { + tList := make([]Service, 0, len(tmap)) + for _, s := range tmap { + tList = append(tList, s.Service) + } + services[uid] = tList + } + + for _, callback := range sm.trackers { + go callback(services) + } +} + +// The ServiceList holds all services. +type ServiceList map[id.ID][]Service + +type slMarshled struct { + Id id.ID + Services []Service +} + +func (sl ServiceList) MarshalJSON() ([]byte, error) { + slList := make([]slMarshled, 0, len(sl)) + for uid, s := range sl { + slList = append(slList, slMarshled{ + Id: uid, + Services: s, + }) + } + return json.Marshal(&slList) +} + +func (sl ServiceList) UnmarshalJSON(b []byte) error { + slList := make([]slMarshled, 0) + if err := json.Unmarshal(b, &slList); err != nil { + return err + } + for _, s := range slList { + sl[s.Id] = s.Services + } + return nil +} diff --git a/network/message/services.go b/network/message/services.go new file mode 100644 index 0000000000000000000000000000000000000000..987a1d3974d782ef0520aadfcb8ecc95186eba2a --- /dev/null +++ b/network/message/services.go @@ -0,0 +1,184 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +/////////////////////////////////////////////////////////////////////////////// + +package message + +import ( + "bytes" + jww "github.com/spf13/jwalterweatherman" + "sync" + + "gitlab.com/elixxir/crypto/sih" + "gitlab.com/xx_network/primitives/id" +) + +/* Service Identification Hash - predefined hash based tags appended to +all cMix messages which,though trial hashing, are used to determine if a message +applies to this client. + +Services are used for 2 purposes - can be processed by the notification system, +or can be used to implement custom non fingerprint processing of payloads (i.e. +key negotiation and broadcast negotiation). + +A tag is appended to the message of the format tag = H(H(messageContents),preimage) +and trial hashing is used to determine if a message adheres to a tag. +WARNING: If a preimage is known by an adversary, they can determine which +messages are for the client. + +Due to the extra overhead of trial hashing, services are processed after +fingerprints. If a fingerprint match occurs on the message, triggers will not be +handled. + +Services are address to the session. When starting a new client, all triggers +must be re-added before StartNetworkFollower is called. +*/ + +type ServicesManager struct { + tmap map[id.ID]map[sih.Preimage]service + trackers []ServicesTracker + numServices uint + sync.Mutex +} + +type service struct { + Service + Processor + defaultList []Processor +} + +func NewServices() *ServicesManager { + // todo: implement me + return &ServicesManager{ + tmap: make(map[id.ID]map[sih.Preimage]service), + } +} + +// Lookup will see if a service exists for the given preimage and message +// contents. It will do this by trial hashing the preimages in the map with the +// received message contents, until either a match to the received identity +// fingerprint is received or it has exhausted the map. +// If a match is found, this means the message received is for the client, and +// that one or multiple services exist to process this message. +// These services are returned to the caller along with the a true boolean. +// If the map has been exhausted with no matches found, it returns nil and false. +func (sm *ServicesManager) get(clientID *id.ID, receivedSIH, + ecrMsgContents []byte) ([]Processor, + bool) { + sm.Lock() + defer sm.Unlock() + cid := *clientID + + services, exists := sm.tmap[cid] + if !exists { + return nil, false + } + for _, s := range services { + // check if the sih matches this service + if s.ForMe(ecrMsgContents, receivedSIH) { + // return this service directly if not the default service + if s.defaultList == nil && s.Tag != sih.Default { + return []Processor{s}, true + // if it is default, and the default list isn't empty, + // return the default list + } else if s.defaultList != nil { + return s.defaultList, true + } + // return false if its for me but i have nothing registered to + // respond to default queries + return []Processor{}, false + } + } + + return nil, false +} + +// AddService adds a service which can call a message handing function or be +// used for notifications. In general a single service can only be registered +// for the same identifier/tag pair. +// preimage - the preimage which is triggered on +// type - a descriptive string of the service. Generally used in notifications +// source - a byte buffer of related data. Generally used in notifications. +// Example: Sender ID +func (sm *ServicesManager) AddService(clientID *id.ID, newService Service, + response Processor) { + sm.Lock() + defer sm.Unlock() + + newEntry := service{ + Service: newService, + Processor: response, + defaultList: nil, + } + + if newService.Tag == sih.Default { + if !bytes.Equal(newService.Identifier, clientID[:]) { + jww.FATAL.Panicf("Cannot accept a malformed 'Default' " + + "service, Identifier must match clientID") + } + oldDefault, exists := sm.tmap[*clientID][newService.preimage()] + if exists { + newEntry = oldDefault + oldDefault.defaultList = append(oldDefault.defaultList, response) + } else { + newEntry.Source = clientID[:] + } + } else if _, exists := sm.tmap[*clientID][newService.preimage()]; exists { + jww.FATAL.Panicf("Cannot add service %s, an identical "+ + "service already exists", newService.Tag) + } + + sm.tmap[*clientID][newService.preimage()] = newEntry + + sm.numServices++ + sm.triggerServiceTracking() +} + +// DeleteService - If only a single response is associated with the preimage, +// the entire preimage is removed. If there is more than one response, only the +// given response is removed. If nil is passed in for response, all triggers for +// the preimage will be removed. +func (sm *ServicesManager) DeleteService(clientID *id.ID, toDelete Service, + processor Processor) { + sm.Lock() + defer sm.Unlock() + cid := *clientID + + idTmap, exists := sm.tmap[cid] + if !exists { + return + } + + services, exists := idTmap[toDelete.preimage()] + if !exists { + return + } + + // do unique handling if this is a default service and there is more + // then one registered + if services.defaultList != nil && len(services.defaultList) > 1 { + for i, p := range services.defaultList { + if p == processor { + services.defaultList = append(services.defaultList[:i], services.defaultList[i+1:]...) + idTmap[toDelete.preimage()] = services + return + } + } + } + + delete(idTmap, toDelete.preimage()) + sm.numServices-- + sm.triggerServiceTracking() + return +} + +// DeleteClientService deletes the mapping associated with an ID. +func (sm *ServicesManager) DeleteClientService(clientID *id.ID) { + sm.Lock() + defer sm.Unlock() + + delete(sm.tmap, *clientID) +} diff --git a/network/message/triggers.go b/network/message/triggers.go deleted file mode 100644 index ab733cdd334ab38943427173898a32f553766e5c..0000000000000000000000000000000000000000 --- a/network/message/triggers.go +++ /dev/null @@ -1,220 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// Copyright © 2020 xx network SEZC // -// // -// Use of this source code is governed by a license that can be found in the // -// LICENSE file // -/////////////////////////////////////////////////////////////////////////////// - -package message - -import ( - "golang.org/x/crypto/blake2b" - "sync" - - "github.com/pkg/errors" - "gitlab.com/elixxir/client/interfaces" - "gitlab.com/elixxir/crypto/fingerprint" - "gitlab.com/xx_network/primitives/id" -) - -/* Trigger - predefined hash based tags appended to all cMix messages which, -though trial hashing, are used to determine if a message applies to this client. - -Triggers are used for 2 purposes - can be processed by the notification system, -or can be used to implement custom non fingerprint processing of payloads (i.e. -key negotiation and broadcast negotiation). - -A tag is appended to the message of the format tag = H(H(messageContents),preimage) -and trial hashing is used to determine if a message adheres to a tag. -WARNING: If a preimage is known by an adversary, they can determine which -messages are for the client. - -Due to the extra overhead of trial hashing, triggers are processed after -fingerprints. If a fingerprint match occurs on the message, triggers will not be -handled. - -Triggers are address to the session. When starting a new client, all triggers -must be re-added before StartNetworkFollower is called. -*/ - -type TriggersManager struct { - tmap map[id.ID]map[interfaces.Preimage][]trigger - trackers []interfaces.TriggerTracker - numTriggers uint - sync.Mutex -} - -type trigger struct { - interfaces.Trigger - interfaces.MessageProcessor -} - -func NewTriggers() *TriggersManager { - // todo: implement me - return &TriggersManager{ - tmap: make(map[id.ID]map[interfaces.Preimage][]trigger, 0), - } -} - -// Lookup will see if a trigger exists for the given preimage and message -// contents. It will do this by trial hashing the preimages in the map with the -// received message contents, until either a match to the received identity -// fingerprint is received or it has exhausted the map. -// If a match is found, this means the message received is for the client, and -// that one or multiple triggers exist to process this message. -// These triggers are returned to the caller along with the a true boolean. -// If the map has been exhausted with no matches found, it returns nil and false. -func (t *TriggersManager) get(clientID *id.ID, receivedIdentityFp, - ecrMsgContents []byte) ([]trigger, - bool) { - t.Lock() - defer t.Unlock() - cid := *clientID - - triggers, exists := t.tmap[cid] - if !exists { - return nil, false - } - - for pi, triggerList := range triggers { - if fingerprint.CheckIdentityFP(receivedIdentityFp, - ecrMsgContents, pi[:]) { - return triggerList, true - } - } - - return nil, false -} - -// AddTrigger adds a trigger which can call a message handing function or be -// used for notifications. Multiple triggers can be registered for the same -// preimage. -// preimage - the preimage which is triggered on -// type - a descriptive string of the trigger. Generally used in notifications -// source - a byte buffer of related data. Generally used in notifications. -// Example: Sender ID -func (t *TriggersManager) AddTrigger(clientID *id.ID, newTrigger interfaces.Trigger, - response interfaces.MessageProcessor) { - t.Lock() - defer t.Unlock() - - newEntry := trigger{ - Trigger: newTrigger, - MessageProcessor: response, - } - - realTrigger := Generate(newTrigger.Preimage,newTrigger.Type) - - cid := *clientID - if _, exists := t.tmap[cid]; !exists { - t.tmap[cid] = make(map[interfaces.Preimage][]trigger) - } - - - if existingTriggers, exists := t.tmap[cid][realTrigger]; exists { - t.tmap[cid][realTrigger] = append(existingTriggers, newEntry) - } - - t.tmap[cid][realTrigger] = []trigger{newEntry} - - t.numTriggers++ - t.triggerTracking() -} - -func Generate(data []byte, t string) []byte { - if t == Default { - return data - } - // Hash fingerprints - h, _ := blake2b.New256(nil) - h.Write(data) - h.Write([]byte(t)) - - // Base 64 encode hash and truncate - return h.Sum(nil) -} - -// DeleteTrigger - If only a single response is associated with the preimage, -// the entire preimage is removed. If there is more than one response, only the -// given response is removed. If nil is passed in for response, all triggers for -// the preimage will be removed. -func (t *TriggersManager) DeleteTrigger(clientID *id.ID, preimage interfaces.Preimage, - response interfaces.MessageProcessor) error { - t.Lock() - defer t.Unlock() - - if response == nil { - return errors.Errorf("response cannot be nil when deleting") - } - - cid := *clientID - - idTmap, exists := t.tmap[cid] - if !exists { - return nil - } - - triggers, exists := idTmap[preimage] - if !exists { - return nil - } - - if len(triggers) == 1 && triggers[0].MessageProcessor == response { - if len(idTmap) == 1 { - delete(t.tmap, cid) - } else { - delete(t.tmap[cid], preimage) - } - } - - for idx, cur := range triggers { - if cur.MessageProcessor == response { - t.tmap[cid][preimage] = append(triggers[:idx], - triggers[idx+1:]...) - return nil - } - } - t.numTriggers-- - t.triggerTracking() - return nil -} - -// DeleteClientTriggers deletes the mapping associated with an ID. -func (t *TriggersManager) DeleteClientTriggers(clientID *id.ID) { - t.Lock() - defer t.Unlock() - - delete(t.tmap, *clientID) -} - -// TrackTriggers adds a trigger tracker to be triggered when a nee trigger -// as added. -func (t *TriggersManager) TrackTriggers(triggerTracker interfaces.TriggerTracker) { - if triggerTracker == nil { - return - } - t.Lock() - defer t.Unlock() - - t.trackers = append(t.trackers, triggerTracker) -} - -//triggerTracking triggers the tracking of triggers -func (t *TriggersManager) triggerTracking() { - if len(t.trackers) == 0 { - return - } - - triggers := make([]interfaces.Trigger, 0, t.numTriggers) - for _, tmap := range t.tmap { - for _, tlist := range tmap { - for i := range tlist { - triggers = append(triggers, tlist[i].Trigger) - } - } - } - - for _, callback := range t.trackers { - go callback(triggers) - } -} diff --git a/network/sendCmix.go b/network/sendCmix.go index 0351f7685e8f23c6585aba876177df4fac022df1..592e24eedb527bb53dd92c9ed079719f39b54734 100644 --- a/network/sendCmix.go +++ b/network/sendCmix.go @@ -12,8 +12,8 @@ import ( "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/event" - "gitlab.com/elixxir/client/interfaces" "gitlab.com/elixxir/client/network/gateway" + "gitlab.com/elixxir/client/network/message" "gitlab.com/elixxir/client/network/nodes" "gitlab.com/elixxir/client/stoppable" pb "gitlab.com/elixxir/comms/mixmessages" @@ -32,55 +32,41 @@ import ( "time" ) - -type StandardSendable struct{ - Recipient *id.ID - Payload []byte - Fingerprint format.Fingerprint - Trigger StandardTrigger -} - -type StandardTrigger struct{ - Preimage - Type string - Source []byte - crystal []byte -} - -func (t *trigger)Crystalize()[]byte{ - if t.crystal==nil{ - t.crystal=t.generate() - } - return copy(t.crystal) -} - -type Sendable interface{ - GetRecipient()*id.ID - GetPayload()[]byte - GetMac()[]byte - GetFingerprint()format.Fingerprint - GetTriggerPreimage()PreimagePrefix -} - -type Trigger interface{ - GetPreimage()[]byte - GetSource()format.Fingerprint - GetType()string - Crystalize()[]byte -} - - // SendCMIX sends a "raw" CMIX message payload to the provided recipient. // Returns the round ID of the round the payload was sent or an error // if it fails. -func (m *manager) SendCMIX(message Sendable, cmixParams CMIXParams) (id.Round, ephemeral.Id, error) { +// This does not have end to end encryption on it and is used exclusively as a +// send for higher order cryptographic protocols. Do not use unless implementing +// a protocol on top. +// recipient - cMix ID of the recipient +// fingerprint - Key Fingerprint. 256 bit field to store a 255 bit +// fingerprint, highest order bit must be 0 (panic otherwise). If your +// system does not use key fingerprints, this must be random bits. +// service - Reception Service. The backup way for a client to identify +// messages on receipt via trial hashing and to identify notifications. +// If unused, use messages.RandomService to fill the field with random data +// payload - Contents of the message. Cannot exceed the payload size for a +// cMix message (panic otherwise). +// mac - 256 bit field to store a 255 bit mac, highest order bit must be 0 +// (panic otherwise). If used, fill with random bits. +// Will return an error if the network is unhealthy or if it fails to send +// (along with the reason). Blocks until successful send or err. +// WARNING: Do not roll your own crypto +func (m *manager) SendCMIX(recipient *id.ID, fingerprint format.Fingerprint, service message.Service, + payload, mac []byte, cmixParams CMIXParams) (id.Round, ephemeral.Id, error) { if !m.Monitor.IsHealthy() { return 0, ephemeral.Id{}, errors.New("Cannot send cmix message when the " + "network is not healthy") } - msgCopy := msg.Copy() - return sendCmixHelper(m.Sender, msgCopy, recipient, cmixParams, m.instance, + //Build message. Will panic if inputs are not correct. + msg := format.NewMessage(m.session.GetCmixGroup().GetP().ByteLen()) + msg.SetKeyFP(fingerprint) + msg.SetContents(payload) + msg.SetMac(mac) + msg.SetSIH(service.Hash(msg.GetContents())) + + return sendCmixHelper(m.Sender, msg, recipient, cmixParams, m.instance, m.session.GetCmixGroup(), m.Registrar, m.rng, m.events, m.session.GetTransmissionID(), m.comms) } @@ -184,7 +170,7 @@ func sendCmixHelper(sender gateway.Sender, msg format.Message, // Build the messages to send wrappedMsg, encMsg, ephID, err := buildSlotMessage(msg, recipient, - firstGateway, stream, senderId, bestRound, roundKeys, cmixParams) + firstGateway, stream, senderId, bestRound, roundKeys) if err != nil { return 0, ephemeral.Id{}, err } diff --git a/network/sendCmixUtils.go b/network/sendCmixUtils.go index 941ef57aa042cab07b922dcce63f1cc9f3465181..18c629e7fde1ab59cb146319f3e566e68655ddb1 100644 --- a/network/sendCmixUtils.go +++ b/network/sendCmixUtils.go @@ -10,12 +10,10 @@ package network import ( "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" - preimage2 "gitlab.com/elixxir/client/interfaces/preimage" "gitlab.com/elixxir/client/network/nodes" pb "gitlab.com/elixxir/comms/mixmessages" "gitlab.com/elixxir/comms/network" "gitlab.com/elixxir/crypto/fastRNG" - "gitlab.com/elixxir/crypto/fingerprint" "gitlab.com/elixxir/primitives/format" "gitlab.com/elixxir/primitives/states" "gitlab.com/xx_network/comms/connect" @@ -112,7 +110,7 @@ func processRound(nodes nodes.Registrar, bestRound *pb.RoundInfo, // the recipient. func buildSlotMessage(msg format.Message, recipient *id.ID, target *id.ID, stream *fastRNG.Stream, senderId *id.ID, bestRound *pb.RoundInfo, - mixCrypt nodes.MixCypher, param CMIXParams) (*pb.GatewaySlot, + mixCrypt nodes.MixCypher) (*pb.GatewaySlot, format.Message, ephemeral.Id, error) { @@ -133,22 +131,6 @@ func buildSlotMessage(msg format.Message, recipient *id.ID, target *id.ID, msg.SetEphemeralRID(ephIdFilled[:]) - // use the alternate identity preimage if it is set - var preimage []byte - if param.IdentityPreimage != nil { - preimage = param.IdentityPreimage - jww.INFO.Printf("Sending to %s with override preimage %v", recipient, preimage) - } else { - preimage = preimage2.MakeDefault(recipient) - jww.INFO.Printf("Sending to %s with default preimage %v", recipient, preimage) - } - - // Set the identity fingerprint - - ifp := fingerprint.IdentityFP(msg.GetContents(), preimage) - - msg.SetIdentityFP(ifp) - // Encrypt the message salt := make([]byte, 32) _, err = stream.Read(salt) @@ -209,7 +191,7 @@ func handleMissingNodeKeys(instance *network.Instance, // string of comma seperated recipient IDs and a string of comma seperated // message digests. Duplicate recipient IDs are printed once. Intended for use // in printing to log. -func messageListToStrings(msgList []TargetedCmixMessage) (string, string) { +func messageListToStrings(msgList []assembeledCmixMessage) (string, string) { idStrings := make([]string, 0, len(msgList)) idMap := make(map[id.ID]bool, len(msgList)) msgDigests := make([]string, len(msgList)) diff --git a/network/sendManyCmix.go b/network/sendManyCmix.go index ad2ee803ba9ec49f3a2dbb2a825f3028a58c96b9..307062798f5b86326e1525c64b07ffe7ebe9a524 100644 --- a/network/sendManyCmix.go +++ b/network/sendManyCmix.go @@ -13,6 +13,7 @@ import ( jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/event" "gitlab.com/elixxir/client/network/gateway" + "gitlab.com/elixxir/client/network/message" "gitlab.com/elixxir/client/network/nodes" "gitlab.com/elixxir/client/stoppable" pb "gitlab.com/elixxir/comms/mixmessages" @@ -33,15 +34,36 @@ import ( // TargetedCmixMessage defines a recipient target pair in a sendMany cMix // message. type TargetedCmixMessage struct { - Recipient *id.ID - Message format.Message + Recipient *id.ID + Payload []byte + Fingerprint format.Fingerprint + Service message.Service + Mac []byte } -// SendManyCMIX sends many "raw" cMix message payloads to each of the provided -// recipients. Used to send messages in group chats. Metadata is NOT protected -// with this call and can leak data about yourself. Returns the round ID of the -// round the payload was sent or an error if it fails. -// WARNING: Potentially Unsafe +// SendManyCMIX sends many "raw" CMIX message payloads to the provided +// recipients all in the same round. +// Returns the round ID of the round the payloads was sent or an error +// if it fails. +// This does not have end to end encryption on it and is used exclusively as a +// send for higher order cryptographic protocols. Do not use unless implementing +// a protocol on top. +// Due to sending multiple payloads, this leaks more metadata than a standard +// cmix send and should be in general avoided. +// recipient - cMix ID of the recipient +// fingerprint - Key Fingerprint. 256 bit field to store a 255 bit +// fingerprint, highest order bit must be 0 (panic otherwise). If your +// system does not use key fingerprints, this must be random bits. +// service - Reception Service. The backup way for a client to identify +// messages on receipt via trial hashing and to identify notifications. +// If unused, use messages.RandomService to fill the field with random data +// payload - Contents of the message. Cannot exceed the payload size for a +// cMix message (panic otherwise). +// mac - 256 bit field to store a 255 bit mac, highest order bit must be 0 +// (panic otherwise). If used, fill with random bits. +// Will return an error if the network is unhealthy or if it fails to send +// (along with the reason). Blocks until successful send or err. +// WARNING: Do not roll your own crypto func (m *manager) SendManyCMIX(messages []TargetedCmixMessage, p CMIXParams) (id.Round, []ephemeral.Id, error) { if !m.Monitor.IsHealthy() { @@ -49,11 +71,30 @@ func (m *manager) SendManyCMIX(messages []TargetedCmixMessage, "message when the network is not healthy") } - return sendManyCmixHelper(m.Sender, messages, p, + acms := make([]assembeledCmixMessage, len(messages)) + for i := range messages { + msg := format.NewMessage(m.session.GetCmixGroup().GetP().ByteLen()) + msg.SetKeyFP(messages[i].Fingerprint) + msg.SetContents(messages[i].Payload) + msg.SetMac(messages[i].Mac) + msg.SetSIH(messages[i].Service.Hash(msg.GetContents())) + + acms[i] = assembeledCmixMessage{ + Recipient: messages[i].Recipient, + Message: msg, + } + } + + return sendManyCmixHelper(m.Sender, acms, p, m.instance, m.session.GetCmixGroup(), m.Registrar, m.rng, m.events, m.session.GetTransmissionID(), m.comms) } +type assembeledCmixMessage struct { + Recipient *id.ID + Message format.Message +} + // sendManyCmixHelper is a helper function for manager.SendManyCMIX. // // NOTE: Payloads sent are not end-to-end encrypted, metadata is NOT protected @@ -66,7 +107,7 @@ func (m *manager) SendManyCMIX(messages []TargetedCmixMessage, // which can be registered with the network instance to get a callback on its // status. func sendManyCmixHelper(sender gateway.Sender, - msgs []TargetedCmixMessage, param CMIXParams, instance *network.Instance, + msgs []assembeledCmixMessage, param CMIXParams, instance *network.Instance, grp *cyclic.Group, registrar nodes.Registrar, rng *fastRNG.StreamGenerator, events event.Manager, senderId *id.ID, comms SendCmixCommsInterface) ( @@ -157,7 +198,7 @@ func sendManyCmixHelper(sender gateway.Sender, for i, msg := range msgs { slots[i], encMsgs[i], ephemeralIDs[i], err = buildSlotMessage( msg.Message, msg.Recipient, firstGateway, stream, senderId, - bestRound, roundKeys, param) + bestRound, roundKeys) if err != nil { stream.Close() jww.INFO.Printf("[SendManyCMIX-%s]error building slot received: %v", param.DebugTag, err) diff --git a/storage/auth/store.go b/storage/auth/store.go index f0a48c6d7dd1c80b19d6c11aac046bb4da9a209d..9a4580333a1b191a87958a73d391e6a5d98f93ce 100644 --- a/storage/auth/store.go +++ b/storage/auth/store.go @@ -397,7 +397,7 @@ func (s *Store) GetRequest(partner *id.ID) (RequestType, *SentRequest, contact.C return Receive, nil, *r.receive, nil default: return 0, nil, contact.Contact{}, - errors.Errorf("invalid Type: %d", r.rt) + errors.Errorf("invalid Tag: %d", r.rt) } } diff --git a/storage/e2e/manager.go b/storage/e2e/manager.go index ff76e9985b72ef1c355be64ee0f643e1c07c3205..610c1b75f2e2c273f745e0824ad60325aae1462d 100644 --- a/storage/e2e/manager.go +++ b/storage/e2e/manager.go @@ -197,7 +197,7 @@ func (m *Manager) GetKeyForSending(st params.SendType) (*Key, error) { default: } - return nil, errors.Errorf("Cannot get session for invalid Send Type: %s", st) + return nil, errors.Errorf("Cannot get session for invalid Send Tag: %s", st) } // GetPartnerID returns a copy of the ID of the partner. diff --git a/storage/utility/sidh_test.go b/storage/utility/sidh_test.go index 13b557c493c277a7f292d99e8cf293ad8f3d6583..34ab3c43cfa84ee85de99fa3dff357e44dec244d 100644 --- a/storage/utility/sidh_test.go +++ b/storage/utility/sidh_test.go @@ -50,7 +50,7 @@ func TestStoreLoadDeleteSIDHPublicKey(t *testing.T) { t.Errorf("Should not load deleted key: %+v", err) } - // Now do the same for Type B keys + // Now do the same for Tag B keys x2 := NewSIDHPublicKey(sidh.KeyVariantSidhB) p2 := NewSIDHPrivateKey(sidh.KeyVariantSidhB) @@ -114,7 +114,7 @@ func TestStoreLoadDeleteSIDHPrivateKey(t *testing.T) { t.Errorf("Should not load deleted key: %+v", err) } - // Now do the same for Type B keys + // Now do the same for Tag B keys p2 := NewSIDHPrivateKey(sidh.KeyVariantSidhB) p2.Generate(myRng) diff --git a/switchboard/switchboard_test.go b/switchboard/switchboard_test.go index 2a726c73cf55a5771db525ac8e86a75168abe596..59786de8b07ef377d6afb400ff09e36ee02b91bb 100644 --- a/switchboard/switchboard_test.go +++ b/switchboard/switchboard_test.go @@ -92,7 +92,7 @@ func TestSwitchboard_RegisterListener(t *testing.T) { setType := sw.messageType.Get(mt) if !setType.Has(l) { - t.Errorf("Listener is not registered by Message Type") + t.Errorf("Listener is not registered by Message Tag") } } @@ -159,7 +159,7 @@ func TestSwitchboard_RegisterFunc(t *testing.T) { setType := sw.messageType.Get(mt) if !setType.Has(lid.listener) { - t.Errorf("Listener is not registered by Message Type") + t.Errorf("Listener is not registered by Message Tag") } lid.listener.Hear(message.Receive{}) @@ -228,7 +228,7 @@ func TestSwitchboard_RegisterChan(t *testing.T) { setType := sw.messageType.Get(mt) if !setType.Has(lid.listener) { - t.Errorf("Listener is not registered by Message Type") + t.Errorf("Listener is not registered by Message Tag") } lid.listener.Hear(message.Receive{}) @@ -335,7 +335,7 @@ func TestSwitchboard_Unregister(t *testing.T) { } if setType.Has(lid1.listener) { - t.Errorf("Removed Listener not registered by Message Type, " + + t.Errorf("Removed Listener not registered by Message Tag, " + "should not be") } @@ -345,6 +345,6 @@ func TestSwitchboard_Unregister(t *testing.T) { } if !setType.Has(lid2.listener) { - t.Errorf("Remaining Listener is not registered by Message Type") + t.Errorf("Remaining Listener is not registered by Message Tag") } }