Refactor messaging (#1510)

This change flattens messaging/chat package. It also removes dependency between multidevice and chat/protobuf packages.

The Publisher interface was also changed a bit to support more native types.

Version got bumped to 0.29.0-beta.3.
This commit is contained in:
Adam Babik 2019-07-03 21:13:11 +02:00 committed by GitHub
parent cfdeaa4b98
commit dbaf622e12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 158 additions and 456 deletions

View File

@ -1 +1 @@
0.29.0-beta.2
0.29.0-beta.3

View File

@ -19,7 +19,7 @@ import (
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/status-im/status-go/account"
"github.com/status-im/status-go/mailserver/registry"
"github.com/status-im/status-go/messaging/chat/crypto"
"github.com/status-im/status-go/messaging/crypto"
"github.com/status-im/status-go/node"
"github.com/status-im/status-go/notifications/push/fcm"
"github.com/status-im/status-go/params"

View File

@ -1,342 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: chat.proto
package chat
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
// What is sent through the wire
type ChatMessagePayload struct {
// Message content
Content string `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"`
// MIME type
ContentType string `protobuf:"bytes,2,opt,name=content_type,json=contentType,proto3" json:"content_type,omitempty"`
// Message type
MessageType string `protobuf:"bytes,3,opt,name=message_type,json=messageType,proto3" json:"message_type,omitempty"`
// Sender's clock value for message ordering
ClockValue float64 `protobuf:"fixed64,4,opt,name=clock_value,json=clockValue,proto3" json:"clock_value,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ChatMessagePayload) Reset() { *m = ChatMessagePayload{} }
func (m *ChatMessagePayload) String() string { return proto.CompactTextString(m) }
func (*ChatMessagePayload) ProtoMessage() {}
func (*ChatMessagePayload) Descriptor() ([]byte, []int) {
return fileDescriptor_8c585a45e2093e54, []int{0}
}
func (m *ChatMessagePayload) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChatMessagePayload.Unmarshal(m, b)
}
func (m *ChatMessagePayload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ChatMessagePayload.Marshal(b, m, deterministic)
}
func (m *ChatMessagePayload) XXX_Merge(src proto.Message) {
xxx_messageInfo_ChatMessagePayload.Merge(m, src)
}
func (m *ChatMessagePayload) XXX_Size() int {
return xxx_messageInfo_ChatMessagePayload.Size(m)
}
func (m *ChatMessagePayload) XXX_DiscardUnknown() {
xxx_messageInfo_ChatMessagePayload.DiscardUnknown(m)
}
var xxx_messageInfo_ChatMessagePayload proto.InternalMessageInfo
func (m *ChatMessagePayload) GetContent() string {
if m != nil {
return m.Content
}
return ""
}
func (m *ChatMessagePayload) GetContentType() string {
if m != nil {
return m.ContentType
}
return ""
}
func (m *ChatMessagePayload) GetMessageType() string {
if m != nil {
return m.MessageType
}
return ""
}
func (m *ChatMessagePayload) GetClockValue() float64 {
if m != nil {
return m.ClockValue
}
return 0
}
// ContactUpdatePayload is sent when a user updates its profile
type ContactUpdatePayload struct {
// Contact display name
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
// Contact profile image, using the data URI scheme (e.g. "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAA...")
ProfileImage string `protobuf:"bytes,2,opt,name=profile_image,json=profileImage,proto3" json:"profile_image,omitempty"`
// Contact address
Address string `protobuf:"bytes,3,opt,name=address,proto3" json:"address,omitempty"`
// Contact Firebase Cloud Messaging token
FcmToken string `protobuf:"bytes,4,opt,name=fcm_token,json=fcmToken,proto3" json:"fcm_token,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ContactUpdatePayload) Reset() { *m = ContactUpdatePayload{} }
func (m *ContactUpdatePayload) String() string { return proto.CompactTextString(m) }
func (*ContactUpdatePayload) ProtoMessage() {}
func (*ContactUpdatePayload) Descriptor() ([]byte, []int) {
return fileDescriptor_8c585a45e2093e54, []int{1}
}
func (m *ContactUpdatePayload) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ContactUpdatePayload.Unmarshal(m, b)
}
func (m *ContactUpdatePayload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ContactUpdatePayload.Marshal(b, m, deterministic)
}
func (m *ContactUpdatePayload) XXX_Merge(src proto.Message) {
xxx_messageInfo_ContactUpdatePayload.Merge(m, src)
}
func (m *ContactUpdatePayload) XXX_Size() int {
return xxx_messageInfo_ContactUpdatePayload.Size(m)
}
func (m *ContactUpdatePayload) XXX_DiscardUnknown() {
xxx_messageInfo_ContactUpdatePayload.DiscardUnknown(m)
}
var xxx_messageInfo_ContactUpdatePayload proto.InternalMessageInfo
func (m *ContactUpdatePayload) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *ContactUpdatePayload) GetProfileImage() string {
if m != nil {
return m.ProfileImage
}
return ""
}
func (m *ContactUpdatePayload) GetAddress() string {
if m != nil {
return m.Address
}
return ""
}
func (m *ContactUpdatePayload) GetFcmToken() string {
if m != nil {
return m.FcmToken
}
return ""
}
// Incoming RPC messages
type OneToOneRPC struct {
Src string `protobuf:"bytes,1,opt,name=src,proto3" json:"src,omitempty"`
Dst string `protobuf:"bytes,2,opt,name=dst,proto3" json:"dst,omitempty"`
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 *OneToOneRPC) Reset() { *m = OneToOneRPC{} }
func (m *OneToOneRPC) String() string { return proto.CompactTextString(m) }
func (*OneToOneRPC) ProtoMessage() {}
func (*OneToOneRPC) Descriptor() ([]byte, []int) {
return fileDescriptor_8c585a45e2093e54, []int{2}
}
func (m *OneToOneRPC) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_OneToOneRPC.Unmarshal(m, b)
}
func (m *OneToOneRPC) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_OneToOneRPC.Marshal(b, m, deterministic)
}
func (m *OneToOneRPC) XXX_Merge(src proto.Message) {
xxx_messageInfo_OneToOneRPC.Merge(m, src)
}
func (m *OneToOneRPC) XXX_Size() int {
return xxx_messageInfo_OneToOneRPC.Size(m)
}
func (m *OneToOneRPC) XXX_DiscardUnknown() {
xxx_messageInfo_OneToOneRPC.DiscardUnknown(m)
}
var xxx_messageInfo_OneToOneRPC proto.InternalMessageInfo
func (m *OneToOneRPC) GetSrc() string {
if m != nil {
return m.Src
}
return ""
}
func (m *OneToOneRPC) GetDst() string {
if m != nil {
return m.Dst
}
return ""
}
func (m *OneToOneRPC) GetPayload() []byte {
if m != nil {
return m.Payload
}
return nil
}
type ContactUpdateRPC struct {
Src string `protobuf:"bytes,1,opt,name=src,proto3" json:"src,omitempty"`
Dst string `protobuf:"bytes,2,opt,name=dst,proto3" json:"dst,omitempty"`
Payload *ContactUpdatePayload `protobuf:"bytes,3,opt,name=payload,proto3" json:"payload,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ContactUpdateRPC) Reset() { *m = ContactUpdateRPC{} }
func (m *ContactUpdateRPC) String() string { return proto.CompactTextString(m) }
func (*ContactUpdateRPC) ProtoMessage() {}
func (*ContactUpdateRPC) Descriptor() ([]byte, []int) {
return fileDescriptor_8c585a45e2093e54, []int{3}
}
func (m *ContactUpdateRPC) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ContactUpdateRPC.Unmarshal(m, b)
}
func (m *ContactUpdateRPC) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ContactUpdateRPC.Marshal(b, m, deterministic)
}
func (m *ContactUpdateRPC) XXX_Merge(src proto.Message) {
xxx_messageInfo_ContactUpdateRPC.Merge(m, src)
}
func (m *ContactUpdateRPC) XXX_Size() int {
return xxx_messageInfo_ContactUpdateRPC.Size(m)
}
func (m *ContactUpdateRPC) XXX_DiscardUnknown() {
xxx_messageInfo_ContactUpdateRPC.DiscardUnknown(m)
}
var xxx_messageInfo_ContactUpdateRPC proto.InternalMessageInfo
func (m *ContactUpdateRPC) GetSrc() string {
if m != nil {
return m.Src
}
return ""
}
func (m *ContactUpdateRPC) GetDst() string {
if m != nil {
return m.Dst
}
return ""
}
func (m *ContactUpdateRPC) GetPayload() *ContactUpdatePayload {
if m != nil {
return m.Payload
}
return nil
}
// Incoming messages
type ChatProtocolMessage struct {
Payload []byte `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ChatProtocolMessage) Reset() { *m = ChatProtocolMessage{} }
func (m *ChatProtocolMessage) String() string { return proto.CompactTextString(m) }
func (*ChatProtocolMessage) ProtoMessage() {}
func (*ChatProtocolMessage) Descriptor() ([]byte, []int) {
return fileDescriptor_8c585a45e2093e54, []int{4}
}
func (m *ChatProtocolMessage) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChatProtocolMessage.Unmarshal(m, b)
}
func (m *ChatProtocolMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ChatProtocolMessage.Marshal(b, m, deterministic)
}
func (m *ChatProtocolMessage) XXX_Merge(src proto.Message) {
xxx_messageInfo_ChatProtocolMessage.Merge(m, src)
}
func (m *ChatProtocolMessage) XXX_Size() int {
return xxx_messageInfo_ChatProtocolMessage.Size(m)
}
func (m *ChatProtocolMessage) XXX_DiscardUnknown() {
xxx_messageInfo_ChatProtocolMessage.DiscardUnknown(m)
}
var xxx_messageInfo_ChatProtocolMessage proto.InternalMessageInfo
func (m *ChatProtocolMessage) GetPayload() []byte {
if m != nil {
return m.Payload
}
return nil
}
func init() {
proto.RegisterType((*ChatMessagePayload)(nil), "chat.ChatMessagePayload")
proto.RegisterType((*ContactUpdatePayload)(nil), "chat.ContactUpdatePayload")
proto.RegisterType((*OneToOneRPC)(nil), "chat.OneToOneRPC")
proto.RegisterType((*ContactUpdateRPC)(nil), "chat.ContactUpdateRPC")
proto.RegisterType((*ChatProtocolMessage)(nil), "chat.ChatProtocolMessage")
}
func init() { proto.RegisterFile("chat.proto", fileDescriptor_8c585a45e2093e54) }
var fileDescriptor_8c585a45e2093e54 = []byte{
// 314 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xcf, 0x4a, 0xc3, 0x40,
0x10, 0xc6, 0x59, 0x5b, 0xd4, 0x4e, 0x2a, 0x94, 0xd5, 0x43, 0xd0, 0x83, 0x35, 0x5e, 0x7a, 0xaa,
0xa0, 0xbe, 0x41, 0x4f, 0x22, 0xd2, 0x12, 0xaa, 0xd7, 0xb0, 0x6e, 0xa6, 0x7f, 0xe8, 0x66, 0x77,
0xc9, 0xae, 0x42, 0x5f, 0xc0, 0x37, 0xf0, 0x7d, 0x65, 0x36, 0x1b, 0x35, 0xe0, 0xc1, 0xdb, 0x37,
0x5f, 0x86, 0xf9, 0x7e, 0x93, 0x59, 0x00, 0xb9, 0x11, 0x7e, 0x6a, 0x6b, 0xe3, 0x0d, 0xef, 0x93,
0xce, 0x3e, 0x19, 0xf0, 0xd9, 0x46, 0xf8, 0x27, 0x74, 0x4e, 0xac, 0x71, 0x21, 0xf6, 0xca, 0x88,
0x92, 0xa7, 0x70, 0x24, 0x8d, 0xf6, 0xa8, 0x7d, 0xca, 0xc6, 0x6c, 0x32, 0xc8, 0xdb, 0x92, 0x5f,
0xc1, 0x30, 0xca, 0xc2, 0xef, 0x2d, 0xa6, 0x07, 0xe1, 0x73, 0x12, 0xbd, 0xe5, 0xde, 0x22, 0xb5,
0x54, 0xcd, 0xb8, 0xa6, 0xa5, 0xd7, 0xb4, 0x44, 0x2f, 0xb4, 0x5c, 0x42, 0x22, 0x95, 0x91, 0xbb,
0xe2, 0x5d, 0xa8, 0x37, 0x4c, 0xfb, 0x63, 0x36, 0x61, 0x39, 0x04, 0xeb, 0x85, 0x9c, 0xec, 0x83,
0xc1, 0xd9, 0xcc, 0x68, 0x2f, 0xa4, 0x7f, 0xb6, 0xa5, 0xf0, 0xdf, 0x64, 0x1c, 0xfa, 0x5a, 0x54,
0x18, 0xb1, 0x82, 0xe6, 0xd7, 0x70, 0x62, 0x6b, 0xb3, 0xda, 0x2a, 0x2c, 0xb6, 0x95, 0x58, 0xb7,
0x50, 0xc3, 0x68, 0x3e, 0x90, 0x47, 0x2b, 0x89, 0xb2, 0xac, 0xd1, 0xb9, 0x08, 0xd4, 0x96, 0xfc,
0x02, 0x06, 0x2b, 0x59, 0x15, 0xde, 0xec, 0x50, 0x07, 0x94, 0x41, 0x7e, 0xbc, 0x92, 0xd5, 0x92,
0xea, 0xec, 0x11, 0x92, 0xb9, 0xc6, 0xa5, 0x99, 0x6b, 0xcc, 0x17, 0x33, 0x3e, 0x82, 0x9e, 0xab,
0x65, 0x4c, 0x27, 0x49, 0x4e, 0xe9, 0x7c, 0x8c, 0x24, 0x49, 0x49, 0xb6, 0xa1, 0x0d, 0x49, 0xc3,
0xbc, 0x2d, 0x33, 0x05, 0xa3, 0xce, 0x52, 0xff, 0x9d, 0x78, 0xdf, 0x9d, 0x98, 0xdc, 0x9e, 0x4f,
0xc3, 0x25, 0xff, 0xfa, 0x43, 0x3f, 0x69, 0x37, 0x70, 0x4a, 0xa7, 0x5d, 0xd0, 0xb9, 0xa5, 0x51,
0xf1, 0xc4, 0xbf, 0xf1, 0x58, 0x07, 0xef, 0xf5, 0x30, 0xbc, 0x8c, 0xbb, 0xaf, 0x00, 0x00, 0x00,
0xff, 0xff, 0xad, 0x31, 0x5d, 0x6c, 0x27, 0x02, 0x00, 0x00,
}

View File

@ -12,9 +12,9 @@ import (
"github.com/ethereum/go-ethereum/log"
dr "github.com/status-im/doubleratchet"
"github.com/status-im/status-go/messaging/chat/crypto"
"github.com/status-im/status-go/messaging/chat/multidevice"
"github.com/status-im/status-go/messaging/chat/protobuf"
"github.com/status-im/status-go/messaging/crypto"
"github.com/status-im/status-go/messaging/multidevice"
)
var (
@ -38,7 +38,7 @@ type ConfirmationData struct {
// EncryptionService defines a service that is responsible for the encryption aspect of the protocol.
type EncryptionService struct {
log log.Logger
persistence PersistenceService
persistence Persistence
config EncryptionServiceConfig
messageIDs map[string]*ConfirmationData
mutex sync.Mutex
@ -71,7 +71,7 @@ func DefaultEncryptionServiceConfig(installationID string) EncryptionServiceConf
}
// NewEncryptionService creates a new EncryptionService instance.
func NewEncryptionService(p PersistenceService, config EncryptionServiceConfig) *EncryptionService {
func NewEncryptionService(p Persistence, config EncryptionServiceConfig) *EncryptionService {
logger := log.New("package", "status-go/services/sshext.chat")
logger.Info("Initialized encryption service", "installationID", config.InstallationID)
return &EncryptionService{

View File

@ -8,8 +8,8 @@ import (
"os"
"testing"
"github.com/status-im/status-go/messaging/chat/multidevice"
"github.com/status-im/status-go/messaging/chat/sharedsecret"
"github.com/status-im/status-go/messaging/multidevice"
"github.com/status-im/status-go/messaging/sharedsecret"
)
const (

View File

@ -13,9 +13,9 @@ import (
"time"
"github.com/ethereum/go-ethereum/crypto"
"github.com/status-im/status-go/messaging/chat/multidevice"
"github.com/status-im/status-go/messaging/chat/protobuf"
"github.com/status-im/status-go/messaging/chat/sharedsecret"
"github.com/status-im/status-go/messaging/multidevice"
"github.com/status-im/status-go/messaging/sharedsecret"
"github.com/stretchr/testify/suite"
)

View File

@ -4,8 +4,8 @@ import (
"crypto/ecdsa"
dr "github.com/status-im/doubleratchet"
"github.com/status-im/status-go/messaging/chat/multidevice"
"github.com/status-im/status-go/messaging/chat/protobuf"
"github.com/status-im/status-go/messaging/multidevice"
)
// RatchetInfo holds the current ratchet state
@ -20,8 +20,8 @@ type RatchetInfo struct {
InstallationID string
}
// PersistenceService defines the interface for a storage service
type PersistenceService interface {
// Persistence defines the interface for a storage service
type Persistence interface {
// GetKeysStorage returns the associated double ratchet KeysStorage object.
GetKeysStorage() dr.KeysStorage
// GetSessionStorage returns the associated double ratchet SessionStorage object.

View File

@ -3,11 +3,14 @@ package chat
import (
"crypto/ecdsa"
"errors"
"fmt"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/status-im/status-go/messaging/chat/multidevice"
"github.com/status-im/status-go/messaging/chat/protobuf"
"github.com/status-im/status-go/messaging/chat/sharedsecret"
"github.com/status-im/status-go/messaging/multidevice"
"github.com/status-im/status-go/messaging/sharedsecret"
)
const ProtocolVersion = 1
@ -197,17 +200,52 @@ func (p *ProtocolService) BuildDHMessage(myIdentityKey *ecdsa.PrivateKey, destin
// ProcessPublicBundle processes a received X3DH bundle.
func (p *ProtocolService) ProcessPublicBundle(myIdentityKey *ecdsa.PrivateKey, bundle *protobuf.Bundle) ([]*multidevice.Installation, error) {
p.log.Debug("Processing bundle", "bundle", bundle)
if err := p.encryption.ProcessPublicBundle(myIdentityKey, bundle); err != nil {
return nil, err
}
theirIdentityKey, err := ExtractIdentity(bundle)
p.log.Debug("Processing bundle", "bundle", bundle)
installations, fromOurs, err := p.recoverInstallationsFromBundle(myIdentityKey, bundle)
if err != nil {
return nil, err
}
return p.multidevice.ProcessPublicBundle(myIdentityKey, theirIdentityKey, bundle)
// TODO(adam): why do we add installations using identity obtained from GetIdentity()
// instead of the output of crypto.CompressPubkey()? I tried the second option
// and the unit tests TestTopic and TestMaxDevices fail.
return p.multidevice.AddInstallations(bundle.GetIdentity(), bundle.GetTimestamp(), installations, fromOurs)
}
// recoverInstallationsFromBundle extracts installations from the bundle.
// It returns extracted installations and true if the installations
// are ours, i.e. the bundle was created by our identity key.
func (p *ProtocolService) recoverInstallationsFromBundle(myIdentityKey *ecdsa.PrivateKey, bundle *protobuf.Bundle) ([]*multidevice.Installation, bool, error) {
var installations []*multidevice.Installation
theirIdentity, err := ExtractIdentity(bundle)
if err != nil {
return nil, false, err
}
myIdentityStr := fmt.Sprintf("0x%x", crypto.FromECDSAPub(&myIdentityKey.PublicKey))
theirIdentityStr := fmt.Sprintf("0x%x", crypto.FromECDSAPub(theirIdentity))
// Any device from other peers will be considered enabled, ours needs to
// be explicitly enabled
fromOurIdentity := theirIdentityStr != myIdentityStr
signedPreKeys := bundle.GetSignedPreKeys()
for installationID, signedPreKey := range signedPreKeys {
if installationID != p.multidevice.InstallationID() {
installations = append(installations, &multidevice.Installation{
Identity: theirIdentityStr,
ID: installationID,
Version: signedPreKey.GetProtocolVersion(),
})
}
}
return installations, fromOurIdentity, nil
}
// GetBundle retrieves or creates a X3DH bundle, given a private identity key.

View File

@ -5,8 +5,8 @@ import (
"testing"
"github.com/ethereum/go-ethereum/crypto"
"github.com/status-im/status-go/messaging/chat/multidevice"
"github.com/status-im/status-go/messaging/chat/sharedsecret"
"github.com/status-im/status-go/messaging/multidevice"
"github.com/status-im/status-go/messaging/sharedsecret"
"github.com/stretchr/testify/suite"
)

View File

@ -6,13 +6,13 @@ import (
"strings"
"github.com/ethereum/go-ethereum/crypto"
dr "github.com/status-im/doubleratchet"
ecrypto "github.com/status-im/status-go/messaging/chat/crypto"
chatDB "github.com/status-im/status-go/messaging/chat/db"
"github.com/status-im/status-go/messaging/chat/multidevice"
"github.com/status-im/status-go/messaging/chat/protobuf"
"github.com/status-im/status-go/messaging/chat/sharedsecret"
ecrypto "github.com/status-im/status-go/messaging/crypto"
msgdb "github.com/status-im/status-go/messaging/db"
"github.com/status-im/status-go/messaging/multidevice"
"github.com/status-im/status-go/messaging/sharedsecret"
)
// A safe max number of rows
@ -23,7 +23,7 @@ type SQLLitePersistence struct {
DB *sql.DB
keysStorage dr.KeysStorage
sessionStorage dr.SessionStorage
secretStorage sharedsecret.PersistenceService
secretStorage sharedsecret.Persistence
multideviceStorage multidevice.Persistence
}
@ -81,7 +81,7 @@ func (s *SQLLitePersistence) GetSessionStorage() dr.SessionStorage {
}
// GetSharedSecretStorage returns the associated secretStorageObject
func (s *SQLLitePersistence) GetSharedSecretStorage() sharedsecret.PersistenceService {
func (s *SQLLitePersistence) GetSharedSecretStorage() sharedsecret.Persistence {
return s.secretStorage
}
@ -92,7 +92,7 @@ func (s *SQLLitePersistence) GetMultideviceStorage() multidevice.Persistence {
// Open opens a file at the specified path
func (s *SQLLitePersistence) Open(path string, key string) error {
db, err := chatDB.Open(path, key, chatDB.KdfIterationsNumber)
db, err := msgdb.Open(path, key, msgdb.KdfIterationsNumber)
if err != nil {
return err
}

View File

@ -6,7 +6,7 @@ import (
"testing"
"github.com/ethereum/go-ethereum/crypto"
"github.com/status-im/status-go/messaging/chat/multidevice"
"github.com/status-im/status-go/messaging/multidevice"
"github.com/stretchr/testify/suite"
)
@ -23,7 +23,7 @@ type SQLLitePersistenceTestSuite struct {
suite.Suite
// nolint: structcheck, megacheck
db *sql.DB
service PersistenceService
service Persistence
}
func (s *SQLLitePersistenceTestSuite) SetupTest() {

View File

@ -9,9 +9,8 @@ import (
"fmt"
"io"
"github.com/ethereum/go-ethereum/crypto/ecies"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/ecies"
dr "github.com/status-im/doubleratchet"
"golang.org/x/crypto/hkdf"
)

View File

@ -9,7 +9,7 @@ import (
"github.com/status-im/migrate/v4"
"github.com/status-im/migrate/v4/database/sqlcipher"
"github.com/status-im/migrate/v4/source/go_bindata"
"github.com/status-im/status-go/messaging/chat/db/migrations"
"github.com/status-im/status-go/messaging/db/migrations"
)
const exportDB = "SELECT sqlcipher_export('newdb')"

View File

@ -7,7 +7,7 @@ import (
"fmt"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/status-im/status-go/messaging/chat/sharedsecret"
"github.com/status-im/status-go/messaging/sharedsecret"
whisper "github.com/status-im/whisper/whisperv6"
"math/big"
"sync"

View File

@ -8,10 +8,11 @@ import (
"testing"
"github.com/ethereum/go-ethereum/crypto"
chatDB "github.com/status-im/status-go/messaging/chat/db"
"github.com/status-im/status-go/messaging/chat/sharedsecret"
whisper "github.com/status-im/whisper/whisperv6"
"github.com/stretchr/testify/suite"
msgdb "github.com/status-im/status-go/messaging/db"
"github.com/status-im/status-go/messaging/sharedsecret"
)
func TestServiceTestSuite(t *testing.T) {
@ -63,7 +64,7 @@ func (s *ServiceTestSuite) SetupTest() {
s.keys = append(s.keys, testKey)
}
db, err := chatDB.Open(s.path, "", 0)
db, err := msgdb.Open(s.path, "", 0)
s.Require().NoError(err)
// Build services

View File

@ -2,9 +2,8 @@ package multidevice
import (
"crypto/ecdsa"
"fmt"
"github.com/ethereum/go-ethereum/crypto"
"github.com/status-im/status-go/messaging/chat/protobuf"
)
type InstallationMetadata struct {
@ -49,6 +48,10 @@ type Service struct {
config *Config
}
func (s *Service) InstallationID() string {
return s.config.InstallationID
}
func (s *Service) GetActiveInstallations(identity *ecdsa.PublicKey) ([]*Installation, error) {
identityC := crypto.CompressPubkey(identity)
return s.persistence.GetActiveInstallations(s.config.MaxInstallations, identityC)
@ -96,6 +99,10 @@ func (s *Service) GetOurInstallations(identity *ecdsa.PublicKey) ([]*Installatio
return installations, nil
}
func (s *Service) AddInstallations(identity []byte, timestamp int64, installations []*Installation, defaultEnabled bool) ([]*Installation, error) {
return s.persistence.AddInstallations(identity, timestamp, installations, defaultEnabled)
}
func (s *Service) SetInstallationMetadata(identity *ecdsa.PublicKey, installationID string, metadata *InstallationMetadata) error {
identityC := crypto.CompressPubkey(identity)
return s.persistence.SetInstallationMetadata(identityC, installationID, metadata)
@ -110,28 +117,3 @@ func (s *Service) DisableInstallation(myIdentityKey *ecdsa.PublicKey, installati
myIdentityKeyC := crypto.CompressPubkey(myIdentityKey)
return s.persistence.DisableInstallation(myIdentityKeyC, installationID)
}
// ProcessPublicBundle persists a bundle and returns a list of tuples identity/installationID
func (s *Service) ProcessPublicBundle(myIdentityKey *ecdsa.PrivateKey, theirIdentity *ecdsa.PublicKey, b *protobuf.Bundle) ([]*Installation, error) {
signedPreKeys := b.GetSignedPreKeys()
var installations []*Installation
myIdentityStr := fmt.Sprintf("0x%x", crypto.FromECDSAPub(&myIdentityKey.PublicKey))
theirIdentityStr := fmt.Sprintf("0x%x", crypto.FromECDSAPub(theirIdentity))
// Any device from other peers will be considered enabled, ours needs to
// be explicitly enabled
fromOurIdentity := theirIdentityStr != myIdentityStr
for installationID, signedPreKey := range signedPreKeys {
if installationID != s.config.InstallationID {
installations = append(installations, &Installation{
Identity: theirIdentityStr,
ID: installationID,
Version: signedPreKey.GetProtocolVersion(),
})
}
}
return s.persistence.AddInstallations(b.GetIdentity(), b.GetTimestamp(), installations, fromOurIdentity)
}

View File

@ -5,7 +5,7 @@ import (
"os"
"testing"
chatDB "github.com/status-im/status-go/messaging/chat/db"
msgdb "github.com/status-im/status-go/messaging/db"
"github.com/stretchr/testify/suite"
)
@ -27,7 +27,7 @@ type SQLLitePersistenceTestSuite struct {
func (s *SQLLitePersistenceTestSuite) SetupTest() {
os.Remove(dbPath)
db, err := chatDB.Open(dbPath, "", 0)
db, err := msgdb.Open(dbPath, "", 0)
s.Require().NoError(err)
s.service = NewSQLLitePersistence(db)

View File

@ -13,9 +13,9 @@ import (
"github.com/ethereum/go-ethereum/log"
"github.com/status-im/status-go/messaging/chat"
"github.com/status-im/status-go/messaging/chat/multidevice"
"github.com/status-im/status-go/messaging/chat/protobuf"
"github.com/status-im/status-go/messaging/filter"
"github.com/status-im/status-go/messaging/multidevice"
"github.com/status-im/status-go/services/shhext/whisperutils"
@ -62,7 +62,7 @@ func New(w *whisper.Whisper, c Config) *Publisher {
config: c,
whisper: w,
whisperAPI: whisper.NewPublicWhisperAPI(w),
log: log.New("package", "status-go/services/publisher.Publisher"),
log: log.New("package", "status-go/messaging/publisher.Publisher"),
}
}
@ -242,22 +242,15 @@ func (p *Publisher) ProcessMessage(msg *whisper.Message, msgID []byte) error {
}
// CreateDirectMessage creates a 1:1 chat message
func (p *Publisher) CreateDirectMessage(signature string, destination hexutil.Bytes, DH bool, payload []byte) (*whisper.NewMessage, error) {
func (p *Publisher) CreateDirectMessage(privateKey *ecdsa.PrivateKey, publicKey *ecdsa.PublicKey, DH bool, payload []byte) (*whisper.NewMessage, error) {
if !p.config.PFSEnabled {
return nil, ErrPFSNotEnabled
}
privateKey, err := p.whisper.GetPrivateKey(signature)
if err != nil {
return nil, err
}
publicKey, err := crypto.UnmarshalPubkey(destination)
if err != nil {
return nil, err
}
var msgSpec *chat.ProtocolMessageSpec
var (
msgSpec *chat.ProtocolMessageSpec
err error
)
if DH {
p.log.Debug("Building dh message")
@ -270,7 +263,7 @@ func (p *Publisher) CreateDirectMessage(signature string, destination hexutil.By
return nil, err
}
whisperMessage, err := p.directMessageToWhisper(privateKey, publicKey, destination, signature, msgSpec)
whisperMessage, err := p.directMessageToWhisper(privateKey, publicKey, msgSpec)
if err != nil {
p.log.Error("sshext-service", "error building whisper message", err)
return nil, err
@ -279,7 +272,7 @@ func (p *Publisher) CreateDirectMessage(signature string, destination hexutil.By
return whisperMessage, nil
}
func (p *Publisher) directMessageToWhisper(myPrivateKey *ecdsa.PrivateKey, theirPublicKey *ecdsa.PublicKey, destination hexutil.Bytes, signature string, spec *chat.ProtocolMessageSpec) (*whisper.NewMessage, error) {
func (p *Publisher) directMessageToWhisper(myPrivateKey *ecdsa.PrivateKey, theirPublicKey *ecdsa.PublicKey, spec *chat.ProtocolMessageSpec) (*whisper.NewMessage, error) {
// marshal for sending to wire
marshaledMessage, err := proto.Marshal(spec.Message)
if err != nil {
@ -287,9 +280,18 @@ func (p *Publisher) directMessageToWhisper(myPrivateKey *ecdsa.PrivateKey, their
return nil, err
}
// We rely on the fact that a deterministic ID is created for the same keys.
sigID, err := p.whisper.AddKeyPair(myPrivateKey)
if err != nil {
p.log.Error("failed to add key pair in order to get signature ID", "err", err)
return nil, err
}
destination := hexutil.Bytes(crypto.FromECDSAPub(theirPublicKey))
whisperMessage := whisperutils.DefaultWhisperMessage()
whisperMessage.Payload = marshaledMessage
whisperMessage.Sig = signature
whisperMessage.Sig = sigID
if spec.SharedSecret != nil {
chat := p.getNegotiatedChat(theirPublicKey)
@ -320,7 +322,7 @@ func (p *Publisher) directMessageToWhisper(myPrivateKey *ecdsa.PrivateKey, their
}
// CreatePublicMessage sends a public chat message to the underlying transport
func (p *Publisher) CreatePublicMessage(signature string, chatID string, payload []byte, wrap bool) (*whisper.NewMessage, error) {
func (p *Publisher) CreatePublicMessage(privateKey *ecdsa.PrivateKey, chatID string, payload []byte, wrap bool) (*whisper.NewMessage, error) {
if !p.config.PFSEnabled {
return nil, ErrPFSNotEnabled
}
@ -329,11 +331,17 @@ func (p *Publisher) CreatePublicMessage(signature string, chatID string, payload
if filter == nil {
return nil, errors.New("not subscribed to chat")
}
p.log.Info("SIG", signature)
sigID, err := p.whisper.AddKeyPair(privateKey)
if err != nil {
return nil, fmt.Errorf("failed to get a signature ID: %v", err)
}
p.log.Info("signature ID", sigID)
// Enrich with transport layer info
whisperMessage := whisperutils.DefaultWhisperMessage()
whisperMessage.Sig = signature
whisperMessage.Sig = sigID
whisperMessage.Topic = whisperutils.ToTopic(chatID)
whisperMessage.SymKeyID = filter.SymKeyID
@ -426,7 +434,7 @@ func (p *Publisher) sendContactCode() (*whisper.NewMessage, error) {
identity := fmt.Sprintf("%x", crypto.FromECDSAPub(&privateKey.PublicKey))
message, err := p.CreatePublicMessage("0x"+identity, filter.ContactCodeTopic(identity), nil, true)
message, err := p.CreatePublicMessage(privateKey, filter.ContactCodeTopic(identity), nil, true)
if err != nil {
p.log.Error("could not build contact code", "identity", identity, "err", err)
return nil, err

View File

@ -11,9 +11,9 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/ecies"
"github.com/status-im/status-go/messaging/chat"
"github.com/status-im/status-go/messaging/chat/multidevice"
"github.com/status-im/status-go/messaging/chat/sharedsecret"
"github.com/status-im/status-go/messaging/filter"
"github.com/status-im/status-go/messaging/multidevice"
"github.com/status-im/status-go/messaging/sharedsecret"
"github.com/status-im/status-go/services/shhext/whisperutils"
whisper "github.com/status-im/whisper/whisperv6"
"github.com/stretchr/testify/suite"
@ -108,7 +108,7 @@ func (s *ServiceTestSuite) SetupTest() {
}
func (s *ServiceTestSuite) TestCreateDirectMessage() {
newMessage, err := s.alice.CreateDirectMessage(s.aliceKey.keyID, s.bobKey.publicKeyBytes, false, []byte("hello"))
newMessage, err := s.alice.CreateDirectMessage(s.aliceKey.privateKey, &s.bobKey.privateKey.PublicKey, false, []byte("hello"))
s.Require().NoError(err)
message := &whisper.Message{
@ -125,7 +125,7 @@ func (s *ServiceTestSuite) TestCreateDirectMessage() {
func (s *ServiceTestSuite) TestTopic() {
// We build an initial message
newMessage1, err := s.alice.CreateDirectMessage(s.aliceKey.keyID, s.bobKey.publicKeyBytes, false, []byte("hello"))
newMessage1, err := s.alice.CreateDirectMessage(s.aliceKey.privateKey, &s.bobKey.privateKey.PublicKey, false, []byte("hello"))
s.Require().NoError(err)
message1 := &whisper.Message{
@ -155,7 +155,7 @@ func (s *ServiceTestSuite) TestTopic() {
s.Require().EqualError(err, chat.ErrNoPayload.Error())
// We build another message, this time it should use the partitioned topic
newMessage3, err := s.alice.CreateDirectMessage(s.aliceKey.keyID, s.bobKey.publicKeyBytes, false, []byte("hello"))
newMessage3, err := s.alice.CreateDirectMessage(s.aliceKey.privateKey, &s.bobKey.privateKey.PublicKey, false, []byte("hello"))
s.Require().NoError(err)
message3 := &whisper.Message{
@ -173,7 +173,7 @@ func (s *ServiceTestSuite) TestTopic() {
s.Require().NoError(err)
// We build another message, this time it should use the negotiated topic
newMessage4, err := s.bob.CreateDirectMessage(s.bobKey.keyID, s.aliceKey.publicKeyBytes, false, []byte("hello"))
newMessage4, err := s.bob.CreateDirectMessage(s.bobKey.privateKey, &s.aliceKey.privateKey.PublicKey, false, []byte("hello"))
s.Require().NoError(err)
message4 := &whisper.Message{
@ -198,7 +198,7 @@ func (s *ServiceTestSuite) TestTopic() {
s.Require().NoError(err)
// Alice sends another message to Bob, this time it should use the negotiated topic
newMessage5, err := s.alice.CreateDirectMessage(s.aliceKey.keyID, s.bobKey.publicKeyBytes, false, []byte("hello"))
newMessage5, err := s.alice.CreateDirectMessage(s.aliceKey.privateKey, &s.bobKey.privateKey.PublicKey, false, []byte("hello"))
s.Require().NoError(err)
message5 := &whisper.Message{

View File

@ -5,7 +5,7 @@ import (
"strings"
)
type PersistenceService interface {
type Persistence interface {
// Add adds a shared secret, associated with an identity and an installationID
Add(identity []byte, secret []byte, installationID string) error
// Get returns a shared secret associated with multiple installationIDs

View File

@ -2,6 +2,7 @@ package sharedsecret
import (
"crypto/ecdsa"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/ecies"
"github.com/ethereum/go-ethereum/log"
@ -11,12 +12,12 @@ const sskLen = 16
type Service struct {
log log.Logger
persistence PersistenceService
persistence Persistence
}
func NewService(persistence PersistenceService) *Service {
func NewService(persistence Persistence) *Service {
return &Service{
log: log.New("package", "status-go/services/sshext/chat.sharedsecret"),
log: log.New("package", "status-go/messaging/sharedsecret.Service"),
persistence: persistence,
}
}

View File

@ -6,7 +6,7 @@ import (
"testing"
"github.com/ethereum/go-ethereum/crypto"
chatDB "github.com/status-im/status-go/messaging/chat/db"
msgdb "github.com/status-im/status-go/messaging/db"
"github.com/stretchr/testify/suite"
)
@ -25,7 +25,7 @@ func (s *ServiceTestSuite) SetupTest() {
s.Require().NoError(err)
s.path = dbFile.Name()
db, err := chatDB.Open(s.path, "", 0)
db, err := msgdb.Open(s.path, "", 0)
s.Require().NoError(err)

View File

@ -19,8 +19,8 @@ import (
"github.com/status-im/status-go/db"
"github.com/status-im/status-go/mailserver"
"github.com/status-im/status-go/messaging/chat"
"github.com/status-im/status-go/messaging/chat/multidevice"
"github.com/status-im/status-go/messaging/filter"
"github.com/status-im/status-go/messaging/multidevice"
"github.com/status-im/status-go/services/shhext/dedup"
"github.com/status-im/status-go/services/shhext/mailservers"
whisper "github.com/status-im/whisper/whisperv6"
@ -477,8 +477,13 @@ func (api *PublicAPI) ConfirmMessagesProcessedByID(messageIDs [][]byte) error {
}
// SendPublicMessage sends a public chat message to the underlying transport
func (api *PublicAPI) SendPublicMessage(ctx context.Context, msg chat.SendPublicMessageRPC) (hexutil.Bytes, error) {
message, err := api.service.CreatePublicMessage(msg.Sig, msg.Chat, msg.Payload, false)
func (api *PublicAPI) SendPublicMessage(ctx context.Context, msg SendPublicMessageRPC) (hexutil.Bytes, error) {
privateKey, err := api.service.w.GetPrivateKey(msg.Sig)
if err != nil {
return nil, fmt.Errorf("failed to obtain a private key from Sig: %v", err)
}
message, err := api.service.CreatePublicMessage(privateKey, msg.Chat, msg.Payload, false)
if err != nil {
return nil, err
}
@ -487,8 +492,18 @@ 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) {
message, err := api.service.CreateDirectMessage(msg.Sig, msg.PubKey, msg.DH, msg.Payload)
func (api *PublicAPI) SendDirectMessage(ctx context.Context, msg SendDirectMessageRPC) (hexutil.Bytes, error) {
privateKey, err := api.service.w.GetPrivateKey(msg.Sig)
if err != nil {
return nil, err
}
publicKey, err := crypto.UnmarshalPubkey(msg.PubKey)
if err != nil {
return nil, err
}
message, err := api.service.CreateDirectMessage(privateKey, publicKey, msg.DH, msg.Payload)
if err != nil {
return nil, err
}

View File

@ -1,7 +1,7 @@
// TODO: These types should be defined using protobuf, but protoc can only emit []byte instead of hexutil.Bytes,
// which causes issues when marshalong to JSON on the react side. Let's do that once the chat protocol is moved to the go repo.
package chat
package shhext
import (
"github.com/ethereum/go-ethereum/common/hexutil"

View File

@ -17,11 +17,11 @@ import (
"github.com/status-im/status-go/db"
"github.com/status-im/status-go/messaging/chat"
chatDB "github.com/status-im/status-go/messaging/chat/db"
"github.com/status-im/status-go/messaging/chat/multidevice"
"github.com/status-im/status-go/messaging/chat/sharedsecret"
msgdb "github.com/status-im/status-go/messaging/db"
"github.com/status-im/status-go/messaging/filter"
"github.com/status-im/status-go/messaging/multidevice"
"github.com/status-im/status-go/messaging/publisher"
"github.com/status-im/status-go/messaging/sharedsecret"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/services/shhext/dedup"
"github.com/status-im/status-go/services/shhext/mailservers"
@ -133,11 +133,11 @@ func (s *Service) initProtocol(address, encKey, password string) error {
v4Path := filepath.Join(dataDir, fmt.Sprintf("%s.v4.db", s.config.InstallationID))
if password != "" {
if err := chatDB.MigrateDBFile(v0Path, v1Path, "ON", password); err != nil {
if err := msgdb.MigrateDBFile(v0Path, v1Path, "ON", password); err != nil {
return err
}
if err := chatDB.MigrateDBFile(v1Path, v2Path, password, encKey); err != nil {
if err := msgdb.MigrateDBFile(v1Path, v2Path, password, encKey); err != nil {
// Remove db file as created with a blank password and never used,
// and there's no need to rekey in this case
os.Remove(v1Path)
@ -145,13 +145,13 @@ func (s *Service) initProtocol(address, encKey, password string) error {
}
}
if err := chatDB.MigrateDBKeyKdfIterations(v2Path, v3Path, encKey); err != nil {
if err := msgdb.MigrateDBKeyKdfIterations(v2Path, v3Path, encKey); err != nil {
os.Remove(v2Path)
os.Remove(v3Path)
}
// Fix IOS not encrypting database
if err := chatDB.EncryptDatabase(v3Path, v4Path, encKey); err != nil {
if err := msgdb.EncryptDatabase(v3Path, v4Path, encKey); err != nil {
os.Remove(v3Path)
os.Remove(v4Path)
}

View File

@ -1,4 +1,4 @@
// Package static embeds static (JS, HTML) resources right into the binaries
package static
//go:generate go-bindata -pkg migrations -o ../../messaging/chat/db/migrations/bindata.go .
//go:generate go-bindata -pkg migrations -o ../../messaging/db/migrations/bindata.go .