package utils;

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import java.util.Random;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;

/**
 * Clase encargada de crear y comparar contraseñas de forma segura
 */
public class Passwords {

  private static final Random RANDOM = new SecureRandom();
  private static final int ITERATIONS = 10000;
  private static final int KEY_LENGHT = 256;

  private Passwords() {
  }

  /**
   * Genera aleatoriamente un <code>byte[]</code> de tamaño 16
   *
   * @return El <code>byte[]</code> generados
   */
  public static byte[] getNextSalt() {
    byte[] salt = new byte[16];
    RANDOM.nextBytes(salt);
    return salt;
  }

  /**
   * Compara 2 constraseñas, la primera sin hashear aun y la segunda ya hasheada
   *
   * @param password <code>char[]</code> que contiene la contraseña sin hashear aun
   * @param salt <code>byte[]</code> que contiene la sal con la que juntar la contraseña al momento de hashearla
   * @param expectedHash <code>byte[]</code> con una contraseña ya hasheada con la que comprar
   * @return un boleano indicando que las constraseñas son iguales o no
   */
  public static boolean isExpectedPassword(char[] password, byte[] salt, byte[] expectedHash) {
    byte[] pwdHash = hash(password, salt);
    Arrays.fill(password, Character.MIN_VALUE);
    if (pwdHash.length != expectedHash.length)
      return false;
    for (int i = 0; i < pwdHash.length; i++) {
      if (pwdHash[i] != expectedHash[i])
        return false;
    }
    return true;
  }

  /**
   * Hashea una contraseña.
   *
   * @param password <code>char[]</code> con la contraseña a hashear
   * @param salt <code>byte[]</code> con la salt que se le aplicara a la contraseña
   * @return <code>byte[]</code> con la constraseña hasheada
   */
  public static byte[] hash(char[] password, byte[] salt) {
    PBEKeySpec spec = new PBEKeySpec(password, salt, ITERATIONS, KEY_LENGHT);
    Arrays.fill(password, Character.MIN_VALUE);
    try {
      SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
      return skf.generateSecret(spec).getEncoded();
    } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
      throw new AssertionError("Error while hashing a password: " + e.getMessage(), e);
    } finally {
      spec.clearPassword();
    }
  }
}
