Add bundles.added signal & pairing endpoint (#1237)

This commit is contained in:
Andrea Maria Piana 2018-10-16 12:31:05 +02:00 committed by GitHub
parent 659d79277b
commit e4ba365b8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 441 additions and 145 deletions

View File

@ -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
}

View File

@ -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)
}
// Ignore errors for now
if err == nil {
msg.Payload = payload
}
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
}
// 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
}
// -----

View File

@ -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,14 +401,10 @@ 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
return s.EncryptPayloadWithDH(theirIdentityKey, payload)
}
response["none"] = dmp
return response, nil
}
response := make(map[string]*DirectMessageProtocol)
for installationID, signedPreKeyContainer := range theirBundle.GetSignedPreKeys() {
if s.installationID == installationID {
@ -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

View File

@ -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,
}

View File

@ -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;
}

View File

@ -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"])
}

View File

@ -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

View File

@ -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{}},
}}

View File

@ -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
}

View File

@ -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,6 +51,7 @@ 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{
InstallationId: p.encryption.installationID,
PublicMessage: payload,
}
@ -65,6 +71,7 @@ func (p *ProtocolService) BuildDirectMessage(myIdentityKey *ecdsa.PrivateKey, th
// Build message
protocolMessage := &ProtocolMessage{
InstallationId: p.encryption.installationID,
DirectMessage: encryptionResponse,
}
@ -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

View File

@ -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)

View File

@ -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,
&timestamp,
@ -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

View File

@ -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")

View File

@ -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()

View File

@ -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",
}

View File

@ -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

View File

@ -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),

View File

@ -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)

View File

@ -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)
}

View File

@ -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})
}

View File

@ -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
}

View File

@ -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)
);

View File

@ -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)
);