github.com/status-im/status-go@v1.1.0/protocol/encryption/encryptor.go (about)

     1  package encryption
     2  
     3  import (
     4  	"crypto/ecdsa"
     5  	"database/sql"
     6  	"encoding/hex"
     7  	"errors"
     8  	"sync"
     9  	"time"
    10  
    11  	dr "github.com/status-im/doubleratchet"
    12  	"go.uber.org/zap"
    13  
    14  	"github.com/status-im/status-go/eth-node/crypto"
    15  	"github.com/status-im/status-go/eth-node/crypto/ecies"
    16  	"github.com/status-im/status-go/eth-node/types"
    17  
    18  	"github.com/status-im/status-go/protocol/encryption/multidevice"
    19  )
    20  
    21  var (
    22  	errSessionNotFound = errors.New("session not found")
    23  	ErrDeviceNotFound  = errors.New("device not found")
    24  	// ErrNotPairedDevice means that we received a message signed with our public key
    25  	// but from a device that has not been paired.
    26  	// This should not happen because the protocol forbids sending a message to
    27  	// non-paired devices, however, in theory it is possible to receive such a message.
    28  	ErrNotPairedDevice            = errors.New("received a message from not paired device")
    29  	ErrHashRatchetSeqNoTooHigh    = errors.New("hash ratchet seq no is too high")
    30  	ErrHashRatchetGroupIDNotFound = errors.New("hash ratchet group id not found")
    31  	ErrNoEncryptionKey            = errors.New("no encryption key found for the community")
    32  )
    33  
    34  // If we have no bundles, we use a constant so that the message can reach any device.
    35  const (
    36  	noInstallationID         = "none"
    37  	maxHashRatchetSeqNoDelta = 100000
    38  )
    39  
    40  type confirmationData struct {
    41  	header *dr.MessageHeader
    42  	drInfo *RatchetInfo
    43  }
    44  
    45  // encryptor defines a service that is responsible for the encryption aspect of the protocol.
    46  type encryptor struct {
    47  	persistence *sqlitePersistence
    48  	config      encryptorConfig
    49  	messageIDs  map[string]*confirmationData
    50  	mutex       sync.Mutex
    51  	logger      *zap.Logger
    52  }
    53  
    54  type encryptorConfig struct {
    55  	InstallationID string
    56  	// Max number of installations we keep synchronized.
    57  	MaxInstallations int
    58  	// How many consecutive messages can be skipped in the receiving chain.
    59  	MaxSkip int
    60  	// Any message with seqNo <= currentSeq - maxKeep will be deleted.
    61  	MaxKeep int
    62  	// How many keys do we store in total per session.
    63  	MaxMessageKeysPerSession int
    64  	// How long before we refresh the interval in milliseconds
    65  	BundleRefreshInterval int64
    66  	// The logging object
    67  	Logger *zap.Logger
    68  }
    69  
    70  // defaultEncryptorConfig returns the default values used by the encryption service
    71  func defaultEncryptorConfig(installationID string, logger *zap.Logger) encryptorConfig {
    72  	if logger == nil {
    73  		logger = zap.NewNop()
    74  	}
    75  
    76  	return encryptorConfig{
    77  		MaxInstallations:         3,
    78  		MaxSkip:                  1000,
    79  		MaxKeep:                  3000,
    80  		MaxMessageKeysPerSession: 2000,
    81  		BundleRefreshInterval:    24 * 60 * 60 * 1000,
    82  		InstallationID:           installationID,
    83  		Logger:                   logger,
    84  	}
    85  }
    86  
    87  // newEncryptor creates a new EncryptionService instance.
    88  func newEncryptor(db *sql.DB, config encryptorConfig) *encryptor {
    89  	return &encryptor{
    90  		persistence: newSQLitePersistence(db),
    91  		config:      config,
    92  		messageIDs:  make(map[string]*confirmationData),
    93  		logger:      config.Logger.With(zap.Namespace("encryptor")),
    94  	}
    95  }
    96  
    97  func (s *encryptor) keyFromActiveX3DH(theirIdentityKey []byte, theirSignedPreKey []byte, myIdentityKey *ecdsa.PrivateKey) ([]byte, *ecdsa.PublicKey, error) {
    98  	sharedKey, ephemeralPubKey, err := PerformActiveX3DH(theirIdentityKey, theirSignedPreKey, myIdentityKey)
    99  	if err != nil {
   100  		return nil, nil, err
   101  	}
   102  
   103  	return sharedKey, ephemeralPubKey, nil
   104  }
   105  
   106  func (s *encryptor) getDRSession(id []byte) (dr.Session, error) {
   107  	sessionStorage := s.persistence.SessionStorage()
   108  	return dr.Load(
   109  		id,
   110  		sessionStorage,
   111  		dr.WithKeysStorage(s.persistence.KeysStorage()),
   112  		dr.WithMaxSkip(s.config.MaxSkip),
   113  		dr.WithMaxKeep(s.config.MaxKeep),
   114  		dr.WithMaxMessageKeysPerSession(s.config.MaxMessageKeysPerSession),
   115  		dr.WithCrypto(crypto.EthereumCrypto{}),
   116  	)
   117  }
   118  
   119  func confirmationIDString(id []byte) string {
   120  	return hex.EncodeToString(id)
   121  }
   122  
   123  // ConfirmMessagesProcessed confirms and deletes message keys for the given messages
   124  func (s *encryptor) ConfirmMessageProcessed(messageID []byte) error {
   125  	s.mutex.Lock()
   126  	defer s.mutex.Unlock()
   127  
   128  	id := confirmationIDString(messageID)
   129  	confirmationData, ok := s.messageIDs[id]
   130  	if !ok {
   131  		s.logger.Debug("could not confirm message or message already confirmed", zap.String("messageID", id))
   132  		// We are ok with this, means no key material is stored (public message, or already confirmed)
   133  		return nil
   134  	}
   135  
   136  	// Load session from store first
   137  	session, err := s.getDRSession(confirmationData.drInfo.ID)
   138  	if err != nil {
   139  		return err
   140  	}
   141  
   142  	if err := session.DeleteMk(confirmationData.header.DH, confirmationData.header.N); err != nil {
   143  		return err
   144  	}
   145  
   146  	// Clean up
   147  	delete(s.messageIDs, id)
   148  
   149  	return nil
   150  }
   151  
   152  // CreateBundle retrieves or creates an X3DH bundle given a private key
   153  func (s *encryptor) CreateBundle(privateKey *ecdsa.PrivateKey, installations []*multidevice.Installation) (*Bundle, error) {
   154  	ourIdentityKeyC := crypto.CompressPubkey(&privateKey.PublicKey)
   155  
   156  	bundleContainer, err := s.persistence.GetAnyPrivateBundle(ourIdentityKeyC, installations)
   157  	if err != nil {
   158  		return nil, err
   159  	}
   160  
   161  	expired := bundleContainer != nil && bundleContainer.GetBundle().Timestamp < time.Now().Add(-1*time.Duration(s.config.BundleRefreshInterval)*time.Millisecond).UnixNano()
   162  
   163  	// If the bundle has expired we create a new one
   164  	if expired {
   165  		// Mark sessions has expired
   166  		if err := s.persistence.MarkBundleExpired(bundleContainer.GetBundle().GetIdentity()); err != nil {
   167  			return nil, err
   168  		}
   169  
   170  	} else if bundleContainer != nil {
   171  		err = SignBundle(privateKey, bundleContainer)
   172  		if err != nil {
   173  			return nil, err
   174  		}
   175  		return bundleContainer.GetBundle(), nil
   176  	}
   177  
   178  	// needs transaction/mutex to avoid creating multiple bundles
   179  	// although not a problem
   180  	bundleContainer, err = NewBundleContainer(privateKey, s.config.InstallationID)
   181  	if err != nil {
   182  		return nil, err
   183  	}
   184  
   185  	if err = s.persistence.AddPrivateBundle(bundleContainer); err != nil {
   186  		return nil, err
   187  	}
   188  
   189  	return s.CreateBundle(privateKey, installations)
   190  }
   191  
   192  // DecryptWithDH decrypts message sent with a DH key exchange, and throws away the key after decryption
   193  func (s *encryptor) DecryptWithDH(myIdentityKey *ecdsa.PrivateKey, theirEphemeralKey *ecdsa.PublicKey, payload []byte) ([]byte, error) {
   194  	key, err := PerformDH(
   195  		ecies.ImportECDSA(myIdentityKey),
   196  		ecies.ImportECDSAPublic(theirEphemeralKey),
   197  	)
   198  	if err != nil {
   199  		return nil, err
   200  	}
   201  
   202  	return crypto.DecryptSymmetric(key, payload)
   203  
   204  }
   205  
   206  // keyFromPassiveX3DH decrypts message sent with a X3DH key exchange, storing the key for future exchanges
   207  func (s *encryptor) keyFromPassiveX3DH(myIdentityKey *ecdsa.PrivateKey, theirIdentityKey *ecdsa.PublicKey, theirEphemeralKey *ecdsa.PublicKey, ourBundleID []byte) ([]byte, error) {
   208  	bundlePrivateKey, err := s.persistence.GetPrivateKeyBundle(ourBundleID)
   209  	if err != nil {
   210  		s.logger.Error("could not get private bundle", zap.Error(err))
   211  		return nil, err
   212  	}
   213  
   214  	if bundlePrivateKey == nil {
   215  		return nil, errSessionNotFound
   216  	}
   217  
   218  	signedPreKey, err := crypto.ToECDSA(bundlePrivateKey)
   219  	if err != nil {
   220  		s.logger.Error("could not convert to ecdsa", zap.Error(err))
   221  		return nil, err
   222  	}
   223  
   224  	key, err := PerformPassiveX3DH(
   225  		theirIdentityKey,
   226  		signedPreKey,
   227  		theirEphemeralKey,
   228  		myIdentityKey,
   229  	)
   230  	if err != nil {
   231  		s.logger.Error("could not perform passive x3dh", zap.Error(err))
   232  		return nil, err
   233  	}
   234  	return key, nil
   235  }
   236  
   237  // ProcessPublicBundle persists a bundle
   238  func (s *encryptor) ProcessPublicBundle(myIdentityKey *ecdsa.PrivateKey, b *Bundle) error {
   239  	return s.persistence.AddPublicBundle(b)
   240  }
   241  
   242  func (s *encryptor) GetMessage(msgs map[string]*EncryptedMessageProtocol) *EncryptedMessageProtocol {
   243  	msg := msgs[s.config.InstallationID]
   244  	if msg == nil {
   245  		msg = msgs[noInstallationID]
   246  	}
   247  	return msg
   248  }
   249  
   250  // DecryptPayload decrypts the payload of a EncryptedMessageProtocol, given an identity private key and the sender's public key
   251  func (s *encryptor) DecryptPayload(myIdentityKey *ecdsa.PrivateKey, theirIdentityKey *ecdsa.PublicKey, theirInstallationID string, msgs map[string]*EncryptedMessageProtocol, messageID []byte) ([]byte, error) {
   252  	s.mutex.Lock()
   253  	defer s.mutex.Unlock()
   254  
   255  	msg := s.GetMessage(msgs)
   256  
   257  	// We should not be sending a signal if it's coming from us, as we receive our own messages
   258  	if msg == nil && !samePublicKeys(*theirIdentityKey, myIdentityKey.PublicKey) {
   259  		s.logger.Debug("message is coming from someone else, but not targeting our installation id")
   260  		return nil, ErrDeviceNotFound
   261  	} else if msg == nil && theirInstallationID != s.config.InstallationID {
   262  		s.logger.Debug("message is coming from same public key, but different installation id")
   263  		return nil, ErrNotPairedDevice
   264  	} else if msg == nil && theirInstallationID == s.config.InstallationID {
   265  		s.logger.Debug("message is coming from us and is nil")
   266  		return nil, nil
   267  	}
   268  
   269  	payload := msg.GetPayload()
   270  
   271  	if x3dhHeader := msg.GetX3DHHeader(); x3dhHeader != nil {
   272  		bundleID := x3dhHeader.GetId()
   273  		theirEphemeralKey, err := crypto.DecompressPubkey(x3dhHeader.GetKey())
   274  
   275  		if err != nil {
   276  			return nil, err
   277  		}
   278  
   279  		symmetricKey, err := s.keyFromPassiveX3DH(myIdentityKey, theirIdentityKey, theirEphemeralKey, bundleID)
   280  		if err != nil {
   281  			return nil, err
   282  		}
   283  
   284  		theirIdentityKeyC := crypto.CompressPubkey(theirIdentityKey)
   285  		err = s.persistence.AddRatchetInfo(symmetricKey, theirIdentityKeyC, bundleID, nil, theirInstallationID)
   286  		if err != nil {
   287  			return nil, err
   288  		}
   289  	}
   290  
   291  	if drHeader := msg.GetDRHeader(); drHeader != nil {
   292  		drMessage := &dr.Message{
   293  			Header: dr.MessageHeader{
   294  				N:  drHeader.GetN(),
   295  				PN: drHeader.GetPn(),
   296  				DH: drHeader.GetKey(),
   297  			},
   298  			Ciphertext: msg.GetPayload(),
   299  		}
   300  
   301  		theirIdentityKeyC := crypto.CompressPubkey(theirIdentityKey)
   302  
   303  		drInfo, err := s.persistence.GetRatchetInfo(drHeader.GetId(), theirIdentityKeyC, theirInstallationID)
   304  		if err != nil {
   305  			s.logger.Error("could not get ratchet info", zap.Error(err))
   306  			return nil, err
   307  		}
   308  
   309  		// We mark the exchange as successful so we stop sending x3dh header
   310  		if err = s.persistence.RatchetInfoConfirmed(drHeader.GetId(), theirIdentityKeyC, theirInstallationID); err != nil {
   311  			s.logger.Error("could not confirm ratchet info", zap.Error(err))
   312  			return nil, err
   313  		}
   314  
   315  		if drInfo == nil {
   316  			s.logger.Error("could not find a session")
   317  			return nil, errSessionNotFound
   318  		}
   319  
   320  		confirmationData := &confirmationData{
   321  			header: &drMessage.Header,
   322  			drInfo: drInfo,
   323  		}
   324  		s.messageIDs[confirmationIDString(messageID)] = confirmationData
   325  
   326  		return s.decryptUsingDR(theirIdentityKey, drInfo, drMessage)
   327  	}
   328  
   329  	// Try DH
   330  	if header := msg.GetDHHeader(); header != nil {
   331  		decompressedKey, err := crypto.DecompressPubkey(header.GetKey())
   332  		if err != nil {
   333  			return nil, err
   334  		}
   335  		return s.DecryptWithDH(myIdentityKey, decompressedKey, payload)
   336  	}
   337  
   338  	// Try Hash Ratchet
   339  	if header := msg.GetHRHeader(); header != nil {
   340  
   341  		ratchet := &HashRatchetKeyCompatibility{
   342  			GroupID: header.GroupId,
   343  			// NOTE: this would be nil in the old format
   344  			keyID: header.KeyId,
   345  		}
   346  
   347  		// Old key format
   348  		if header.DeprecatedKeyId != 0 {
   349  			ratchet.Timestamp = uint64(header.DeprecatedKeyId)
   350  		}
   351  
   352  		decryptedPayload, err := s.DecryptWithHR(ratchet, header.SeqNo, payload)
   353  
   354  		return decryptedPayload, err
   355  	}
   356  	return nil, errors.New("no key specified")
   357  }
   358  
   359  func (s *encryptor) createNewSession(drInfo *RatchetInfo, sk []byte, keyPair crypto.DHPair) (dr.Session, error) {
   360  	var err error
   361  	var session dr.Session
   362  
   363  	if drInfo.PrivateKey != nil {
   364  		session, err = dr.New(
   365  			drInfo.ID,
   366  			sk,
   367  			keyPair,
   368  			s.persistence.SessionStorage(),
   369  			dr.WithKeysStorage(s.persistence.KeysStorage()),
   370  			dr.WithMaxSkip(s.config.MaxSkip),
   371  			dr.WithMaxKeep(s.config.MaxKeep),
   372  			dr.WithMaxMessageKeysPerSession(s.config.MaxMessageKeysPerSession),
   373  			dr.WithCrypto(crypto.EthereumCrypto{}))
   374  	} else {
   375  		session, err = dr.NewWithRemoteKey(
   376  			drInfo.ID,
   377  			sk,
   378  			keyPair.PubKey,
   379  			s.persistence.SessionStorage(),
   380  			dr.WithKeysStorage(s.persistence.KeysStorage()),
   381  			dr.WithMaxSkip(s.config.MaxSkip),
   382  			dr.WithMaxKeep(s.config.MaxKeep),
   383  			dr.WithMaxMessageKeysPerSession(s.config.MaxMessageKeysPerSession),
   384  			dr.WithCrypto(crypto.EthereumCrypto{}))
   385  	}
   386  
   387  	return session, err
   388  }
   389  
   390  func (s *encryptor) encryptUsingDR(theirIdentityKey *ecdsa.PublicKey, drInfo *RatchetInfo, payload []byte) ([]byte, *DRHeader, error) {
   391  	var err error
   392  
   393  	var session dr.Session
   394  
   395  	keyPair := crypto.DHPair{
   396  		PrvKey: drInfo.PrivateKey,
   397  		PubKey: drInfo.PublicKey,
   398  	}
   399  
   400  	// Load session from store first
   401  	session, err = s.getDRSession(drInfo.ID)
   402  
   403  	if err != nil {
   404  		return nil, nil, err
   405  	}
   406  
   407  	// Create a new one
   408  	if session == nil {
   409  		session, err = s.createNewSession(drInfo, drInfo.Sk, keyPair)
   410  		if err != nil {
   411  			return nil, nil, err
   412  		}
   413  	}
   414  
   415  	response, err := session.RatchetEncrypt(payload, nil)
   416  	if err != nil {
   417  		return nil, nil, err
   418  	}
   419  
   420  	header := &DRHeader{
   421  		Id:  drInfo.BundleID,
   422  		Key: response.Header.DH[:],
   423  		N:   response.Header.N,
   424  		Pn:  response.Header.PN,
   425  	}
   426  
   427  	return response.Ciphertext, header, nil
   428  }
   429  
   430  func (s *encryptor) decryptUsingDR(theirIdentityKey *ecdsa.PublicKey, drInfo *RatchetInfo, payload *dr.Message) ([]byte, error) {
   431  	var err error
   432  
   433  	var session dr.Session
   434  
   435  	keyPair := crypto.DHPair{
   436  		PrvKey: drInfo.PrivateKey,
   437  		PubKey: drInfo.PublicKey,
   438  	}
   439  
   440  	session, err = s.getDRSession(drInfo.ID)
   441  	if err != nil {
   442  		return nil, err
   443  	}
   444  
   445  	if session == nil {
   446  		session, err = s.createNewSession(drInfo, drInfo.Sk, keyPair)
   447  		if err != nil {
   448  			return nil, err
   449  		}
   450  	}
   451  
   452  	plaintext, err := session.RatchetDecrypt(*payload, nil)
   453  	if err != nil {
   454  		return nil, err
   455  	}
   456  
   457  	return plaintext, nil
   458  }
   459  
   460  func (s *encryptor) encryptWithDH(theirIdentityKey *ecdsa.PublicKey, payload []byte) (*EncryptedMessageProtocol, error) {
   461  	symmetricKey, ourEphemeralKey, err := PerformActiveDH(theirIdentityKey)
   462  	if err != nil {
   463  		return nil, err
   464  	}
   465  
   466  	encryptedPayload, err := crypto.EncryptSymmetric(symmetricKey, payload)
   467  	if err != nil {
   468  		return nil, err
   469  	}
   470  
   471  	return &EncryptedMessageProtocol{
   472  		DHHeader: &DHHeader{
   473  			Key: crypto.CompressPubkey(ourEphemeralKey),
   474  		},
   475  		Payload: encryptedPayload,
   476  	}, nil
   477  }
   478  
   479  func (s *encryptor) EncryptPayloadWithDH(theirIdentityKey *ecdsa.PublicKey, payload []byte) (map[string]*EncryptedMessageProtocol, error) {
   480  	response := make(map[string]*EncryptedMessageProtocol)
   481  	dmp, err := s.encryptWithDH(theirIdentityKey, payload)
   482  	if err != nil {
   483  		return nil, err
   484  	}
   485  
   486  	response[noInstallationID] = dmp
   487  	return response, nil
   488  }
   489  
   490  // GetPublicBundle returns the active installations bundles for a given user
   491  func (s *encryptor) GetPublicBundle(theirIdentityKey *ecdsa.PublicKey, installations []*multidevice.Installation) (*Bundle, error) {
   492  	return s.persistence.GetPublicBundle(theirIdentityKey, installations)
   493  }
   494  
   495  // EncryptPayload returns a new EncryptedMessageProtocol with a given payload encrypted, given a recipient's public key and the sender private identity key
   496  func (s *encryptor) EncryptPayload(theirIdentityKey *ecdsa.PublicKey, myIdentityKey *ecdsa.PrivateKey, installations []*multidevice.Installation, payload []byte) (map[string]*EncryptedMessageProtocol, []*multidevice.Installation, error) {
   497  	logger := s.logger.With(
   498  		zap.String("site", "EncryptPayload"),
   499  		zap.String("their-identity-key", types.EncodeHex(crypto.FromECDSAPub(theirIdentityKey))))
   500  
   501  	// Which installations we are sending the message to
   502  	var targetedInstallations []*multidevice.Installation
   503  
   504  	s.mutex.Lock()
   505  	defer s.mutex.Unlock()
   506  
   507  	if len(installations) == 0 {
   508  		// We don't have any, send a message with DH
   509  		logger.Debug("no installations, sending to all devices")
   510  		encryptedPayload, err := s.EncryptPayloadWithDH(theirIdentityKey, payload)
   511  		return encryptedPayload, targetedInstallations, err
   512  	}
   513  
   514  	theirIdentityKeyC := crypto.CompressPubkey(theirIdentityKey)
   515  	response := make(map[string]*EncryptedMessageProtocol)
   516  
   517  	for _, installation := range installations {
   518  		installationID := installation.ID
   519  		ilogger := logger.With(zap.String("installation-id", installationID))
   520  		ilogger.Debug("processing installation")
   521  		if s.config.InstallationID == installationID {
   522  			continue
   523  		}
   524  
   525  		bundle, err := s.persistence.GetPublicBundle(theirIdentityKey, []*multidevice.Installation{installation})
   526  		if err != nil {
   527  			return nil, nil, err
   528  		}
   529  
   530  		// See if a session is there already
   531  		drInfo, err := s.persistence.GetAnyRatchetInfo(theirIdentityKeyC, installationID)
   532  		if err != nil {
   533  			return nil, nil, err
   534  		}
   535  
   536  		targetedInstallations = append(targetedInstallations, installation)
   537  
   538  		if drInfo != nil {
   539  			ilogger.Debug("found DR info for installation")
   540  			encryptedPayload, drHeader, err := s.encryptUsingDR(theirIdentityKey, drInfo, payload)
   541  			if err != nil {
   542  				return nil, nil, err
   543  			}
   544  
   545  			dmp := EncryptedMessageProtocol{
   546  				Payload:  encryptedPayload,
   547  				DRHeader: drHeader,
   548  			}
   549  
   550  			if drInfo.EphemeralKey != nil {
   551  				dmp.X3DHHeader = &X3DHHeader{
   552  					Key: drInfo.EphemeralKey,
   553  					Id:  drInfo.BundleID,
   554  				}
   555  			}
   556  
   557  			response[drInfo.InstallationID] = &dmp
   558  			continue
   559  		}
   560  
   561  		theirSignedPreKeyContainer := bundle.GetSignedPreKeys()[installationID]
   562  
   563  		// This should not be nil at this point
   564  		if theirSignedPreKeyContainer == nil {
   565  			ilogger.Warn("could not find DR info or bundle for installation")
   566  			continue
   567  
   568  		}
   569  
   570  		ilogger.Debug("DR info not found, using bundle")
   571  
   572  		theirSignedPreKey := theirSignedPreKeyContainer.GetSignedPreKey()
   573  
   574  		sharedKey, ourEphemeralKey, err := s.keyFromActiveX3DH(theirIdentityKeyC, theirSignedPreKey, myIdentityKey)
   575  		if err != nil {
   576  			return nil, nil, err
   577  		}
   578  		theirIdentityKeyC := crypto.CompressPubkey(theirIdentityKey)
   579  		ourEphemeralKeyC := crypto.CompressPubkey(ourEphemeralKey)
   580  
   581  		err = s.persistence.AddRatchetInfo(sharedKey, theirIdentityKeyC, theirSignedPreKey, ourEphemeralKeyC, installationID)
   582  		if err != nil {
   583  			return nil, nil, err
   584  		}
   585  
   586  		x3dhHeader := &X3DHHeader{
   587  			Key: ourEphemeralKeyC,
   588  			Id:  theirSignedPreKey,
   589  		}
   590  
   591  		drInfo, err = s.persistence.GetRatchetInfo(theirSignedPreKey, theirIdentityKeyC, installationID)
   592  		if err != nil {
   593  			return nil, nil, err
   594  		}
   595  
   596  		if drInfo != nil {
   597  			encryptedPayload, drHeader, err := s.encryptUsingDR(theirIdentityKey, drInfo, payload)
   598  			if err != nil {
   599  				return nil, nil, err
   600  			}
   601  
   602  			dmp := &EncryptedMessageProtocol{
   603  				Payload:    encryptedPayload,
   604  				X3DHHeader: x3dhHeader,
   605  				DRHeader:   drHeader,
   606  			}
   607  
   608  			response[drInfo.InstallationID] = dmp
   609  		}
   610  	}
   611  
   612  	var installationIDs []string
   613  	for _, i := range targetedInstallations {
   614  		installationIDs = append(installationIDs, i.ID)
   615  	}
   616  	logger.Info(
   617  		"built a message",
   618  		zap.Strings("installation-ids", installationIDs),
   619  	)
   620  
   621  	return response, targetedInstallations, nil
   622  }
   623  
   624  func (s *encryptor) getNextHashRatchet(groupID []byte) (*HashRatchetKeyCompatibility, error) {
   625  	latestKey, err := s.persistence.GetCurrentKeyForGroup(groupID)
   626  	if err != nil {
   627  		return nil, err
   628  	}
   629  	return latestKey.GenerateNext()
   630  }
   631  
   632  // GenerateHashRatchetKey Generates and stores a hash ratchet key given a group ID
   633  func (s *encryptor) GenerateHashRatchetKey(groupID []byte) (*HashRatchetKeyCompatibility, error) {
   634  
   635  	key, err := s.getNextHashRatchet(groupID)
   636  	if err != nil {
   637  		return nil, err
   638  	}
   639  
   640  	return key, s.persistence.SaveHashRatchetKey(key)
   641  }
   642  
   643  // EncryptHashRatchetPayload returns a new EncryptedMessageProtocol with a given payload encrypted, given a group's key
   644  func (s *encryptor) EncryptHashRatchetPayload(ratchet *HashRatchetKeyCompatibility, payload []byte) (map[string]*EncryptedMessageProtocol, error) {
   645  	logger := s.logger.With(
   646  		zap.String("site", "EncryptHashRatchetPayload"),
   647  		zap.Any("group-id", ratchet.GroupID),
   648  		zap.Any("key-id", ratchet.keyID))
   649  
   650  	s.mutex.Lock()
   651  	defer s.mutex.Unlock()
   652  
   653  	logger.Debug("encrypting hash ratchet message")
   654  	encryptedPayload, newSeqNo, err := s.EncryptWithHR(ratchet, payload)
   655  	if err != nil {
   656  		return nil, err
   657  	}
   658  
   659  	keyID, err := ratchet.GetKeyID()
   660  	if err != nil {
   661  		return nil, err
   662  	}
   663  
   664  	dmp := &EncryptedMessageProtocol{
   665  		HRHeader: &HRHeader{
   666  			DeprecatedKeyId: ratchet.DeprecatedKeyID(),
   667  			GroupId:         ratchet.GroupID,
   668  			KeyId:           keyID,
   669  			SeqNo:           newSeqNo,
   670  		},
   671  		Payload: encryptedPayload,
   672  	}
   673  
   674  	response := make(map[string]*EncryptedMessageProtocol)
   675  	response[noInstallationID] = dmp
   676  	return response, err
   677  }
   678  
   679  func samePublicKeys(pubKey1, pubKey2 ecdsa.PublicKey) bool {
   680  	return pubKey1.X.Cmp(pubKey2.X) == 0 && pubKey1.Y.Cmp(pubKey2.Y) == 0
   681  }
   682  
   683  func (s *encryptor) EncryptWithHR(ratchet *HashRatchetKeyCompatibility, payload []byte) ([]byte, uint32, error) {
   684  	hrCache, err := s.persistence.GetHashRatchetCache(ratchet, 0) // Get latest seqNo
   685  
   686  	if err != nil {
   687  		return nil, 0, err
   688  	}
   689  	if hrCache == nil {
   690  		return nil, 0, ErrNoEncryptionKey
   691  	}
   692  
   693  	var dbHash []byte
   694  	if len(hrCache.Hash) == 0 {
   695  		dbHash = hrCache.Key
   696  	} else {
   697  		dbHash = hrCache.Hash
   698  	}
   699  
   700  	hash := crypto.Keccak256Hash(dbHash)
   701  	encryptedPayload, err := crypto.EncryptSymmetric(hash.Bytes(), payload)
   702  	if err != nil {
   703  		return nil, 0, err
   704  	}
   705  	newSeqNo := hrCache.SeqNo + 1
   706  	err = s.persistence.SaveHashRatchetKeyHash(ratchet, hash.Bytes(), newSeqNo)
   707  	if err != nil {
   708  		return nil, 0, err
   709  	}
   710  
   711  	return encryptedPayload, newSeqNo, nil
   712  }
   713  
   714  func (s *encryptor) DecryptWithHR(ratchet *HashRatchetKeyCompatibility, seqNo uint32, payload []byte) ([]byte, error) {
   715  	// Key exchange message, nothing to decrypt
   716  	if seqNo == 0 {
   717  		return payload, nil
   718  	}
   719  
   720  	hrCache, err := s.persistence.GetHashRatchetCache(ratchet, seqNo)
   721  	if err != nil {
   722  		return nil, err
   723  	}
   724  
   725  	if hrCache == nil {
   726  		return nil, ErrHashRatchetGroupIDNotFound
   727  	}
   728  
   729  	// Handle mesages with seqNo less than the one in db
   730  	// 1. Check cache. If present for a particular seqNo, all good
   731  	// 2. Otherwise, get the latest one for that keyId
   732  	// 3. Every time the key is generated, it has to be saved in the cache along with the hash
   733  	var hash []byte = hrCache.Hash
   734  	if hrCache.SeqNo == seqNo {
   735  		// We already have the hash for this seqNo
   736  		hash = hrCache.Hash
   737  	} else {
   738  		if hrCache.SeqNo == 0 {
   739  			// No cache records found for this keyId
   740  			hash = hrCache.Key
   741  		}
   742  		// We should not have "holes" in seq numbers,
   743  		// so a case when hrCache.SeqNo > seqNo shouldn't occur
   744  		if seqNo-hrCache.SeqNo > maxHashRatchetSeqNoDelta {
   745  			return nil, ErrHashRatchetSeqNoTooHigh
   746  		}
   747  		for i := hrCache.SeqNo; i < seqNo; i++ {
   748  			hash = crypto.Keccak256Hash(hash).Bytes()
   749  			err := s.persistence.SaveHashRatchetKeyHash(ratchet, hash, i+1)
   750  			if err != nil {
   751  				return nil, err
   752  			}
   753  		}
   754  	}
   755  
   756  	decryptedPayload, err := crypto.DecryptSymmetric(hash, payload)
   757  
   758  	if err != nil {
   759  		s.logger.Error("failed to decrypt hash", zap.Error(err))
   760  		return nil, err
   761  	}
   762  	return decryptedPayload, nil
   763  }