/*
 * 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.slogger.validator.signature;

import com.scytl.slogger.crypto.SecureLoggerCryptographyProvider;
import com.scytl.slogger.event.SecureLoggingEvent;
import com.scytl.slogger.event.SecureMessageProperties;
import com.scytl.slogger.event.SecurePatternLayout;
import com.scytl.slogger.validator.EventValidatorException;
import com.scytl.slogger.validator.LoggingEventValidator;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.PublicKey;
import java.util.Base64;

/**
 * Validator that verifies signed log entries.
 */
public class SignatureEventValidator implements LoggingEventValidator {

  private static final String SIG_VERIFICATION_FAILED = "The Signature verification has failed: ";

  private final PublicKey publicKey;

  private final SecureLoggerCryptographyProvider cryptographyProvider;

  /**
   * Constructor.
   *
   * @param publicKey the public key
   * @param cryptographyProvider the cryptography provider
   */
  public SignatureEventValidator(final PublicKey publicKey,
      final SecureLoggerCryptographyProvider cryptographyProvider) {
    this.cryptographyProvider = cryptographyProvider;
    this.publicKey = publicKey;
  }

  @Override
  public void stopValidation(final SecurePatternLayout layout) throws EventValidatorException {}

  @Override
  public void validateEvent(final SecureLoggingEvent event, final SecurePatternLayout layout)
      throws EventValidatorException {
    
    String signature = event.getSecureMessage().properties().get(SecureMessageProperties.SIGNATURE);
    try {
      if (signature != null) {

        SecureLoggingEvent wle = event.removeProperty(SecureMessageProperties.SIGNATURE);

        String eventFormated = layout.format(wle);
        boolean signatureVerification = verifyEventSignature(
            Base64.getDecoder().decode(signature),
            eventFormated);

        if (!signatureVerification) {
          throw new EventValidatorException(SIG_VERIFICATION_FAILED, event, layout);
        }
      }
    } catch (GeneralSecurityException | IOException e) {
      throw new EventValidatorException(e);
    }
  }

  private boolean verifyEventSignature(final byte[] signature, final String signedData)
      throws GeneralSecurityException, IOException {
    byte[] data = signedData.getBytes(StandardCharsets.UTF_8);
    return cryptographyProvider.verify(data, signature, publicKey);
  }
}
