TLSHUtil.java

/*
 * Copyright 2020 Keve Müller
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package app.keve.ktlsh;

import java.nio.charset.StandardCharsets;
import java.security.Provider;
import java.security.Security;
import java.util.ServiceLoader;

import app.keve.ktlsh.impl.TLSH;
import app.keve.ktlsh.spi.KProvider;

/**
 * Utility class to perform basic operations on TLSH hashes.
 * 
 * @author keve
 *
 */
public final class TLSHUtil {
    /**
     * Lookup table for upper case hex characters.
     */
    private static final byte[] HEX_UC = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);

    private TLSHUtil() {
    }

    /**
     * Obtain the name of the K provider.
     * 
     * @return the name of the K provider.
     */
    public static String providerNameK() {
        return KProvider.NAME;
    }

    /**
     * Dynamically register the K provider.
     */
    public static void registerProvider() {
        final ServiceLoader<Provider> serviceLoader = ServiceLoader.load(Provider.class);
        for (final Provider p : serviceLoader) {
            if (KProvider.NAME.equals(p.getName())) {
                Security.addProvider(p);
            }
        }
    }

    /**
     * Convert a sequence of hex characters to a buffer of bytes.
     * 
     * @param hex the hex string
     * @return the byte buffer
     */
    public static byte[] hexToBytes(final CharSequence hex) {
        final int len = hex.length();
        final byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4) + Character.digit(hex.charAt(i + 1), 16));
        }
        return data;
    }

    /**
     * Convert a byte buffer to upper case hex character string.
     * 
     * @param bytes the buffer
     * @return the hex string
     */
    public static String bytesToHEX(final byte[] bytes) {
        final byte[] hexChars = new byte[bytes.length * 2];
        for (int j = 0; j < bytes.length; j++) {
            final int value = bytes[j] & 0xFF;
            hexChars[j * 2] = HEX_UC[value >>> 4];
            hexChars[j * 2 + 1] = HEX_UC[value & 0x0F];
        }
        return new String(hexChars, StandardCharsets.UTF_8);
    }

    /**
     * Encode the buffer representation of the TLSH hash.
     * 
     * @param hash the TLSH hash in buffer representation.
     * @return the TLSH hash in hexadecimal string representation.
     */
    public static String encoded(final byte[] hash) {
        return bytesToHEX(hash);
    }

    /**
     * Encode the buffer representation of the TLSH hash with version prefix.
     * 
     * @param hash the TLSH hash in buffer representation.
     * @return the TLSH hash in hexadecimal string representation with version
     *         prefix.
     */
    public static String encodedT1(final byte[] hash) {
        return "T1" + bytesToHEX(hash);
    }

    /**
     * Score two TLSH hashes in buffer representation.
     * 
     * @param hash1   the TLSH hash in buffer representation.
     * @param hash2   the TLSH hash in buffer representation.
     * @param lenDiff true, if the length difference should be scored.
     * @return the score value.
     */
    public static int score(final byte[] hash1, final byte[] hash2, final boolean lenDiff) {
        return TLSH.of(hash1).score(TLSH.of(hash2), lenDiff);
    }
}