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

io.amient.affinity.core.util.TimeCryptoProof Maven / Gradle / Ivy

/*
 * Copyright 2016 Michal Harish, [email protected]
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 io.amient.affinity.core.util;

import javax.xml.bind.DatatypeConverter;
import java.nio.ByteBuffer;
import java.security.SecureRandom;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.Arrays;

public abstract class TimeCryptoProof {

    static private java.util.Random random = new SecureRandom();
    private final byte[] salt;

    /**
     * convert bytes to hexadecimal representation
     * @param bytes input bytes
     * @return result hexadecimal number
     */
    static public String toHex(byte[] bytes) {
        return DatatypeConverter.printHexBinary(bytes);
    }

    /**
     * convert hexadecimal representation to bytes
     * @param hex input
     * @return byte array
     */
    static public byte[] fromHex(String hex) {
        return DatatypeConverter.parseHexBinary(hex);
    }

    /**
     * generate a random secure salt
     * @return byte array salt
     */
    static public byte[] generateSalt() {
        byte[] bytes = new byte[16];
        Arrays.fill(bytes, (byte) 0);
        random.nextBytes(bytes);
        return bytes;
    }

    static private long utcInWholeMinutes(int offset) {
        return ZonedDateTime.now(ZoneOffset.UTC).withNano(0).withSecond(0).toEpochSecond() + offset * 60;
    }

    /**
     * Construct a Thread-Safe instance of Crypto with a given salt
     * @param salt byte array salt
     */
    public TimeCryptoProof(byte[] salt) {
        this.salt = salt;
    }

    /**
     * Construct a Thread-Safe instance of Crypto with a given salt
     * @param hexSalt hexadecimal salt
     */
    public TimeCryptoProof(String hexSalt) {
        this.salt = fromHex(hexSalt);
    }

    /**
     * Generate deterministic salted hash
     * @param arg String input
     * @return String output hash
     * @throws Exception if anything goes wrong
     */
    final public String hash(String arg) throws Exception {
        byte[] input = arg.getBytes("UTF-8");
        ByteBuffer in = ByteBuffer.allocate(salt.length + input.length);
        in.put(salt);
        in.put(input);
        in.flip();
        return toHex(encode(in.array()));
    }

    /**
     * Sign a given input argument with time-based salted hash
     * @param arg input string to sign
     * @return signature string
     * @throws Exception if anything goes wrong
     */
    final public String sign(String arg) throws Exception {
        return toHex(sign(arg.getBytes("UTF-8")));
    }

    /**
     * Sign a given input argument with time-based salted hash
     * @param arg byte array input
     * @return byte array signature
     * @throws Exception if anything goes wrong
     */
    final public byte[] sign(byte[] arg) throws Exception {
        return sign(arg, utcInWholeMinutes(0));
    }

    private byte[] sign(byte[] arg, long utc) throws Exception {
        ByteBuffer in = ByteBuffer.allocate(salt.length + 8 + arg.length);
        in.put(salt);
        in.putLong(utc);
        in.put(arg);
        in.flip();
        return encode(in.array());
    }


    final public boolean verify(String signature, String arg) throws Exception {
        return verify(fromHex(signature), arg.getBytes("UTF-8"));
    }

    final public boolean verify(byte[] signature, byte[] arg) throws Exception {
        return (Arrays.equals(sign(arg, utcInWholeMinutes(0)), signature))
                || (Arrays.equals(sign(arg, utcInWholeMinutes(-1)), signature))
                || (Arrays.equals(sign(arg, utcInWholeMinutes(+1)), signature));
    }

    abstract protected byte[] encode(byte[] input) throws Exception;


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy