/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

let exports = this;

const scripts = [
  "pkijs/common.js",
  "pkijs/asn1.js",
  "pkijs/x509_schema.js",
  "pkijs/x509_simpl.js",
  "browser/cbor.js",
  "browser/u2futil.js",
];

for (let script of scripts) {
  Services.scriptloader.loadSubScript(
    `chrome://mochitests/content/browser/dom/webauthn/tests/${script}`,
    this
  );
}

function memcmp(x, y) {
  let xb = new Uint8Array(x);
  let yb = new Uint8Array(y);

  if (x.byteLength != y.byteLength) {
    return false;
  }

  for (let i = 0; i < xb.byteLength; ++i) {
    if (xb[i] != yb[i]) {
      return false;
    }
  }

  return true;
}

function arrivingHereIsBad(aResult) {
  ok(false, "Bad result! Received a: " + aResult);
}

function expectError(aType) {
  let expected = `${aType}Error`;
  return function(aResult) {
    is(
      aResult.slice(0, expected.length),
      expected,
      `Expecting a ${aType}Error`
    );
  };
}

/* eslint-disable no-shadow */
function promiseWebAuthnMakeCredential(
  tab,
  attestation = "none",
  extensions = {}
) {
  return ContentTask.spawn(
    tab.linkedBrowser,
    [attestation, extensions],
    ([attestation, extensions]) => {
      const cose_alg_ECDSA_w_SHA256 = -7;

      let challenge = content.crypto.getRandomValues(new Uint8Array(16));

      let pubKeyCredParams = [
        {
          type: "public-key",
          alg: cose_alg_ECDSA_w_SHA256,
        },
      ];

      let publicKey = {
        rp: { id: content.document.domain, name: "none", icon: "none" },
        user: {
          id: new Uint8Array(),
          name: "none",
          icon: "none",
          displayName: "none",
        },
        pubKeyCredParams,
        extensions,
        attestation,
        challenge,
      };

      return content.navigator.credentials
        .create({ publicKey })
        .then(credential => {
          return {
            attObj: credential.response.attestationObject,
            rawId: credential.rawId,
          };
        });
    }
  );
}

function promiseWebAuthnGetAssertion(tab, key_handle = null, extensions = {}) {
  return ContentTask.spawn(
    tab.linkedBrowser,
    [key_handle, extensions],
    ([key_handle, extensions]) => {
      let challenge = content.crypto.getRandomValues(new Uint8Array(16));
      if (key_handle == null) {
        key_handle = content.crypto.getRandomValues(new Uint8Array(16));
      }

      let credential = {
        id: key_handle,
        type: "public-key",
        transports: ["usb"],
      };

      let publicKey = {
        challenge,
        extensions,
        rpId: content.document.domain,
        allowCredentials: [credential],
      };

      return content.navigator.credentials
        .get({ publicKey })
        .then(assertion => {
          return {
            authenticatorData: assertion.response.authenticatorData,
            clientDataJSON: assertion.response.clientDataJSON,
            extensions: assertion.getClientExtensionResults(),
            signature: assertion.response.signature,
          };
        });
    }
  );
}

function checkRpIdHash(rpIdHash, hostname) {
  return crypto.subtle
    .digest("SHA-256", string2buffer(hostname))
    .then(calculatedRpIdHash => {
      let calcHashStr = bytesToBase64UrlSafe(
        new Uint8Array(calculatedRpIdHash)
      );
      let providedHashStr = bytesToBase64UrlSafe(new Uint8Array(rpIdHash));

      if (calcHashStr != providedHashStr) {
        throw new Error("Calculated RP ID hash doesn't match.");
      }
    });
}
/* eslint-enable no-shadow */
