/*
 * 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.config.model.authentication.service;

import java.nio.charset.StandardCharsets;
import java.util.Optional;

import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang.StringUtils;

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.CryptoAPIPBKDFDeriver;
import com.scytl.cryptolib.primitives.securerandom.configuration.SecureRandomPolicyFromProperties;
import com.scytl.cryptolib.primitives.securerandom.factory.CryptoRandomString;
import com.scytl.cryptolib.primitives.securerandom.factory.SecureRandomFactory;
import com.scytl.products.ov.config.model.authentication.AuthenticationDerivedElement;
import com.scytl.products.ov.config.model.authentication.ChallengeGenerator;
import com.scytl.products.ov.config.model.authentication.ExtendedAuthChallenge;
import com.scytl.products.ov.config.model.authentication.ExtraParams;
import com.scytl.products.ov.constants.Constants;

/**
 * Class that creates the challenge to be used in extended auth
 */
public class ChallengeService implements ChallengeServiceAPI {

    public static final String DELIMITER = "";

    private final PrimitivesServiceAPI primitivesService;

    private final ChallengeGenerator challengeGenerator;

    private final CryptoRandomString cryptoRandomString;

    public ChallengeService(final PrimitivesServiceAPI primitivesService, final ChallengeGenerator challengeGenerator) {

        this.primitivesService = primitivesService;
        this.challengeGenerator = challengeGenerator;

        final String path = com.scytl.cryptolib.commons.constants.Constants.CRYPTOLIB_POLICY_PROPERTIES_FILE_PATH;
        SecureRandomFactory secureRandomFactory = new SecureRandomFactory(new SecureRandomPolicyFromProperties(path));
        this.cryptoRandomString = secureRandomFactory.createStringRandom(Constants.SVK_ALPHABET);
    }

    /**
     * Creates the challenge used for extended authentication
     *
     * @param challenges
     * @return
     * @throws GeneralCryptoLibException
     */
    @Override
    public Optional<ExtendedAuthChallenge> createExtendedAuthChallenge() throws GeneralCryptoLibException {

        String randomSalt = cryptoRandomString.nextRandom(Constants.RANDOM_SALT_LENGTH);
        String randomSaltHex = new String(Hex.encodeHex(randomSalt.getBytes(StandardCharsets.UTF_8)));

        ExtraParams extraParams = challengeGenerator.generateExtraParams();
        if (!extraParams.getValue().isPresent()) {
            return Optional.empty();
        }
        String extraAuthParam = extraParams.getValue().get();
        Optional<String> alias = extraParams.getAlias();

        byte[] salt = randomSaltHex.getBytes();

        if (extraAuthParam.length() < Constants.PBKDF2_MIN_EXTRA_PARAM_LENGTH) {
            extraAuthParam = StringUtils.leftPad(extraAuthParam, Constants.PBKDF2_MIN_EXTRA_PARAM_LENGTH);
            
        }
        final CryptoAPIPBKDFDeriver pbkdfDeriver = primitivesService.getPBKDFDeriver();
        final CryptoAPIDerivedKey cryptoAPIDerivedKey = pbkdfDeriver.deriveKey(extraAuthParam.toCharArray(), salt);
        final byte[] keyIdBytes = cryptoAPIDerivedKey.getEncoded();
        final AuthenticationDerivedElement authenticationDerivedElement =
            AuthenticationDerivedElement.of(cryptoAPIDerivedKey, new String(Hex.encodeHex(keyIdBytes)));

        return Optional.of(ExtendedAuthChallenge.of(authenticationDerivedElement, alias, salt));
    }
}
