Skip to content
Snippets Groups Projects
Commit 50823fb2 authored by Bernardo Cardoso's avatar Bernardo Cardoso
Browse files

Refactor client send/receive logic to include separate E2E / CMIX

parent 8dc8b552
Branches
Tags
No related merge requests found
...@@ -363,8 +363,8 @@ func (cl *Client) Login(UID *id.User, email, addr string, tlsCert string) (strin ...@@ -363,8 +363,8 @@ func (cl *Client) Login(UID *id.User, email, addr string, tlsCert string) (strin
func (cl *Client) Send(message parse.MessageInterface) error { func (cl *Client) Send(message parse.MessageInterface) error {
// FIXME: There should (at least) be a version of this that takes a byte array // FIXME: There should (at least) be a version of this that takes a byte array
recipientID := message.GetRecipient() recipientID := message.GetRecipient()
err := cl.comm.SendMessage(cl.sess, recipientID, message.Pack()) cryptoType := message.GetCryptoType()
return err return cl.comm.SendMessage(cl.sess, recipientID, cryptoType, message.Pack())
} }
// DisableBlockingTransmission turns off blocking transmission, for // DisableBlockingTransmission turns off blocking transmission, for
......
...@@ -10,7 +10,7 @@ package api ...@@ -10,7 +10,7 @@ package api
import ( import (
"fmt" "fmt"
jww "github.com/spf13/jwalterweatherman" jww "github.com/spf13/jwalterweatherman"
"gitlab.com/elixxir/client/crypto" "gitlab.com/elixxir/client/globals"
"gitlab.com/elixxir/client/user" "gitlab.com/elixxir/client/user"
"gitlab.com/elixxir/comms/gateway" "gitlab.com/elixxir/comms/gateway"
pb "gitlab.com/elixxir/comms/mixmessages" pb "gitlab.com/elixxir/comms/mixmessages"
...@@ -285,7 +285,7 @@ func testMainWrapper(m *testing.M) int { ...@@ -285,7 +285,7 @@ func testMainWrapper(m *testing.M) int {
} }
func getGroup() *cyclic.Group { func getGroup() *cyclic.Group {
return crypto.InitCrypto() return globals.InitCrypto()
} }
func fmtAddress(port int) string { return fmt.Sprintf("localhost:%d", port)} func fmtAddress(port int) string { return fmt.Sprintf("localhost:%d", port)}
...@@ -169,16 +169,23 @@ func (cl *Client) Login(UID []byte, email, addr string, ...@@ -169,16 +169,23 @@ func (cl *Client) Login(UID []byte, email, addr string,
// Sends a message structured via the message interface // Sends a message structured via the message interface
// Automatically serializes the message type before the rest of the payload // Automatically serializes the message type before the rest of the payload
// Returns an error if either sender or recipient are too short // Returns an error if either sender or recipient are too short
func (cl *Client) Send(m Message) error { func (cl *Client) Send(m Message, encrypt bool) error {
sender := new(id.User).SetBytes(m.GetSender()) sender := new(id.User).SetBytes(m.GetSender())
recipient := new(id.User).SetBytes(m.GetRecipient()) recipient := new(id.User).SetBytes(m.GetRecipient())
var cryptoType format.CryptoType
if encrypt {
cryptoType = format.E2E
} else {
cryptoType = format.Unencrypted
}
return cl.client.Send(&parse.Message{ return cl.client.Send(&parse.Message{
TypedBody: parse.TypedBody{ TypedBody: parse.TypedBody{
MessageType: m.GetMessageType(), MessageType: m.GetMessageType(),
Body: m.GetPayload(), Body: m.GetPayload(),
}, },
CryptoType: format.Unencrypted, CryptoType: cryptoType,
Sender: sender, Sender: sender,
Receiver: recipient, Receiver: recipient,
}) })
......
...@@ -86,7 +86,8 @@ func InitBots(s user.Session,m io.Communications) { ...@@ -86,7 +86,8 @@ func InitBots(s user.Session,m io.Communications) {
// Callers that need to wait on a response should implement waiting with a // Callers that need to wait on a response should implement waiting with a
// listener. // listener.
func sendCommand(botID *id.User, command []byte) error { func sendCommand(botID *id.User, command []byte) error {
return messaging.SendMessage(session, botID, command) return messaging.SendMessage(session, botID,
format.Unencrypted, command)
} }
// Nickname Lookup function // Nickname Lookup function
......
...@@ -32,6 +32,7 @@ type dummyMessaging struct { ...@@ -32,6 +32,7 @@ type dummyMessaging struct {
// SendMessage to the server // SendMessage to the server
func (d *dummyMessaging) SendMessage(sess user.Session, func (d *dummyMessaging) SendMessage(sess user.Session,
recipientID *id.User, recipientID *id.User,
cryptoType format.CryptoType,
message []byte) error { message []byte) error {
jww.INFO.Printf("Sending: %s", string(message)) jww.INFO.Printf("Sending: %s", string(message))
return nil return nil
......
...@@ -15,10 +15,8 @@ import ( ...@@ -15,10 +15,8 @@ import (
jww "github.com/spf13/jwalterweatherman" jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/viper" "github.com/spf13/viper"
"gitlab.com/elixxir/client/api" "gitlab.com/elixxir/client/api"
"gitlab.com/elixxir/client/bindings"
"gitlab.com/elixxir/client/bots" "gitlab.com/elixxir/client/bots"
"gitlab.com/elixxir/client/cmixproto" "gitlab.com/elixxir/client/cmixproto"
"gitlab.com/elixxir/client/crypto"
"gitlab.com/elixxir/client/globals" "gitlab.com/elixxir/client/globals"
"gitlab.com/elixxir/client/parse" "gitlab.com/elixxir/client/parse"
"gitlab.com/elixxir/client/user" "gitlab.com/elixxir/client/user"
...@@ -311,7 +309,7 @@ var rootCmd = &cobra.Command{ ...@@ -311,7 +309,7 @@ var rootCmd = &cobra.Command{
parseUdbMessage(message, client) parseUdbMessage(message, client)
} else { } else {
// Handle sending to any other destination // Handle sending to any other destination
wireOut := bindings.FormatTextMessage(message) wireOut := api.FormatTextMessage(message)
fmt.Printf("Sending Message to %d, %v: %s\n", destinationUserId, fmt.Printf("Sending Message to %d, %v: %s\n", destinationUserId,
recipientNick, message) recipientNick, message)
...@@ -350,7 +348,7 @@ var rootCmd = &cobra.Command{ ...@@ -350,7 +348,7 @@ var rootCmd = &cobra.Command{
Sender: userID, Sender: userID,
TypedBody: parse.TypedBody{ TypedBody: parse.TypedBody{
MessageType: int32(cmixproto.Type_TEXT_MESSAGE), MessageType: int32(cmixproto.Type_TEXT_MESSAGE),
Body: bindings.FormatTextMessage(message), Body: api.FormatTextMessage(message),
}, },
CryptoType: format.Unencrypted, CryptoType: format.Unencrypted,
Receiver: recipientId} Receiver: recipientId}
...@@ -458,7 +456,7 @@ func SetCertPaths(gwCertPath, registrationCertPath string) { ...@@ -458,7 +456,7 @@ func SetCertPaths(gwCertPath, registrationCertPath string) {
// initConfig reads in config file and ENV variables if set. // initConfig reads in config file and ENV variables if set.
func initConfig() { func initConfig() {
// Temporarily need to get group as JSON data into viper // Temporarily need to get group as JSON data into viper
json, err := crypto.InitCrypto().MarshalJSON() json, err := globals.InitCrypto().MarshalJSON()
if err != nil { if err != nil {
// panic // panic
} }
......
...@@ -9,72 +9,68 @@ package crypto ...@@ -9,72 +9,68 @@ package crypto
import ( import (
"errors" "errors"
jww "github.com/spf13/jwalterweatherman" jww "github.com/spf13/jwalterweatherman"
pb "gitlab.com/elixxir/comms/mixmessages" "gitlab.com/elixxir/client/keyStore"
"gitlab.com/elixxir/client/user"
"gitlab.com/elixxir/crypto/cmix"
"gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/crypto/cyclic"
"gitlab.com/elixxir/crypto/e2e" "gitlab.com/elixxir/crypto/e2e"
"gitlab.com/elixxir/crypto/hash" "gitlab.com/elixxir/crypto/hash"
pb "gitlab.com/elixxir/comms/mixmessages"
"gitlab.com/elixxir/primitives/format" "gitlab.com/elixxir/primitives/format"
) )
// Decrypt decrypts messages // CMIX Decrypt performs the decryption
func Decrypt(nodeKey *cyclic.Int, grp *cyclic.Group, // of a message from a team of nodes
cmixMsg *pb.CmixMessage) ( // It returns a new message
*format.Message, error) { func CMIX_Decrypt(session user.Session,
msg *pb.CmixMessage) *format.Message {
// Receive and decrypt a message salt := msg.Salt
payload := grp.NewIntFromBytes(cmixMsg.MessagePayload) nodeKeys := session.GetKeys()
baseKeys := make([]*cyclic.Int, len(nodeKeys))
for i, key := range nodeKeys {
baseKeys[i] = key.ReceptionKey
//TODO: Add KMAC verification here
}
// perform the CMIX decryption newMsg := format.NewMessage()
grp.Mul(payload, nodeKey, payload) newMsg.Payload = format.DeserializePayload(
msg.MessagePayload)
newMsg.AssociatedData = format.DeserializeAssociatedData(
msg.AssociatedData)
// unpack the message from a MessageBytes return cmix.ClientEncryptDecrypt(false, session.GetGroup(), newMsg, salt, baseKeys)
var message format.Message }
payloadSerial := payload.LeftpadBytes(uint64(format.TOTAL_LEN))
message.AssociatedData = format.DeserializeAssociatedData(cmixMsg.AssociatedData)
message.Payload = format.DeserializePayload(payloadSerial)
// TODO Should salt be []byte instead of cyclic.Int? // E2E_Decrypt uses the E2E key to decrypt the message
// TODO Should the method return []byte instead of cyclic.Int? // It returns an error in case of HMAC verification failure
// That might give better results in the case that the key happens to be // or in case of a decryption error (related to padding)
// not the correct length in bytes. Unlikely, but possible. // If it succeeds, it modifies the passed message
clientKey := e2e.Keygen(grp, nil, nil) func E2E_Decrypt(key *keyStore.E2EKey, grp *cyclic.Group,
// Assuming that result of e2e.Keygen() will be nil for non-e2e messages msg *format.Message) error {
// TODO BC: why is this assumption valid ?
if clientKey != nil {
clientKeyBytes := clientKey.LeftpadBytes(uint64(format.TOTAL_LEN))
// First thing to do is check MAC // First thing to do is check MAC
if !hash.VerifyHMAC(payloadSerial, message.GetMAC(), clientKeyBytes) { if !hash.VerifyHMAC(msg.SerializePayload(),
return nil, errors.New("HMAC failed for end-to-end message") msg.GetMAC(), key.GetKey().Bytes()) {
return errors.New("HMAC verification failed for E2E message")
} }
var iv [e2e.AESBlockSize]byte var iv [e2e.AESBlockSize]byte
fp := message.GetKeyFingerprint() fp := msg.GetKeyFingerprint()
copy(iv[:], fp[:e2e.AESBlockSize]) copy(iv[:], fp[:e2e.AESBlockSize])
// decrypt the timestamp in the associated data // decrypt the timestamp in the associated data
decryptedTimestamp, err := e2e.DecryptAES256WithIV(clientKeyBytes, iv, message.GetTimestamp()) decryptedTimestamp, err := e2e.DecryptAES256WithIV(
key.GetKey().Bytes(), iv, msg.GetTimestamp())
if err != nil { if err != nil {
jww.ERROR.Panicf(err.Error()) jww.ERROR.Panicf(err.Error())
} }
// TODO deserialize this somewhere along the line and provide methods // TODO deserialize this somewhere along the line and provide methods
// to mobile developers on the bindings to interact with the timestamps // to mobile developers on the bindings to interact with the timestamps
message.SetTimestamp(decryptedTimestamp) msg.SetTimestamp(decryptedTimestamp)
// Decrypt e2e // Decrypt e2e
decryptedPayload, err := e2e.Decrypt(grp, clientKey, payloadSerial) decryptedPayload, err := e2e.Decrypt(grp, key.GetKey(), msg.SerializePayload())
if err != nil { if err != nil {
return nil, errors.New(err.Error() + return errors.New("Failed to decrypt E2E message: " + err.Error())
"Failed to decrypt e2e message despite non" +
"-nil client key result")
} }
if message.SetSplitPayload(decryptedPayload) != len(decryptedPayload) { if msg.SetSplitPayload(decryptedPayload) != len(decryptedPayload) {
jww.ERROR.Panicf("Error setting decrypted payload") jww.ERROR.Panicf("Error setting decrypted payload")
} }
return &message, nil return nil
} else {
// Check MAC for non-e2e
fp := message.GetKeyFingerprint()
if hash.VerifyHMAC(payloadSerial, message.GetMAC(), fp[:]) {
return &message, nil
} else {
return nil, errors.New("HMAC failed for plaintext message")
}
}
} }
...@@ -7,77 +7,77 @@ ...@@ -7,77 +7,77 @@
package crypto package crypto
import ( import (
jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/globals"
"gitlab.com/elixxir/client/keyStore"
"gitlab.com/elixxir/client/user"
"gitlab.com/elixxir/crypto/cmix"
"gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/crypto/cyclic"
"gitlab.com/elixxir/crypto/e2e" "gitlab.com/elixxir/crypto/e2e"
"gitlab.com/elixxir/crypto/hash" "gitlab.com/elixxir/crypto/hash"
"gitlab.com/elixxir/crypto/verification" "gitlab.com/elixxir/crypto/verification"
"gitlab.com/elixxir/primitives/format" "gitlab.com/elixxir/primitives/format"
"time"
) )
// Encrypt uses the encryption key to encrypt the passed message and populate // CMIX Encrypt performs the encryption
// the associated data // of the msg to a team of nodes
// You must also encrypt the message for the nodes // It returns a new msg
func Encrypt(key *cyclic.Int, grp *cyclic.Group, func CMIX_Encrypt(session user.Session,
message *format.Message, e2eKey *cyclic.Int) (encryptedAssociatedData []byte, salt []byte,
encryptedPayload []byte) { msg *format.Message) *format.Message {
e2eKeyBytes := e2eKey.LeftpadBytes(uint64(format.TOTAL_LEN)) // Generate the encryption key
// Key fingerprint is 256 bits given by H(e2ekey) nodeKeys := session.GetKeys()
// For now use Blake2B baseKeys := make([]*cyclic.Int, len(nodeKeys))
h, _ := hash.NewCMixHash() for i, key := range nodeKeys {
h.Write(e2eKeyBytes) baseKeys[i] = key.TransmissionKey
keyFp := format.NewFingerprint(h.Sum(nil)) //TODO: Add KMAC generation here
message.SetKeyFingerprint(*keyFp) }
fp := msg.GetKeyFingerprint()
// Calculate MIC
recipientMicList := [][]byte{
msg.GetRecipientID(),
fp[:],
msg.GetTimestamp(),
msg.GetMAC(),
}
mic := verification.GenerateMIC(recipientMicList, uint64(format.AD_RMIC_LEN))
msg.SetRecipientMIC(mic)
return cmix.ClientEncryptDecrypt(true, session.GetGroup(), msg, salt, baseKeys)
}
// E2E_Encrypt 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 E2E_Encrypt(key *keyStore.E2EKey, grp *cyclic.Group,
msg *format.Message) {
keyFP := key.KeyFingerprint()
msg.SetKeyFingerprint(keyFP)
// Encrypt the timestamp using the e2ekey // Encrypt the timestamp using key
// TODO BC: this will produce a 32 byte ciphertext, where the first 16 bytes // Timestamp bytes were previously stored
// is the IV internally generated AES. This is fine right now since there are 32 bytes // and GO only uses 15 bytes, so use those
// of space in Associated Data for the timestamp.
// If we want to decrease that to 16 bytes, we need to use the key fingerprint
// as the IV for AES encryption
// TODO: timestamp like this is kinda hacky, maybe it should be set right here
// However, this would lead to parts of same message having potentially different timestamps
// Get relevant bytes from timestamp by unmarshalling and then marshalling again
timestamp := time.Time{}
timestamp.UnmarshalBinary(message.GetTimestamp())
timeBytes, _ := timestamp.MarshalBinary()
var iv [e2e.AESBlockSize]byte var iv [e2e.AESBlockSize]byte
copy(iv[:], keyFp[:e2e.AESBlockSize]) copy(iv[:], keyFP[:e2e.AESBlockSize])
encryptedTimestamp, err := e2e.EncryptAES256WithIV(e2eKeyBytes, iv, timeBytes) encryptedTimestamp, err :=
e2e.EncryptAES256WithIV(key.GetKey().Bytes(), iv,
msg.GetTimestamp()[:15])
// Make sure the encrypted timestamp fits // Make sure the encrypted timestamp fits
if len(encryptedTimestamp) != format.AD_TIMESTAMP_LEN || err != nil { if len(encryptedTimestamp) != format.AD_TIMESTAMP_LEN || err != nil {
jww.ERROR.Panicf(err.Error()) globals.Log.ERROR.Panicf(err.Error())
} }
message.SetTimestamp(encryptedTimestamp) msg.SetTimestamp(encryptedTimestamp)
// E2E encrypt the message // E2E encrypt the msg
encPayload, err := e2e.Encrypt(grp, e2eKey, message.GetPayload()) encPayload, err := e2e.Encrypt(grp, key.GetKey(), msg.GetPayload())
if len(encPayload) != format.TOTAL_LEN || err != nil { if len(encPayload) != format.TOTAL_LEN || err != nil {
jww.ERROR.Panicf(err.Error()) globals.Log.ERROR.Panicf(err.Error())
} }
message.SetPayload(encPayload) msg.SetPayload(encPayload)
// MAC is HMAC(key, ciphertext) // MAC is HMAC(key, ciphertext)
// Currently, the MAC doesn't include any of the associated data // Currently, the MAC doesn't include any of the associated data
MAC := hash.CreateHMAC(encPayload, e2eKeyBytes) MAC := hash.CreateHMAC(encPayload, key.GetKey().Bytes())
message.SetMAC(MAC) msg.SetMAC(MAC)
recipientMicList := [][]byte{
message.AssociatedData.GetRecipientID(),
keyFp[:],
message.AssociatedData.GetTimestamp(),
message.AssociatedData.GetMAC(),
}
mic := verification.GenerateMIC(recipientMicList, uint64(format.AD_RMIC_LEN))
message.SetRecipientMIC(mic)
// perform the CMIX encryption
resultPayload := grp.NewIntFromBytes(message.SerializePayload())
resultAssociatedData := grp.NewIntFromBytes(message.SerializeAssociatedData())
grp.Mul(resultPayload, key, resultPayload)
grp.Mul(resultAssociatedData, key, resultAssociatedData)
return resultAssociatedData.LeftpadBytes(uint64(format.TOTAL_LEN)),
resultPayload.LeftpadBytes(uint64(format.TOTAL_LEN))
} }
...@@ -7,15 +7,9 @@ ...@@ -7,15 +7,9 @@
package crypto_test package crypto_test
import ( import (
"bytes"
"gitlab.com/elixxir/client/crypto"
"gitlab.com/elixxir/client/user" "gitlab.com/elixxir/client/user"
pb "gitlab.com/elixxir/comms/mixmessages"
"gitlab.com/elixxir/crypto/cmix"
"gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/crypto/cyclic"
"gitlab.com/elixxir/crypto/e2e"
"gitlab.com/elixxir/crypto/large" "gitlab.com/elixxir/crypto/large"
"gitlab.com/elixxir/primitives/format"
"gitlab.com/elixxir/primitives/id" "gitlab.com/elixxir/primitives/id"
"testing" "testing"
) )
...@@ -69,55 +63,3 @@ func setup(t *testing.T) { ...@@ -69,55 +63,3 @@ func setup(t *testing.T) {
session = user.NewSession(nil, u, "", nk, session = user.NewSession(nil, u, "", nk,
nil, nil, grp) nil, nil, grp)
} }
func TestEncryptDecrypt(t *testing.T) {
setup(t)
grp := session.GetGroup()
sender := id.NewUserFromUint(38, t)
recipient := id.NewUserFromUint(29, t)
msg := format.NewMessage()
msg.SetSender(sender)
msg.SetRecipient(recipient)
msgPayload := []byte("help me, i'm stuck in an" +
" EnterpriseTextLabelDescriptorSetPipelineStateFactoryBeanFactory")
msg.SetPayloadData(msgPayload)
// Generate a compound encryption key
encryptionKey := grp.NewInt(1)
for _, key := range session.GetKeys() {
baseKey := key.TransmissionKey
partialEncryptionKey := cmix.NewEncryptionKey(salt, baseKey, grp)
grp.Mul(encryptionKey, encryptionKey, partialEncryptionKey)
//TODO: Add KMAC generation here
}
decryptionKey := grp.NewMaxInt()
grp.Inverse(encryptionKey, decryptionKey)
// do the encryption and the decryption
e2eKey := e2e.Keygen(grp, nil, nil)
assocData, payload := crypto.Encrypt(encryptionKey, grp, msg, e2eKey)
encryptedNet := &pb.CmixMessage{
SenderID: sender.Bytes(),
MessagePayload: payload,
AssociatedData: assocData,
}
decrypted, err := crypto.Decrypt(decryptionKey, grp, encryptedNet)
if err != nil {
t.Fatalf("Couldn't decrypt message: %v", err.Error())
}
if *decrypted.GetSender() != *sender {
t.Errorf("Sender differed from expected: Got %q, expected %q",
decrypted.GetRecipient(), sender)
}
if *decrypted.GetRecipient() != *recipient {
t.Errorf("Recipient differed from expected: Got %q, expected %q",
decrypted.GetRecipient(), sender)
}
if !bytes.Equal(decrypted.GetPayloadData(), msgPayload) {
t.Errorf("Decrypted payload differed from expected: Got %q, "+
"expected %q", decrypted.GetPayloadData(), msgPayload)
}
}
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
// / // /
// All rights reserved. / // All rights reserved. /
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
package crypto package globals
import ( import (
"gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/crypto/cyclic"
......
...@@ -9,6 +9,7 @@ package io ...@@ -9,6 +9,7 @@ package io
import ( import (
"gitlab.com/elixxir/client/user" "gitlab.com/elixxir/client/user"
"gitlab.com/elixxir/primitives/format"
"gitlab.com/elixxir/primitives/id" "gitlab.com/elixxir/primitives/id"
"time" "time"
) )
...@@ -16,7 +17,8 @@ import ( ...@@ -16,7 +17,8 @@ import (
// Communication interface implements send/receive functionality with the server // Communication interface implements send/receive functionality with the server
type Communications interface { type Communications interface {
// SendMessage to the server // SendMessage to the server
SendMessage(session user.Session, recipientID *id.User, message []byte) error SendMessage(session user.Session, recipientID *id.User,
cryptoType format.CryptoType, message []byte) error
// MessageReceiver thread to get new messages // MessageReceiver thread to get new messages
MessageReceiver(session user.Session, delay time.Duration) MessageReceiver(session user.Session, delay time.Duration)
} }
...@@ -13,13 +13,13 @@ import ( ...@@ -13,13 +13,13 @@ import (
"fmt" "fmt"
"gitlab.com/elixxir/client/crypto" "gitlab.com/elixxir/client/crypto"
"gitlab.com/elixxir/client/globals" "gitlab.com/elixxir/client/globals"
"gitlab.com/elixxir/client/keyStore"
"gitlab.com/elixxir/client/parse" "gitlab.com/elixxir/client/parse"
"gitlab.com/elixxir/client/user" "gitlab.com/elixxir/client/user"
"gitlab.com/elixxir/comms/client" "gitlab.com/elixxir/comms/client"
pb "gitlab.com/elixxir/comms/mixmessages" pb "gitlab.com/elixxir/comms/mixmessages"
"gitlab.com/elixxir/crypto/cmix" "gitlab.com/elixxir/crypto/cmix"
"gitlab.com/elixxir/crypto/csprng" "gitlab.com/elixxir/crypto/csprng"
"gitlab.com/elixxir/crypto/cyclic"
"gitlab.com/elixxir/crypto/e2e" "gitlab.com/elixxir/crypto/e2e"
"gitlab.com/elixxir/primitives/format" "gitlab.com/elixxir/primitives/format"
"gitlab.com/elixxir/primitives/id" "gitlab.com/elixxir/primitives/id"
...@@ -64,6 +64,7 @@ func NewMessenger() *Messaging { ...@@ -64,6 +64,7 @@ func NewMessenger() *Messaging {
// w.r.t. generating message IDs for multi-part messages.) // w.r.t. generating message IDs for multi-part messages.)
func (m *Messaging) SendMessage(session user.Session, func (m *Messaging) SendMessage(session user.Session,
recipientID *id.User, recipientID *id.User,
cryptoType format.CryptoType,
message []byte) error { message []byte) error {
// FIXME: We should really bring the plaintext parts of the NewMessage logic // FIXME: We should really bring the plaintext parts of the NewMessage logic
// into this module, then have an EncryptedMessage type that is sent to/from // into this module, then have an EncryptedMessage type that is sent to/from
...@@ -75,7 +76,6 @@ func (m *Messaging) SendMessage(session user.Session, ...@@ -75,7 +76,6 @@ func (m *Messaging) SendMessage(session user.Session,
// in this library? why not pass a sender object instead? // in this library? why not pass a sender object instead?
globals.Log.DEBUG.Printf("Sending message to %q: %q", *recipientID, message) globals.Log.DEBUG.Printf("Sending message to %q: %q", *recipientID, message)
userID := session.GetCurrentUser().User userID := session.GetCurrentUser().User
grp := session.GetGroup()
parts, err := parse.Partition([]byte(message), parts, err := parse.Partition([]byte(message),
m.nextId()) m.nextId())
if err != nil { if err != nil {
...@@ -83,17 +83,9 @@ func (m *Messaging) SendMessage(session user.Session, ...@@ -83,17 +83,9 @@ func (m *Messaging) SendMessage(session user.Session,
} }
// Every part should have the same timestamp // Every part should have the same timestamp
now := time.Now() now := time.Now()
// TODO Is it better to use Golang's binary timestamp format, or // GO Timestamp binary serialization is 15 bytes, which
// use the 2 int64 technique with Unix seconds+nanoseconds? // allows the encrypted timestamp to fit in 16 bytes
// 2 int64s is 128 bits, which is as much as can fit in the timestamp field, // using AES encryption
// but the binary serialization is 15 bytes, which is slightly smaller but
// not smaller enough to make a difference.
// The binary serialized timestamp also includes zone data, which could be
// a feature, but might compromise a little bit of anonymity.
// Using binary timestamp format for now.
// TODO BC: It is actually better to use the 15 byte version since this will
// allow the encrypted timestamp to fit in 16 bytes instead of 32, by using
// the key fingerprint as the IV for AES encryption
nowBytes, err := now.MarshalBinary() nowBytes, err := now.MarshalBinary()
if err != nil { if err != nil {
return fmt.Errorf("SendMessage MarshalBinary() error: %v", err.Error()) return fmt.Errorf("SendMessage MarshalBinary() error: %v", err.Error())
...@@ -103,9 +95,10 @@ func (m *Messaging) SendMessage(session user.Session, ...@@ -103,9 +95,10 @@ func (m *Messaging) SendMessage(session user.Session,
message.SetSender(userID) message.SetSender(userID)
message.SetRecipient(recipientID) message.SetRecipient(recipientID)
// The timestamp will be encrypted later // The timestamp will be encrypted later
// NOTE: This sets 15 bytes, not 16
message.SetTimestamp(nowBytes) message.SetTimestamp(nowBytes)
message.SetPayloadData(parts[i]) message.SetPayloadData(parts[i])
err = m.send(session, userID, message, grp) err = m.send(session, cryptoType, message)
if err != nil { if err != nil {
return fmt.Errorf("SendMessage send() error: %v", err.Error()) return fmt.Errorf("SendMessage send() error: %v", err.Error())
} }
...@@ -113,9 +106,51 @@ func (m *Messaging) SendMessage(session user.Session, ...@@ -113,9 +106,51 @@ func (m *Messaging) SendMessage(session user.Session,
return nil return nil
} }
// Send Message without doing partitions
// This function will be needed for example to send a Rekey
// message, where a new public key will take up all the message
func (m *Messaging) SendMessageNoPartition(session user.Session,
recipientID *id.User,
cryptoType format.CryptoType,
message []byte) error {
size := len(message)
if size > format.TOTAL_LEN {
return fmt.Errorf("SendMessageNoPartition() error: message to be sent is too big")
}
userID := session.GetCurrentUser().User
now := time.Now()
// GO Timestamp binary serialization is 15 bytes, which
// allows the encrypted timestamp to fit in 16 bytes
// using AES encryption
nowBytes, err := now.MarshalBinary()
if err != nil {
return fmt.Errorf("SendMessageNoPartition MarshalBinary() error: %v", err.Error())
}
msg := format.NewMessage()
msg.SetRecipient(recipientID)
// The timestamp will be encrypted later
// NOTE: This sets 15 bytes, not 16
msg.SetTimestamp(nowBytes)
// If message is bigger than payload size
// use SenderID space to send it
if size > format.MP_PAYLOAD_LEN {
msg.SetSenderID(message[:format.MP_SID_END])
msg.SetPayloadData(message[format.MP_SID_END:])
} else {
msg.SetSender(userID)
msg.SetPayloadData(message)
}
err = m.send(session, cryptoType, msg)
if err != nil {
return fmt.Errorf("SendMessageNoPartition send() error: %v", err.Error())
}
return nil
}
// send actually sends the message to the server // send actually sends the message to the server
func (m *Messaging) send(session user.Session, func (m *Messaging) send(session user.Session,
senderID *id.User, message *format.Message, grp *cyclic.Group) error { cryptoType format.CryptoType,
message *format.Message) error {
// Enable transmission blocking if enabled // Enable transmission blocking if enabled
if m.BlockTransmissions { if m.BlockTransmissions {
m.sendLock.Lock() m.sendLock.Lock()
...@@ -125,39 +160,54 @@ func (m *Messaging) send(session user.Session, ...@@ -125,39 +160,54 @@ func (m *Messaging) send(session user.Session,
}() }()
} }
salt := cmix.NewSalt(csprng.Source(&csprng.SystemRNG{}), 16) // Check message type
if cryptoType == format.E2E {
// TBD: Add key macs to this message handleE2ESending(session, message)
macs := make([][]byte, 0) } else {
padded, err := e2e.Pad(message.GetPayload(), format.TOTAL_LEN)
// Generate a compound encryption key if err != nil {
encryptionKey := grp.NewInt(1) return err
for _, key := range session.GetKeys() { }
baseKey := key.TransmissionKey message.SetPayload(padded)
partialEncryptionKey := cmix.NewEncryptionKey(salt, baseKey, grp) e2e.SetUnencrypted(message)
grp.Mul(encryptionKey, partialEncryptionKey, encryptionKey)
//TODO: Add KMAC generation here
} }
// TBD: Is there a really good reason we have to specify the Grp and not a // CMIX Encryption
// key? Should we even be doing the encryption here? salt := cmix.NewSalt(csprng.Source(&csprng.SystemRNG{}), 16)
// TODO: Use salt here / generate n key map encMsg := crypto.CMIX_Encrypt(session, salt, message)
e2eKey := e2e.Keygen(grp, nil, nil)
associatedData, payload := crypto.Encrypt(encryptionKey, grp,
message, e2eKey)
msgPacket := &pb.CmixMessage{ msgPacket := &pb.CmixMessage{
SenderID: senderID.Bytes(), SenderID: session.GetCurrentUser().User.Bytes(),
MessagePayload: payload, MessagePayload: encMsg.SerializePayload(),
AssociatedData: associatedData, AssociatedData: encMsg.SerializeAssociatedData(),
Salt: salt, Salt: salt,
KMACs: macs, KMACs: make([][]byte, 0),
} }
var err error
globals.Log.INFO.Println("Sending put message to gateway") globals.Log.INFO.Println("Sending put message to gateway")
err = client.SendPutMessage(m.SendAddress, msgPacket) return client.SendPutMessage(m.SendAddress, msgPacket)
}
return err func handleE2ESending(session user.Session,
message *format.Message) {
recipientID := message.GetRecipient()
// Get send key
sendKey, action := session.GetKeyStore().
TransmissionKeys.Pop(recipientID)
if sendKey == nil {
globals.Log.FATAL.Panicf("Couldn't get key to E2E encrypt message to" +
" user %v", *recipientID)
} else if action == keyStore.Deleted {
globals.Log.FATAL.Panicf("Key Manager is deleted when trying to get E2E Send Key")
}
if action == keyStore.Rekey {
// TODO handle Send Rekey message to SW
}
crypto.E2E_Encrypt(sendKey, session.GetGroup(), message)
} }
// MessageReceiver is a polling thread for receiving messages -- again.. we // MessageReceiver is a polling thread for receiving messages -- again.. we
...@@ -185,6 +235,7 @@ func (m *Messaging) MessageReceiver(session user.Session, delay time.Duration) { ...@@ -185,6 +235,7 @@ func (m *Messaging) MessageReceiver(session user.Session, delay time.Duration) {
decryptedMessages := m.receiveMessagesFromGateway(session, &pollingMessage) decryptedMessages := m.receiveMessagesFromGateway(session, &pollingMessage)
if decryptedMessages != nil { if decryptedMessages != nil {
for i := range decryptedMessages { for i := range decryptedMessages {
// TODO Handle messages that do not need partitioning
assembledMessage := m.collator.AddMessage( assembledMessage := m.collator.AddMessage(
decryptedMessages[i], time.Minute) decryptedMessages[i], time.Minute)
if assembledMessage != nil { if assembledMessage != nil {
...@@ -197,6 +248,28 @@ func (m *Messaging) MessageReceiver(session user.Session, delay time.Duration) { ...@@ -197,6 +248,28 @@ func (m *Messaging) MessageReceiver(session user.Session, delay time.Duration) {
} }
} }
func handleE2EReceiving(session user.Session,
message *format.Message) error {
keyFingerprint := message.GetKeyFingerprint()
// Lookup reception key
recpKey := session.GetKeyStore().
ReceptionKeys.Pop(keyFingerprint)
if recpKey == nil {
// TODO Handle sending error message to SW
return fmt.Errorf("E2EKey for matching fingerprint not found, can't process message")
} else if recpKey.GetOuterType() == format.Rekey {
// TODO Handle Receiving Keys Rekey (partner rekey)
}
err := crypto.E2E_Decrypt(recpKey, session.GetGroup(), message)
if err != nil {
// TODO handle Garbled message to SW
}
return err
}
func (m *Messaging) receiveMessagesFromGateway(session user.Session, func (m *Messaging) receiveMessagesFromGateway(session user.Session,
pollingMessage *pb.ClientPollMessage) []*format.Message { pollingMessage *pb.ClientPollMessage) []*format.Message {
if session != nil { if session != nil {
...@@ -212,7 +285,6 @@ func (m *Messaging) receiveMessagesFromGateway(session user.Session, ...@@ -212,7 +285,6 @@ func (m *Messaging) receiveMessagesFromGateway(session user.Session,
globals.Log.INFO.Printf("Checking novelty of %v messages", len(messages.MessageIDs)) globals.Log.INFO.Printf("Checking novelty of %v messages", len(messages.MessageIDs))
results := make([]*format.Message, 0, len(messages.MessageIDs)) results := make([]*format.Message, 0, len(messages.MessageIDs))
grp := session.GetGroup()
for _, messageID := range messages.MessageIDs { for _, messageID := range messages.MessageIDs {
// Get the first unseen message from the list of IDs // Get the first unseen message from the list of IDs
_, received := m.ReceivedMessages[messageID] _, received := m.ReceivedMessages[messageID]
...@@ -239,15 +311,28 @@ func (m *Messaging) receiveMessagesFromGateway(session user.Session, ...@@ -239,15 +311,28 @@ func (m *Messaging) receiveMessagesFromGateway(session user.Session,
continue continue
} }
// Generate a compound decryption key // CMIX Decryption
salt := newMessage.Salt decMsg := crypto.CMIX_Decrypt(session, newMessage)
decryptionKey := grp.NewInt(1)
for _, key := range session.GetKeys() { var err error = nil
baseKey := key.ReceptionKey var unpadded []byte
partialDecryptionKey := cmix.NewDecryptionKey(salt, baseKey, // If message is E2E, handle decryption
grp) if !e2e.IsUnencrypted(decMsg) {
grp.Mul(decryptionKey, partialDecryptionKey, decryptionKey) err = handleE2EReceiving(session, decMsg)
//TODO: Add KMAC verification here } else {
// If message is non E2E, need to unpad payload
unpadded, err = e2e.Unpad(decMsg.SerializePayload())
if err == nil {
decMsg.SetSplitPayload(unpadded)
}
}
if err != nil {
globals.Log.WARN.Printf(
"Message did not decrypt properly, "+
"not adding to results array: %v", err.Error())
} else {
results = append(results, decMsg)
} }
globals.Log.INFO.Printf( globals.Log.INFO.Printf(
...@@ -255,16 +340,6 @@ func (m *Messaging) receiveMessagesFromGateway(session user.Session, ...@@ -255,16 +340,6 @@ func (m *Messaging) receiveMessagesFromGateway(session user.Session,
m.ReceivedMessages[messageID] = struct{}{} m.ReceivedMessages[messageID] = struct{}{}
session.SetLastMessageID(messageID) session.SetLastMessageID(messageID)
session.StoreSession() session.StoreSession()
decryptedMsg, err2 := crypto.Decrypt(decryptionKey, grp,
newMessage)
if err2 != nil {
globals.Log.WARN.Printf(
"Message did not decrypt properly, "+
"not adding to results array: %v", err2.Error())
} else {
results = append(results, decryptedMsg)
}
} }
} }
} }
......
...@@ -8,7 +8,6 @@ package user ...@@ -8,7 +8,6 @@ package user
import ( import (
"crypto/sha256" "crypto/sha256"
"gitlab.com/elixxir/client/crypto"
"gitlab.com/elixxir/client/globals" "gitlab.com/elixxir/client/globals"
"gitlab.com/elixxir/primitives/id" "gitlab.com/elixxir/primitives/id"
) )
...@@ -58,7 +57,7 @@ func newRegistry() Registry { ...@@ -58,7 +57,7 @@ func newRegistry() Registry {
nk := make(map[id.User]*NodeKeys) nk := make(map[id.User]*NodeKeys)
// Initialize group object // Initialize group object
grp := crypto.InitCrypto() grp := globals.InitCrypto()
// Deterministically create NUM_DEMO_USERS users // Deterministically create NUM_DEMO_USERS users
// TODO Replace this with real user registration/discovery // TODO Replace this with real user registration/discovery
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment