/*
 * 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.ei.services.domain.model.rule;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.PublicKey;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.inject.Inject;

import com.scytl.products.ov.ei.services.domain.service.certificate.X509CertificateService;
import org.bouncycastle.util.encoders.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.scytl.cryptolib.api.asymmetric.AsymmetricServiceAPI;
import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.products.ov.commons.beans.domain.model.vote.Vote;
import com.scytl.products.ov.commons.crypto.InputDataFormatterService;
import com.scytl.products.ov.commons.domain.model.rule.AbstractRule;
import com.scytl.products.ov.commons.validation.ValidationError;
import com.scytl.products.ov.commons.beans.validation.ValidationErrorType;

/**
 * The Class implementing the rule for verifying signature of the vote.
 */
public class VerifySignatureRule implements AbstractRule<Vote> {

    // The asymmetric service.
    @Inject
    private AsymmetricServiceAPI asymmetricService;

    // The input data formatter service.
    @Inject
    private InputDataFormatterService inputDataFormatterService;

    private static final Logger LOG = LoggerFactory.getLogger("std");

    @Inject
    private X509CertificateService certificateFactory;

    /**
     * Return the name of the rule.
     *
     * @return the name of the rule.
     */
    @Override
    public String getName() {
        return RuleNames.VOTE_VERIFY_SIGNATURE.getText();
    }

    /**
     * This method executes this rule by checking if signature is verified. @param vote - the vote to be
     * verified. @return The ValidationResultType.SUCCESS if the signature is valid. Otherwise, a ValidationResultType
     * for the corresponding error. @see AbstractRule#execute(com.scytl.products.ov.ei.services .domain.model.vote.Vote
     */
    @Override
    public ValidationError execute(Vote vote) {
        ValidationError result = new ValidationError();

        try {

            // Obtain certificate
            InputStream inputStream = new ByteArrayInputStream(vote.getCertificate().getBytes(StandardCharsets.UTF_8));
            X509Certificate certificate = certificateFactory.generateCertificate(inputStream);

            PublicKey publicKey = certificate.getPublicKey();

            // verify signature on encrypted vote
            String signature = vote.getSignature();

            byte[] dataToVerify = inputDataFormatterService.concatenate(vote.getFieldsAsStringArray());
            byte[] signatureBytes = Base64.decode(signature);
            if (asymmetricService.verifySignature(signatureBytes, publicKey, dataToVerify)) {
                result.setValidationErrorType(ValidationErrorType.SUCCESS);
            }
        } catch (CertificateException | GeneralCryptoLibException e) {
            LOG.error("", e);
        }

        return result;
    }
}
