Communities encryption

This commit is contained in:
Vitaliy Vlasov 2021-09-21 18:47:04 +03:00 committed by Vitaliy Vlasov
parent 51a6d8ee7c
commit e6dffe8d8d
15 changed files with 3226 additions and 742 deletions

View File

@ -309,7 +309,7 @@ func (s *MessageSender) sendPrivate(
s.transport.Track(messageIDs, hash, newMessage)
} else {
messageSpec, err := s.protocol.BuildDirectMessage(rawMessage.Sender, recipient, wrappedMessage)
messageSpec, err := s.protocol.BuildEncryptedMessage(rawMessage.Sender, recipient, wrappedMessage)
if err != nil {
return nil, errors.Wrap(err, "failed to encrypt message")
}
@ -660,7 +660,7 @@ func (s *MessageSender) sendDataSync(ctx context.Context, publicKey *ecdsa.Publi
hexMessageIDs = append(hexMessageIDs, mid.String())
}
messageSpec, err := s.protocol.BuildDirectMessage(s.identity, publicKey, marshalledDatasyncPayload)
messageSpec, err := s.protocol.BuildEncryptedMessage(s.identity, publicKey, marshalledDatasyncPayload)
if err != nil {
return errors.Wrap(err, "failed to encrypt message")
}

View File

@ -204,7 +204,7 @@ func (s *MessageSenderSuite) TestHandleDecodedMessagesDatasyncEncrypted() {
s.logger,
)
messageSpec, err := senderEncryptionProtocol.BuildDirectMessage(
messageSpec, err := senderEncryptionProtocol.BuildEncryptedMessage(
relayerKey,
&s.sender.identity.PublicKey,
marshalledDataSyncMessage,

View File

@ -0,0 +1,12 @@
# protocol/encryption package
## Hash ratchet encryption
`encryptor.GenerateHashRatchetKey()` generates a hash ratchet key and stores it in in the DB.
There, 2 new tables are created: `hash_ratchet_encryption` and `hash_ratchet_encryption_cache`.
Each hash ratchet key is uniquely identified by the `(groupId, keyId)` pair, where `keyId` is derived from a clock value.
`protocol.BuildHashRatchetKeyExchangeMessage` builds an 1-on-1 message containing the hash ratchet key, given it's ID.
`protocol.BuildHashRatchetMessage` builds a hash ratchet message with arbitrary payload, given `groupId`. It will use the latest hash ratchet key available. `encryptor.encryptWithHR` encrypts the payload using Hash Ratchet algorithms. Intermediate hashes are stored in `hash_ratchet_encryption_cache` table.
`protocol.HandleMessage` uses `encryptor.decryptWithHR` fn for decryption.

View File

@ -237,9 +237,9 @@ func (s *EncryptionServiceMultiDeviceSuite) TestMaxDevices() {
s.Require().NoError(err)
// Bob sends a message to alice
msg, err := bob1.BuildDirectMessage(bobKey, &aliceKey.PublicKey, []byte("test"))
msg, err := bob1.BuildEncryptedMessage(bobKey, &aliceKey.PublicKey, []byte("test"))
s.Require().NoError(err)
payload := msg.Message.GetDirectMessage()
payload := msg.Message.GetEncryptedMessage()
s.Require().Equal(3, len(payload))
s.Require().NotNil(payload["alice1"])
s.Require().NotNil(payload["alice3"])
@ -267,9 +267,9 @@ func (s *EncryptionServiceMultiDeviceSuite) TestMaxDevices() {
s.Require().NoError(err)
// Bob sends a message to alice
msg, err = bob1.BuildDirectMessage(bobKey, &aliceKey.PublicKey, []byte("test"))
msg, err = bob1.BuildEncryptedMessage(bobKey, &aliceKey.PublicKey, []byte("test"))
s.Require().NoError(err)
payload = msg.Message.GetDirectMessage()
payload = msg.Message.GetEncryptedMessage()
s.Require().Equal(3, len(payload))
s.Require().NotNil(payload["alice1"])
s.Require().NotNil(payload["alice2"])
@ -312,9 +312,9 @@ func (s *EncryptionServiceMultiDeviceSuite) TestMaxDevicesRefreshedBundle() {
s.Require().NoError(err)
// Bob sends a message to alice
msg1, err := bob1.BuildDirectMessage(bobKey, &aliceKey.PublicKey, []byte("test"))
msg1, err := bob1.BuildEncryptedMessage(bobKey, &aliceKey.PublicKey, []byte("test"))
s.Require().NoError(err)
payload := msg1.Message.GetDirectMessage()
payload := msg1.Message.GetEncryptedMessage()
s.Require().Equal(3, len(payload))
// Alice1 is the oldest bundle and is rotated out
// as we send maximum to 3 devices
@ -324,7 +324,7 @@ func (s *EncryptionServiceMultiDeviceSuite) TestMaxDevicesRefreshedBundle() {
s.Require().NotNil(payload["alice4"])
// We send a message to bob from alice1, the timestamp should be refreshed
msg2, err := alice1.BuildDirectMessage(aliceKey, &bobKey.PublicKey, []byte("test"))
msg2, err := alice1.BuildEncryptedMessage(aliceKey, &bobKey.PublicKey, []byte("test"))
s.Require().NoError(err)
alice1Bundle = msg2.Message.GetBundles()[0]
@ -334,9 +334,9 @@ func (s *EncryptionServiceMultiDeviceSuite) TestMaxDevicesRefreshedBundle() {
s.Require().NoError(err)
// Bob sends a message to alice
msg3, err := bob1.BuildDirectMessage(bobKey, &aliceKey.PublicKey, []byte("test"))
msg3, err := bob1.BuildEncryptedMessage(bobKey, &aliceKey.PublicKey, []byte("test"))
s.Require().NoError(err)
payload = msg3.Message.GetDirectMessage()
payload = msg3.Message.GetEncryptedMessage()
s.Require().Equal(3, len(payload))
// Alice 1 is added back to the list of active devices
s.Require().NotNil(payload["alice1"])

View File

@ -69,7 +69,7 @@ func (s *EncryptionServiceTestSuite) initDatabases(config encryptorConfig) {
}
func (s *EncryptionServiceTestSuite) SetupTest() {
s.logger = zap.NewNop()
s.logger, _ = zap.NewProduction()
s.initDatabases(defaultEncryptorConfig("none", s.logger))
}
@ -92,6 +92,118 @@ func (s *EncryptionServiceTestSuite) TestGetBundle() {
s.NotEqual(aliceBundle1.Timestamp, aliceBundle2.Timestamp, "It refreshes the timestamp")
}
func (s *EncryptionServiceTestSuite) TestHashRatchetSend() {
aliceKey, err := crypto.GenerateKey()
s.Require().NoError(err)
bobKey, err := crypto.GenerateKey()
s.Require().NoError(err)
communityID := "test_community_id"
s.Require().NotNil(aliceKey)
s.Require().NotNil(bobKey)
s.logger.Info("Hash ratchet key exchange 1")
keyID1, _ := s.alice.encryptor.GenerateHashRatchetKey([]byte(communityID))
hashRatchetKeyExMsg1, _ := s.alice.BuildHashRatchetKeyExchangeMessage(aliceKey, &bobKey.PublicKey, communityID, keyID1)
s.logger.Info("Hash ratchet key exchange 1", zap.Any("msg", hashRatchetKeyExMsg1.Message))
s.Require().NotNil(hashRatchetKeyExMsg1)
s.logger.Info("Handle hash ratchet key msg 1")
decryptedResponse1, _ := s.bob.HandleMessage(bobKey, &aliceKey.PublicKey, hashRatchetKeyExMsg1.Message, defaultMessageID)
decryptedHashRatchetKeyBytes1 := decryptedResponse1.DecryptedMessage
decryptedHashRatchetKeyID1, _ := s.bob.encryptor.persistence.GetCurrentKeyForGroup(communityID)
s.logger.Info("Current hash ratchet key in DB 1", zap.Any("keyId", decryptedHashRatchetKeyID1))
s.Require().NotNil(decryptedHashRatchetKeyID1)
s.Require().NotNil(decryptedHashRatchetKeyBytes1)
//s.Equal(decryptedHashRatchetKey1, decryptedHashRatchetKeyBytes1)
payload1 := []byte("community msg 1")
hashRatchetMsg1, err := s.bob.BuildHashRatchetMessage([]byte(communityID), payload1)
s.logger.Info("BuildHashRatchetMessage 1", zap.Any("err", err))
s.Require().NotNil(hashRatchetMsg1)
s.Require().NotNil(hashRatchetMsg1.Message)
decryptedResponse2, err := s.alice.HandleMessage(aliceKey, nil, hashRatchetMsg1.Message, defaultMessageID)
s.logger.Info("HandleHashRatchetMessage 1", zap.Any("err", err))
s.Require().NotNil(decryptedResponse2)
s.Equal(payload1, decryptedResponse2.DecryptedMessage)
payload2 := []byte("community msg 2")
hashRatchetMsg2, err := s.alice.BuildHashRatchetMessage([]byte(communityID), payload2)
s.logger.Info("BuildHashRatchetMessage 2", zap.Any("err", err))
s.Require().NotNil(hashRatchetMsg2)
s.Require().NotNil(hashRatchetMsg2.Message)
decryptedResponse3, err := s.bob.HandleMessage(bobKey, nil, hashRatchetMsg2.Message, defaultMessageID)
s.logger.Info("HandleHashRatchetMessage 2", zap.Any("err", err))
s.Require().NotNil(decryptedResponse3)
s.Equal(payload2, decryptedResponse3.DecryptedMessage)
// Re-generate hash ratchet key. Bob generates a new key and sends it to Alice
keyID2, _ := s.bob.encryptor.GenerateHashRatchetKey([]byte(communityID))
hashRatchetKeyExMsg2, _ := s.bob.BuildHashRatchetKeyExchangeMessage(bobKey, &aliceKey.PublicKey, communityID, keyID2)
s.logger.Info("Hash ratchet key exchange 2", zap.Any("msg", hashRatchetKeyExMsg2.Message))
s.Require().NotNil(hashRatchetKeyExMsg2)
s.logger.Info("Handle hash ratchet key msg 2")
decryptedResponse4, _ := s.alice.HandleMessage(aliceKey, &bobKey.PublicKey, hashRatchetKeyExMsg2.Message, defaultMessageID)
decryptedHashRatchetKeyBytes2 := decryptedResponse4.DecryptedMessage
decryptedHashRatchetKeyID2, _ := s.alice.encryptor.persistence.GetCurrentKeyForGroup(communityID)
s.logger.Info("Current hash ratchet key in DB 2", zap.Any("keyId", decryptedHashRatchetKeyID2))
s.Require().NotNil(decryptedHashRatchetKeyID2)
s.Require().NotNil(decryptedHashRatchetKeyBytes2)
payload3 := []byte("community msg 3")
hashRatchetMsg3, err := s.alice.BuildHashRatchetMessage([]byte(communityID), payload3)
s.logger.Info("BuildHashRatchetMessage err", zap.Any("err", err))
s.Require().NotNil(hashRatchetMsg3)
s.Require().NotNil(hashRatchetMsg3.Message)
//directMsg1 := hashRatchetMsg.Message.GetEncryptedMessage()
decryptedResponse5, err := s.bob.HandleMessage(bobKey, nil, hashRatchetMsg3.Message, defaultMessageID)
s.logger.Info("HandleHashRatchetMessage err", zap.Any("err", err))
s.Require().NotNil(decryptedResponse5)
s.Equal(payload3, decryptedResponse5.DecryptedMessage)
payload4 := []byte("community msg 4")
payload5 := []byte("community msg 5")
payload6 := []byte("community msg 6")
hashRatchetMsg4, _ := s.alice.BuildHashRatchetMessage([]byte(communityID), payload4) // seqNo=2
hashRatchetMsg5, _ := s.alice.BuildHashRatchetMessage([]byte(communityID), payload5) // seqNo=3
hashRatchetMsg6, _ := s.alice.BuildHashRatchetMessage([]byte(communityID), payload6) // seqNo=3
// Handle them out of order plus an older one we've received earlier with seqNo=1
decryptedResponse6, _ := s.bob.HandleMessage(bobKey, nil, hashRatchetMsg6.Message, defaultMessageID)
decryptedResponse7, _ := s.bob.HandleMessage(bobKey, nil, hashRatchetMsg5.Message, defaultMessageID)
decryptedResponse8, _ := s.bob.HandleMessage(bobKey, nil, hashRatchetMsg4.Message, defaultMessageID)
decryptedResponse9, _ := s.bob.HandleMessage(bobKey, nil, hashRatchetMsg3.Message, defaultMessageID)
s.logger.Info("HandleHashRatchetMessage err", zap.Any("err", err))
s.Require().NotNil(decryptedResponse6)
s.Equal(payload6, decryptedResponse6.DecryptedMessage)
s.Require().NotNil(decryptedResponse7)
s.Equal(payload5, decryptedResponse7.DecryptedMessage)
s.Require().NotNil(decryptedResponse8)
s.Equal(payload4, decryptedResponse8.DecryptedMessage)
s.Require().NotNil(decryptedResponse9)
s.Equal(payload3, decryptedResponse9.DecryptedMessage)
// Handle message with previous key
decryptedResponse10, _ := s.bob.HandleMessage(bobKey, nil, hashRatchetMsg2.Message, defaultMessageID)
s.Require().NotNil(decryptedResponse10)
s.Equal(payload2, decryptedResponse10.DecryptedMessage)
}
// Alice sends Bob an encrypted message with DH using an ephemeral key
// and Bob's identity key.
// Bob is able to decrypt it.
@ -103,10 +215,10 @@ func (s *EncryptionServiceTestSuite) TestEncryptPayloadNoBundle() {
aliceKey, err := crypto.GenerateKey()
s.Require().NoError(err)
response1, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, cleartext)
response1, err := s.alice.BuildEncryptedMessage(aliceKey, &bobKey.PublicKey, cleartext)
s.Require().NoError(err)
encryptionResponse1 := response1.Message.GetDirectMessage()
encryptionResponse1 := response1.Message.GetEncryptedMessage()
installationResponse1 := encryptionResponse1["none"]
// That's for any device
@ -124,10 +236,10 @@ func (s *EncryptionServiceTestSuite) TestEncryptPayloadNoBundle() {
s.Equal(cleartext, decryptedPayload1.DecryptedMessage, "It correctly decrypts the payload using DH")
// The next message will not be re-using the same key
response2, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, cleartext)
response2, err := s.alice.BuildEncryptedMessage(aliceKey, &bobKey.PublicKey, cleartext)
s.Require().NoError(err)
encryptionResponse2 := response2.Message.GetDirectMessage()
encryptionResponse2 := response2.Message.GetEncryptedMessage()
installationResponse2 := encryptionResponse2[aliceInstallationID]
@ -160,10 +272,10 @@ func (s *EncryptionServiceTestSuite) TestEncryptPayloadBundle() {
s.Require().NoError(err)
// We send a message using the bundle
response1, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, cleartext)
response1, err := s.alice.BuildEncryptedMessage(aliceKey, &bobKey.PublicKey, cleartext)
s.Require().NoError(err)
encryptionResponse1 := response1.Message.GetDirectMessage()
encryptionResponse1 := response1.Message.GetEncryptedMessage()
installationResponse1 := encryptionResponse1[bobInstallationID]
s.Require().NotNil(installationResponse1)
@ -221,13 +333,13 @@ func (s *EncryptionServiceTestSuite) TestConsequentMessagesBundle() {
s.Require().NoError(err)
// We send a message using the bundle
_, err = s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, cleartext1)
_, err = s.alice.BuildEncryptedMessage(aliceKey, &bobKey.PublicKey, cleartext1)
s.Require().NoError(err)
// We send another message using the bundle
response, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, cleartext2)
response, err := s.alice.BuildEncryptedMessage(aliceKey, &bobKey.PublicKey, cleartext2)
s.Require().NoError(err)
encryptionResponse := response.Message.GetDirectMessage()
encryptionResponse := response.Message.GetEncryptedMessage()
installationResponse := encryptionResponse[bobInstallationID]
s.Require().NotNil(installationResponse)
@ -295,7 +407,7 @@ func (s *EncryptionServiceTestSuite) TestConversation() {
s.Require().NoError(err)
// Alice sends a message
response, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, cleartext1)
response, err := s.alice.BuildEncryptedMessage(aliceKey, &bobKey.PublicKey, cleartext1)
s.Require().NoError(err)
// Bob receives the message
@ -303,7 +415,7 @@ func (s *EncryptionServiceTestSuite) TestConversation() {
s.Require().NoError(err)
// Bob replies to the message
response, err = s.bob.BuildDirectMessage(bobKey, &aliceKey.PublicKey, cleartext1)
response, err = s.bob.BuildEncryptedMessage(bobKey, &aliceKey.PublicKey, cleartext1)
s.Require().NoError(err)
// Alice receives the message
@ -311,9 +423,9 @@ func (s *EncryptionServiceTestSuite) TestConversation() {
s.Require().NoError(err)
// We send another message using the bundle
response, err = s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, cleartext2)
response, err = s.alice.BuildEncryptedMessage(aliceKey, &bobKey.PublicKey, cleartext2)
s.Require().NoError(err)
encryptionResponse := response.Message.GetDirectMessage()
encryptionResponse := response.Message.GetEncryptedMessage()
installationResponse := encryptionResponse[bobInstallationID]
s.Require().NotNil(installationResponse)
@ -378,12 +490,12 @@ func (s *EncryptionServiceTestSuite) TestMaxSkipKeys() {
// Bob sends a message
for i := 0; i < s.alice.encryptor.config.MaxSkip; i++ {
_, err = s.bob.BuildDirectMessage(bobKey, &aliceKey.PublicKey, bobText)
_, err = s.bob.BuildEncryptedMessage(bobKey, &aliceKey.PublicKey, bobText)
s.Require().NoError(err)
}
// Bob sends a message
bobMessage1, err := s.bob.BuildDirectMessage(bobKey, &aliceKey.PublicKey, bobText)
bobMessage1, err := s.bob.BuildEncryptedMessage(bobKey, &aliceKey.PublicKey, bobText)
s.Require().NoError(err)
// Alice receives the message
@ -391,11 +503,11 @@ func (s *EncryptionServiceTestSuite) TestMaxSkipKeys() {
s.Require().NoError(err)
// Bob sends a message
_, err = s.bob.BuildDirectMessage(bobKey, &aliceKey.PublicKey, bobText)
_, err = s.bob.BuildEncryptedMessage(bobKey, &aliceKey.PublicKey, bobText)
s.Require().NoError(err)
// Bob sends a message
bobMessage2, err := s.bob.BuildDirectMessage(bobKey, &aliceKey.PublicKey, bobText)
bobMessage2, err := s.bob.BuildEncryptedMessage(bobKey, &aliceKey.PublicKey, bobText)
s.Require().NoError(err)
// Alice receives the message, we should have maxSkip + 1 keys in the db, but
@ -433,12 +545,12 @@ func (s *EncryptionServiceTestSuite) TestMaxSkipKeysError() {
// Bob sends a message
for i := 0; i < s.alice.encryptor.config.MaxSkip+1; i++ {
_, err = s.bob.BuildDirectMessage(bobKey, &aliceKey.PublicKey, bobText)
_, err = s.bob.BuildEncryptedMessage(bobKey, &aliceKey.PublicKey, bobText)
s.Require().NoError(err)
}
// Bob sends a message
bobMessage1, err := s.bob.BuildDirectMessage(bobKey, &aliceKey.PublicKey, bobText)
bobMessage1, err := s.bob.BuildEncryptedMessage(bobKey, &aliceKey.PublicKey, bobText)
s.Require().NoError(err)
// Alice receives the message
@ -483,14 +595,14 @@ func (s *EncryptionServiceTestSuite) TestMaxMessageKeysPerSession() {
nMessages := s.alice.encryptor.config.MaxMessageKeysPerSession
messages := make([]*ProtocolMessage, nMessages)
for i := 0; i < nMessages; i++ {
m, err := s.bob.BuildDirectMessage(bobKey, &aliceKey.PublicKey, bobText)
m, err := s.bob.BuildEncryptedMessage(bobKey, &aliceKey.PublicKey, bobText)
s.Require().NoError(err)
messages[i] = m.Message
}
// Another message to trigger the deletion
m, err := s.bob.BuildDirectMessage(bobKey, &aliceKey.PublicKey, bobText)
m, err := s.bob.BuildEncryptedMessage(bobKey, &aliceKey.PublicKey, bobText)
s.Require().NoError(err)
_, err = s.alice.HandleMessage(aliceKey, &bobKey.PublicKey, m.Message, defaultMessageID)
s.Require().NoError(err)
@ -538,7 +650,7 @@ func (s *EncryptionServiceTestSuite) TestMaxKeep() {
// We decrypt all messages but 1 & 2
messages := make([]*ProtocolMessage, s.alice.encryptor.config.MaxKeep)
for i := 0; i < s.alice.encryptor.config.MaxKeep; i++ {
m, err := s.bob.BuildDirectMessage(bobKey, &aliceKey.PublicKey, bobText)
m, err := s.bob.BuildEncryptedMessage(bobKey, &aliceKey.PublicKey, bobText)
messages[i] = m.Message
s.Require().NoError(err)
@ -597,11 +709,11 @@ func (s *EncryptionServiceTestSuite) TestConcurrentBundles() {
s.Require().NoError(err)
// Alice sends a message
aliceMessage1, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, aliceText1)
aliceMessage1, err := s.alice.BuildEncryptedMessage(aliceKey, &bobKey.PublicKey, aliceText1)
s.Require().NoError(err)
// Bob sends a message
bobMessage1, err := s.bob.BuildDirectMessage(bobKey, &aliceKey.PublicKey, bobText1)
bobMessage1, err := s.bob.BuildEncryptedMessage(bobKey, &aliceKey.PublicKey, bobText1)
s.Require().NoError(err)
// Bob receives the message
@ -613,11 +725,11 @@ func (s *EncryptionServiceTestSuite) TestConcurrentBundles() {
s.Require().NoError(err)
// Bob replies to the message
bobMessage2, err := s.bob.BuildDirectMessage(bobKey, &aliceKey.PublicKey, bobText2)
bobMessage2, err := s.bob.BuildEncryptedMessage(bobKey, &aliceKey.PublicKey, bobText2)
s.Require().NoError(err)
// Alice sends a message
aliceMessage2, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, aliceText2)
aliceMessage2, err := s.alice.BuildEncryptedMessage(aliceKey, &bobKey.PublicKey, aliceText2)
s.Require().NoError(err)
// Alice receives the message
@ -647,7 +759,7 @@ func publish(
go func() {
defer wg.Done()
time.Sleep(time.Duration(rand.Intn(50)) * time.Millisecond) // nolint: gosec
response, err := e.BuildDirectMessage(privateKey, publicKey, cleartext)
response, err := e.BuildEncryptedMessage(privateKey, publicKey, cleartext)
if err != nil {
errChan <- err
return
@ -775,7 +887,7 @@ func (s *EncryptionServiceTestSuite) TestBundleNotExisting() {
s.Require().NoError(err)
// Alice sends a message
aliceMessage, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, aliceText)
aliceMessage, err := s.alice.BuildEncryptedMessage(aliceKey, &bobKey.PublicKey, aliceText)
s.Require().NoError(err)
// Bob receives the message, and returns a bundlenotfound error
@ -808,7 +920,7 @@ func (s *EncryptionServiceTestSuite) TestDeviceNotIncluded() {
s.Require().NoError(err)
// Alice sends a message
aliceMessage, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, []byte("does not matter"))
aliceMessage, err := s.alice.BuildEncryptedMessage(aliceKey, &bobKey.PublicKey, []byte("does not matter"))
s.Require().NoError(err)
// Bob receives the message, and returns a bundlenotfound error
@ -849,9 +961,9 @@ func (s *EncryptionServiceTestSuite) TestRefreshedBundle() {
s.Require().NoError(err)
// Alice sends a message
response1, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, []byte("anything"))
response1, err := s.alice.BuildEncryptedMessage(aliceKey, &bobKey.PublicKey, []byte("anything"))
s.Require().NoError(err)
encryptionResponse1 := response1.Message.GetDirectMessage()
encryptionResponse1 := response1.Message.GetEncryptedMessage()
installationResponse1 := encryptionResponse1[bobInstallationID]
s.Require().NotNil(installationResponse1)
@ -871,9 +983,9 @@ func (s *EncryptionServiceTestSuite) TestRefreshedBundle() {
s.Require().NoError(err)
// Alice sends a message
response2, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, []byte("anything"))
response2, err := s.alice.BuildEncryptedMessage(aliceKey, &bobKey.PublicKey, []byte("anything"))
s.Require().NoError(err)
encryptionResponse2 := response2.Message.GetDirectMessage()
encryptionResponse2 := response2.Message.GetEncryptedMessage()
installationResponse2 := encryptionResponse2[bobInstallationID]
s.Require().NotNil(installationResponse2)
@ -915,7 +1027,7 @@ func (s *EncryptionServiceTestSuite) TestMessageConfirmation() {
s.Require().NoError(err)
// Bob sends a message
bobMessage1, err := s.bob.BuildDirectMessage(bobKey, &aliceKey.PublicKey, bobText1)
bobMessage1, err := s.bob.BuildEncryptedMessage(bobKey, &aliceKey.PublicKey, bobText1)
s.Require().NoError(err)
bobMessage1ID := []byte("bob-message-1-id")
@ -936,12 +1048,12 @@ func (s *EncryptionServiceTestSuite) TestMessageConfirmation() {
s.Require().Equal(errors.New("can't skip current chain message keys: bad until: probably an out-of-order message that was deleted"), err)
// Bob sends a message
bobMessage2, err := s.bob.BuildDirectMessage(bobKey, &aliceKey.PublicKey, bobText1)
bobMessage2, err := s.bob.BuildEncryptedMessage(bobKey, &aliceKey.PublicKey, bobText1)
s.Require().NoError(err)
bobMessage2ID := []byte("bob-message-2-id")
// Bob sends a message
bobMessage3, err := s.bob.BuildDirectMessage(bobKey, &aliceKey.PublicKey, bobText1)
bobMessage3, err := s.bob.BuildEncryptedMessage(bobKey, &aliceKey.PublicKey, bobText1)
s.Require().NoError(err)
bobMessage3ID := []byte("bob-message-3-id")

View File

@ -25,11 +25,15 @@ var (
// but from a device that has not been paired.
// This should not happen because the protocol forbids sending a message to
// non-paired devices, however, in theory it is possible to receive such a message.
ErrNotPairedDevice = errors.New("received a message from not paired device")
ErrNotPairedDevice = errors.New("received a message from not paired device")
ErrHashRatchetSeqNoTooHigh = errors.New("Hash ratchet seq no is too high")
)
// If we have no bundles, we use a constant so that the message can reach any device.
const noInstallationID = "none"
const (
noInstallationID = "none"
maxHashRatchetSeqNoDelta = 1000
)
type confirmationData struct {
header *dr.MessageHeader
@ -233,8 +237,8 @@ func (s *encryptor) ProcessPublicBundle(myIdentityKey *ecdsa.PrivateKey, b *Bund
return s.persistence.AddPublicBundle(b)
}
// DecryptPayload decrypts the payload of a DirectMessageProtocol, given an identity private key and the sender's public key
func (s *encryptor) DecryptPayload(myIdentityKey *ecdsa.PrivateKey, theirIdentityKey *ecdsa.PublicKey, theirInstallationID string, msgs map[string]*DirectMessageProtocol, messageID []byte) ([]byte, error) {
// DecryptPayload decrypts the payload of a EncryptedMessageProtocol, given an identity private key and the sender's public key
func (s *encryptor) DecryptPayload(myIdentityKey *ecdsa.PrivateKey, theirIdentityKey *ecdsa.PublicKey, theirInstallationID string, msgs map[string]*EncryptedMessageProtocol, messageID []byte) ([]byte, error) {
s.mutex.Lock()
defer s.mutex.Unlock()
@ -324,6 +328,10 @@ func (s *encryptor) DecryptPayload(myIdentityKey *ecdsa.PrivateKey, theirIdentit
return s.DecryptWithDH(myIdentityKey, decompressedKey, payload)
}
// Try Hash Ratchet
if header := msg.GetHRHeader(); header != nil {
return s.decryptWithHR([]byte(header.GroupId), header.KeyId, header.SeqNo, payload)
}
return nil, errors.New("no key specified")
}
@ -428,7 +436,7 @@ func (s *encryptor) decryptUsingDR(theirIdentityKey *ecdsa.PublicKey, drInfo *Ra
return plaintext, nil
}
func (s *encryptor) encryptWithDH(theirIdentityKey *ecdsa.PublicKey, payload []byte) (*DirectMessageProtocol, error) {
func (s *encryptor) encryptWithDH(theirIdentityKey *ecdsa.PublicKey, payload []byte) (*EncryptedMessageProtocol, error) {
symmetricKey, ourEphemeralKey, err := PerformActiveDH(theirIdentityKey)
if err != nil {
return nil, err
@ -439,7 +447,7 @@ func (s *encryptor) encryptWithDH(theirIdentityKey *ecdsa.PublicKey, payload []b
return nil, err
}
return &DirectMessageProtocol{
return &EncryptedMessageProtocol{
DHHeader: &DHHeader{
Key: crypto.CompressPubkey(ourEphemeralKey),
},
@ -447,8 +455,8 @@ func (s *encryptor) encryptWithDH(theirIdentityKey *ecdsa.PublicKey, payload []b
}, nil
}
func (s *encryptor) EncryptPayloadWithDH(theirIdentityKey *ecdsa.PublicKey, payload []byte) (map[string]*DirectMessageProtocol, error) {
response := make(map[string]*DirectMessageProtocol)
func (s *encryptor) EncryptPayloadWithDH(theirIdentityKey *ecdsa.PublicKey, payload []byte) (map[string]*EncryptedMessageProtocol, error) {
response := make(map[string]*EncryptedMessageProtocol)
dmp, err := s.encryptWithDH(theirIdentityKey, payload)
if err != nil {
return nil, err
@ -463,8 +471,8 @@ func (s *encryptor) GetPublicBundle(theirIdentityKey *ecdsa.PublicKey, installat
return s.persistence.GetPublicBundle(theirIdentityKey, installations)
}
// EncryptPayload returns a new DirectMessageProtocol with a given payload encrypted, given a recipient's public key and the sender private identity key
func (s *encryptor) EncryptPayload(theirIdentityKey *ecdsa.PublicKey, myIdentityKey *ecdsa.PrivateKey, installations []*multidevice.Installation, payload []byte) (map[string]*DirectMessageProtocol, []*multidevice.Installation, error) {
// EncryptPayload returns a new EncryptedMessageProtocol with a given payload encrypted, given a recipient's public key and the sender private identity key
func (s *encryptor) EncryptPayload(theirIdentityKey *ecdsa.PublicKey, myIdentityKey *ecdsa.PrivateKey, installations []*multidevice.Installation, payload []byte) (map[string]*EncryptedMessageProtocol, []*multidevice.Installation, error) {
logger := s.logger.With(
zap.String("site", "EncryptPayload"),
zap.String("their-identity-key", types.EncodeHex(crypto.FromECDSAPub(theirIdentityKey))))
@ -475,15 +483,15 @@ func (s *encryptor) EncryptPayload(theirIdentityKey *ecdsa.PublicKey, myIdentity
s.mutex.Lock()
defer s.mutex.Unlock()
// We don't have any, send a message with DH
if len(installations) == 0 {
// We don't have any, send a message with DH
logger.Debug("no installations, sending to all devices")
encryptedPayload, err := s.EncryptPayloadWithDH(theirIdentityKey, payload)
return encryptedPayload, targetedInstallations, err
}
theirIdentityKeyC := crypto.CompressPubkey(theirIdentityKey)
response := make(map[string]*DirectMessageProtocol)
response := make(map[string]*EncryptedMessageProtocol)
for _, installation := range installations {
installationID := installation.ID
@ -513,7 +521,7 @@ func (s *encryptor) EncryptPayload(theirIdentityKey *ecdsa.PublicKey, myIdentity
return nil, nil, err
}
dmp := DirectMessageProtocol{
dmp := EncryptedMessageProtocol{
Payload: encryptedPayload,
DRHeader: drHeader,
}
@ -570,7 +578,7 @@ func (s *encryptor) EncryptPayload(theirIdentityKey *ecdsa.PublicKey, myIdentity
return nil, nil, err
}
dmp := &DirectMessageProtocol{
dmp := &EncryptedMessageProtocol{
Payload: encryptedPayload,
X3DHHeader: x3dhHeader,
DRHeader: drHeader,
@ -592,6 +600,131 @@ func (s *encryptor) EncryptPayload(theirIdentityKey *ecdsa.PublicKey, myIdentity
return response, targetedInstallations, nil
}
func (s *encryptor) getNextHashRatchetKeyID(groupID string) (uint32, error) {
latestKeyID, err := s.persistence.GetCurrentKeyForGroup(groupID)
if err != nil {
return 0, err
}
currentTime := (uint32)(time.Now().UnixNano() / int64(time.Millisecond))
keyIDBump := (uint32)(10)
if latestKeyID < currentTime {
return currentTime + keyIDBump, nil
}
return latestKeyID + 1, nil
}
// Generates and stores a hash ratchet key given a group ID
func (s *encryptor) GenerateHashRatchetKey(groupID []byte) (uint32, error) {
// Randomly generate a hash ratchet key
hrKey, err := crypto.GenerateKey()
if err != nil {
return 0, err
}
hrKeyBytes := crypto.FromECDSA(hrKey)
keyID, err := s.getNextHashRatchetKeyID(string(groupID))
if err != nil {
return 0, err
}
err = s.persistence.SaveHashRatchetKey(string(groupID), keyID, hrKeyBytes)
return keyID, err
}
// EncryptHashRatchetPayload returns a new EncryptedMessageProtocol with a given payload encrypted, given a group's key
func (s *encryptor) EncryptHashRatchetPayload(groupID []byte, keyID uint32, payload []byte) (map[string]*EncryptedMessageProtocol, error) {
logger := s.logger.With(
zap.String("site", "EncryptHashRatchetPayload"),
zap.Any("group-id", groupID),
zap.Any("key-id", keyID))
s.mutex.Lock()
defer s.mutex.Unlock()
logger.Debug("encrypting hash ratchet message")
dmp, err := s.encryptWithHR(groupID, keyID, payload)
response := make(map[string]*EncryptedMessageProtocol)
response[noInstallationID] = dmp
return response, err
}
func samePublicKeys(pubKey1, pubKey2 ecdsa.PublicKey) bool {
return pubKey1.X.Cmp(pubKey2.X) == 0 && pubKey1.Y.Cmp(pubKey2.Y) == 0
}
func (s *encryptor) encryptWithHR(groupID []byte, keyID uint32, payload []byte) (*EncryptedMessageProtocol, error) {
hrCache, err := s.persistence.GetHashRatchetKeyByID(groupID, keyID, 0) // Get latest seqNo
if err != nil {
return nil, err
}
var dbHash []byte
if len(hrCache.Hash) == 0 {
dbHash = hrCache.Key
} else {
dbHash = hrCache.Hash
}
hash := crypto.Keccak256Hash(dbHash)
encryptedPayload, err := crypto.EncryptSymmetric(hash.Bytes(), payload)
if err != nil {
return nil, err
}
newSeqNo := hrCache.SeqNo + 1
err = s.persistence.SaveHashRatchetKeyHash(groupID, keyID, hash.Bytes(), newSeqNo)
if err != nil {
return nil, err
}
dmp := &EncryptedMessageProtocol{
HRHeader: &HRHeader{
GroupId: string(groupID),
KeyId: keyID,
SeqNo: newSeqNo,
},
Payload: encryptedPayload,
}
return dmp, nil
}
func (s *encryptor) decryptWithHR(groupID []byte, keyID uint32, seqNo uint32, payload []byte) ([]byte, error) {
hrCache, err := s.persistence.GetHashRatchetKeyByID(groupID, keyID, seqNo)
if err != nil {
return nil, err
}
// Handle mesages with seqNo less than the one in db
// 1. Check cache. If present for a particular seqNo, all good
// 2. Otherwise, get the latest one for that keyId
// 3. Every time the key is generated, it has to be saved in the cache along with the hash
var hash []byte = hrCache.Hash
if hrCache.SeqNo == seqNo {
// We already have the hash for this seqNo
hash = hrCache.Hash
} else {
if hrCache.SeqNo == 0 {
// No cache records found for this keyId
hash = hrCache.Key
}
// We should not have "holes" in seq numbers,
// so a case when hrCache.SeqNo > seqNo shouldn't occur
if seqNo-hrCache.SeqNo > maxHashRatchetSeqNoDelta {
return nil, ErrHashRatchetSeqNoTooHigh
}
for i := hrCache.SeqNo; i < seqNo; i++ {
hash = crypto.Keccak256Hash(hash).Bytes()
err := s.persistence.SaveHashRatchetKeyHash(groupID, keyID, hash, i+1)
if err != nil {
return nil, err
}
}
}
decryptedPayload, err := crypto.DecryptSymmetric(hash, payload)
return decryptedPayload, err
}

View File

@ -1,540 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: protocol_message.proto
package encryption
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type SignedPreKey struct {
SignedPreKey []byte `protobuf:"bytes,1,opt,name=signed_pre_key,json=signedPreKey,proto3" json:"signed_pre_key,omitempty"`
Version uint32 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"`
ProtocolVersion uint32 `protobuf:"varint,3,opt,name=protocol_version,json=protocolVersion,proto3" json:"protocol_version,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *SignedPreKey) Reset() { *m = SignedPreKey{} }
func (m *SignedPreKey) String() string { return proto.CompactTextString(m) }
func (*SignedPreKey) ProtoMessage() {}
func (*SignedPreKey) Descriptor() ([]byte, []int) {
return fileDescriptor_4e37b52004a72e16, []int{0}
}
func (m *SignedPreKey) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SignedPreKey.Unmarshal(m, b)
}
func (m *SignedPreKey) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_SignedPreKey.Marshal(b, m, deterministic)
}
func (m *SignedPreKey) XXX_Merge(src proto.Message) {
xxx_messageInfo_SignedPreKey.Merge(m, src)
}
func (m *SignedPreKey) XXX_Size() int {
return xxx_messageInfo_SignedPreKey.Size(m)
}
func (m *SignedPreKey) XXX_DiscardUnknown() {
xxx_messageInfo_SignedPreKey.DiscardUnknown(m)
}
var xxx_messageInfo_SignedPreKey proto.InternalMessageInfo
func (m *SignedPreKey) GetSignedPreKey() []byte {
if m != nil {
return m.SignedPreKey
}
return nil
}
func (m *SignedPreKey) GetVersion() uint32 {
if m != nil {
return m.Version
}
return 0
}
func (m *SignedPreKey) GetProtocolVersion() uint32 {
if m != nil {
return m.ProtocolVersion
}
return 0
}
// X3DH prekey bundle
type Bundle struct {
// Identity key
Identity []byte `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"`
// Installation id
SignedPreKeys map[string]*SignedPreKey `protobuf:"bytes,2,rep,name=signed_pre_keys,json=signedPreKeys,proto3" json:"signed_pre_keys,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
// Prekey signature
Signature []byte `protobuf:"bytes,4,opt,name=signature,proto3" json:"signature,omitempty"`
// When the bundle was created locally
Timestamp int64 `protobuf:"varint,5,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Bundle) Reset() { *m = Bundle{} }
func (m *Bundle) String() string { return proto.CompactTextString(m) }
func (*Bundle) ProtoMessage() {}
func (*Bundle) Descriptor() ([]byte, []int) {
return fileDescriptor_4e37b52004a72e16, []int{1}
}
func (m *Bundle) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Bundle.Unmarshal(m, b)
}
func (m *Bundle) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Bundle.Marshal(b, m, deterministic)
}
func (m *Bundle) XXX_Merge(src proto.Message) {
xxx_messageInfo_Bundle.Merge(m, src)
}
func (m *Bundle) XXX_Size() int {
return xxx_messageInfo_Bundle.Size(m)
}
func (m *Bundle) XXX_DiscardUnknown() {
xxx_messageInfo_Bundle.DiscardUnknown(m)
}
var xxx_messageInfo_Bundle proto.InternalMessageInfo
func (m *Bundle) GetIdentity() []byte {
if m != nil {
return m.Identity
}
return nil
}
func (m *Bundle) GetSignedPreKeys() map[string]*SignedPreKey {
if m != nil {
return m.SignedPreKeys
}
return nil
}
func (m *Bundle) GetSignature() []byte {
if m != nil {
return m.Signature
}
return nil
}
func (m *Bundle) GetTimestamp() int64 {
if m != nil {
return m.Timestamp
}
return 0
}
type BundleContainer struct {
// X3DH prekey bundle
Bundle *Bundle `protobuf:"bytes,1,opt,name=bundle,proto3" json:"bundle,omitempty"`
// Private signed prekey
PrivateSignedPreKey []byte `protobuf:"bytes,2,opt,name=private_signed_pre_key,json=privateSignedPreKey,proto3" json:"private_signed_pre_key,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *BundleContainer) Reset() { *m = BundleContainer{} }
func (m *BundleContainer) String() string { return proto.CompactTextString(m) }
func (*BundleContainer) ProtoMessage() {}
func (*BundleContainer) Descriptor() ([]byte, []int) {
return fileDescriptor_4e37b52004a72e16, []int{2}
}
func (m *BundleContainer) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_BundleContainer.Unmarshal(m, b)
}
func (m *BundleContainer) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_BundleContainer.Marshal(b, m, deterministic)
}
func (m *BundleContainer) XXX_Merge(src proto.Message) {
xxx_messageInfo_BundleContainer.Merge(m, src)
}
func (m *BundleContainer) XXX_Size() int {
return xxx_messageInfo_BundleContainer.Size(m)
}
func (m *BundleContainer) XXX_DiscardUnknown() {
xxx_messageInfo_BundleContainer.DiscardUnknown(m)
}
var xxx_messageInfo_BundleContainer proto.InternalMessageInfo
func (m *BundleContainer) GetBundle() *Bundle {
if m != nil {
return m.Bundle
}
return nil
}
func (m *BundleContainer) GetPrivateSignedPreKey() []byte {
if m != nil {
return m.PrivateSignedPreKey
}
return nil
}
type DRHeader struct {
// Current ratchet public key
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
// Number of the message in the sending chain
N uint32 `protobuf:"varint,2,opt,name=n,proto3" json:"n,omitempty"`
// Length of the previous sending chain
Pn uint32 `protobuf:"varint,3,opt,name=pn,proto3" json:"pn,omitempty"`
// Bundle ID
Id []byte `protobuf:"bytes,4,opt,name=id,proto3" json:"id,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *DRHeader) Reset() { *m = DRHeader{} }
func (m *DRHeader) String() string { return proto.CompactTextString(m) }
func (*DRHeader) ProtoMessage() {}
func (*DRHeader) Descriptor() ([]byte, []int) {
return fileDescriptor_4e37b52004a72e16, []int{3}
}
func (m *DRHeader) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DRHeader.Unmarshal(m, b)
}
func (m *DRHeader) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_DRHeader.Marshal(b, m, deterministic)
}
func (m *DRHeader) XXX_Merge(src proto.Message) {
xxx_messageInfo_DRHeader.Merge(m, src)
}
func (m *DRHeader) XXX_Size() int {
return xxx_messageInfo_DRHeader.Size(m)
}
func (m *DRHeader) XXX_DiscardUnknown() {
xxx_messageInfo_DRHeader.DiscardUnknown(m)
}
var xxx_messageInfo_DRHeader proto.InternalMessageInfo
func (m *DRHeader) GetKey() []byte {
if m != nil {
return m.Key
}
return nil
}
func (m *DRHeader) GetN() uint32 {
if m != nil {
return m.N
}
return 0
}
func (m *DRHeader) GetPn() uint32 {
if m != nil {
return m.Pn
}
return 0
}
func (m *DRHeader) GetId() []byte {
if m != nil {
return m.Id
}
return nil
}
type DHHeader struct {
// Compressed ephemeral public key
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *DHHeader) Reset() { *m = DHHeader{} }
func (m *DHHeader) String() string { return proto.CompactTextString(m) }
func (*DHHeader) ProtoMessage() {}
func (*DHHeader) Descriptor() ([]byte, []int) {
return fileDescriptor_4e37b52004a72e16, []int{4}
}
func (m *DHHeader) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DHHeader.Unmarshal(m, b)
}
func (m *DHHeader) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_DHHeader.Marshal(b, m, deterministic)
}
func (m *DHHeader) XXX_Merge(src proto.Message) {
xxx_messageInfo_DHHeader.Merge(m, src)
}
func (m *DHHeader) XXX_Size() int {
return xxx_messageInfo_DHHeader.Size(m)
}
func (m *DHHeader) XXX_DiscardUnknown() {
xxx_messageInfo_DHHeader.DiscardUnknown(m)
}
var xxx_messageInfo_DHHeader proto.InternalMessageInfo
func (m *DHHeader) GetKey() []byte {
if m != nil {
return m.Key
}
return nil
}
type X3DHHeader struct {
// Ephemeral key used
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
// Used bundle's signed prekey
Id []byte `protobuf:"bytes,4,opt,name=id,proto3" json:"id,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *X3DHHeader) Reset() { *m = X3DHHeader{} }
func (m *X3DHHeader) String() string { return proto.CompactTextString(m) }
func (*X3DHHeader) ProtoMessage() {}
func (*X3DHHeader) Descriptor() ([]byte, []int) {
return fileDescriptor_4e37b52004a72e16, []int{5}
}
func (m *X3DHHeader) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_X3DHHeader.Unmarshal(m, b)
}
func (m *X3DHHeader) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_X3DHHeader.Marshal(b, m, deterministic)
}
func (m *X3DHHeader) XXX_Merge(src proto.Message) {
xxx_messageInfo_X3DHHeader.Merge(m, src)
}
func (m *X3DHHeader) XXX_Size() int {
return xxx_messageInfo_X3DHHeader.Size(m)
}
func (m *X3DHHeader) XXX_DiscardUnknown() {
xxx_messageInfo_X3DHHeader.DiscardUnknown(m)
}
var xxx_messageInfo_X3DHHeader proto.InternalMessageInfo
func (m *X3DHHeader) GetKey() []byte {
if m != nil {
return m.Key
}
return nil
}
func (m *X3DHHeader) GetId() []byte {
if m != nil {
return m.Id
}
return nil
}
// Direct message value
type DirectMessageProtocol struct {
X3DHHeader *X3DHHeader `protobuf:"bytes,1,opt,name=X3DH_header,json=X3DHHeader,proto3" json:"X3DH_header,omitempty"`
DRHeader *DRHeader `protobuf:"bytes,2,opt,name=DR_header,json=DRHeader,proto3" json:"DR_header,omitempty"`
DHHeader *DHHeader `protobuf:"bytes,101,opt,name=DH_header,json=DHHeader,proto3" json:"DH_header,omitempty"`
// Encrypted payload
Payload []byte `protobuf:"bytes,3,opt,name=payload,proto3" json:"payload,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *DirectMessageProtocol) Reset() { *m = DirectMessageProtocol{} }
func (m *DirectMessageProtocol) String() string { return proto.CompactTextString(m) }
func (*DirectMessageProtocol) ProtoMessage() {}
func (*DirectMessageProtocol) Descriptor() ([]byte, []int) {
return fileDescriptor_4e37b52004a72e16, []int{6}
}
func (m *DirectMessageProtocol) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DirectMessageProtocol.Unmarshal(m, b)
}
func (m *DirectMessageProtocol) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_DirectMessageProtocol.Marshal(b, m, deterministic)
}
func (m *DirectMessageProtocol) XXX_Merge(src proto.Message) {
xxx_messageInfo_DirectMessageProtocol.Merge(m, src)
}
func (m *DirectMessageProtocol) XXX_Size() int {
return xxx_messageInfo_DirectMessageProtocol.Size(m)
}
func (m *DirectMessageProtocol) XXX_DiscardUnknown() {
xxx_messageInfo_DirectMessageProtocol.DiscardUnknown(m)
}
var xxx_messageInfo_DirectMessageProtocol proto.InternalMessageInfo
func (m *DirectMessageProtocol) GetX3DHHeader() *X3DHHeader {
if m != nil {
return m.X3DHHeader
}
return nil
}
func (m *DirectMessageProtocol) GetDRHeader() *DRHeader {
if m != nil {
return m.DRHeader
}
return nil
}
func (m *DirectMessageProtocol) GetDHHeader() *DHHeader {
if m != nil {
return m.DHHeader
}
return nil
}
func (m *DirectMessageProtocol) GetPayload() []byte {
if m != nil {
return m.Payload
}
return nil
}
// Top-level protocol message
type ProtocolMessage struct {
// The device id of the sender
InstallationId string `protobuf:"bytes,2,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"`
// List of bundles
Bundles []*Bundle `protobuf:"bytes,3,rep,name=bundles,proto3" json:"bundles,omitempty"`
// One to one message, encrypted, indexed by installation_id
DirectMessage map[string]*DirectMessageProtocol `protobuf:"bytes,101,rep,name=direct_message,json=directMessage,proto3" json:"direct_message,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
// Public chats, not encrypted
PublicMessage []byte `protobuf:"bytes,102,opt,name=public_message,json=publicMessage,proto3" json:"public_message,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ProtocolMessage) Reset() { *m = ProtocolMessage{} }
func (m *ProtocolMessage) String() string { return proto.CompactTextString(m) }
func (*ProtocolMessage) ProtoMessage() {}
func (*ProtocolMessage) Descriptor() ([]byte, []int) {
return fileDescriptor_4e37b52004a72e16, []int{7}
}
func (m *ProtocolMessage) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ProtocolMessage.Unmarshal(m, b)
}
func (m *ProtocolMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ProtocolMessage.Marshal(b, m, deterministic)
}
func (m *ProtocolMessage) XXX_Merge(src proto.Message) {
xxx_messageInfo_ProtocolMessage.Merge(m, src)
}
func (m *ProtocolMessage) XXX_Size() int {
return xxx_messageInfo_ProtocolMessage.Size(m)
}
func (m *ProtocolMessage) XXX_DiscardUnknown() {
xxx_messageInfo_ProtocolMessage.DiscardUnknown(m)
}
var xxx_messageInfo_ProtocolMessage proto.InternalMessageInfo
func (m *ProtocolMessage) GetInstallationId() string {
if m != nil {
return m.InstallationId
}
return ""
}
func (m *ProtocolMessage) GetBundles() []*Bundle {
if m != nil {
return m.Bundles
}
return nil
}
func (m *ProtocolMessage) GetDirectMessage() map[string]*DirectMessageProtocol {
if m != nil {
return m.DirectMessage
}
return nil
}
func (m *ProtocolMessage) GetPublicMessage() []byte {
if m != nil {
return m.PublicMessage
}
return nil
}
func init() {
proto.RegisterType((*SignedPreKey)(nil), "encryption.SignedPreKey")
proto.RegisterType((*Bundle)(nil), "encryption.Bundle")
proto.RegisterMapType((map[string]*SignedPreKey)(nil), "encryption.Bundle.SignedPreKeysEntry")
proto.RegisterType((*BundleContainer)(nil), "encryption.BundleContainer")
proto.RegisterType((*DRHeader)(nil), "encryption.DRHeader")
proto.RegisterType((*DHHeader)(nil), "encryption.DHHeader")
proto.RegisterType((*X3DHHeader)(nil), "encryption.X3DHHeader")
proto.RegisterType((*DirectMessageProtocol)(nil), "encryption.DirectMessageProtocol")
proto.RegisterType((*ProtocolMessage)(nil), "encryption.ProtocolMessage")
proto.RegisterMapType((map[string]*DirectMessageProtocol)(nil), "encryption.ProtocolMessage.DirectMessageEntry")
}
func init() {
proto.RegisterFile("protocol_message.proto", fileDescriptor_4e37b52004a72e16)
}
var fileDescriptor_4e37b52004a72e16 = []byte{
// 581 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x54, 0xdd, 0x6a, 0xdb, 0x4c,
0x10, 0x45, 0x52, 0xe2, 0x9f, 0xb1, 0xfc, 0xc3, 0x7e, 0x5f, 0x83, 0x30, 0xa1, 0xb8, 0xa2, 0xa1,
0x6e, 0x29, 0x0a, 0xb5, 0x0b, 0x29, 0xbd, 0x4c, 0x5d, 0x70, 0x53, 0x02, 0x61, 0x4b, 0x4b, 0xc9,
0x8d, 0x90, 0xa5, 0xad, 0xb3, 0x54, 0x5e, 0x09, 0xed, 0xda, 0x54, 0xcf, 0xd0, 0x47, 0xeb, 0x4d,
0x1f, 0xa9, 0x68, 0xa5, 0xb5, 0xd6, 0x3f, 0xb9, 0xd3, 0x9e, 0x9d, 0x39, 0x33, 0xe7, 0xcc, 0x8e,
0xe0, 0x2c, 0xcd, 0x12, 0x91, 0x84, 0x49, 0xec, 0xaf, 0x08, 0xe7, 0xc1, 0x92, 0x78, 0x12, 0x40,
0x40, 0x58, 0x98, 0xe5, 0xa9, 0xa0, 0x09, 0x73, 0x73, 0xb0, 0xbf, 0xd0, 0x25, 0x23, 0xd1, 0x5d,
0x46, 0x3e, 0x93, 0x1c, 0x3d, 0x87, 0x1e, 0x97, 0x67, 0x3f, 0xcd, 0x88, 0xff, 0x93, 0xe4, 0x8e,
0x31, 0x32, 0xc6, 0x36, 0xb6, 0xb9, 0x1e, 0xe5, 0x40, 0x73, 0x43, 0x32, 0x4e, 0x13, 0xe6, 0x98,
0x23, 0x63, 0xdc, 0xc5, 0xea, 0x88, 0x5e, 0xc2, 0x60, 0x5b, 0x55, 0x85, 0x58, 0x32, 0xa4, 0xaf,
0xf0, 0x6f, 0x25, 0xec, 0xfe, 0x36, 0xa1, 0x71, 0xbd, 0x66, 0x51, 0x4c, 0xd0, 0x10, 0x5a, 0x34,
0x22, 0x4c, 0x50, 0xa1, 0xea, 0x6d, 0xcf, 0xe8, 0x16, 0xfa, 0xbb, 0x1d, 0x71, 0xc7, 0x1c, 0x59,
0xe3, 0xce, 0xe4, 0xc2, 0xab, 0x75, 0x78, 0x25, 0x91, 0xa7, 0x6b, 0xe1, 0x1f, 0x99, 0xc8, 0x72,
0xdc, 0xd5, 0x3b, 0xe7, 0xe8, 0x1c, 0xda, 0x05, 0x10, 0x88, 0x75, 0x46, 0x9c, 0x13, 0x59, 0xab,
0x06, 0x8a, 0x5b, 0x41, 0x57, 0x84, 0x8b, 0x60, 0x95, 0x3a, 0xa7, 0x23, 0x63, 0x6c, 0xe1, 0x1a,
0x18, 0xde, 0x03, 0x3a, 0x2c, 0x80, 0x06, 0x60, 0x29, 0x9f, 0xda, 0xb8, 0xf8, 0x44, 0x1e, 0x9c,
0x6e, 0x82, 0x78, 0x4d, 0xa4, 0x39, 0x9d, 0x89, 0xa3, 0x37, 0xaa, 0x13, 0xe0, 0x32, 0xec, 0xbd,
0xf9, 0xce, 0x70, 0x7f, 0x41, 0xbf, 0xd4, 0xf0, 0x21, 0x61, 0x22, 0xa0, 0x8c, 0x64, 0xe8, 0x15,
0x34, 0x16, 0x12, 0x92, 0xdc, 0x9d, 0x09, 0x3a, 0x14, 0x8c, 0xab, 0x08, 0x34, 0x2d, 0xa6, 0x4d,
0x37, 0x81, 0x20, 0xfe, 0xde, 0xfc, 0x4c, 0xa9, 0xf1, 0xbf, 0xea, 0x56, 0x2f, 0x7f, 0x73, 0xd2,
0xb2, 0x06, 0x27, 0xee, 0x0d, 0xb4, 0x66, 0x78, 0x4e, 0x82, 0x88, 0x64, 0xba, 0x16, 0xbb, 0xd4,
0x62, 0x83, 0xa1, 0x86, 0x6c, 0x30, 0xd4, 0x03, 0x33, 0x55, 0x03, 0x35, 0x53, 0x79, 0xa6, 0x51,
0x65, 0xa3, 0x49, 0x23, 0xf7, 0x1c, 0x5a, 0xb3, 0xf9, 0x63, 0x5c, 0xee, 0x5b, 0x80, 0xef, 0xd3,
0xc7, 0xef, 0xf7, 0xd9, 0xaa, 0xfe, 0xfe, 0x1a, 0xf0, 0x64, 0x46, 0x33, 0x12, 0x8a, 0xdb, 0xf2,
0x19, 0xdf, 0x55, 0x0f, 0x09, 0x5d, 0x41, 0xa7, 0xe0, 0xf3, 0x1f, 0x24, 0x61, 0xe5, 0xd2, 0x99,
0xee, 0x52, 0x5d, 0x0e, 0xeb, 0xa5, 0xdf, 0x40, 0x7b, 0x86, 0x55, 0x5a, 0x39, 0xa4, 0xff, 0xf5,
0x34, 0xe5, 0x07, 0xae, 0x9d, 0x29, 0x52, 0xb6, 0x95, 0xc8, 0x91, 0x94, 0xf9, 0x36, 0x45, 0x55,
0x71, 0xa0, 0x99, 0x06, 0x79, 0x9c, 0x04, 0x91, 0x74, 0xcc, 0xc6, 0xea, 0xe8, 0xfe, 0x31, 0xa1,
0xaf, 0x54, 0x54, 0xa2, 0xd0, 0x0b, 0xe8, 0x53, 0xc6, 0x45, 0x10, 0xc7, 0x41, 0x41, 0xe8, 0xd3,
0x48, 0x76, 0xd6, 0xc6, 0x3d, 0x1d, 0xfe, 0x14, 0xa1, 0xd7, 0xd0, 0x2c, 0x87, 0xce, 0x1d, 0x4b,
0x2e, 0xc2, 0xb1, 0x77, 0xa1, 0x42, 0xd0, 0x57, 0xe8, 0x45, 0xd2, 0x3c, 0xf5, 0x13, 0x70, 0x88,
0x4c, 0xf2, 0xf4, 0xa4, 0xbd, 0x5e, 0xbc, 0x1d, 0xbb, 0xab, 0x35, 0x8a, 0x74, 0x0c, 0x5d, 0x40,
0x2f, 0x5d, 0x2f, 0x62, 0x1a, 0x6e, 0x69, 0x7f, 0x48, 0x89, 0xdd, 0x12, 0xad, 0xc2, 0x86, 0x21,
0xa0, 0x43, 0xae, 0x23, 0x1b, 0x73, 0xb5, 0xbb, 0x31, 0xcf, 0x76, 0x9c, 0x3d, 0x36, 0x7b, 0x6d,
0x75, 0xae, 0x47, 0xf7, 0x4f, 0x97, 0x54, 0x3c, 0xac, 0x17, 0x5e, 0x98, 0xac, 0x2e, 0xd5, 0x6f,
0xe6, 0xb2, 0xa6, 0x58, 0x34, 0x24, 0x38, 0xfd, 0x17, 0x00, 0x00, 0xff, 0xff, 0xe8, 0xd9, 0xaa,
0x89, 0x12, 0x05, 0x00, 0x00,
}

View File

@ -16,6 +16,8 @@
// 1559627659_add_contact_code.up.sql (198B)
// 1561368210_add_installation_metadata.down.sql (35B)
// 1561368210_add_installation_metadata.up.sql (267B)
// 1632236298_add_communities.down.sql (151B)
// 1632236298_add_communities.up.sql (584B)
// 1636536507_add_index_bundles.up.sql (347B)
// doc.go (377B)
@ -101,7 +103,7 @@ func _1536754952_initial_schemaDownSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1536754952_initial_schema.down.sql", size: 83, mode: os.FileMode(0644), modTime: time.Unix(1633220904, 0)}
info := bindataFileInfo{name: "1536754952_initial_schema.down.sql", size: 83, mode: os.FileMode(0644), modTime: time.Unix(1586880790, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x44, 0xcf, 0x76, 0x71, 0x1f, 0x5e, 0x9a, 0x43, 0xd8, 0xcd, 0xb8, 0xc3, 0x70, 0xc3, 0x7f, 0xfc, 0x90, 0xb4, 0x25, 0x1e, 0xf4, 0x66, 0x20, 0xb8, 0x33, 0x7e, 0xb0, 0x76, 0x1f, 0xc, 0xc0, 0x75}}
return a, nil
}
@ -121,7 +123,7 @@ func _1536754952_initial_schemaUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1536754952_initial_schema.up.sql", size: 962, mode: os.FileMode(0644), modTime: time.Unix(1633220904, 0)}
info := bindataFileInfo{name: "1536754952_initial_schema.up.sql", size: 962, mode: os.FileMode(0644), modTime: time.Unix(1586880790, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xea, 0x90, 0x5a, 0x59, 0x3e, 0x3, 0xe2, 0x3c, 0x81, 0x42, 0xcd, 0x4c, 0x9a, 0xe8, 0xda, 0x93, 0x2b, 0x70, 0xa4, 0xd5, 0x29, 0x3e, 0xd5, 0xc9, 0x27, 0xb6, 0xb7, 0x65, 0xff, 0x0, 0xcb, 0xde}}
return a, nil
}
@ -141,7 +143,7 @@ func _1539249977_update_ratchet_infoDownSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1539249977_update_ratchet_info.down.sql", size: 311, mode: os.FileMode(0644), modTime: time.Unix(1633220904, 0)}
info := bindataFileInfo{name: "1539249977_update_ratchet_info.down.sql", size: 311, mode: os.FileMode(0644), modTime: time.Unix(1586880790, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1, 0xa4, 0xeb, 0xa0, 0xe6, 0xa0, 0xd4, 0x48, 0xbb, 0xad, 0x6f, 0x7d, 0x67, 0x8c, 0xbd, 0x25, 0xde, 0x1f, 0x73, 0x9a, 0xbb, 0xa8, 0xc9, 0x30, 0xb7, 0xa9, 0x7c, 0xaf, 0xb5, 0x1, 0x61, 0xdd}}
return a, nil
}
@ -161,7 +163,7 @@ func _1539249977_update_ratchet_infoUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1539249977_update_ratchet_info.up.sql", size: 368, mode: os.FileMode(0644), modTime: time.Unix(1633220904, 0)}
info := bindataFileInfo{name: "1539249977_update_ratchet_info.up.sql", size: 368, mode: os.FileMode(0644), modTime: time.Unix(1586880790, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc, 0x8e, 0xbf, 0x6f, 0xa, 0xc0, 0xe1, 0x3c, 0x42, 0x28, 0x88, 0x1d, 0xdb, 0xba, 0x1c, 0x83, 0xec, 0xba, 0xd3, 0x5f, 0x5c, 0x77, 0x5e, 0xa7, 0x46, 0x36, 0xec, 0x69, 0xa, 0x4b, 0x17, 0x79}}
return a, nil
}
@ -181,7 +183,7 @@ func _1540715431_add_versionDownSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1540715431_add_version.down.sql", size: 127, mode: os.FileMode(0644), modTime: time.Unix(1633220904, 0)}
info := bindataFileInfo{name: "1540715431_add_version.down.sql", size: 127, mode: os.FileMode(0644), modTime: time.Unix(1586880790, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf5, 0x9, 0x4, 0xe3, 0x76, 0x2e, 0xb8, 0x9, 0x23, 0xf0, 0x70, 0x93, 0xc4, 0x50, 0xe, 0x9d, 0x84, 0x22, 0x8c, 0x94, 0xd3, 0x24, 0x9, 0x9a, 0xc1, 0xa1, 0x48, 0x45, 0xfd, 0x40, 0x6e, 0xe6}}
return a, nil
}
@ -201,7 +203,7 @@ func _1540715431_add_versionUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1540715431_add_version.up.sql", size: 265, mode: os.FileMode(0644), modTime: time.Unix(1633220904, 0)}
info := bindataFileInfo{name: "1540715431_add_version.up.sql", size: 265, mode: os.FileMode(0644), modTime: time.Unix(1586880790, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc7, 0x4c, 0x36, 0x96, 0xdf, 0x16, 0x10, 0xa6, 0x27, 0x1a, 0x79, 0x8b, 0x42, 0x83, 0x23, 0xc, 0x7e, 0xb6, 0x3d, 0x2, 0xda, 0xa4, 0xb4, 0xd, 0x27, 0x55, 0xba, 0xdc, 0xb2, 0x88, 0x8f, 0xa6}}
return a, nil
}
@ -221,7 +223,7 @@ func _1541164797_add_installationsDownSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1541164797_add_installations.down.sql", size: 26, mode: os.FileMode(0644), modTime: time.Unix(1633220904, 0)}
info := bindataFileInfo{name: "1541164797_add_installations.down.sql", size: 26, mode: os.FileMode(0644), modTime: time.Unix(1586880790, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf5, 0xfd, 0xe6, 0xd8, 0xca, 0x3b, 0x38, 0x18, 0xee, 0x0, 0x5f, 0x36, 0x9e, 0x1e, 0xd, 0x19, 0x3e, 0xb4, 0x73, 0x53, 0xe9, 0xa5, 0xac, 0xdd, 0xa1, 0x2f, 0xc7, 0x6c, 0xa8, 0xd9, 0xa, 0x88}}
return a, nil
}
@ -241,7 +243,7 @@ func _1541164797_add_installationsUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1541164797_add_installations.up.sql", size: 216, mode: os.FileMode(0644), modTime: time.Unix(1633220904, 0)}
info := bindataFileInfo{name: "1541164797_add_installations.up.sql", size: 216, mode: os.FileMode(0644), modTime: time.Unix(1586880790, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x2d, 0x18, 0x26, 0xb8, 0x88, 0x47, 0xdb, 0x83, 0xcc, 0xb6, 0x9d, 0x1c, 0x1, 0xae, 0x2f, 0xde, 0x97, 0x82, 0x3, 0x30, 0xa8, 0x63, 0xa1, 0x78, 0x4b, 0xa5, 0x9, 0x8, 0x75, 0xa2, 0x57, 0x81}}
return a, nil
}
@ -261,7 +263,7 @@ func _1558084410_add_secretDownSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1558084410_add_secret.down.sql", size: 56, mode: os.FileMode(0644), modTime: time.Unix(1633220904, 0)}
info := bindataFileInfo{name: "1558084410_add_secret.down.sql", size: 56, mode: os.FileMode(0644), modTime: time.Unix(1586880790, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x49, 0xb, 0x65, 0xdf, 0x59, 0xbf, 0xe9, 0x5, 0x5b, 0x6f, 0xd5, 0x3a, 0xb7, 0x57, 0xe8, 0x78, 0x38, 0x73, 0x53, 0x57, 0xf7, 0x24, 0x4, 0xe4, 0xa2, 0x49, 0x22, 0xa2, 0xc6, 0xfd, 0x80, 0xa4}}
return a, nil
}
@ -281,7 +283,7 @@ func _1558084410_add_secretUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1558084410_add_secret.up.sql", size: 301, mode: os.FileMode(0644), modTime: time.Unix(1633220904, 0)}
info := bindataFileInfo{name: "1558084410_add_secret.up.sql", size: 301, mode: os.FileMode(0644), modTime: time.Unix(1586880790, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf5, 0x32, 0x36, 0x8e, 0x47, 0xb0, 0x8f, 0xc1, 0xc6, 0xf7, 0xc6, 0x9f, 0x2d, 0x44, 0x75, 0x2b, 0x26, 0xec, 0x6, 0xa0, 0x7b, 0xa5, 0xbd, 0xc8, 0x76, 0x8a, 0x82, 0x68, 0x2, 0x42, 0xb5, 0xf4}}
return a, nil
}
@ -301,7 +303,7 @@ func _1558588866_add_versionDownSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1558588866_add_version.down.sql", size: 47, mode: os.FileMode(0644), modTime: time.Unix(1633220904, 0)}
info := bindataFileInfo{name: "1558588866_add_version.down.sql", size: 47, mode: os.FileMode(0644), modTime: time.Unix(1586880790, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xde, 0x52, 0x34, 0x3c, 0x46, 0x4a, 0xf0, 0x72, 0x47, 0x6f, 0x49, 0x5c, 0xc7, 0xf9, 0x32, 0xce, 0xc4, 0x3d, 0xfd, 0x61, 0xa1, 0x8b, 0x8f, 0xf2, 0x31, 0x34, 0xde, 0x15, 0x49, 0xa6, 0xde, 0xb9}}
return a, nil
}
@ -321,7 +323,7 @@ func _1558588866_add_versionUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1558588866_add_version.up.sql", size: 57, mode: os.FileMode(0644), modTime: time.Unix(1633220904, 0)}
info := bindataFileInfo{name: "1558588866_add_version.up.sql", size: 57, mode: os.FileMode(0644), modTime: time.Unix(1586880790, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x2a, 0xea, 0x64, 0x39, 0x61, 0x20, 0x83, 0x83, 0xb, 0x2e, 0x79, 0x64, 0xb, 0x53, 0xfa, 0xfe, 0xc6, 0xf7, 0x67, 0x42, 0xd3, 0x4f, 0xdc, 0x7e, 0x30, 0x32, 0xe8, 0x14, 0x41, 0xe9, 0xe7, 0x3b}}
return a, nil
}
@ -341,7 +343,7 @@ func _1559627659_add_contact_codeDownSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1559627659_add_contact_code.down.sql", size: 32, mode: os.FileMode(0644), modTime: time.Unix(1633220904, 0)}
info := bindataFileInfo{name: "1559627659_add_contact_code.down.sql", size: 32, mode: os.FileMode(0644), modTime: time.Unix(1586880790, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x5d, 0x64, 0x6d, 0xce, 0x24, 0x42, 0x20, 0x8d, 0x4f, 0x37, 0xaa, 0x9d, 0xc, 0x57, 0x98, 0xc1, 0xd1, 0x1a, 0x34, 0xcd, 0x9f, 0x8f, 0x34, 0x86, 0xb3, 0xd3, 0xdc, 0xf1, 0x7d, 0xe5, 0x1b, 0x6e}}
return a, nil
}
@ -361,7 +363,7 @@ func _1559627659_add_contact_codeUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1559627659_add_contact_code.up.sql", size: 198, mode: os.FileMode(0644), modTime: time.Unix(1633220904, 0)}
info := bindataFileInfo{name: "1559627659_add_contact_code.up.sql", size: 198, mode: os.FileMode(0644), modTime: time.Unix(1586880790, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x16, 0xf6, 0xc2, 0x62, 0x9c, 0xd2, 0xc9, 0x1e, 0xd8, 0xea, 0xaa, 0xea, 0x95, 0x8f, 0x89, 0x6a, 0x85, 0x5d, 0x9d, 0x99, 0x78, 0x3c, 0x90, 0x66, 0x99, 0x3e, 0x4b, 0x19, 0x62, 0xfb, 0x31, 0x4d}}
return a, nil
}
@ -381,7 +383,7 @@ func _1561368210_add_installation_metadataDownSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1561368210_add_installation_metadata.down.sql", size: 35, mode: os.FileMode(0644), modTime: time.Unix(1633220904, 0)}
info := bindataFileInfo{name: "1561368210_add_installation_metadata.down.sql", size: 35, mode: os.FileMode(0644), modTime: time.Unix(1586880790, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xa8, 0xde, 0x3f, 0xd2, 0x4a, 0x50, 0x98, 0x56, 0xe3, 0xc0, 0xcd, 0x9d, 0xb0, 0x34, 0x3b, 0xe5, 0x62, 0x18, 0xb5, 0x20, 0xc9, 0x3e, 0xdc, 0x6a, 0x40, 0x36, 0x66, 0xea, 0x51, 0x8c, 0x71, 0xf5}}
return a, nil
}
@ -401,11 +403,51 @@ func _1561368210_add_installation_metadataUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1561368210_add_installation_metadata.up.sql", size: 267, mode: os.FileMode(0644), modTime: time.Unix(1633220904, 0)}
info := bindataFileInfo{name: "1561368210_add_installation_metadata.up.sql", size: 267, mode: os.FileMode(0644), modTime: time.Unix(1586880790, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xb4, 0x71, 0x8f, 0x29, 0xb1, 0xaa, 0xd6, 0xd1, 0x8c, 0x17, 0xef, 0x6c, 0xd5, 0x80, 0xb8, 0x2c, 0xc3, 0xfe, 0xec, 0x24, 0x4d, 0xc8, 0x25, 0xd3, 0xb4, 0xcd, 0xa9, 0xac, 0x63, 0x61, 0xb2, 0x9c}}
return a, nil
}
var __1632236298_add_communitiesDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x09\xf2\x0f\x50\xf0\xf4\x73\x71\x8d\x50\xc8\x4c\xa9\x88\xcf\x48\x2c\xce\x88\x2f\x4a\x2c\x49\xce\x48\x2d\x89\x4f\xcd\x4b\xb6\xe6\x22\xa0\x20\x3e\x39\x31\x39\x23\xd5\x9a\x0b\xa2\x2e\xc4\xd1\xc9\xc7\x55\x01\x5d\x4d\x51\x65\x41\x49\x66\x7e\x1e\x4c\x29\x61\x95\xd6\x5c\x80\x00\x00\x00\xff\xff\xa4\x97\x4f\xad\x97\x00\x00\x00")
func _1632236298_add_communitiesDownSqlBytes() ([]byte, error) {
return bindataRead(
__1632236298_add_communitiesDownSql,
"1632236298_add_communities.down.sql",
)
}
func _1632236298_add_communitiesDownSql() (*asset, error) {
bytes, err := _1632236298_add_communitiesDownSqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "1632236298_add_communities.down.sql", size: 151, mode: os.FileMode(0644), modTime: time.Unix(1637942211, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x26, 0xe5, 0x47, 0xd1, 0xe5, 0xec, 0x5b, 0x3e, 0xdc, 0x22, 0xf4, 0x27, 0xee, 0x70, 0xf3, 0x9, 0x4f, 0xd2, 0x9f, 0x92, 0xf, 0x5a, 0x18, 0x11, 0xb7, 0x40, 0xab, 0xf1, 0x98, 0x72, 0xd6, 0x60}}
return a, nil
}
var __1632236298_add_communitiesUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x94\x90\xdf\x4a\xc3\x30\x14\xc6\xef\xf3\x14\xe7\x72\x83\xbe\x81\x57\xed\x3c\x1b\xc1\x7a\xa2\x31\x05\x77\x15\x4a\x16\x4c\x10\xd3\xd9\x46\xb0\x6f\x2f\xc1\x0c\x71\x71\xd2\xdd\x7e\x9c\xf3\xfd\xf9\x6d\x24\xd6\x0a\x41\xd5\x4d\x8b\xe0\xfa\xc9\xe9\xb1\x8f\xc6\xd9\xa8\x6d\x30\xe3\x7c\x8c\x7e\x08\xb0\x62\x00\x2f\xe3\xf0\x71\xd4\xfe\x00\x4d\x2b\x1a\x20\xa1\x80\xba\xb6\xad\x18\xc0\xab\x9d\x93\xce\x49\x9d\xcb\xe5\xed\x83\xe4\xf7\xb5\xdc\xc3\x1d\xee\x57\x27\xc7\x2a\x3b\xac\xd9\xfa\x86\xb1\x5c\xa8\x23\xfe\xd8\x21\x70\xba\xc5\x67\xf0\x87\x4f\x7d\xde\x0d\x04\x5d\xea\x5b\x3a\xff\xf8\xfe\x3b\x54\x9b\xde\x38\xbb\x6c\xae\x0f\xf1\x97\x3c\xd9\x77\x1d\x86\x44\x01\x77\x28\x93\x92\x42\xca\xf7\x69\x7e\x4b\x89\xfa\x84\x27\x69\x5b\x21\x91\xef\xe8\x6f\x2a\x20\x71\x8b\x12\x69\x83\x4f\xcb\x07\x5f\x85\x32\xcf\xbe\x0c\xf4\xfb\xa0\x48\xa9\xf2\xe8\x94\xf5\x15\x00\x00\xff\xff\x61\x30\xb4\xa0\x48\x02\x00\x00")
func _1632236298_add_communitiesUpSqlBytes() ([]byte, error) {
return bindataRead(
__1632236298_add_communitiesUpSql,
"1632236298_add_communities.up.sql",
)
}
func _1632236298_add_communitiesUpSql() (*asset, error) {
bytes, err := _1632236298_add_communitiesUpSqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "1632236298_add_communities.up.sql", size: 584, mode: os.FileMode(0644), modTime: time.Unix(1637942211, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x8f, 0xe0, 0x1, 0x6e, 0x84, 0xc, 0x35, 0xe4, 0x5a, 0xf, 0xbe, 0xcb, 0xf7, 0xd2, 0xa8, 0x25, 0xf5, 0xdb, 0x7, 0xcb, 0xa3, 0xe6, 0xf4, 0xc4, 0x1b, 0xa5, 0xec, 0x32, 0x1e, 0x1e, 0x48, 0x60}}
return a, nil
}
var __1636536507_add_index_bundlesUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x0e\x72\x75\x0c\x71\x55\xf0\xf4\x73\x71\x8d\x50\x48\xcd\x4b\x2e\xaa\x2c\x28\xc9\xcc\xcf\x8b\x4f\x2a\xcd\x4b\xc9\x49\x2d\x8e\x4f\xad\x28\xc8\x2c\x4a\x4d\x89\xcf\x4c\x49\xcd\x2b\xc9\x2c\xa9\x8c\xcf\xcc\x2b\x2e\x49\xcc\xc9\x49\x04\xab\xca\x4c\x89\x2f\x4b\x2d\x2a\xce\xcc\xcf\x53\xc8\xcf\x53\x80\xea\x51\xd0\x80\x6a\xd2\x51\x80\xe9\xd2\x51\x40\xd3\xa6\xa3\x00\xd5\xa7\x69\xcd\x45\xa9\x13\x50\xac\x26\xdd\xc6\xa2\xc4\x92\xe4\x8c\xd4\x92\xf8\xcc\xbc\xb4\xfc\xf8\x32\x23\xbc\xd6\xa0\xa9\xc5\x63\x9d\xa6\x35\x17\x20\x00\x00\xff\xff\xd4\xde\x07\x5c\x5b\x01\x00\x00")
func _1636536507_add_index_bundlesUpSqlBytes() ([]byte, error) {
@ -421,7 +463,7 @@ func _1636536507_add_index_bundlesUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1636536507_add_index_bundles.up.sql", size: 347, mode: os.FileMode(0644), modTime: time.Unix(1637535623, 0)}
info := bindataFileInfo{name: "1636536507_add_index_bundles.up.sql", size: 347, mode: os.FileMode(0644), modTime: time.Unix(1637329330, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf1, 0xb9, 0x3c, 0x16, 0xfc, 0xfb, 0xb2, 0xb4, 0x3b, 0xfe, 0xdc, 0xf5, 0x9c, 0x42, 0xa0, 0xa0, 0xd4, 0xd, 0x5b, 0x97, 0x10, 0x80, 0x95, 0xe, 0x13, 0xc1, 0x18, 0x8, 0xee, 0xf, 0x99, 0xee}}
return a, nil
}
@ -441,7 +483,7 @@ func docGo() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "doc.go", size: 377, mode: os.FileMode(0644), modTime: time.Unix(1633220904, 0)}
info := bindataFileInfo{name: "doc.go", size: 377, mode: os.FileMode(0644), modTime: time.Unix(1586880790, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xef, 0xaf, 0xdf, 0xcf, 0x65, 0xae, 0x19, 0xfc, 0x9d, 0x29, 0xc1, 0x91, 0xaf, 0xb5, 0xd5, 0xb1, 0x56, 0xf3, 0xee, 0xa8, 0xba, 0x13, 0x65, 0xdb, 0xab, 0xcf, 0x4e, 0xac, 0x92, 0xe9, 0x60, 0xf1}}
return a, nil
}
@ -569,6 +611,10 @@ var _bindata = map[string]func() (*asset, error){
"1561368210_add_installation_metadata.up.sql": _1561368210_add_installation_metadataUpSql,
"1632236298_add_communities.down.sql": _1632236298_add_communitiesDownSql,
"1632236298_add_communities.up.sql": _1632236298_add_communitiesUpSql,
"1636536507_add_index_bundles.up.sql": _1636536507_add_index_bundlesUpSql,
"doc.go": docGo,
@ -631,6 +677,8 @@ var _bintree = &bintree{nil, map[string]*bintree{
"1559627659_add_contact_code.up.sql": &bintree{_1559627659_add_contact_codeUpSql, map[string]*bintree{}},
"1561368210_add_installation_metadata.down.sql": &bintree{_1561368210_add_installation_metadataDownSql, map[string]*bintree{}},
"1561368210_add_installation_metadata.up.sql": &bintree{_1561368210_add_installation_metadataUpSql, map[string]*bintree{}},
"1632236298_add_communities.down.sql": &bintree{_1632236298_add_communitiesDownSql, map[string]*bintree{}},
"1632236298_add_communities.up.sql": &bintree{_1632236298_add_communitiesUpSql, map[string]*bintree{}},
"1636536507_add_index_bundles.up.sql": &bintree{_1636536507_add_index_bundlesUpSql, map[string]*bintree{}},
"doc.go": &bintree{docGo, map[string]*bintree{}},
}}

View File

@ -0,0 +1,5 @@
DROP INDEX idx_hash_ratchet_enc;
DROP INDEX idx_hash_ratchet_enc_cache;
DROP TABLE hash_ratchet_encryption_cache;
DROP TABLE hash_ratchet_encryption;

View File

@ -0,0 +1,20 @@
CREATE TABLE hash_ratchet_encryption (
group_id BLOB NOT NULL,
key_id INT NOT NULL,
key BLOB NOT NULL,
PRIMARY KEY(group_id, key_id)
);
CREATE UNIQUE INDEX idx_hash_ratchet_enc ON hash_ratchet_encryption(group_id, key_id);
CREATE TABLE hash_ratchet_encryption_cache (
group_id BLOB NOT NULL,
key_id int NOT NULL,
seq_no INTEGER,
hash BLOB NOT NULL,
sym_enc_key BLOB,
FOREIGN KEY(group_id, key_id) REFERENCES hash_ratchet_encryption(group_id, key_id)
);
CREATE UNIQUE INDEX idx_hash_ratchet_enc_cache ON hash_ratchet_encryption_cache(group_id, key_id, seq_no);

View File

@ -727,3 +727,129 @@ func (s *sqliteSessionStorage) Load(id []byte) (*dr.State, error) {
return nil, err
}
}
type HRCache struct {
GroupID string
KeyID uint32
Key []byte
Hash []byte
SeqNo uint32
}
// GetHashRatchetKeyByID retrieves a hash ratchet key by group ID and seqNo.
// If cache data with given seqNo (e.g. 0) is not found,
// then the query will return the cache data with the latest seqNo
func (s *sqlitePersistence) GetHashRatchetKeyByID(groupID []byte, keyID uint32, seqNo uint32) (*HRCache, error) {
stmt, err := s.DB.Prepare(
`WITH input AS (
select ? AS group_id, ? AS key_id, ? as seq_no
),
cec AS (
SELECT e.key, c.seq_no, c.hash FROM hash_ratchet_encryption e, input i
LEFT JOIN hash_ratchet_encryption_cache c ON e.group_id=c.group_id AND e.key_id=c.key_id
WHERE e.key_id=i.key_id AND e.group_id=i.group_id),
seq_nos AS (
select CASE
WHEN EXISTS (SELECT c.seq_no from cec c, input i where c.seq_no=i.seq_no)
THEN i.seq_no
ELSE (select max(seq_no) from cec)
END as seq_no from input i
)
SELECT c.key, c.seq_no, c.hash FROM cec c, input i, seq_nos s
where case when not exists(select seq_no from seq_nos where seq_no is not null)
then 1 else c.seq_no = s.seq_no end`)
if err != nil {
return nil, err
}
defer stmt.Close()
var key, hash []byte
var seqNoPtr *uint32
err = stmt.QueryRow(groupID, keyID, seqNo).Scan(&key, &seqNoPtr, &hash)
var seqNoResult uint32
if seqNoPtr == nil {
seqNoResult = 0
} else {
seqNoResult = *seqNoPtr
}
res := &HRCache{
KeyID: keyID,
Key: key,
Hash: hash,
SeqNo: seqNoResult,
}
switch err {
case sql.ErrNoRows:
return nil, nil
case nil:
return res, nil
default:
return nil, err
}
}
// GetCurrentKeyIDForGroup retrieves a key ID for given group ID
// (with an assumption that key ids are shared in the group, and
// at any given time there is a single key used)
func (s *sqlitePersistence) GetCurrentKeyForGroup(groupID string) (uint32, error) {
stmt, err := s.DB.Prepare(`SELECT key_id
FROM hash_ratchet_encryption
WHERE group_id = ? order by key_id desc limit 1`)
if err != nil {
return 0, err
}
defer stmt.Close()
var keyID uint32
err = stmt.QueryRow([]byte(groupID)).Scan(&keyID)
switch err {
case sql.ErrNoRows:
return 0, nil
case nil:
return keyID, nil
default:
return 0, err
}
}
// SaveHashRachetKeyHash saves a hash ratchet key cache data
func (s *sqlitePersistence) SaveHashRatchetKeyHash(
groupID []byte,
keyID uint32,
hash []byte,
seqNo uint32,
) error {
stmt, err := s.DB.Prepare(`INSERT INTO hash_ratchet_encryption_cache(group_id,key_id,hash,seq_no)
VALUES(?, ?, ?, ?)`)
if err != nil {
return err
}
defer stmt.Close()
_, err = stmt.Exec(groupID, keyID, hash, seqNo)
return err
}
// SaveHashRatchetKey saves a hash ratchet key
func (s *sqlitePersistence) SaveHashRatchetKey(
groupID string,
keyID uint32,
key []byte,
) error {
stmt, err := s.DB.Prepare(`INSERT INTO hash_ratchet_encryption(group_id, key_id, key)
VALUES(?,?,?)`)
if err != nil {
return err
}
defer stmt.Close()
_, err = stmt.Exec([]byte(groupID), keyID, key)
return err
}

View File

@ -179,8 +179,8 @@ func (p *Protocol) BuildPublicMessage(myIdentityKey *ecdsa.PrivateKey, payload [
return &ProtocolMessageSpec{Message: message, Public: true}, nil
}
// BuildDirectMessage returns a 1:1 chat message and optionally a negotiated topic given the user identity private key, the recipient's public key, and a payload
func (p *Protocol) BuildDirectMessage(myIdentityKey *ecdsa.PrivateKey, publicKey *ecdsa.PublicKey, payload []byte) (*ProtocolMessageSpec, error) {
// BuildEncryptedMessage returns a 1:1 chat message and optionally a negotiated topic given the user identity private key, the recipient's public key, and a payload
func (p *Protocol) BuildEncryptedMessage(myIdentityKey *ecdsa.PrivateKey, publicKey *ecdsa.PublicKey, payload []byte) (*ProtocolMessageSpec, error) {
// Get recipients installations.
activeInstallations, err := p.multidevice.GetActiveInstallations(publicKey)
@ -189,15 +189,15 @@ func (p *Protocol) BuildDirectMessage(myIdentityKey *ecdsa.PrivateKey, publicKey
}
// Encrypt payload
directMessagesByInstalls, installations, err := p.encryptor.EncryptPayload(publicKey, myIdentityKey, activeInstallations, payload)
encryptedMessagesByInstalls, installations, err := p.encryptor.EncryptPayload(publicKey, myIdentityKey, activeInstallations, payload)
if err != nil {
return nil, err
}
// Build message
message := &ProtocolMessage{
InstallationId: p.encryptor.config.InstallationID,
DirectMessage: directMessagesByInstalls,
InstallationId: p.encryptor.config.InstallationID,
EncryptedMessage: encryptedMessagesByInstalls,
}
err = p.addBundle(myIdentityKey, message)
@ -208,7 +208,7 @@ func (p *Protocol) BuildDirectMessage(myIdentityKey *ecdsa.PrivateKey, publicKey
// Check who we are sending the message to, and see if we have a shared secret
// across devices
var installationIDs []string
for installationID := range message.GetDirectMessage() {
for installationID := range message.GetEncryptedMessage() {
if installationID != noInstallationID {
installationIDs = append(installationIDs, installationID)
}
@ -228,6 +228,62 @@ func (p *Protocol) BuildDirectMessage(myIdentityKey *ecdsa.PrivateKey, publicKey
return spec, nil
}
// BuildHashRatchetKeyExchangeMessage builds a 1:1 message
// containing newly generated hash ratchet key
func (p *Protocol) BuildHashRatchetKeyExchangeMessage(myIdentityKey *ecdsa.PrivateKey, publicKey *ecdsa.PublicKey, groupID string, keyID uint32) (*ProtocolMessageSpec, error) {
logger := p.logger.With(zap.String("site", "BuildHashRatchetKeyExchangeMessage"))
keyData, err := p.encryptor.persistence.GetHashRatchetKeyByID([]byte(groupID), keyID, 0)
if err != nil {
return nil, err
}
response, err := p.BuildEncryptedMessage(myIdentityKey, publicKey, keyData.Key)
if err != nil {
return nil, err
}
// Loop through installations and assign HRHeader
// SeqNo=0 has a special meaning for HandleMessage
// and signifies a message with hash ratchet key payload
for _, v := range response.Message.EncryptedMessage {
v.HRHeader = &HRHeader{
KeyId: keyID,
SeqNo: 0,
GroupId: groupID,
}
}
logger.Info("Key saved", zap.Any("err", err))
return response, err
}
// BuildHashRatchetMessage returns a hash ratchet chat message
func (p *Protocol) BuildHashRatchetMessage(groupID []byte, payload []byte) (*ProtocolMessageSpec, error) {
keyID, err := p.encryptor.persistence.GetCurrentKeyForGroup(string(groupID))
if err != nil {
return nil, err
}
// Encrypt payload
encryptedMessagesByInstalls, err := p.encryptor.EncryptHashRatchetPayload(groupID, keyID, payload)
if err != nil {
return nil, err
}
// Build message
message := &ProtocolMessage{
InstallationId: p.encryptor.config.InstallationID,
EncryptedMessage: encryptedMessagesByInstalls,
}
spec := &ProtocolMessageSpec{
Message: message,
}
return spec, nil
}
// BuildDHMessage builds a message with DH encryption so that it can be decrypted by any other device.
func (p *Protocol) BuildDHMessage(myIdentityKey *ecdsa.PrivateKey, destination *ecdsa.PublicKey, payload []byte) (*ProtocolMessageSpec, error) {
// Encrypt payload
@ -238,8 +294,8 @@ func (p *Protocol) BuildDHMessage(myIdentityKey *ecdsa.PrivateKey, destination *
// Build message
message := &ProtocolMessage{
InstallationId: p.encryptor.config.InstallationID,
DirectMessage: encryptionResponse,
InstallationId: p.encryptor.config.InstallationID,
EncryptedMessage: encryptionResponse,
}
err = p.addBundle(myIdentityKey, message)
@ -405,18 +461,31 @@ func (p *Protocol) HandleMessage(
}
// Decrypt message
if directMessage := protocolMessage.GetDirectMessage(); directMessage != nil {
if encryptedMessage := protocolMessage.GetEncryptedMessage(); encryptedMessage != nil {
message, err := p.encryptor.DecryptPayload(
myIdentityKey,
theirPublicKey,
protocolMessage.GetInstallationId(),
directMessage,
encryptedMessage,
messageID,
)
if err != nil {
return nil, err
}
dmProtocol := encryptedMessage[p.encryptor.config.InstallationID]
if dmProtocol == nil {
dmProtocol = encryptedMessage[noInstallationID]
}
hrHeader := dmProtocol.HRHeader
if hrHeader != nil && hrHeader.SeqNo == 0 {
// Payload contains hash ratchet key
err = p.encryptor.persistence.SaveHashRatchetKey(hrHeader.GroupId, hrHeader.KeyId, message)
if err != nil {
return nil, err
}
}
bundles := protocolMessage.GetBundles()
version := getProtocolVersion(bundles, protocolMessage.GetInstallationId())
if version >= sharedSecretNegotiationVersion {

File diff suppressed because it is too large Load Diff

View File

@ -54,11 +54,22 @@ message X3DHHeader {
bytes id = 4;
}
// Hash Ratchet Header
message HRHeader {
// community key ID
uint32 key_id = 1;
// Community message number for this key_id
uint32 seq_no = 2;
// Community ID
string group_id = 3;
}
// Direct message value
message DirectMessageProtocol {
message EncryptedMessageProtocol {
X3DHHeader X3DH_header = 1;
DRHeader DR_header = 2;
DRHeader DR_header = 2;
DHHeader DH_header = 101;
HRHeader HR_header = 102;
// Encrypted payload
bytes payload = 3;
}
@ -72,7 +83,8 @@ message ProtocolMessage {
repeated Bundle bundles = 3;
// One to one message, encrypted, indexed by installation_id
map<string,DirectMessageProtocol> direct_message = 101;
// TODO map here is redundant in case of community messages
map<string,EncryptedMessageProtocol> encrypted_message = 101;
// Public chats, not encrypted
bytes public_message = 102;

View File

@ -78,7 +78,7 @@ func (s *ProtocolServiceTestSuite) TestBuildPublicMessage() {
s.NotNilf(msg.Message.GetBundles(), "It adds a bundle to the message")
}
func (s *ProtocolServiceTestSuite) TestBuildDirectMessage() {
func (s *ProtocolServiceTestSuite) TestBuildEncryptedMessage() {
bobKey, err := crypto.GenerateKey()
s.NoError(err)
aliceKey, err := crypto.GenerateKey()
@ -86,7 +86,7 @@ func (s *ProtocolServiceTestSuite) TestBuildDirectMessage() {
payload := []byte("test")
msgSpec, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, payload)
msgSpec, err := s.alice.BuildEncryptedMessage(aliceKey, &bobKey.PublicKey, payload)
s.NoError(err)
s.NotNil(msgSpec, "It creates a message spec")
@ -95,7 +95,7 @@ func (s *ProtocolServiceTestSuite) TestBuildDirectMessage() {
s.NotNilf(msg.GetBundles(), "It adds a bundle to the message")
directMessage := msg.GetDirectMessage()
directMessage := msg.GetEncryptedMessage()
s.NotNilf(directMessage, "It sets the direct message")
encryptedPayload := directMessage["none"].GetPayload()
@ -104,7 +104,7 @@ func (s *ProtocolServiceTestSuite) TestBuildDirectMessage() {
s.NotEqualf(payload, encryptedPayload, "It encrypts the payload")
}
func (s *ProtocolServiceTestSuite) TestBuildAndReadDirectMessage() {
func (s *ProtocolServiceTestSuite) TestBuildAndReadEncryptedMessage() {
bobKey, err := crypto.GenerateKey()
s.Require().NoError(err)
aliceKey, err := crypto.GenerateKey()
@ -113,7 +113,7 @@ func (s *ProtocolServiceTestSuite) TestBuildAndReadDirectMessage() {
payload := []byte("test")
// Message is sent with DH
msgSpec, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, payload)
msgSpec, err := s.alice.BuildEncryptedMessage(aliceKey, &bobKey.PublicKey, payload)
s.Require().NoError(err)
s.Require().NotNil(msgSpec)
@ -140,7 +140,7 @@ func (s *ProtocolServiceTestSuite) TestSecretNegotiation() {
_, err = s.bob.Start(bobKey)
s.Require().NoError(err)
msgSpec, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, payload)
msgSpec, err := s.alice.BuildEncryptedMessage(aliceKey, &bobKey.PublicKey, payload)
s.NoError(err)
s.NotNil(msgSpec, "It creates a message spec")
s.Require().NotNil(msgSpec.SharedSecret)