/*
 * 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.vv.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.CertificateFactory;
import java.security.cert.X509Certificate;

import javax.inject.Inject;
import javax.json.JsonException;
import javax.json.JsonObject;

import org.apache.commons.codec.binary.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.beans.exceptions.ResourceNotFoundException;
import com.scytl.products.ov.commons.beans.validation.ValidationErrorType;
import com.scytl.products.ov.commons.domain.model.rule.AbstractRule;
import com.scytl.products.ov.commons.util.JsonUtils;
import com.scytl.products.ov.commons.validation.ValidationError;
import com.scytl.products.ov.vv.domain.model.verificationset.VerificationSetEntity;
import com.scytl.products.ov.vv.domain.model.verificationset.VerificationSetRepository;

/**
 * Verifies the digital signature of the Verification Card Public Key using the
 * Verification Card Issuer X.509 Certificate.
 */
public class VerifyVerificationCardPKSignatureRule implements AbstractRule<Vote> {

    private static final String DIGITAL_SIGNATURE_VERIFICATION_ERROR_MESSSAGE =
        "Error verifying the digital signature of the Verification Card Public Key";

    @Inject
    private AsymmetricServiceAPI asymmetricService;

    @Inject
    private VerificationSetRepository verificationSetRepository;

    private static final Logger LOG = LoggerFactory.getLogger(VerifyVerificationCardPKSignatureRule.class);

    /**
     * @see com.scytl.products.ov.commons.domain.model.rule.AbstractRule#execute(java.lang.Object)
     */
    @Override
    public ValidationError execute(Vote vote) {
        ValidationError result = new ValidationError();
        try {
            // Obtain certificate
            VerificationSetEntity verificationSetData =
                verificationSetRepository.findByTenantIdElectionEventIdVerificationCardSetId(vote.getTenantId(),
                    vote.getElectionEventId(), vote.getVerificationCardSetId());
            String verificationSetDataJson = verificationSetData.getJSON();
            JsonObject verificationSetDataJsonObject = JsonUtils.getJsonObject(verificationSetDataJson);
            String certificateString = verificationSetDataJsonObject.getString("verificationCardIssuerCert");
            InputStream inputStream = new ByteArrayInputStream(certificateString.getBytes(StandardCharsets.UTF_8));
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            X509Certificate certificate = (X509Certificate) cf.generateCertificate(inputStream);
            PublicKey verificationCardPublicKey = certificate.getPublicKey();

            // verify signature on encrypted vote
            // the signature
            byte[] verificationCardPKSignatureBytes = Base64.decodeBase64(vote.getVerificationCardPKSignature());

            // data to verify
            final byte[] verificationCardPublicKeyAsBytes = Base64.decodeBase64(vote.getVerificationCardPublicKey());
            final byte[] eeidAsBytes = vote.getElectionEventId().getBytes(StandardCharsets.UTF_8);
            final byte[] verificationCardIDAsBytes = vote.getVerificationCardId().getBytes(StandardCharsets.UTF_8);

            // verification
            if (asymmetricService.verifySignature(verificationCardPKSignatureBytes, verificationCardPublicKey,
                verificationCardPublicKeyAsBytes, eeidAsBytes, verificationCardIDAsBytes)) {
                result.setValidationErrorType(ValidationErrorType.SUCCESS);
            } else {
                result.setErrorArgs(new String[] {"Invalid digital signature of the Verification Card Public Key" });
            }
        } catch (CertificateException | GeneralCryptoLibException | ResourceNotFoundException | JsonException
                | IllegalStateException e) {
            LOG.error(DIGITAL_SIGNATURE_VERIFICATION_ERROR_MESSSAGE, e);
            result.setErrorArgs(new String[] {DIGITAL_SIGNATURE_VERIFICATION_ERROR_MESSSAGE });
        }

        return result;
    }

    /**
     * @see com.scytl.products.ov.commons.domain.model.rule.AbstractRule#getName()
     */
    @Override
    public String getName() {
        return RuleNames.VOTE_VERIFICATION_CARD_PUBLIC_KEY_SIGNATURE_VALIDATION.getText();
    }

}
