/*
 * 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
 */
var pbkdfKeyDeriverTest = {

  /**
   * Verifies a PBKDF key derived using Java and checks if the verification
   * was true.
   *
   * @param {String}
   *            derivedKeyB64 the derived key, in Base64 encoded format.
   * @param {String}
   *            passwordB64 the password used to derive the PBKDF key, in
   *            Base64 encoded format.
   * @param {String}
   *            saltB64 the salt used to derive the PBKDF key, in Base64
   *            encoded format.
   * @throws {Error}
   *             if the derived PBKDF key verification operation fails or the
   *             verification is false.
   */
  verify: function(derivedKeyB64, passwordB64, saltB64) {

    'use strict';

    var errorData = {};
    errorData['Derived key expected (Base64 encoded) [Java]'] = derivedKeyB64;
    errorData['Password used to derive key (Base64 encoded) [Java]'] =
        passwordB64;
    errorData['Salt used to derive key (Base64 encoded) [Java]'] = saltB64;

    try {
      cryptolib('primitives.service', function(box) {
        var derivedKeyB64Found =
            box.primitives.service.getPbkdfSecretKeyGenerator().generateSecret(
                passwordB64, saltB64);

        if (derivedKeyB64Found !== derivedKeyB64) {
          errorData['Derived key found (Base64 encoded) [JS]'] =
              derivedKeyB64Found;

          throw 'Expected derived key (Base64 encoded): ' + derivedKeyB64 +
              ' ; Found: ' + derivedKeyB64Found;
        }
      });
    } catch (error) {
      throw testUtils.serializeError(error, errorData);
    }
  },

  /**
   * Derives a PBKDF key that will be verified using Java.
   *
   * @param {String}
   *            passwordB64 the password used to derive the PBKDF key, in
   *            Base64 encoded format.
   * @param {String}
   *            saltB64 the salt used to derive the PBKDF key, in Base64
   *            encoded format.
   * @returns {String} the derived PBKDF key, in Base64 encoded format.
   * @throws {Error}
   *             if the PBKDF key derivation operation fails.
   */
  derive: function(passwordB64, saltB64) {

    'use strict';

    var errorData = {};
    errorData['Password to derive key (Base64 encoded)'] = passwordB64;
    errorData['Salt to derive key (Base64 encoded)'] = saltB64;

    try {
      var derivedKeyB64;
      cryptolib('primitives.service', function(box) {
        derivedKeyB64 =
            box.primitives.service.getPbkdfSecretKeyGenerator().generateSecret(
                passwordB64, saltB64);
      });

      return derivedKeyB64;
    } catch (error) {
      throw testUtils.serializeError(error, errorData);
    }
  },

  /**
   * Verifies, with Forge, a PBKDF key derived using Java and checks if the
   * verification was true.
   *
   * @param {String}
   *            derivedKeyB64 the derived key, in Base64 encoded format.
   * @param {String}
   *            passwordB64 the password used to derive the PBKDF key, in
   *            Base64 encoded format.
   * @param {String}
   *            saltB64 the salt used to derive the PBKDF key, in Base64
   *            encoded format.
   * @throws {Error}
   *             if the derived PBKDF key verification operation fails or the
   *             verification is false.
   */
  verifyWithForge: function(derivedKeyB64, passwordB64, saltB64) {

    'use strict';

    var errorData = {};
    errorData['Derived key expected (Base64 encoded) [Java]'] = derivedKeyB64;
    errorData['Password used to derive key (Base64 encoded) [Java]'] =
        passwordB64;
    errorData['Salt used to derive key (Base64 encoded) [Java]'] = saltB64;

    try {
      var password = _converters.base64Decode(passwordB64);
      var salt = _converters.base64Decode(saltB64);
      var iterations = cryptolibPolicies.primitives.derivation.pbkdf.iterations;
      var keyLengthBytes =
          cryptolibPolicies.primitives.derivation.pbkdf.keyLengthBytes;

      var derivedKey = forge.pkcs5.pbkdf2(
          password, salt, iterations, keyLengthBytes,
          getHashGenerator(
              cryptolibPolicies.primitives.derivation.pbkdf.hash,
              Config.primitives.derivation.pbkdf.hash));

      var derivedKeyB64Found = _converters.base64Encode(derivedKey);

      if (derivedKeyB64Found !== derivedKeyB64) {
        errorData['Forge derived key found (Base64 encoded) [JS]'] =
            derivedKeyB64Found;

        throw 'Expected Forge derived key (Base64 encoded): ' + derivedKeyB64 +
            ' ; Found: ' + derivedKeyB64Found;
      }
    } catch (error) {
      throw testUtils.serializeError(error, errorData);
    }
  },

  /**
   * Derives, with Forge, a PBKDF key that will be verified using Java.
   *
   * @param {String}
   *            passwordB64 the password used to derive the PBKDF key, in
   *            Base64 encoded format.
   * @param {String}
   *            saltB64 the salt used to derive the PBKDF key, in Base64
   *            encoded format.
   * @returns {String} the derived PBKDF key, in Base64 encoded format.
   * @throws {Error}
   *             if the PBKDF key derivation operation fails.
   */
  deriveWithForge: function(passwordB64, saltB64) {

    'use strict';

    var errorData = {};
    errorData['Password to derive key (Base64 encoded)'] = passwordB64;
    errorData['Salt to derive key (Base64 encoded)'] = saltB64;

    try {
      var password = _converters.base64Decode(passwordB64);
      var salt = _converters.base64Decode(saltB64);
      var iterations = cryptolibPolicies.primitives.derivation.pbkdf.iterations;
      var keyLengthBytes =
          cryptolibPolicies.primitives.derivation.pbkdf.keyLengthBytes;

      var derivedKey = forge.pkcs5.pbkdf2(
          password, salt, iterations, keyLengthBytes,
          getHashGenerator(
              cryptolibPolicies.primitives.derivation.pbkdf.hash,
              Config.primitives.derivation.pbkdf.hash));

      return _converters.base64Encode(derivedKey);
    } catch (error) {
      throw testUtils.serializeError(error, errorData);
    }
  },
};

function getHashGenerator(algorithm, config) {
  if (algorithm === config.SHA256) {
    return forge.sha256.create();
  } else {
    throw 'Hash algorithm \'' + algorithm + '\' is unsupported';
  }
}