Add GetContactCode call, add DH flag (#1367)
This PR does a few things: 1) Add a call GetContactCode to check whether we have a bundle for a given user. 2) Add a DH flag to the API (non-breaking change), for those messages that we want to target all devices (contact-requests for example). 3) Fixes a few small issues with installations, namely if for example a messages is sent without a bundle (currently not done by any client), we still infer installation info, so that we can communicate securely and making it truly optional.
This commit is contained in:
parent
a249151d05
commit
2a4382369a
|
@ -2,6 +2,7 @@ package api
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
@ -556,6 +557,35 @@ func (b *StatusBackend) CreateContactCode() (string, error) {
|
|||
return bundle.ToBase64()
|
||||
}
|
||||
|
||||
// GetContactCode return the latest contact code
|
||||
func (b *StatusBackend) GetContactCode(identity string) (string, error) {
|
||||
st, err := b.statusNode.ShhExtService()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
publicKeyBytes, err := hex.DecodeString(identity)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
publicKey, err := ethcrypto.UnmarshalPubkey(publicKeyBytes)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
bundle, err := st.GetPublicBundle(publicKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if bundle == nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return bundle.ToBase64()
|
||||
}
|
||||
|
||||
// ProcessContactCode process and adds the someone else's bundle
|
||||
func (b *StatusBackend) ProcessContactCode(contactCode string) error {
|
||||
selectedChatAccount, err := b.AccountManager().SelectedChatAccount()
|
||||
|
|
|
@ -70,6 +70,24 @@ func ProcessContactCode(bundleString *C.char) *C.char {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Get an X3DH bundle
|
||||
//export GetContactCode
|
||||
func GetContactCode(identityString *C.char) *C.char {
|
||||
bundle, err := statusBackend.GetContactCode(C.GoString(identityString))
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
|
||||
data, err := json.Marshal(struct {
|
||||
ContactCode string `json:"code"`
|
||||
}{ContactCode: bundle})
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
|
||||
return C.CString(string(data))
|
||||
}
|
||||
|
||||
//export ExtractIdentityFromContactCode
|
||||
func ExtractIdentityFromContactCode(bundleString *C.char) *C.char {
|
||||
bundle := C.GoString(bundleString)
|
||||
|
|
|
@ -501,3 +501,21 @@ func SetMobileSignalHandler(handler SignalHandler) {
|
|||
func SetSignalEventCallback(cb unsafe.Pointer) {
|
||||
signal.SetSignalEventCallback(cb)
|
||||
}
|
||||
|
||||
// Get an X3DH bundle
|
||||
//export GetContactCode
|
||||
func GetContactCode(identity string) string {
|
||||
bundle, err := statusBackend.GetContactCode(identity)
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
|
||||
data, err := json.Marshal(struct {
|
||||
ContactCode string `json:"code"`
|
||||
}{ContactCode: bundle})
|
||||
if err != nil {
|
||||
return makeJSONResponse(err)
|
||||
}
|
||||
|
||||
return string(data)
|
||||
}
|
||||
|
|
|
@ -422,7 +422,7 @@ func (api *PublicAPI) SendPublicMessage(ctx context.Context, msg chat.SendPublic
|
|||
}
|
||||
|
||||
// SendDirectMessage sends a 1:1 chat message to the underlying transport
|
||||
func (api *PublicAPI) SendDirectMessage(ctx context.Context, msg chat.SendDirectMessageRPC) ([]hexutil.Bytes, error) {
|
||||
func (api *PublicAPI) SendDirectMessage(ctx context.Context, msg chat.SendDirectMessageRPC) (hexutil.Bytes, error) {
|
||||
if !api.service.pfsEnabled {
|
||||
return nil, ErrPFSNotEnabled
|
||||
}
|
||||
|
@ -438,29 +438,26 @@ func (api *PublicAPI) SendDirectMessage(ctx context.Context, msg chat.SendDirect
|
|||
}
|
||||
|
||||
// This is transport layer-agnostic
|
||||
protocolMessages, err := api.service.protocol.BuildDirectMessage(privateKey, msg.Payload, publicKey)
|
||||
var protocolMessage []byte
|
||||
|
||||
if msg.DH {
|
||||
protocolMessage, err = api.service.protocol.BuildDHMessage(privateKey, &privateKey.PublicKey, msg.Payload)
|
||||
} else {
|
||||
protocolMessage, err = api.service.protocol.BuildDirectMessage(privateKey, publicKey, msg.Payload)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response []hexutil.Bytes
|
||||
// Enrich with transport layer info
|
||||
whisperMessage := chat.DirectMessageToWhisper(msg, protocolMessage)
|
||||
|
||||
for key, message := range protocolMessages {
|
||||
msg.PubKey = crypto.FromECDSAPub(key)
|
||||
// Enrich with transport layer info
|
||||
whisperMessage := chat.DirectMessageToWhisper(msg, message)
|
||||
|
||||
// And dispatch
|
||||
hash, err := api.Post(ctx, whisperMessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response = append(response, hash)
|
||||
|
||||
}
|
||||
return response, nil
|
||||
// And dispatch
|
||||
return api.Post(ctx, whisperMessage)
|
||||
}
|
||||
|
||||
// DEPRECATED: use SendDirectMessage with DH flag
|
||||
// SendPairingMessage sends a 1:1 chat message to our own devices to initiate a pairing session
|
||||
func (api *PublicAPI) SendPairingMessage(ctx context.Context, msg chat.SendDirectMessageRPC) ([]hexutil.Bytes, error) {
|
||||
if !api.service.pfsEnabled {
|
||||
|
@ -477,7 +474,7 @@ func (api *PublicAPI) SendPairingMessage(ctx context.Context, msg chat.SendDirec
|
|||
return nil, err
|
||||
}
|
||||
|
||||
protocolMessage, err := api.service.protocol.BuildPairingMessage(privateKey, msg.Payload)
|
||||
protocolMessage, err := api.service.protocol.BuildDHMessage(privateKey, &privateKey.PublicKey, msg.Payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -497,57 +494,6 @@ func (api *PublicAPI) SendPairingMessage(ctx context.Context, msg chat.SendDirec
|
|||
return response, nil
|
||||
}
|
||||
|
||||
// SendGroupMessage sends a group messag chat message to the underlying transport
|
||||
func (api *PublicAPI) SendGroupMessage(ctx context.Context, msg chat.SendGroupMessageRPC) ([]hexutil.Bytes, error) {
|
||||
if !api.service.pfsEnabled {
|
||||
return nil, ErrPFSNotEnabled
|
||||
}
|
||||
|
||||
// To be completely agnostic from whisper we should not be using whisper to store the key
|
||||
privateKey, err := api.service.w.GetPrivateKey(msg.Sig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var keys []*ecdsa.PublicKey
|
||||
|
||||
for _, k := range msg.PubKeys {
|
||||
publicKey, err := crypto.UnmarshalPubkey(k)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keys = append(keys, publicKey)
|
||||
}
|
||||
|
||||
// This is transport layer-agnostic
|
||||
protocolMessages, err := api.service.protocol.BuildDirectMessage(privateKey, msg.Payload, keys...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response []hexutil.Bytes
|
||||
|
||||
for key, message := range protocolMessages {
|
||||
directMessage := chat.SendDirectMessageRPC{
|
||||
PubKey: crypto.FromECDSAPub(key),
|
||||
Payload: msg.Payload,
|
||||
Sig: msg.Sig,
|
||||
}
|
||||
|
||||
// Enrich with transport layer info
|
||||
whisperMessage := chat.DirectMessageToWhisper(directMessage, message)
|
||||
|
||||
// And dispatch
|
||||
hash, err := api.Post(ctx, whisperMessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response = append(response, hash)
|
||||
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (api *PublicAPI) processPFSMessage(msg *whisper.Message) error {
|
||||
|
||||
privateKeyID := api.service.w.SelectedKeyPairID()
|
||||
|
@ -567,21 +513,26 @@ func (api *PublicAPI) processPFSMessage(msg *whisper.Message) error {
|
|||
|
||||
response, err := api.service.protocol.HandleMessage(privateKey, publicKey, msg.Payload)
|
||||
|
||||
// Notify that someone tried to contact us using an invalid bundle
|
||||
if err == chat.ErrDeviceNotFound && privateKey.PublicKey != *publicKey {
|
||||
api.log.Warn("Device not found, sending signal", "err", err)
|
||||
keyString := fmt.Sprintf("0x%x", crypto.FromECDSAPub(publicKey))
|
||||
handler := EnvelopeSignalHandler{}
|
||||
handler.DecryptMessageFailed(keyString)
|
||||
return nil
|
||||
} else if err != nil {
|
||||
// Ignore errors for now as those might be non-pfs messages
|
||||
switch err {
|
||||
case nil:
|
||||
// Set the decrypted payload
|
||||
msg.Payload = response
|
||||
case chat.ErrDeviceNotFound:
|
||||
// Notify that someone tried to contact us using an invalid bundle
|
||||
if privateKey.PublicKey != *publicKey {
|
||||
api.log.Warn("Device not found, sending signal", "err", err)
|
||||
keyString := fmt.Sprintf("0x%x", crypto.FromECDSAPub(publicKey))
|
||||
handler := EnvelopeSignalHandler{}
|
||||
handler.DecryptMessageFailed(keyString)
|
||||
}
|
||||
case chat.ErrNotProtocolMessage:
|
||||
// Not using encryption, pass directly to the layer below
|
||||
api.log.Debug("Not a protocol message", "err", err)
|
||||
default:
|
||||
// Log and pass to the client, even if failed to decrypt
|
||||
api.log.Error("Failed handling message with error", "err", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add unencrypted payload
|
||||
msg.Payload = response
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -285,6 +285,11 @@ func (s *EncryptionService) DecryptPayload(myIdentityKey *ecdsa.PrivateKey, thei
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// Add installations with a timestamp of 0, as we don't have bundle informations
|
||||
if err = s.persistence.AddInstallations(theirIdentityKeyC, 0, []string{theirInstallationID}, true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We mark the exchange as successful so we stop sending x3dh header
|
||||
if err = s.persistence.RatchetInfoConfirmed(drHeader.GetId(), theirIdentityKeyC, theirInstallationID); err != nil {
|
||||
s.log.Error("Could not confirm ratchet info", "err", err)
|
||||
|
@ -450,13 +455,8 @@ func (s *EncryptionService) EncryptPayloadWithDH(theirIdentityKey *ecdsa.PublicK
|
|||
return response, nil
|
||||
}
|
||||
|
||||
// EncryptPayload returns a new DirectMessageProtocol with a given payload encrypted, given a recipient's public key and the sender private identity key
|
||||
// TODO: refactor this
|
||||
// nolint: gocyclo
|
||||
func (s *EncryptionService) EncryptPayload(theirIdentityKey *ecdsa.PublicKey, myIdentityKey *ecdsa.PrivateKey, payload []byte) (map[string]*DirectMessageProtocol, error) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
// GetPublicBundle returns the active installations bundles for a given user
|
||||
func (s *EncryptionService) GetPublicBundle(theirIdentityKey *ecdsa.PublicKey) (*Bundle, error) {
|
||||
theirIdentityKeyC := ecrypto.CompressPubkey(theirIdentityKey)
|
||||
|
||||
installationIDs, err := s.persistence.GetActiveInstallations(s.config.MaxInstallations, theirIdentityKeyC)
|
||||
|
@ -464,25 +464,43 @@ func (s *EncryptionService) EncryptPayload(theirIdentityKey *ecdsa.PublicKey, my
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// Get their latest bundle
|
||||
theirBundle, err := s.persistence.GetPublicBundle(theirIdentityKey, installationIDs)
|
||||
return s.persistence.GetPublicBundle(theirIdentityKey, installationIDs)
|
||||
}
|
||||
|
||||
// EncryptPayload returns a new DirectMessageProtocol with a given payload encrypted, given a recipient's public key and the sender private identity key
|
||||
// TODO: refactor this
|
||||
// nolint: gocyclo
|
||||
func (s *EncryptionService) EncryptPayload(theirIdentityKey *ecdsa.PublicKey, myIdentityKey *ecdsa.PrivateKey, payload []byte) (map[string]*DirectMessageProtocol, error) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
s.log.Debug("Sending message", "theirKey", theirIdentityKey)
|
||||
|
||||
theirIdentityKeyC := ecrypto.CompressPubkey(theirIdentityKey)
|
||||
|
||||
// Get their installationIds
|
||||
installationIds, err := s.persistence.GetActiveInstallations(s.config.MaxInstallations, theirIdentityKeyC)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We don't have any, send a message with DH
|
||||
if theirBundle == nil && !bytes.Equal(theirIdentityKeyC, ecrypto.CompressPubkey(&myIdentityKey.PublicKey)) {
|
||||
if installationIds == nil && !bytes.Equal(theirIdentityKeyC, ecrypto.CompressPubkey(&myIdentityKey.PublicKey)) {
|
||||
return s.EncryptPayloadWithDH(theirIdentityKey, payload)
|
||||
}
|
||||
|
||||
response := make(map[string]*DirectMessageProtocol)
|
||||
|
||||
for installationID, signedPreKeyContainer := range theirBundle.GetSignedPreKeys() {
|
||||
for _, installationID := range installationIds {
|
||||
s.log.Debug("Processing installation", "installationID", installationID)
|
||||
if s.config.InstallationID == installationID {
|
||||
continue
|
||||
}
|
||||
bundle, err := s.persistence.GetPublicBundle(theirIdentityKey, []string{installationID})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
theirSignedPreKey := signedPreKeyContainer.GetSignedPreKey()
|
||||
// See if a session is there already
|
||||
drInfo, err := s.persistence.GetAnyRatchetInfo(theirIdentityKeyC, installationID)
|
||||
if err != nil {
|
||||
|
@ -490,6 +508,7 @@ func (s *EncryptionService) EncryptPayload(theirIdentityKey *ecdsa.PublicKey, my
|
|||
}
|
||||
|
||||
if drInfo != nil {
|
||||
s.log.Debug("Found DR info", "installationID", installationID)
|
||||
encryptedPayload, drHeader, err := s.encryptUsingDR(theirIdentityKey, drInfo, payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -511,6 +530,18 @@ func (s *EncryptionService) EncryptPayload(theirIdentityKey *ecdsa.PublicKey, my
|
|||
continue
|
||||
}
|
||||
|
||||
theirSignedPreKeyContainer := bundle.GetSignedPreKeys()[installationID]
|
||||
|
||||
// This should not be nil at this point
|
||||
if theirSignedPreKeyContainer == nil {
|
||||
s.log.Warn("Could not find either a ratchet info or a bundle for installationId", "installationID", installationID)
|
||||
continue
|
||||
|
||||
}
|
||||
s.log.Debug("DR info not found, using bundle", "installationID", installationID)
|
||||
|
||||
theirSignedPreKey := theirSignedPreKeyContainer.GetSignedPreKey()
|
||||
|
||||
sharedKey, ourEphemeralKey, err := s.keyFromActiveX3DH(theirIdentityKeyC, theirSignedPreKey, myIdentityKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -549,5 +580,7 @@ func (s *EncryptionService) EncryptPayload(theirIdentityKey *ecdsa.PublicKey, my
|
|||
}
|
||||
}
|
||||
|
||||
s.log.Debug("Built message", "theirKey", theirIdentityKey)
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@ type ProtocolService struct {
|
|||
Enabled bool
|
||||
}
|
||||
|
||||
var ErrNotProtocolMessage = errors.New("Not a protocol message")
|
||||
|
||||
// NewProtocolService creates a new ProtocolService instance
|
||||
func NewProtocolService(encryption *EncryptionService, addedBundlesHandler func([]IdentityAndIDPair)) *ProtocolService {
|
||||
return &ProtocolService{
|
||||
|
@ -62,38 +64,27 @@ func (p *ProtocolService) BuildPublicMessage(myIdentityKey *ecdsa.PrivateKey, pa
|
|||
}
|
||||
|
||||
// BuildDirectMessage marshals a 1:1 chat message given the user identity private key, the recipient's public key, and a payload
|
||||
func (p *ProtocolService) BuildDirectMessage(myIdentityKey *ecdsa.PrivateKey, payload []byte, theirPublicKeys ...*ecdsa.PublicKey) (map[*ecdsa.PublicKey][]byte, error) {
|
||||
response := make(map[*ecdsa.PublicKey][]byte)
|
||||
for _, publicKey := range theirPublicKeys {
|
||||
// Encrypt payload
|
||||
encryptionResponse, err := p.encryption.EncryptPayload(publicKey, myIdentityKey, payload)
|
||||
if err != nil {
|
||||
p.log.Error("encryption-service", "error encrypting payload", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Build message
|
||||
protocolMessage := &ProtocolMessage{
|
||||
InstallationId: p.encryption.config.InstallationID,
|
||||
DirectMessage: encryptionResponse,
|
||||
}
|
||||
|
||||
payload, err := p.addBundleAndMarshal(myIdentityKey, protocolMessage, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(payload) != 0 {
|
||||
response[publicKey] = payload
|
||||
}
|
||||
func (p *ProtocolService) BuildDirectMessage(myIdentityKey *ecdsa.PrivateKey, publicKey *ecdsa.PublicKey, payload []byte) ([]byte, error) {
|
||||
// Encrypt payload
|
||||
encryptionResponse, err := p.encryption.EncryptPayload(publicKey, myIdentityKey, payload)
|
||||
if err != nil {
|
||||
p.log.Error("encryption-service", "error encrypting payload", err)
|
||||
return nil, err
|
||||
}
|
||||
return response, nil
|
||||
|
||||
// Build message
|
||||
protocolMessage := &ProtocolMessage{
|
||||
InstallationId: p.encryption.config.InstallationID,
|
||||
DirectMessage: encryptionResponse,
|
||||
}
|
||||
|
||||
return p.addBundleAndMarshal(myIdentityKey, protocolMessage, true)
|
||||
}
|
||||
|
||||
// BuildPairingMessage sends a message to our own devices using DH so that it can be decrypted by any other device.
|
||||
func (p *ProtocolService) BuildPairingMessage(myIdentityKey *ecdsa.PrivateKey, payload []byte) ([]byte, error) {
|
||||
// BuildDHMessage builds a message with DH encryption so that it can be decrypted by any other device.
|
||||
func (p *ProtocolService) BuildDHMessage(myIdentityKey *ecdsa.PrivateKey, destination *ecdsa.PublicKey, payload []byte) ([]byte, error) {
|
||||
// Encrypt payload
|
||||
encryptionResponse, err := p.encryption.EncryptPayloadWithDH(&myIdentityKey.PublicKey, payload)
|
||||
encryptionResponse, err := p.encryption.EncryptPayloadWithDH(destination, payload)
|
||||
if err != nil {
|
||||
p.log.Error("encryption-service", "error encrypting payload", err)
|
||||
return nil, err
|
||||
|
@ -128,6 +119,11 @@ func (p *ProtocolService) DisableInstallation(myIdentityKey *ecdsa.PublicKey, in
|
|||
return p.encryption.DisableInstallation(myIdentityKey, installationID)
|
||||
}
|
||||
|
||||
// GetPublicBundle retrieves a public bundle given an identity
|
||||
func (p *ProtocolService) GetPublicBundle(theirIdentityKey *ecdsa.PublicKey) (*Bundle, error) {
|
||||
return p.encryption.GetPublicBundle(theirIdentityKey)
|
||||
}
|
||||
|
||||
// HandleMessage unmarshals a message and processes it, decrypting it if it is a 1:1 message.
|
||||
func (p *ProtocolService) HandleMessage(myIdentityKey *ecdsa.PrivateKey, theirPublicKey *ecdsa.PublicKey, payload []byte) ([]byte, error) {
|
||||
if p.encryption == nil {
|
||||
|
@ -138,7 +134,7 @@ func (p *ProtocolService) HandleMessage(myIdentityKey *ecdsa.PrivateKey, theirPu
|
|||
protocolMessage := &ProtocolMessage{}
|
||||
|
||||
if err := proto.Unmarshal(payload, protocolMessage); err != nil {
|
||||
return nil, err
|
||||
return nil, ErrNotProtocolMessage
|
||||
}
|
||||
|
||||
// Process bundle, deprecated, here for backward compatibility
|
||||
|
|
|
@ -80,13 +80,12 @@ func (s *ProtocolServiceTestSuite) TestBuildDirectMessage() {
|
|||
})
|
||||
s.NoError(err)
|
||||
|
||||
marshaledMsg, err := s.alice.BuildDirectMessage(aliceKey, payload, &bobKey.PublicKey, &aliceKey.PublicKey)
|
||||
marshaledMsg, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, payload)
|
||||
s.NoError(err)
|
||||
s.NotNil(marshaledMsg, "It creates a message")
|
||||
s.NotNil(marshaledMsg[&aliceKey.PublicKey], "It creates a single message")
|
||||
|
||||
unmarshaledMsg := &ProtocolMessage{}
|
||||
err = proto.Unmarshal(marshaledMsg[&bobKey.PublicKey], unmarshaledMsg)
|
||||
err = proto.Unmarshal(marshaledMsg, unmarshaledMsg)
|
||||
s.NoError(err)
|
||||
s.NotNilf(unmarshaledMsg.GetBundle(), "It adds a bundle to the message")
|
||||
|
||||
|
@ -116,12 +115,12 @@ func (s *ProtocolServiceTestSuite) TestBuildAndReadDirectMessage() {
|
|||
s.NoError(err)
|
||||
|
||||
// Message is sent with DH
|
||||
marshaledMsg, err := s.alice.BuildDirectMessage(aliceKey, marshaledPayload, &bobKey.PublicKey)
|
||||
marshaledMsg, err := s.alice.BuildDirectMessage(aliceKey, &bobKey.PublicKey, marshaledPayload)
|
||||
|
||||
s.NoError(err)
|
||||
|
||||
// Bob is able to decrypt the message
|
||||
unmarshaledMsg, err := s.bob.HandleMessage(bobKey, &aliceKey.PublicKey, marshaledMsg[&bobKey.PublicKey])
|
||||
unmarshaledMsg, err := s.bob.HandleMessage(bobKey, &aliceKey.PublicKey, marshaledMsg)
|
||||
s.NoError(err)
|
||||
|
||||
s.NotNil(unmarshaledMsg)
|
||||
|
|
|
@ -20,11 +20,5 @@ type SendDirectMessageRPC struct {
|
|||
Chat string
|
||||
Payload hexutil.Bytes
|
||||
PubKey hexutil.Bytes
|
||||
}
|
||||
|
||||
// SendGroupMessageRPC represents the RPC payload for the SendGroupMessage RPC method
|
||||
type SendGroupMessageRPC struct {
|
||||
Sig string
|
||||
Payload hexutil.Bytes
|
||||
PubKeys []hexutil.Bytes
|
||||
DH bool
|
||||
}
|
||||
|
|
|
@ -983,11 +983,13 @@ func (s *SQLLitePersistence) AddInstallations(identity []byte, timestamp int64,
|
|||
return err
|
||||
}
|
||||
|
||||
// We update timestamp if present without changing enabled
|
||||
// We update timestamp if present without changing enabled, only if this is a new bundle
|
||||
if err != sql.ErrNoRows {
|
||||
stmt, err = tx.Prepare(`UPDATE installations
|
||||
SET timestamp = ?, enabled = ?
|
||||
WHERE identity = ? AND installation_id = ?`)
|
||||
WHERE identity = ?
|
||||
AND installation_id = ?
|
||||
AND timestamp < ?`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -997,6 +999,7 @@ func (s *SQLLitePersistence) AddInstallations(identity []byte, timestamp int64,
|
|||
oldEnabled,
|
||||
identity,
|
||||
installationID,
|
||||
timestamp,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -204,6 +204,14 @@ func (s *Service) EnableInstallation(myIdentityKey *ecdsa.PublicKey, installatio
|
|||
return s.protocol.EnableInstallation(myIdentityKey, installationID)
|
||||
}
|
||||
|
||||
func (s *Service) GetPublicBundle(identityKey *ecdsa.PublicKey) (*chat.Bundle, error) {
|
||||
if s.protocol == nil {
|
||||
return nil, errProtocolNotInitialized
|
||||
}
|
||||
|
||||
return s.protocol.GetPublicBundle(identityKey)
|
||||
}
|
||||
|
||||
// DisableInstallation disables an installation for multi-device sync.
|
||||
func (s *Service) DisableInstallation(myIdentityKey *ecdsa.PublicKey, installationID string) error {
|
||||
if s.protocol == nil {
|
||||
|
|
Loading…
Reference in New Issue