keys.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. // Copyright (c) 2021 Tulir Asokan
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this
  5. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. // Package appstate implements encoding and decoding WhatsApp's app state patches.
  7. package appstate
  8. import (
  9. "context"
  10. "encoding/base64"
  11. "sync"
  12. "go.mau.fi/whatsmeow/store"
  13. "go.mau.fi/whatsmeow/util/hkdfutil"
  14. waLog "go.mau.fi/whatsmeow/util/log"
  15. )
  16. // WAPatchName represents a type of app state patch.
  17. type WAPatchName string
  18. const (
  19. // WAPatchCriticalBlock contains the user's settings like push name and locale.
  20. WAPatchCriticalBlock WAPatchName = "critical_block"
  21. // WAPatchCriticalUnblockLow contains the user's contact list.
  22. WAPatchCriticalUnblockLow WAPatchName = "critical_unblock_low"
  23. // WAPatchRegularLow contains some local chat settings like pin, archive status, and the setting of whether to unarchive chats when messages come in.
  24. WAPatchRegularLow WAPatchName = "regular_low"
  25. // WAPatchRegularHigh contains more local chat settings like mute status and starred messages.
  26. WAPatchRegularHigh WAPatchName = "regular_high"
  27. // WAPatchRegular contains protocol info about app state patches like key expiration.
  28. WAPatchRegular WAPatchName = "regular"
  29. )
  30. // AllPatchNames contains all currently known patch state names.
  31. var AllPatchNames = [...]WAPatchName{WAPatchCriticalBlock, WAPatchCriticalUnblockLow, WAPatchRegularHigh, WAPatchRegular, WAPatchRegularLow}
  32. // Constants for the first part of app state indexes.
  33. const (
  34. IndexMute = "mute"
  35. IndexPin = "pin_v1"
  36. IndexArchive = "archive"
  37. IndexContact = "contact"
  38. IndexClearChat = "clearChat"
  39. IndexDeleteChat = "deleteChat"
  40. IndexStar = "star"
  41. IndexDeleteMessageForMe = "deleteMessageForMe"
  42. IndexMarkChatAsRead = "markChatAsRead"
  43. IndexSettingPushName = "setting_pushName"
  44. IndexSettingUnarchiveChats = "setting_unarchiveChats"
  45. IndexUserStatusMute = "userStatusMute"
  46. IndexLabelEdit = "label_edit"
  47. IndexLabelAssociationChat = "label_jid"
  48. IndexLabelAssociationMessage = "label_message"
  49. )
  50. type Processor struct {
  51. keyCache map[string]ExpandedAppStateKeys
  52. keyCacheLock sync.Mutex
  53. Store *store.Device
  54. Log waLog.Logger
  55. }
  56. func NewProcessor(store *store.Device, log waLog.Logger) *Processor {
  57. return &Processor{
  58. keyCache: make(map[string]ExpandedAppStateKeys),
  59. Store: store,
  60. Log: log,
  61. }
  62. }
  63. type ExpandedAppStateKeys struct {
  64. Index []byte
  65. ValueEncryption []byte
  66. ValueMAC []byte
  67. SnapshotMAC []byte
  68. PatchMAC []byte
  69. }
  70. func expandAppStateKeys(keyData []byte) (keys ExpandedAppStateKeys) {
  71. appStateKeyExpanded := hkdfutil.SHA256(keyData, nil, []byte("WhatsApp Mutation Keys"), 160)
  72. return ExpandedAppStateKeys{appStateKeyExpanded[0:32], appStateKeyExpanded[32:64], appStateKeyExpanded[64:96], appStateKeyExpanded[96:128], appStateKeyExpanded[128:160]}
  73. }
  74. func (proc *Processor) getAppStateKey(ctx context.Context, keyID []byte) (keys ExpandedAppStateKeys, err error) {
  75. keyCacheID := base64.RawStdEncoding.EncodeToString(keyID)
  76. var ok bool
  77. proc.keyCacheLock.Lock()
  78. defer proc.keyCacheLock.Unlock()
  79. keys, ok = proc.keyCache[keyCacheID]
  80. if !ok {
  81. var keyData *store.AppStateSyncKey
  82. keyData, err = proc.Store.AppStateKeys.GetAppStateSyncKey(ctx, keyID)
  83. if keyData != nil {
  84. keys = expandAppStateKeys(keyData.Data)
  85. proc.keyCache[keyCacheID] = keys
  86. } else if err == nil {
  87. err = ErrKeyNotFound
  88. }
  89. }
  90. return
  91. }
  92. func (proc *Processor) GetMissingKeyIDs(ctx context.Context, pl *PatchList) [][]byte {
  93. cache := make(map[string]bool)
  94. var missingKeys [][]byte
  95. checkMissing := func(keyID []byte) {
  96. if keyID == nil {
  97. return
  98. }
  99. stringKeyID := base64.RawStdEncoding.EncodeToString(keyID)
  100. _, alreadyAdded := cache[stringKeyID]
  101. if !alreadyAdded {
  102. keyData, err := proc.Store.AppStateKeys.GetAppStateSyncKey(ctx, keyID)
  103. if err != nil {
  104. proc.Log.Warnf("Error fetching key %X while checking if it's missing: %v", keyID, err)
  105. }
  106. missing := keyData == nil && err == nil
  107. cache[stringKeyID] = missing
  108. if missing {
  109. missingKeys = append(missingKeys, keyID)
  110. }
  111. }
  112. }
  113. if pl.Snapshot != nil {
  114. checkMissing(pl.Snapshot.GetKeyID().GetID())
  115. for _, record := range pl.Snapshot.GetRecords() {
  116. checkMissing(record.GetKeyID().GetID())
  117. }
  118. }
  119. for _, patch := range pl.Patches {
  120. checkMissing(patch.GetKeyID().GetID())
  121. }
  122. return missingKeys
  123. }