Add bundles.added signal & pairing endpoint (#1237)
This commit is contained in:
parent
659d79277b
commit
e4ba365b8a
|
@ -483,7 +483,7 @@ func (b *StatusBackend) ProcessContactCode(contactCode string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if err := st.ProcessPublicBundle(selectedAccount.AccountKey.PrivateKey, bundle); err != nil {
|
||||
if _, err := st.ProcessPublicBundle(selectedAccount.AccountKey.PrivateKey, bundle); err != nil {
|
||||
b.log.Error("error adding bundle", "err", err)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -237,11 +237,11 @@ func (api *PublicAPI) SendPublicMessage(ctx context.Context, msg chat.SendPublic
|
|||
}
|
||||
|
||||
// Enrich with transport layer info
|
||||
whisperMessage := chat.PublicMessageToWhisper(&msg, protocolMessage)
|
||||
whisperMessage := chat.PublicMessageToWhisper(msg, protocolMessage)
|
||||
whisperMessage.SymKeyID = symKeyID
|
||||
|
||||
// And dispatch
|
||||
return api.Post(ctx, *whisperMessage)
|
||||
return api.Post(ctx, whisperMessage)
|
||||
}
|
||||
|
||||
// SendDirectMessage sends a 1:1 chat message to the underlying transport
|
||||
|
@ -272,10 +272,10 @@ func (api *PublicAPI) SendDirectMessage(ctx context.Context, msg chat.SendDirect
|
|||
for key, message := range protocolMessages {
|
||||
msg.PubKey = crypto.FromECDSAPub(key)
|
||||
// Enrich with transport layer info
|
||||
whisperMessage := chat.DirectMessageToWhisper(&msg, message)
|
||||
whisperMessage := chat.DirectMessageToWhisper(msg, message)
|
||||
|
||||
// And dispatch
|
||||
hash, err := api.Post(ctx, *whisperMessage)
|
||||
hash, err := api.Post(ctx, whisperMessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -285,6 +285,42 @@ func (api *PublicAPI) SendDirectMessage(ctx context.Context, msg chat.SendDirect
|
|||
return response, nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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
|
||||
}
|
||||
|
||||
msg.PubKey = crypto.FromECDSAPub(&privateKey.PublicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
protocolMessage, err := api.service.protocol.BuildPairingMessage(privateKey, msg.Payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var response []hexutil.Bytes
|
||||
|
||||
// Enrich with transport layer info
|
||||
whisperMessage := chat.DirectMessageToWhisper(msg, protocolMessage)
|
||||
|
||||
// And dispatch
|
||||
hash, err := api.Post(ctx, whisperMessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response = append(response, hash)
|
||||
|
||||
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 {
|
||||
|
@ -323,10 +359,10 @@ func (api *PublicAPI) SendGroupMessage(ctx context.Context, msg chat.SendGroupMe
|
|||
}
|
||||
|
||||
// Enrich with transport layer info
|
||||
whisperMessage := chat.DirectMessageToWhisper(&directMessage, message)
|
||||
whisperMessage := chat.DirectMessageToWhisper(directMessage, message)
|
||||
|
||||
// And dispatch
|
||||
hash, err := api.Post(ctx, *whisperMessage)
|
||||
hash, err := api.Post(ctx, whisperMessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -360,11 +396,9 @@ func (api *PublicAPI) processPFSMessage(msg *whisper.Message) error {
|
|||
}
|
||||
}
|
||||
|
||||
payload, err := api.service.protocol.HandleMessage(privateKey, publicKey, msg.Payload)
|
||||
response, err := api.service.protocol.HandleMessage(privateKey, publicKey, msg.Payload)
|
||||
|
||||
if err != nil {
|
||||
api.log.Error("Failed handling message with error", "err", err)
|
||||
}
|
||||
handler := EnvelopeSignalHandler{}
|
||||
|
||||
// Notify that someone tried to contact us using an invalid bundle
|
||||
if err == chat.ErrSessionNotFound {
|
||||
|
@ -372,14 +406,24 @@ func (api *PublicAPI) processPFSMessage(msg *whisper.Message) error {
|
|||
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
|
||||
api.log.Error("Failed handling message with error", "err", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ignore errors for now
|
||||
if err == nil {
|
||||
msg.Payload = payload
|
||||
// Add unencrypted payload
|
||||
msg.Payload = response.Message
|
||||
|
||||
// Notify of added bundles
|
||||
if response.AddedBundles != nil {
|
||||
for _, bundle := range response.AddedBundles {
|
||||
handler.BundleAdded(bundle[0], bundle[1])
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// -----
|
||||
|
|
|
@ -18,6 +18,9 @@ import (
|
|||
|
||||
var ErrSessionNotFound = errors.New("session not found")
|
||||
|
||||
// If we have no bundles, we use a constant so that the message can reach any device
|
||||
const noInstallationID = "none"
|
||||
|
||||
// EncryptionService defines a service that is responsible for the encryption aspect of the protocol
|
||||
type EncryptionService struct {
|
||||
log log.Logger
|
||||
|
@ -26,6 +29,8 @@ type EncryptionService struct {
|
|||
mutex sync.Mutex
|
||||
}
|
||||
|
||||
type IdentityAndIDPair [2]string
|
||||
|
||||
// NewEncryptionService creates a new EncryptionService instance
|
||||
func NewEncryptionService(p PersistenceService, installationID string) *EncryptionService {
|
||||
logger := log.New("package", "status-go/services/sshext.chat")
|
||||
|
@ -129,26 +134,40 @@ func (s *EncryptionService) keyFromPassiveX3DH(myIdentityKey *ecdsa.PrivateKey,
|
|||
return key, nil
|
||||
}
|
||||
|
||||
// ProcessPublicBundle persists a bundle
|
||||
func (s *EncryptionService) ProcessPublicBundle(myIdentityKey *ecdsa.PrivateKey, b *Bundle) error {
|
||||
// ProcessPublicBundle persists a bundle and returns a list of tuples identity/installationID
|
||||
func (s *EncryptionService) ProcessPublicBundle(myIdentityKey *ecdsa.PrivateKey, b *Bundle) ([]IdentityAndIDPair, error) {
|
||||
// Make sure the bundle belongs to who signed it
|
||||
err := VerifyBundle(b)
|
||||
identity, err := ExtractIdentity(b)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
return s.persistence.AddPublicBundle(b)
|
||||
signedPreKeys := b.GetSignedPreKeys()
|
||||
response := make([]IdentityAndIDPair, len(signedPreKeys))
|
||||
|
||||
if err = s.persistence.AddPublicBundle(b); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
index := 0
|
||||
for installationID := range signedPreKeys {
|
||||
response[index] = IdentityAndIDPair{identity, installationID}
|
||||
index++
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// DecryptPayload decrypts the payload of a DirectMessageProtocol, given an identity private key and the sender's public key
|
||||
func (s *EncryptionService) DecryptPayload(myIdentityKey *ecdsa.PrivateKey, theirIdentityKey *ecdsa.PublicKey, msgs map[string]*DirectMessageProtocol) ([]byte, error) {
|
||||
func (s *EncryptionService) DecryptPayload(myIdentityKey *ecdsa.PrivateKey, theirIdentityKey *ecdsa.PublicKey, theirInstallationID string, msgs map[string]*DirectMessageProtocol) ([]byte, error) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
msg := msgs[s.installationID]
|
||||
if msg == nil {
|
||||
msg = msgs["none"]
|
||||
msg = msgs[noInstallationID]
|
||||
}
|
||||
|
||||
// We should not be sending a signal if it's coming from us, as we receive our own messages
|
||||
if msg == nil {
|
||||
return nil, ErrSessionNotFound
|
||||
}
|
||||
|
@ -168,7 +187,7 @@ func (s *EncryptionService) DecryptPayload(myIdentityKey *ecdsa.PrivateKey, thei
|
|||
}
|
||||
|
||||
theirIdentityKeyC := ecrypto.CompressPubkey(theirIdentityKey)
|
||||
err = s.persistence.AddRatchetInfo(symmetricKey, theirIdentityKeyC, bundleID, nil, x3dhHeader.GetInstallationId())
|
||||
err = s.persistence.AddRatchetInfo(symmetricKey, theirIdentityKeyC, bundleID, nil, theirInstallationID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -189,14 +208,14 @@ func (s *EncryptionService) DecryptPayload(myIdentityKey *ecdsa.PrivateKey, thei
|
|||
|
||||
theirIdentityKeyC := ecrypto.CompressPubkey(theirIdentityKey)
|
||||
|
||||
drInfo, err := s.persistence.GetRatchetInfo(drHeader.GetId(), theirIdentityKeyC)
|
||||
drInfo, err := s.persistence.GetRatchetInfo(drHeader.GetId(), theirIdentityKeyC, theirInstallationID)
|
||||
if err != nil {
|
||||
s.log.Error("Could not get ratchet info", "err", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We mark the exchange as successful so we stop sending x3dh header
|
||||
if err = s.persistence.RatchetInfoConfirmed(drHeader.GetId(), theirIdentityKeyC); err != nil {
|
||||
if err = s.persistence.RatchetInfoConfirmed(drHeader.GetId(), theirIdentityKeyC, theirInstallationID); err != nil {
|
||||
s.log.Error("Could not confirm ratchet info", "err", err)
|
||||
return nil, err
|
||||
}
|
||||
|
@ -354,6 +373,17 @@ func (s *EncryptionService) encryptWithDH(theirIdentityKey *ecdsa.PublicKey, pay
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (s *EncryptionService) EncryptPayloadWithDH(theirIdentityKey *ecdsa.PublicKey, payload []byte) (map[string]*DirectMessageProtocol, error) {
|
||||
response := make(map[string]*DirectMessageProtocol)
|
||||
dmp, err := s.encryptWithDH(theirIdentityKey, payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response[noInstallationID] = dmp
|
||||
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
|
||||
|
@ -361,7 +391,6 @@ func (s *EncryptionService) EncryptPayload(theirIdentityKey *ecdsa.PublicKey, my
|
|||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
response := make(map[string]*DirectMessageProtocol)
|
||||
theirIdentityKeyC := ecrypto.CompressPubkey(theirIdentityKey)
|
||||
|
||||
// Get their latest bundle
|
||||
|
@ -372,15 +401,11 @@ func (s *EncryptionService) EncryptPayload(theirIdentityKey *ecdsa.PublicKey, my
|
|||
|
||||
// We don't have any, send a message with DH
|
||||
if theirBundle == nil && !bytes.Equal(theirIdentityKeyC, ecrypto.CompressPubkey(&myIdentityKey.PublicKey)) {
|
||||
dmp, err := s.encryptWithDH(theirIdentityKey, payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response["none"] = dmp
|
||||
return response, nil
|
||||
return s.EncryptPayloadWithDH(theirIdentityKey, payload)
|
||||
}
|
||||
|
||||
response := make(map[string]*DirectMessageProtocol)
|
||||
|
||||
for installationID, signedPreKeyContainer := range theirBundle.GetSignedPreKeys() {
|
||||
if s.installationID == installationID {
|
||||
continue
|
||||
|
@ -413,8 +438,7 @@ func (s *EncryptionService) EncryptPayload(theirIdentityKey *ecdsa.PublicKey, my
|
|||
}
|
||||
|
||||
response[drInfo.InstallationID] = &dmp
|
||||
|
||||
return response, nil
|
||||
continue
|
||||
}
|
||||
|
||||
// check if a bundle is there
|
||||
|
|
|
@ -289,7 +289,7 @@ type X3DHHeader struct {
|
|||
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"`
|
||||
// The device id
|
||||
// DEPRECATED: The device id
|
||||
InstallationId string `protobuf:"bytes,3,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
|
@ -343,9 +343,9 @@ func (m *X3DHHeader) GetInstallationId() string {
|
|||
|
||||
// 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"`
|
||||
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:"-"`
|
||||
|
@ -409,6 +409,8 @@ func (m *DirectMessageProtocol) GetPayload() []byte {
|
|||
type ProtocolMessage struct {
|
||||
// An optional bundle is exchanged with each message
|
||||
Bundle *Bundle `protobuf:"bytes,1,opt,name=bundle,proto3" json:"bundle,omitempty"`
|
||||
// The device id of the sender
|
||||
InstallationId string `protobuf:"bytes,2,opt,name=installation_id,json=installationId,proto3" json:"installation_id,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
|
||||
|
@ -449,6 +451,13 @@ func (m *ProtocolMessage) GetBundle() *Bundle {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (m *ProtocolMessage) GetInstallationId() string {
|
||||
if m != nil {
|
||||
return m.InstallationId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *ProtocolMessage) GetDirectMessage() map[string]*DirectMessageProtocol {
|
||||
if m != nil {
|
||||
return m.DirectMessage
|
||||
|
@ -479,39 +488,39 @@ func init() {
|
|||
func init() { proto.RegisterFile("encryption.proto", fileDescriptor_8293a649ce9418c6) }
|
||||
|
||||
var fileDescriptor_8293a649ce9418c6 = []byte{
|
||||
// 535 bytes of a gzipped FileDescriptorProto
|
||||
// 539 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x54, 0x51, 0x8b, 0xd3, 0x40,
|
||||
0x10, 0x26, 0xe9, 0xd9, 0x6b, 0xa7, 0x69, 0x5a, 0x56, 0x94, 0x50, 0x0b, 0x96, 0x70, 0x62, 0x40,
|
||||
0x28, 0x5c, 0xfb, 0x22, 0x3e, 0x6a, 0xc5, 0xaa, 0xa8, 0xc7, 0x2a, 0xe8, 0x8b, 0x84, 0x6d, 0x77,
|
||||
0xbc, 0x5b, 0x4c, 0x37, 0x61, 0xb3, 0x2d, 0xf6, 0x2f, 0xf8, 0xe0, 0x4f, 0xf2, 0xbf, 0xf8, 0x4f,
|
||||
0x24, 0xbb, 0x49, 0x9b, 0xf6, 0xee, 0xe0, 0xde, 0x32, 0xb3, 0xb3, 0xdf, 0x7c, 0xdf, 0x37, 0x3b,
|
||||
0x81, 0x3e, 0xca, 0xa5, 0xda, 0x66, 0x5a, 0xa4, 0x72, 0x9c, 0xa9, 0x54, 0xa7, 0xe4, 0x64, 0x79,
|
||||
0xc5, 0x74, 0xf8, 0x11, 0xbc, 0xcf, 0xe2, 0x52, 0x22, 0xbf, 0x50, 0xf8, 0x1e, 0xb7, 0xe4, 0x0c,
|
||||
0xfc, 0xdc, 0xc4, 0x71, 0xa6, 0x30, 0xfe, 0x89, 0xdb, 0xc0, 0x19, 0x39, 0x91, 0x47, 0xbd, 0xbc,
|
||||
0x5e, 0x15, 0xc0, 0xe9, 0x06, 0x55, 0x2e, 0x52, 0x19, 0xb8, 0x23, 0x27, 0xea, 0xd2, 0x2a, 0x0c,
|
||||
0xff, 0x39, 0xd0, 0x7c, 0xb9, 0x96, 0x3c, 0x41, 0x32, 0x80, 0x96, 0xe0, 0x28, 0xb5, 0xd0, 0x15,
|
||||
0xc8, 0x2e, 0x26, 0x6f, 0xa0, 0x77, 0xd8, 0x26, 0x0f, 0xdc, 0x51, 0x23, 0xea, 0x4c, 0x1e, 0x8f,
|
||||
0x0b, 0x5a, 0x63, 0x0b, 0x31, 0xae, 0x53, 0xcb, 0x5f, 0x4b, 0xad, 0xb6, 0xb4, 0x5b, 0x27, 0x92,
|
||||
0x93, 0x21, 0xb4, 0x8b, 0x04, 0xd3, 0x6b, 0x85, 0xc1, 0x89, 0xe9, 0xb2, 0x4f, 0x0c, 0xbe, 0x00,
|
||||
0xb9, 0x0e, 0x41, 0xfa, 0xd0, 0xa8, 0x84, 0xb5, 0x69, 0xf1, 0x49, 0x22, 0xb8, 0xb7, 0x61, 0xc9,
|
||||
0x1a, 0x8d, 0x9a, 0xce, 0x84, 0x58, 0x12, 0xf5, 0xab, 0xd4, 0x16, 0xbc, 0x70, 0x9f, 0x3b, 0xe1,
|
||||
0x6f, 0x07, 0x7a, 0x96, 0xe0, 0xab, 0x54, 0x6a, 0x26, 0x24, 0x2a, 0x72, 0x06, 0xcd, 0x85, 0x49,
|
||||
0x19, 0xd8, 0xce, 0xc4, 0xab, 0xeb, 0xa0, 0xe5, 0x19, 0x99, 0xc2, 0xc3, 0x4c, 0x89, 0x0d, 0xd3,
|
||||
0x18, 0x1f, 0xb9, 0xec, 0x1a, 0xea, 0xf7, 0xcb, 0xd3, 0x83, 0x91, 0x0c, 0xa1, 0xad, 0xc5, 0x0a,
|
||||
0x73, 0xcd, 0x56, 0x59, 0xd0, 0x18, 0x39, 0x51, 0x83, 0xee, 0x13, 0xe1, 0x3b, 0x68, 0xcd, 0xe8,
|
||||
0x1c, 0x19, 0x47, 0x55, 0x17, 0xe6, 0x59, 0x61, 0x1e, 0x38, 0xd5, 0x88, 0x1c, 0x49, 0x7c, 0x70,
|
||||
0x33, 0x69, 0x20, 0xba, 0xd4, 0xcd, 0x4c, 0x2c, 0x78, 0xe9, 0x9a, 0x2b, 0x78, 0x38, 0x84, 0xd6,
|
||||
0x6c, 0x7e, 0x1b, 0x56, 0xf8, 0x15, 0xe0, 0xdb, 0xf4, 0xf6, 0xf3, 0x63, 0x34, 0xf2, 0x14, 0x7a,
|
||||
0x42, 0xe6, 0x9a, 0x25, 0x09, 0x2b, 0x9e, 0x5d, 0x2c, 0xb8, 0x69, 0xdd, 0xa6, 0x7e, 0x3d, 0xfd,
|
||||
0x96, 0x87, 0x7f, 0x1d, 0x78, 0x30, 0x13, 0x0a, 0x97, 0xfa, 0x03, 0xe6, 0x39, 0xbb, 0xc4, 0x8b,
|
||||
0xe2, 0x81, 0x2e, 0xd3, 0x84, 0x9c, 0x43, 0xa7, 0x68, 0x19, 0x5f, 0x99, 0x9e, 0xa5, 0xb5, 0x7d,
|
||||
0x6b, 0xed, 0x9e, 0x0b, 0x85, 0x5f, 0x7b, 0x5e, 0xcf, 0xa0, 0x3d, 0xa3, 0xd5, 0x05, 0x3b, 0x4e,
|
||||
0xdf, 0x5e, 0xa8, 0x6c, 0xa2, 0x2d, 0x4e, 0x6b, 0xc5, 0x3b, 0x74, 0x3c, 0x28, 0x9e, 0xef, 0x8a,
|
||||
0x2b, 0xe4, 0x00, 0x4e, 0x33, 0xb6, 0x4d, 0x52, 0x66, 0x75, 0x78, 0xb4, 0x0a, 0xc3, 0x3f, 0x2e,
|
||||
0xf4, 0x2a, 0xce, 0xa5, 0x84, 0x3b, 0x3e, 0x88, 0x4f, 0xe0, 0x73, 0xa3, 0x3c, 0x5e, 0xd9, 0x7b,
|
||||
0x01, 0x9a, 0x35, 0x88, 0x6c, 0xf5, 0x11, 0xe8, 0xf8, 0xc0, 0xa5, 0x72, 0x1f, 0x78, 0x3d, 0x47,
|
||||
0x9e, 0x80, 0x9f, 0xad, 0x17, 0x89, 0x58, 0xee, 0x00, 0x7f, 0x18, 0xae, 0x5d, 0x9b, 0x2d, 0xcb,
|
||||
0x06, 0xdf, 0x81, 0x5c, 0xc7, 0xba, 0x61, 0x31, 0xce, 0x0f, 0x17, 0xe3, 0x51, 0x69, 0xce, 0x4d,
|
||||
0xc3, 0xaa, 0x6d, 0xc8, 0xa2, 0x69, 0x7e, 0x31, 0xd3, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0x23,
|
||||
0x4b, 0xe1, 0x7c, 0x76, 0x04, 0x00, 0x00,
|
||||
0x28, 0x5c, 0xfb, 0x22, 0x3e, 0x6a, 0xc5, 0xaa, 0xa8, 0xc7, 0x2a, 0xe8, 0x8b, 0x84, 0x6d, 0x33,
|
||||
0xde, 0x2d, 0xa6, 0x9b, 0xb0, 0xd9, 0x16, 0xfa, 0x17, 0xfc, 0x43, 0xbe, 0xf9, 0x5f, 0xfc, 0x27,
|
||||
0x92, 0xdd, 0xa4, 0xdd, 0xf6, 0x7a, 0xe0, 0x5b, 0x66, 0x76, 0xf6, 0x9b, 0xef, 0xfb, 0x66, 0x27,
|
||||
0xd0, 0x47, 0xb1, 0x94, 0xdb, 0x5c, 0xf1, 0x4c, 0x8c, 0x73, 0x99, 0xa9, 0x8c, 0x9c, 0x2d, 0x6f,
|
||||
0x98, 0x0a, 0x3f, 0x82, 0xf7, 0x99, 0x5f, 0x0b, 0x4c, 0xae, 0x24, 0xbe, 0xc7, 0x2d, 0xb9, 0x00,
|
||||
0xbf, 0xd0, 0x71, 0x9c, 0x4b, 0x8c, 0x7f, 0xe2, 0x36, 0x70, 0x46, 0x4e, 0xe4, 0x51, 0xaf, 0xb0,
|
||||
0xab, 0x02, 0x38, 0xdf, 0xa0, 0x2c, 0x78, 0x26, 0x02, 0x77, 0xe4, 0x44, 0x5d, 0x5a, 0x87, 0xe1,
|
||||
0x5f, 0x07, 0x9a, 0x2f, 0xd7, 0x22, 0x49, 0x91, 0x0c, 0xa0, 0xc5, 0x13, 0x14, 0x8a, 0xab, 0x1a,
|
||||
0x64, 0x17, 0x93, 0x37, 0xd0, 0x3b, 0x6c, 0x53, 0x04, 0xee, 0xa8, 0x11, 0x75, 0x26, 0x8f, 0xc7,
|
||||
0x25, 0xad, 0xb1, 0x81, 0x18, 0xdb, 0xd4, 0x8a, 0xd7, 0x42, 0xc9, 0x2d, 0xed, 0xda, 0x44, 0x0a,
|
||||
0x32, 0x84, 0x76, 0x99, 0x60, 0x6a, 0x2d, 0x31, 0x38, 0xd3, 0x5d, 0xf6, 0x89, 0xc1, 0x17, 0x20,
|
||||
0xb7, 0x21, 0x48, 0x1f, 0x1a, 0xb5, 0xb0, 0x36, 0x2d, 0x3f, 0x49, 0x04, 0xf7, 0x36, 0x2c, 0x5d,
|
||||
0xa3, 0x56, 0xd3, 0x99, 0x10, 0x43, 0xc2, 0xbe, 0x4a, 0x4d, 0xc1, 0x0b, 0xf7, 0xb9, 0x13, 0xfe,
|
||||
0x72, 0xa0, 0x67, 0x08, 0xbe, 0xca, 0x84, 0x62, 0x5c, 0xa0, 0x24, 0x17, 0xd0, 0x5c, 0xe8, 0x94,
|
||||
0x86, 0xed, 0x4c, 0x3c, 0x5b, 0x07, 0xad, 0xce, 0xc8, 0x14, 0x1e, 0xe6, 0x92, 0x6f, 0x98, 0xc2,
|
||||
0xf8, 0xc8, 0x65, 0x57, 0x53, 0xbf, 0x5f, 0x9d, 0x1e, 0x8c, 0x64, 0x08, 0x6d, 0xc5, 0x57, 0x58,
|
||||
0x28, 0xb6, 0xca, 0x83, 0xc6, 0xc8, 0x89, 0x1a, 0x74, 0x9f, 0x08, 0xdf, 0x41, 0x6b, 0x46, 0xe7,
|
||||
0xc8, 0x12, 0x94, 0xb6, 0x30, 0xcf, 0x08, 0xf3, 0xc0, 0xa9, 0x47, 0xe4, 0x08, 0xe2, 0x83, 0x9b,
|
||||
0x0b, 0x0d, 0xd1, 0xa5, 0x6e, 0xae, 0x63, 0x9e, 0x54, 0xae, 0xb9, 0x3c, 0x09, 0x87, 0xd0, 0x9a,
|
||||
0xcd, 0xef, 0xc2, 0x0a, 0xbf, 0x02, 0x7c, 0x9b, 0xde, 0x7d, 0x7e, 0x8c, 0x46, 0x9e, 0x42, 0x8f,
|
||||
0x8b, 0x42, 0xb1, 0x34, 0x65, 0xe5, 0xb3, 0x8b, 0x79, 0xa2, 0x5b, 0xb7, 0xa9, 0x6f, 0xa7, 0xdf,
|
||||
0x26, 0xe1, 0x1f, 0x07, 0x1e, 0xcc, 0xb8, 0xc4, 0xa5, 0xfa, 0x80, 0x45, 0xc1, 0xae, 0xf1, 0xaa,
|
||||
0x7c, 0xa0, 0xcb, 0x2c, 0x25, 0x97, 0xd0, 0x29, 0x5b, 0xc6, 0x37, 0xba, 0x67, 0x65, 0x6d, 0xdf,
|
||||
0x58, 0xbb, 0xe7, 0x42, 0x6d, 0x5e, 0xcf, 0xa0, 0x3d, 0xa3, 0xf5, 0x05, 0x33, 0x4e, 0xdf, 0x5c,
|
||||
0xa8, 0x6d, 0xa2, 0x7b, 0xc3, 0xca, 0xe2, 0x1d, 0x3a, 0x1e, 0x14, 0xcf, 0x77, 0xc5, 0x35, 0x72,
|
||||
0x00, 0xe7, 0x39, 0xdb, 0xa6, 0x19, 0x33, 0x3a, 0x3c, 0x5a, 0x87, 0xe1, 0x6f, 0x17, 0x7a, 0x35,
|
||||
0xe7, 0x4a, 0xc2, 0x7f, 0x3e, 0x88, 0x13, 0x1e, 0xb9, 0xa7, 0x3c, 0x22, 0x9f, 0xc0, 0x4f, 0xb4,
|
||||
0x45, 0xf1, 0xca, 0x34, 0x08, 0x50, 0xef, 0x4b, 0x64, 0x60, 0x8f, 0xba, 0x8f, 0x0f, 0xec, 0xac,
|
||||
0x16, 0x27, 0xb1, 0x73, 0xe4, 0x09, 0xf8, 0xf9, 0x7a, 0x91, 0xf2, 0xe5, 0x0e, 0xf0, 0x87, 0x16,
|
||||
0xd5, 0x35, 0xd9, 0xaa, 0x6c, 0xf0, 0x1d, 0xc8, 0x6d, 0xac, 0x13, 0x1b, 0x74, 0x79, 0xb8, 0x41,
|
||||
0x8f, 0x2a, 0x17, 0x4f, 0x4d, 0xd5, 0x5a, 0xa5, 0x45, 0x53, 0xff, 0x8b, 0xa6, 0xff, 0x02, 0x00,
|
||||
0x00, 0xff, 0xff, 0x42, 0x9c, 0x34, 0x10, 0x9f, 0x04, 0x00, 0x00,
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ message X3DHHeader {
|
|||
bytes key = 1;
|
||||
// Used bundle's signed prekey
|
||||
bytes id = 4;
|
||||
// The device id
|
||||
// DEPRECATED: The device id
|
||||
string installation_id = 3;
|
||||
}
|
||||
|
||||
|
@ -66,9 +66,13 @@ message ProtocolMessage {
|
|||
// An optional bundle is exchanged with each message
|
||||
Bundle bundle = 1;
|
||||
|
||||
// The device id of the sender
|
||||
string installation_id = 2;
|
||||
|
||||
// One to one message, encrypted, indexed by installation_id
|
||||
map<string,DirectMessageProtocol> direct_message = 101;
|
||||
|
||||
// Public chats, not encrypted
|
||||
bytes public_message = 102;
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"os"
|
||||
"sort"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
@ -68,18 +69,57 @@ func (s *EncryptionServiceMultiDeviceSuite) TestProcessPublicBundle() {
|
|||
aliceKey, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
|
||||
_, err = s.alice1.CreateBundle(aliceKey)
|
||||
alice1Bundle, err := s.alice1.CreateBundle(aliceKey)
|
||||
s.Require().NoError(err)
|
||||
|
||||
alice1Identity, err := ExtractIdentity(alice1Bundle)
|
||||
s.Require().NoError(err)
|
||||
|
||||
alice2Bundle, err := s.alice2.CreateBundle(aliceKey)
|
||||
s.Require().NoError(err)
|
||||
|
||||
err = s.alice1.ProcessPublicBundle(aliceKey, alice2Bundle)
|
||||
alice2Identity, err := ExtractIdentity(alice2Bundle)
|
||||
s.Require().NoError(err)
|
||||
|
||||
response, err := s.alice1.ProcessPublicBundle(aliceKey, alice2Bundle)
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(IdentityAndIDPair{alice2Identity, "alice2"}, response[0])
|
||||
|
||||
alice1MergedBundle1, err := s.alice1.CreateBundle(aliceKey)
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().NotNil(alice1MergedBundle1.GetSignedPreKeys()["alice1"])
|
||||
s.Require().NotNil(alice1MergedBundle1.GetSignedPreKeys()["alice2"])
|
||||
|
||||
response, err = s.alice1.ProcessPublicBundle(aliceKey, alice1MergedBundle1)
|
||||
s.Require().NoError(err)
|
||||
sort.Slice(response, func(i, j int) bool {
|
||||
return response[i][1] < response[j][1]
|
||||
})
|
||||
s.Require().Equal(IdentityAndIDPair{alice1Identity, "alice1"}, response[0])
|
||||
s.Require().Equal(IdentityAndIDPair{alice2Identity, "alice2"}, response[1])
|
||||
}
|
||||
|
||||
func (s *EncryptionServiceMultiDeviceSuite) TestProcessPublicBundleOutOfOrder() {
|
||||
aliceKey, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Alice1 creates a bundle
|
||||
alice1Bundle, err := s.alice1.CreateBundle(aliceKey)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Alice2 Receives the bundle
|
||||
_, err = s.alice2.ProcessPublicBundle(aliceKey, alice1Bundle)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Alice2 Creates a Bundle
|
||||
_, err = s.alice2.CreateBundle(aliceKey)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// It should contain both bundles
|
||||
alice1MergedBundle1, err := s.alice2.CreateBundle(aliceKey)
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().NotNil(alice1MergedBundle1.GetSignedPreKeys()["alice1"])
|
||||
s.Require().NotNil(alice1MergedBundle1.GetSignedPreKeys()["alice2"])
|
||||
}
|
||||
|
|
|
@ -94,7 +94,7 @@ func (s *EncryptionServiceTestSuite) TestEncryptPayloadNoBundle() {
|
|||
s.NotEqual(cyphertext1, cleartext, "It encrypts the payload correctly")
|
||||
|
||||
// On the receiver side, we should be able to decrypt using our private key and the ephemeral just sent
|
||||
decryptedPayload1, err := s.bob.DecryptPayload(bobKey, &aliceKey.PublicKey, encryptionResponse1)
|
||||
decryptedPayload1, err := s.bob.DecryptPayload(bobKey, &aliceKey.PublicKey, aliceInstallationID, encryptionResponse1)
|
||||
s.Require().NoError(err)
|
||||
s.Equal(cleartext, decryptedPayload1, "It correctly decrypts the payload using DH")
|
||||
|
||||
|
@ -109,7 +109,7 @@ func (s *EncryptionServiceTestSuite) TestEncryptPayloadNoBundle() {
|
|||
s.NotEqual(cyphertext1, cyphertext2, "It does not re-use the symmetric key")
|
||||
s.NotEqual(ephemeralKey1, ephemeralKey2, "It does not re-use the ephemeral key")
|
||||
|
||||
decryptedPayload2, err := s.bob.DecryptPayload(bobKey, &aliceKey.PublicKey, encryptionResponse2)
|
||||
decryptedPayload2, err := s.bob.DecryptPayload(bobKey, &aliceKey.PublicKey, aliceInstallationID, encryptionResponse2)
|
||||
s.Require().NoError(err)
|
||||
s.Equal(cleartext, decryptedPayload2, "It correctly decrypts the payload using DH")
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ func (s *EncryptionServiceTestSuite) TestEncryptPayloadBundle() {
|
|||
s.Require().NoError(err)
|
||||
|
||||
// We add bob bundle
|
||||
err = s.alice.ProcessPublicBundle(aliceKey, bobBundle)
|
||||
_, err = s.alice.ProcessPublicBundle(aliceKey, bobBundle)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// We send a message using the bundle
|
||||
|
@ -161,7 +161,7 @@ func (s *EncryptionServiceTestSuite) TestEncryptPayloadBundle() {
|
|||
s.Equal(uint32(0), drHeader.GetPn(), "It adds the correct length of the message chain")
|
||||
|
||||
// Bob is able to decrypt it using the bundle
|
||||
decryptedPayload1, err := s.bob.DecryptPayload(bobKey, &aliceKey.PublicKey, encryptionResponse1)
|
||||
decryptedPayload1, err := s.bob.DecryptPayload(bobKey, &aliceKey.PublicKey, aliceInstallationID, encryptionResponse1)
|
||||
s.Require().NoError(err)
|
||||
s.Equal(cleartext, decryptedPayload1, "It correctly decrypts the payload using X3DH")
|
||||
}
|
||||
|
@ -188,7 +188,7 @@ func (s *EncryptionServiceTestSuite) TestConsequentMessagesBundle() {
|
|||
s.Require().NoError(err)
|
||||
|
||||
// We add bob bundle
|
||||
err = s.alice.ProcessPublicBundle(aliceKey, bobBundle)
|
||||
_, err = s.alice.ProcessPublicBundle(aliceKey, bobBundle)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// We send a message using the bundle
|
||||
|
@ -225,7 +225,7 @@ func (s *EncryptionServiceTestSuite) TestConsequentMessagesBundle() {
|
|||
s.Equal(uint32(0), drHeader.GetPn(), "It adds the correct length of the message chain")
|
||||
|
||||
// Bob is able to decrypt it using the bundle
|
||||
decryptedPayload1, err := s.bob.DecryptPayload(bobKey, &aliceKey.PublicKey, encryptionResponse)
|
||||
decryptedPayload1, err := s.bob.DecryptPayload(bobKey, &aliceKey.PublicKey, aliceInstallationID, encryptionResponse)
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Equal(cleartext2, decryptedPayload1, "It correctly decrypts the payload using X3DH")
|
||||
|
@ -257,11 +257,11 @@ func (s *EncryptionServiceTestSuite) TestConversation() {
|
|||
s.Require().NoError(err)
|
||||
|
||||
// We add bob bundle
|
||||
err = s.alice.ProcessPublicBundle(aliceKey, bobBundle)
|
||||
_, err = s.alice.ProcessPublicBundle(aliceKey, bobBundle)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// We add alice bundle
|
||||
err = s.bob.ProcessPublicBundle(bobKey, aliceBundle)
|
||||
_, err = s.bob.ProcessPublicBundle(bobKey, aliceBundle)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Alice sends a message
|
||||
|
@ -269,7 +269,7 @@ func (s *EncryptionServiceTestSuite) TestConversation() {
|
|||
s.Require().NoError(err)
|
||||
|
||||
// Bob receives the message
|
||||
_, err = s.bob.DecryptPayload(bobKey, &aliceKey.PublicKey, encryptionResponse)
|
||||
_, err = s.bob.DecryptPayload(bobKey, &aliceKey.PublicKey, aliceInstallationID, encryptionResponse)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Bob replies to the message
|
||||
|
@ -277,7 +277,7 @@ func (s *EncryptionServiceTestSuite) TestConversation() {
|
|||
s.Require().NoError(err)
|
||||
|
||||
// Alice receives the message
|
||||
_, err = s.alice.DecryptPayload(aliceKey, &bobKey.PublicKey, encryptionResponse)
|
||||
_, err = s.alice.DecryptPayload(aliceKey, &bobKey.PublicKey, bobInstallationID, encryptionResponse)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// We send another message using the bundle
|
||||
|
@ -308,7 +308,7 @@ func (s *EncryptionServiceTestSuite) TestConversation() {
|
|||
s.Equal(uint32(1), drHeader.GetPn(), "It adds the correct length of the message chain")
|
||||
|
||||
// Bob is able to decrypt it using the bundle
|
||||
decryptedPayload1, err := s.bob.DecryptPayload(bobKey, &aliceKey.PublicKey, encryptionResponse)
|
||||
decryptedPayload1, err := s.bob.DecryptPayload(bobKey, &aliceKey.PublicKey, aliceInstallationID, encryptionResponse)
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Equal(cleartext2, decryptedPayload1, "It correctly decrypts the payload using X3DH")
|
||||
|
@ -339,7 +339,7 @@ func (s *EncryptionServiceTestSuite) TestConcurrentBundles() {
|
|||
s.Require().NoError(err)
|
||||
|
||||
// We add bob bundle
|
||||
err = s.alice.ProcessPublicBundle(aliceKey, bobBundle)
|
||||
_, err = s.alice.ProcessPublicBundle(aliceKey, bobBundle)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Create a bundle
|
||||
|
@ -347,7 +347,7 @@ func (s *EncryptionServiceTestSuite) TestConcurrentBundles() {
|
|||
s.Require().NoError(err)
|
||||
|
||||
// We add alice bundle
|
||||
err = s.bob.ProcessPublicBundle(bobKey, aliceBundle)
|
||||
_, err = s.bob.ProcessPublicBundle(bobKey, aliceBundle)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Alice sends a message
|
||||
|
@ -359,11 +359,11 @@ func (s *EncryptionServiceTestSuite) TestConcurrentBundles() {
|
|||
s.Require().NoError(err)
|
||||
|
||||
// Bob receives the message
|
||||
_, err = s.bob.DecryptPayload(bobKey, &aliceKey.PublicKey, aliceMessage1)
|
||||
_, err = s.bob.DecryptPayload(bobKey, &aliceKey.PublicKey, aliceInstallationID, aliceMessage1)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Alice receives the message
|
||||
_, err = s.alice.DecryptPayload(aliceKey, &bobKey.PublicKey, bobMessage1)
|
||||
_, err = s.alice.DecryptPayload(aliceKey, &bobKey.PublicKey, bobInstallationID, bobMessage1)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Bob replies to the message
|
||||
|
@ -375,11 +375,11 @@ func (s *EncryptionServiceTestSuite) TestConcurrentBundles() {
|
|||
s.Require().NoError(err)
|
||||
|
||||
// Alice receives the message
|
||||
_, err = s.alice.DecryptPayload(aliceKey, &bobKey.PublicKey, bobMessage2)
|
||||
_, err = s.alice.DecryptPayload(aliceKey, &bobKey.PublicKey, bobInstallationID, bobMessage2)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Bob receives the message
|
||||
_, err = s.bob.DecryptPayload(bobKey, &aliceKey.PublicKey, aliceMessage2)
|
||||
_, err = s.bob.DecryptPayload(bobKey, &aliceKey.PublicKey, aliceInstallationID, aliceMessage2)
|
||||
s.Require().NoError(err)
|
||||
}
|
||||
|
||||
|
@ -420,13 +420,14 @@ func receiver(
|
|||
s *EncryptionService,
|
||||
privateKey *ecdsa.PrivateKey,
|
||||
publicKey *ecdsa.PublicKey,
|
||||
installationID string,
|
||||
errChan chan error,
|
||||
input chan map[string]*DirectMessageProtocol,
|
||||
) {
|
||||
i := 0
|
||||
|
||||
for payload := range input {
|
||||
actualCleartext, err := s.DecryptPayload(privateKey, publicKey, payload)
|
||||
actualCleartext, err := s.DecryptPayload(privateKey, publicKey, installationID, payload)
|
||||
if err != nil {
|
||||
errChan <- err
|
||||
return
|
||||
|
@ -459,7 +460,7 @@ func (s *EncryptionServiceTestSuite) TestRandomised() {
|
|||
s.Require().NoError(err)
|
||||
|
||||
// We add bob bundle
|
||||
err = s.alice.ProcessPublicBundle(aliceKey, bobBundle)
|
||||
_, err = s.alice.ProcessPublicBundle(aliceKey, bobBundle)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Create a bundle
|
||||
|
@ -467,7 +468,7 @@ func (s *EncryptionServiceTestSuite) TestRandomised() {
|
|||
s.Require().NoError(err)
|
||||
|
||||
// We add alice bundle
|
||||
err = s.bob.ProcessPublicBundle(bobKey, aliceBundle)
|
||||
_, err = s.bob.ProcessPublicBundle(bobKey, aliceBundle)
|
||||
s.Require().NoError(err)
|
||||
|
||||
aliceChan := make(chan map[string]*DirectMessageProtocol, 100)
|
||||
|
@ -485,10 +486,10 @@ func (s *EncryptionServiceTestSuite) TestRandomised() {
|
|||
go publisher(s.bob, bobKey, &aliceKey.PublicKey, bobPublisherErrChan, aliceChan)
|
||||
|
||||
// Set up bob receiver
|
||||
go receiver(s.bob, bobKey, &aliceKey.PublicKey, bobReceiverErrChan, bobChan)
|
||||
go receiver(s.bob, bobKey, &aliceKey.PublicKey, aliceInstallationID, bobReceiverErrChan, bobChan)
|
||||
|
||||
// Set up alice receiver
|
||||
go receiver(s.alice, aliceKey, &bobKey.PublicKey, aliceReceiverErrChan, aliceChan)
|
||||
go receiver(s.alice, aliceKey, &bobKey.PublicKey, bobInstallationID, aliceReceiverErrChan, aliceChan)
|
||||
|
||||
aliceErr := <-alicePublisherErrChan
|
||||
s.Require().NoError(aliceErr)
|
||||
|
@ -525,7 +526,7 @@ func (s *EncryptionServiceTestSuite) TestBundleNotExisting() {
|
|||
bobBundle := bobBundleContainer.GetBundle()
|
||||
|
||||
// We add bob bundle
|
||||
err = s.alice.ProcessPublicBundle(aliceKey, bobBundle)
|
||||
_, err = s.alice.ProcessPublicBundle(aliceKey, bobBundle)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Alice sends a message
|
||||
|
@ -533,7 +534,7 @@ func (s *EncryptionServiceTestSuite) TestBundleNotExisting() {
|
|||
s.Require().NoError(err)
|
||||
|
||||
// Bob receives the message, and returns a bundlenotfound error
|
||||
_, err = s.bob.DecryptPayload(bobKey, &aliceKey.PublicKey, aliceMessage)
|
||||
_, err = s.bob.DecryptPayload(bobKey, &aliceKey.PublicKey, aliceInstallationID, aliceMessage)
|
||||
s.Require().Error(err)
|
||||
s.Equal(ErrSessionNotFound, err)
|
||||
}
|
||||
|
@ -561,7 +562,7 @@ func (s *EncryptionServiceTestSuite) TestRefreshedBundle() {
|
|||
s.Require().NoError(err)
|
||||
|
||||
// We add the first bob bundle
|
||||
err = s.alice.ProcessPublicBundle(aliceKey, bobBundle1.GetBundle())
|
||||
_, err = s.alice.ProcessPublicBundle(aliceKey, bobBundle1.GetBundle())
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Alice sends a message
|
||||
|
@ -578,7 +579,7 @@ func (s *EncryptionServiceTestSuite) TestRefreshedBundle() {
|
|||
s.Equal(bobBundle1.GetBundle().GetSignedPreKeys()[bobInstallationID].GetSignedPreKey(), x3dhHeader1.GetId())
|
||||
|
||||
// We add the second bob bundle
|
||||
err = s.alice.ProcessPublicBundle(aliceKey, bobBundle2.GetBundle())
|
||||
_, err = s.alice.ProcessPublicBundle(aliceKey, bobBundle2.GetBundle())
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Alice sends a message
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
// sources:
|
||||
// 1536754952_initial_schema.down.sql
|
||||
// 1536754952_initial_schema.up.sql
|
||||
// 1539249977_update_ratchet_info.down.sql
|
||||
// 1539249977_update_ratchet_info.up.sql
|
||||
// static.go
|
||||
// DO NOT EDIT!
|
||||
|
||||
|
@ -85,7 +87,7 @@ func _1536754952_initial_schemaDownSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1536754952_initial_schema.down.sql", size: 83, mode: os.FileMode(420), modTime: time.Unix(1536915096, 0)}
|
||||
info := bindataFileInfo{name: "1536754952_initial_schema.down.sql", size: 83, mode: os.FileMode(420), modTime: time.Unix(1537862328, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -105,7 +107,47 @@ func _1536754952_initial_schemaUpSql() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1536754952_initial_schema.up.sql", size: 962, mode: os.FileMode(420), modTime: time.Unix(1536915096, 0)}
|
||||
info := bindataFileInfo{name: "1536754952_initial_schema.up.sql", size: 962, mode: os.FileMode(420), modTime: time.Unix(1539252806, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var __1539249977_update_ratchet_infoDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x6c\x8f\x41\x4b\xc4\x30\x10\x85\xef\xf9\x15\xef\xd8\xc2\x9e\xbc\xee\xa9\x8d\x53\x29\x86\x64\x8d\x29\xe8\x29\xd4\xed\xe8\x06\xdb\xec\xd2\x46\xa1\xff\x5e\x22\x52\x59\xf4\x3a\xdf\xf7\x78\x6f\x6e\xad\x39\xc0\x55\xb5\x22\xcc\x7d\x3a\x9e\x38\xf9\x10\x5f\xcf\xfe\xf3\x66\x2f\x84\xb4\x54\x39\xfa\x07\xa3\x10\xc0\xcb\x47\x1c\x46\xf6\x61\x40\xad\x4c\x0d\x6d\x1c\x74\xa7\xd4\x4e\x00\x7c\x39\xf1\xc4\x73\x3f\xfa\x77\x5e\xbf\x71\xbe\x86\x81\x63\x0a\x69\xfd\xeb\x2f\xeb\x34\x71\x9a\xc3\x71\xf3\xaf\x70\x88\x4b\xea\xc7\xb1\x4f\xe1\x1c\x73\x9f\xa3\x27\x77\x25\x74\xba\x7d\xe8\xa8\xd8\x16\xed\xb6\xae\x12\x46\x43\x1a\xdd\xa8\x56\x3a\x58\x3a\xa8\x4a\x52\x8e\x34\xc6\x52\x7b\xa7\x71\x4f\xcf\xf8\x0d\x96\xb0\xd4\x90\x25\x2d\xe9\xf1\xe7\xc1\xa5\x58\xc2\x5b\xe4\xc1\x5f\x66\xce\xf3\x4a\x51\xee\xc5\x57\x00\x00\x00\xff\xff\x69\x51\x9b\xb4\x37\x01\x00\x00")
|
||||
|
||||
func _1539249977_update_ratchet_infoDownSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__1539249977_update_ratchet_infoDownSql,
|
||||
"1539249977_update_ratchet_info.down.sql",
|
||||
)
|
||||
}
|
||||
|
||||
func _1539249977_update_ratchet_infoDownSql() (*asset, error) {
|
||||
bytes, err := _1539249977_update_ratchet_infoDownSqlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1539249977_update_ratchet_info.down.sql", size: 311, mode: os.FileMode(420), modTime: time.Unix(1539250187, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var __1539249977_update_ratchet_infoUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x6c\x8f\x41\x4f\x84\x30\x10\x85\xef\xfd\x15\x73\x84\x84\x93\x57\x4e\x50\x07\x43\xac\xed\x5a\x4b\xa2\xa7\x06\x97\xd1\x6d\x16\xca\x86\x56\x13\xfe\xbd\xa9\x31\x28\xea\xf5\xbd\x6f\xde\x7b\x73\x8d\x02\x0d\x42\xa3\xd5\x1d\x04\x0a\xc1\xcd\x3e\x94\xec\xa7\x7a\xa6\x35\x29\x5a\x1d\xc0\x54\xb5\x40\x58\xfa\x78\x3c\x51\xb4\xce\xbf\xcc\x25\x63\x5c\x63\x65\xf0\x1f\xcf\xbe\x5f\x41\xc6\x00\x9e\xdf\xfc\x30\x92\x75\x03\xd4\x42\xd5\x20\x95\x01\xd9\x09\x51\x30\x00\xba\x9c\x68\xa2\xa5\x1f\xed\x99\xd6\x4f\x3b\xa9\x6e\x20\x1f\x5d\x5c\xff\xf2\x61\x9d\x26\x8a\x8b\x3b\x6e\xfc\xce\x76\x3e\xc4\x7e\x1c\xfb\xe8\x66\x9f\xfa\x0c\x3e\x9a\x1d\xd0\xc9\xf6\xbe\xc3\x6c\x5b\x54\x6c\x5d\xc5\xef\xe3\x1c\x94\x04\xae\x64\x23\x5a\x6e\x40\xe3\x41\x54\x1c\x53\x46\xa3\x34\xb6\x37\x12\x6e\xf1\x09\xbe\x93\x72\xd0\xd8\xa0\x46\xc9\xf1\xe1\xeb\xe3\x90\x05\xf7\xea\x69\xb0\x97\x85\xd2\xde\x9c\xe5\x25\xfb\x08\x00\x00\xff\xff\xb6\x31\x2b\x32\x70\x01\x00\x00")
|
||||
|
||||
func _1539249977_update_ratchet_infoUpSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__1539249977_update_ratchet_infoUpSql,
|
||||
"1539249977_update_ratchet_info.up.sql",
|
||||
)
|
||||
}
|
||||
|
||||
func _1539249977_update_ratchet_infoUpSql() (*asset, error) {
|
||||
bytes, err := _1539249977_update_ratchet_infoUpSqlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1539249977_update_ratchet_info.up.sql", size: 368, mode: os.FileMode(420), modTime: time.Unix(1539250201, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -125,7 +167,7 @@ func staticGo() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "static.go", size: 188, mode: os.FileMode(420), modTime: time.Unix(1536915096, 0)}
|
||||
info := bindataFileInfo{name: "static.go", size: 188, mode: os.FileMode(420), modTime: time.Unix(1537862328, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -184,6 +226,8 @@ func AssetNames() []string {
|
|||
var _bindata = map[string]func() (*asset, error){
|
||||
"1536754952_initial_schema.down.sql": _1536754952_initial_schemaDownSql,
|
||||
"1536754952_initial_schema.up.sql": _1536754952_initial_schemaUpSql,
|
||||
"1539249977_update_ratchet_info.down.sql": _1539249977_update_ratchet_infoDownSql,
|
||||
"1539249977_update_ratchet_info.up.sql": _1539249977_update_ratchet_infoUpSql,
|
||||
"static.go": staticGo,
|
||||
}
|
||||
|
||||
|
@ -229,6 +273,8 @@ type bintree struct {
|
|||
var _bintree = &bintree{nil, map[string]*bintree{
|
||||
"1536754952_initial_schema.down.sql": &bintree{_1536754952_initial_schemaDownSql, map[string]*bintree{}},
|
||||
"1536754952_initial_schema.up.sql": &bintree{_1536754952_initial_schemaUpSql, map[string]*bintree{}},
|
||||
"1539249977_update_ratchet_info.down.sql": &bintree{_1539249977_update_ratchet_infoDownSql, map[string]*bintree{}},
|
||||
"1539249977_update_ratchet_info.up.sql": &bintree{_1539249977_update_ratchet_infoUpSql, map[string]*bintree{}},
|
||||
"static.go": &bintree{staticGo, map[string]*bintree{}},
|
||||
}}
|
||||
|
||||
|
|
|
@ -42,10 +42,10 @@ type PersistenceService interface {
|
|||
// AddRatchetInfo persists the specified ratchet info
|
||||
AddRatchetInfo([]byte, []byte, []byte, []byte, string) error
|
||||
// GetRatchetInfo retrieves the existing RatchetInfo for a specified bundle ID and interlocutor public key
|
||||
GetRatchetInfo([]byte, []byte) (*RatchetInfo, error)
|
||||
GetRatchetInfo([]byte, []byte, string) (*RatchetInfo, error)
|
||||
// GetAnyRatchetInfo retrieves any existing RatchetInfo for a specified interlocutor public key
|
||||
GetAnyRatchetInfo([]byte, string) (*RatchetInfo, error)
|
||||
// RatchetInfoConfirmed clears the ephemeral key in the RatchetInfo
|
||||
// associated with the specified bundle ID and interlocutor identity public key
|
||||
RatchetInfoConfirmed([]byte, []byte) error
|
||||
RatchetInfoConfirmed([]byte, []byte, string) error
|
||||
}
|
||||
|
|
|
@ -14,6 +14,11 @@ type ProtocolService struct {
|
|||
Enabled bool
|
||||
}
|
||||
|
||||
type HandleMessageResponse struct {
|
||||
AddedBundles []IdentityAndIDPair
|
||||
Message []byte
|
||||
}
|
||||
|
||||
// NewProtocolService creates a new ProtocolService instance
|
||||
func NewProtocolService(encryption *EncryptionService) *ProtocolService {
|
||||
return &ProtocolService{
|
||||
|
@ -46,7 +51,8 @@ func (p *ProtocolService) addBundleAndMarshal(myIdentityKey *ecdsa.PrivateKey, m
|
|||
func (p *ProtocolService) BuildPublicMessage(myIdentityKey *ecdsa.PrivateKey, payload []byte) ([]byte, error) {
|
||||
// Build message not encrypted
|
||||
protocolMessage := &ProtocolMessage{
|
||||
PublicMessage: payload,
|
||||
InstallationId: p.encryption.installationID,
|
||||
PublicMessage: payload,
|
||||
}
|
||||
|
||||
return p.addBundleAndMarshal(myIdentityKey, protocolMessage)
|
||||
|
@ -65,7 +71,8 @@ func (p *ProtocolService) BuildDirectMessage(myIdentityKey *ecdsa.PrivateKey, th
|
|||
|
||||
// Build message
|
||||
protocolMessage := &ProtocolMessage{
|
||||
DirectMessage: encryptionResponse,
|
||||
InstallationId: p.encryption.installationID,
|
||||
DirectMessage: encryptionResponse,
|
||||
}
|
||||
|
||||
payload, err := p.addBundleAndMarshal(myIdentityKey, protocolMessage)
|
||||
|
@ -80,8 +87,26 @@ func (p *ProtocolService) BuildDirectMessage(myIdentityKey *ecdsa.PrivateKey, th
|
|||
return response, nil
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// Encrypt payload
|
||||
encryptionResponse, err := p.encryption.EncryptPayloadWithDH(&myIdentityKey.PublicKey, payload)
|
||||
if err != nil {
|
||||
p.log.Error("encryption-service", "error encrypting payload", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Build message
|
||||
protocolMessage := &ProtocolMessage{
|
||||
InstallationId: p.encryption.installationID,
|
||||
DirectMessage: encryptionResponse,
|
||||
}
|
||||
|
||||
return p.addBundleAndMarshal(myIdentityKey, protocolMessage)
|
||||
}
|
||||
|
||||
// ProcessPublicBundle processes a received X3DH bundle
|
||||
func (p *ProtocolService) ProcessPublicBundle(myIdentityKey *ecdsa.PrivateKey, bundle *Bundle) error {
|
||||
func (p *ProtocolService) ProcessPublicBundle(myIdentityKey *ecdsa.PrivateKey, bundle *Bundle) ([]IdentityAndIDPair, error) {
|
||||
return p.encryption.ProcessPublicBundle(myIdentityKey, bundle)
|
||||
}
|
||||
|
||||
|
@ -91,11 +116,13 @@ func (p *ProtocolService) GetBundle(myIdentityKey *ecdsa.PrivateKey) (*Bundle, e
|
|||
}
|
||||
|
||||
// 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) {
|
||||
func (p *ProtocolService) HandleMessage(myIdentityKey *ecdsa.PrivateKey, theirPublicKey *ecdsa.PublicKey, payload []byte) (*HandleMessageResponse, error) {
|
||||
if p.encryption == nil {
|
||||
return nil, errors.New("encryption service not initialized")
|
||||
}
|
||||
|
||||
response := &HandleMessageResponse{}
|
||||
|
||||
// Unmarshal message
|
||||
protocolMessage := &ProtocolMessage{}
|
||||
|
||||
|
@ -106,21 +133,29 @@ func (p *ProtocolService) HandleMessage(myIdentityKey *ecdsa.PrivateKey, theirPu
|
|||
// Process bundle
|
||||
if bundle := protocolMessage.GetBundle(); bundle != nil {
|
||||
// Should we stop processing if the bundle cannot be verified?
|
||||
err := p.encryption.ProcessPublicBundle(myIdentityKey, bundle)
|
||||
addedBundles, err := p.encryption.ProcessPublicBundle(myIdentityKey, bundle)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.AddedBundles = addedBundles
|
||||
}
|
||||
|
||||
// Check if it's a public message
|
||||
if publicMessage := protocolMessage.GetPublicMessage(); publicMessage != nil {
|
||||
response.Message = publicMessage
|
||||
// Nothing to do, as already in cleartext
|
||||
return publicMessage, nil
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// Decrypt message
|
||||
if directMessage := protocolMessage.GetDirectMessage(); directMessage != nil {
|
||||
return p.encryption.DecryptPayload(myIdentityKey, theirPublicKey, directMessage)
|
||||
message, err := p.encryption.DecryptPayload(myIdentityKey, theirPublicKey, protocolMessage.GetInstallationId(), directMessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.Message = message
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// Return error
|
||||
|
|
|
@ -106,8 +106,9 @@ func (s *ProtocolServiceTestSuite) TestBuildAndReadDirectMessage() {
|
|||
s.NoError(err)
|
||||
|
||||
// Bob is able to decrypt the message
|
||||
unmarshaledMsg, err := s.bob.HandleMessage(bobKey, &aliceKey.PublicKey, marshaledMsg[&bobKey.PublicKey])
|
||||
response, err := s.bob.HandleMessage(bobKey, &aliceKey.PublicKey, marshaledMsg[&bobKey.PublicKey])
|
||||
s.NoError(err)
|
||||
unmarshaledMsg := response.Message
|
||||
|
||||
s.NotNil(unmarshaledMsg)
|
||||
|
||||
|
|
|
@ -183,7 +183,7 @@ func (s *SQLLitePersistence) AddPublicBundle(b *Bundle) error {
|
|||
|
||||
// GetAnyPrivateBundle retrieves any bundle from the database containing a private key
|
||||
func (s *SQLLitePersistence) GetAnyPrivateBundle(myIdentityKey []byte) (*BundleContainer, error) {
|
||||
stmt, err := s.db.Prepare("SELECT identity, signed_pre_key, installation_id, timestamp FROM bundles WHERE identity = ? AND expired = 0")
|
||||
stmt, err := s.db.Prepare("SELECT identity, private_key, signed_pre_key, installation_id, timestamp FROM bundles WHERE identity = ? AND expired = 0")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -191,6 +191,7 @@ func (s *SQLLitePersistence) GetAnyPrivateBundle(myIdentityKey []byte) (*BundleC
|
|||
|
||||
var timestamp int64
|
||||
var identity []byte
|
||||
var privateKey []byte
|
||||
|
||||
rows, err := stmt.Query(myIdentityKey)
|
||||
rowCount := 0
|
||||
|
@ -205,12 +206,17 @@ func (s *SQLLitePersistence) GetAnyPrivateBundle(myIdentityKey []byte) (*BundleC
|
|||
SignedPreKeys: make(map[string]*SignedPreKey),
|
||||
}
|
||||
|
||||
bundleContainer := &BundleContainer{
|
||||
Bundle: bundle,
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
var signedPreKey []byte
|
||||
var installationID string
|
||||
rowCount++
|
||||
err = rows.Scan(
|
||||
&identity,
|
||||
&privateKey,
|
||||
&signedPreKey,
|
||||
&installationID,
|
||||
×tamp,
|
||||
|
@ -218,18 +224,21 @@ func (s *SQLLitePersistence) GetAnyPrivateBundle(myIdentityKey []byte) (*BundleC
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// If there is a private key, we set the timestamp of the bundle container
|
||||
if privateKey != nil {
|
||||
bundleContainer.Timestamp = timestamp
|
||||
}
|
||||
|
||||
bundle.SignedPreKeys[installationID] = &SignedPreKey{SignedPreKey: signedPreKey}
|
||||
bundle.Identity = identity
|
||||
}
|
||||
|
||||
if rowCount == 0 {
|
||||
// If no records are found or no record with private key, return nil
|
||||
if rowCount == 0 || bundleContainer.Timestamp == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return &BundleContainer{
|
||||
Bundle: bundle,
|
||||
Timestamp: timestamp,
|
||||
}, nil
|
||||
|
||||
return bundleContainer, nil
|
||||
|
||||
}
|
||||
|
||||
|
@ -318,7 +327,7 @@ func (s *SQLLitePersistence) GetPublicBundle(publicKey *ecdsa.PublicKey) (*Bundl
|
|||
|
||||
// AddRatchetInfo persists the specified ratchet info into the database
|
||||
func (s *SQLLitePersistence) AddRatchetInfo(key []byte, identity []byte, bundleID []byte, ephemeralKey []byte, installationID string) error {
|
||||
stmt, err := s.db.Prepare("INSERT INTO ratchet_info(symmetric_key, identity, bundle_id, ephemeral_key, installation_id) VALUES(?, ?, ?, ?, ?)")
|
||||
stmt, err := s.db.Prepare("INSERT INTO ratchet_info_v2(symmetric_key, identity, bundle_id, ephemeral_key, installation_id) VALUES(?, ?, ?, ?, ?)")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -336,8 +345,8 @@ func (s *SQLLitePersistence) AddRatchetInfo(key []byte, identity []byte, bundleI
|
|||
}
|
||||
|
||||
// GetRatchetInfo retrieves the existing RatchetInfo for a specified bundle ID and interlocutor public key from the database
|
||||
func (s *SQLLitePersistence) GetRatchetInfo(bundleID []byte, theirIdentity []byte) (*RatchetInfo, error) {
|
||||
stmt, err := s.db.Prepare("SELECT ratchet_info.identity, ratchet_info.symmetric_key, bundles.private_key, bundles.signed_pre_key, ratchet_info.ephemeral_key, ratchet_info.installation_id FROM ratchet_info JOIN bundles ON bundle_id = signed_pre_key WHERE ratchet_info.identity = ? AND bundle_id = ? LIMIT 1")
|
||||
func (s *SQLLitePersistence) GetRatchetInfo(bundleID []byte, theirIdentity []byte, installationID string) (*RatchetInfo, error) {
|
||||
stmt, err := s.db.Prepare("SELECT ratchet_info_v2.identity, ratchet_info_v2.symmetric_key, bundles.private_key, bundles.signed_pre_key, ratchet_info_v2.ephemeral_key, ratchet_info_v2.installation_id FROM ratchet_info_v2 JOIN bundles ON bundle_id = signed_pre_key WHERE ratchet_info_v2.identity = ? AND ratchet_info_v2.installation_id = ? AND bundle_id = ? LIMIT 1")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -347,7 +356,7 @@ func (s *SQLLitePersistence) GetRatchetInfo(bundleID []byte, theirIdentity []byt
|
|||
BundleID: bundleID,
|
||||
}
|
||||
|
||||
err = stmt.QueryRow(theirIdentity, bundleID).Scan(
|
||||
err = stmt.QueryRow(theirIdentity, installationID, bundleID).Scan(
|
||||
&ratchetInfo.Identity,
|
||||
&ratchetInfo.Sk,
|
||||
&ratchetInfo.PrivateKey,
|
||||
|
@ -368,7 +377,7 @@ func (s *SQLLitePersistence) GetRatchetInfo(bundleID []byte, theirIdentity []byt
|
|||
|
||||
// GetAnyRatchetInfo retrieves any existing RatchetInfo for a specified interlocutor public key from the database
|
||||
func (s *SQLLitePersistence) GetAnyRatchetInfo(identity []byte, installationID string) (*RatchetInfo, error) {
|
||||
stmt, err := s.db.Prepare("SELECT symmetric_key, bundles.private_key, signed_pre_key, bundle_id, ephemeral_key FROM ratchet_info JOIN bundles ON bundle_id = signed_pre_key WHERE expired = 0 AND ratchet_info.identity = ? AND ratchet_info.installation_id = ? LIMIT 1")
|
||||
stmt, err := s.db.Prepare("SELECT symmetric_key, bundles.private_key, signed_pre_key, bundle_id, ephemeral_key FROM ratchet_info_v2 JOIN bundles ON bundle_id = signed_pre_key WHERE expired = 0 AND ratchet_info_v2.identity = ? AND ratchet_info_v2.installation_id = ? LIMIT 1")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -399,8 +408,8 @@ func (s *SQLLitePersistence) GetAnyRatchetInfo(identity []byte, installationID s
|
|||
|
||||
// RatchetInfoConfirmed clears the ephemeral key in the RatchetInfo
|
||||
// associated with the specified bundle ID and interlocutor identity public key
|
||||
func (s *SQLLitePersistence) RatchetInfoConfirmed(bundleID []byte, theirIdentity []byte) error {
|
||||
stmt, err := s.db.Prepare("UPDATE ratchet_info SET ephemeral_key = NULL WHERE identity = ? AND bundle_id = ?")
|
||||
func (s *SQLLitePersistence) RatchetInfoConfirmed(bundleID []byte, theirIdentity []byte, installationID string) error {
|
||||
stmt, err := s.db.Prepare("UPDATE ratchet_info_v2 SET ephemeral_key = NULL WHERE identity = ? AND bundle_id = ? AND installation_id = ?")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -409,6 +418,7 @@ func (s *SQLLitePersistence) RatchetInfoConfirmed(bundleID []byte, theirIdentity
|
|||
_, err = stmt.Exec(
|
||||
theirIdentity,
|
||||
bundleID,
|
||||
installationID,
|
||||
)
|
||||
|
||||
return err
|
||||
|
|
|
@ -134,6 +134,42 @@ func (s *SQLLitePersistenceTestSuite) TestMultiplePublicBundle() {
|
|||
|
||||
}
|
||||
|
||||
func (s *SQLLitePersistenceTestSuite) TestMultiDevicePublicBundle() {
|
||||
key, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
|
||||
actualBundle, err := s.service.GetPublicBundle(&key.PublicKey)
|
||||
s.Require().NoError(err, "It does not return an error if the bundle is not there")
|
||||
s.Nil(actualBundle)
|
||||
|
||||
bundleContainer, err := NewBundleContainer(key, "1")
|
||||
s.Require().NoError(err)
|
||||
|
||||
bundle := bundleContainer.GetBundle()
|
||||
err = s.service.AddPublicBundle(bundle)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Adding it again does not throw an error
|
||||
err = s.service.AddPublicBundle(bundle)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Adding a different bundle from a different instlation id
|
||||
bundleContainer, err = NewBundleContainer(key, "2")
|
||||
s.Require().NoError(err)
|
||||
|
||||
bundle = bundleContainer.GetBundle()
|
||||
err = s.service.AddPublicBundle(bundle)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Returns the most recent bundle
|
||||
actualBundle, err = s.service.GetPublicBundle(&key.PublicKey)
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Equal(bundle.GetIdentity(), actualBundle.GetIdentity(), "It sets the identity")
|
||||
s.NotNil(actualBundle.GetSignedPreKeys()["1"])
|
||||
s.NotNil(actualBundle.GetSignedPreKeys()["2"])
|
||||
}
|
||||
|
||||
func (s *SQLLitePersistenceTestSuite) TestRatchetInfoPrivateBundle() {
|
||||
key, err := crypto.GenerateKey()
|
||||
s.Require().NoError(err)
|
||||
|
@ -154,9 +190,10 @@ func (s *SQLLitePersistenceTestSuite) TestRatchetInfoPrivateBundle() {
|
|||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
ratchetInfo, err := s.service.GetRatchetInfo(bundle.GetBundle().GetSignedPreKeys()["2"].GetSignedPreKey(), []byte("their-public-key"))
|
||||
ratchetInfo, err := s.service.GetRatchetInfo(bundle.GetBundle().GetSignedPreKeys()["2"].GetSignedPreKey(), []byte("their-public-key"), "1")
|
||||
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotNil(ratchetInfo)
|
||||
s.NotNil(ratchetInfo.ID, "It adds an id")
|
||||
s.Equal(ratchetInfo.PrivateKey, bundle.GetPrivateSignedPreKey(), "It returns the private key")
|
||||
s.Equal(ratchetInfo.Sk, []byte("symmetric-key"), "It returns the symmetric key")
|
||||
|
@ -191,7 +228,7 @@ func (s *SQLLitePersistenceTestSuite) TestRatchetInfoPublicBundle() {
|
|||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
ratchetInfo, err := s.service.GetRatchetInfo(signedPreKey, theirPublicKey)
|
||||
ratchetInfo, err := s.service.GetRatchetInfo(signedPreKey, theirPublicKey, installationID)
|
||||
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotNil(ratchetInfo, "It returns the ratchet info")
|
||||
|
@ -227,7 +264,7 @@ func (s *SQLLitePersistenceTestSuite) TestRatchetInfoNoBundle() {
|
|||
|
||||
s.Error(err, "It returns an error")
|
||||
|
||||
_, err = s.service.GetRatchetInfo([]byte("non-existing-bundle"), []byte("their-public-key"))
|
||||
_, err = s.service.GetRatchetInfo([]byte("non-existing-bundle"), []byte("their-public-key"), "none")
|
||||
s.Require().NoError(err)
|
||||
|
||||
ratchetInfo, err := s.service.GetAnyRatchetInfo([]byte("their-public-key"), "4")
|
||||
|
|
|
@ -12,8 +12,8 @@ func toTopic(s string) whisper.TopicType {
|
|||
return whisper.BytesToTopic(crypto.Keccak256([]byte(s)))
|
||||
}
|
||||
|
||||
func defaultWhisperMessage() *whisper.NewMessage {
|
||||
msg := &whisper.NewMessage{}
|
||||
func defaultWhisperMessage() whisper.NewMessage {
|
||||
msg := whisper.NewMessage{}
|
||||
|
||||
msg.TTL = 10
|
||||
msg.PowTarget = 0.002
|
||||
|
@ -22,7 +22,7 @@ func defaultWhisperMessage() *whisper.NewMessage {
|
|||
return msg
|
||||
}
|
||||
|
||||
func PublicMessageToWhisper(rpcMsg *SendPublicMessageRPC, payload []byte) *whisper.NewMessage {
|
||||
func PublicMessageToWhisper(rpcMsg SendPublicMessageRPC, payload []byte) whisper.NewMessage {
|
||||
msg := defaultWhisperMessage()
|
||||
|
||||
msg.Topic = toTopic(rpcMsg.Chat)
|
||||
|
@ -33,7 +33,7 @@ func PublicMessageToWhisper(rpcMsg *SendPublicMessageRPC, payload []byte) *whisp
|
|||
return msg
|
||||
}
|
||||
|
||||
func DirectMessageToWhisper(rpcMsg *SendDirectMessageRPC, payload []byte) *whisper.NewMessage {
|
||||
func DirectMessageToWhisper(rpcMsg SendDirectMessageRPC, payload []byte) whisper.NewMessage {
|
||||
|
||||
msg := defaultWhisperMessage()
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
)
|
||||
|
||||
func TestPublicMessageToWhisper(t *testing.T) {
|
||||
rpcMessage := &SendPublicMessageRPC{
|
||||
rpcMessage := SendPublicMessageRPC{
|
||||
Chat: "test-chat",
|
||||
Sig: "test",
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ func TestPublicMessageToWhisper(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestDirectMessageToWhisper(t *testing.T) {
|
||||
rpcMessage := &SendDirectMessageRPC{
|
||||
rpcMessage := SendDirectMessageRPC{
|
||||
PubKey: []byte("some pubkey"),
|
||||
Sig: "test",
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@ func buildSignatureMaterial(signedPreKeys *map[string]*SignedPreKey) []byte {
|
|||
signedPreKey := (*signedPreKeys)[installationID]
|
||||
signatureMaterial = append(signatureMaterial, []byte(installationID)...)
|
||||
signatureMaterial = append(signatureMaterial, signedPreKey.SignedPreKey...)
|
||||
signatureMaterial = append(signatureMaterial, []byte(fmt.Sprint(signedPreKey.Version))...)
|
||||
}
|
||||
return signatureMaterial
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@ func TestNewBundleContainer(t *testing.T) {
|
|||
require.NotNil(t, bundle, "Bundle should be generated without errors")
|
||||
|
||||
signatureMaterial := append([]byte(bobInstallationID), bundle.GetSignedPreKeys()[bobInstallationID].GetSignedPreKey()...)
|
||||
signatureMaterial = append(signatureMaterial, []byte("0")...)
|
||||
recoveredPublicKey, err := crypto.SigToPub(
|
||||
crypto.Keccak256(signatureMaterial),
|
||||
bundle.Signature,
|
||||
|
@ -97,8 +98,10 @@ func TestSignBundle(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
signatureMaterial := append([]byte("1"), bundle1.GetSignedPreKeys()["1"].GetSignedPreKey()...)
|
||||
signatureMaterial = append(signatureMaterial, []byte("0")...)
|
||||
signatureMaterial = append(signatureMaterial, []byte("2")...)
|
||||
signatureMaterial = append(signatureMaterial, []byte("key")...)
|
||||
signatureMaterial = append(signatureMaterial, []byte("0")...)
|
||||
|
||||
recoveredPublicKey, err := crypto.SigToPub(
|
||||
crypto.Keccak256(signatureMaterial),
|
||||
|
|
|
@ -106,9 +106,9 @@ func (s *Service) InitProtocol(address string, password string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) ProcessPublicBundle(myIdentityKey *ecdsa.PrivateKey, bundle *chat.Bundle) error {
|
||||
func (s *Service) ProcessPublicBundle(myIdentityKey *ecdsa.PrivateKey, bundle *chat.Bundle) ([]chat.IdentityAndIDPair, error) {
|
||||
if s.protocol == nil {
|
||||
return errProtocolNotInitialized
|
||||
return nil, errProtocolNotInitialized
|
||||
}
|
||||
|
||||
return s.protocol.ProcessPublicBundle(myIdentityKey, bundle)
|
||||
|
|
|
@ -31,3 +31,7 @@ func (h EnvelopeSignalHandler) MailServerRequestExpired(hash common.Hash) {
|
|||
func (h EnvelopeSignalHandler) DecryptMessageFailed(pubKey string) {
|
||||
signal.SendDecryptMessageFailed(pubKey)
|
||||
}
|
||||
|
||||
func (h EnvelopeSignalHandler) BundleAdded(identity string, installationID string) {
|
||||
signal.SendBundleAdded(identity, installationID)
|
||||
}
|
||||
|
|
|
@ -23,6 +23,9 @@ const (
|
|||
|
||||
// EventDecryptMessageFailed is triggered when we receive a message from a bundle we don't have
|
||||
EventDecryptMessageFailed = "messages.decrypt.failed"
|
||||
|
||||
// EventBundleAdded is triggered when we receive a bundle
|
||||
EventBundleAdded = "bundles.added"
|
||||
)
|
||||
|
||||
// EnvelopeSignal includes hash of the envelope.
|
||||
|
@ -42,6 +45,12 @@ type DecryptMessageFailedSignal struct {
|
|||
Sender string `json:"sender"`
|
||||
}
|
||||
|
||||
// BundleAddedSignal holds the identity and installation id of the user
|
||||
type BundleAddedSignal struct {
|
||||
Identity string `json:"identity"`
|
||||
InstallationID string `json:"installationID"`
|
||||
}
|
||||
|
||||
// SendEnvelopeSent triggered when envelope delivered at least to 1 peer.
|
||||
func SendEnvelopeSent(hash common.Hash) {
|
||||
send(EventEnvelopeSent, EnvelopeSignal{hash})
|
||||
|
@ -85,3 +94,7 @@ func SendEnodeDiscovered(enode, topic string) {
|
|||
func SendDecryptMessageFailed(sender string) {
|
||||
send(EventDecryptMessageFailed, DecryptMessageFailedSignal{sender})
|
||||
}
|
||||
|
||||
func SendBundleAdded(identity string, installationID string) {
|
||||
send(EventBundleAdded, BundleAddedSignal{Identity: identity, InstallationID: installationID})
|
||||
}
|
||||
|
|
|
@ -88,7 +88,7 @@ func ConfigCliFleetEthBetaJson() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "../config/cli/fleet-eth.beta.json", size: 3237, mode: os.FileMode(420), modTime: time.Unix(1537450683, 0)}
|
||||
info := bindataFileInfo{name: "../config/cli/fleet-eth.beta.json", size: 3237, mode: os.FileMode(420), modTime: time.Unix(1537514234, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ func ConfigCliFleetEthStagingJson() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "../config/cli/fleet-eth.staging.json", size: 1838, mode: os.FileMode(420), modTime: time.Unix(1537450683, 0)}
|
||||
info := bindataFileInfo{name: "../config/cli/fleet-eth.staging.json", size: 1838, mode: os.FileMode(420), modTime: time.Unix(1537514234, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ func ConfigCliFleetEthTestJson() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "../config/cli/fleet-eth.test.json", size: 1519, mode: os.FileMode(420), modTime: time.Unix(1537450683, 0)}
|
||||
info := bindataFileInfo{name: "../config/cli/fleet-eth.test.json", size: 1519, mode: os.FileMode(420), modTime: time.Unix(1537514234, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -148,12 +148,12 @@ func ConfigCliLesEnabledJson() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "../config/cli/les-enabled.json", size: 58, mode: os.FileMode(420), modTime: time.Unix(1537450683, 0)}
|
||||
info := bindataFileInfo{name: "../config/cli/les-enabled.json", size: 58, mode: os.FileMode(420), modTime: time.Unix(1536858252, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _ConfigCliMailserverEnabledJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xaa\xe6\x52\x50\x50\x50\x50\x0a\xcf\xc8\x2c\x2e\x48\x2d\x72\xce\xcf\x4b\xcb\x4c\x57\xb2\x52\x80\x08\x83\xa5\x5c\xf3\x12\x93\x72\x52\x7d\x13\x33\x73\x82\x53\x8b\xca\x52\x8b\x94\xac\x14\x4a\x8a\x4a\x53\x75\x10\x2a\x10\x72\x01\x89\xc5\xc5\xe5\xf9\x45\x29\x4a\x56\x0a\x4a\xc5\x25\x89\x25\xa5\xc5\xba\xf9\x69\x69\x39\x99\x79\xa9\xba\x99\x79\x49\xf9\x15\x4a\x60\x4d\xb5\x5c\xb5\x5c\x80\x00\x00\x00\xff\xff\x84\xf6\x09\xc4\x78\x00\x00\x00")
|
||||
var _ConfigCliMailserverEnabledJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xaa\xe6\x52\x50\x50\x50\x50\x0a\xcf\xc8\x2c\x2e\x48\x2d\x72\xce\xcf\x4b\xcb\x4c\x57\xb2\x52\x80\x08\x83\xa5\x5c\xf3\x12\x93\x72\x52\x53\x94\xac\x14\x4a\x8a\x4a\x53\x75\xd0\x25\xfc\x42\x02\x82\x2b\xf3\x92\x71\x49\xfb\x26\x66\xe6\x04\xa7\x16\x95\xa5\x16\x61\xaa\x40\xc8\x05\x24\x16\x17\x97\xe7\x17\x81\x2c\x51\x2a\x2e\x49\x2c\x29\x2d\xd6\xcd\x4f\x4b\xcb\xc9\xcc\x4b\xd5\xcd\xcc\x4b\xca\xaf\x50\x02\x6b\xaa\xe5\xaa\xe5\x02\x04\x00\x00\xff\xff\x7c\x73\xee\xbb\xb0\x00\x00\x00")
|
||||
|
||||
func ConfigCliMailserverEnabledJsonBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
|
@ -168,7 +168,7 @@ func ConfigCliMailserverEnabledJson() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "../config/cli/mailserver-enabled.json", size: 120, mode: os.FileMode(420), modTime: time.Unix(1537450683, 0)}
|
||||
info := bindataFileInfo{name: "../config/cli/mailserver-enabled.json", size: 176, mode: os.FileMode(420), modTime: time.Unix(1538032850, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
@ -188,7 +188,7 @@ func ConfigStatusChainGenesisJson() (*asset, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "../config/status-chain-genesis.json", size: 612, mode: os.FileMode(420), modTime: time.Unix(1537450683, 0)}
|
||||
info := bindataFileInfo{name: "../config/status-chain-genesis.json", size: 612, mode: os.FileMode(420), modTime: time.Unix(1536858252, 0)}
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
return a, nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
DROP TABLE ratchet_info_v2;
|
||||
|
||||
CREATE TABLE ratchet_info (
|
||||
bundle_id BLOB NOT NULL,
|
||||
ephemeral_key BLOB,
|
||||
identity BLOB NOT NULL,
|
||||
symmetric_key BLOB NOT NULL,
|
||||
installation_id TEXT NOT NULL,
|
||||
UNIQUE(bundle_id, identity) ON CONFLICT REPLACE,
|
||||
FOREIGN KEY (bundle_id) REFERENCES bundles(signed_pre_key)
|
||||
);
|
|
@ -0,0 +1,13 @@
|
|||
DELETE FROM sessions;
|
||||
DELETE FROM keys;
|
||||
DROP TABLE ratchet_info;
|
||||
|
||||
CREATE TABLE ratchet_info_v2 (
|
||||
bundle_id BLOB NOT NULL,
|
||||
ephemeral_key BLOB,
|
||||
identity BLOB NOT NULL,
|
||||
symmetric_key BLOB NOT NULL,
|
||||
installation_id TEXT NOT NULL,
|
||||
UNIQUE(bundle_id, identity, installation_id) ON CONFLICT REPLACE,
|
||||
FOREIGN KEY (bundle_id) REFERENCES bundles(signed_pre_key)
|
||||
);
|
Loading…
Reference in New Issue