diff options
Diffstat (limited to 'vendor/go.mau.fi/whatsmeow/appstate/hash.go')
-rw-r--r-- | vendor/go.mau.fi/whatsmeow/appstate/hash.go | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/vendor/go.mau.fi/whatsmeow/appstate/hash.go b/vendor/go.mau.fi/whatsmeow/appstate/hash.go new file mode 100644 index 00000000..4fdbabf6 --- /dev/null +++ b/vendor/go.mau.fi/whatsmeow/appstate/hash.go @@ -0,0 +1,96 @@ +// 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" + waProto "go.mau.fi/whatsmeow/binary/proto" +) + +type Mutation struct { + Operation waProto.SyncdMutation_SyncdMutationSyncdOperation + Action *waProto.SyncActionValue + Index []string + IndexMAC []byte + ValueMAC []byte +} + +type HashState struct { + Version uint64 + Hash [128]byte +} + +func (hs *HashState) updateHash(mutations []*waProto.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() == waProto.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() == waProto.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 *waProto.SyncdPatch, name WAPatchName, key []byte) []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)-1] = []byte(name) + return concatAndHMAC(sha256.New, key, dataToHash...) +} + +func generateContentMAC(operation waProto.SyncdMutation_SyncdMutationSyncdOperation, 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] +} |