summaryrefslogtreecommitdiffstats
path: root/vendor/go.mau.fi/whatsmeow/appstate
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/go.mau.fi/whatsmeow/appstate')
-rw-r--r--vendor/go.mau.fi/whatsmeow/appstate/decode.go2
-rw-r--r--vendor/go.mau.fi/whatsmeow/appstate/encode.go200
-rw-r--r--vendor/go.mau.fi/whatsmeow/appstate/hash.go4
-rw-r--r--vendor/go.mau.fi/whatsmeow/appstate/keys.go16
4 files changed, 219 insertions, 3 deletions
diff --git a/vendor/go.mau.fi/whatsmeow/appstate/decode.go b/vendor/go.mau.fi/whatsmeow/appstate/decode.go
index 5c895470..980c20de 100644
--- a/vendor/go.mau.fi/whatsmeow/appstate/decode.go
+++ b/vendor/go.mau.fi/whatsmeow/appstate/decode.go
@@ -290,7 +290,7 @@ func (proc *Processor) DecodePatches(list *PatchList, initialState HashState, va
if err != nil {
return
}
- patchMAC := generatePatchMAC(patch, list.Name, keys.PatchMAC)
+ patchMAC := generatePatchMAC(patch, list.Name, keys.PatchMAC, patch.GetVersion().GetVersion())
if !bytes.Equal(patchMAC, patch.GetPatchMac()) {
err = fmt.Errorf("failed to verify patch v%d: %w", version, ErrMismatchingPatchMAC)
return
diff --git a/vendor/go.mau.fi/whatsmeow/appstate/encode.go b/vendor/go.mau.fi/whatsmeow/appstate/encode.go
new file mode 100644
index 00000000..1cb7d659
--- /dev/null
+++ b/vendor/go.mau.fi/whatsmeow/appstate/encode.go
@@ -0,0 +1,200 @@
+package appstate
+
+import (
+ "crypto/sha256"
+ "encoding/json"
+ "fmt"
+ "time"
+
+ "google.golang.org/protobuf/proto"
+
+ waProto "go.mau.fi/whatsmeow/binary/proto"
+ "go.mau.fi/whatsmeow/types"
+ "go.mau.fi/whatsmeow/util/cbcutil"
+)
+
+// MutationInfo contains information about a single mutation to the app state.
+type MutationInfo struct {
+ // Index contains the thing being mutated (like `mute` or `pin_v1`), followed by parameters like the target JID.
+ Index []string
+ // Version is a static number that depends on the thing being mutated.
+ Version int32
+ // Value contains the data for the mutation.
+ Value *waProto.SyncActionValue
+}
+
+// PatchInfo contains information about a patch to the app state.
+// A patch can contain multiple mutations, as long as all mutations are in the same app state type.
+type PatchInfo struct {
+ // Timestamp is the time when the patch was created. This will be filled automatically in EncodePatch if it's zero.
+ Timestamp time.Time
+ // Type is the app state type being mutated.
+ Type WAPatchName
+ // Mutations contains the individual mutations to apply to the app state in this patch.
+ Mutations []MutationInfo
+}
+
+// BuildMute builds an app state patch for muting or unmuting a chat.
+//
+// If mute is true and the mute duration is zero, the chat is muted forever.
+func BuildMute(target types.JID, mute bool, muteDuration time.Duration) PatchInfo {
+ var muteEndTimestamp *int64
+ if muteDuration > 0 {
+ muteEndTimestamp = proto.Int64(time.Now().Add(muteDuration).UnixMilli())
+ }
+
+ return PatchInfo{
+ Type: WAPatchRegularHigh,
+ Mutations: []MutationInfo{{
+ Index: []string{IndexMute, target.String()},
+ Version: 2,
+ Value: &waProto.SyncActionValue{
+ MuteAction: &waProto.MuteAction{
+ Muted: proto.Bool(mute),
+ MuteEndTimestamp: muteEndTimestamp,
+ },
+ },
+ }},
+ }
+}
+
+func newPinMutationInfo(target types.JID, pin bool) MutationInfo {
+ return MutationInfo{
+ Index: []string{IndexPin, target.String()},
+ Version: 5,
+ Value: &waProto.SyncActionValue{
+ PinAction: &waProto.PinAction{
+ Pinned: &pin,
+ },
+ },
+ }
+}
+
+// BuildPin builds an app state patch for pinning or unpinning a chat.
+func BuildPin(target types.JID, pin bool) PatchInfo {
+ return PatchInfo{
+ Type: WAPatchRegularLow,
+ Mutations: []MutationInfo{
+ newPinMutationInfo(target, pin),
+ },
+ }
+}
+
+// BuildArchive builds an app state patch for archiving or unarchiving a chat.
+//
+// The last message timestamp and last message key are optional and can be set to zero values (`time.Time{}` and `nil`).
+//
+// Archiving a chat will also unpin it automatically.
+func BuildArchive(target types.JID, archive bool, lastMessageTimestamp time.Time, lastMessageKey *waProto.MessageKey) PatchInfo {
+ if lastMessageTimestamp.IsZero() {
+ lastMessageTimestamp = time.Now()
+ }
+ archiveMutationInfo := MutationInfo{
+ Index: []string{IndexArchive, target.String()},
+ Version: 3,
+ Value: &waProto.SyncActionValue{
+ ArchiveChatAction: &waProto.ArchiveChatAction{
+ Archived: &archive,
+ MessageRange: &waProto.SyncActionMessageRange{
+ LastMessageTimestamp: proto.Int64(lastMessageTimestamp.Unix()),
+ // TODO set LastSystemMessageTimestamp?
+ },
+ },
+ },
+ }
+
+ if lastMessageKey != nil {
+ archiveMutationInfo.Value.ArchiveChatAction.MessageRange.Messages = []*waProto.SyncActionMessage{{
+ Key: lastMessageKey,
+ Timestamp: proto.Int64(lastMessageTimestamp.Unix()),
+ }}
+ }
+
+ mutations := []MutationInfo{archiveMutationInfo}
+ if archive {
+ mutations = append(mutations, newPinMutationInfo(target, false))
+ }
+
+ result := PatchInfo{
+ Type: WAPatchRegularLow,
+ Mutations: mutations,
+ }
+
+ return result
+}
+
+func (proc *Processor) EncodePatch(keyID []byte, state HashState, patchInfo PatchInfo) ([]byte, error) {
+ keys, err := proc.getAppStateKey(keyID)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get app state key details with key ID %x: %w", keyID, err)
+ }
+
+ if patchInfo.Timestamp.IsZero() {
+ patchInfo.Timestamp = time.Now()
+ }
+
+ mutations := make([]*waProto.SyncdMutation, 0, len(patchInfo.Mutations))
+ for _, mutationInfo := range patchInfo.Mutations {
+ mutationInfo.Value.Timestamp = proto.Int64(patchInfo.Timestamp.UnixMilli())
+
+ indexBytes, err := json.Marshal(mutationInfo.Index)
+ if err != nil {
+ return nil, fmt.Errorf("failed to marshal mutation index: %w", err)
+ }
+
+ pbObj := &waProto.SyncActionData{
+ Index: indexBytes,
+ Value: mutationInfo.Value,
+ Padding: []byte{},
+ Version: &mutationInfo.Version,
+ }
+
+ content, err := proto.Marshal(pbObj)
+ if err != nil {
+ return nil, fmt.Errorf("failed to marshal mutation: %w", err)
+ }
+
+ encryptedContent, err := cbcutil.Encrypt(keys.ValueEncryption, nil, content)
+ if err != nil {
+ return nil, fmt.Errorf("failed to encrypt mutation: %w", err)
+ }
+
+ valueMac := generateContentMAC(waProto.SyncdMutation_SET, encryptedContent, keyID, keys.ValueMAC)
+ indexMac := concatAndHMAC(sha256.New, keys.Index, indexBytes)
+
+ mutations = append(mutations, &waProto.SyncdMutation{
+ Operation: waProto.SyncdMutation_SET.Enum(),
+ Record: &waProto.SyncdRecord{
+ Index: &waProto.SyncdIndex{Blob: indexMac},
+ Value: &waProto.SyncdValue{Blob: append(encryptedContent, valueMac...)},
+ KeyId: &waProto.KeyId{Id: keyID},
+ },
+ })
+ }
+
+ warn, err := state.updateHash(mutations, func(indexMAC []byte, _ int) ([]byte, error) {
+ return proc.Store.AppState.GetAppStateMutationMAC(string(patchInfo.Type), indexMAC)
+ })
+ if len(warn) > 0 {
+ proc.Log.Warnf("Warnings while updating hash for %s (sending new app state): %+v", patchInfo.Type, warn)
+ }
+ if err != nil {
+ return nil, fmt.Errorf("failed to update state hash: %w", err)
+ }
+
+ state.Version += 1
+
+ syncdPatch := &waProto.SyncdPatch{
+ SnapshotMac: state.generateSnapshotMAC(patchInfo.Type, keys.SnapshotMAC),
+ KeyId: &waProto.KeyId{Id: keyID},
+ Mutations: mutations,
+ }
+ syncdPatch.PatchMac = generatePatchMAC(syncdPatch, patchInfo.Type, keys.PatchMAC, state.Version)
+
+ result, err := proto.Marshal(syncdPatch)
+ if err != nil {
+ return nil, fmt.Errorf("failed to marshal compiled patch: %w", err)
+ }
+
+ return result, nil
+}
diff --git a/vendor/go.mau.fi/whatsmeow/appstate/hash.go b/vendor/go.mau.fi/whatsmeow/appstate/hash.go
index bb17eeac..2bb0924a 100644
--- a/vendor/go.mau.fi/whatsmeow/appstate/hash.go
+++ b/vendor/go.mau.fi/whatsmeow/appstate/hash.go
@@ -77,14 +77,14 @@ func (hs *HashState) generateSnapshotMAC(name WAPatchName, key []byte) []byte {
return concatAndHMAC(sha256.New, key, hs.Hash[:], uint64ToBytes(hs.Version), []byte(name))
}
-func generatePatchMAC(patch *waProto.SyncdPatch, name WAPatchName, key []byte) []byte {
+func generatePatchMAC(patch *waProto.SyncdPatch, name WAPatchName, key []byte, version uint64) []byte {
dataToHash := make([][]byte, len(patch.GetMutations())+3)
dataToHash[0] = patch.GetSnapshotMac()
for i, mutation := range patch.Mutations {
val := mutation.GetRecord().GetValue().GetBlob()
dataToHash[i+1] = val[len(val)-32:]
}
- dataToHash[len(dataToHash)-2] = uint64ToBytes(patch.GetVersion().GetVersion())
+ dataToHash[len(dataToHash)-2] = uint64ToBytes(version)
dataToHash[len(dataToHash)-1] = []byte(name)
return concatAndHMAC(sha256.New, key, dataToHash...)
}
diff --git a/vendor/go.mau.fi/whatsmeow/appstate/keys.go b/vendor/go.mau.fi/whatsmeow/appstate/keys.go
index ec19dc26..95f7d134 100644
--- a/vendor/go.mau.fi/whatsmeow/appstate/keys.go
+++ b/vendor/go.mau.fi/whatsmeow/appstate/keys.go
@@ -35,6 +35,22 @@ const (
// AllPatchNames contains all currently known patch state names.
var AllPatchNames = [...]WAPatchName{WAPatchCriticalBlock, WAPatchCriticalUnblockLow, WAPatchRegularHigh, WAPatchRegular, WAPatchRegularLow}
+// Constants for the first part of app state indexes.
+const (
+ IndexMute = "mute"
+ IndexPin = "pin_v1"
+ IndexArchive = "archive"
+ IndexContact = "contact"
+ IndexClearChat = "clearChat"
+ IndexDeleteChat = "deleteChat"
+ IndexStar = "star"
+ IndexDeleteMessageForMe = "deleteMessageForMe"
+ IndexMarkChatAsRead = "markChatAsRead"
+ IndexSettingPushName = "setting_pushName"
+ IndexSettingUnarchiveChats = "setting_unarchiveChats"
+ IndexUserStatusMute = "userStatusMute"
+)
+
type Processor struct {
keyCache map[string]ExpandedAppStateKeys
keyCacheLock sync.Mutex