12
vendor/github.com/status-im/status-go/protocol/encryption/README.md
generated
vendored
Normal file
12
vendor/github.com/status-im/status-go/protocol/encryption/README.md
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
# protocol/encryption package
|
||||
|
||||
## Hash ratchet encryption
|
||||
`encryptor.GenerateHashRatchetKey()` generates a hash ratchet key and stores it in in the DB.
|
||||
There, 2 new tables are created: `hash_ratchet_encryption` and `hash_ratchet_encryption_cache`.
|
||||
Each hash ratchet key is uniquely identified by the `(groupId, keyId)` pair, where `keyId` is derived from a clock value.
|
||||
|
||||
`protocol.BuildHashRatchetKeyExchangeMessage` builds an 1-on-1 message containing the hash ratchet key, given it's ID.
|
||||
|
||||
`protocol.BuildHashRatchetMessage` builds a hash ratchet message with arbitrary payload, given `groupId`. It will use the latest hash ratchet key available. `encryptor.encryptWithHR` encrypts the payload using Hash Ratchet algorithms. Intermediate hashes are stored in `hash_ratchet_encryption_cache` table.
|
||||
|
||||
`protocol.HandleMessage` uses `encryptor.decryptWithHR` fn for decryption.
|
||||
763
vendor/github.com/status-im/status-go/protocol/encryption/encryptor.go
generated
vendored
Normal file
763
vendor/github.com/status-im/status-go/protocol/encryption/encryptor.go
generated
vendored
Normal file
@@ -0,0 +1,763 @@
|
||||
package encryption
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
dr "github.com/status-im/doubleratchet"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/eth-node/crypto/ecies"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
|
||||
"github.com/status-im/status-go/protocol/encryption/multidevice"
|
||||
)
|
||||
|
||||
var (
|
||||
errSessionNotFound = errors.New("session not found")
|
||||
ErrDeviceNotFound = errors.New("device not found")
|
||||
// ErrNotPairedDevice means that we received a message signed with our public key
|
||||
// but from a device that has not been paired.
|
||||
// This should not happen because the protocol forbids sending a message to
|
||||
// non-paired devices, however, in theory it is possible to receive such a message.
|
||||
ErrNotPairedDevice = errors.New("received a message from not paired device")
|
||||
ErrHashRatchetSeqNoTooHigh = errors.New("Hash ratchet seq no is too high")
|
||||
ErrHashRatchetGroupIDNotFound = errors.New("Hash ratchet group id not found")
|
||||
ErrNoEncryptionKey = errors.New("no encryption key found for the community")
|
||||
)
|
||||
|
||||
// If we have no bundles, we use a constant so that the message can reach any device.
|
||||
const (
|
||||
noInstallationID = "none"
|
||||
maxHashRatchetSeqNoDelta = 100000
|
||||
)
|
||||
|
||||
type confirmationData struct {
|
||||
header *dr.MessageHeader
|
||||
drInfo *RatchetInfo
|
||||
}
|
||||
|
||||
// encryptor defines a service that is responsible for the encryption aspect of the protocol.
|
||||
type encryptor struct {
|
||||
persistence *sqlitePersistence
|
||||
config encryptorConfig
|
||||
messageIDs map[string]*confirmationData
|
||||
mutex sync.Mutex
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
type encryptorConfig struct {
|
||||
InstallationID string
|
||||
// Max number of installations we keep synchronized.
|
||||
MaxInstallations int
|
||||
// How many consecutive messages can be skipped in the receiving chain.
|
||||
MaxSkip int
|
||||
// Any message with seqNo <= currentSeq - maxKeep will be deleted.
|
||||
MaxKeep int
|
||||
// How many keys do we store in total per session.
|
||||
MaxMessageKeysPerSession int
|
||||
// How long before we refresh the interval in milliseconds
|
||||
BundleRefreshInterval int64
|
||||
// The logging object
|
||||
Logger *zap.Logger
|
||||
}
|
||||
|
||||
// defaultEncryptorConfig returns the default values used by the encryption service
|
||||
func defaultEncryptorConfig(installationID string, logger *zap.Logger) encryptorConfig {
|
||||
if logger == nil {
|
||||
logger = zap.NewNop()
|
||||
}
|
||||
|
||||
return encryptorConfig{
|
||||
MaxInstallations: 3,
|
||||
MaxSkip: 1000,
|
||||
MaxKeep: 3000,
|
||||
MaxMessageKeysPerSession: 2000,
|
||||
BundleRefreshInterval: 24 * 60 * 60 * 1000,
|
||||
InstallationID: installationID,
|
||||
Logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// newEncryptor creates a new EncryptionService instance.
|
||||
func newEncryptor(db *sql.DB, config encryptorConfig) *encryptor {
|
||||
return &encryptor{
|
||||
persistence: newSQLitePersistence(db),
|
||||
config: config,
|
||||
messageIDs: make(map[string]*confirmationData),
|
||||
logger: config.Logger.With(zap.Namespace("encryptor")),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *encryptor) keyFromActiveX3DH(theirIdentityKey []byte, theirSignedPreKey []byte, myIdentityKey *ecdsa.PrivateKey) ([]byte, *ecdsa.PublicKey, error) {
|
||||
sharedKey, ephemeralPubKey, err := PerformActiveX3DH(theirIdentityKey, theirSignedPreKey, myIdentityKey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return sharedKey, ephemeralPubKey, nil
|
||||
}
|
||||
|
||||
func (s *encryptor) getDRSession(id []byte) (dr.Session, error) {
|
||||
sessionStorage := s.persistence.SessionStorage()
|
||||
return dr.Load(
|
||||
id,
|
||||
sessionStorage,
|
||||
dr.WithKeysStorage(s.persistence.KeysStorage()),
|
||||
dr.WithMaxSkip(s.config.MaxSkip),
|
||||
dr.WithMaxKeep(s.config.MaxKeep),
|
||||
dr.WithMaxMessageKeysPerSession(s.config.MaxMessageKeysPerSession),
|
||||
dr.WithCrypto(crypto.EthereumCrypto{}),
|
||||
)
|
||||
}
|
||||
|
||||
func confirmationIDString(id []byte) string {
|
||||
return hex.EncodeToString(id)
|
||||
}
|
||||
|
||||
// ConfirmMessagesProcessed confirms and deletes message keys for the given messages
|
||||
func (s *encryptor) ConfirmMessageProcessed(messageID []byte) error {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
id := confirmationIDString(messageID)
|
||||
confirmationData, ok := s.messageIDs[id]
|
||||
if !ok {
|
||||
s.logger.Debug("could not confirm message or message already confirmed", zap.String("messageID", id))
|
||||
// We are ok with this, means no key material is stored (public message, or already confirmed)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Load session from store first
|
||||
session, err := s.getDRSession(confirmationData.drInfo.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := session.DeleteMk(confirmationData.header.DH, confirmationData.header.N); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Clean up
|
||||
delete(s.messageIDs, id)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateBundle retrieves or creates an X3DH bundle given a private key
|
||||
func (s *encryptor) CreateBundle(privateKey *ecdsa.PrivateKey, installations []*multidevice.Installation) (*Bundle, error) {
|
||||
ourIdentityKeyC := crypto.CompressPubkey(&privateKey.PublicKey)
|
||||
|
||||
bundleContainer, err := s.persistence.GetAnyPrivateBundle(ourIdentityKeyC, installations)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
expired := bundleContainer != nil && bundleContainer.GetBundle().Timestamp < time.Now().Add(-1*time.Duration(s.config.BundleRefreshInterval)*time.Millisecond).UnixNano()
|
||||
|
||||
// If the bundle has expired we create a new one
|
||||
if expired {
|
||||
// Mark sessions has expired
|
||||
if err := s.persistence.MarkBundleExpired(bundleContainer.GetBundle().GetIdentity()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
} else if bundleContainer != nil {
|
||||
err = SignBundle(privateKey, bundleContainer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bundleContainer.GetBundle(), nil
|
||||
}
|
||||
|
||||
// needs transaction/mutex to avoid creating multiple bundles
|
||||
// although not a problem
|
||||
bundleContainer, err = NewBundleContainer(privateKey, s.config.InstallationID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = s.persistence.AddPrivateBundle(bundleContainer); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.CreateBundle(privateKey, installations)
|
||||
}
|
||||
|
||||
// DecryptWithDH decrypts message sent with a DH key exchange, and throws away the key after decryption
|
||||
func (s *encryptor) DecryptWithDH(myIdentityKey *ecdsa.PrivateKey, theirEphemeralKey *ecdsa.PublicKey, payload []byte) ([]byte, error) {
|
||||
key, err := PerformDH(
|
||||
ecies.ImportECDSA(myIdentityKey),
|
||||
ecies.ImportECDSAPublic(theirEphemeralKey),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return crypto.DecryptSymmetric(key, payload)
|
||||
|
||||
}
|
||||
|
||||
// keyFromPassiveX3DH decrypts message sent with a X3DH key exchange, storing the key for future exchanges
|
||||
func (s *encryptor) keyFromPassiveX3DH(myIdentityKey *ecdsa.PrivateKey, theirIdentityKey *ecdsa.PublicKey, theirEphemeralKey *ecdsa.PublicKey, ourBundleID []byte) ([]byte, error) {
|
||||
bundlePrivateKey, err := s.persistence.GetPrivateKeyBundle(ourBundleID)
|
||||
if err != nil {
|
||||
s.logger.Error("could not get private bundle", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if bundlePrivateKey == nil {
|
||||
return nil, errSessionNotFound
|
||||
}
|
||||
|
||||
signedPreKey, err := crypto.ToECDSA(bundlePrivateKey)
|
||||
if err != nil {
|
||||
s.logger.Error("could not convert to ecdsa", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
key, err := PerformPassiveX3DH(
|
||||
theirIdentityKey,
|
||||
signedPreKey,
|
||||
theirEphemeralKey,
|
||||
myIdentityKey,
|
||||
)
|
||||
if err != nil {
|
||||
s.logger.Error("could not perform passive x3dh", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// ProcessPublicBundle persists a bundle
|
||||
func (s *encryptor) ProcessPublicBundle(myIdentityKey *ecdsa.PrivateKey, b *Bundle) error {
|
||||
return s.persistence.AddPublicBundle(b)
|
||||
}
|
||||
|
||||
func (s *encryptor) GetMessage(msgs map[string]*EncryptedMessageProtocol) *EncryptedMessageProtocol {
|
||||
msg := msgs[s.config.InstallationID]
|
||||
if msg == nil {
|
||||
msg = msgs[noInstallationID]
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
// DecryptPayload decrypts the payload of a EncryptedMessageProtocol, given an identity private key and the sender's public key
|
||||
func (s *encryptor) DecryptPayload(myIdentityKey *ecdsa.PrivateKey, theirIdentityKey *ecdsa.PublicKey, theirInstallationID string, msgs map[string]*EncryptedMessageProtocol, messageID []byte) ([]byte, error) {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
msg := s.GetMessage(msgs)
|
||||
|
||||
// We should not be sending a signal if it's coming from us, as we receive our own messages
|
||||
if msg == nil && !samePublicKeys(*theirIdentityKey, myIdentityKey.PublicKey) {
|
||||
s.logger.Debug("message is coming from someone else, but not targeting our installation id")
|
||||
return nil, ErrDeviceNotFound
|
||||
} else if msg == nil && theirInstallationID != s.config.InstallationID {
|
||||
s.logger.Debug("message is coming from same public key, but different installation id")
|
||||
return nil, ErrNotPairedDevice
|
||||
} else if msg == nil && theirInstallationID == s.config.InstallationID {
|
||||
s.logger.Debug("message is coming from us and is nil")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
payload := msg.GetPayload()
|
||||
|
||||
if x3dhHeader := msg.GetX3DHHeader(); x3dhHeader != nil {
|
||||
bundleID := x3dhHeader.GetId()
|
||||
theirEphemeralKey, err := crypto.DecompressPubkey(x3dhHeader.GetKey())
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
symmetricKey, err := s.keyFromPassiveX3DH(myIdentityKey, theirIdentityKey, theirEphemeralKey, bundleID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
theirIdentityKeyC := crypto.CompressPubkey(theirIdentityKey)
|
||||
err = s.persistence.AddRatchetInfo(symmetricKey, theirIdentityKeyC, bundleID, nil, theirInstallationID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if drHeader := msg.GetDRHeader(); drHeader != nil {
|
||||
drMessage := &dr.Message{
|
||||
Header: dr.MessageHeader{
|
||||
N: drHeader.GetN(),
|
||||
PN: drHeader.GetPn(),
|
||||
DH: drHeader.GetKey(),
|
||||
},
|
||||
Ciphertext: msg.GetPayload(),
|
||||
}
|
||||
|
||||
theirIdentityKeyC := crypto.CompressPubkey(theirIdentityKey)
|
||||
|
||||
drInfo, err := s.persistence.GetRatchetInfo(drHeader.GetId(), theirIdentityKeyC, theirInstallationID)
|
||||
if err != nil {
|
||||
s.logger.Error("could not get ratchet info", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We mark the exchange as successful so we stop sending x3dh header
|
||||
if err = s.persistence.RatchetInfoConfirmed(drHeader.GetId(), theirIdentityKeyC, theirInstallationID); err != nil {
|
||||
s.logger.Error("could not confirm ratchet info", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if drInfo == nil {
|
||||
s.logger.Error("could not find a session")
|
||||
return nil, errSessionNotFound
|
||||
}
|
||||
|
||||
confirmationData := &confirmationData{
|
||||
header: &drMessage.Header,
|
||||
drInfo: drInfo,
|
||||
}
|
||||
s.messageIDs[confirmationIDString(messageID)] = confirmationData
|
||||
|
||||
return s.decryptUsingDR(theirIdentityKey, drInfo, drMessage)
|
||||
}
|
||||
|
||||
// Try DH
|
||||
if header := msg.GetDHHeader(); header != nil {
|
||||
decompressedKey, err := crypto.DecompressPubkey(header.GetKey())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.DecryptWithDH(myIdentityKey, decompressedKey, payload)
|
||||
}
|
||||
|
||||
// Try Hash Ratchet
|
||||
if header := msg.GetHRHeader(); header != nil {
|
||||
|
||||
ratchet := &HashRatchetKeyCompatibility{
|
||||
GroupID: header.GroupId,
|
||||
// NOTE: this would be nil in the old format
|
||||
keyID: header.KeyId,
|
||||
}
|
||||
|
||||
// Old key format
|
||||
if header.DeprecatedKeyId != 0 {
|
||||
ratchet.Timestamp = uint64(header.DeprecatedKeyId)
|
||||
}
|
||||
|
||||
decryptedPayload, err := s.DecryptWithHR(ratchet, header.SeqNo, payload)
|
||||
|
||||
return decryptedPayload, err
|
||||
}
|
||||
return nil, errors.New("no key specified")
|
||||
}
|
||||
|
||||
func (s *encryptor) createNewSession(drInfo *RatchetInfo, sk []byte, keyPair crypto.DHPair) (dr.Session, error) {
|
||||
var err error
|
||||
var session dr.Session
|
||||
|
||||
if drInfo.PrivateKey != nil {
|
||||
session, err = dr.New(
|
||||
drInfo.ID,
|
||||
sk,
|
||||
keyPair,
|
||||
s.persistence.SessionStorage(),
|
||||
dr.WithKeysStorage(s.persistence.KeysStorage()),
|
||||
dr.WithMaxSkip(s.config.MaxSkip),
|
||||
dr.WithMaxKeep(s.config.MaxKeep),
|
||||
dr.WithMaxMessageKeysPerSession(s.config.MaxMessageKeysPerSession),
|
||||
dr.WithCrypto(crypto.EthereumCrypto{}))
|
||||
} else {
|
||||
session, err = dr.NewWithRemoteKey(
|
||||
drInfo.ID,
|
||||
sk,
|
||||
keyPair.PubKey,
|
||||
s.persistence.SessionStorage(),
|
||||
dr.WithKeysStorage(s.persistence.KeysStorage()),
|
||||
dr.WithMaxSkip(s.config.MaxSkip),
|
||||
dr.WithMaxKeep(s.config.MaxKeep),
|
||||
dr.WithMaxMessageKeysPerSession(s.config.MaxMessageKeysPerSession),
|
||||
dr.WithCrypto(crypto.EthereumCrypto{}))
|
||||
}
|
||||
|
||||
return session, err
|
||||
}
|
||||
|
||||
func (s *encryptor) encryptUsingDR(theirIdentityKey *ecdsa.PublicKey, drInfo *RatchetInfo, payload []byte) ([]byte, *DRHeader, error) {
|
||||
var err error
|
||||
|
||||
var session dr.Session
|
||||
|
||||
keyPair := crypto.DHPair{
|
||||
PrvKey: drInfo.PrivateKey,
|
||||
PubKey: drInfo.PublicKey,
|
||||
}
|
||||
|
||||
// Load session from store first
|
||||
session, err = s.getDRSession(drInfo.ID)
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Create a new one
|
||||
if session == nil {
|
||||
session, err = s.createNewSession(drInfo, drInfo.Sk, keyPair)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
response, err := session.RatchetEncrypt(payload, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
header := &DRHeader{
|
||||
Id: drInfo.BundleID,
|
||||
Key: response.Header.DH[:],
|
||||
N: response.Header.N,
|
||||
Pn: response.Header.PN,
|
||||
}
|
||||
|
||||
return response.Ciphertext, header, nil
|
||||
}
|
||||
|
||||
func (s *encryptor) decryptUsingDR(theirIdentityKey *ecdsa.PublicKey, drInfo *RatchetInfo, payload *dr.Message) ([]byte, error) {
|
||||
var err error
|
||||
|
||||
var session dr.Session
|
||||
|
||||
keyPair := crypto.DHPair{
|
||||
PrvKey: drInfo.PrivateKey,
|
||||
PubKey: drInfo.PublicKey,
|
||||
}
|
||||
|
||||
session, err = s.getDRSession(drInfo.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if session == nil {
|
||||
session, err = s.createNewSession(drInfo, drInfo.Sk, keyPair)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
plaintext, err := session.RatchetDecrypt(*payload, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
func (s *encryptor) encryptWithDH(theirIdentityKey *ecdsa.PublicKey, payload []byte) (*EncryptedMessageProtocol, error) {
|
||||
symmetricKey, ourEphemeralKey, err := PerformActiveDH(theirIdentityKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
encryptedPayload, err := crypto.EncryptSymmetric(symmetricKey, payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &EncryptedMessageProtocol{
|
||||
DHHeader: &DHHeader{
|
||||
Key: crypto.CompressPubkey(ourEphemeralKey),
|
||||
},
|
||||
Payload: encryptedPayload,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *encryptor) EncryptPayloadWithDH(theirIdentityKey *ecdsa.PublicKey, payload []byte) (map[string]*EncryptedMessageProtocol, error) {
|
||||
response := make(map[string]*EncryptedMessageProtocol)
|
||||
dmp, err := s.encryptWithDH(theirIdentityKey, payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response[noInstallationID] = dmp
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// GetPublicBundle returns the active installations bundles for a given user
|
||||
func (s *encryptor) GetPublicBundle(theirIdentityKey *ecdsa.PublicKey, installations []*multidevice.Installation) (*Bundle, error) {
|
||||
return s.persistence.GetPublicBundle(theirIdentityKey, installations)
|
||||
}
|
||||
|
||||
// EncryptPayload returns a new EncryptedMessageProtocol with a given payload encrypted, given a recipient's public key and the sender private identity key
|
||||
func (s *encryptor) EncryptPayload(theirIdentityKey *ecdsa.PublicKey, myIdentityKey *ecdsa.PrivateKey, installations []*multidevice.Installation, payload []byte) (map[string]*EncryptedMessageProtocol, []*multidevice.Installation, error) {
|
||||
logger := s.logger.With(
|
||||
zap.String("site", "EncryptPayload"),
|
||||
zap.String("their-identity-key", types.EncodeHex(crypto.FromECDSAPub(theirIdentityKey))))
|
||||
|
||||
// Which installations we are sending the message to
|
||||
var targetedInstallations []*multidevice.Installation
|
||||
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
if len(installations) == 0 {
|
||||
// We don't have any, send a message with DH
|
||||
logger.Debug("no installations, sending to all devices")
|
||||
encryptedPayload, err := s.EncryptPayloadWithDH(theirIdentityKey, payload)
|
||||
return encryptedPayload, targetedInstallations, err
|
||||
}
|
||||
|
||||
theirIdentityKeyC := crypto.CompressPubkey(theirIdentityKey)
|
||||
response := make(map[string]*EncryptedMessageProtocol)
|
||||
|
||||
for _, installation := range installations {
|
||||
installationID := installation.ID
|
||||
ilogger := logger.With(zap.String("installation-id", installationID))
|
||||
ilogger.Debug("processing installation")
|
||||
if s.config.InstallationID == installationID {
|
||||
continue
|
||||
}
|
||||
|
||||
bundle, err := s.persistence.GetPublicBundle(theirIdentityKey, []*multidevice.Installation{installation})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// See if a session is there already
|
||||
drInfo, err := s.persistence.GetAnyRatchetInfo(theirIdentityKeyC, installationID)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
targetedInstallations = append(targetedInstallations, installation)
|
||||
|
||||
if drInfo != nil {
|
||||
ilogger.Debug("found DR info for installation")
|
||||
encryptedPayload, drHeader, err := s.encryptUsingDR(theirIdentityKey, drInfo, payload)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
dmp := EncryptedMessageProtocol{
|
||||
Payload: encryptedPayload,
|
||||
DRHeader: drHeader,
|
||||
}
|
||||
|
||||
if drInfo.EphemeralKey != nil {
|
||||
dmp.X3DHHeader = &X3DHHeader{
|
||||
Key: drInfo.EphemeralKey,
|
||||
Id: drInfo.BundleID,
|
||||
}
|
||||
}
|
||||
|
||||
response[drInfo.InstallationID] = &dmp
|
||||
continue
|
||||
}
|
||||
|
||||
theirSignedPreKeyContainer := bundle.GetSignedPreKeys()[installationID]
|
||||
|
||||
// This should not be nil at this point
|
||||
if theirSignedPreKeyContainer == nil {
|
||||
ilogger.Warn("could not find DR info or bundle for installation")
|
||||
continue
|
||||
|
||||
}
|
||||
|
||||
ilogger.Debug("DR info not found, using bundle")
|
||||
|
||||
theirSignedPreKey := theirSignedPreKeyContainer.GetSignedPreKey()
|
||||
|
||||
sharedKey, ourEphemeralKey, err := s.keyFromActiveX3DH(theirIdentityKeyC, theirSignedPreKey, myIdentityKey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
theirIdentityKeyC := crypto.CompressPubkey(theirIdentityKey)
|
||||
ourEphemeralKeyC := crypto.CompressPubkey(ourEphemeralKey)
|
||||
|
||||
err = s.persistence.AddRatchetInfo(sharedKey, theirIdentityKeyC, theirSignedPreKey, ourEphemeralKeyC, installationID)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
x3dhHeader := &X3DHHeader{
|
||||
Key: ourEphemeralKeyC,
|
||||
Id: theirSignedPreKey,
|
||||
}
|
||||
|
||||
drInfo, err = s.persistence.GetRatchetInfo(theirSignedPreKey, theirIdentityKeyC, installationID)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if drInfo != nil {
|
||||
encryptedPayload, drHeader, err := s.encryptUsingDR(theirIdentityKey, drInfo, payload)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
dmp := &EncryptedMessageProtocol{
|
||||
Payload: encryptedPayload,
|
||||
X3DHHeader: x3dhHeader,
|
||||
DRHeader: drHeader,
|
||||
}
|
||||
|
||||
response[drInfo.InstallationID] = dmp
|
||||
}
|
||||
}
|
||||
|
||||
var installationIDs []string
|
||||
for _, i := range targetedInstallations {
|
||||
installationIDs = append(installationIDs, i.ID)
|
||||
}
|
||||
logger.Info(
|
||||
"built a message",
|
||||
zap.Strings("installation-ids", installationIDs),
|
||||
)
|
||||
|
||||
return response, targetedInstallations, nil
|
||||
}
|
||||
|
||||
func (s *encryptor) getNextHashRatchet(groupID []byte) (*HashRatchetKeyCompatibility, error) {
|
||||
latestKey, err := s.persistence.GetCurrentKeyForGroup(groupID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return latestKey.GenerateNext()
|
||||
}
|
||||
|
||||
// GenerateHashRatchetKey Generates and stores a hash ratchet key given a group ID
|
||||
func (s *encryptor) GenerateHashRatchetKey(groupID []byte) (*HashRatchetKeyCompatibility, error) {
|
||||
|
||||
key, err := s.getNextHashRatchet(groupID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return key, s.persistence.SaveHashRatchetKey(key)
|
||||
}
|
||||
|
||||
// EncryptHashRatchetPayload returns a new EncryptedMessageProtocol with a given payload encrypted, given a group's key
|
||||
func (s *encryptor) EncryptHashRatchetPayload(ratchet *HashRatchetKeyCompatibility, payload []byte) (map[string]*EncryptedMessageProtocol, error) {
|
||||
logger := s.logger.With(
|
||||
zap.String("site", "EncryptHashRatchetPayload"),
|
||||
zap.Any("group-id", ratchet.GroupID),
|
||||
zap.Any("key-id", ratchet.keyID))
|
||||
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
||||
logger.Debug("encrypting hash ratchet message")
|
||||
encryptedPayload, newSeqNo, err := s.EncryptWithHR(ratchet, payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keyID, err := ratchet.GetKeyID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dmp := &EncryptedMessageProtocol{
|
||||
HRHeader: &HRHeader{
|
||||
DeprecatedKeyId: ratchet.DeprecatedKeyID(),
|
||||
GroupId: ratchet.GroupID,
|
||||
KeyId: keyID,
|
||||
SeqNo: newSeqNo,
|
||||
},
|
||||
Payload: encryptedPayload,
|
||||
}
|
||||
|
||||
response := make(map[string]*EncryptedMessageProtocol)
|
||||
response[noInstallationID] = dmp
|
||||
return response, err
|
||||
}
|
||||
|
||||
func samePublicKeys(pubKey1, pubKey2 ecdsa.PublicKey) bool {
|
||||
return pubKey1.X.Cmp(pubKey2.X) == 0 && pubKey1.Y.Cmp(pubKey2.Y) == 0
|
||||
}
|
||||
|
||||
func (s *encryptor) EncryptWithHR(ratchet *HashRatchetKeyCompatibility, payload []byte) ([]byte, uint32, error) {
|
||||
hrCache, err := s.persistence.GetHashRatchetCache(ratchet, 0) // Get latest seqNo
|
||||
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if hrCache == nil {
|
||||
return nil, 0, ErrNoEncryptionKey
|
||||
}
|
||||
|
||||
var dbHash []byte
|
||||
if len(hrCache.Hash) == 0 {
|
||||
dbHash = hrCache.Key
|
||||
} else {
|
||||
dbHash = hrCache.Hash
|
||||
}
|
||||
|
||||
hash := crypto.Keccak256Hash(dbHash)
|
||||
encryptedPayload, err := crypto.EncryptSymmetric(hash.Bytes(), payload)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
newSeqNo := hrCache.SeqNo + 1
|
||||
err = s.persistence.SaveHashRatchetKeyHash(ratchet, hash.Bytes(), newSeqNo)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return encryptedPayload, newSeqNo, nil
|
||||
}
|
||||
|
||||
func (s *encryptor) DecryptWithHR(ratchet *HashRatchetKeyCompatibility, seqNo uint32, payload []byte) ([]byte, error) {
|
||||
// Key exchange message, nothing to decrypt
|
||||
if seqNo == 0 {
|
||||
return payload, nil
|
||||
}
|
||||
|
||||
hrCache, err := s.persistence.GetHashRatchetCache(ratchet, seqNo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if hrCache == nil {
|
||||
return nil, ErrHashRatchetGroupIDNotFound
|
||||
}
|
||||
|
||||
// Handle mesages with seqNo less than the one in db
|
||||
// 1. Check cache. If present for a particular seqNo, all good
|
||||
// 2. Otherwise, get the latest one for that keyId
|
||||
// 3. Every time the key is generated, it has to be saved in the cache along with the hash
|
||||
var hash []byte = hrCache.Hash
|
||||
if hrCache.SeqNo == seqNo {
|
||||
// We already have the hash for this seqNo
|
||||
hash = hrCache.Hash
|
||||
} else {
|
||||
if hrCache.SeqNo == 0 {
|
||||
// No cache records found for this keyId
|
||||
hash = hrCache.Key
|
||||
}
|
||||
// We should not have "holes" in seq numbers,
|
||||
// so a case when hrCache.SeqNo > seqNo shouldn't occur
|
||||
if seqNo-hrCache.SeqNo > maxHashRatchetSeqNoDelta {
|
||||
return nil, ErrHashRatchetSeqNoTooHigh
|
||||
}
|
||||
for i := hrCache.SeqNo; i < seqNo; i++ {
|
||||
hash = crypto.Keccak256Hash(hash).Bytes()
|
||||
err := s.persistence.SaveHashRatchetKeyHash(ratchet, hash, i+1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
decryptedPayload, err := crypto.DecryptSymmetric(hash, payload)
|
||||
|
||||
if err != nil {
|
||||
s.logger.Error("failed to decrypt hash", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
return decryptedPayload, nil
|
||||
}
|
||||
164
vendor/github.com/status-im/status-go/protocol/encryption/helpers.go
generated
vendored
Normal file
164
vendor/github.com/status-im/status-go/protocol/encryption/helpers.go
generated
vendored
Normal file
@@ -0,0 +1,164 @@
|
||||
package encryption
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/eth-node/crypto/ecies"
|
||||
)
|
||||
|
||||
const keyBumpValue = uint64(10)
|
||||
|
||||
// GetCurrentTime64 returns the current unix time in milliseconds
|
||||
func GetCurrentTime() uint64 {
|
||||
return (uint64)(time.Now().UnixNano() / int64(time.Millisecond))
|
||||
}
|
||||
|
||||
// bumpKeyID takes a timestampID and returns its value incremented by the keyBumpValue
|
||||
func bumpKeyID(timestampID uint64) uint64 {
|
||||
return timestampID + keyBumpValue
|
||||
}
|
||||
|
||||
func generateHashRatchetKeyID(groupID []byte, timestamp uint64, keyBytes []byte) []byte {
|
||||
var keyMaterial []byte
|
||||
|
||||
keyMaterial = append(keyMaterial, groupID...)
|
||||
|
||||
timestampBytes := make([]byte, 8) // 8 bytes for a uint64
|
||||
binary.LittleEndian.PutUint64(timestampBytes, timestamp)
|
||||
keyMaterial = append(keyMaterial, timestampBytes...)
|
||||
|
||||
keyMaterial = append(keyMaterial, keyBytes...)
|
||||
|
||||
return crypto.Keccak256(keyMaterial)
|
||||
}
|
||||
|
||||
func publicKeyMostRelevantBytes(key *ecdsa.PublicKey) uint32 {
|
||||
|
||||
keyBytes := crypto.FromECDSAPub(key)
|
||||
|
||||
return binary.LittleEndian.Uint32(keyBytes[1:5])
|
||||
}
|
||||
|
||||
func encrypt(plaintext []byte, key []byte, reader io.Reader) ([]byte, error) {
|
||||
c, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nonce := make([]byte, gcm.NonceSize())
|
||||
if _, err = io.ReadFull(reader, nonce); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return gcm.Seal(nonce, nonce, plaintext, nil), nil
|
||||
}
|
||||
|
||||
func generateSharedKey(privateKey *ecdsa.PrivateKey, publicKey *ecdsa.PublicKey) ([]byte, error) {
|
||||
|
||||
const encryptedPayloadKeyLength = 16
|
||||
|
||||
return ecies.ImportECDSA(privateKey).GenerateShared(
|
||||
ecies.ImportECDSAPublic(publicKey),
|
||||
encryptedPayloadKeyLength,
|
||||
encryptedPayloadKeyLength,
|
||||
)
|
||||
}
|
||||
|
||||
func buildGroupRekeyMessage(privateKey *ecdsa.PrivateKey, groupID []byte, timestamp uint64, keyMaterial []byte, keys []*ecdsa.PublicKey) (*RekeyGroup, error) {
|
||||
|
||||
message := &RekeyGroup{
|
||||
Timestamp: timestamp,
|
||||
}
|
||||
|
||||
message.Keys = make(map[uint32][]byte)
|
||||
|
||||
for _, k := range keys {
|
||||
|
||||
sharedKey, err := generateSharedKey(privateKey, k)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
encryptedKey, err := encrypt(keyMaterial, sharedKey, rand.Reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
kBytes := publicKeyMostRelevantBytes(k)
|
||||
|
||||
if message.Keys[kBytes] == nil {
|
||||
message.Keys[kBytes] = encryptedKey
|
||||
} else {
|
||||
message.Keys[kBytes] = append(message.Keys[kBytes], encryptedKey...)
|
||||
}
|
||||
}
|
||||
|
||||
return message, nil
|
||||
}
|
||||
|
||||
const nonceLength = 12
|
||||
|
||||
func decrypt(cyphertext []byte, key []byte) ([]byte, error) {
|
||||
if len(cyphertext) < nonceLength {
|
||||
return nil, errors.New("invalid cyphertext length")
|
||||
}
|
||||
|
||||
c, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nonce := cyphertext[:nonceLength]
|
||||
return gcm.Open(nil, nonce, cyphertext[nonceLength:], nil)
|
||||
}
|
||||
|
||||
const keySize = 60
|
||||
|
||||
func decryptGroupRekeyMessage(privateKey *ecdsa.PrivateKey, publicKey *ecdsa.PublicKey, message *RekeyGroup) ([]byte, error) {
|
||||
kBytes := publicKeyMostRelevantBytes(&privateKey.PublicKey)
|
||||
if message.Keys == nil || message.Keys[kBytes] == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
sharedKey, err := generateSharedKey(privateKey, publicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keys := message.Keys[kBytes]
|
||||
|
||||
nKeys := len(keys) / keySize
|
||||
|
||||
var decryptedKey []byte
|
||||
for i := 0; i < nKeys; i++ {
|
||||
|
||||
encryptedKey := keys[i*keySize : i*keySize+keySize]
|
||||
decryptedKey, err = decrypt(encryptedKey, sharedKey)
|
||||
if err != nil {
|
||||
continue
|
||||
} else {
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return decryptedKey, nil
|
||||
}
|
||||
712
vendor/github.com/status-im/status-go/protocol/encryption/migrations/migrations.go
generated
vendored
Normal file
712
vendor/github.com/status-im/status-go/protocol/encryption/migrations/migrations.go
generated
vendored
Normal file
@@ -0,0 +1,712 @@
|
||||
// Code generated by go-bindata. DO NOT EDIT.
|
||||
// sources:
|
||||
// 1536754952_initial_schema.down.sql (83B)
|
||||
// 1536754952_initial_schema.up.sql (962B)
|
||||
// 1539249977_update_ratchet_info.down.sql (311B)
|
||||
// 1539249977_update_ratchet_info.up.sql (368B)
|
||||
// 1540715431_add_version.down.sql (127B)
|
||||
// 1540715431_add_version.up.sql (265B)
|
||||
// 1541164797_add_installations.down.sql (26B)
|
||||
// 1541164797_add_installations.up.sql (216B)
|
||||
// 1558084410_add_secret.down.sql (56B)
|
||||
// 1558084410_add_secret.up.sql (301B)
|
||||
// 1558588866_add_version.down.sql (47B)
|
||||
// 1558588866_add_version.up.sql (57B)
|
||||
// 1559627659_add_contact_code.down.sql (32B)
|
||||
// 1559627659_add_contact_code.up.sql (198B)
|
||||
// 1561368210_add_installation_metadata.down.sql (35B)
|
||||
// 1561368210_add_installation_metadata.up.sql (267B)
|
||||
// 1632236298_add_communities.down.sql (151B)
|
||||
// 1632236298_add_communities.up.sql (584B)
|
||||
// 1636536507_add_index_bundles.up.sql (347B)
|
||||
// doc.go (377B)
|
||||
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func bindataRead(data []byte, name string) ([]byte, error) {
|
||||
gz, err := gzip.NewReader(bytes.NewBuffer(data))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read %q: %w", name, err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
_, err = io.Copy(&buf, gz)
|
||||
clErr := gz.Close()
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read %q: %w", name, err)
|
||||
}
|
||||
if clErr != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
type asset struct {
|
||||
bytes []byte
|
||||
info os.FileInfo
|
||||
digest [sha256.Size]byte
|
||||
}
|
||||
|
||||
type bindataFileInfo struct {
|
||||
name string
|
||||
size int64
|
||||
mode os.FileMode
|
||||
modTime time.Time
|
||||
}
|
||||
|
||||
func (fi bindataFileInfo) Name() string {
|
||||
return fi.name
|
||||
}
|
||||
func (fi bindataFileInfo) Size() int64 {
|
||||
return fi.size
|
||||
}
|
||||
func (fi bindataFileInfo) Mode() os.FileMode {
|
||||
return fi.mode
|
||||
}
|
||||
func (fi bindataFileInfo) ModTime() time.Time {
|
||||
return fi.modTime
|
||||
}
|
||||
func (fi bindataFileInfo) IsDir() bool {
|
||||
return false
|
||||
}
|
||||
func (fi bindataFileInfo) Sys() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
var __1536754952_initial_schemaDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x09\xf2\x0f\x50\x08\x71\x74\xf2\x71\x55\x28\x4e\x2d\x2e\xce\xcc\xcf\x2b\xb6\xe6\x42\x12\x4c\x2a\xcd\x4b\xc9\x49\x45\x15\xcb\x4e\xad\x44\x15\x28\x4a\x2c\x49\xce\x48\x2d\x89\xcf\xcc\x4b\xcb\xb7\xe6\x02\x04\x00\x00\xff\xff\x72\x61\x3f\x92\x53\x00\x00\x00")
|
||||
|
||||
func _1536754952_initial_schemaDownSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__1536754952_initial_schemaDownSql,
|
||||
"1536754952_initial_schema.down.sql",
|
||||
)
|
||||
}
|
||||
|
||||
func _1536754952_initial_schemaDownSql() (*asset, error) {
|
||||
bytes, err := _1536754952_initial_schemaDownSqlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1536754952_initial_schema.down.sql", size: 83, mode: os.FileMode(0644), modTime: time.Unix(1704739012, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x44, 0xcf, 0x76, 0x71, 0x1f, 0x5e, 0x9a, 0x43, 0xd8, 0xcd, 0xb8, 0xc3, 0x70, 0xc3, 0x7f, 0xfc, 0x90, 0xb4, 0x25, 0x1e, 0xf4, 0x66, 0x20, 0xb8, 0x33, 0x7e, 0xb0, 0x76, 0x1f, 0xc, 0xc0, 0x75}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var __1536754952_initial_schemaUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x52\xc1\x8e\x9b\x30\x10\xbd\xe7\x2b\xe6\x98\x48\x39\xf4\xde\x13\xb0\x13\x84\x4a\xcd\xd6\x0b\x52\xf7\x64\x79\xe3\x69\xb0\x16\x1b\x64\x3b\xab\xe6\xef\x2b\x20\xa5\xb8\x65\xdb\xde\x98\xc7\x9b\x99\x37\xef\x39\xe3\x98\xd4\x08\x75\x92\x96\x08\x9e\xbc\xd7\xbd\xf5\xb0\xdf\x01\xa8\xd6\x41\x5a\x56\xe9\x71\xfa\xf6\x62\xb8\xbe\x74\xfa\x1c\x43\x4e\xbf\xc9\x40\x0b\xe6\xfa\x3e\x88\x73\x2b\xb5\x15\xaf\x74\x5b\x60\x4f\x56\xfd\x1d\xb6\x50\xb0\x1a\x73\xe4\xd3\x14\x3a\xbf\x6d\xd0\x57\x70\x44\xf7\x81\x86\x75\x3d\x58\x58\x97\x5a\x4d\x13\x80\x55\x35\xb0\xa6\x2c\xe1\x91\x17\x9f\x13\xfe\x0c\x9f\xf0\x79\xfc\xdf\xb0\xe2\x4b\x83\x7b\xad\x0e\x50\x31\xc8\x2a\x76\x2a\x8b\xac\x06\x8e\x8f\x65\x92\xe1\xee\xf0\x71\xb7\x8b\x3c\x7a\xa5\xdb\xec\xcf\xec\xc7\x22\x71\x59\x30\x0e\x35\xfe\x22\xec\xd5\xac\x75\x18\xf2\x5e\x5e\x68\x9b\x3f\x8b\x80\xfd\xbd\xef\xb8\x66\xff\xa7\xae\x97\xab\x55\x1d\xcd\xd2\xb4\x22\x1b\x74\xd8\x58\xa4\xad\x0f\xb2\xeb\x64\xd0\xbd\x15\x5a\x41\x8d\x5f\xeb\x88\x70\x8f\x34\x0e\x4a\x5f\x2c\x29\x31\xb8\x0d\xf5\x6b\x3b\x23\xa1\x45\xce\x2a\x8e\x63\x7b\xd0\x86\x7c\x90\x66\x80\x86\x3d\x15\x39\xc3\x07\x48\x8b\x7c\xf4\x26\xda\x4c\xdf\x07\xed\x48\x41\x5a\x55\x25\x26\x0c\x1e\xf0\x94\x34\x65\x0d\x1f\xfe\xbc\xd5\xc9\x70\x6e\x29\x08\x6d\xbf\xf5\xd3\xc1\xf3\xf1\xe2\xf7\xac\xa7\xb1\x43\x4b\x86\x9c\xec\xa2\x93\xde\x77\xc8\xdf\x8c\xa1\xe0\xde\x4b\xf6\x9f\x06\xde\xdf\xd3\xa2\xe8\xb8\xec\xda\x0c\x72\x6c\x39\x55\x1c\x8b\x9c\x4d\x16\xfe\x6a\x3c\x00\xc7\x13\x72\x64\x19\x3e\xfd\x4c\x77\x1f\x47\x71\x18\xad\xf9\x11\x00\x00\xff\xff\xa9\x50\xa8\xb2\xc2\x03\x00\x00")
|
||||
|
||||
func _1536754952_initial_schemaUpSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__1536754952_initial_schemaUpSql,
|
||||
"1536754952_initial_schema.up.sql",
|
||||
)
|
||||
}
|
||||
|
||||
func _1536754952_initial_schemaUpSql() (*asset, error) {
|
||||
bytes, err := _1536754952_initial_schemaUpSqlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1536754952_initial_schema.up.sql", size: 962, mode: os.FileMode(0644), modTime: time.Unix(1704739012, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xea, 0x90, 0x5a, 0x59, 0x3e, 0x3, 0xe2, 0x3c, 0x81, 0x42, 0xcd, 0x4c, 0x9a, 0xe8, 0xda, 0x93, 0x2b, 0x70, 0xa4, 0xd5, 0x29, 0x3e, 0xd5, 0xc9, 0x27, 0xb6, 0xb7, 0x65, 0xff, 0x0, 0xcb, 0xde}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var __1539249977_update_ratchet_infoDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x6c\x8f\x41\x4b\xc4\x30\x10\x85\xef\xf9\x15\xef\xd8\xc2\x9e\xbc\xee\xa9\x8d\x53\x29\x86\x64\x8d\x29\xe8\x29\xd4\xed\xe8\x06\xdb\xec\xd2\x46\xa1\xff\x5e\x22\x52\x59\xf4\x3a\xdf\xf7\x78\x6f\x6e\xad\x39\xc0\x55\xb5\x22\xcc\x7d\x3a\x9e\x38\xf9\x10\x5f\xcf\xfe\xf3\x66\x2f\x84\xb4\x54\x39\xfa\x07\xa3\x10\xc0\xcb\x47\x1c\x46\xf6\x61\x40\xad\x4c\x0d\x6d\x1c\x74\xa7\xd4\x4e\x00\x7c\x39\xf1\xc4\x73\x3f\xfa\x77\x5e\xbf\x71\xbe\x86\x81\x63\x0a\x69\xfd\xeb\x2f\xeb\x34\x71\x9a\xc3\x71\xf3\xaf\x70\x88\x4b\xea\xc7\xb1\x4f\xe1\x1c\x73\x9f\xa3\x27\x77\x25\x74\xba\x7d\xe8\xa8\xd8\x16\xed\xb6\xae\x12\x46\x43\x1a\xdd\xa8\x56\x3a\x58\x3a\xa8\x4a\x52\x8e\x34\xc6\x52\x7b\xa7\x71\x4f\xcf\xf8\x0d\x96\xb0\xd4\x90\x25\x2d\xe9\xf1\xe7\xc1\xa5\x58\xc2\x5b\xe4\xc1\x5f\x66\xce\xf3\x4a\x51\xee\xc5\x57\x00\x00\x00\xff\xff\x69\x51\x9b\xb4\x37\x01\x00\x00")
|
||||
|
||||
func _1539249977_update_ratchet_infoDownSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__1539249977_update_ratchet_infoDownSql,
|
||||
"1539249977_update_ratchet_info.down.sql",
|
||||
)
|
||||
}
|
||||
|
||||
func _1539249977_update_ratchet_infoDownSql() (*asset, error) {
|
||||
bytes, err := _1539249977_update_ratchet_infoDownSqlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1539249977_update_ratchet_info.down.sql", size: 311, mode: os.FileMode(0644), modTime: time.Unix(1704739012, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1, 0xa4, 0xeb, 0xa0, 0xe6, 0xa0, 0xd4, 0x48, 0xbb, 0xad, 0x6f, 0x7d, 0x67, 0x8c, 0xbd, 0x25, 0xde, 0x1f, 0x73, 0x9a, 0xbb, 0xa8, 0xc9, 0x30, 0xb7, 0xa9, 0x7c, 0xaf, 0xb5, 0x1, 0x61, 0xdd}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var __1539249977_update_ratchet_infoUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x6c\x8f\x41\x4f\x84\x30\x10\x85\xef\xfd\x15\x73\x84\x84\x93\x57\x4e\x50\x07\x43\xac\xed\x5a\x4b\xa2\xa7\x06\x97\xd1\x6d\x16\xca\x86\x56\x13\xfe\xbd\xa9\x31\x28\xea\xf5\xbd\x6f\xde\x7b\x73\x8d\x02\x0d\x42\xa3\xd5\x1d\x04\x0a\xc1\xcd\x3e\x94\xec\xa7\x7a\xa6\x35\x29\x5a\x1d\xc0\x54\xb5\x40\x58\xfa\x78\x3c\x51\xb4\xce\xbf\xcc\x25\x63\x5c\x63\x65\xf0\x1f\xcf\xbe\x5f\x41\xc6\x00\x9e\xdf\xfc\x30\x92\x75\x03\xd4\x42\xd5\x20\x95\x01\xd9\x09\x51\x30\x00\xba\x9c\x68\xa2\xa5\x1f\xed\x99\xd6\x4f\x3b\xa9\x6e\x20\x1f\x5d\x5c\xff\xf2\x61\x9d\x26\x8a\x8b\x3b\x6e\xfc\xce\x76\x3e\xc4\x7e\x1c\xfb\xe8\x66\x9f\xfa\x0c\x3e\x9a\x1d\xd0\xc9\xf6\xbe\xc3\x6c\x5b\x54\x6c\x5d\xc5\xef\xe3\x1c\x94\x04\xae\x64\x23\x5a\x6e\x40\xe3\x41\x54\x1c\x53\x46\xa3\x34\xb6\x37\x12\x6e\xf1\x09\xbe\x93\x72\xd0\xd8\xa0\x46\xc9\xf1\xe1\xeb\xe3\x90\x05\xf7\xea\x69\xb0\x97\x85\xd2\xde\x9c\xe5\x25\xfb\x08\x00\x00\xff\xff\xb6\x31\x2b\x32\x70\x01\x00\x00")
|
||||
|
||||
func _1539249977_update_ratchet_infoUpSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__1539249977_update_ratchet_infoUpSql,
|
||||
"1539249977_update_ratchet_info.up.sql",
|
||||
)
|
||||
}
|
||||
|
||||
func _1539249977_update_ratchet_infoUpSql() (*asset, error) {
|
||||
bytes, err := _1539249977_update_ratchet_infoUpSqlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1539249977_update_ratchet_info.up.sql", size: 368, mode: os.FileMode(0644), modTime: time.Unix(1704739012, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc, 0x8e, 0xbf, 0x6f, 0xa, 0xc0, 0xe1, 0x3c, 0x42, 0x28, 0x88, 0x1d, 0xdb, 0xba, 0x1c, 0x83, 0xec, 0xba, 0xd3, 0x5f, 0x5c, 0x77, 0x5e, 0xa7, 0x46, 0x36, 0xec, 0x69, 0xa, 0x4b, 0x17, 0x79}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var __1540715431_add_versionDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\xf4\x09\x71\x0d\x52\x08\x71\x74\xf2\x71\x55\xc8\x4e\xad\x2c\x56\x70\x09\xf2\x0f\x50\x70\xf6\xf7\x09\xf5\xf5\x53\x28\x4e\x2d\x2e\xce\xcc\xcf\x8b\xcf\x4c\xb1\xe6\x42\x56\x08\x15\x47\x55\x0c\xd2\x1d\x9f\x9c\x5f\x9a\x57\x82\xaa\x38\xa9\x34\x2f\x25\x27\x15\x55\x6d\x59\x6a\x11\xc8\x00\x6b\x2e\x40\x00\x00\x00\xff\xff\xda\x5d\x80\x2d\x7f\x00\x00\x00")
|
||||
|
||||
func _1540715431_add_versionDownSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__1540715431_add_versionDownSql,
|
||||
"1540715431_add_version.down.sql",
|
||||
)
|
||||
}
|
||||
|
||||
func _1540715431_add_versionDownSql() (*asset, error) {
|
||||
bytes, err := _1540715431_add_versionDownSqlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1540715431_add_version.down.sql", size: 127, mode: os.FileMode(0644), modTime: time.Unix(1704739012, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf5, 0x9, 0x4, 0xe3, 0x76, 0x2e, 0xb8, 0x9, 0x23, 0xf0, 0x70, 0x93, 0xc4, 0x50, 0xe, 0x9d, 0x84, 0x22, 0x8c, 0x94, 0xd3, 0x24, 0x9, 0x9a, 0xc1, 0xa1, 0x48, 0x45, 0xfd, 0x40, 0x6e, 0xe6}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var __1540715431_add_versionUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\xcd\xb1\x0e\x02\x21\x0c\xc6\xf1\xdd\xa7\xf8\x1e\xc1\xdd\x09\xa4\x67\x4c\x7a\x90\x90\x32\x93\xe8\x31\x5c\x54\x2e\x8a\x98\xf8\xf6\x06\xe3\xc2\xa2\xae\x6d\xff\xbf\x1a\x62\x12\xc2\xe0\xdd\x88\x53\x7a\x96\xcd\x4a\xb1\x90\x87\x28\xcd\xf4\x9e\x40\x19\x83\xad\xe3\x30\x5a\x94\x74\x8d\xb9\x5e\xb0\xb7\x42\x3b\xf2\xb0\x4e\x60\x03\x33\x0c\x0d\x2a\xb0\x60\xfd\xab\x2f\x65\x5e\x72\x9c\x27\x68\x76\xba\x3f\xfe\x2c\xbb\xa0\x01\xf1\xb8\xd4\x7c\xff\xfb\xe7\xa1\xe6\xe9\x9c\x3a\xe5\x91\x6e\x4d\xfe\x4a\xbc\x02\x00\x00\xff\xff\x0e\x27\x2c\x52\x09\x01\x00\x00")
|
||||
|
||||
func _1540715431_add_versionUpSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__1540715431_add_versionUpSql,
|
||||
"1540715431_add_version.up.sql",
|
||||
)
|
||||
}
|
||||
|
||||
func _1540715431_add_versionUpSql() (*asset, error) {
|
||||
bytes, err := _1540715431_add_versionUpSqlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1540715431_add_version.up.sql", size: 265, mode: os.FileMode(0644), modTime: time.Unix(1704739012, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc7, 0x4c, 0x36, 0x96, 0xdf, 0x16, 0x10, 0xa6, 0x27, 0x1a, 0x79, 0x8b, 0x42, 0x83, 0x23, 0xc, 0x7e, 0xb6, 0x3d, 0x2, 0xda, 0xa4, 0xb4, 0xd, 0x27, 0x55, 0xba, 0xdc, 0xb2, 0x88, 0x8f, 0xa6}}
|
||||
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(0644), modTime: time.Unix(1704739012, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf5, 0xfd, 0xe6, 0xd8, 0xca, 0x3b, 0x38, 0x18, 0xee, 0x0, 0x5f, 0x36, 0x9e, 0x1e, 0xd, 0x19, 0x3e, 0xb4, 0x73, 0x53, 0xe9, 0xa5, 0xac, 0xdd, 0xa1, 0x2f, 0xc7, 0x6c, 0xa8, 0xd9, 0xa, 0x88}}
|
||||
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(0644), modTime: time.Unix(1704739012, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x2d, 0x18, 0x26, 0xb8, 0x88, 0x47, 0xdb, 0x83, 0xcc, 0xb6, 0x9d, 0x1c, 0x1, 0xae, 0x2f, 0xde, 0x97, 0x82, 0x3, 0x30, 0xa8, 0x63, 0xa1, 0x78, 0x4b, 0xa5, 0x9, 0x8, 0x75, 0xa2, 0x57, 0x81}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var __1558084410_add_secretDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x09\xf2\x0f\x50\x08\x71\x74\xf2\x71\x55\x28\x4e\x4d\x2e\x4a\x2d\x89\xcf\xcc\x2b\x2e\x49\xcc\xc9\x49\x2c\xc9\xcc\xcf\x8b\xcf\x4c\x29\xb6\xe6\xc2\x50\x53\x6c\xcd\x05\x08\x00\x00\xff\xff\xd3\xcd\x41\x83\x38\x00\x00\x00")
|
||||
|
||||
func _1558084410_add_secretDownSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__1558084410_add_secretDownSql,
|
||||
"1558084410_add_secret.down.sql",
|
||||
)
|
||||
}
|
||||
|
||||
func _1558084410_add_secretDownSql() (*asset, error) {
|
||||
bytes, err := _1558084410_add_secretDownSqlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1558084410_add_secret.down.sql", size: 56, mode: os.FileMode(0644), modTime: time.Unix(1704739012, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x49, 0xb, 0x65, 0xdf, 0x59, 0xbf, 0xe9, 0x5, 0x5b, 0x6f, 0xd5, 0x3a, 0xb7, 0x57, 0xe8, 0x78, 0x38, 0x73, 0x53, 0x57, 0xf7, 0x24, 0x4, 0xe4, 0xa2, 0x49, 0x22, 0xa2, 0xc6, 0xfd, 0x80, 0xa4}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var __1558084410_add_secretUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x74\x50\xcf\x0a\x82\x30\x1c\xbe\xef\x29\xbe\xa3\x82\x6f\xd0\x49\xc7\x4f\x19\xad\xdf\x6a\x4d\xc8\x93\x48\xf3\x30\x10\x83\xdc\xa5\xb7\x0f\x23\x45\xa1\xce\xdf\xff\x4f\x5a\xca\x1d\xc1\xe5\x85\x26\x4c\xfd\xfd\xd9\xc7\x09\x89\x00\x82\xef\xc7\x18\xe2\x0b\x85\x36\x05\xd8\x38\x70\xad\x35\xce\x56\x9d\x72\xdb\xe0\x48\x0d\x0c\x43\x1a\x2e\xb5\x92\x0e\xaa\x62\x63\x29\x13\xf8\x9a\xec\x65\x22\x3d\x08\xf1\x23\xaa\x0d\xe3\x14\xbb\x61\xe8\x62\x78\x8c\x6d\xf0\x4b\x34\x1c\xdd\xdc\xaa\xce\x36\x75\xda\xe0\xf7\xd6\x33\x58\xb3\xba\xd4\x94\x04\x9f\x6d\x79\xe9\x9f\x82\xa5\xb1\xa4\x2a\xfe\x4c\x48\x76\x7c\x4b\x25\x59\x62\x49\xd7\xe5\x8a\x15\x4f\xe7\x09\xef\x00\x00\x00\xff\xff\xa6\xbb\x2c\x23\x2d\x01\x00\x00")
|
||||
|
||||
func _1558084410_add_secretUpSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__1558084410_add_secretUpSql,
|
||||
"1558084410_add_secret.up.sql",
|
||||
)
|
||||
}
|
||||
|
||||
func _1558084410_add_secretUpSql() (*asset, error) {
|
||||
bytes, err := _1558084410_add_secretUpSqlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1558084410_add_secret.up.sql", size: 301, mode: os.FileMode(0644), modTime: time.Unix(1704739012, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf5, 0x32, 0x36, 0x8e, 0x47, 0xb0, 0x8f, 0xc1, 0xc6, 0xf7, 0xc6, 0x9f, 0x2d, 0x44, 0x75, 0x2b, 0x26, 0xec, 0x6, 0xa0, 0x7b, 0xa5, 0xbd, 0xc8, 0x76, 0x8a, 0x82, 0x68, 0x2, 0x42, 0xb5, 0xf4}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var __1558588866_add_versionDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\xf4\x09\x71\x0d\x52\x08\x71\x74\xf2\x71\x55\xc8\xcc\x2b\x2e\x49\xcc\xc9\x49\x2c\xc9\xcc\xcf\x2b\x56\x70\x09\xf2\x0f\x50\x70\xf6\xf7\x09\xf5\xf5\x53\x28\x4b\x2d\x2a\xce\xcc\xcf\xb3\xe6\x02\x04\x00\x00\xff\xff\xdf\x6b\x9f\xbb\x2f\x00\x00\x00")
|
||||
|
||||
func _1558588866_add_versionDownSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__1558588866_add_versionDownSql,
|
||||
"1558588866_add_version.down.sql",
|
||||
)
|
||||
}
|
||||
|
||||
func _1558588866_add_versionDownSql() (*asset, error) {
|
||||
bytes, err := _1558588866_add_versionDownSqlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1558588866_add_version.down.sql", size: 47, mode: os.FileMode(0644), modTime: time.Unix(1704739012, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xde, 0x52, 0x34, 0x3c, 0x46, 0x4a, 0xf0, 0x72, 0x47, 0x6f, 0x49, 0x5c, 0xc7, 0xf9, 0x32, 0xce, 0xc4, 0x3d, 0xfd, 0x61, 0xa1, 0x8b, 0x8f, 0xf2, 0x31, 0x34, 0xde, 0x15, 0x49, 0xa6, 0xde, 0xb9}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var __1558588866_add_versionUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\xf4\x09\x71\x0d\x52\x08\x71\x74\xf2\x71\x55\xc8\xcc\x2b\x2e\x49\xcc\xc9\x49\x2c\xc9\xcc\xcf\x2b\x56\x70\x74\x71\x51\x28\x4b\x2d\x2a\xce\xcc\xcf\x53\xf0\xf4\x0b\x71\x75\x77\x0d\x52\x70\x71\x75\x73\x0c\xf5\x09\x51\x30\xb0\xe6\x02\x04\x00\x00\xff\xff\x14\x7b\x07\xb5\x39\x00\x00\x00")
|
||||
|
||||
func _1558588866_add_versionUpSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__1558588866_add_versionUpSql,
|
||||
"1558588866_add_version.up.sql",
|
||||
)
|
||||
}
|
||||
|
||||
func _1558588866_add_versionUpSql() (*asset, error) {
|
||||
bytes, err := _1558588866_add_versionUpSqlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1558588866_add_version.up.sql", size: 57, mode: os.FileMode(0644), modTime: time.Unix(1704739012, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x2a, 0xea, 0x64, 0x39, 0x61, 0x20, 0x83, 0x83, 0xb, 0x2e, 0x79, 0x64, 0xb, 0x53, 0xfa, 0xfe, 0xc6, 0xf7, 0x67, 0x42, 0xd3, 0x4f, 0xdc, 0x7e, 0x30, 0x32, 0xe8, 0x14, 0x41, 0xe9, 0xe7, 0x3b}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var __1559627659_add_contact_codeDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x09\xf2\x0f\x50\x08\x71\x74\xf2\x71\x55\x48\xce\xcf\x2b\x49\x4c\x2e\x89\x4f\xce\x4f\x49\x8d\x4f\xce\xcf\x4b\xcb\x4c\xb7\xe6\x02\x04\x00\x00\xff\xff\x73\x7b\x50\x80\x20\x00\x00\x00")
|
||||
|
||||
func _1559627659_add_contact_codeDownSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__1559627659_add_contact_codeDownSql,
|
||||
"1559627659_add_contact_code.down.sql",
|
||||
)
|
||||
}
|
||||
|
||||
func _1559627659_add_contact_codeDownSql() (*asset, error) {
|
||||
bytes, err := _1559627659_add_contact_codeDownSqlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1559627659_add_contact_code.down.sql", size: 32, mode: os.FileMode(0644), modTime: time.Unix(1704739012, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x5d, 0x64, 0x6d, 0xce, 0x24, 0x42, 0x20, 0x8d, 0x4f, 0x37, 0xaa, 0x9d, 0xc, 0x57, 0x98, 0xc1, 0xd1, 0x1a, 0x34, 0xcd, 0x9f, 0x8f, 0x34, 0x86, 0xb3, 0xd3, 0xdc, 0xf1, 0x7d, 0xe5, 0x1b, 0x6e}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var __1559627659_add_contact_codeUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x6c\xce\xc1\x8e\x82\x30\x18\x04\xe0\x7b\x9f\x62\x6e\x40\xb2\x07\xf6\xcc\xa9\xbb\xfb\xaf\x21\xd6\x62\x4a\x31\x72\x22\xb5\xa0\x34\x21\x45\xa1\xf8\xfc\x06\x13\xe3\xc5\xeb\xe4\x9b\xc9\xfc\x2a\xe2\x9a\xa0\xf9\x8f\x20\xd8\xd1\x07\x63\x43\x63\xc7\xb6\x6b\xec\xe8\xcf\xee\x82\x98\x01\x58\xbc\xbb\x2d\xcf\x68\x0e\x93\x71\x3e\xe0\x6e\x26\xdb\x9b\x29\xfe\x4e\x20\x0b\x0d\x59\x09\x81\xbd\xca\x77\x5c\xd5\xd8\x52\x8d\x3f\xfa\xe7\x95\xd0\x88\x8e\xd1\x17\x03\x06\x33\x87\xe6\xba\x9c\x06\x37\xf7\x5d\x8b\x5c\x6a\xda\x90\x7a\x57\x5f\x3c\x65\x49\xc6\x58\x2e\x4b\x52\x7a\x55\xc5\xc7\x4f\x07\x2e\x2a\x2a\x11\xaf\xe3\x48\x93\x8c\x3d\x02\x00\x00\xff\xff\xdc\x7c\x0c\xd3\xc6\x00\x00\x00")
|
||||
|
||||
func _1559627659_add_contact_codeUpSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__1559627659_add_contact_codeUpSql,
|
||||
"1559627659_add_contact_code.up.sql",
|
||||
)
|
||||
}
|
||||
|
||||
func _1559627659_add_contact_codeUpSql() (*asset, error) {
|
||||
bytes, err := _1559627659_add_contact_codeUpSqlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1559627659_add_contact_code.up.sql", size: 198, mode: os.FileMode(0644), modTime: time.Unix(1704739012, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x16, 0xf6, 0xc2, 0x62, 0x9c, 0xd2, 0xc9, 0x1e, 0xd8, 0xea, 0xaa, 0xea, 0x95, 0x8f, 0x89, 0x6a, 0x85, 0x5d, 0x9d, 0x99, 0x78, 0x3c, 0x90, 0x66, 0x99, 0x3e, 0x4b, 0x19, 0x62, 0xfb, 0x31, 0x4d}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var __1561368210_add_installation_metadataDownSql = []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\x8e\xcf\x4d\x2d\x49\x4c\x49\x2c\x49\xb4\xe6\x02\x04\x00\x00\xff\xff\x03\x72\x7f\x08\x23\x00\x00\x00")
|
||||
|
||||
func _1561368210_add_installation_metadataDownSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__1561368210_add_installation_metadataDownSql,
|
||||
"1561368210_add_installation_metadata.down.sql",
|
||||
)
|
||||
}
|
||||
|
||||
func _1561368210_add_installation_metadataDownSql() (*asset, error) {
|
||||
bytes, err := _1561368210_add_installation_metadataDownSqlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1561368210_add_installation_metadata.down.sql", size: 35, mode: os.FileMode(0644), modTime: time.Unix(1704739012, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xa8, 0xde, 0x3f, 0xd2, 0x4a, 0x50, 0x98, 0x56, 0xe3, 0xc0, 0xcd, 0x9d, 0xb0, 0x34, 0x3b, 0xe5, 0x62, 0x18, 0xb5, 0x20, 0xc9, 0x3e, 0xdc, 0x6a, 0x40, 0x36, 0x66, 0xea, 0x51, 0x8c, 0x71, 0xf5}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var __1561368210_add_installation_metadataUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x7c\xce\xc1\x8a\x83\x30\x10\xc6\xf1\xbb\x4f\xf1\xdd\x54\xf0\x0d\xf6\x14\xb3\x23\x08\x21\xd9\x95\x04\x7a\x93\x60\x52\x08\xd5\x58\xe8\x50\xf0\xed\x8b\x87\x42\xed\xc1\xeb\xcc\xef\x83\xbf\x1c\x48\x58\x82\x15\xad\x22\xa4\xfc\x60\x3f\xcf\x9e\xd3\x9a\xc7\x25\xb2\x0f\x9e\x3d\x50\x15\x40\x0a\x31\x73\xe2\x0d\xad\x32\x2d\xb4\xb1\xd0\x4e\xa9\x66\xff\x7c\x8e\x52\x80\xa5\x8b\x3d\x80\xec\x97\x78\xbc\xe2\x97\x3a\xe1\x94\x45\x59\xee\x20\xc4\x67\x9a\xe2\xc8\xdb\xfd\xdc\x5d\xa7\x65\xe4\xf5\x16\xf3\xa9\x72\xba\xff\x77\x54\xbd\x83\x9b\xef\xc0\x1a\x46\x43\x1a\xdd\xa9\x5e\x5a\x0c\xf4\xa7\x84\xa4\xa2\xfe\x29\x5e\x01\x00\x00\xff\xff\x5d\x6f\xe6\xd3\x0b\x01\x00\x00")
|
||||
|
||||
func _1561368210_add_installation_metadataUpSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__1561368210_add_installation_metadataUpSql,
|
||||
"1561368210_add_installation_metadata.up.sql",
|
||||
)
|
||||
}
|
||||
|
||||
func _1561368210_add_installation_metadataUpSql() (*asset, error) {
|
||||
bytes, err := _1561368210_add_installation_metadataUpSqlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1561368210_add_installation_metadata.up.sql", size: 267, mode: os.FileMode(0644), modTime: time.Unix(1704739012, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xb4, 0x71, 0x8f, 0x29, 0xb1, 0xaa, 0xd6, 0xd1, 0x8c, 0x17, 0xef, 0x6c, 0xd5, 0x80, 0xb8, 0x2c, 0xc3, 0xfe, 0xec, 0x24, 0x4d, 0xc8, 0x25, 0xd3, 0xb4, 0xcd, 0xa9, 0xac, 0x63, 0x61, 0xb2, 0x9c}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var __1632236298_add_communitiesDownSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x09\xf2\x0f\x50\xf0\xf4\x73\x71\x8d\x50\xc8\x4c\xa9\x88\xcf\x48\x2c\xce\x88\x2f\x4a\x2c\x49\xce\x48\x2d\x89\x4f\xcd\x4b\xb6\xe6\x22\xa0\x20\x3e\x39\x31\x39\x23\xd5\x9a\x0b\xa2\x2e\xc4\xd1\xc9\xc7\x55\x01\x5d\x4d\x51\x65\x41\x49\x66\x7e\x1e\x4c\x29\x61\x95\xd6\x5c\x80\x00\x00\x00\xff\xff\xa4\x97\x4f\xad\x97\x00\x00\x00")
|
||||
|
||||
func _1632236298_add_communitiesDownSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__1632236298_add_communitiesDownSql,
|
||||
"1632236298_add_communities.down.sql",
|
||||
)
|
||||
}
|
||||
|
||||
func _1632236298_add_communitiesDownSql() (*asset, error) {
|
||||
bytes, err := _1632236298_add_communitiesDownSqlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1632236298_add_communities.down.sql", size: 151, mode: os.FileMode(0644), modTime: time.Unix(1704739012, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x26, 0xe5, 0x47, 0xd1, 0xe5, 0xec, 0x5b, 0x3e, 0xdc, 0x22, 0xf4, 0x27, 0xee, 0x70, 0xf3, 0x9, 0x4f, 0xd2, 0x9f, 0x92, 0xf, 0x5a, 0x18, 0x11, 0xb7, 0x40, 0xab, 0xf1, 0x98, 0x72, 0xd6, 0x60}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var __1632236298_add_communitiesUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x94\x90\xdf\x4a\xc3\x30\x14\xc6\xef\xf3\x14\xe7\x72\x83\xbe\x81\x57\xed\x3c\x1b\xc1\x7a\xa2\x31\x05\x77\x15\x4a\x16\x4c\x10\xd3\xd9\x46\xb0\x6f\x2f\xc1\x0c\x71\x71\xd2\xdd\x7e\x9c\xf3\xfd\xf9\x6d\x24\xd6\x0a\x41\xd5\x4d\x8b\xe0\xfa\xc9\xe9\xb1\x8f\xc6\xd9\xa8\x6d\x30\xe3\x7c\x8c\x7e\x08\xb0\x62\x00\x2f\xe3\xf0\x71\xd4\xfe\x00\x4d\x2b\x1a\x20\xa1\x80\xba\xb6\xad\x18\xc0\xab\x9d\x93\xce\x49\x9d\xcb\xe5\xed\x83\xe4\xf7\xb5\xdc\xc3\x1d\xee\x57\x27\xc7\x2a\x3b\xac\xd9\xfa\x86\xb1\x5c\xa8\x23\xfe\xd8\x21\x70\xba\xc5\x67\xf0\x87\x4f\x7d\xde\x0d\x04\x5d\xea\x5b\x3a\xff\xf8\xfe\x3b\x54\x9b\xde\x38\xbb\x6c\xae\x0f\xf1\x97\x3c\xd9\x77\x1d\x86\x44\x01\x77\x28\x93\x92\x42\xca\xf7\x69\x7e\x4b\x89\xfa\x84\x27\x69\x5b\x21\x91\xef\xe8\x6f\x2a\x20\x71\x8b\x12\x69\x83\x4f\xcb\x07\x5f\x85\x32\xcf\xbe\x0c\xf4\xfb\xa0\x48\xa9\xf2\xe8\x94\xf5\x15\x00\x00\xff\xff\x61\x30\xb4\xa0\x48\x02\x00\x00")
|
||||
|
||||
func _1632236298_add_communitiesUpSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__1632236298_add_communitiesUpSql,
|
||||
"1632236298_add_communities.up.sql",
|
||||
)
|
||||
}
|
||||
|
||||
func _1632236298_add_communitiesUpSql() (*asset, error) {
|
||||
bytes, err := _1632236298_add_communitiesUpSqlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1632236298_add_communities.up.sql", size: 584, mode: os.FileMode(0644), modTime: time.Unix(1704739012, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x8f, 0xe0, 0x1, 0x6e, 0x84, 0xc, 0x35, 0xe4, 0x5a, 0xf, 0xbe, 0xcb, 0xf7, 0xd2, 0xa8, 0x25, 0xf5, 0xdb, 0x7, 0xcb, 0xa3, 0xe6, 0xf4, 0xc4, 0x1b, 0xa5, 0xec, 0x32, 0x1e, 0x1e, 0x48, 0x60}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var __1636536507_add_index_bundlesUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\x0e\x72\x75\x0c\x71\x55\xf0\xf4\x73\x71\x8d\x50\x48\xcd\x4b\x2e\xaa\x2c\x28\xc9\xcc\xcf\x8b\x4f\x2a\xcd\x4b\xc9\x49\x2d\x8e\x4f\xad\x28\xc8\x2c\x4a\x4d\x89\xcf\x4c\x49\xcd\x2b\xc9\x2c\xa9\x8c\xcf\xcc\x2b\x2e\x49\xcc\xc9\x49\x04\xab\xca\x4c\x89\x2f\x4b\x2d\x2a\xce\xcc\xcf\x53\xc8\xcf\x53\x80\xea\x51\xd0\x80\x6a\xd2\x51\x80\xe9\xd2\x51\x40\xd3\xa6\xa3\x00\xd5\xa7\x69\xcd\x45\xa9\x13\x50\xac\x26\xdd\xc6\xa2\xc4\x92\xe4\x8c\xd4\x92\xf8\xcc\xbc\xb4\xfc\xf8\x32\x23\xbc\xd6\xa0\xa9\xc5\x63\x9d\xa6\x35\x17\x20\x00\x00\xff\xff\xd4\xde\x07\x5c\x5b\x01\x00\x00")
|
||||
|
||||
func _1636536507_add_index_bundlesUpSqlBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
__1636536507_add_index_bundlesUpSql,
|
||||
"1636536507_add_index_bundles.up.sql",
|
||||
)
|
||||
}
|
||||
|
||||
func _1636536507_add_index_bundlesUpSql() (*asset, error) {
|
||||
bytes, err := _1636536507_add_index_bundlesUpSqlBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "1636536507_add_index_bundles.up.sql", size: 347, mode: os.FileMode(0644), modTime: time.Unix(1704739012, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf1, 0xb9, 0x3c, 0x16, 0xfc, 0xfb, 0xb2, 0xb4, 0x3b, 0xfe, 0xdc, 0xf5, 0x9c, 0x42, 0xa0, 0xa0, 0xd4, 0xd, 0x5b, 0x97, 0x10, 0x80, 0x95, 0xe, 0x13, 0xc1, 0x18, 0x8, 0xee, 0xf, 0x99, 0xee}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
var _docGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x84\x8f\xbb\x6e\xc3\x30\x0c\x45\x77\x7f\xc5\x45\x96\x2c\xb5\xb4\x74\xea\xd6\xb1\x7b\x7f\x80\x91\x68\x89\x88\x1e\xae\x48\xe7\xf1\xf7\x85\xd3\x02\xcd\xd6\xf5\x00\xe7\xf0\xd2\x7b\x7c\x66\x51\x2c\x52\x18\xa2\x68\x1c\x58\x95\xc6\x1d\x27\x0e\xb4\x29\xe3\x90\xc4\xf2\x76\x72\xa1\x57\xaf\x46\xb6\xe9\x2c\xd5\x57\x49\x83\x8c\xfd\xe5\xf5\x30\x79\x8f\x40\xed\x68\xc8\xd4\x62\xe1\x47\x4b\xa1\x46\xc3\xa4\x25\x5c\xc5\x32\x08\xeb\xe0\x45\x6e\x0e\xef\x86\xc2\xa4\x06\xcb\x64\x47\x85\x65\x46\x20\xe5\x3d\xb3\xf4\x81\xd4\xe7\x93\xb4\x48\x46\x6e\x47\x1f\xcb\x13\xd9\x17\x06\x2a\x85\x23\x96\xd1\xeb\xc3\x55\xaa\x8c\x28\x83\x83\xf5\x71\x7f\x01\xa9\xb2\xa1\x51\x65\xdd\xfd\x4c\x17\x46\xeb\xbf\xe7\x41\x2d\xfe\xff\x11\xae\x7d\x9c\x15\xa4\xe0\xdb\xca\xc1\x38\xba\x69\x5a\x29\x9c\x29\x31\xf4\xab\x88\xf1\x34\x79\x9f\xfa\x5b\xe2\xc6\xbb\xf5\xbc\x71\x5e\xcf\x09\x3f\x35\xe9\x4d\x31\x77\x38\xe7\xff\x80\x4b\x1d\x6e\xfa\x0e\x00\x00\xff\xff\x9d\x60\x3d\x88\x79\x01\x00\x00")
|
||||
|
||||
func docGoBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
_docGo,
|
||||
"doc.go",
|
||||
)
|
||||
}
|
||||
|
||||
func docGo() (*asset, error) {
|
||||
bytes, err := docGoBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{name: "doc.go", size: 377, mode: os.FileMode(0644), modTime: time.Unix(1704739012, 0)}
|
||||
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xef, 0xaf, 0xdf, 0xcf, 0x65, 0xae, 0x19, 0xfc, 0x9d, 0x29, 0xc1, 0x91, 0xaf, 0xb5, 0xd5, 0xb1, 0x56, 0xf3, 0xee, 0xa8, 0xba, 0x13, 0x65, 0xdb, 0xab, 0xcf, 0x4e, 0xac, 0x92, 0xe9, 0x60, 0xf1}}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// Asset loads and returns the asset for the given name.
|
||||
// It returns an error if the asset could not be found or
|
||||
// could not be loaded.
|
||||
func Asset(name string) ([]byte, error) {
|
||||
canonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
if f, ok := _bindata[canonicalName]; ok {
|
||||
a, err := f()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
|
||||
}
|
||||
return a.bytes, nil
|
||||
}
|
||||
return nil, fmt.Errorf("Asset %s not found", name)
|
||||
}
|
||||
|
||||
// AssetString returns the asset contents as a string (instead of a []byte).
|
||||
func AssetString(name string) (string, error) {
|
||||
data, err := Asset(name)
|
||||
return string(data), err
|
||||
}
|
||||
|
||||
// MustAsset is like Asset but panics when Asset would return an error.
|
||||
// It simplifies safe initialization of global variables.
|
||||
func MustAsset(name string) []byte {
|
||||
a, err := Asset(name)
|
||||
if err != nil {
|
||||
panic("asset: Asset(" + name + "): " + err.Error())
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
// MustAssetString is like AssetString but panics when Asset would return an
|
||||
// error. It simplifies safe initialization of global variables.
|
||||
func MustAssetString(name string) string {
|
||||
return string(MustAsset(name))
|
||||
}
|
||||
|
||||
// AssetInfo loads and returns the asset info for the given name.
|
||||
// It returns an error if the asset could not be found or
|
||||
// could not be loaded.
|
||||
func AssetInfo(name string) (os.FileInfo, error) {
|
||||
canonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
if f, ok := _bindata[canonicalName]; ok {
|
||||
a, err := f()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
|
||||
}
|
||||
return a.info, nil
|
||||
}
|
||||
return nil, fmt.Errorf("AssetInfo %s not found", name)
|
||||
}
|
||||
|
||||
// AssetDigest returns the digest of the file with the given name. It returns an
|
||||
// error if the asset could not be found or the digest could not be loaded.
|
||||
func AssetDigest(name string) ([sha256.Size]byte, error) {
|
||||
canonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
if f, ok := _bindata[canonicalName]; ok {
|
||||
a, err := f()
|
||||
if err != nil {
|
||||
return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s can't read by error: %v", name, err)
|
||||
}
|
||||
return a.digest, nil
|
||||
}
|
||||
return [sha256.Size]byte{}, fmt.Errorf("AssetDigest %s not found", name)
|
||||
}
|
||||
|
||||
// Digests returns a map of all known files and their checksums.
|
||||
func Digests() (map[string][sha256.Size]byte, error) {
|
||||
mp := make(map[string][sha256.Size]byte, len(_bindata))
|
||||
for name := range _bindata {
|
||||
a, err := _bindata[name]()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mp[name] = a.digest
|
||||
}
|
||||
return mp, nil
|
||||
}
|
||||
|
||||
// AssetNames returns the names of the assets.
|
||||
func AssetNames() []string {
|
||||
names := make([]string, 0, len(_bindata))
|
||||
for name := range _bindata {
|
||||
names = append(names, name)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
// _bindata is a table, holding each asset generator, mapped to its name.
|
||||
var _bindata = map[string]func() (*asset, error){
|
||||
"1536754952_initial_schema.down.sql": _1536754952_initial_schemaDownSql,
|
||||
"1536754952_initial_schema.up.sql": _1536754952_initial_schemaUpSql,
|
||||
"1539249977_update_ratchet_info.down.sql": _1539249977_update_ratchet_infoDownSql,
|
||||
"1539249977_update_ratchet_info.up.sql": _1539249977_update_ratchet_infoUpSql,
|
||||
"1540715431_add_version.down.sql": _1540715431_add_versionDownSql,
|
||||
"1540715431_add_version.up.sql": _1540715431_add_versionUpSql,
|
||||
"1541164797_add_installations.down.sql": _1541164797_add_installationsDownSql,
|
||||
"1541164797_add_installations.up.sql": _1541164797_add_installationsUpSql,
|
||||
"1558084410_add_secret.down.sql": _1558084410_add_secretDownSql,
|
||||
"1558084410_add_secret.up.sql": _1558084410_add_secretUpSql,
|
||||
"1558588866_add_version.down.sql": _1558588866_add_versionDownSql,
|
||||
"1558588866_add_version.up.sql": _1558588866_add_versionUpSql,
|
||||
"1559627659_add_contact_code.down.sql": _1559627659_add_contact_codeDownSql,
|
||||
"1559627659_add_contact_code.up.sql": _1559627659_add_contact_codeUpSql,
|
||||
"1561368210_add_installation_metadata.down.sql": _1561368210_add_installation_metadataDownSql,
|
||||
"1561368210_add_installation_metadata.up.sql": _1561368210_add_installation_metadataUpSql,
|
||||
"1632236298_add_communities.down.sql": _1632236298_add_communitiesDownSql,
|
||||
"1632236298_add_communities.up.sql": _1632236298_add_communitiesUpSql,
|
||||
"1636536507_add_index_bundles.up.sql": _1636536507_add_index_bundlesUpSql,
|
||||
"doc.go": docGo,
|
||||
}
|
||||
|
||||
// AssetDebug is true if the assets were built with the debug flag enabled.
|
||||
const AssetDebug = false
|
||||
|
||||
// AssetDir returns the file names below a certain
|
||||
// directory embedded in the file by go-bindata.
|
||||
// For example if you run go-bindata on data/... and data contains the
|
||||
// following hierarchy:
|
||||
//
|
||||
// data/
|
||||
// foo.txt
|
||||
// img/
|
||||
// a.png
|
||||
// b.png
|
||||
//
|
||||
// then AssetDir("data") would return []string{"foo.txt", "img"},
|
||||
// AssetDir("data/img") would return []string{"a.png", "b.png"},
|
||||
// AssetDir("foo.txt") and AssetDir("notexist") would return an error, and
|
||||
// AssetDir("") will return []string{"data"}.
|
||||
func AssetDir(name string) ([]string, error) {
|
||||
node := _bintree
|
||||
if len(name) != 0 {
|
||||
canonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
pathList := strings.Split(canonicalName, "/")
|
||||
for _, p := range pathList {
|
||||
node = node.Children[p]
|
||||
if node == nil {
|
||||
return nil, fmt.Errorf("Asset %s not found", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
if node.Func != nil {
|
||||
return nil, fmt.Errorf("Asset %s not found", name)
|
||||
}
|
||||
rv := make([]string, 0, len(node.Children))
|
||||
for childName := range node.Children {
|
||||
rv = append(rv, childName)
|
||||
}
|
||||
return rv, nil
|
||||
}
|
||||
|
||||
type bintree struct {
|
||||
Func func() (*asset, error)
|
||||
Children map[string]*bintree
|
||||
}
|
||||
|
||||
var _bintree = &bintree{nil, map[string]*bintree{
|
||||
"1536754952_initial_schema.down.sql": {_1536754952_initial_schemaDownSql, map[string]*bintree{}},
|
||||
"1536754952_initial_schema.up.sql": {_1536754952_initial_schemaUpSql, map[string]*bintree{}},
|
||||
"1539249977_update_ratchet_info.down.sql": {_1539249977_update_ratchet_infoDownSql, map[string]*bintree{}},
|
||||
"1539249977_update_ratchet_info.up.sql": {_1539249977_update_ratchet_infoUpSql, map[string]*bintree{}},
|
||||
"1540715431_add_version.down.sql": {_1540715431_add_versionDownSql, map[string]*bintree{}},
|
||||
"1540715431_add_version.up.sql": {_1540715431_add_versionUpSql, map[string]*bintree{}},
|
||||
"1541164797_add_installations.down.sql": {_1541164797_add_installationsDownSql, map[string]*bintree{}},
|
||||
"1541164797_add_installations.up.sql": {_1541164797_add_installationsUpSql, map[string]*bintree{}},
|
||||
"1558084410_add_secret.down.sql": {_1558084410_add_secretDownSql, map[string]*bintree{}},
|
||||
"1558084410_add_secret.up.sql": {_1558084410_add_secretUpSql, map[string]*bintree{}},
|
||||
"1558588866_add_version.down.sql": {_1558588866_add_versionDownSql, map[string]*bintree{}},
|
||||
"1558588866_add_version.up.sql": {_1558588866_add_versionUpSql, map[string]*bintree{}},
|
||||
"1559627659_add_contact_code.down.sql": {_1559627659_add_contact_codeDownSql, map[string]*bintree{}},
|
||||
"1559627659_add_contact_code.up.sql": {_1559627659_add_contact_codeUpSql, map[string]*bintree{}},
|
||||
"1561368210_add_installation_metadata.down.sql": {_1561368210_add_installation_metadataDownSql, map[string]*bintree{}},
|
||||
"1561368210_add_installation_metadata.up.sql": {_1561368210_add_installation_metadataUpSql, map[string]*bintree{}},
|
||||
"1632236298_add_communities.down.sql": {_1632236298_add_communitiesDownSql, map[string]*bintree{}},
|
||||
"1632236298_add_communities.up.sql": {_1632236298_add_communitiesUpSql, map[string]*bintree{}},
|
||||
"1636536507_add_index_bundles.up.sql": {_1636536507_add_index_bundlesUpSql, map[string]*bintree{}},
|
||||
"doc.go": {docGo, map[string]*bintree{}},
|
||||
}}
|
||||
|
||||
// RestoreAsset restores an asset under the given directory.
|
||||
func RestoreAsset(dir, name string) error {
|
||||
data, err := Asset(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
info, err := AssetInfo(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.WriteFile(_filePath(dir, name), data, info.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
|
||||
}
|
||||
|
||||
// RestoreAssets restores an asset under the given directory recursively.
|
||||
func RestoreAssets(dir, name string) error {
|
||||
children, err := AssetDir(name)
|
||||
// File
|
||||
if err != nil {
|
||||
return RestoreAsset(dir, name)
|
||||
}
|
||||
// Dir
|
||||
for _, child := range children {
|
||||
err = RestoreAssets(dir, filepath.Join(name, child))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func _filePath(dir, name string) string {
|
||||
canonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
return filepath.Join(append([]string{dir}, strings.Split(canonicalName, "/")...)...)
|
||||
}
|
||||
125
vendor/github.com/status-im/status-go/protocol/encryption/multidevice/multidevice.go
generated
vendored
Normal file
125
vendor/github.com/status-im/status-go/protocol/encryption/multidevice/multidevice.go
generated
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
package multidevice
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"database/sql"
|
||||
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
)
|
||||
|
||||
type InstallationMetadata struct {
|
||||
// The name of the device
|
||||
Name string `json:"name"`
|
||||
// The type of device
|
||||
DeviceType string `json:"deviceType"`
|
||||
// The FCMToken for mobile devices
|
||||
FCMToken string `json:"fcmToken"`
|
||||
}
|
||||
|
||||
type Installation struct {
|
||||
// Identity is the string identity of the owner
|
||||
Identity string `json:"identity"`
|
||||
// The installation-id of the device
|
||||
ID string `json:"id"`
|
||||
// The last known protocol version of the device
|
||||
Version uint32 `json:"version"`
|
||||
// Enabled is whether the installation is enabled
|
||||
Enabled bool `json:"enabled"`
|
||||
// Timestamp is the last time we saw this device
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
// InstallationMetadata
|
||||
InstallationMetadata *InstallationMetadata `json:"metadata"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
MaxInstallations int
|
||||
ProtocolVersion uint32
|
||||
InstallationID string
|
||||
}
|
||||
|
||||
type Multidevice struct {
|
||||
persistence *sqlitePersistence
|
||||
config *Config
|
||||
}
|
||||
|
||||
func New(db *sql.DB, config *Config) *Multidevice {
|
||||
return &Multidevice{
|
||||
config: config,
|
||||
persistence: newSQLitePersistence(db),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Multidevice) InstallationID() string {
|
||||
return s.config.InstallationID
|
||||
}
|
||||
|
||||
func (s *Multidevice) GetActiveInstallations(identity *ecdsa.PublicKey) ([]*Installation, error) {
|
||||
identityC := crypto.CompressPubkey(identity)
|
||||
return s.persistence.GetActiveInstallations(s.config.MaxInstallations, identityC)
|
||||
}
|
||||
|
||||
func (s *Multidevice) GetOurActiveInstallations(identity *ecdsa.PublicKey) ([]*Installation, error) {
|
||||
identityC := crypto.CompressPubkey(identity)
|
||||
installations, err := s.persistence.GetActiveInstallations(s.config.MaxInstallations-1, identityC)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
installations = append(installations, &Installation{
|
||||
ID: s.config.InstallationID,
|
||||
Version: s.config.ProtocolVersion,
|
||||
})
|
||||
|
||||
return installations, nil
|
||||
}
|
||||
|
||||
func (s *Multidevice) GetOurInstallations(identity *ecdsa.PublicKey) ([]*Installation, error) {
|
||||
var found bool
|
||||
identityC := crypto.CompressPubkey(identity)
|
||||
installations, err := s.persistence.GetInstallations(identityC)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, installation := range installations {
|
||||
if installation.ID == s.config.InstallationID {
|
||||
found = true
|
||||
installation.Enabled = true
|
||||
installation.Version = s.config.ProtocolVersion
|
||||
}
|
||||
|
||||
}
|
||||
if !found {
|
||||
installations = append(installations, &Installation{
|
||||
ID: s.config.InstallationID,
|
||||
Enabled: true,
|
||||
Version: s.config.ProtocolVersion,
|
||||
})
|
||||
}
|
||||
|
||||
return installations, nil
|
||||
}
|
||||
|
||||
func (s *Multidevice) AddInstallations(identity []byte, timestamp int64, installations []*Installation, defaultEnabled bool) ([]*Installation, error) {
|
||||
return s.persistence.AddInstallations(identity, timestamp, installations, defaultEnabled)
|
||||
}
|
||||
|
||||
func (s *Multidevice) SetInstallationMetadata(identity *ecdsa.PublicKey, installationID string, metadata *InstallationMetadata) error {
|
||||
identityC := crypto.CompressPubkey(identity)
|
||||
return s.persistence.SetInstallationMetadata(identityC, installationID, metadata)
|
||||
}
|
||||
|
||||
func (s *Multidevice) SetInstallationName(identity *ecdsa.PublicKey, installationID string, name string) error {
|
||||
identityC := crypto.CompressPubkey(identity)
|
||||
return s.persistence.SetInstallationName(identityC, installationID, name)
|
||||
}
|
||||
|
||||
func (s *Multidevice) EnableInstallation(identity *ecdsa.PublicKey, installationID string) error {
|
||||
identityC := crypto.CompressPubkey(identity)
|
||||
return s.persistence.EnableInstallation(identityC, installationID)
|
||||
}
|
||||
|
||||
func (s *Multidevice) DisableInstallation(myIdentityKey *ecdsa.PublicKey, installationID string) error {
|
||||
myIdentityKeyC := crypto.CompressPubkey(myIdentityKey)
|
||||
return s.persistence.DisableInstallation(myIdentityKeyC, installationID)
|
||||
}
|
||||
278
vendor/github.com/status-im/status-go/protocol/encryption/multidevice/persistence.go
generated
vendored
Normal file
278
vendor/github.com/status-im/status-go/protocol/encryption/multidevice/persistence.go
generated
vendored
Normal file
@@ -0,0 +1,278 @@
|
||||
package multidevice
|
||||
|
||||
import "database/sql"
|
||||
|
||||
type sqlitePersistence struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func newSQLitePersistence(db *sql.DB) *sqlitePersistence {
|
||||
return &sqlitePersistence{db: db}
|
||||
}
|
||||
|
||||
// GetActiveInstallations returns the active installations for a given identity
|
||||
func (s *sqlitePersistence) GetActiveInstallations(maxInstallations int, identity []byte) ([]*Installation, error) {
|
||||
stmt, err := s.db.Prepare(`SELECT installation_id, version
|
||||
FROM installations
|
||||
WHERE enabled = 1 AND identity = ?
|
||||
ORDER BY timestamp DESC
|
||||
LIMIT ?`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var installations []*Installation
|
||||
rows, err := stmt.Query(identity, maxInstallations)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var (
|
||||
installationID string
|
||||
version uint32
|
||||
)
|
||||
err = rows.Scan(
|
||||
&installationID,
|
||||
&version,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
installations = append(installations, &Installation{
|
||||
ID: installationID,
|
||||
Version: version,
|
||||
Enabled: true,
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
return installations, nil
|
||||
|
||||
}
|
||||
|
||||
// GetInstallations returns all the installations for a given identity
|
||||
// we both return the installations & the metadata
|
||||
// metadata is currently stored in a separate table, as in some cases we
|
||||
// might have metadata for a device, but no other information on the device
|
||||
func (s *sqlitePersistence) GetInstallations(identity []byte) ([]*Installation, error) {
|
||||
installationMap := make(map[string]*Installation)
|
||||
var installations []*Installation
|
||||
|
||||
// We query both tables as sqlite does not support full outer joins
|
||||
installationsStmt, err := s.db.Prepare(`SELECT installation_id, version, enabled, timestamp FROM installations WHERE identity = ?`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer installationsStmt.Close()
|
||||
|
||||
installationRows, err := installationsStmt.Query(identity)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for installationRows.Next() {
|
||||
var installation Installation
|
||||
err = installationRows.Scan(
|
||||
&installation.ID,
|
||||
&installation.Version,
|
||||
&installation.Enabled,
|
||||
&installation.Timestamp,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// We initialized to empty in this case as we want to
|
||||
// return metadata as well in this endpoint, but not in others
|
||||
installation.InstallationMetadata = &InstallationMetadata{}
|
||||
installationMap[installation.ID] = &installation
|
||||
}
|
||||
|
||||
metadataStmt, err := s.db.Prepare(`SELECT installation_id, name, device_type, fcm_token FROM installation_metadata WHERE identity = ?`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer metadataStmt.Close()
|
||||
|
||||
metadataRows, err := metadataStmt.Query(identity)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for metadataRows.Next() {
|
||||
var (
|
||||
installationID string
|
||||
name sql.NullString
|
||||
deviceType sql.NullString
|
||||
fcmToken sql.NullString
|
||||
installation *Installation
|
||||
)
|
||||
err = metadataRows.Scan(
|
||||
&installationID,
|
||||
&name,
|
||||
&deviceType,
|
||||
&fcmToken,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, ok := installationMap[installationID]; ok {
|
||||
installation = installationMap[installationID]
|
||||
} else {
|
||||
installation = &Installation{ID: installationID}
|
||||
}
|
||||
installation.InstallationMetadata = &InstallationMetadata{
|
||||
Name: name.String,
|
||||
DeviceType: deviceType.String,
|
||||
FCMToken: fcmToken.String,
|
||||
}
|
||||
installationMap[installationID] = installation
|
||||
}
|
||||
|
||||
for _, installation := range installationMap {
|
||||
installations = append(installations, installation)
|
||||
}
|
||||
|
||||
return installations, nil
|
||||
}
|
||||
|
||||
// AddInstallations adds the installations for a given identity, maintaining the enabled flag
|
||||
func (s *sqlitePersistence) AddInstallations(identity []byte, timestamp int64, installations []*Installation, defaultEnabled bool) ([]*Installation, error) {
|
||||
tx, err := s.db.Begin()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var insertedInstallations []*Installation
|
||||
|
||||
for _, installation := range installations {
|
||||
stmt, err := tx.Prepare(`SELECT enabled, version
|
||||
FROM installations
|
||||
WHERE identity = ? AND installation_id = ?
|
||||
LIMIT 1`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
var oldEnabled bool
|
||||
// We don't override version once we saw one
|
||||
var oldVersion uint32
|
||||
latestVersion := installation.Version
|
||||
|
||||
err = stmt.QueryRow(identity, installation.ID).Scan(&oldEnabled, &oldVersion)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
stmt, err = tx.Prepare(`INSERT INTO installations(identity, installation_id, timestamp, enabled, version)
|
||||
VALUES (?, ?, ?, ?, ?)`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
_, err = stmt.Exec(
|
||||
identity,
|
||||
installation.ID,
|
||||
timestamp,
|
||||
defaultEnabled,
|
||||
latestVersion,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
insertedInstallations = append(insertedInstallations, installation)
|
||||
} else {
|
||||
// We update timestamp if present without changing enabled, only if this is a new bundle
|
||||
// and we set the version to the latest we ever saw
|
||||
if oldVersion > installation.Version {
|
||||
latestVersion = oldVersion
|
||||
}
|
||||
|
||||
stmt, err = tx.Prepare(`UPDATE installations
|
||||
SET timestamp = ?, enabled = ?, version = ?
|
||||
WHERE identity = ?
|
||||
AND installation_id = ?
|
||||
AND timestamp < ?`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
_, err = stmt.Exec(
|
||||
timestamp,
|
||||
oldEnabled,
|
||||
latestVersion,
|
||||
identity,
|
||||
installation.ID,
|
||||
timestamp,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
_ = tx.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return insertedInstallations, nil
|
||||
|
||||
}
|
||||
|
||||
// EnableInstallation enables the installation
|
||||
func (s *sqlitePersistence) 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 *sqlitePersistence) 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
|
||||
}
|
||||
|
||||
// SetInstallationMetadata sets the metadata for a given installation
|
||||
func (s *sqlitePersistence) SetInstallationMetadata(identity []byte, installationID string, metadata *InstallationMetadata) error {
|
||||
stmt, err := s.db.Prepare(`INSERT INTO installation_metadata(name, device_type, fcm_token, identity, installation_id) VALUES(?,?,?,?,?)`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = stmt.Exec(metadata.Name, metadata.DeviceType, metadata.FCMToken, identity, installationID)
|
||||
return err
|
||||
}
|
||||
|
||||
// SetInstallationName sets the only the name in metadata for a given installation
|
||||
func (s *sqlitePersistence) SetInstallationName(identity []byte, installationID string, name string) error {
|
||||
stmt, err := s.db.Prepare(`UPDATE installation_metadata
|
||||
SET name = ?
|
||||
WHERE identity = ? AND installation_id = ?`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = stmt.Exec(name, identity, installationID)
|
||||
return err
|
||||
}
|
||||
1005
vendor/github.com/status-im/status-go/protocol/encryption/persistence.go
generated
vendored
Normal file
1005
vendor/github.com/status-im/status-go/protocol/encryption/persistence.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
824
vendor/github.com/status-im/status-go/protocol/encryption/protocol.go
generated
vendored
Normal file
824
vendor/github.com/status-im/status-go/protocol/encryption/protocol.go
generated
vendored
Normal file
@@ -0,0 +1,824 @@
|
||||
package encryption
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
|
||||
"github.com/status-im/status-go/protocol/encryption/multidevice"
|
||||
"github.com/status-im/status-go/protocol/encryption/publisher"
|
||||
"github.com/status-im/status-go/protocol/encryption/sharedsecret"
|
||||
)
|
||||
|
||||
//go:generate protoc --go_out=. ./protocol_message.proto
|
||||
|
||||
const (
|
||||
protocolVersion = 1
|
||||
sharedSecretNegotiationVersion = 1
|
||||
partitionedTopicMinVersion = 1
|
||||
defaultMinVersion = 0
|
||||
)
|
||||
|
||||
type PartitionTopicMode int
|
||||
|
||||
const (
|
||||
PartitionTopicNoSupport PartitionTopicMode = iota
|
||||
PartitionTopicV1
|
||||
)
|
||||
|
||||
type ProtocolMessageSpec struct {
|
||||
Message *ProtocolMessage
|
||||
// Installations is the targeted devices
|
||||
Installations []*multidevice.Installation
|
||||
// SharedSecret is a shared secret established among the installations
|
||||
SharedSecret *sharedsecret.Secret
|
||||
// AgreedSecret indicates whether the shared secret has been agreed
|
||||
AgreedSecret bool
|
||||
// Public means that the spec contains a public wrapped message
|
||||
Public bool
|
||||
}
|
||||
|
||||
func (p *ProtocolMessageSpec) MinVersion() uint32 {
|
||||
if len(p.Installations) == 0 {
|
||||
return defaultMinVersion
|
||||
}
|
||||
|
||||
version := p.Installations[0].Version
|
||||
|
||||
for _, installation := range p.Installations[1:] {
|
||||
if installation.Version < version {
|
||||
version = installation.Version
|
||||
}
|
||||
}
|
||||
return version
|
||||
}
|
||||
|
||||
func (p *ProtocolMessageSpec) PartitionedTopicMode() PartitionTopicMode {
|
||||
if p.MinVersion() >= partitionedTopicMinVersion {
|
||||
return PartitionTopicV1
|
||||
}
|
||||
return PartitionTopicNoSupport
|
||||
}
|
||||
|
||||
type Protocol struct {
|
||||
encryptor *encryptor
|
||||
secret *sharedsecret.SharedSecret
|
||||
multidevice *multidevice.Multidevice
|
||||
publisher *publisher.Publisher
|
||||
subscriptions *Subscriptions
|
||||
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
var (
|
||||
// ErrNoPayload means that there was no payload found in the received protocol message.
|
||||
ErrNoPayload = errors.New("no payload")
|
||||
ErrNoRatchetKey = errors.New("no ratchet key for given keyID")
|
||||
)
|
||||
|
||||
// New creates a new ProtocolService instance
|
||||
func New(
|
||||
db *sql.DB,
|
||||
installationID string,
|
||||
logger *zap.Logger,
|
||||
) *Protocol {
|
||||
return NewWithEncryptorConfig(
|
||||
db,
|
||||
installationID,
|
||||
defaultEncryptorConfig(installationID, logger),
|
||||
logger,
|
||||
)
|
||||
}
|
||||
|
||||
// DB and migrations are shared between encryption package
|
||||
// and its sub-packages.
|
||||
func NewWithEncryptorConfig(
|
||||
db *sql.DB,
|
||||
installationID string,
|
||||
encryptorConfig encryptorConfig,
|
||||
logger *zap.Logger,
|
||||
) *Protocol {
|
||||
return &Protocol{
|
||||
encryptor: newEncryptor(db, encryptorConfig),
|
||||
secret: sharedsecret.New(db, logger),
|
||||
multidevice: multidevice.New(db, &multidevice.Config{
|
||||
MaxInstallations: 3,
|
||||
ProtocolVersion: protocolVersion,
|
||||
InstallationID: installationID,
|
||||
}),
|
||||
publisher: publisher.New(logger),
|
||||
logger: logger.With(zap.Namespace("Protocol")),
|
||||
}
|
||||
}
|
||||
|
||||
type Subscriptions struct {
|
||||
SharedSecrets []*sharedsecret.Secret
|
||||
SendContactCode <-chan struct{}
|
||||
Quit chan struct{}
|
||||
}
|
||||
|
||||
func (p *Protocol) Start(myIdentity *ecdsa.PrivateKey) (*Subscriptions, error) {
|
||||
// Propagate currently cached shared secrets.
|
||||
secrets, err := p.secret.All()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get all secrets")
|
||||
}
|
||||
p.subscriptions = &Subscriptions{
|
||||
SharedSecrets: secrets,
|
||||
SendContactCode: p.publisher.Start(),
|
||||
Quit: make(chan struct{}),
|
||||
}
|
||||
return p.subscriptions, nil
|
||||
}
|
||||
|
||||
func (p *Protocol) Stop() error {
|
||||
p.publisher.Stop()
|
||||
if p.subscriptions != nil {
|
||||
close(p.subscriptions.Quit)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Protocol) addBundle(myIdentityKey *ecdsa.PrivateKey, msg *ProtocolMessage) error {
|
||||
// Get a bundle
|
||||
installations, err := p.multidevice.GetOurActiveInstallations(&myIdentityKey.PublicKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bundle, err := p.encryptor.CreateBundle(myIdentityKey, installations)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg.Bundles = []*Bundle{bundle}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// BuildPublicMessage marshals a public chat message given the user identity private key and a payload
|
||||
func (p *Protocol) BuildPublicMessage(myIdentityKey *ecdsa.PrivateKey, payload []byte) (*ProtocolMessageSpec, error) {
|
||||
// Build message not encrypted
|
||||
message := &ProtocolMessage{
|
||||
InstallationId: p.encryptor.config.InstallationID,
|
||||
PublicMessage: payload,
|
||||
}
|
||||
|
||||
err := p.addBundle(myIdentityKey, message)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ProtocolMessageSpec{Message: message, Public: true}, nil
|
||||
}
|
||||
|
||||
// BuildEncryptedMessage returns a 1:1 chat message and optionally a negotiated topic given the user identity private key, the recipient's public key, and a payload
|
||||
func (p *Protocol) BuildEncryptedMessage(myIdentityKey *ecdsa.PrivateKey, publicKey *ecdsa.PublicKey, payload []byte) (*ProtocolMessageSpec, error) {
|
||||
|
||||
// Get recipients installations.
|
||||
activeInstallations, err := p.multidevice.GetActiveInstallations(publicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Encrypt payload
|
||||
encryptedMessagesByInstalls, installations, err := p.encryptor.EncryptPayload(publicKey, myIdentityKey, activeInstallations, payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Build message
|
||||
message := &ProtocolMessage{
|
||||
InstallationId: p.encryptor.config.InstallationID,
|
||||
EncryptedMessage: encryptedMessagesByInstalls,
|
||||
}
|
||||
|
||||
err = p.addBundle(myIdentityKey, message)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check who we are sending the message to, and see if we have a shared secret
|
||||
// across devices
|
||||
var installationIDs []string
|
||||
for installationID := range message.GetEncryptedMessage() {
|
||||
if installationID != noInstallationID {
|
||||
installationIDs = append(installationIDs, installationID)
|
||||
}
|
||||
}
|
||||
|
||||
sharedSecret, agreed, err := p.secret.Agreed(myIdentityKey, p.encryptor.config.InstallationID, publicKey, installationIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
spec := &ProtocolMessageSpec{
|
||||
SharedSecret: sharedSecret,
|
||||
AgreedSecret: agreed,
|
||||
Message: message,
|
||||
Installations: installations,
|
||||
}
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
func (p *Protocol) GenerateHashRatchetKey(groupID []byte) (*HashRatchetKeyCompatibility, error) {
|
||||
return p.encryptor.GenerateHashRatchetKey(groupID)
|
||||
}
|
||||
|
||||
func (p *Protocol) GetAllHREncodedKeys(groupID []byte) ([]byte, error) {
|
||||
keys, err := p.encryptor.persistence.GetKeysForGroup(groupID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(keys) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return p.GetMarshaledHREncodedKeys(groupID, keys)
|
||||
}
|
||||
|
||||
// GetKeyIDsForGroup returns a slice of key IDs belonging to a given group ID
|
||||
func (p *Protocol) GetKeysForGroup(groupID []byte) ([]*HashRatchetKeyCompatibility, error) {
|
||||
return p.encryptor.persistence.GetKeysForGroup(groupID)
|
||||
}
|
||||
|
||||
func (p *Protocol) GetHREncodedKeys(groupID []byte, ratchets []*HashRatchetKeyCompatibility) *HRKeys {
|
||||
keys := &HRKeys{}
|
||||
for _, ratchet := range ratchets {
|
||||
key := &HRKey{
|
||||
DeprecatedKeyId: ratchet.DeprecatedKeyID(),
|
||||
Key: ratchet.Key,
|
||||
Timestamp: ratchet.Timestamp,
|
||||
}
|
||||
keys.Keys = append(keys.Keys, key)
|
||||
}
|
||||
|
||||
return keys
|
||||
}
|
||||
|
||||
func (p *Protocol) GetMarshaledHREncodedKeys(groupID []byte, ratchets []*HashRatchetKeyCompatibility) ([]byte, error) {
|
||||
keys := p.GetHREncodedKeys(groupID, ratchets)
|
||||
return proto.Marshal(keys)
|
||||
}
|
||||
|
||||
// BuildHashRatchetRekeyGroup builds a public message
|
||||
// with the new key
|
||||
func (p *Protocol) BuildHashRatchetReKeyGroupMessage(myIdentityKey *ecdsa.PrivateKey, recipients []*ecdsa.PublicKey, groupID []byte, payload []byte, ratchet *HashRatchetKeyCompatibility) (*ProtocolMessageSpec, error) {
|
||||
|
||||
var err error
|
||||
if ratchet == nil {
|
||||
ratchet, err = p.GenerateHashRatchetKey(groupID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
message, err := buildGroupRekeyMessage(myIdentityKey, groupID, ratchet.Timestamp, ratchet.Key, recipients)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keys := &HRKeys{
|
||||
RekeyGroup: message,
|
||||
}
|
||||
spec := &ProtocolMessageSpec{
|
||||
Public: true,
|
||||
Message: &ProtocolMessage{
|
||||
InstallationId: p.encryptor.config.InstallationID,
|
||||
EncryptedMessage: map[string]*EncryptedMessageProtocol{noInstallationID: &EncryptedMessageProtocol{
|
||||
HRHeader: &HRHeader{
|
||||
SeqNo: 0,
|
||||
GroupId: groupID,
|
||||
Keys: keys,
|
||||
},
|
||||
Payload: payload,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
// BuildHashRatchetKeyExchangeMessage builds a 1:1 message
|
||||
// containing newly generated hash ratchet key
|
||||
func (p *Protocol) BuildHashRatchetKeyExchangeMessage(myIdentityKey *ecdsa.PrivateKey, publicKey *ecdsa.PublicKey, groupID []byte, ratchets []*HashRatchetKeyCompatibility) (*ProtocolMessageSpec, error) {
|
||||
|
||||
keys := p.GetHREncodedKeys(groupID, ratchets)
|
||||
|
||||
encodedKeys, err := proto.Marshal(keys)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response, err := p.BuildEncryptedMessage(myIdentityKey, publicKey, encodedKeys)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Loop through installations and assign HRHeader
|
||||
// SeqNo=0 has a special meaning for HandleMessage
|
||||
// and signifies a message with hash ratchet key payload
|
||||
for _, v := range response.Message.EncryptedMessage {
|
||||
v.HRHeader = &HRHeader{
|
||||
SeqNo: 0,
|
||||
GroupId: groupID,
|
||||
Keys: keys,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return response, err
|
||||
}
|
||||
|
||||
func (p *Protocol) BuildHashRatchetKeyExchangeMessageWithPayload(myIdentityKey *ecdsa.PrivateKey, publicKey *ecdsa.PublicKey, groupID []byte, ratchets []*HashRatchetKeyCompatibility, payload []byte) (*ProtocolMessageSpec, error) {
|
||||
|
||||
keys := p.GetHREncodedKeys(groupID, ratchets)
|
||||
|
||||
response, err := p.BuildEncryptedMessage(myIdentityKey, publicKey, payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Loop through installations and assign HRHeader
|
||||
// SeqNo=0 has a special meaning for HandleMessage
|
||||
// and signifies a message with hash ratchet key payload
|
||||
for _, v := range response.Message.EncryptedMessage {
|
||||
v.HRHeader = &HRHeader{
|
||||
SeqNo: 0,
|
||||
GroupId: groupID,
|
||||
Keys: keys,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return response, err
|
||||
}
|
||||
|
||||
func (p *Protocol) GetCurrentKeyForGroup(groupID []byte) (*HashRatchetKeyCompatibility, error) {
|
||||
return p.encryptor.persistence.GetCurrentKeyForGroup(groupID)
|
||||
|
||||
}
|
||||
|
||||
// BuildHashRatchetMessage returns a hash ratchet chat message
|
||||
func (p *Protocol) BuildHashRatchetMessage(groupID []byte, payload []byte) (*ProtocolMessageSpec, error) {
|
||||
|
||||
ratchet, err := p.encryptor.persistence.GetCurrentKeyForGroup(groupID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Encrypt payload
|
||||
encryptedMessagesByInstalls, err := p.encryptor.EncryptHashRatchetPayload(ratchet, payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Build message
|
||||
message := &ProtocolMessage{
|
||||
InstallationId: p.encryptor.config.InstallationID,
|
||||
EncryptedMessage: encryptedMessagesByInstalls,
|
||||
}
|
||||
|
||||
spec := &ProtocolMessageSpec{
|
||||
Message: message,
|
||||
}
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
func (p *Protocol) GetKeyExMessageSpecs(groupID []byte, identity *ecdsa.PrivateKey, recipients []*ecdsa.PublicKey, forceRekey bool) ([]*ProtocolMessageSpec, error) {
|
||||
var ratchets []*HashRatchetKeyCompatibility
|
||||
var err error
|
||||
if !forceRekey {
|
||||
ratchets, err = p.encryptor.persistence.GetKeysForGroup(groupID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if len(ratchets) == 0 || forceRekey {
|
||||
ratchet, err := p.GenerateHashRatchetKey(groupID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ratchets = []*HashRatchetKeyCompatibility{ratchet}
|
||||
}
|
||||
specs := make([]*ProtocolMessageSpec, len(recipients))
|
||||
for i, recipient := range recipients {
|
||||
keyExMsg, err := p.BuildHashRatchetKeyExchangeMessage(identity, recipient, groupID, ratchets)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
specs[i] = keyExMsg
|
||||
|
||||
}
|
||||
|
||||
return specs, nil
|
||||
}
|
||||
|
||||
// BuildDHMessage builds a message with DH encryption so that it can be decrypted by any other device.
|
||||
func (p *Protocol) BuildDHMessage(myIdentityKey *ecdsa.PrivateKey, destination *ecdsa.PublicKey, payload []byte) (*ProtocolMessageSpec, error) {
|
||||
// Encrypt payload
|
||||
encryptionResponse, err := p.encryptor.EncryptPayloadWithDH(destination, payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Build message
|
||||
message := &ProtocolMessage{
|
||||
InstallationId: p.encryptor.config.InstallationID,
|
||||
EncryptedMessage: encryptionResponse,
|
||||
}
|
||||
|
||||
err = p.addBundle(myIdentityKey, message)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ProtocolMessageSpec{Message: message}, nil
|
||||
}
|
||||
|
||||
// ProcessPublicBundle processes a received X3DH bundle.
|
||||
func (p *Protocol) ProcessPublicBundle(myIdentityKey *ecdsa.PrivateKey, bundle *Bundle) ([]*multidevice.Installation, error) {
|
||||
logger := p.logger.With(zap.String("site", "ProcessPublicBundle"))
|
||||
|
||||
if err := p.encryptor.ProcessPublicBundle(myIdentityKey, bundle); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
installations, enabled, err := p.recoverInstallationsFromBundle(myIdentityKey, bundle)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO(adam): why do we add installations using identity obtained from GetIdentity()
|
||||
// instead of the output of crypto.CompressPubkey()? I tried the second option
|
||||
// and the unit tests TestTopic and TestMaxDevices fail.
|
||||
identityFromBundle := bundle.GetIdentity()
|
||||
theirIdentity, err := ExtractIdentity(bundle)
|
||||
if err != nil {
|
||||
logger.Panic("unrecoverable error extracting identity", zap.Error(err))
|
||||
}
|
||||
compressedIdentity := crypto.CompressPubkey(theirIdentity)
|
||||
if !bytes.Equal(identityFromBundle, compressedIdentity) {
|
||||
logger.Panic("identity from bundle and compressed are not equal")
|
||||
}
|
||||
|
||||
return p.multidevice.AddInstallations(bundle.GetIdentity(), bundle.GetTimestamp(), installations, enabled)
|
||||
}
|
||||
|
||||
func (p *Protocol) GetMultiDevice() *multidevice.Multidevice {
|
||||
return p.multidevice
|
||||
}
|
||||
|
||||
// recoverInstallationsFromBundle extracts installations from the bundle.
|
||||
// It returns extracted installations and true if the installations
|
||||
// are ours, i.e. the bundle was created by our identity key.
|
||||
func (p *Protocol) recoverInstallationsFromBundle(myIdentityKey *ecdsa.PrivateKey, bundle *Bundle) ([]*multidevice.Installation, bool, error) {
|
||||
var installations []*multidevice.Installation
|
||||
|
||||
theirIdentity, err := ExtractIdentity(bundle)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
myIdentityStr := fmt.Sprintf("0x%x", crypto.FromECDSAPub(&myIdentityKey.PublicKey))
|
||||
theirIdentityStr := fmt.Sprintf("0x%x", crypto.FromECDSAPub(theirIdentity))
|
||||
// Any device from other peers will be considered enabled, ours needs to
|
||||
// be explicitly enabled.
|
||||
enabled := theirIdentityStr != myIdentityStr
|
||||
signedPreKeys := bundle.GetSignedPreKeys()
|
||||
|
||||
for installationID, signedPreKey := range signedPreKeys {
|
||||
if installationID != p.multidevice.InstallationID() {
|
||||
installations = append(installations, &multidevice.Installation{
|
||||
Identity: theirIdentityStr,
|
||||
ID: installationID,
|
||||
Version: signedPreKey.GetProtocolVersion(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return installations, enabled, nil
|
||||
}
|
||||
|
||||
// GetBundle retrieves or creates a X3DH bundle, given a private identity key.
|
||||
func (p *Protocol) GetBundle(myIdentityKey *ecdsa.PrivateKey) (*Bundle, error) {
|
||||
installations, err := p.multidevice.GetOurActiveInstallations(&myIdentityKey.PublicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p.encryptor.CreateBundle(myIdentityKey, installations)
|
||||
}
|
||||
|
||||
// EnableInstallation enables an installation for multi-device sync.
|
||||
func (p *Protocol) EnableInstallation(myIdentityKey *ecdsa.PublicKey, installationID string) error {
|
||||
return p.multidevice.EnableInstallation(myIdentityKey, installationID)
|
||||
}
|
||||
|
||||
// DisableInstallation disables an installation for multi-device sync.
|
||||
func (p *Protocol) DisableInstallation(myIdentityKey *ecdsa.PublicKey, installationID string) error {
|
||||
return p.multidevice.DisableInstallation(myIdentityKey, installationID)
|
||||
}
|
||||
|
||||
// GetOurInstallations returns all the installations available given an identity
|
||||
func (p *Protocol) GetOurInstallations(myIdentityKey *ecdsa.PublicKey) ([]*multidevice.Installation, error) {
|
||||
return p.multidevice.GetOurInstallations(myIdentityKey)
|
||||
}
|
||||
|
||||
// GetOurActiveInstallations returns all the active installations available given an identity
|
||||
func (p *Protocol) GetOurActiveInstallations(myIdentityKey *ecdsa.PublicKey) ([]*multidevice.Installation, error) {
|
||||
return p.multidevice.GetOurActiveInstallations(myIdentityKey)
|
||||
}
|
||||
|
||||
// SetInstallationMetadata sets the metadata for our own installation
|
||||
func (p *Protocol) SetInstallationMetadata(myIdentityKey *ecdsa.PublicKey, installationID string, data *multidevice.InstallationMetadata) error {
|
||||
return p.multidevice.SetInstallationMetadata(myIdentityKey, installationID, data)
|
||||
}
|
||||
|
||||
// SetInstallationName sets the metadata for our own installation
|
||||
func (p *Protocol) SetInstallationName(myIdentityKey *ecdsa.PublicKey, installationID string, name string) error {
|
||||
return p.multidevice.SetInstallationName(myIdentityKey, installationID, name)
|
||||
}
|
||||
|
||||
// GetPublicBundle retrieves a public bundle given an identity
|
||||
func (p *Protocol) GetPublicBundle(theirIdentityKey *ecdsa.PublicKey) (*Bundle, error) {
|
||||
installations, err := p.multidevice.GetActiveInstallations(theirIdentityKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p.encryptor.GetPublicBundle(theirIdentityKey, installations)
|
||||
}
|
||||
|
||||
// ConfirmMessageProcessed confirms and deletes message keys for the given messages
|
||||
func (p *Protocol) ConfirmMessageProcessed(messageID []byte) error {
|
||||
logger := p.logger.With(zap.String("site", "ConfirmMessageProcessed"))
|
||||
logger.Debug("confirming message", zap.String("messageID", types.EncodeHex(messageID)))
|
||||
return p.encryptor.ConfirmMessageProcessed(messageID)
|
||||
}
|
||||
|
||||
type HashRatchetInfo struct {
|
||||
GroupID []byte
|
||||
KeyID []byte
|
||||
}
|
||||
type DecryptMessageResponse struct {
|
||||
DecryptedMessage []byte
|
||||
Installations []*multidevice.Installation
|
||||
SharedSecrets []*sharedsecret.Secret
|
||||
HashRatchetInfo []*HashRatchetInfo
|
||||
}
|
||||
|
||||
func (p *Protocol) HandleHashRatchetKeysPayload(groupID, encodedKeys []byte, myIdentityKey *ecdsa.PrivateKey, theirIdentityKey *ecdsa.PublicKey) ([]*HashRatchetInfo, error) {
|
||||
|
||||
keys := &HRKeys{}
|
||||
err := proto.Unmarshal(encodedKeys, keys)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p.HandleHashRatchetKeys(groupID, keys, myIdentityKey, theirIdentityKey)
|
||||
}
|
||||
|
||||
func (p *Protocol) HandleHashRatchetKeys(groupID []byte, keys *HRKeys, myIdentityKey *ecdsa.PrivateKey, theirIdentityKey *ecdsa.PublicKey) ([]*HashRatchetInfo, error) {
|
||||
if keys == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var info []*HashRatchetInfo
|
||||
|
||||
for _, key := range keys.Keys {
|
||||
ratchet := &HashRatchetKeyCompatibility{
|
||||
GroupID: groupID,
|
||||
Timestamp: key.Timestamp,
|
||||
Key: key.Key,
|
||||
}
|
||||
|
||||
// If there's no timestamp, is coming from an older client
|
||||
if key.Timestamp == 0 {
|
||||
ratchet.Timestamp = uint64(key.DeprecatedKeyId)
|
||||
}
|
||||
keyID, err := ratchet.GetKeyID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.logger.Debug("retrieved keys", zap.String("keyID", types.Bytes2Hex(keyID)))
|
||||
|
||||
// Payload contains hash ratchet key
|
||||
err = p.encryptor.persistence.SaveHashRatchetKey(ratchet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info = append(info, &HashRatchetInfo{GroupID: groupID, KeyID: keyID})
|
||||
}
|
||||
|
||||
if keys.RekeyGroup != nil {
|
||||
if keys.RekeyGroup.Timestamp == 0 {
|
||||
return nil, errors.New("timestamp can't be nil")
|
||||
}
|
||||
|
||||
encryptionKey, err := decryptGroupRekeyMessage(myIdentityKey, theirIdentityKey, keys.RekeyGroup)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(encryptionKey) != 0 {
|
||||
|
||||
ratchet := &HashRatchetKeyCompatibility{
|
||||
GroupID: groupID,
|
||||
Timestamp: keys.RekeyGroup.Timestamp,
|
||||
Key: encryptionKey,
|
||||
}
|
||||
|
||||
keyID, err := ratchet.GetKeyID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.logger.Debug("retrieved group keys", zap.String("keyID", types.Bytes2Hex(keyID)))
|
||||
// Payload contains hash ratchet key
|
||||
err = p.encryptor.persistence.SaveHashRatchetKey(ratchet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info = append(info, &HashRatchetInfo{GroupID: groupID, KeyID: keyID})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// HandleMessage unmarshals a message and processes it, decrypting it if it is a 1:1 message.
|
||||
func (p *Protocol) HandleMessage(
|
||||
myIdentityKey *ecdsa.PrivateKey,
|
||||
theirPublicKey *ecdsa.PublicKey,
|
||||
protocolMessage *ProtocolMessage,
|
||||
messageID []byte,
|
||||
) (*DecryptMessageResponse, error) {
|
||||
logger := p.logger.With(zap.String("site", "HandleMessage"))
|
||||
response := &DecryptMessageResponse{}
|
||||
|
||||
logger.Debug("received a protocol message",
|
||||
zap.String("sender-public-key",
|
||||
types.EncodeHex(crypto.FromECDSAPub(theirPublicKey))),
|
||||
zap.String("my-installation-id", p.encryptor.config.InstallationID),
|
||||
zap.String("messageID", types.EncodeHex(messageID)))
|
||||
|
||||
if p.encryptor == nil {
|
||||
return nil, errors.New("encryption service not initialized")
|
||||
}
|
||||
|
||||
// Process bundles
|
||||
for _, bundle := range protocolMessage.GetBundles() {
|
||||
// Should we stop processing if the bundle cannot be verified?
|
||||
newInstallations, err := p.ProcessPublicBundle(myIdentityKey, bundle)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
response.Installations = newInstallations
|
||||
}
|
||||
|
||||
// Check if it's a public message
|
||||
if publicMessage := protocolMessage.GetPublicMessage(); publicMessage != nil {
|
||||
// Nothing to do, as already in cleartext
|
||||
response.DecryptedMessage = publicMessage
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// Decrypt message
|
||||
if encryptedMessage := protocolMessage.GetEncryptedMessage(); encryptedMessage != nil {
|
||||
message, err := p.encryptor.DecryptPayload(
|
||||
myIdentityKey,
|
||||
theirPublicKey,
|
||||
protocolMessage.GetInstallationId(),
|
||||
encryptedMessage,
|
||||
messageID,
|
||||
)
|
||||
|
||||
if err == ErrHashRatchetGroupIDNotFound {
|
||||
msg := p.encryptor.GetMessage(encryptedMessage)
|
||||
|
||||
if msg != nil {
|
||||
if header := msg.GetHRHeader(); header != nil {
|
||||
response.HashRatchetInfo = append(response.HashRatchetInfo, &HashRatchetInfo{GroupID: header.GroupId, KeyID: header.KeyId})
|
||||
}
|
||||
}
|
||||
return response, err
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dmProtocol := encryptedMessage[p.encryptor.config.InstallationID]
|
||||
if dmProtocol == nil {
|
||||
dmProtocol = encryptedMessage[noInstallationID]
|
||||
}
|
||||
if dmProtocol != nil {
|
||||
hrHeader := dmProtocol.HRHeader
|
||||
if hrHeader != nil && hrHeader.SeqNo == 0 {
|
||||
var hashRatchetKeys []*HashRatchetInfo
|
||||
if hrHeader.Keys != nil {
|
||||
hashRatchetKeys, err = p.HandleHashRatchetKeys(hrHeader.GroupId, hrHeader.Keys, myIdentityKey, theirPublicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
} else {
|
||||
// For backward compatibility
|
||||
hashRatchetKeys, err = p.HandleHashRatchetKeysPayload(hrHeader.GroupId, message, myIdentityKey, theirPublicKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
response.HashRatchetInfo = hashRatchetKeys
|
||||
}
|
||||
}
|
||||
|
||||
bundles := protocolMessage.GetBundles()
|
||||
version := getProtocolVersion(bundles, protocolMessage.GetInstallationId())
|
||||
if version >= sharedSecretNegotiationVersion {
|
||||
sharedSecret, err := p.secret.Generate(myIdentityKey, theirPublicKey, protocolMessage.GetInstallationId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response.SharedSecrets = []*sharedsecret.Secret{sharedSecret}
|
||||
}
|
||||
response.DecryptedMessage = message
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// Return error
|
||||
return nil, ErrNoPayload
|
||||
}
|
||||
|
||||
func (p *Protocol) ShouldAdvertiseBundle(publicKey *ecdsa.PublicKey, time int64) (bool, error) {
|
||||
return p.publisher.ShouldAdvertiseBundle(publicKey, time)
|
||||
}
|
||||
|
||||
func (p *Protocol) ConfirmBundleAdvertisement(publicKey *ecdsa.PublicKey, time int64) {
|
||||
p.publisher.SetLastAck(publicKey, time)
|
||||
}
|
||||
|
||||
func (p *Protocol) BuildBundleAdvertiseMessage(myIdentityKey *ecdsa.PrivateKey, publicKey *ecdsa.PublicKey) (*ProtocolMessageSpec, error) {
|
||||
return p.BuildDHMessage(myIdentityKey, publicKey, nil)
|
||||
}
|
||||
|
||||
func getProtocolVersion(bundles []*Bundle, installationID string) uint32 {
|
||||
if installationID == "" {
|
||||
return defaultMinVersion
|
||||
}
|
||||
|
||||
for _, bundle := range bundles {
|
||||
if bundle != nil {
|
||||
signedPreKeys := bundle.GetSignedPreKeys()
|
||||
if signedPreKeys == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
signedPreKey := signedPreKeys[installationID]
|
||||
if signedPreKey == nil {
|
||||
return defaultMinVersion
|
||||
}
|
||||
|
||||
return signedPreKey.GetProtocolVersion()
|
||||
}
|
||||
}
|
||||
|
||||
return defaultMinVersion
|
||||
}
|
||||
|
||||
func (p *Protocol) EncryptWithHashRatchet(groupID []byte, payload []byte) ([]byte, *HashRatchetKeyCompatibility, uint32, error) {
|
||||
ratchet, err := p.encryptor.persistence.GetCurrentKeyForGroup(groupID)
|
||||
if err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
||||
encryptedPayload, newSeqNo, err := p.encryptor.EncryptWithHR(ratchet, payload)
|
||||
if err != nil {
|
||||
return nil, nil, 0, err
|
||||
}
|
||||
|
||||
return encryptedPayload, ratchet, newSeqNo, nil
|
||||
}
|
||||
|
||||
func (p *Protocol) DecryptWithHashRatchet(keyID []byte, seqNo uint32, payload []byte) ([]byte, error) {
|
||||
ratchet, err := p.encryptor.persistence.GetHashRatchetKeyByID(keyID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ratchet == nil {
|
||||
return nil, ErrNoRatchetKey
|
||||
}
|
||||
|
||||
return p.encryptor.DecryptWithHR(ratchet, seqNo, payload)
|
||||
}
|
||||
791
vendor/github.com/status-im/status-go/protocol/encryption/protocol_message.pb.go
generated
vendored
Normal file
791
vendor/github.com/status-im/status-go/protocol/encryption/protocol_message.pb.go
generated
vendored
Normal file
@@ -0,0 +1,791 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: protocol_message.proto
|
||||
|
||||
package encryption
|
||||
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
math "math"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type SignedPreKey struct {
|
||||
SignedPreKey []byte `protobuf:"bytes,1,opt,name=signed_pre_key,json=signedPreKey,proto3" json:"signed_pre_key,omitempty"`
|
||||
Version uint32 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"`
|
||||
ProtocolVersion uint32 `protobuf:"varint,3,opt,name=protocol_version,json=protocolVersion,proto3" json:"protocol_version,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *SignedPreKey) Reset() { *m = SignedPreKey{} }
|
||||
func (m *SignedPreKey) String() string { return proto.CompactTextString(m) }
|
||||
func (*SignedPreKey) ProtoMessage() {}
|
||||
func (*SignedPreKey) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_4e37b52004a72e16, []int{0}
|
||||
}
|
||||
|
||||
func (m *SignedPreKey) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_SignedPreKey.Unmarshal(m, b)
|
||||
}
|
||||
func (m *SignedPreKey) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_SignedPreKey.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *SignedPreKey) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_SignedPreKey.Merge(m, src)
|
||||
}
|
||||
func (m *SignedPreKey) XXX_Size() int {
|
||||
return xxx_messageInfo_SignedPreKey.Size(m)
|
||||
}
|
||||
func (m *SignedPreKey) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_SignedPreKey.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_SignedPreKey proto.InternalMessageInfo
|
||||
|
||||
func (m *SignedPreKey) GetSignedPreKey() []byte {
|
||||
if m != nil {
|
||||
return m.SignedPreKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *SignedPreKey) GetVersion() uint32 {
|
||||
if m != nil {
|
||||
return m.Version
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *SignedPreKey) GetProtocolVersion() uint32 {
|
||||
if m != nil {
|
||||
return m.ProtocolVersion
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// X3DH prekey bundle
|
||||
type Bundle struct {
|
||||
// Identity key
|
||||
Identity []byte `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"`
|
||||
// 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"`
|
||||
// Prekey signature
|
||||
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_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Bundle) Reset() { *m = Bundle{} }
|
||||
func (m *Bundle) String() string { return proto.CompactTextString(m) }
|
||||
func (*Bundle) ProtoMessage() {}
|
||||
func (*Bundle) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_4e37b52004a72e16, []int{1}
|
||||
}
|
||||
|
||||
func (m *Bundle) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Bundle.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Bundle) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Bundle.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *Bundle) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Bundle.Merge(m, src)
|
||||
}
|
||||
func (m *Bundle) XXX_Size() int {
|
||||
return xxx_messageInfo_Bundle.Size(m)
|
||||
}
|
||||
func (m *Bundle) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Bundle.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Bundle proto.InternalMessageInfo
|
||||
|
||||
func (m *Bundle) GetIdentity() []byte {
|
||||
if m != nil {
|
||||
return m.Identity
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Bundle) GetSignedPreKeys() map[string]*SignedPreKey {
|
||||
if m != nil {
|
||||
return m.SignedPreKeys
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Bundle) GetSignature() []byte {
|
||||
if m != nil {
|
||||
return m.Signature
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Bundle) GetTimestamp() int64 {
|
||||
if m != nil {
|
||||
return m.Timestamp
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type BundleContainer struct {
|
||||
// X3DH prekey bundle
|
||||
Bundle *Bundle `protobuf:"bytes,1,opt,name=bundle,proto3" json:"bundle,omitempty"`
|
||||
// Private signed prekey
|
||||
PrivateSignedPreKey []byte `protobuf:"bytes,2,opt,name=private_signed_pre_key,json=privateSignedPreKey,proto3" json:"private_signed_pre_key,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *BundleContainer) Reset() { *m = BundleContainer{} }
|
||||
func (m *BundleContainer) String() string { return proto.CompactTextString(m) }
|
||||
func (*BundleContainer) ProtoMessage() {}
|
||||
func (*BundleContainer) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_4e37b52004a72e16, []int{2}
|
||||
}
|
||||
|
||||
func (m *BundleContainer) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_BundleContainer.Unmarshal(m, b)
|
||||
}
|
||||
func (m *BundleContainer) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_BundleContainer.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *BundleContainer) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_BundleContainer.Merge(m, src)
|
||||
}
|
||||
func (m *BundleContainer) XXX_Size() int {
|
||||
return xxx_messageInfo_BundleContainer.Size(m)
|
||||
}
|
||||
func (m *BundleContainer) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_BundleContainer.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_BundleContainer proto.InternalMessageInfo
|
||||
|
||||
func (m *BundleContainer) GetBundle() *Bundle {
|
||||
if m != nil {
|
||||
return m.Bundle
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *BundleContainer) GetPrivateSignedPreKey() []byte {
|
||||
if m != nil {
|
||||
return m.PrivateSignedPreKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type DRHeader struct {
|
||||
// Current ratchet public key
|
||||
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
// Number of the message in the sending chain
|
||||
N uint32 `protobuf:"varint,2,opt,name=n,proto3" json:"n,omitempty"`
|
||||
// Length of the previous sending chain
|
||||
Pn uint32 `protobuf:"varint,3,opt,name=pn,proto3" json:"pn,omitempty"`
|
||||
// Bundle ID
|
||||
Id []byte `protobuf:"bytes,4,opt,name=id,proto3" json:"id,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *DRHeader) Reset() { *m = DRHeader{} }
|
||||
func (m *DRHeader) String() string { return proto.CompactTextString(m) }
|
||||
func (*DRHeader) ProtoMessage() {}
|
||||
func (*DRHeader) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_4e37b52004a72e16, []int{3}
|
||||
}
|
||||
|
||||
func (m *DRHeader) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_DRHeader.Unmarshal(m, b)
|
||||
}
|
||||
func (m *DRHeader) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_DRHeader.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *DRHeader) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_DRHeader.Merge(m, src)
|
||||
}
|
||||
func (m *DRHeader) XXX_Size() int {
|
||||
return xxx_messageInfo_DRHeader.Size(m)
|
||||
}
|
||||
func (m *DRHeader) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_DRHeader.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_DRHeader proto.InternalMessageInfo
|
||||
|
||||
func (m *DRHeader) GetKey() []byte {
|
||||
if m != nil {
|
||||
return m.Key
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *DRHeader) GetN() uint32 {
|
||||
if m != nil {
|
||||
return m.N
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *DRHeader) GetPn() uint32 {
|
||||
if m != nil {
|
||||
return m.Pn
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *DRHeader) GetId() []byte {
|
||||
if m != nil {
|
||||
return m.Id
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type DHHeader struct {
|
||||
// Compressed ephemeral public key
|
||||
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *DHHeader) Reset() { *m = DHHeader{} }
|
||||
func (m *DHHeader) String() string { return proto.CompactTextString(m) }
|
||||
func (*DHHeader) ProtoMessage() {}
|
||||
func (*DHHeader) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_4e37b52004a72e16, []int{4}
|
||||
}
|
||||
|
||||
func (m *DHHeader) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_DHHeader.Unmarshal(m, b)
|
||||
}
|
||||
func (m *DHHeader) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_DHHeader.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *DHHeader) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_DHHeader.Merge(m, src)
|
||||
}
|
||||
func (m *DHHeader) XXX_Size() int {
|
||||
return xxx_messageInfo_DHHeader.Size(m)
|
||||
}
|
||||
func (m *DHHeader) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_DHHeader.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_DHHeader proto.InternalMessageInfo
|
||||
|
||||
func (m *DHHeader) GetKey() []byte {
|
||||
if m != nil {
|
||||
return m.Key
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type X3DHHeader struct {
|
||||
// Ephemeral key used
|
||||
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
// Used bundle's signed prekey
|
||||
Id []byte `protobuf:"bytes,4,opt,name=id,proto3" json:"id,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *X3DHHeader) Reset() { *m = X3DHHeader{} }
|
||||
func (m *X3DHHeader) String() string { return proto.CompactTextString(m) }
|
||||
func (*X3DHHeader) ProtoMessage() {}
|
||||
func (*X3DHHeader) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_4e37b52004a72e16, []int{5}
|
||||
}
|
||||
|
||||
func (m *X3DHHeader) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_X3DHHeader.Unmarshal(m, b)
|
||||
}
|
||||
func (m *X3DHHeader) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_X3DHHeader.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *X3DHHeader) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_X3DHHeader.Merge(m, src)
|
||||
}
|
||||
func (m *X3DHHeader) XXX_Size() int {
|
||||
return xxx_messageInfo_X3DHHeader.Size(m)
|
||||
}
|
||||
func (m *X3DHHeader) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_X3DHHeader.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_X3DHHeader proto.InternalMessageInfo
|
||||
|
||||
func (m *X3DHHeader) GetKey() []byte {
|
||||
if m != nil {
|
||||
return m.Key
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *X3DHHeader) GetId() []byte {
|
||||
if m != nil {
|
||||
return m.Id
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Hash Ratchet Header
|
||||
type HRHeader struct {
|
||||
// deprecated group key ID
|
||||
DeprecatedKeyId uint32 `protobuf:"varint,1,opt,name=deprecated_key_id,json=deprecatedKeyId,proto3" json:"deprecated_key_id,omitempty"`
|
||||
// group message number for this key_id
|
||||
SeqNo uint32 `protobuf:"varint,2,opt,name=seq_no,json=seqNo,proto3" json:"seq_no,omitempty"`
|
||||
// group ID
|
||||
GroupId []byte `protobuf:"bytes,3,opt,name=group_id,json=groupId,proto3" json:"group_id,omitempty"`
|
||||
// group key ID
|
||||
KeyId []byte `protobuf:"bytes,4,opt,name=key_id,json=keyId,proto3" json:"key_id,omitempty"`
|
||||
Keys *HRKeys `protobuf:"bytes,5,opt,name=keys,proto3" json:"keys,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *HRHeader) Reset() { *m = HRHeader{} }
|
||||
func (m *HRHeader) String() string { return proto.CompactTextString(m) }
|
||||
func (*HRHeader) ProtoMessage() {}
|
||||
func (*HRHeader) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_4e37b52004a72e16, []int{6}
|
||||
}
|
||||
|
||||
func (m *HRHeader) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_HRHeader.Unmarshal(m, b)
|
||||
}
|
||||
func (m *HRHeader) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_HRHeader.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *HRHeader) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_HRHeader.Merge(m, src)
|
||||
}
|
||||
func (m *HRHeader) XXX_Size() int {
|
||||
return xxx_messageInfo_HRHeader.Size(m)
|
||||
}
|
||||
func (m *HRHeader) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_HRHeader.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_HRHeader proto.InternalMessageInfo
|
||||
|
||||
func (m *HRHeader) GetDeprecatedKeyId() uint32 {
|
||||
if m != nil {
|
||||
return m.DeprecatedKeyId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *HRHeader) GetSeqNo() uint32 {
|
||||
if m != nil {
|
||||
return m.SeqNo
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *HRHeader) GetGroupId() []byte {
|
||||
if m != nil {
|
||||
return m.GroupId
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *HRHeader) GetKeyId() []byte {
|
||||
if m != nil {
|
||||
return m.KeyId
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *HRHeader) GetKeys() *HRKeys {
|
||||
if m != nil {
|
||||
return m.Keys
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type RekeyGroup struct {
|
||||
Timestamp uint64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
|
||||
Keys map[uint32][]byte `protobuf:"bytes,4,rep,name=keys,proto3" json:"keys,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *RekeyGroup) Reset() { *m = RekeyGroup{} }
|
||||
func (m *RekeyGroup) String() string { return proto.CompactTextString(m) }
|
||||
func (*RekeyGroup) ProtoMessage() {}
|
||||
func (*RekeyGroup) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_4e37b52004a72e16, []int{7}
|
||||
}
|
||||
|
||||
func (m *RekeyGroup) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_RekeyGroup.Unmarshal(m, b)
|
||||
}
|
||||
func (m *RekeyGroup) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_RekeyGroup.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *RekeyGroup) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_RekeyGroup.Merge(m, src)
|
||||
}
|
||||
func (m *RekeyGroup) XXX_Size() int {
|
||||
return xxx_messageInfo_RekeyGroup.Size(m)
|
||||
}
|
||||
func (m *RekeyGroup) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_RekeyGroup.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_RekeyGroup proto.InternalMessageInfo
|
||||
|
||||
func (m *RekeyGroup) GetTimestamp() uint64 {
|
||||
if m != nil {
|
||||
return m.Timestamp
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *RekeyGroup) GetKeys() map[uint32][]byte {
|
||||
if m != nil {
|
||||
return m.Keys
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type HRKeys struct {
|
||||
Keys []*HRKey `protobuf:"bytes,1,rep,name=keys,proto3" json:"keys,omitempty"`
|
||||
RekeyGroup *RekeyGroup `protobuf:"bytes,2,opt,name=rekey_group,json=rekeyGroup,proto3" json:"rekey_group,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *HRKeys) Reset() { *m = HRKeys{} }
|
||||
func (m *HRKeys) String() string { return proto.CompactTextString(m) }
|
||||
func (*HRKeys) ProtoMessage() {}
|
||||
func (*HRKeys) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_4e37b52004a72e16, []int{8}
|
||||
}
|
||||
|
||||
func (m *HRKeys) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_HRKeys.Unmarshal(m, b)
|
||||
}
|
||||
func (m *HRKeys) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_HRKeys.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *HRKeys) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_HRKeys.Merge(m, src)
|
||||
}
|
||||
func (m *HRKeys) XXX_Size() int {
|
||||
return xxx_messageInfo_HRKeys.Size(m)
|
||||
}
|
||||
func (m *HRKeys) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_HRKeys.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_HRKeys proto.InternalMessageInfo
|
||||
|
||||
func (m *HRKeys) GetKeys() []*HRKey {
|
||||
if m != nil {
|
||||
return m.Keys
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *HRKeys) GetRekeyGroup() *RekeyGroup {
|
||||
if m != nil {
|
||||
return m.RekeyGroup
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type HRKey struct {
|
||||
DeprecatedKeyId uint32 `protobuf:"varint,1,opt,name=deprecated_key_id,json=deprecatedKeyId,proto3" json:"deprecated_key_id,omitempty"`
|
||||
Key []byte `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
|
||||
Timestamp uint64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *HRKey) Reset() { *m = HRKey{} }
|
||||
func (m *HRKey) String() string { return proto.CompactTextString(m) }
|
||||
func (*HRKey) ProtoMessage() {}
|
||||
func (*HRKey) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_4e37b52004a72e16, []int{9}
|
||||
}
|
||||
|
||||
func (m *HRKey) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_HRKey.Unmarshal(m, b)
|
||||
}
|
||||
func (m *HRKey) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_HRKey.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *HRKey) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_HRKey.Merge(m, src)
|
||||
}
|
||||
func (m *HRKey) XXX_Size() int {
|
||||
return xxx_messageInfo_HRKey.Size(m)
|
||||
}
|
||||
func (m *HRKey) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_HRKey.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_HRKey proto.InternalMessageInfo
|
||||
|
||||
func (m *HRKey) GetDeprecatedKeyId() uint32 {
|
||||
if m != nil {
|
||||
return m.DeprecatedKeyId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *HRKey) GetKey() []byte {
|
||||
if m != nil {
|
||||
return m.Key
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *HRKey) GetTimestamp() uint64 {
|
||||
if m != nil {
|
||||
return m.Timestamp
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Direct message value
|
||||
type EncryptedMessageProtocol struct {
|
||||
X3DHHeader *X3DHHeader `protobuf:"bytes,1,opt,name=X3DH_header,json=X3DHHeader,proto3" json:"X3DH_header,omitempty"`
|
||||
DRHeader *DRHeader `protobuf:"bytes,2,opt,name=DR_header,json=DRHeader,proto3" json:"DR_header,omitempty"`
|
||||
DHHeader *DHHeader `protobuf:"bytes,101,opt,name=DH_header,json=DHHeader,proto3" json:"DH_header,omitempty"`
|
||||
HRHeader *HRHeader `protobuf:"bytes,102,opt,name=HR_header,json=HRHeader,proto3" json:"HR_header,omitempty"`
|
||||
// Encrypted payload
|
||||
Payload []byte `protobuf:"bytes,3,opt,name=payload,proto3" json:"payload,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *EncryptedMessageProtocol) Reset() { *m = EncryptedMessageProtocol{} }
|
||||
func (m *EncryptedMessageProtocol) String() string { return proto.CompactTextString(m) }
|
||||
func (*EncryptedMessageProtocol) ProtoMessage() {}
|
||||
func (*EncryptedMessageProtocol) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_4e37b52004a72e16, []int{10}
|
||||
}
|
||||
|
||||
func (m *EncryptedMessageProtocol) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_EncryptedMessageProtocol.Unmarshal(m, b)
|
||||
}
|
||||
func (m *EncryptedMessageProtocol) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_EncryptedMessageProtocol.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *EncryptedMessageProtocol) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_EncryptedMessageProtocol.Merge(m, src)
|
||||
}
|
||||
func (m *EncryptedMessageProtocol) XXX_Size() int {
|
||||
return xxx_messageInfo_EncryptedMessageProtocol.Size(m)
|
||||
}
|
||||
func (m *EncryptedMessageProtocol) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_EncryptedMessageProtocol.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_EncryptedMessageProtocol proto.InternalMessageInfo
|
||||
|
||||
func (m *EncryptedMessageProtocol) GetX3DHHeader() *X3DHHeader {
|
||||
if m != nil {
|
||||
return m.X3DHHeader
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *EncryptedMessageProtocol) GetDRHeader() *DRHeader {
|
||||
if m != nil {
|
||||
return m.DRHeader
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *EncryptedMessageProtocol) GetDHHeader() *DHHeader {
|
||||
if m != nil {
|
||||
return m.DHHeader
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *EncryptedMessageProtocol) GetHRHeader() *HRHeader {
|
||||
if m != nil {
|
||||
return m.HRHeader
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *EncryptedMessageProtocol) GetPayload() []byte {
|
||||
if m != nil {
|
||||
return m.Payload
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Top-level protocol message
|
||||
type ProtocolMessage struct {
|
||||
// The device id of the sender
|
||||
InstallationId string `protobuf:"bytes,2,opt,name=installation_id,json=installationId,proto3" json:"installation_id,omitempty"`
|
||||
// List of bundles
|
||||
Bundles []*Bundle `protobuf:"bytes,3,rep,name=bundles,proto3" json:"bundles,omitempty"`
|
||||
// One to one message, encrypted, indexed by installation_id
|
||||
// TODO map here is redundant in case of community messages
|
||||
EncryptedMessage map[string]*EncryptedMessageProtocol `protobuf:"bytes,101,rep,name=encrypted_message,json=encryptedMessage,proto3" json:"encrypted_message,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
// Public chats, not encrypted
|
||||
PublicMessage []byte `protobuf:"bytes,102,opt,name=public_message,json=publicMessage,proto3" json:"public_message,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *ProtocolMessage) Reset() { *m = ProtocolMessage{} }
|
||||
func (m *ProtocolMessage) String() string { return proto.CompactTextString(m) }
|
||||
func (*ProtocolMessage) ProtoMessage() {}
|
||||
func (*ProtocolMessage) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_4e37b52004a72e16, []int{11}
|
||||
}
|
||||
|
||||
func (m *ProtocolMessage) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_ProtocolMessage.Unmarshal(m, b)
|
||||
}
|
||||
func (m *ProtocolMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_ProtocolMessage.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (m *ProtocolMessage) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ProtocolMessage.Merge(m, src)
|
||||
}
|
||||
func (m *ProtocolMessage) XXX_Size() int {
|
||||
return xxx_messageInfo_ProtocolMessage.Size(m)
|
||||
}
|
||||
func (m *ProtocolMessage) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_ProtocolMessage.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_ProtocolMessage proto.InternalMessageInfo
|
||||
|
||||
func (m *ProtocolMessage) GetInstallationId() string {
|
||||
if m != nil {
|
||||
return m.InstallationId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *ProtocolMessage) GetBundles() []*Bundle {
|
||||
if m != nil {
|
||||
return m.Bundles
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ProtocolMessage) GetEncryptedMessage() map[string]*EncryptedMessageProtocol {
|
||||
if m != nil {
|
||||
return m.EncryptedMessage
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ProtocolMessage) GetPublicMessage() []byte {
|
||||
if m != nil {
|
||||
return m.PublicMessage
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*SignedPreKey)(nil), "encryption.SignedPreKey")
|
||||
proto.RegisterType((*Bundle)(nil), "encryption.Bundle")
|
||||
proto.RegisterMapType((map[string]*SignedPreKey)(nil), "encryption.Bundle.SignedPreKeysEntry")
|
||||
proto.RegisterType((*BundleContainer)(nil), "encryption.BundleContainer")
|
||||
proto.RegisterType((*DRHeader)(nil), "encryption.DRHeader")
|
||||
proto.RegisterType((*DHHeader)(nil), "encryption.DHHeader")
|
||||
proto.RegisterType((*X3DHHeader)(nil), "encryption.X3DHHeader")
|
||||
proto.RegisterType((*HRHeader)(nil), "encryption.HRHeader")
|
||||
proto.RegisterType((*RekeyGroup)(nil), "encryption.RekeyGroup")
|
||||
proto.RegisterMapType((map[uint32][]byte)(nil), "encryption.RekeyGroup.KeysEntry")
|
||||
proto.RegisterType((*HRKeys)(nil), "encryption.HRKeys")
|
||||
proto.RegisterType((*HRKey)(nil), "encryption.HRKey")
|
||||
proto.RegisterType((*EncryptedMessageProtocol)(nil), "encryption.EncryptedMessageProtocol")
|
||||
proto.RegisterType((*ProtocolMessage)(nil), "encryption.ProtocolMessage")
|
||||
proto.RegisterMapType((map[string]*EncryptedMessageProtocol)(nil), "encryption.ProtocolMessage.EncryptedMessageEntry")
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterFile("protocol_message.proto", fileDescriptor_4e37b52004a72e16)
|
||||
}
|
||||
|
||||
var fileDescriptor_4e37b52004a72e16 = []byte{
|
||||
// 770 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x55, 0xdb, 0x4e, 0xdc, 0x48,
|
||||
0x10, 0x95, 0xed, 0xb9, 0xd6, 0x5c, 0xe9, 0x05, 0xe4, 0x1d, 0xf1, 0x30, 0xb2, 0x60, 0x77, 0x16,
|
||||
0xad, 0xbc, 0xe2, 0x22, 0xb1, 0x22, 0x6f, 0x04, 0x94, 0x01, 0x44, 0x84, 0x3a, 0x52, 0x14, 0xf1,
|
||||
0x10, 0xcb, 0x8c, 0x0b, 0xb0, 0x18, 0x6c, 0x63, 0x7b, 0x50, 0xfc, 0x03, 0x79, 0xc9, 0x17, 0xe4,
|
||||
0x0b, 0xf2, 0x4f, 0xf9, 0x9a, 0xa8, 0x2f, 0xb6, 0x7b, 0x2e, 0x3c, 0xe4, 0xcd, 0x5d, 0x5d, 0x75,
|
||||
0xea, 0x54, 0xd5, 0xe9, 0x32, 0x6c, 0x46, 0x71, 0x98, 0x86, 0x93, 0x70, 0xea, 0x3c, 0x61, 0x92,
|
||||
0xb8, 0xf7, 0x68, 0x73, 0x03, 0x01, 0x0c, 0x26, 0x71, 0x16, 0xa5, 0x7e, 0x18, 0x58, 0x19, 0xb4,
|
||||
0x3f, 0xf8, 0xf7, 0x01, 0x7a, 0xd7, 0x31, 0x5e, 0x62, 0x46, 0xb6, 0xa1, 0x9b, 0xf0, 0xb3, 0x13,
|
||||
0xc5, 0xe8, 0x3c, 0x62, 0x66, 0x6a, 0x43, 0x6d, 0xd4, 0xa6, 0xed, 0x44, 0xf5, 0x32, 0xa1, 0xfe,
|
||||
0x82, 0x71, 0xe2, 0x87, 0x81, 0xa9, 0x0f, 0xb5, 0x51, 0x87, 0xe6, 0x47, 0xf2, 0x0f, 0xf4, 0x8b,
|
||||
0xac, 0xb9, 0x8b, 0xc1, 0x5d, 0x7a, 0xb9, 0xfd, 0xa3, 0x30, 0x5b, 0xdf, 0x74, 0xa8, 0x9d, 0xcc,
|
||||
0x02, 0x6f, 0x8a, 0x64, 0x00, 0x0d, 0xdf, 0xc3, 0x20, 0xf5, 0xd3, 0x3c, 0x5f, 0x71, 0x26, 0x57,
|
||||
0xd0, 0x9b, 0x67, 0x94, 0x98, 0xfa, 0xd0, 0x18, 0xb5, 0xf6, 0x77, 0xec, 0xb2, 0x0e, 0x5b, 0x00,
|
||||
0xd9, 0x6a, 0x2d, 0xc9, 0x59, 0x90, 0xc6, 0x19, 0xed, 0xa8, 0xcc, 0x13, 0xb2, 0x05, 0x4d, 0x66,
|
||||
0x70, 0xd3, 0x59, 0x8c, 0x66, 0x85, 0xe7, 0x2a, 0x0d, 0xec, 0x36, 0xf5, 0x9f, 0x30, 0x49, 0xdd,
|
||||
0xa7, 0xc8, 0xac, 0x0e, 0xb5, 0x91, 0x41, 0x4b, 0xc3, 0xe0, 0x06, 0xc8, 0x72, 0x02, 0xd2, 0x07,
|
||||
0x23, 0xef, 0x53, 0x93, 0xb2, 0x4f, 0x62, 0x43, 0xf5, 0xc5, 0x9d, 0xce, 0x90, 0x37, 0xa7, 0xb5,
|
||||
0x6f, 0xaa, 0x44, 0x55, 0x00, 0x2a, 0xdc, 0x8e, 0xf5, 0xff, 0x35, 0xeb, 0x0b, 0xf4, 0x44, 0x0d,
|
||||
0x6f, 0xc3, 0x20, 0x75, 0xfd, 0x00, 0x63, 0xb2, 0x0b, 0xb5, 0x5b, 0x6e, 0xe2, 0xd8, 0xad, 0x7d,
|
||||
0xb2, 0x5c, 0x30, 0x95, 0x1e, 0xe4, 0x80, 0x4d, 0xdb, 0x7f, 0x71, 0x53, 0x74, 0x16, 0xe6, 0xa7,
|
||||
0xf3, 0x1a, 0xff, 0x90, 0xb7, 0x6a, 0xfa, 0x8b, 0x4a, 0xc3, 0xe8, 0x57, 0xac, 0x0b, 0x68, 0x9c,
|
||||
0xd2, 0x31, 0xba, 0x1e, 0xc6, 0x6a, 0x2d, 0x6d, 0x51, 0x4b, 0x1b, 0xb4, 0x7c, 0xc8, 0x5a, 0x40,
|
||||
0xba, 0xa0, 0x47, 0xf9, 0x40, 0xf5, 0x88, 0x9f, 0x7d, 0x4f, 0xb6, 0x51, 0xf7, 0x3d, 0x6b, 0x0b,
|
||||
0x1a, 0xa7, 0xe3, 0xd7, 0xb0, 0xac, 0x43, 0x80, 0x4f, 0x07, 0xaf, 0xdf, 0x2f, 0xa2, 0x49, 0x7e,
|
||||
0x3f, 0x34, 0x68, 0x8c, 0x73, 0x82, 0xbb, 0xb0, 0xe6, 0x61, 0x14, 0xe3, 0xc4, 0x4d, 0xd1, 0x63,
|
||||
0xf5, 0x39, 0xbe, 0xc7, 0x21, 0x3a, 0xb4, 0x57, 0x5e, 0x5c, 0x62, 0x76, 0xee, 0x91, 0x0d, 0xa8,
|
||||
0x25, 0xf8, 0xec, 0x04, 0xa1, 0xe4, 0x5f, 0x4d, 0xf0, 0xf9, 0x7d, 0x48, 0xfe, 0x84, 0xc6, 0x7d,
|
||||
0x1c, 0xce, 0x22, 0x16, 0x69, 0xf0, 0x5c, 0x75, 0x7e, 0x16, 0x11, 0x12, 0x52, 0x90, 0xa8, 0x3e,
|
||||
0x72, 0xa0, 0xbf, 0xa0, 0xc2, 0x75, 0x57, 0x5d, 0x1e, 0xc3, 0x98, 0x32, 0x21, 0x50, 0x7e, 0x6f,
|
||||
0x7d, 0xd7, 0x00, 0x28, 0x3e, 0x62, 0xf6, 0x8e, 0xe1, 0xcd, 0x8b, 0x89, 0x51, 0xa8, 0x28, 0x62,
|
||||
0x22, 0x87, 0x12, 0xb4, 0xc2, 0xc5, 0x3c, 0x54, 0x41, 0x4b, 0x0c, 0xbb, 0xd4, 0x31, 0xf7, 0x1e,
|
||||
0x1c, 0x41, 0x73, 0xa5, 0xf2, 0x3a, 0xa2, 0x83, 0xeb, 0xaa, 0xf2, 0xda, 0xaa, 0xbe, 0x1e, 0xa0,
|
||||
0x26, 0xb8, 0x92, 0x1d, 0x99, 0x58, 0xe3, 0x89, 0xd7, 0x96, 0xaa, 0x11, 0x99, 0xc8, 0x11, 0xb4,
|
||||
0x62, 0xc6, 0xc3, 0xe1, 0xcd, 0x91, 0x52, 0xde, 0x5c, 0x4d, 0x93, 0x42, 0x5c, 0x7c, 0x5b, 0x13,
|
||||
0xa8, 0x72, 0x9c, 0xdf, 0x9a, 0x95, 0x2c, 0x45, 0x2f, 0xc5, 0x30, 0xd7, 0x3d, 0x63, 0xa1, 0x7b,
|
||||
0xd6, 0x57, 0x1d, 0xcc, 0x33, 0x41, 0x05, 0xbd, 0x2b, 0xb1, 0xde, 0xae, 0xe5, 0x82, 0x61, 0xd4,
|
||||
0x99, 0xce, 0x9c, 0x07, 0xae, 0x19, 0xf9, 0x7a, 0xe6, 0xa8, 0x97, 0x32, 0xa4, 0xaa, 0x24, 0xf7,
|
||||
0xa0, 0x79, 0x4a, 0xf3, 0x30, 0x51, 0xf1, 0xba, 0x1a, 0x96, 0xbf, 0x13, 0x5a, 0xbe, 0x18, 0x16,
|
||||
0x52, 0x64, 0xc2, 0x15, 0x21, 0xe3, 0x22, 0x44, 0xc9, 0x32, 0x2e, 0xb2, 0xdc, 0x2d, 0x87, 0x8c,
|
||||
0x8b, 0x2c, 0x85, 0xec, 0x4d, 0xa8, 0x47, 0x6e, 0x36, 0x0d, 0xdd, 0x42, 0xb2, 0xf2, 0x68, 0xfd,
|
||||
0xd4, 0xa1, 0x97, 0x17, 0x2e, 0xfb, 0x40, 0xfe, 0x86, 0x9e, 0x1f, 0x24, 0xa9, 0x3b, 0x9d, 0xba,
|
||||
0x0c, 0x90, 0xb5, 0x5d, 0xe7, 0xdb, 0xa9, 0xab, 0x9a, 0xcf, 0x3d, 0xf2, 0x2f, 0xd4, 0xc5, 0xfe,
|
||||
0x48, 0x4c, 0x83, 0xab, 0x61, 0xd5, 0x8a, 0xc9, 0x5d, 0xc8, 0x67, 0x58, 0xc3, 0xbc, 0xe5, 0xf9,
|
||||
0x2f, 0xc5, 0x44, 0x1e, 0xb7, 0xa7, 0xc6, 0x2d, 0xd0, 0xb1, 0x17, 0xe7, 0x24, 0xf4, 0xdc, 0xc7,
|
||||
0x05, 0x33, 0xd9, 0x81, 0x6e, 0x34, 0xbb, 0x9d, 0xfa, 0x93, 0x02, 0xfc, 0x8e, 0xd7, 0xda, 0x11,
|
||||
0x56, 0xe9, 0x36, 0xf0, 0x61, 0x63, 0x25, 0xe2, 0x8a, 0x45, 0x7c, 0x3c, 0xbf, 0x88, 0xb7, 0x55,
|
||||
0x96, 0xaf, 0xa9, 0x47, 0x79, 0x34, 0x27, 0xbd, 0x9b, 0x8e, 0xfd, 0xdf, 0x9b, 0x32, 0xe8, 0xb6,
|
||||
0xc6, 0x7f, 0x62, 0x07, 0xbf, 0x02, 0x00, 0x00, 0xff, 0xff, 0x5d, 0x20, 0xbb, 0xc5, 0x5b, 0x07,
|
||||
0x00, 0x00,
|
||||
}
|
||||
111
vendor/github.com/status-im/status-go/protocol/encryption/protocol_message.proto
generated
vendored
Normal file
111
vendor/github.com/status-im/status-go/protocol/encryption/protocol_message.proto
generated
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option go_package = "./;encryption";
|
||||
package encryption;
|
||||
|
||||
message SignedPreKey {
|
||||
bytes signed_pre_key = 1;
|
||||
uint32 version = 2;
|
||||
uint32 protocol_version = 3;
|
||||
}
|
||||
|
||||
// X3DH prekey bundle
|
||||
message Bundle {
|
||||
// Identity key
|
||||
bytes identity = 1;
|
||||
// Installation id
|
||||
map<string,SignedPreKey> signed_pre_keys = 2;
|
||||
// Prekey signature
|
||||
bytes signature = 4;
|
||||
|
||||
// When the bundle was created locally
|
||||
int64 timestamp = 5;
|
||||
}
|
||||
|
||||
message BundleContainer {
|
||||
reserved 3;
|
||||
// X3DH prekey bundle
|
||||
Bundle bundle = 1;
|
||||
// Private signed prekey
|
||||
bytes private_signed_pre_key = 2;
|
||||
}
|
||||
|
||||
message DRHeader {
|
||||
// Current ratchet public key
|
||||
bytes key = 1;
|
||||
// Number of the message in the sending chain
|
||||
uint32 n = 2;
|
||||
// Length of the previous sending chain
|
||||
uint32 pn = 3;
|
||||
// Bundle ID
|
||||
bytes id = 4;
|
||||
}
|
||||
|
||||
message DHHeader {
|
||||
// Compressed ephemeral public key
|
||||
bytes key = 1;
|
||||
}
|
||||
|
||||
message X3DHHeader {
|
||||
reserved 3;
|
||||
// Ephemeral key used
|
||||
bytes key = 1;
|
||||
// Used bundle's signed prekey
|
||||
bytes id = 4;
|
||||
}
|
||||
|
||||
// Hash Ratchet Header
|
||||
message HRHeader {
|
||||
// deprecated group key ID
|
||||
uint32 deprecated_key_id = 1;
|
||||
// group message number for this key_id
|
||||
uint32 seq_no = 2;
|
||||
// group ID
|
||||
bytes group_id = 3;
|
||||
// group key ID
|
||||
bytes key_id = 4;
|
||||
HRKeys keys = 5;
|
||||
}
|
||||
|
||||
message RekeyGroup {
|
||||
uint64 timestamp = 2;
|
||||
|
||||
map<uint32, bytes> keys = 4;
|
||||
}
|
||||
|
||||
message HRKeys {
|
||||
repeated HRKey keys = 1;
|
||||
RekeyGroup rekey_group = 2;
|
||||
}
|
||||
|
||||
message HRKey {
|
||||
uint32 deprecated_key_id = 1;
|
||||
bytes key = 2;
|
||||
uint64 timestamp = 3;
|
||||
}
|
||||
|
||||
// Direct message value
|
||||
message EncryptedMessageProtocol {
|
||||
X3DHHeader X3DH_header = 1;
|
||||
DRHeader DR_header = 2;
|
||||
DHHeader DH_header = 101;
|
||||
HRHeader HR_header = 102;
|
||||
// Encrypted payload
|
||||
bytes payload = 3;
|
||||
}
|
||||
|
||||
// Top-level protocol message
|
||||
message ProtocolMessage {
|
||||
// The device id of the sender
|
||||
string installation_id = 2;
|
||||
|
||||
// List of bundles
|
||||
repeated Bundle bundles = 3;
|
||||
|
||||
// One to one message, encrypted, indexed by installation_id
|
||||
// TODO map here is redundant in case of community messages
|
||||
map<string,EncryptedMessageProtocol> encrypted_message = 101;
|
||||
|
||||
// Public chats, not encrypted
|
||||
bytes public_message = 102;
|
||||
}
|
||||
6
vendor/github.com/status-im/status-go/protocol/encryption/publisher/doc.go
generated
vendored
Normal file
6
vendor/github.com/status-im/status-go/protocol/encryption/publisher/doc.go
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
// Publisher periodically publishes an info about itself on a known channel.
|
||||
// This channel is a particular topic calculated from the public key.
|
||||
// It is required for other peers to start a secure conversation immediately
|
||||
// using distibuted data through the channel.
|
||||
|
||||
package publisher
|
||||
38
vendor/github.com/status-im/status-go/protocol/encryption/publisher/persistence.go
generated
vendored
Normal file
38
vendor/github.com/status-im/status-go/protocol/encryption/publisher/persistence.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
package publisher
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type persistence struct {
|
||||
lastAcksMutex sync.Mutex
|
||||
lastPublished int64
|
||||
lastAcks map[string]int64
|
||||
}
|
||||
|
||||
func newPersistence() *persistence {
|
||||
return &persistence{
|
||||
lastAcks: make(map[string]int64),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *persistence) getLastPublished() int64 {
|
||||
return s.lastPublished
|
||||
}
|
||||
|
||||
func (s *persistence) setLastPublished(lastPublished int64) {
|
||||
s.lastPublished = lastPublished
|
||||
}
|
||||
|
||||
func (s *persistence) lastAck(identity []byte) int64 {
|
||||
s.lastAcksMutex.Lock()
|
||||
defer s.lastAcksMutex.Unlock()
|
||||
return s.lastAcks[hex.EncodeToString(identity)]
|
||||
}
|
||||
|
||||
func (s *persistence) setLastAck(identity []byte, lastAck int64) {
|
||||
s.lastAcksMutex.Lock()
|
||||
defer s.lastAcksMutex.Unlock()
|
||||
s.lastAcks[hex.EncodeToString(identity)] = lastAck
|
||||
}
|
||||
128
vendor/github.com/status-im/status-go/protocol/encryption/publisher/publisher.go
generated
vendored
Normal file
128
vendor/github.com/status-im/status-go/protocol/encryption/publisher/publisher.go
generated
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
package publisher
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/logutils"
|
||||
)
|
||||
|
||||
const (
|
||||
// How often a ticker fires in seconds.
|
||||
tickerInterval = 120
|
||||
// How often we should publish a contact code in seconds.
|
||||
publishInterval = 21600
|
||||
// Cooldown period on acking messages when not targeting our device.
|
||||
deviceNotFoundAckInterval = 7200
|
||||
)
|
||||
|
||||
var (
|
||||
errNotEnoughTimePassed = errors.New("not enough time passed")
|
||||
)
|
||||
|
||||
type Publisher struct {
|
||||
persistence *persistence
|
||||
logger *zap.Logger
|
||||
notifyCh chan struct{}
|
||||
quit chan struct{}
|
||||
}
|
||||
|
||||
func New(logger *zap.Logger) *Publisher {
|
||||
if logger == nil {
|
||||
logger = logutils.ZapLogger()
|
||||
}
|
||||
|
||||
return &Publisher{
|
||||
persistence: newPersistence(),
|
||||
logger: logger.With(zap.Namespace("Publisher")),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Publisher) Start() <-chan struct{} {
|
||||
logger := p.logger.With(zap.String("site", "Start"))
|
||||
|
||||
logger.Info("starting publisher")
|
||||
|
||||
p.notifyCh = make(chan struct{}, 100)
|
||||
p.quit = make(chan struct{})
|
||||
|
||||
go p.tickerLoop()
|
||||
|
||||
return p.notifyCh
|
||||
}
|
||||
|
||||
func (p *Publisher) Stop() {
|
||||
// If hasn't started, ignore
|
||||
if p.quit == nil {
|
||||
return
|
||||
}
|
||||
select {
|
||||
case _, ok := <-p.quit:
|
||||
if !ok {
|
||||
// channel already closed
|
||||
return
|
||||
}
|
||||
default:
|
||||
close(p.quit)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Publisher) tickerLoop() {
|
||||
ticker := time.NewTicker(tickerInterval * time.Second)
|
||||
|
||||
go func() {
|
||||
logger := p.logger.With(zap.String("site", "tickerLoop"))
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
err := p.notify()
|
||||
switch err {
|
||||
case errNotEnoughTimePassed:
|
||||
logger.Debug("not enough time passed")
|
||||
case nil:
|
||||
// skip
|
||||
default:
|
||||
logger.Error("error while sending a contact code", zap.Error(err))
|
||||
}
|
||||
case <-p.quit:
|
||||
ticker.Stop()
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (p *Publisher) notify() error {
|
||||
lastPublished := p.persistence.getLastPublished()
|
||||
|
||||
now := time.Now().Unix()
|
||||
|
||||
if now-lastPublished < publishInterval {
|
||||
return errNotEnoughTimePassed
|
||||
}
|
||||
|
||||
select {
|
||||
case p.notifyCh <- struct{}{}:
|
||||
default:
|
||||
p.logger.Warn("publisher channel full, dropping message")
|
||||
}
|
||||
|
||||
p.persistence.setLastPublished(now)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Publisher) ShouldAdvertiseBundle(publicKey *ecdsa.PublicKey, now int64) (bool, error) {
|
||||
identity := crypto.CompressPubkey(publicKey)
|
||||
lastAcked := p.persistence.lastAck(identity)
|
||||
return now-lastAcked < deviceNotFoundAckInterval, nil
|
||||
}
|
||||
|
||||
func (p *Publisher) SetLastAck(publicKey *ecdsa.PublicKey, now int64) {
|
||||
identity := crypto.CompressPubkey(publicKey)
|
||||
p.persistence.setLastAck(identity, now)
|
||||
}
|
||||
120
vendor/github.com/status-im/status-go/protocol/encryption/sharedsecret/persistence.go
generated
vendored
Normal file
120
vendor/github.com/status-im/status-go/protocol/encryption/sharedsecret/persistence.go
generated
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
package sharedsecret
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Response struct {
|
||||
secret []byte
|
||||
installationIDs map[string]bool
|
||||
}
|
||||
|
||||
type sqlitePersistence struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func newSQLitePersistence(db *sql.DB) *sqlitePersistence {
|
||||
return &sqlitePersistence{db: db}
|
||||
}
|
||||
|
||||
func (s *sqlitePersistence) Add(identity []byte, secret []byte, installationID string) error {
|
||||
tx, err := s.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
insertSecretStmt, err := tx.Prepare("INSERT INTO secrets(identity, secret) VALUES (?, ?)")
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
defer insertSecretStmt.Close()
|
||||
|
||||
_, err = insertSecretStmt.Exec(identity, secret)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
insertInstallationIDStmt, err := tx.Prepare("INSERT INTO secret_installation_ids(id, identity_id) VALUES (?, ?)")
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
defer insertInstallationIDStmt.Close()
|
||||
|
||||
_, err = insertInstallationIDStmt.Exec(installationID, identity)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func (s *sqlitePersistence) Get(identity []byte, installationIDs []string) (*Response, error) {
|
||||
response := &Response{
|
||||
installationIDs: make(map[string]bool),
|
||||
}
|
||||
args := make([]interface{}, len(installationIDs)+1)
|
||||
args[0] = identity
|
||||
for i, installationID := range installationIDs {
|
||||
args[i+1] = installationID
|
||||
}
|
||||
|
||||
/* #nosec */
|
||||
query := `SELECT secret, id
|
||||
FROM secrets t
|
||||
JOIN
|
||||
secret_installation_ids tid
|
||||
ON t.identity = tid.identity_id
|
||||
WHERE
|
||||
t.identity = ?
|
||||
AND
|
||||
tid.id IN (?` + strings.Repeat(",?", len(installationIDs)-1) + `)`
|
||||
|
||||
rows, err := s.db.Query(query, args...)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var installationID string
|
||||
var secret []byte
|
||||
err = rows.Scan(&secret, &installationID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response.secret = secret
|
||||
response.installationIDs[installationID] = true
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (s *sqlitePersistence) All() ([][][]byte, error) {
|
||||
query := "SELECT identity, secret FROM secrets"
|
||||
|
||||
var secrets [][][]byte
|
||||
|
||||
rows, err := s.db.Query(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var secret []byte
|
||||
var identity []byte
|
||||
err = rows.Scan(&identity, &secret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
secrets = append(secrets, [][]byte{identity, secret})
|
||||
}
|
||||
|
||||
return secrets, nil
|
||||
}
|
||||
111
vendor/github.com/status-im/status-go/protocol/encryption/sharedsecret/sharedsecret.go
generated
vendored
Normal file
111
vendor/github.com/status-im/status-go/protocol/encryption/sharedsecret/sharedsecret.go
generated
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
package sharedsecret
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"database/sql"
|
||||
"errors"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/eth-node/crypto/ecies"
|
||||
)
|
||||
|
||||
const sskLen = 16
|
||||
|
||||
type Secret struct {
|
||||
Identity *ecdsa.PublicKey
|
||||
Key []byte
|
||||
}
|
||||
|
||||
// SharedSecret generates and manages negotiated secrets.
|
||||
// Identities (public keys) stored by SharedSecret
|
||||
// are compressed.
|
||||
// TODO: make compression of public keys a responsibility of sqlitePersistence instead of SharedSecret.
|
||||
type SharedSecret struct {
|
||||
persistence *sqlitePersistence
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func New(db *sql.DB, logger *zap.Logger) *SharedSecret {
|
||||
if logger == nil {
|
||||
logger = zap.NewNop()
|
||||
}
|
||||
|
||||
return &SharedSecret{
|
||||
persistence: newSQLitePersistence(db),
|
||||
logger: logger.With(zap.Namespace("SharedSecret")),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SharedSecret) generate(myPrivateKey *ecdsa.PrivateKey, theirPublicKey *ecdsa.PublicKey, installationID string) (*Secret, error) {
|
||||
sharedKey, err := ecies.ImportECDSA(myPrivateKey).GenerateShared(
|
||||
ecies.ImportECDSAPublic(theirPublicKey),
|
||||
sskLen,
|
||||
sskLen,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
theirIdentity := crypto.CompressPubkey(theirPublicKey)
|
||||
if err = s.persistence.Add(theirIdentity, sharedKey, installationID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Secret{Key: sharedKey, Identity: theirPublicKey}, err
|
||||
}
|
||||
|
||||
// Generate will generate a shared secret for a given identity, and return it.
|
||||
func (s *SharedSecret) Generate(myPrivateKey *ecdsa.PrivateKey, theirPublicKey *ecdsa.PublicKey, installationID string) (*Secret, error) {
|
||||
return s.generate(myPrivateKey, theirPublicKey, installationID)
|
||||
}
|
||||
|
||||
// Agreed returns true if a secret has been acknowledged by all the installationIDs.
|
||||
func (s *SharedSecret) Agreed(myPrivateKey *ecdsa.PrivateKey, myInstallationID string, theirPublicKey *ecdsa.PublicKey, theirInstallationIDs []string) (*Secret, bool, error) {
|
||||
secret, err := s.generate(myPrivateKey, theirPublicKey, myInstallationID)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
if len(theirInstallationIDs) == 0 {
|
||||
return secret, false, nil
|
||||
}
|
||||
|
||||
theirIdentity := crypto.CompressPubkey(theirPublicKey)
|
||||
response, err := s.persistence.Get(theirIdentity, theirInstallationIDs)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
for _, installationID := range theirInstallationIDs {
|
||||
if !response.installationIDs[installationID] {
|
||||
return secret, false, nil
|
||||
}
|
||||
}
|
||||
|
||||
if !bytes.Equal(secret.Key, response.secret) {
|
||||
return nil, false, errors.New("computed and saved secrets are different for a given identity")
|
||||
}
|
||||
|
||||
return secret, true, nil
|
||||
}
|
||||
|
||||
func (s *SharedSecret) All() ([]*Secret, error) {
|
||||
var secrets []*Secret
|
||||
tuples, err := s.persistence.All()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, tuple := range tuples {
|
||||
key, err := crypto.DecompressPubkey(tuple[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
secrets = append(secrets, &Secret{Identity: key, Key: tuple[1]})
|
||||
}
|
||||
|
||||
return secrets, nil
|
||||
}
|
||||
249
vendor/github.com/status-im/status-go/protocol/encryption/x3dh.go
generated
vendored
Normal file
249
vendor/github.com/status-im/status-go/protocol/encryption/x3dh.go
generated
vendored
Normal file
@@ -0,0 +1,249 @@
|
||||
package encryption
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"errors"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/eth-node/crypto/ecies"
|
||||
)
|
||||
|
||||
const (
|
||||
// Shared secret key length
|
||||
sskLen = 16
|
||||
)
|
||||
|
||||
func buildSignatureMaterial(bundle *Bundle) []byte {
|
||||
signedPreKeys := bundle.GetSignedPreKeys()
|
||||
timestamp := bundle.GetTimestamp()
|
||||
var keys []string
|
||||
|
||||
for k := range signedPreKeys {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
var signatureMaterial []byte
|
||||
|
||||
sort.Strings(keys)
|
||||
|
||||
for _, installationID := range keys {
|
||||
signedPreKey := signedPreKeys[installationID]
|
||||
signatureMaterial = append(signatureMaterial, []byte(installationID)...)
|
||||
signatureMaterial = append(signatureMaterial, signedPreKey.SignedPreKey...)
|
||||
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
|
||||
|
||||
}
|
||||
|
||||
// SignBundle signs the bundle and refreshes the timestamps
|
||||
func SignBundle(identity *ecdsa.PrivateKey, bundleContainer *BundleContainer) error {
|
||||
bundleContainer.Bundle.Timestamp = time.Now().UnixNano()
|
||||
signatureMaterial := buildSignatureMaterial(bundleContainer.GetBundle())
|
||||
|
||||
signature, err := crypto.Sign(crypto.Keccak256(signatureMaterial), identity)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bundleContainer.Bundle.Signature = signature
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewBundleContainer creates a new BundleContainer from an identity private key
|
||||
func NewBundleContainer(identity *ecdsa.PrivateKey, installationID string) (*BundleContainer, error) {
|
||||
preKey, err := crypto.GenerateKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
compressedPreKey := crypto.CompressPubkey(&preKey.PublicKey)
|
||||
compressedIdentityKey := crypto.CompressPubkey(&identity.PublicKey)
|
||||
|
||||
encodedPreKey := crypto.FromECDSA(preKey)
|
||||
signedPreKeys := make(map[string]*SignedPreKey)
|
||||
signedPreKeys[installationID] = &SignedPreKey{
|
||||
ProtocolVersion: protocolVersion,
|
||||
SignedPreKey: compressedPreKey,
|
||||
}
|
||||
|
||||
bundle := Bundle{
|
||||
Timestamp: time.Now().UnixNano(),
|
||||
Identity: compressedIdentityKey,
|
||||
SignedPreKeys: signedPreKeys,
|
||||
}
|
||||
|
||||
return &BundleContainer{
|
||||
Bundle: &bundle,
|
||||
PrivateSignedPreKey: encodedPreKey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// VerifyBundle checks that a bundle is valid
|
||||
func VerifyBundle(bundle *Bundle) error {
|
||||
_, err := ExtractIdentity(bundle)
|
||||
return err
|
||||
}
|
||||
|
||||
// ExtractIdentity extracts the identity key from a given bundle
|
||||
func ExtractIdentity(bundle *Bundle) (*ecdsa.PublicKey, error) {
|
||||
bundleIdentityKey, err := crypto.DecompressPubkey(bundle.GetIdentity())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
signatureMaterial := buildSignatureMaterial(bundle)
|
||||
|
||||
recoveredKey, err := crypto.SigToPub(
|
||||
crypto.Keccak256(signatureMaterial),
|
||||
bundle.GetSignature(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if crypto.PubkeyToAddress(*recoveredKey) != crypto.PubkeyToAddress(*bundleIdentityKey) {
|
||||
return nil, errors.New("identity key and signature mismatch")
|
||||
}
|
||||
|
||||
return recoveredKey, nil
|
||||
}
|
||||
|
||||
// PerformDH generates a shared key given a private and a public key
|
||||
func PerformDH(privateKey *ecies.PrivateKey, publicKey *ecies.PublicKey) ([]byte, error) {
|
||||
return privateKey.GenerateShared(
|
||||
publicKey,
|
||||
sskLen,
|
||||
sskLen,
|
||||
)
|
||||
}
|
||||
|
||||
func getSharedSecret(dh1 []byte, dh2 []byte, dh3 []byte) []byte {
|
||||
secretInput := append(append(dh1, dh2...), dh3...)
|
||||
|
||||
return crypto.Keccak256(secretInput)
|
||||
}
|
||||
|
||||
// x3dhActive handles initiating an X3DH session
|
||||
func x3dhActive(
|
||||
myIdentityKey *ecies.PrivateKey,
|
||||
theirSignedPreKey *ecies.PublicKey,
|
||||
myEphemeralKey *ecies.PrivateKey,
|
||||
theirIdentityKey *ecies.PublicKey,
|
||||
) ([]byte, error) {
|
||||
var dh1, dh2, dh3 []byte
|
||||
var err error
|
||||
|
||||
if dh1, err = PerformDH(myIdentityKey, theirSignedPreKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if dh2, err = PerformDH(myEphemeralKey, theirIdentityKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if dh3, err = PerformDH(myEphemeralKey, theirSignedPreKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return getSharedSecret(dh1, dh2, dh3), nil
|
||||
}
|
||||
|
||||
// x3dhPassive handles the response to an initiated X3DH session
|
||||
func x3dhPassive(
|
||||
theirIdentityKey *ecies.PublicKey,
|
||||
mySignedPreKey *ecies.PrivateKey,
|
||||
theirEphemeralKey *ecies.PublicKey,
|
||||
myIdentityKey *ecies.PrivateKey,
|
||||
) ([]byte, error) {
|
||||
var dh1, dh2, dh3 []byte
|
||||
var err error
|
||||
|
||||
if dh1, err = PerformDH(mySignedPreKey, theirIdentityKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if dh2, err = PerformDH(myIdentityKey, theirEphemeralKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if dh3, err = PerformDH(mySignedPreKey, theirEphemeralKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return getSharedSecret(dh1, dh2, dh3), nil
|
||||
}
|
||||
|
||||
// PerformActiveDH performs a Diffie-Hellman exchange using a public key and a generated ephemeral key.
|
||||
// Returns the key resulting from the DH exchange as well as the ephemeral public key.
|
||||
func PerformActiveDH(publicKey *ecdsa.PublicKey) ([]byte, *ecdsa.PublicKey, error) {
|
||||
ephemeralKey, err := crypto.GenerateKey()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
key, err := PerformDH(
|
||||
ecies.ImportECDSA(ephemeralKey),
|
||||
ecies.ImportECDSAPublic(publicKey),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return key, &ephemeralKey.PublicKey, err
|
||||
}
|
||||
|
||||
// PerformActiveX3DH takes someone else's bundle and calculates shared secret.
|
||||
// Returns the shared secret and the ephemeral key used.
|
||||
func PerformActiveX3DH(identity []byte, signedPreKey []byte, prv *ecdsa.PrivateKey) ([]byte, *ecdsa.PublicKey, error) {
|
||||
bundleIdentityKey, err := crypto.DecompressPubkey(identity)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
bundleSignedPreKey, err := crypto.DecompressPubkey(signedPreKey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
ephemeralKey, err := crypto.GenerateKey()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
sharedSecret, err := x3dhActive(
|
||||
ecies.ImportECDSA(prv),
|
||||
ecies.ImportECDSAPublic(bundleSignedPreKey),
|
||||
ecies.ImportECDSA(ephemeralKey),
|
||||
ecies.ImportECDSAPublic(bundleIdentityKey),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return sharedSecret, &ephemeralKey.PublicKey, nil
|
||||
}
|
||||
|
||||
// PerformPassiveX3DH handles the part of the protocol where
|
||||
// our interlocutor used our bundle, with ID of the signedPreKey,
|
||||
// we loaded our identity key and the correct signedPreKey and we perform X3DH
|
||||
func PerformPassiveX3DH(theirIdentityKey *ecdsa.PublicKey, mySignedPreKey *ecdsa.PrivateKey, theirEphemeralKey *ecdsa.PublicKey, myPrivateKey *ecdsa.PrivateKey) ([]byte, error) {
|
||||
sharedSecret, err := x3dhPassive(
|
||||
ecies.ImportECDSAPublic(theirIdentityKey),
|
||||
ecies.ImportECDSA(mySignedPreKey),
|
||||
ecies.ImportECDSAPublic(theirEphemeralKey),
|
||||
ecies.ImportECDSA(myPrivateKey),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sharedSecret, nil
|
||||
}
|
||||
Reference in New Issue
Block a user