All Downloads are FREE. Search and download functionalities are using the official Maven repository.

it.auties.whatsapp.crypto.LTHash Maven / Gradle / Ivy

There is a newer version: 2.7.2
Show newest version
package it.auties.whatsapp.crypto;

import it.auties.bytes.Bytes;
import it.auties.whatsapp.model.sync.LTHashState;
import it.auties.whatsapp.model.sync.RecordSync;
import lombok.NonNull;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class LTHash {
    private static final int EXPAND_SIZE = 128;

    private final byte @NonNull [] salt;

    private final byte @NonNull [] hash;

    @NonNull
    private final Map indexValueMap;

    @NonNull
    private final List add, subtract;

    public LTHash(LTHashState hash) {
        this.salt = "WhatsApp Patch Integrity".getBytes(StandardCharsets.UTF_8);
        this.hash = hash.hash();
        this.indexValueMap = new HashMap<>(hash.indexValueMap());
        this.add = new ArrayList<>();
        this.subtract = new ArrayList<>();
    }

    public void mix(byte[] indexMac, byte[] valueMac, RecordSync.Operation operation) {
        var indexMacBase64 = Bytes.of(indexMac).toBase64();
        var prevOp = indexValueMap.get(indexMacBase64);
        if (operation == RecordSync.Operation.REMOVE) {
            if (prevOp == null) {
                return;
            }
            indexValueMap.remove(indexMacBase64, prevOp);
        } else {
            add.add(valueMac);
            indexValueMap.put(indexMacBase64, valueMac);
        }
        if (prevOp != null) {
            subtract.add(prevOp);
        }
    }

    public Result finish() {
        var subtracted = perform(hash, false);
        var added = perform(subtracted, true);
        return new Result(added, indexValueMap);
    }

    private byte[] perform(byte[] input, boolean sum) {
        for (var item : sum ? add : subtract) {
            input = perform(input, item, sum);
        }
        return input;
    }

    private byte[] perform(byte[] input, byte[] buffer, boolean sum) {
        var expanded = Hkdf.extractAndExpand(buffer, salt, EXPAND_SIZE);
        var eRead = ByteBuffer.wrap(input).order(ByteOrder.LITTLE_ENDIAN);
        var tRead = ByteBuffer.wrap(expanded).order(ByteOrder.LITTLE_ENDIAN);
        var write = ByteBuffer.allocate(input.length).order(ByteOrder.LITTLE_ENDIAN);
        for (var index = 0; index < input.length; index += 2) {
            var first = Short.toUnsignedInt(eRead.getShort(index));
            var second = Short.toUnsignedInt(tRead.getShort(index));
            write.putShort(index, (short) (sum ? first + second : first - second));
        }
        var result = new byte[input.length];
        write.get(result);
        return result;
    }

    public record Result(byte[] hash, Map indexValueMap) {
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy