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 }