diff --git a/network/message/handler.go b/network/message/handler.go index 8e3b34f30de7502844daebc01b057b62810802ac..6c7daf1d7acff12d39cf53b1c72c9c0f8a2c1eed 100644 --- a/network/message/handler.go +++ b/network/message/handler.go @@ -1,9 +1,10 @@ package message import ( + "bytes" jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/interfaces/message" - "gitlab.com/elixxir/crypto/e2e" + "gitlab.com/elixxir/crypto/hash" "gitlab.com/elixxir/primitives/format" "gitlab.com/xx_network/primitives/id" "time" @@ -40,6 +41,7 @@ func (m *Manager) handleMessage(ecrMsg format.Message) { // try to get the key fingerprint, process as e2e encryption if // the fingerprint is found if key, isE2E := e2eKv.PopKey(fingerprint); isE2E { + jww.INFO.Printf("is e2e message") // Decrypt encrypted message msg, err = key.Decrypt(ecrMsg) // get the sender @@ -55,13 +57,15 @@ func (m *Manager) handleMessage(ecrMsg format.Message) { } //set the type as E2E encrypted encTy = message.E2E - } else if isUnencrypted, uSender := e2e.IsUnencrypted(ecrMsg); isUnencrypted { + } else if isUnencrypted, uSender := IsUnencrypted(ecrMsg); isUnencrypted { + jww.INFO.Printf("is unencrypted") // if the key fingerprint does not match, try to treat it as an // unencrypted message sender = uSender msg = ecrMsg encTy = message.None } else { + jww.INFO.Printf("is raw") // if it doesnt match any form of encrypted, hear it as a raw message // and add it to garbled messages to be handled later msg = ecrMsg @@ -92,3 +96,75 @@ func (m *Manager) handleMessage(ecrMsg format.Message) { } } } + +const macMask = 0b00111111 + +// IsUnencrypted determines if the message is unencrypted by comparing the hash +// of the message payload to the MAC. Returns true if the message is unencrypted +// and false otherwise. +// the highest bit of the recpient ID is stored in the highest bit of the MAC +// field. This is accounted for and the id is reassembled, with a presumed user +// type +func IsUnencrypted(m format.Message) (bool, *id.ID) { + + expectedMac := makeUnencryptedMAC(m.GetContents()) + receivedMac := m.GetMac() + idHighBit := (receivedMac[0] & 0b01000000) << 1 + receivedMac[0] &= macMask + + //return false if the message is not unencrypted + if !bytes.Equal(expectedMac, receivedMac) { + jww.INFO.Printf("Failed isUnencrypted! Expected: %v; " + + " Received: %v", expectedMac, receivedMac) + return false, nil + } + + //extract the user ID + idBytes := m.GetKeyFP() + idBytes[0] |= idHighBit + uid := id.ID{} + copy(uid[:], idBytes[:]) + uid.SetType(id.User) + + // Return true if the byte slices are equal + return true, &uid +} + +// SetUnencrypted sets up the condition where the message would be determined to +// be unencrypted by setting the MAC to the hash of the message payload. +func SetUnencrypted(m format.Message, uid *id.ID) { + mac := makeUnencryptedMAC(m.GetContents()) + + //copy in the high bit of the userID for storage + mac[0] |= (uid[0] & 0b10000000) >> 1 + + // Set the MAC + m.SetMac(mac) + + //remove the type byte off of the userID and clear the highest bit so + //it can be stored in the fingerprint + fp := format.Fingerprint{} + copy(fp[:], uid[:format.KeyFPLen]) + fp[0] &= 0b01111111 + + m.SetKeyFP(fp) +} + +// returns the mac, fingerprint, and the highest byte +func makeUnencryptedMAC(payload []byte)[]byte{ + // Create new hash + h, err := hash.NewCMixHash() + + if err != nil { + jww.ERROR.Panicf("Failed to create hash: %v", err) + } + + // Hash the message payload + h.Write(payload) + payloadHash := h.Sum(nil) + + //set the first bit as zero to ensure everything stays in the group + payloadHash[0] &= macMask + + return payloadHash +} \ No newline at end of file