/*
 * 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.service.confirmation;

import com.scytl.cryptolib.api.asymmetric.AsymmetricServiceAPI;
import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.products.ov.commons.beans.domain.model.authentication.AuthenticationToken;
import com.scytl.products.ov.commons.crypto.InputDataFormatterService;
import com.scytl.products.ov.commons.logging.I18nLoggerMessages;
import com.scytl.products.ov.commons.validation.ValidationError;
import com.scytl.products.ov.commons.beans.validation.ValidationErrorType;
import com.scytl.products.ov.ei.services.domain.model.confirmation.ConfirmationInformation;
import com.scytl.products.ov.ei.services.domain.model.confirmation.ConfirmationMessageValidation;
import org.bouncycastle.util.encoders.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
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;

/**
 * This class implements the signature validation of the confirmation message.
 */
public class ConfirmationMessageSignatureValidation implements ConfirmationMessageValidation {

	private static final Logger LOG = LoggerFactory.getLogger(ConfirmationMessageSignatureValidation.class);
	
	private static final I18nLoggerMessages I18N = I18nLoggerMessages.getInstance();

	@Inject
	private AsymmetricServiceAPI asymmetricService;

	@Inject
	private InputDataFormatterService inputDataFormatterService;

	/**
	 * This method implements the validation of signature.
	 *
	 * @param tenantId - the tenant identifier.
	 * @param electionEventId - the election event identifier.
	 * @param votingCardId - the voting card identifier.
	 * @param confirmationInformation - the confirmation information to be validated.
	 * @param authenticationToken - the authentication token.
	 * @return A ValidationError describing if the rule is satisfied or not.
	 */
	@Override
	public ValidationError execute(String tenantId, String electionEventId, String votingCardId,
			ConfirmationInformation confirmationInformation, AuthenticationToken authenticationToken) {
		ValidationError result = new ValidationError();
		try {
			// get public key
			String certificateString = confirmationInformation.getCertificate();

			// Obtain certificate
			InputStream inputStream = new ByteArrayInputStream(certificateString.getBytes(StandardCharsets.UTF_8));
			CertificateFactory cf = CertificateFactory.getInstance("X.509");
			X509Certificate certificate = (X509Certificate) cf.generateCertificate(inputStream);
			PublicKey publicKey = certificate.getPublicKey();

			// verify signature
			byte[] confirmationMessageSignatureBytes =
				Base64.decode(confirmationInformation.getConfirmationMessage().getSignature());

			String authTokenSignature = authenticationToken.getSignature();
			byte[] confirmationMessageDataToVerify =
				inputDataFormatterService.concatenate(confirmationInformation.getConfirmationMessage().getConfirmationKey(),
					authTokenSignature, votingCardId, electionEventId);

			if (asymmetricService.verifySignature(confirmationMessageSignatureBytes, publicKey,
				confirmationMessageDataToVerify)) {
				result.setValidationErrorType(ValidationErrorType.SUCCESS);
			}
		} catch (GeneralCryptoLibException | CertificateException e) {
			LOG.error(I18N.getMessage("AuthenticationTokenSignatureValidation.execute.errorValidatingSignature"), tenantId,
				electionEventId, votingCardId, e);
		}
		return result;
	}
}
