mirror of
https://github.com/status-im/status-go.git
synced 2025-02-16 16:56:53 +00:00
Add enabling/disabling of installations (#1264)
This commit adds a list new table, installations, which is used to keep track of which installation are active for a given identity key. In general, we limit the number of installation that we keep synchronized to 5, to avoid excessive usage of resources. Any installation coming from our own identity, will have to be manually enabled, otherwise we trust the other peer has correctly paired their devices. We use a timestamp to decide which installations to keep synchronized as a logical clock would have make the creation of the bundle more complicated, but this can always be converted to a logical clock at later stages without breaking compatibility.
This commit is contained in:
parent
3fe5b25ff2
commit
1f6cccd0fc
@ -517,12 +517,6 @@ func (b *StatusBackend) ExtractIdentityFromContactCode(contactCode string) (stri
|
|||||||
return chat.ExtractIdentity(bundle)
|
return chat.ExtractIdentity(bundle)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DEPRECATED
|
|
||||||
// VerifyGroupMembershipSignatures verifies that the signatures are valid
|
|
||||||
func (b *StatusBackend) VerifyGroupMembershipSignatures(signaturePairs [][3]string) error {
|
|
||||||
return crypto.VerifySignatures(signaturePairs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtractGroupMembershipSignatures extract signatures from tuples of content/signature
|
// ExtractGroupMembershipSignatures extract signatures from tuples of content/signature
|
||||||
func (b *StatusBackend) ExtractGroupMembershipSignatures(signaturePairs [][2]string) ([]string, error) {
|
func (b *StatusBackend) ExtractGroupMembershipSignatures(signaturePairs [][2]string) ([]string, error) {
|
||||||
return crypto.ExtractSignatures(signaturePairs)
|
return crypto.ExtractSignatures(signaturePairs)
|
||||||
@ -537,3 +531,43 @@ func (b *StatusBackend) SignGroupMembership(content string) (string, error) {
|
|||||||
|
|
||||||
return crypto.Sign(content, selectedAccount.AccountKey.PrivateKey)
|
return crypto.Sign(content, selectedAccount.AccountKey.PrivateKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EnableInstallation enables an installation for multi-device sync.
|
||||||
|
func (b *StatusBackend) EnableInstallation(installationID string) error {
|
||||||
|
selectedAccount, err := b.AccountManager().SelectedAccount()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
st, err := b.statusNode.ShhExtService()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := st.EnableInstallation(&selectedAccount.AccountKey.PrivateKey.PublicKey, installationID); err != nil {
|
||||||
|
b.log.Error("error enabling installation", "err", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableInstallation disables an installation for multi-device sync.
|
||||||
|
func (b *StatusBackend) DisableInstallation(installationID string) error {
|
||||||
|
selectedAccount, err := b.AccountManager().SelectedAccount()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
st, err := b.statusNode.ShhExtService()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := st.DisableInstallation(&selectedAccount.AccountKey.PrivateKey.PublicKey, installationID); err != nil {
|
||||||
|
b.log.Error("error disabling installation", "err", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -94,29 +94,6 @@ func ExtractIdentityFromContactCode(bundleString *C.char) *C.char {
|
|||||||
return C.CString(string(data))
|
return C.CString(string(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyGroupMembershipSignatures ensure the signature pairs are valid
|
|
||||||
//export VerifyGroupMembershipSignatures
|
|
||||||
func VerifyGroupMembershipSignatures(signaturePairsStr *C.char) *C.char {
|
|
||||||
var signaturePairs [][3]string
|
|
||||||
|
|
||||||
if err := json.Unmarshal([]byte(C.GoString(signaturePairsStr)), &signaturePairs); err != nil {
|
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := statusBackend.VerifyGroupMembershipSignatures(signaturePairs); err != nil {
|
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := json.Marshal(struct {
|
|
||||||
Success bool `json:"success"`
|
|
||||||
}{Success: true})
|
|
||||||
if err != nil {
|
|
||||||
return makeJSONResponse(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return C.CString(string(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtractGroupMembershipSignatures extract public keys from tuples of content/signature
|
// ExtractGroupMembershipSignatures extract public keys from tuples of content/signature
|
||||||
//export ExtractGroupMembershipSignatures
|
//export ExtractGroupMembershipSignatures
|
||||||
func ExtractGroupMembershipSignatures(signaturePairsStr *C.char) *C.char {
|
func ExtractGroupMembershipSignatures(signaturePairsStr *C.char) *C.char {
|
||||||
@ -159,6 +136,42 @@ func SignGroupMembership(content *C.char) *C.char {
|
|||||||
return C.CString(string(data))
|
return C.CString(string(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EnableInstallation enables an installation for multi-device sync.
|
||||||
|
//export EnableInstallation
|
||||||
|
func EnableInstallation(installationID *C.char) *C.char {
|
||||||
|
err := statusBackend.EnableInstallation(C.GoString(installationID))
|
||||||
|
if err != nil {
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(struct {
|
||||||
|
Response string `json:"response"`
|
||||||
|
}{Response: "ok"})
|
||||||
|
if err != nil {
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return C.CString(string(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableInstallation disables an installation for multi-device sync.
|
||||||
|
//export DisableInstallation
|
||||||
|
func DisableInstallation(installationID *C.char) *C.char {
|
||||||
|
err := statusBackend.DisableInstallation(C.GoString(installationID))
|
||||||
|
if err != nil {
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(struct {
|
||||||
|
Response string `json:"response"`
|
||||||
|
}{Response: "ok"})
|
||||||
|
if err != nil {
|
||||||
|
return makeJSONResponse(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return C.CString(string(data))
|
||||||
|
}
|
||||||
|
|
||||||
//ValidateNodeConfig validates config for status node
|
//ValidateNodeConfig validates config for status node
|
||||||
//export ValidateNodeConfig
|
//export ValidateNodeConfig
|
||||||
func ValidateNodeConfig(configJSON *C.char) *C.char {
|
func ValidateNodeConfig(configJSON *C.char) *C.char {
|
||||||
|
@ -3,9 +3,11 @@
|
|||||||
|
|
||||||
package chat
|
package chat
|
||||||
|
|
||||||
import proto "github.com/golang/protobuf/proto"
|
import (
|
||||||
import fmt "fmt"
|
fmt "fmt"
|
||||||
import math "math"
|
proto "github.com/golang/protobuf/proto"
|
||||||
|
math "math"
|
||||||
|
)
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
var _ = proto.Marshal
|
var _ = proto.Marshal
|
||||||
@ -39,6 +41,7 @@ func (*ChatMessagePayload) ProtoMessage() {}
|
|||||||
func (*ChatMessagePayload) Descriptor() ([]byte, []int) {
|
func (*ChatMessagePayload) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_8c585a45e2093e54, []int{0}
|
return fileDescriptor_8c585a45e2093e54, []int{0}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *ChatMessagePayload) XXX_Unmarshal(b []byte) error {
|
func (m *ChatMessagePayload) XXX_Unmarshal(b []byte) error {
|
||||||
return xxx_messageInfo_ChatMessagePayload.Unmarshal(m, b)
|
return xxx_messageInfo_ChatMessagePayload.Unmarshal(m, b)
|
||||||
}
|
}
|
||||||
@ -106,6 +109,7 @@ func (*ContactUpdatePayload) ProtoMessage() {}
|
|||||||
func (*ContactUpdatePayload) Descriptor() ([]byte, []int) {
|
func (*ContactUpdatePayload) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_8c585a45e2093e54, []int{1}
|
return fileDescriptor_8c585a45e2093e54, []int{1}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *ContactUpdatePayload) XXX_Unmarshal(b []byte) error {
|
func (m *ContactUpdatePayload) XXX_Unmarshal(b []byte) error {
|
||||||
return xxx_messageInfo_ContactUpdatePayload.Unmarshal(m, b)
|
return xxx_messageInfo_ContactUpdatePayload.Unmarshal(m, b)
|
||||||
}
|
}
|
||||||
@ -168,6 +172,7 @@ func (*OneToOneRPC) ProtoMessage() {}
|
|||||||
func (*OneToOneRPC) Descriptor() ([]byte, []int) {
|
func (*OneToOneRPC) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_8c585a45e2093e54, []int{2}
|
return fileDescriptor_8c585a45e2093e54, []int{2}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *OneToOneRPC) XXX_Unmarshal(b []byte) error {
|
func (m *OneToOneRPC) XXX_Unmarshal(b []byte) error {
|
||||||
return xxx_messageInfo_OneToOneRPC.Unmarshal(m, b)
|
return xxx_messageInfo_OneToOneRPC.Unmarshal(m, b)
|
||||||
}
|
}
|
||||||
@ -222,6 +227,7 @@ func (*ContactUpdateRPC) ProtoMessage() {}
|
|||||||
func (*ContactUpdateRPC) Descriptor() ([]byte, []int) {
|
func (*ContactUpdateRPC) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_8c585a45e2093e54, []int{3}
|
return fileDescriptor_8c585a45e2093e54, []int{3}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *ContactUpdateRPC) XXX_Unmarshal(b []byte) error {
|
func (m *ContactUpdateRPC) XXX_Unmarshal(b []byte) error {
|
||||||
return xxx_messageInfo_ContactUpdateRPC.Unmarshal(m, b)
|
return xxx_messageInfo_ContactUpdateRPC.Unmarshal(m, b)
|
||||||
}
|
}
|
||||||
@ -275,6 +281,7 @@ func (*ChatProtocolMessage) ProtoMessage() {}
|
|||||||
func (*ChatProtocolMessage) Descriptor() ([]byte, []int) {
|
func (*ChatProtocolMessage) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_8c585a45e2093e54, []int{4}
|
return fileDescriptor_8c585a45e2093e54, []int{4}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *ChatProtocolMessage) XXX_Unmarshal(b []byte) error {
|
func (m *ChatProtocolMessage) XXX_Unmarshal(b []byte) error {
|
||||||
return xxx_messageInfo_ChatProtocolMessage.Unmarshal(m, b)
|
return xxx_messageInfo_ChatProtocolMessage.Unmarshal(m, b)
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
ecrypto "github.com/ethereum/go-ethereum/crypto"
|
ecrypto "github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/crypto/ecies"
|
"github.com/ethereum/go-ethereum/crypto/ecies"
|
||||||
@ -18,19 +19,22 @@ import (
|
|||||||
|
|
||||||
var ErrSessionNotFound = errors.New("session not found")
|
var ErrSessionNotFound = errors.New("session not found")
|
||||||
|
|
||||||
// If we have no bundles, we use a constant so that the message can reach any device
|
// Max number of installations we keep synchronized.
|
||||||
|
const maxInstallations = 5
|
||||||
|
|
||||||
|
// If we have no bundles, we use a constant so that the message can reach any device.
|
||||||
const noInstallationID = "none"
|
const noInstallationID = "none"
|
||||||
|
|
||||||
// How many consecutive messages can be skipped in the receiving chain
|
// How many consecutive messages can be skipped in the receiving chain.
|
||||||
const maxSkip = 1000
|
const maxSkip = 1000
|
||||||
|
|
||||||
// Any message with seqNo <= currentSeq - maxKeep will be deleted
|
// Any message with seqNo <= currentSeq - maxKeep will be deleted.
|
||||||
const maxKeep = 3000
|
const maxKeep = 3000
|
||||||
|
|
||||||
// How many keys do we store in total per session
|
// How many keys do we store in total per session.
|
||||||
const maxMessageKeysPerSession = 2000
|
const maxMessageKeysPerSession = 2000
|
||||||
|
|
||||||
// EncryptionService defines a service that is responsible for the encryption aspect of the protocol
|
// EncryptionService defines a service that is responsible for the encryption aspect of the protocol.
|
||||||
type EncryptionService struct {
|
type EncryptionService struct {
|
||||||
log log.Logger
|
log log.Logger
|
||||||
persistence PersistenceService
|
persistence PersistenceService
|
||||||
@ -40,7 +44,7 @@ type EncryptionService struct {
|
|||||||
|
|
||||||
type IdentityAndIDPair [2]string
|
type IdentityAndIDPair [2]string
|
||||||
|
|
||||||
// NewEncryptionService creates a new EncryptionService instance
|
// NewEncryptionService creates a new EncryptionService instance.
|
||||||
func NewEncryptionService(p PersistenceService, installationID string) *EncryptionService {
|
func NewEncryptionService(p PersistenceService, installationID string) *EncryptionService {
|
||||||
logger := log.New("package", "status-go/services/sshext.chat")
|
logger := log.New("package", "status-go/services/sshext.chat")
|
||||||
logger.Info("Initialized encryption service", "installationID", installationID)
|
logger.Info("Initialized encryption service", "installationID", installationID)
|
||||||
@ -64,13 +68,21 @@ func (s *EncryptionService) keyFromActiveX3DH(theirIdentityKey []byte, theirSign
|
|||||||
// CreateBundle retrieves or creates an X3DH bundle given a private key
|
// CreateBundle retrieves or creates an X3DH bundle given a private key
|
||||||
func (s *EncryptionService) CreateBundle(privateKey *ecdsa.PrivateKey) (*Bundle, error) {
|
func (s *EncryptionService) CreateBundle(privateKey *ecdsa.PrivateKey) (*Bundle, error) {
|
||||||
ourIdentityKeyC := ecrypto.CompressPubkey(&privateKey.PublicKey)
|
ourIdentityKeyC := ecrypto.CompressPubkey(&privateKey.PublicKey)
|
||||||
bundleContainer, err := s.persistence.GetAnyPrivateBundle(ourIdentityKeyC)
|
|
||||||
|
installationIDs, err := s.persistence.GetActiveInstallations(maxInstallations-1, ourIdentityKeyC)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
installationIDs = append(installationIDs, s.installationID)
|
||||||
|
|
||||||
|
bundleContainer, err := s.persistence.GetAnyPrivateBundle(ourIdentityKeyC, installationIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the bundle has expired we create a new one
|
// If the bundle has expired we create a new one
|
||||||
if bundleContainer != nil && bundleContainer.Timestamp < time.Now().AddDate(0, 0, -14).UnixNano() {
|
if bundleContainer != nil && bundleContainer.GetBundle().Timestamp < time.Now().AddDate(0, 0, -14).UnixNano() {
|
||||||
// Mark sessions has expired
|
// Mark sessions has expired
|
||||||
if err := s.persistence.MarkBundleExpired(bundleContainer.GetBundle().GetIdentity()); err != nil {
|
if err := s.persistence.MarkBundleExpired(bundleContainer.GetBundle().GetIdentity()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -143,6 +155,16 @@ func (s *EncryptionService) keyFromPassiveX3DH(myIdentityKey *ecdsa.PrivateKey,
|
|||||||
return key, nil
|
return key, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *EncryptionService) EnableInstallation(myIdentityKey *ecdsa.PublicKey, installationID string) error {
|
||||||
|
myIdentityKeyC := ecrypto.CompressPubkey(myIdentityKey)
|
||||||
|
return s.persistence.EnableInstallation(myIdentityKeyC, installationID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *EncryptionService) DisableInstallation(myIdentityKey *ecdsa.PublicKey, installationID string) error {
|
||||||
|
myIdentityKeyC := ecrypto.CompressPubkey(myIdentityKey)
|
||||||
|
return s.persistence.DisableInstallation(myIdentityKeyC, installationID)
|
||||||
|
}
|
||||||
|
|
||||||
// ProcessPublicBundle persists a bundle and returns a list of tuples identity/installationID
|
// ProcessPublicBundle persists a bundle and returns a list of tuples identity/installationID
|
||||||
func (s *EncryptionService) ProcessPublicBundle(myIdentityKey *ecdsa.PrivateKey, b *Bundle) ([]IdentityAndIDPair, error) {
|
func (s *EncryptionService) ProcessPublicBundle(myIdentityKey *ecdsa.PrivateKey, b *Bundle) ([]IdentityAndIDPair, error) {
|
||||||
// Make sure the bundle belongs to who signed it
|
// Make sure the bundle belongs to who signed it
|
||||||
@ -151,16 +173,27 @@ func (s *EncryptionService) ProcessPublicBundle(myIdentityKey *ecdsa.PrivateKey,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
signedPreKeys := b.GetSignedPreKeys()
|
signedPreKeys := b.GetSignedPreKeys()
|
||||||
response := make([]IdentityAndIDPair, len(signedPreKeys))
|
var response []IdentityAndIDPair
|
||||||
|
var installationIDs []string
|
||||||
|
myIdentityStr := fmt.Sprintf("0x%x", ecrypto.FromECDSAPub(&myIdentityKey.PublicKey))
|
||||||
|
|
||||||
if err = s.persistence.AddPublicBundle(b); err != nil {
|
// Any device from other peers will be considered enabled, ours needs to
|
||||||
|
// be explicitly enabled
|
||||||
|
fromOurIdentity := identity != myIdentityStr
|
||||||
|
|
||||||
|
for installationID := range signedPreKeys {
|
||||||
|
if installationID != s.installationID {
|
||||||
|
installationIDs = append(installationIDs, installationID)
|
||||||
|
response = append(response, IdentityAndIDPair{identity, installationID})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = s.persistence.AddInstallations(b.GetIdentity(), b.GetTimestamp(), installationIDs, fromOurIdentity); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
index := 0
|
if err = s.persistence.AddPublicBundle(b); err != nil {
|
||||||
for installationID := range signedPreKeys {
|
return nil, err
|
||||||
response[index] = IdentityAndIDPair{identity, installationID}
|
|
||||||
index++
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return response, nil
|
return response, nil
|
||||||
@ -414,8 +447,13 @@ func (s *EncryptionService) EncryptPayload(theirIdentityKey *ecdsa.PublicKey, my
|
|||||||
|
|
||||||
theirIdentityKeyC := ecrypto.CompressPubkey(theirIdentityKey)
|
theirIdentityKeyC := ecrypto.CompressPubkey(theirIdentityKey)
|
||||||
|
|
||||||
|
installationIDs, err := s.persistence.GetActiveInstallations(maxInstallations, theirIdentityKeyC)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Get their latest bundle
|
// Get their latest bundle
|
||||||
theirBundle, err := s.persistence.GetPublicBundle(theirIdentityKey)
|
theirBundle, err := s.persistence.GetPublicBundle(theirIdentityKey, installationIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -452,9 +490,8 @@ func (s *EncryptionService) EncryptPayload(theirIdentityKey *ecdsa.PublicKey, my
|
|||||||
|
|
||||||
if drInfo.EphemeralKey != nil {
|
if drInfo.EphemeralKey != nil {
|
||||||
dmp.X3DHHeader = &X3DHHeader{
|
dmp.X3DHHeader = &X3DHHeader{
|
||||||
Key: drInfo.EphemeralKey,
|
Key: drInfo.EphemeralKey,
|
||||||
Id: drInfo.BundleID,
|
Id: drInfo.BundleID,
|
||||||
InstallationId: s.installationID,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -462,50 +499,41 @@ func (s *EncryptionService) EncryptPayload(theirIdentityKey *ecdsa.PublicKey, my
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if a bundle is there
|
sharedKey, ourEphemeralKey, err := s.keyFromActiveX3DH(theirIdentityKeyC, theirSignedPreKey, myIdentityKey)
|
||||||
theirBundle, err := s.persistence.GetPublicBundle(theirIdentityKey)
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
theirIdentityKeyC := ecrypto.CompressPubkey(theirIdentityKey)
|
||||||
|
ourEphemeralKeyC := ecrypto.CompressPubkey(ourEphemeralKey)
|
||||||
|
|
||||||
|
err = s.persistence.AddRatchetInfo(sharedKey, theirIdentityKeyC, theirSignedPreKey, ourEphemeralKeyC, installationID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if theirBundle != nil {
|
x3dhHeader := &X3DHHeader{
|
||||||
sharedKey, ourEphemeralKey, err := s.keyFromActiveX3DH(theirIdentityKeyC, theirSignedPreKey, myIdentityKey)
|
Key: ourEphemeralKeyC,
|
||||||
if err != nil {
|
Id: theirSignedPreKey,
|
||||||
return nil, err
|
}
|
||||||
}
|
|
||||||
theirIdentityKeyC := ecrypto.CompressPubkey(theirIdentityKey)
|
|
||||||
ourEphemeralKeyC := ecrypto.CompressPubkey(ourEphemeralKey)
|
|
||||||
|
|
||||||
err = s.persistence.AddRatchetInfo(sharedKey, theirIdentityKeyC, theirSignedPreKey, ourEphemeralKeyC, installationID)
|
drInfo, err = s.persistence.GetAnyRatchetInfo(theirIdentityKeyC, installationID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if drInfo != nil {
|
||||||
|
encryptedPayload, drHeader, err := s.encryptUsingDR(theirIdentityKey, drInfo, payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
x3dhHeader := &X3DHHeader{
|
dmp := &DirectMessageProtocol{
|
||||||
Key: ourEphemeralKeyC,
|
Payload: encryptedPayload,
|
||||||
Id: theirSignedPreKey,
|
X3DHHeader: x3dhHeader,
|
||||||
InstallationId: s.installationID,
|
DRHeader: drHeader,
|
||||||
}
|
}
|
||||||
|
|
||||||
drInfo, err := s.persistence.GetAnyRatchetInfo(theirIdentityKeyC, installationID)
|
response[drInfo.InstallationID] = dmp
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if drInfo != nil {
|
|
||||||
encryptedPayload, drHeader, err := s.encryptUsingDR(theirIdentityKey, drInfo, payload)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
dmp := &DirectMessageProtocol{
|
|
||||||
Payload: encryptedPayload,
|
|
||||||
X3DHHeader: x3dhHeader,
|
|
||||||
DRHeader: drHeader,
|
|
||||||
}
|
|
||||||
|
|
||||||
response[drInfo.InstallationID] = dmp
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,9 +3,11 @@
|
|||||||
|
|
||||||
package chat
|
package chat
|
||||||
|
|
||||||
import proto "github.com/golang/protobuf/proto"
|
import (
|
||||||
import fmt "fmt"
|
fmt "fmt"
|
||||||
import math "math"
|
proto "github.com/golang/protobuf/proto"
|
||||||
|
math "math"
|
||||||
|
)
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
var _ = proto.Marshal
|
var _ = proto.Marshal
|
||||||
@ -32,6 +34,7 @@ func (*SignedPreKey) ProtoMessage() {}
|
|||||||
func (*SignedPreKey) Descriptor() ([]byte, []int) {
|
func (*SignedPreKey) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_8293a649ce9418c6, []int{0}
|
return fileDescriptor_8293a649ce9418c6, []int{0}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *SignedPreKey) XXX_Unmarshal(b []byte) error {
|
func (m *SignedPreKey) XXX_Unmarshal(b []byte) error {
|
||||||
return xxx_messageInfo_SignedPreKey.Unmarshal(m, b)
|
return xxx_messageInfo_SignedPreKey.Unmarshal(m, b)
|
||||||
}
|
}
|
||||||
@ -71,7 +74,9 @@ type Bundle struct {
|
|||||||
// Installation id
|
// Installation id
|
||||||
SignedPreKeys map[string]*SignedPreKey `protobuf:"bytes,2,rep,name=signed_pre_keys,json=signedPreKeys,proto3" json:"signed_pre_keys,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
SignedPreKeys map[string]*SignedPreKey `protobuf:"bytes,2,rep,name=signed_pre_keys,json=signedPreKeys,proto3" json:"signed_pre_keys,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||||
// Prekey signature
|
// Prekey signature
|
||||||
Signature []byte `protobuf:"bytes,4,opt,name=signature,proto3" json:"signature,omitempty"`
|
Signature []byte `protobuf:"bytes,4,opt,name=signature,proto3" json:"signature,omitempty"`
|
||||||
|
// When the bundle was created locally
|
||||||
|
Timestamp int64 `protobuf:"varint,5,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_unrecognized []byte `json:"-"`
|
||||||
XXX_sizecache int32 `json:"-"`
|
XXX_sizecache int32 `json:"-"`
|
||||||
@ -83,6 +88,7 @@ func (*Bundle) ProtoMessage() {}
|
|||||||
func (*Bundle) Descriptor() ([]byte, []int) {
|
func (*Bundle) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_8293a649ce9418c6, []int{1}
|
return fileDescriptor_8293a649ce9418c6, []int{1}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Bundle) XXX_Unmarshal(b []byte) error {
|
func (m *Bundle) XXX_Unmarshal(b []byte) error {
|
||||||
return xxx_messageInfo_Bundle.Unmarshal(m, b)
|
return xxx_messageInfo_Bundle.Unmarshal(m, b)
|
||||||
}
|
}
|
||||||
@ -122,13 +128,18 @@ func (m *Bundle) GetSignature() []byte {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Bundle) GetTimestamp() int64 {
|
||||||
|
if m != nil {
|
||||||
|
return m.Timestamp
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
type BundleContainer struct {
|
type BundleContainer struct {
|
||||||
// X3DH prekey bundle
|
// X3DH prekey bundle
|
||||||
Bundle *Bundle `protobuf:"bytes,1,opt,name=bundle,proto3" json:"bundle,omitempty"`
|
Bundle *Bundle `protobuf:"bytes,1,opt,name=bundle,proto3" json:"bundle,omitempty"`
|
||||||
// Private signed prekey
|
// Private signed prekey
|
||||||
PrivateSignedPreKey []byte `protobuf:"bytes,2,opt,name=private_signed_pre_key,json=privateSignedPreKey,proto3" json:"private_signed_pre_key,omitempty"`
|
PrivateSignedPreKey []byte `protobuf:"bytes,2,opt,name=private_signed_pre_key,json=privateSignedPreKey,proto3" json:"private_signed_pre_key,omitempty"`
|
||||||
// Local time creation
|
|
||||||
Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
|
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_unrecognized []byte `json:"-"`
|
||||||
XXX_sizecache int32 `json:"-"`
|
XXX_sizecache int32 `json:"-"`
|
||||||
@ -140,6 +151,7 @@ func (*BundleContainer) ProtoMessage() {}
|
|||||||
func (*BundleContainer) Descriptor() ([]byte, []int) {
|
func (*BundleContainer) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_8293a649ce9418c6, []int{2}
|
return fileDescriptor_8293a649ce9418c6, []int{2}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *BundleContainer) XXX_Unmarshal(b []byte) error {
|
func (m *BundleContainer) XXX_Unmarshal(b []byte) error {
|
||||||
return xxx_messageInfo_BundleContainer.Unmarshal(m, b)
|
return xxx_messageInfo_BundleContainer.Unmarshal(m, b)
|
||||||
}
|
}
|
||||||
@ -172,13 +184,6 @@ func (m *BundleContainer) GetPrivateSignedPreKey() []byte {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *BundleContainer) GetTimestamp() int64 {
|
|
||||||
if m != nil {
|
|
||||||
return m.Timestamp
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
type DRHeader struct {
|
type DRHeader struct {
|
||||||
// Current ratchet public key
|
// Current ratchet public key
|
||||||
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||||
@ -199,6 +204,7 @@ func (*DRHeader) ProtoMessage() {}
|
|||||||
func (*DRHeader) Descriptor() ([]byte, []int) {
|
func (*DRHeader) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_8293a649ce9418c6, []int{3}
|
return fileDescriptor_8293a649ce9418c6, []int{3}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *DRHeader) XXX_Unmarshal(b []byte) error {
|
func (m *DRHeader) XXX_Unmarshal(b []byte) error {
|
||||||
return xxx_messageInfo_DRHeader.Unmarshal(m, b)
|
return xxx_messageInfo_DRHeader.Unmarshal(m, b)
|
||||||
}
|
}
|
||||||
@ -259,6 +265,7 @@ func (*DHHeader) ProtoMessage() {}
|
|||||||
func (*DHHeader) Descriptor() ([]byte, []int) {
|
func (*DHHeader) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_8293a649ce9418c6, []int{4}
|
return fileDescriptor_8293a649ce9418c6, []int{4}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *DHHeader) XXX_Unmarshal(b []byte) error {
|
func (m *DHHeader) XXX_Unmarshal(b []byte) error {
|
||||||
return xxx_messageInfo_DHHeader.Unmarshal(m, b)
|
return xxx_messageInfo_DHHeader.Unmarshal(m, b)
|
||||||
}
|
}
|
||||||
@ -288,9 +295,7 @@ type X3DHHeader struct {
|
|||||||
// Ephemeral key used
|
// Ephemeral key used
|
||||||
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||||
// Used bundle's signed prekey
|
// Used bundle's signed prekey
|
||||||
Id []byte `protobuf:"bytes,4,opt,name=id,proto3" json:"id,omitempty"`
|
Id []byte `protobuf:"bytes,4,opt,name=id,proto3" json:"id,omitempty"`
|
||||||
// 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_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_unrecognized []byte `json:"-"`
|
||||||
XXX_sizecache int32 `json:"-"`
|
XXX_sizecache int32 `json:"-"`
|
||||||
@ -302,6 +307,7 @@ func (*X3DHHeader) ProtoMessage() {}
|
|||||||
func (*X3DHHeader) Descriptor() ([]byte, []int) {
|
func (*X3DHHeader) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_8293a649ce9418c6, []int{5}
|
return fileDescriptor_8293a649ce9418c6, []int{5}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *X3DHHeader) XXX_Unmarshal(b []byte) error {
|
func (m *X3DHHeader) XXX_Unmarshal(b []byte) error {
|
||||||
return xxx_messageInfo_X3DHHeader.Unmarshal(m, b)
|
return xxx_messageInfo_X3DHHeader.Unmarshal(m, b)
|
||||||
}
|
}
|
||||||
@ -334,13 +340,6 @@ func (m *X3DHHeader) GetId() []byte {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *X3DHHeader) GetInstallationId() string {
|
|
||||||
if m != nil {
|
|
||||||
return m.InstallationId
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Direct message value
|
// Direct message value
|
||||||
type DirectMessageProtocol struct {
|
type DirectMessageProtocol struct {
|
||||||
X3DHHeader *X3DHHeader `protobuf:"bytes,1,opt,name=X3DH_header,json=X3DHHeader,proto3" json:"X3DH_header,omitempty"`
|
X3DHHeader *X3DHHeader `protobuf:"bytes,1,opt,name=X3DH_header,json=X3DHHeader,proto3" json:"X3DH_header,omitempty"`
|
||||||
@ -359,6 +358,7 @@ func (*DirectMessageProtocol) ProtoMessage() {}
|
|||||||
func (*DirectMessageProtocol) Descriptor() ([]byte, []int) {
|
func (*DirectMessageProtocol) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_8293a649ce9418c6, []int{6}
|
return fileDescriptor_8293a649ce9418c6, []int{6}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *DirectMessageProtocol) XXX_Unmarshal(b []byte) error {
|
func (m *DirectMessageProtocol) XXX_Unmarshal(b []byte) error {
|
||||||
return xxx_messageInfo_DirectMessageProtocol.Unmarshal(m, b)
|
return xxx_messageInfo_DirectMessageProtocol.Unmarshal(m, b)
|
||||||
}
|
}
|
||||||
@ -426,6 +426,7 @@ func (*ProtocolMessage) ProtoMessage() {}
|
|||||||
func (*ProtocolMessage) Descriptor() ([]byte, []int) {
|
func (*ProtocolMessage) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_8293a649ce9418c6, []int{7}
|
return fileDescriptor_8293a649ce9418c6, []int{7}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *ProtocolMessage) XXX_Unmarshal(b []byte) error {
|
func (m *ProtocolMessage) XXX_Unmarshal(b []byte) error {
|
||||||
return xxx_messageInfo_ProtocolMessage.Unmarshal(m, b)
|
return xxx_messageInfo_ProtocolMessage.Unmarshal(m, b)
|
||||||
}
|
}
|
||||||
@ -488,39 +489,39 @@ func init() {
|
|||||||
func init() { proto.RegisterFile("encryption.proto", fileDescriptor_8293a649ce9418c6) }
|
func init() { proto.RegisterFile("encryption.proto", fileDescriptor_8293a649ce9418c6) }
|
||||||
|
|
||||||
var fileDescriptor_8293a649ce9418c6 = []byte{
|
var fileDescriptor_8293a649ce9418c6 = []byte{
|
||||||
// 539 bytes of a gzipped FileDescriptorProto
|
// 535 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x54, 0x51, 0x8b, 0xd3, 0x40,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x54, 0x5d, 0x8b, 0xd3, 0x40,
|
||||||
0x10, 0x26, 0xe9, 0xd9, 0x6b, 0xa7, 0x69, 0x5a, 0x56, 0x94, 0x50, 0x0b, 0x96, 0x70, 0x62, 0x40,
|
0x14, 0x25, 0x69, 0xb7, 0xdb, 0xde, 0xa6, 0x69, 0x19, 0x51, 0x42, 0x5d, 0xb0, 0x84, 0x15, 0x03,
|
||||||
0x28, 0x5c, 0xfb, 0x22, 0x3e, 0x6a, 0xc5, 0xaa, 0xa8, 0xc7, 0x2a, 0xe8, 0x8b, 0x84, 0x6d, 0x33,
|
0x42, 0x61, 0x5b, 0x1f, 0xc4, 0x47, 0xad, 0x58, 0x57, 0xd4, 0x65, 0xf4, 0xc1, 0x17, 0x09, 0xd3,
|
||||||
0xde, 0x2d, 0xa6, 0x9b, 0xb0, 0xd9, 0x16, 0xfa, 0x17, 0xfc, 0x43, 0xbe, 0xf9, 0x5f, 0xfc, 0x27,
|
0xe6, 0xba, 0x3b, 0x98, 0x4e, 0xc2, 0x64, 0x5a, 0xe8, 0x2f, 0xf3, 0xcd, 0xbf, 0xa6, 0x64, 0x26,
|
||||||
0x92, 0xdd, 0xa4, 0xdd, 0xf6, 0x7a, 0xe0, 0x5b, 0x66, 0x76, 0xf6, 0x9b, 0xef, 0xfb, 0x66, 0x27,
|
0x69, 0xa7, 0xdd, 0x5d, 0xd8, 0xb7, 0xde, 0x8f, 0x39, 0xe7, 0xdc, 0x73, 0x7b, 0x03, 0x03, 0x14,
|
||||||
0xd0, 0x47, 0xb1, 0x94, 0xdb, 0x5c, 0xf1, 0x4c, 0x8c, 0x73, 0x99, 0xa9, 0x8c, 0x9c, 0x2d, 0x6f,
|
0x4b, 0xb9, 0xcd, 0x15, 0xcf, 0xc4, 0x38, 0x97, 0x99, 0xca, 0x48, 0x73, 0x79, 0xc3, 0x54, 0xf8,
|
||||||
0x98, 0x0a, 0x3f, 0x82, 0xf7, 0x99, 0x5f, 0x0b, 0x4c, 0xae, 0x24, 0xbe, 0xc7, 0x2d, 0xb9, 0x00,
|
0x05, 0xbc, 0x6f, 0xfc, 0x5a, 0x60, 0x72, 0x25, 0xf1, 0x13, 0x6e, 0xc9, 0x39, 0xf8, 0x85, 0x8e,
|
||||||
0xbf, 0xd0, 0x71, 0x9c, 0x4b, 0x8c, 0x7f, 0xe2, 0x36, 0x70, 0x46, 0x4e, 0xe4, 0x51, 0xaf, 0xb0,
|
0xe3, 0x5c, 0x62, 0xfc, 0x1b, 0xb7, 0x81, 0x33, 0x72, 0x22, 0x8f, 0x7a, 0x85, 0xdd, 0x15, 0xc0,
|
||||||
0xab, 0x02, 0x38, 0xdf, 0xa0, 0x2c, 0x78, 0x26, 0x02, 0x77, 0xe4, 0x44, 0x5d, 0x5a, 0x87, 0xe1,
|
0xe9, 0x06, 0x65, 0xc1, 0x33, 0x11, 0xb8, 0x23, 0x27, 0xea, 0xd1, 0x3a, 0x0c, 0xff, 0x39, 0xd0,
|
||||||
0x5f, 0x07, 0x9a, 0x2f, 0xd7, 0x22, 0x49, 0x91, 0x0c, 0xa0, 0xc5, 0x13, 0x14, 0x8a, 0xab, 0x1a,
|
0x7a, 0xbb, 0x16, 0x49, 0x8a, 0x64, 0x08, 0x6d, 0x9e, 0xa0, 0x50, 0x5c, 0xd5, 0x20, 0xbb, 0x98,
|
||||||
0x64, 0x17, 0x93, 0x37, 0xd0, 0x3b, 0x6c, 0x53, 0x04, 0xee, 0xa8, 0x11, 0x75, 0x26, 0x8f, 0xc7,
|
0x7c, 0x80, 0xfe, 0x21, 0x4d, 0x11, 0xb8, 0xa3, 0x46, 0xd4, 0x9d, 0x3c, 0x1b, 0x97, 0xb2, 0xc6,
|
||||||
0x25, 0xad, 0xb1, 0x81, 0x18, 0xdb, 0xd4, 0x8a, 0xd7, 0x42, 0xc9, 0x2d, 0xed, 0xda, 0x44, 0x0a,
|
0x06, 0x62, 0x6c, 0x4b, 0x2b, 0xde, 0x0b, 0x25, 0xb7, 0xb4, 0x67, 0x0b, 0x29, 0xc8, 0x19, 0x74,
|
||||||
0x32, 0x84, 0x76, 0x99, 0x60, 0x6a, 0x2d, 0x31, 0x38, 0xd3, 0x5d, 0xf6, 0x89, 0xc1, 0x17, 0x20,
|
0xca, 0x04, 0x53, 0x6b, 0x89, 0x41, 0x53, 0xb3, 0xec, 0x13, 0x65, 0x55, 0xf1, 0x15, 0x16, 0x8a,
|
||||||
0xb7, 0x21, 0x48, 0x1f, 0x1a, 0xb5, 0xb0, 0x36, 0x2d, 0x3f, 0x49, 0x04, 0xf7, 0x36, 0x2c, 0x5d,
|
0xad, 0xf2, 0xe0, 0x64, 0xe4, 0x44, 0x0d, 0xba, 0x4f, 0x0c, 0xbf, 0x03, 0xb9, 0x4d, 0x40, 0x06,
|
||||||
0xa3, 0x56, 0xd3, 0x99, 0x10, 0x43, 0xc2, 0xbe, 0x4a, 0x4d, 0xc1, 0x0b, 0xf7, 0xb9, 0x13, 0xfe,
|
0xd0, 0xa8, 0xc7, 0xee, 0xd0, 0xf2, 0x27, 0x89, 0xe0, 0x64, 0xc3, 0xd2, 0x35, 0xea, 0x59, 0xbb,
|
||||||
0x72, 0xa0, 0x67, 0x08, 0xbe, 0xca, 0x84, 0x62, 0x5c, 0xa0, 0x24, 0x17, 0xd0, 0x5c, 0xe8, 0x94,
|
0x13, 0x62, 0x24, 0xda, 0x4f, 0xa9, 0x69, 0x78, 0xe3, 0xbe, 0x76, 0x42, 0x09, 0x7d, 0xa3, 0xfe,
|
||||||
0x86, 0xed, 0x4c, 0x3c, 0x5b, 0x07, 0xad, 0xce, 0xc8, 0x14, 0x1e, 0xe6, 0x92, 0x6f, 0x98, 0xc2,
|
0x5d, 0x26, 0x14, 0xe3, 0x02, 0x25, 0x39, 0x87, 0xd6, 0x42, 0xa7, 0x34, 0x6a, 0x77, 0xe2, 0xd9,
|
||||||
0xf8, 0xc8, 0x65, 0x57, 0x53, 0xbf, 0x5f, 0x9d, 0x1e, 0x8c, 0x64, 0x08, 0x6d, 0xc5, 0x57, 0x58,
|
0x43, 0xd2, 0xaa, 0x46, 0xa6, 0xf0, 0x24, 0x97, 0x7c, 0xc3, 0x14, 0xc6, 0x47, 0x2b, 0x70, 0xf5,
|
||||||
0x28, 0xb6, 0xca, 0x83, 0xc6, 0xc8, 0x89, 0x1a, 0x74, 0x9f, 0x08, 0xdf, 0x41, 0x6b, 0x46, 0xe7,
|
0x5c, 0x8f, 0xaa, 0xaa, 0x4d, 0x7c, 0xd9, 0x6c, 0x37, 0x06, 0xcd, 0xf0, 0x12, 0xda, 0x33, 0x3a,
|
||||||
0xc8, 0x12, 0x94, 0xb6, 0x30, 0xcf, 0x08, 0xf3, 0xc0, 0xa9, 0x47, 0xe4, 0x08, 0xe2, 0x83, 0x9b,
|
0x47, 0x96, 0xa0, 0xb4, 0xf5, 0x7b, 0x46, 0xbf, 0x07, 0x4e, 0xbd, 0x27, 0x47, 0x10, 0x1f, 0xdc,
|
||||||
0x0b, 0x0d, 0xd1, 0xa5, 0x6e, 0xae, 0x63, 0x9e, 0x54, 0xae, 0xb9, 0x3c, 0x09, 0x87, 0xd0, 0x9a,
|
0x5c, 0x04, 0x0d, 0x1d, 0xba, 0xb9, 0x8e, 0x79, 0x52, 0x59, 0xe7, 0xf2, 0x24, 0x3c, 0x83, 0xf6,
|
||||||
0xcd, 0xef, 0xc2, 0x0a, 0xbf, 0x02, 0x7c, 0x9b, 0xde, 0x7d, 0x7e, 0x8c, 0x46, 0x9e, 0x42, 0x8f,
|
0x6c, 0x7e, 0x1f, 0x56, 0xf8, 0x0a, 0xe0, 0xc7, 0xf4, 0xfe, 0xfa, 0x31, 0x5a, 0xa5, 0xef, 0xaf,
|
||||||
0x8b, 0x42, 0xb1, 0x34, 0x65, 0xe5, 0xb3, 0x8b, 0x79, 0xa2, 0x5b, 0xb7, 0xa9, 0x6f, 0xa7, 0xdf,
|
0x03, 0x8f, 0x67, 0x5c, 0xe2, 0x52, 0x7d, 0xc6, 0xa2, 0x60, 0xd7, 0x78, 0x55, 0xfe, 0x05, 0x97,
|
||||||
0x26, 0xe1, 0x1f, 0x07, 0x1e, 0xcc, 0xb8, 0xc4, 0xa5, 0xfa, 0x80, 0x45, 0xc1, 0xae, 0xf1, 0xaa,
|
0x59, 0x4a, 0x2e, 0xa0, 0x5b, 0xe2, 0xc5, 0x37, 0x1a, 0xb0, 0xf2, 0x67, 0x60, 0xfc, 0xd9, 0x13,
|
||||||
0x7c, 0xa0, 0xcb, 0x2c, 0x25, 0x97, 0xd0, 0x29, 0x5b, 0xc6, 0x37, 0xba, 0x67, 0x65, 0x6d, 0xdf,
|
0x51, 0x9b, 0xf4, 0x25, 0x74, 0x66, 0xb4, 0x7e, 0x60, 0x56, 0xe2, 0x9b, 0x07, 0xb5, 0x07, 0x74,
|
||||||
0x58, 0xbb, 0xe7, 0x42, 0x6d, 0x5e, 0xcf, 0xa0, 0x3d, 0xa3, 0xf5, 0x05, 0x33, 0x4e, 0xdf, 0x5c,
|
0xef, 0x46, 0xd9, 0xbc, 0x43, 0xc7, 0x83, 0xe6, 0xf9, 0xae, 0xb9, 0x46, 0x0e, 0xe0, 0x34, 0x67,
|
||||||
0xa8, 0x6d, 0xa2, 0x7b, 0xc3, 0xca, 0xe2, 0x1d, 0x3a, 0x1e, 0x14, 0xcf, 0x77, 0xc5, 0x35, 0x72,
|
0xdb, 0x34, 0x63, 0x89, 0xf6, 0xc7, 0xa3, 0x75, 0x18, 0xfe, 0x71, 0xa1, 0x5f, 0x6b, 0xae, 0x46,
|
||||||
0x00, 0xe7, 0x39, 0xdb, 0xa6, 0x19, 0x33, 0x3a, 0x3c, 0x5a, 0x87, 0xe1, 0x6f, 0x17, 0x7a, 0x35,
|
0x78, 0xe0, 0x56, 0x5f, 0x40, 0x9f, 0x8b, 0x42, 0xb1, 0x34, 0x65, 0xe5, 0xf1, 0xc5, 0x3c, 0xd1,
|
||||||
0xe7, 0x4a, 0xc2, 0x7f, 0x3e, 0x88, 0x13, 0x1e, 0xb9, 0xa7, 0x3c, 0x22, 0x9f, 0xc0, 0x4f, 0xb4,
|
0x9a, 0x3b, 0xd4, 0xb7, 0xd3, 0x1f, 0x13, 0xf2, 0x15, 0xfc, 0x44, 0x5b, 0x14, 0xaf, 0x0c, 0x41,
|
||||||
0x45, 0xf1, 0xca, 0x34, 0x08, 0x50, 0xef, 0x4b, 0x64, 0x60, 0x8f, 0xba, 0x8f, 0x0f, 0xec, 0xac,
|
0x80, 0xfa, 0x22, 0x22, 0x03, 0x7b, 0xc4, 0x3e, 0x3e, 0xb0, 0xb3, 0x3a, 0x8d, 0xc4, 0xce, 0x91,
|
||||||
0x16, 0x27, 0xb1, 0x73, 0xe4, 0x09, 0xf8, 0xf9, 0x7a, 0x91, 0xf2, 0xe5, 0x0e, 0xf0, 0x87, 0x16,
|
0xe7, 0xe0, 0xe7, 0xeb, 0x45, 0xca, 0x97, 0x3b, 0xc0, 0x5f, 0x7a, 0xa8, 0x9e, 0xc9, 0x56, 0x6d,
|
||||||
0xd5, 0x35, 0xd9, 0xaa, 0x6c, 0xf0, 0x1d, 0xc8, 0x6d, 0xac, 0x13, 0x1b, 0x74, 0x79, 0xb8, 0x41,
|
0xc3, 0x9f, 0x40, 0x6e, 0x63, 0xdd, 0x71, 0x05, 0x17, 0x87, 0x57, 0xf0, 0xb4, 0x72, 0xf1, 0xae,
|
||||||
0x8f, 0x2a, 0x17, 0x4f, 0x4d, 0xd5, 0x5a, 0xa5, 0x45, 0x53, 0xff, 0x8b, 0xa6, 0xff, 0x02, 0x00,
|
0xad, 0x5a, 0xe7, 0xb0, 0x68, 0xe9, 0xaf, 0xcd, 0xf4, 0x7f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xed,
|
||||||
0x00, 0xff, 0xff, 0x42, 0x9c, 0x34, 0x10, 0x9f, 0x04, 0x00, 0x00,
|
0x53, 0xaf, 0x00, 0x81, 0x04, 0x00, 0x00,
|
||||||
}
|
}
|
||||||
|
@ -15,16 +15,17 @@ message Bundle {
|
|||||||
map<string,SignedPreKey> signed_pre_keys = 2;
|
map<string,SignedPreKey> signed_pre_keys = 2;
|
||||||
// Prekey signature
|
// Prekey signature
|
||||||
bytes signature = 4;
|
bytes signature = 4;
|
||||||
|
|
||||||
|
// When the bundle was created locally
|
||||||
|
int64 timestamp = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message BundleContainer {
|
message BundleContainer {
|
||||||
|
reserved 3;
|
||||||
// X3DH prekey bundle
|
// X3DH prekey bundle
|
||||||
Bundle bundle = 1;
|
Bundle bundle = 1;
|
||||||
// Private signed prekey
|
// Private signed prekey
|
||||||
bytes private_signed_pre_key = 2;
|
bytes private_signed_pre_key = 2;
|
||||||
|
|
||||||
// Local time creation
|
|
||||||
int64 timestamp = 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message DRHeader {
|
message DRHeader {
|
||||||
@ -44,12 +45,11 @@ message DHHeader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message X3DHHeader {
|
message X3DHHeader {
|
||||||
|
reserved 3;
|
||||||
// Ephemeral key used
|
// Ephemeral key used
|
||||||
bytes key = 1;
|
bytes key = 1;
|
||||||
// Used bundle's signed prekey
|
// Used bundle's signed prekey
|
||||||
bytes id = 4;
|
bytes id = 4;
|
||||||
// DEPRECATED: The device id
|
|
||||||
string installation_id = 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Direct message value
|
// Direct message value
|
||||||
|
@ -18,6 +18,7 @@ type EncryptionServiceMultiDeviceSuite struct {
|
|||||||
bob1 *EncryptionService
|
bob1 *EncryptionService
|
||||||
alice2 *EncryptionService
|
alice2 *EncryptionService
|
||||||
bob2 *EncryptionService
|
bob2 *EncryptionService
|
||||||
|
alice3 *EncryptionService
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *EncryptionServiceMultiDeviceSuite) SetupTest() {
|
func (s *EncryptionServiceMultiDeviceSuite) SetupTest() {
|
||||||
@ -26,6 +27,8 @@ func (s *EncryptionServiceMultiDeviceSuite) SetupTest() {
|
|||||||
aliceDBKey1 = "alice1"
|
aliceDBKey1 = "alice1"
|
||||||
aliceDBPath2 = "/tmp/alice2.db"
|
aliceDBPath2 = "/tmp/alice2.db"
|
||||||
aliceDBKey2 = "alice2"
|
aliceDBKey2 = "alice2"
|
||||||
|
aliceDBPath3 = "/tmp/alice3.db"
|
||||||
|
aliceDBKey3 = "alice3"
|
||||||
bobDBPath1 = "/tmp/bob1.db"
|
bobDBPath1 = "/tmp/bob1.db"
|
||||||
bobDBKey1 = "bob1"
|
bobDBKey1 = "bob1"
|
||||||
bobDBPath2 = "/tmp/bob2.db"
|
bobDBPath2 = "/tmp/bob2.db"
|
||||||
@ -36,6 +39,7 @@ func (s *EncryptionServiceMultiDeviceSuite) SetupTest() {
|
|||||||
os.Remove(bobDBPath1)
|
os.Remove(bobDBPath1)
|
||||||
os.Remove(aliceDBPath2)
|
os.Remove(aliceDBPath2)
|
||||||
os.Remove(bobDBPath2)
|
os.Remove(bobDBPath2)
|
||||||
|
os.Remove(aliceDBPath3)
|
||||||
|
|
||||||
alicePersistence1, err := NewSQLLitePersistence(aliceDBPath1, aliceDBKey1)
|
alicePersistence1, err := NewSQLLitePersistence(aliceDBPath1, aliceDBKey1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -47,6 +51,11 @@ func (s *EncryptionServiceMultiDeviceSuite) SetupTest() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
alicePersistence3, err := NewSQLLitePersistence(aliceDBPath3, aliceDBKey3)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
bobPersistence1, err := NewSQLLitePersistence(bobDBPath1, bobDBKey1)
|
bobPersistence1, err := NewSQLLitePersistence(bobDBPath1, bobDBKey1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@ -63,41 +72,79 @@ func (s *EncryptionServiceMultiDeviceSuite) SetupTest() {
|
|||||||
s.alice2 = NewEncryptionService(alicePersistence2, "alice2")
|
s.alice2 = NewEncryptionService(alicePersistence2, "alice2")
|
||||||
s.bob2 = NewEncryptionService(bobPersistence2, "bob2")
|
s.bob2 = NewEncryptionService(bobPersistence2, "bob2")
|
||||||
|
|
||||||
|
s.alice3 = NewEncryptionService(alicePersistence3, "alice3")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *EncryptionServiceMultiDeviceSuite) TestProcessPublicBundle() {
|
func (s *EncryptionServiceMultiDeviceSuite) TestProcessPublicBundle() {
|
||||||
aliceKey, err := crypto.GenerateKey()
|
aliceKey, err := crypto.GenerateKey()
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
alice1Bundle, err := s.alice1.CreateBundle(aliceKey)
|
|
||||||
s.Require().NoError(err)
|
|
||||||
|
|
||||||
alice1Identity, err := ExtractIdentity(alice1Bundle)
|
|
||||||
s.Require().NoError(err)
|
|
||||||
|
|
||||||
alice2Bundle, err := s.alice2.CreateBundle(aliceKey)
|
alice2Bundle, err := s.alice2.CreateBundle(aliceKey)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
alice2Identity, err := ExtractIdentity(alice2Bundle)
|
alice2Identity, err := ExtractIdentity(alice2Bundle)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
alice3Bundle, err := s.alice3.CreateBundle(aliceKey)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
alice3Identity, err := ExtractIdentity(alice2Bundle)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// Add alice2 bundle
|
||||||
response, err := s.alice1.ProcessPublicBundle(aliceKey, alice2Bundle)
|
response, err := s.alice1.ProcessPublicBundle(aliceKey, alice2Bundle)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Require().Equal(IdentityAndIDPair{alice2Identity, "alice2"}, response[0])
|
s.Require().Equal(IdentityAndIDPair{alice2Identity, "alice2"}, response[0])
|
||||||
|
|
||||||
|
// Add alice3 bundle
|
||||||
|
response, err = s.alice1.ProcessPublicBundle(aliceKey, alice3Bundle)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().Equal(IdentityAndIDPair{alice3Identity, "alice3"}, response[0])
|
||||||
|
|
||||||
|
// No installation is enabled
|
||||||
alice1MergedBundle1, err := s.alice1.CreateBundle(aliceKey)
|
alice1MergedBundle1, err := s.alice1.CreateBundle(aliceKey)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
s.Require().Equal(1, len(alice1MergedBundle1.GetSignedPreKeys()))
|
||||||
s.Require().NotNil(alice1MergedBundle1.GetSignedPreKeys()["alice1"])
|
s.Require().NotNil(alice1MergedBundle1.GetSignedPreKeys()["alice1"])
|
||||||
s.Require().NotNil(alice1MergedBundle1.GetSignedPreKeys()["alice2"])
|
|
||||||
|
|
||||||
response, err = s.alice1.ProcessPublicBundle(aliceKey, alice1MergedBundle1)
|
// We enable the installations
|
||||||
|
err = s.alice1.EnableInstallation(&aliceKey.PublicKey, "alice2")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
err = s.alice1.EnableInstallation(&aliceKey.PublicKey, "alice3")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
alice1MergedBundle2, err := s.alice1.CreateBundle(aliceKey)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// We get back a bundle with all the installations
|
||||||
|
s.Require().Equal(3, len(alice1MergedBundle2.GetSignedPreKeys()))
|
||||||
|
s.Require().NotNil(alice1MergedBundle2.GetSignedPreKeys()["alice1"])
|
||||||
|
s.Require().NotNil(alice1MergedBundle2.GetSignedPreKeys()["alice2"])
|
||||||
|
s.Require().NotNil(alice1MergedBundle2.GetSignedPreKeys()["alice3"])
|
||||||
|
|
||||||
|
response, err = s.alice1.ProcessPublicBundle(aliceKey, alice1MergedBundle2)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
sort.Slice(response, func(i, j int) bool {
|
sort.Slice(response, func(i, j int) bool {
|
||||||
return response[i][1] < response[j][1]
|
return response[i][1] < response[j][1]
|
||||||
})
|
})
|
||||||
s.Require().Equal(IdentityAndIDPair{alice1Identity, "alice1"}, response[0])
|
// We only get back installationIDs not equal to us
|
||||||
s.Require().Equal(IdentityAndIDPair{alice2Identity, "alice2"}, response[1])
|
s.Require().Equal(2, len(response))
|
||||||
|
s.Require().Equal(IdentityAndIDPair{alice2Identity, "alice2"}, response[0])
|
||||||
|
s.Require().Equal(IdentityAndIDPair{alice2Identity, "alice3"}, response[1])
|
||||||
|
|
||||||
|
// We disable the installations
|
||||||
|
err = s.alice1.DisableInstallation(&aliceKey.PublicKey, "alice2")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
alice1MergedBundle3, err := s.alice1.CreateBundle(aliceKey)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// We get back a bundle with all the installations
|
||||||
|
s.Require().Equal(2, len(alice1MergedBundle3.GetSignedPreKeys()))
|
||||||
|
s.Require().NotNil(alice1MergedBundle3.GetSignedPreKeys()["alice1"])
|
||||||
|
s.Require().NotNil(alice1MergedBundle3.GetSignedPreKeys()["alice3"])
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *EncryptionServiceMultiDeviceSuite) TestProcessPublicBundleOutOfOrder() {
|
func (s *EncryptionServiceMultiDeviceSuite) TestProcessPublicBundleOutOfOrder() {
|
||||||
@ -116,10 +163,14 @@ func (s *EncryptionServiceMultiDeviceSuite) TestProcessPublicBundleOutOfOrder()
|
|||||||
_, err = s.alice2.CreateBundle(aliceKey)
|
_, err = s.alice2.CreateBundle(aliceKey)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
// It should contain both bundles
|
// We enable the installation
|
||||||
alice1MergedBundle1, err := s.alice2.CreateBundle(aliceKey)
|
err = s.alice2.EnableInstallation(&aliceKey.PublicKey, "alice1")
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
s.Require().NotNil(alice1MergedBundle1.GetSignedPreKeys()["alice1"])
|
// It should contain both bundles
|
||||||
s.Require().NotNil(alice1MergedBundle1.GetSignedPreKeys()["alice2"])
|
alice2MergedBundle1, err := s.alice2.CreateBundle(aliceKey)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
s.Require().NotNil(alice2MergedBundle1.GetSignedPreKeys()["alice1"])
|
||||||
|
s.Require().NotNil(alice2MergedBundle1.GetSignedPreKeys()["alice2"])
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
// 1539249977_update_ratchet_info.up.sql
|
// 1539249977_update_ratchet_info.up.sql
|
||||||
// 1540715431_add_version.down.sql
|
// 1540715431_add_version.down.sql
|
||||||
// 1540715431_add_version.up.sql
|
// 1540715431_add_version.up.sql
|
||||||
|
// 1541164797_add_installations.down.sql
|
||||||
|
// 1541164797_add_installations.up.sql
|
||||||
// static.go
|
// static.go
|
||||||
// DO NOT EDIT!
|
// DO NOT EDIT!
|
||||||
|
|
||||||
@ -169,7 +171,7 @@ func _1540715431_add_versionDownSql() (*asset, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "1540715431_add_version.down.sql", size: 127, mode: os.FileMode(420), modTime: time.Unix(1540989119, 0)}
|
info := bindataFileInfo{name: "1540715431_add_version.down.sql", size: 127, mode: os.FileMode(420), modTime: time.Unix(1541170185, 0)}
|
||||||
a := &asset{bytes: bytes, info: info}
|
a := &asset{bytes: bytes, info: info}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
@ -189,7 +191,47 @@ func _1540715431_add_versionUpSql() (*asset, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "1540715431_add_version.up.sql", size: 265, mode: os.FileMode(420), modTime: time.Unix(1540989075, 0)}
|
info := bindataFileInfo{name: "1540715431_add_version.up.sql", size: 265, mode: os.FileMode(420), modTime: time.Unix(1541170185, 0)}
|
||||||
|
a := &asset{bytes: bytes, info: info}
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var __1541164797_add_installationsDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x09\xf2\x0f\x50\x08\x71\x74\xf2\x71\x55\xc8\xcc\x2b\x2e\x49\xcc\xc9\x49\x2c\xc9\xcc\xcf\x2b\xb6\xe6\x02\x04\x00\x00\xff\xff\xd8\xbf\x14\x75\x1a\x00\x00\x00")
|
||||||
|
|
||||||
|
func _1541164797_add_installationsDownSqlBytes() ([]byte, error) {
|
||||||
|
return bindataRead(
|
||||||
|
__1541164797_add_installationsDownSql,
|
||||||
|
"1541164797_add_installations.down.sql",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _1541164797_add_installationsDownSql() (*asset, error) {
|
||||||
|
bytes, err := _1541164797_add_installationsDownSqlBytes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info := bindataFileInfo{name: "1541164797_add_installations.down.sql", size: 26, mode: os.FileMode(420), modTime: time.Unix(1541165366, 0)}
|
||||||
|
a := &asset{bytes: bytes, info: info}
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var __1541164797_add_installationsUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x5c\xce\xb1\x6a\xc3\x30\x14\x85\xe1\xdd\x4f\x71\x46\x1b\xbc\x74\xee\x24\xc9\xd7\x46\x70\xb9\x6a\x5d\x09\xba\x05\x05\x6b\x10\xd8\x4a\xc0\x5a\xf2\xf6\xc1\x43\x20\xce\xfc\x7f\x70\x8e\x99\x49\x79\x82\x57\x9a\x09\xb9\xec\x35\xae\x6b\xac\xf9\x56\x76\xa0\x6d\x80\xbc\xa4\x52\x73\x7d\x40\xb3\xd3\x10\xe7\x21\x81\xb9\x3f\xca\x1b\xbe\xe4\x05\x9e\xfe\xfd\x09\xd4\xbc\xa5\xbd\xc6\xed\x8e\x20\x7f\x76\x12\x1a\xa0\xed\x04\x2b\x67\x96\x4a\xbc\xae\x69\x81\x76\x8e\x49\x09\x06\x1a\x55\x60\x8f\xaf\x23\x06\xb1\xbf\x81\xda\xd7\x8b\xfe\x73\xb5\x83\x13\x18\x27\x23\x5b\xe3\x31\xd3\x0f\x2b\x43\x4d\xf7\xdd\x3c\x03\x00\x00\xff\xff\x28\x14\xac\x9d\xd8\x00\x00\x00")
|
||||||
|
|
||||||
|
func _1541164797_add_installationsUpSqlBytes() ([]byte, error) {
|
||||||
|
return bindataRead(
|
||||||
|
__1541164797_add_installationsUpSql,
|
||||||
|
"1541164797_add_installations.up.sql",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _1541164797_add_installationsUpSql() (*asset, error) {
|
||||||
|
bytes, err := _1541164797_add_installationsUpSqlBytes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info := bindataFileInfo{name: "1541164797_add_installations.up.sql", size: 216, mode: os.FileMode(420), modTime: time.Unix(1541165467, 0)}
|
||||||
a := &asset{bytes: bytes, info: info}
|
a := &asset{bytes: bytes, info: info}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
@ -272,6 +314,8 @@ var _bindata = map[string]func() (*asset, error){
|
|||||||
"1539249977_update_ratchet_info.up.sql": _1539249977_update_ratchet_infoUpSql,
|
"1539249977_update_ratchet_info.up.sql": _1539249977_update_ratchet_infoUpSql,
|
||||||
"1540715431_add_version.down.sql": _1540715431_add_versionDownSql,
|
"1540715431_add_version.down.sql": _1540715431_add_versionDownSql,
|
||||||
"1540715431_add_version.up.sql": _1540715431_add_versionUpSql,
|
"1540715431_add_version.up.sql": _1540715431_add_versionUpSql,
|
||||||
|
"1541164797_add_installations.down.sql": _1541164797_add_installationsDownSql,
|
||||||
|
"1541164797_add_installations.up.sql": _1541164797_add_installationsUpSql,
|
||||||
"static.go": staticGo,
|
"static.go": staticGo,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,6 +365,8 @@ var _bintree = &bintree{nil, map[string]*bintree{
|
|||||||
"1539249977_update_ratchet_info.up.sql": &bintree{_1539249977_update_ratchet_infoUpSql, map[string]*bintree{}},
|
"1539249977_update_ratchet_info.up.sql": &bintree{_1539249977_update_ratchet_infoUpSql, map[string]*bintree{}},
|
||||||
"1540715431_add_version.down.sql": &bintree{_1540715431_add_versionDownSql, map[string]*bintree{}},
|
"1540715431_add_version.down.sql": &bintree{_1540715431_add_versionDownSql, map[string]*bintree{}},
|
||||||
"1540715431_add_version.up.sql": &bintree{_1540715431_add_versionUpSql, map[string]*bintree{}},
|
"1540715431_add_version.up.sql": &bintree{_1540715431_add_versionUpSql, map[string]*bintree{}},
|
||||||
|
"1541164797_add_installations.down.sql": &bintree{_1541164797_add_installationsDownSql, map[string]*bintree{}},
|
||||||
|
"1541164797_add_installations.up.sql": &bintree{_1541164797_add_installationsUpSql, map[string]*bintree{}},
|
||||||
"static.go": &bintree{staticGo, map[string]*bintree{}},
|
"static.go": &bintree{staticGo, map[string]*bintree{}},
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
@ -20,32 +20,41 @@ type RatchetInfo struct {
|
|||||||
|
|
||||||
// PersistenceService defines the interface for a storage service
|
// PersistenceService defines the interface for a storage service
|
||||||
type PersistenceService interface {
|
type PersistenceService interface {
|
||||||
// GetKeysStorage returns the associated double ratchet KeysStorage object
|
// GetKeysStorage returns the associated double ratchet KeysStorage object.
|
||||||
GetKeysStorage() dr.KeysStorage
|
GetKeysStorage() dr.KeysStorage
|
||||||
// GetSessionStorage returns the associated double ratchet SessionStorage object
|
// GetSessionStorage returns the associated double ratchet SessionStorage object.
|
||||||
GetSessionStorage() dr.SessionStorage
|
GetSessionStorage() dr.SessionStorage
|
||||||
|
|
||||||
// GetPublicBundle retrieves an existing Bundle for the specified public key
|
// GetPublicBundle retrieves an existing Bundle for the specified public key & installationIDs.
|
||||||
GetPublicBundle(*ecdsa.PublicKey) (*Bundle, error)
|
GetPublicBundle(*ecdsa.PublicKey, []string) (*Bundle, error)
|
||||||
// AddPublicBundle persists a specified Bundle
|
// AddPublicBundle persists a specified Bundle
|
||||||
AddPublicBundle(*Bundle) error
|
AddPublicBundle(*Bundle) error
|
||||||
|
|
||||||
// GetAnyPrivateBundle retrieves any bundle containing a private key
|
// GetAnyPrivateBundle retrieves any bundle for our identity & installationIDs
|
||||||
GetAnyPrivateBundle([]byte) (*BundleContainer, error)
|
GetAnyPrivateBundle([]byte, []string) (*BundleContainer, error)
|
||||||
// GetPrivateKeyBundle retrieves a BundleContainer with the specified signed prekey
|
// GetPrivateKeyBundle retrieves a BundleContainer with the specified signed prekey.
|
||||||
GetPrivateKeyBundle([]byte) ([]byte, error)
|
GetPrivateKeyBundle([]byte) ([]byte, error)
|
||||||
// AddPrivateBundle persists a BundleContainer
|
// AddPrivateBundle persists a BundleContainer.
|
||||||
AddPrivateBundle(*BundleContainer) error
|
AddPrivateBundle(*BundleContainer) error
|
||||||
// MarkBundleExpired marks a bundle as expired, not to be used for encryption anymore
|
// MarkBundleExpired marks a private bundle as expired, not to be used for encryption anymore.
|
||||||
MarkBundleExpired([]byte) error
|
MarkBundleExpired([]byte) error
|
||||||
|
|
||||||
// AddRatchetInfo persists the specified ratchet info
|
// AddRatchetInfo persists the specified ratchet info
|
||||||
AddRatchetInfo([]byte, []byte, []byte, []byte, string) error
|
AddRatchetInfo([]byte, []byte, []byte, []byte, string) error
|
||||||
// GetRatchetInfo retrieves the existing RatchetInfo for a specified bundle ID and interlocutor public key
|
// GetRatchetInfo retrieves the existing RatchetInfo for a specified bundle ID and interlocutor public key.
|
||||||
GetRatchetInfo([]byte, []byte, string) (*RatchetInfo, error)
|
GetRatchetInfo([]byte, []byte, string) (*RatchetInfo, error)
|
||||||
// GetAnyRatchetInfo retrieves any existing RatchetInfo for a specified interlocutor public key
|
// GetAnyRatchetInfo retrieves any existing RatchetInfo for a specified interlocutor public key.
|
||||||
GetAnyRatchetInfo([]byte, string) (*RatchetInfo, error)
|
GetAnyRatchetInfo([]byte, string) (*RatchetInfo, error)
|
||||||
// RatchetInfoConfirmed clears the ephemeral key in the RatchetInfo
|
// RatchetInfoConfirmed clears the ephemeral key in the RatchetInfo
|
||||||
// associated with the specified bundle ID and interlocutor identity public key
|
// associated with the specified bundle ID and interlocutor identity public key.
|
||||||
RatchetInfoConfirmed([]byte, []byte, string) error
|
RatchetInfoConfirmed([]byte, []byte, string) error
|
||||||
|
|
||||||
|
// GetActiveInstallations returns the active installations for a given identity.
|
||||||
|
GetActiveInstallations(maxInstallations uint, identity []byte) ([]string, error)
|
||||||
|
// AddInstallations adds the installations for a given identity.
|
||||||
|
AddInstallations(identity []byte, timestamp int64, installationIDs []string, enabled bool) error
|
||||||
|
// EnableInstallation enables the installation.
|
||||||
|
EnableInstallation(identity []byte, installationID string) error
|
||||||
|
// DisableInstallation disable the installation.
|
||||||
|
DisableInstallation(identity []byte, installationID string) error
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ func (p *ProtocolService) BuildDirectMessage(myIdentityKey *ecdsa.PrivateKey, pa
|
|||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildPairingMessage sends a message to our own devices using DH so that it can be decrypted by any other device
|
// 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) {
|
func (p *ProtocolService) BuildPairingMessage(myIdentityKey *ecdsa.PrivateKey, payload []byte) ([]byte, error) {
|
||||||
// Encrypt payload
|
// Encrypt payload
|
||||||
encryptionResponse, err := p.encryption.EncryptPayloadWithDH(&myIdentityKey.PublicKey, payload)
|
encryptionResponse, err := p.encryption.EncryptPayloadWithDH(&myIdentityKey.PublicKey, payload)
|
||||||
@ -105,17 +105,27 @@ func (p *ProtocolService) BuildPairingMessage(myIdentityKey *ecdsa.PrivateKey, p
|
|||||||
return p.addBundleAndMarshal(myIdentityKey, protocolMessage)
|
return p.addBundleAndMarshal(myIdentityKey, protocolMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProcessPublicBundle processes a received X3DH bundle
|
// ProcessPublicBundle processes a received X3DH bundle.
|
||||||
func (p *ProtocolService) ProcessPublicBundle(myIdentityKey *ecdsa.PrivateKey, bundle *Bundle) ([]IdentityAndIDPair, error) {
|
func (p *ProtocolService) ProcessPublicBundle(myIdentityKey *ecdsa.PrivateKey, bundle *Bundle) ([]IdentityAndIDPair, error) {
|
||||||
return p.encryption.ProcessPublicBundle(myIdentityKey, bundle)
|
return p.encryption.ProcessPublicBundle(myIdentityKey, bundle)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBundle retrieves or creates a X3DH bundle, given a private identity key
|
// GetBundle retrieves or creates a X3DH bundle, given a private identity key.
|
||||||
func (p *ProtocolService) GetBundle(myIdentityKey *ecdsa.PrivateKey) (*Bundle, error) {
|
func (p *ProtocolService) GetBundle(myIdentityKey *ecdsa.PrivateKey) (*Bundle, error) {
|
||||||
return p.encryption.CreateBundle(myIdentityKey)
|
return p.encryption.CreateBundle(myIdentityKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleMessage unmarshals a message and processes it, decrypting it if it is a 1:1 message
|
// EnableInstallation enables an installation for multi-device sync.
|
||||||
|
func (p *ProtocolService) EnableInstallation(myIdentityKey *ecdsa.PublicKey, installationID string) error {
|
||||||
|
return p.encryption.EnableInstallation(myIdentityKey, installationID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableInstallation disables an installation for multi-device sync.
|
||||||
|
func (p *ProtocolService) DisableInstallation(myIdentityKey *ecdsa.PublicKey, installationID string) error {
|
||||||
|
return p.encryption.DisableInstallation(myIdentityKey, installationID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) (*HandleMessageResponse, error) {
|
func (p *ProtocolService) HandleMessage(myIdentityKey *ecdsa.PrivateKey, theirPublicKey *ecdsa.PublicKey, payload []byte) (*HandleMessageResponse, error) {
|
||||||
if p.encryption == nil {
|
if p.encryption == nil {
|
||||||
return nil, errors.New("encryption service not initialized")
|
return nil, errors.New("encryption service not initialized")
|
||||||
|
@ -3,7 +3,7 @@ package chat
|
|||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"time"
|
"strings"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
|
||||||
@ -103,13 +103,13 @@ func (s *SQLLitePersistence) Open(path string, key string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AddPrivateBundle adds the specified BundleContainer to the database
|
// AddPrivateBundle adds the specified BundleContainer to the database
|
||||||
func (s *SQLLitePersistence) AddPrivateBundle(b *BundleContainer) error {
|
func (s *SQLLitePersistence) AddPrivateBundle(bc *BundleContainer) error {
|
||||||
tx, err := s.db.Begin()
|
tx, err := s.db.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for installationID, signedPreKey := range b.GetBundle().GetSignedPreKeys() {
|
for installationID, signedPreKey := range bc.GetBundle().GetSignedPreKeys() {
|
||||||
var version uint32
|
var version uint32
|
||||||
stmt, err := tx.Prepare("SELECT version FROM bundles WHERE installation_id = ? AND identity = ? ORDER BY version DESC LIMIT 1")
|
stmt, err := tx.Prepare("SELECT version FROM bundles WHERE installation_id = ? AND identity = ? ORDER BY version DESC LIMIT 1")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -118,7 +118,7 @@ func (s *SQLLitePersistence) AddPrivateBundle(b *BundleContainer) error {
|
|||||||
|
|
||||||
defer stmt.Close()
|
defer stmt.Close()
|
||||||
|
|
||||||
err = stmt.QueryRow(installationID, b.GetBundle().GetIdentity()).Scan(&version)
|
err = stmt.QueryRow(installationID, bc.GetBundle().GetIdentity()).Scan(&version)
|
||||||
if err != nil && err != sql.ErrNoRows {
|
if err != nil && err != sql.ErrNoRows {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -130,12 +130,12 @@ func (s *SQLLitePersistence) AddPrivateBundle(b *BundleContainer) error {
|
|||||||
defer stmt.Close()
|
defer stmt.Close()
|
||||||
|
|
||||||
_, err = stmt.Exec(
|
_, err = stmt.Exec(
|
||||||
b.GetBundle().GetIdentity(),
|
bc.GetBundle().GetIdentity(),
|
||||||
b.GetPrivateSignedPreKey(),
|
bc.GetPrivateSignedPreKey(),
|
||||||
signedPreKey.GetSignedPreKey(),
|
signedPreKey.GetSignedPreKey(),
|
||||||
installationID,
|
installationID,
|
||||||
version+1,
|
version+1,
|
||||||
time.Now().UnixNano(),
|
bc.GetBundle().GetTimestamp(),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = tx.Rollback()
|
_ = tx.Rollback()
|
||||||
@ -173,7 +173,7 @@ func (s *SQLLitePersistence) AddPublicBundle(b *Bundle) error {
|
|||||||
signedPreKey,
|
signedPreKey,
|
||||||
installationID,
|
installationID,
|
||||||
version,
|
version,
|
||||||
time.Now().UnixNano(),
|
b.GetTimestamp(),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = tx.Rollback()
|
_ = tx.Rollback()
|
||||||
@ -202,8 +202,11 @@ func (s *SQLLitePersistence) AddPublicBundle(b *Bundle) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetAnyPrivateBundle retrieves any bundle from the database containing a private key
|
// GetAnyPrivateBundle retrieves any bundle from the database containing a private key
|
||||||
func (s *SQLLitePersistence) GetAnyPrivateBundle(myIdentityKey []byte) (*BundleContainer, error) {
|
func (s *SQLLitePersistence) GetAnyPrivateBundle(myIdentityKey []byte, installationIDs []string) (*BundleContainer, error) {
|
||||||
stmt, err := s.db.Prepare("SELECT identity, private_key, signed_pre_key, installation_id, timestamp FROM bundles WHERE identity = ? AND expired = 0")
|
|
||||||
|
/* #nosec */
|
||||||
|
statement := "SELECT identity, private_key, signed_pre_key, installation_id, timestamp FROM bundles WHERE expired = 0 AND identity = ? AND installation_id IN (?" + strings.Repeat(",?", len(installationIDs)-1) + ")"
|
||||||
|
stmt, err := s.db.Prepare(statement)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -213,7 +216,13 @@ func (s *SQLLitePersistence) GetAnyPrivateBundle(myIdentityKey []byte) (*BundleC
|
|||||||
var identity []byte
|
var identity []byte
|
||||||
var privateKey []byte
|
var privateKey []byte
|
||||||
|
|
||||||
rows, err := stmt.Query(myIdentityKey)
|
args := make([]interface{}, len(installationIDs)+1)
|
||||||
|
args[0] = myIdentityKey
|
||||||
|
for i, installationID := range installationIDs {
|
||||||
|
args[i+1] = installationID
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := stmt.Query(args...)
|
||||||
rowCount := 0
|
rowCount := 0
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -246,7 +255,7 @@ func (s *SQLLitePersistence) GetAnyPrivateBundle(myIdentityKey []byte) (*BundleC
|
|||||||
}
|
}
|
||||||
// If there is a private key, we set the timestamp of the bundle container
|
// If there is a private key, we set the timestamp of the bundle container
|
||||||
if privateKey != nil {
|
if privateKey != nil {
|
||||||
bundleContainer.Timestamp = timestamp
|
bundle.Timestamp = timestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
bundle.SignedPreKeys[installationID] = &SignedPreKey{SignedPreKey: signedPreKey}
|
bundle.SignedPreKeys[installationID] = &SignedPreKey{SignedPreKey: signedPreKey}
|
||||||
@ -254,7 +263,7 @@ func (s *SQLLitePersistence) GetAnyPrivateBundle(myIdentityKey []byte) (*BundleC
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If no records are found or no record with private key, return nil
|
// If no records are found or no record with private key, return nil
|
||||||
if rowCount == 0 || bundleContainer.Timestamp == 0 {
|
if rowCount == 0 || bundleContainer.GetBundle().Timestamp == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,10 +292,9 @@ func (s *SQLLitePersistence) GetPrivateKeyBundle(bundleID []byte) ([]byte, error
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RatchetInfoConfirmed clears the ephemeral key in the RatchetInfo
|
// MarkBundleExpired expires any private bundle for a given identity
|
||||||
// associated with the specified bundle ID and interlocutor identity public key
|
|
||||||
func (s *SQLLitePersistence) MarkBundleExpired(identity []byte) error {
|
func (s *SQLLitePersistence) MarkBundleExpired(identity []byte) error {
|
||||||
stmt, err := s.db.Prepare("UPDATE bundles SET expired = 1 WHERE identity = ?")
|
stmt, err := s.db.Prepare("UPDATE bundles SET expired = 1 WHERE identity = ? AND private_key IS NOT NULL")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -298,16 +306,29 @@ func (s *SQLLitePersistence) MarkBundleExpired(identity []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetPublicBundle retrieves an existing Bundle for the specified public key from the database
|
// GetPublicBundle retrieves an existing Bundle for the specified public key from the database
|
||||||
func (s *SQLLitePersistence) GetPublicBundle(publicKey *ecdsa.PublicKey) (*Bundle, error) {
|
func (s *SQLLitePersistence) GetPublicBundle(publicKey *ecdsa.PublicKey, installationIDs []string) (*Bundle, error) {
|
||||||
|
|
||||||
|
if len(installationIDs) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
identity := crypto.CompressPubkey(publicKey)
|
identity := crypto.CompressPubkey(publicKey)
|
||||||
stmt, err := s.db.Prepare("SELECT signed_pre_key,installation_id, version FROM bundles WHERE expired = 0 AND identity = ? ORDER BY version DESC")
|
|
||||||
|
/* #nosec */
|
||||||
|
statement := "SELECT signed_pre_key,installation_id, version FROM bundles WHERE expired = 0 AND identity = ? AND installation_id IN (?" + strings.Repeat(",?", len(installationIDs)-1) + ") ORDER BY version DESC"
|
||||||
|
stmt, err := s.db.Prepare(statement)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer stmt.Close()
|
defer stmt.Close()
|
||||||
|
|
||||||
rows, err := stmt.Query(identity)
|
args := make([]interface{}, len(installationIDs)+1)
|
||||||
|
args[0] = identity
|
||||||
|
for i, installationID := range installationIDs {
|
||||||
|
args[i+1] = installationID
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := stmt.Query(args...)
|
||||||
rowCount := 0
|
rowCount := 0
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -687,6 +708,126 @@ func (s *SQLLiteSessionStorage) Load(id []byte) (*dr.State, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetActiveInstallations returns the active installations for a given identity
|
||||||
|
func (s *SQLLitePersistence) GetActiveInstallations(maxInstallations uint, identity []byte) ([]string, error) {
|
||||||
|
stmt, err := s.db.Prepare("SELECT installation_id FROM installations WHERE enabled = 1 AND identity = ? ORDER BY timestamp DESC LIMIT ?")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var installations []string
|
||||||
|
rows, err := stmt.Query(identity, maxInstallations)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var installationID string
|
||||||
|
err = rows.Scan(
|
||||||
|
&installationID,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
installations = append(installations, installationID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return installations, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddInstallations adds the installations for a given identity, maintaining the enabled flag
|
||||||
|
func (s *SQLLitePersistence) AddInstallations(identity []byte, timestamp int64, installationIDs []string, defaultEnabled bool) error {
|
||||||
|
tx, err := s.db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, installationID := range installationIDs {
|
||||||
|
stmt, err := tx.Prepare("SELECT enabled FROM installations WHERE identity = ? AND installation_id = ? LIMIT 1")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
|
||||||
|
var oldEnabled bool
|
||||||
|
|
||||||
|
err = stmt.QueryRow(identity, installationID).Scan(&oldEnabled)
|
||||||
|
if err != nil && err != sql.ErrNoRows {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// We update timestamp if present without changing enabled
|
||||||
|
if err != sql.ErrNoRows {
|
||||||
|
stmt, err = tx.Prepare("UPDATE installations SET timestamp = ?, enabled = ? WHERE identity = ? AND installation_id = ?")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = stmt.Exec(
|
||||||
|
timestamp,
|
||||||
|
oldEnabled,
|
||||||
|
identity,
|
||||||
|
installationID,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
|
||||||
|
} else {
|
||||||
|
stmt, err = tx.Prepare("INSERT INTO installations(identity, installation_id, timestamp, enabled) VALUES (?, ?, ?, ?)")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = stmt.Exec(
|
||||||
|
identity,
|
||||||
|
installationID,
|
||||||
|
timestamp,
|
||||||
|
defaultEnabled,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Commit(); err != nil {
|
||||||
|
_ = tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableInstallation enables the installation
|
||||||
|
func (s *SQLLitePersistence) EnableInstallation(identity []byte, installationID string) error {
|
||||||
|
stmt, err := s.db.Prepare("UPDATE installations SET enabled = 1 WHERE identity = ? AND installation_id = ?")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = stmt.Exec(identity, installationID)
|
||||||
|
return err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableInstallation disable the installation
|
||||||
|
func (s *SQLLitePersistence) DisableInstallation(identity []byte, installationID string) error {
|
||||||
|
|
||||||
|
stmt, err := s.db.Prepare("UPDATE installations SET enabled = 0 WHERE identity = ? AND installation_id = ?")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = stmt.Exec(identity, installationID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func toKey(a []byte) dr.Key {
|
func toKey(a []byte) dr.Key {
|
||||||
var k [32]byte
|
var k [32]byte
|
||||||
copy(k[:], a)
|
copy(k[:], a)
|
||||||
|
@ -54,7 +54,7 @@ func (s *SQLLitePersistenceTestSuite) TestPrivateBundle() {
|
|||||||
s.Require().NoError(err, "Error was not returned even though bundle is not there")
|
s.Require().NoError(err, "Error was not returned even though bundle is not there")
|
||||||
s.Nil(actualKey)
|
s.Nil(actualKey)
|
||||||
|
|
||||||
anyPrivateBundle, err := s.service.GetAnyPrivateBundle([]byte("non-existing-id"))
|
anyPrivateBundle, err := s.service.GetAnyPrivateBundle([]byte("non-existing-id"), []string{installationID})
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Nil(anyPrivateBundle)
|
s.Nil(anyPrivateBundle)
|
||||||
|
|
||||||
@ -71,7 +71,7 @@ func (s *SQLLitePersistenceTestSuite) TestPrivateBundle() {
|
|||||||
s.Equal(bundle.GetPrivateSignedPreKey(), actualKey, "It returns the same key")
|
s.Equal(bundle.GetPrivateSignedPreKey(), actualKey, "It returns the same key")
|
||||||
|
|
||||||
identity := crypto.CompressPubkey(&key.PublicKey)
|
identity := crypto.CompressPubkey(&key.PublicKey)
|
||||||
anyPrivateBundle, err = s.service.GetAnyPrivateBundle(identity)
|
anyPrivateBundle, err = s.service.GetAnyPrivateBundle(identity, []string{installationID})
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.NotNil(anyPrivateBundle)
|
s.NotNil(anyPrivateBundle)
|
||||||
s.True(proto.Equal(bundle.GetBundle(), anyPrivateBundle.GetBundle()), "It returns the same bundle")
|
s.True(proto.Equal(bundle.GetBundle(), anyPrivateBundle.GetBundle()), "It returns the same bundle")
|
||||||
@ -81,7 +81,7 @@ func (s *SQLLitePersistenceTestSuite) TestPublicBundle() {
|
|||||||
key, err := crypto.GenerateKey()
|
key, err := crypto.GenerateKey()
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
actualBundle, err := s.service.GetPublicBundle(&key.PublicKey)
|
actualBundle, err := s.service.GetPublicBundle(&key.PublicKey, []string{"1"})
|
||||||
s.Require().NoError(err, "Error was not returned even though bundle is not there")
|
s.Require().NoError(err, "Error was not returned even though bundle is not there")
|
||||||
s.Nil(actualBundle)
|
s.Nil(actualBundle)
|
||||||
|
|
||||||
@ -92,7 +92,7 @@ func (s *SQLLitePersistenceTestSuite) TestPublicBundle() {
|
|||||||
err = s.service.AddPublicBundle(bundle)
|
err = s.service.AddPublicBundle(bundle)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
actualBundle, err = s.service.GetPublicBundle(&key.PublicKey)
|
actualBundle, err = s.service.GetPublicBundle(&key.PublicKey, []string{"1"})
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Equal(bundle.GetIdentity(), actualBundle.GetIdentity(), "It sets the right identity")
|
s.Equal(bundle.GetIdentity(), actualBundle.GetIdentity(), "It sets the right identity")
|
||||||
s.Equal(bundle.GetSignedPreKeys(), actualBundle.GetSignedPreKeys(), "It sets the right prekeys")
|
s.Equal(bundle.GetSignedPreKeys(), actualBundle.GetSignedPreKeys(), "It sets the right prekeys")
|
||||||
@ -102,7 +102,7 @@ func (s *SQLLitePersistenceTestSuite) TestUpdatedBundle() {
|
|||||||
key, err := crypto.GenerateKey()
|
key, err := crypto.GenerateKey()
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
actualBundle, err := s.service.GetPublicBundle(&key.PublicKey)
|
actualBundle, err := s.service.GetPublicBundle(&key.PublicKey, []string{"1"})
|
||||||
s.Require().NoError(err, "Error was not returned even though bundle is not there")
|
s.Require().NoError(err, "Error was not returned even though bundle is not there")
|
||||||
s.Nil(actualBundle)
|
s.Nil(actualBundle)
|
||||||
|
|
||||||
@ -124,7 +124,7 @@ func (s *SQLLitePersistenceTestSuite) TestUpdatedBundle() {
|
|||||||
err = s.service.AddPublicBundle(bundle)
|
err = s.service.AddPublicBundle(bundle)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
actualBundle, err = s.service.GetPublicBundle(&key.PublicKey)
|
actualBundle, err = s.service.GetPublicBundle(&key.PublicKey, []string{"1"})
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Equal(bundle.GetIdentity(), actualBundle.GetIdentity(), "It sets the right identity")
|
s.Equal(bundle.GetIdentity(), actualBundle.GetIdentity(), "It sets the right identity")
|
||||||
s.Equal(bundle.GetSignedPreKeys(), actualBundle.GetSignedPreKeys(), "It sets the right prekeys")
|
s.Equal(bundle.GetSignedPreKeys(), actualBundle.GetSignedPreKeys(), "It sets the right prekeys")
|
||||||
@ -134,7 +134,7 @@ func (s *SQLLitePersistenceTestSuite) TestOutOfOrderBundles() {
|
|||||||
key, err := crypto.GenerateKey()
|
key, err := crypto.GenerateKey()
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
actualBundle, err := s.service.GetPublicBundle(&key.PublicKey)
|
actualBundle, err := s.service.GetPublicBundle(&key.PublicKey, []string{"1"})
|
||||||
s.Require().NoError(err, "Error was not returned even though bundle is not there")
|
s.Require().NoError(err, "Error was not returned even though bundle is not there")
|
||||||
s.Nil(actualBundle)
|
s.Nil(actualBundle)
|
||||||
|
|
||||||
@ -161,7 +161,7 @@ func (s *SQLLitePersistenceTestSuite) TestOutOfOrderBundles() {
|
|||||||
err = s.service.AddPublicBundle(bundle1)
|
err = s.service.AddPublicBundle(bundle1)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
actualBundle, err = s.service.GetPublicBundle(&key.PublicKey)
|
actualBundle, err = s.service.GetPublicBundle(&key.PublicKey, []string{"1"})
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Equal(bundle2.GetIdentity(), actualBundle.GetIdentity(), "It sets the right identity")
|
s.Equal(bundle2.GetIdentity(), actualBundle.GetIdentity(), "It sets the right identity")
|
||||||
s.Equal(bundle2.GetSignedPreKeys()["1"].GetVersion(), uint32(1))
|
s.Equal(bundle2.GetSignedPreKeys()["1"].GetVersion(), uint32(1))
|
||||||
@ -172,7 +172,7 @@ func (s *SQLLitePersistenceTestSuite) TestMultiplePublicBundle() {
|
|||||||
key, err := crypto.GenerateKey()
|
key, err := crypto.GenerateKey()
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
actualBundle, err := s.service.GetPublicBundle(&key.PublicKey)
|
actualBundle, err := s.service.GetPublicBundle(&key.PublicKey, []string{"1"})
|
||||||
s.Require().NoError(err, "Error was not returned even though bundle is not there")
|
s.Require().NoError(err, "Error was not returned even though bundle is not there")
|
||||||
s.Nil(actualBundle)
|
s.Nil(actualBundle)
|
||||||
|
|
||||||
@ -198,7 +198,7 @@ func (s *SQLLitePersistenceTestSuite) TestMultiplePublicBundle() {
|
|||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
// Returns the most recent bundle
|
// Returns the most recent bundle
|
||||||
actualBundle, err = s.service.GetPublicBundle(&key.PublicKey)
|
actualBundle, err = s.service.GetPublicBundle(&key.PublicKey, []string{"1"})
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
s.Equal(bundle.GetIdentity(), actualBundle.GetIdentity(), "It sets the identity")
|
s.Equal(bundle.GetIdentity(), actualBundle.GetIdentity(), "It sets the identity")
|
||||||
@ -210,7 +210,7 @@ func (s *SQLLitePersistenceTestSuite) TestMultiDevicePublicBundle() {
|
|||||||
key, err := crypto.GenerateKey()
|
key, err := crypto.GenerateKey()
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
actualBundle, err := s.service.GetPublicBundle(&key.PublicKey)
|
actualBundle, err := s.service.GetPublicBundle(&key.PublicKey, []string{"1"})
|
||||||
s.Require().NoError(err, "Error was not returned even though bundle is not there")
|
s.Require().NoError(err, "Error was not returned even though bundle is not there")
|
||||||
s.Nil(actualBundle)
|
s.Nil(actualBundle)
|
||||||
|
|
||||||
@ -234,7 +234,7 @@ func (s *SQLLitePersistenceTestSuite) TestMultiDevicePublicBundle() {
|
|||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
// Returns the most recent bundle
|
// Returns the most recent bundle
|
||||||
actualBundle, err = s.service.GetPublicBundle(&key.PublicKey)
|
actualBundle, err = s.service.GetPublicBundle(&key.PublicKey, []string{"1", "2"})
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
|
|
||||||
s.Equal(bundle.GetIdentity(), actualBundle.GetIdentity(), "It sets the identity")
|
s.Equal(bundle.GetIdentity(), actualBundle.GetIdentity(), "It sets the identity")
|
||||||
@ -344,4 +344,137 @@ func (s *SQLLitePersistenceTestSuite) TestRatchetInfoNoBundle() {
|
|||||||
s.Nil(ratchetInfo, "It returns nil when no bundle is there")
|
s.Nil(ratchetInfo, "It returns nil when no bundle is there")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SQLLitePersistenceTestSuite) TestAddInstallations() {
|
||||||
|
identity := []byte("alice")
|
||||||
|
installations := []string{"alice-1", "alice-2"}
|
||||||
|
err := s.service.AddInstallations(
|
||||||
|
identity,
|
||||||
|
1,
|
||||||
|
installations,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
enabledInstallations, err := s.service.GetActiveInstallations(5, identity)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
s.Require().Equal(installations, enabledInstallations)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SQLLitePersistenceTestSuite) TestAddInstallationsLimit() {
|
||||||
|
identity := []byte("alice")
|
||||||
|
|
||||||
|
installations := []string{"alice-1", "alice-2"}
|
||||||
|
err := s.service.AddInstallations(
|
||||||
|
identity,
|
||||||
|
1,
|
||||||
|
installations,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
installations = []string{"alice-2", "alice-3"}
|
||||||
|
err = s.service.AddInstallations(
|
||||||
|
identity,
|
||||||
|
2,
|
||||||
|
installations,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
installations = []string{"alice-2", "alice-3", "alice-4"}
|
||||||
|
err = s.service.AddInstallations(
|
||||||
|
identity,
|
||||||
|
3,
|
||||||
|
installations,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
enabledInstallations, err := s.service.GetActiveInstallations(3, identity)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
s.Require().Equal([]string{"alice-2", "alice-3", "alice-4"}, enabledInstallations)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SQLLitePersistenceTestSuite) TestAddInstallationsDisabled() {
|
||||||
|
identity := []byte("alice")
|
||||||
|
|
||||||
|
installations := []string{"alice-1", "alice-2"}
|
||||||
|
err := s.service.AddInstallations(
|
||||||
|
identity,
|
||||||
|
1,
|
||||||
|
installations,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
actualInstallations, err := s.service.GetActiveInstallations(3, identity)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
s.Require().Nil(actualInstallations)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SQLLitePersistenceTestSuite) TestDisableInstallation() {
|
||||||
|
identity := []byte("alice")
|
||||||
|
|
||||||
|
installations := []string{"alice-1", "alice-2"}
|
||||||
|
err := s.service.AddInstallations(
|
||||||
|
identity,
|
||||||
|
1,
|
||||||
|
installations,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
err = s.service.DisableInstallation(identity, "alice-1")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// We add the installations again
|
||||||
|
installations = []string{"alice-1", "alice-2"}
|
||||||
|
err = s.service.AddInstallations(
|
||||||
|
identity,
|
||||||
|
1,
|
||||||
|
installations,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
actualInstallations, err := s.service.GetActiveInstallations(3, identity)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
s.Require().Equal([]string{"alice-2"}, actualInstallations)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SQLLitePersistenceTestSuite) TestEnableInstallation() {
|
||||||
|
identity := []byte("alice")
|
||||||
|
|
||||||
|
installations := []string{"alice-1", "alice-2"}
|
||||||
|
err := s.service.AddInstallations(
|
||||||
|
identity,
|
||||||
|
1,
|
||||||
|
installations,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
err = s.service.DisableInstallation(identity, "alice-1")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
actualInstallations, err := s.service.GetActiveInstallations(3, identity)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
s.Require().Equal([]string{"alice-2"}, actualInstallations)
|
||||||
|
|
||||||
|
err = s.service.EnableInstallation(identity, "alice-1")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
actualInstallations, err = s.service.GetActiveInstallations(3, identity)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
s.Require().Equal([]string{"alice-1", "alice-2"}, actualInstallations)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Add test for MarkBundleExpired
|
// TODO: Add test for MarkBundleExpired
|
||||||
|
@ -6,6 +6,8 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/crypto/ecies"
|
"github.com/ethereum/go-ethereum/crypto/ecies"
|
||||||
@ -43,9 +45,12 @@ func FromBase64(str string) (*Bundle, error) {
|
|||||||
return bundle, nil
|
return bundle, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildSignatureMaterial(signedPreKeys *map[string]*SignedPreKey) []byte {
|
func buildSignatureMaterial(bundle *Bundle) []byte {
|
||||||
|
signedPreKeys := bundle.GetSignedPreKeys()
|
||||||
|
timestamp := bundle.GetTimestamp()
|
||||||
var keys []string
|
var keys []string
|
||||||
for k := range *signedPreKeys {
|
|
||||||
|
for k := range signedPreKeys {
|
||||||
keys = append(keys, k)
|
keys = append(keys, k)
|
||||||
}
|
}
|
||||||
var signatureMaterial []byte
|
var signatureMaterial []byte
|
||||||
@ -53,18 +58,23 @@ func buildSignatureMaterial(signedPreKeys *map[string]*SignedPreKey) []byte {
|
|||||||
sort.Strings(keys)
|
sort.Strings(keys)
|
||||||
|
|
||||||
for _, installationID := range keys {
|
for _, installationID := range keys {
|
||||||
signedPreKey := (*signedPreKeys)[installationID]
|
signedPreKey := signedPreKeys[installationID]
|
||||||
signatureMaterial = append(signatureMaterial, []byte(installationID)...)
|
signatureMaterial = append(signatureMaterial, []byte(installationID)...)
|
||||||
signatureMaterial = append(signatureMaterial, signedPreKey.SignedPreKey...)
|
signatureMaterial = append(signatureMaterial, signedPreKey.SignedPreKey...)
|
||||||
signatureMaterial = append(signatureMaterial, []byte(fmt.Sprint(signedPreKey.Version))...)
|
signatureMaterial = append(signatureMaterial, []byte(strconv.FormatUint(uint64(signedPreKey.Version), 10))...)
|
||||||
|
// We don't use timestamp in the signature if it's 0, for backward compatibility
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if timestamp != 0 {
|
||||||
|
signatureMaterial = append(signatureMaterial, []byte(strconv.FormatInt(timestamp, 10))...)
|
||||||
|
}
|
||||||
|
|
||||||
return signatureMaterial
|
return signatureMaterial
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func SignBundle(identity *ecdsa.PrivateKey, bundleContainer *BundleContainer) error {
|
func SignBundle(identity *ecdsa.PrivateKey, bundleContainer *BundleContainer) error {
|
||||||
signedPreKeys := bundleContainer.GetBundle().GetSignedPreKeys()
|
signatureMaterial := buildSignatureMaterial(bundleContainer.GetBundle())
|
||||||
signatureMaterial := buildSignatureMaterial(&signedPreKeys)
|
|
||||||
|
|
||||||
signature, err := crypto.Sign(crypto.Keccak256(signatureMaterial), identity)
|
signature, err := crypto.Sign(crypto.Keccak256(signatureMaterial), identity)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -89,6 +99,7 @@ func NewBundleContainer(identity *ecdsa.PrivateKey, installationID string) (*Bun
|
|||||||
signedPreKeys[installationID] = &SignedPreKey{SignedPreKey: compressedPreKey}
|
signedPreKeys[installationID] = &SignedPreKey{SignedPreKey: compressedPreKey}
|
||||||
|
|
||||||
bundle := Bundle{
|
bundle := Bundle{
|
||||||
|
Timestamp: time.Now().UnixNano(),
|
||||||
Identity: compressedIdentityKey,
|
Identity: compressedIdentityKey,
|
||||||
SignedPreKeys: signedPreKeys,
|
SignedPreKeys: signedPreKeys,
|
||||||
}
|
}
|
||||||
@ -112,8 +123,7 @@ func ExtractIdentity(bundle *Bundle) (string, error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
signedPreKeys := bundle.GetSignedPreKeys()
|
signatureMaterial := buildSignatureMaterial(bundle)
|
||||||
signatureMaterial := buildSignatureMaterial(&signedPreKeys)
|
|
||||||
|
|
||||||
recoveredKey, err := crypto.SigToPub(
|
recoveredKey, err := crypto.SigToPub(
|
||||||
crypto.Keccak256(signatureMaterial),
|
crypto.Keccak256(signatureMaterial),
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package chat
|
package chat
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
@ -65,6 +66,7 @@ func TestNewBundleContainer(t *testing.T) {
|
|||||||
|
|
||||||
signatureMaterial := append([]byte(bobInstallationID), bundle.GetSignedPreKeys()[bobInstallationID].GetSignedPreKey()...)
|
signatureMaterial := append([]byte(bobInstallationID), bundle.GetSignedPreKeys()[bobInstallationID].GetSignedPreKey()...)
|
||||||
signatureMaterial = append(signatureMaterial, []byte("0")...)
|
signatureMaterial = append(signatureMaterial, []byte("0")...)
|
||||||
|
signatureMaterial = append(signatureMaterial, []byte(fmt.Sprint(bundle.GetTimestamp()))...)
|
||||||
recoveredPublicKey, err := crypto.SigToPub(
|
recoveredPublicKey, err := crypto.SigToPub(
|
||||||
crypto.Keccak256(signatureMaterial),
|
crypto.Keccak256(signatureMaterial),
|
||||||
bundle.Signature,
|
bundle.Signature,
|
||||||
@ -102,6 +104,7 @@ func TestSignBundle(t *testing.T) {
|
|||||||
signatureMaterial = append(signatureMaterial, []byte("2")...)
|
signatureMaterial = append(signatureMaterial, []byte("2")...)
|
||||||
signatureMaterial = append(signatureMaterial, []byte("key")...)
|
signatureMaterial = append(signatureMaterial, []byte("key")...)
|
||||||
signatureMaterial = append(signatureMaterial, []byte("0")...)
|
signatureMaterial = append(signatureMaterial, []byte("0")...)
|
||||||
|
signatureMaterial = append(signatureMaterial, []byte(fmt.Sprint(bundle1.GetTimestamp()))...)
|
||||||
|
|
||||||
recoveredPublicKey, err := crypto.SigToPub(
|
recoveredPublicKey, err := crypto.SigToPub(
|
||||||
crypto.Keccak256(signatureMaterial),
|
crypto.Keccak256(signatureMaterial),
|
||||||
|
@ -122,6 +122,24 @@ func (s *Service) GetBundle(myIdentityKey *ecdsa.PrivateKey) (*chat.Bundle, erro
|
|||||||
return s.protocol.GetBundle(myIdentityKey)
|
return s.protocol.GetBundle(myIdentityKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EnableInstallation enables an installation for multi-device sync.
|
||||||
|
func (s *Service) EnableInstallation(myIdentityKey *ecdsa.PublicKey, installationID string) error {
|
||||||
|
if s.protocol == nil {
|
||||||
|
return errProtocolNotInitialized
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.protocol.EnableInstallation(myIdentityKey, installationID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableInstallation disables an installation for multi-device sync.
|
||||||
|
func (s *Service) DisableInstallation(myIdentityKey *ecdsa.PublicKey, installationID string) error {
|
||||||
|
if s.protocol == nil {
|
||||||
|
return errProtocolNotInitialized
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.protocol.DisableInstallation(myIdentityKey, installationID)
|
||||||
|
}
|
||||||
|
|
||||||
// APIs returns a list of new APIs.
|
// APIs returns a list of new APIs.
|
||||||
func (s *Service) APIs() []rpc.API {
|
func (s *Service) APIs() []rpc.API {
|
||||||
apis := []rpc.API{
|
apis := []rpc.API{
|
||||||
|
@ -88,7 +88,7 @@ func ConfigCliFleetEthBetaJson() (*asset, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "../config/cli/fleet-eth.beta.json", size: 3237, mode: os.FileMode(420), modTime: time.Unix(1539606161, 0)}
|
info := bindataFileInfo{name: "../config/cli/fleet-eth.beta.json", size: 3237, mode: os.FileMode(420), modTime: time.Unix(1541148771, 0)}
|
||||||
a := &asset{bytes: bytes, info: info}
|
a := &asset{bytes: bytes, info: info}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
@ -108,7 +108,7 @@ func ConfigCliFleetEthStagingJson() (*asset, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "../config/cli/fleet-eth.staging.json", size: 1838, mode: os.FileMode(420), modTime: time.Unix(1539606161, 0)}
|
info := bindataFileInfo{name: "../config/cli/fleet-eth.staging.json", size: 1838, mode: os.FileMode(420), modTime: time.Unix(1541148771, 0)}
|
||||||
a := &asset{bytes: bytes, info: info}
|
a := &asset{bytes: bytes, info: info}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
@ -128,7 +128,7 @@ func ConfigCliFleetEthTestJson() (*asset, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
info := bindataFileInfo{name: "../config/cli/fleet-eth.test.json", size: 1519, mode: os.FileMode(420), modTime: time.Unix(1539606161, 0)}
|
info := bindataFileInfo{name: "../config/cli/fleet-eth.test.json", size: 1519, mode: os.FileMode(420), modTime: time.Unix(1541148771, 0)}
|
||||||
a := &asset{bytes: bytes, info: info}
|
a := &asset{bytes: bytes, info: info}
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
1
static/migrations/1541164797_add_installations.down.sql
Normal file
1
static/migrations/1541164797_add_installations.down.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
DROP TABLE installations;
|
7
static/migrations/1541164797_add_installations.up.sql
Normal file
7
static/migrations/1541164797_add_installations.up.sql
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
CREATE TABLE installations (
|
||||||
|
identity BLOB NOT NULL,
|
||||||
|
installation_id TEXT NOT NULL,
|
||||||
|
timestamp UNSIGNED BIG INT NOT NULL,
|
||||||
|
enabled BOOLEAN DEFAULT 1,
|
||||||
|
UNIQUE(identity, installation_id) ON CONFLICT REPLACE
|
||||||
|
);
|
Loading…
x
Reference in New Issue
Block a user