| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798 |
- // Copyright (c) 2021 Tulir Asokan
- //
- // This Source Code Form is subject to the terms of the Mozilla Public
- // License, v. 2.0. If a copy of the MPL was not distributed with this
- // file, You can obtain one at http://mozilla.org/MPL/2.0/.
- package appstate
- import (
- "crypto/hmac"
- "crypto/sha256"
- "crypto/sha512"
- "encoding/binary"
- "fmt"
- "hash"
- "go.mau.fi/whatsmeow/appstate/lthash"
- "go.mau.fi/whatsmeow/proto/waServerSync"
- "go.mau.fi/whatsmeow/proto/waSyncAction"
- )
- type Mutation struct {
- Operation waServerSync.SyncdMutation_SyncdOperation
- Action *waSyncAction.SyncActionValue
- Version int32
- Index []string
- IndexMAC []byte
- ValueMAC []byte
- }
- type HashState struct {
- Version uint64
- Hash [128]byte
- }
- func (hs *HashState) updateHash(mutations []*waServerSync.SyncdMutation, getPrevSetValueMAC func(indexMAC []byte, maxIndex int) ([]byte, error)) ([]error, error) {
- var added, removed [][]byte
- var warnings []error
- for i, mutation := range mutations {
- if mutation.GetOperation() == waServerSync.SyncdMutation_SET {
- value := mutation.GetRecord().GetValue().GetBlob()
- added = append(added, value[len(value)-32:])
- }
- indexMAC := mutation.GetRecord().GetIndex().GetBlob()
- removal, err := getPrevSetValueMAC(indexMAC, i)
- if err != nil {
- return warnings, fmt.Errorf("failed to get value MAC of previous SET operation: %w", err)
- } else if removal != nil {
- removed = append(removed, removal)
- } else if mutation.GetOperation() == waServerSync.SyncdMutation_REMOVE {
- // TODO figure out if there are certain cases that are safe to ignore and others that aren't
- // At least removing contact access from WhatsApp seems to create a REMOVE op for your own JID
- // that points to a non-existent index and is safe to ignore here. Other keys might not be safe to ignore.
- warnings = append(warnings, fmt.Errorf("%w for %X", ErrMissingPreviousSetValueOperation, indexMAC))
- //return ErrMissingPreviousSetValueOperation
- }
- }
- lthash.WAPatchIntegrity.SubtractThenAddInPlace(hs.Hash[:], removed, added)
- return warnings, nil
- }
- func uint64ToBytes(val uint64) []byte {
- data := make([]byte, 8)
- binary.BigEndian.PutUint64(data, val)
- return data
- }
- func concatAndHMAC(alg func() hash.Hash, key []byte, data ...[]byte) []byte {
- h := hmac.New(alg, key)
- for _, item := range data {
- h.Write(item)
- }
- return h.Sum(nil)
- }
- func (hs *HashState) generateSnapshotMAC(name WAPatchName, key []byte) []byte {
- return concatAndHMAC(sha256.New, key, hs.Hash[:], uint64ToBytes(hs.Version), []byte(name))
- }
- func generatePatchMAC(patch *waServerSync.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(version)
- dataToHash[len(dataToHash)-1] = []byte(name)
- return concatAndHMAC(sha256.New, key, dataToHash...)
- }
- func generateContentMAC(operation waServerSync.SyncdMutation_SyncdOperation, data, keyID, key []byte) []byte {
- operationBytes := []byte{byte(operation) + 1}
- keyDataLength := uint64ToBytes(uint64(len(keyID) + 1))
- return concatAndHMAC(sha512.New, key, operationBytes, keyID, data, keyDataLength)[:32]
- }
|