/*
 * Copyright 2018 Scytl Secure Electronic Voting SA
 *
 * All rights reserved
 *
 * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
 */
package com.scytl.products.ov.channel.cc.commons.services.compute;

import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.api.primitives.PrimitivesServiceAPI;
import com.scytl.cryptolib.derivation.cryptoapi.CryptoAPIDerivedKey;
import com.scytl.cryptolib.derivation.cryptoapi.CryptoAPIKDFDeriver;
import com.scytl.cryptolib.mathematical.groups.impl.Exponent;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * Derive a key.
 */
@Service
public class KeyDerivationService {
    private static final int DERIVED_KEY_BYTELENGTH = 256;

    private static final int DERIVED_KEY_BITLENGTH = 2048;

    @Autowired
    private PrimitivesServiceAPI primitivesServiceAPI;

    /**
     * Derive the control component key. The deriver input is the result of
     * concatenating the component key with the seed in order to get a different key. 
     * The derivation result must have 2047 bit length and its value has to be 
     * less than the value of Q. To achieve that, deriver output length is set 
     * to 2048 bits and the result is truncated to 2047. If the derivation result 
     * value is bigger than Q, the result is passed as input to the deriver and the
     * derivation is performed again. The process is repeated until getting a result 
     * that accomplish the requirements.
     *
     * @param componentKey
     *            the component key
     * @param seed
     *            the seed
     * @param q
     *            the q
     * @return the derived Key
     * @throws GeneralCryptoLibException
     */
    public Exponent deriveKey(Exponent componentKey, String seed, BigInteger q)
            throws GeneralCryptoLibException {
        BigInteger derivedValue;
        boolean smallerThanQ;
        CryptoAPIDerivedKey derivedKey;
        CryptoAPIKDFDeriver kdfDeriver = primitivesServiceAPI.getKDFDeriver();
        String concatenation = seed.concat(componentKey.getValue().toString());
        byte[] derivedKeyBytes = concatenation.getBytes(StandardCharsets.UTF_8);

        do {
            derivedKey = kdfDeriver.deriveKey(derivedKeyBytes, DERIVED_KEY_BYTELENGTH);
            derivedKeyBytes = derivedKey.getEncoded();
            derivedValue = new BigInteger(1, derivedKeyBytes);
            derivedValue = derivedValue.clearBit(DERIVED_KEY_BITLENGTH - 1);
            smallerThanQ = derivedValue.compareTo(q) < 0;
        } while (!smallerThanQ);

        return new Exponent(q, derivedValue);
    }
}
