From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001
From: Daniel Baumann <daniel.baumann@progress-linux.org>
Date: Fri, 19 Apr 2024 02:47:55 +0200
Subject: Adding upstream version 124.0.1.

Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
---
 dom/webauthn/tests/browser/browser.toml            |   35 +
 .../tests/browser/browser_abort_visibility.js      |  277 +
 .../tests/browser/browser_fido_appid_extension.js  |  131 +
 .../browser_webauthn_conditional_mediation.js      |  177 +
 .../tests/browser/browser_webauthn_ipaddress.js    |   30 +
 .../tests/browser/browser_webauthn_prompts.js      |  501 ++
 dom/webauthn/tests/browser/head.js                 |  259 +
 .../tests/browser/tab_webauthn_result.html         |   14 +
 dom/webauthn/tests/cbor.js                         |  406 ++
 dom/webauthn/tests/empty.html                      |    0
 dom/webauthn/tests/get_assertion_dead_object.html  |   21 +
 dom/webauthn/tests/mochitest.toml                  |  118 +
 dom/webauthn/tests/pkijs/LICENSE                   |   30 +
 dom/webauthn/tests/pkijs/README                    |    1 +
 dom/webauthn/tests/pkijs/asn1.js                   | 5466 +++++++++++++++
 dom/webauthn/tests/pkijs/common.js                 | 1542 +++++
 dom/webauthn/tests/pkijs/x509_schema.js            | 1889 +++++
 dom/webauthn/tests/pkijs/x509_simpl.js             | 7239 ++++++++++++++++++++
 dom/webauthn/tests/test_webauthn_abort_signal.html |  146 +
 .../test_webauthn_attestation_conveyance.html      |  103 +
 .../test_webauthn_authenticator_selection.html     |  152 +
 .../test_webauthn_authenticator_transports.html    |  165 +
 .../test_webauthn_crossorigin_featurepolicy.html   |  258 +
 .../test_webauthn_ctap2_omitted_credential_id.html |   68 +
 .../tests/test_webauthn_get_assertion.html         |  212 +
 .../test_webauthn_get_assertion_dead_object.html   |   34 +
 ..._webauthn_isplatformauthenticatoravailable.html |   48 +
 dom/webauthn/tests/test_webauthn_loopback.html     |  179 +
 .../tests/test_webauthn_make_credential.html       |  459 ++
 dom/webauthn/tests/test_webauthn_no_token.html     |   87 +
 dom/webauthn/tests/test_webauthn_sameorigin.html   |  317 +
 .../test_webauthn_sameoriginwithancestors.html     |  108 +
 .../tests/test_webauthn_serialization.html         |  304 +
 .../tests/test_webauthn_store_credential.html      |   58 +
 ...t_webauthn_webdriver_virtual_authenticator.html |   58 +
 dom/webauthn/tests/u2futil.js                      |  511 ++
 36 files changed, 21403 insertions(+)
 create mode 100644 dom/webauthn/tests/browser/browser.toml
 create mode 100644 dom/webauthn/tests/browser/browser_abort_visibility.js
 create mode 100644 dom/webauthn/tests/browser/browser_fido_appid_extension.js
 create mode 100644 dom/webauthn/tests/browser/browser_webauthn_conditional_mediation.js
 create mode 100644 dom/webauthn/tests/browser/browser_webauthn_ipaddress.js
 create mode 100644 dom/webauthn/tests/browser/browser_webauthn_prompts.js
 create mode 100644 dom/webauthn/tests/browser/head.js
 create mode 100644 dom/webauthn/tests/browser/tab_webauthn_result.html
 create mode 100644 dom/webauthn/tests/cbor.js
 create mode 100644 dom/webauthn/tests/empty.html
 create mode 100644 dom/webauthn/tests/get_assertion_dead_object.html
 create mode 100644 dom/webauthn/tests/mochitest.toml
 create mode 100644 dom/webauthn/tests/pkijs/LICENSE
 create mode 100644 dom/webauthn/tests/pkijs/README
 create mode 100644 dom/webauthn/tests/pkijs/asn1.js
 create mode 100644 dom/webauthn/tests/pkijs/common.js
 create mode 100644 dom/webauthn/tests/pkijs/x509_schema.js
 create mode 100644 dom/webauthn/tests/pkijs/x509_simpl.js
 create mode 100644 dom/webauthn/tests/test_webauthn_abort_signal.html
 create mode 100644 dom/webauthn/tests/test_webauthn_attestation_conveyance.html
 create mode 100644 dom/webauthn/tests/test_webauthn_authenticator_selection.html
 create mode 100644 dom/webauthn/tests/test_webauthn_authenticator_transports.html
 create mode 100644 dom/webauthn/tests/test_webauthn_crossorigin_featurepolicy.html
 create mode 100644 dom/webauthn/tests/test_webauthn_ctap2_omitted_credential_id.html
 create mode 100644 dom/webauthn/tests/test_webauthn_get_assertion.html
 create mode 100644 dom/webauthn/tests/test_webauthn_get_assertion_dead_object.html
 create mode 100644 dom/webauthn/tests/test_webauthn_isplatformauthenticatoravailable.html
 create mode 100644 dom/webauthn/tests/test_webauthn_loopback.html
 create mode 100644 dom/webauthn/tests/test_webauthn_make_credential.html
 create mode 100644 dom/webauthn/tests/test_webauthn_no_token.html
 create mode 100644 dom/webauthn/tests/test_webauthn_sameorigin.html
 create mode 100644 dom/webauthn/tests/test_webauthn_sameoriginwithancestors.html
 create mode 100644 dom/webauthn/tests/test_webauthn_serialization.html
 create mode 100644 dom/webauthn/tests/test_webauthn_store_credential.html
 create mode 100644 dom/webauthn/tests/test_webauthn_webdriver_virtual_authenticator.html
 create mode 100644 dom/webauthn/tests/u2futil.js

(limited to 'dom/webauthn/tests')

diff --git a/dom/webauthn/tests/browser/browser.toml b/dom/webauthn/tests/browser/browser.toml
new file mode 100644
index 0000000000..41dc82d6ee
--- /dev/null
+++ b/dom/webauthn/tests/browser/browser.toml
@@ -0,0 +1,35 @@
+[DEFAULT]
+support-files = [
+  "head.js",
+  "tab_webauthn_result.html",
+  "../pkijs/*",
+  "../cbor.js",
+  "../u2futil.js",
+]
+prefs = [
+ "security.webauth.webauthn=true",
+ "security.webauth.webauthn_enable_softtoken=true",
+ "security.webauth.webauthn_enable_usbtoken=false",
+ "security.webauthn.ctap2=true",
+ "security.webauthn.enable_conditional_mediation=true",
+ "security.webauthn.enable_macos_passkeys=false",
+]
+
+["browser_abort_visibility.js"]
+skip-if = [
+  "win11_2009", # Test not relevant on 1903+
+]
+
+["browser_fido_appid_extension.js"]
+skip-if = [
+  "win11_2009", # Test not relevant on 1903+
+]
+
+["browser_webauthn_conditional_mediation.js"]
+
+["browser_webauthn_ipaddress.js"]
+
+["browser_webauthn_prompts.js"]
+skip-if = [
+  "win11_2009", # Test not relevant on 1903+
+]
diff --git a/dom/webauthn/tests/browser/browser_abort_visibility.js b/dom/webauthn/tests/browser/browser_abort_visibility.js
new file mode 100644
index 0000000000..d8b2b5edcc
--- /dev/null
+++ b/dom/webauthn/tests/browser/browser_abort_visibility.js
@@ -0,0 +1,277 @@
+/* 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";
+
+const TEST_URL =
+  "https://example.com/browser/dom/webauthn/tests/browser/tab_webauthn_result.html";
+
+add_task(async function test_setup() {
+  return SpecialPowers.pushPrefEnv({
+    set: [
+      ["security.webauth.webauthn_enable_softtoken", false],
+      ["security.webauth.webauthn_enable_usbtoken", true],
+    ],
+  });
+});
+add_task(test_switch_tab);
+add_task(test_new_window_make);
+add_task(test_new_window_get);
+add_task(test_minimize_make);
+add_task(test_minimize_get);
+
+async function assertStatus(tab, expected) {
+  let actual = await SpecialPowers.spawn(
+    tab.linkedBrowser,
+    [],
+    async function () {
+      info("visbility state: " + content.document.visibilityState);
+      info("active: " + content.browsingContext.isActive);
+      return content.document.getElementById("status").value;
+    }
+  );
+  is(actual, expected, "webauthn request " + expected);
+}
+
+async function waitForStatus(tab, expected) {
+  /* eslint-disable no-shadow */
+  await SpecialPowers.spawn(
+    tab.linkedBrowser,
+    [[expected]],
+    async function (expected) {
+      return ContentTaskUtils.waitForCondition(() => {
+        info(
+          "expecting " +
+            expected +
+            ", visbility state: " +
+            content.document.visibilityState
+        );
+        info(
+          "expecting " +
+            expected +
+            ", active: " +
+            content.browsingContext.isActive
+        );
+        return content.document.getElementById("status").value == expected;
+      });
+    }
+  );
+  /* eslint-enable no-shadow */
+
+  await assertStatus(tab, expected);
+}
+
+function startMakeCredentialRequest(tab) {
+  return SpecialPowers.spawn(tab.linkedBrowser, [], async function () {
+    const cose_alg_ECDSA_w_SHA256 = -7;
+
+    let publicKey = {
+      rp: { id: content.document.domain, name: "none" },
+      user: {
+        id: new Uint8Array(),
+        name: "none",
+        displayName: "none",
+      },
+      challenge: content.crypto.getRandomValues(new Uint8Array(16)),
+      timeout: 5000, // the minimum timeout is actually 15 seconds
+      pubKeyCredParams: [{ type: "public-key", alg: cose_alg_ECDSA_w_SHA256 }],
+    };
+
+    let status = content.document.getElementById("status");
+
+    info(
+      "Attempting to create credential for origin: " +
+        content.document.nodePrincipal.origin
+    );
+    content.navigator.credentials
+      .create({ publicKey })
+      .then(() => {
+        status.value = "completed";
+      })
+      .catch(() => {
+        status.value = "aborted";
+      });
+
+    status.value = "pending";
+  });
+}
+
+function startGetAssertionRequest(tab) {
+  return SpecialPowers.spawn(tab.linkedBrowser, [], async function () {
+    let newCredential = {
+      type: "public-key",
+      id: content.crypto.getRandomValues(new Uint8Array(16)),
+      transports: ["usb"],
+    };
+
+    let publicKey = {
+      challenge: content.crypto.getRandomValues(new Uint8Array(16)),
+      timeout: 5000, // the minimum timeout is actually 15 seconds
+      rpId: content.document.domain,
+      allowCredentials: [newCredential],
+    };
+
+    let status = content.document.getElementById("status");
+
+    info(
+      "Attempting to get credential for origin: " +
+        content.document.nodePrincipal.origin
+    );
+    content.navigator.credentials
+      .get({ publicKey })
+      .then(() => {
+        status.value = "completed";
+      })
+      .catch(ex => {
+        info("aborted: " + ex);
+        status.value = "aborted";
+      });
+
+    status.value = "pending";
+  });
+}
+
+// Test that MakeCredential() and GetAssertion() requests
+// are aborted when the current tab loses its focus.
+async function test_switch_tab() {
+  // Create a new tab for the MakeCredential() request.
+  let tab_create = await BrowserTestUtils.openNewForegroundTab(
+    gBrowser,
+    TEST_URL
+  );
+
+  // Start the request.
+  await startMakeCredentialRequest(tab_create);
+  await assertStatus(tab_create, "pending");
+
+  // Open another tab and switch to it. The first will lose focus.
+  let tab_get = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+  await assertStatus(tab_create, "pending");
+
+  // Start a GetAssertion() request in the second tab, the first is aborted
+  await startGetAssertionRequest(tab_get);
+  await waitForStatus(tab_create, "aborted");
+  await assertStatus(tab_get, "pending");
+
+  // Start a second request in the second tab. It should abort.
+  await startGetAssertionRequest(tab_get);
+  await waitForStatus(tab_get, "aborted");
+
+  // Close tabs.
+  BrowserTestUtils.removeTab(tab_create);
+  BrowserTestUtils.removeTab(tab_get);
+}
+
+function waitForWindowActive(win, active) {
+  return Promise.all([
+    BrowserTestUtils.waitForEvent(win, active ? "focus" : "blur"),
+    BrowserTestUtils.waitForEvent(win, active ? "activate" : "deactivate"),
+  ]);
+}
+
+async function test_new_window_make() {
+  // Create a new tab for the MakeCredential() request.
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+
+  // Start a MakeCredential request.
+  await startMakeCredentialRequest(tab);
+  await assertStatus(tab, "pending");
+
+  let windowGonePromise = waitForWindowActive(window, false);
+  // Open a new window. The tab will lose focus.
+  let win = await BrowserTestUtils.openNewBrowserWindow();
+  await windowGonePromise;
+  await assertStatus(tab, "pending");
+
+  let windowBackPromise = waitForWindowActive(window, true);
+  await BrowserTestUtils.closeWindow(win);
+  await windowBackPromise;
+
+  // Close tab.
+  await BrowserTestUtils.removeTab(tab);
+}
+
+async function test_new_window_get() {
+  // Create a new tab for the GetAssertion() request.
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+
+  // Start a GetAssertion request.
+  await startGetAssertionRequest(tab);
+  await assertStatus(tab, "pending");
+
+  let windowGonePromise = waitForWindowActive(window, false);
+  // Open a new window. The tab will lose focus.
+  let win = await BrowserTestUtils.openNewBrowserWindow();
+  await windowGonePromise;
+  await assertStatus(tab, "pending");
+
+  let windowBackPromise = waitForWindowActive(window, true);
+  await BrowserTestUtils.closeWindow(win);
+  await windowBackPromise;
+
+  // Close tab.
+  BrowserTestUtils.removeTab(tab);
+}
+
+async function test_minimize_make() {
+  // Minimizing windows doesn't supported in headless mode.
+  if (Services.env.get("MOZ_HEADLESS")) {
+    return;
+  }
+
+  // Create a new window for the MakeCredential() request.
+  let win = await BrowserTestUtils.openNewBrowserWindow();
+  let tab = await BrowserTestUtils.openNewForegroundTab(win.gBrowser, TEST_URL);
+
+  // Start a MakeCredential request.
+  await startMakeCredentialRequest(tab);
+  await assertStatus(tab, "pending");
+
+  // Minimize the window.
+  let windowGonePromise = waitForWindowActive(win, false);
+  win.minimize();
+  await assertStatus(tab, "pending");
+  await windowGonePromise;
+
+  // Restore the window.
+  await new Promise(resolve => SimpleTest.waitForFocus(resolve, win));
+  await assertStatus(tab, "pending");
+
+  // Close window and wait for main window to be focused again.
+  let windowBackPromise = waitForWindowActive(window, true);
+  await BrowserTestUtils.removeTab(tab);
+  await BrowserTestUtils.closeWindow(win);
+  await windowBackPromise;
+}
+
+async function test_minimize_get() {
+  // Minimizing windows doesn't supported in headless mode.
+  if (Services.env.get("MOZ_HEADLESS")) {
+    return;
+  }
+
+  // Create a new window for the GetAssertion() request.
+  let win = await BrowserTestUtils.openNewBrowserWindow();
+  let tab = await BrowserTestUtils.openNewForegroundTab(win.gBrowser, TEST_URL);
+
+  // Start a GetAssertion request.
+  await startGetAssertionRequest(tab);
+  await assertStatus(tab, "pending");
+
+  // Minimize the window.
+  let windowGonePromise = waitForWindowActive(win, false);
+  win.minimize();
+  await assertStatus(tab, "pending");
+  await windowGonePromise;
+
+  // Restore the window.
+  await new Promise(resolve => SimpleTest.waitForFocus(resolve, win));
+  await assertStatus(tab, "pending");
+
+  // Close window and wait for main window to be focused again.
+  let windowBackPromise = waitForWindowActive(window, true);
+  await BrowserTestUtils.removeTab(tab);
+  await BrowserTestUtils.closeWindow(win);
+  await windowBackPromise;
+}
diff --git a/dom/webauthn/tests/browser/browser_fido_appid_extension.js b/dom/webauthn/tests/browser/browser_fido_appid_extension.js
new file mode 100644
index 0000000000..fca4443074
--- /dev/null
+++ b/dom/webauthn/tests/browser/browser_fido_appid_extension.js
@@ -0,0 +1,131 @@
+/* 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";
+
+const TEST_URL = "https://example.com/";
+
+let expectNotSupportedError = expectError("NotSupported");
+let expectNotAllowedError = expectError("NotAllowed");
+let expectSecurityError = expectError("Security");
+
+let gAppId = "https://example.com/appId";
+let gCrossOriginAppId = "https://example.org/appId";
+let gAuthenticatorId = add_virtual_authenticator();
+
+add_task(async function test_appid() {
+  // Open a new tab.
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+
+  // The FIDO AppId extension can't be used for MakeCredential.
+  await promiseWebAuthnMakeCredential(tab, "none", "discouraged", {
+    appid: gAppId,
+  })
+    .then(arrivingHereIsBad)
+    .catch(expectNotSupportedError);
+
+  // Side-load a credential with an RP ID matching the App ID.
+  let credIdB64 = await addCredential(gAuthenticatorId, gAppId);
+  let credId = base64ToBytesUrlSafe(credIdB64);
+
+  // And another for a different origin
+  let crossOriginCredIdB64 = await addCredential(
+    gAuthenticatorId,
+    gCrossOriginAppId
+  );
+  let crossOriginCredId = base64ToBytesUrlSafe(crossOriginCredIdB64);
+
+  // The App ID extension is required
+  await promiseWebAuthnGetAssertion(tab, credId)
+    .then(arrivingHereIsBad)
+    .catch(expectNotAllowedError);
+
+  // The value in the App ID extension must match the origin.
+  await promiseWebAuthnGetAssertion(tab, crossOriginCredId, {
+    appid: gCrossOriginAppId,
+  })
+    .then(arrivingHereIsBad)
+    .catch(expectSecurityError);
+
+  // The value in the App ID extension must match the credential's RP ID.
+  await promiseWebAuthnGetAssertion(tab, credId, { appid: gAppId + "2" })
+    .then(arrivingHereIsBad)
+    .catch(expectNotAllowedError);
+
+  // Succeed with the right App ID.
+  let rpIdHash = await promiseWebAuthnGetAssertion(tab, credId, {
+    appid: gAppId,
+  })
+    .then(({ authenticatorData, extensions }) => {
+      is(extensions.appid, true, "appid extension was acted upon");
+      return authenticatorData.slice(0, 32);
+    })
+    .then(rpIdHash => {
+      // Make sure the returned RP ID hash matches the hash of the App ID.
+      checkRpIdHash(rpIdHash, gAppId);
+    })
+    .catch(arrivingHereIsBad);
+
+  removeCredential(gAuthenticatorId, credIdB64);
+  removeCredential(gAuthenticatorId, crossOriginCredIdB64);
+
+  // Close tab.
+  BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function test_appid_unused() {
+  // Open a new tab.
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+
+  let appid = "https://example.com/appId";
+
+  let { attObj, rawId } = await promiseWebAuthnMakeCredential(tab);
+  let { authDataObj } = await webAuthnDecodeCBORAttestation(attObj);
+
+  // Make sure the RP ID hash matches what we calculate.
+  await checkRpIdHash(authDataObj.rpIdHash, "example.com");
+
+  // Get a new assertion.
+  let { clientDataJSON, authenticatorData, signature, extensions } =
+    await promiseWebAuthnGetAssertion(tab, rawId, { appid });
+
+  ok(
+    "appid" in extensions,
+    `appid should be populated in the extensions data, but saw: ` +
+      `${JSON.stringify(extensions)}`
+  );
+  is(extensions.appid, false, "appid extension should indicate it was unused");
+
+  // Check auth data.
+  let attestation = await webAuthnDecodeAuthDataArray(
+    new Uint8Array(authenticatorData)
+  );
+  is(
+    "" + (attestation.flags & flag_TUP),
+    "" + flag_TUP,
+    "Assertion's user presence byte set correctly"
+  );
+
+  // Verify the signature.
+  let params = await deriveAppAndChallengeParam(
+    "example.com",
+    clientDataJSON,
+    attestation
+  );
+  let signedData = await assembleSignedData(
+    params.appParam,
+    params.attestation.flags,
+    params.attestation.counter,
+    params.challengeParam
+  );
+  let valid = await verifySignature(
+    authDataObj.publicKeyHandle,
+    signedData,
+    signature
+  );
+  ok(valid, "signature is valid");
+
+  // Close tab.
+  BrowserTestUtils.removeTab(tab);
+});
diff --git a/dom/webauthn/tests/browser/browser_webauthn_conditional_mediation.js b/dom/webauthn/tests/browser/browser_webauthn_conditional_mediation.js
new file mode 100644
index 0000000000..fff1ec5dab
--- /dev/null
+++ b/dom/webauthn/tests/browser/browser_webauthn_conditional_mediation.js
@@ -0,0 +1,177 @@
+/* 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";
+
+const TEST_URL = "https://example.com";
+
+let gAuthenticatorId = add_virtual_authenticator();
+let gExpectNotAllowedError = expectError("NotAllowed");
+let gExpectAbortError = expectError("Abort");
+let gPendingConditionalGetSubject = "webauthn:conditional-get-pending";
+let gWebAuthnService = Cc["@mozilla.org/webauthn/service;1"].getService(
+  Ci.nsIWebAuthnService
+);
+
+add_task(async function test_webauthn_modal_request_cancels_conditional_get() {
+  // Open a new tab.
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+
+  let browser = tab.linkedBrowser.browsingContext.embedderElement;
+  let browsingContextId = browser.browsingContext.id;
+
+  let transactionId = gWebAuthnService.hasPendingConditionalGet(
+    browsingContextId,
+    TEST_URL
+  );
+  Assert.equal(transactionId, 0, "should not have a pending conditional get");
+
+  let requestStarted = TestUtils.topicObserved(gPendingConditionalGetSubject);
+
+  let active = true;
+  let condPromise = promiseWebAuthnGetAssertionDiscoverable(tab, "conditional")
+    .then(arrivingHereIsBad)
+    .catch(gExpectAbortError)
+    .then(() => (active = false));
+
+  await requestStarted;
+
+  transactionId = gWebAuthnService.hasPendingConditionalGet(
+    browsingContextId,
+    TEST_URL
+  );
+  Assert.notEqual(transactionId, 0, "should have a pending conditional get");
+
+  ok(active, "conditional request should still be active");
+
+  let promptPromise = promiseNotification("webauthn-prompt-register-direct");
+  let modalPromise = promiseWebAuthnMakeCredential(tab, "direct")
+    .then(arrivingHereIsBad)
+    .catch(gExpectNotAllowedError);
+
+  await condPromise;
+
+  ok(!active, "conditional request should not be active");
+
+  // Cancel the modal request with the button.
+  await promptPromise;
+  PopupNotifications.panel.firstElementChild.secondaryButton.click();
+  await modalPromise;
+
+  // Close tab.
+  await BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function test_webauthn_resume_conditional_get() {
+  // Open a new tab.
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+
+  let browser = tab.linkedBrowser.browsingContext.embedderElement;
+  let browsingContextId = browser.browsingContext.id;
+
+  let transactionId = gWebAuthnService.hasPendingConditionalGet(
+    browsingContextId,
+    TEST_URL
+  );
+  Assert.equal(transactionId, 0, "should not have a pending conditional get");
+
+  let requestStarted = TestUtils.topicObserved(gPendingConditionalGetSubject);
+
+  let active = true;
+  let promise = promiseWebAuthnGetAssertionDiscoverable(tab, "conditional")
+    .then(arrivingHereIsBad)
+    .catch(gExpectNotAllowedError)
+    .then(() => (active = false));
+
+  await requestStarted;
+
+  transactionId = gWebAuthnService.hasPendingConditionalGet(0, TEST_URL);
+  Assert.equal(
+    transactionId,
+    0,
+    "hasPendingConditionalGet should check the browsing context id"
+  );
+
+  transactionId = gWebAuthnService.hasPendingConditionalGet(
+    browsingContextId,
+    "https://example.org"
+  );
+  Assert.equal(
+    transactionId,
+    0,
+    "hasPendingConditionalGet should check the origin"
+  );
+
+  transactionId = gWebAuthnService.hasPendingConditionalGet(
+    browsingContextId,
+    TEST_URL
+  );
+  Assert.notEqual(transactionId, 0, "should have a pending conditional get");
+
+  ok(active, "request should still be active");
+
+  gWebAuthnService.resumeConditionalGet(transactionId);
+  await promise;
+
+  ok(!active, "request should not be active");
+
+  // Close tab.
+  await BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function test_webauthn_select_autofill_entry() {
+  // Open a new tab.
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+
+  // Add credentials
+  let cred1 = await addCredential(gAuthenticatorId, "example.com");
+  let cred2 = await addCredential(gAuthenticatorId, "example.com");
+
+  let browser = tab.linkedBrowser.browsingContext.embedderElement;
+  let browsingContextId = browser.browsingContext.id;
+
+  let transactionId = gWebAuthnService.hasPendingConditionalGet(
+    browsingContextId,
+    TEST_URL
+  );
+  Assert.equal(transactionId, 0, "should not have a pending conditional get");
+
+  let requestStarted = TestUtils.topicObserved(gPendingConditionalGetSubject);
+
+  let active = true;
+  let promise = promiseWebAuthnGetAssertionDiscoverable(tab, "conditional")
+    .catch(arrivingHereIsBad)
+    .then(() => (active = false));
+
+  await requestStarted;
+
+  transactionId = gWebAuthnService.hasPendingConditionalGet(
+    browsingContextId,
+    TEST_URL
+  );
+  Assert.notEqual(transactionId, 0, "should have a pending conditional get");
+
+  let autoFillEntries = gWebAuthnService.getAutoFillEntries(transactionId);
+  ok(
+    autoFillEntries.length == 2 &&
+      autoFillEntries[0].rpId == "example.com" &&
+      autoFillEntries[1].rpId == "example.com",
+    "should have two autofill entries for example.com"
+  );
+
+  gWebAuthnService.selectAutoFillEntry(
+    transactionId,
+    autoFillEntries[0].credentialId
+  );
+  let result = await promise;
+
+  ok(!active, "request should not be active");
+
+  // Remove credentials
+  gWebAuthnService.removeCredential(gAuthenticatorId, cred1);
+  gWebAuthnService.removeCredential(gAuthenticatorId, cred2);
+
+  // Close tab.
+  await BrowserTestUtils.removeTab(tab);
+});
diff --git a/dom/webauthn/tests/browser/browser_webauthn_ipaddress.js b/dom/webauthn/tests/browser/browser_webauthn_ipaddress.js
new file mode 100644
index 0000000000..6658ae5f02
--- /dev/null
+++ b/dom/webauthn/tests/browser/browser_webauthn_ipaddress.js
@@ -0,0 +1,30 @@
+/* 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";
+
+add_virtual_authenticator();
+
+let expectSecurityError = expectError("Security");
+
+add_task(async function test_setup() {
+  return SpecialPowers.pushPrefEnv({
+    set: [["network.proxy.allow_hijacking_localhost", true]],
+  });
+});
+
+add_task(async function test_appid() {
+  // 127.0.0.1 triggers special cases in ssltunnel, so let's use .2!
+  const TEST_URL = "https://127.0.0.2/";
+
+  // Open a new tab.
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+
+  await promiseWebAuthnMakeCredential(tab)
+    .then(arrivingHereIsBad)
+    .catch(expectSecurityError);
+
+  // Close tab.
+  BrowserTestUtils.removeTab(tab);
+});
diff --git a/dom/webauthn/tests/browser/browser_webauthn_prompts.js b/dom/webauthn/tests/browser/browser_webauthn_prompts.js
new file mode 100644
index 0000000000..05c77271d5
--- /dev/null
+++ b/dom/webauthn/tests/browser/browser_webauthn_prompts.js
@@ -0,0 +1,501 @@
+/* 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";
+
+XPCOMUtils.defineLazyScriptGetter(
+  this,
+  ["FullScreen"],
+  "chrome://browser/content/browser-fullScreenAndPointerLock.js"
+);
+
+const TEST_URL = "https://example.com/";
+var gAuthenticatorId;
+
+/**
+ * Waits for the PopupNotifications button enable delay to expire so the
+ * Notification can be interacted with using the buttons.
+ */
+async function waitForPopupNotificationSecurityDelay() {
+  let notification = PopupNotifications.panel.firstChild.notification;
+  let notificationEnableDelayMS = Services.prefs.getIntPref(
+    "security.notification_enable_delay"
+  );
+  await TestUtils.waitForCondition(
+    () => {
+      let timeSinceShown = performance.now() - notification.timeShown;
+      return timeSinceShown > notificationEnableDelayMS;
+    },
+    "Wait for security delay to expire",
+    500,
+    50
+  );
+}
+
+add_task(async function test_setup_usbtoken() {
+  return SpecialPowers.pushPrefEnv({
+    set: [
+      ["security.webauth.webauthn_enable_softtoken", false],
+      ["security.webauth.webauthn_enable_usbtoken", true],
+    ],
+  });
+});
+add_task(test_register);
+add_task(test_register_escape);
+add_task(test_register_direct_cancel);
+add_task(test_register_direct_presence);
+add_task(test_sign);
+add_task(test_sign_escape);
+add_task(test_tab_switching);
+add_task(test_window_switching);
+add_task(async function test_setup_fullscreen() {
+  return SpecialPowers.pushPrefEnv({
+    set: [
+      ["browser.fullscreen.autohide", true],
+      ["full-screen-api.enabled", true],
+      ["full-screen-api.allow-trusted-requests-only", false],
+    ],
+  });
+});
+add_task(test_fullscreen_show_nav_toolbar);
+add_task(test_no_fullscreen_dom);
+add_task(async function test_setup_softtoken() {
+  gAuthenticatorId = add_virtual_authenticator();
+  return SpecialPowers.pushPrefEnv({
+    set: [
+      ["security.webauth.webauthn_enable_softtoken", true],
+      ["security.webauth.webauthn_enable_usbtoken", false],
+    ],
+  });
+});
+add_task(test_register_direct_proceed);
+add_task(test_register_direct_proceed_anon);
+add_task(test_select_sign_result);
+
+function promiseNavToolboxStatus(aExpectedStatus) {
+  let navToolboxStatus;
+  return TestUtils.topicObserved("fullscreen-nav-toolbox", (subject, data) => {
+    navToolboxStatus = data;
+    return data == aExpectedStatus;
+  }).then(() =>
+    Assert.equal(
+      navToolboxStatus,
+      aExpectedStatus,
+      "nav toolbox is " + aExpectedStatus
+    )
+  );
+}
+
+function promiseFullScreenPaint(aExpectedStatus) {
+  return TestUtils.topicObserved("fullscreen-painted");
+}
+
+function triggerMainPopupCommand(popup) {
+  info("triggering main command");
+  let notifications = popup.childNodes;
+  ok(notifications.length, "at least one notification displayed");
+  let notification = notifications[0];
+  info("triggering command: " + notification.getAttribute("buttonlabel"));
+
+  return EventUtils.synthesizeMouseAtCenter(notification.button, {});
+}
+
+let expectNotAllowedError = expectError("NotAllowed");
+
+function verifyAnonymizedCertificate(aResult) {
+  return webAuthnDecodeCBORAttestation(aResult.attObj).then(
+    ({ fmt, attStmt }) => {
+      is(fmt, "none", "Is a None Attestation");
+      is(typeof attStmt, "object", "attStmt is a map");
+      is(Object.keys(attStmt).length, 0, "attStmt is empty");
+    }
+  );
+}
+
+async function verifyDirectCertificate(aResult) {
+  let clientDataHash = await crypto.subtle
+    .digest("SHA-256", aResult.clientDataJSON)
+    .then(digest => new Uint8Array(digest));
+  let { fmt, attStmt, authData, authDataObj } =
+    await webAuthnDecodeCBORAttestation(aResult.attObj);
+  is(fmt, "packed", "Is a Packed Attestation");
+  let signedData = new Uint8Array(authData.length + clientDataHash.length);
+  signedData.set(authData);
+  signedData.set(clientDataHash, authData.length);
+  let valid = await verifySignature(
+    authDataObj.publicKeyHandle,
+    signedData,
+    new Uint8Array(attStmt.sig)
+  );
+  ok(valid, "Signature is valid.");
+}
+
+async function test_register() {
+  // Open a new tab.
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+
+  // Request a new credential and wait for the prompt.
+  let active = true;
+  let request = promiseWebAuthnMakeCredential(tab)
+    .then(arrivingHereIsBad)
+    .catch(expectNotAllowedError)
+    .then(() => (active = false));
+  await promiseNotification("webauthn-prompt-presence");
+
+  // Cancel the request with the button.
+  ok(active, "request should still be active");
+  PopupNotifications.panel.firstElementChild.button.click();
+  await request;
+
+  // Close tab.
+  await BrowserTestUtils.removeTab(tab);
+}
+
+async function test_register_escape() {
+  // Open a new tab.
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+
+  // Request a new credential and wait for the prompt.
+  let active = true;
+  let request = promiseWebAuthnMakeCredential(tab)
+    .then(arrivingHereIsBad)
+    .catch(expectNotAllowedError)
+    .then(() => (active = false));
+  await promiseNotification("webauthn-prompt-presence");
+
+  // Cancel the request by hitting escape.
+  ok(active, "request should still be active");
+  EventUtils.synthesizeKey("KEY_Escape");
+  await request;
+
+  // Close tab.
+  await BrowserTestUtils.removeTab(tab);
+}
+
+async function test_sign() {
+  // Open a new tab.
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+
+  // Request a new assertion and wait for the prompt.
+  let active = true;
+  let request = promiseWebAuthnGetAssertion(tab)
+    .then(arrivingHereIsBad)
+    .catch(expectNotAllowedError)
+    .then(() => (active = false));
+  await promiseNotification("webauthn-prompt-presence");
+
+  // Cancel the request with the button.
+  ok(active, "request should still be active");
+  PopupNotifications.panel.firstElementChild.button.click();
+  await request;
+
+  // Close tab.
+  await BrowserTestUtils.removeTab(tab);
+}
+
+async function test_sign_escape() {
+  // Open a new tab.
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+
+  // Request a new assertion and wait for the prompt.
+  let active = true;
+  let request = promiseWebAuthnGetAssertion(tab)
+    .then(arrivingHereIsBad)
+    .catch(expectNotAllowedError)
+    .then(() => (active = false));
+  await promiseNotification("webauthn-prompt-presence");
+
+  // Cancel the request by hitting escape.
+  ok(active, "request should still be active");
+  EventUtils.synthesizeKey("KEY_Escape");
+  await request;
+
+  // Close tab.
+  await BrowserTestUtils.removeTab(tab);
+}
+
+async function test_register_direct_cancel() {
+  // Open a new tab.
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+
+  // Request a new credential with direct attestation and wait for the prompt.
+  let active = true;
+  let promise = promiseWebAuthnMakeCredential(tab, "direct")
+    .then(arrivingHereIsBad)
+    .catch(expectNotAllowedError)
+    .then(() => (active = false));
+  await promiseNotification("webauthn-prompt-register-direct");
+
+  // Cancel the request.
+  ok(active, "request should still be active");
+  PopupNotifications.panel.firstElementChild.secondaryButton.click();
+  await promise;
+
+  // Close tab.
+  await BrowserTestUtils.removeTab(tab);
+}
+
+async function test_register_direct_presence() {
+  // Open a new tab.
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+
+  // Request a new credential with direct attestation and wait for the prompt.
+  let active = true;
+  let promise = promiseWebAuthnMakeCredential(tab, "direct")
+    .then(arrivingHereIsBad)
+    .catch(expectNotAllowedError)
+    .then(() => (active = false));
+  await promiseNotification("webauthn-prompt-register-direct");
+
+  // Click "proceed" and wait for presence prompt
+  let presence = promiseNotification("webauthn-prompt-presence");
+  PopupNotifications.panel.firstElementChild.button.click();
+  await presence;
+
+  // Cancel the request.
+  ok(active, "request should still be active");
+  PopupNotifications.panel.firstElementChild.button.click();
+  await promise;
+
+  // Close tab.
+  await BrowserTestUtils.removeTab(tab);
+}
+
+// Add two tabs, open WebAuthn in the first, switch, assert the prompt is
+// not visible, switch back, assert the prompt is there and cancel it.
+async function test_tab_switching() {
+  // Open a new tab.
+  let tab_one = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+
+  // Request a new credential and wait for the prompt.
+  let active = true;
+  let request = promiseWebAuthnMakeCredential(tab_one)
+    .then(arrivingHereIsBad)
+    .catch(expectNotAllowedError)
+    .then(() => (active = false));
+  await promiseNotification("webauthn-prompt-presence");
+  is(PopupNotifications.panel.state, "open", "Doorhanger is visible");
+
+  // Open and switch to a second tab.
+  let tab_two = await BrowserTestUtils.openNewForegroundTab(
+    gBrowser,
+    "https://example.org/"
+  );
+
+  await TestUtils.waitForCondition(
+    () => PopupNotifications.panel.state == "closed"
+  );
+  is(PopupNotifications.panel.state, "closed", "Doorhanger is hidden");
+
+  // Go back to the first tab
+  await BrowserTestUtils.removeTab(tab_two);
+
+  await promiseNotification("webauthn-prompt-presence");
+
+  await TestUtils.waitForCondition(
+    () => PopupNotifications.panel.state == "open"
+  );
+  is(PopupNotifications.panel.state, "open", "Doorhanger is visible");
+
+  // Cancel the request.
+  ok(active, "request should still be active");
+  await triggerMainPopupCommand(PopupNotifications.panel);
+  await request;
+  ok(!active, "request should be stopped");
+
+  // Close tab.
+  await BrowserTestUtils.removeTab(tab_one);
+}
+
+// Add two tabs, open WebAuthn in the first, switch, assert the prompt is
+// not visible, switch back, assert the prompt is there and cancel it.
+async function test_window_switching() {
+  // Open a new tab.
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+
+  // Request a new credential and wait for the prompt.
+  let active = true;
+  let request = promiseWebAuthnMakeCredential(tab)
+    .then(arrivingHereIsBad)
+    .catch(expectNotAllowedError)
+    .then(() => (active = false));
+  await promiseNotification("webauthn-prompt-presence");
+
+  await TestUtils.waitForCondition(
+    () => PopupNotifications.panel.state == "open"
+  );
+  is(PopupNotifications.panel.state, "open", "Doorhanger is visible");
+
+  // Open and switch to a second window
+  let new_window = await BrowserTestUtils.openNewBrowserWindow();
+  await SimpleTest.promiseFocus(new_window);
+
+  await TestUtils.waitForCondition(
+    () => new_window.PopupNotifications.panel.state == "closed"
+  );
+  is(
+    new_window.PopupNotifications.panel.state,
+    "closed",
+    "Doorhanger is hidden"
+  );
+
+  // Go back to the first tab
+  await BrowserTestUtils.closeWindow(new_window);
+  await SimpleTest.promiseFocus(window);
+
+  await TestUtils.waitForCondition(
+    () => PopupNotifications.panel.state == "open"
+  );
+  is(PopupNotifications.panel.state, "open", "Doorhanger is still visible");
+
+  // Cancel the request.
+  ok(active, "request should still be active");
+  await triggerMainPopupCommand(PopupNotifications.panel);
+  await request;
+  ok(!active, "request should be stopped");
+
+  // Close tab.
+  await BrowserTestUtils.removeTab(tab);
+}
+
+async function test_register_direct_proceed() {
+  // Open a new tab.
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+
+  // Request a new credential with direct attestation and wait for the prompt.
+  let request = promiseWebAuthnMakeCredential(tab, "direct");
+  await promiseNotification("webauthn-prompt-register-direct");
+
+  // Proceed.
+  PopupNotifications.panel.firstElementChild.button.click();
+
+  // Ensure we got "direct" attestation.
+  await request.then(verifyDirectCertificate);
+
+  // Close tab.
+  await BrowserTestUtils.removeTab(tab);
+}
+
+async function test_register_direct_proceed_anon() {
+  // Open a new tab.
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+
+  // Request a new credential with direct attestation and wait for the prompt.
+  let request = promiseWebAuthnMakeCredential(tab, "direct");
+  await promiseNotification("webauthn-prompt-register-direct");
+
+  // Check "anonymize anyway" and proceed.
+  PopupNotifications.panel.firstElementChild.checkbox.checked = true;
+  PopupNotifications.panel.firstElementChild.button.click();
+
+  // Ensure we got "none" attestation.
+  await request.then(verifyAnonymizedCertificate);
+
+  // Close tab.
+  await BrowserTestUtils.removeTab(tab);
+}
+
+async function test_select_sign_result() {
+  // Open a new tab.
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+
+  // Make two discoverable credentials for the same RP ID so that
+  // the user has to select one to return.
+  let cred1 = await addCredential(gAuthenticatorId, "example.com");
+  let cred2 = await addCredential(gAuthenticatorId, "example.com");
+
+  let active = true;
+  let request = promiseWebAuthnGetAssertionDiscoverable(tab)
+    .then(arrivingHereIsBad)
+    .catch(expectNotAllowedError)
+    .then(() => (active = false));
+
+  // Ensure the selection prompt is shown
+  await promiseNotification("webauthn-prompt-select-sign-result");
+
+  ok(active, "request is active");
+
+  // Cancel the request
+  PopupNotifications.panel.firstElementChild.button.click();
+  await request;
+
+  await removeCredential(gAuthenticatorId, cred1);
+  await removeCredential(gAuthenticatorId, cred2);
+  await BrowserTestUtils.removeTab(tab);
+}
+
+async function test_fullscreen_show_nav_toolbar() {
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+
+  // Start with the window fullscreen and the nav toolbox hidden
+  let fullscreenState = window.fullScreen;
+
+  let navToolboxHiddenPromise = promiseNavToolboxStatus("hidden");
+
+  window.fullScreen = true;
+  FullScreen.hideNavToolbox(false);
+
+  await navToolboxHiddenPromise;
+
+  // Request a new credential and wait for the direct attestation consent
+  // prompt.
+  let promptPromise = promiseNotification("webauthn-prompt-register-direct");
+  let navToolboxShownPromise = promiseNavToolboxStatus("shown");
+
+  let active = true;
+  let requestPromise = promiseWebAuthnMakeCredential(tab, "direct")
+    .then(arrivingHereIsBad)
+    .catch(expectNotAllowedError)
+    .then(() => (active = false));
+
+  await Promise.all([promptPromise, navToolboxShownPromise]);
+
+  ok(active, "request is active");
+  ok(window.fullScreen, "window is fullscreen");
+
+  // Cancel the request.
+  PopupNotifications.panel.firstElementChild.secondaryButton.click();
+  await requestPromise;
+
+  window.fullScreen = fullscreenState;
+
+  // Close tab.
+  await BrowserTestUtils.removeTab(tab);
+}
+
+async function test_no_fullscreen_dom() {
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+
+  let fullScreenPaintPromise = promiseFullScreenPaint();
+  // Make a DOM element fullscreen
+  await ContentTask.spawn(tab.linkedBrowser, [], () => {
+    return content.document.body.requestFullscreen();
+  });
+  await fullScreenPaintPromise;
+  ok(!!document.fullscreenElement, "a DOM element is fullscreen");
+
+  // Request a new credential and wait for the direct attestation consent
+  // prompt.
+  let promptPromise = promiseNotification("webauthn-prompt-register-direct");
+  fullScreenPaintPromise = promiseFullScreenPaint();
+
+  let active = true;
+  let requestPromise = promiseWebAuthnMakeCredential(tab, "direct")
+    .then(arrivingHereIsBad)
+    .catch(expectNotAllowedError)
+    .then(() => (active = false));
+
+  await Promise.all([promptPromise, fullScreenPaintPromise]);
+
+  ok(active, "request is active");
+  ok(!document.fullscreenElement, "no DOM element is fullscreen");
+
+  // Cancel the request.
+  await waitForPopupNotificationSecurityDelay();
+  PopupNotifications.panel.firstElementChild.secondaryButton.click();
+  await requestPromise;
+
+  // Close tab.
+  await BrowserTestUtils.removeTab(tab);
+}
diff --git a/dom/webauthn/tests/browser/head.js b/dom/webauthn/tests/browser/head.js
new file mode 100644
index 0000000000..d6cbd56133
--- /dev/null
+++ b/dom/webauthn/tests/browser/head.js
@@ -0,0 +1,259 @@
+/* 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 add_virtual_authenticator(autoremove = true) {
+  let webauthnService = Cc["@mozilla.org/webauthn/service;1"].getService(
+    Ci.nsIWebAuthnService
+  );
+  let id = webauthnService.addVirtualAuthenticator(
+    "ctap2_1",
+    "internal",
+    true,
+    true,
+    true,
+    true
+  );
+  if (autoremove) {
+    registerCleanupFunction(() => {
+      webauthnService.removeVirtualAuthenticator(id);
+    });
+  }
+  return id;
+}
+
+async function addCredential(authenticatorId, rpId) {
+  let keyPair = await crypto.subtle.generateKey(
+    {
+      name: "ECDSA",
+      namedCurve: "P-256",
+    },
+    true,
+    ["sign"]
+  );
+
+  let credId = new Uint8Array(32);
+  crypto.getRandomValues(credId);
+  credId = bytesToBase64UrlSafe(credId);
+
+  let privateKey = await crypto.subtle
+    .exportKey("pkcs8", keyPair.privateKey)
+    .then(privateKey => bytesToBase64UrlSafe(privateKey));
+
+  let webauthnService = Cc["@mozilla.org/webauthn/service;1"].getService(
+    Ci.nsIWebAuthnService
+  );
+
+  webauthnService.addCredential(
+    authenticatorId,
+    credId,
+    true, // resident key
+    rpId,
+    privateKey,
+    "VGVzdCBVc2Vy", // "Test User"
+    0 // sign count
+  );
+
+  return credId;
+}
+
+async function removeCredential(authenticatorId, credId) {
+  let webauthnService = Cc["@mozilla.org/webauthn/service;1"].getService(
+    Ci.nsIWebAuthnService
+  );
+
+  webauthnService.removeCredential(authenticatorId, credId);
+}
+
+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",
+  residentKey = "discouraged",
+  extensions = {}
+) {
+  return ContentTask.spawn(
+    tab.linkedBrowser,
+    [attestation, residentKey, extensions],
+    ([attestation, residentKey, 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" },
+        user: {
+          id: new Uint8Array(),
+          name: "none",
+          displayName: "none",
+        },
+        pubKeyCredParams,
+        authenticatorSelection: {
+          authenticatorAttachment: "cross-platform",
+          residentKey,
+        },
+        extensions,
+        attestation,
+        challenge,
+      };
+
+      return content.navigator.credentials
+        .create({ publicKey })
+        .then(credential => {
+          return {
+            clientDataJSON: credential.response.clientDataJSON,
+            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 promiseWebAuthnGetAssertionDiscoverable(
+  tab,
+  mediation = "optional",
+  extensions = {}
+) {
+  return ContentTask.spawn(
+    tab.linkedBrowser,
+    [extensions, mediation],
+    ([extensions, mediation]) => {
+      let challenge = content.crypto.getRandomValues(new Uint8Array(16));
+
+      let publicKey = {
+        challenge,
+        extensions,
+        rpId: content.document.domain,
+        allowCredentials: [],
+      };
+
+      return content.navigator.credentials.get({ publicKey, mediation });
+    }
+  );
+}
+
+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.");
+      }
+    });
+}
+
+function promiseNotification(id) {
+  return new Promise(resolve => {
+    PopupNotifications.panel.addEventListener("popupshown", function shown() {
+      let notification = PopupNotifications.getNotification(id);
+      if (notification) {
+        ok(true, `${id} prompt visible`);
+        PopupNotifications.panel.removeEventListener("popupshown", shown);
+        resolve();
+      }
+    });
+  });
+}
+/* eslint-enable no-shadow */
diff --git a/dom/webauthn/tests/browser/tab_webauthn_result.html b/dom/webauthn/tests/browser/tab_webauthn_result.html
new file mode 100644
index 0000000000..8e8b9f82cd
--- /dev/null
+++ b/dom/webauthn/tests/browser/tab_webauthn_result.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <title>Generic W3C Web Authentication Test Result Page</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<h1>Generic W3C Web Authentication Test Result Page</h1>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1415675">Mozilla Bug 1415675</a>
+<input type="text" id="status" value="init" />
+
+</body>
+</html>
diff --git a/dom/webauthn/tests/cbor.js b/dom/webauthn/tests/cbor.js
new file mode 100644
index 0000000000..3e1f300df3
--- /dev/null
+++ b/dom/webauthn/tests/cbor.js
@@ -0,0 +1,406 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2014-2016 Patrick Gansterer <paroga@paroga.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+(function(global, undefined) { "use strict";
+var POW_2_24 = 5.960464477539063e-8,
+    POW_2_32 = 4294967296,
+    POW_2_53 = 9007199254740992;
+
+function encode(value) {
+  var data = new ArrayBuffer(256);
+  var dataView = new DataView(data);
+  var lastLength;
+  var offset = 0;
+
+  function prepareWrite(length) {
+    var newByteLength = data.byteLength;
+    var requiredLength = offset + length;
+    while (newByteLength < requiredLength)
+      newByteLength <<= 1;
+    if (newByteLength !== data.byteLength) {
+      var oldDataView = dataView;
+      data = new ArrayBuffer(newByteLength);
+      dataView = new DataView(data);
+      var uint32count = (offset + 3) >> 2;
+      for (var i = 0; i < uint32count; ++i)
+        dataView.setUint32(i << 2, oldDataView.getUint32(i << 2));
+    }
+
+    lastLength = length;
+    return dataView;
+  }
+  function commitWrite() {
+    offset += lastLength;
+  }
+  function writeFloat64(value) {
+    commitWrite(prepareWrite(8).setFloat64(offset, value));
+  }
+  function writeUint8(value) {
+    commitWrite(prepareWrite(1).setUint8(offset, value));
+  }
+  function writeUint8Array(value) {
+    var dataView = prepareWrite(value.length);
+    for (var i = 0; i < value.length; ++i)
+      dataView.setUint8(offset + i, value[i]);
+    commitWrite();
+  }
+  function writeUint16(value) {
+    commitWrite(prepareWrite(2).setUint16(offset, value));
+  }
+  function writeUint32(value) {
+    commitWrite(prepareWrite(4).setUint32(offset, value));
+  }
+  function writeUint64(value) {
+    var low = value % POW_2_32;
+    var high = (value - low) / POW_2_32;
+    var dataView = prepareWrite(8);
+    dataView.setUint32(offset, high);
+    dataView.setUint32(offset + 4, low);
+    commitWrite();
+  }
+  function writeTypeAndLength(type, length) {
+    if (length < 24) {
+      writeUint8(type << 5 | length);
+    } else if (length < 0x100) {
+      writeUint8(type << 5 | 24);
+      writeUint8(length);
+    } else if (length < 0x10000) {
+      writeUint8(type << 5 | 25);
+      writeUint16(length);
+    } else if (length < 0x100000000) {
+      writeUint8(type << 5 | 26);
+      writeUint32(length);
+    } else {
+      writeUint8(type << 5 | 27);
+      writeUint64(length);
+    }
+  }
+
+  function encodeItem(value) {
+    var i;
+
+    if (value === false)
+      return writeUint8(0xf4);
+    if (value === true)
+      return writeUint8(0xf5);
+    if (value === null)
+      return writeUint8(0xf6);
+    if (value === undefined)
+      return writeUint8(0xf7);
+
+    switch (typeof value) {
+      case "number":
+        if (Math.floor(value) === value) {
+          if (0 <= value && value <= POW_2_53)
+            return writeTypeAndLength(0, value);
+          if (-POW_2_53 <= value && value < 0)
+            return writeTypeAndLength(1, -(value + 1));
+        }
+        writeUint8(0xfb);
+        return writeFloat64(value);
+
+      case "string":
+        var utf8data = [];
+        for (i = 0; i < value.length; ++i) {
+          var charCode = value.charCodeAt(i);
+          if (charCode < 0x80) {
+            utf8data.push(charCode);
+          } else if (charCode < 0x800) {
+            utf8data.push(0xc0 | charCode >> 6);
+            utf8data.push(0x80 | charCode & 0x3f);
+          } else if (charCode < 0xd800) {
+            utf8data.push(0xe0 | charCode >> 12);
+            utf8data.push(0x80 | (charCode >> 6)  & 0x3f);
+            utf8data.push(0x80 | charCode & 0x3f);
+          } else {
+            charCode = (charCode & 0x3ff) << 10;
+            charCode |= value.charCodeAt(++i) & 0x3ff;
+            charCode += 0x10000;
+
+            utf8data.push(0xf0 | charCode >> 18);
+            utf8data.push(0x80 | (charCode >> 12)  & 0x3f);
+            utf8data.push(0x80 | (charCode >> 6)  & 0x3f);
+            utf8data.push(0x80 | charCode & 0x3f);
+          }
+        }
+
+        writeTypeAndLength(3, utf8data.length);
+        return writeUint8Array(utf8data);
+
+      default:
+        var length;
+        if (Array.isArray(value)) {
+          length = value.length;
+          writeTypeAndLength(4, length);
+          for (i = 0; i < length; ++i)
+            encodeItem(value[i]);
+        } else if (value instanceof Uint8Array) {
+          writeTypeAndLength(2, value.length);
+          writeUint8Array(value);
+        } else {
+          var keys = Object.keys(value);
+          length = keys.length;
+          writeTypeAndLength(5, length);
+          for (i = 0; i < length; ++i) {
+            var key = keys[i];
+            encodeItem(key);
+            encodeItem(value[key]);
+          }
+        }
+    }
+  }
+
+  encodeItem(value);
+
+  if ("slice" in data)
+    return data.slice(0, offset);
+
+  var ret = new ArrayBuffer(offset);
+  var retView = new DataView(ret);
+  for (var i = 0; i < offset; ++i)
+    retView.setUint8(i, dataView.getUint8(i));
+  return ret;
+}
+
+function decode(data, tagger, simpleValue) {
+  var dataView = new DataView(data);
+  var offset = 0;
+
+  if (typeof tagger !== "function")
+    tagger = function(value) { return value; };
+  if (typeof simpleValue !== "function")
+    simpleValue = function() { return undefined; };
+
+  function commitRead(length, value) {
+    offset += length;
+    return value;
+  }
+  function readArrayBuffer(length) {
+    return commitRead(length, new Uint8Array(data, offset, length));
+  }
+  function readFloat16() {
+    var tempArrayBuffer = new ArrayBuffer(4);
+    var tempDataView = new DataView(tempArrayBuffer);
+    var value = readUint16();
+
+    var sign = value & 0x8000;
+    var exponent = value & 0x7c00;
+    var fraction = value & 0x03ff;
+
+    if (exponent === 0x7c00)
+      exponent = 0xff << 10;
+    else if (exponent !== 0)
+      exponent += (127 - 15) << 10;
+    else if (fraction !== 0)
+      return (sign ? -1 : 1) * fraction * POW_2_24;
+
+    tempDataView.setUint32(0, sign << 16 | exponent << 13 | fraction << 13);
+    return tempDataView.getFloat32(0);
+  }
+  function readFloat32() {
+    return commitRead(4, dataView.getFloat32(offset));
+  }
+  function readFloat64() {
+    return commitRead(8, dataView.getFloat64(offset));
+  }
+  function readUint8() {
+    return commitRead(1, dataView.getUint8(offset));
+  }
+  function readUint16() {
+    return commitRead(2, dataView.getUint16(offset));
+  }
+  function readUint32() {
+    return commitRead(4, dataView.getUint32(offset));
+  }
+  function readUint64() {
+    return readUint32() * POW_2_32 + readUint32();
+  }
+  function readBreak() {
+    if (dataView.getUint8(offset) !== 0xff)
+      return false;
+    offset += 1;
+    return true;
+  }
+  function readLength(additionalInformation) {
+    if (additionalInformation < 24)
+      return additionalInformation;
+    if (additionalInformation === 24)
+      return readUint8();
+    if (additionalInformation === 25)
+      return readUint16();
+    if (additionalInformation === 26)
+      return readUint32();
+    if (additionalInformation === 27)
+      return readUint64();
+    if (additionalInformation === 31)
+      return -1;
+    throw "Invalid length encoding";
+  }
+  function readIndefiniteStringLength(majorType) {
+    var initialByte = readUint8();
+    if (initialByte === 0xff)
+      return -1;
+    var length = readLength(initialByte & 0x1f);
+    if (length < 0 || (initialByte >> 5) !== majorType)
+      throw "Invalid indefinite length element";
+    return length;
+  }
+
+  function appendUtf16Data(utf16data, length) {
+    for (var i = 0; i < length; ++i) {
+      var value = readUint8();
+      if (value & 0x80) {
+        if (value < 0xe0) {
+          value = (value & 0x1f) <<  6
+                | (readUint8() & 0x3f);
+          length -= 1;
+        } else if (value < 0xf0) {
+          value = (value & 0x0f) << 12
+                | (readUint8() & 0x3f) << 6
+                | (readUint8() & 0x3f);
+          length -= 2;
+        } else {
+          value = (value & 0x0f) << 18
+                | (readUint8() & 0x3f) << 12
+                | (readUint8() & 0x3f) << 6
+                | (readUint8() & 0x3f);
+          length -= 3;
+        }
+      }
+
+      if (value < 0x10000) {
+        utf16data.push(value);
+      } else {
+        value -= 0x10000;
+        utf16data.push(0xd800 | (value >> 10));
+        utf16data.push(0xdc00 | (value & 0x3ff));
+      }
+    }
+  }
+
+  function decodeItem() {
+    var initialByte = readUint8();
+    var majorType = initialByte >> 5;
+    var additionalInformation = initialByte & 0x1f;
+    var i;
+    var length;
+
+    if (majorType === 7) {
+      switch (additionalInformation) {
+        case 25:
+          return readFloat16();
+        case 26:
+          return readFloat32();
+        case 27:
+          return readFloat64();
+      }
+    }
+
+    length = readLength(additionalInformation);
+    if (length < 0 && (majorType < 2 || 6 < majorType))
+      throw "Invalid length";
+
+    switch (majorType) {
+      case 0:
+        return length;
+      case 1:
+        return -1 - length;
+      case 2:
+        if (length < 0) {
+          var elements = [];
+          var fullArrayLength = 0;
+          while ((length = readIndefiniteStringLength(majorType)) >= 0) {
+            fullArrayLength += length;
+            elements.push(readArrayBuffer(length));
+          }
+          var fullArray = new Uint8Array(fullArrayLength);
+          var fullArrayOffset = 0;
+          for (i = 0; i < elements.length; ++i) {
+            fullArray.set(elements[i], fullArrayOffset);
+            fullArrayOffset += elements[i].length;
+          }
+          return fullArray;
+        }
+        return readArrayBuffer(length);
+      case 3:
+        var utf16data = [];
+        if (length < 0) {
+          while ((length = readIndefiniteStringLength(majorType)) >= 0)
+            appendUtf16Data(utf16data, length);
+        } else
+          appendUtf16Data(utf16data, length);
+        return String.fromCharCode.apply(null, utf16data);
+      case 4:
+        var retArray;
+        if (length < 0) {
+          retArray = [];
+          while (!readBreak())
+            retArray.push(decodeItem());
+        } else {
+          retArray = new Array(length);
+          for (i = 0; i < length; ++i)
+            retArray[i] = decodeItem();
+        }
+        return retArray;
+      case 5:
+        var retObject = {};
+        for (i = 0; i < length || length < 0 && !readBreak(); ++i) {
+          var key = decodeItem();
+          retObject[key] = decodeItem();
+        }
+        return retObject;
+      case 6:
+        return tagger(decodeItem(), length);
+      case 7:
+        switch (length) {
+          case 20:
+            return false;
+          case 21:
+            return true;
+          case 22:
+            return null;
+          case 23:
+            return undefined;
+          default:
+            return simpleValue(length);
+        }
+    }
+  }
+
+  var ret = decodeItem();
+  if (offset !== data.byteLength)
+    throw "Remaining bytes";
+  return ret;
+}
+
+var obj = { encode: encode, decode: decode };
+
+if (typeof define === "function" && define.amd)
+  define("cbor/cbor", obj);
+else if (typeof module !== "undefined" && module.exports)
+  module.exports = obj;
+else if (!global.CBOR)
+  global.CBOR = obj;
+
+})(this);
diff --git a/dom/webauthn/tests/empty.html b/dom/webauthn/tests/empty.html
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/dom/webauthn/tests/get_assertion_dead_object.html b/dom/webauthn/tests/get_assertion_dead_object.html
new file mode 100644
index 0000000000..e7de9d3deb
--- /dev/null
+++ b/dom/webauthn/tests/get_assertion_dead_object.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=utf-8>
+</head>
+<body>
+<script type="text/javascript">
+  window.addEventListener('load', function() {
+    let o = [];
+    o[0] = window.navigator;
+    document.writeln('');
+    // Since the USB token is enabled by default, this will pop up a notification that the
+    // user can insert/interact with it. Since this is just a test, this won't happen. The
+    // request will eventually time out.
+    // Unfortunately the minimum timeout is 15 seconds.
+    o[0].credentials.get({ publicKey: { challenge: new Uint8Array(128), timeout: 15000 } });
+    o.forEach((n, i) => o[i] = null);
+  });
+</script>
+</body>
+</html>
diff --git a/dom/webauthn/tests/mochitest.toml b/dom/webauthn/tests/mochitest.toml
new file mode 100644
index 0000000000..2082d39a58
--- /dev/null
+++ b/dom/webauthn/tests/mochitest.toml
@@ -0,0 +1,118 @@
+[DEFAULT]
+support-files = [
+  "cbor.js",
+  "u2futil.js",
+  "pkijs/*",
+  "get_assertion_dead_object.html",
+  "empty.html",
+]
+scheme = "https"
+prefs = [
+  "dom.security.featurePolicy.webidl.enabled=true",
+  "security.webauth.webauthn=true",
+  "security.webauth.webauthn_enable_softtoken=true",
+  "security.webauth.webauthn_enable_usbtoken=false",
+  "security.webauthn.ctap2=true",
+  "security.webauthn.enable_conditional_mediation=true",
+  "security.webauthn.enable_macos_passkeys=false",
+]
+
+["test_webauthn_abort_signal.html"]
+fail-if = ["xorigin"]
+skip-if = [
+  "win10_2009", # Bug 1718296 (Windows 10 1903+ has its own window and U2F that we cannot control with tests.)
+  "win11_2009", # Bug 1718296 (Windows 10 1903+ has its own window and U2F that we cannot control with tests.)
+  "os == 'android'", # Test sets security.webauth.webauthn_enable_usbtoken to true, which isn't applicable to android
+]
+
+["test_webauthn_attestation_conveyance.html"]
+fail-if = ["xorigin"] # NotAllowedError
+skip-if = [
+  "win10_2009", # Bug 1718296 (Windows 10 1903+ has its own window and U2F that we cannot control with tests.)
+  "win11_2009", # Bug 1718296 (Windows 10 1903+ has its own window and U2F that we cannot control with tests.)
+]
+
+["test_webauthn_authenticator_selection.html"]
+fail-if = ["xorigin"] # NotAllowedError
+skip-if = [
+  "win10_2009", # Bug 1718296 (Windows 10 1903+ has its own window and U2F that we cannot control with tests.)
+  "win11_2009", # Bug 1718296 (Windows 10 1903+ has its own window and U2F that we cannot control with tests.)
+]
+
+["test_webauthn_authenticator_transports.html"]
+fail-if = ["xorigin"] # NotAllowedError
+skip-if = [
+  "win10_2009", # Bug 1718296 (Windows 10 1903+ has its own window and U2F that we cannot control with tests.)
+  "win11_2009", # Bug 1718296 (Windows 10 1903+ has its own window and U2F that we cannot control with tests.)
+]
+
+["test_webauthn_crossorigin_featurepolicy.html"]
+fail-if = ["xorigin"] # Cross-origin use of WebAuthn requires a feature policy.
+
+["test_webauthn_ctap2_omitted_credential_id.html"]
+fail-if = ["xorigin"] # NotAllowedError
+
+["test_webauthn_get_assertion.html"]
+fail-if = ["xorigin"] # NotAllowedError
+skip-if = [
+  "win10_2009", # Bug 1718296 (Windows 10 1903+ has its own window and U2F that we cannot control with tests.)
+  "win11_2009", # Bug 1718296 (Windows 10 1903+ has its own window and U2F that we cannot control with tests.)
+  "os == 'android'", # Test sets security.webauth.webauthn_enable_usbtoken to true, which isn't applicable to android
+]
+
+["test_webauthn_get_assertion_dead_object.html"]
+skip-if = [
+  "win10_2009", # Bug 1718296 (Windows 10 1903+ has its own window and U2F that we cannot control with tests.)
+  "win11_2009", # Bug 1718296 (Windows 10 1903+ has its own window and U2F that we cannot control with tests.)
+]
+
+["test_webauthn_isplatformauthenticatoravailable.html"]
+
+["test_webauthn_loopback.html"]
+skip-if = [
+  "xorigin", # Hangs, JavaScript error: https://example.org/tests/SimpleTest/SimpleTest.js, line 76: DataCloneError: The object could not be cloned.
+  "win10_2009", # Bug 1718296 (Windows 10 1903+ has its own window and U2F that we cannot control with tests.)
+  "win11_2009", # Bug 1718296 (Windows 10 1903+ has its own window and U2F that we cannot control with tests.)
+]
+
+["test_webauthn_make_credential.html"]
+fail-if = ["xorigin"] # NotAllowedError
+skip-if = [
+  "win10_2009", # Bug 1718296 (Windows 10 1903+ has its own window and U2F that we cannot control with tests.)
+  "win11_2009", # Bug 1718296 (Windows 10 1903+ has its own window and U2F that we cannot control with tests.)
+]
+
+["test_webauthn_no_token.html"]
+skip-if = [
+  "xorigin", # JavaScript error: https://example.org/tests/SimpleTest/SimpleTest.js, line 76: DataCloneError: The object could not be cloned.
+  "win10_2009", # Bug 1718296 (Windows 10 1903+ has its own window and U2F that we cannot control with tests.)
+  "win11_2009", # Bug 1718296 (Windows 10 1903+ has its own window and U2F that we cannot control with tests.)
+  "os == 'android'", # Test disables all tokens, which is an unsupported configuration on android.
+]
+
+["test_webauthn_sameorigin.html"]
+fail-if = ["xorigin"] # NotAllowedError
+skip-if = [
+  "win10_2009", # Bug 1718296 (Windows 10 1903+ has its own window and U2F that we cannot control with tests.)
+  "win11_2009", # Bug 1718296 (Windows 10 1903+ has its own window and U2F that we cannot control with tests.)
+]
+
+["test_webauthn_sameoriginwithancestors.html"]
+skip-if = [
+  "xorigin", # this test has its own cross-origin setup
+  "win10_2009", # Bug 1718296 (Windows 10 1903+ has its own window and U2F that we cannot control with tests.)
+  "win11_2009", # Bug 1718296 (Windows 10 1903+ has its own window and U2F that we cannot control with tests.)
+]
+
+["test_webauthn_serialization.html"]
+fail-if = ["xorigin"]
+
+["test_webauthn_store_credential.html"]
+fail-if = ["xorigin"] # NotAllowedError
+skip-if = [
+  "win10_2009", # Bug 1718296 (Windows 10 1903+ has its own window and U2F that we cannot control with tests.)
+  "win11_2009", # Bug 1718296 (Windows 10 1903+ has its own window and U2F that we cannot control with tests.)
+]
+
+["test_webauthn_webdriver_virtual_authenticator.html"]
+fail-if = ["xorigin"] # Cross-origin use of WebAuthn requires a feature policy.
diff --git a/dom/webauthn/tests/pkijs/LICENSE b/dom/webauthn/tests/pkijs/LICENSE
new file mode 100644
index 0000000000..4f71696a7d
--- /dev/null
+++ b/dom/webauthn/tests/pkijs/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2014, GMO GlobalSign
+Copyright (c) 2015, Peculiar Ventures
+All rights reserved.
+
+Author 2014-2015, Yury Strozhevsky
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice, this
+  list of conditions and the following disclaimer in the documentation and/or
+  other materials provided with the distribution.
+
+* Neither the name of the copyright holder nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/dom/webauthn/tests/pkijs/README b/dom/webauthn/tests/pkijs/README
new file mode 100644
index 0000000000..9213c9d438
--- /dev/null
+++ b/dom/webauthn/tests/pkijs/README
@@ -0,0 +1 @@
+PKIjs and ASN1js are from https://pkijs.org/ and https://asn1js.org/.
\ No newline at end of file
diff --git a/dom/webauthn/tests/pkijs/asn1.js b/dom/webauthn/tests/pkijs/asn1.js
new file mode 100644
index 0000000000..ddee052407
--- /dev/null
+++ b/dom/webauthn/tests/pkijs/asn1.js
@@ -0,0 +1,5466 @@
+/*
+ * Copyright (c) 2014, GMO GlobalSign
+ * Copyright (c) 2015, Peculiar Ventures
+ * All rights reserved.
+ *
+ * Author 2014-2015, Yury Strozhevsky <www.strozhevsky.com>.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, 
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, 
+ *    this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, 
+ *    this list of conditions and the following disclaimer in the documentation 
+ *    and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors 
+ *    may be used to endorse or promote products derived from this software without 
+ *    specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
+ * OF SUCH DAMAGE. 
+ *
+ */
+(
+function(in_window)
+{
+    //**************************************************************************************
+    // #region Declaration of global variables 
+    //**************************************************************************************
+    // #region "org" namespace 
+    if(typeof in_window.org === "undefined")
+        in_window.org = {};
+    else
+    {
+        if(typeof in_window.org !== "object")
+            throw new Error("Name org already exists and it's not an object");
+    }
+    // #endregion 
+
+    // #region "org.pkijs" namespace 
+    if(typeof in_window.org.pkijs === "undefined")
+        in_window.org.pkijs = {};
+    else
+    {
+        if(typeof in_window.org.pkijs !== "object")
+            throw new Error("Name org.pkijs already exists and it's not an object" + " but " + (typeof in_window.org.pkijs));
+    }
+    // #endregion 
+
+    // #region "org.pkijs.asn1" namespace 
+    if(typeof in_window.org.pkijs.asn1 === "undefined")
+        in_window.org.pkijs.asn1 = {};
+    else
+    {
+        if(typeof in_window.org.pkijs.asn1 !== "object")
+            throw new Error("Name org.pkijs.asn1 already exists and it's not an object" + " but " + (typeof in_window.org.pkijs.asn1));
+    }
+    // #endregion 
+
+    // #region "local" namespace 
+    var local = {};
+    // #endregion   
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Aux-functions 
+    //**************************************************************************************
+    function util_frombase(input_buffer, input_base)
+    {
+        /// <summary>Convert number from 2^base to 2^10</summary>
+        /// <param name="input_buffer" type="Uint8Array">Array of bytes representing the number to convert</param>
+        /// <param name="input_base" type="Number">The base of initial number</param>
+
+        var result = 0; 
+
+        for(var i = (input_buffer.length - 1); i >= 0; i-- )
+            result += input_buffer[(input_buffer.length - 1) - i] * Math.pow(2, input_base * i);
+
+        return result;
+    }
+    //**************************************************************************************
+    function util_tobase(value, base, reserved)
+    {
+        /// <summary>Convert number from 2^10 to 2^base</summary>
+        /// <param name="value" type="Number">The number to convert</param>
+        /// <param name="base" type="Number">The base for 2^base</param>
+        /// <param name="reserved" type="Number">Pre-defined number of bytes in output array (-1 = limited by function itself)</param>
+
+        reserved = reserved || (-1);
+
+        var result = 0;
+        var biggest = Math.pow(2, base);
+
+        for(var i = 1; i < 8; i++)
+        {
+            if(value < biggest)
+            {
+                var ret_buf;
+
+                if( reserved < 0 )
+                {
+                    ret_buf = new ArrayBuffer(i);
+                    result = i;
+                }
+                else
+                {
+                    if(reserved < i)
+                        return (new ArrayBuffer(0));
+
+                    ret_buf = new ArrayBuffer(reserved);
+
+                    result = reserved;
+                }
+
+                var ret_view = new Uint8Array(ret_buf);
+
+                for(var j = ( i - 1 ); j >= 0; j-- )
+                {
+                    var basis = Math.pow(2, j * base);
+
+                    ret_view[ result - j - 1 ] = Math.floor( value / basis );
+                    value -= ( ret_view[ result - j - 1 ] ) * basis;
+                }
+
+                return ret_buf;
+            }
+
+            biggest *= Math.pow(2, base);
+        }
+    }
+    //**************************************************************************************
+    function util_encode_tc(value)
+    {
+        /// <summary>Encode integer value to "two complement" format</summary>
+        /// <param name="value" type="Number">Value to encode</param>
+
+        var mod_value = (value < 0) ? (value * (-1)) : value;
+        var big_int = 128;
+
+        for(var i = 1; i < 8; i++) 
+        {
+            if( mod_value <= big_int )
+            {
+                if( value < 0 )
+                {
+                    var small_int = big_int - mod_value;
+
+                    var ret_buf = util_tobase( small_int, 8, i );
+                    var ret_view = new Uint8Array(ret_buf);
+
+                    ret_view[ 0 ] |= 0x80;
+
+                    return ret_buf;
+                }
+                else
+                {
+                    var ret_buf = util_tobase( mod_value, 8, i );
+                    var ret_view = new Uint8Array(ret_buf);
+
+                    if( ret_view[ 0 ] & 0x80 )
+                    {
+                        var temp_buf = util_copybuf(ret_buf);
+                        var temp_view = new Uint8Array(temp_buf);
+
+                        ret_buf = new ArrayBuffer( ret_buf.byteLength + 1 );
+                        ret_view = new Uint8Array(ret_buf);
+
+                        for(var k = 0; k < temp_buf.byteLength; k++)
+                            ret_view[k + 1] = temp_view[k];
+
+                        ret_view[0] = 0x00;
+                    }
+
+                    return ret_buf;
+                }
+            }
+
+            big_int *= Math.pow(2, 8);
+        }
+
+        return (new ArrayBuffer(0));
+    }
+    //**************************************************************************************
+    function util_decode_tc()
+    {
+        /// <summary>Decoding of "two complement" values</summary>
+        /// <remarks>The function must be called in scope of instance of "hex_block" class ("value_hex" and "warnings" properties must be present)</remarks>
+
+        var buf = new Uint8Array(this.value_hex);
+
+        if(this.value_hex.byteLength >= 2)
+        {
+            var condition_1 = (buf[0] == 0xFF) && (buf[1] & 0x80);
+            var condition_2 = (buf[0] == 0x00) && ((buf[1] & 0x80) == 0x00);
+
+            if(condition_1 || condition_2)
+                this.warnings.push("Needlessly long format");
+        }
+
+        // #region Create big part of the integer
+        var big_int_buffer = new ArrayBuffer(this.value_hex.byteLength);
+        var big_int_view = new Uint8Array(big_int_buffer);
+        for(var i = 0; i < this.value_hex.byteLength; i++)
+            big_int_view[i] = 0;
+
+        big_int_view[0] = (buf[0] & 0x80); // mask only the biggest bit
+
+        var big_int = util_frombase(big_int_view, 8);
+        // #endregion   
+
+        // #region Create small part of the integer 
+        var small_int_buffer = new ArrayBuffer(this.value_hex.byteLength);
+        var small_int_view = new Uint8Array(small_int_buffer);
+        for(var j = 0; j < this.value_hex.byteLength; j++)
+            small_int_view[j] = buf[j];
+
+        small_int_view[0] &= 0x7F; // mask biggest bit
+
+        var small_int = util_frombase(small_int_view, 8);
+        // #endregion 
+
+        return (small_int - big_int);
+    }
+    //**************************************************************************************
+    function util_copybuf(input_buffer)
+    {
+        /// <summary>Creating a copy of input ArrayBuffer</summary>
+        /// <param name="input_buffer" type="ArrayBuffer">ArrayBuffer for coping</param>
+
+        if(check_buffer_params(input_buffer, 0, input_buffer.byteLength) === false)
+            return (new ArrayBuffer(0));
+
+        var input_view = new Uint8Array(input_buffer);
+
+        var ret_buf = new ArrayBuffer(input_buffer.byteLength);
+        var ret_view = new Uint8Array(ret_buf);
+
+        for(var i = 0; i < input_buffer.byteLength; i++)
+            ret_view[i] = input_view[i];
+
+        return ret_buf;
+    }
+    //**************************************************************************************
+    function util_copybuf_offset(input_buffer, input_offset, input_length)
+    {
+        /// <summary>Creating a copy of input ArrayBuffer</summary>
+        /// <param name="input_buffer" type="ArrayBuffer">ArrayBuffer for coping</param>
+
+        if(check_buffer_params(input_buffer, input_offset, input_length) === false)
+            return (new ArrayBuffer(0));
+
+        var input_view = new Uint8Array(input_buffer, input_offset, input_length);
+
+        var ret_buf = new ArrayBuffer(input_length);
+        var ret_view = new Uint8Array(ret_buf);
+
+        for(var i = 0; i < input_length; i++)
+            ret_view[i] = input_view[i];
+
+        return ret_buf;
+    }
+    //**************************************************************************************
+    function util_concatbuf(input_buf1, input_buf2)
+    {
+        /// <summary>Concatenate two ArrayBuffers</summary>
+        /// <param name="input_buf1" type="ArrayBuffer">First ArrayBuffer (first part of concatenated array)</param>
+        /// <param name="input_buf2" type="ArrayBuffer">Second ArrayBuffer (second part of concatenated array)</param>
+
+        var input_view1 = new Uint8Array(input_buf1);
+        var input_view2 = new Uint8Array(input_buf2);
+
+        var ret_buf = new ArrayBuffer(input_buf1.byteLength + input_buf2.byteLength);
+        var ret_view = new Uint8Array(ret_buf);
+
+        for(var i = 0; i < input_buf1.byteLength; i++)
+            ret_view[i] = input_view1[i];
+
+        for(var j = 0; j < input_buf2.byteLength; j++)
+            ret_view[input_buf1.byteLength + j] = input_view2[j];
+
+        return ret_buf;
+    }
+    //**************************************************************************************
+    function check_buffer_params(input_buffer, input_offset, input_length)
+    {
+        if((input_buffer instanceof ArrayBuffer) === false)
+        {
+            this.error = "Wrong parameter: input_buffer must be \"ArrayBuffer\"";
+            return false;
+        }
+
+        if(input_buffer.byteLength === 0)
+        {
+            this.error = "Wrong parameter: input_buffer has zero length";
+            return false;
+        }
+
+        if(input_offset < 0)
+        {
+            this.error = "Wrong parameter: input_offset less than zero";
+            return false;
+        }
+
+        if(input_length < 0)
+        {
+            this.error = "Wrong parameter: input_length less than zero";
+            return false;
+        }
+
+        if((input_buffer.byteLength - input_offset - input_length) < 0)
+        {
+            this.error = "End of input reached before message was fully decoded (inconsistent offset and length values)";
+            return false;
+        }
+
+        return true;
+    }
+    //**************************************************************************************
+    function to_hex_codes(input_buffer, input_offset, input_lenght)
+    {
+        if(check_buffer_params(input_buffer, input_offset, input_lenght) === false)
+            return "";
+
+        var result = "";
+
+        var int_buffer = new Uint8Array(input_buffer, input_offset, input_lenght);
+        
+        for(var i = 0; i < int_buffer.length; i++)
+        {
+            var str = int_buffer[i].toString(16).toUpperCase();
+            result = result + ((str.length === 1) ? " 0" : " ") + str;
+        }
+
+        return result;
+    }
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Declaration of base block class 
+    //**************************************************************************************
+    local.base_block =
+    function()
+    {
+        /// <summary>General class of all ASN.1 blocks</summary>
+
+        if(arguments[0] instanceof Object)
+        {
+            this.block_length = in_window.org.pkijs.getValue(arguments[0], "block_length", 0);
+            this.error = in_window.org.pkijs.getValue(arguments[0], "error", new String());
+            this.warnings = in_window.org.pkijs.getValue(arguments[0], "warnings", new Array());
+            if("value_before_decode" in arguments[0])
+                this.value_before_decode = util_copybuf(arguments[0].value_before_decode);
+            else
+                this.value_before_decode = new ArrayBuffer(0);
+        }
+        else
+        {
+            this.block_length = 0;
+            this.error = new String();
+            this.warnings = new Array();
+            /// <field>Copy of the value of incoming ArrayBuffer done before decoding</field>
+            this.value_before_decode = new ArrayBuffer(0);
+        }
+    };
+    //**************************************************************************************
+    local.base_block.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "base_block";
+    };
+    //**************************************************************************************
+    local.base_block.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        return {
+            block_name: local.base_block.prototype.block_name.call(this),
+            block_length: this.block_length,
+            error: this.error,
+            warnings: this.warnings,
+            value_before_decode: in_window.org.pkijs.bufferToHexCodes(this.value_before_decode, 0, this.value_before_decode.byteLength)
+        };
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Declaration of hex block class 
+    //**************************************************************************************
+    local.hex_block =
+    function()
+    {
+        /// <summary>Descendant of "base_block" with internal ArrayBuffer. Need to have it in case it is not possible to store ASN.1 value in native formats</summary>
+
+        local.base_block.call(this, arguments[0]);
+
+        if(arguments[0] instanceof Object)
+        {
+            this.is_hex_only = in_window.org.pkijs.getValue(arguments[0], "is_hex_only", false);
+            if("value_hex" in arguments[0])
+                this.value_hex = util_copybuf(arguments[0].value_hex);
+            else
+                this.value_hex = new ArrayBuffer(0);
+        }
+        else
+        {
+            this.is_hex_only = false;
+            this.value_hex = new ArrayBuffer(0);
+        }
+    };
+    //**************************************************************************************
+    local.hex_block.prototype = new local.base_block();
+    local.hex_block.constructor = local.hex_block;
+    //**************************************************************************************
+    local.hex_block.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "hex_block";
+    };
+    //**************************************************************************************
+    local.hex_block.prototype.fromBER =
+    function(input_buffer, input_offset, input_length)
+    {
+        /// <summary>Base function for converting block from BER encoded array of bytes</summary>
+        /// <param name="input_buffer" type="ArrayBuffer">ASN.1 BER encoded array</param>
+        /// <param name="input_offset" type="Number">Offset in ASN.1 BER encoded array where decoding should be started</param>
+        /// <param name="input_length" type="Number">Maximum length of array of bytes which can be using in this function</param>
+
+        // #region Basic check for parameters 
+        if(check_buffer_params.call(this, input_buffer, input_offset, input_length) === false)
+            return (-1);
+        // #endregion 
+
+        // #region Getting Uint8Array from ArrayBuffer 
+        var int_buffer = new Uint8Array(input_buffer, input_offset, input_length);
+        // #endregion 
+
+        // #region Initial checks 
+        if(int_buffer.length == 0)
+        {
+            this.warnings.push("Zero buffer length");
+            return input_offset;
+        }
+        // #endregion 
+
+        // #region Copy input buffer to internal buffer 
+        this.value_hex = new ArrayBuffer(input_length);
+        var view = new Uint8Array(this.value_hex);
+
+        for(var i = 0; i < int_buffer.length; i++)
+            view[i] = int_buffer[i];
+        // #endregion 
+
+        this.block_length = input_length;
+
+        return (input_offset + input_length);
+    };
+    //**************************************************************************************
+    local.hex_block.prototype.toBER =
+    function(size_only)
+    {
+        /// <summary>Encoding of current ASN.1 block into ASN.1 encoded array (BER rules)</summary>
+        /// <param name="size_only" type="Boolean">Flag that we need only a size of encoding, not a real array of bytes</param>
+
+        if(typeof size_only === "undefined")
+            size_only = false;
+
+        if(this.is_hex_only !== true)
+        {
+            this.error = "Flag \"is_hex_only\" is not set, abort";
+            return (new ArrayBuffer(0));
+        }
+
+        var ret_buf = new ArrayBuffer(this.value_hex.byteLength);
+
+        if(size_only === true)
+            return ret_buf;
+
+        var ret_view = new Uint8Array(ret_buf);
+        var cur_view = new Uint8Array(this.value_hex);
+
+        for(var i = 0; i < cur_view.length; i++)
+            ret_view[i] = cur_view[i];
+
+        return ret_buf;
+    };
+    //**************************************************************************************
+    local.hex_block.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = local.base_block.prototype.toJSON.call(this);
+
+        _object.block_name = local.hex_block.prototype.block_name.call(this);
+        _object.is_hex_only = this.is_hex_only;
+        _object.value_hex = in_window.org.pkijs.bufferToHexCodes(this.value_hex, 0, this.value_hex.byteLength);
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Declaration of identification block class 
+    //**************************************************************************************
+    local.identification_block =
+    function()
+    {
+        /// <summary>Base class of ASN.1 "identification block"</summary>
+
+        local.hex_block.call(this, arguments[0]);
+
+        this.tag_class = (-1);
+        this.tag_number = (-1);
+        this.is_constructed = false;
+
+        if(arguments[0] instanceof Object)
+        {
+            if("id_block" in arguments[0])
+            {
+                // #region Properties from hex_block class 
+                this.is_hex_only = in_window.org.pkijs.getValue(arguments[0].id_block, "is_hex_only", false);
+                this.value_hex = in_window.org.pkijs.getValue(arguments[0].id_block, "value_hex", new ArrayBuffer(0));
+                // #endregion   
+
+                this.tag_class = in_window.org.pkijs.getValue(arguments[0].id_block, "tag_class", (-1));
+                this.tag_number = in_window.org.pkijs.getValue(arguments[0].id_block, "tag_number", (-1));
+                this.is_constructed = in_window.org.pkijs.getValue(arguments[0].id_block, "is_constructed", false);
+            }
+        }
+    };
+    //**************************************************************************************
+    local.identification_block.prototype = new local.hex_block();
+    local.identification_block.constructor = local.identification_block;
+    //**************************************************************************************
+    local.identification_block.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "identification_block";
+    };
+    //**************************************************************************************
+    local.identification_block.prototype.toBER =
+    function(size_only)
+    {
+        /// <summary>Encoding of current ASN.1 block into ASN.1 encoded array (BER rules)</summary>
+        /// <param name="size_only" type="Boolean">Flag that we need only a size of encoding, not a real array of bytes</param>
+
+        if(typeof size_only === "undefined")
+            size_only = false;
+
+        var first_octet = 0;
+
+        switch(this.tag_class)
+        {
+            case 1:
+                first_octet |= 0x00; // UNIVERSAL
+                break;
+            case 2:
+                first_octet |= 0x40; // APPLICATION
+                break;
+            case 3:
+                first_octet |= 0x80; // CONTEXT-SPECIFIC
+                break;
+            case 4:
+                first_octet |= 0xC0; // PRIVATE
+                break;
+            default:
+                this.error = "Unknown tag class";
+                return (new ArrayBuffer(0));
+        }
+
+        if(this.is_constructed)
+            first_octet |= 0x20;
+
+        if((this.tag_number < 31) && (!this.is_hex_only))
+        {
+            var ret_buf = new ArrayBuffer(1);
+            var ret_view = new Uint8Array(ret_buf);
+
+            if(!size_only)
+            {
+                var number = this.tag_number;
+                number &= 0x1F;
+                first_octet |= number;
+
+                ret_view[0] = first_octet;
+            }
+
+            return ret_buf;
+        }
+        else
+        {
+            if(this.is_hex_only === false)
+            {
+                var encoded_buf = util_tobase(this.tag_number, 7);
+                var encoded_view = new Uint8Array(encoded_buf);
+                var size = encoded_buf.byteLength;
+
+                var ret_buf = new ArrayBuffer(size + 1);
+                var ret_view = new Uint8Array(ret_buf);
+
+                ret_view[0] = (first_octet | 0x1F);
+
+                if(!size_only)
+                {
+                    for(var i = 0; i < (size - 1) ; i++)
+                        ret_view[i + 1] = encoded_view[i] | 0x80;
+
+                    ret_view[size] = encoded_view[size - 1];
+                }
+
+                return ret_buf;
+            }
+            else
+            {
+                var ret_buf = new ArrayBuffer(this.value_hex.byteLength + 1);
+                var ret_view = new Uint8Array(ret_buf);
+
+                ret_view[0] = (first_octet | 0x1F);
+
+                if(size_only === false)
+                {
+                    var cur_view = new Uint8Array(this.value_hex);
+
+                    for(var i = 0; i < (cur_view.length - 1); i++)
+                        ret_view[i + 1] = cur_view[i] | 0x80;
+
+                    ret_view[this.value_hex.byteLength] = cur_view[cur_view.length - 1];
+                }
+
+                return ret_buf;
+            }
+        }
+    };
+    //**************************************************************************************
+    local.identification_block.prototype.fromBER =
+    function(input_buffer, input_offset, input_length)
+    {
+        /// <summary>Base function for converting block from BER encoded array of bytes</summary>
+        /// <param name="input_buffer" type="ArrayBuffer">ASN.1 BER encoded array</param>
+        /// <param name="input_offset" type="Number">Offset in ASN.1 BER encoded array where decoding should be started</param>
+        /// <param name="input_length" type="Number">Maximum length of array of bytes which can be using in this function</param>
+
+        // #region Basic check for parameters 
+        if(check_buffer_params.call(this, input_buffer, input_offset, input_length) === false)
+            return (-1);
+        // #endregion 
+
+        // #region Getting Uint8Array from ArrayBuffer 
+        var int_buffer = new Uint8Array(input_buffer, input_offset, input_length);
+        // #endregion 
+
+        // #region Initial checks 
+        if(int_buffer.length == 0)
+        {
+            this.error = "Zero buffer length";
+            return (-1);
+        }
+        // #endregion 
+
+        // #region Find tag class 
+        var tag_class_mask = int_buffer[0] & 0xC0;
+
+        switch(tag_class_mask)
+        {
+            case 0x00:
+                this.tag_class = (1); // UNIVERSAL
+                break;
+            case 0x40:
+                this.tag_class = (2); // APPLICATION
+                break;
+            case 0x80:
+                this.tag_class = (3); // CONTEXT-SPECIFIC
+                break;
+            case 0xC0:
+                this.tag_class = (4); // PRIVATE
+                break;
+            default:
+                this.error = "Unknown tag class";
+                return ( -1 );
+        }
+        // #endregion 
+
+        // #region Find it's constructed or not 
+        this.is_constructed = (int_buffer[0] & 0x20) == 0x20;
+        // #endregion 
+
+        // #region Find tag number 
+        this.is_hex_only = false;
+
+        var tag_number_mask = int_buffer[0] & 0x1F;
+
+        // #region Simple case (tag number < 31)
+        if(tag_number_mask != 0x1F) 
+        {
+            this.tag_number = (tag_number_mask);
+            this.block_length = 1;
+        }
+            // #endregion 
+        // #region Tag number bigger or equal to 31 
+        else
+        {
+            var count = 1;
+
+            this.value_hex = new ArrayBuffer(255);
+            var tag_number_buffer_max_length = 255;
+            var int_tag_number_buffer = new Uint8Array(this.value_hex);
+
+            while(int_buffer[count] & 0x80)
+            {
+                int_tag_number_buffer[count - 1] = int_buffer[count] & 0x7F;
+                count++;
+
+                if(count >= int_buffer.length)
+                {
+                    this.error = "End of input reached before message was fully decoded";
+                    return (-1);
+                }
+
+                // #region In case if tag number length is greater than 255 bytes (rare but possible case)
+                if(count == tag_number_buffer_max_length)
+                {
+                    tag_number_buffer_max_length += 255;
+
+                    var temp_buffer = new ArrayBuffer(tag_number_buffer_max_length);
+                    var temp_buffer_view = new Uint8Array(temp_buffer);
+
+                    for(var i = 0; i < int_tag_number_buffer.length; i++)
+                        temp_buffer_view[i] = int_tag_number_buffer[i];
+
+                    this.value_hex = new ArrayBuffer(tag_number_buffer_max_length);
+                    int_tag_number_buffer = new Uint8Array(this.value_hex);
+                }
+                // #endregion 
+            }
+
+            this.block_length = (count + 1);
+            int_tag_number_buffer[count - 1] = int_buffer[count] & 0x7F; // Write last byte to buffer
+
+            // #region Cut buffer 
+            var temp_buffer = new ArrayBuffer(count);
+            var temp_buffer_view = new Uint8Array(temp_buffer);
+            for(var i = 0; i < count; i++)
+                temp_buffer_view[i] = int_tag_number_buffer[i];
+
+            this.value_hex = new ArrayBuffer(count);
+            int_tag_number_buffer = new Uint8Array(this.value_hex);
+            int_tag_number_buffer.set(temp_buffer_view);
+            // #endregion 
+
+            // #region Try to convert long tag number to short form 
+            if(this.block_length <= 9)
+                this.tag_number = util_frombase(int_tag_number_buffer, 7);
+            else
+            {
+                this.is_hex_only = true;
+                this.warnings.push("Tag too long, represented as hex-coded");
+            }
+            // #endregion 
+        }
+        // #endregion 
+        // #endregion 
+
+        // #region Check if constructed encoding was using for primitive type 
+        if(((this.tag_class == 1)) &&
+            (this.is_constructed))
+        {
+            switch(this.tag_number)
+            {
+                case 1:  // BOOLEAN
+                case 2:  // REAL
+                case 5:  // NULL
+                case 6:  // OBJECT IDENTIFIER
+                case 9:  // REAL
+                case 14: // TIME
+                case 23:
+                case 24:
+                case 31:
+                case 32:
+                case 33:
+                case 34:
+                    this.error = "Constructed encoding used for primitive type";
+                    return (-1);
+                default:
+                    ;
+            }
+        }
+        // #endregion 
+
+        return ( input_offset + this.block_length ); // Return current offset in input buffer
+    };
+    //**************************************************************************************
+    local.identification_block.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = local.hex_block.prototype.toJSON.call(this);
+
+        _object.block_name = local.identification_block.prototype.block_name.call(this);
+        _object.tag_class = this.tag_class;
+        _object.tag_number = this.tag_number;
+        _object.is_constructed = this.is_constructed;
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Declaration of length block class 
+    //**************************************************************************************
+    local.length_block =
+    function()
+    {
+        /// <summary>Base class of ASN.1 "length block"</summary>
+
+        local.base_block.call(this, arguments[0]);
+
+        this.is_indefinite_form = false;
+        this.long_form_used = false;
+        this.length = (0);
+
+        if(arguments[0] instanceof Object)
+        {
+            if("len_block" in arguments[0])
+            {
+                this.is_indefinite_form = in_window.org.pkijs.getValue(arguments[0].len_block, "is_indefinite_form", false);
+                this.long_form_used = in_window.org.pkijs.getValue(arguments[0].len_block, "long_form_used", false);
+                this.length = in_window.org.pkijs.getValue(arguments[0].len_block, "length", 0);
+            }
+        }
+    };
+    //**************************************************************************************
+    local.length_block.prototype = new local.base_block();
+    local.length_block.constructor = local.length_block;
+    //**************************************************************************************
+    local.length_block.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "length_block";
+    };
+    //**************************************************************************************
+    local.length_block.prototype.fromBER =
+    function(input_buffer, input_offset, input_length)
+    {
+        /// <summary>Base function for converting block from BER encoded array of bytes</summary>
+        /// <param name="input_buffer" type="ArrayBuffer">ASN.1 BER encoded array</param>
+        /// <param name="input_offset" type="Number">Offset in ASN.1 BER encoded array where decoding should be started</param>
+        /// <param name="input_length" type="Number">Maximum length of array of bytes which can be using in this function</param>
+
+        // #region Basic check for parameters 
+        if(check_buffer_params.call(this, input_buffer, input_offset, input_length) === false)
+            return (-1);
+        // #endregion 
+
+        // #region Getting Uint8Array from ArrayBuffer 
+        var int_buffer = new Uint8Array(input_buffer, input_offset, input_length);
+        // #endregion 
+
+        // #region Initial checks 
+        if(int_buffer.length == 0)
+        {
+            this.error = "Zero buffer length";
+            return (-1);
+        }
+
+        if(int_buffer[0] == 0xFF)
+        {
+            this.error = "Length block 0xFF is reserved by standard";
+            return (-1);
+        }
+        // #endregion 
+
+        // #region Check for length form type 
+        this.is_indefinite_form = int_buffer[0] == 0x80;
+        // #endregion 
+
+        // #region Stop working in case of indefinite length form 
+        if(this.is_indefinite_form == true)
+        {
+            this.block_length = 1;
+            return (input_offset + this.block_length);
+        }
+        // #endregion 
+
+        // #region Check is long form of length encoding using 
+        this.long_form_used = !!(int_buffer[0] & 0x80);
+        // #endregion 
+
+        // #region Stop working in case of short form of length value 
+        if(this.long_form_used == false)
+        {
+            this.length = (int_buffer[0]);
+            this.block_length = 1;
+            return (input_offset + this.block_length);
+        }
+        // #endregion 
+
+        // #region Calculate length value in case of long form 
+        var count = int_buffer[0] & 0x7F;
+
+        if(count > 8) // Too big length value
+        {
+            this.error = "Too big integer";
+            return (-1);
+        }
+
+        if((count + 1) > int_buffer.length)
+        {
+            this.error = "End of input reached before message was fully decoded";
+            return (-1);
+        }
+
+        var length_buffer_view = new Uint8Array(count);
+
+        for(var i = 0; i < count; i++)
+            length_buffer_view[i] = int_buffer[i + 1];
+
+        if(length_buffer_view[count - 1] == 0x00)
+            this.warnings.push("Needlessly long encoded length");
+
+        this.length = util_frombase(length_buffer_view, 8);
+
+        if(this.long_form_used && (this.length <= 127))
+            this.warnings.push("Unneccesary usage of long length form");
+
+        this.block_length = count + 1;
+        // #endregion 
+
+        return (input_offset + this.block_length); // Return current offset in input buffer
+    };
+    //**************************************************************************************
+    local.length_block.prototype.toBER =
+    function(size_only)
+    {
+        /// <summary>Encoding of current ASN.1 block into ASN.1 encoded array (BER rules)</summary>
+        /// <param name="size_only" type="Boolean">Flag that we need only a size of encoding, not a real array of bytes</param>
+
+        if(typeof size_only === "undefined")
+            size_only = false;
+
+        if(this.length > 127)
+            this.long_form_used = true;
+
+        if(this.is_indefinite_form)
+        {
+            var ret_buf = new ArrayBuffer(1);
+
+            if(size_only === false)
+            {
+                var ret_view = new Uint8Array(ret_buf);
+                ret_view[0] = 0x80;
+            }
+
+            return ret_buf;
+        }
+
+        if(this.long_form_used === true)
+        {
+            var encoded_buf = util_tobase(this.length, 8);
+
+            if(encoded_buf.byteLength > 127)
+            {
+                this.error = "Too big length";
+                return (new ArrayBuffer(0));
+            }
+
+            var ret_buf = new ArrayBuffer(encoded_buf.byteLength + 1);
+
+            if(size_only === true)
+                return ret_buf;
+
+            var encoded_view = new Uint8Array(encoded_buf);
+            var ret_view = new Uint8Array(ret_buf);
+
+            ret_view[0] = encoded_buf.byteLength | 0x80;
+
+            for(var i = 0; i < encoded_buf.byteLength; i++)
+                ret_view[i + 1] = encoded_view[i];
+
+            return ret_buf;
+        }
+        else
+        {
+            var ret_buf = new ArrayBuffer(1);
+
+            if(size_only === false)
+            {
+                var ret_view = new Uint8Array(ret_buf);
+
+                ret_view[0] = this.length;
+            }
+
+            return ret_buf;
+        }
+
+        return (new ArrayBuffer(0));
+    };
+    //**************************************************************************************
+    local.length_block.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = local.base_block.prototype.toJSON.call(this);
+
+        _object.block_name = local.length_block.prototype.block_name.call(this);
+        _object.is_indefinite_form = this.is_indefinite_form;
+        _object.long_form_used = this.long_form_used;
+        _object.length = this.length;
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Declaration of value block class 
+    //**************************************************************************************
+    local.value_block =
+    function()
+    {
+        /// <summary>Generic class of ASN.1 "value block"</summary>
+        local.base_block.call(this, arguments[0]);
+    };
+    //**************************************************************************************
+    local.value_block.prototype = new local.base_block();
+    local.value_block.constructor = local.value_block;
+    //**************************************************************************************
+    local.value_block.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "value_block";
+    };
+    //**************************************************************************************
+    local.value_block.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = local.base_block.prototype.toJSON.call(this);
+
+        _object.block_name = local.value_block.prototype.block_name.call(this);
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Declaration of basic ASN.1 block class 
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.ASN1_block =
+    function()
+    {
+        /// <summary>Base class of ASN.1 block (identification block + length block + value block)</summary>
+
+        local.base_block.call(this, arguments[0]);
+
+        if(arguments[0] instanceof Object)
+        {
+            this.name = in_window.org.pkijs.getValue(arguments[0], "name", "");
+            this.optional = in_window.org.pkijs.getValue(arguments[0], "optional", false);
+
+            if("primitive_schema" in arguments[0])
+                this.primitive_schema = arguments[0].primitive_schema;
+        }
+
+        this.id_block = new local.identification_block(arguments[0]);
+        this.len_block = new local.length_block(arguments[0]);
+        this.value_block = new local.value_block(arguments[0]);
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.ASN1_block.prototype = new local.base_block();
+    in_window.org.pkijs.asn1.ASN1_block.constructor = in_window.org.pkijs.asn1.ASN1_block;
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.ASN1_block.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "ASN1_block";
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.ASN1_block.prototype.fromBER =
+    function(input_buffer, input_offset, input_length)
+    {
+        /// <summary>Base function for converting block from BER encoded array of bytes</summary>
+        /// <param name="input_buffer" type="ArrayBuffer">ASN.1 BER encoded array</param>
+        /// <param name="input_offset" type="Number">Offset in ASN.1 BER encoded array where decoding should be started</param>
+        /// <param name="input_length" type="Number">Maximum length of array of bytes which can be using in this function</param>
+
+        var result_offset = this.value_block.fromBER(input_buffer, input_offset, (this.len_block.is_indefinite_form == true) ? input_length : this.len_block.length);
+        if(result_offset == (-1))
+        {
+            this.error = this.value_block.error;
+            return result_offset;
+        }
+
+        if(this.id_block.error.length == 0)
+            this.block_length += this.id_block.block_length;
+
+        if(this.len_block.error.length == 0)
+            this.block_length += this.len_block.block_length;
+
+        if(this.value_block.error.length == 0)
+            this.block_length += this.value_block.block_length;
+
+        return result_offset;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.ASN1_block.prototype.toBER =
+    function(size_only)
+    {
+        /// <summary>Encoding of current ASN.1 block into ASN.1 encoded array (BER rules)</summary>
+        /// <param name="size_only" type="Boolean">Flag that we need only a size of encoding, not a real array of bytes</param>
+
+        if(typeof size_only === "undefined")
+            size_only = false;
+
+        var ret_buf;
+
+        var id_block_buf = this.id_block.toBER(size_only);
+        var value_block_size_buf = this.value_block.toBER(true);
+
+        this.len_block.length = value_block_size_buf.byteLength;
+        var len_block_buf = this.len_block.toBER(size_only);
+
+        ret_buf = util_concatbuf(id_block_buf, len_block_buf);
+
+        var value_block_buf;
+
+        if(size_only === false)
+            value_block_buf = this.value_block.toBER(size_only);
+        else
+            value_block_buf = new ArrayBuffer(this.len_block.length);
+
+        ret_buf = util_concatbuf(ret_buf, value_block_buf);
+
+        if(this.len_block.is_indefinite_form === true)
+        {
+            var indef_buf = new ArrayBuffer(2);
+
+            if(size_only === false)
+            {
+                var indef_view = new Uint8Array(indef_buf);
+
+                indef_view[0] = 0x00;
+                indef_view[1] = 0x00;
+            }
+
+            ret_buf = util_concatbuf(ret_buf, indef_buf);
+        }
+
+        return ret_buf;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.ASN1_block.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = local.base_block.prototype.toJSON.call(this);
+
+        _object.block_name = in_window.org.pkijs.asn1.ASN1_block.prototype.block_name.call(this);
+        _object.id_block = this.id_block.toJSON();
+        _object.len_block = this.len_block.toJSON();
+        _object.value_block = this.value_block.toJSON();
+
+        if("name" in this)
+            _object.name = this.name;
+        if("optional" in this)
+            _object.optional = this.optional;
+        if("primitive_schema" in this)
+            _object.primitive_schema = this.primitive_schema.toJSON();
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Declaration of basic block for all PRIMITIVE types 
+    //**************************************************************************************
+    local.ASN1_PRIMITIVE_value_block =
+    function()
+    {
+        /// <summary>Base class of ASN.1 value block for primitive values (non-constructive encoding)</summary>
+
+        local.value_block.call(this, arguments[0]);
+
+        if(arguments[0] instanceof Object)
+        {
+            // #region Variables from "hex_block" class 
+            if("value_hex" in arguments[0])
+                this.value_hex = util_copybuf(arguments[0].value_hex);
+            else
+                this.value_hex = new ArrayBuffer(0);
+
+            this.is_hex_only = in_window.org.pkijs.getValue(arguments[0], "is_hex_only", true);
+            // #endregion 
+        }
+        else
+        {
+            // #region Variables from "hex_block" class 
+            this.value_hex = new ArrayBuffer(0);
+            this.is_hex_only = true;
+            // #endregion 
+        }
+    };
+    //**************************************************************************************
+    local.ASN1_PRIMITIVE_value_block.prototype = new local.value_block();
+    local.ASN1_PRIMITIVE_value_block.constructor = local.ASN1_PRIMITIVE_value_block;
+    //**************************************************************************************
+    local.ASN1_PRIMITIVE_value_block.prototype.fromBER =
+    function(input_buffer, input_offset, input_length)
+    {
+        /// <summary>Base function for converting block from BER encoded array of bytes</summary>
+        /// <param name="input_buffer" type="ArrayBuffer">ASN.1 BER encoded array</param>
+        /// <param name="input_offset" type="Number">Offset in ASN.1 BER encoded array where decoding should be started</param>
+        /// <param name="input_length" type="Number">Maximum length of array of bytes which can be using in this function</param>
+
+        // #region Basic check for parameters 
+        if(check_buffer_params.call(this, input_buffer, input_offset, input_length) === false)
+            return (-1);
+        // #endregion 
+
+        // #region Getting Uint8Array from ArrayBuffer 
+        var int_buffer = new Uint8Array(input_buffer, input_offset, input_length);
+        // #endregion 
+
+        // #region Initial checks 
+        if(int_buffer.length == 0)
+        {
+            this.warnings.push("Zero buffer length");
+            return input_offset;
+        }
+        // #endregion 
+
+        // #region Copy input buffer into internal buffer 
+        this.value_hex = new ArrayBuffer(int_buffer.length);
+        var value_hex_view = new Uint8Array(this.value_hex);
+
+        for(var i = 0; i < int_buffer.length; i++)
+            value_hex_view[i] = int_buffer[i];
+        // #endregion 
+
+        this.block_length = input_length;
+
+        return (input_offset + input_length);
+    };
+    //**************************************************************************************
+    local.ASN1_PRIMITIVE_value_block.prototype.toBER =
+    function(size_only)
+    {
+        /// <summary>Encoding of current ASN.1 block into ASN.1 encoded array (BER rules)</summary>
+        /// <param name="size_only" type="Boolean">Flag that we need only a size of encoding, not a real array of bytes</param>
+
+        return util_copybuf(this.value_hex);
+    };
+    //**************************************************************************************
+    local.ASN1_PRIMITIVE_value_block.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "ASN1_PRIMITIVE_value_block";
+    };
+    //**************************************************************************************
+    local.ASN1_PRIMITIVE_value_block.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = local.value_block.prototype.toJSON.call(this);
+
+        _object.block_name = local.ASN1_PRIMITIVE_value_block.prototype.block_name.call(this);
+        _object.value_hex = in_window.org.pkijs.bufferToHexCodes(this.value_hex, 0, this.value_hex.byteLength);
+        _object.is_hex_only = this.is_hex_only;
+
+        return _object;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.ASN1_PRIMITIVE =
+    function()
+    {
+        /// <summary>Base class of ASN.1 block for primitive values (non-constructive encoding)</summary>
+
+        in_window.org.pkijs.asn1.ASN1_block.call(this, arguments[0]);
+
+        this.id_block.is_constructed = false;
+        this.value_block = new local.ASN1_PRIMITIVE_value_block(arguments[0]);
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.ASN1_PRIMITIVE.prototype = new in_window.org.pkijs.asn1.ASN1_block();
+    in_window.org.pkijs.asn1.ASN1_PRIMITIVE.constructor = in_window.org.pkijs.asn1.ASN1_PRIMITIVE;
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.ASN1_PRIMITIVE.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "PRIMITIVE";
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.ASN1_PRIMITIVE.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = in_window.org.pkijs.asn1.ASN1_block.prototype.toJSON.call(this);
+
+        _object.block_name = in_window.org.pkijs.asn1.ASN1_PRIMITIVE.prototype.block_name.call(this);
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Declaration of basic block for all CONSTRUCTED types 
+    //**************************************************************************************
+    local.ASN1_CONSTRUCTED_value_block =
+    function()
+    {
+        /// <summary>Base class of ASN.1 value block for constructive values (constructive encoding)</summary>
+
+        local.value_block.call(this, arguments[0]);
+
+        if(arguments[0] instanceof Object)
+        {
+            this.value = in_window.org.pkijs.getValue(arguments[0], "value", new Array());
+            this.is_indefinite_form = in_window.org.pkijs.getValue(arguments[0], "is_indefinite_form", false);
+        }
+        else
+        {
+            this.value = new Array();
+            this.is_indefinite_form = false;
+        }
+    };
+    //**************************************************************************************
+    local.ASN1_CONSTRUCTED_value_block.prototype = new local.value_block();
+    local.ASN1_CONSTRUCTED_value_block.constructor = local.ASN1_CONSTRUCTED_value_block;
+    //**************************************************************************************
+    local.ASN1_CONSTRUCTED_value_block.prototype.fromBER =
+    function(input_buffer, input_offset, input_length)
+    {
+        /// <summary>Base function for converting block from BER encoded array of bytes</summary>
+        /// <param name="input_buffer" type="ArrayBuffer">ASN.1 BER encoded array</param>
+        /// <param name="input_offset" type="Number">Offset in ASN.1 BER encoded array where decoding should be started</param>
+        /// <param name="input_length" type="Number">Maximum length of array of bytes which can be using in this function</param>
+
+        // #region Store initial offset and length 
+        var initial_offset = input_offset;
+        var initial_length = input_length;
+        // #endregion 
+
+        // #region Basic check for parameters 
+        if(check_buffer_params.call(this, input_buffer, input_offset, input_length) === false)
+            return (-1);
+        // #endregion 
+
+        // #region Getting Uint8Array from ArrayBuffer 
+        var int_buffer = new Uint8Array(input_buffer, input_offset, input_length);
+        // #endregion 
+
+        // #region Initial checks 
+        if(int_buffer.length == 0)
+        {
+            this.warnings.push("Zero buffer length");
+            return input_offset;
+        }
+        // #endregion 
+
+        // #region Aux function 
+        function check_len(_indefinite_length, _length)
+        {
+            if(_indefinite_length == true)
+                return 1;
+
+            return _length;
+        }
+        // #endregion 
+
+        var current_offset = input_offset;
+
+        while(check_len(this.is_indefinite_form, input_length) > 0)
+        {
+            var return_object = fromBER_raw(input_buffer, current_offset, input_length);
+            if(return_object.offset == (-1))
+            {
+                this.error = return_object.result.error;
+                this.warnings.concat(return_object.result.warnings);
+                return (-1);
+            }
+
+            current_offset = return_object.offset;
+
+            this.block_length += return_object.result.block_length;
+            input_length -= return_object.result.block_length;
+
+            this.value.push(return_object.result);
+
+            if((this.is_indefinite_form == true) && (return_object.result.block_name() == in_window.org.pkijs.asn1.EOC.prototype.block_name()))
+                break;
+        }
+
+        if(this.is_indefinite_form == true)
+        {
+            if(this.value[this.value.length - 1].block_name() == in_window.org.pkijs.asn1.EOC.prototype.block_name())
+                this.value.pop();
+            else
+                this.warnings.push("No EOC block encoded");
+        }
+
+        // #region Copy "input_buffer" to "value_before_decode" 
+        this.value_before_decode = util_copybuf_offset(input_buffer, initial_offset, initial_length);
+        // #endregion 
+
+        return current_offset;
+    };
+    //**************************************************************************************
+    local.ASN1_CONSTRUCTED_value_block.prototype.toBER =
+    function(size_only)
+    {
+        /// <summary>Encoding of current ASN.1 block into ASN.1 encoded array (BER rules)</summary>
+        /// <param name="size_only" type="Boolean">Flag that we need only a size of encoding, not a real array of bytes</param>
+
+        if(typeof size_only === "undefined")
+            size_only = false;
+
+        var ret_buf = new ArrayBuffer(0);
+
+        for(var i = 0; i < this.value.length; i++)
+        {
+            var value_buf = this.value[i].toBER(size_only);
+            ret_buf = util_concatbuf(ret_buf, value_buf);
+        }
+
+        return ret_buf;
+    };
+    //**************************************************************************************
+    local.ASN1_CONSTRUCTED_value_block.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "ASN1_CONSTRUCTED_value_block";
+    };
+    //**************************************************************************************
+    local.ASN1_CONSTRUCTED_value_block.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = local.value_block.prototype.toJSON.call(this);
+
+        _object.block_name = local.ASN1_CONSTRUCTED_value_block.prototype.block_name.call(this);
+        _object.is_indefinite_form = this.is_indefinite_form;
+        _object.value = new Array();
+        for(var i = 0; i < this.value.length; i++)
+            _object.value.push(this.value[i].toJSON());
+
+        return _object;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.ASN1_CONSTRUCTED =
+    function()
+    {
+        /// <summary>Base class of ASN.1 block for constructive values (constructive encoding)</summary>
+
+        in_window.org.pkijs.asn1.ASN1_block.call(this, arguments[0]);
+
+        this.id_block.is_constructed = true;
+        this.value_block = new local.ASN1_CONSTRUCTED_value_block(arguments[0]);
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.ASN1_CONSTRUCTED.prototype = new in_window.org.pkijs.asn1.ASN1_block();
+    in_window.org.pkijs.asn1.ASN1_CONSTRUCTED.constructor = in_window.org.pkijs.asn1.ASN1_CONSTRUCTED;
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.ASN1_CONSTRUCTED.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "CONSTRUCTED";
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.ASN1_CONSTRUCTED.prototype.fromBER =
+    function(input_buffer, input_offset, input_length)
+    {
+        /// <summary>Base function for converting block from BER encoded array of bytes</summary>
+        /// <param name="input_buffer" type="ArrayBuffer">ASN.1 BER encoded array</param>
+        /// <param name="input_offset" type="Number">Offset in ASN.1 BER encoded array where decoding should be started</param>
+        /// <param name="input_length" type="Number">Maximum length of array of bytes which can be using in this function</param>
+
+        this.value_block.is_indefinite_form = this.len_block.is_indefinite_form;
+
+        var result_offset = this.value_block.fromBER(input_buffer, input_offset, (this.len_block.is_indefinite_form == true) ? input_length : this.len_block.length);
+        if(result_offset == (-1))
+        {
+            this.error = this.value_block.error;
+            return result_offset;
+        }
+
+        if(this.id_block.error.length == 0)
+            this.block_length += this.id_block.block_length;
+
+        if(this.len_block.error.length == 0)
+            this.block_length += this.len_block.block_length;
+
+        if(this.value_block.error.length == 0)
+            this.block_length += this.value_block.block_length;
+
+        return result_offset;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.ASN1_CONSTRUCTED.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = in_window.org.pkijs.asn1.ASN1_block.prototype.toJSON.call(this);
+
+        _object.block_name = in_window.org.pkijs.asn1.ASN1_CONSTRUCTED.prototype.block_name.call(this);
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Declaration of ASN.1 EOC type class
+    //**************************************************************************************
+    local.EOC_value_block =
+    function()
+    {
+        local.value_block.call(this, arguments[0]);
+    };
+    //**************************************************************************************
+    local.EOC_value_block.prototype = new local.value_block();
+    local.EOC_value_block.constructor = local.EOC_value_block;
+    //**************************************************************************************
+    local.EOC_value_block.prototype.fromBER =
+    function(input_buffer, input_offset, input_length)
+    {
+        /// <summary>Base function for converting block from BER encoded array of bytes</summary>
+        /// <param name="input_buffer" type="ArrayBuffer">ASN.1 BER encoded array</param>
+        /// <param name="input_offset" type="Number">Offset in ASN.1 BER encoded array where decoding should be started</param>
+        /// <param name="input_length" type="Number">Maximum length of array of bytes which can be using in this function</param>
+
+        // #region There is no "value block" for EOC type and we need to return the same offset 
+        return input_offset;
+        // #endregion 
+    };
+    //**************************************************************************************
+    local.EOC_value_block.prototype.toBER =
+    function(size_only)
+    {
+        /// <summary>Encoding of current ASN.1 block into ASN.1 encoded array (BER rules)</summary>
+        /// <param name="size_only" type="Boolean">Flag that we need only a size of encoding, not a real array of bytes</param>
+
+        return (new ArrayBuffer(0));
+    };
+    //**************************************************************************************
+    local.EOC_value_block.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "EOC_value_block";
+    };
+    //**************************************************************************************
+    local.EOC_value_block.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = local.value_block.prototype.toJSON.call(this);
+
+        _object.block_name = local.EOC_value_block.prototype.block_name.call(this);
+
+        return _object;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.EOC =
+    function()
+    {
+        in_window.org.pkijs.asn1.ASN1_block.call(this, arguments[0]);
+
+        this.value_block = new local.EOC_value_block();
+
+        this.id_block.tag_class = 1; // UNIVERSAL
+        this.id_block.tag_number = 0; // EOC
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.EOC.prototype = new in_window.org.pkijs.asn1.ASN1_block();
+    in_window.org.pkijs.asn1.EOC.constructor = local.EOC_value_block;
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.EOC.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "END_OF_CONTENT";
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.EOC.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = in_window.org.pkijs.asn1.ASN1_block.prototype.toJSON.call(this);
+
+        _object.block_name = in_window.org.pkijs.asn1.EOC.prototype.block_name.call(this);
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Declaration of ASN.1 BOOLEAN type class
+    //**************************************************************************************
+    local.BOOLEAN_value_block =
+    function()
+    {
+        local.value_block.call(this, arguments[0]);
+
+        if(arguments[0] instanceof Object)
+        {
+            this.value = in_window.org.pkijs.getValue(arguments[0], "value", false);
+
+            // #region Variables from hex_block class 
+            this.is_hex_only = in_window.org.pkijs.getValue(arguments[0], "is_hex_only", false);
+            if("value_hex" in arguments[0])
+                this.value_hex = util_copybuf(arguments[0].value_hex);
+            else
+            {
+                this.value_hex = new ArrayBuffer(1);
+                if(this.value === true)
+                {
+                    var view = new Uint8Array(this.value_hex);
+                    view[0] = 0xFF;
+                }
+            }
+            // #endregion 
+        }
+        else
+        {
+            this.value = false;
+
+            // #region Variables from hex_block class 
+            this.is_hex_only = false;
+            this.value_hex = new ArrayBuffer(1);
+            // #endregion 
+        }
+    };
+    //**************************************************************************************
+    local.BOOLEAN_value_block.prototype = new local.value_block();
+    local.BOOLEAN_value_block.constructor = local.BOOLEAN_value_block;
+    //**************************************************************************************
+    local.BOOLEAN_value_block.prototype.fromBER =
+    function(input_buffer, input_offset, input_length)
+    {
+        /// <summary>Base function for converting block from BER encoded array of bytes</summary>
+        /// <param name="input_buffer" type="ArrayBuffer">ASN.1 BER encoded array</param>
+        /// <param name="input_offset" type="Number">Offset in ASN.1 BER encoded array where decoding should be started</param>
+        /// <param name="input_length" type="Number">Maximum length of array of bytes which can be using in this function</param>
+
+        // #region Basic check for parameters 
+        if(check_buffer_params.call(this, input_buffer, input_offset, input_length) === false)
+            return (-1);
+        // #endregion 
+
+        // #region Getting Uint8Array from ArrayBuffer 
+        var int_buffer = new Uint8Array(input_buffer, input_offset, input_length);
+        // #endregion 
+
+        if(input_length > 1)
+            this.warnings.push("BOOLEAN value encoded in more then 1 octet");
+
+        this.value = int_buffer[0] != 0x00;
+
+        this.is_hex_only = true;
+
+        // #region Copy input buffer to internal array 
+        this.value_hex = new ArrayBuffer(int_buffer.length);
+        var view = new Uint8Array(this.value_hex);
+
+        for(var i = 0; i < int_buffer.length; i++)
+            view[i] = int_buffer[i];
+        // #endregion 
+
+        this.block_length = input_length;
+
+        return (input_offset + input_length);
+    };
+    //**************************************************************************************
+    local.BOOLEAN_value_block.prototype.toBER =
+    function(size_only)
+    {
+        /// <summary>Encoding of current ASN.1 block into ASN.1 encoded array (BER rules)</summary>
+        /// <param name="size_only" type="Boolean">Flag that we need only a size of encoding, not a real array of bytes</param>
+
+        if(typeof size_only === "undefined")
+            size_only = false;
+
+        return this.value_hex;
+    };
+    //**************************************************************************************
+    local.BOOLEAN_value_block.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "BOOLEAN_value_block";
+    };
+    //**************************************************************************************
+    local.BOOLEAN_value_block.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = local.value_block.prototype.toJSON.call(this);
+
+        _object.block_name = local.BOOLEAN_value_block.prototype.block_name.call(this);
+        _object.value = this.value;
+        _object.is_hex_only = this.is_hex_only;
+        _object.value_hex = in_window.org.pkijs.bufferToHexCodes(this.value_hex, 0, this.value_hex.byteLength);
+
+        return _object;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.BOOLEAN =
+    function()
+    {
+        in_window.org.pkijs.asn1.ASN1_block.call(this, arguments[0]);
+
+        this.value_block = new local.BOOLEAN_value_block(arguments[0]);
+
+        this.id_block.tag_class = 1; // UNIVERSAL
+        this.id_block.tag_number = 1; // BOOLEAN
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.BOOLEAN.prototype = new in_window.org.pkijs.asn1.ASN1_block();
+    in_window.org.pkijs.asn1.BOOLEAN.constructor = local.BOOLEAN_value_block;
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.BOOLEAN.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "BOOLEAN";
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.BOOLEAN.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = in_window.org.pkijs.asn1.ASN1_block.prototype.toJSON.call(this);
+
+        _object.block_name = in_window.org.pkijs.asn1.BOOLEAN.prototype.block_name.call(this);
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Declaration of ASN.1 SEQUENCE and SET type classes
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.SEQUENCE =
+    function()
+    {
+        in_window.org.pkijs.asn1.ASN1_CONSTRUCTED.call(this, arguments[0]);
+
+        this.id_block.tag_class = 1; // UNIVERSAL
+        this.id_block.tag_number = 16; // SEQUENCE
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.SEQUENCE.prototype = new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED();
+    in_window.org.pkijs.asn1.SEQUENCE.constructor = in_window.org.pkijs.asn1.SEQUENCE;
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.SEQUENCE.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "SEQUENCE";
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.SEQUENCE.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = in_window.org.pkijs.asn1.ASN1_CONSTRUCTED.prototype.toJSON.call(this);
+
+        _object.block_name = in_window.org.pkijs.asn1.SEQUENCE.prototype.block_name.call(this);
+
+        return _object;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.SET =
+    function()
+    {
+        in_window.org.pkijs.asn1.ASN1_CONSTRUCTED.call(this, arguments[0]);
+
+        this.id_block.tag_class = 1; // UNIVERSAL
+        this.id_block.tag_number = 17; // SET
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.SET.prototype = new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED();
+    in_window.org.pkijs.asn1.SET.constructor = in_window.org.pkijs.asn1.SET;
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.SET.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "SET";
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.SET.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = in_window.org.pkijs.asn1.ASN1_CONSTRUCTED.prototype.toJSON.call(this);
+
+        _object.block_name = in_window.org.pkijs.asn1.SET.prototype.block_name.call(this);
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Declaration of ASN.1 NULL type class 
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.NULL =
+    function()
+    {
+        in_window.org.pkijs.asn1.ASN1_block.call(this, arguments[0]);
+
+        this.id_block.tag_class = 1; // UNIVERSAL
+        this.id_block.tag_number = 5; // NULL
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.NULL.prototype = new in_window.org.pkijs.asn1.ASN1_block();
+    in_window.org.pkijs.asn1.NULL.constructor = in_window.org.pkijs.asn1.NULL;
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.NULL.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "NULL";
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.NULL.prototype.fromBER =
+    function(input_buffer, input_offset, input_length)
+    {
+        /// <summary>Base function for converting block from BER encoded array of bytes</summary>
+        /// <param name="input_buffer" type="ArrayBuffer">ASN.1 BER encoded array</param>
+        /// <param name="input_offset" type="Number">Offset in ASN.1 BER encoded array where decoding should be started</param>
+        /// <param name="input_length" type="Number">Maximum length of array of bytes which can be using in this function</param>
+
+        if(this.len_block.length > 0)
+            this.warnings.push("Non-zero length of value block for NULL type");
+
+        if(this.id_block.error.length === 0)
+            this.block_length += this.id_block.block_length;
+
+        if(this.len_block.error.length === 0)
+            this.block_length += this.len_block.block_length;
+
+        this.block_length += input_length;
+
+        return (input_offset + input_length);
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.NULL.prototype.toBER =
+    function(size_only)
+    {
+        /// <summary>Encoding of current ASN.1 block into ASN.1 encoded array (BER rules)</summary>
+        /// <param name="size_only" type="Boolean">Flag that we need only a size of encoding, not a real array of bytes</param>
+
+        if(typeof size_only === "undefined")
+            size_only = false;
+
+        var ret_buf = new ArrayBuffer(2);
+
+        if(size_only === true)
+            return ret_buf;
+
+        var ret_view = new Uint8Array(ret_buf);
+        ret_view[0] = 0x05;
+        ret_view[1] = 0x00;
+
+        return ret_buf;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.NULL.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = in_window.org.pkijs.asn1.ASN1_block.prototype.toJSON.call(this);
+
+        _object.block_name = in_window.org.pkijs.asn1.NULL.prototype.block_name.call(this);
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Declaration of ASN.1 OCTETSTRING type class 
+    //**************************************************************************************
+    local.OCTETSTRING_value_block =
+    function()
+    {
+        /// <param name="input_value_hex" type="ArrayBuffer"></param>
+        /// <param name="input_value" type="Array"></param>
+        /// <param name="input_constructed" type="Boolean"></param>
+        /// <remarks>Value for the OCTETSTRING may be as hex, as well as a constructed value.</remarks>
+        /// <remarks>Constructed values consists of other OCTETSTRINGs</remarks>
+
+        local.ASN1_CONSTRUCTED_value_block.call(this, arguments[0]);
+
+        if(arguments[0] instanceof Object)
+        {
+            this.is_constructed = in_window.org.pkijs.getValue(arguments[0], "is_constructed", false);
+
+            // #region Variables from hex_block type 
+            this.is_hex_only = in_window.org.pkijs.getValue(arguments[0], "is_hex_only", false);
+            if("value_hex" in arguments[0])
+                this.value_hex = util_copybuf(arguments[0].value_hex);
+            else
+                this.value_hex = new ArrayBuffer(0);
+            // #endregion 
+        }
+        else
+        {
+            this.is_constructed = false;
+
+            // #region Variables from hex_block type 
+            this.is_hex_only = false;
+            this.value_hex = new ArrayBuffer(0);
+            // #endregion 
+        }
+    };
+    //**************************************************************************************
+    local.OCTETSTRING_value_block.prototype = new local.ASN1_CONSTRUCTED_value_block();
+    local.OCTETSTRING_value_block.constructor = local.OCTETSTRING_value_block;
+    //**************************************************************************************
+    local.OCTETSTRING_value_block.prototype.fromBER =
+    function(input_buffer, input_offset, input_length)
+    {
+        /// <summary>Base function for converting block from BER encoded array of bytes</summary>
+        /// <param name="input_buffer" type="ArrayBuffer">ASN.1 BER encoded array</param>
+        /// <param name="input_offset" type="Number">Offset in ASN.1 BER encoded array where decoding should be started</param>
+        /// <param name="input_length" type="Number">Maximum length of array of bytes which can be using in this function</param>
+
+        var result_offset = 0;
+
+        if(this.is_constructed == true)
+        {
+            this.is_hex_only = false;
+
+            result_offset = local.ASN1_CONSTRUCTED_value_block.prototype.fromBER.call(this, input_buffer, input_offset, input_length);
+            if(result_offset == (-1))
+                return result_offset;
+
+            for(var i = 0; i < this.value.length; i++)
+            {
+                var current_block_name = this.value[i].block_name();
+
+                if(current_block_name == in_window.org.pkijs.asn1.EOC.prototype.block_name())
+                {
+                    if(this.is_indefinite_form == true)
+                        break;
+                    else
+                    {
+                        this.error = "EOC is unexpected, OCTET STRING may consists of OCTET STRINGs only";
+                        return (-1);
+                    }
+                }
+
+                if(current_block_name != in_window.org.pkijs.asn1.OCTETSTRING.prototype.block_name())
+                {
+                    this.error = "OCTET STRING may consists of OCTET STRINGs only";
+                    return (-1);
+                }
+            }
+        }
+        else
+        {
+            this.is_hex_only = true;
+
+            result_offset = local.hex_block.prototype.fromBER.call(this, input_buffer, input_offset, input_length);
+            this.block_length = input_length;
+        }
+
+        return result_offset;
+    };
+    //**************************************************************************************
+    local.OCTETSTRING_value_block.prototype.toBER =
+    function(size_only)
+    {
+        /// <summary>Encoding of current ASN.1 block into ASN.1 encoded array (BER rules)</summary>
+        /// <param name="size_only" type="Boolean">Flag that we need only a size of encoding, not a real array of bytes</param>
+
+        if(typeof size_only === "undefined")
+            size_only = false;
+
+        if(this.is_constructed === true)
+            return local.ASN1_CONSTRUCTED_value_block.prototype.toBER.call(this, size_only);
+        else
+        {
+            var ret_buf = new ArrayBuffer(this.value_hex.byteLength);
+
+            if(size_only === true)
+                return ret_buf;
+
+            if(this.value_hex.byteLength == 0)
+                return ret_buf;
+
+            ret_buf = util_copybuf(this.value_hex);
+
+            return ret_buf;
+        }
+
+        return (new ArrayBuffer(0));
+    };
+    //**************************************************************************************
+    local.OCTETSTRING_value_block.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "OCTETSTRING_value_block";
+    };
+    //**************************************************************************************
+    local.OCTETSTRING_value_block.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = local.ASN1_CONSTRUCTED_value_block.prototype.toJSON.call(this);
+
+        _object.block_name = local.OCTETSTRING_value_block.prototype.block_name.call(this);
+        _object.is_constructed = this.is_constructed;
+        _object.is_hex_only = this.is_hex_only;
+        _object.value_hex = in_window.org.pkijs.bufferToHexCodes(this.value_hex, 0, this.value_hex.byteLength);
+
+        return _object;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.OCTETSTRING =
+    function()
+    {
+        in_window.org.pkijs.asn1.ASN1_block.call(this, arguments[0]);
+
+        this.value_block = new local.OCTETSTRING_value_block(arguments[0]);
+
+        this.id_block.tag_class = 1; // UNIVERSAL
+        this.id_block.tag_number = 4; // OCTETSTRING
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.OCTETSTRING.prototype = new in_window.org.pkijs.asn1.ASN1_block();
+    in_window.org.pkijs.asn1.OCTETSTRING.constructor = in_window.org.pkijs.asn1.OCTETSTRING;
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.OCTETSTRING.prototype.fromBER =
+    function(input_buffer, input_offset, input_length)
+    {
+        /// <summary>Base function for converting block from BER encoded array of bytes</summary>
+        /// <param name="input_buffer" type="ArrayBuffer">ASN.1 BER encoded array</param>
+        /// <param name="input_offset" type="Number">Offset in ASN.1 BER encoded array where decoding should be started</param>
+        /// <param name="input_length" type="Number">Maximum length of array of bytes which can be using in this function</param>
+
+        this.value_block.is_constructed = this.id_block.is_constructed;
+        this.value_block.is_indefinite_form = this.len_block.is_indefinite_form;
+
+        // #region Ability to encode empty OCTET STRING 
+        if(input_length == 0)
+        {
+            if(this.id_block.error.length == 0)
+                this.block_length += this.id_block.block_length;
+
+            if(this.len_block.error.length == 0)
+                this.block_length += this.len_block.block_length;
+
+            return input_offset;
+        }
+        // #endregion 
+
+        return in_window.org.pkijs.asn1.ASN1_block.prototype.fromBER.call(this, input_buffer, input_offset, input_length);
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.OCTETSTRING.prototype.block_name =
+    function()
+    {
+        return "OCTETSTRING";
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.OCTETSTRING.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = in_window.org.pkijs.asn1.ASN1_block.prototype.toJSON.call(this);
+
+        _object.block_name = in_window.org.pkijs.asn1.OCTETSTRING.prototype.block_name.call(this);
+
+        return _object;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.OCTETSTRING.prototype.isEqual =
+    function(octetString)
+    {
+        /// <summaryChecking that two OCTETSTRINGs are equal></summary>
+        /// <param name="octetString" type="in_window.org.pkijs.asn1.OCTETSTRING">The OCTETSTRING to compare with</param>
+
+        // #region Check input type 
+        if((octetString instanceof in_window.org.pkijs.asn1.OCTETSTRING) == false)
+            return false;
+        // #endregion 
+
+        // #region Compare two JSON strings 
+        if(JSON.stringify(this) != JSON.stringify(octetString))
+            return false;
+        // #endregion 
+
+        return true;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Declaration of ASN.1 BITSTRING type class
+    //**************************************************************************************
+    local.BITSTRING_value_block =
+    function()
+    {
+        local.ASN1_CONSTRUCTED_value_block.call(this, arguments[0]);
+
+        if(arguments[0] instanceof Object)
+        {
+            this.unused_bits = in_window.org.pkijs.getValue(arguments[0], "unused_bits", 0);
+            this.is_constructed = in_window.org.pkijs.getValue(arguments[0], "is_constructed", false);
+
+            // #region Variables from hex_block type 
+            this.is_hex_only = in_window.org.pkijs.getValue(arguments[0], "is_hex_only", false);
+
+            if("value_hex" in arguments[0])
+                this.value_hex = util_copybuf(arguments[0].value_hex);
+            else
+                this.value_hex = new ArrayBuffer(0);
+
+            this.block_length = this.value_hex.byteLength;
+            // #endregion 
+        }
+        else
+        {
+            this.unused_bits = 0;
+            this.is_constructed = false;
+
+            // #region Variables from hex_block type 
+            this.is_hex_only = false;
+            this.value_hex = new ArrayBuffer(0);
+            // #endregion 
+        }
+    };
+    //**************************************************************************************
+    local.BITSTRING_value_block.prototype = new local.ASN1_CONSTRUCTED_value_block();
+    local.BITSTRING_value_block.constructor = local.BITSTRING_value_block;
+    //**************************************************************************************
+    local.BITSTRING_value_block.prototype.fromBER =
+    function(input_buffer, input_offset, input_length)
+    {
+        /// <summary>Base function for converting block from BER encoded array of bytes</summary>
+        /// <param name="input_buffer" type="ArrayBuffer">ASN.1 BER encoded array</param>
+        /// <param name="input_offset" type="Number">Offset in ASN.1 BER encoded array where decoding should be started</param>
+        /// <param name="input_length" type="Number">Maximum length of array of bytes which can be using in this function</param>
+
+        // #region Ability to decode zero-length BITSTRING value 
+        if(input_length == 0)
+            return input_offset;
+        // #endregion 
+
+        var result_offset = (-1);
+
+        // #region If the BISTRING supposed to be a constructed value 
+        if(this.is_constructed == true)
+        {
+            result_offset = local.ASN1_CONSTRUCTED_value_block.prototype.fromBER.call(this, input_buffer, input_offset, input_length);
+            if(result_offset == (-1))
+                return result_offset;
+
+            for(var i = 0; i < this.value.length; i++)
+            {
+                var current_block_name = this.value[i].block_name();
+
+                if(current_block_name == in_window.org.pkijs.asn1.EOC.prototype.block_name())
+                {
+                    if(this.is_indefinite_form == true)
+                        break;
+                    else
+                    {
+                        this.error = "EOC is unexpected, BIT STRING may consists of BIT STRINGs only";
+                        return (-1);
+                    }
+                }
+
+                if(current_block_name != in_window.org.pkijs.asn1.BITSTRING.prototype.block_name())
+                {
+                    this.error = "BIT STRING may consists of BIT STRINGs only";
+                    return (-1);
+                }
+
+                if((this.unused_bits > 0) && (this.value[i].unused_bits > 0))
+                {
+                    this.error = "Usign of \"unused bits\" inside constructive BIT STRING allowed for least one only";
+                    return (-1);
+                }
+                else
+                {
+                    this.unused_bits = this.value[i].unused_bits;
+                    if(this.unused_bits > 7)
+                    {
+                        this.error = "Unused bits for BITSTRING must be in range 0-7";
+                        return (-1);
+                    }
+                }
+            }
+
+            return result_offset;
+        }
+            // #endregion 
+        // #region If the BITSTRING supposed to be a primitive value
+        else
+        {
+            // #region Basic check for parameters 
+            if(check_buffer_params.call(this, input_buffer, input_offset, input_length) === false)
+                return (-1);
+            // #endregion 
+
+            var int_buffer = new Uint8Array(input_buffer, input_offset, input_length);
+
+            this.unused_bits = int_buffer[0];
+            if(this.unused_bits > 7)
+            {
+                this.error = "Unused bits for BITSTRING must be in range 0-7";
+                return (-1);
+            }
+
+            // #region Copy input buffer to internal buffer 
+            this.value_hex = new ArrayBuffer(int_buffer.length - 1);
+            var view = new Uint8Array(this.value_hex);
+            for(var i = 0; i < (input_length - 1) ; i++)
+                view[i] = int_buffer[i + 1];
+            // #endregion 
+
+            this.block_length = int_buffer.length;
+
+            return (input_offset + input_length);
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    local.BITSTRING_value_block.prototype.toBER =
+    function(size_only)
+    {
+        /// <summary>Encoding of current ASN.1 block into ASN.1 encoded array (BER rules)</summary>
+        /// <param name="size_only" type="Boolean">Flag that we need only a size of encoding, not a real array of bytes</param>
+
+        if(typeof size_only === "undefined")
+            size_only = false;
+
+        if(this.is_constructed === true)
+            return local.ASN1_CONSTRUCTED_value_block.prototype.toBER.call(this, size_only);
+        else
+        {
+            if(size_only === true)
+                return (new ArrayBuffer(this.value_hex.byteLength + 1));
+
+            if(this.value_hex.byteLength == 0)
+                return (new ArrayBuffer(0));
+
+            var cur_view = new Uint8Array(this.value_hex);
+
+            var ret_buf = new ArrayBuffer(this.value_hex.byteLength + 1);
+            var ret_view = new Uint8Array(ret_buf);
+
+            ret_view[0] = this.unused_bits;
+
+            for(var i = 0; i < this.value_hex.byteLength; i++)
+                ret_view[i + 1] = cur_view[i];
+
+            return ret_buf;
+        }
+
+        return (new ArrayBuffer(0));
+    };
+    //**************************************************************************************
+    local.BITSTRING_value_block.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "BITSTRING_value_block";
+    };
+    //**************************************************************************************
+    local.BITSTRING_value_block.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = local.ASN1_CONSTRUCTED_value_block.prototype.toJSON.call(this);
+
+        _object.block_name = local.BITSTRING_value_block.prototype.block_name.call(this);
+        _object.unused_bits = this.unused_bits;
+        _object.is_constructed = this.is_constructed;
+        _object.is_hex_only = this.is_hex_only;
+        _object.value_hex = in_window.org.pkijs.bufferToHexCodes(this.value_hex, 0, this.value_hex.byteLength);
+
+        return _object;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.BITSTRING =
+    function()
+    {
+        in_window.org.pkijs.asn1.ASN1_block.call(this, arguments[0]);
+
+        this.value_block = new local.BITSTRING_value_block(arguments[0]);
+
+        this.id_block.tag_class = 1; // UNIVERSAL
+        this.id_block.tag_number = 3; // BITSTRING
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.BITSTRING.prototype = new in_window.org.pkijs.asn1.ASN1_block();
+    in_window.org.pkijs.asn1.BITSTRING.constructor = in_window.org.pkijs.asn1.BITSTRING;
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.BITSTRING.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "BITSTRING";
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.BITSTRING.prototype.fromBER =
+    function(input_buffer, input_offset, input_length)
+    {
+        /// <summary>Base function for converting block from BER encoded array of bytes</summary>
+        /// <param name="input_buffer" type="ArrayBuffer">ASN.1 BER encoded array</param>
+        /// <param name="input_offset" type="Number">Offset in ASN.1 BER encoded array where decoding should be started</param>
+        /// <param name="input_length" type="Number">Maximum length of array of bytes which can be using in this function</param>
+
+        // #region Ability to encode empty BITSTRING 
+        if(input_length == 0)
+            return input_offset;
+        // #endregion 
+
+        this.value_block.is_constructed = this.id_block.is_constructed;
+        this.value_block.is_indefinite_form = this.len_block.is_indefinite_form;
+
+        return in_window.org.pkijs.asn1.ASN1_block.prototype.fromBER.call(this, input_buffer, input_offset, input_length);
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.BITSTRING.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = in_window.org.pkijs.asn1.ASN1_block.prototype.toJSON.call(this);
+
+        _object.block_name = in_window.org.pkijs.asn1.BITSTRING.prototype.block_name.call(this);
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Declaration of ASN.1 INTEGER type class 
+    //**************************************************************************************
+    local.INTEGER_value_block =
+    function()
+    {
+        local.value_block.call(this, arguments[0]);
+
+        if(arguments[0] instanceof Object)
+        {
+            this.value_dec = in_window.org.pkijs.getValue(arguments[0], "value", 0);
+
+            // #region Variables from hex_block type 
+            this.is_hex_only = in_window.org.pkijs.getValue(arguments[0], "is_hex_only", false);
+            if("value_hex" in arguments[0])
+            {
+                this.value_hex = util_copybuf(arguments[0].value_hex);
+
+                if(this.value_hex.byteLength >= 4) // Dummy's protection
+                    this.is_hex_only = true;
+                else
+                    this.value_dec = util_decode_tc.call(this);
+            }
+            else
+                this.value_hex = util_encode_tc(this.value_dec);
+            // #endregion 
+        }
+        else
+        {
+            this.value_dec = 0;
+
+            // #region Variables from hex_block type 
+            this.is_hex_only = false;
+            this.value_hex = new ArrayBuffer(0);
+            // #endregion 
+        }
+    };
+    //**************************************************************************************
+    local.INTEGER_value_block.prototype = new local.value_block();
+    local.INTEGER_value_block.constructor = local.INTEGER_value_block;
+    //**************************************************************************************
+    local.INTEGER_value_block.prototype.fromBER =
+    function(input_buffer, input_offset, input_length)
+    {
+        /// <summary>Base function for converting block from BER encoded array of bytes</summary>
+        /// <param name="input_buffer" type="ArrayBuffer">ASN.1 BER encoded array</param>
+        /// <param name="input_offset" type="Number">Offset in ASN.1 BER encoded array where decoding should be started</param>
+        /// <param name="input_length" type="Number">Maximum length of array of bytes which can be using in this function</param>
+
+        var result_offset = local.hex_block.prototype.fromBER.call(this, input_buffer, input_offset, input_length);
+        if(result_offset == (-1))
+            return result_offset;
+
+        if(this.value_hex.byteLength > 4) // In JavaScript we can effectively work with 32-bit integers only
+        {
+            this.warnings.push("Too big INTEGER for decoding, hex only");
+            this.is_hex_only = true;
+        }
+        else
+            this.value_dec = util_decode_tc.call(this);
+
+        this.block_length = input_length;
+
+        return (input_offset + input_length);
+    };
+    //**************************************************************************************
+    local.INTEGER_value_block.prototype.toBER =
+    function(size_only)
+    {
+        /// <summary>Encoding of current ASN.1 block into ASN.1 encoded array (BER rules)</summary>
+        /// <param name="size_only" type="Boolean">Flag that we need only a size of encoding, not a real array of bytes</param>
+
+        if(typeof size_only === "undefined")
+            size_only = false;
+
+        if(this.is_hex_only === false)
+        {
+            var encoded_buf = util_encode_tc(this.value_dec);
+            if(encoded_buf.byteLength == 0)
+            {
+                this.error = "Error during encoding INTEGER value";
+                return (new ArrayBuffer(0));
+            }
+
+            return util_copybuf(encoded_buf);
+        }
+        else
+            return util_copybuf(this.value_hex);
+
+        return (new ArrayBuffer(0));
+    };
+    //**************************************************************************************
+    local.INTEGER_value_block.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "INTEGER_value_block";
+    };
+    //**************************************************************************************
+    local.INTEGER_value_block.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = local.value_block.prototype.toJSON.call(this);
+
+        _object.block_name = local.INTEGER_value_block.prototype.block_name.call(this);
+        _object.value_dec = this.value_dec;
+        _object.is_hex_only = this.is_hex_only;
+        _object.value_hex = in_window.org.pkijs.bufferToHexCodes(this.value_hex, 0, this.value_hex.byteLength);
+
+        return _object;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.INTEGER =
+    function()
+    {
+        in_window.org.pkijs.asn1.ASN1_block.call(this, arguments[0]);
+
+        this.value_block = new local.INTEGER_value_block(arguments[0]);
+
+        this.id_block.tag_class = 1; // UNIVERSAL
+        this.id_block.tag_number = 2; // INTEGER
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.INTEGER.prototype = new in_window.org.pkijs.asn1.ASN1_block();
+    in_window.org.pkijs.asn1.INTEGER.constructor = in_window.org.pkijs.asn1.INTEGER;
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.INTEGER.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "INTEGER";
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.INTEGER.prototype.isEqual =
+    function()
+    {
+        /// <summary>Compare two INTEGER object, or INTEGER and ArrayBuffer objects</summary>
+        /// <returns type="Boolean"></returns>
+
+        if(arguments[0] instanceof in_window.org.pkijs.asn1.INTEGER)
+        {
+            if(this.value_block.is_hex_only && arguments[0].value_block.is_hex_only) // Compare two ArrayBuffers
+                return in_window.org.pkijs.isEqual_buffer(this.value_block.value_hex, arguments[0].value_block.value_hex);
+            else
+            {
+                if(this.value_block.is_hex_only === arguments[0].value_block.is_hex_only)
+                    return (this.value_block.value_dec == arguments[0].value_block.value_dec);
+                else
+                    return false;
+            }
+        }
+        else
+        {
+            if(arguments[0] instanceof ArrayBuffer)
+                return in_window.org.pkijs.isEqual_buffer(this.value_block.value_hex, arguments[0]);
+            else
+                return false;
+        }
+
+        return false;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.INTEGER.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = in_window.org.pkijs.asn1.ASN1_block.prototype.toJSON.call(this);
+
+        _object.block_name = in_window.org.pkijs.asn1.INTEGER.prototype.block_name.call(this);
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Declaration of ASN.1 ENUMERATED type class 
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.ENUMERATED =
+    function()
+    {
+        in_window.org.pkijs.asn1.INTEGER.call(this, arguments[0]);
+
+        this.id_block.tag_class = 1; // UNIVERSAL
+        this.id_block.tag_number = 10; // ENUMERATED
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.ENUMERATED.prototype = new in_window.org.pkijs.asn1.INTEGER();
+    in_window.org.pkijs.asn1.ENUMERATED.constructor = in_window.org.pkijs.asn1.ENUMERATED;
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.ENUMERATED.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "ENUMERATED";
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.ENUMERATED.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = in_window.org.pkijs.asn1.INTEGER.prototype.toJSON.call(this);
+
+        _object.block_name = in_window.org.pkijs.asn1.ENUMERATED.prototype.block_name.call(this);
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Declaration of ASN.1 OBJECT IDENTIFIER type class 
+    //**************************************************************************************
+    local.SID_value_block =
+    function()
+    {
+        local.hex_block.call(this, arguments[0]);
+
+        if(arguments[0] instanceof Object)
+        {
+            this.value_dec = in_window.org.pkijs.getValue(arguments[0], "value_dec", -1);
+            this.is_first_sid = in_window.org.pkijs.getValue(arguments[0], "is_first_sid", false);
+        }
+        else
+        {
+            this.value_dec = (-1);
+            this.is_first_sid = false;
+        }
+    };
+    //**************************************************************************************
+    local.SID_value_block.prototype = new local.hex_block();
+    local.SID_value_block.constructor = local.SID_value_block;
+    //**************************************************************************************
+    local.SID_value_block.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "sid_block";
+    };
+    //**************************************************************************************
+    local.SID_value_block.prototype.fromBER =
+    function(input_buffer, input_offset, input_length)
+    {
+        /// <summary>Base function for converting block from BER encoded array of bytes</summary>
+        /// <param name="input_buffer" type="ArrayBuffer">ASN.1 BER encoded array</param>
+        /// <param name="input_offset" type="Number">Offset in ASN.1 BER encoded array where decoding should be started</param>
+        /// <param name="input_length" type="Number">Maximum length of array of bytes which can be using in this function</param>
+
+        if(input_length == 0)
+            return input_offset;
+
+        // #region Basic check for parameters 
+        if(check_buffer_params.call(this, input_buffer, input_offset, input_length) === false)
+            return (-1);
+        // #endregion 
+
+        var int_buffer = new Uint8Array(input_buffer, input_offset, input_length);
+
+        this.value_hex = new ArrayBuffer(input_length);
+        var view = new Uint8Array(this.value_hex);
+
+        for(var i = 0; i < input_length; i++)
+        {
+            view[i] = int_buffer[i] & 0x7F;
+
+            this.block_length++;
+
+            if((int_buffer[i] & 0x80) == 0x00)
+                break;
+        }
+
+        // #region Ajust size of value_hex buffer 
+        var temp_value_hex = new ArrayBuffer(this.block_length);
+        var temp_view = new Uint8Array(temp_value_hex);
+
+        for(var i = 0; i < this.block_length; i++)
+            temp_view[i] = view[i];
+
+        this.value_hex = util_copybuf(temp_value_hex);
+        view = new Uint8Array(this.value_hex);
+        // #endregion   
+
+        if((int_buffer[this.block_length - 1] & 0x80) != 0x00)
+        {
+            this.error = "End of input reached before message was fully decoded";
+            return (-1);
+        }
+
+        if(view[0] == 0x00)
+            this.warnings.push("Needlessly long format of SID encoding");
+
+        if(this.block_length <= 8)
+            this.value_dec = util_frombase(view, 7);
+        else
+        {
+            this.is_hex_only = true;
+            this.warnings.push("Too big SID for decoding, hex only");
+        }
+
+        return (input_offset + this.block_length);
+    };
+    //**************************************************************************************
+    local.SID_value_block.prototype.toBER =
+    function(size_only)
+    {
+        /// <summary>Encoding of current ASN.1 block into ASN.1 encoded array (BER rules)</summary>
+        /// <param name="size_only" type="Boolean">Flag that we need only a size of encoding, not a real array of bytes</param>
+
+        if(typeof size_only === "undefined")
+            size_only = false;
+
+        if(this.is_hex_only)
+        {
+            if(size_only === true)
+                return (new ArrayBuffer(this.value_hex.byteLength));
+
+            var cur_view = new Uint8Array(this.value_hex);
+
+            var ret_buf = new ArrayBuffer(this.block_length);
+            var ret_view = new Uint8Array(ret_buf);
+
+            for(var i = 0; i < (this.block_length - 1) ; i++)
+                ret_view[i] = cur_view[i] | 0x80;
+
+            ret_view[this.block_length - 1] = cur_view[this.block_length - 1];
+
+            return ret_buf;
+        }
+        else
+        {
+            var encoded_buf = util_tobase(this.value_dec, 7);
+            if(encoded_buf.byteLength === 0)
+            {
+                this.error = "Error during encoding SID value";
+                return (new ArrayBuffer(0));
+            }
+
+            var ret_buf = new ArrayBuffer(encoded_buf.byteLength);
+
+            if(size_only === false)
+            {
+                var encoded_view = new Uint8Array(encoded_buf);
+                var ret_view = new Uint8Array(ret_buf);
+
+                for(var i = 0; i < (encoded_buf.byteLength - 1) ; i++)
+                    ret_view[i] = encoded_view[i] | 0x80;
+
+                ret_view[encoded_buf.byteLength - 1] = encoded_view[encoded_buf.byteLength - 1];
+            }
+
+            return ret_buf;
+        }
+    };
+    //**************************************************************************************
+    local.SID_value_block.prototype.toString =
+    function()
+    {
+        var result = "";
+
+        if(this.is_hex_only === true)
+            result = to_hex_codes(this.value_hex);
+        else
+        {
+            if(this.is_first_sid)
+            {
+                var sid_value = this.value_dec;
+
+                if(this.value_dec <= 39)
+                    result = "0.";
+                else
+                {
+                    if(this.value_dec <= 79)
+                    {
+                        result = "1.";
+                        sid_value -= 40;
+                    }
+                    else
+                    {
+                        result = "2.";
+                        sid_value -= 80;
+                    }
+                }
+
+                result = result + sid_value.toString();
+            }
+            else
+                result = this.value_dec.toString();
+        }
+
+        return result;
+    };
+    //**************************************************************************************
+    local.SID_value_block.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = local.hex_block.prototype.toJSON.call(this);
+
+        _object.block_name = local.SID_value_block.prototype.block_name.call(this);
+        _object.value_dec = this.value_dec;
+        _object.is_first_sid = this.is_first_sid;
+
+        return _object;
+    };
+    //**************************************************************************************
+    local.OID_value_block =
+    function()
+    {
+        local.value_block.call(this, arguments[0]);
+
+        this.value = new Array();
+
+        if(arguments[0] instanceof Object)
+            this.fromString(in_window.org.pkijs.getValue(arguments[0], "value", ""));
+    };
+    //**************************************************************************************
+    local.OID_value_block.prototype = new local.value_block();
+    local.OID_value_block.constructor = local.OID_value_block;
+    //**************************************************************************************
+    local.OID_value_block.prototype.fromBER =
+    function(input_buffer, input_offset, input_length)
+    {
+        /// <summary>Base function for converting block from BER encoded array of bytes</summary>
+        /// <param name="input_buffer" type="ArrayBuffer">ASN.1 BER encoded array</param>
+        /// <param name="input_offset" type="Number">Offset in ASN.1 BER encoded array where decoding should be started</param>
+        /// <param name="input_length" type="Number">Maximum length of array of bytes which can be using in this function</param>
+
+        var result_offset = input_offset;
+
+        while(input_length > 0)
+        {
+            var sid_block = new local.SID_value_block();
+            result_offset = sid_block.fromBER(input_buffer, result_offset, input_length);
+            if(result_offset == (-1))
+            {
+                this.block_length = 0;
+                this.error = sid_block.error;
+                return result_offset;
+            }
+
+            if(this.value.length == 0)
+                sid_block.is_first_sid = true;
+
+            this.block_length += sid_block.block_length;
+            input_length -= sid_block.block_length;
+
+            this.value.push(sid_block);
+        }
+
+        return result_offset;
+    };
+    //**************************************************************************************
+    local.OID_value_block.prototype.toBER =
+    function(size_only)
+    {
+        /// <summary>Encoding of current ASN.1 block into ASN.1 encoded array (BER rules)</summary>
+        /// <param name="size_only" type="Boolean">Flag that we need only a size of encoding, not a real array of bytes</param>
+
+        if(typeof size_only === "undefined")
+            size_only = false;
+
+        var ret_buf = new ArrayBuffer(0);
+
+        for(var i = 0; i < this.value.length; i++)
+        {
+            var value_buf = this.value[i].toBER(size_only);
+            if(value_buf.byteLength === 0)
+            {
+                this.error = this.value[i].error;
+                return (new ArrayBuffer(0));
+            }
+
+            ret_buf = util_concatbuf(ret_buf, value_buf);
+        }
+
+        return ret_buf;
+    };
+    //**************************************************************************************
+    local.OID_value_block.prototype.fromString =
+    function(str)
+    {
+        this.value = new Array(); // Clear existing SID values
+
+        var pos1 = 0;
+        var pos2 = 0;
+
+        var sid = "";
+
+        var flag = false;
+
+        do
+        {
+            pos2 = str.indexOf('.', pos1);
+            if(pos2 === (-1))
+                sid = str.substr(pos1);
+            else
+                sid = str.substr(pos1, pos2 - pos1);
+
+            pos1 = pos2 + 1;
+
+            if(flag)
+            {
+                var sid_block = this.value[0];
+
+                var plus = 0;
+
+                switch(sid_block.value_dec)
+                {
+                    case 0:
+                        break;
+                    case 1:
+                        plus = 40;
+                        break;
+                    case 2:
+                        plus = 80;
+                        break;
+                    default:
+                        this.value = new Array(); // clear SID array
+                        return false; // ???
+                }
+
+                var parsedSID = parseInt(sid, 10);
+                if(isNaN(parsedSID))
+                    return true;
+
+                sid_block.value_dec = parsedSID + plus;
+
+                flag = false;
+            }
+            else
+            {
+                var sid_block = new local.SID_value_block();
+                sid_block.value_dec = parseInt(sid, 10);
+                if(isNaN(sid_block.value_dec))
+                    return true;
+
+                if(this.value.length === 0)
+                {
+                    sid_block.is_first_sid = true;
+                    flag = true;
+                }
+
+                this.value.push(sid_block);
+            }
+
+        } while(pos2 !== (-1));
+
+        return true;
+    };
+    //**************************************************************************************
+    local.OID_value_block.prototype.toString =
+    function()
+    {
+        var result = "";
+        var is_hex_only = false;
+
+        for(var i = 0; i < this.value.length; i++)
+        {
+            is_hex_only = this.value[i].is_hex_only;
+
+            var sid_str = this.value[i].toString();
+
+            if(i !== 0)
+                result = result + ".";
+
+            if(is_hex_only)
+            {
+                sid_str = "{" + sid_str + "}";
+
+                if(this.value[i].is_first_sid)
+                    result = "2.{" + sid_str + " - 80}";
+                else
+                    result = result + sid_str;
+            }
+            else
+                result = result + sid_str;
+        }
+
+        return result;
+    };
+    //**************************************************************************************
+    local.OID_value_block.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "OID_value_block";
+    };
+    //**************************************************************************************
+    local.OID_value_block.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = local.value_block.prototype.toJSON.call(this);
+
+        _object.block_name = local.OID_value_block.prototype.block_name.call(this);
+        _object.value = local.OID_value_block.prototype.toString.call(this);
+        _object.sid_array = new Array();
+        for(var i = 0; i < this.value.length; i++)
+            _object.sid_array.push(this.value[i].toJSON());
+
+        return _object;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.OID =
+    function()
+    {
+        in_window.org.pkijs.asn1.ASN1_block.call(this, arguments[0]);
+
+        this.value_block = new local.OID_value_block(arguments[0]);
+
+        this.id_block.tag_class = 1; // UNIVERSAL
+        this.id_block.tag_number = 6; // OBJECT IDENTIFIER
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.OID.prototype = new in_window.org.pkijs.asn1.ASN1_block();
+    in_window.org.pkijs.asn1.OID.constructor = in_window.org.pkijs.asn1.OID;
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.OID.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "OID";
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.OID.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = in_window.org.pkijs.asn1.ASN1_block.prototype.toJSON.call(this);
+
+        _object.block_name = in_window.org.pkijs.asn1.OID.prototype.block_name.call(this);
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion   
+    //**************************************************************************************
+    // #region Declaration of all string's classes 
+    //**************************************************************************************
+    local.UTF8STRING_value_block =
+    function()
+    {
+        local.hex_block.call(this, arguments[0]);
+
+        this.is_hex_only = true;
+        this.value = ""; // String representation of decoded ArrayBuffer
+    };
+    //**************************************************************************************
+    local.UTF8STRING_value_block.prototype = new local.hex_block();
+    local.UTF8STRING_value_block.constructor = local.UTF8STRING_value_block;
+    //**************************************************************************************
+    local.UTF8STRING_value_block.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "UTF8STRING_value_block";
+    };
+    //**************************************************************************************
+    local.UTF8STRING_value_block.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = local.hex_block.prototype.toJSON.call(this);
+
+        _object.block_name = local.UTF8STRING_value_block.prototype.block_name.call(this);
+        _object.value = this.value;
+
+        return _object;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.UTF8STRING =
+    function()
+    {
+        in_window.org.pkijs.asn1.ASN1_block.call(this, arguments[0]);
+
+        this.value_block = new local.UTF8STRING_value_block();
+
+        if(arguments[0] instanceof Object)
+        {
+            if("value" in arguments[0])
+                in_window.org.pkijs.asn1.UTF8STRING.prototype.fromString.call(this,arguments[0].value);
+        }
+
+        this.id_block.tag_class = 1; // UNIVERSAL
+        this.id_block.tag_number = 12; // UTF8STRING
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.UTF8STRING.prototype = new in_window.org.pkijs.asn1.ASN1_block();
+    in_window.org.pkijs.asn1.UTF8STRING.constructor = in_window.org.pkijs.asn1.UTF8STRING;
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.UTF8STRING.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "UTF8STRING";
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.UTF8STRING.prototype.fromBER =
+    function(input_buffer, input_offset, input_length)
+    {
+        /// <summary>Base function for converting block from BER encoded array of bytes</summary>
+        /// <param name="input_buffer" type="ArrayBuffer">ASN.1 BER encoded array</param>
+        /// <param name="input_offset" type="Number">Offset in ASN.1 BER encoded array where decoding should be started</param>
+        /// <param name="input_length" type="Number">Maximum length of array of bytes which can be using in this function</param>
+
+        var result_offset = this.value_block.fromBER(input_buffer, input_offset, (this.len_block.is_indefinite_form == true) ? input_length : this.len_block.length);
+        if(result_offset == (-1))
+        {
+            this.error = this.value_block.error;
+            return result_offset;
+        }
+
+        in_window.org.pkijs.asn1.UTF8STRING.prototype.fromBuffer.call(this, this.value_block.value_hex);
+
+        if(this.id_block.error.length == 0)
+            this.block_length += this.id_block.block_length;
+
+        if(this.len_block.error.length == 0)
+            this.block_length += this.len_block.block_length;
+
+        if(this.value_block.error.length == 0)
+            this.block_length += this.value_block.block_length;
+
+        return result_offset;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.UTF8STRING.prototype.fromBuffer =
+    function(input_buffer)
+    {
+        /// <param name="input_buffer" type="ArrayBuffer">Array with encoded string</param>
+        this.value_block.value = String.fromCharCode.apply(null, new Uint8Array(input_buffer));
+
+        try
+        {
+            this.value_block.value = decodeURIComponent(escape(this.value_block.value));
+        }
+        catch(ex)
+        {
+            this.warnings.push("Error during \"decodeURIComponent\": " + ex + ", using raw string");
+        }
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.UTF8STRING.prototype.fromString =
+    function(input_string)
+    {
+        /// <param name="input_string" type="String">String with UNIVERSALSTRING value</param>
+
+        var str = unescape(encodeURIComponent(input_string));
+        var str_len = str.length;
+
+        this.value_block.value_hex = new ArrayBuffer(str_len);
+        var view = new Uint8Array(this.value_block.value_hex);
+
+        for(var i = 0; i < str_len; i++)
+            view[i] = str.charCodeAt(i);
+
+        this.value_block.value = input_string;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.UTF8STRING.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = in_window.org.pkijs.asn1.ASN1_block.prototype.toJSON.call(this);
+
+        _object.block_name = in_window.org.pkijs.asn1.UTF8STRING.prototype.block_name.call(this);
+
+        return _object;
+    };
+    //**************************************************************************************
+    local.BMPSTRING_value_block =
+    function()
+    {
+        local.hex_block.call(this, arguments[0]);
+
+        this.is_hex_only = true;
+        this.value = "";
+    };
+    //**************************************************************************************
+    local.BMPSTRING_value_block.prototype = new local.hex_block();
+    local.BMPSTRING_value_block.constructor = local.BMPSTRING_value_block;
+    //**************************************************************************************
+    local.BMPSTRING_value_block.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "BMPSTRING_value_block";
+    };
+    //**************************************************************************************
+    local.BMPSTRING_value_block.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = local.hex_block.prototype.toJSON.call(this);
+
+        _object.block_name = local.BMPSTRING_value_block.prototype.block_name.call(this);
+        _object.value = this.value;
+
+        return _object;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.BMPSTRING =
+    function()
+    {
+        in_window.org.pkijs.asn1.ASN1_block.call(this, arguments[0]);
+
+        this.value_block = new local.BMPSTRING_value_block();
+
+        if(arguments[0] instanceof Object)
+        {
+            if("value" in arguments[0])
+                in_window.org.pkijs.asn1.BMPSTRING.prototype.fromString.call(this, arguments[0].value);
+        }
+
+        this.id_block.tag_class = 1; // UNIVERSAL
+        this.id_block.tag_number = 30; // BMPSTRING
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.BMPSTRING.prototype = new in_window.org.pkijs.asn1.ASN1_block();
+    in_window.org.pkijs.asn1.BMPSTRING.constructor = in_window.org.pkijs.asn1.BMPSTRING;
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.BMPSTRING.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "BMPSTRING";
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.BMPSTRING.prototype.fromBER =
+    function(input_buffer, input_offset, input_length)
+    {
+        /// <summary>Base function for converting block from BER encoded array of bytes</summary>
+        /// <param name="input_buffer" type="ArrayBuffer">ASN.1 BER encoded array</param>
+        /// <param name="input_offset" type="Number">Offset in ASN.1 BER encoded array where decoding should be started</param>
+        /// <param name="input_length" type="Number">Maximum length of array of bytes which can be using in this function</param>
+
+        var result_offset = this.value_block.fromBER(input_buffer, input_offset, (this.len_block.is_indefinite_form == true) ? input_length : this.len_block.length);
+        if(result_offset == (-1))
+        {
+            this.error = this.value_block.error;
+            return result_offset;
+        }
+
+        in_window.org.pkijs.asn1.BMPSTRING.prototype.fromBuffer.call(this, this.value_block.value_hex);
+
+        if(this.id_block.error.length == 0)
+            this.block_length += this.id_block.block_length;
+
+        if(this.len_block.error.length == 0)
+            this.block_length += this.len_block.block_length;
+
+        if(this.value_block.error.length == 0)
+            this.block_length += this.value_block.block_length;
+
+        return result_offset;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.BMPSTRING.prototype.fromBuffer =
+    function(input_buffer)
+    {
+        /// <param name="input_buffer" type="ArrayBuffer">Array with encoded string</param>
+
+        var copy_buffer = in_window.org.pkijs.copyBuffer(input_buffer);
+
+        var value_view = new Uint8Array(copy_buffer);
+
+        for(var i = 0; i < value_view.length; i = i + 2)
+        {
+            var temp = value_view[i];
+
+            value_view[i] = value_view[i + 1];
+            value_view[i + 1] = temp;
+        }
+
+        this.value_block.value = String.fromCharCode.apply(null, new Uint16Array(copy_buffer));
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.BMPSTRING.prototype.fromString =
+    function(input_string)
+    {
+        /// <param name="input_string" type="String">String with UNIVERSALSTRING value</param>
+
+        var str_length = input_string.length;
+
+        this.value_block.value_hex = new ArrayBuffer(str_length * 2);
+        var value_hex_view = new Uint8Array(this.value_block.value_hex);
+
+        for(var i = 0; i < str_length; i++)
+        {
+            var code_buf = util_tobase(input_string.charCodeAt(i), 8);
+            var code_view = new Uint8Array(code_buf);
+            if(code_view.length > 2)
+                continue;
+
+            var dif = 2 - code_view.length;
+
+            for(var j = (code_view.length - 1) ; j >= 0; j--)
+                value_hex_view[i * 2 + j + dif] = code_view[j];
+        }
+
+        this.value_block.value = input_string;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.BMPSTRING.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = in_window.org.pkijs.asn1.ASN1_block.prototype.toJSON.call(this);
+
+        _object.block_name = in_window.org.pkijs.asn1.BMPSTRING.prototype.block_name.call(this);
+
+        return _object;
+    };
+    //**************************************************************************************
+    local.UNIVERSALSTRING_value_block =
+    function()
+    {
+        local.hex_block.call(this, arguments[0]);
+
+        this.is_hex_only = true;
+        this.value = "";
+    };
+    //**************************************************************************************
+    local.UNIVERSALSTRING_value_block.prototype = new local.hex_block();
+    local.UNIVERSALSTRING_value_block.constructor = local.UNIVERSALSTRING_value_block;
+    //**************************************************************************************
+    local.UNIVERSALSTRING_value_block.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "UNIVERSALSTRING_value_block";
+    };
+    //**************************************************************************************
+    local.UNIVERSALSTRING_value_block.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = local.hex_block.prototype.toJSON.call(this);
+
+        _object.block_name = local.UNIVERSALSTRING_value_block.prototype.block_name.call(this);
+        _object.value = this.value;
+
+        return _object;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.UNIVERSALSTRING =
+    function()
+    {
+        in_window.org.pkijs.asn1.ASN1_block.call(this, arguments[0]);
+
+        this.value_block = new local.UNIVERSALSTRING_value_block();
+
+        if(arguments[0] instanceof Object)
+        {
+            if("value" in arguments[0])
+                in_window.org.pkijs.asn1.UNIVERSALSTRING.prototype.fromString.call(this, arguments[0].value);
+        }
+
+        this.id_block.tag_class = 1; // UNIVERSAL
+        this.id_block.tag_number = 28; // UNIVERSALSTRING
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.UNIVERSALSTRING.prototype = new in_window.org.pkijs.asn1.ASN1_block();
+    in_window.org.pkijs.asn1.UNIVERSALSTRING.constructor = in_window.org.pkijs.asn1.UNIVERSALSTRING;
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.UNIVERSALSTRING.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "UNIVERSALSTRING";
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.UNIVERSALSTRING.prototype.fromBER =
+    function(input_buffer, input_offset, input_length)
+    {
+        /// <summary>Base function for converting block from BER encoded array of bytes</summary>
+        /// <param name="input_buffer" type="ArrayBuffer">ASN.1 BER encoded array</param>
+        /// <param name="input_offset" type="Number">Offset in ASN.1 BER encoded array where decoding should be started</param>
+        /// <param name="input_length" type="Number">Maximum length of array of bytes which can be using in this function</param>
+
+        var result_offset = this.value_block.fromBER(input_buffer, input_offset, (this.len_block.is_indefinite_form == true) ? input_length : this.len_block.length);
+        if(result_offset == (-1))
+        {
+            this.error = this.value_block.error;
+            return result_offset;
+        }
+
+        in_window.org.pkijs.asn1.UNIVERSALSTRING.prototype.fromBuffer.call(this, this.value_block.value_hex);
+
+        if(this.id_block.error.length == 0)
+            this.block_length += this.id_block.block_length;
+
+        if(this.len_block.error.length == 0)
+            this.block_length += this.len_block.block_length;
+
+        if(this.value_block.error.length == 0)
+            this.block_length += this.value_block.block_length;
+
+        return result_offset;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.UNIVERSALSTRING.prototype.fromBuffer =
+    function(input_buffer)
+    {
+        /// <param name="input_buffer" type="ArrayBuffer">Array with encoded string</param>
+
+        var copy_buffer = in_window.org.pkijs.copyBuffer(input_buffer);
+
+        var value_view = new Uint8Array(copy_buffer);
+
+        for(var i = 0; i < value_view.length; i = i + 4)
+        {
+            value_view[i] = value_view[i + 3];
+            value_view[i + 1] = value_view[i + 2];
+            value_view[i + 2] = 0x00;
+            value_view[i + 3] = 0x00;
+        }
+
+        this.value_block.value = String.fromCharCode.apply(null, new Uint32Array(copy_buffer));
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.UNIVERSALSTRING.prototype.fromString =
+    function(input_string)
+    {
+        /// <param name="input_string" type="String">String with UNIVERSALSTRING value</param>
+
+        var str_length = input_string.length;
+
+        this.value_block.value_hex = new ArrayBuffer(str_length * 4);
+        var value_hex_view = new Uint8Array(this.value_block.value_hex);
+
+        for(var i = 0; i < str_length; i++)
+        {
+            var code_buf = util_tobase(input_string.charCodeAt(i), 8);
+            var code_view = new Uint8Array(code_buf);
+            if(code_view.length > 4)
+                continue;
+
+            var dif = 4 - code_view.length;
+
+            for(var j = (code_view.length - 1) ; j >= 0; j--)
+                value_hex_view[i*4 + j + dif] = code_view[j];
+        }
+
+        this.value_block.value = input_string;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.UNIVERSALSTRING.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = in_window.org.pkijs.asn1.ASN1_block.prototype.toJSON.call(this);
+
+        _object.block_name = in_window.org.pkijs.asn1.UNIVERSALSTRING.prototype.block_name.call(this);
+
+        return _object;
+    };
+    //**************************************************************************************
+    local.SIMPLESTRING_value_block =
+    function()
+    {
+        local.hex_block.call(this, arguments[0]);
+
+        /// <field type="String">Native string representation</field>
+        this.value = "";
+        this.is_hex_only = true;
+    };
+    //**************************************************************************************
+    local.SIMPLESTRING_value_block.prototype = new local.hex_block();
+    local.SIMPLESTRING_value_block.constructor = local.SIMPLESTRING_value_block;
+    //**************************************************************************************
+    local.SIMPLESTRING_value_block.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "SIMPLESTRING_value_block";
+    };
+    //**************************************************************************************
+    local.SIMPLESTRING_value_block.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = local.hex_block.prototype.toJSON.call(this);
+
+        _object.block_name = local.SIMPLESTRING_value_block.prototype.block_name.call(this);
+        _object.value = this.value;
+
+        return _object;
+    };
+    //**************************************************************************************
+    local.SIMPLESTRING_block =
+    function()
+    {
+        in_window.org.pkijs.asn1.ASN1_block.call(this, arguments[0]);
+
+        this.value_block = new local.SIMPLESTRING_value_block();
+
+        if(arguments[0] instanceof Object)
+        {
+            if("value" in arguments[0])
+                local.SIMPLESTRING_block.prototype.fromString.call(this, arguments[0].value);
+        }
+    };
+    //**************************************************************************************
+    local.SIMPLESTRING_block.prototype = new in_window.org.pkijs.asn1.ASN1_block();
+    local.SIMPLESTRING_block.constructor = local.SIMPLESTRING_block;
+    //**************************************************************************************
+    local.SIMPLESTRING_block.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "SIMPLESTRING";
+    };
+    //**************************************************************************************
+    local.SIMPLESTRING_block.prototype.fromBER =
+    function(input_buffer, input_offset, input_length)
+    {
+        /// <summary>Base function for converting block from BER encoded array of bytes</summary>
+        /// <param name="input_buffer" type="ArrayBuffer">ASN.1 BER encoded array</param>
+        /// <param name="input_offset" type="Number">Offset in ASN.1 BER encoded array where decoding should be started</param>
+        /// <param name="input_length" type="Number">Maximum length of array of bytes which can be using in this function</param>
+
+        var result_offset = this.value_block.fromBER(input_buffer, input_offset, (this.len_block.is_indefinite_form == true) ? input_length : this.len_block.length);
+        if(result_offset == (-1))
+        {
+            this.error = this.value_block.error;
+            return result_offset;
+        }
+
+        local.SIMPLESTRING_block.prototype.fromBuffer.call(this, this.value_block.value_hex);
+
+        if(this.id_block.error.length == 0)
+            this.block_length += this.id_block.block_length;
+
+        if(this.len_block.error.length == 0)
+            this.block_length += this.len_block.block_length;
+
+        if(this.value_block.error.length == 0)
+            this.block_length += this.value_block.block_length;
+
+        return result_offset;
+    };
+    //**************************************************************************************
+    local.SIMPLESTRING_block.prototype.fromBuffer =
+    function(input_buffer)
+    {
+        /// <param name="input_buffer" type="ArrayBuffer">Array with encoded string</param>
+
+        this.value_block.value = String.fromCharCode.apply(null, new Uint8Array(input_buffer));
+    };
+    //**************************************************************************************
+    local.SIMPLESTRING_block.prototype.fromString =
+    function(input_string)
+    {
+        /// <param name="input_string" type="String">String with UNIVERSALSTRING value</param>
+        var str_len = input_string.length;
+
+        this.value_block.value_hex = new ArrayBuffer(str_len);
+        var view = new Uint8Array(this.value_block.value_hex);
+
+        for(var i = 0; i < str_len; i++)
+            view[i] = input_string.charCodeAt(i);
+
+        this.value_block.value = input_string;
+    };
+    //**************************************************************************************
+    local.SIMPLESTRING_block.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = in_window.org.pkijs.asn1.ASN1_block.prototype.toJSON.call(this);
+
+        _object.block_name = local.SIMPLESTRING_block.prototype.block_name.call(this);
+
+        return _object;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.NUMERICSTRING =
+    function()
+    {
+        local.SIMPLESTRING_block.call(this, arguments[0]);
+
+        this.id_block.tag_class = 1; // UNIVERSAL
+        this.id_block.tag_number = 18; // NUMERICSTRING
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.NUMERICSTRING.prototype = new local.SIMPLESTRING_block();
+    in_window.org.pkijs.asn1.NUMERICSTRING.constructor = in_window.org.pkijs.asn1.NUMERICSTRING;
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.NUMERICSTRING.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "NUMERICSTRING";
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.NUMERICSTRING.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = local.SIMPLESTRING_block.prototype.toJSON.call(this);
+
+        _object.block_name = in_window.org.pkijs.asn1.NUMERICSTRING.prototype.block_name.call(this);
+
+        return _object;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.PRINTABLESTRING =
+    function()
+    {
+        local.SIMPLESTRING_block.call(this, arguments[0]);
+
+        this.id_block.tag_class = 1; // UNIVERSAL
+        this.id_block.tag_number = 19; // PRINTABLESTRING
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.PRINTABLESTRING.prototype = new local.SIMPLESTRING_block();
+    in_window.org.pkijs.asn1.PRINTABLESTRING.constructor = in_window.org.pkijs.asn1.PRINTABLESTRING;
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.PRINTABLESTRING.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "PRINTABLESTRING";
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.PRINTABLESTRING.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = local.SIMPLESTRING_block.prototype.toJSON.call(this);
+
+        _object.block_name = in_window.org.pkijs.asn1.PRINTABLESTRING.prototype.block_name.call(this);
+
+        return _object;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.TELETEXSTRING =
+    function()
+    {
+        local.SIMPLESTRING_block.call(this, arguments[0]);
+
+        this.id_block.tag_class = 1; // UNIVERSAL
+        this.id_block.tag_number = 20; // TELETEXSTRING
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.TELETEXSTRING.prototype = new local.SIMPLESTRING_block();
+    in_window.org.pkijs.asn1.TELETEXSTRING.constructor = in_window.org.pkijs.asn1.TELETEXSTRING;
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.TELETEXSTRING.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "TELETEXSTRING";
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.TELETEXSTRING.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = local.SIMPLESTRING_block.prototype.toJSON.call(this);
+
+        _object.block_name = in_window.org.pkijs.asn1.TELETEXSTRING.prototype.block_name.call(this);
+
+        return _object;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.VIDEOTEXSTRING =
+    function()
+    {
+        local.SIMPLESTRING_block.call(this, arguments[0]);
+
+        this.id_block.tag_class = 1; // UNIVERSAL
+        this.id_block.tag_number = 21; // VIDEOTEXSTRING
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.VIDEOTEXSTRING.prototype = new local.SIMPLESTRING_block();
+    in_window.org.pkijs.asn1.VIDEOTEXSTRING.constructor = in_window.org.pkijs.asn1.VIDEOTEXSTRING;
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.VIDEOTEXSTRING.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "VIDEOTEXSTRING";
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.VIDEOTEXSTRING.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = local.SIMPLESTRING_block.prototype.toJSON.call(this);
+
+        _object.block_name = in_window.org.pkijs.asn1.VIDEOTEXSTRING.prototype.block_name.call(this);
+
+        return _object;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.IA5STRING =
+    function()
+    {
+        local.SIMPLESTRING_block.call(this, arguments[0]);
+
+        this.id_block.tag_class = 1; // UNIVERSAL
+        this.id_block.tag_number = 22; // IA5STRING
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.IA5STRING.prototype = new local.SIMPLESTRING_block();
+    in_window.org.pkijs.asn1.IA5STRING.constructor = in_window.org.pkijs.asn1.IA5STRING;
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.IA5STRING.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "IA5STRING";
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.IA5STRING.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = local.SIMPLESTRING_block.prototype.toJSON.call(this);
+
+        _object.block_name = in_window.org.pkijs.asn1.IA5STRING.prototype.block_name.call(this);
+
+        return _object;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.GRAPHICSTRING =
+    function()
+    {
+        local.SIMPLESTRING_block.call(this, arguments[0]);
+
+        this.id_block.tag_class = 1; // UNIVERSAL
+        this.id_block.tag_number = 25; // GRAPHICSTRING
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.GRAPHICSTRING.prototype = new local.SIMPLESTRING_block();
+    in_window.org.pkijs.asn1.GRAPHICSTRING.constructor = in_window.org.pkijs.asn1.GRAPHICSTRING;
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.GRAPHICSTRING.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "GRAPHICSTRING";
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.GRAPHICSTRING.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = local.SIMPLESTRING_block.prototype.toJSON.call(this);
+
+        _object.block_name = in_window.org.pkijs.asn1.GRAPHICSTRING.prototype.block_name.call(this);
+
+        return _object;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.VISIBLESTRING =
+    function()
+    {
+        local.SIMPLESTRING_block.call(this, arguments[0]);
+
+        this.id_block.tag_class = 1; // UNIVERSAL
+        this.id_block.tag_number = 26; // VISIBLESTRING
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.VISIBLESTRING.prototype = new local.SIMPLESTRING_block();
+    in_window.org.pkijs.asn1.VISIBLESTRING.constructor = in_window.org.pkijs.asn1.VISIBLESTRING;
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.VISIBLESTRING.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "VISIBLESTRING";
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.VISIBLESTRING.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = local.SIMPLESTRING_block.prototype.toJSON.call(this);
+
+        _object.block_name = in_window.org.pkijs.asn1.VISIBLESTRING.prototype.block_name.call(this);
+
+        return _object;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.GENERALSTRING =
+    function()
+    {
+        local.SIMPLESTRING_block.call(this, arguments[0]);
+
+        this.id_block.tag_class = 1; // UNIVERSAL
+        this.id_block.tag_number = 27; // GENERALSTRING
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.GENERALSTRING.prototype = new local.SIMPLESTRING_block();
+    in_window.org.pkijs.asn1.GENERALSTRING.constructor = in_window.org.pkijs.asn1.GENERALSTRING;
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.GENERALSTRING.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "GENERALSTRING";
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.GENERALSTRING.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = local.SIMPLESTRING_block.prototype.toJSON.call(this);
+
+        _object.block_name = in_window.org.pkijs.asn1.GENERALSTRING.prototype.block_name.call(this);
+
+        return _object;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.CHARACTERSTRING =
+    function()
+    {
+        local.SIMPLESTRING_block.call(this, arguments[0]);
+
+        this.id_block.tag_class = 1; // UNIVERSAL
+        this.id_block.tag_number = 29; // CHARACTERSTRING
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.CHARACTERSTRING.prototype = new local.SIMPLESTRING_block();
+    in_window.org.pkijs.asn1.CHARACTERSTRING.constructor = in_window.org.pkijs.asn1.CHARACTERSTRING;
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.CHARACTERSTRING.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "CHARACTERSTRING";
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.CHARACTERSTRING.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = local.SIMPLESTRING_block.prototype.toJSON.call(this);
+
+        _object.block_name = in_window.org.pkijs.asn1.CHARACTERSTRING.prototype.block_name.call(this);
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Declaration of all date and time classes 
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.UTCTIME =
+    function()
+    {
+        in_window.org.pkijs.asn1.VISIBLESTRING.call(this, arguments[0]);
+
+        this.year = 0;
+        this.month = 0;
+        this.day = 0;
+        this.hour = 0;
+        this.minute = 0;
+        this.second = 0;
+
+        // #region Create UTCTIME from ASN.1 UTC string value 
+        if((arguments[0] instanceof Object) && ("value" in arguments[0]))
+        {
+            in_window.org.pkijs.asn1.UTCTIME.prototype.fromString.call(this, arguments[0].value);
+
+            this.value_block.value_hex = new ArrayBuffer(arguments[0].value.length);
+            var view = new Uint8Array(this.value_block.value_hex);
+
+            for(var i = 0; i < arguments[0].value.length; i++)
+                view[i] = arguments[0].value.charCodeAt(i);
+        }
+        // #endregion 
+        // #region Create UTCTIME from JavaScript Date type 
+        if((arguments[0] instanceof Object) && ("value_date" in arguments[0]))
+        {
+            in_window.org.pkijs.asn1.UTCTIME.prototype.fromDate.call(this, arguments[0].value_date);
+            this.value_block.value_hex = in_window.org.pkijs.asn1.UTCTIME.prototype.toBuffer.call(this);
+        }
+        // #endregion 
+
+        this.id_block.tag_class = 1; // UNIVERSAL
+        this.id_block.tag_number = 23; // UTCTIME
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.UTCTIME.prototype = new in_window.org.pkijs.asn1.VISIBLESTRING();
+    in_window.org.pkijs.asn1.UTCTIME.constructor = in_window.org.pkijs.asn1.UTCTIME;
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.UTCTIME.prototype.fromBER =
+    function(input_buffer, input_offset, input_length)
+    {
+        /// <summary>Base function for converting block from BER encoded array of bytes</summary>
+        /// <param name="input_buffer" type="ArrayBuffer">ASN.1 BER encoded array</param>
+        /// <param name="input_offset" type="Number">Offset in ASN.1 BER encoded array where decoding should be started</param>
+        /// <param name="input_length" type="Number">Maximum length of array of bytes which can be using in this function</param>
+
+        var result_offset = this.value_block.fromBER(input_buffer, input_offset, (this.len_block.is_indefinite_form == true) ? input_length : this.len_block.length);
+        if(result_offset == (-1))
+        {
+            this.error = this.value_block.error;
+            return result_offset;
+        }
+
+        in_window.org.pkijs.asn1.UTCTIME.prototype.fromBuffer.call(this, this.value_block.value_hex);
+
+        if(this.id_block.error.length == 0)
+            this.block_length += this.id_block.block_length;
+
+        if(this.len_block.error.length == 0)
+            this.block_length += this.len_block.block_length;
+
+        if(this.value_block.error.length == 0)
+            this.block_length += this.value_block.block_length;
+
+        return result_offset;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.UTCTIME.prototype.fromBuffer =
+    function(input_buffer)
+    {
+        in_window.org.pkijs.asn1.UTCTIME.prototype.fromString.call(this, String.fromCharCode.apply(null, new Uint8Array(input_buffer)));
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.UTCTIME.prototype.toBuffer =
+    function()
+    {
+        var str = in_window.org.pkijs.asn1.UTCTIME.prototype.toString.call(this);
+
+        var buffer = new ArrayBuffer(str.length);
+        var view = new Uint8Array(buffer);
+
+        for(var i = 0; i < str.length; i++)
+            view[i] = str.charCodeAt(i);
+
+        return buffer;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.UTCTIME.prototype.fromDate =
+    function(input_date)
+    {
+        /// <summary>Create "UTCTime" ASN.1 type from JavaScript "Date" type</summary>
+
+        this.year = input_date.getUTCFullYear();
+        this.month = input_date.getUTCMonth() + 1;
+        this.day = input_date.getUTCDate();
+        this.hour = input_date.getUTCHours();
+        this.minute = input_date.getUTCMinutes();
+        this.second = input_date.getUTCSeconds();
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.UTCTIME.prototype.toDate =
+    function()
+    {
+        return (new Date(Date.UTC(this.year, this.month - 1, this.day, this.hour, this.minute, this.second)));
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.UTCTIME.prototype.fromString =
+    function(input_string)
+    {
+        /// <summary>Create "UTCTime" ASN.1 type from JavaScript "String" type</summary>
+
+        // #region Parse input string 
+        var parser = /(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})Z/ig;
+        var parser_array = parser.exec(input_string);
+        if(parser_array === null)
+        {
+            this.error = "Wrong input string for convertion";
+            return;
+        }
+        // #endregion 
+
+        // #region Store parsed values 
+        var year = parseInt(parser_array[1], 10);
+        if(year >= 50)
+            this.year = 1900 + year;
+        else
+            this.year = 2000 + year;
+
+        this.month = parseInt(parser_array[2], 10);
+        this.day = parseInt(parser_array[3], 10);
+        this.hour = parseInt(parser_array[4], 10);
+        this.minute = parseInt(parser_array[5], 10);
+        this.second = parseInt(parser_array[6], 10);
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.UTCTIME.prototype.toString =
+    function()
+    {
+        var output_array = new Array(7);
+
+        output_array[0] = in_window.org.pkijs.padNumber(((this.year < 2000) ? (this.year - 1900) : (this.year - 2000)), 2);
+        output_array[1] = in_window.org.pkijs.padNumber(this.month, 2);
+        output_array[2] = in_window.org.pkijs.padNumber(this.day, 2);
+        output_array[3] = in_window.org.pkijs.padNumber(this.hour, 2);
+        output_array[4] = in_window.org.pkijs.padNumber(this.minute, 2);
+        output_array[5] = in_window.org.pkijs.padNumber(this.second, 2);
+        output_array[6] = "Z";
+
+        return output_array.join('');
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.UTCTIME.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "UTCTIME";
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.UTCTIME.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = in_window.org.pkijs.asn1.VISIBLESTRING.prototype.toJSON.call(this);
+
+        _object.block_name = in_window.org.pkijs.asn1.UTCTIME.prototype.block_name.call(this);
+        _object.year = this.year;
+        _object.month = this.month;
+        _object.day = this.day;
+        _object.hour = this.hour;
+        _object.minute = this.minute;
+        _object.second = this.second;
+
+        return _object;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.GENERALIZEDTIME =
+    function()
+    {
+        in_window.org.pkijs.asn1.VISIBLESTRING.call(this, arguments[0]);
+
+        this.year = 0;
+        this.month = 0;
+        this.day = 0;
+        this.hour = 0;
+        this.minute = 0;
+        this.second = 0;
+        this.millisecond = 0;
+
+        // #region Create GeneralizedTime from ASN.1 string value 
+        if((arguments[0] instanceof Object) && ("value" in arguments[0]))
+        {
+            in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype.fromString.call(this, arguments[0].value);
+
+            this.value_block.value_hex = new ArrayBuffer(arguments[0].value.length);
+            var view = new Uint8Array(this.value_block.value_hex);
+
+            for(var i = 0; i < arguments[0].value.length; i++)
+                view[i] = arguments[0].value.charCodeAt(i);
+        }
+        // #endregion 
+        // #region Create GeneralizedTime from JavaScript Date type 
+        if((arguments[0] instanceof Object) && ("value_date" in arguments[0]))
+        {
+            in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype.fromDate.call(this, arguments[0].value_date);
+            this.value_block.value_hex = in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype.toBuffer.call(this);
+        }
+        // #endregion 
+
+        this.id_block.tag_class = 1; // UNIVERSAL
+        this.id_block.tag_number = 24; // GENERALIZEDTIME
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype = new in_window.org.pkijs.asn1.VISIBLESTRING();
+    in_window.org.pkijs.asn1.GENERALIZEDTIME.constructor = in_window.org.pkijs.asn1.GENERALIZEDTIME;
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype.fromBER =
+    function(input_buffer, input_offset, input_length)
+    {
+        /// <summary>Base function for converting block from BER encoded array of bytes</summary>
+        /// <param name="input_buffer" type="ArrayBuffer">ASN.1 BER encoded array</param>
+        /// <param name="input_offset" type="Number">Offset in ASN.1 BER encoded array where decoding should be started</param>
+        /// <param name="input_length" type="Number">Maximum length of array of bytes which can be using in this function</param>
+
+        var result_offset = this.value_block.fromBER(input_buffer, input_offset, (this.len_block.is_indefinite_form == true) ? input_length : this.len_block.length);
+        if(result_offset == (-1))
+        {
+            this.error = this.value_block.error;
+            return result_offset;
+        }
+
+        in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype.fromBuffer.call(this, this.value_block.value_hex);
+
+        if(this.id_block.error.length == 0)
+            this.block_length += this.id_block.block_length;
+
+        if(this.len_block.error.length == 0)
+            this.block_length += this.len_block.block_length;
+
+        if(this.value_block.error.length == 0)
+            this.block_length += this.value_block.block_length;
+
+        return result_offset;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype.fromBuffer =
+    function(input_buffer)
+    {
+        in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype.fromString.call(this, String.fromCharCode.apply(null, new Uint8Array(input_buffer)));
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype.toBuffer =
+    function()
+    {
+        var str = in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype.toString.call(this);
+
+        var buffer = new ArrayBuffer(str.length);
+        var view = new Uint8Array(buffer);
+
+        for(var i = 0; i < str.length; i++)
+            view[i] = str.charCodeAt(i);
+
+        return buffer;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype.fromDate =
+    function(input_date)
+    {
+        /// <summary>Create "GeneralizedTime" ASN.1 type from JavaScript "Date" type</summary>
+
+        this.year = input_date.getUTCFullYear();
+        this.month = input_date.getUTCMonth();
+        this.day = input_date.getUTCDate();
+        this.hour = input_date.getUTCHours();
+        this.minute = input_date.getUTCMinutes();
+        this.second = input_date.getUTCSeconds();
+        this.millisecond = input_date.getUTCMilliseconds();
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype.toDate =
+    function()
+    {
+        return (new Date(Date.UTC(this.year, this.month - 1, this.day, this.hour, this.minute, this.second, this.millisecond)));
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype.fromString =
+    function(input_string)
+    {
+        /// <summary>Create "GeneralizedTime" ASN.1 type from JavaScript "String" type</summary>
+
+        // #region Initial variables 
+        var isUTC = false;
+
+        var timeString = "";
+        var dateTimeString = "";
+        var fractionPart = 0;
+
+        var parser;
+
+        var hourDifference = 0;
+        var minuteDifference = 0;
+        // #endregion 
+
+        // #region Convert as UTC time 
+        if(input_string[input_string.length - 1] == "Z")
+        {
+            timeString = input_string.substr(0, input_string.length - 1);
+
+            isUTC = true;
+        }
+        // #endregion 
+        // #region Convert as local time 
+        else
+        {
+            var number = new Number(input_string[input_string.length - 1]);
+
+            if(isNaN(number.valueOf()))
+                throw new Error("Wrong input string for convertion");
+
+            timeString = input_string;
+        }
+        // #endregion 
+
+        // #region Check that we do not have a "+" and "-" symbols inside UTC time 
+        if(isUTC)
+        {
+            if(timeString.indexOf("+") != (-1))
+                throw new Error("Wrong input string for convertion");
+
+            if(timeString.indexOf("-") != (-1))
+                throw new Error("Wrong input string for convertion");
+        }
+        // #endregion 
+        // #region Get "UTC time difference" in case of local time
+        else
+        {
+            var multiplier = 1;
+            var differencePosition = timeString.indexOf("+");
+            var differenceString = "";
+
+            if(differencePosition == (-1))
+            {
+                differencePosition = timeString.indexOf("-");
+                multiplier = (-1);
+            }
+
+            if(differencePosition != (-1))
+            {
+                differenceString = timeString.substr(differencePosition + 1);
+                timeString = timeString.substr(0, differencePosition);
+
+                if((differenceString.length != 2) && (differenceString.length != 4))
+                    throw new Error("Wrong input string for convertion");
+
+                var number = new Number(differenceString.substr(0, 2));
+
+                if(isNaN(number.valueOf()))
+                    throw new Error("Wrong input string for convertion");
+
+                hourDifference = multiplier * number;
+
+                if(differenceString.length == 4)
+                {
+                    number = new Number(differenceString.substr(2, 2));
+
+                    if(isNaN(number.valueOf()))
+                        throw new Error("Wrong input string for convertion");
+
+                    minuteDifference = multiplier * number;
+                }
+            }
+        }
+        // #endregion 
+
+        // #region Get position of fraction point 
+        var fractionPointPosition = timeString.indexOf("."); // Check for "full stop" symbol
+        if(fractionPointPosition == (-1))
+            fractionPointPosition = timeString.indexOf(","); // Check for "comma" symbol
+        // #endregion 
+
+        // #region Get fraction part 
+        if(fractionPointPosition != (-1))
+        {
+            var fractionPartCheck = new Number("0" + timeString.substr(fractionPointPosition));
+
+            if(isNaN(fractionPartCheck.valueOf()))
+                throw new Error("Wrong input string for convertion");
+
+            fractionPart = fractionPartCheck.valueOf();
+
+            dateTimeString = timeString.substr(0, fractionPointPosition);
+        }
+        else
+            dateTimeString = timeString;
+        // #endregion 
+
+        // #region Parse internal date 
+        switch(true)
+        {
+            case (dateTimeString.length == 8): // "YYYYMMDD"
+                parser = /(\d{4})(\d{2})(\d{2})/ig;
+                if(fractionPointPosition !== (-1))
+                    throw new Error("Wrong input string for convertion"); // Here we should not have a "fraction point"
+                break;
+            case (dateTimeString.length == 10): // "YYYYMMDDHH"
+                parser = /(\d{4})(\d{2})(\d{2})(\d{2})/ig;
+
+                if(fractionPointPosition !== (-1))
+                {
+                    var fractionResult = 60 * fractionPart;
+                    this.minute = Math.floor(fractionResult);
+
+                    fractionResult = 60 * (fractionResult - this.minute);
+                    this.second = Math.floor(fractionResult);
+
+                    fractionResult = 1000 * (fractionResult - this.second);
+                    this.millisecond = Math.floor(fractionResult);
+                }
+                break;
+            case (dateTimeString.length == 12): // "YYYYMMDDHHMM"
+                parser = /(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})/ig;
+
+                if(fractionPointPosition !== (-1))
+                {
+                    var fractionResult = 60 * fractionPart;
+                    this.second = Math.floor(fractionResult);
+
+                    fractionResult = 1000 * (fractionResult - this.second);
+                    this.millisecond = Math.floor(fractionResult);
+                }
+                break;
+            case (dateTimeString.length == 14): // "YYYYMMDDHHMMSS"
+                parser = /(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/ig;
+
+                if(fractionPointPosition !== (-1))
+                {
+                    var fractionResult = 1000 * fractionPart;
+                    this.millisecond = Math.floor(fractionResult);
+                }
+                break;
+            default:
+                throw new Error("Wrong input string for convertion");
+        }
+        // #endregion 
+
+        // #region Put parsed values at right places 
+        var parser_array = parser.exec(dateTimeString);
+        if(parser_array == null)
+            throw new Error("Wrong input string for convertion");
+
+        for(var j = 1; j < parser_array.length; j++)
+        {
+            switch(j)
+            {
+                case 1:
+                    this.year = parseInt(parser_array[j], 10);
+                    break;
+                case 2:
+                    this.month = parseInt(parser_array[j], 10) - 1; // In JavaScript we have month range as "0 - 11"
+                    break;
+                case 3:
+                    this.day = parseInt(parser_array[j], 10);
+                    break;
+                case 4:
+                    this.hour = parseInt(parser_array[j], 10) + hourDifference;
+                    break;
+                case 5:
+                    this.minute = parseInt(parser_array[j], 10) + minuteDifference;
+                    break;
+                case 6:
+                    this.second = parseInt(parser_array[j], 10);
+                    break;
+                default:
+                    throw new Error("Wrong input string for convertion");
+            }
+        }
+        // #endregion 
+
+        // #region Get final date 
+        if(isUTC == false)
+        {
+            var tempDate = new Date(this.year, this.month, this.day, this.hour, this.minute, this.second, this.millisecond);
+
+            this.year = tempDate.getUTCFullYear();
+            this.month = tempDate.getUTCMonth();
+            this.day = tempDate.getUTCDay();
+            this.hour = tempDate.getUTCHours();
+            this.minute = tempDate.getUTCMinutes();
+            this.second = tempDate.getUTCSeconds();
+            this.millisecond = tempDate.getUTCMilliseconds();
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype.toString =
+    function()
+    {
+        var output_array = new Array();
+
+        output_array.push(in_window.org.pkijs.padNumber(this.year, 4));
+        output_array.push(in_window.org.pkijs.padNumber(this.month, 2));
+        output_array.push(in_window.org.pkijs.padNumber(this.day, 2));
+        output_array.push(in_window.org.pkijs.padNumber(this.hour, 2));
+        output_array.push(in_window.org.pkijs.padNumber(this.minute, 2));
+        output_array.push(in_window.org.pkijs.padNumber(this.second, 2));
+        if(this.millisecond != 0)
+        {
+            output_array.push(".");
+            output_array.push(in_window.org.pkijs.padNumber(this.millisecond, 3));
+        }
+        output_array.push("Z");
+
+        return output_array.join('');
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "GENERALIZEDTIME";
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = in_window.org.pkijs.asn1.VISIBLESTRING.prototype.toJSON.call(this);
+
+        _object.block_name = in_window.org.pkijs.asn1.GENERALIZEDTIME.prototype.block_name.call(this);
+        _object.year = this.year;
+        _object.month = this.month;
+        _object.day = this.day;
+        _object.hour = this.hour;
+        _object.minute = this.minute;
+        _object.second = this.second;
+        _object.millisecond = this.millisecond;
+
+        return _object;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.DATE =
+    function()
+    {
+        in_window.org.pkijs.asn1.UTF8STRING.call(this, arguments[0]);
+
+        this.id_block.tag_class = 1; // UNIVERSAL
+        this.id_block.tag_number = 31; // DATE
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.DATE.prototype = new in_window.org.pkijs.asn1.UTF8STRING();
+    in_window.org.pkijs.asn1.DATE.constructor = in_window.org.pkijs.asn1.DATE;
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.DATE.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "DATE";
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.DATE.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = in_window.org.pkijs.asn1.UTF8STRING.prototype.toJSON.call(this);
+
+        _object.block_name = in_window.org.pkijs.asn1.DATE.prototype.block_name.call(this);
+
+        return _object;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.TIMEOFDAY =
+    function()
+    {
+        in_window.org.pkijs.asn1.UTF8STRING.call(this, arguments[0]);
+
+        this.id_block.tag_class = 1; // UNIVERSAL
+        this.id_block.tag_number = 32; // TIMEOFDAY
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.TIMEOFDAY.prototype = new in_window.org.pkijs.asn1.UTF8STRING();
+    in_window.org.pkijs.asn1.TIMEOFDAY.constructor = in_window.org.pkijs.asn1.TIMEOFDAY;
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.TIMEOFDAY.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "TIMEOFDAY";
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.TIMEOFDAY.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = in_window.org.pkijs.asn1.UTF8STRING.prototype.toJSON.call(this);
+
+        _object.block_name = in_window.org.pkijs.asn1.TIMEOFDAY.prototype.block_name.call(this);
+
+        return _object;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.DATETIME =
+    function()
+    {
+        in_window.org.pkijs.asn1.UTF8STRING.call(this, arguments[0]);
+
+        this.id_block.tag_class = 1; // UNIVERSAL
+        this.id_block.tag_number = 33; // DATETIME
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.DATETIME.prototype = new in_window.org.pkijs.asn1.UTF8STRING();
+    in_window.org.pkijs.asn1.DATETIME.constructor = in_window.org.pkijs.asn1.DATETIME;
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.DATETIME.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "DATETIME";
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.DATETIME.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = in_window.org.pkijs.asn1.UTF8STRING.prototype.toJSON.call(this);
+
+        _object.block_name = in_window.org.pkijs.asn1.DATETIME.prototype.block_name.call(this);
+
+        return _object;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.DURATION =
+    function()
+    {
+        in_window.org.pkijs.asn1.UTF8STRING.call(this, arguments[0]);
+
+        this.id_block.tag_class = 1; // UNIVERSAL
+        this.id_block.tag_number = 34; // DURATION
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.DURATION.prototype = new in_window.org.pkijs.asn1.UTF8STRING();
+    in_window.org.pkijs.asn1.DURATION.constructor = in_window.org.pkijs.asn1.DURATION;
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.DURATION.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "DURATION";
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.DURATION.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = in_window.org.pkijs.asn1.UTF8STRING.prototype.toJSON.call(this);
+
+        _object.block_name = in_window.org.pkijs.asn1.DURATION.prototype.block_name.call(this);
+
+        return _object;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.TIME =
+    function()
+    {
+        in_window.org.pkijs.asn1.UTF8STRING.call(this, arguments[0]);
+
+        this.id_block.tag_class = 1; // UNIVERSAL
+        this.id_block.tag_number = 14; // TIME
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.TIME.prototype = new in_window.org.pkijs.asn1.UTF8STRING();
+    in_window.org.pkijs.asn1.TIME.constructor = in_window.org.pkijs.asn1.TIME;
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.TIME.prototype.block_name =
+    function()
+    {
+        /// <summary>Aux function, need to get a block name. Need to have it here for inhiritence</summary>
+
+        return "TIME";
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.TIME.prototype.toJSON =
+    function()
+    {
+        /// <summary>Convertion for the block to JSON object</summary>
+
+        var _object = in_window.org.pkijs.asn1.UTF8STRING.prototype.toJSON.call(this);
+
+        _object.block_name = in_window.org.pkijs.asn1.TIME.prototype.block_name.call(this);
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Declaration of special ASN.1 schema type CHOICE 
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.CHOICE =
+    function()
+    {
+        if(arguments[0] instanceof Object)
+        {
+            this.value = in_window.org.pkijs.getValue(arguments[0], "value", new Array()); // Array of ASN.1 types for make a choice from
+            this.optional = in_window.org.pkijs.getValue(arguments[0], "optional", false);
+        }
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Declaration of special ASN.1 schema type ANY 
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.ANY =
+    function()
+    {
+        if(arguments[0] instanceof Object)
+        {
+            this.name = in_window.org.pkijs.getValue(arguments[0], "name", "");
+            this.optional = in_window.org.pkijs.getValue(arguments[0], "optional", false);
+        }
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Declaration of special ASN.1 schema type REPEATED 
+    //**************************************************************************************
+    in_window.org.pkijs.asn1.REPEATED =
+    function()
+    {
+        if(arguments[0] instanceof Object)
+        {
+            this.name = in_window.org.pkijs.getValue(arguments[0], "name", "");
+            this.optional = in_window.org.pkijs.getValue(arguments[0], "optional", false);
+            this.value = in_window.org.pkijs.getValue(arguments[0], "value", new in_window.org.pkijs.asn1.ANY());
+            this.local = in_window.org.pkijs.getValue(arguments[0], "local", false); // Could local or global array to store elements
+        }
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Major ASN.1 BER decoding function
+    //**************************************************************************************
+    function fromBER_raw(input_buffer, input_offset, input_length)
+    {
+        var incoming_offset = input_offset; // Need to store initial offset since "input_offset" is changing in the function
+
+        // #region Local function changing a type for ASN.1 classes 
+        function local_change_type(input_object, new_type)
+        {
+            if(input_object instanceof new_type)
+                return input_object;
+
+            var new_object = new new_type();
+            new_object.id_block = input_object.id_block;
+            new_object.len_block = input_object.len_block;
+            new_object.warnings = input_object.warnings;
+            new_object.value_before_decode = util_copybuf(input_object.value_before_decode);
+
+            return new_object;
+        }
+        // #endregion 
+
+        // #region Create a basic ASN.1 type since we need to return errors and warnings from the function 
+        var return_object = new in_window.org.pkijs.asn1.ASN1_block();
+        // #endregion 
+
+        // #region Basic check for parameters 
+        if(check_buffer_params(input_buffer, input_offset, input_length) === false)
+        {
+            return_object.error = "Wrong input parameters";
+            return {
+                offset: (-1),
+                result: return_object
+            };
+        }
+        // #endregion 
+
+        // #region Getting Uint8Array from ArrayBuffer 
+        var int_buffer = new Uint8Array(input_buffer, input_offset, input_length);
+        // #endregion 
+
+        // #region Initial checks 
+        if(int_buffer.length == 0)
+        {
+            this.error = "Zero buffer length";
+            return {
+                offset: (-1),
+                result: return_object
+            };
+        }
+        // #endregion 
+
+        // #region Decode indentifcation block of ASN.1 BER structure 
+        var result_offset = return_object.id_block.fromBER(input_buffer, input_offset, input_length);
+        return_object.warnings.concat(return_object.id_block.warnings);
+        if(result_offset == (-1))
+        {
+            return_object.error = return_object.id_block.error;
+            return {
+                offset: (-1),
+                result: return_object
+            };
+        }
+
+        input_offset = result_offset;
+        input_length -= return_object.id_block.block_length;
+        // #endregion 
+
+        // #region Decode length block of ASN.1 BER structure 
+        result_offset = return_object.len_block.fromBER(input_buffer, input_offset, input_length);
+        return_object.warnings.concat(return_object.len_block.warnings);
+        if(result_offset == (-1))
+        {
+            return_object.error = return_object.len_block.error;
+            return {
+                offset: (-1),
+                result: return_object
+            };
+        }
+
+        input_offset = result_offset;
+        input_length -= return_object.len_block.block_length;
+        // #endregion 
+
+        // #region Check for usign indefinite length form in encoding for primitive types 
+        if((return_object.id_block.is_constructed == false) &&
+           (return_object.len_block.is_indefinite_form == true))
+        {
+            return_object.error = new String("Indefinite length form used for primitive encoding form");
+            return {
+                offset: (-1),
+                result: return_object
+            };
+        }
+        // #endregion 
+
+        // #region Switch ASN.1 block type 
+        var new_asn1_type = in_window.org.pkijs.asn1.ASN1_block;
+
+        switch(return_object.id_block.tag_class)
+        {
+            // #region UNIVERSAL 
+            case 1: 
+                // #region Check for reserved tag numbers 
+                if((return_object.id_block.tag_number >= 37) &&
+                   (return_object.id_block.is_hex_only == false))
+                {
+                    return_object.error = "UNIVERSAL 37 and upper tags are reserved by ASN.1 standard";
+                    return {
+                        offset: (-1),
+                        result: return_object
+                    };
+                }
+                // #endregion 
+
+                switch(return_object.id_block.tag_number)
+                {
+                    // #region EOC type 
+                    case 0:
+                        // #region Check for EOC type 
+                        if((return_object.id_block.is_constructed == true) &&
+                           (return_object.len_block.length > 0))
+                        {
+                            return_object.error = "Type [UNIVERSAL 0] is reserved";
+                            return {
+                                offset: (-1),
+                                result: return_object
+                            };
+                        }
+                        // #endregion 
+
+                        new_asn1_type = in_window.org.pkijs.asn1.EOC;
+
+                        break;
+                        // #endregion 
+                    // #region BOOLEAN type 
+                    case 1:
+                        new_asn1_type = in_window.org.pkijs.asn1.BOOLEAN;
+                        break;
+                    // #endregion 
+                    // #region INTEGER type 
+                    case 2:
+                        new_asn1_type = in_window.org.pkijs.asn1.INTEGER;
+                        break;
+                    // #endregion 
+                    // #region BITSTRING type 
+                    case 3:
+                        new_asn1_type = in_window.org.pkijs.asn1.BITSTRING;
+                        break;
+                    // #endregion 
+                    // #region OCTETSTRING type 
+                    case 4:
+                        new_asn1_type = in_window.org.pkijs.asn1.OCTETSTRING;
+                        break;
+                    // #endregion 
+                    // #region NULL type 
+                    case 5:
+                        new_asn1_type = in_window.org.pkijs.asn1.NULL;
+                        break;
+                    // #endregion 
+                    // #region OBJECT IDENTIFIER type 
+                    case 6:
+                        new_asn1_type = in_window.org.pkijs.asn1.OID;
+                        break;
+                    // #endregion 
+                    // #region ENUMERATED type 
+                    case 10:
+                        new_asn1_type = in_window.org.pkijs.asn1.ENUMERATED;
+                        break;
+                    // #endregion 
+                    // #region UTF8STRING type 
+                    case 12:
+                        new_asn1_type = in_window.org.pkijs.asn1.UTF8STRING;
+                        break;
+                    // #endregion 
+                    // #region TIME type 
+                    case 14:
+                        new_asn1_type = in_window.org.pkijs.asn1.TIME;
+                        break;
+                    // #endregion 
+                    // #region ASN.1 reserved type 
+                    case 15:
+                        return_object.error = "[UNIVERSAL 15] is reserved by ASN.1 standard";
+                        return {
+                            offset: (-1),
+                            result: return_object
+                        };
+                        break;
+                    // #endregion 
+                    // #region SEQUENCE type 
+                    case 16:
+                        new_asn1_type = in_window.org.pkijs.asn1.SEQUENCE;
+                        break;
+                    // #endregion 
+                    // #region SET type 
+                    case 17:
+                        new_asn1_type = in_window.org.pkijs.asn1.SET;
+                        break;
+                    // #endregion 
+                    // #region NUMERICSTRING type 
+                    case 18:
+                        new_asn1_type = in_window.org.pkijs.asn1.NUMERICSTRING;
+                        break;
+                    // #endregion 
+                    // #region PRINTABLESTRING type 
+                    case 19:
+                        new_asn1_type = in_window.org.pkijs.asn1.PRINTABLESTRING;
+                        break;
+                    // #endregion 
+                    // #region TELETEXSTRING type 
+                    case 20:
+                        new_asn1_type = in_window.org.pkijs.asn1.TELETEXSTRING;
+                        break;
+                    // #endregion 
+                    // #region VIDEOTEXSTRING type 
+                    case 21:
+                        new_asn1_type = in_window.org.pkijs.asn1.VIDEOTEXSTRING;
+                        break;
+                    // #endregion 
+                    // #region IA5STRING type 
+                    case 22:
+                        new_asn1_type = in_window.org.pkijs.asn1.IA5STRING;
+                        break;
+                    // #endregion 
+                    // #region UTCTIME type 
+                    case 23:
+                        new_asn1_type = in_window.org.pkijs.asn1.UTCTIME;
+                        break;
+                    // #endregion 
+                    // #region GENERALIZEDTIME type 
+                    case 24:
+                        new_asn1_type = in_window.org.pkijs.asn1.GENERALIZEDTIME;
+                        break;
+                    // #endregion 
+                    // #region GRAPHICSTRING type 
+                    case 25:
+                        new_asn1_type = in_window.org.pkijs.asn1.GRAPHICSTRING;
+                        break;
+                    // #endregion 
+                    // #region VISIBLESTRING type 
+                    case 26:
+                        new_asn1_type = in_window.org.pkijs.asn1.VISIBLESTRING;
+                        break;
+                    // #endregion 
+                    // #region GENERALSTRING type 
+                    case 27:
+                        new_asn1_type = in_window.org.pkijs.asn1.GENERALSTRING;
+                        break;
+                    // #endregion 
+                    // #region UNIVERSALSTRING type 
+                    case 28:
+                        new_asn1_type = in_window.org.pkijs.asn1.UNIVERSALSTRING;
+                        break;
+                    // #endregion 
+                    // #region CHARACTERSTRING type 
+                    case 29:
+                        new_asn1_type = in_window.org.pkijs.asn1.CHARACTERSTRING;
+                        break;
+                    // #endregion 
+                    // #region BMPSTRING type 
+                    case 30:
+                        new_asn1_type = in_window.org.pkijs.asn1.BMPSTRING;
+                        break;
+                    // #endregion 
+                    // #region DATE type 
+                    case 31:
+                        new_asn1_type = in_window.org.pkijs.asn1.DATE;
+                        break;
+                    // #endregion 
+                    // #region TIMEOFDAY type 
+                    case 32:
+                        new_asn1_type = in_window.org.pkijs.asn1.TIMEOFDAY;
+                        break;
+                    // #endregion 
+                    // #region DATE-TIME type 
+                    case 33:
+                        new_asn1_type = in_window.org.pkijs.asn1.DATETIME;
+                        break;
+                    // #endregion 
+                    // #region DURATION type 
+                    case 34:
+                        new_asn1_type = in_window.org.pkijs.asn1.DURATION;
+                        break;
+                    // #endregion 
+                    // #region default 
+                    default:
+                        {
+                            var new_object;
+
+                            if(return_object.id_block.is_constructed == true)
+                                new_object = new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED();
+                            else
+                                new_object = new in_window.org.pkijs.asn1.ASN1_PRIMITIVE();
+
+                            new_object.id_block = return_object.id_block;
+                            new_object.len_block = return_object.len_block;
+                            new_object.warnings = return_object.warnings;
+
+                            return_object = new_object;
+
+                            result_offset = return_object.fromBER(input_buffer, input_offset, input_length);
+                        }
+                    // #endregion 
+                }
+                break;
+            // #endregion 
+            // #region All other tag classes 
+            case 2: // APPLICATION
+            case 3: // CONTEXT-SPECIFIC
+            case 4: // PRIVATE
+            default:
+                {
+                    if(return_object.id_block.is_constructed == true)
+                        new_asn1_type = in_window.org.pkijs.asn1.ASN1_CONSTRUCTED;
+                    else
+                        new_asn1_type = in_window.org.pkijs.asn1.ASN1_PRIMITIVE;
+                }
+            // #endregion 
+        }
+        // #endregion 
+
+        // #region Change type and perform BER decoding 
+        return_object = local_change_type(return_object, new_asn1_type);
+        result_offset = return_object.fromBER(input_buffer, input_offset, (return_object.len_block.is_indefinite_form == true) ? input_length : return_object.len_block.length);
+        // #endregion 
+
+        // #region Coping incoming buffer for entire ASN.1 block 
+        return_object.value_before_decode = util_copybuf_offset(input_buffer, incoming_offset, return_object.block_length);
+        // #endregion 
+
+        return {
+            offset: result_offset,
+            result: return_object
+        };
+    }
+    //**************************************************************************************
+    in_window.org.pkijs.fromBER = 
+    function(input_buffer)
+    {
+        /// <summary>Major function for decoding ASN.1 BER array into internal library structuries</summary>
+        /// <param name="input_buffer" type="ArrayBuffer">ASN.1 BER encoded array of bytes</param>
+
+        if(input_buffer.byteLength == 0)
+        {
+            var result = new in_window.org.pkijs.asn1.ASN1_block();
+            result.error = "Input buffer has zero length";
+
+            return {
+                offset: (-1),
+                result: result
+            };
+        }
+
+        return fromBER_raw(input_buffer, 0, input_buffer.byteLength);
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Major scheme verification function 
+    //**************************************************************************************
+    in_window.org.pkijs.compareSchema =
+    function(root, input_asn1_data, input_asn1_schema)
+    {
+        // #region Special case for CHOICE schema element type 
+        if(input_asn1_schema instanceof in_window.org.pkijs.asn1.CHOICE)
+        {
+            var choice_result = false;
+
+            for(var j = 0; j < input_asn1_schema.value.length; j++)
+            {
+                var result = in_window.org.pkijs.compareSchema(root, input_asn1_data, input_asn1_schema.value[j]);
+                if(result.verified === true)
+                    return {
+                        verified: true,
+                        result: root
+                    };
+            }
+
+            if(choice_result === false)
+            {
+                var _result = {
+                    verified: false,
+                    result: {
+                        error: "Wrong values for CHOICE type"
+                    }
+                };
+
+                if(input_asn1_schema.hasOwnProperty('name'))
+                    _result.name = input_asn1_schema.name;
+
+                return _result;
+            }
+        }
+        // #endregion 
+
+        // #region Special case for ANY schema element type 
+        if(input_asn1_schema instanceof in_window.org.pkijs.asn1.ANY)
+        {
+            // #region Add named component of ASN.1 schema 
+            if(input_asn1_schema.hasOwnProperty('name'))
+                root[input_asn1_schema.name] = input_asn1_data;
+            // #endregion 
+
+            return {
+                verified: true,
+                result: root
+            };
+        }
+        // #endregion 
+
+        // #region Initial check 
+        if((root instanceof Object) === false)
+            return {
+                verified: false,
+                result: { error: "Wrong root object" }
+            };
+
+        if((input_asn1_data instanceof Object) === false)
+            return {
+                verified: false,
+                result: { error: "Wrong ASN.1 data" }
+            };
+
+        if((input_asn1_schema instanceof Object) === false)
+            return {
+                verified: false,
+                result: { error: "Wrong ASN.1 schema" }
+            };
+
+        if(('id_block' in input_asn1_schema) === false)
+            return {
+                verified: false,
+                result: { error: "Wrong ASN.1 schema" }
+            };
+        // #endregion 
+
+        // #region Comparing id_block properties in ASN.1 data and ASN.1 schema 
+        // #region Encode and decode ASN.1 schema id_block 
+        /// <remarks>This encoding/decoding is neccessary because could be an errors in schema definition</remarks>
+        if(('fromBER' in input_asn1_schema.id_block) === false)
+            return {
+                verified: false,
+                result: { error: "Wrong ASN.1 schema" }
+            };
+
+        if(('toBER' in input_asn1_schema.id_block) === false)
+            return {
+                verified: false,
+                result: { error: "Wrong ASN.1 schema" }
+            };
+
+        var encoded_id = input_asn1_schema.id_block.toBER(false);
+        if(encoded_id.byteLength === 0)
+            return {
+                verified: false,
+                result: { error: "Error encoding id_block for ASN.1 schema" }
+            };
+
+        var decoded_offset = input_asn1_schema.id_block.fromBER(encoded_id, 0, encoded_id.byteLength);
+        if(decoded_offset === (-1))
+            return {
+                verified: false,
+                result: { error: "Error decoding id_block for ASN.1 schema" }
+            };
+        // #endregion 
+
+        // #region tag_class 
+        if(input_asn1_schema.id_block.hasOwnProperty('tag_class') === false)
+            return {
+                verified: false,
+                result: { error: "Wrong ASN.1 schema" }
+            };
+
+        if(input_asn1_schema.id_block.tag_class !== input_asn1_data.id_block.tag_class)
+            return {
+                verified: false,
+                result: root
+            };
+        // #endregion 
+        // #region tag_number 
+        if(input_asn1_schema.id_block.hasOwnProperty('tag_number') === false)
+            return {
+                verified: false,
+                result: { error: "Wrong ASN.1 schema" }
+            };
+
+        if(input_asn1_schema.id_block.tag_number !== input_asn1_data.id_block.tag_number)
+            return {
+                verified: false,
+                result: root
+            };
+        // #endregion 
+        // #region is_constructed 
+        if(input_asn1_schema.id_block.hasOwnProperty('is_constructed') === false)
+            return {
+                verified: false,
+                result: { error: "Wrong ASN.1 schema" }
+            };
+
+        if(input_asn1_schema.id_block.is_constructed !== input_asn1_data.id_block.is_constructed)
+            return {
+                verified: false,
+                result: root
+            };
+        // #endregion 
+        // #region is_hex_only 
+        if(('is_hex_only' in input_asn1_schema.id_block) === false) // Since 'is_hex_only' is an inhirited property
+            return {
+                verified: false,
+                result: { error: "Wrong ASN.1 schema" }
+            };
+
+        if(input_asn1_schema.id_block.is_hex_only !== input_asn1_data.id_block.is_hex_only)
+            return {
+                verified: false,
+                result: root
+            };
+        // #endregion 
+        // #region value_hex 
+        if(input_asn1_schema.id_block.is_hex_only === true)
+        {
+            if(('value_hex' in input_asn1_schema.id_block) === false) // Since 'value_hex' is an inhirited property
+                return {
+                    verified: false,
+                    result: { error: "Wrong ASN.1 schema" }
+                };
+
+            var schema_view = new Uint8Array(input_asn1_schema.id_block.value_hex);
+            var asn1_view = new Uint8Array(input_asn1_data.id_block.value_hex);
+
+            if(schema_view.length !== asn1_view.length)
+                return {
+                    verified: false,
+                    result: root
+                };
+
+            for(var i = 0; i < schema_view.length; i++)
+            {
+                if(schema_view[i] !== asn1_view[1])
+                    return {
+                        verified: false,
+                        result: root
+                    };
+            }
+        }
+        // #endregion 
+        // #endregion 
+
+        // #region Add named component of ASN.1 schema 
+        if(input_asn1_schema.hasOwnProperty('name'))
+        {
+            input_asn1_schema.name = input_asn1_schema.name.replace(/^\s+|\s+$/g, '');
+            if(input_asn1_schema.name !== "")
+                root[input_asn1_schema.name] = input_asn1_data;
+        }
+        // #endregion 
+
+        // #region Getting next ASN.1 block for comparition 
+        if(input_asn1_schema.id_block.is_constructed === true)
+        {
+            var admission = 0;
+            var result = { verified: false };
+
+            var max_length = input_asn1_schema.value_block.value.length;
+
+            if(max_length > 0)
+            {
+                if(input_asn1_schema.value_block.value[0] instanceof in_window.org.pkijs.asn1.REPEATED)
+                    max_length = input_asn1_data.value_block.value.length;
+            }
+
+            // #region Special case when constructive value has no elements 
+            if(max_length === 0)
+                return {
+                    verified: true,
+                    result: root
+                };
+            // #endregion 
+
+            // #region Special case when "input_asn1_data" has no values and "input_asn1_schema" has all optional values
+            if((input_asn1_data.value_block.value.length === 0) && 
+               (input_asn1_schema.value_block.value.length !== 0))
+            {
+                var _optional = true;
+
+                for(var i = 0; i < input_asn1_schema.value_block.value.length; i++)
+                    _optional = _optional && (input_asn1_schema.value_block.value[i].optional || false);
+
+                if(_optional === true)
+                {
+                    return {
+                        verified: true,
+                        result: root
+                    };
+                }
+                else
+                {
+                    // #region Delete early added name of block 
+                    if(input_asn1_schema.hasOwnProperty('name'))
+                    {
+                        input_asn1_schema.name = input_asn1_schema.name.replace(/^\s+|\s+$/g, '');
+                        if(input_asn1_schema.name !== "")
+                            delete root[input_asn1_schema.name];
+                    }
+                    // #endregion 
+
+                    root.error = "Inconsistent object length";
+
+                    return {
+                        verified: false,
+                        result: root
+                    };
+                }
+            }
+            // #endregion 
+
+            for(var i = 0; i < max_length; i++)
+            {
+                // #region Special case when there is an "optional" element of ASN.1 schema at the end 
+                if((i - admission) >= input_asn1_data.value_block.value.length)
+                {
+                    if(input_asn1_schema.value_block.value[i].optional === false)
+                    {
+                        var _result = {
+                            verified: false,
+                            result: root
+                        };
+
+                        root.error = "Inconsistent length between ASN.1 data and schema";
+
+                        // #region Delete early added name of block 
+                        if(input_asn1_schema.hasOwnProperty('name'))
+                        {
+                            input_asn1_schema.name = input_asn1_schema.name.replace(/^\s+|\s+$/g, '');
+                            if(input_asn1_schema.name !== "")
+                            {
+                                delete root[input_asn1_schema.name];
+                                _result.name = input_asn1_schema.name;
+                            }
+                        }
+                        // #endregion 
+
+                        return _result;
+                    }
+                }
+                    // #endregion 
+                else
+                {
+                    // #region Special case for REPEATED type of ASN.1 schema element 
+                    if(input_asn1_schema.value_block.value[0] instanceof in_window.org.pkijs.asn1.REPEATED)
+                    {
+                        result = in_window.org.pkijs.compareSchema(root, input_asn1_data.value_block.value[i], input_asn1_schema.value_block.value[0].value);
+                        if(result.verified === false)
+                        {
+                            if(input_asn1_schema.value_block.value[0].optional === true)
+                                admission++;
+                            else
+                            {
+                                // #region Delete early added name of block 
+                                if(input_asn1_schema.hasOwnProperty('name'))
+                                {
+                                    input_asn1_schema.name = input_asn1_schema.name.replace(/^\s+|\s+$/g, '');
+                                    if(input_asn1_schema.name !== "")
+                                        delete root[input_asn1_schema.name];
+                                }
+                                // #endregion 
+
+                                return result;
+                            }
+                        }
+
+                        if(("name" in input_asn1_schema.value_block.value[0]) && (input_asn1_schema.value_block.value[0].name.length > 0))
+                        {
+                            var array_root = {};
+
+                            if(("local" in input_asn1_schema.value_block.value[0]) && (input_asn1_schema.value_block.value[0].local === true))
+                                array_root = input_asn1_data;
+                            else
+                                array_root = root;
+
+                            if(typeof array_root[input_asn1_schema.value_block.value[0].name] === "undefined")
+                                array_root[input_asn1_schema.value_block.value[0].name] = new Array();
+
+                            array_root[input_asn1_schema.value_block.value[0].name].push(input_asn1_data.value_block.value[i]);
+                        }
+                    }
+                        // #endregion 
+                    else
+                    {
+                        result = in_window.org.pkijs.compareSchema(root, input_asn1_data.value_block.value[i - admission], input_asn1_schema.value_block.value[i]);
+                        if(result.verified === false)
+                        {
+                            if(input_asn1_schema.value_block.value[i].optional === true)
+                                admission++;
+                            else
+                            {
+                                // #region Delete early added name of block 
+                                if(input_asn1_schema.hasOwnProperty('name'))
+                                {
+                                    input_asn1_schema.name = input_asn1_schema.name.replace(/^\s+|\s+$/g, '');
+                                    if(input_asn1_schema.name !== "")
+                                        delete root[input_asn1_schema.name];
+                                }
+                                // #endregion 
+
+                                return result;
+                            }
+                        }
+                    }
+                }
+            }
+
+            if(result.verified === false) // The situation may take place if last element is "optional" and verification failed
+            {
+                var _result = {
+                    verified: false,
+                    result: root
+                };
+
+                // #region Delete early added name of block 
+                if(input_asn1_schema.hasOwnProperty('name'))
+                {
+                    input_asn1_schema.name = input_asn1_schema.name.replace(/^\s+|\s+$/g, '');
+                    if(input_asn1_schema.name !== "")
+                    {
+                        delete root[input_asn1_schema.name];
+                        _result.name = input_asn1_schema.name;
+                    }
+                }
+                // #endregion 
+
+                return _result;
+            }
+
+            return {
+                verified: true,
+                result: root
+            };
+        }
+        // #endregion 
+        // #region Ability to parse internal value for primitive-encoded value (value of OCTETSTRING, for example)
+        else
+        {
+            if( ("primitive_schema" in input_asn1_schema) &&
+                ("value_hex" in input_asn1_data.value_block) )
+            {
+                // #region Decoding of raw ASN.1 data 
+                var asn1 = in_window.org.pkijs.fromBER(input_asn1_data.value_block.value_hex);
+                if(asn1.offset === (-1))
+                {
+                    var _result = {
+                        verified: false,
+                        result: asn1.result
+                    };
+
+                    // #region Delete early added name of block 
+                    if(input_asn1_schema.hasOwnProperty('name'))
+                    {
+                        input_asn1_schema.name = input_asn1_schema.name.replace(/^\s+|\s+$/g, '');
+                        if(input_asn1_schema.name !== "")
+                        {
+                            delete root[input_asn1_schema.name];
+                            _result.name = input_asn1_schema.name;
+                        }
+                    }
+                    // #endregion 
+
+                    return _result;
+                }
+                // #endregion 
+
+                return in_window.org.pkijs.compareSchema(root, asn1.result, input_asn1_schema.primitive_schema);
+            }
+            else
+                return {
+                    verified: true,
+                    result: root
+                };
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.verifySchema =
+    function(input_buffer, input_schema)
+    {
+        // #region Initial check 
+        if((input_schema instanceof Object) === false)
+            return {
+                verified: false,
+                result: { error: "Wrong ASN.1 schema type" }
+            };
+        // #endregion 
+
+        // #region Decoding of raw ASN.1 data 
+        var asn1 = in_window.org.pkijs.fromBER(input_buffer);
+        if(asn1.offset === (-1))
+            return {
+                verified: false,
+                result: asn1.result
+            };
+        // #endregion 
+
+        // #region Compare ASN.1 struct with input schema 
+        return in_window.org.pkijs.compareSchema(asn1.result, asn1.result, input_schema);
+        // #endregion 
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Major function converting JSON to ASN.1 objects 
+    //**************************************************************************************
+    in_window.org.pkijs.fromJSON = 
+    function(json)
+    {
+        /// <summary>Converting from JSON to ASN.1 objects</summary>
+        /// <param name="json" type="String|Object">JSON string or object to convert to ASN.1 objects</param>
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+}
+)(typeof exports !== "undefined" ? exports : window);
\ No newline at end of file
diff --git a/dom/webauthn/tests/pkijs/common.js b/dom/webauthn/tests/pkijs/common.js
new file mode 100644
index 0000000000..1bc9eda7f8
--- /dev/null
+++ b/dom/webauthn/tests/pkijs/common.js
@@ -0,0 +1,1542 @@
+/*
+ * Copyright (c) 2014, GMO GlobalSign
+ * Copyright (c) 2015, Peculiar Ventures
+ * All rights reserved.
+ *
+ * Author 2014-2015, Yury Strozhevsky <www.strozhevsky.com>.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, 
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, 
+ *    this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, 
+ *    this list of conditions and the following disclaimer in the documentation 
+ *    and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors 
+ *    may be used to endorse or promote products derived from this software without 
+ *    specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
+ * OF SUCH DAMAGE. 
+ *
+ */
+(
+function(in_window)
+{
+    //**************************************************************************************
+    // #region Declaration of global variables 
+    //**************************************************************************************
+    // #region "org" namespace 
+    if(typeof in_window.org === "undefined")
+        in_window.org = {};
+    else
+    {
+        if(typeof in_window.org !== "object")
+            throw new Error("Name org already exists and it's not an object");
+    }
+    // #endregion 
+
+    // #region "org.pkijs" namespace 
+    if(typeof in_window.org.pkijs === "undefined")
+        in_window.org.pkijs = {};
+    else
+    {
+        if(typeof in_window.org.pkijs !== "object")
+            throw new Error("Name org.pkijs already exists and it's not an object" + " but " + (typeof in_window.org.pkijs));
+    }
+    // #endregion 
+
+    // #region "local" namespace 
+    var local = {};
+    // #endregion   
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Settings for "crypto engine" 
+    //**************************************************************************************
+    local.engine = {
+        name: "none",
+        crypto: null,
+        subtle: null
+    };
+
+    if(typeof window != "undefined")
+    {
+        if("crypto" in window)
+        {
+            var engineName = "webcrypto";
+            var cryptoObject = window.crypto;
+            var subtleObject = null;
+
+            // Apple Safari support
+            if("webkitSubtle" in window.crypto)
+                subtleObject = window.crypto.webkitSubtle;
+
+            if("subtle" in window.crypto)
+                subtleObject = window.crypto.subtle;
+
+            local.engine = {
+                name: engineName,
+                crypto: cryptoObject,
+                subtle: subtleObject
+            };
+        }
+    }
+    //**************************************************************************************
+    in_window.org.pkijs.setEngine =
+    function(name, crypto, subtle)
+    {
+        /// <summary>Setting the global "crypto engine" parameters</summary>
+        /// <param name="name" type="String">Auxiliary name for "crypto engine"</param>
+        /// <param name="crypto" type="Object">Object handling all root cryptographic requests (in fact currently it must handle only "getRandomValues")</param>
+        /// <param name="subtle" type="Object">Object handling all main cryptographic requests</param>
+
+        local.engine = {
+            name: name,
+            crypto: crypto,
+            subtle: subtle
+        };
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.getEngine =
+    function()
+    {
+        return local.engine;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Declaration of common functions 
+    //**************************************************************************************
+    in_window.org.pkijs.emptyObject =
+    function()
+    {
+        this.toJSON = function()
+        {
+            return {};
+        };
+        this.toSchema = function()
+        {
+            return {};
+        };
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.getNames =
+    function(arg)
+    {
+        /// <summary>Get correct "names" array for all "schema" objects</summary>
+
+        var names = {};
+
+        if(arg instanceof Object)
+            names = (arg.names || {});
+
+        return names;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.inheriteObjectFields =
+    function(from)
+    {
+        for(var i in from.prototype)
+        {
+            if(typeof from.prototype[i] === "function")
+                continue;
+
+            this[i] = from.prototype[i];
+        }
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.getUTCDate =
+    function(date)
+    {
+        /// <summary>Making UTC date from local date</summary>
+        /// <param name="date" type="Date">Date to convert from</param>
+
+        var current_date = date;
+        return new Date(current_date.getTime() + (current_date.getTimezoneOffset() * 60000));
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.padNumber =
+    function(input_number, full_length)
+    {
+        var str = input_number.toString(10);
+        var dif = full_length - str.length;
+
+        var padding = new Array(dif);
+        for(var i = 0; i < dif; i++)
+            padding[i] = '0';
+
+        var padding_string = padding.join('');
+
+        return padding_string.concat(str);
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.getValue =
+    function(args, item, default_value)
+    {
+        if(item in args)
+            return args[item];
+        else
+            return default_value;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.isEqual_view =
+    function(input_view1, input_view2)
+    {
+        /// <summary>Compare two Uint8Arrays</summary>
+        /// <param name="input_view1" type="Uint8Array">First Uint8Array for comparision</param>
+        /// <param name="input_view2" type="Uint8Array">Second Uint8Array for comparision</param>
+
+        if(input_view1.length !== input_view2.length)
+            return false;
+
+        for(var i = 0; i < input_view1.length; i++)
+        {
+            if(input_view1[i] != input_view2[i])
+                return false;
+        }
+
+        return true;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.isEqual_buffer =
+    function(input_buffer1, input_buffer2)
+    {
+        /// <summary>Compare two array buffers</summary>
+        /// <param name="input_buffer1" type="ArrayBuffer">First ArrayBuffer for comparision</param>
+        /// <param name="input_buffer2" type="ArrayBuffer">Second ArrayBuffer for comparision</param>
+
+        if(input_buffer1.byteLength != input_buffer2.byteLength)
+            return false;
+
+        var view1 = new Uint8Array(input_buffer1);
+        var view2 = new Uint8Array(input_buffer2);
+
+        return in_window.org.pkijs.isEqual_view(view1, view2);
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.concat_buffers =
+    function(input_buf1, input_buf2)
+    {
+        /// <summary>Concatenate two ArrayBuffers</summary>
+        /// <param name="input_buf1" type="ArrayBuffer">First ArrayBuffer (first part of concatenated array)</param>
+        /// <param name="input_buf2" type="ArrayBuffer">Second ArrayBuffer (second part of concatenated array)</param>
+
+        var input_view1 = new Uint8Array(input_buf1);
+        var input_view2 = new Uint8Array(input_buf2);
+
+        var ret_buf = new ArrayBuffer(input_buf1.byteLength + input_buf2.byteLength);
+        var ret_view = new Uint8Array(ret_buf);
+
+        for(var i = 0; i < input_buf1.byteLength; i++)
+            ret_view[i] = input_view1[i];
+
+        for(var j = 0; j < input_buf2.byteLength; j++)
+            ret_view[input_buf1.byteLength + j] = input_view2[j];
+
+        return ret_buf;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.copyBuffer =
+    function(input_buffer)
+    {
+        var result = new ArrayBuffer(input_buffer.byteLength);
+
+        var resultView = new Uint8Array(result);
+        var inputView = new Uint8Array(input_buffer);
+
+        for(var i = 0; i < inputView.length; i++)
+            resultView[i] = inputView[i];
+
+        return result;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.getCrypto =
+    function()
+    {
+        var crypto_temp;
+
+        if(local.engine.subtle !== null)
+            crypto_temp = local.engine.subtle;
+
+        return crypto_temp;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.stringPrep =
+    function(input_string)
+    {
+        /// <summary>String preparation function. In a future here will be realization of algorithm from RFC4518.</summary>
+        /// <param name="input_string" type="String">JavaScript string. As soon as for each ASN.1 string type we have a specific transformation function here we will work with pure JavaScript string</param>
+        /// <returns type="String">Formated string</returns>
+
+        var result = input_string.replace(/^\s+|\s+$/g, ""); // Trim input string
+        result = result.replace(/\s+/g, " "); // Change all sequence of SPACE down to SPACE char
+        result = result.toLowerCase();
+
+        return result;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.bufferToHexCodes =
+    function(input_buffer, input_offset, input_lenght)
+    {
+        var result = "";
+
+        var int_buffer = new Uint8Array(input_buffer, input_offset, input_lenght);
+
+        for(var i = 0; i < int_buffer.length; i++)
+        {
+            var str = int_buffer[i].toString(16).toUpperCase();
+            result = result + ((str.length === 1) ? "0" : "") + str;
+        }
+
+        return result;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.bufferFromHexCodes =
+    function(hexString)
+    {
+        /// <summary>Create an ArrayBuffer from string having hexdecimal codes</summary>
+        /// <param name="hexString" type="String">String to create ArrayBuffer from</param>
+
+        // #region Initial variables 
+        var stringLength = hexString.length;
+
+        var resultBuffer = new ArrayBuffer(stringLength >> 1);
+        var resultView = new Uint8Array(resultBuffer);
+
+        var hex_map = {};
+
+        hex_map['0'] = 0x00;
+        hex_map['1'] = 0x01;
+        hex_map['2'] = 0x02;
+        hex_map['3'] = 0x03;
+        hex_map['4'] = 0x04;
+        hex_map['5'] = 0x05;
+        hex_map['6'] = 0x06;
+        hex_map['7'] = 0x07;
+        hex_map['8'] = 0x08;
+        hex_map['9'] = 0x09;
+        hex_map['A'] = 0x0A;
+        hex_map['a'] = 0x0A;
+        hex_map['B'] = 0x0B;
+        hex_map['b'] = 0x0B;
+        hex_map['C'] = 0x0C;
+        hex_map['c'] = 0x0C;
+        hex_map['D'] = 0x0D;
+        hex_map['d'] = 0x0D;
+        hex_map['E'] = 0x0E;
+        hex_map['e'] = 0x0E;
+        hex_map['F'] = 0x0F;
+        hex_map['f'] = 0x0F;
+
+        var j = 0;
+        var temp = 0x00;
+        // #endregion 
+
+        // #region Convert char-by-char 
+        for(var i = 0; i < stringLength; i++)
+        {
+            if(!(i % 2))
+                temp = hex_map[hexString.charAt(i)] << 4;
+            else
+            {
+                temp |= hex_map[hexString.charAt(i)];
+
+                resultView[j] = temp;
+                j++;
+            }
+        }
+        // #endregion 
+
+        return resultBuffer;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.getRandomValues =
+    function(view)
+    {
+        /// <param name="view" type="Uint8Array">New array which gives a length for random value</param>
+
+        if(local.engine.crypto !== null)
+            return local.engine.crypto.getRandomValues(view);
+        else
+            throw new Error("No support for Web Cryptography API");
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.getAlgorithmParameters =
+    function(algorithmName, operation)
+    {
+        /// <param name="algorithmName" type="String">Algorithm name to get common parameters for</param>
+        /// <param name="operation" type="String">Kind of operation: "sign", "encrypt", "generatekey", "importkey", "exportkey", "verify"</param>
+
+        var result = {
+            algorithm: {},
+            usages: []
+        };
+
+        switch(algorithmName.toUpperCase())
+        {
+            case "RSASSA-PKCS1-V1_5":
+                switch(operation.toLowerCase())
+                {
+                    case "generatekey":
+                        result = {
+                            algorithm: {
+                                name: "RSASSA-PKCS1-v1_5",
+                                modulusLength: 2048,
+                                publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
+                                hash: {
+                                    name: "SHA-256"
+                                }
+                            },
+                            usages: ["sign", "verify"]
+                        };
+                        break;
+                    case "verify":
+                    case "sign":
+                    case "importkey":
+                        result = {
+                            algorithm: {
+                                name: "RSASSA-PKCS1-v1_5",
+                                hash: {
+                                    name: "SHA-256"
+                                }
+                            },
+                            usages: ["verify"] // For importKey("pkcs8") usage must be "sign" only
+                        };
+                        break;
+                    case "exportkey":
+                    default:
+                        return {
+                            algorithm: {
+                                name: "RSASSA-PKCS1-v1_5"
+                            },
+                            usages: []
+                        };
+                }
+                break;
+            case "RSA-PSS":
+                switch(operation.toLowerCase())
+                {
+                    case "sign":
+                    case "verify":
+                        result = {
+                            algorithm: {
+                                name: "RSA-PSS",
+                                hash: {
+                                    name: "SHA-1"
+                                },
+                                saltLength: 20
+                            },
+                            usages: ["sign", "verify"]
+                        };
+                        break;
+                    case "generatekey":
+                        result = {
+                            algorithm: {
+                                name: "RSA-PSS",
+                                modulusLength: 2048,
+                                publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
+                                hash: {
+                                    name: "SHA-1"
+                                }
+                            },
+                            usages: ["sign", "verify"]
+                        };
+                        break;
+                    case "importkey":
+                        result = {
+                            algorithm: {
+                                name: "RSA-PSS",
+                                hash: {
+                                    name: "SHA-1"
+                                }
+                            },
+                            usages: ["verify"] // For importKey("pkcs8") usage must be "sign" only
+                        };
+                        break;
+                    case "exportkey":
+                    default:
+                        return {
+                            algorithm: {
+                                name: "RSA-PSS"
+                            },
+                            usages: []
+                        };
+                }
+                break;
+            case "RSA-OAEP":
+                switch(operation.toLowerCase())
+                {
+                    case "encrypt":
+                    case "decrypt":
+                        result = {
+                            algorithm: {
+                                name: "RSA-OAEP"
+                            },
+                            usages: ["encrypt", "decrypt"]
+                        };
+                        break;
+                        break;
+                    case "generatekey":
+                        result = {
+                            algorithm: {
+                                name: "RSA-OAEP",
+                                modulusLength: 2048,
+                                publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
+                                hash: {
+                                    name: "SHA-256"
+                                }
+                            },
+                            usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"]
+                        };
+                        break;
+                    case "importkey":
+                        result = {
+                            algorithm: {
+                                name: "RSA-OAEP",
+                                hash: {
+                                    name: "SHA-256"
+                                }
+                            },
+                            usages: ["encrypt"] // encrypt for "spki" and decrypt for "pkcs8"
+                        };
+                        break;
+                    case "exportkey":
+                    default:
+                        return {
+                            algorithm: {
+                                name: "RSA-OAEP"
+                            },
+                            usages: []
+                        };
+                }
+                break;
+            case "ECDSA":
+                switch(operation.toLowerCase())
+                {
+                    case "generatekey":
+                        result = {
+                            algorithm: {
+                                name: "ECDSA",
+                                namedCurve: "P-256"
+                            },
+                            usages: ["sign", "verify"]
+                        };
+                        break;
+                    case "importkey":
+                        result = {
+                            algorithm: {
+                                name: "ECDSA",
+                                namedCurve: "P-256"
+                            },
+                            usages: ["verify"] // "sign" for "pkcs8"
+                        };
+                        break;
+                    case "verify":
+                    case "sign":
+                        result = {
+                            algorithm: {
+                                name: "ECDSA",
+                                hash: {
+                                    name: "SHA-256"
+                                }
+                            },
+                            usages: ["sign"]
+                        };
+                        break;
+                    default:
+                        return {
+                            algorithm: {
+                                name: "ECDSA"
+                            },
+                            usages: []
+                        };
+                }
+                break;
+            case "ECDH":
+                switch(operation.toLowerCase())
+                {
+                    case "exportkey":
+                    case "importkey":
+                    case "generatekey":
+                        result = {
+                            algorithm: {
+                                name: "ECDH",
+                                namedCurve: "P-256"
+                            },
+                            usages: ["deriveKey", "deriveBits"]
+                        };
+                        break;
+                    case "derivekey":
+                    case "derivebits":
+                        result = {
+                            algorithm: {
+                                name: "ECDH",
+                                namedCurve: "P-256",
+                                public: [] // Must be a "publicKey"
+                            },
+                            usages: ["encrypt", "decrypt"]
+                        };
+                        break;
+                    default:
+                        return {
+                            algorithm: {
+                                name: "ECDH"
+                            },
+                            usages: []
+                        };
+                }
+                break;
+            case "AES-CTR":
+                switch(operation.toLowerCase())
+                {
+                    case "importkey":
+                    case "exportkey":
+                    case "generatekey":
+                        result = {
+                            algorithm: {
+                                name: "AES-CTR",
+                                length: 256
+                            },
+                            usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"]
+                        };
+                        break;
+                    case "decrypt":
+                    case "encrypt":
+                        result = {
+                            algorithm: {
+                                name: "AES-CTR",
+                                counter: new Uint8Array(16),
+                                length: 10
+                            },
+                            usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"]
+                        };
+                        break;
+                    default:
+                        return {
+                            algorithm: {
+                                name: "AES-CTR"
+                            },
+                            usages: []
+                        };
+                }
+                break;
+            case "AES-CBC":
+                switch(operation.toLowerCase())
+                {
+                    case "importkey":
+                    case "exportkey":
+                    case "generatekey":
+                        result = {
+                            algorithm: {
+                                name: "AES-CBC",
+                                length: 256
+                            },
+                            usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"]
+                        };
+                        break;
+                    case "decrypt":
+                    case "encrypt":
+                        result = {
+                            algorithm: {
+                                name: "AES-CBC",
+                                iv: in_window.org.pkijs.getRandomValues(new Uint8Array(16)) // For "decrypt" the value should be replaced with value got on "encrypt" step
+                            },
+                            usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"]
+                        };
+                        break;
+                    default:
+                        return {
+                            algorithm: {
+                                name: "AES-CBC"
+                            },
+                            usages: []
+                        };
+                }
+                break;
+            case "AES-GCM":
+                switch(operation.toLowerCase())
+                {
+                    case "importkey":
+                    case "exportkey":
+                    case "generatekey":
+                        result = {
+                            algorithm: {
+                                name: "AES-GCM",
+                                length: 256
+                            },
+                            usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"]
+                        };
+                        break;
+                    case "decrypt":
+                    case "encrypt":
+                        result = {
+                            algorithm: {
+                                name: "AES-GCM",
+                                iv: in_window.org.pkijs.getRandomValues(new Uint8Array(16)) // For "decrypt" the value should be replaced with value got on "encrypt" step
+                            },
+                            usages: ["encrypt", "decrypt", "wrapKey", "unwrapKey"]
+                        };
+                        break;
+                    default:
+                        return {
+                            algorithm: {
+                                name: "AES-GCM"
+                            },
+                            usages: []
+                        };
+                }
+                break;
+            case "AES-KW":
+                switch(operation.toLowerCase())
+                {
+                    case "importkey":
+                    case "exportkey":
+                    case "generatekey":
+                    case "wrapkey":
+                    case "unwrapkey":
+                        result = {
+                            algorithm: {
+                                name: "AES-KW",
+                                length: 256
+                            },
+                            usages: ["wrapKey", "unwrapKey"]
+                        };
+                        break;
+                    default:
+                        return {
+                            algorithm: {
+                                name: "AES-KW"
+                            },
+                            usages: []
+                        };
+                }
+                break;
+            case "HMAC":
+                switch(operation.toLowerCase())
+                {
+                    case "sign":
+                    case "verify":
+                        result = {
+                            algorithm: {
+                                name: "HMAC"
+                            },
+                            usages: ["sign", "verify"]
+                        };
+                        break;
+                    case "importkey":
+                    case "exportkey":
+                    case "generatekey":
+                        result = {
+                            algorithm: {
+                                name: "HMAC",
+                                length: 32,
+                                hash: {
+                                    name: "SHA-256"
+                                }
+                            },
+                            usages: ["sign", "verify"]
+                        };
+                        break;
+                    default:
+                        return {
+                            algorithm: {
+                                name: "HMAC"
+                            },
+                            usages: []
+                        };
+                }
+                break;
+            case "HKDF":
+                switch(operation.toLowerCase())
+                {
+                    case "derivekey":
+                        result = {
+                            algorithm: {
+                                name: "HKDF",
+                                hash: "SHA-256",
+                                salt: new Uint8Array(),
+                                info: new Uint8Array()
+                            },
+                            usages: ["encrypt", "decrypt"]
+                        };
+                        break;
+                    default:
+                        return {
+                            algorithm: {
+                                name: "HKDF"
+                            },
+                            usages: []
+                        };
+                }
+                break;
+            case "PBKDF2":
+                switch(operation.toLowerCase())
+                {
+                    case "derivekey":
+                        result = {
+                            algorithm: {
+                                name: "PBKDF2",
+                                hash: { name: "SHA-256" },
+                                salt: new Uint8Array(),
+                                iterations: 1000
+                            },
+                            usages: ["encrypt", "decrypt"]
+                        };
+                        break;
+                    default:
+                        return {
+                            algorithm: {
+                                name: "PBKDF2"
+                            },
+                            usages: []
+                        };
+                }
+                break;
+            default:
+        }
+
+        return result;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.getOIDByAlgorithm =
+    function(algorithm)
+    {
+        /// <summary>Get OID for each specific WebCrypto algorithm</summary>
+        /// <param name="algorithm" type="Object">WebCrypto algorithm</param>
+
+        var result = "";
+
+        switch(algorithm.name.toUpperCase())
+        {
+            case "RSASSA-PKCS1-V1_5":
+                switch(algorithm.hash.name.toUpperCase())
+                {
+                    case "SHA-1":
+                        result = "1.2.840.113549.1.1.5";
+                        break;
+                    case "SHA-256":
+                        result = "1.2.840.113549.1.1.11";
+                        break;
+                    case "SHA-384":
+                        result = "1.2.840.113549.1.1.12";
+                        break;
+                    case "SHA-512":
+                        result = "1.2.840.113549.1.1.13";
+                        break;
+                    default:
+                }
+                break;
+            case "RSA-PSS":
+                result = "1.2.840.113549.1.1.10";
+                break;
+            case "RSA-OAEP":
+                result = "1.2.840.113549.1.1.7";
+                break;
+            case "ECDSA":
+                switch(algorithm.hash.name.toUpperCase())
+                {
+                    case "SHA-1":
+                        result = "1.2.840.10045.4.1";
+                        break;
+                    case "SHA-256":
+                        result = "1.2.840.10045.4.3.2";
+                        break;
+                    case "SHA-384":
+                        result = "1.2.840.10045.4.3.3";
+                        break;
+                    case "SHA-512":
+                        result = "1.2.840.10045.4.3.4";
+                        break;
+                    default:
+                }
+                break;
+            case "ECDH":
+                switch(algorithm.kdf.toUpperCase()) // Non-standard addition - hash algorithm of KDF function
+                {
+                    case "SHA-1":
+                        result = "1.3.133.16.840.63.0.2"; // dhSinglePass-stdDH-sha1kdf-scheme
+                        break;
+                    case "SHA-256":
+                        result = "1.3.132.1.11.1"; // dhSinglePass-stdDH-sha256kdf-scheme 
+                        break;
+                    case "SHA-384":
+                        result = "1.3.132.1.11.2"; // dhSinglePass-stdDH-sha384kdf-scheme
+                        break;
+                    case "SHA-512":
+                        result = "1.3.132.1.11.3"; // dhSinglePass-stdDH-sha512kdf-scheme
+                        break;
+                    default:
+                }
+                break;
+            case "AES-CTR":
+                break;
+            case "AES-CBC":
+                switch(algorithm.length)
+                {
+                    case 128:
+                        result = "2.16.840.1.101.3.4.1.2";
+                        break;
+                    case 192:
+                        result = "2.16.840.1.101.3.4.1.22";
+                        break;
+                    case 256:
+                        result = "2.16.840.1.101.3.4.1.42";
+                        break;
+                    default:
+                }
+                break;
+            case "AES-CMAC":
+                break;
+            case "AES-GCM":
+                switch(algorithm.length)
+                {
+                    case 128:
+                        result = "2.16.840.1.101.3.4.1.6";
+                        break;
+                    case 192:
+                        result = "2.16.840.1.101.3.4.1.26";
+                        break;
+                    case 256:
+                        result = "2.16.840.1.101.3.4.1.46";
+                        break;
+                    default:
+                }
+                break;
+            case "AES-CFB":
+                switch(algorithm.length)
+                {
+                    case 128:
+                        result = "2.16.840.1.101.3.4.1.4";
+                        break;
+                    case 192:
+                        result = "2.16.840.1.101.3.4.1.24";
+                        break;
+                    case 256:
+                        result = "2.16.840.1.101.3.4.1.44";
+                        break;
+                    default:
+                }
+                break;
+            case "AES-KW":
+                switch(algorithm.length)
+                {
+                    case 128:
+                        result = "2.16.840.1.101.3.4.1.5";
+                        break;
+                    case 192:
+                        result = "2.16.840.1.101.3.4.1.25";
+                        break;
+                    case 256:
+                        result = "2.16.840.1.101.3.4.1.45";
+                        break;
+                    default:
+                }
+                break;
+            case "HMAC":
+                switch(algorithm.hash.name.toUpperCase())
+                {
+                    case "SHA-1":
+                        result = "1.2.840.113549.2.7";
+                        break;
+                    case "SHA-256":
+                        result = "1.2.840.113549.2.9";
+                        break;
+                    case "SHA-384":
+                        result = "1.2.840.113549.2.10";
+                        break;
+                    case "SHA-512":
+                        result = "1.2.840.113549.2.11";
+                        break;
+                    default:
+                }
+                break;
+            case "DH":
+                result = "1.2.840.113549.1.9.16.3.5";
+                break;
+            case "SHA-1":
+                result = "1.3.14.3.2.26";
+                break;
+            case "SHA-256":
+                result = "2.16.840.1.101.3.4.2.1";
+                break;
+            case "SHA-384":
+                result = "2.16.840.1.101.3.4.2.2";
+                break;
+            case "SHA-512":
+                result = "2.16.840.1.101.3.4.2.3";
+                break;
+            case "CONCAT":
+                break;
+            case "HKDF":
+                break;
+            case "PBKDF2":
+                result = "1.2.840.113549.1.5.12";
+                break;
+            // #region Special case - OIDs for ECC curves 
+            case "P-256":
+                result = "1.2.840.10045.3.1.7";
+                break;
+            case "P-384":
+                result = "1.3.132.0.34";
+                break;
+            case "P-521":
+                result = "1.3.132.0.35";
+                break;
+            // #endregion 
+
+            default:
+        }
+
+        return result;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.getAlgorithmByOID =
+    function(oid)
+    {
+        /// <summary>Get WebCrypto algorithm by wel-known OID</summary>
+        /// <param name="oid" type="String">Wel-known OID to search for</param>
+
+        var result = {};
+
+        switch(oid)
+        {
+            case "1.2.840.113549.1.1.5":
+                result = {
+                    name: "RSASSA-PKCS1-v1_5",
+                    hash: {
+                        name: "SHA-1"
+                    }
+                };
+                break;
+            case "1.2.840.113549.1.1.11":
+                result = {
+                    name: "RSASSA-PKCS1-v1_5",
+                    hash: {
+                        name: "SHA-256"
+                    }
+                };
+                break;
+            case "1.2.840.113549.1.1.12":
+                result = {
+                    name: "RSASSA-PKCS1-v1_5",
+                    hash: {
+                        name: "SHA-384"
+                    }
+                };
+                break;
+            case "1.2.840.113549.1.1.13":
+                result = {
+                    name: "RSASSA-PKCS1-v1_5",
+                    hash: {
+                        name: "SHA-512"
+                    }
+                };
+                break;
+            case "1.2.840.113549.1.1.10":
+                result = {
+                    name: "RSA-PSS"
+                };
+                break;
+            case "1.2.840.113549.1.1.7":
+                result = {
+                    name: "RSA-OAEP"
+                };
+                break;
+            case "1.2.840.10045.4.1":
+                result = {
+                    name: "ECDSA",
+                    hash: {
+                        name: "SHA-1"
+                    }
+                };
+                break;
+            case "1.2.840.10045.4.3.2":
+                result = {
+                    name: "ECDSA",
+                    hash: {
+                        name: "SHA-256"
+                    }
+                };
+                break;
+            case "1.2.840.10045.4.3.3":
+                result = {
+                    name: "ECDSA",
+                    hash: {
+                        name: "SHA-384"
+                    }
+                };
+                break;
+            case "1.2.840.10045.4.3.4":
+                result = {
+                    name: "ECDSA",
+                    hash: {
+                        name: "SHA-512"
+                    }
+                };
+                break;
+            case "1.3.133.16.840.63.0.2":
+                result = {
+                    name: "ECDH",
+                    kdf: "SHA-1"
+                };
+                break;
+            case "1.3.132.1.11.1":
+                result = {
+                    name: "ECDH",
+                    kdf: "SHA-256"
+                };
+                break;
+            case "1.3.132.1.11.2":
+                result = {
+                    name: "ECDH",
+                    kdf: "SHA-384"
+                };
+                break;
+            case "1.3.132.1.11.3":
+                result = {
+                    name: "ECDH",
+                    kdf: "SHA-512"
+                };
+                break;
+            case "2.16.840.1.101.3.4.1.2":
+                result = {
+                    name: "AES-CBC",
+                    length: 128
+                };
+                break;
+            case "2.16.840.1.101.3.4.1.22":
+                result = {
+                    name: "AES-CBC",
+                    length: 192
+                };
+                break;
+            case "2.16.840.1.101.3.4.1.42":
+                result = {
+                    name: "AES-CBC",
+                    length: 256
+                };
+                break;
+            case "2.16.840.1.101.3.4.1.6":
+                result = {
+                    name: "AES-GCM",
+                    length: 128
+                };
+                break;
+            case "2.16.840.1.101.3.4.1.26":
+                result = {
+                    name: "AES-GCM",
+                    length: 192
+                };
+                break;
+            case "2.16.840.1.101.3.4.1.46":
+                result = {
+                    name: "AES-GCM",
+                    length: 256
+                };
+                break;
+            case "2.16.840.1.101.3.4.1.4":
+                result = {
+                    name: "AES-CFB",
+                    length: 128
+                };
+                break;
+            case "2.16.840.1.101.3.4.1.24":
+                result = {
+                    name: "AES-CFB",
+                    length: 192
+                };
+                break;
+            case "2.16.840.1.101.3.4.1.44":
+                result = {
+                    name: "AES-CFB",
+                    length: 256
+                };
+                break;
+            case "2.16.840.1.101.3.4.1.5":
+                result = {
+                    name: "AES-KW",
+                    length: 128
+                };
+                break;
+            case "2.16.840.1.101.3.4.1.25":
+                result = {
+                    name: "AES-KW",
+                    length: 192
+                };
+                break;
+            case "2.16.840.1.101.3.4.1.45":
+                result = {
+                    name: "AES-KW",
+                    length: 256
+                };
+                break;
+            case "1.2.840.113549.2.7":
+                result = {
+                    name: "HMAC",
+                    hash: {
+                        name: "SHA-1"
+                    }
+                };
+                break;
+            case "1.2.840.113549.2.9":
+                result = {
+                    name: "HMAC",
+                    hash: {
+                        name: "SHA-256"
+                    }
+                };
+                break;
+            case "1.2.840.113549.2.10":
+                result = {
+                    name: "HMAC",
+                    hash: {
+                        name: "SHA-384"
+                    }
+                };
+                break;
+            case "1.2.840.113549.2.11":
+                result = {
+                    name: "HMAC",
+                    hash: {
+                        name: "SHA-512"
+                    }
+                };
+                break;
+            case "1.2.840.113549.1.9.16.3.5":
+                result = {
+                    name: "DH"
+                };
+                break;
+            case "1.3.14.3.2.26":
+                result = {
+                    name: "SHA-1"
+                };
+                break;
+            case "2.16.840.1.101.3.4.2.1":
+                result = {
+                    name: "SHA-256"
+                };
+                break;
+            case "2.16.840.1.101.3.4.2.2":
+                result = {
+                    name: "SHA-384"
+                };
+                break;
+            case "2.16.840.1.101.3.4.2.3":
+                result = {
+                    name: "SHA-512"
+                };
+                break;
+            case "1.2.840.113549.1.5.12":
+                result = {
+                    name: "PBKDF2"
+                };
+                break;
+            // #region Special case - OIDs for ECC curves 
+            case "1.2.840.10045.3.1.7":
+                result = {
+                    name: "P-256"
+                };
+                break;
+            case "1.3.132.0.34":
+                result = {
+                    name: "P-384"
+                };
+                break;
+            case "1.3.132.0.35":
+                result = {
+                    name: "P-521"
+                };
+                break;
+            // #endregion 
+
+            default:
+        }
+
+        return result;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.getHashAlgorithm =
+    function(signatureAlgorithm)
+    {
+        /// <summary>Getting hash algorithm by signature algorithm</summary>
+        /// <param name="signatureAlgorithm" type="in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER">Signature algorithm</param>
+
+        var result = "";
+
+        switch(signatureAlgorithm.algorithm_id)
+        {
+            case "1.2.840.10045.4.1": // ecdsa-with-SHA1
+            case "1.2.840.113549.1.1.5":
+                result = "SHA-1";
+                break;
+            case "1.2.840.10045.4.3.2": // ecdsa-with-SHA256
+            case "1.2.840.113549.1.1.11":
+                result = "SHA-256";
+                break;
+            case "1.2.840.10045.4.3.3": // ecdsa-with-SHA384
+            case "1.2.840.113549.1.1.12":
+                result = "SHA-384";
+                break;
+            case "1.2.840.10045.4.3.4": // ecdsa-with-SHA512
+            case "1.2.840.113549.1.1.13":
+                result = "SHA-512";
+                break;
+            case "1.2.840.113549.1.1.10": // RSA-PSS
+                {
+                    var params;
+
+                    try
+                    {
+                        params = new in_window.org.pkijs.simpl.x509.RSASSA_PSS_params({ schema: signatureAlgorithm.algorithm_params });
+                        if("hashAlgorithm" in params)
+                        {
+                            var algorithm = in_window.org.pkijs.getAlgorithmByOID(params.hashAlgorithm.algorithm_id);
+                            if(("name" in algorithm) === false)
+                                return "";
+
+                            result = algorithm.name;
+                        }
+                        else
+                            result = "SHA-1";
+                    }
+                    catch(ex)
+                    {
+                    }
+                }
+                break;
+            default:
+        }
+
+        return result;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.createCMSECDSASignature =
+    function(signatureBuffer)
+    {
+        /// <summary>Create CMS ECDSA signature from WebCrypto ECDSA signature</summary>
+        /// <param name="signatureBuffer" type="ArrayBuffer">WebCrypto result of "sign" function</param>
+
+        // #region Initial check for correct length 
+        if((signatureBuffer.byteLength % 2) != 0)
+            return new ArrayBuffer(0);
+        // #endregion 
+
+        // #region Initial variables 
+        var i = 0;
+        var length = signatureBuffer.byteLength / 2; // There are two equal parts inside incoming ArrayBuffer
+
+        var signatureView = new Uint8Array(signatureBuffer);
+
+        var r_buffer = new ArrayBuffer(length);
+        var r_view = new Uint8Array(r_buffer);
+        var r_corrected_buffer;
+        var r_corrected_view;
+
+        var s_buffer = new ArrayBuffer(length);
+        var s_view = new Uint8Array(s_buffer);
+        var s_corrected_buffer;
+        var s_corrected_view;
+        // #endregion   
+
+        // #region Get "r" part of ECDSA signature 
+        for(; i < length; i++)
+            r_view[i] = signatureView[i];
+
+        if(r_view[0] & 0x80)
+        {
+            r_corrected_buffer = new ArrayBuffer(length + 1);
+            r_corrected_view = new Uint8Array(r_corrected_buffer);
+
+            r_corrected_view[0] = 0x00;
+
+            for(var j = 0; j < length; j++)
+                r_corrected_view[j + 1] = r_view[j];
+        }
+        else
+        {
+            r_corrected_buffer = r_buffer;
+            r_corrected_view = r_view;
+        }
+        // #endregion 
+
+        // #region Get "s" part of ECDSA signature 
+        for(; i < signatureBuffer.byteLength; i++)
+            s_view[i - length] = signatureView[i];
+
+
+        if(s_view[0] & 0x80)
+        {
+            s_corrected_buffer = new ArrayBuffer(length + 1);
+            s_corrected_view = new Uint8Array(s_corrected_buffer);
+
+            s_corrected_view[0] = 0x00;
+
+            for(var j = 0; j < length; j++)
+                s_corrected_view[j + 1] = s_view[j];
+        }
+        else
+        {
+            s_corrected_buffer = s_buffer;
+            s_corrected_view = s_view;
+        }
+        // #endregion 
+
+        // #region Create ASN.1 structure of CMS ECDSA signature 
+        var r_integer = new in_window.org.pkijs.asn1.INTEGER();
+        r_integer.value_block.is_hex_only = true;
+        r_integer.value_block.value_hex = in_window.org.pkijs.copyBuffer(r_corrected_buffer);
+
+        var s_integer = new in_window.org.pkijs.asn1.INTEGER();
+        s_integer.value_block.is_hex_only = true;
+        s_integer.value_block.value_hex = in_window.org.pkijs.copyBuffer(s_corrected_buffer);
+
+        var asn1 = new in_window.org.pkijs.asn1.SEQUENCE({
+            value: [
+                r_integer,
+                s_integer
+            ]
+        });
+        // #endregion   
+
+        return asn1.toBER(false);
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.createECDSASignatureFromCMS =
+    function(cmsSignature)
+    {
+        /// <summary>Create a single ArrayBuffer from CMS ECDSA signature</summary>
+        /// <param name="cmsSignature" type="in_window.org.pkijs.asn1.SEQUENCE">ASN.1 SEQUENCE contains CMS ECDSA signature</param>
+
+        // #region Initial variables 
+        var length = 0;
+
+        var r_start = 0;
+        var s_start = 0;
+
+        var r_length = cmsSignature.value_block.value[0].value_block.value_hex.byteLength;
+        var s_length = cmsSignature.value_block.value[1].value_block.value_hex.byteLength;
+        // #endregion 
+
+        // #region Get length of final "ArrayBuffer" 
+        var r_view = new Uint8Array(cmsSignature.value_block.value[0].value_block.value_hex);
+        if((r_view[0] === 0x00) && (r_view[1] & 0x80))
+        {
+            length = r_length - 1;
+            r_start = 1;
+        }
+        else
+            length = r_length;
+
+        var s_view = new Uint8Array(cmsSignature.value_block.value[1].value_block.value_hex);
+        if((s_view[0] === 0x00) && (s_view[1] & 0x80))
+        {
+            length += s_length - 1;
+            s_start = 1;
+        }
+        else
+            length += s_length;
+        // #endregion 
+
+        // #region Copy values from CMS ECDSA signature 
+        var result = new ArrayBuffer(length);
+        var result_view = new Uint8Array(result);
+
+        for(var i = r_start; i < r_length; i++)
+            result_view[i - r_start] = r_view[i];
+
+        for(var i = s_start; i < s_length; i++)
+            result_view[i - s_start + r_length - r_start] = s_view[i];
+        // #endregion 
+
+        return result;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.getEncryptionAlgorithm =
+    function(algorithm)
+    {
+        /// <summary>Get encryption algorithm OID by WebCrypto algorithm's object</summary>
+        /// <param name="algorithm" type="WebCryptoAlgorithm">WebCrypto algorithm object</param>
+
+        var result = "";
+
+        switch(algorithm.name.toUpperCase())
+        {
+            case "AES-CBC":
+                switch(algorithm.length)
+                {
+                    case 128:
+                        result = "2.16.840.1.101.3.4.1.2";
+                        break;
+                    case 192:
+                        result = "2.16.840.1.101.3.4.1.22";
+                        break;
+                    case 256:
+                        result = "2.16.840.1.101.3.4.1.42";
+                        break;
+                    default:
+                }
+                break;
+            case "AES-GCM":
+                switch(algorithm.length)
+                {
+                    case 128:
+                        result = "2.16.840.1.101.3.4.1.6";
+                        break;
+                    case 192:
+                        result = "2.16.840.1.101.3.4.1.26";
+                        break;
+                    case 256:
+                        result = "2.16.840.1.101.3.4.1.46";
+                        break;
+                    default:
+                }
+                break;
+            default:
+        }
+
+        return result;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.getAlgorithmByEncryptionOID =
+    function(oid)
+    {
+        /// <summary>Get encryption algorithm name by OID</summary>
+        /// <param name="oid" type="String">OID of encryption algorithm</param>
+
+        var result = "";
+
+        switch(oid)
+        {
+            case "2.16.840.1.101.3.4.1.2":
+            case "2.16.840.1.101.3.4.1.22":
+            case "2.16.840.1.101.3.4.1.42":
+                result = "AES-CBC";
+                break;
+            case "2.16.840.1.101.3.4.1.6":
+            case "2.16.840.1.101.3.4.1.26":
+            case "2.16.840.1.101.3.4.1.46":
+                result = "AES-GCM";
+                break;
+            default:
+        }
+
+        return result;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+}
+)(typeof exports !== "undefined" ? exports : window);
\ No newline at end of file
diff --git a/dom/webauthn/tests/pkijs/x509_schema.js b/dom/webauthn/tests/pkijs/x509_schema.js
new file mode 100644
index 0000000000..15b0929bb5
--- /dev/null
+++ b/dom/webauthn/tests/pkijs/x509_schema.js
@@ -0,0 +1,1889 @@
+/*
+ * Copyright (c) 2014, GMO GlobalSign
+ * Copyright (c) 2015, Peculiar Ventures
+ * All rights reserved.
+ *
+ * Author 2014-2015, Yury Strozhevsky <www.strozhevsky.com>.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, 
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, 
+ *    this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, 
+ *    this list of conditions and the following disclaimer in the documentation 
+ *    and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors 
+ *    may be used to endorse or promote products derived from this software without 
+ *    specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
+ * OF SUCH DAMAGE. 
+ *
+ */
+(
+function(in_window)
+{
+    //**************************************************************************************
+    // #region Declaration of global variables 
+    //**************************************************************************************
+    // #region "org" namespace 
+    if(typeof in_window.org === "undefined")
+        in_window.org = {};
+    else
+    {
+        if(typeof in_window.org !== "object")
+            throw new Error("Name org already exists and it's not an object");
+    }
+    // #endregion 
+
+    // #region "org.pkijs" namespace 
+    if(typeof in_window.org.pkijs === "undefined")
+        in_window.org.pkijs = {};
+    else
+    {
+        if(typeof in_window.org.pkijs !== "object")
+            throw new Error("Name org.pkijs already exists and it's not an object" + " but " + (typeof in_window.org.pkijs));
+    }
+    // #endregion 
+
+    // #region "org.pkijs.schema" namespace 
+    if(typeof in_window.org.pkijs.schema === "undefined")
+        in_window.org.pkijs.schema = {};
+    else
+    {
+        if(typeof in_window.org.pkijs.schema !== "object")
+            throw new Error("Name org.pkijs.schema already exists and it's not an object" + " but " + (typeof in_window.org.pkijs.schema));
+    }
+    // #endregion 
+
+    // #region "org.pkijs.schema.x509" namespace 
+    if(typeof in_window.org.pkijs.schema.x509 === "undefined")
+        in_window.org.pkijs.schema.x509 = {};
+    else
+    {
+        if(typeof in_window.org.pkijs.schema.x509 !== "object")
+            throw new Error("Name org.pkijs.schema.x509 already exists and it's not an object" + " but " + (typeof in_window.org.pkijs.schema.x509));
+    }
+    // #endregion 
+
+    // #region "local" namespace 
+    var local = {};
+    // #endregion   
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for "Time" type 
+    //**************************************************************************************
+    in_window.org.pkijs.schema.TIME =
+    function(input_names, input_optional)
+    {
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+        var optional = (input_optional || false);
+
+        return (new in_window.org.pkijs.asn1.CHOICE({
+            optional: optional,
+            value: [
+                new in_window.org.pkijs.asn1.UTCTIME({ name: (names.utcTimeName || "") }),
+                new in_window.org.pkijs.asn1.GENERALIZEDTIME({ name: (names.generalTimeName || "") })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for X.509 v3 certificate (RFC5280)
+    //**************************************************************************************
+    local.tbsCertificate =
+    function()
+    {
+        //TBSCertificate  ::=  SEQUENCE  {
+        //    version         [0]  EXPLICIT Version DEFAULT v1,
+        //    serialNumber         CertificateSerialNumber,
+        //    signature            AlgorithmIdentifier,
+        //    issuer               Name,
+        //    validity             Validity,
+        //    subject              Name,
+        //    subjectPublicKeyInfo SubjectPublicKeyInfo,
+        //    issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
+        //                         -- If present, version MUST be v2 or v3
+        //    subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
+        //                         -- If present, version MUST be v2 or v3
+        //    extensions      [3]  EXPLICIT Extensions OPTIONAL
+        //    -- If present, version MUST be v3
+        //}
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || "tbsCertificate"),
+            value: [
+                new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                    optional: true,
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 0 // [0]
+                    },
+                value: [
+                    new in_window.org.pkijs.asn1.INTEGER({ name: (names.tbsCertificate_version || "tbsCertificate.version") }) // EXPLICIT integer value
+                ]
+            }),
+                new in_window.org.pkijs.asn1.INTEGER({ name: (names.tbsCertificate_serialNumber || "tbsCertificate.serialNumber") }),
+                in_window.org.pkijs.schema.ALGORITHM_IDENTIFIER(names.signature || {
+                    names: {
+                        block_name: "tbsCertificate.signature"
+                    }
+                }),
+                in_window.org.pkijs.schema.RDN(names.issuer || {
+                    names: {
+                        block_name: "tbsCertificate.issuer"
+                    }
+                }),
+                new in_window.org.pkijs.asn1.SEQUENCE({
+                    name: (names.tbsCertificate_validity || "tbsCertificate.validity"),
+                    value: [
+                        in_window.org.pkijs.schema.TIME(names.not_before || {
+                            names: {
+                                utcTimeName: "tbsCertificate.notBefore",
+                                generalTimeName: "tbsCertificate.notBefore"
+                            }
+                        }),
+                        in_window.org.pkijs.schema.TIME(names.not_after || {
+                            names: {
+                                utcTimeName: "tbsCertificate.notAfter",
+                                generalTimeName: "tbsCertificate.notAfter"
+                            }
+                        })
+                    ]
+                }),
+                in_window.org.pkijs.schema.RDN(names.subject || {
+                    names: {
+                        block_name: "tbsCertificate.subject"
+                    }
+                }),
+                in_window.org.pkijs.schema.PUBLIC_KEY_INFO(names.subjectPublicKeyInfo || {
+                    names: {
+                        block_name: "tbsCertificate.subjectPublicKeyInfo"
+                    }
+                }),
+                new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                    name: (names.tbsCertificate_issuerUniqueID ||"tbsCertificate.issuerUniqueID"),
+                    optional: true,
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 1 // [1]
+                    }
+                }), // IMPLICIT bistring value
+                new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                    name: (names.tbsCertificate_subjectUniqueID ||"tbsCertificate.subjectUniqueID"),
+                    optional: true,
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 2 // [2]
+                    }
+                }), // IMPLICIT bistring value
+                new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                    optional: true,
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 3 // [3]
+                    },
+                    value: [in_window.org.pkijs.schema.EXTENSIONS(names.extensions || {
+                        names: {
+                            block_name: "tbsCertificate.extensions"
+                        }
+                    })]
+                }) // EXPLICIT SEQUENCE value
+            ]
+        }));
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.schema.CERT =
+    function()
+    {
+        //Certificate  ::=  SEQUENCE  {
+        //    tbsCertificate       TBSCertificate,
+        //    signatureAlgorithm   AlgorithmIdentifier,
+        //    signatureValue       BIT STRING  }
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || ""),
+            value: [
+                local.tbsCertificate(names.tbsCertificate),
+                in_window.org.pkijs.schema.ALGORITHM_IDENTIFIER(names.signatureAlgorithm || {
+                    names: {
+                        block_name: "signatureAlgorithm"
+                    }
+                }),
+                new in_window.org.pkijs.asn1.BITSTRING({ name: (names.signatureValue || "signatureValue") })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for X.509 CRL (Certificate Revocation List)(RFC5280)  
+    //**************************************************************************************
+    local.tbsCertList =
+    function()
+    {
+        //TBSCertList  ::=  SEQUENCE  {
+        //    version                 Version OPTIONAL,
+        //                                 -- if present, MUST be v2
+        //    signature               AlgorithmIdentifier,
+        //    issuer                  Name,
+        //    thisUpdate              Time,
+        //    nextUpdate              Time OPTIONAL,
+        //    revokedCertificates     SEQUENCE OF SEQUENCE  {
+        //        userCertificate         CertificateSerialNumber,
+        //        revocationDate          Time,
+        //        crlEntryExtensions      Extensions OPTIONAL
+        //        -- if present, version MUST be v2
+        //    }  OPTIONAL,
+        //    crlExtensions           [0]  EXPLICIT Extensions OPTIONAL
+        //    -- if present, version MUST be v2
+        //}
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || "tbsCertList"),
+            value: [
+                        new in_window.org.pkijs.asn1.INTEGER({
+                            optional: true,
+                            name: (names.tbsCertList_version || "tbsCertList.version"),
+                            value: 2
+                        }), // EXPLICIT integer value (v2)
+                        in_window.org.pkijs.schema.ALGORITHM_IDENTIFIER(names.signature || {
+                            names: {
+                                block_name: "tbsCertList.signature"
+                            }
+                        }),
+                        in_window.org.pkijs.schema.RDN(names.issuer || {
+                            names: {
+                                block_name: "tbsCertList.issuer"
+                            }
+                        }),
+                        in_window.org.pkijs.schema.TIME(names.tbsCertList_thisUpdate || {
+                            names: {
+                                utcTimeName: "tbsCertList.thisUpdate",
+                                generalTimeName: "tbsCertList.thisUpdate"
+                            }
+                        }),
+                        in_window.org.pkijs.schema.TIME(names.tbsCertList_thisUpdate || {
+                            names: {
+                                utcTimeName: "tbsCertList.nextUpdate",
+                                generalTimeName: "tbsCertList.nextUpdate"
+                            }
+                        }, true),
+                        new in_window.org.pkijs.asn1.SEQUENCE({
+                            optional: true,
+                            value: [
+                                new in_window.org.pkijs.asn1.REPEATED({
+                                    name: (names.tbsCertList_revokedCertificates || "tbsCertList.revokedCertificates"),
+                                    value: new in_window.org.pkijs.asn1.SEQUENCE({
+                                        value: [
+                                            new in_window.org.pkijs.asn1.INTEGER(),
+                                            in_window.org.pkijs.schema.TIME(),
+                                            in_window.org.pkijs.schema.EXTENSIONS({}, true)
+                                        ]
+                                    })
+                                })
+                            ]
+                        }),
+                        new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                            optional: true,
+                            id_block: {
+                                tag_class: 3, // CONTEXT-SPECIFIC
+                                tag_number: 0 // [0]
+                            },
+                            value: [in_window.org.pkijs.schema.EXTENSIONS(names.crlExtensions || {
+                                names: {
+                                    block_name: "tbsCertList.extensions"
+                                }
+                            })]
+                        }) // EXPLICIT SEQUENCE value
+                    ]
+        }));
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.schema.CRL =
+    function()
+    {
+        //CertificateList  ::=  SEQUENCE  {
+        //    tbsCertList          TBSCertList,
+        //    signatureAlgorithm   AlgorithmIdentifier,
+        //    signatureValue       BIT STRING  }
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || "CertificateList"),
+            value: [
+                local.tbsCertList(arguments[0]),
+                in_window.org.pkijs.schema.ALGORITHM_IDENTIFIER(names.signatureAlgorithm || {
+                    names: {
+                        block_name: "signatureAlgorithm"
+                    }
+                }),
+                new in_window.org.pkijs.asn1.BITSTRING({ name: (names.signatureValue || "signatureValue") })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for PKCS#10 certificate request 
+    //**************************************************************************************
+    local.CertificationRequestInfo =
+    function()
+    {
+        //CertificationRequestInfo ::= SEQUENCE {
+        //    version       INTEGER { v1(0) } (v1,...),
+        //    subject       Name,
+        //    subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }},
+        //    attributes    [0] Attributes{{ CRIAttributes }}
+        //}
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.CertificationRequestInfo || "CertificationRequestInfo"),
+            value: [
+                new in_window.org.pkijs.asn1.INTEGER({ name: (names.CertificationRequestInfo_version || "CertificationRequestInfo.version") }),
+                new in_window.org.pkijs.schema.RDN(names.subject || {
+                    names: {
+                        block_name: "CertificationRequestInfo.subject"
+                    }
+                }),
+                new in_window.org.pkijs.schema.PUBLIC_KEY_INFO({
+                    names: {
+                        block_name: "CertificationRequestInfo.subjectPublicKeyInfo"
+                    }
+                }),
+                new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                    optional: true,
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 0 // [0]
+                    },
+                    value: [
+                        new in_window.org.pkijs.asn1.REPEATED({
+                            optional: true, // Because OpenSSL makes wrong "attributes" field
+                            name: (names.CertificationRequestInfo_attributes || "CertificationRequestInfo.attributes"),
+                            value: in_window.org.pkijs.schema.ATTRIBUTE(names.attributes || {})
+                        })
+                    ]
+                })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.schema.PKCS10 =
+    function()
+    {
+        //CertificationRequest ::= SEQUENCE {
+        //    certificationRequestInfo CertificationRequestInfo,
+        //    signatureAlgorithm       AlgorithmIdentifier{{ SignatureAlgorithms }},
+        //    signature                BIT STRING
+        //}
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: [
+                local.CertificationRequestInfo(names.certificationRequestInfo || {}),
+                new in_window.org.pkijs.asn1.SEQUENCE({
+                    name: (names.signatureAlgorithm || "signatureAlgorithm"),
+                    value: [
+                        new in_window.org.pkijs.asn1.OID(),
+                        new in_window.org.pkijs.asn1.ANY({ optional: true })
+                    ]
+                }),
+                new in_window.org.pkijs.asn1.BITSTRING({ name: (names.signatureValue || "signatureValue") })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for PKCS#8 private key bag
+    //**************************************************************************************
+    in_window.org.pkijs.schema.PKCS8 =
+    function()
+    {
+        //PrivateKeyInfo ::= SEQUENCE {
+        //    version Version,
+        //    privateKeyAlgorithm AlgorithmIdentifier {{PrivateKeyAlgorithms}},
+        //    privateKey PrivateKey,
+        //    attributes [0] Attributes OPTIONAL }
+        //
+        //Version ::= INTEGER {v1(0)} (v1,...)
+        //
+        //PrivateKey ::= OCTET STRING
+        //
+        //Attributes ::= SET OF Attribute
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: [
+                new in_window.org.pkijs.asn1.INTEGER({ name: (names.version || "") }),
+                in_window.org.pkijs.schema.ALGORITHM_IDENTIFIER(names.privateKeyAlgorithm || ""),
+                new in_window.org.pkijs.asn1.OCTETSTRING({ name: (names.privateKey || "") }),
+                new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                    optional: true,
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 0 // [0]
+                    },
+                    value: [
+                        new in_window.org.pkijs.asn1.REPEATED({
+                            name: (names.attributes || ""),
+                            value: in_window.org.pkijs.schema.ATTRIBUTE()
+                        })
+                    ]
+                })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for "GeneralName" type 
+    //**************************************************************************************
+    local.BuiltInStandardAttributes =
+    function(optional_flag)
+    {
+        //BuiltInStandardAttributes ::= SEQUENCE {
+        //    country-name                  CountryName OPTIONAL,
+        //    administration-domain-name    AdministrationDomainName OPTIONAL,
+        //    network-address           [0] IMPLICIT NetworkAddress OPTIONAL,
+        //    terminal-identifier       [1] IMPLICIT TerminalIdentifier OPTIONAL,
+        //    private-domain-name       [2] PrivateDomainName OPTIONAL,
+        //    organization-name         [3] IMPLICIT OrganizationName OPTIONAL,
+        //    numeric-user-identifier   [4] IMPLICIT NumericUserIdentifier OPTIONAL,
+        //    personal-name             [5] IMPLICIT PersonalName OPTIONAL,
+        //    organizational-unit-names [6] IMPLICIT OrganizationalUnitNames OPTIONAL }
+
+        if(typeof optional_flag === "undefined")
+            optional_flag = false;
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            optional: optional_flag,
+            value: [
+                new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                    optional: true,
+                    id_block: {
+                        tag_class: 2, // APPLICATION-SPECIFIC
+                        tag_number: 1 // [1]
+                    },
+                    name: (names.country_name || ""),
+                    value: [
+                        new in_window.org.pkijs.asn1.CHOICE({
+                            value: [
+                                new in_window.org.pkijs.asn1.NUMERICSTRING(),
+                                new in_window.org.pkijs.asn1.PRINTABLESTRING()
+                            ]
+                        })
+                    ]
+                }),
+                new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                    optional: true,
+                    id_block: {
+                        tag_class: 2, // APPLICATION-SPECIFIC
+                        tag_number: 2 // [2]
+                    },
+                    name: (names.administration_domain_name || ""),
+                    value: [
+                        new in_window.org.pkijs.asn1.CHOICE({
+                            value: [
+                                new in_window.org.pkijs.asn1.NUMERICSTRING(),
+                                new in_window.org.pkijs.asn1.PRINTABLESTRING()
+                            ]
+                        })
+                    ]
+                }),
+                new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                    optional: true,
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 0 // [0]
+                    },
+                    name: (names.network_address || ""),
+                    is_hex_only: true
+                }),
+                new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                    optional: true,
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 1 // [1]
+                    },
+                    name: (names.terminal_identifier || ""),
+                    is_hex_only: true
+                }),
+                new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                    optional: true,
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 2 // [2]
+                    },
+                    name: (names.private_domain_name || ""),
+                    value: [
+                        new in_window.org.pkijs.asn1.CHOICE({
+                            value: [
+                                new in_window.org.pkijs.asn1.NUMERICSTRING(),
+                                new in_window.org.pkijs.asn1.PRINTABLESTRING()
+                            ]
+                        })
+                    ]
+                }),
+                new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                    optional: true,
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 3 // [3]
+                    },
+                    name: (names.organization_name || ""),
+                    is_hex_only: true
+                }),
+                new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                    optional: true,
+                    name: (names.numeric_user_identifier || ""),
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 4 // [4]
+                    },
+                    is_hex_only: true
+                }),
+                new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                    optional: true,
+                    name: (names.personal_name || ""),
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 5 // [5]
+                    },
+                    value: [
+                        new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                            id_block: {
+                                tag_class: 3, // CONTEXT-SPECIFIC
+                                tag_number: 0 // [0]
+                            },
+                            is_hex_only: true
+                        }),
+                        new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                            optional: true,
+                            id_block: {
+                                tag_class: 3, // CONTEXT-SPECIFIC
+                                tag_number: 1 // [1]
+                            },
+                            is_hex_only: true
+                        }),
+                        new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                            optional: true,
+                            id_block: {
+                                tag_class: 3, // CONTEXT-SPECIFIC
+                                tag_number: 2 // [2]
+                            },
+                            is_hex_only: true
+                        }),
+                        new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                            optional: true,
+                            id_block: {
+                                tag_class: 3, // CONTEXT-SPECIFIC
+                                tag_number: 3 // [3]
+                            },
+                            is_hex_only: true
+                        })
+                    ]
+                }),
+                new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                    optional: true,
+                    name: (names.organizational_unit_names || ""),
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 6 // [6]
+                    },
+                    value: [
+                        new in_window.org.pkijs.asn1.REPEATED({
+                            value: new in_window.org.pkijs.asn1.PRINTABLESTRING()
+                        })
+                    ]
+                })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    local.BuiltInDomainDefinedAttributes =
+    function(optional_flag)
+    {
+        if(typeof optional_flag === "undefined")
+            optional_flag = false;
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            optional: optional_flag,
+            value: [
+                new in_window.org.pkijs.asn1.PRINTABLESTRING(),
+                new in_window.org.pkijs.asn1.PRINTABLESTRING()
+            ]
+        }));
+    };
+    //**************************************************************************************
+    local.ExtensionAttributes =
+    function(optional_flag)
+    {
+        if(typeof optional_flag === "undefined")
+            optional_flag = false;
+
+        return (new in_window.org.pkijs.asn1.SET({
+            optional: optional_flag,
+            value: [
+                new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                    optional: true,
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 0 // [0]
+                    },
+                    is_hex_only: true
+                }),
+                new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                    optional: true,
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 1 // [1]
+                    },
+                    value: [new in_window.org.pkijs.asn1.ANY()]
+                })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.schema.GENERAL_NAME =
+    function()
+    {
+        /// <remarks>By passing "names" array as an argument you can name each element of "GENERAL NAME" choice</remarks>
+
+        //GeneralName ::= CHOICE {
+        //    otherName                       [0]     OtherName,
+        //    rfc822Name                      [1]     IA5String,
+        //    dNSName                         [2]     IA5String,
+        //    x400Address                     [3]     ORAddress,
+        //    directoryName                   [4]     Name,
+        //    ediPartyName                    [5]     EDIPartyName,
+        //    uniformResourceIdentifier       [6]     IA5String,
+        //    iPAddress                       [7]     OCTET STRING,
+        //    registeredID                    [8]     OBJECT IDENTIFIER }
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.CHOICE({
+            value: [
+                new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 0 // [0]
+                    },
+                    name: (names.block_name || ""),
+                    value: [
+                            new in_window.org.pkijs.asn1.OID(),
+                            new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                                id_block: {
+                                    tag_class: 3, // CONTEXT-SPECIFIC
+                                    tag_number: 0 // [0]
+                                },
+                                value: [new in_window.org.pkijs.asn1.ANY()]
+                            })
+                    ]
+                }),
+                new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                    name: (names.block_name || ""),
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 1 // [1]
+                    }
+                }),
+                new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                    name: (names.block_name || ""),
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 2 // [2]
+                    }
+                }),
+                new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 3 // [3]
+                    },
+                    name: (names.block_name || ""),
+                    value: [
+                            local.BuiltInStandardAttributes(false),
+                            local.BuiltInDomainDefinedAttributes(true),
+                            local.ExtensionAttributes(true)
+                    ]
+                }),
+                new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 4 // [4]
+                    },
+                    name: (names.block_name || ""),
+                    value: [in_window.org.pkijs.schema.RDN(names.directoryName || {})]
+                }),
+                new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 5 // [5]
+                    },
+                    name: (names.block_name || ""),
+                    value: [
+                            new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                                optional: true,
+                                id_block: {
+                                    tag_class: 3, // CONTEXT-SPECIFIC
+                                    tag_number: 0 // [0]
+                                },
+                                value: [
+                                    new in_window.org.pkijs.asn1.CHOICE({
+                                        value: [
+                                            new in_window.org.pkijs.asn1.TELETEXSTRING(),
+                                            new in_window.org.pkijs.asn1.PRINTABLESTRING(),
+                                            new in_window.org.pkijs.asn1.UNIVERSALSTRING(),
+                                            new in_window.org.pkijs.asn1.UTF8STRING(),
+                                            new in_window.org.pkijs.asn1.BMPSTRING()
+                                        ]
+                                    })
+                                ]
+                            }),
+                            new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                                id_block: {
+                                    tag_class: 3, // CONTEXT-SPECIFIC
+                                    tag_number: 1 // [1]
+                                },
+                                value: [
+                                    new in_window.org.pkijs.asn1.CHOICE({
+                                        value: [
+                                            new in_window.org.pkijs.asn1.TELETEXSTRING(),
+                                            new in_window.org.pkijs.asn1.PRINTABLESTRING(),
+                                            new in_window.org.pkijs.asn1.UNIVERSALSTRING(),
+                                            new in_window.org.pkijs.asn1.UTF8STRING(),
+                                            new in_window.org.pkijs.asn1.BMPSTRING()
+                                        ]
+                                    })
+                                ]
+                            })
+                    ]
+                }),
+                new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                    name: (names.block_name || ""),
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 6 // [6]
+                    }
+                }),
+                new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                    name: (names.block_name || ""),
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 7 // [7]
+                    }
+                }),
+                new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                    name: (names.block_name || ""),
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 8 // [8]
+                    }
+                })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for "AlgorithmIdentifier" type 
+    //**************************************************************************************
+    in_window.org.pkijs.schema.ALGORITHM_IDENTIFIER =
+    function()
+    {
+        //AlgorithmIdentifier  ::=  SEQUENCE  {
+        //    algorithm               OBJECT IDENTIFIER,
+        //    parameters              ANY DEFINED BY algorithm OPTIONAL  }
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || ""),
+            optional: (names.optional || false),
+            value: [
+                new in_window.org.pkijs.asn1.OID({ name: (names.algorithmIdentifier || "") }),
+                new in_window.org.pkijs.asn1.ANY({ name: (names.algorithmParams || ""), optional: true })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for "RSAPublicKey" type (RFC3447)
+    //**************************************************************************************
+    in_window.org.pkijs.schema.x509.RSAPublicKey =
+    function()
+    {
+        //RSAPublicKey ::= SEQUENCE {
+        //    modulus           INTEGER,  -- n
+        //    publicExponent    INTEGER   -- e
+        //}
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || ""),
+            value: [
+                new in_window.org.pkijs.asn1.INTEGER({ name: (names.modulus || "") }),
+                new in_window.org.pkijs.asn1.INTEGER({ name: (names.publicExponent || "") })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for "OtherPrimeInfo" type (RFC3447) 
+    //**************************************************************************************
+    in_window.org.pkijs.schema.x509.OtherPrimeInfo =
+    function()
+    {
+        //OtherPrimeInfo ::= SEQUENCE {
+        //    prime             INTEGER,  -- ri
+        //    exponent          INTEGER,  -- di
+        //    coefficient       INTEGER   -- ti
+        //}
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || ""),
+            value: [
+                new in_window.org.pkijs.asn1.INTEGER({ name: (names.prime || "") }),
+                new in_window.org.pkijs.asn1.INTEGER({ name: (names.exponent || "") }),
+                new in_window.org.pkijs.asn1.INTEGER({ name: (names.coefficient || "") })
+    ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for "RSAPrivateKey" type (RFC3447)
+    //**************************************************************************************
+    in_window.org.pkijs.schema.x509.RSAPrivateKey =
+    function()
+    {
+        //RSAPrivateKey ::= SEQUENCE {
+        //    version           Version,
+        //    modulus           INTEGER,  -- n
+        //    publicExponent    INTEGER,  -- e
+        //    privateExponent   INTEGER,  -- d
+        //    prime1            INTEGER,  -- p
+        //    prime2            INTEGER,  -- q
+        //    exponent1         INTEGER,  -- d mod (p-1)
+        //    exponent2         INTEGER,  -- d mod (q-1)
+        //    coefficient       INTEGER,  -- (inverse of q) mod p
+        //    otherPrimeInfos   OtherPrimeInfos OPTIONAL
+        //}
+        //
+        //OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || ""),
+            value: [
+                new in_window.org.pkijs.asn1.INTEGER({ name: (names.version || "") }),
+                new in_window.org.pkijs.asn1.INTEGER({ name: (names.modulus || "") }),
+                new in_window.org.pkijs.asn1.INTEGER({ name: (names.publicExponent || "") }),
+                new in_window.org.pkijs.asn1.INTEGER({ name: (names.privateExponent || "") }),
+                new in_window.org.pkijs.asn1.INTEGER({ name: (names.prime1 || "") }),
+                new in_window.org.pkijs.asn1.INTEGER({ name: (names.prime2 || "") }),
+                new in_window.org.pkijs.asn1.INTEGER({ name: (names.exponent1 || "") }),
+                new in_window.org.pkijs.asn1.INTEGER({ name: (names.exponent2 || "") }),
+                new in_window.org.pkijs.asn1.INTEGER({ name: (names.coefficient || "") }),
+                new in_window.org.pkijs.asn1.SEQUENCE({
+                    optional: true,
+                    value: [
+                        new in_window.org.pkijs.asn1.REPEATED({
+                            name: (names.otherPrimeInfos || ""),
+                            value: in_window.org.pkijs.schema.x509.OtherPrimeInfo(names.otherPrimeInfo || {})
+                        })
+                    ]
+                })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for "RSASSA-PSS-params" type (RFC3447)
+    //**************************************************************************************
+    in_window.org.pkijs.schema.x509.RSASSA_PSS_params =
+    function()
+    {
+        //RSASSA-PSS-params  ::=  SEQUENCE  {
+        //    hashAlgorithm      [0] HashAlgorithm DEFAULT sha1Identifier,
+        //    maskGenAlgorithm   [1] MaskGenAlgorithm DEFAULT mgf1SHA1Identifier,
+        //    saltLength         [2] INTEGER DEFAULT 20,
+        //    trailerField       [3] INTEGER DEFAULT 1  }
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || ""),
+            value: [
+                new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 0 // [0]
+                    },
+                    optional: true,
+                    value: [in_window.org.pkijs.schema.ALGORITHM_IDENTIFIER(names.hashAlgorithm || {})]
+                }),
+                new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 1 // [1]
+                    },
+                    optional: true,
+                    value: [in_window.org.pkijs.schema.ALGORITHM_IDENTIFIER(names.maskGenAlgorithm || {})]
+                }),
+                new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 2 // [2]
+                    },
+                    optional: true,
+                    value: [new in_window.org.pkijs.asn1.INTEGER({ name: (names.saltLength || "") })]
+                }),
+                new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 3 // [3]
+                    },
+                    optional: true,
+                    value: [new in_window.org.pkijs.asn1.INTEGER({ name: (names.trailerField || "") })]
+                })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for "SubjectPublicKeyInfo" type 
+    //**************************************************************************************
+    in_window.org.pkijs.schema.PUBLIC_KEY_INFO =
+    function()
+    {
+        //SubjectPublicKeyInfo  ::=  SEQUENCE  {
+        //    algorithm            AlgorithmIdentifier,
+        //    subjectPublicKey     BIT STRING  }
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || ""),
+            value: [
+                in_window.org.pkijs.schema.ALGORITHM_IDENTIFIER(names.algorithm || {}),
+                new in_window.org.pkijs.asn1.BITSTRING({ name: (names.subjectPublicKey || "") })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for "Attribute" type 
+    //**************************************************************************************
+    in_window.org.pkijs.schema.ATTRIBUTE =
+    function()
+    {
+        // Attribute { ATTRIBUTE:IOSet } ::= SEQUENCE {
+        //    type   ATTRIBUTE.&id({IOSet}),
+        //    values SET SIZE(1..MAX) OF ATTRIBUTE.&Type({IOSet}{@type})
+        //}
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || ""),
+            value: [
+                new in_window.org.pkijs.asn1.OID({ name: (names.type || "") }),
+                new in_window.org.pkijs.asn1.SET({
+                    name: (names.set_name || ""),
+                    value: [
+                        new in_window.org.pkijs.asn1.REPEATED({
+                            name: (names.values || ""),
+                            value: new in_window.org.pkijs.asn1.ANY()
+                        })
+                    ]
+                })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for "AttributeTypeAndValue" type 
+    //**************************************************************************************
+    in_window.org.pkijs.schema.ATTR_TYPE_AND_VALUE =
+    function()
+    {
+        //AttributeTypeAndValue ::= SEQUENCE {
+        //    type     AttributeType,
+        //    value    AttributeValue }
+        //
+        //AttributeType ::= OBJECT IDENTIFIER
+        //
+        //AttributeValue ::= ANY -- DEFINED BY AttributeType
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || ""),
+            value: [
+                new in_window.org.pkijs.asn1.OID({ name: (names.type || "") }),
+                new in_window.org.pkijs.asn1.ANY({ name: (names.value || "") })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for "RelativeDistinguishedName" type 
+    //**************************************************************************************
+    in_window.org.pkijs.schema.RDN =
+    function()
+    {
+        //RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+        //
+        //RelativeDistinguishedName ::=
+        //SET SIZE (1..MAX) OF AttributeTypeAndValue
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || ""),
+            value: [
+                new in_window.org.pkijs.asn1.REPEATED({
+                    name: (names.repeated_sequence || ""),
+                    value: new in_window.org.pkijs.asn1.SET({
+                        value: [
+                            new in_window.org.pkijs.asn1.REPEATED({
+                                name: (names.repeated_set || ""),
+                                value: in_window.org.pkijs.schema.ATTR_TYPE_AND_VALUE(names.attr_type_and_value || {})
+                            })
+                        ]
+                    })
+                })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for "Extension" type 
+    //**************************************************************************************
+    in_window.org.pkijs.schema.EXTENSION =
+    function()
+    {
+        //Extension  ::=  SEQUENCE  {
+        //    extnID      OBJECT IDENTIFIER,
+        //    critical    BOOLEAN DEFAULT FALSE,
+        //    extnValue   OCTET STRING
+        //}
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || ""),
+            value: [
+                new in_window.org.pkijs.asn1.OID({ name: (names.extnID || "") }),
+                new in_window.org.pkijs.asn1.BOOLEAN({
+                    name: (names.critical || ""),
+                    optional: true
+                }),
+                new in_window.org.pkijs.asn1.OCTETSTRING({ name: (names.extnValue || "") })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for "Extensions" type (sequence of many Extension)
+    //**************************************************************************************
+    in_window.org.pkijs.schema.EXTENSIONS =
+    function(input_names, input_optional)
+    {
+        //Extensions  ::=  SEQUENCE SIZE (1..MAX) OF Extension
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+        var optional = input_optional || false;
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            optional: optional,
+            name: (names.block_name || ""),
+            value: [
+                new in_window.org.pkijs.asn1.REPEATED({
+                    name: (names.extensions || ""),
+                    value: in_window.org.pkijs.schema.EXTENSION(names.extension || {})
+                })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for "AuthorityKeyIdentifier" type of extension 
+    //**************************************************************************************
+    in_window.org.pkijs.schema.x509.AuthorityKeyIdentifier =
+    function()
+    {
+        // AuthorityKeyIdentifier OID ::= 2.5.29.35
+        // 
+        //AuthorityKeyIdentifier ::= SEQUENCE {
+        //    keyIdentifier             [0] KeyIdentifier           OPTIONAL,
+        //    authorityCertIssuer       [1] GeneralNames            OPTIONAL,
+        //    authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL  }
+        //
+        //KeyIdentifier ::= OCTET STRING
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || ""),
+            value: [
+                new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                    name: (names.keyIdentifier || ""),
+                    optional: true,
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 0 // [0]
+                    }
+                }),
+                new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                    optional: true,
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 1 // [1]
+                    },
+                    value: [
+                            new in_window.org.pkijs.asn1.REPEATED({
+                                name: (names.authorityCertIssuer || ""),
+                                value: in_window.org.pkijs.schema.GENERAL_NAME()
+                            })
+                    ]
+                }),
+                new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                    name: (names.authorityCertSerialNumber || ""),
+                    optional: true,
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 2 // [2]
+                    }
+                })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for "PrivateKeyUsagePeriod" type of extension 
+    //**************************************************************************************
+    in_window.org.pkijs.schema.x509.PrivateKeyUsagePeriod =
+    function()
+    {
+        // PrivateKeyUsagePeriod OID ::= 2.5.29.16
+        //
+        //PrivateKeyUsagePeriod ::= SEQUENCE {
+        //    notBefore       [0]     GeneralizedTime OPTIONAL,
+        //    notAfter        [1]     GeneralizedTime OPTIONAL }
+        //-- either notBefore or notAfter MUST be present
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || ""),
+            value: [
+                new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                    name: (names.notBefore || ""),
+                    optional: true,
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 0 // [0]
+                    }
+                }),
+                new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                    name: (names.notAfter || ""),
+                    optional: true,
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 1 // [1]
+                    }
+                })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for "IssuerAltName" and "SubjectAltName" types of extension 
+    //**************************************************************************************
+    in_window.org.pkijs.schema.x509.AltName =
+    function()
+    {
+        // SubjectAltName OID ::= 2.5.29.17
+        // IssuerAltName OID ::= 2.5.29.18
+        //
+        // AltName ::= GeneralNames
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || ""),
+            value: [
+                new in_window.org.pkijs.asn1.REPEATED({
+                    name: (names.altNames || ""),
+                    value: in_window.org.pkijs.schema.GENERAL_NAME()
+                })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for "SubjectDirectoryAttributes" type of extension 
+    //**************************************************************************************
+    in_window.org.pkijs.schema.x509.SubjectDirectoryAttributes =
+    function()
+    {
+        // SubjectDirectoryAttributes OID ::= 2.5.29.9
+        //
+        //SubjectDirectoryAttributes ::= SEQUENCE SIZE (1..MAX) OF Attribute
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || ""),
+            value: [
+                new in_window.org.pkijs.asn1.REPEATED({
+                    name: (names.attributes || ""),
+                    value: in_window.org.pkijs.schema.ATTRIBUTE()
+                })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for "GeneralSubtree" type 
+    //**************************************************************************************
+    in_window.org.pkijs.schema.x509.GeneralSubtree =
+    function()
+    {
+        //GeneralSubtree ::= SEQUENCE {
+        //    base                    GeneralName,
+        //    minimum         [0]     BaseDistance DEFAULT 0,
+        //    maximum         [1]     BaseDistance OPTIONAL }
+        //
+        //BaseDistance ::= INTEGER (0..MAX)
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || ""),
+            value: [
+                in_window.org.pkijs.schema.GENERAL_NAME(names.base || ""),
+                new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                    optional: true,
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 0 // [0]
+                    },
+                    value: [new in_window.org.pkijs.asn1.INTEGER({ name: (names.minimum || "") })]
+                }),
+                new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                    optional: true,
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 1 // [1]
+                    },
+                    value: [new in_window.org.pkijs.asn1.INTEGER({ name: (names.maximum || "") })]
+                })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for "NameConstraints" type of extension 
+    //**************************************************************************************
+    in_window.org.pkijs.schema.x509.NameConstraints =
+    function()
+    {
+        // NameConstraints OID ::= 2.5.29.30
+        //
+        //NameConstraints ::= SEQUENCE {
+        //    permittedSubtrees       [0]     GeneralSubtrees OPTIONAL,
+        //    excludedSubtrees        [1]     GeneralSubtrees OPTIONAL }
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || ""),
+            value: [
+                new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                    optional: true,
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 0 // [0]
+                    },
+                    value: [
+                        new in_window.org.pkijs.asn1.REPEATED({
+                            name: (names.permittedSubtrees || ""),
+                            value: in_window.org.pkijs.schema.x509.GeneralSubtree()
+                        })
+                    ]
+                }),
+                new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                    optional: true,
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 1 // [1]
+                    },
+                    value: [
+                        new in_window.org.pkijs.asn1.REPEATED({
+                            name: (names.excludedSubtrees || ""),
+                            value: in_window.org.pkijs.schema.x509.GeneralSubtree()
+                        })
+                    ]
+                })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for "BasicConstraints" type of extension 
+    //**************************************************************************************
+    in_window.org.pkijs.schema.x509.BasicConstraints =
+    function()
+    {
+        // BasicConstraints OID ::= 2.5.29.19
+        //
+        //BasicConstraints ::= SEQUENCE {
+        //    cA                      BOOLEAN DEFAULT FALSE,
+        //    pathLenConstraint       INTEGER (0..MAX) OPTIONAL }
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || ""),
+            value: [
+                new in_window.org.pkijs.asn1.BOOLEAN({
+                    optional: true,
+                    name: (names.cA || "")
+                }),
+                new in_window.org.pkijs.asn1.INTEGER({
+                    optional: true,
+                    name: (names.pathLenConstraint || "")
+                })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for "PolicyQualifierInfo" type 
+    //**************************************************************************************
+    in_window.org.pkijs.schema.x509.PolicyQualifierInfo =
+    function()
+    {
+        //PolicyQualifierInfo ::= SEQUENCE {
+        //    policyQualifierId  PolicyQualifierId,
+        //    qualifier          ANY DEFINED BY policyQualifierId }
+        //
+        //id-qt          OBJECT IDENTIFIER ::=  { id-pkix 2 }
+        //id-qt-cps      OBJECT IDENTIFIER ::=  { id-qt 1 }
+        //id-qt-unotice  OBJECT IDENTIFIER ::=  { id-qt 2 }
+        //
+        //PolicyQualifierId ::= OBJECT IDENTIFIER ( id-qt-cps | id-qt-unotice )
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || ""),
+            value: [
+                new in_window.org.pkijs.asn1.OID({ name: (names.policyQualifierId || "") }),
+                new in_window.org.pkijs.asn1.ANY({ name: (names.qualifier || "") })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for "PolicyInformation" type 
+    //**************************************************************************************
+    in_window.org.pkijs.schema.x509.PolicyInformation =
+    function()
+    {
+        //PolicyInformation ::= SEQUENCE {
+        //    policyIdentifier   CertPolicyId,
+        //    policyQualifiers   SEQUENCE SIZE (1..MAX) OF
+        //    PolicyQualifierInfo OPTIONAL }
+        //
+        //CertPolicyId ::= OBJECT IDENTIFIER
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || ""),
+            value: [
+                new in_window.org.pkijs.asn1.OID({ name: (names.policyIdentifier || "") }),
+                new in_window.org.pkijs.asn1.SEQUENCE({
+                    optional: true,
+                    value: [
+                        new in_window.org.pkijs.asn1.REPEATED({
+                            name: (names.policyQualifiers || ""),
+                            value: in_window.org.pkijs.schema.x509.PolicyQualifierInfo()
+                        })
+                    ]
+                })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for "CertificatePolicies" type of extension 
+    //**************************************************************************************
+    in_window.org.pkijs.schema.x509.CertificatePolicies =
+    function()
+    {
+        // CertificatePolicies OID ::= 2.5.29.32
+        //
+        //certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || ""),
+            value: [
+                new in_window.org.pkijs.asn1.REPEATED({
+                    name: (names.certificatePolicies || ""),
+                    value: in_window.org.pkijs.schema.x509.PolicyInformation()
+                })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for "PolicyMapping" type
+    //**************************************************************************************
+    in_window.org.pkijs.schema.x509.PolicyMapping =
+    function()
+    {
+        //PolicyMapping ::= SEQUENCE {
+        //    issuerDomainPolicy      CertPolicyId,
+        //    subjectDomainPolicy     CertPolicyId }
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || ""),
+            value: [
+                new in_window.org.pkijs.asn1.OID({ name: (names.issuerDomainPolicy || "") }),
+                new in_window.org.pkijs.asn1.OID({ name: (names.subjectDomainPolicy || "") })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for "PolicyMappings" type of extension 
+    //**************************************************************************************
+    in_window.org.pkijs.schema.x509.PolicyMappings =
+    function()
+    {
+        // PolicyMappings OID ::= 2.5.29.33
+        //
+        //PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF PolicyMapping
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || ""),
+            value: [
+                new in_window.org.pkijs.asn1.REPEATED({
+                    name: (names.mappings || ""),
+                    value: in_window.org.pkijs.schema.x509.PolicyMapping()
+                })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for "PolicyConstraints" type of extension 
+    //**************************************************************************************
+    in_window.org.pkijs.schema.x509.PolicyConstraints =
+    function()
+    {
+        // PolicyMappings OID ::= 2.5.29.36
+        //
+        //PolicyConstraints ::= SEQUENCE {
+        //    requireExplicitPolicy           [0] SkipCerts OPTIONAL,
+        //    inhibitPolicyMapping            [1] SkipCerts OPTIONAL }
+        //
+        //SkipCerts ::= INTEGER (0..MAX)
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || ""),
+            value: [
+                new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                    name: (names.requireExplicitPolicy || ""),
+                    optional: true,
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 0 // [0]
+                    }
+                }), // IMPLICIT integer value
+                new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                    name: (names.inhibitPolicyMapping || ""),
+                    optional: true,
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 1 // [1]
+                    }
+                }) // IMPLICIT integer value
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for "ExtKeyUsage" type of extension 
+    //**************************************************************************************
+    in_window.org.pkijs.schema.x509.ExtKeyUsage =
+    function()
+    {
+        // ExtKeyUsage OID ::= 2.5.29.37
+        //
+        // ExtKeyUsage ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
+
+        // KeyPurposeId ::= OBJECT IDENTIFIER
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || ""),
+            value: [
+                new in_window.org.pkijs.asn1.REPEATED({
+                    name: (names.keyPurposes || ""),
+                    value: new in_window.org.pkijs.asn1.OID()
+                })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for "DistributionPoint" type
+    //**************************************************************************************
+    in_window.org.pkijs.schema.x509.DistributionPoint =
+    function()
+    {
+        //DistributionPoint ::= SEQUENCE {
+        //    distributionPoint       [0]     DistributionPointName OPTIONAL,
+        //    reasons                 [1]     ReasonFlags OPTIONAL,
+        //    cRLIssuer               [2]     GeneralNames OPTIONAL }
+        //
+        //DistributionPointName ::= CHOICE {
+        //    fullName                [0]     GeneralNames,
+        //    nameRelativeToCRLIssuer [1]     RelativeDistinguishedName }
+        //
+        //ReasonFlags ::= BIT STRING {
+        //    unused                  (0),
+        //    keyCompromise           (1),
+        //    cACompromise            (2),
+        //    affiliationChanged      (3),
+        //    superseded              (4),
+        //    cessationOfOperation    (5),
+        //    certificateHold         (6),
+        //    privilegeWithdrawn      (7),
+        //    aACompromise            (8) }
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || ""),
+            value: [
+                new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                    optional: true,
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 0 // [0]
+                    },
+                    value: [
+                        new in_window.org.pkijs.asn1.CHOICE({
+                            value: [
+                                new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                                    name: (names.distributionPoint || ""),
+                                    optional: true,
+                                    id_block: {
+                                        tag_class: 3, // CONTEXT-SPECIFIC
+                                        tag_number: 0 // [0]
+                                    },
+                                    value: [
+                                        new in_window.org.pkijs.asn1.REPEATED({
+                                            name: (names.distributionPoint_names || ""),
+                                            value: in_window.org.pkijs.schema.GENERAL_NAME()
+                                        })
+                                    ]
+                                }),
+                                new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                                    name: (names.distributionPoint || ""),
+                                    optional: true,
+                                    id_block: {
+                                        tag_class: 3, // CONTEXT-SPECIFIC
+                                        tag_number: 1 // [1]
+                                    },
+                                    value: in_window.org.pkijs.schema.RDN().value_block.value
+                                })
+                            ]
+                        })
+                    ]
+                }),
+                new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                    name: (names.reasons || ""),
+                    optional: true,
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 1 // [1]
+                    }
+                }), // IMPLICIT bitstring value
+                new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                    name: (names.cRLIssuer || ""),
+                    optional: true,
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 2 // [2]
+                    },
+                    value: [
+                        new in_window.org.pkijs.asn1.REPEATED({
+                            name: (names.cRLIssuer_names || ""),
+                            value: in_window.org.pkijs.schema.GENERAL_NAME()
+                        })
+                    ]
+                }) // IMPLICIT bitstring value
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for "CRLDistributionPoints" type of extension 
+    //**************************************************************************************
+    in_window.org.pkijs.schema.x509.CRLDistributionPoints =
+    function()
+    {
+        // CRLDistributionPoints OID ::= 2.5.29.31
+        //
+        //CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || ""),
+            value: [
+                new in_window.org.pkijs.asn1.REPEATED({
+                    name: (names.distributionPoints || ""),
+                    value: in_window.org.pkijs.schema.x509.DistributionPoint()
+                })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for "AccessDescription" type
+    //**************************************************************************************
+    in_window.org.pkijs.schema.x509.AccessDescription =
+    function()
+    {
+        //AccessDescription  ::=  SEQUENCE {
+        //    accessMethod          OBJECT IDENTIFIER,
+        //    accessLocation        GeneralName  }
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || ""),
+            value: [
+                new in_window.org.pkijs.asn1.OID({ name: (names.accessMethod || "") }),
+                in_window.org.pkijs.schema.GENERAL_NAME(names.accessLocation || "")
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for "AuthorityInfoAccess" and "SubjectInfoAccess" types of extension 
+    //**************************************************************************************
+    in_window.org.pkijs.schema.x509.InfoAccess =
+    function()
+    {
+        // AuthorityInfoAccess OID ::= 1.3.6.1.5.5.7.1.1
+        // SubjectInfoAccess OID ::= 1.3.6.1.5.5.7.1.11
+        //
+        //AuthorityInfoAccessSyntax  ::=
+        //SEQUENCE SIZE (1..MAX) OF AccessDescription
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || ""),
+            value: [
+                new in_window.org.pkijs.asn1.REPEATED({
+                    name: (names.accessDescriptions || ""),
+                    value: in_window.org.pkijs.schema.x509.AccessDescription()
+                })
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region ASN.1 schema definition for "IssuingDistributionPoint" type of extension 
+    //**************************************************************************************
+    in_window.org.pkijs.schema.x509.IssuingDistributionPoint =
+    function()
+    {
+        // IssuingDistributionPoint OID ::= 2.5.29.28
+        //
+        //IssuingDistributionPoint ::= SEQUENCE {
+        //    distributionPoint          [0] DistributionPointName OPTIONAL,
+        //    onlyContainsUserCerts      [1] BOOLEAN DEFAULT FALSE,
+        //    onlyContainsCACerts        [2] BOOLEAN DEFAULT FALSE,
+        //    onlySomeReasons            [3] ReasonFlags OPTIONAL,
+        //    indirectCRL                [4] BOOLEAN DEFAULT FALSE,
+        //    onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE }
+        //
+        //ReasonFlags ::= BIT STRING {
+        //    unused                  (0),
+        //    keyCompromise           (1),
+        //    cACompromise            (2),
+        //    affiliationChanged      (3),
+        //    superseded              (4),
+        //    cessationOfOperation    (5),
+        //    certificateHold         (6),
+        //    privilegeWithdrawn      (7),
+        //    aACompromise            (8) }
+
+        var names = in_window.org.pkijs.getNames(arguments[0]);
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            name: (names.block_name || ""),
+            value: [
+                new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                    optional: true,
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 0 // [0]
+                    },
+                    value: [
+                        new in_window.org.pkijs.asn1.CHOICE({
+                            value: [
+                                new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                                    name: (names.distributionPoint || ""),
+                                    id_block: {
+                                        tag_class: 3, // CONTEXT-SPECIFIC
+                                        tag_number: 0 // [0]
+                                    },
+                                    value: [
+                                        new in_window.org.pkijs.asn1.REPEATED({
+                                            name: (names.distributionPoint_names || ""),
+                                            value: in_window.org.pkijs.schema.GENERAL_NAME()
+                                        })
+                                    ]
+                                }),
+                                new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                                    name: (names.distributionPoint || ""),
+                                    id_block: {
+                                        tag_class: 3, // CONTEXT-SPECIFIC
+                                        tag_number: 1 // [1]
+                                    },
+                                    value: in_window.org.pkijs.schema.RDN().value_block.value
+                                })
+                            ]
+                        })
+                    ]
+                }),
+                new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                    name: (names.onlyContainsUserCerts || ""),
+                    optional: true,
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 1 // [1]
+                    }
+                }), // IMPLICIT boolean value
+                new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                    name: (names.onlyContainsCACerts || ""),
+                    optional: true,
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 2 // [2]
+                    }
+                }), // IMPLICIT boolean value
+                new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                    name: (names.onlySomeReasons || ""),
+                    optional: true,
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 3 // [3]
+                    }
+                }), // IMPLICIT bitstring value
+                new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                    name: (names.indirectCRL || ""),
+                    optional: true,
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 4 // [4]
+                    }
+                }), // IMPLICIT boolean value
+                new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                    name: (names.onlyContainsAttributeCerts || ""),
+                    optional: true,
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 5 // [5]
+                    }
+                }) // IMPLICIT boolean value
+            ]
+        }));
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+}
+)(typeof exports !== "undefined" ? exports : window);
\ No newline at end of file
diff --git a/dom/webauthn/tests/pkijs/x509_simpl.js b/dom/webauthn/tests/pkijs/x509_simpl.js
new file mode 100644
index 0000000000..c00e79d55b
--- /dev/null
+++ b/dom/webauthn/tests/pkijs/x509_simpl.js
@@ -0,0 +1,7239 @@
+/*
+ * Copyright (c) 2014, GMO GlobalSign
+ * Copyright (c) 2015, Peculiar Ventures
+ * All rights reserved.
+ *
+ * Author 2014-2015, Yury Strozhevsky <www.strozhevsky.com>.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, 
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, 
+ *    this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, 
+ *    this list of conditions and the following disclaimer in the documentation 
+ *    and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its contributors 
+ *    may be used to endorse or promote products derived from this software without 
+ *    specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
+ * OF SUCH DAMAGE. 
+ *
+ */
+(
+function(in_window)
+{
+    //**************************************************************************************
+    // #region Declaration of global variables 
+    //**************************************************************************************
+    // #region "org" namespace 
+    if(typeof in_window.org === "undefined")
+        in_window.org = {};
+    else
+    {
+        if(typeof in_window.org !== "object")
+            throw new Error("Name org already exists and it's not an object");
+    }
+    // #endregion 
+
+    // #region "org.pkijs" namespace 
+    if(typeof in_window.org.pkijs === "undefined")
+        in_window.org.pkijs = {};
+    else
+    {
+        if(typeof in_window.org.pkijs !== "object")
+            throw new Error("Name org.pkijs already exists and it's not an object" + " but " + (typeof in_window.org.pkijs));
+    }
+    // #endregion 
+
+    // #region "org.pkijs.simpl" namespace 
+    if(typeof in_window.org.pkijs.simpl === "undefined")
+        in_window.org.pkijs.simpl = {};
+    else
+    {
+        if(typeof in_window.org.pkijs.simpl !== "object")
+            throw new Error("Name org.pkijs.simpl already exists and it's not an object" + " but " + (typeof in_window.org.pkijs.simpl));
+    }
+    // #endregion 
+
+    // #region "org.pkijs.simpl.x509" namespace 
+    if(typeof in_window.org.pkijs.simpl.x509 === "undefined")
+        in_window.org.pkijs.simpl.x509 = {};
+    else
+    {
+        if(typeof in_window.org.pkijs.simpl.x509 !== "object")
+            throw new Error("Name org.pkijs.simpl.x509 already exists and it's not an object" + " but " + (typeof in_window.org.pkijs.simpl.x509));
+    }
+    // #endregion 
+
+    // #region "local" namespace 
+    var local = {};
+    // #endregion   
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "Time" type
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.TIME =
+    function()
+    {
+        // #region Internal properties of the object 
+        this.type = 0; // 0 - UTCTime; 1 - GeneralizedTime; 2 - empty value
+        this.value = new Date(0, 0, 0);
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.TIME.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                this.type = (arguments[0].type || 0);
+                this.value = (arguments[0].value || (new Date(0, 0, 0)));
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.TIME.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.TIME({
+                names: {
+                    utcTimeName: "utcTimeName",
+                    generalTimeName: "generalTimeName"
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for TIME");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema 
+        if("utcTimeName" in asn1.result)
+        {
+            this.type = 0;
+            this.value = asn1.result.utcTimeName.toDate();
+        }
+        if("generalTimeName" in asn1.result)
+        {
+            this.type = 1;
+            this.value = asn1.result.generalTimeName.toDate();
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.TIME.prototype.toSchema =
+    function()
+    {
+        // #region Construct and return new ASN.1 schema for this object 
+        var result = {};
+
+        if(this.type === 0)
+            result = new in_window.org.pkijs.asn1.UTCTIME({ value_date: this.value });
+        if(this.type === 1)
+            result = new in_window.org.pkijs.asn1.GENERALIZEDTIME({ value_date: this.value });
+
+        return result;
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.TIME.prototype.toJSON =
+    function()
+    {
+        return {
+            type: this.type,
+            value: this.value
+        };
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "GeneralName" type 
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.GENERAL_NAME =
+    function()
+    {
+        // #region Internal properties of the object 
+        this.NameType = 9; // Name type - from a tagged value (0 for "otherName", 1 for "rfc822Name" etc.)
+        this.Name = {};
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.GENERAL_NAME.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                this.NameType = arguments[0].NameType || 9;
+                this.Name = arguments[0].Name || {};
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.GENERAL_NAME.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.GENERAL_NAME({
+                names: {
+                    block_name: "block_name",
+                    otherName: "otherName",
+                    rfc822Name: "rfc822Name",
+                    dNSName: "dNSName",
+                    x400Address: "x400Address",
+                    directoryName: {
+                            names: {
+                                block_name: "directoryName"
+                            }
+                        },
+                    ediPartyName: "ediPartyName",
+                    uniformResourceIdentifier: "uniformResourceIdentifier",
+                    iPAddress: "iPAddress",
+                    registeredID: "registeredID"
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for GENERAL_NAME");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema
+        this.NameType = asn1.result["block_name"].id_block.tag_number;
+
+        switch(this.NameType)
+        {
+            case 0: // otherName
+                this.Name = asn1.result["block_name"];
+                break;
+            case 1: // rfc822Name + dNSName + uniformResourceIdentifier
+            case 2:
+            case 6:
+                {
+                    var value = asn1.result["block_name"];
+
+                    value.id_block.tag_class = 1; // UNIVERSAL
+                    value.id_block.tag_number = 22; // IA5STRING
+
+                    var value_ber = value.toBER(false);
+
+                    this.Name = in_window.org.pkijs.fromBER(value_ber).result.value_block.value;
+                }
+                break;
+            case 3: // x400Address
+                this.Name = asn1.result["block_name"];
+                break;
+            case 4: // directoryName
+                this.Name = new in_window.org.pkijs.simpl.RDN({ schema: asn1.result["directoryName"] });
+                break;
+            case 5: // ediPartyName
+                this.Name = asn1.result["ediPartyName"];
+                break;
+            case 7: // iPAddress
+                this.Name = new in_window.org.pkijs.asn1.OCTETSTRING({ value_hex: asn1.result["block_name"].value_block.value_hex });
+                break;
+            case 8: // registeredID
+                {
+                    var value = asn1.result["block_name"];
+
+                    value.id_block.tag_class = 1; // UNIVERSAL
+                    value.id_block.tag_number = 6; // OID
+
+                    var value_ber = value.toBER(false);
+
+                    this.Name = in_window.org.pkijs.fromBER(value_ber).result.value_block.toString(); // Getting a string representation of the OID
+                }
+                break;
+            default:
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.GENERAL_NAME.prototype.toSchema =
+    function(schema)
+    {
+        // #region Construct and return new ASN.1 schema for this object
+        switch(this.NameType)
+        {
+            case 0:
+            case 3:
+            case 5:
+                return new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: this.NameType
+                    },
+                    value: [
+                        this.Name
+                    ]
+                });
+
+                break;
+            case 1:
+            case 2:
+            case 6:
+                {
+                    var value = new in_window.org.pkijs.asn1.IA5STRING({ value: this.Name });
+
+                    value.id_block.tag_class = 3;
+                    value.id_block.tag_number = this.NameType;
+
+                    return value;
+                }
+                break;
+            case 4:
+                return new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 4
+                    },
+                    value: [this.Name.toSchema()]
+                });
+                break;
+            case 7:
+                {
+                    var value = this.Name;
+
+                    value.id_block.tag_class = 3;
+                    value.id_block.tag_number = this.NameType;
+
+                    return value;
+                }
+                break;
+            case 8:
+                {
+                    var value = new in_window.org.pkijs.asn1.OID({ value: this.Name });
+
+                    value.id_block.tag_class = 3;
+                    value.id_block.tag_number = this.NameType;
+
+                    return value;
+                }
+                break;
+            default:
+                return in_window.org.pkijs.schema.GENERAL_NAME();
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.GENERAL_NAME.prototype.toJSON =
+    function()
+    {
+        var _object = {
+            NameType: this.NameType
+        };
+
+        if((typeof this.Name) === "string")
+            _object.Name = this.Name;
+        else
+            _object.Name = this.Name.toJSON();
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "GeneralNames" type 
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.GENERAL_NAMES =
+    function()
+    {
+        // #region Internal properties of the object 
+        this.names = new Array(); // Array of "org.pkijs.simpl.GENERAL_NAME"
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.GENERAL_NAMES.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                this.names = arguments[0].names || new Array(); // Array of "org.pkijs.simpl.GENERAL_NAME"
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.GENERAL_NAMES.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            new in_window.org.pkijs.asn1.SEQUENCE({
+                value: [
+                    new in_window.org.pkijs.asn1.REPEATED({
+                        name: "names",
+                        value: in_window.org.pkijs.schema.GENERAL_NAME()
+                    })
+                ]
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for GENERAL_NAMES");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema
+        var n = asn1.result["names"];
+
+        for(var i = 0; i < n.length; i++)
+            this.names.push(new in_window.org.pkijs.simpl.GENERAL_NAME({ schema: n[i] }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.GENERAL_NAMES.prototype.toSchema =
+    function(schema)
+    {
+        // #region Construct and return new ASN.1 schema for this object
+        var output_array = new Array();
+
+        for(var i = 0; i < this.names.length; i++)
+            output_array.push(this.names[i].toSchema());
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: output_array
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.GENERAL_NAMES.prototype.toJSON =
+    function()
+    {
+        var _names = new Array();
+
+        for(var i = 0; i < this.names.length; i++)
+            _names.push(this.names[i].toJSON());
+
+        return {
+            names: _names
+        };
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "AlgorithmIdentifier" type 
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER =
+    function()
+    {
+        // #region Internal properties of the object 
+        this.algorithm_id = "";
+        // OPTIONAL this.algorithm_params = new in_window.org.pkijs.asn1.NULL();
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                this.algorithm_id = arguments[0].algorithm_id || "";
+                if("algorithm_params" in arguments[0])
+                    this.algorithm_params = arguments[0].algorithm_params;
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.ALGORITHM_IDENTIFIER({ 
+                names: {
+                    algorithmIdentifier: "algorithm",
+                    algorithmParams: "params"
+                }
+                })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for ALGORITHM_IDENTIFIER");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema 
+        this.algorithm_id = asn1.result.algorithm.value_block.toString();
+        if("params" in asn1.result)
+            this.algorithm_params = asn1.result.params;
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER.prototype.toSchema =
+    function()
+    {
+        // #region Create array for output sequence 
+        var output_array = new Array();
+
+        output_array.push(new in_window.org.pkijs.asn1.OID({ value: this.algorithm_id }));
+        if("algorithm_params" in this)
+            output_array.push(this.algorithm_params);
+        // #endregion 
+
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: output_array
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER.prototype.getCommonName =
+    function()
+    {
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER.prototype.toJSON =
+    function()
+    {
+        var _object = {
+            algorithm_id: this.algorithm_id
+        };
+
+        if("algorithm_params" in this)
+            _object.algorithm_params = this.algorithm_params.toJSON();
+
+        return _object;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER.prototype.isEqual =
+    function(algorithmIdentifier)
+    {
+        /// <summary>Check that two "ALGORITHM_IDENTIFIERs" are equal</summary>
+        /// <param name="algorithmIdentifier" type="in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER">The algorithm identifier to compare with</param>
+
+        // #region Check input type 
+        if((algorithmIdentifier instanceof in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER) == false)
+            return false;
+        // #endregion 
+
+        // #region Check "algorithm_id" 
+        if(this.algorithm_id != algorithmIdentifier.algorithm_id)
+            return false;
+        // #endregion 
+
+        // #region Check "algorithm_params" 
+        if("algorithm_params" in this)
+        {
+            if("algorithm_params" in algorithmIdentifier)
+            {
+                return JSON.stringify(this.algorithm_params) == JSON.stringify(algorithmIdentifier.algorithm_params);
+            }
+            else
+                return false;
+        }
+        else
+        {
+            if("algorithm_params" in algorithmIdentifier)
+                return false;
+        }
+        // #endregion 
+
+        return true;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "RSAPublicKey" type (RFC3447)
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.RSAPublicKey =
+    function()
+    {
+        // #region Internal properties of the object 
+        this.modulus = new in_window.org.pkijs.asn1.INTEGER();
+        this.publicExponent = new in_window.org.pkijs.asn1.INTEGER();
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.x509.RSAPublicKey.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                this.modulus = arguments[0].modulus || new in_window.org.pkijs.asn1.INTEGER();
+                this.publicExponent = arguments[0].publicExponent || new in_window.org.pkijs.asn1.INTEGER();
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.RSAPublicKey.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.x509.RSAPublicKey({
+                names: {
+                    modulus: "modulus",
+                    publicExponent: "publicExponent"
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for RSAPublicKey");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema 
+        this.modulus = asn1.result["modulus"];
+        this.publicExponent = asn1.result["publicExponent"];
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.RSAPublicKey.prototype.toSchema =
+    function()
+    {
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: [
+                this.modulus,
+                this.publicExponent
+            ]
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.RSAPublicKey.prototype.toJSON =
+    function()
+    {
+        return {
+            modulus: this.modulus.toJSON(),
+            publicExponent: this.publicExponent.toJSON()
+        };
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "OtherPrimeInfo" type (RFC3447)
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.OtherPrimeInfo =
+    function()
+    {
+        // #region Internal properties of the object 
+        this.prime = new in_window.org.pkijs.asn1.INTEGER();
+        this.exponent = new in_window.org.pkijs.asn1.INTEGER();
+        this.coefficient = new in_window.org.pkijs.asn1.INTEGER();
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.x509.OtherPrimeInfo.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                this.prime = arguments[0].prime || new in_window.org.pkijs.asn1.INTEGER();
+                this.exponent = arguments[0].exponent || new in_window.org.pkijs.asn1.INTEGER();
+                this.coefficient = arguments[0].coefficient || new in_window.org.pkijs.asn1.INTEGER();
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.OtherPrimeInfo.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.x509.OtherPrimeInfo({
+                names: {
+                    prime: "prime",
+                    exponent: "exponent",
+                    coefficient: "coefficient"
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for OtherPrimeInfo");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema 
+        this.prime = asn1.result["prime"];
+        this.exponent = asn1.result["exponent"];
+        this.coefficient = asn1.result["coefficient"];
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.OtherPrimeInfo.prototype.toSchema =
+    function()
+    {
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: [
+                this.prime,
+                this.exponent,
+                this.coefficient
+            ]
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.OtherPrimeInfo.prototype.toJSON =
+    function()
+    {
+        return {
+            prime: this.prime.toJSON(),
+            exponent: this.exponent.toJSON(),
+            coefficient: this.coefficient.toJSON()
+        };
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "RSAPrivateKey" type (RFC3447)
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.RSAPrivateKey =
+    function()
+    {
+        // #region Internal properties of the object 
+        this.version = 0;
+        this.modulus = new in_window.org.pkijs.asn1.INTEGER();
+        this.publicExponent = new in_window.org.pkijs.asn1.INTEGER();
+        this.privateExponent = new in_window.org.pkijs.asn1.INTEGER();
+        this.prime1 = new in_window.org.pkijs.asn1.INTEGER();
+        this.prime2 = new in_window.org.pkijs.asn1.INTEGER();
+        this.exponent1 = new in_window.org.pkijs.asn1.INTEGER();
+        this.exponent2 = new in_window.org.pkijs.asn1.INTEGER();
+        this.coefficient = new in_window.org.pkijs.asn1.INTEGER();
+        // OPTIONAL this.otherPrimeInfos = new Array(); // Array of "OtherPrimeInfo"
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.x509.RSAPrivateKey.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                this.version = arguments[0].version || 0;
+                this.modulus = arguments[0].modulus || new in_window.org.pkijs.asn1.INTEGER();
+                this.publicExponent = arguments[0].publicExponent || new in_window.org.pkijs.asn1.INTEGER();
+                this.privateExponent = arguments[0].privateExponent || new in_window.org.pkijs.asn1.INTEGER();
+                this.prime1 = arguments[0].prime1 || new in_window.org.pkijs.asn1.INTEGER();
+                this.prime2 = arguments[0].prime2 || new in_window.org.pkijs.asn1.INTEGER();
+                this.exponent1 = arguments[0].exponent1 || new in_window.org.pkijs.asn1.INTEGER();
+                this.exponent2 = arguments[0].exponent2 || new in_window.org.pkijs.asn1.INTEGER();
+                this.coefficient = arguments[0].coefficient || new in_window.org.pkijs.asn1.INTEGER();
+                if("otherPrimeInfos" in arguments[0])
+                    this.otherPrimeInfos = arguments[0].otherPrimeInfos || new Array();
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.RSAPrivateKey.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.x509.RSAPrivateKey({
+                names: {
+                    version: "version",
+                    modulus: "modulus",
+                    publicExponent: "publicExponent",
+                    privateExponent: "privateExponent",
+                    prime1: "prime1",
+                    prime2: "prime2",
+                    exponent1: "exponent1",
+                    exponent2: "exponent2",
+                    coefficient: "coefficient",
+                    otherPrimeInfos: "otherPrimeInfos"
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for RSAPrivateKey");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema 
+        this.version = asn1.result["version"].value_block.value_dec;
+        this.modulus = asn1.result["modulus"];
+        this.publicExponent = asn1.result["publicExponent"];
+        this.privateExponent = asn1.result["privateExponent"];
+        this.prime1 = asn1.result["prime1"];
+        this.prime2 = asn1.result["prime2"];
+        this.exponent1 = asn1.result["exponent1"];
+        this.exponent2 = asn1.result["exponent2"];
+        this.coefficient = asn1.result["coefficient"];
+
+        if("otherPrimeInfos" in asn1.result)
+        {
+            var otherPrimeInfos_array = asn1.result["otherPrimeInfos"];
+
+            for(var i = 0; i < otherPrimeInfos_array.length; i++)
+                this.otherPrimeInfos.push(new in_window.org.pkijs.simpl.x509.OtherPrimeInfo({ schema: otherPrimeInfos_array[i] }));
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.RSAPrivateKey.prototype.toSchema =
+    function()
+    {
+        // #region Create array for output sequence 
+        var output_array = new Array();
+
+        output_array.push(new in_window.org.pkijs.asn1.INTEGER({ value: this.version }));
+        output_array.push(this.modulus);
+        output_array.push(this.publicExponent);
+        output_array.push(this.privateExponent);
+        output_array.push(this.prime1);
+        output_array.push(this.prime2);
+        output_array.push(this.exponent1);
+        output_array.push(this.exponent2);
+        output_array.push(this.coefficient);
+
+        if("otherPrimeInfos" in this)
+        {
+            var otherPrimeInfos_array = new Array();
+
+            for(var i = 0; i < this.otherPrimeInfos.length; i++)
+                otherPrimeInfos_array.push(this.otherPrimeInfos[i].toSchema());
+
+            output_array.push(new in_window.org.pkijs.asn1.SEQUENCE({ value: otherPrimeInfos_array }));
+        }
+        // #endregion 
+
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: output_array
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.RSAPrivateKey.prototype.toJSON =
+    function()
+    {
+        var _object = {
+            version: this.version,
+            modulus: this.modulus.toJSON(),
+            publicExponent: this.publicExponent.toJSON(),
+            privateExponent: this.privateExponent.toJSON(),
+            prime1: this.prime1.toJSON(),
+            prime2: this.prime2.toJSON(),
+            exponent1: this.exponent1.toJSON(),
+            exponent2: this.exponent2.toJSON(),
+            coefficient: this.coefficient.toJSON()
+        };
+
+        if("otherPrimeInfos" in this)
+        {
+            _object.otherPrimeInfos = new Array();
+
+            for(var i = 0; i < this.otherPrimeInfos.length; i++)
+                _object.otherPrimeInfos.push(this.otherPrimeInfos[i].toJSON());
+        }
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "RSASSA_PSS_params" type (RFC3447)
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.RSASSA_PSS_params =
+    function()
+    {
+        // #region Internal properties of the object 
+        // OPTIONAL this.hashAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER();
+        // OPTIONAL this.maskGenAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER();
+        // OPTIONAL this.saltLength = 20; // new in_window.org.pkijs.asn1.INTEGER();
+        // OPTIONAL this.trailerField = 1; // new in_window.org.pkijs.asn1.INTEGER();
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.x509.RSASSA_PSS_params.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                if("hashAlgorithm" in arguments[0])
+                    this.hashAlgorithm = arguments[0].hashAlgorithm;
+
+                if("maskGenAlgorithm" in arguments[0])
+                    this.maskGenAlgorithm = arguments[0].maskGenAlgorithm;
+
+                if("saltLength" in arguments[0])
+                    this.saltLength = arguments[0].saltLength;
+
+                if("trailerField" in arguments[0])
+                    this.trailerField = arguments[0].trailerField;
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.RSASSA_PSS_params.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.x509.RSASSA_PSS_params({
+                names: {
+                    hashAlgorithm: {
+                        names: {
+                            block_name: "hashAlgorithm"
+                        }
+                    },
+                    maskGenAlgorithm: {
+                        names: {
+                            block_name: "maskGenAlgorithm"
+                        }
+                    },
+                    saltLength: "saltLength",
+                    trailerField: "trailerField"
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for RSASSA_PSS_params");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema 
+        if("hashAlgorithm" in asn1.result)
+            this.hashAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ schema: asn1.result["hashAlgorithm"] });
+
+        if("maskGenAlgorithm" in asn1.result)
+            this.maskGenAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ schema: asn1.result["maskGenAlgorithm"] });
+
+        if("saltLength" in asn1.result)
+            this.saltLength = asn1.result["saltLength"].value_block.value_dec;
+
+        if("trailerField" in asn1.result)
+            this.trailerField = asn1.result["trailerField"].value_block.value_dec;
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.RSASSA_PSS_params.prototype.toSchema =
+    function()
+    {
+        // #region Create array for output sequence 
+        var output_array = new Array();
+
+        if("hashAlgorithm" in this)
+            output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                id_block: {
+                    tag_class: 3, // CONTEXT-SPECIFIC
+                    tag_number: 0 // [0]
+                },
+                value: [this.hashAlgorithm.toSchema()]
+            }));
+
+        if("maskGenAlgorithm" in this)
+            output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                id_block: {
+                    tag_class: 3, // CONTEXT-SPECIFIC
+                    tag_number: 1 // [1]
+                },
+                value: [this.maskGenAlgorithm.toSchema()]
+            }));
+
+        if("saltLength" in this)
+            output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                id_block: {
+                    tag_class: 3, // CONTEXT-SPECIFIC
+                    tag_number: 2 // [2]
+                },
+                value: [new in_window.org.pkijs.asn1.INTEGER({ value: this.saltLength })]
+            }));
+
+        if("trailerField" in this)
+            output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                id_block: {
+                    tag_class: 3, // CONTEXT-SPECIFIC
+                    tag_number: 3 // [3]
+                },
+                value: [new in_window.org.pkijs.asn1.INTEGER({ value: this.trailerField })]
+            }));
+        // #endregion 
+
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: output_array
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.RSASSA_PSS_params.prototype.toJSON =
+    function()
+    {
+        var _object = {};
+
+        if("hashAlgorithm" in this)
+            _object.hashAlgorithm = this.hashAlgorithm.toJSON();
+
+        if("maskGenAlgorithm" in this)
+            _object.maskGenAlgorithm = this.maskGenAlgorithm.toJSON();
+
+        if("saltLength" in this)
+            _object.saltLength = this.saltLength.toJSON();
+
+        if("trailerField" in this)
+            _object.trailerField = this.trailerField.toJSON();
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "SubjectPublicKeyInfo" type 
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.PUBLIC_KEY_INFO =
+    function()
+    {
+        // #region Internal properties of the object 
+        this.algorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER();
+        this.subjectPublicKey = new in_window.org.pkijs.asn1.BITSTRING();
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.PUBLIC_KEY_INFO.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                this.algorithm = (arguments[0].algorithm || (new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER()));
+                this.subjectPublicKey = (arguments[0].subjectPublicKey || (new in_window.org.pkijs.asn1.BITSTRING()));
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.PUBLIC_KEY_INFO.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.PUBLIC_KEY_INFO({
+                names: {
+                    algorithm: {
+                        names: {
+                            block_name: "algorithm"
+                        }
+                    },
+                    subjectPublicKey: "subjectPublicKey"
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for PUBLIC_KEY_INFO");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema 
+        this.algorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ schema: asn1.result.algorithm });
+        this.subjectPublicKey = asn1.result.subjectPublicKey;
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.PUBLIC_KEY_INFO.prototype.toSchema =
+    function()
+    {
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: [
+                this.algorithm.toSchema(),
+                this.subjectPublicKey
+            ]
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.PUBLIC_KEY_INFO.prototype.importKey =
+    function(publicKey)
+    {
+        /// <param name="publicKey" type="Key">Public key to work with</param>
+
+        // #region Initial variables 
+        var sequence = Promise.resolve();
+        var _this = this;
+        // #endregion   
+
+        // #region Initial check 
+        if(typeof publicKey === "undefined")
+            return new Promise(function(resolve, reject) { reject("Need to provide publicKey input parameter"); });
+        // #endregion 
+
+        // #region Get a "crypto" extension 
+        var crypto = in_window.org.pkijs.getCrypto();
+        if(typeof crypto == "undefined")
+            return new Promise(function(resolve, reject) { reject("Unable to create WebCrypto object"); });
+        // #endregion 
+
+        // #region Export public key 
+        sequence = sequence.then(
+            function()
+            {
+                return crypto.exportKey("spki", publicKey);
+            }
+            );
+        // #endregion 
+
+        // #region Initialize internal variables by parsing exported value
+        sequence = sequence.then(
+            function(exportedKey)
+            {
+                var asn1 = in_window.org.pkijs.fromBER(exportedKey);
+                try
+                {
+                    in_window.org.pkijs.simpl.PUBLIC_KEY_INFO.prototype.fromSchema.call(_this, asn1.result);
+                }
+                catch(exception)
+                {
+                    return new Promise(function(resolve, reject) { reject("Error during initializing object from schema"); });
+                }
+            },
+            function(error)
+            {
+                return new Promise(function(resolve, reject) { reject("Error during exporting public key: " + error); });
+            }
+            );
+        // #endregion 
+
+        return sequence;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.PUBLIC_KEY_INFO.prototype.toJSON =
+    function()
+    {
+        return {
+            algorithm: this.algorithm.toJSON(),
+            subjectPublicKey: this.subjectPublicKey.toJSON()
+        };
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "AttributeTypeAndValue" type (part of RelativeDistinguishedName)
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.ATTR_TYPE_AND_VALUE =
+    function()
+    {
+        // #region Internal properties of the object 
+        this.type = "";
+        this.value = {}; // ANY -- DEFINED BY AttributeType
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.ATTR_TYPE_AND_VALUE.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                this.type = (arguments[0].type || "");
+                this.value = (arguments[0].value || {});
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.ATTR_TYPE_AND_VALUE.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.ATTR_TYPE_AND_VALUE({
+                names: {
+                    type: "type",
+                    value: "typeValue"
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for ATTR_TYPE_AND_VALUE");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema 
+        this.type = asn1.result.type.value_block.toString();
+        this.value = asn1.result.typeValue;
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.ATTR_TYPE_AND_VALUE.prototype.toSchema =
+    function()
+    {
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: [
+                new in_window.org.pkijs.asn1.OID({ value: this.type }),
+                this.value
+            ]
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.ATTR_TYPE_AND_VALUE.prototype.isEqual =
+    function()
+    {
+        if(arguments[0] instanceof in_window.org.pkijs.simpl.ATTR_TYPE_AND_VALUE)
+        {
+            if(this.type !== arguments[0].type)
+                return false;
+
+            if(((this.value instanceof in_window.org.pkijs.asn1.UTF8STRING) && (arguments[0].value instanceof in_window.org.pkijs.asn1.UTF8STRING)) ||
+               ((this.value instanceof in_window.org.pkijs.asn1.BMPSTRING) && (arguments[0].value instanceof in_window.org.pkijs.asn1.BMPSTRING)) ||
+               ((this.value instanceof in_window.org.pkijs.asn1.UNIVERSALSTRING) && (arguments[0].value instanceof in_window.org.pkijs.asn1.UNIVERSALSTRING)) ||
+               ((this.value instanceof in_window.org.pkijs.asn1.NUMERICSTRING) && (arguments[0].value instanceof in_window.org.pkijs.asn1.NUMERICSTRING)) ||
+               ((this.value instanceof in_window.org.pkijs.asn1.PRINTABLESTRING) && (arguments[0].value instanceof in_window.org.pkijs.asn1.PRINTABLESTRING)) ||
+               ((this.value instanceof in_window.org.pkijs.asn1.TELETEXSTRING) && (arguments[0].value instanceof in_window.org.pkijs.asn1.TELETEXSTRING)) ||
+               ((this.value instanceof in_window.org.pkijs.asn1.VIDEOTEXSTRING) && (arguments[0].value instanceof in_window.org.pkijs.asn1.VIDEOTEXSTRING)) ||
+               ((this.value instanceof in_window.org.pkijs.asn1.IA5STRING) && (arguments[0].value instanceof in_window.org.pkijs.asn1.IA5STRING)) ||
+               ((this.value instanceof in_window.org.pkijs.asn1.GRAPHICSTRING) && (arguments[0].value instanceof in_window.org.pkijs.asn1.GRAPHICSTRING)) ||
+               ((this.value instanceof in_window.org.pkijs.asn1.VISIBLESTRING) && (arguments[0].value instanceof in_window.org.pkijs.asn1.VISIBLESTRING)) ||
+               ((this.value instanceof in_window.org.pkijs.asn1.GENERALSTRING) && (arguments[0].value instanceof in_window.org.pkijs.asn1.GENERALSTRING)) ||
+               ((this.value instanceof in_window.org.pkijs.asn1.CHARACTERSTRING) && (arguments[0].value instanceof in_window.org.pkijs.asn1.CHARACTERSTRING)))
+            {
+                var value1 = in_window.org.pkijs.stringPrep(this.value.value_block.value);
+                var value2 = in_window.org.pkijs.stringPrep(arguments[0].value.value_block.value);
+
+                if(value1.localeCompare(value2) !== 0)
+                    return false;
+            }
+            else // Comparing as two ArrayBuffers
+            {
+                if(in_window.org.pkijs.isEqual_buffer(this.value.value_before_decode, arguments[0].value.value_before_decode) === false)
+                    return false;
+            }
+
+            return true;
+        }
+        else
+            return false;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.ATTR_TYPE_AND_VALUE.prototype.toJSON =
+    function()
+    {
+        var _object = {
+            type: this.type
+        };
+
+        if(Object.keys(this.value).length !== 0)
+            _object.value = this.value.toJSON();
+        else
+            _object.value = this.value;
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "RelativeDistinguishedName" type
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.RDN =
+    function()
+    {
+        // #region Internal properties of the object 
+        /// <field name="types_and_values" type="Array" elementType="in_window.org.pkijs.simpl.ATTR_TYPE_AND_VALUE">Array of "type and value" objects</field>
+        this.types_and_values = new Array();
+        /// <field name="value_before_decode" type="ArrayBuffer">Value of the RDN before decoding from schema</field>
+        this.value_before_decode = new ArrayBuffer(0);
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.RDN.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                this.types_and_values = (arguments[0].types_and_values || (new Array()));
+                this.value_before_decode = arguments[0].value_before_decode || new ArrayBuffer(0);
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.RDN.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.RDN({
+                names: {
+                    block_name: "RDN",
+                    repeated_set: "types_and_values"
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for RDN");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema 
+        if("types_and_values" in asn1.result) // Could be a case when there is no "types and values"
+        {
+            var types_and_values_array = asn1.result.types_and_values;
+            for(var i = 0; i < types_and_values_array.length; i++)
+                this.types_and_values.push(new in_window.org.pkijs.simpl.ATTR_TYPE_AND_VALUE({ schema: types_and_values_array[i] }));
+        }
+
+        this.value_before_decode = asn1.result.RDN.value_before_decode;
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.RDN.prototype.toSchema =
+    function()
+    {
+        // #region Decode stored TBS value 
+        if(this.value_before_decode.byteLength === 0) // No stored encoded array, create "from scratch"
+        {
+            // #region Create array for output set 
+            var output_array = new Array();
+
+            for(var i = 0; i < this.types_and_values.length; i++)
+                output_array.push(this.types_and_values[i].toSchema());
+            // #endregion 
+
+            return (new in_window.org.pkijs.asn1.SEQUENCE({
+                value: [new in_window.org.pkijs.asn1.SET({ value: output_array })]
+            }));
+        }
+
+        var asn1 = in_window.org.pkijs.fromBER(this.value_before_decode);
+        // #endregion 
+
+        // #region Construct and return new ASN.1 schema for this object 
+        return asn1.result;
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.RDN.prototype.isEqual =
+    function()
+    {
+        if(arguments[0] instanceof in_window.org.pkijs.simpl.RDN)
+        {
+            if(this.types_and_values.length != arguments[0].types_and_values.length)
+                return false;
+
+            for(var i = 0; i < this.types_and_values.length; i++)
+            {
+                if(this.types_and_values[i].isEqual(arguments[0].types_and_values[i]) === false)
+                    return false;
+            }
+
+            return true;
+        }
+        else
+        {
+            if(arguments[0] instanceof ArrayBuffer)
+                return in_window.org.pkijs.isEqual_buffer(this.value_before_decode, arguments[0]);
+            else
+                return false;
+        }
+
+        return false;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.RDN.prototype.toJSON =
+    function()
+    {
+        var _object = {
+            types_and_values: new Array()
+        };
+
+        for(var i = 0; i < this.types_and_values.length; i++)
+            _object.types_and_values.push(this.types_and_values[i].toJSON());
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "AuthorityKeyIdentifier" type of extension
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.AuthorityKeyIdentifier =
+    function()
+    {
+        // #region Internal properties of the object 
+        // OPTIONAL this.keyIdentifier - OCTETSTRING
+        // OPTIONAL this.authorityCertIssuer - Array of GeneralName
+        // OPTIONAL this.authorityCertSerialNumber - INTEGER
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.x509.AuthorityKeyIdentifier.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                if("keyIdentifier" in arguments[0])
+                    this.keyIdentifier = arguments[0].keyIdentifier;
+
+                if("authorityCertIssuer" in arguments[0])
+                    this.authorityCertIssuer = arguments[0].authorityCertIssuer;
+
+                if("authorityCertSerialNumber" in arguments[0])
+                    this.authorityCertSerialNumber = arguments[0].authorityCertSerialNumber;
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.AuthorityKeyIdentifier.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.x509.AuthorityKeyIdentifier({
+                names: {
+                    keyIdentifier: "keyIdentifier",
+                    authorityCertIssuer: "authorityCertIssuer",
+                    authorityCertSerialNumber: "authorityCertSerialNumber"
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for AuthorityKeyIdentifier");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema 
+        if("keyIdentifier" in asn1.result)
+        {
+            asn1.result["keyIdentifier"].id_block.tag_class = 1; // UNIVERSAL
+            asn1.result["keyIdentifier"].id_block.tag_number = 4; // OCTETSTRING
+
+            this.keyIdentifier = asn1.result["keyIdentifier"];
+        }
+
+        if("authorityCertIssuer" in asn1.result)
+        {
+            this.authorityCertIssuer = new Array();
+            var issuer_array = asn1.result["authorityCertIssuer"];
+
+            for(var i = 0; i < issuer_array.length; i++)
+                this.authorityCertIssuer.push(new in_window.org.pkijs.simpl.GENERAL_NAME({ schema: issuer_array[i] }));
+        }
+
+        if("authorityCertSerialNumber" in asn1.result)
+        {
+            asn1.result["authorityCertSerialNumber"].id_block.tag_class = 1; // UNIVERSAL
+            asn1.result["authorityCertSerialNumber"].id_block.tag_number = 2; // INTEGER
+
+            this.authorityCertSerialNumber = asn1.result["authorityCertSerialNumber"];
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.AuthorityKeyIdentifier.prototype.toSchema =
+    function()
+    {
+        // #region Create array for output sequence 
+        var output_array = new Array();
+
+        if("keyIdentifier" in this)
+        {
+            var value = this.keyIdentifier;
+
+            value.id_block.tag_class = 3; // CONTEXT-SPECIFIC
+            value.id_block.tag_number = 0; // [0]
+
+            output_array.push(value);
+        }
+
+        if("authorityCertIssuer" in this)
+        {
+            var issuer_array = new Array();
+
+            for(var i = 0; i < this.authorityCertIssuer.length; i++)
+                issuer_array.push(this.authorityCertIssuer[i].toSchema());
+
+            output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                optional: true,
+                id_block: {
+                    tag_class: 3, // CONTEXT-SPECIFIC
+                    tag_number: 1 // [1]
+                },
+                value: [new in_window.org.pkijs.asn1.SEQUENCE({
+                    value: issuer_array
+                })]
+            }));
+        }
+
+        if("authorityCertSerialNumber" in this)
+        {
+            var value = this.authorityCertSerialNumber;
+
+            value.id_block.tag_class = 3; // CONTEXT-SPECIFIC
+            value.id_block.tag_number = 2; // [2]
+
+            output_array.push(value);
+        }
+        // #endregion 
+
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: output_array
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.AuthorityKeyIdentifier.prototype.toJSON =
+    function()
+    {
+        var _object = {};
+
+        if("keyIdentifier" in this)
+            _object.keyIdentifier = this.keyIdentifier.toJSON();
+
+        if("authorityCertIssuer" in this)
+        {
+            _object.authorityCertIssuer = new Array();
+
+            for(var i = 0; i < this.authorityCertIssuer.length; i++)
+                _object.authorityCertIssuer.push(this.authorityCertIssuer[i].toJSON());
+        }
+
+        if("authorityCertSerialNumber" in this)
+            _object.authorityCertSerialNumber = this.authorityCertSerialNumber.toJSON();
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "PrivateKeyUsagePeriod" type of extension
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.PrivateKeyUsagePeriod =
+    function()
+    {
+        // #region Internal properties of the object 
+        // OPTIONAL this.notBefore - new Date()
+        // OPTIONAL this.notAfter - new Date()
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.x509.PrivateKeyUsagePeriod.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                if("notBefore" in arguments[0])
+                    this.notBefore = arguments[0].notBefore;
+
+                if("notAfter" in arguments[0])
+                    this.notAfter = arguments[0].notAfter;
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.PrivateKeyUsagePeriod.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.x509.PrivateKeyUsagePeriod({
+                names: {
+                    notBefore: "notBefore",
+                    notAfter: "notAfter"
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for PrivateKeyUsagePeriod");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema 
+        if("notBefore" in asn1.result)
+        {
+            var localNotBefore = new in_window.org.pkijs.asn1.GENERALIZEDTIME();
+            localNotBefore.fromBuffer(asn1.result["notBefore"].value_block.value_hex);
+            this.notBefore = localNotBefore.toDate();
+        }
+
+        if("notAfter" in asn1.result)
+        {
+            var localNotAfter = new in_window.org.pkijs.asn1.GENERALIZEDTIME({ value_hex: asn1.result["notAfter"].value_block.value_hex });
+            localNotAfter.fromBuffer(asn1.result["notAfter"].value_block.value_hex);
+            this.notAfter = localNotAfter.toDate();
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.PrivateKeyUsagePeriod.prototype.toSchema =
+    function()
+    {
+        // #region Create array for output sequence 
+        var output_array = new Array();
+
+        if("notBefore" in this)
+            output_array.push(new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                id_block: {
+                    tag_class: 3, // CONTEXT-SPECIFIC
+                    tag_number: 0 // [0]
+                },
+                value_hex: (new in_window.org.pkijs.asn1.GENERALIZEDTIME({ value_date: this.notBefore })).value_block.value_hex
+            }));
+
+        if("notAfter" in this)
+            output_array.push(new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                id_block: {
+                    tag_class: 3, // CONTEXT-SPECIFIC
+                    tag_number: 1 // [1]
+                },
+                value_hex: (new in_window.org.pkijs.asn1.GENERALIZEDTIME({ value_date: this.notAfter })).value_block.value_hex
+            }));
+        // #endregion 
+
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: output_array
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.PrivateKeyUsagePeriod.prototype.toJSON =
+    function()
+    {
+        var _object = {};
+
+        if("notBefore" in this)
+            _object.notBefore = this.notBefore;
+
+        if("notAfter" in this)
+            _object.notAfter = this.notAfter;
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "IssuerAltName" and "SubjectAltName" types of extension
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.AltName =
+    function()
+    {
+        // #region Internal properties of the object 
+        this.altNames = new Array(); //Array of GeneralName
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.x509.AltName.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                this.altNames = arguments[0].altNames || new Array();
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.AltName.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.x509.AltName({
+                names: {
+                    altNames: "altNames"
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for AltName");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema 
+        if("altNames" in asn1.result)
+        {
+            var altNames_array = asn1.result["altNames"];
+
+            for(var i = 0; i < altNames_array.length; i++)
+                this.altNames.push(new in_window.org.pkijs.simpl.GENERAL_NAME({ schema: altNames_array[i] }));
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.AltName.prototype.toSchema =
+    function()
+    {
+        // #region Create array for output sequence 
+        var output_array = new Array();
+
+        for(var i = 0; i < this.altNames.length; i++)
+            output_array.push(this.altNames[i].toSchema());
+        // #endregion 
+
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: output_array
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.AltName.prototype.toJSON =
+    function()
+    {
+        var _object = {
+            altNames: new Array()
+        };
+
+        for(var i = 0; i < this.altNames.length; i++)
+            _object.altNames.push(this.altNames[i].toJSON());
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "SubjectDirectoryAttributes" type of extension
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.SubjectDirectoryAttributes =
+    function()
+    {
+        // #region Internal properties of the object 
+        this.attributes = new Array(); // Array of "simpl.ATTRIBUTE"
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.x509.SubjectDirectoryAttributes.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                this.attributes = arguments[0].attributes || new Array(); // Array of "simpl.ATTRIBUTE"
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.SubjectDirectoryAttributes.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.x509.SubjectDirectoryAttributes({
+                names: {
+                    attributes: "attributes"
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for SubjectDirectoryAttributes");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema
+        var attrs = asn1.result["attributes"];
+
+        for(var i = 0; i < attrs.length; i++)
+            this.attributes.push(new in_window.org.pkijs.simpl.ATTRIBUTE({ schema: attrs[i] }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.SubjectDirectoryAttributes.prototype.toSchema =
+    function()
+    {
+        // #region Create array for output sequence 
+        var output_array = new Array();
+
+        for(var i = 0; i < this.attributes.length; i++)
+            output_array.push(this.attributes[i].toSchema());
+        // #endregion 
+
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: output_array
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.SubjectDirectoryAttributes.prototype.toJSON =
+    function()
+    {
+        var _object = {
+            attributes: new Array()
+        };
+
+        for(var i = 0; i < this.attributes.length; i++)
+            _object.attributes.push(this.attributes[i].toJSON());
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "PolicyMapping" type
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.PolicyMapping =
+    function()
+    {
+        // #region Internal properties of the object 
+        this.issuerDomainPolicy = "";
+        this.subjectDomainPolicy = "";
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.x509.PolicyMapping.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                this.issuerDomainPolicy = arguments[0].issuerDomainPolicy || "";
+                this.subjectDomainPolicy = arguments[0].subjectDomainPolicy || "";
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.PolicyMapping.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.x509.PolicyMapping({
+                names: {
+                    issuerDomainPolicy: "issuerDomainPolicy",
+                    subjectDomainPolicy: "subjectDomainPolicy"
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for PolicyMapping");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema
+        this.issuerDomainPolicy = asn1.result["issuerDomainPolicy"].value_block.toString();
+        this.subjectDomainPolicy = asn1.result["subjectDomainPolicy"].value_block.toString();
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.PolicyMapping.prototype.toSchema =
+    function()
+    {
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: [
+                new in_window.org.pkijs.asn1.OID({ value: this.issuerDomainPolicy }),
+                new in_window.org.pkijs.asn1.OID({ value: this.subjectDomainPolicy })
+            ]
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.PolicyMapping.prototype.toJSON =
+    function()
+    {
+        return {
+            issuerDomainPolicy: this.issuerDomainPolicy,
+            subjectDomainPolicy: this.subjectDomainPolicy
+        };
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "PolicyMappings" type of extension
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.PolicyMappings =
+    function()
+    {
+        // #region Internal properties of the object 
+        this.mappings = new Array(); // Array of "simpl.x509.PolicyMapping"
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.x509.PolicyMappings.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                this.mappings = arguments[0].mappings || new Array();
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.PolicyMappings.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.x509.PolicyMappings({
+                names: {
+                    mappings: "mappings"
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for PolicyMappings");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema 
+        var maps = asn1.result["mappings"];
+
+        for(var i = 0; i < maps.length; i++)
+            this.mappings.push(new in_window.org.pkijs.simpl.x509.PolicyMapping({ schema: maps[i] }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.PolicyMappings.prototype.toSchema =
+    function()
+    {
+        // #region Create array for output sequence 
+        var output_array = new Array();
+
+        for(var i = 0; i < this.mappings.length; i++)
+            output_array.push(this.mappings.toSchema());
+        // #endregion 
+
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: output_array
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.PolicyMappings.prototype.toJSON =
+    function()
+    {
+        var _object = {
+            mappings: new Array()
+        };
+
+        for(var i = 0; i < this.mappings.length; i++)
+            _object.mappings.push(this.mappings[i].toJSON());
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "GeneralSubtree" type
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.GeneralSubtree =
+    function()
+    {
+        // #region Internal properties of the object 
+        this.base = new in_window.org.pkijs.simpl.GENERAL_NAME();
+        // OPTIONAL this.minimum // in_window.org.pkijs.asn1.INTEGER
+        // OPTIONAL this.maximum // in_window.org.pkijs.asn1.INTEGER
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.x509.GeneralSubtree.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                this.base = arguments[0].base || new in_window.org.pkijs.simpl.GENERAL_NAME();
+
+                if("minimum" in arguments[0])
+                    this.minimum = arguments[0].minimum;
+
+                if("maximum" in arguments[0])
+                    this.maximum = arguments[0].maximum;
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.GeneralSubtree.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.x509.GeneralSubtree({
+                names: {
+                    base: {
+                        names: {
+                            block_name: "base"
+                        }
+                    },
+                    minimum: "minimum",
+                    maximum: "maximum"
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for ");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema 
+        this.base = new in_window.org.pkijs.simpl.GENERAL_NAME({ schema: asn1.result["base"] });
+
+        if("minimum" in asn1.result)
+        {
+            if(asn1.result["minimum"].value_block.is_hex_only)
+                this.minimum = asn1.result["minimum"];
+            else
+                this.minimum = asn1.result["minimum"].value_block.value_dec;
+        }
+
+        if("maximum" in asn1.result)
+        {
+            if(asn1.result["maximum"].value_block.is_hex_only)
+                this.maximum = asn1.result["maximum"];
+            else
+                this.maximum = asn1.result["maximum"].value_block.value_dec;
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.GeneralSubtree.prototype.toSchema =
+    function()
+    {
+        // #region Create array for output sequence 
+        var output_array = new Array();
+
+        output_array.push(this.base.toSchema());
+
+        if("minimum" in this)
+        {
+            var value_minimum = 0;
+
+            if(this.minimum instanceof in_window.org.pkijs.asn1.INTEGER)
+                value_minimum = this.minimum;
+            else
+                value_minimum = new in_window.org.pkijs.asn1.INTEGER({ value: this.minimum });
+
+            output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                optional: true,
+                id_block: {
+                    tag_class: 3, // CONTEXT-SPECIFIC
+                    tag_number: 0 // [0]
+                },
+                value: [value_minimum]
+            }));
+        }
+
+        if("maximum" in this)
+        {
+            var value_maximum = 0;
+
+            if(this.maximum instanceof in_window.org.pkijs.asn1.INTEGER)
+                value_maximum = this.maximum;
+            else
+                value_maximum = new in_window.org.pkijs.asn1.INTEGER({ value: this.maximum });
+
+            output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                optional: true,
+                id_block: {
+                    tag_class: 3, // CONTEXT-SPECIFIC
+                    tag_number: 1 // [1]
+                },
+                value: [value_maximum]
+            }));
+        }
+        // #endregion 
+
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: output_array
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.GeneralSubtree.prototype.toJSON =
+    function()
+    {
+        var _object = {
+            base: this.base.toJSON()
+        };
+
+        if("minimum" in this)
+        {
+            if((typeof this.minimum) === "number")
+                _object.minimum = this.minimum;
+            else
+                _object.minimum = this.minimum.toJSON();
+        }
+
+        if("maximum" in this)
+        {
+            if((typeof this.maximum) === "number")
+                _object.maximum = this.maximum;
+            else
+                _object.maximum = this.maximum.toJSON();
+        }
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "NameConstraints" type of extension
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.NameConstraints =
+    function()
+    {
+        // #region Internal properties of the object 
+        // OPTIONAL this.permittedSubtrees - Array of "simpl.x509.GeneralSubtree"
+        // OPTIONAL this.excludedSubtrees - Array of "simpl.x509.GeneralSubtree"
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.x509.NameConstraints.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                if("permittedSubtrees" in arguments[0])
+                    this.permittedSubtrees = arguments[0].permittedSubtrees;
+
+                if("excludedSubtrees" in arguments[0])
+                    this.excludedSubtrees = arguments[0].excludedSubtrees;
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.NameConstraints.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.x509.NameConstraints({
+                names: {
+                    permittedSubtrees: "permittedSubtrees",
+                    excludedSubtrees: "excludedSubtrees"
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for NameConstraints");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema 
+        if("permittedSubtrees" in asn1.result)
+        {
+            this.permittedSubtrees = new Array();
+            var permited_array = asn1.result["permittedSubtrees"];
+
+            for(var i = 0; i < permited_array.length; i++)
+                this.permittedSubtrees.push(new in_window.org.pkijs.simpl.x509.GeneralSubtree({ schema: permited_array[i] }));
+        }
+
+        if("excludedSubtrees" in asn1.result)
+        {
+            this.excludedSubtrees = new Array();
+            var excluded_array = asn1.result["excludedSubtrees"];
+
+            for(var i = 0; i < excluded_array.length; i++)
+                this.excludedSubtrees.push(new in_window.org.pkijs.simpl.x509.GeneralSubtree({ schema: excluded_array[i] }));
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.NameConstraints.prototype.toSchema =
+    function()
+    {
+        // #region Create array for output sequence 
+        var output_array = new Array();
+
+        if("permittedSubtrees" in this)
+        {
+            var permited_array = new Array();
+
+            for(var i = 0; i < this.permittedSubtrees.length; i++)
+                permited_array.push(this.permittedSubtrees[i].toSchema());
+
+            output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                optional: true,
+                id_block: {
+                    tag_class: 3, // CONTEXT-SPECIFIC
+                    tag_number: 0 // [0]
+                },
+                value: [new in_window.org.pkijs.asn1.SEQUENCE({
+                    value: permited_array
+                })]
+            }));
+        }
+
+        if("excludedSubtrees" in this)
+        {
+            var excluded_array = new Array();
+
+            for(var i = 0; i < this.excludedSubtrees.length; i++)
+                excluded_array.push(this.excludedSubtrees[i].toSchema());
+
+            output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                optional: true,
+                id_block: {
+                    tag_class: 3, // CONTEXT-SPECIFIC
+                    tag_number: 1 // [1]
+                },
+                value: [new in_window.org.pkijs.asn1.SEQUENCE({
+                    value: excluded_array
+                })]
+            }));
+        }
+        // #endregion 
+
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: output_array
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.NameConstraints.prototype.toJSON =
+    function()
+    {
+        var _object = {};
+
+        if("permittedSubtrees" in this)
+        {
+            _object.permittedSubtrees = new Array();
+
+            for(var i = 0; i < this.permittedSubtrees.length; i++)
+                _object.permittedSubtrees.push(this.permittedSubtrees[i].toJSON());
+        }
+
+        if("excludedSubtrees" in this)
+        {
+            _object.excludedSubtrees = new Array();
+
+            for(var i = 0; i < this.excludedSubtrees.length; i++)
+                _object.excludedSubtrees.push(this.excludedSubtrees[i].toJSON());
+        }
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "BasicConstraints" type of extension
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.BasicConstraints =
+    function()
+    {
+        // #region Internal properties of the object 
+        // OPTIONAL this.cA - boolean value
+        // OPTIONAL this.pathLenConstraint - integer value
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.x509.BasicConstraints.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                if("cA" in arguments[0])
+                    this.cA = arguments[0].cA;
+
+                if("pathLenConstraint" in arguments[0])
+                    this.pathLenConstraint = arguments[0].pathLenConstraint;
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.BasicConstraints.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.x509.BasicConstraints({
+                names: {
+                    cA: "cA",
+                    pathLenConstraint: "pathLenConstraint"
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for BasicConstraints");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema 
+        if("cA" in asn1.result)
+            this.cA = asn1.result["cA"].value_block.value;
+
+        if("pathLenConstraint" in asn1.result)
+            this.pathLenConstraint = asn1.result["pathLenConstraint"].value_block.value_dec;
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.BasicConstraints.prototype.toSchema =
+    function()
+    {
+        // #region Create array for output sequence 
+        var output_array = new Array();
+
+        if("cA" in this)
+            output_array.push(new in_window.org.pkijs.asn1.BOOLEAN({ value: this.cA }));
+
+        if("pathLenConstraint" in this)
+            output_array.push(new in_window.org.pkijs.asn1.INTEGER({ value: this.pathLenConstraint }));
+        // #endregion 
+
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: output_array
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.BasicConstraints.prototype.toJSON =
+    function()
+    {
+        var _object = {};
+
+        if("cA" in this)
+            _object.cA = this.cA;
+
+        if("pathLenConstraint" in this)
+            _object.pathLenConstraint = this.pathLenConstraint;
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "PolicyQualifierInfo" type
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.PolicyQualifierInfo =
+    function()
+    {
+        // #region Internal properties of the object 
+        this.policyQualifierId = "";
+        this.qualifier = new in_window.org.pkijs.asn1.ANY();
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.x509.PolicyQualifierInfo.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                this.policyQualifierId = arguments[0].policyQualifierId || "";
+                this.qualifier = arguments[0].qualifier || new in_window.org.pkijs.asn1.ANY();
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.PolicyQualifierInfo.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.x509.PolicyQualifierInfo({
+                names: {
+                    policyQualifierId: "policyQualifierId",
+                    qualifier: "qualifier"
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for PolicyQualifierInfo");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema 
+        this.policyQualifierId = asn1.result["policyQualifierId"].value_block.toString();
+        this.qualifier = asn1.result["qualifier"];
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.PolicyQualifierInfo.prototype.toSchema =
+    function()
+    {
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: [
+                new in_window.org.pkijs.asn1.OID({ value: this.policyQualifierId }),
+                this.qualifier
+            ]
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.PolicyQualifierInfo.prototype.toJSON =
+    function()
+    {
+        return {
+            policyQualifierId: this.policyQualifierId,
+            qualifier: this.qualifier.toJSON()
+        };
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "PolicyInformation" type
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.PolicyInformation =
+    function()
+    {
+        // #region Internal properties of the object 
+        this.policyIdentifier = "";
+        // OPTIONAL this.policyQualifiers = new Array(); // Array of "simpl.x509.PolicyQualifierInfo"
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.x509.PolicyInformation.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                this.policyIdentifier = arguments[0].policyIdentifier || "";
+
+                if("policyQualifiers" in arguments[0])
+                    this.policyQualifiers = arguments[0].policyQualifiers;
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.PolicyInformation.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.x509.PolicyInformation({
+                names: {
+                    policyIdentifier: "policyIdentifier",
+                    policyQualifiers: "policyQualifiers"
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for PolicyInformation");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema 
+        this.policyIdentifier = asn1.result["policyIdentifier"].value_block.toString();
+
+        if("policyQualifiers" in asn1.result)
+        {
+            this.policyQualifiers = new Array();
+            var qualifiers = asn1.result["policyQualifiers"];
+
+            for(var i = 0; i < qualifiers.length; i++)
+                this.policyQualifiers.push(new in_window.org.pkijs.simpl.x509.PolicyQualifierInfo({ schema: qualifiers[i] }));
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.PolicyInformation.prototype.toSchema =
+    function()
+    {
+        // #region Create array for output sequence 
+        var output_array = new Array();
+
+        output_array.push(new in_window.org.pkijs.asn1.OID({ value: this.policyIdentifier }));
+
+        if("policyQualifiers" in this)
+        {
+            var qualifiers = new Array();
+
+            for(var i = 0; i < this.policyQualifiers.length; i++)
+                qualifiers.push(this.policyQualifiers[i].toSchema());
+
+            output_array.push(new in_window.org.pkijs.asn1.SEQUENCE({
+                value: qualifiers
+            }));
+        }
+        // #endregion 
+
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: output_array
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.PolicyInformation.prototype.toJSON =
+    function()
+    {
+        var _object = {
+            policyIdentifier: this.policyIdentifier
+        };
+
+        if("policyQualifiers" in this)
+        {
+            _object.policyQualifiers = new Array();
+
+            for(var i = 0; i < this.policyQualifiers.length; i++)
+                _object.policyQualifiers.push(this.policyQualifiers[i].toJSON());
+        }
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "CertificatePolicies" type of extension
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.CertificatePolicies =
+    function()
+    {
+        // #region Internal properties of the object 
+        this.certificatePolicies = new Array(); // Array of "simpl.x509.PolicyInformation"
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.x509.CertificatePolicies.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                this.certificatePolicies = arguments[0].certificatePolicies || new Array(); // Array of "simpl.x509.PolicyInformation"
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.CertificatePolicies.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.x509.CertificatePolicies({
+                names: {
+                    certificatePolicies: "certificatePolicies"
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for CertificatePolicies");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema
+        var policies = asn1.result["certificatePolicies"];
+
+        for(var i = 0; i < policies.length; i++)
+            this.certificatePolicies.push(new in_window.org.pkijs.simpl.x509.PolicyInformation({ schema: policies[i] }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.CertificatePolicies.prototype.toSchema =
+    function()
+    {
+        // #region Create array for output sequence 
+        var output_array = new Array();
+
+        for(var i = 0; i < this.certificatePolicies.length; i++)
+            output_array.push(this.certificatePolicies[i].toSchema());
+        // #endregion 
+
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: output_array
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.CertificatePolicies.prototype.toJSON =
+    function()
+    {
+        var _object = {
+            certificatePolicies: new Array()
+        };
+
+        for(var i = 0; i < this.certificatePolicies.length; i++)
+            _object.certificatePolicies.push(this.certificatePolicies[i].toJSON());
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "PolicyConstraints" type of extension
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.PolicyConstraints =
+    function()
+    {
+        // #region Internal properties of the object 
+        // OPTIONAL this.requireExplicitPolicy = 0;
+        // OPTIONAL this.inhibitPolicyMapping = 0;
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.x509.PolicyConstraints.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                this.requireExplicitPolicy = arguments[0].requireExplicitPolicy || 0;
+                this.inhibitPolicyMapping = arguments[0].inhibitPolicyMapping || 0;
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.PolicyConstraints.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.x509.PolicyConstraints({
+                names: {
+                    requireExplicitPolicy: "requireExplicitPolicy",
+                    inhibitPolicyMapping: "inhibitPolicyMapping"
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for PolicyConstraints");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema
+        if("requireExplicitPolicy" in asn1.result)
+        {
+            var field1 = asn1.result["requireExplicitPolicy"];
+
+            field1.id_block.tag_class = 1; // UNIVERSAL
+            field1.id_block.tag_number = 2; // INTEGER
+
+            var ber1 = field1.toBER(false);
+            var int1 = in_window.org.pkijs.fromBER(ber1);
+
+            this.requireExplicitPolicy = int1.result.value_block.value_dec;
+        }
+
+        if("inhibitPolicyMapping" in asn1.result)
+        {
+            var field2 = asn1.result["inhibitPolicyMapping"];
+
+            field2.id_block.tag_class = 1; // UNIVERSAL
+            field2.id_block.tag_number = 2; // INTEGER
+
+            var ber2 = field2.toBER(false);
+            var int2 = in_window.org.pkijs.fromBER(ber2);
+
+            this.inhibitPolicyMapping = int2.result.value_block.value_dec;
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.PolicyConstraints.prototype.toSchema =
+    function()
+    {
+        // #region Create correct values for output sequence 
+        var output_array = new Array();
+
+        if("requireExplicitPolicy" in this)
+        {
+            var int1 = new in_window.org.pkijs.asn1.INTEGER({ value: this.requireExplicitPolicy });
+
+            int1.id_block.tag_class = 3; // CONTEXT-SPECIFIC
+            int1.id_block.tag_number = 0; // [0]
+
+            output_array.push(int1);
+        }
+
+        if("inhibitPolicyMapping" in this)
+        {
+            var int2 = new in_window.org.pkijs.asn1.INTEGER({ value: this.inhibitPolicyMapping });
+
+            int1.id_block.tag_class = 3; // CONTEXT-SPECIFIC
+            int1.id_block.tag_number = 1; // [1]
+
+            output_array.push(int2);
+        }
+        // #endregion 
+
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: output_array
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.PolicyConstraints.prototype.toJSON =
+    function()
+    {
+        var _object = {};
+
+        if("requireExplicitPolicy" in this)
+            _object.requireExplicitPolicy = this.requireExplicitPolicy;
+
+        if("inhibitPolicyMapping" in this)
+            _object.inhibitPolicyMapping = this.inhibitPolicyMapping;
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "ExtKeyUsage" type of extension
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.ExtKeyUsage =
+    function()
+    {
+        // #region Internal properties of the object 
+        this.keyPurposes = new Array(); // Array of strings (OIDs value for key purposes)
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.x509.ExtKeyUsage.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                this.keyPurposes = arguments[0].keyPurposes || new Array(); // Array of strings (OIDs value for key purposes)
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.ExtKeyUsage.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.x509.ExtKeyUsage({
+                names: {
+                    keyPurposes: "keyPurposes"
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for ExtKeyUsage");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema 
+        var purposes = asn1.result["keyPurposes"];
+
+        for(var i = 0; i < purposes.length; i++)
+            this.keyPurposes.push(purposes[i].value_block.toString());
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.ExtKeyUsage.prototype.toSchema =
+    function()
+    {
+        // #region Create array for output sequence 
+        var output_array = new Array();
+
+        for(var i = 0; i < this.keyPurposes.length; i++)
+            output_array.push(new in_window.org.pkijs.asn1.OID({ value: this.keyPurposes[i] }));
+        // #endregion 
+
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: output_array
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.ExtKeyUsage.prototype.toJSON =
+    function()
+    {
+        var _object = {
+            keyPurposes: new Array()
+        };
+
+        for(var i = 0; i < this.keyPurposes.length; i++)
+            _object.keyPurposes.push(this.keyPurposes[i]);
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "DistributionPoint" type
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.DistributionPoint =
+    function()
+    {
+        // #region Internal properties of the object 
+        // OPTIONAL this.distributionPoint // Array of "simpl.GENERAL_NAME" or a value of "simpl.RDN" type
+        // OPTIONAL this.reasons // BITSTRING value
+        // OPTIONAL this.cRLIssuer // Array of "simpl.GENERAL_NAME"
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.x509.DistributionPoint.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                if("distributionPoint" in arguments[0])
+                    this.distributionPoint = arguments[0].distributionPoint;
+
+                if("reasons" in arguments[0])
+                    this.reasons = arguments[0].reasons;
+
+                if("cRLIssuer" in arguments[0])
+                    this.cRLIssuer = arguments[0].cRLIssuer;
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.DistributionPoint.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.x509.DistributionPoint({
+                names: {
+                    distributionPoint: "distributionPoint",
+                    distributionPoint_names: "distributionPoint_names",
+                    reasons: "reasons",
+                    cRLIssuer: "cRLIssuer",
+                    cRLIssuer_names: "cRLIssuer_names"
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for DistributionPoint");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema 
+        if("distributionPoint" in asn1.result)
+        {
+            if(asn1.result["distributionPoint"].id_block.tag_number == 0) // GENERAL_NAMES variant
+            {
+                this.distributionPoint = new Array();
+                var names = asn1.result["distributionPoint_names"];
+
+                for(var i = 0; i < names.length; i++)
+                    this.distributionPoint.push(new in_window.org.pkijs.simpl.GENERAL_NAME({ schema: names[i] }));
+            }
+
+            if(asn1.result["distributionPoint"].id_block.tag_number == 1) // RDN variant
+            {
+                asn1.result["distributionPoint"].id_block.tag_class = 1; // UNIVERSAL
+                asn1.result["distributionPoint"].id_block.tag_number = 16; // SEQUENCE
+
+                this.distributionPoint = new in_window.org.pkijs.simpl.RDN({ schema: asn1.result["distributionPoint"] });
+            }
+        }
+
+        if("reasons" in asn1.result)
+            this.reasons = new in_window.org.pkijs.asn1.BITSTRING({ value_hex: asn1.result["reasons"].value_block.value_hex });
+
+        if("cRLIssuer" in asn1.result)
+        {
+            this.cRLIssuer = new Array();
+            var crl_names = asn1.result["cRLIssuer_names"];
+
+            for(var i = 0; i < crl_names; i++)
+                this.cRLIssuer.push(new in_window.org.pkijs.simpl.GENERAL_NAME({ schema: crl_names[i] }));
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.DistributionPoint.prototype.toSchema =
+    function()
+    {
+        // #region Create array for output sequence 
+        var output_array = new Array();
+
+        if("distributionPoint" in this)
+        {
+            var internalValue;
+
+            if(this.distributionPoint instanceof Array)
+            {
+                var namesArray = new Array();
+
+                for(var i = 0; i < this.distributionPoint.length; i++)
+                    namesArray.push(this.distributionPoint[i].toSchema());
+
+                internalValue = new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 0 // [0]
+                    },
+                    value: namesArray
+                });
+            }
+            else
+            {
+                internalValue = new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 1 // [1]
+                    },
+                    value: [this.distributionPoint.toSchema()]
+                });
+            }
+
+            output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                id_block: {
+                    tag_class: 3, // CONTEXT-SPECIFIC
+                    tag_number: 0 // [0]
+                },
+                value: [internalValue]
+            }));
+        }
+
+        if("reasons" in this)
+        {
+            output_array.push(new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                id_block: {
+                    tag_class: 3, // CONTEXT-SPECIFIC
+                    tag_number: 1 // [1]
+                },
+                value_hex: this.reasons.value_block.value_hex
+            }));
+        }
+
+        if("cRLIssuer" in this)
+        {
+            var value = new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                id_block: {
+                    tag_class: 3, // CONTEXT-SPECIFIC
+                    tag_number: 2 // [2]
+                }
+            });
+
+            for(var i = 0; i < this.cRLIssuer.length; i++)
+                value.value_block.value.push(this.cRLIssuer[i].toSchema());
+
+            output_array.push(value);
+        }
+        // #endregion 
+
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: output_array
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.DistributionPoint.prototype.toJSON =
+    function()
+    {
+        var _object = {};
+
+        if("distributionPoint" in this)
+        {
+            if(this.distributionPoint instanceof Array)
+            {
+                _object.distributionPoint = new Array();
+
+                for(var i = 0; i < this.distributionPoint.length; i++)
+                    _object.distributionPoint.push(this.distributionPoint[i].toJSON());
+            }
+            else
+                _object.distributionPoint = this.distributionPoint.toJSON();
+        }
+
+        if("reasons" in this)
+            _object.reasons = this.reasons.toJSON();
+
+        if("cRLIssuer" in this)
+        {
+            _object.cRLIssuer = new Array();
+
+            for(var i = 0; i < this.cRLIssuer.length; i++)
+                _object.cRLIssuer.push(this.cRLIssuer[i].toJSON());
+        }
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "CRLDistributionPoints" type of extension
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.CRLDistributionPoints =
+    function()
+    {
+        // #region Internal properties of the object 
+        this.distributionPoints = new Array(); // Array of "simpl.x509.DistributionPoint"
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.x509.CRLDistributionPoints.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                this.distributionPoints = arguments[0].distributionPoints || new Array(); // Array of "simpl.x509.DistributionPoint"
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.CRLDistributionPoints.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.x509.CRLDistributionPoints({
+                names: {
+                    distributionPoints: "distributionPoints"
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for CRLDistributionPoints");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema 
+        var points = asn1.result["distributionPoints"];
+
+        for(var i = 0; i < points.length; i++)
+            this.distributionPoints.push(new in_window.org.pkijs.simpl.x509.DistributionPoint({ schema: points[i] }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.CRLDistributionPoints.prototype.toSchema =
+    function()
+    {
+        // #region Create array for output sequence 
+        var output_array = new Array();
+
+        for(var i = 0; i < this.distributionPoints.length; i++)
+            output_array.push(this.distributionPoints[i].toSchema());
+        // #endregion 
+
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: output_array
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.CRLDistributionPoints.prototype.toJSON =
+    function()
+    {
+        var _object = {
+            distributionPoints: new Array()
+        };
+
+        for(var i = 0; i < this.distributionPoints.length; i++)
+            _object.distributionPoints.push(this.distributionPoints[i].toJSON());
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "AccessDescription" type
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.AccessDescription =
+    function()
+    {
+        // #region Internal properties of the object 
+        this.accessMethod = "";
+        this.accessLocation = new in_window.org.pkijs.simpl.GENERAL_NAME();
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.x509.AccessDescription.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                this.accessMethod = arguments[0].accessMethod || "";
+                this.accessLocation = arguments[0].accessLocation || new in_window.org.pkijs.simpl.GENERAL_NAME();
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.AccessDescription.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.x509.AccessDescription({
+                names: {
+                    accessMethod: "accessMethod",
+                    accessLocation: {
+                        names: {
+                            block_name: "accessLocation"
+                        }
+                    }
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for AccessDescription");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema 
+        this.accessMethod = asn1.result["accessMethod"].value_block.toString();
+        this.accessLocation = new in_window.org.pkijs.simpl.GENERAL_NAME({ schema: asn1.result["accessLocation"] });
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.AccessDescription.prototype.toSchema =
+    function()
+    {
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: [
+                new in_window.org.pkijs.asn1.OID({ value: this.accessMethod }),
+                this.accessLocation.toSchema()
+            ]
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.AccessDescription.prototype.toJSON =
+    function()
+    {
+        return {
+            accessMethod: this.accessMethod,
+            accessLocation: this.accessLocation.toJSON()
+        };
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "AuthorityInfoAccess" and "SubjectInfoAccess" types of extension
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.InfoAccess =
+    function()
+    {
+        // #region Internal properties of the object 
+        this.accessDescriptions = new Array(); // Array of "simpl.x509.AccessDescription"
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.x509.InfoAccess.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                this.accessDescriptions = arguments[0].accessDescriptions || new Array(); // Array of "simpl.x509.DistributionPoint"
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.InfoAccess.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.x509.InfoAccess({
+                names: {
+                    accessDescriptions: "accessDescriptions"
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for InfoAccess");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema 
+        var descriptions = asn1.result["accessDescriptions"];
+
+        for(var i = 0; i < descriptions.length; i++)
+            this.accessDescriptions.push(new in_window.org.pkijs.simpl.x509.AccessDescription({ schema: descriptions[i] }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.InfoAccess.prototype.toSchema =
+    function()
+    {
+        // #region Create array for output sequence 
+        var output_array = new Array();
+
+        for(var i = 0; i < this.accessDescriptions.length; i++)
+            output_array.push(this.accessDescriptions[i].toSchema());
+        // #endregion 
+
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: output_array
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.InfoAccess.prototype.toJSON =
+    function()
+    {
+        var _object = {
+            accessDescriptions: new Array()
+        };
+
+        for(var i = 0; i < this.accessDescriptions.length; i++)
+            _object.accessDescriptions.push(this.accessDescriptions[i].toJSON());
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "IssuingDistributionPoint" type of extension
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.IssuingDistributionPoint =
+    function()
+    {
+        // #region Internal properties of the object 
+        // OPTIONAL this.distributionPoint // Array of "simpl.GENERAL_NAME" or a value of "simpl.RDN" type
+        // OPTIONAL this.onlyContainsUserCerts // BOOLEAN flag
+        // OPTIONAL this.onlyContainsCACerts // BOOLEAN flag
+        // OPTIONAL this.onlySomeReasons // BITSTRING
+        // OPTIONAL this.indirectCRL // BOOLEAN flag
+        // OPTIONAL this.onlyContainsAttributeCerts // BOOLEAN flag
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.x509.IssuingDistributionPoint.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                if("distributionPoint" in arguments[0])
+                    this.distributionPoint = arguments[0].distributionPoint;
+
+                if("onlyContainsUserCerts" in arguments[0])
+                    this.onlyContainsUserCerts = arguments[0].onlyContainsUserCerts;
+
+                if("onlyContainsCACerts" in arguments[0])
+                    this.onlyContainsCACerts = arguments[0].onlyContainsCACerts;
+
+                if("onlySomeReasons" in arguments[0])
+                    this.onlySomeReasons = arguments[0].onlySomeReasons;
+
+                if("indirectCRL" in arguments[0])
+                    this.indirectCRL = arguments[0].indirectCRL;
+
+                if("onlyContainsAttributeCerts" in arguments[0])
+                    this.onlyContainsAttributeCerts = arguments[0].onlyContainsAttributeCerts;
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.IssuingDistributionPoint.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.x509.IssuingDistributionPoint({
+                names: {
+                    distributionPoint: "distributionPoint",
+                    distributionPoint_names: "distributionPoint_names",
+                    onlyContainsUserCerts: "onlyContainsUserCerts",
+                    onlyContainsCACerts: "onlyContainsCACerts",
+                    onlySomeReasons: "onlySomeReasons",
+                    indirectCRL: "indirectCRL",
+                    onlyContainsAttributeCerts: "onlyContainsAttributeCerts"
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for IssuingDistributionPoint");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema 
+        if("distributionPoint" in asn1.result)
+        {
+            if(asn1.result["distributionPoint"].id_block.tag_number == 0) // GENERAL_NAMES variant
+            {
+                this.distributionPoint = new Array();
+                var names = asn1.result["distributionPoint_names"];
+
+                for(var i = 0; i < names.length; i++)
+                    this.distributionPoint.push(new in_window.org.pkijs.simpl.GENERAL_NAME({ schema: names[i] }));
+            }
+
+            if(asn1.result["distributionPoint"].id_block.tag_number == 1) // RDN variant
+            {
+                asn1.result["distributionPoint"].id_block.tag_class = 1; // UNIVERSAL
+                asn1.result["distributionPoint"].id_block.tag_number = 16; // SEQUENCE
+
+                this.distributionPoint = new in_window.org.pkijs.simpl.RDN({ schema: asn1.result["distributionPoint"] });
+            }
+        }
+
+        if("onlyContainsUserCerts" in asn1.result)
+        {
+            var view = new Uint8Array(asn1.result["onlyContainsUserCerts"].value_block.value_hex);
+            this.onlyContainsUserCerts = (view[0] !== 0x00);
+        }
+
+        if("onlyContainsCACerts" in asn1.result)
+        {
+            var view = new Uint8Array(asn1.result["onlyContainsCACerts"].value_block.value_hex);
+            this.onlyContainsCACerts = (view[0] !== 0x00);
+        }
+
+        if("onlySomeReasons" in asn1.result)
+        {
+            var view = new Uint8Array(asn1.result["onlySomeReasons"].value_block.value_hex);
+            this.onlySomeReasons = view[0];
+        }
+
+        if("indirectCRL" in asn1.result)
+        {
+            var view = new Uint8Array(asn1.result["indirectCRL"].value_block.value_hex);
+            this.indirectCRL = (view[0] !== 0x00);
+        }
+
+        if("onlyContainsAttributeCerts" in asn1.result)
+        {
+            var view = new Uint8Array(asn1.result["onlyContainsAttributeCerts"].value_block.value_hex);
+            this.onlyContainsAttributeCerts = (view[0] !== 0x00);
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.IssuingDistributionPoint.prototype.toSchema =
+    function()
+    {
+        // #region Create array for output sequence 
+        var output_array = new Array();
+
+        if("distributionPoint" in this)
+        {
+            var value;
+
+            if(this.distributionPoint instanceof Array)
+            {
+                value = new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                    id_block: {
+                        tag_class: 3, // CONTEXT-SPECIFIC
+                        tag_number: 0 // [0]
+                    }
+                });
+
+                for(var i = 0; i < this.distributionPoint.length; i++)
+                    value.value_block.value.push(this.distributionPoint[i].toSchema());
+            }
+            else
+            {
+                value = this.distributionPoint.toSchema();
+
+                value.id_block.tag_class = 3; // CONTEXT - SPECIFIC
+                value.id_block.tag_number = 1; // [1]
+            }
+
+            output_array.push(value);
+        }
+
+        if("onlyContainsUserCerts" in this)
+        {
+            var buffer = new ArrayBuffer(1);
+            var view = new Uint8Array(buffer);
+
+            view[0] = (this.onlyContainsUserCerts === false) ? 0x00 : 0xFF;
+
+            output_array.push(new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                id_block: {
+                    tag_class: 3, // CONTEXT-SPECIFIC
+                    tag_number: 1 // [1]
+                },
+                value_hex: buffer
+            }));
+        }
+
+        if("onlyContainsCACerts" in this)
+        {
+            var buffer = new ArrayBuffer(1);
+            var view = new Uint8Array(buffer);
+
+            view[0] = (this.onlyContainsCACerts === false) ? 0x00 : 0xFF;
+
+            output_array.push(new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                id_block: {
+                    tag_class: 3, // CONTEXT-SPECIFIC
+                    tag_number: 2 // [2]
+                },
+                value_hex: buffer
+            }));
+        }
+
+        if("onlySomeReasons" in this)
+        {
+            var buffer = new ArrayBuffer(1);
+            var view = new Uint8Array(buffer);
+
+            view[0] = this.onlySomeReasons;
+
+            output_array.push(new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                id_block: {
+                    tag_class: 3, // CONTEXT-SPECIFIC
+                    tag_number: 3 // [3]
+                },
+                value_hex: buffer
+            }));
+        }
+
+        if("indirectCRL" in this)
+        {
+            var buffer = new ArrayBuffer(1);
+            var view = new Uint8Array(buffer);
+
+            view[0] = (this.indirectCRL === false) ? 0x00 : 0xFF;
+
+            output_array.push(new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                id_block: {
+                    tag_class: 3, // CONTEXT-SPECIFIC
+                    tag_number: 4 // [4]
+                },
+                value_hex: buffer
+            }));
+        }
+
+        if("onlyContainsAttributeCerts" in this)
+        {
+            var buffer = new ArrayBuffer(1);
+            var view = new Uint8Array(buffer);
+
+            view[0] = (this.onlyContainsAttributeCerts === false) ? 0x00 : 0xFF;
+
+            output_array.push(new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                id_block: {
+                    tag_class: 3, // CONTEXT-SPECIFIC
+                    tag_number: 5 // [5]
+                },
+                value_hex: buffer
+            }));
+        }
+        // #endregion 
+
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: output_array
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.x509.IssuingDistributionPoint.prototype.toJSON =
+    function()
+    {
+        var _object = {};
+
+        if("distributionPoint" in this)
+        {
+            if(this.distributionPoint instanceof Array)
+            {
+                _object.distributionPoint = new Array();
+
+                for(var i = 0; i < this.distributionPoint.length; i++)
+                    _object.distributionPoint.push(this.distributionPoint[i].toJSON());
+            }
+            else
+                _object.distributionPoint = this.distributionPoint.toJSON();
+        }
+
+        if("onlyContainsUserCerts" in this)
+            _object.onlyContainsUserCerts = this.onlyContainsUserCerts;
+
+        if("onlyContainsCACerts" in this)
+            _object.onlyContainsCACerts = this.onlyContainsCACerts;
+
+        if("onlySomeReasons" in this)
+            _object.onlySomeReasons = this.onlySomeReasons.toJSON();
+
+        if("indirectCRL" in this)
+            _object.indirectCRL = this.indirectCRL;
+
+        if("onlyContainsAttributeCerts" in this)
+            _object.onlyContainsAttributeCerts = this.onlyContainsAttributeCerts;
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "Extension" type
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.EXTENSION =
+    function()
+    {
+        // #region Internal properties of the object 
+        this.extnID = "";
+        this.critical = false;
+        this.extnValue = new in_window.org.pkijs.asn1.OCTETSTRING();
+
+        // OPTIONAL this.parsedValue - Parsed "extnValue" in case of well-known "extnID"
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.EXTENSION.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                this.extnID = (arguments[0].extnID || "");
+                this.critical = (arguments[0].critical || false);
+                if("extnValue" in arguments[0])
+                    this.extnValue = new in_window.org.pkijs.asn1.OCTETSTRING({ value_hex: arguments[0].extnValue });
+                else
+                    this.extnValue = new in_window.org.pkijs.asn1.OCTETSTRING();
+
+                if("parsedValue" in arguments[0])
+                    this.parsedValue = arguments[0].parsedValue;
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.EXTENSION.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.EXTENSION({
+                names: {
+                    extnID: "extnID",
+                    critical: "critical",
+                    extnValue: "extnValue"
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for EXTENSION");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema 
+        this.extnID = asn1.result.extnID.value_block.toString();
+        if("critical" in asn1.result)
+            this.critical = asn1.result.critical.value_block.value;
+        this.extnValue = asn1.result.extnValue;
+
+        // #region Get "parsedValue" for well-known extensions 
+        var asn1 = in_window.org.pkijs.fromBER(this.extnValue.value_block.value_hex);
+        if(asn1.offset === (-1))
+            return;
+
+        switch(this.extnID)
+        {
+            case "2.5.29.9": // SubjectDirectoryAttributes
+                this.parsedValue = new in_window.org.pkijs.simpl.x509.SubjectDirectoryAttributes({ schema: asn1.result });
+                break;
+            case "2.5.29.14": // SubjectKeyIdentifier
+                this.parsedValue = asn1.result; // Should be just a simple OCTETSTRING
+                break;
+            case "2.5.29.15": // KeyUsage
+                this.parsedValue = asn1.result; // Should be just a simple BITSTRING
+                break;
+            case "2.5.29.16": // PrivateKeyUsagePeriod
+                this.parsedValue = new in_window.org.pkijs.simpl.x509.PrivateKeyUsagePeriod({ schema: asn1.result });
+                break;
+            case "2.5.29.17": // SubjectAltName
+            case "2.5.29.18": // IssuerAltName
+                this.parsedValue = new in_window.org.pkijs.simpl.x509.AltName({ schema: asn1.result });
+                break;
+            case "2.5.29.19": // BasicConstraints
+                this.parsedValue = new in_window.org.pkijs.simpl.x509.BasicConstraints({ schema: asn1.result });
+                break;
+            case "2.5.29.20": // CRLNumber
+            case "2.5.29.27": // BaseCRLNumber (delta CRL indicator)
+                this.parsedValue = asn1.result; // Should be just a simple INTEGER
+                break;
+            case "2.5.29.21": // CRLReason
+                this.parsedValue = asn1.result; // Should be just a simple ENUMERATED
+                break;
+            case "2.5.29.24": // InvalidityDate
+                this.parsedValue = asn1.result; // Should be just a simple GeneralizedTime
+                break;
+            case "2.5.29.28": // IssuingDistributionPoint
+                this.parsedValue = new in_window.org.pkijs.simpl.x509.IssuingDistributionPoint({ schema: asn1.result });
+                break;
+            case "2.5.29.29": // CertificateIssuer
+                this.parsedValue = new in_window.org.pkijs.simpl.GENERAL_NAMES({ schema: asn1.result }); // Should be just a simple 
+                break;
+            case "2.5.29.30": // NameConstraints
+                this.parsedValue = new in_window.org.pkijs.simpl.x509.NameConstraints({ schema: asn1.result });
+                break;
+            case "2.5.29.31": // CRLDistributionPoints
+            case "2.5.29.46": // FreshestCRL
+                this.parsedValue = new in_window.org.pkijs.simpl.x509.CRLDistributionPoints({ schema: asn1.result });
+                break;
+            case "2.5.29.32": // CertificatePolicies
+                this.parsedValue = new in_window.org.pkijs.simpl.x509.CertificatePolicies({ schema: asn1.result });
+                break;
+            case "2.5.29.33": // PolicyMappings
+                this.parsedValue = new in_window.org.pkijs.simpl.x509.PolicyMappings({ schema: asn1.result });
+                break;
+            case "2.5.29.35": // AuthorityKeyIdentifier
+                this.parsedValue = new in_window.org.pkijs.simpl.x509.AuthorityKeyIdentifier({ schema: asn1.result });
+                break;
+            case "2.5.29.36": // PolicyConstraints
+                this.parsedValue = new in_window.org.pkijs.simpl.x509.PolicyConstraints({ schema: asn1.result });
+                break;
+            case "2.5.29.37": // ExtKeyUsage
+                this.parsedValue = new in_window.org.pkijs.simpl.x509.ExtKeyUsage({ schema: asn1.result });
+                break;
+            case "2.5.29.54": // InhibitAnyPolicy
+                this.parsedValue = asn1.result; // Should be just a simple INTEGER
+                break;
+            case "1.3.6.1.5.5.7.1.1": // AuthorityInfoAccess
+            case "1.3.6.1.5.5.7.1.11": // SubjectInfoAccess
+                this.parsedValue = new in_window.org.pkijs.simpl.x509.InfoAccess({ schema: asn1.result });
+                break;
+            default:
+        }
+        // #endregion 
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.EXTENSION.prototype.toSchema =
+    function()
+    {
+        // #region Create array for output sequence 
+        var output_array = new Array();
+
+        output_array.push(new in_window.org.pkijs.asn1.OID({ value: this.extnID }));
+
+        if(this.critical)
+            output_array.push(new in_window.org.pkijs.asn1.BOOLEAN({ value: this.critical }));
+
+        output_array.push(this.extnValue);
+        // #endregion 
+
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: output_array
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.EXTENSION.prototype.toJSON =
+    function()
+    {
+        var _object = {
+            extnID: this.extnID,
+            critical: this.critical,
+            extnValue: this.extnValue.toJSON()
+        };
+
+        if("parsedValue" in this)
+            _object.parsedValue = this.parsedValue.toJSON();
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "Extensions" type (sequence of many Extension)
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.EXTENSIONS =
+    function()
+    {
+        // #region Internal properties of the object 
+        this.extensions_array = new Array();
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.EXTENSIONS.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+                this.extensions_array = (arguments[0].extensions_array || (new Array()));
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.EXTENSIONS.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.EXTENSIONS({
+                names: {
+                    extensions: "extensions"
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for EXTENSIONS");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema 
+        for(var i = 0; i < asn1.result.extensions.length; i++)
+            this.extensions_array.push(new in_window.org.pkijs.simpl.EXTENSION({ schema: asn1.result.extensions[i] }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.EXTENSIONS.prototype.toSchema =
+    function()
+    {
+        // #region Construct and return new ASN.1 schema for this object 
+        var extension_schemas = new Array();
+
+        for(var i = 0; i < this.extensions_array.length; i++)
+            extension_schemas.push(this.extensions_array[i].toSchema());
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: extension_schemas
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.EXTENSIONS.prototype.toJSON =
+    function()
+    {
+        var _object = {
+            extensions_array: new Array()
+        };
+
+        for(var i = 0; i < this.extensions_array.length; i++)
+            _object.extensions_array.push(this.extensions_array[i].toJSON());
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for X.509 v3 certificate (RFC5280)
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.CERT =
+    function()
+    {
+        // #region Internal properties of the object 
+        // #region Properties from certificate TBS part 
+        this.tbs = new ArrayBuffer(0); // Encoded value of certificate TBS (need to have it for certificate validation)
+
+        // OPTIONAL this.version = 0;
+        this.serialNumber = new in_window.org.pkijs.asn1.INTEGER(); // Might be a very long integer value
+        this.signature = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER(); // Signature algorithm from certificate TBS part
+        this.issuer = new in_window.org.pkijs.simpl.RDN();
+        this.notBefore = new in_window.org.pkijs.simpl.TIME();
+        this.notAfter = new in_window.org.pkijs.simpl.TIME();
+        this.subject = new in_window.org.pkijs.simpl.RDN();
+        this.subjectPublicKeyInfo = new in_window.org.pkijs.simpl.PUBLIC_KEY_INFO();
+        // OPTIONAL this.issuerUniqueID = new ArrayBuffer(0); // IMPLICIT bistring value
+        // OPTIONAL this.subjectUniqueID = new ArrayBuffer(0); // IMPLICIT bistring value
+        // OPTIONAL this.extensions = new Array(); // Array of "simpl.EXTENSION"
+        // #endregion 
+
+        // #region Properties from certificate major part 
+        this.signatureAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER(); // Signature algorithm from certificate major part
+        this.signatureValue = new in_window.org.pkijs.asn1.BITSTRING();
+        // #endregion 
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.CERT.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                // #region Properties from certificate TBS part 
+                this.tbs = arguments[0].tbs || new ArrayBuffer(0);
+
+                if("version" in arguments[0])
+                    this.version = arguments[0].version;
+                this.serialNumber = arguments[0].serialNumber || new in_window.org.pkijs.asn1.INTEGER(); // Might be a very long integer value
+                this.signature = arguments[0].signature || new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER(); // Signature algorithm from certificate TBS part
+                this.issuer = arguments[0].issuer || new in_window.org.pkijs.simpl.RDN();
+                this.notBefore = arguments[0].not_before || new in_window.org.pkijs.simpl.TIME();
+                this.notAfter = arguments[0].not_after || new in_window.org.pkijs.simpl.TIME();
+                this.subject = arguments[0].subject || new in_window.org.pkijs.simpl.RDN();
+                this.subjectPublicKeyInfo = arguments[0].subjectPublicKeyInfo || new in_window.org.pkijs.simpl.PUBLIC_KEY_INFO();
+                if("issuerUniqueID" in arguments[0])
+                    this.issuerUniqueID = arguments[0].issuerUniqueID;
+                if("subjectUniqueID" in arguments[0])
+                    this.subjectUniqueID = arguments[0].subjectUniqueID;
+                if("extensions" in arguments[0])
+                    this.extensions = arguments[0].extensions;
+                // #endregion 
+
+                // #region Properties from certificate major part 
+                this.signatureAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER(); // Signature algorithm from certificate major part
+                this.signatureValue = new in_window.org.pkijs.asn1.BITSTRING();
+                // #endregion 
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.CERT.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.CERT({
+                names: {
+                    tbsCertificate: {
+                        names: {
+                            extensions: {
+                                names: {
+                                    extensions: "tbsCertificate.extensions"
+                                }
+                            }
+                        }
+                    }
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for CERT");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema 
+        this.tbs = asn1.result["tbsCertificate"].value_before_decode;
+
+        if("tbsCertificate.version" in asn1.result)
+            this.version = asn1.result["tbsCertificate.version"].value_block.value_dec;
+        this.serialNumber = asn1.result["tbsCertificate.serialNumber"];
+        this.signature = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ schema: asn1.result["tbsCertificate.signature"] });
+        this.issuer = new in_window.org.pkijs.simpl.RDN({ schema: asn1.result["tbsCertificate.issuer"] });
+        this.notBefore = new in_window.org.pkijs.simpl.TIME({ schema: asn1.result["tbsCertificate.notBefore"] });
+        this.notAfter = new in_window.org.pkijs.simpl.TIME({ schema: asn1.result["tbsCertificate.notAfter"] });
+        this.subject = new in_window.org.pkijs.simpl.RDN({ schema: asn1.result["tbsCertificate.subject"] });
+        this.subjectPublicKeyInfo = new in_window.org.pkijs.simpl.PUBLIC_KEY_INFO({ schema: asn1.result["tbsCertificate.subjectPublicKeyInfo"] });
+        if("tbsCertificate.issuerUniqueID" in asn1.result)
+            this.issuerUniqueID = asn1.result["tbsCertificate.issuerUniqueID"].value_block.value_hex;
+        if("tbsCertificate.subjectUniqueID" in asn1.result)
+            this.issuerUniqueID = asn1.result["tbsCertificate.subjectUniqueID"].value_block.value_hex;
+        if("tbsCertificate.extensions" in asn1.result)
+        {
+            this.extensions = new Array();
+
+            var extensions = asn1.result["tbsCertificate.extensions"];
+
+            for(var i = 0; i < extensions.length; i++)
+                this.extensions.push(new in_window.org.pkijs.simpl.EXTENSION({ schema: extensions[i] }));
+        }
+
+        this.signatureAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ schema: asn1.result["signatureAlgorithm"] });
+        this.signatureValue = asn1.result["signatureValue"];
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.CERT.prototype.encodeTBS =
+    function()
+    {
+        /// <summary>Create ASN.1 schema for existing values of TBS part for the certificate</summary>
+
+        // #region Create array for output sequence 
+        var output_array = new Array();
+
+        if("version" in this)
+            output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                optional: true,
+                id_block: {
+                    tag_class: 3, // CONTEXT-SPECIFIC
+                    tag_number: 0 // [0]
+                },
+                value: [
+                    new in_window.org.pkijs.asn1.INTEGER({ value: this.version }) // EXPLICIT integer value
+                ]
+            }));
+
+        output_array.push(this.serialNumber);
+        output_array.push(this.signature.toSchema());
+        output_array.push(this.issuer.toSchema());
+
+        output_array.push(new in_window.org.pkijs.asn1.SEQUENCE({
+            value: [
+                this.notBefore.toSchema(),
+                this.notAfter.toSchema()
+            ]
+        }));
+
+        output_array.push(this.subject.toSchema());
+        output_array.push(this.subjectPublicKeyInfo.toSchema());
+
+        if("issuerUniqueID" in this)
+            output_array.push(new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                optional: true,
+                id_block: {
+                    tag_class: 3, // CONTEXT-SPECIFIC
+                    tag_number: 1 // [1]
+                },
+                value_hex: this.issuerUniqueID
+            }));
+        if("subjectUniqueID" in this)
+            output_array.push(new in_window.org.pkijs.asn1.ASN1_PRIMITIVE({
+                optional: true,
+                id_block: {
+                    tag_class: 3, // CONTEXT-SPECIFIC
+                    tag_number: 2 // [2]
+                },
+                value_hex: this.subjectUniqueID
+            }));
+
+        if("subjectUniqueID" in this)
+            output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                optional: true,
+                id_block: {
+                    tag_class: 3, // CONTEXT-SPECIFIC
+                    tag_number: 3 // [3]
+                },
+                value: [this.extensions.toSchema()]
+            }));
+
+        if("extensions" in this)
+        {
+            var extensions = new Array();
+
+            for(var i = 0; i < this.extensions.length; i++)
+                extensions.push(this.extensions[i].toSchema());
+
+            output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                optional: true,
+                id_block: {
+                    tag_class: 3, // CONTEXT-SPECIFIC
+                    tag_number: 3 // [3]
+                },
+                value: [new in_window.org.pkijs.asn1.SEQUENCE({
+                    value: extensions
+                })]
+            }));
+        }
+        // #endregion 
+
+        // #region Create and return output sequence 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: output_array
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.CERT.prototype.toSchema =
+    function(encodeFlag)
+    {
+        /// <param name="encodeFlag" type="Boolean">If param equal to false then create TBS schema via decoding stored value. In othe case create TBS schema via assembling from TBS parts.</param>
+
+        if(typeof encodeFlag === "undefined")
+            encodeFlag = false;
+
+        var tbs_schema = {};
+
+        // #region Decode stored TBS value 
+        if(encodeFlag === false)
+        {
+            if(this.tbs.length === 0) // No stored certificate TBS part
+                return in_window.org.pkijs.schema.CERT().value[0];
+
+            var tbs_asn1 = in_window.org.pkijs.fromBER(this.tbs);
+
+            tbs_schema = tbs_asn1.result;
+        }
+        // #endregion 
+        // #region Create TBS schema via assembling from TBS parts 
+        else
+            tbs_schema = in_window.org.pkijs.simpl.CERT.prototype.encodeTBS.call(this);
+        // #endregion 
+
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: [
+                tbs_schema,
+                this.signatureAlgorithm.toSchema(),
+                this.signatureValue
+            ]
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.CERT.prototype.verify =
+    function()
+    {
+        /// <summary>!!! Works well in Chrome dev versions only (April 2014th) !!!</summary>
+        /// <returns type="Promise">Returns a new Promise object (in case of error), or a result of "crypto.subtle.veryfy" function</returns>
+
+        // #region Global variables 
+        var sequence = Promise.resolve();
+
+        var subjectPublicKeyInfo = {};
+
+        var signature = this.signatureValue;
+        var tbs = this.tbs;
+
+        var _this = this;
+        // #endregion 
+
+        // #region Set correct "subjectPublicKeyInfo" value 
+        if(arguments[0] instanceof Object)
+        {
+            if("issuerCertificate" in arguments[0]) // Must be of type "simpl.CERT"
+                subjectPublicKeyInfo = arguments[0].issuerCertificate.subjectPublicKeyInfo;
+        }
+        else
+        {
+            if(this.issuer.isEqual(this.subject)) // Self-signed certificate
+                subjectPublicKeyInfo = this.subjectPublicKeyInfo;
+        }
+
+        if((subjectPublicKeyInfo instanceof in_window.org.pkijs.simpl.PUBLIC_KEY_INFO) === false)
+            return new Promise(function(resolve, reject) { reject("Please provide issuer certificate as a parameter"); });
+        // #endregion 
+
+        // #region Get a "crypto" extension 
+        var crypto = in_window.org.pkijs.getCrypto();
+        if(typeof crypto == "undefined")
+            return new Promise(function(resolve, reject) { reject("Unable to create WebCrypto object"); });
+        // #endregion 
+
+        // #region Find signer's hashing algorithm 
+        var sha_algorithm = in_window.org.pkijs.getHashAlgorithm(this.signatureAlgorithm);
+        if(sha_algorithm === "")
+            return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + _this.signatureAlgorithm.algorithm_id); });
+        // #endregion 
+
+        // #region Importing public key 
+        sequence = sequence.then(
+            function()
+            {
+                // #region Get information about public key algorithm and default parameters for import
+                var algorithmObject = in_window.org.pkijs.getAlgorithmByOID(_this.signatureAlgorithm.algorithm_id);
+                if(("name" in algorithmObject) === false)
+                    return new Promise(function(resolve, reject) { reject("Unsupported public key algorithm: " + _this.signatureAlgorithm.algorithm_id); });
+
+                var algorithm_name = algorithmObject.name;
+
+                var algorithm = in_window.org.pkijs.getAlgorithmParameters(algorithm_name, "importkey");
+                if("hash" in algorithm.algorithm)
+                    algorithm.algorithm.hash.name = sha_algorithm;
+                // #endregion 
+
+                var publicKeyInfo_schema = subjectPublicKeyInfo.toSchema();
+                var publicKeyInfo_buffer = publicKeyInfo_schema.toBER(false);
+                var publicKeyInfo_view = new Uint8Array(publicKeyInfo_buffer);
+
+                return crypto.importKey("spki", publicKeyInfo_view, algorithm.algorithm, true, algorithm.usages);
+            }
+            );
+        // #endregion 
+
+        // #region Verify signature for the certificate 
+        sequence = sequence.then(
+            function(publicKey)
+            {
+                // #region Get default algorithm parameters for verification 
+                var algorithm = in_window.org.pkijs.getAlgorithmParameters(publicKey.algorithm.name, "verify");
+                if("hash" in algorithm.algorithm)
+                    algorithm.algorithm.hash.name = sha_algorithm;
+                // #endregion 
+
+                // #region Special case for ECDSA signatures 
+                var signature_value = signature.value_block.value_hex;
+
+                if(publicKey.algorithm.name === "ECDSA")
+                {
+                    var asn1 = in_window.org.pkijs.fromBER(signature_value);
+                    signature_value = in_window.org.pkijs.createECDSASignatureFromCMS(asn1.result);
+                }
+                // #endregion 
+
+                // #region Special case for RSA-PSS 
+                if(publicKey.algorithm.name === "RSA-PSS")
+                {
+                    var pssParameters;
+
+                    try
+                    {
+                        pssParameters = new in_window.org.pkijs.simpl.x509.RSASSA_PSS_params({ schema: _this.signatureAlgorithm.algorithm_params });
+                    }
+                    catch(ex)
+                    {
+                        return new Promise(function(resolve, reject) { reject(ex); });
+                    }
+
+                    if("saltLength" in pssParameters)
+                        algorithm.algorithm.saltLength = pssParameters.saltLength;
+                    else
+                        algorithm.algorithm.saltLength = 20;
+
+                    var hash_algo = "SHA-1";
+
+                    if("hashAlgorithm" in pssParameters)
+                    {
+                        var hashAlgorithm = in_window.org.pkijs.getAlgorithmByOID(pssParameters.hashAlgorithm.algorithm_id);
+                        if(("name" in hashAlgorithm) === false)
+                            return new Promise(function(resolve, reject) { reject("Unrecognized hash algorithm: " + pssParameters.hashAlgorithm.algorithm_id); });
+
+                        hash_algo = hashAlgorithm.name;
+                    }
+
+                    algorithm.algorithm.hash.name = hash_algo;
+                }
+                // #endregion 
+
+                return crypto.verify(algorithm.algorithm,
+                    publicKey,
+                    new Uint8Array(signature_value),
+                    new Uint8Array(tbs));
+            }
+            );
+        // #endregion 
+
+        return sequence;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.CERT.prototype.sign =
+    function(privateKey, hashAlgorithm)
+    {
+        /// <param name="privateKey" type="CryptoKey">Private key for "subjectPublicKeyInfo" structure</param>
+        /// <param name="hashAlgorithm" type="String" optional="true">Hashing algorithm. Default SHA-1</param>
+
+        // #region Initial variables 
+        var _this = this;
+        // #endregion 
+
+        // #region Get a private key from function parameter 
+        if(typeof privateKey === "undefined")
+            return new Promise(function(resolve, reject) { reject("Need to provide a private key for signing"); });
+        // #endregion 
+
+        // #region Get hashing algorithm 
+        if(typeof hashAlgorithm === "undefined")
+            hashAlgorithm = "SHA-1";
+        else
+        {
+            // #region Simple check for supported algorithm 
+            var oid = in_window.org.pkijs.getOIDByAlgorithm({ name: hashAlgorithm });
+            if(oid === "")
+                return new Promise(function(resolve, reject) { reject("Unsupported hash algorithm: " + hashAlgorithm); });
+            // #endregion 
+        }
+        // #endregion 
+
+        // #region Get a "default parameters" for current algorithm 
+        var defParams = in_window.org.pkijs.getAlgorithmParameters(privateKey.algorithm.name, "sign");
+        defParams.algorithm.hash.name = hashAlgorithm;
+        // #endregion 
+
+        // #region Fill internal structures base on "privateKey" and "hashAlgorithm" 
+        switch(privateKey.algorithm.name.toUpperCase())
+        {
+            case "RSASSA-PKCS1-V1_5":
+            case "ECDSA":
+                _this.signature.algorithm_id = in_window.org.pkijs.getOIDByAlgorithm(defParams.algorithm);
+                _this.signatureAlgorithm.algorithm_id = _this.signature.algorithm_id;
+                break;
+            case "RSA-PSS":
+                {
+                    // #region Set "saltLength" as a length (in octets) of hash function result 
+                    switch(hashAlgorithm.toUpperCase())
+                    {
+                        case "SHA-256":
+                            defParams.algorithm.saltLength = 32;
+                            break;
+                        case "SHA-384":
+                            defParams.algorithm.saltLength = 48;
+                            break;
+                        case "SHA-512":
+                            defParams.algorithm.saltLength = 64;
+                            break;
+                        default:
+                    }
+                    // #endregion 
+
+                    // #region Fill "RSASSA_PSS_params" object 
+                    var paramsObject = {};
+
+                    if(hashAlgorithm.toUpperCase() !== "SHA-1")
+                    {
+                        var hashAlgorithmOID = in_window.org.pkijs.getOIDByAlgorithm({ name: hashAlgorithm });
+                        if(hashAlgorithmOID === "")
+                            return new Promise(function(resolve, reject) { reject("Unsupported hash algorithm: " + hashAlgorithm); });
+
+                        paramsObject.hashAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({
+                            algorithm_id: hashAlgorithmOID,
+                            algorithm_params: new in_window.org.pkijs.asn1.NULL()
+                        });
+
+                        paramsObject.maskGenAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({
+                            algorithm_id: "1.2.840.113549.1.1.8", // MGF1
+                            algorithm_params: paramsObject.hashAlgorithm.toSchema()
+                        })
+                    }
+
+                    if(defParams.algorithm.saltLength !== 20)
+                        paramsObject.saltLength = defParams.algorithm.saltLength;
+
+                    var pssParameters = new in_window.org.pkijs.simpl.x509.RSASSA_PSS_params(paramsObject);
+                    // #endregion   
+
+                    // #region Automatically set signature algorithm 
+                    _this.signature = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({
+                        algorithm_id: "1.2.840.113549.1.1.10",
+                        algorithm_params: pssParameters.toSchema()
+                    });
+                    _this.signatureAlgorithm = _this.signature; // Must be the same
+                    // #endregion 
+                }
+                break;
+            default:
+                return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + privateKey.algorithm.name); });
+        }
+        // #endregion 
+
+        // #region Create TBS data for signing 
+        _this.tbs = in_window.org.pkijs.simpl.CERT.prototype.encodeTBS.call(this).toBER(false);
+        // #endregion 
+
+        // #region Get a "crypto" extension 
+        var crypto = in_window.org.pkijs.getCrypto();
+        if(typeof crypto == "undefined")
+            return new Promise(function(resolve, reject) { reject("Unable to create WebCrypto object"); });
+        // #endregion 
+
+        // #region Signing TBS data on provided private key 
+        return crypto.sign(defParams.algorithm,
+            privateKey,
+            new Uint8Array(_this.tbs)).then(
+            function(result)
+            {
+                // #region Special case for ECDSA algorithm 
+                if(defParams.algorithm.name === "ECDSA")
+                    result = in_window.org.pkijs.createCMSECDSASignature(result);
+                // #endregion 
+
+                _this.signatureValue = new in_window.org.pkijs.asn1.BITSTRING({ value_hex: result });
+            },
+            function(error)
+            {
+                return new Promise(function(resolve, reject) { reject("Signing error: " + error); });
+            }
+            );
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.CERT.prototype.getPublicKey =
+    function()
+    {
+        /// <summary>Importing public key for current certificate</summary>
+
+        // #region Initial variables 
+        var algorithm;
+        // #endregion 
+
+        // #region Get a "crypto" extension 
+        var crypto = in_window.org.pkijs.getCrypto();
+        if(typeof crypto == "undefined")
+            return new Promise(function(resolve, reject) { reject("Unable to create WebCrypto object"); });
+        // #endregion 
+
+        // #region Find correct algorithm for imported public key 
+        if(arguments[0] instanceof Object)
+        {
+            if("algorithm" in arguments[0])
+                algorithm = arguments[0].algorithm;
+            else
+                return new Promise(function(resolve, reject) { reject("Absent mandatory parameter \"algorithm\""); });
+        }
+        else
+        {
+            // #region Find signer's hashing algorithm 
+            var sha_algorithm = in_window.org.pkijs.getHashAlgorithm(this.signatureAlgorithm);
+            if(sha_algorithm === "")
+                return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + _this.signatureAlgorithm.algorithm_id); });
+            // #endregion   
+
+            // #region Get information about public key algorithm and default parameters for import
+            var algorithmObject = in_window.org.pkijs.getAlgorithmByOID(this.signatureAlgorithm.algorithm_id);
+            if(("name" in algorithmObject) === false)
+                return new Promise(function(resolve, reject) { reject("Unsupported public key algorithm: " + _this.signatureAlgorithm.algorithm_id); });
+
+            var algorithm_name = algorithmObject.name;
+
+            algorithm = in_window.org.pkijs.getAlgorithmParameters(algorithm_name, "importkey");
+            if("hash" in algorithm.algorithm)
+                algorithm.algorithm.hash.name = sha_algorithm;
+            // #endregion 
+        }
+        // #endregion 
+
+        // #region Get neccessary values from internal fields for current certificate 
+        var publicKeyInfo_schema = this.subjectPublicKeyInfo.toSchema();
+        var publicKeyInfo_buffer = publicKeyInfo_schema.toBER(false);
+        var publicKeyInfo_view = new Uint8Array(publicKeyInfo_buffer);
+        // #endregion 
+
+        return crypto.importKey("spki", publicKeyInfo_view, algorithm.algorithm, true, algorithm.usages);
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.CERT.prototype.getKeyHash =
+    function()
+    {
+        /// <summary>Get SHA-1 hash value for subject public key</summary>
+
+        // #region Get a "crypto" extension 
+        var crypto = in_window.org.pkijs.getCrypto();
+        if(typeof crypto == "undefined")
+            return new Promise(function(resolve, reject) { reject("Unable to create WebCrypto object"); });
+        // #endregion 
+
+        return crypto.digest({ name: "sha-1" }, new Uint8Array(this.subjectPublicKeyInfo.subjectPublicKey.value_block.value_hex));
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.CERT.prototype.toJSON =
+    function()
+    {
+        var _object = {
+            tbs: in_window.org.pkijs.bufferToHexCodes(this.tbs, 0, this.tbs.byteLength),
+            serialNumber: this.serialNumber.toJSON(),
+            signature: this.signature.toJSON(),
+            issuer: this.issuer.toJSON(),
+            notBefore: this.notBefore.toJSON(),
+            notAfter: this.notAfter.toJSON(),
+            subject: this.subject.toJSON(),
+            subjectPublicKeyInfo: this.subjectPublicKeyInfo.toJSON(),
+            signatureAlgorithm: this.signatureAlgorithm.toJSON(),
+            signatureValue: this.signatureValue.toJSON()
+        };
+
+        if("version" in this)
+            _object.version = this.version;
+
+        if("issuerUniqueID" in this)
+            _object.issuerUniqueID = in_window.org.pkijs.bufferToHexCodes(this.issuerUniqueID, 0, this.issuerUniqueID.byteLength);
+
+        if("subjectUniqueID" in this)
+            _object.subjectUniqueID = in_window.org.pkijs.bufferToHexCodes(this.subjectUniqueID, 0, this.subjectUniqueID.byteLength);
+
+        if("extensions" in this)
+        {
+            _object.extensions = new Array();
+
+            for(var i = 0; i < this.extensions.length; i++)
+                _object.extensions.push(this.extensions[i].toJSON());
+        }
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "revoked certificate" type (to use in CRL)
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.REV_CERT =
+    function()
+    {
+        // #region Internal properties of the object 
+        this.userCertificate = new in_window.org.pkijs.asn1.INTEGER();
+        this.revocationDate = new in_window.org.pkijs.simpl.TIME();
+        // OPTIONAL this.crlEntryExtensions = new Array(); // Array of "in_window.org.pkijs.simpl.EXTENSION");
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.REV_CERT.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                this.userCertificate = arguments[0].userCertificate || new in_window.org.pkijs.asn1.INTEGER();
+                this.revocationDate = arguments[0].revocationDate || new in_window.org.pkijs.simpl.TIME();
+                if("crlEntryExtensions" in arguments[0])
+                    this.crlEntryExtensions = arguments[0].crlEntryExtensions;
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.REV_CERT.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            new in_window.org.pkijs.asn1.SEQUENCE({
+                value: [
+                    new in_window.org.pkijs.asn1.INTEGER({ name: "userCertificate" }),
+                    in_window.org.pkijs.schema.TIME({
+                        names: {
+                            utcTimeName: "revocationDate",
+                            generalTimeName: "revocationDate"
+                    }
+                    }),
+                    in_window.org.pkijs.schema.EXTENSIONS({
+                        names: {
+                            block_name: "crlEntryExtensions"
+                        }
+                    }, true)
+                ]
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for REV_CERT");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema 
+        this.userCertificate = asn1.result["userCertificate"];
+        this.revocationDate = new in_window.org.pkijs.simpl.TIME({ schema: asn1.result["revocationDate"] });
+
+        if("crlEntryExtensions" in asn1.result)
+        {
+            this.crlEntryExtensions = new Array();
+            var exts = asn1.result["crlEntryExtensions"].value_block.value;
+
+            for(var i = 0; i < exts.length; i++)
+                this.crlEntryExtensions.push(new in_window.org.pkijs.simpl.EXTENSION({ schema: exts[i] }));
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.REV_CERT.prototype.toSchema =
+    function()
+    {
+        // #region Create array for output sequence 
+        var sequence_array = new Array();
+        sequence_array.push(this.userCertificate);
+        sequence_array.push(this.revocationDate.toSchema());
+
+        if("crlEntryExtensions" in this)
+        {
+            var exts = new Array();
+
+            for(var i = 0; i < this.crlEntryExtensions.length; i++)
+                exts.push(this.crlEntryExtensions[i].toSchema());
+
+            sequence_array.push(new in_window.org.pkijs.asn1.SEQUENCE({ value: exts }));
+        }
+        // #endregion 
+
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: sequence_array
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.REV_CERT.prototype.toJSON =
+    function()
+    {
+        var _object = {
+            userCertificate: this.userCertificate.toJSON(),
+            revocationDate: this.revocationDate.toJSON
+        };
+
+        if("crlEntryExtensions" in this)
+        {
+            _object.crlEntryExtensions = new Array();
+
+            for(var i = 0; i < this.crlEntryExtensions.length; i++)
+                _object.crlEntryExtensions.push(this.crlEntryExtensions[i].toJSON());
+        }
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for X.509 CRL (Certificate Revocation List)(RFC5280)  
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.CRL =
+    function()
+    {
+        // #region Internal properties of the object 
+        // #region Properties from CRL TBS part 
+        this.tbs = new ArrayBuffer(0);
+
+        // OPTIONAL this.version = 1;
+        this.signature = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER();
+        this.issuer = new in_window.org.pkijs.simpl.RDN();
+        this.thisUpdate = new in_window.org.pkijs.simpl.TIME();
+        // OPTIONAL this.nextUpdate = new in_window.org.pkijs.simpl.TIME();
+        // OPTIONAL this.revokedCertificates = new Array(); // Array of REV_CERT objects
+        // OPTIONAL this.crlExtensions = new Array(); // Array of in_window.org.pkijs.simpl.EXTENSION();
+        // #endregion 
+
+        // #region Properties from CRL major part 
+        this.signatureAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER();
+        this.signatureValue = new in_window.org.pkijs.asn1.BITSTRING();
+        // #endregion 
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.CRL.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                // #region Properties from CRL TBS part 
+                this.tbs = arguments[0].tbs || new ArrayBuffer(0);
+
+                if("version" in arguments[0])
+                    this.version = arguments[0].version;
+                this.signature = arguments[0].signature || new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER();
+                this.issuer = arguments[0].issuer || new in_window.org.pkijs.simpl.RDN();
+                this.thisUpdate = arguments[0].thisUpdate || new in_window.org.pkijs.simpl.TIME();
+                if("nextUpdate" in arguments[0])
+                    this.nextUpdate = arguments[0].nextUpdate;
+                if("revokedCertificates" in arguments[0])
+                    this.revokedCertificates = arguments[0].revokedCertificates;
+                if("crlExtensions" in arguments[0])
+                    this.crlExtensions = arguments[0].crlExtensions;
+                // #endregion 
+
+                // #region Properties from CRL major part 
+                this.signatureAlgorithm = arguments[0].signatureAlgorithm || new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER();
+                this.signatureValue = arguments[0].signatureValue || new in_window.org.pkijs.asn1.BITSTRING();
+                // #endregion 
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.CRL.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.CRL()
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for CRL");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema 
+        this.tbs = asn1.result["tbsCertList"].value_before_decode;
+
+        if("tbsCertList.version" in asn1.result)
+            this.version = asn1.result["tbsCertList.version"].value_block.value_dec;
+        this.signature = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ schema: asn1.result["tbsCertList.signature"] });
+        this.issuer = new in_window.org.pkijs.simpl.RDN({ schema: asn1.result["tbsCertList.issuer"] });
+        this.thisUpdate = new in_window.org.pkijs.simpl.TIME({ schema: asn1.result["tbsCertList.thisUpdate"] });
+        if("tbsCertList.nextUpdate" in asn1.result)
+            this.nextUpdate = new in_window.org.pkijs.simpl.TIME({ schema: asn1.result["tbsCertList.nextUpdate"] });
+        if("tbsCertList.revokedCertificates" in asn1.result)
+        {
+            this.revokedCertificates = new Array();
+
+            var rev_certs = asn1.result["tbsCertList.revokedCertificates"];
+            for(var i = 0; i < rev_certs.length; i++)
+                this.revokedCertificates.push(new in_window.org.pkijs.simpl.REV_CERT({ schema: rev_certs[i] }));
+        }
+        if("tbsCertList.extensions" in asn1.result)
+        {
+            this.crlExtensions = new Array();
+            var exts = asn1.result["tbsCertList.extensions"].value_block.value;
+
+            for(var i = 0; i < exts.length; i++)
+                this.crlExtensions.push(new in_window.org.pkijs.simpl.EXTENSION({ schema: exts[i] }));
+        }
+
+        this.signatureAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ schema: asn1.result["signatureAlgorithm"] });
+        this.signatureValue = asn1.result["signatureValue"];
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.CRL.prototype.encodeTBS =
+    function()
+    {
+        // #region Create array for output sequence 
+        var output_array = new Array();
+
+        if("version" in this)
+            output_array.push(new in_window.org.pkijs.asn1.INTEGER({ value: this.version }));
+
+        output_array.push(this.signature.toSchema());
+        output_array.push(this.issuer.toSchema());
+        output_array.push(this.thisUpdate.toSchema());
+
+        if("nextUpdate" in this)
+            output_array.push(this.nextUpdate.toSchema());
+
+        if("revokedCertificates" in this)
+        {
+            var rev_certificates = new Array();
+
+            for(var i = 0; i < this.revokedCertificates.length; i++)
+                rev_certificates.push(this.revokedCertificates[i].toSchema());
+
+            output_array.push(new in_window.org.pkijs.asn1.SEQUENCE({
+                value: rev_certificates
+            }));
+        }
+
+        if("crlExtensions" in this)
+        {
+            var extensions = new Array();
+
+            for(var j = 0; j < this.crlExtensions.length; j++)
+                extensions.push(this.crlExtensions[j].toSchema());
+
+            output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                optional: true,
+                id_block: {
+                    tag_class: 3, // CONTEXT-SPECIFIC
+                    tag_number: 0 // [0]
+                },
+                value: [
+                    new in_window.org.pkijs.asn1.SEQUENCE({
+                        value: extensions
+                    })
+                ]
+            }));
+        }
+        // #endregion 
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: output_array
+        }));
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.CRL.prototype.toSchema =
+    function(encodeFlag)
+    {
+        /// <param name="encodeFlag" type="Boolean">If param equal to false then create TBS schema via decoding stored value. In othe case create TBS schema via assembling from TBS parts.</param>
+
+        // #region Check "encodeFlag" 
+        if(typeof encodeFlag === "undefined")
+            encodeFlag = false;
+        // #endregion 
+
+        // #region Decode stored TBS value 
+        var tbs_schema;
+
+        if(encodeFlag === false)
+        {
+            if(this.tbs.length === 0) // No stored TBS part
+                return in_window.org.pkijs.schema.CRL();
+
+            tbs_schema = in_window.org.pkijs.fromBER(this.tbs).result;
+        }
+        // #endregion 
+        // #region Create TBS schema via assembling from TBS parts 
+        else
+            tbs_schema = in_window.org.pkijs.simpl.CRL.prototype.encodeTBS.call(this);
+        // #endregion 
+
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: [
+                tbs_schema,
+                this.signatureAlgorithm.toSchema(),
+                this.signatureValue
+            ]
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.CRL.prototype.verify =
+    function()
+    {
+        // #region Global variables 
+        var sequence = Promise.resolve();
+
+        var signature = this.signatureValue;
+        var tbs = this.tbs;
+
+        var subjectPublicKeyInfo = -1;
+
+        var _this = this;
+        // #endregion 
+
+        // #region Get information about CRL issuer certificate 
+        if(arguments[0] instanceof Object)
+        {
+            if("issuerCertificate" in arguments[0]) // "issuerCertificate" must be of type "simpl.CERT"
+            {
+                subjectPublicKeyInfo = arguments[0].issuerCertificate.subjectPublicKeyInfo;
+
+                // The CRL issuer name and "issuerCertificate" subject name are not equal
+                if(this.issuer.isEqual(arguments[0].issuerCertificate.subject) == false)
+                    return new Promise(function(resolve, reject) { resolve(false); });
+            }
+
+            // #region In case if there is only public key during verification 
+            if("publicKeyInfo" in arguments[0])
+                subjectPublicKeyInfo = arguments[0].publicKeyInfo; // Must be of type "org.pkijs.simpl.PUBLIC_KEY_INFO"
+            // #endregion 
+        }
+
+        if((subjectPublicKeyInfo instanceof in_window.org.pkijs.simpl.PUBLIC_KEY_INFO) === false)
+            return new Promise(function(resolve, reject) { reject("Issuer's certificate must be provided as an input parameter"); });
+        // #endregion 
+
+        // #region Check the CRL for unknown critical extensions 
+        if("crlExtensions" in this)
+        {
+            for(var i = 0; i < this.crlExtensions.length; i++)
+            {
+                if(this.crlExtensions[i].critical)
+                {
+                    // We can not be sure that unknown extension has no value for CRL signature
+                    if(("parsedValue" in this.crlExtensions[i]) == false)
+                        return new Promise(function(resolve, reject) { resolve(false); });
+                }
+            }
+        }
+        // #endregion 
+
+        // #region Get a "crypto" extension 
+        var crypto = in_window.org.pkijs.getCrypto();
+        if(typeof crypto == "undefined")
+            return new Promise(function(resolve, reject) { reject("Unable to create WebCrypto object"); });
+        // #endregion 
+
+        // #region Find signer's hashing algorithm 
+        var sha_algorithm = in_window.org.pkijs.getHashAlgorithm(this.signatureAlgorithm);
+        if(sha_algorithm === "")
+            return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + _this.signatureAlgorithm.algorithm_id); });
+        // #endregion 
+
+        // #region Import public key 
+        sequence = sequence.then(
+            function()
+            {
+                // #region Get information about public key algorithm and default parameters for import
+                var algorithmObject = in_window.org.pkijs.getAlgorithmByOID(_this.signature.algorithm_id);
+                if(("name" in algorithmObject) === "")
+                    return new Promise(function(resolve, reject) { reject("Unsupported public key algorithm: " + _this.signature.algorithm_id); });
+
+                var algorithm_name = algorithmObject.name;
+
+                var algorithm = in_window.org.pkijs.getAlgorithmParameters(algorithm_name, "importkey");
+                if("hash" in algorithm.algorithm)
+                    algorithm.algorithm.hash.name = sha_algorithm;
+                // #endregion 
+
+                var publicKeyInfo_schema = subjectPublicKeyInfo.toSchema();
+                var publicKeyInfo_buffer = publicKeyInfo_schema.toBER(false);
+                var publicKeyInfo_view = new Uint8Array(publicKeyInfo_buffer);
+
+                return crypto.importKey("spki",
+                    publicKeyInfo_view,
+                    algorithm.algorithm,
+                    true,
+                    algorithm.usages);
+            }
+            );
+        // #endregion 
+
+        // #region Verify signature for the certificate 
+        sequence = sequence.then(
+            function(publicKey)
+            {
+                // #region Get default algorithm parameters for verification 
+                var algorithm = in_window.org.pkijs.getAlgorithmParameters(publicKey.algorithm.name, "verify");
+                if("hash" in algorithm.algorithm)
+                    algorithm.algorithm.hash.name = sha_algorithm;
+                // #endregion 
+
+                // #region Special case for ECDSA signatures 
+                var signature_value = signature.value_block.value_hex;
+
+                if(publicKey.algorithm.name === "ECDSA")
+                {
+                    var asn1 = in_window.org.pkijs.fromBER(signature_value);
+                    signature_value = in_window.org.pkijs.createECDSASignatureFromCMS(asn1.result);
+                }
+                // #endregion 
+
+                // #region Special case for RSA-PSS 
+                if(publicKey.algorithm.name === "RSA-PSS")
+                {
+                    var pssParameters;
+
+                    try
+                    {
+                        pssParameters = new in_window.org.pkijs.simpl.x509.RSASSA_PSS_params({ schema: _this.signatureAlgorithm.algorithm_params });
+                    }
+                    catch(ex)
+                    {
+                        return new Promise(function(resolve, reject) { reject(ex); });
+                    }
+
+                    if("saltLength" in pssParameters)
+                        algorithm.algorithm.saltLength = pssParameters.saltLength;
+                    else
+                        algorithm.algorithm.saltLength = 20;
+
+                    var hash_algo = "SHA-1";
+
+                    if("hashAlgorithm" in pssParameters)
+                    {
+                        var hashAlgorithm = in_window.org.pkijs.getAlgorithmByOID(pssParameters.hashAlgorithm.algorithm_id);
+                        if(("name" in hashAlgorithm) === false)
+                            return new Promise(function(resolve, reject) { reject("Unrecognized hash algorithm: " + pssParameters.hashAlgorithm.algorithm_id); });
+
+                        hash_algo = hashAlgorithm.name;
+                    }
+
+                    algorithm.algorithm.hash.name = hash_algo;
+                }
+                // #endregion 
+
+                return crypto.verify(algorithm.algorithm,
+                    publicKey,
+                    new Uint8Array(signature_value),
+                    new Uint8Array(tbs));
+            }
+            );
+        // #endregion 
+
+        return sequence;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.CRL.prototype.sign =
+    function(privateKey, hashAlgorithm)
+    {
+        /// <param name="privateKey" type="Key">Private key for "subjectPublicKeyInfo" structure</param>
+        /// <param name="hashAlgorithm" type="String" optional="true">Hashing algorithm. Default SHA-1</param>
+
+        // #region Initial variables 
+        var _this = this;
+        // #endregion 
+
+        // #region Get a private key from function parameter 
+        if(typeof privateKey === "undefined")
+            return new Promise(function(resolve, reject) { reject("Need to provide a private key for signing"); });
+        // #endregion 
+
+        // #region Get hashing algorithm 
+        if(typeof hashAlgorithm === "undefined")
+            hashAlgorithm = "SHA-1";
+        else
+        {
+            // #region Simple check for supported algorithm 
+            var oid = in_window.org.pkijs.getOIDByAlgorithm({ name: hashAlgorithm });
+            if(oid === "")
+                return new Promise(function(resolve, reject) { reject("Unsupported hash algorithm: " + hashAlgorithm); });
+            // #endregion 
+        }
+        // #endregion 
+
+        // #region Get a "default parameters" for current algorithm 
+        var defParams = in_window.org.pkijs.getAlgorithmParameters(privateKey.algorithm.name, "sign");
+        defParams.algorithm.hash.name = hashAlgorithm;
+        // #endregion 
+
+        // #region Fill internal structures base on "privateKey" and "hashAlgorithm" 
+        switch(privateKey.algorithm.name.toUpperCase())
+        {
+            case "RSASSA-PKCS1-V1_5":
+            case "ECDSA":
+                _this.signature.algorithm_id = in_window.org.pkijs.getOIDByAlgorithm(defParams.algorithm);
+                _this.signatureAlgorithm.algorithm_id = _this.signature.algorithm_id;
+                break;
+            case "RSA-PSS":
+                {
+                    // #region Set "saltLength" as a length (in octets) of hash function result 
+                    switch(hashAlgorithm.toUpperCase())
+                    {
+                        case "SHA-256":
+                            defParams.algorithm.saltLength = 32;
+                            break;
+                        case "SHA-384":
+                            defParams.algorithm.saltLength = 48;
+                            break;
+                        case "SHA-512":
+                            defParams.algorithm.saltLength = 64;
+                            break;
+                        default:
+                    }
+                    // #endregion 
+
+                    // #region Fill "RSASSA_PSS_params" object 
+                    var paramsObject = {};
+
+                    if(hashAlgorithm.toUpperCase() !== "SHA-1")
+                    {
+                        var hashAlgorithmOID = in_window.org.pkijs.getOIDByAlgorithm({ name: hashAlgorithm });
+                        if(hashAlgorithmOID === "")
+                            return new Promise(function(resolve, reject) { reject("Unsupported hash algorithm: " + hashAlgorithm); });
+
+                        paramsObject.hashAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({
+                            algorithm_id: hashAlgorithmOID,
+                            algorithm_params: new in_window.org.pkijs.asn1.NULL()
+                        });
+
+                        paramsObject.maskGenAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({
+                            algorithm_id: "1.2.840.113549.1.1.8", // MGF1
+                            algorithm_params: paramsObject.hashAlgorithm.toSchema()
+                        })
+                    }
+
+                    if(defParams.algorithm.saltLength !== 20)
+                        paramsObject.saltLength = defParams.algorithm.saltLength;
+
+                    var pssParameters = new in_window.org.pkijs.simpl.x509.RSASSA_PSS_params(paramsObject);
+                    // #endregion   
+
+                    // #region Automatically set signature algorithm 
+                    _this.signature = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({
+                        algorithm_id: "1.2.840.113549.1.1.10",
+                        algorithm_params: pssParameters.toSchema()
+                    });
+                    _this.signatureAlgorithm = _this.signature; // Must be the same
+                    // #endregion 
+                }
+                break;
+            default:
+                return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + privateKey.algorithm.name); });
+        }
+        // #endregion 
+
+        // #region Create TBS data for signing 
+        _this.tbs = in_window.org.pkijs.simpl.CRL.prototype.encodeTBS.call(this).toBER(false);
+        // #endregion 
+
+        // #region Get a "crypto" extension 
+        var crypto = in_window.org.pkijs.getCrypto();
+        if(typeof crypto == "undefined")
+            return new Promise(function(resolve, reject) { reject("Unable to create WebCrypto object"); });
+        // #endregion 
+
+        // #region Signing TBS data on provided private key 
+        return crypto.sign(
+            defParams.algorithm,
+            privateKey,
+            new Uint8Array(_this.tbs)).
+            then(
+            function(result)
+            {
+                // #region Special case for ECDSA algorithm 
+                if(defParams.algorithm.name === "ECDSA")
+                    result = in_window.org.pkijs.createCMSECDSASignature(result);
+                // #endregion 
+
+                _this.signatureValue = new in_window.org.pkijs.asn1.BITSTRING({ value_hex: result });
+            },
+            function(error)
+            {
+                return new Promise(function(resolve, reject) { reject("Signing error: " + error); });
+            }
+            );
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.CRL.prototype.isCertificateRevoked =
+    function()
+    {
+        // #region Get input certificate 
+        var certificate = {};
+
+        if(arguments[0] instanceof Object)
+        {
+            if("certificate" in arguments[0])
+                certificate = arguments[0].certificate;
+        }
+
+        if((certificate instanceof in_window.org.pkijs.simpl.CERT) === false)
+            return false;
+        // #endregion 
+
+        // #region Check that issuer of the input certificate is the same with issuer of this CRL 
+        if(this.issuer.isEqual(certificate.issuer) === false)
+            return false;
+        // #endregion 
+
+        // #region Check that there are revoked certificates in this CRL 
+        if(("revokedCertificates" in this) === false)
+            return false;
+        // #endregion 
+
+        // #region Search for input certificate in revoked certificates array 
+        for(var i = 0; i < this.revokedCertificates.length; i++)
+        {
+            if(this.revokedCertificates[i].userCertificate.isEqual(certificate.serialNumber))
+                return true;
+        }
+        // #endregion 
+
+        return false;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.CRL.prototype.toJSON =
+    function()
+    {
+        var _object = {
+            tbs: in_window.org.pkijs.bufferToHexCodes(this.tbs, 0, this.tbs.byteLength),
+            signature: this.signature.toJSON(),
+            issuer: this.issuer.toJSON(),
+            thisUpdate: this.thisUpdate.toJSON(),
+            signatureAlgorithm: this.signatureAlgorithm.toJSON(),
+            signatureValue: this.signatureValue.toJSON()
+        };
+
+        if("version" in this)
+            _object.version = this.version;
+
+        if("nextUpdate" in this)
+            _object.nextUpdate = this.nextUpdate.toJSON();
+
+        if("revokedCertificates" in this)
+        {
+            _object.revokedCertificates = new Array();
+
+            for(var i = 0; i < this.revokedCertificates.length; i++)
+                _object.revokedCertificates.push(this.revokedCertificates[i].toJSON());
+        }
+
+        if("crlExtensions" in this)
+        {
+            _object.crlExtensions = new Array();
+
+            for(var i = 0; i < this.crlExtensions.length; i++)
+                _object.crlExtensions.push(this.crlExtensions[i].toJSON());
+        }
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for "Attribute" type
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.ATTRIBUTE =
+    function()
+    {
+        // #region Internal properties of the object 
+        this.type = "";
+        this.values = new Array();
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.ATTRIBUTE.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                this.type = arguments[0].type || "";
+                this.values = arguments[0].values || new Array();
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.ATTRIBUTE.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.ATTRIBUTE({
+                names: {
+                    type: "type",
+                    values: "values"
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for ATTRIBUTE");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema 
+        this.type = asn1.result["type"].value_block.toString();
+        this.values = asn1.result["values"];
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.ATTRIBUTE.prototype.toSchema =
+    function()
+    {
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: [
+                new in_window.org.pkijs.asn1.OID({ value: this.type }),
+                new in_window.org.pkijs.asn1.SET({
+                    value: this.values
+                })
+            ]
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.ATTRIBUTE.prototype.toJSON =
+    function()
+    {
+        var _object = {
+            type: this.type,
+            values: new Array()
+        };
+
+        for(var i = 0; i < this.values.length; i++)
+            _object.values.push(this.values[i].toJSON());
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for PKCS#10 certificate request
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.PKCS10 =
+    function()
+    {
+        // #region Internal properties of the object 
+        this.tbs = new ArrayBuffer(0);
+
+        this.version = 0;
+        this.subject = new in_window.org.pkijs.simpl.RDN();
+        this.subjectPublicKeyInfo = new in_window.org.pkijs.simpl.PUBLIC_KEY_INFO();
+        // OPTIONAL this.attributes = new Array(); // Array of simpl.ATTRIBUTE objects
+
+        this.signatureAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER(); // Signature algorithm from certificate major part
+        this.signatureValue = new in_window.org.pkijs.asn1.BITSTRING();
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.PKCS10.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                this.tbs = arguments[0].tbs || new ArrayBuffer(0);
+
+                this.version = arguments[0].version || 0;
+                this.subject = arguments[0].subject || new in_window.org.pkijs.simpl.RDN();
+                this.subjectPublicKeyInfo = arguments[0].subjectPublicKeyInfo || new in_window.org.pkijs.simpl.PUBLIC_KEY_INFO();
+
+                if("attributes" in arguments[0])
+                    this.attributes = arguments[0].attributes;
+
+                this.signatureAlgorithm = arguments[0].signatureAlgorithm || new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER(); // Signature algorithm from certificate major part
+                this.signatureValue = arguments[0].signatureValue || new in_window.org.pkijs.asn1.BITSTRING();
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.PKCS10.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.PKCS10()
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for PKCS10");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema 
+        this.tbs = asn1.result["CertificationRequestInfo"].value_before_decode;
+
+        this.version = asn1.result["CertificationRequestInfo.version"].value_block.value_dec;
+        this.subject = new in_window.org.pkijs.simpl.RDN({ schema: asn1.result["CertificationRequestInfo.subject"] });
+        this.subjectPublicKeyInfo = new in_window.org.pkijs.simpl.PUBLIC_KEY_INFO({ schema: asn1.result["CertificationRequestInfo.subjectPublicKeyInfo"] });
+        if("CertificationRequestInfo.attributes" in asn1.result)
+        {
+            this.attributes = new Array();
+
+            var attrs = asn1.result["CertificationRequestInfo.attributes"];
+            for(var i = 0; i < attrs.length; i++)
+                this.attributes.push(new in_window.org.pkijs.simpl.ATTRIBUTE({ schema: attrs[i] }));
+        }
+
+        this.signatureAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ schema: asn1.result["signatureAlgorithm"] });
+        this.signatureValue = asn1.result["signatureValue"];
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.PKCS10.prototype.encodeTBS =
+    function()
+    {
+        // #region Create array for output sequence 
+        var output_array = new Array();
+
+        output_array.push(new in_window.org.pkijs.asn1.INTEGER({ value: this.version }));
+        output_array.push(this.subject.toSchema());
+        output_array.push(this.subjectPublicKeyInfo.toSchema());
+
+        if("attributes" in this)
+        {
+            var attributes = new Array();
+
+            for(var i = 0; i < this.attributes.length; i++)
+                attributes.push(this.attributes[i].toSchema());
+
+            output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                id_block: {
+                    tag_class: 3, // CONTEXT-SPECIFIC
+                    tag_number: 0 // [0]
+                },
+                value: attributes
+            }));
+        }
+        // #endregion 
+
+        return (new in_window.org.pkijs.asn1.SEQUENCE({ value: output_array }));
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.PKCS10.prototype.toSchema =
+    function(encodeFlag)
+    {
+        /// <param name="encodeFlag" type="Boolean">If param equal to false then create TBS schema via decoding stored value. In othe case create TBS schema via assembling from TBS parts.</param>
+
+        // #region Check "encodeFlag" 
+        if(typeof encodeFlag === "undefined")
+            encodeFlag = false;
+        // #endregion 
+
+        // #region Decode stored TBS value 
+        var tbs_schema;
+
+        if(encodeFlag === false)
+        {
+            if(this.tbs.length === 0) // No stored TBS part
+                return in_window.org.pkijs.schema.PKCS10();
+
+            tbs_schema = in_window.org.pkijs.fromBER(this.tbs).result;
+        }
+        // #endregion 
+        // #region Create TBS schema via assembling from TBS parts 
+        else
+            tbs_schema = in_window.org.pkijs.simpl.PKCS10.prototype.encodeTBS.call(this);
+        // #endregion 
+
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: [
+                tbs_schema,
+                this.signatureAlgorithm.toSchema(),
+                this.signatureValue
+            ]
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.PKCS10.prototype.verify =
+    function()
+    {
+        /// <summary>!!! Works well in Chrome dev versions only (April 2014th) !!!</summary>
+        /// <returns type="Promise">Returns a new Promise object (in case of error), or a result of "crypto.subtle.veryfy" function</returns>
+
+        // #region Global variables 
+        var _this = this;
+        var sha_algorithm = "";
+
+        var sequence = Promise.resolve();
+
+        var subjectPublicKeyInfo = this.subjectPublicKeyInfo;
+        var signature = this.signatureValue;
+        var tbs = this.tbs;
+        // #endregion 
+
+        // #region Get a "crypto" extension 
+        var crypto = in_window.org.pkijs.getCrypto();
+        if(typeof crypto == "undefined")
+            return new Promise(function(resolve, reject) { reject("Unable to create WebCrypto object"); });
+        // #endregion 
+
+        // #region Find a correct hashing algorithm 
+        sha_algorithm = in_window.org.pkijs.getHashAlgorithm(this.signatureAlgorithm);
+        if(sha_algorithm === "")
+            return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + _this.signatureAlgorithm.algorithm_id); });
+        // #endregion 
+
+        // #region Importing public key 
+        sequence = sequence.then(
+            function()
+            {
+                // #region Get information about public key algorithm and default parameters for import
+                var algorithmObject = in_window.org.pkijs.getAlgorithmByOID(_this.signatureAlgorithm.algorithm_id);
+                if(("name" in algorithmObject) === false)
+                    return new Promise(function(resolve, reject) { reject("Unsupported public key algorithm: " + _this.signatureAlgorithm.algorithm_id); });
+
+                var algorithm_name = algorithmObject.name;
+
+                var algorithm = in_window.org.pkijs.getAlgorithmParameters(algorithm_name, "importkey");
+                if("hash" in algorithm.algorithm)
+                    algorithm.algorithm.hash.name = sha_algorithm;
+                // #endregion 
+
+                var publicKeyInfo_schema = subjectPublicKeyInfo.toSchema();
+                var publicKeyInfo_buffer = publicKeyInfo_schema.toBER(false);
+                var publicKeyInfo_view = new Uint8Array(publicKeyInfo_buffer);
+
+                return crypto.importKey("spki", publicKeyInfo_view, algorithm.algorithm, true, algorithm.usages);
+            }
+            );
+        // #endregion 
+
+        // #region Verify signature  
+        sequence = sequence.then(
+            function(publicKey)
+            {
+                // #region Get default algorithm parameters for verification 
+                var algorithm = in_window.org.pkijs.getAlgorithmParameters(publicKey.algorithm.name, "verify");
+                if("hash" in algorithm.algorithm)
+                    algorithm.algorithm.hash.name = sha_algorithm;
+                // #endregion 
+
+                // #region Special case for ECDSA signatures 
+                var signature_value = signature.value_block.value_hex;
+
+                if(publicKey.algorithm.name === "ECDSA")
+                {
+                    var asn1 = in_window.org.pkijs.fromBER(signature_value);
+                    signature_value = in_window.org.pkijs.createECDSASignatureFromCMS(asn1.result);
+                }
+                // #endregion 
+
+                // #region Special case for RSA-PSS 
+                if(publicKey.algorithm.name === "RSA-PSS")
+                {
+                    var pssParameters;
+
+                    try
+                    {
+                        pssParameters = new in_window.org.pkijs.simpl.x509.RSASSA_PSS_params({ schema: _this.signatureAlgorithm.algorithm_params });
+                    }
+                    catch(ex)
+                    {
+                        return new Promise(function(resolve, reject) { reject(ex); });
+                    }
+
+                    if("saltLength" in pssParameters)
+                        algorithm.algorithm.saltLength = pssParameters.saltLength;
+                    else
+                        algorithm.algorithm.saltLength = 20;
+
+                    var hash_algo = "SHA-1";
+
+                    if("hashAlgorithm" in pssParameters)
+                    {
+                        var hashAlgorithm = in_window.org.pkijs.getAlgorithmByOID(pssParameters.hashAlgorithm.algorithm_id);
+                        if(("name" in hashAlgorithm) === false)
+                            return new Promise(function(resolve, reject) { reject("Unrecognized hash algorithm: " + pssParameters.hashAlgorithm.algorithm_id); });
+
+                        hash_algo = hashAlgorithm.name;
+                    }
+
+                    algorithm.algorithm.hash.name = hash_algo;
+                }
+                // #endregion 
+
+                return crypto.verify(algorithm.algorithm,
+                    publicKey,
+                    new Uint8Array(signature_value),
+                    new Uint8Array(tbs));
+            }
+            );
+        // #endregion   
+
+        return sequence;
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.PKCS10.prototype.sign =
+    function(privateKey, hashAlgorithm)
+    {
+        /// <param name="privateKey" type="Key">Private key for "subjectPublicKeyInfo" structure</param>
+        /// <param name="hashAlgorithm" type="String" optional="true">Hashing algorithm. Default SHA-1</param>
+
+        // #region Initial variables 
+        var _this = this;
+        // #endregion 
+
+        // #region Get a private key from function parameter 
+        if(typeof privateKey === "undefined")
+            return new Promise(function(resolve, reject) { reject("Need to provide a private key for signing"); });
+        // #endregion 
+
+        // #region Get hashing algorithm 
+        if(typeof hashAlgorithm === "undefined")
+            hashAlgorithm = "SHA-1";
+        else
+        {
+            // #region Simple check for supported algorithm 
+            var oid = in_window.org.pkijs.getOIDByAlgorithm({ name: hashAlgorithm });
+            if(oid === "")
+                return new Promise(function(resolve, reject) { reject("Unsupported hash algorithm: " + hashAlgorithm); });
+            // #endregion 
+        }
+        // #endregion 
+
+        // #region Get a "default parameters" for current algorithm 
+        var defParams = in_window.org.pkijs.getAlgorithmParameters(privateKey.algorithm.name, "sign");
+        defParams.algorithm.hash.name = hashAlgorithm;
+        // #endregion 
+
+        // #region Fill internal structures base on "privateKey" and "hashAlgorithm" 
+        switch(privateKey.algorithm.name.toUpperCase())
+        {
+            case "RSASSA-PKCS1-V1_5":
+            case "ECDSA":
+                _this.signatureAlgorithm.algorithm_id = in_window.org.pkijs.getOIDByAlgorithm(defParams.algorithm);
+                break;
+            case "RSA-PSS":
+                {
+                    // #region Set "saltLength" as a length (in octets) of hash function result 
+                    switch(hashAlgorithm.toUpperCase())
+                    {
+                        case "SHA-256":
+                            defParams.algorithm.saltLength = 32;
+                            break;
+                        case "SHA-384":
+                            defParams.algorithm.saltLength = 48;
+                            break;
+                        case "SHA-512":
+                            defParams.algorithm.saltLength = 64;
+                            break;
+                        default:
+                    }
+                    // #endregion 
+
+                    // #region Fill "RSASSA_PSS_params" object 
+                    var paramsObject = {};
+
+                    if(hashAlgorithm.toUpperCase() !== "SHA-1")
+                    {
+                        var hashAlgorithmOID = in_window.org.pkijs.getOIDByAlgorithm({ name: hashAlgorithm });
+                        if(hashAlgorithmOID === "")
+                            return new Promise(function(resolve, reject) { reject("Unsupported hash algorithm: " + hashAlgorithm); });
+
+                        paramsObject.hashAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({
+                            algorithm_id: hashAlgorithmOID,
+                            algorithm_params: new in_window.org.pkijs.asn1.NULL()
+                        });
+
+                        paramsObject.maskGenAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({
+                            algorithm_id: "1.2.840.113549.1.1.8", // MGF1
+                            algorithm_params: paramsObject.hashAlgorithm.toSchema()
+                        })
+                    }
+
+                    if(defParams.algorithm.saltLength !== 20)
+                        paramsObject.saltLength = defParams.algorithm.saltLength;
+
+                    var pssParameters = new in_window.org.pkijs.simpl.x509.RSASSA_PSS_params(paramsObject);
+                    // #endregion   
+
+                    // #region Automatically set signature algorithm 
+                    _this.signatureAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({
+                        algorithm_id: "1.2.840.113549.1.1.10",
+                        algorithm_params: pssParameters.toSchema()
+                    });
+                    // #endregion 
+                }
+                break;
+            default:
+                return new Promise(function(resolve, reject) { reject("Unsupported signature algorithm: " + privateKey.algorithm.name); });
+        }
+        // #endregion 
+
+        // #region Create TBS data for signing 
+        _this.tbs = in_window.org.pkijs.simpl.PKCS10.prototype.encodeTBS.call(this).toBER(false);
+        // #endregion 
+
+        // #region Get a "crypto" extension 
+        var crypto = in_window.org.pkijs.getCrypto();
+        if(typeof crypto == "undefined")
+            return new Promise(function(resolve, reject) { reject("Unable to create WebCrypto object"); });
+        // #endregion 
+
+        // #region Signing TBS data on provided private key 
+        return crypto.sign(defParams.algorithm,
+            privateKey,
+            new Uint8Array(_this.tbs)).then(
+            function(result)
+            {
+                // #region Special case for ECDSA algorithm 
+                if(defParams.algorithm.name === "ECDSA")
+                    result = in_window.org.pkijs.createCMSECDSASignature(result);
+                // #endregion 
+
+                _this.signatureValue = new in_window.org.pkijs.asn1.BITSTRING({ value_hex: result });
+            },
+            function(error)
+            {
+                return new Promise(function(resolve, reject) { reject("Signing error: " + error); });
+            }
+            );
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.PKCS10.prototype.toJSON =
+    function()
+    {
+        var _object = {
+            tbs: in_window.org.pkijs.bufferToHexCodes(this.tbs, 0, this.tbs.byteLength),
+            version: this.version,
+            subject: this.subject.toJSON(),
+            subjectPublicKeyInfo: this.subjectPublicKeyInfo.toJSON(),
+            signatureAlgorithm: this.signatureAlgorithm.toJSON(),
+            signatureValue: this.signatureValue.toJSON()
+        };
+
+        if("attributes" in this)
+        {
+            _object.attributes = new Array();
+
+            for(var i = 0; i < this.attributes.length; i++)
+                _object.attributes.push(this.attributes[i].toJSON());
+        }
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for PKCS#8 private key bag
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.PKCS8 =
+    function()
+    {
+        // #region Internal properties of the object 
+        this.version = 0;
+        this.privateKeyAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER();
+        this.privateKey = new in_window.org.pkijs.asn1.OCTETSTRING();
+        // OPTIONAL this.attributes // Array of "in_window.org.pkijs.simpl.ATTRIBUTE"
+        // #endregion 
+
+        // #region If input argument array contains "schema" for this object 
+        if((arguments[0] instanceof Object) && ("schema" in arguments[0]))
+            in_window.org.pkijs.simpl.PKCS8.prototype.fromSchema.call(this, arguments[0].schema);
+        // #endregion 
+        // #region If input argument array contains "native" values for internal properties 
+        else
+        {
+            if(arguments[0] instanceof Object)
+            {
+                this.version = arguments[0].version || 0;
+                this.privateKeyAlgorithm = arguments[0].privateKeyAlgorithm || new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER();
+                this.privateKey = arguments[0].privateKey || new in_window.org.pkijs.asn1.OCTETSTRING();
+
+                if("attributes" in arguments[0])
+                    this.attributes = arguments[0].attributes;
+            }
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.PKCS8.prototype.fromSchema =
+    function(schema)
+    {
+        // #region Check the schema is valid 
+        var asn1 = in_window.org.pkijs.compareSchema(schema,
+            schema,
+            in_window.org.pkijs.schema.PKCS8({
+                names: {
+                    version: "version",
+                    privateKeyAlgorithm: {
+                        names: {
+                            block_name: "privateKeyAlgorithm"
+                        }
+                    },
+                    privateKey: "privateKey",
+                    attributes: "attributes"
+                }
+            })
+            );
+
+        if(asn1.verified === false)
+            throw new Error("Object's schema was not verified against input data for PKCS8");
+        // #endregion 
+
+        // #region Get internal properties from parsed schema 
+        this.version = asn1.result["version"].value_block.value_dec;
+        this.privateKeyAlgorithm = new in_window.org.pkijs.simpl.ALGORITHM_IDENTIFIER({ schema: asn1.result["privateKeyAlgorithm"] });
+        this.privateKey = asn1.result["privateKey"];
+
+        if("attributes" in asn1.result)
+        {
+            this.attributes = new Array();
+            var attrs = asn1.result["attributes"];
+
+            for(var i = 0; i < attrs.length; i++)
+                this.attributes.push(new in_window.org.pkijs.simpl.ATTRIBUTE({ schema: attrs[i] }));
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.PKCS8.prototype.toSchema =
+    function()
+    {
+        // #region Create array for output sequence 
+        var output_array = new Array();
+
+        output_array.push(new in_window.org.pkijs.asn1.INTEGER({ value: this.version }));
+        output_array.push(this.privateKeyAlgorithm.toSchema());
+        output_array.push(this.privateKey);
+
+        if("attributes" in this)
+        {
+            var attrs = new Array();
+
+            for(var i = 0; i < this.attributes.length; i++)
+                attrs.push(this.attributes[i].toSchema());
+
+            output_array.push(new in_window.org.pkijs.asn1.ASN1_CONSTRUCTED({
+                optional: true,
+                id_block: {
+                    tag_class: 3, // CONTEXT-SPECIFIC
+                    tag_number: 0 // [0]
+                },
+                value: attrs
+            }));
+        }
+        // #endregion 
+
+        // #region Construct and return new ASN.1 schema for this object 
+        return (new in_window.org.pkijs.asn1.SEQUENCE({
+            value: output_array
+        }));
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.PKCS8.prototype.toJSON =
+    function()
+    {
+        var _object = {
+            version: this.version,
+            privateKeyAlgorithm: this.privateKeyAlgorithm.toJSON(),
+            privateKey: this.privateKey.toJSON()
+        };
+
+        if("attributes" in this)
+        {
+            _object.attributes = new Array();
+
+            for(var i = 0; i < this.attributes.length; i++)
+                _object.attributes.push(this.attributes[i].toJSON());
+        }
+
+        return _object;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+    // #region Simplified structure for working with X.509 certificate chains 
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.CERT_CHAIN =
+    function()
+    {
+        // #region Internal properties of the object 
+        /// <field name="trusted_certs" type="Array" elementType="in_window.org.pkijs.simpl.CERT">Array of pre-defined trusted (by user) certificates</field>
+        this.trusted_certs = new Array();
+        /// <field name="certs" type="Array" elementType="in_window.org.pkijs.simpl.CERT">Array with certificate chain. Could be only one end-user certificate in there!</field>
+        this.certs = new Array();
+        /// <field name="crls" type="Array" elementType="in_window.org.pkijs.simpl.CRL">Array of all CRLs for all certificates from certificate chain</field>
+        this.crls = new Array();
+        // #endregion 
+
+        // #region Initialize internal properties by input values
+        if(arguments[0] instanceof Object)
+        {
+            this.trusted_certs = arguments[0].trusted_certs || new Array();
+            this.certs = arguments[0].certs || new Array();
+            this.crls = arguments[0].crls || new Array();
+        }
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.CERT_CHAIN.prototype.sort =
+    function()
+    {
+        // #region Initial variables 
+        /// <var type="Array" elementType="in_window.org.pkijs.simpl.CERT">Array of sorted certificates</var>
+        var sorted_certs = new Array();
+
+        /// <var type="Array" elementType="in_window.org.pkijs.simpl.CERT">Initial array of certificates</var>
+        var certs = this.certs.slice(0); // Explicity copy "this.certs"
+
+        /// <var type="Date">Date for checking certificate validity period</var>
+        var check_date = new Date();
+
+        var _this = this;
+        // #endregion 
+
+        // #region Initial checks 
+        if(certs.length === 0)
+            return new Promise(function(resolve, reject)
+            {
+                reject({
+                    result: false,
+                    result_code: 2,
+                    result_message: "Certificate's array can not be empty"
+                });
+            });
+        // #endregion 
+
+        // #region Find end-user certificate 
+        var end_user_index = -1;
+
+        for(var i = 0; i < certs.length; i++)
+        {
+            var isCA = false;
+
+            if("extensions" in certs[i])
+            {
+                var mustBeCA = false;
+                var keyUsagePresent = false;
+                var cRLSign = false;
+
+                for(var j = 0; j < certs[i].extensions.length; j++)
+                {
+                    if((certs[i].extensions[j].critical === true) &&
+                       (("parsedValue" in certs[i].extensions[j]) === false))
+                    {
+                        return new Promise(function(resolve, reject)
+                        {
+                            reject({
+                                result: false,
+                                result_code: 6,
+                                result_message: "Unable to parse critical certificate extension: " + certs[i].extensions[j].extnID
+                            });
+                        });
+                    }
+
+                    if(certs[i].extensions[j].extnID === "2.5.29.15") // KeyUsage
+                    {
+                        keyUsagePresent = true;
+
+                        var view = new Uint8Array(certs[i].extensions[j].parsedValue.value_block.value_hex);
+
+                        if((view[0] & 0x04) === 0x04) // Set flag "keyCertSign"
+                            mustBeCA = true;
+
+                        if((view[0] & 0x02) === 0x02) // Set flag "cRLSign"
+                            cRLSign = true;
+                    }
+
+                    if(certs[i].extensions[j].extnID === "2.5.29.19") // BasicConstraints
+                    {
+                        if("cA" in certs[i].extensions[j].parsedValue)
+                        {
+                            if(certs[i].extensions[j].parsedValue.cA === true)
+                                isCA = true;
+                        }
+                    }
+                }
+
+                if((mustBeCA === true) && (isCA === false))
+                    return new Promise(function(resolve, reject)
+                    {
+                        reject({
+                            result: false,
+                            result_code: 3,
+                            result_message: "Unable to build certificate chain - using \"keyCertSign\" flag set without BasicConstaints"
+                        });
+                    });
+
+                if((keyUsagePresent === true) && (isCA === true) && (mustBeCA === false))
+                    return new Promise(function(resolve, reject)
+                    {
+                        reject({
+                            result: false,
+                            result_code: 4,
+                            result_message: "Unable to build certificate chain - \"keyCertSign\" flag was not set"
+                        });
+                    });
+
+                if((isCA === true) && (keyUsagePresent === true) && (cRLSign === false))
+                    return new Promise(function(resolve, reject)
+                    {
+                        reject({
+                            result: false,
+                            result_code: 5,
+                            result_message: "Unable to build certificate chain - intermediate certificate must have \"cRLSign\" key usage flag"
+                        });
+                    });
+            }
+
+            if(isCA === false)
+            {
+                if(sorted_certs.length !== 0)
+                    return new Promise(function(resolve, reject)
+                    {
+                        reject({
+                            result: false,
+                            result_code: 7,
+                            result_message: "Unable to build certificate chain - more than one possible end-user certificate"
+                        });
+                    });
+
+                sorted_certs.push(certs[i]);
+                end_user_index = i;
+            }
+        }
+
+        certs.splice(end_user_index, 1);
+        // #endregion 
+
+        // #region Check that end-user certificate was found 
+        if(sorted_certs.length === 0)
+            return new Promise(function(resolve, reject)
+            {
+                reject({
+                    result: false,
+                    result_code: 1,
+                    result_message: "Can't find end-user certificate"
+                });
+            });
+        // #endregion 
+
+        // #region Return if there is only one certificate in certificate's array 
+        if(certs.length === 0)
+        {
+            if(sorted_certs[0].issuer.isEqual(sorted_certs[0].subject) === true)
+                return new Promise(function(resolve, reject) { resolve(sorted_certs); });
+            else
+            {
+                if(this.trusted_certs.length === 0)
+                {
+                    return new Promise(function(resolve, reject)
+                    {
+                        reject({
+                            result: false,
+                            result_code: 70,
+                            result_message: "Can't find root certificate"
+                        });
+                    });
+                }
+                else
+                {
+                    certs = _this.trusted_certs.splice(0);
+                }
+            }
+
+        }
+        // #endregion 
+
+        /// <var type="in_window.org.pkijs.simpl.CERT">Current certificate (to find issuer for)</var>
+        var current_certificate = sorted_certs[0];
+
+        // #region Auxiliary functions working with Promises
+        function basic(subject_certificate, issuer_certificate)
+        {
+            /// <summary>Basic certificate checks</summary>
+            /// <param name="subject_certificate" type="in_window.org.pkijs.simpl.CERT">Certificate for testing (subject)</param>
+            /// <param name="issuer_certificate" type="in_window.org.pkijs.simpl.CERT">Certificate for issuer of subject certificate</param>
+
+            // #region Initial variables 
+            var sequence = Promise.resolve();
+            // #endregion 
+
+            // #region Check validity period for subject certificate 
+            sequence = sequence.then(
+                function()
+                {
+                    if((subject_certificate.notBefore.value > check_date) ||
+                       (subject_certificate.notAfter.value < check_date))
+                    {
+                        return new Promise(function(resolve, reject)
+                        {
+                            reject({
+                                result: false,
+                                result_code: 8,
+                                result_message: "Certificate validity period is out of checking date"
+                            });
+                        });
+                    }
+                }
+                );
+            // #endregion 
+
+            // #region Give ability to not provide CRLs (all certificates assume to be valid) 
+            if(_this.crls.length === 0)
+                return sequence.then(
+                    function()
+                    {
+                        return new Promise(function(resolve, reject) { resolve(); });
+                    }
+                    );
+            // #endregion 
+
+            // #region Find correct CRL for "issuer_certificate" 
+            function find_crl(index)
+            {
+                return _this.crls[index].verify({ issuerCertificate: issuer_certificate }).then(
+                    function(result)
+                    {
+                        if(result === true)
+                            return new Promise(function(resolve, reject) { resolve(_this.crls[index]); });
+                        else
+                        {
+                            index++;
+
+                            if(index < _this.crls.length)
+                                return find_crl(index);
+                            else
+                                return new Promise(function(resolve, reject)
+                                {
+                                    reject({
+                                        result: false,
+                                        result_code: 9,
+                                        result_message: "Unable to find CRL for issuer's certificate"
+                                    });
+                                });
+                        }
+                    },
+                    function(error)
+                    {
+                        return new Promise(function(resolve, reject)
+                        {
+                            reject({
+                                result: false,
+                                result_code: 10,
+                                result_message: "Unable to find CRL for issuer's certificate"
+                            });
+                        });
+                    }
+                    );
+            }
+
+            sequence = sequence.then(
+                function()
+                {
+                    return find_crl(0);
+                }
+                );
+            // #endregion 
+
+            // #region Check that subject certificate is not in the CRL 
+            sequence = sequence.then(
+                function(crl)
+                {
+                    /// <param name="crl" type="in_window.org.pkijs.simpl.CRL">CRL for issuer's certificate</param>                
+
+                    if(crl.isCertificateRevoked({ certificate: subject_certificate }) === true)
+                        return new Promise(function(resolve, reject)
+                        {
+                            reject({
+                                result: false,
+                                result_code: 11,
+                                result_message: "Subject certificate was revoked"
+                            });
+                        });
+                    else
+                        return new Promise(function(resolve, reject) { resolve(); });
+                },
+                function(error)
+                {
+                    /// <summary>Not for all certificates we have a CRL. So, this "stub" is for handling such situation - assiming we have a valid, non-revoked certificate</summary>
+                    return new Promise(function(resolve, reject) { resolve(); });
+                }
+                );
+            // #endregion 
+
+            return sequence;
+        }
+
+        function outer()
+        {
+            return inner(current_certificate, 0).then(
+                function(index)
+                {
+                    sorted_certs.push(certs[index]);
+                    current_certificate = certs[index];
+
+                    certs.splice(index, 1);
+
+                    if(current_certificate.issuer.isEqual(current_certificate.subject) === true)
+                    {
+                        // #region Check that the "self-signed" certificate there is in "trusted_certs" array 
+                        var found = (_this.trusted_certs.length === 0); // If user did not set "trusted_certs" then we have an option to trust any self-signed certificate as root
+
+                        for(var i = 0; i < _this.trusted_certs.length; i++)
+                        {
+                            if((current_certificate.issuer.isEqual(_this.trusted_certs[i].issuer) === true) &&
+                               (current_certificate.subject.isEqual(_this.trusted_certs[i].subject) === true) &&
+                               (current_certificate.serialNumber.isEqual(_this.trusted_certs[i].serialNumber) === true))
+                            {
+                                found = true;
+                                break;
+                            }
+                        }
+
+                        if(found === false)
+                            return new Promise(function(resolve, reject)
+                            {
+                                reject({
+                                    result: false,
+                                    result_code: 22,
+                                    result_message: "Self-signed root certificate not in \"trusted certificates\" array"
+                                });
+                            });
+                        // #endregion 
+
+                        return (current_certificate.verify()).then( // Verifing last, self-signed certificate
+                            function(result)
+                            {
+                                if(result === true)
+                                    return basic(current_certificate, current_certificate).then(
+                                        function()
+                                        {
+                                            return new Promise(function(resolve, reject) { resolve(sorted_certs); });
+                                        },
+                                        function(error)
+                                        {
+                                            return new Promise(function(resolve, reject)
+                                            {
+                                                reject({
+                                                    result: false,
+                                                    result_code: 12,
+                                                    result_message: error
+                                                });
+                                            });
+                                        }
+                                        );
+                                else
+                                    return new Promise(function(resolve, reject)
+                                    {
+                                        reject({
+                                            result: false,
+                                            result_code: 13,
+                                            result_message: "Unable to build certificate chain - signature of root certificate is invalid"
+                                        });
+                                    });
+                            },
+                            function(error)
+                            {
+                                return new Promise(function(resolve, reject)
+                                {
+                                    reject({
+                                        result: false,
+                                        result_code: 14,
+                                        result_message: error
+                                    });
+                                });
+                            }
+                            );
+                    }
+                    else // In case if self-signed cert for the chain in the "trusted_certs" array
+                    {
+                        if(certs.length > 0)
+                            return outer();
+                        else
+                        {
+                            if(_this.trusted_certs.length !== 0)
+                            {
+                                certs = _this.trusted_certs.splice(0);
+                                return outer();
+                            }
+                            else
+                                return new Promise(function(resolve, reject)
+                                {
+                                    reject({
+                                        result: false,
+                                        result_code: 23,
+                                        result_message: "Root certificate not found"
+                                    });
+                                });
+                        }
+                    }
+                },
+                function(error)
+                {
+                    return new Promise(function(resolve, reject)
+                    {
+                        reject(error);
+                    });
+                }
+                );
+        }
+
+        function inner(current_certificate, index)
+        {
+            if(certs[index].subject.isEqual(current_certificate.issuer) === true)
+            {
+                return current_certificate.verify({ issuerCertificate: certs[index] }).then(
+                    function(result)
+                    {
+                        if(result === true)
+                        {
+                            return basic(current_certificate, certs[index]).then(
+                                function()
+                                {
+                                    return new Promise(function(resolve, reject) { resolve(index); });
+                                },
+                                function(error)
+                                {
+                                    return new Promise(function(resolve, reject)
+                                    {
+                                        reject({
+                                            result: false,
+                                            result_code: 16,
+                                            result_message: error
+                                        });
+                                    });
+                                }
+                                );
+                        }
+                        else
+                        {
+                            if(index < (certs.length - 1))
+                                return inner(current_certificate, index + 1);
+                            else
+                                return new Promise(function(resolve, reject)
+                                {
+                                    reject({
+                                        result: false,
+                                        result_code: 17,
+                                        result_message: "Unable to build certificate chain - incomplete certificate chain or signature of some certificate is invalid"
+                                    });
+                                });
+                        }
+                    },
+                    function(error)
+                    {
+                        return new Promise(function(resolve, reject)
+                        {
+                            reject({
+                                result: false,
+                                result_code: 18,
+                                result_message: "Unable to build certificate chain - error during certificate signature verification"
+                            });
+                        });
+                    }
+                    );
+            }
+            else
+            {
+                if(index < (certs.length - 1))
+                    return inner(current_certificate, index + 1);
+                else
+                    return new Promise(function(resolve, reject)
+                    {
+                        reject({
+                            result: false,
+                            result_code: 19,
+                            result_message: "Unable to build certificate chain - incomplete certificate chain"
+                        });
+                    });
+            }
+        }
+        // #endregion   
+
+        // #region Find certificates for all issuers 
+        return outer();
+        // #endregion 
+    };
+    //**************************************************************************************
+    in_window.org.pkijs.simpl.CERT_CHAIN.prototype.verify =
+    function()
+    {
+        // #region Initial checks 
+        if(this.certs.length === 0)
+            return new Promise(function(resolve, reject) { reject("Empty certificate array"); });
+        // #endregion 
+
+        // #region Initial variables 
+        var sequence = Promise.resolve();
+
+        var _this = this;
+        // #endregion 
+
+        // #region Get input variables 
+        var initial_policy_set = new Array();
+        initial_policy_set.push("2.5.29.32.0"); // "anyPolicy"
+
+        var initial_explicit_policy = false;
+        var initial_policy_mapping_inhibit = false;
+        var initial_inhibit_policy = false;
+
+        var initial_permitted_subtrees_set = new Array(); // Array of "simpl.x509.GeneralSubtree"
+        var initial_excluded_subtrees_set = new Array();  // Array of "simpl.x509.GeneralSubtree"
+        var initial_required_name_forms = new Array();    // Array of "simpl.x509.GeneralSubtree"
+
+        var verification_time = new Date();
+
+        if(arguments[0] instanceof Object)
+        {
+            if("initial_policy_set" in arguments[0])
+                initial_policy_set = arguments[0].initial_policy_set;
+
+            if("initial_explicit_policy" in arguments[0])
+                initial_explicit_policy = arguments[0].initial_explicit_policy;
+
+            if("initial_policy_mapping_inhibit" in arguments[0])
+                initial_policy_mapping_inhibit = arguments[0].initial_policy_mapping_inhibit;
+
+            if("initial_inhibit_policy" in arguments[0])
+                initial_inhibit_policy = arguments[0].initial_inhibit_policy;
+
+            if("initial_permitted_subtrees_set" in arguments[0])
+                initial_permitted_subtrees_set = arguments[0].initial_permitted_subtrees_set;
+
+            if("initial_excluded_subtrees_set" in arguments[0])
+                initial_excluded_subtrees_set = arguments[0].initial_excluded_subtrees_set;
+
+            if("initial_required_name_forms" in arguments[0])
+                initial_required_name_forms = arguments[0].initial_required_name_forms;
+        }
+
+        var explicit_policy_indicator = initial_explicit_policy;
+        var policy_mapping_inhibit_indicator = initial_policy_mapping_inhibit;
+        var inhibit_any_policy_indicator = initial_inhibit_policy;
+
+        var pending_constraints = new Array(3);
+        pending_constraints[0] = false; // For "explicit_policy_pending"
+        pending_constraints[1] = false; // For "policy_mapping_inhibit_pending"
+        pending_constraints[2] = false; // For "inhibit_any_policy_pending"
+
+        var explicit_policy_pending = 0;
+        var policy_mapping_inhibit_pending = 0;
+        var inhibit_any_policy_pending = 0;
+
+        var permitted_subtrees = initial_permitted_subtrees_set;
+        var excluded_subtrees = initial_excluded_subtrees_set;
+        var required_name_forms = initial_required_name_forms;
+
+        var path_depth = 1;
+        // #endregion 
+
+        // #region Sorting certificates in the chain array 
+        sequence = (in_window.org.pkijs.simpl.CERT_CHAIN.prototype.sort.call(this)).then(
+            function(sorted_certs)
+            {
+                _this.certs = sorted_certs;
+            }
+            );
+        // #endregion 
+
+        // #region Work with policies
+        sequence = sequence.then(
+            function()
+            {
+                // #region Support variables 
+                var all_policies = new Array(); // Array of all policies (string values)
+                all_policies.push("2.5.29.32.0"); // Put "anyPolicy" at first place
+
+                var policies_and_certs = new Array(); // In fact "array of array" where rows are for each specific policy, column for each certificate and value is "true/false"
+
+                var any_policy_array = new Array(_this.certs.length - 1); // Minus "trusted anchor"
+                for(var ii = 0; ii < (_this.certs.length - 1) ; ii++)
+                    any_policy_array[ii] = true;
+
+                policies_and_certs.push(any_policy_array);
+
+                var policy_mappings = new Array(_this.certs.length - 1); // Array of "PolicyMappings" for each certificate
+                var cert_policies = new Array(_this.certs.length - 1); // Array of "CertificatePolicies" for each certificate
+                // #endregion 
+
+                for(var i = (_this.certs.length - 2) ; i >= 0 ; i--, path_depth++)
+                {
+                    if("extensions" in _this.certs[i])
+                    {
+                        for(var j = 0; j < _this.certs[i].extensions.length; j++)
+                        {
+                            // #region CertificatePolicies 
+                            if(_this.certs[i].extensions[j].extnID === "2.5.29.32")
+                            {
+                                cert_policies[i] = _this.certs[i].extensions[j].parsedValue;
+
+                                for(var k = 0; k < _this.certs[i].extensions[j].parsedValue.certificatePolicies.length; k++)
+                                {
+                                    var policy_index = (-1);
+
+                                    // #region Try to find extension in "all_policies" array 
+                                    for(var s = 0; s < all_policies.length; s++)
+                                    {
+                                        if(_this.certs[i].extensions[j].parsedValue.certificatePolicies[k].policyIdentifier === all_policies[s])
+                                        {
+                                            policy_index = s;
+                                            break;
+                                        }
+                                    }
+                                    // #endregion 
+
+                                    if(policy_index === (-1))
+                                    {
+                                        all_policies.push(_this.certs[i].extensions[j].parsedValue.certificatePolicies[k].policyIdentifier);
+
+                                        var cert_array = new Array(_this.certs.length - 1);
+                                        cert_array[i] = true;
+
+                                        policies_and_certs.push(cert_array);
+                                    }
+                                    else(policies_and_certs[policy_index])[i] = true;
+                                }
+                            }
+                            // #endregion 
+
+                            // #region PolicyMappings 
+                            if(_this.certs[i].extensions[j].extnID === "2.5.29.33")
+                                policy_mappings[i] = _this.certs[i].extensions[j].parsedValue;
+                            // #endregion 
+
+                            // #region PolicyConstraints 
+                            if(_this.certs[i].extensions[j].extnID === "2.5.29.36")
+                            {
+                                if(explicit_policy_indicator == false)
+                                {
+                                    // #region requireExplicitPolicy 
+                                    if(_this.certs[i].extensions[j].parsedValue.requireExplicitPolicy === 0)
+                                        explicit_policy_indicator = true;
+                                    else
+                                    {
+                                        if(pending_constraints[0] === false)
+                                        {
+                                            pending_constraints[0] = true;
+                                            explicit_policy_pending = _this.certs[i].extensions[j].parsedValue.requireExplicitPolicy;
+                                        }
+                                        else
+                                        {
+                                            explicit_policy_pending = (explicit_policy_pending > _this.certs[i].extensions[j].parsedValue.requireExplicitPolicy) ? _this.certs[i].extensions[j].parsedValue.requireExplicitPolicy : explicit_policy_pending;
+                                        }
+                                    }
+                                    // #endregion 
+
+                                    // #region inhibitPolicyMapping 
+                                    if(_this.certs[i].extensions[j].parsedValue.inhibitPolicyMapping === 0)
+                                        policy_mapping_inhibit_indicator = true;
+                                    else
+                                    {
+                                        if(pending_constraints[1] === false)
+                                        {
+                                            pending_constraints[1] = true;
+                                            policy_mapping_inhibit_pending = _this.certs[i].extensions[j].parsedValue.requireExplicitPolicy;
+                                        }
+                                        else
+                                        {
+                                            policy_mapping_inhibit_pending = (policy_mapping_inhibit_pending > _this.certs[i].extensions[j].parsedValue.requireExplicitPolicy) ? _this.certs[i].extensions[j].parsedValue.requireExplicitPolicy : policy_mapping_inhibit_pending;
+                                        }
+                                    }
+                                    // #endregion   
+                                }
+                            }
+                            // #endregion 
+
+                            // #region InhibitAnyPolicy
+                            if(_this.certs[i].extensions[j].extnID === "2.5.29.54")
+                            {
+                                if(inhibit_any_policy_indicator === false)
+                                {
+                                    if(_this.certs[i].extensions[j].parsedValue.value_block.value_dec === 0)
+                                        inhibit_any_policy_indicator = true;
+                                    else
+                                    {
+                                        if(pending_constraints[2] === false)
+                                        {
+                                            pending_constraints[2] = true;
+                                            inhibit_any_policy_pending = _this.certs[i].extensions[j].parsedValue.value_block.value_dec;
+                                        }
+                                        else
+                                        {
+                                            inhibit_any_policy_pending = (inhibit_any_policy_pending > _this.certs[i].extensions[j].parsedValue.value_block.value_dec) ? _this.certs[i].extensions[j].parsedValue.value_block.value_dec : inhibit_any_policy_pending;
+                                        }
+                                    }
+                                }
+                            }
+                            // #endregion 
+                        }
+
+                        // #region Check "inhibit_any_policy_indicator" 
+                        if(inhibit_any_policy_indicator === true)
+                            delete (policies_and_certs[0])[i]; // Unset value to "undefined" for "anyPolicies" value for current certificate
+                        // #endregion 
+
+                        // #region Combine information from certificate policies and policy mappings 
+                        if((typeof cert_policies[i] !== "undefined") &&
+                           (typeof policy_mappings[i] !== "undefined") &&
+                           (policy_mapping_inhibit_indicator === false))
+                        {
+                            for(var m = 0; m < cert_policies[i].certificatePolicies.length; m++)
+                            {
+                                var domainPolicy = "";
+
+                                // #region Find if current policy is in "mappings" array 
+                                for(var n = 0; n < policy_mappings[i].mappings.length; n++)
+                                {
+                                    if(policy_mappings[i].mappings[n].subjectDomainPolicy === cert_policies[i].certificatePolicies[m].policyIdentifier)
+                                    {
+                                        domainPolicy = policy_mappings[i].mappings[n].issuerDomainPolicy;
+                                        break;
+                                    }
+
+                                    // #region Could be the case for some reasons 
+                                    if(policy_mappings[i].mappings[n].issuerDomainPolicy === cert_policies[i].certificatePolicies[m].policyIdentifier)
+                                    {
+                                        domainPolicy = policy_mappings[i].mappings[n].subjectDomainPolicy;
+                                        break;
+                                    }
+                                    // #endregion 
+                                }
+
+                                if(domainPolicy === "")
+                                    continue;
+                                // #endregion
+
+                                // #region Find the index of "domainPolicy"  
+                                var domainPolicy_index = (-1);
+
+                                for(var p = 0; p < all_policies.length; p++)
+                                {
+                                    if(all_policies[p] === domainPolicy)
+                                    {
+                                        domainPolicy_index = p;
+                                        break;
+                                    }
+                                }
+                                // #endregion 
+
+                                // #region Change array value for "domainPolicy" 
+                                if(domainPolicy_index !== (-1))
+                                    (policies_and_certs[domainPolicy_index])[i] = true; // Put "set" in "domainPolicy" cell for specific certificate
+                                // #endregion 
+                            }
+                        }
+                        // #endregion 
+
+                        // #region Process with "pending constraints" 
+                        if(explicit_policy_indicator === false)
+                        {
+                            if(pending_constraints[0] === true)
+                            {
+                                explicit_policy_pending--;
+                                if(explicit_policy_pending === 0)
+                                {
+                                    explicit_policy_indicator = true;
+                                    pending_constraints[0] = false;
+                                }
+                            }
+                        }
+
+                        if(policy_mapping_inhibit_indicator === false)
+                        {
+                            if(pending_constraints[1] === true)
+                            {
+                                policy_mapping_inhibit_pending--;
+                                if(policy_mapping_inhibit_pending === 0)
+                                {
+                                    policy_mapping_inhibit_indicator = true;
+                                    pending_constraints[1] = false;
+                                }
+                            }
+                        }
+
+                        if(inhibit_any_policy_indicator === false)
+                        {
+                            if(pending_constraints[2] === true)
+                            {
+                                inhibit_any_policy_pending--;
+                                if(inhibit_any_policy_pending === 0)
+                                {
+                                    inhibit_any_policy_indicator = true;
+                                    pending_constraints[2] = false;
+                                }
+                            }
+                        }
+                        // #endregion 
+                    }
+                }
+
+                // #region Create "set of authorities-constrained policies"
+                var auth_constr_policies = new Array();
+
+                for(var i = 0; i < policies_and_certs.length; i++)
+                {
+                    var found = true;
+
+                    for(var j = 0; j < (_this.certs.length - 1) ; j++)
+                    {
+                        if(typeof (policies_and_certs[i])[j] === "undefined")
+                        {
+                            found = false;
+                            break;
+                        }
+                    }
+
+                    if(found === true)
+                        auth_constr_policies.push(all_policies[i]);
+                }
+                // #endregion 
+
+                // #region Create "set of user-constrained policies"
+                var user_constr_policies = new Array();
+
+                for(var i = 0; i < auth_constr_policies.length; i++)
+                {
+                    for(var j = 0; j < initial_policy_set.length; j++)
+                    {
+                        if(initial_policy_set[j] === auth_constr_policies[i])
+                        {
+                            user_constr_policies.push(initial_policy_set[j]);
+                            break;
+                        }
+                    }
+                }
+                // #endregion 
+
+                // #region Combine output object 
+                return {
+                    result: (user_constr_policies.length > 0),
+                    result_code: 0,
+                    result_message: (user_constr_policies.length > 0) ? "" : "Zero \"user_constr_policies\" array, no intersections with \"auth_constr_policies\"",
+                    auth_constr_policies: auth_constr_policies,
+                    user_constr_policies: user_constr_policies,
+                    explicit_policy_indicator: explicit_policy_indicator,
+                    policy_mappings: policy_mappings
+                };
+                // #endregion 
+            }
+            );
+        // #endregion 
+
+        // #region Work with name constraints
+        sequence = sequence.then(
+            function(policy_result)
+            {
+                // #region Auxiliary functions for name constraints checking
+                function compare_dNSName(name, constraint)
+                {
+                    /// <summary>Compare two dNSName values</summary>
+                    /// <param name="name" type="String">DNS from name</param>
+                    /// <param name="constraint" type="String">Constraint for DNS from name</param>
+                    /// <returns type="Boolean">Boolean result - valid or invalid the "name" against the "constraint"</returns>
+
+                    // #region Make a "string preparation" for both name and constrain 
+                    var name_prepared = in_window.org.pkijs.stringPrep(name);
+                    var constraint_prepared = in_window.org.pkijs.stringPrep(constraint);
+                    // #endregion 
+
+                    // #region Make a "splitted" versions of "constraint" and "name" 
+                    var name_splitted = name_prepared.split(".");
+                    var constraint_splitted = constraint_prepared.split(".");
+                    // #endregion 
+
+                    // #region Length calculation and additional check 
+                    var name_len = name_splitted.length;
+                    var constr_len = constraint_splitted.length;
+
+                    if((name_len === 0) || (constr_len === 0) || (name_len < constr_len))
+                        return false;
+                    // #endregion 
+
+                    // #region Check that no part of "name" has zero length 
+                    for(var i = 0; i < name_len; i++)
+                    {
+                        if(name_splitted[i].length === 0)
+                            return false;
+                    }
+                    // #endregion 
+
+                    // #region Check that no part of "constraint" has zero length
+                    for(var i = 0; i < constr_len; i++)
+                    {
+                        if(constraint_splitted[i].length === 0)
+                        {
+                            if(i === 0)
+                            {
+                                if(constr_len === 1)
+                                    return false;
+                                else
+                                    continue;
+                            }
+
+                            return false;
+                        }
+                    }
+                    // #endregion 
+
+                    // #region Check that "name" has a tail as "constraint" 
+
+                    for(var i = 0; i < constr_len; i++)
+                    {
+                        if(constraint_splitted[constr_len - 1 - i].length === 0)
+                            continue;
+
+                        if(name_splitted[name_len - 1 - i].localeCompare(constraint_splitted[constr_len - 1 - i]) !== 0)
+                            return false;
+                    }
+                    // #endregion 
+
+                    return true;
+                }
+
+                function compare_rfc822Name(name, constraint)
+                {
+                    /// <summary>Compare two rfc822Name values</summary>
+                    /// <param name="name" type="String">E-mail address from name</param>
+                    /// <param name="constraint" type="String">Constraint for e-mail address from name</param>
+                    /// <returns type="Boolean">Boolean result - valid or invalid the "name" against the "constraint"</returns>
+
+                    // #region Make a "string preparation" for both name and constrain 
+                    var name_prepared = in_window.org.pkijs.stringPrep(name);
+                    var constraint_prepared = in_window.org.pkijs.stringPrep(constraint);
+                    // #endregion 
+
+                    // #region Make a "splitted" versions of "constraint" and "name" 
+                    var name_splitted = name_prepared.split("@");
+                    var constraint_splitted = constraint_prepared.split("@");
+                    // #endregion 
+
+                    // #region Splitted array length checking 
+                    if((name_splitted.length === 0) || (constraint_splitted.length === 0) || (name_splitted.length < constraint_splitted.length))
+                        return false;
+                    // #endregion 
+
+                    if(constraint_splitted.length === 1)
+                    {
+                        var result = compare_dNSName(name_splitted[1], constraint_splitted[0]);
+
+                        if(result)
+                        {
+                            // #region Make a "splitted" versions of domain name from "constraint" and "name" 
+                            var ns = name_splitted[1].split(".");
+                            var cs = constraint_splitted[0].split(".");
+                            // #endregion 
+
+                            if(cs[0].length === 0)
+                                return true;
+
+                            return ns.length === cs.length;
+                        }
+                        else
+                            return false;
+                    }
+                    else
+                        return (name_prepared.localeCompare(constraint_prepared) === 0);
+
+                    return false;
+                }
+
+                function compare_uniformResourceIdentifier(name, constraint)
+                {
+                    /// <summary>Compare two uniformResourceIdentifier values</summary>
+                    /// <param name="name" type="String">uniformResourceIdentifier from name</param>
+                    /// <param name="constraint" type="String">Constraint for uniformResourceIdentifier from name</param>
+                    /// <returns type="Boolean">Boolean result - valid or invalid the "name" against the "constraint"</returns>
+
+                    // #region Make a "string preparation" for both name and constrain 
+                    var name_prepared = in_window.org.pkijs.stringPrep(name);
+                    var constraint_prepared = in_window.org.pkijs.stringPrep(constraint);
+                    // #endregion 
+
+                    // #region Find out a major URI part to compare with
+                    var ns = name_prepared.split("/");
+                    var cs = constraint_prepared.split("/");
+
+                    if(cs.length > 1) // Malformed constraint
+                        return false;
+
+                    if(ns.length > 1) // Full URI string
+                    {
+                        for(var i = 0; i < ns.length; i++)
+                        {
+                            if((ns[i].length > 0) && (ns[i].charAt(ns[i].length - 1) !== ':'))
+                            {
+                                var ns_port = ns[i].split(":");
+                                name_prepared = ns_port[0];
+                                break;
+                            }
+                        }
+                    }
+                    // #endregion 
+
+                    var result = compare_dNSName(name_prepared, constraint_prepared);
+
+                    if(result)
+                    {
+                        // #region Make a "splitted" versions of "constraint" and "name" 
+                        var name_splitted = name_prepared.split(".");
+                        var constraint_splitted = constraint_prepared.split(".");
+                        // #endregion 
+
+                        if(constraint_splitted[0].length === 0)
+                            return true;
+
+                        return name_splitted.length === constraint_splitted.length;
+                    }
+                    else
+                        return false;
+
+                    return false;
+                }
+
+                function compare_iPAddress(name, constraint)
+                {
+                    /// <summary>Compare two iPAddress values</summary>
+                    /// <param name="name" type="in_window.org.pkijs.asn1.OCTETSTRING">iPAddress from name</param>
+                    /// <param name="constraint" type="in_window.org.pkijs.asn1.OCTETSTRING">Constraint for iPAddress from name</param>
+                    /// <returns type="Boolean">Boolean result - valid or invalid the "name" against the "constraint"</returns>
+
+                    // #region Common variables 
+                    var name_view = new Uint8Array(name.value_block.value_hex);
+                    var constraint_view = new Uint8Array(constraint.value_block.value_hex);
+                    // #endregion 
+
+                    // #region Work with IPv4 addresses 
+                    if((name_view.length === 4) && (constraint_view.length === 8))
+                    {
+                        for(var i = 0; i < 4; i++)
+                        {
+                            if((name_view[i] ^ constraint_view[i]) & constraint_view[i + 4])
+                                return false;
+                        }
+
+                        return true;
+                    }
+                    // #endregion 
+
+                    // #region Work with IPv6 addresses 
+                    if((name_view.length === 16) && (constraint_view.length === 32))
+                    {
+                        for(var i = 0; i < 16; i++)
+                        {
+                            if((name_view[i] ^ constraint_view[i]) & constraint_view[i + 16])
+                                return false;
+                        }
+
+                        return true;
+                    }
+                    // #endregion 
+
+                    return false;
+                }
+
+                function compare_directoryName(name, constraint)
+                {
+                    /// <summary>Compare two directoryName values</summary>
+                    /// <param name="name" type="in_window.org.pkijs.simpl.RDN">directoryName from name</param>
+                    /// <param name="constraint" type="in_window.org.pkijs.simpl.RDN">Constraint for directoryName from name</param>
+                    /// <param name="any" type="Boolean">Boolean flag - should be comparision interrupted after first match or we need to match all "constraints" parts</param>
+                    /// <returns type="Boolean">Boolean result - valid or invalid the "name" against the "constraint"</returns>
+
+                    // #region Initial check 
+                    if((name.types_and_values.length === 0) || (constraint.types_and_values.length === 0))
+                        return true;
+
+                    if(name.types_and_values.length < constraint.types_and_values.length)
+                        return false;
+                    // #endregion 
+
+                    // #region Initial variables 
+                    var result = true;
+                    var name_start = 0;
+                    // #endregion 
+
+                    for(var i = 0; i < constraint.types_and_values.length; i++)
+                    {
+                        var local_result = false;
+
+                        for(var j = name_start; j < name.types_and_values.length; j++)
+                        {
+                            local_result = name.types_and_values[j].isEqual(constraint.types_and_values[i]);
+
+                            if(name.types_and_values[j].type === constraint.types_and_values[i].type)
+                                result = result && local_result;
+
+                            if(local_result === true)
+                            {
+                                if((name_start === 0) || (name_start === j))
+                                {
+                                    name_start = j + 1;
+                                    break;
+                                }
+                                else // Structure of "name" must be the same with "constraint"
+                                    return false;
+                            }
+                        }
+
+                        if(local_result === false)
+                            return false;
+                    }
+
+                    return (name_start === 0) ? false : result;
+                }
+                // #endregion 
+
+                // #region Check a result from "policy checking" part  
+                if(policy_result.result === false)
+                    return policy_result;
+                // #endregion 
+
+                // #region Check all certificates, excluding "trust anchor" 
+                path_depth = 1;
+
+                for(var i = (_this.certs.length - 2) ; i >= 0 ; i--, path_depth++)
+                {
+                    // #region Support variables 
+                    var subject_alt_names = new Array();
+
+                    var cert_permitted_subtrees = new Array();
+                    var cert_excluded_subtrees = new Array();
+                    // #endregion 
+
+                    if("extensions" in _this.certs[i])
+                    {
+                        for(var j = 0; j < _this.certs[i].extensions.length; j++)
+                        {
+                            // #region NameConstraints 
+                            if(_this.certs[i].extensions[j].extnID === "2.5.29.30")
+                            {
+                                if("permittedSubtrees" in _this.certs[i].extensions[j].parsedValue)
+                                    cert_permitted_subtrees = cert_permitted_subtrees.concat(_this.certs[i].extensions[j].parsedValue.permittedSubtrees);
+
+                                if("excludedSubtrees" in _this.certs[i].extensions[j].parsedValue)
+                                    cert_excluded_subtrees = cert_excluded_subtrees.concat(_this.certs[i].extensions[j].parsedValue.excludedSubtrees);
+                            }
+                            // #endregion   
+
+                            // #region SubjectAltName 
+                            if(_this.certs[i].extensions[j].extnID === "2.5.29.17")
+                                subject_alt_names = subject_alt_names.concat(_this.certs[i].extensions[j].parsedValue.altNames);
+                            // #endregion 
+                        }
+                    }
+
+                    // #region Checking for "required name forms" 
+                    var form_found = (required_name_forms.length <= 0);
+
+                    for(var j = 0; j < required_name_forms.length; j++)
+                    {
+                        switch(required_name_forms[j].base.NameType)
+                        {
+                            case 4: // directoryName
+                                {
+                                    if(required_name_forms[j].base.Name.types_and_values.length !== _this.certs[i].subject.types_and_values.length)
+                                        continue;
+
+                                    form_found = true;
+
+                                    for(var k = 0; k < _this.certs[i].subject.types_and_values.length; k++)
+                                    {
+                                        if(_this.certs[i].subject.types_and_values[k].type !== required_name_forms[j].base.Name.types_and_values[k].type)
+                                        {
+                                            form_found = false;
+                                            break;
+                                        }
+                                    }
+
+                                    if(form_found === true)
+                                        break;
+                                }
+                                break;
+                            default: // ??? Probably here we should reject the certificate ???
+                        }
+                    }
+
+                    if(form_found === false)
+                    {
+                        policy_result.result = false;
+                        policy_result.result_code = 21;
+                        policy_result.result_message = "No neccessary name form found";
+
+                        return new Promise(function(resolve, reject)
+                        {
+                            reject(policy_result);
+                        });
+                    }
+                    // #endregion 
+
+                    // #region Checking for "permited sub-trees" 
+                    // #region Make groups for all types of constraints 
+                    var constr_groups = new Array(); // Array of array for groupped constraints
+                    constr_groups[0] = new Array(); // rfc822Name
+                    constr_groups[1] = new Array(); // dNSName
+                    constr_groups[2] = new Array(); // directoryName
+                    constr_groups[3] = new Array(); // uniformResourceIdentifier
+                    constr_groups[4] = new Array(); // iPAddress
+
+                    for(var j = 0; j < permitted_subtrees.length; j++)
+                    {
+                        switch(permitted_subtrees[j].base.NameType)
+                        {
+                            // #region rfc822Name 
+                            case 1:
+                                constr_groups[0].push(permitted_subtrees[j]);
+                                break;
+                                // #endregion 
+                                // #region dNSName 
+                            case 2:
+                                constr_groups[1].push(permitted_subtrees[j]);
+                                break;
+                                // #endregion 
+                                // #region directoryName 
+                            case 4:
+                                constr_groups[2].push(permitted_subtrees[j]);
+                                break;
+                                // #endregion 
+                                // #region uniformResourceIdentifier 
+                            case 6:
+                                constr_groups[3].push(permitted_subtrees[j]);
+                                break;
+                                // #endregion 
+                                // #region iPAddress 
+                            case 7:
+                                constr_groups[4].push(permitted_subtrees[j]);
+                                break;
+                                // #endregion 
+                                // #region default 
+
+                            default:
+                            // #endregion
+                        }
+                    }
+                    // #endregion   
+
+                    // #region Check name constraints groupped by type, one-by-one 
+                    for(var p = 0; p < 5; p++)
+                    {
+                        var group_permitted = false;
+                        var valueExists = false;
+                        var group = constr_groups[p];
+
+                        for(var j = 0; j < group.length; j++)
+                        {
+                            switch(p)
+                            {
+                                // #region rfc822Name 
+                                case 0:
+                                    if(subject_alt_names.length > 0)
+                                    {
+                                        for(var k = 0; k < subject_alt_names.length; k++)
+                                        {
+                                            if(subject_alt_names[k].NameType === 1) // rfc822Name
+                                            {
+                                                valueExists = true;
+                                                group_permitted = group_permitted || compare_rfc822Name(subject_alt_names[k].Name, group[j].base.Name);
+                                            }
+                                        }
+                                    }
+                                    else // Try to find out "emailAddress" inside "subject"
+                                    {
+                                        for(var k = 0; k < _this.certs[i].subject.types_and_values.length; k++)
+                                        {
+                                            if((_this.certs[i].subject.types_and_values[k].type === "1.2.840.113549.1.9.1") ||    // PKCS#9 e-mail address
+                                               (_this.certs[i].subject.types_and_values[k].type === "0.9.2342.19200300.100.1.3")) // RFC1274 "rfc822Mailbox" e-mail address
+                                            {
+                                                valueExists = true;
+                                                group_permitted = group_permitted || compare_rfc822Name(_this.certs[i].subject.types_and_values[k].value.value_block.value, group[j].base.Name);
+                                            }
+                                        }
+                                    }
+                                    break;
+                                    // #endregion 
+                                    // #region dNSName 
+                                case 1:
+                                    if(subject_alt_names.length > 0)
+                                    {
+                                        for(var k = 0; k < subject_alt_names.length; k++)
+                                        {
+                                            if(subject_alt_names[k].NameType === 2) // dNSName
+                                            {
+                                                valueExists = true;
+                                                group_permitted = group_permitted || compare_dNSName(subject_alt_names[k].Name, group[j].base.Name);
+                                            }
+                                        }
+                                    }
+                                    break;
+                                    // #endregion 
+                                    // #region directoryName 
+                                case 2:
+                                    valueExists = true;
+                                    group_permitted = compare_directoryName(_this.certs[i].subject, group[j].base.Name);
+                                    break;
+                                    // #endregion 
+                                    // #region uniformResourceIdentifier 
+                                case 3:
+                                    if(subject_alt_names.length > 0)
+                                    {
+                                        for(var k = 0; k < subject_alt_names.length; k++)
+                                        {
+                                            if(subject_alt_names[k].NameType === 6) // uniformResourceIdentifier
+                                            {
+                                                valueExists = true;
+                                                group_permitted = group_permitted || compare_uniformResourceIdentifier(subject_alt_names[k].Name, group[j].base.Name);
+                                            }
+                                        }
+                                    }
+                                    break;
+                                    // #endregion 
+                                    // #region iPAddress 
+                                case 4:
+                                    if(subject_alt_names.length > 0)
+                                    {
+                                        for(var k = 0; k < subject_alt_names.length; k++)
+                                        {
+                                            if(subject_alt_names[k].NameType === 7) // iPAddress
+                                            {
+                                                valueExists = true;
+                                                group_permitted = group_permitted || compare_iPAddress(subject_alt_names[k].Name, group[j].base.Name);
+                                            }
+                                        }
+                                    }
+                                    break;
+                                    // #endregion 
+                                    // #region default 
+
+                                default:
+                                // #endregion
+                            }
+
+                            if(group_permitted)
+                                break;
+                        }
+
+                        if((group_permitted === false) && (group.length > 0) && valueExists)
+                        {
+                            policy_result.result = false;
+                            policy_result.result_code = 41;
+                            policy_result.result_message = "Failed to meet \"permitted sub-trees\" name constraint";
+
+                            return new Promise(function(resolve, reject)
+                            {
+                                reject(policy_result);
+                            });
+                        }
+                    }
+                    // #endregion 
+                    // #endregion 
+
+                    // #region Checking for "excluded sub-trees" 
+                    var excluded = false;
+
+                    for(var j = 0; j < excluded_subtrees.length; j++)
+                    {
+                        switch(excluded_subtrees[j].base.NameType)
+                        {
+                            // #region rfc822Name 
+                            case 1:
+                                if(subject_alt_names.length >= 0)
+                                {
+                                    for(var k = 0; k < subject_alt_names.length; k++)
+                                    {
+                                        if(subject_alt_names[k].NameType === 1) // rfc822Name
+                                            excluded = excluded || compare_rfc822Name(subject_alt_names[k].Name, excluded_subtrees[j].base.Name);
+                                    }
+                                }
+                                else // Try to find out "emailAddress" inside "subject"
+                                {
+                                    for(var k = 0; k < _this.subject.types_and_values.length; k++)
+                                    {
+                                        if((_this.subject.types_and_values[k].type === "1.2.840.113549.1.9.1") ||    // PKCS#9 e-mail address
+                                           (_this.subject.types_and_values[k].type === "0.9.2342.19200300.100.1.3")) // RFC1274 "rfc822Mailbox" e-mail address
+                                        {
+                                            excluded = excluded || compare_rfc822Name(_this.subject.types_and_values[k].value.value_block.value, excluded_subtrees[j].base.Name);
+                                        }
+                                    }
+                                }
+                                break;
+                                // #endregion 
+                                // #region dNSName 
+                            case 2:
+                                if(subject_alt_names.length > 0)
+                                {
+                                    for(var k = 0; k < subject_alt_names.length; k++)
+                                    {
+                                        if(subject_alt_names[k].NameType === 2) // dNSName
+                                            excluded = excluded || compare_dNSName(subject_alt_names[k].Name, excluded_subtrees[j].base.Name);
+                                    }
+                                }
+                                break;
+                                // #endregion 
+                                // #region directoryName 
+                            case 4:
+                                excluded = excluded || compare_directoryName(_this.certs[i].subject, excluded_subtrees[j].base.Name);
+                                break;
+                                // #endregion 
+                                // #region uniformResourceIdentifier 
+                            case 6:
+                                if(subject_alt_names.length > 0)
+                                {
+                                    for(var k = 0; k < subject_alt_names.length; k++)
+                                    {
+                                        if(subject_alt_names[k].NameType === 6) // uniformResourceIdentifier
+                                            excluded = excluded || compare_uniformResourceIdentifier(subject_alt_names[k].Name, excluded_subtrees[j].base.Name);
+                                    }
+                                }
+                                break;
+                                // #endregion 
+                                // #region iPAddress 
+                            case 7:
+                                if(subject_alt_names.length > 0)
+                                {
+                                    for(var k = 0; k < subject_alt_names.length; k++)
+                                    {
+                                        if(subject_alt_names[k].NameType === 7) // iPAddress
+                                            excluded = excluded || compare_iPAddress(subject_alt_names[k].Name, excluded_subtrees[j].base.Name);
+                                    }
+                                }
+                                break;
+                                // #endregion 
+                                // #region default 
+
+                            default: // No action, but probably here we need to create a warning for "malformed constraint"
+                            // #endregion
+                        }
+
+                        if(excluded)
+                            break;
+                    }
+
+                    if(excluded === true)
+                    {
+                        policy_result.result = false;
+                        policy_result.result_code = 42;
+                        policy_result.result_message = "Failed to meet \"excluded sub-trees\" name constraint";
+
+                        return new Promise(function(resolve, reject)
+                        {
+                            reject(policy_result);
+                        });
+                    }
+                    // #endregion 
+
+                    // #region Append "cert_..._subtrees" to "..._subtrees" 
+                    permitted_subtrees = permitted_subtrees.concat(cert_permitted_subtrees);
+                    excluded_subtrees = excluded_subtrees.concat(cert_excluded_subtrees);
+                    // #endregion   
+                }
+                // #endregion 
+
+                return policy_result;
+            }
+            );
+        // #endregion   
+
+        return sequence;
+    };
+    //**************************************************************************************
+    // #endregion 
+    //**************************************************************************************
+}
+)(typeof exports !== "undefined" ? exports : window);
diff --git a/dom/webauthn/tests/test_webauthn_abort_signal.html b/dom/webauthn/tests/test_webauthn_abort_signal.html
new file mode 100644
index 0000000000..eb9f43333a
--- /dev/null
+++ b/dom/webauthn/tests/test_webauthn_abort_signal.html
@@ -0,0 +1,146 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <title>Test for aborting W3C Web Authentication request</title>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="u2futil.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+  <h1>Test for aborting W3C Web Authentication request</h1>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1415675">Mozilla Bug 1415675</a>
+
+  <script class="testbody" type="text/javascript">
+    "use strict";
+
+    function arrivingHereIsBad(aResult) {
+      ok(false, "Bad result! Received a: " + aResult);
+    }
+
+    function expectAbortError(aResult) {
+      is(aResult.code, DOMException.ABORT_ERR, "Expecting an AbortError");
+    }
+
+    add_task(async () => {
+      // Enable USB tokens.
+      await SpecialPowers.pushPrefEnv({"set": [
+        ["security.webauth.webauthn_enable_softtoken", false],
+        ["security.webauth.webauthn_enable_usbtoken", true],
+      ]});
+    });
+
+    // Start a new MakeCredential() request.
+    function requestMakeCredential(signal) {
+      let publicKey = {
+        rp: {id: document.domain, name: "none"},
+        user: {id: new Uint8Array(), name: "none", displayName: "none"},
+        challenge: crypto.getRandomValues(new Uint8Array(16)),
+        timeout: 5000, // the minimum timeout is actually 15 seconds
+        pubKeyCredParams: [{type: "public-key", alg: cose_alg_ECDSA_w_SHA256}],
+      };
+
+      return navigator.credentials.create({publicKey, signal});
+    }
+
+    // Start a new GetAssertion() request.
+    async function requestGetAssertion(signal) {
+      let newCredential = {
+        type: "public-key",
+        id: crypto.getRandomValues(new Uint8Array(16)),
+        transports: ["usb"],
+      };
+
+      let publicKey = {
+        challenge: crypto.getRandomValues(new Uint8Array(16)),
+        timeout: 5000, // the minimum timeout is actually 15 seconds
+        rpId: document.domain,
+        allowCredentials: [newCredential]
+      };
+
+      // Start the request, handle failures only.
+      return navigator.credentials.get({publicKey, signal});
+    }
+
+    // Create an AbortController and abort immediately.
+    add_task(async function test_create_abortcontroller_and_abort() {
+      let ctrl = new AbortController();
+      ctrl.abort();
+
+      // The event shouldn't fire.
+      ctrl.signal.onabort = arrivingHereIsBad;
+
+      // MakeCredential() should abort immediately.
+      await requestMakeCredential(ctrl.signal)
+        .then(arrivingHereIsBad)
+        .catch(expectAbortError);
+
+      // GetAssertion() should abort immediately.
+      await requestGetAssertion(ctrl.signal)
+        .then(arrivingHereIsBad)
+        .catch(expectAbortError);
+    });
+
+    // Request a new credential and abort the request.
+    add_task(async function test_request_credential_and_abort() {
+      let aborted = false;
+      let ctrl = new AbortController();
+
+      ctrl.signal.onabort = () => {
+        ok(!aborted, "abort event fired once");
+        aborted = true;
+      };
+
+      // Request a new credential.
+      let request = requestMakeCredential(ctrl.signal)
+        .then(arrivingHereIsBad)
+        .catch(err => {
+          ok(aborted, "abort event was fired");
+          expectAbortError(err);
+        });
+
+      // Wait a tick for the statemachine to start.
+      await Promise.resolve();
+
+      // Abort the request.
+      ok(!aborted, "request still pending");
+      ctrl.abort();
+      ok(aborted, "request aborted");
+
+      // Wait for the request to terminate.
+      await request;
+    });
+
+    // Request a new assertion and abort the request.
+    add_task(async function test_request_assertion_and_abort() {
+      let aborted = false;
+      let ctrl = new AbortController();
+
+      ctrl.signal.onabort = () => {
+        ok(!aborted, "abort event fired once");
+        aborted = true;
+      };
+
+      // Request a new assertion.
+      let request = requestGetAssertion(ctrl.signal)
+        .then(arrivingHereIsBad)
+        .catch(err => {
+          ok(aborted, "abort event was fired");
+          expectAbortError(err);
+        });
+
+      // Wait a tick for the statemachine to start.
+      await Promise.resolve();
+
+      // Abort the request.
+      ok(!aborted, "request still pending");
+      ctrl.abort();
+      ok(aborted, "request aborted");
+
+      // Wait for the request to terminate.
+      await request;
+    });
+  </script>
+
+</body>
+</html>
diff --git a/dom/webauthn/tests/test_webauthn_attestation_conveyance.html b/dom/webauthn/tests/test_webauthn_attestation_conveyance.html
new file mode 100644
index 0000000000..061e2c2624
--- /dev/null
+++ b/dom/webauthn/tests/test_webauthn_attestation_conveyance.html
@@ -0,0 +1,103 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <title>W3C Web Authentication - Attestation Conveyance</title>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="u2futil.js"></script>
+  <script type="text/javascript" src="pkijs/common.js"></script>
+  <script type="text/javascript" src="pkijs/asn1.js"></script>
+  <script type="text/javascript" src="pkijs/x509_schema.js"></script>
+  <script type="text/javascript" src="pkijs/x509_simpl.js"></script>
+  <script type="text/javascript" src="cbor.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+  <h1>W3C Web Authentication - Attestation Conveyance</h1>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1428916">Mozilla Bug 1428916</a>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1416056">Mozilla Bug 1416056</a>
+
+  <script class="testbody" type="text/javascript">
+    "use strict";
+
+    add_task(async () => {
+      await SpecialPowers.pushPrefEnv({"set": [
+        ["security.webauth.webauthn_testing_allow_direct_attestation", true],
+      ]});
+      await addVirtualAuthenticator();
+    });
+
+    function verifyAnonymizedCertificate(aResult) {
+      return webAuthnDecodeCBORAttestation(aResult.response.attestationObject)
+      .then(({fmt, attStmt}) => {
+        is(fmt, "none", "Is a None Attestation");
+        is(typeof(attStmt), "object", "attStmt is a map");
+        is(Object.keys(attStmt).length, 0, "attStmt is empty");
+      });
+    }
+
+    async function verifyDirectCertificate(aResult) {
+      let clientDataHash = await crypto.subtle.digest("SHA-256", aResult.response.clientDataJSON)
+          .then(digest => new Uint8Array(digest));
+      let {fmt, attStmt, authData, authDataObj} = await webAuthnDecodeCBORAttestation(aResult.response.attestationObject);
+      is(fmt, "packed", "Is a Packed Attestation");
+      let signedData = new Uint8Array(authData.length + clientDataHash.length);
+      signedData.set(authData);
+      signedData.set(clientDataHash, authData.length);
+      let valid = await verifySignature(authDataObj.publicKeyHandle, signedData, new Uint8Array(attStmt.sig));
+      ok(valid, "Signature is valid.");
+    }
+
+    function arrivingHereIsBad(aResult) {
+      ok(false, "Bad result! Received a: " + aResult);
+    }
+
+    function expectTypeError(aResult) {
+      ok(aResult.toString().startsWith("TypeError"), "Expecting a TypeError, got " + aResult);
+    }
+
+    // Start a new MakeCredential() request.
+    function requestMakeCredential(attestation) {
+      let publicKey = {
+        rp: {id: document.domain, name: "none"},
+        user: {id: new Uint8Array(), name: "none", displayName: "none"},
+        challenge: crypto.getRandomValues(new Uint8Array(16)),
+        timeout: 5000, // the minimum timeout is actually 15 seconds
+        pubKeyCredParams: [{type: "public-key", alg: cose_alg_ECDSA_w_SHA256}],
+        attestation,
+      };
+
+      return navigator.credentials.create({publicKey});
+    }
+
+    // Test success cases for make credential.
+    add_task(async function test_make_credential_success () {
+      // No selection criteria should be equal to none, which means anonymized
+      await requestMakeCredential()
+        .then(verifyAnonymizedCertificate)
+        .catch(arrivingHereIsBad);
+
+      // Request an unknown attestation type. This should be treated as "none".
+      await requestMakeCredential("unknown")
+        .then(verifyAnonymizedCertificate)
+        .catch(arrivingHereIsBad);
+
+      // Request no attestation.
+      await requestMakeCredential("none")
+        .then(verifyAnonymizedCertificate)
+        .catch(arrivingHereIsBad);
+
+      // Request indirect attestation, which is the same as direct.
+      await requestMakeCredential("indirect")
+        .then(verifyDirectCertificate)
+        .catch(arrivingHereIsBad);
+
+      // Request direct attestation, which will prompt for user intervention.
+      await requestMakeCredential("direct")
+        .then(verifyDirectCertificate)
+        .catch(arrivingHereIsBad);
+    });
+  </script>
+
+</body>
+</html>
diff --git a/dom/webauthn/tests/test_webauthn_authenticator_selection.html b/dom/webauthn/tests/test_webauthn_authenticator_selection.html
new file mode 100644
index 0000000000..35667493a7
--- /dev/null
+++ b/dom/webauthn/tests/test_webauthn_authenticator_selection.html
@@ -0,0 +1,152 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <title>W3C Web Authentication - Authenticator Selection Criteria</title>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="u2futil.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+  <h1>W3C Web Authentication - Authenticator Selection Criteria</h1>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1406462">Mozilla Bug 1406462</a>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1406467">Mozilla Bug 1406467</a>
+
+  <script class="testbody" type="text/javascript">
+    "use strict";
+
+    add_task(async () => {
+      await addVirtualAuthenticator();
+    });
+
+    function arrivingHereIsGood(aResult) {
+      ok(true, "Good result! Received a: " + aResult);
+    }
+
+    function arrivingHereIsBad(aResult) {
+      ok(false, "Bad result! Received a: " + aResult);
+    }
+
+    function expectNotAllowedError(aResult) {
+      ok(aResult.toString().startsWith("NotAllowedError"), "Expecting a NotAllowedError, got " + aResult);
+    }
+
+    // We store the credential of the first successful make credential
+    // operation so we can use it for get assertion tests later.
+    let gCredential;
+
+    // Start a new MakeCredential() request.
+    function requestMakeCredential(authenticatorSelection) {
+      let publicKey = {
+        rp: {id: document.domain, name: "none"},
+        user: {id: new Uint8Array(), name: "none", displayName: "none"},
+        challenge: crypto.getRandomValues(new Uint8Array(16)),
+        timeout: 5000, // the minimum timeout is actually 15 seconds
+        pubKeyCredParams: [{type: "public-key", alg: cose_alg_ECDSA_w_SHA256}],
+        authenticatorSelection,
+      };
+
+      return navigator.credentials.create({publicKey});
+    }
+
+    // Start a new GetAssertion() request.
+    function requestGetAssertion(userVerification) {
+      let newCredential = {
+        type: "public-key",
+        id: gCredential,
+        transports: ["usb"],
+      };
+
+      let publicKey = {
+        challenge: crypto.getRandomValues(new Uint8Array(16)),
+        timeout: 5000, // the minimum timeout is actually 15 seconds
+        rpId: document.domain,
+        allowCredentials: [newCredential]
+      };
+
+      if (userVerification) {
+        publicKey.userVerification = userVerification;
+      }
+
+      return navigator.credentials.get({publicKey});
+    }
+
+    // Test success cases for make credential.
+    add_task(async function test_make_credential_successes() {
+      // No selection criteria.
+      await requestMakeCredential({})
+         // Save the credential so we can use it for sign success tests.
+        .then(res => gCredential = res.rawId)
+        .then(arrivingHereIsGood)
+        .catch(arrivingHereIsBad);
+
+      // Request a cross-platform authenticator.
+      await requestMakeCredential({authenticatorAttachment: "cross-platform"})
+        .then(arrivingHereIsGood)
+        .catch(arrivingHereIsBad);
+
+      // Require a resident key.
+      await requestMakeCredential({requireResidentKey: true})
+        .then(arrivingHereIsGood)
+        .catch(arrivingHereIsBad);
+
+      // Don't require a resident key.
+      await requestMakeCredential({requireResidentKey: false})
+        .then(arrivingHereIsGood)
+        .catch(arrivingHereIsBad);
+
+      // Require user verification.
+      await requestMakeCredential({userVerification: "required"})
+        .then(arrivingHereIsGood)
+        .catch(arrivingHereIsBad);
+
+      // Prefer user verification.
+      await requestMakeCredential({userVerification: "preferred"})
+        .then(arrivingHereIsGood)
+        .catch(arrivingHereIsBad);
+
+      // Discourage user verification.
+      await requestMakeCredential({userVerification: "discouraged"})
+        .then(arrivingHereIsGood)
+        .catch(arrivingHereIsBad);
+    });
+
+    // Test success cases for get assertion.
+    add_task(async function test_get_assertion_successes() {
+      // No selection criteria.
+      await requestGetAssertion()
+        .then(arrivingHereIsGood)
+        .catch(arrivingHereIsBad);
+
+      // Require user verification.
+      await requestGetAssertion("required")
+        .then(arrivingHereIsGood)
+        .catch(arrivingHereIsBad);
+
+      // Prefer user verification.
+      await requestGetAssertion("preferred")
+        .then(arrivingHereIsGood)
+        .catch(arrivingHereIsBad);
+
+      // Discourage user verification.
+      await requestGetAssertion("discouraged")
+        .then(arrivingHereIsGood)
+        .catch(arrivingHereIsBad);
+    });
+
+    // Test failure cases for make credential.
+    add_task(async function test_make_credential_failures() {
+      // Request a platform authenticator.
+      await requestMakeCredential({authenticatorAttachment: "platform"})
+        .then(arrivingHereIsBad)
+        .catch(expectNotAllowedError);
+    });
+
+    // Test failure cases for get assertion.
+    add_task(async function test_get_assertion_failures() {
+      // No failures currently tested
+    });
+  </script>
+
+</body>
+</html>
diff --git a/dom/webauthn/tests/test_webauthn_authenticator_transports.html b/dom/webauthn/tests/test_webauthn_authenticator_transports.html
new file mode 100644
index 0000000000..0d6e6d3055
--- /dev/null
+++ b/dom/webauthn/tests/test_webauthn_authenticator_transports.html
@@ -0,0 +1,165 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <title>W3C Web Authentication - Authenticator Transports</title>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="u2futil.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+  <h1>W3C Web Authentication - Authenticator Transports</h1>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1406467">Mozilla Bug 1406467</a>
+
+  <script class="testbody" type="text/javascript">
+    "use strict";
+
+    add_task(async () => {
+      await addVirtualAuthenticator();
+    });
+
+    function arrivingHereIsGood(aResult) {
+      ok(true, "Good result! Received a: " + aResult);
+    }
+
+    function arrivingHereIsBad(aResult) {
+      ok(false, "Bad result! Received a: " + aResult);
+    }
+
+    function expectNotAllowedError(aResult) {
+      ok(aResult.toString().startsWith("NotAllowedError"), "Expecting a NotAllowedError, got " + aResult);
+    }
+
+    function expectInvalidStateError(aResult) {
+      ok(aResult.toString().startsWith("InvalidStateError"), "Expecting a InvalidStateError, got " + aResult);
+    }
+
+    // Store the credential and transports of the first successful make credential
+    // operation so we can use them to get assertions later.
+    let gCredential;
+    let gTransports;
+
+    // Start a new MakeCredential() request.
+    function requestMakeCredential(excludeCredentials) {
+      let publicKey = {
+        rp: {id: document.domain, name: "none"},
+        user: {id: new Uint8Array(), name: "none", displayName: "none"},
+        challenge: crypto.getRandomValues(new Uint8Array(16)),
+        timeout: 5000, // the minimum timeout is actually 15 seconds
+        pubKeyCredParams: [{type: "public-key", alg: cose_alg_ECDSA_w_SHA256}],
+        excludeCredentials
+      };
+
+      return navigator.credentials.create({publicKey});
+    }
+
+    // Start a new GetAssertion() request.
+    function requestGetAssertion(allowCredentials) {
+      let publicKey = {
+        challenge: crypto.getRandomValues(new Uint8Array(16)),
+        timeout: 5000, // the minimum timeout is actually 15 seconds
+        rpId: document.domain,
+        allowCredentials
+      };
+
+      return navigator.credentials.get({publicKey});
+    }
+
+    // Test make credential behavior.
+    add_task(async function test_make_credential() {
+      // Make a credential.
+      await requestMakeCredential([])
+         // Save the credential for later.
+        .then(res => {
+          gCredential = res.rawId;
+          gTransports = res.response.getTransports();
+        })
+        .then(arrivingHereIsGood)
+        .catch(arrivingHereIsBad);
+
+      // gTransports should be "a sequence of zero or more unique DOMStrings in lexicographical order."
+      for (let i = 0; i < gTransports.length - 1; i++) {
+         if (gTransports[i] >= gTransports[i+1]) {
+           ok(false, "getTransports() should return a sorted list");
+         }
+      }
+
+      // Pass a random credential to exclude.
+      await requestMakeCredential([{
+        type: "public-key",
+        id: crypto.getRandomValues(new Uint8Array(16)),
+        transports: gTransports,
+      }]).then(arrivingHereIsGood)
+         .catch(arrivingHereIsBad);
+
+      // Pass gCredential with transport=usb.
+      // The credential already exists, and the softoken consents to create,
+      // so the error is InvalidState and not NotAllowed.
+      await requestMakeCredential([{
+        type: "public-key",
+        id: gCredential,
+        transports: ["usb"],
+      }]).then(arrivingHereIsBad)
+         .catch(expectInvalidStateError);
+
+      // Pass gCredential with transport=nfc.
+      // The softoken pretends to support all transports.
+      // Also, as above, the credential exists and the token indicates consent.
+      await requestMakeCredential([{
+        type: "public-key",
+        id: gCredential,
+        transports: ["nfc"],
+      }]).then(arrivingHereIsBad)
+         .catch(expectInvalidStateError);
+
+      // Pass gCredential with an empty transports list.
+      // As above, the token indicates consent, so expect InvalidStateError.
+      await requestMakeCredential([{
+        type: "public-key",
+        id: gCredential,
+        transports: [],
+      }]).then(arrivingHereIsBad)
+         .catch(expectInvalidStateError);
+    });
+
+    // Test get assertion behavior.
+    add_task(async function test_get_assertion() {
+      // Request an assertion for gCredential.
+      await requestGetAssertion([{
+        type: "public-key",
+        id: gCredential,
+        transports: gTransports,
+      }]).then(arrivingHereIsGood)
+         .catch(arrivingHereIsBad);
+
+      // Request an assertion for a random credential. This should be
+      // indistinguishable from the user denying consent for a known
+      // credential, so expect a NotAllowedError.
+      await requestGetAssertion([{
+        type: "public-key",
+        id: crypto.getRandomValues(new Uint8Array(16)),
+        transports: ["usb"],
+      }]).then(arrivingHereIsBad)
+         .catch(expectNotAllowedError);
+
+      // Request an assertion for gCredential with transport=nfc.
+      // The softoken pretends to support all transports.
+      await requestGetAssertion([{
+        type: "public-key",
+        id: gCredential,
+        transports: ["nfc"],
+      }]).then(arrivingHereIsGood)
+         .catch(arrivingHereIsBad);
+
+      // Request an assertion for gCredential with an empty transports list.
+      await requestGetAssertion([{
+        type: "public-key",
+        id: gCredential,
+        transports: [],
+      }]).then(arrivingHereIsGood)
+         .catch(arrivingHereIsBad);
+    });
+  </script>
+
+</body>
+</html>
diff --git a/dom/webauthn/tests/test_webauthn_crossorigin_featurepolicy.html b/dom/webauthn/tests/test_webauthn_crossorigin_featurepolicy.html
new file mode 100644
index 0000000000..6e092d02ba
--- /dev/null
+++ b/dom/webauthn/tests/test_webauthn_crossorigin_featurepolicy.html
@@ -0,0 +1,258 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <title>Tests for Publickey-Credentials-Get Feature Policy for W3C Web Authentication</title>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="u2futil.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+  <h1>Tests for Publickey-Credentials-Get Feature Policy for W3C Web Authentication</h1>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1460986">Mozilla Bug 1460986</a>
+
+  <script class="testbody" type="text/javascript">
+    "use strict";
+
+    var gAuthenticatorId;
+    var gSameCredId;
+    var gCrossCredId;
+
+    const CROSS_DOMAIN = "example.org";
+
+    function compare(a, b) {
+      if (a.length != b.length) return false;
+      for (let i = 0; i < a.length; i += 1) {
+        if (a[i] !== b[i]) return false;
+      }
+      return true;
+    }
+
+    function arrivingHereIsGood(aResult) {
+      ok(true, "Good result! Received a: " + aResult);
+    }
+
+    function arrivingHereIsBad(aResult) {
+      ok(false, "Bad result! Received a: " + aResult);
+    }
+
+    function expectNotAllowedError(aResult) {
+      ok(aResult == "NotAllowedError", "Expecting a NotAllowedError, got " + aResult);
+    }
+
+    function expectSameCredId(aResult) {
+      ok(compare(aResult, gSameCredId), "Expecting credential for " + document.domain);
+    }
+
+    function expectCrossCredId(aResult) {
+      ok(compare(aResult, gCrossCredId), "Expecting credential for " + CROSS_DOMAIN);
+    }
+
+    function getAssertion(id) {
+        let chall = new Uint8Array(16);
+        this.content.window.crypto.getRandomValues(chall);
+
+        let options = {
+          challenge: chall,
+          allowCredentials: [ { type: "public-key", id } ],
+        };
+
+        return this.content.window.navigator.credentials.get({publicKey: options})
+          .then(res => Promise.resolve(new Uint8Array(res.rawId)))
+          .catch(e => Promise.reject(e.name));
+    }
+
+    function createCredential() {
+      this.content.document.notifyUserGestureActivation();
+
+      const cose_alg_ECDSA_w_SHA256 = -7;
+      let publicKey = {
+        rp: {id: this.content.window.document.domain, name: "none"},
+        user: {id: new Uint8Array(), name: "none", displayName: "none"},
+        challenge: this.content.window.crypto.getRandomValues(new Uint8Array(16)),
+        pubKeyCredParams: [{type: "public-key", alg: cose_alg_ECDSA_w_SHA256}],
+      };
+
+      return this.content.window.navigator.credentials.create({publicKey})
+          .then(res => Promise.resolve(new Uint8Array(res.rawId)))
+          .catch(e => Promise.reject(e.name));
+    }
+
+    async function setup(preloadSame, preloadCross) {
+      if (!gAuthenticatorId) {
+          gAuthenticatorId = await addVirtualAuthenticator();
+      }
+      if (gSameCredId) {
+        removeCredential(gAuthenticatorId, bytesToBase64UrlSafe(gSameCredId));
+        gSameCredId = undefined;
+      }
+      if (gCrossCredId) {
+        removeCredential(gAuthenticatorId, bytesToBase64UrlSafe(gCrossCredId));
+        gCrossCredId = undefined;
+      }
+      if (preloadSame) {
+        gSameCredId = await addCredential(gAuthenticatorId, document.domain).then(id => base64ToBytesUrlSafe(id));
+      }
+      if (preloadCross) {
+        gCrossCredId = await addCredential(gAuthenticatorId, CROSS_DOMAIN).then(id => base64ToBytesUrlSafe(id));
+      }
+    }
+
+    add_task(async function test_same_origin_iframe_allow() {
+      // Don't preload any credentials. We'll try to create one in content.
+      await setup(false, false);
+
+      let iframe = document.createElement("iframe");
+      iframe.setAttribute("src", "https://" + document.domain + "/tests/dom/webauthn/tests/empty.html");
+      document.body.appendChild(iframe);
+      await new Promise(resolve => iframe.addEventListener("load", resolve, {once: true}));
+
+      ok("featurePolicy" in iframe, "we have iframe.featurePolicy");
+      ok(iframe.featurePolicy.allowsFeature("publickey-credentials-create"), "iframe allows publickey-credentials-create");
+      ok(iframe.featurePolicy.allowsFeature("publickey-credentials-get"), "iframe allows publickey-credentials-get");
+
+      // We should be able to create a credential in a same-origin iframe by default.
+      is(gSameCredId, undefined);
+      gSameCredId = new Uint8Array(await SpecialPowers.spawn(iframe, [], createCredential));
+
+      // We should be able to assert a credential in a same-origin iframe by default.
+      await SpecialPowers.spawn(iframe, [gSameCredId], getAssertion)
+          .then(expectSameCredId)
+          .catch(expectNotAllowedError);
+    });
+
+    add_task(async function test_same_origin_iframe_deny() {
+      // Preload same-origin credential to ensure we cannot assert it.
+      await setup(true, false);
+
+      let iframe = document.createElement("iframe");
+      iframe.setAttribute("src", "https://" + document.domain + "/tests/dom/webauthn/tests/empty.html");
+      iframe.setAttribute("allow", "publickey-credentials-create 'none'; publickey-credentials-get 'none'");
+      document.body.appendChild(iframe);
+      await new Promise(resolve => iframe.addEventListener("load", resolve, {once: true}));
+
+      ok("featurePolicy" in iframe, "we have iframe.featurePolicy");
+      ok(!iframe.featurePolicy.allowsFeature("publickey-credentials-create"), "iframe does not allow publickey-credentials-create");
+      ok(!iframe.featurePolicy.allowsFeature("publickey-credentials-get"), "iframe does not allow publickey-credentials-get");
+
+      // We should not be able to create a credential in a same-origin iframe if
+      // the iframe does not allow publickey-credentials-create.
+      await SpecialPowers.spawn(iframe, [], createCredential)
+          .then(arrivingHereIsBad)
+          .catch(expectNotAllowedError);
+
+      // We should not be able to assert a credential in a same-origin iframe if
+      // the iframe does not allow publickey-credentials-get.
+      await SpecialPowers.spawn(iframe, [gSameCredId], getAssertion)
+          .then(arrivingHereIsBad)
+          .catch(expectNotAllowedError);
+    });
+
+    add_task(async function test_cross_origin_iframe_allow() {
+      // Don't preload any credentials. We'll try to create one in content.
+      await setup(false, false);
+
+      let iframe = document.createElement("iframe");
+      iframe.setAttribute("src", "https://" + CROSS_DOMAIN + "/tests/dom/webauthn/tests/empty.html");
+      iframe.setAttribute("allow", "publickey-credentials-create https://" + CROSS_DOMAIN + "; publickey-credentials-get https://" + CROSS_DOMAIN);
+      document.body.appendChild(iframe);
+      await new Promise(resolve => iframe.addEventListener("load", resolve, {once: true}));
+
+      ok("featurePolicy" in iframe, "we have iframe.featurePolicy");
+      ok(iframe.featurePolicy.allowsFeature("publickey-credentials-create"), "iframe allows publickey-credentials-create");
+      ok(iframe.featurePolicy.allowsFeature("publickey-credentials-get"), "iframe allows publickey-credentials-get");
+
+      // We should be able to create a credential in a same-origin iframe if
+      // the iframe allows publickey-credentials-create.
+      is(gCrossCredId, undefined);
+      gCrossCredId = new Uint8Array(await SpecialPowers.spawn(iframe, [], createCredential));
+
+      // We should be able to assert a credential in a cross-origin iframe if
+      // the iframe allows publickey-credentials-get.
+      await SpecialPowers.spawn(iframe, [gCrossCredId], getAssertion)
+          .then(expectCrossCredId)
+          .catch(arrivingHereIsBad);
+    });
+
+    add_task(async function test_cross_origin_iframe_deny() {
+      // Preload cross-origin credential to ensure we cannot assert it.
+      await setup(false, true);
+
+      let iframe = document.createElement("iframe");
+      iframe.setAttribute("src", "https://" + CROSS_DOMAIN + "/tests/dom/webauthn/tests/empty.html");
+      document.body.appendChild(iframe);
+      await new Promise(resolve => iframe.addEventListener("load", resolve, {once: true}));
+
+      ok("featurePolicy" in iframe, "we have iframe.featurePolicy");
+      ok(!iframe.featurePolicy.allowsFeature("publickey-credentials-create"), "iframe does not allow publickey-credentials-create");
+      ok(!iframe.featurePolicy.allowsFeature("publickey-credentials-get"), "iframe does not allow publickey-credentials-get");
+
+      // We should not be able to create a credential in a cross-origin iframe if
+      // the iframe does not allow publickey-credentials-create.
+      await SpecialPowers.spawn(iframe, [], createCredential)
+          .then(arrivingHereIsBad)
+          .catch(expectNotAllowedError);
+
+      // We should not be able to assert a credential in a cross-origin iframe if
+      // the iframe does not allow publickey-credentials-get.
+      await SpecialPowers.spawn(iframe, [gCrossCredId], getAssertion)
+          .then(arrivingHereIsBad)
+          .catch(expectNotAllowedError);
+    });
+
+    add_task(async function test_cross_origin_iframe_create_but_not_get() {
+      // Don't preload any credentials. We'll try to create one in content.
+      await setup(false, false);
+
+      let iframe = document.createElement("iframe");
+      iframe.setAttribute("src", "https://" + CROSS_DOMAIN + "/tests/dom/webauthn/tests/empty.html");
+      iframe.setAttribute("allow", "publickey-credentials-create https://" + CROSS_DOMAIN);
+      document.body.appendChild(iframe);
+      await new Promise(resolve => iframe.addEventListener("load", resolve, {once: true}));
+
+      ok("featurePolicy" in iframe, "we have iframe.featurePolicy");
+      ok(iframe.featurePolicy.allowsFeature("publickey-credentials-create"), "iframe allows publickey-credentials-create");
+      ok(!iframe.featurePolicy.allowsFeature("publickey-credentials-get"), "iframe does not allow publickey-credentials-get");
+
+      // We should be able to create a credential in a cross-origin iframe if
+      // the iframe allows publickey-credentials-create.
+      is(gCrossCredId, undefined);
+      gCrossCredId = new Uint8Array(await SpecialPowers.spawn(iframe, [], createCredential));
+
+      // We should not be able to assert a credential in a cross-origin iframe if
+      // the iframe does not allow publickey-credentials-get.
+      await SpecialPowers.spawn(iframe, [gCrossCredId], getAssertion)
+          .then(arrivingHereIsBad)
+          .catch(expectNotAllowedError);
+    });
+
+    add_task(async function test_cross_origin_iframe_get_but_not_create() {
+      // Preload cross-origin credential so we can assert it.
+      await setup(false, true);
+
+      let iframe = document.createElement("iframe");
+      iframe.setAttribute("src", "https://" + CROSS_DOMAIN + "/tests/dom/webauthn/tests/empty.html");
+      iframe.setAttribute("allow", "publickey-credentials-get https://" + CROSS_DOMAIN);
+      document.body.appendChild(iframe);
+      await new Promise(resolve => iframe.addEventListener("load", resolve, {once: true}));
+
+      ok("featurePolicy" in iframe, "we have iframe.featurePolicy");
+      ok(!iframe.featurePolicy.allowsFeature("publickey-credentials-create"), "iframe does not publickey-credentials-create");
+      ok(iframe.featurePolicy.allowsFeature("publickey-credentials-get"), "iframe allows publickey-credentials-get");
+
+      // We should not be able to create a credential in a cross-origin iframe if
+      // the iframe does not allow publickey-credentials-create.
+      await SpecialPowers.spawn(iframe, [], createCredential)
+          .then(arrivingHereIsBad)
+          .catch(expectNotAllowedError);
+
+      // We should not be able to assert a credential in a cross-origin iframe if
+      // the iframe does not allow publickey-credentials-get.
+      await SpecialPowers.spawn(iframe, [gCrossCredId], getAssertion)
+          .then(arrivingHereIsGood)
+          .catch(arrivingHereIsBad);
+    });
+  </script>
+
+</body>
+</html>
diff --git a/dom/webauthn/tests/test_webauthn_ctap2_omitted_credential_id.html b/dom/webauthn/tests/test_webauthn_ctap2_omitted_credential_id.html
new file mode 100644
index 0000000000..a2c14eb91d
--- /dev/null
+++ b/dom/webauthn/tests/test_webauthn_ctap2_omitted_credential_id.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <title>Tests for omitted credential ID in a CTAP 2.0 authenticator response</title>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="u2futil.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+  <h1>Tests for omitted credential ID in a CTAP 2.0 authenticator response</h1>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1864504">Mozilla Bug 1864504</a>
+
+  <script class="testbody" type="text/javascript">
+    "use strict";
+
+    add_task(async () => {
+      // CTAP 2.0 allows GetAssertion responses to omit a credential
+      // ID if the allowlist has length one. This can cause problems in
+      // MakeCredential as well because GetAssertion is used for pre-flighting.
+      await addVirtualAuthenticator("ctap2");
+    });
+
+    let validCred = null;
+
+    add_task(test_setup_valid_credential);
+    add_task(test_create_with_one_excluded_credential);
+    add_task(test_get_with_one_allowed_credential);
+
+    async function test_setup_valid_credential() {
+      let publicKey = {
+        rp: {id: document.domain, name: "none"},
+        user: {id: new Uint8Array(), name: "none", displayName: "none"},
+        challenge: crypto.getRandomValues(new Uint8Array(16)),
+        pubKeyCredParams: [{type: "public-key", alg: cose_alg_ECDSA_w_SHA256}],
+      };
+
+      let res = await navigator.credentials.create({publicKey});
+      validCred = {type: "public-key", id: res.rawId};
+    }
+
+    async function test_create_with_one_excluded_credential() {
+      let publicKey = {
+        rp: {id: document.domain, name: "none"},
+        user: {id: new Uint8Array(), name: "none", displayName: "none"},
+        challenge: crypto.getRandomValues(new Uint8Array(16)),
+        excludeList: [validCred],
+        pubKeyCredParams: [{type: "public-key", alg: cose_alg_ECDSA_w_SHA256}],
+      };
+
+      await navigator.credentials.create({publicKey});
+      ok(true, "create should not throw");
+    }
+
+    async function test_get_with_one_allowed_credential() {
+      let publicKey = {
+        challenge: crypto.getRandomValues(new Uint8Array(16)),
+        allowCredentials: [validCred]
+      };
+
+      await navigator.credentials.get({publicKey});
+      ok(true, "get should not throw");
+    }
+
+  </script>
+
+</body>
+</html>
diff --git a/dom/webauthn/tests/test_webauthn_get_assertion.html b/dom/webauthn/tests/test_webauthn_get_assertion.html
new file mode 100644
index 0000000000..3de282a09a
--- /dev/null
+++ b/dom/webauthn/tests/test_webauthn_get_assertion.html
@@ -0,0 +1,212 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <title>Tests for GetAssertion for W3C Web Authentication</title>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="u2futil.js"></script>
+  <script type="text/javascript" src="pkijs/common.js"></script>
+  <script type="text/javascript" src="pkijs/asn1.js"></script>
+  <script type="text/javascript" src="pkijs/x509_schema.js"></script>
+  <script type="text/javascript" src="pkijs/x509_simpl.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+  <h1>Tests for GetAssertion for W3C Web Authentication</h1>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1309284">Mozilla Bug 1309284</a>
+
+  <script class="testbody" type="text/javascript">
+    "use strict";
+
+    add_task(async () => {
+      await addVirtualAuthenticator();
+    });
+
+    is(navigator.authentication, undefined, "navigator.authentication does not exist any longer");
+    isnot(navigator.credentials, undefined, "Credential Management API endpoint must exist");
+    isnot(navigator.credentials.create, undefined, "CredentialManagement create API endpoint must exist");
+    isnot(navigator.credentials.get, undefined, "CredentialManagement get API endpoint must exist");
+
+    let gAssertionChallenge = new Uint8Array(16);
+    window.crypto.getRandomValues(gAssertionChallenge);
+
+    let unknownCredType = {type: "Magic", id: base64ToBytes("AAA=")};
+    let unknownCred = {type: "public-key", id: base64ToBytes("AAA=")};
+    let validCred = null;
+
+    add_task(test_setup_valid_credential);
+    add_task(test_with_credential);
+    add_task(test_unexpected_option);
+    add_task(test_unexpected_option_with_credential);
+    add_task(test_unexpected_transport);
+    add_task(test_unknown_credential_type);
+    add_task(test_unknown_credential);
+    add_task(test_too_many_credentials);
+    add_task(test_unexpected_option_unknown_credential_type);
+    add_task(test_empty_credential_list);
+
+    function requestGetAssertion(params) {
+      return navigator.credentials.get(params);
+    }
+
+    function arrivingHereIsGood(aResult) {
+      ok(true, "Good result! Received a: " + aResult);
+    }
+
+    function arrivingHereIsBad(aResult) {
+      ok(false, "Bad result! Received a: " + aResult);
+    }
+
+    function expectNotAllowedError(aResult) {
+      ok(aResult.toString().startsWith("NotAllowedError"), "Expecting a NotAllowedError, got " + aResult);
+    }
+
+    function expectInvalidStateError(aResult) {
+      ok(aResult.toString().startsWith("InvalidStateError"), "Expecting a InvalidStateError, got " + aResult);
+    }
+
+    function expectTypeError(aResult) {
+      ok(aResult.toString().startsWith("TypeError"), "Expecting a TypeError, got " + aResult);
+    }
+
+    function expectSecurityError(aResult) {
+      ok(aResult.toString().startsWith("SecurityError"), "Expecting a SecurityError, got " + aResult);
+    }
+
+    function expectAbortError(aResult) {
+      is(aResult.code, DOMException.ABORT_ERR, "Expecting an AbortError");
+    }
+
+    // Set up a valid credential
+    async function test_setup_valid_credential() {
+      let publicKey = {
+        rp: {id: document.domain, name: "none"},
+        user: {id: new Uint8Array(), name: "none", displayName: "none"},
+        challenge: crypto.getRandomValues(new Uint8Array(16)),
+        pubKeyCredParams: [{type: "public-key", alg: cose_alg_ECDSA_w_SHA256}],
+      };
+
+      return navigator.credentials.create({publicKey})
+      .then(res => validCred = {type: "public-key", id: res.rawId} );
+    }
+
+    // Test with a valid credential
+    async function test_with_credential() {
+      let publicKey = {
+        challenge: gAssertionChallenge,
+        allowCredentials: [validCred]
+      };
+
+      await requestGetAssertion({publicKey})
+        .then(arrivingHereIsGood)
+        .catch(arrivingHereIsBad);
+    }
+
+    // Test with an unexpected option. That won't stop anything, and we'll
+    // fail with NotAllowed just as if we had no valid credentials -- which
+    // we don't.
+    async function test_unexpected_option() {
+      let publicKey = {
+        challenge: gAssertionChallenge,
+        unknownValue: "hi"
+      };
+
+      await requestGetAssertion({publicKey})
+        .then(arrivingHereIsBad)
+        .catch(expectNotAllowedError);
+    }
+
+    // Test with an unexpected option but a valid credential
+    async function test_unexpected_option_with_credential() {
+      let publicKey = {
+        challenge: gAssertionChallenge,
+        unknownValue: "hi",
+        allowCredentials: [validCred]
+      };
+
+      await requestGetAssertion({publicKey})
+        .then(arrivingHereIsGood)
+        .catch(arrivingHereIsBad);
+    }
+
+    // Test with an unexpected transport on a valid credential
+    async function test_unexpected_transport() {
+      let cred = validCred;
+      cred.transports = ["unknown", "usb"];
+
+      let publicKey = {
+        challenge: gAssertionChallenge,
+        unknownValue: "hi",
+        allowCredentials: [cred]
+      };
+
+      await requestGetAssertion({publicKey})
+        .then(arrivingHereIsGood)
+        .catch(arrivingHereIsBad);
+    }
+
+    // Test with an unknown credential type
+    async function test_unknown_credential_type() {
+      let publicKey = {
+        challenge: gAssertionChallenge,
+        allowCredentials: [unknownCredType]
+      };
+
+      await requestGetAssertion({publicKey})
+        .then(arrivingHereIsBad)
+        .catch(expectNotAllowedError);
+    }
+
+    // Test with an unknown credential
+    async function test_unknown_credential() {
+      let publicKey = {
+        challenge: gAssertionChallenge,
+        allowCredentials: [unknownCred]
+      };
+
+      await requestGetAssertion({publicKey})
+        .then(arrivingHereIsBad)
+        .catch(expectNotAllowedError);
+    }
+
+    // Test with too many credentials
+    async function test_too_many_credentials() {
+      let tooManyCredentials = Array(21).fill(validCred);
+      let publicKey = {
+        challenge: gAssertionChallenge,
+        allowCredentials: tooManyCredentials,
+      };
+
+      await requestGetAssertion({publicKey})
+        .then(arrivingHereIsBad)
+        .catch(expectSecurityError);
+    }
+
+    // Test with an unexpected option and an unknown credential type
+    async function test_unexpected_option_unknown_credential_type() {
+      let publicKey = {
+        challenge: gAssertionChallenge,
+        unknownValue: "hi",
+        allowCredentials: [unknownCredType]
+      };
+
+      await requestGetAssertion({publicKey})
+        .then(arrivingHereIsBad)
+        .catch(expectNotAllowedError);
+    }
+
+    // Test with an empty credential list
+    async function test_empty_credential_list() {
+      let publicKey = {
+        challenge: gAssertionChallenge,
+        allowCredentials: []
+      };
+
+      await requestGetAssertion({publicKey})
+        .then(arrivingHereIsBad)
+        .catch(expectNotAllowedError);
+    }
+  </script>
+
+</body>
+</html>
diff --git a/dom/webauthn/tests/test_webauthn_get_assertion_dead_object.html b/dom/webauthn/tests/test_webauthn_get_assertion_dead_object.html
new file mode 100644
index 0000000000..35892a60d1
--- /dev/null
+++ b/dom/webauthn/tests/test_webauthn_get_assertion_dead_object.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <title>Test for GetAssertion on dead object</title>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="u2futil.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+  <h1>Test for GetAssertion on dead object</h1>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1483905">Mozilla Bug 1483905</a>
+
+  <script class="testbody" type="text/javascript">
+    "use strict";
+    SimpleTest.waitForExplicitFinish();
+    SimpleTest.requestFlakyTimeout(
+      "Due to the nature of this test, there's no way for the window we're opening to signal " +
+      "that it's done (the `document.writeln('')` is essential and basically clears any state " +
+      "we could use). So, we have to wait at least 15 seconds for the webauthn call to time out.");
+    SpecialPowers.pushPrefEnv({"set": [
+      ["security.webauth.webauthn_enable_softtoken", false],
+      ["security.webauth.webauthn_enable_usbtoken", true],
+    ]}).then(() => {
+      let win = window.open("https://example.com/tests/dom/webauthn/tests/get_assertion_dead_object.html");
+      setTimeout(() => {
+        win.close();
+        SimpleTest.finish();
+      }, 20000);
+    });
+  </script>
+
+</body>
+</html>
diff --git a/dom/webauthn/tests/test_webauthn_isplatformauthenticatoravailable.html b/dom/webauthn/tests/test_webauthn_isplatformauthenticatoravailable.html
new file mode 100644
index 0000000000..15b83659d5
--- /dev/null
+++ b/dom/webauthn/tests/test_webauthn_isplatformauthenticatoravailable.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <title>Test for W3C Web Authentication isUserVerifyingPlatformAuthenticatorAvailable</title>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="u2futil.js"></script>
+  <script type="text/javascript" src="pkijs/common.js"></script>
+  <script type="text/javascript" src="pkijs/asn1.js"></script>
+  <script type="text/javascript" src="pkijs/x509_schema.js"></script>
+  <script type="text/javascript" src="pkijs/x509_simpl.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<h1>Test for W3C Web Authentication isUserVerifyingPlatformAuthenticatorAvailable</h1>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1309284">Mozilla Bug 1309284</a>
+
+<script class="testbody" type="text/javascript">
+"use strict";
+
+add_task(async function test_uvpaa_with_no_authenticator() {
+  let uvpaa = await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
+  ok(uvpaa === false, "Platform authenticator is not available");
+});
+
+add_task(async () => {
+  await addVirtualAuthenticator("ctap2_1", "usb");
+});
+
+add_task(async function test_uvpaa_with_usb_authenticator() {
+  let uvpaa = await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
+  ok(uvpaa === false, "Platform authenticator is not available");
+});
+
+add_task(async () => {
+  await addVirtualAuthenticator("ctap2_1", "internal");
+});
+
+add_task(async function test_uvpaa_with_usb_and_platform_authenticator() {
+  let uvpaa = await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
+  ok(uvpaa === true, "Platform authenticator is available");
+});
+
+
+</script>
+
+</body>
+</html>
diff --git a/dom/webauthn/tests/test_webauthn_loopback.html b/dom/webauthn/tests/test_webauthn_loopback.html
new file mode 100644
index 0000000000..70905739a0
--- /dev/null
+++ b/dom/webauthn/tests/test_webauthn_loopback.html
@@ -0,0 +1,179 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <title>Full-run test for MakeCredential/GetAssertion for W3C Web Authentication</title>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="u2futil.js"></script>
+  <script type="text/javascript" src="pkijs/common.js"></script>
+  <script type="text/javascript" src="pkijs/asn1.js"></script>
+  <script type="text/javascript" src="pkijs/x509_schema.js"></script>
+  <script type="text/javascript" src="pkijs/x509_simpl.js"></script>
+  <script type="text/javascript" src="cbor.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<h1>Full-run test for MakeCredential/GetAssertion for W3C Web Authentication</h1>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1309284">Mozilla Bug 1309284</a>
+
+<script class="testbody" type="text/javascript">
+"use strict";
+
+add_task(async function() {
+  // This test intentionally compares items to themselves.
+  /* eslint-disable no-self-compare */
+  await SpecialPowers.pushPrefEnv({"set": [["security.webauth.webauthn_testing_allow_direct_attestation", true]]});
+  await addVirtualAuthenticator();
+  is(navigator.authentication, undefined, "navigator.authentication does not exist any longer");
+  isnot(navigator.credentials, undefined, "Credential Management API endpoint must exist");
+  isnot(navigator.credentials.create, undefined, "CredentialManagement create API endpoint must exist");
+  isnot(navigator.credentials.get, undefined, "CredentialManagement get API endpoint must exist");
+
+  let credm = navigator.credentials;
+
+  let gCredentialChallenge = new Uint8Array(16);
+  window.crypto.getRandomValues(gCredentialChallenge);
+  let gAssertionChallenge = new Uint8Array(16);
+  window.crypto.getRandomValues(gAssertionChallenge);
+
+  await testMakeCredential();
+
+  async function decodeCreatedCredential(aCredInfo) {
+    /* PublicKeyCredential : Credential
+       - rawId: Key Handle buffer pulled from U2F Register() Response
+       - id: Key Handle buffer in base64url form, should == rawId
+       - type: Literal 'public-key'
+       - response : AuthenticatorAttestationResponse : AuthenticatorResponse
+         - attestationObject: CBOR object
+         - clientDataJSON: serialized JSON
+    */
+
+    is(aCredInfo.type, "public-key", "Credential type must be public-key")
+
+    ok(aCredInfo.rawId.byteLength > 0, "Key ID exists");
+    is(aCredInfo.id, bytesToBase64UrlSafe(aCredInfo.rawId), "Encoded Key ID and Raw Key ID match");
+
+    ok(aCredInfo.rawId === aCredInfo.rawId, "PublicKeyCredential.RawID is SameObject");
+    ok(aCredInfo.response === aCredInfo.response, "PublicKeyCredential.Response is SameObject");
+    ok(aCredInfo.response.clientDataJSON === aCredInfo.response.clientDataJSON, "PublicKeyCredential.Response.ClientDataJSON is SameObject");
+    ok(aCredInfo.response.attestationObject === aCredInfo.response.attestationObject, "PublicKeyCredential.Response.AttestationObject is SameObject");
+
+    let clientData = JSON.parse(buffer2string(aCredInfo.response.clientDataJSON));
+    is(clientData.challenge, bytesToBase64UrlSafe(gCredentialChallenge), "Challenge is correct");
+    is(clientData.origin, window.location.origin, "Origin is correct");
+    is(clientData.type, "webauthn.create", "Type is correct");
+
+    let attestationObj = await webAuthnDecodeCBORAttestation(aCredInfo.response.attestationObject);
+    // Make sure the RP ID hash matches what we calculate.
+    let calculatedRpIdHash = await crypto.subtle.digest("SHA-256", string2buffer(document.domain));
+    let calcHashStr = bytesToBase64UrlSafe(new Uint8Array(calculatedRpIdHash));
+    let providedHashStr = bytesToBase64UrlSafe(new Uint8Array(attestationObj.authDataObj.rpIdHash));
+    is(calcHashStr, providedHashStr, "Calculated RP ID hash must match what the browser derived.");
+    ok(true, attestationObj.authDataObj.flags[0] & (flag_TUP | flag_AT));
+    ok(attestationObj.authDataObj.flags[0] & (flag_TUP | flag_AT) == (flag_TUP | flag_AT),
+       "User presence and Attestation Object flags must be set");
+    aCredInfo.clientDataObj = clientData;
+    aCredInfo.publicKeyHandle = attestationObj.authDataObj.publicKeyHandle;
+    aCredInfo.attestationObject = attestationObj.authDataObj.attestationAuthData;
+    return aCredInfo;
+  }
+
+  async function checkAssertionAndSigValid(aPublicKey, aAssertion) {
+    /* PublicKeyCredential : Credential
+       - rawId: ID of Credential from AllowList that succeeded
+       - id: Key Handle buffer in base64url form, should == rawId
+       - type: Literal 'public-key'
+       - response : AuthenticatorAssertionResponse : AuthenticatorResponse
+         - clientDataJSON: serialized JSON
+         - authenticatorData: RP ID Hash || U2F Sign() Response
+         - signature: U2F Sign() Response
+    */
+    is(aAssertion.type, "public-key", "Credential type must be public-key")
+    ok(aAssertion.rawId.byteLength > 0, "Key ID exists");
+    is(aAssertion.id, bytesToBase64UrlSafe(new Uint8Array(aAssertion.rawId)), "Encoded Key ID and Raw Key ID match");
+    ok(aAssertion.response.authenticatorData === aAssertion.response.authenticatorData, "AuthenticatorAssertionResponse.AuthenticatorData is SameObject");
+    ok(aAssertion.response.authenticatorData instanceof ArrayBuffer, "AuthenticatorAssertionResponse.AuthenticatorData is an ArrayBuffer");
+    ok(aAssertion.response.signature === aAssertion.response.signature, "AuthenticatorAssertionResponse.Signature is SameObject");
+    ok(aAssertion.response.signature instanceof ArrayBuffer, "AuthenticatorAssertionResponse.Signature is an ArrayBuffer");
+    ok(aAssertion.response.userHandle === null, "AuthenticatorAssertionResponse.UserHandle is null for u2f authenticators");
+
+    ok(aAssertion.response.authenticatorData.byteLength > 0, "Authenticator data exists");
+    let clientData = JSON.parse(buffer2string(aAssertion.response.clientDataJSON));
+    is(clientData.challenge, bytesToBase64UrlSafe(gAssertionChallenge), "Challenge is correct");
+    is(clientData.origin, window.location.origin, "Origin is correct");
+    is(clientData.type, "webauthn.get", "Type is correct");
+
+    let attestation = await webAuthnDecodeAuthDataArray(aAssertion.response.authenticatorData);
+    ok(new Uint8Array(attestation.flags)[0] & flag_TUP == flag_TUP, "User presence flag must be set");
+    is(attestation.counter.byteLength, 4, "Counter must be 4 bytes");
+    let params = await deriveAppAndChallengeParam(window.location.host, aAssertion.response.clientDataJSON, attestation);
+    console.log(params);
+    console.log("ClientData buffer: ", hexEncode(aAssertion.response.clientDataJSON));
+    console.log("ClientDataHash: ", hexEncode(params.challengeParam));
+    let signedData = await assembleSignedData(params.appParam, params.attestation.flags,
+                                              params.attestation.counter, params.challengeParam);
+    console.log(aPublicKey, signedData, aAssertion.response.signature);
+    return await verifySignature(aPublicKey, signedData, aAssertion.response.signature);
+  }
+
+  async function testMakeCredential() {
+    let rp = {id: document.domain, name: "none"};
+    let user = {id: new Uint8Array(), name: "none", displayName: "none"};
+    let param = {type: "public-key", alg: cose_alg_ECDSA_w_SHA256};
+    let makeCredentialOptions = {
+      rp,
+      user,
+      challenge: gCredentialChallenge,
+      pubKeyCredParams: [param],
+      attestation: "direct"
+    };
+    let credential = await credm.create({publicKey: makeCredentialOptions})
+    let decodedCredential = await decodeCreatedCredential(credential);
+    await testMakeDuplicate(decodedCredential);
+  }
+
+  async function testMakeDuplicate(aCredInfo) {
+    let rp = {id: document.domain, name: "none"};
+    let user = {id: new Uint8Array(), name: "none", displayName: "none"};
+    let param = {type: "public-key", alg: cose_alg_ECDSA_w_SHA256};
+    let makeCredentialOptions = {
+      rp,
+      user,
+      challenge: gCredentialChallenge,
+      pubKeyCredParams: [param],
+      excludeCredentials: [{type: "public-key", id: new Uint8Array(aCredInfo.rawId),
+                     transports: ["usb"]}]
+    };
+    await credm.create({publicKey: makeCredentialOptions})
+    .then(function() {
+      // We should have errored here!
+      ok(false, "The excludeList didn't stop a duplicate being created!");
+    }).catch((aReason) => {
+      ok(aReason.toString().startsWith("InvalidStateError"), "Expect InvalidStateError, got " + aReason);
+    });
+    await testAssertion(aCredInfo);
+  }
+
+  async function testAssertion(aCredInfo) {
+    let newCredential = {
+      type: "public-key",
+      id: new Uint8Array(aCredInfo.rawId),
+      transports: ["usb"],
+    }
+
+    let publicKeyCredentialRequestOptions = {
+      challenge: gAssertionChallenge,
+      timeout: 5000, // the minimum timeout is actually 15 seconds
+      rpId: document.domain,
+      allowCredentials: [newCredential]
+    };
+    let assertion = await credm.get({publicKey: publicKeyCredentialRequestOptions});
+    let sigVerifyResult = await checkAssertionAndSigValid(aCredInfo.publicKeyHandle, assertion);
+    ok(sigVerifyResult, "Signing signature verified");
+  }
+});
+
+</script>
+
+</body>
+</html>
diff --git a/dom/webauthn/tests/test_webauthn_make_credential.html b/dom/webauthn/tests/test_webauthn_make_credential.html
new file mode 100644
index 0000000000..4812d0da0d
--- /dev/null
+++ b/dom/webauthn/tests/test_webauthn_make_credential.html
@@ -0,0 +1,459 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <title>Test for MakeCredential for W3C Web Authentication</title>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="u2futil.js"></script>
+  <script type="text/javascript" src="pkijs/common.js"></script>
+  <script type="text/javascript" src="pkijs/asn1.js"></script>
+  <script type="text/javascript" src="pkijs/x509_schema.js"></script>
+  <script type="text/javascript" src="pkijs/x509_simpl.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+  <h1>Test for MakeCredential for W3C Web Authentication</h1>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1309284">Mozilla Bug 1309284</a>
+
+  <script class="testbody" type="text/javascript">
+    "use strict";
+
+    add_task(async () => {
+      await addVirtualAuthenticator();
+    });
+
+    is(navigator.authentication, undefined, "navigator.authentication does not exist any longer");
+    isnot(navigator.credentials, undefined, "Credential Management API endpoint must exist");
+    isnot(navigator.credentials.create, undefined, "CredentialManagement create API endpoint must exist");
+    isnot(navigator.credentials.get, undefined, "CredentialManagement get API endpoint must exist");
+
+    let credm;
+    let gCredentialChallenge;
+    let rp;
+    let user;
+    let param;
+    let unsupportedParam;
+    let unknownParam;
+
+    // Setup test env
+    add_task(() => {
+      gCredentialChallenge = new Uint8Array(16);
+      window.crypto.getRandomValues(gCredentialChallenge);
+
+      rp = {id: document.domain, name: "none"};
+      user = {id: new Uint8Array(64), name: "none", displayName: "none"};
+      param = {type: "public-key", alg: cose_alg_ECDSA_w_SHA256};
+      unsupportedParam = {type: "public-key", alg: cose_alg_ECDSA_w_SHA512};
+      unknownParam = {type: "SimplePassword", alg: "MaxLength=2"};
+      credm = navigator.credentials;
+    });
+    // Add tests
+    add_task(test_good_call);
+    add_task(test_empty_account);
+    add_task(test_without_rp_name);
+    add_task(test_without_user_id);
+    add_task(test_without_user_name);
+    add_task(test_without_user_displayname);
+    add_task(test_user_too_large);
+    add_task(test_empty_parameters);
+    add_task(test_without_parameters);
+    add_task(test_unsupported_parameter);
+    add_task(test_unsupported_but_one_param);
+    add_task(test_one_unknown_parameter);
+    add_task(test_one_unknown_one_unsupported_param);
+    add_task(test_one_of_each_parameters);
+    add_task(test_without_challenge);
+    add_task(test_invalid_challenge);
+    add_task(test_duplicate_pub_key);
+    add_task(test_invalid_rp_id);
+    add_task(test_invalid_rp_id_2);
+    add_task(test_missing_rp);
+    add_task(test_incorrect_user_id_type);
+    add_task(test_missing_user);
+    add_task(test_complete_account);
+    add_task(test_too_large_user_id);
+    add_task(test_excluding_unknown_transports);
+    add_task(test_unknown_attestation_type);
+    add_task(test_unknown_selection_criteria);
+    add_task(test_no_unexpected_extensions);
+    add_task(test_cred_props_with_rk_required);
+    add_task(test_cred_props_with_rk_discouraged);
+
+    function arrivingHereIsGood(aResult) {
+      ok(true, "Good result! Received a: " + aResult);
+      return Promise.resolve();
+    }
+
+    function arrivingHereIsBad(aResult) {
+      ok(false, "Bad result! Received a: " + aResult);
+      return Promise.resolve();
+    }
+
+    function expectNotAllowedError(aResult) {
+      ok(aResult.toString().startsWith("NotAllowedError"), "Expecting a NotAllowedError");
+      return Promise.resolve();
+    }
+
+    function expectTypeError(aResult) {
+      ok(aResult.toString().startsWith("TypeError"), "Expecting a TypeError");
+      return Promise.resolve();
+    }
+
+    function expectNotSupportedError(aResult) {
+      ok(aResult.toString().startsWith("NotSupportedError"), "Expecting a NotSupportedError");
+      return Promise.resolve();
+    }
+
+    // Test basic good call
+    async function test_good_call() {
+      let makeCredentialOptions = {
+        rp, user, challenge: gCredentialChallenge, pubKeyCredParams: [param]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+                  .then(arrivingHereIsGood)
+                  .catch(arrivingHereIsBad);
+    }
+
+    // Test empty account
+    async function test_empty_account() {
+      let makeCredentialOptions = {
+        challenge: gCredentialChallenge, pubKeyCredParams: [param]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+                  .then(arrivingHereIsBad)
+                  .catch(expectTypeError);
+    }
+
+    // Test without rp.name
+    async function test_without_rp_name() {
+      let rp1 = {id: document.domain};
+      let makeCredentialOptions = {
+        rp: rp1, user, challenge: gCredentialChallenge, pubKeyCredParams: [param]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+                  .then(arrivingHereIsBad)
+                  .catch(expectTypeError);
+    }
+
+    // Test without user.id
+    async function test_without_user_id() {
+      let user1 = {name: "none", displayName: "none"};
+      let makeCredentialOptions = {
+        rp, user: user1, challenge: gCredentialChallenge, pubKeyCredParams: [param]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+                  .then(arrivingHereIsBad)
+                  .catch(expectTypeError);
+    }
+
+    // Test without user.name
+    async function test_without_user_name() {
+      let user1 = {id: new Uint8Array(64), displayName: "none"};
+      let makeCredentialOptions = {
+        rp, user: user1, challenge: gCredentialChallenge, pubKeyCredParams: [param]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+                  .then(arrivingHereIsBad)
+                  .catch(expectTypeError);
+    }
+
+    // Test without user.displayName
+    async function test_without_user_displayname() {
+      let user1 = {id: new Uint8Array(64), name: "none"};
+      let makeCredentialOptions = {
+        rp, user: user1, challenge: gCredentialChallenge, pubKeyCredParams: [param]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+                  .then(arrivingHereIsBad)
+                  .catch(expectTypeError);
+    }
+
+    // Test with a user handle that exceeds the max length
+    async function test_user_too_large() {
+      let user1 = {id: new Uint8Array(65), name: "none", displayName: "none"};
+      let makeCredentialOptions = {
+        rp, user: user1, challenge: gCredentialChallenge, pubKeyCredParams: [param]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+                  .then(arrivingHereIsBad)
+                  .catch(expectTypeError);
+    }
+
+    // Test without any parameters; this is acceptable meaning the RP ID is
+    // happy to accept either ECDSA-SHA256 or RSA-SHA256
+    async function test_empty_parameters() {
+      let makeCredentialOptions = {
+        rp, user, challenge: gCredentialChallenge, pubKeyCredParams: []
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+                  .then(arrivingHereIsGood)
+                  .catch(arrivingHereIsBad);
+    }
+
+    // Test without a parameter array at all
+    async function test_without_parameters() {
+      let makeCredentialOptions = {
+        rp, user, challenge: gCredentialChallenge
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+           .then(arrivingHereIsBad)
+           .catch(expectTypeError);
+    }
+
+    // Test with an unsupported parameter
+    // The result depends on the tokens that are available, so we
+    // expect a NotAllowedError so as not to leak this.
+    async function test_unsupported_parameter() {
+      let makeCredentialOptions = {
+        rp, user, challenge: gCredentialChallenge, pubKeyCredParams: [unsupportedParam]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+                  .then(arrivingHereIsBad)
+                  .catch(expectNotAllowedError);
+    }
+
+    // Test with an unsupported parameter and a good one
+    async function test_unsupported_but_one_param() {
+      let makeCredentialOptions = {
+        rp, user, challenge: gCredentialChallenge,
+        pubKeyCredParams: [param, unsupportedParam]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+                  .then(arrivingHereIsGood)
+                  .catch(arrivingHereIsBad);
+    }
+
+    // Test with one unknown (not "public-key") parameter.
+    async function test_one_unknown_parameter() {
+      let makeCredentialOptions = {
+        rp, user, challenge: gCredentialChallenge, pubKeyCredParams: [unknownParam]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+           .then(arrivingHereIsBad)
+           .catch(expectNotSupportedError);
+    }
+
+    // Test with an unsupported parameter, and an unknown one
+    // The result depends on the tokens that are available, so we
+    // expect a NotAllowedError so as not to leak this.
+    async function test_one_unknown_one_unsupported_param() {
+      let makeCredentialOptions = {
+        rp, user, challenge: gCredentialChallenge,
+        pubKeyCredParams: [unsupportedParam, unknownParam]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+                  .then(arrivingHereIsBad)
+                  .catch(expectNotAllowedError);
+    }
+
+    // Test with an unsupported parameter, an unknown one, and a good one. This
+    // should succeed, as the unsupported and unknown should be ignored.
+    async function test_one_of_each_parameters() {
+      let makeCredentialOptions = {
+        rp, user, challenge: gCredentialChallenge,
+        pubKeyCredParams: [param, unsupportedParam, unknownParam]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+                  .then(arrivingHereIsGood)
+                  .catch(arrivingHereIsBad);
+    }
+
+    // Test without a challenge
+    async function test_without_challenge() {
+      let makeCredentialOptions = {
+        rp, user, pubKeyCredParams: [param]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+                  .then(arrivingHereIsBad)
+                  .catch(expectTypeError);
+    }
+
+    // Test with an invalid challenge
+    async function test_invalid_challenge() {
+      let makeCredentialOptions = {
+        rp, user, challenge: "begone, thou ill-fitting moist glove!",
+        pubKeyCredParams: [unsupportedParam]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+           .then(arrivingHereIsBad)
+           .catch(expectTypeError);
+    }
+
+    // Test with duplicate pubKeyCredParams
+    async function test_duplicate_pub_key() {
+      let makeCredentialOptions = {
+        rp, user, challenge: gCredentialChallenge,
+        pubKeyCredParams: [param, param, param]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+                  .then(arrivingHereIsGood)
+                  .catch(arrivingHereIsBad);
+    }
+
+    // Test with an RP ID that is not a valid domain string
+    async function test_invalid_rp_id() {
+      let rp1 = { id: document.domain + ":somejunk", name: "none"};
+      let makeCredentialOptions = {
+        rp: rp1, user, challenge: gCredentialChallenge, pubKeyCredParams: [param]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+                  .then(arrivingHereIsBad)
+                  .catch(arrivingHereIsGood);
+    }
+
+    // Test with another RP ID that is not a valid domain string
+    async function test_invalid_rp_id_2() {
+      let rp1 = { id: document.domain + ":8888", name: "none"};
+      let makeCredentialOptions = {
+        rp: rp1, user, challenge: gCredentialChallenge, pubKeyCredParams: [param]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+                  .then(arrivingHereIsBad)
+                  .catch(arrivingHereIsGood);
+    }
+
+    // Test with missing rp
+    async function test_missing_rp() {
+      let makeCredentialOptions = {
+        user, challenge: gCredentialChallenge, pubKeyCredParams: [param]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+                  .then(arrivingHereIsBad)
+                  .catch(expectTypeError);
+    }
+
+    // Test with incorrect user ID type
+    async function test_incorrect_user_id_type() {
+      let invalidType = {id: "a string, which is not a buffer", name: "none", displayName: "none"};
+      let makeCredentialOptions = {
+        user: invalidType, challenge: gCredentialChallenge, pubKeyCredParams: [param]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+                  .then(arrivingHereIsBad)
+                  .catch(expectTypeError);
+    }
+
+    // Test with missing user
+    async function test_missing_user() {
+      let makeCredentialOptions = {
+        rp, challenge: gCredentialChallenge, pubKeyCredParams: [param]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+                  .then(arrivingHereIsBad)
+                  .catch(expectTypeError);
+    }
+
+    // Test a complete account
+    async function test_complete_account() {
+      // the icon fields are deprecated, but including them should not cause an error
+      let completeRP = {id: document.domain, name: "Foxxy Name",
+                        icon: "https://example.com/fox.svg"};
+      let completeUser = {id: string2buffer("foxes_are_the_best@example.com"),
+                          name: "Fox F. Foxington",
+                          icon: "https://example.com/fox.svg",
+                          displayName: "Foxxy V"};
+      let makeCredentialOptions = {
+        rp: completeRP, user: completeUser, challenge: gCredentialChallenge,
+        pubKeyCredParams: [param]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+                  .then(arrivingHereIsGood)
+                  .catch(arrivingHereIsBad);
+    }
+
+    // Test with too-large user ID buffer
+    async function test_too_large_user_id() {
+      let hugeUser = {id: new Uint8Array(65),
+                          name: "Fox F. Foxington",
+                          displayName: "Foxxy V"};
+      let makeCredentialOptions = {
+        rp, user: hugeUser, challenge: gCredentialChallenge,
+        pubKeyCredParams: [param]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+                  .then(arrivingHereIsBad)
+                  .catch(expectTypeError);
+    }
+
+    // Test with excluding unknown transports
+    async function test_excluding_unknown_transports() {
+      let completeRP = {id: document.domain, name: "Foxxy Name"};
+      let completeUser = {id: string2buffer("foxes_are_the_best@example.com"),
+                          name: "Fox F. Foxington",
+                          displayName: "Foxxy V"};
+      let excludedUnknownTransport = {type: "public-key",
+                                      id: string2buffer("123"),
+                                      transports: ["unknown", "usb"]};
+      let makeCredentialOptions = {
+        rp: completeRP, user: completeUser, challenge: gCredentialChallenge,
+        pubKeyCredParams: [param], excludeCredentials: [excludedUnknownTransport]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+                  .then(arrivingHereIsGood)
+                  .catch(arrivingHereIsBad);
+    }
+
+    async function test_unknown_attestation_type() {
+      let makeCredentialOptions = {
+        rp, user, challenge: gCredentialChallenge, pubKeyCredParams: [param],
+        attestation: "unknown"
+      };
+      return credm.create({publicKey: makeCredentialOptions })
+                  .then(arrivingHereIsGood)
+                  .catch(arrivingHereIsBad);
+    }
+
+    async function test_unknown_selection_criteria() {
+      let makeCredentialOptions = {
+        rp, user, challenge: gCredentialChallenge, pubKeyCredParams: [param],
+            authenticatorSelection: {
+                  userVerificationRequirement: "unknown UV requirement",
+                  authenticatorAttachment: "unknown authenticator attachment type"
+            },
+      };
+      return credm.create({publicKey: makeCredentialOptions })
+                  .then(arrivingHereIsGood)
+                  .catch(arrivingHereIsBad);
+    }
+
+    async function test_no_unexpected_extensions() {
+      let makeCredentialOptions = {
+        rp, user, challenge: gCredentialChallenge, pubKeyCredParams: [param],
+      };
+      let cred = await credm.create({publicKey: makeCredentialOptions}).catch(arrivingHereIsBad);
+      let extensionResults = cred.getClientExtensionResults();
+      is(extensionResults.credProps, undefined, "no credProps output");
+    }
+
+    async function test_cred_props_with_rk_required() {
+      let makeCredentialOptions = {
+        rp, user, challenge: gCredentialChallenge, pubKeyCredParams: [param],
+            authenticatorSelection: {
+                  authenticatorAttachment: "cross-platform",
+                  residentKey: "required",
+            },
+            extensions: { credProps: true }
+      };
+      let cred = await credm.create({publicKey: makeCredentialOptions}).catch(arrivingHereIsBad);
+      let extensionResults = cred.getClientExtensionResults();
+      is(extensionResults.credProps?.rk, true, "rk is true");
+    }
+
+    async function test_cred_props_with_rk_discouraged() {
+      let makeCredentialOptions = {
+        rp, user, challenge: gCredentialChallenge, pubKeyCredParams: [param],
+            authenticatorSelection: {
+                  authenticatorAttachment: "cross-platform",
+                  residentKey: "discouraged",
+            },
+            extensions: { credProps: true }
+      };
+      let cred = await credm.create({publicKey: makeCredentialOptions}).catch(arrivingHereIsBad);
+      let extensionResults = cred.getClientExtensionResults();
+      is(extensionResults.credProps?.rk, false, "rk is false");
+    }
+
+  </script>
+
+</body>
+</html>
diff --git a/dom/webauthn/tests/test_webauthn_no_token.html b/dom/webauthn/tests/test_webauthn_no_token.html
new file mode 100644
index 0000000000..28fe6e43eb
--- /dev/null
+++ b/dom/webauthn/tests/test_webauthn_no_token.html
@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <title>Test for W3C Web Authentication with no token</title>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="u2futil.js"></script>
+  <script type="text/javascript" src="pkijs/common.js"></script>
+  <script type="text/javascript" src="pkijs/asn1.js"></script>
+  <script type="text/javascript" src="pkijs/x509_schema.js"></script>
+  <script type="text/javascript" src="pkijs/x509_simpl.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<h1>Test for W3C Web Authentication with no token</h1>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1309284">Mozilla Bug 1309284</a>
+
+<script class="testbody" type="text/javascript">
+"use strict";
+
+is(navigator.authentication, undefined, "navigator.authentication does not exist any longer");
+isnot(navigator.credentials, undefined, "Credential Management API endpoint must exist");
+isnot(navigator.credentials.create, undefined, "CredentialManagement create API endpoint must exist");
+isnot(navigator.credentials.get, undefined, "CredentialManagement get API endpoint must exist");
+
+let credm;
+let credentialChallenge;
+let assertionChallenge;
+let credentialId;
+
+// Setup test env
+add_task(async () => {
+  credentialChallenge = new Uint8Array(16);
+  window.crypto.getRandomValues(credentialChallenge);
+  assertionChallenge = new Uint8Array(16);
+  window.crypto.getRandomValues(assertionChallenge);
+  credentialId = new Uint8Array(128);
+  window.crypto.getRandomValues(credentialId);
+  credm = navigator.credentials;
+  // Turn off all tokens. This should result in "not allowed" failures
+  await SpecialPowers.pushPrefEnv({"set": [
+    ["security.webauth.webauthn_enable_softtoken", false],
+    ["security.webauth.webauthn_enable_usbtoken", false],
+  ]});
+});
+
+add_task(async function test_no_token_make_credential() {
+  let rp = {id: document.domain, name: "none"};
+  let user = {id: new Uint8Array(), name: "none", displayName: "none"};
+  let param = {type: "public-key", alg: cose_alg_ECDSA_w_SHA256};
+  let makeCredentialOptions = {
+    rp, user, challenge: credentialChallenge, pubKeyCredParams: [param]
+  };
+  return credm.create({publicKey: makeCredentialOptions})
+  .then(function(aResult) {
+    ok(false, "Should have failed.");
+  })
+  .catch(function(aReason) {
+    ok(aReason.toString().startsWith("NotAllowedError"), aReason);
+  });
+});
+
+add_task(async function test_no_token_get_assertion() {
+  let newCredential = {
+    type: "public-key",
+    id: credentialId,
+    transports: ["usb"],
+  }
+  let publicKeyCredentialRequestOptions = {
+    challenge: assertionChallenge,
+    timeout: 5000, // the minimum timeout is actually 15 seconds
+    rpId: document.domain,
+    allowCredentials: [newCredential]
+  };
+  return credm.get({publicKey: publicKeyCredentialRequestOptions})
+  .then(function(aResult) {
+    ok(false, "Should have failed.");
+  })
+  .catch(function(aReason) {
+    ok(aReason.toString().startsWith("NotAllowedError"), aReason);
+  })
+});
+
+</script>
+
+</body>
+</html>
diff --git a/dom/webauthn/tests/test_webauthn_sameorigin.html b/dom/webauthn/tests/test_webauthn_sameorigin.html
new file mode 100644
index 0000000000..61a6430e87
--- /dev/null
+++ b/dom/webauthn/tests/test_webauthn_sameorigin.html
@@ -0,0 +1,317 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <title>Test for MakeCredential for W3C Web Authentication</title>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="u2futil.js"></script>
+  <script type="text/javascript" src="pkijs/common.js"></script>
+  <script type="text/javascript" src="pkijs/asn1.js"></script>
+  <script type="text/javascript" src="pkijs/x509_schema.js"></script>
+  <script type="text/javascript" src="pkijs/x509_simpl.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+  <h1>Test Same Origin Policy for W3C Web Authentication</h1>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1309284">Mozilla Bug 1309284</a>
+
+  <script class="testbody" type="text/javascript">
+    "use strict";
+
+    add_task(async () => {
+      await addVirtualAuthenticator();
+    });
+
+    is(navigator.authentication, undefined, "navigator.authentication does not exist any longer");
+    isnot(navigator.credentials, undefined, "Credential Management API endpoint must exist");
+    isnot(navigator.credentials.create, undefined, "CredentialManagement create API endpoint must exist");
+    isnot(navigator.credentials.get, undefined, "CredentialManagement get API endpoint must exist");
+
+    let credm;
+    let chall;
+    let user;
+    let param;
+    let gTrackedCredential;
+    add_task(() => {
+      credm = navigator.credentials;
+
+      chall = new Uint8Array(16);
+      window.crypto.getRandomValues(chall);
+
+      user = {id: new Uint8Array(16), name: "none", displayName: "none"};
+      param = {type: "public-key", alg: cose_alg_ECDSA_w_SHA256};
+      gTrackedCredential = {};
+    });
+
+    add_task(test_basic_good);
+    add_task(test_rp_id_unset);
+    add_task(test_rp_name_unset);
+    add_task(test_origin_with_optional_fields);
+    add_task(test_blank_rp_id);
+    add_task(test_subdomain);
+    add_task(test_same_origin);
+    add_task(test_etld);
+    add_task(test_different_domain_same_tld);
+    add_task(test_assertion_basic_good);
+    add_task(test_assertion_rp_id_unset);
+    add_task(test_assertion_origin_with_optional_fields);
+    add_task(test_assertion_blank_rp_id);
+    add_task(test_assertion_subdomain);
+    add_task(test_assertion_same_origin);
+    add_task(test_assertion_etld);
+    add_task(test_assertion_different_domain_same_tld);
+    add_task(test_basic_good_with_origin);
+    add_task(test_assertion_basic_good_with_origin);
+    add_task(test_assertion_invalid_rp_id);
+    add_task(test_assertion_another_invalid_rp_id);
+
+    function arrivingHereIsGood(aResult) {
+      ok(true, "Good result! Received a: " + aResult);
+    }
+
+    function arrivingHereIsBad(aResult) {
+      ok(false, "Bad result! Received a: " + aResult);
+    }
+
+    function expectSecurityError(aResult) {
+      ok(aResult.toString().startsWith("SecurityError"), "Expecting a SecurityError");
+    }
+
+    function expectTypeError(aResult) {
+      ok(aResult.toString().startsWith("TypeError"), "Expecting a TypeError");
+    }
+
+    function keepThisPublicKeyCredential(aIdentifier) {
+      return function(aPublicKeyCredential) {
+        gTrackedCredential[aIdentifier] = {
+          type: "public-key",
+          id: new Uint8Array(aPublicKeyCredential.rawId),
+          transports: [ "usb" ],
+        }
+        return Promise.resolve(aPublicKeyCredential);
+      }
+    }
+
+    function test_basic_good() {
+      // Test basic good call
+      let rp = {id: document.domain, name: "none"};
+      let makeCredentialOptions = {
+        rp, user, challenge: chall, pubKeyCredParams: [param]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+                  .then(keepThisPublicKeyCredential("basic"))
+                  .then(arrivingHereIsGood)
+                  .catch(arrivingHereIsBad);
+    }
+    function test_rp_id_unset() {
+      // Test rp.id being unset
+      let makeCredentialOptions = {
+        rp: {name: "none"}, user, challenge: chall, pubKeyCredParams: [param]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+                  .then(arrivingHereIsGood)
+                  .catch(arrivingHereIsBad);
+    }
+    function test_rp_name_unset() {
+      // Test rp.name being unset
+      let makeCredentialOptions = {
+        rp: {id: document.domain}, user, challenge: chall, pubKeyCredParams: [param]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+                  .then(arrivingHereIsBad)
+                  .catch(expectTypeError);
+    }
+    function test_origin_with_optional_fields() {
+      // Test this origin with optional fields
+      let rp = {id: "user:pass@" + document.domain + ":8888", name: "none"};
+      let makeCredentialOptions = {
+        rp, user, challenge: chall, pubKeyCredParams: [param]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+                  .then(arrivingHereIsBad)
+                  .catch(expectSecurityError);
+    }
+    function test_blank_rp_id() {
+      // Test blank rp.id
+      let rp = {id: "", name: "none"};
+      let makeCredentialOptions = {
+        rp, user, challenge: chall, pubKeyCredParams: [param]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+                  .then(arrivingHereIsBad)
+                  .catch(expectSecurityError);
+    }
+    function test_subdomain() {
+      // Test subdomain of this origin
+      let rp = {id: "subdomain." + document.domain, name: "none"};
+      let makeCredentialOptions = {
+        rp, user, challenge: chall, pubKeyCredParams: [param]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+                  .then(arrivingHereIsBad)
+                  .catch(expectSecurityError);
+    }
+    function test_same_origin() {
+      // Test the same origin
+      let rp = {id: "example.com", name: "none"};
+      let makeCredentialOptions = {
+        rp, user, challenge: chall, pubKeyCredParams: [param]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+                  .then(arrivingHereIsGood)
+                  .catch(arrivingHereIsBad);
+    }
+    function test_etld() {
+      // Test the eTLD
+      let rp = {id: "com", name: "none"};
+      let makeCredentialOptions = {
+        rp, user, challenge: chall, pubKeyCredParams: [param]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+                  .then(arrivingHereIsBad)
+                  .catch(expectSecurityError);
+    }
+    function test_different_domain_same_tld() {
+      // Test a different domain within the same TLD
+      let rp = {id: "alt.test", name: "none"};
+      let makeCredentialOptions = {
+        rp, user, challenge: chall, pubKeyCredParams: [param]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+                  .then(arrivingHereIsBad)
+                  .catch(expectSecurityError);
+    }
+    function test_assertion_basic_good() {
+      // Test basic good call
+      let publicKeyCredentialRequestOptions = {
+        challenge: chall,
+        rpId: document.domain,
+        allowCredentials: [gTrackedCredential.basic]
+      };
+      return credm.get({publicKey: publicKeyCredentialRequestOptions})
+                  .then(arrivingHereIsGood)
+                  .catch(arrivingHereIsBad);
+    }
+    function test_assertion_rp_id_unset() {
+      // Test rpId being unset
+      let publicKeyCredentialRequestOptions = {
+        challenge: chall,
+        allowCredentials: [gTrackedCredential.basic]
+      };
+      return credm.get({publicKey: publicKeyCredentialRequestOptions})
+                  .then(arrivingHereIsGood)
+                  .catch(arrivingHereIsBad);
+    }
+    function test_assertion_origin_with_optional_fields() {
+      // Test this origin with optional fields
+      let publicKeyCredentialRequestOptions = {
+        challenge: chall,
+        rpId: "user:pass@" + document.origin + ":8888",
+        allowCredentials: [gTrackedCredential.basic]
+      };
+      return credm.get({publicKey: publicKeyCredentialRequestOptions})
+                  .then(arrivingHereIsBad)
+                  .catch(expectSecurityError);
+    }
+    function test_assertion_blank_rp_id() {
+      // Test blank rpId
+      let publicKeyCredentialRequestOptions = {
+        challenge: chall,
+        rpId: "",
+        allowCredentials: [gTrackedCredential.basic]
+      };
+      return credm.get({publicKey: publicKeyCredentialRequestOptions})
+                  .then(arrivingHereIsBad)
+                  .catch(expectSecurityError);
+    }
+    function test_assertion_subdomain() {
+      // Test subdomain of this origin
+      let publicKeyCredentialRequestOptions = {
+        challenge: chall,
+        rpId: "subdomain." + document.domain,
+        allowCredentials: [gTrackedCredential.basic]
+      };
+      return credm.get({publicKey: publicKeyCredentialRequestOptions})
+                  .then(arrivingHereIsBad)
+                  .catch(expectSecurityError);
+    }
+    function test_assertion_same_origin() {
+      // Test the same origin
+      let publicKeyCredentialRequestOptions = {
+        challenge: chall,
+        rpId: "example.com",
+        allowCredentials: [gTrackedCredential.basic]
+      };
+      return credm.get({publicKey: publicKeyCredentialRequestOptions})
+                  .then(arrivingHereIsGood)
+                  .catch(arrivingHereIsBad);
+    }
+    function test_assertion_etld() {
+      // Test the eTLD
+      let publicKeyCredentialRequestOptions = {
+        challenge: chall,
+        rpId: "com",
+        allowCredentials: [gTrackedCredential.basic]
+      };
+      return credm.get({publicKey: publicKeyCredentialRequestOptions})
+                  .then(arrivingHereIsBad)
+                  .catch(expectSecurityError);
+    }
+    function test_assertion_different_domain_same_tld() {
+      // Test a different domain within the same TLD
+      let publicKeyCredentialRequestOptions = {
+        challenge: chall,
+        rpId: "alt.test",
+        allowCredentials: [gTrackedCredential.basic]
+      };
+      return credm.get({publicKey: publicKeyCredentialRequestOptions})
+                  .then(arrivingHereIsBad)
+                  .catch(expectSecurityError);
+    }
+    function test_basic_good_with_origin() {
+      // Test basic good Create call but using an origin (Bug 1380421)
+      let rp = {id: window.origin, name: "none"};
+      let makeCredentialOptions = {
+        rp, user, challenge: chall, pubKeyCredParams: [param]
+      };
+      return credm.create({publicKey: makeCredentialOptions})
+                  .then(arrivingHereIsBad)
+                  .catch(expectSecurityError);
+    }
+    function test_assertion_basic_good_with_origin() {
+      // Test basic good Get call but using an origin (Bug 1380421)
+      let publicKeyCredentialRequestOptions = {
+        challenge: chall,
+        rpId: window.origin,
+        allowCredentials: [gTrackedCredential.basic]
+      };
+      return credm.get({publicKey: publicKeyCredentialRequestOptions})
+                  .then(arrivingHereIsBad)
+                  .catch(expectSecurityError);
+    }
+    function test_assertion_invalid_rp_id() {
+      // Test with an rpId that is not a valid domain string
+      let publicKeyCredentialRequestOptions = {
+        challenge: chall,
+        rpId: document.domain + ":somejunk",
+        allowCredentials: [gTrackedCredential.basic]
+      };
+      return credm.get({publicKey: publicKeyCredentialRequestOptions})
+                  .then(arrivingHereIsBad)
+                  .catch(arrivingHereIsGood);
+    }
+    function test_assertion_another_invalid_rp_id() {
+      // Test with another rpId that is not a valid domain string
+      let publicKeyCredentialRequestOptions = {
+        challenge: chall,
+        rpId: document.domain + ":8888",
+        allowCredentials: [gTrackedCredential.basic]
+      };
+      return credm.get({publicKey: publicKeyCredentialRequestOptions})
+                  .then(arrivingHereIsBad)
+                  .catch(arrivingHereIsGood);
+    }
+  </script>
+
+</body>
+</html>
diff --git a/dom/webauthn/tests/test_webauthn_sameoriginwithancestors.html b/dom/webauthn/tests/test_webauthn_sameoriginwithancestors.html
new file mode 100644
index 0000000000..276ac16bdd
--- /dev/null
+++ b/dom/webauthn/tests/test_webauthn_sameoriginwithancestors.html
@@ -0,0 +1,108 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <title>Test for MakeCredential for W3C Web Authentication (sameOriginWithAncestors = false)</title>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="u2futil.js"></script>
+  <script type="text/javascript" src="pkijs/common.js"></script>
+  <script type="text/javascript" src="pkijs/asn1.js"></script>
+  <script type="text/javascript" src="pkijs/x509_schema.js"></script>
+  <script type="text/javascript" src="pkijs/x509_simpl.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+  <h1>Test Same Origin Policy for W3C Web Authentication (sameOriginWithAncestors = false)</h1>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1694639">Mozilla Bug 1694639</a>
+
+  <script class="testbody" type="text/javascript">
+    "use strict";
+
+    add_task(async () => {
+      await addVirtualAuthenticator();
+    });
+
+    var gTrackedCredential = {};
+
+    function arrivingHereIsGood(aResult) {
+      ok(true, "Good result! Received a: " + aResult);
+    }
+
+    function arrivingHereIsBad(aResult) {
+      ok(false, "Bad result! Received a: " + aResult);
+    }
+
+    function expectNotAllowedError(aResult) {
+      ok(aResult == "NotAllowedError", "Expecting a NotAllowedError, got " + aResult);
+    }
+
+    function keepThisPublicKeyCredential(aIdentifier) {
+      return function(aPublicKeyCredential) {
+        gTrackedCredential[aIdentifier] = {
+          type: "public-key",
+          id: new Uint8Array(aPublicKeyCredential.rawId),
+          transports: [ "usb" ],
+        }
+        return Promise.resolve(aPublicKeyCredential);
+      }
+    }
+
+    add_task(async function runTests() {
+      let iframe = document.createElement("iframe");
+      iframe.src = "https://example.org";
+      document.body.appendChild(iframe);
+      await new Promise(resolve => iframe.addEventListener("load", resolve, {once: true}));
+
+      is(navigator.authentication, undefined, "navigator.authentication does not exist any longer");
+      isnot(navigator.credentials, undefined, "Credential Management API endpoint must exist");
+      isnot(navigator.credentials.create, undefined, "CredentialManagement create API endpoint must exist");
+      isnot(navigator.credentials.get, undefined, "CredentialManagement get API endpoint must exist");
+
+      let credm = navigator.credentials;
+
+      let chall = new Uint8Array(16);
+      window.crypto.getRandomValues(chall);
+
+      let user = {id: new Uint8Array(16), name: "none", displayName: "none"};
+      let param = {type: "public-key", alg: cose_alg_ECDSA_w_SHA256};
+
+      let rp = {id: document.domain, name: "none"};
+      let makeCredentialOptions = {
+        rp, user, challenge: chall, pubKeyCredParams: [param]
+      };
+      await credm.create({publicKey: makeCredentialOptions})
+        .then(keepThisPublicKeyCredential("basic"))
+        .catch(arrivingHereIsBad);
+
+      var testFuncs = [
+        function (args) {
+          // Test create when sameOriginWithAncestors = false
+          let credentialOptions = {
+            rp: args.rp, user: args.user, challenge: args.challenge, pubKeyCredParams: [args.param]
+          };
+          return this.content.window.navigator.credentials.create({publicKey: credentialOptions})
+            .catch(e => Promise.reject(e.name));
+        },
+        function (args) {
+          // Test get when sameOriginWithAncestors = false
+          let publicKeyCredentialRequestOptions = {
+            challenge: args.challenge,
+            rpId: args.rp.id,
+            allowCredentials: [args.trackedCredential.basic]
+          };
+          return this.content.window.navigator.credentials.get({publicKey: publicKeyCredentialRequestOptions})
+            .catch(e => Promise.reject(e.name));
+        },
+      ];
+
+      let args = { user, param, rp, challenge: chall, trackedCredential: gTrackedCredential }
+      for(let func of testFuncs) {
+        await SpecialPowers.spawn(iframe, [args], func)
+          .then(arrivingHereIsBad)
+          .catch(expectNotAllowedError);
+      }
+    });
+  </script>
+
+</body>
+</html>
diff --git a/dom/webauthn/tests/test_webauthn_serialization.html b/dom/webauthn/tests/test_webauthn_serialization.html
new file mode 100644
index 0000000000..492749434f
--- /dev/null
+++ b/dom/webauthn/tests/test_webauthn_serialization.html
@@ -0,0 +1,304 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <title>Tests W3C Web Authentication Data Types Serialization</title>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="u2futil.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+  <h1>Tests W3C Web Authentication Data Types Serialization</h1>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1823782">Mozilla Bug 1823782</a>
+
+  <script class="testbody" type="text/javascript">
+    "use strict";
+
+    const { Assert } = SpecialPowers.ChromeUtils.importESModule(
+      "resource://testing-common/Assert.sys.mjs"
+    );
+
+    function arrayBufferEqualsArray(actual, expected, description) {
+      ok(actual instanceof ArrayBuffer, `${description} (actual should be array)`);
+      ok(expected instanceof Array, `${description} (expected should be array)`);
+      is(actual.byteLength, expected.length, `${description} (actual and expected should have same length)`);
+      let actualView = new Uint8Array(actual);
+      for (let i = 0; i < actualView.length; i++) {
+        if (actualView[i] != expected[i]) {
+          throw new Error(`actual and expected differ in byte ${i}: ${actualView[i]} vs ${expected[i]}`);
+        }
+      }
+      ok(true, description);
+    }
+
+    function isEmptyArray(arr, description) {
+      ok(arr instanceof Array, `${description} (expecting Array)`);
+      is(arr.length, 0, `${description} (array should be empty)`);
+    }
+
+    function shouldThrow(func, expectedError, description) {
+      let threw = false;
+      try {
+        func();
+      } catch (e) {
+        is(e.message, expectedError);
+        threw = true;
+      }
+      ok(threw, description);
+    }
+
+    add_task(function test_parseCreationOptionsFromJSON_minimal() {
+      let creationOptionsJSON = {
+        rp: { name: "Example" },
+        user: { id: "-l3qNLTKJng", name: "username", displayName: "display name" },
+        challenge: "XNJTTB3kfqk",
+        pubKeyCredParams: [],
+      };
+      let creationOptions = PublicKeyCredential.parseCreationOptionsFromJSON(creationOptionsJSON);
+      is(Object.getOwnPropertyNames(creationOptions).length, 8, "creation options should have 8 properties");
+      is(creationOptions.rp.id, undefined, "rp.id should be undefined");
+      is(creationOptions.rp.name, "Example", "rp.name should be Example");
+      arrayBufferEqualsArray(creationOptions.user.id, [ 250, 93, 234, 52, 180, 202, 38, 120 ], "user.id should be as expected");
+      is(creationOptions.user.displayName, "display name", "user.displayName should be 'display name'");
+      is(creationOptions.user.name, "username", "user.name should be username");
+      arrayBufferEqualsArray(creationOptions.challenge, [ 92, 210, 83, 76, 29, 228, 126, 169 ], "challenge should be as expected");
+      isEmptyArray(creationOptions.pubKeyCredParams, "pubKeyCredParams should be an empty array");
+      is(creationOptions.timeout, undefined, "timeout should be undefined");
+      isEmptyArray(creationOptions.excludeCredentials, "excludeCredentials should be an empty array");
+      is(creationOptions.authenticatorSelection.authenticatorAttachment, undefined, "authenticatorSelection.authenticatorAttachment should be undefined");
+      is(creationOptions.authenticatorSelection.residentKey, undefined, "creationOptions.authenticatorSelection.residentKey should be undefined");
+      is(creationOptions.authenticatorSelection.requireResidentKey, false, "creationOptions.authenticatorSelection.requireResidentKey should be false");
+      is(creationOptions.authenticatorSelection.userVerification, "preferred", "creationOptions.authenticatorSelection.userVerification should be preferred");
+      is(creationOptions.attestation, "none", "attestation should be none");
+      is(Object.getOwnPropertyNames(creationOptions.extensions).length, 0, "extensions should be an empty object");
+    });
+
+    add_task(function test_parseCreationOptionsFromJSON() {
+      let creationOptionsJSON = {
+        rp: { name: "Example", id: "example.com" },
+        user: { id: "19TVpqBBOAM", name: "username2", displayName: "another display name" },
+        challenge: "dR82FeUh5q4",
+        pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+        timeout: 20000,
+        excludeCredentials: [{ type: "public-key", id: "TeM2k_di7Dk", transports: [ "usb" ]}],
+        authenticatorSelection: { authenticatorAttachment: "platform", residentKey: "required", requireResidentKey: true, userVerification: "discouraged" },
+        hints: ["hybrid"],
+        attestation: "indirect",
+        attestationFormats: ["fido-u2f"],
+        extensions: { appid: "https://www.example.com/appID", credProps: true, hmacCreateSecret: true, minPinLength: true },
+      };
+      let creationOptions = PublicKeyCredential.parseCreationOptionsFromJSON(creationOptionsJSON);
+      is(Object.getOwnPropertyNames(creationOptions).length, 9, "creation options should have 9 properties");
+      is(creationOptions.rp.name, "Example", "rp.name should be Example");
+      is(creationOptions.rp.id, "example.com", "rp.id should be example.com");
+      arrayBufferEqualsArray(creationOptions.user.id, [ 215, 212, 213, 166, 160, 65, 56, 3 ], "user.id should be as expected");
+      is(creationOptions.user.displayName, "another display name", "user.displayName should be 'another display name'");
+      is(creationOptions.user.name, "username2", "user.name should be username2");
+      arrayBufferEqualsArray(creationOptions.challenge, [ 117, 31, 54, 21, 229, 33, 230, 174 ], "challenge should be as expected");
+      is(creationOptions.pubKeyCredParams.length, 1, "pubKeyCredParams should have one element");
+      is(creationOptions.pubKeyCredParams[0].type, "public-key", "pubKeyCredParams[0].type should be public-key");
+      is(creationOptions.pubKeyCredParams[0].alg, -7, "pubKeyCredParams[0].alg should be -7");
+      is(creationOptions.timeout, 20000, "timeout should be 20000");
+      is(creationOptions.excludeCredentials.length, 1, "excludeCredentials should have one element");
+      is(creationOptions.excludeCredentials[0].type, "public-key", "excludeCredentials[0].type should be public-key");
+      arrayBufferEqualsArray(creationOptions.excludeCredentials[0].id, [ 77, 227, 54, 147, 247, 98, 236, 57 ], "excludeCredentials[0].id should be as expected");
+      is(creationOptions.excludeCredentials[0].transports.length, 1, "excludeCredentials[0].transports should have one element");
+      is(creationOptions.excludeCredentials[0].transports[0], "usb", "excludeCredentials[0].transports[0] should be usb");
+      is(creationOptions.authenticatorSelection.authenticatorAttachment, "platform", "authenticatorSelection.authenticatorAttachment should be platform");
+      is(creationOptions.authenticatorSelection.residentKey, "required", "creationOptions.authenticatorSelection.residentKey should be required");
+      is(creationOptions.authenticatorSelection.requireResidentKey, true, "creationOptions.authenticatorSelection.requireResidentKey should be true");
+      is(creationOptions.authenticatorSelection.userVerification, "discouraged", "creationOptions.authenticatorSelection.userVerification should be discouraged");
+      is(creationOptions.attestation, "indirect", "attestation should be indirect");
+      is(creationOptions.extensions.appid, "https://www.example.com/appID", "extensions.appid should be https://www.example.com/appID");
+      is(creationOptions.extensions.credProps, true, "extensions.credProps should be true");
+      is(creationOptions.extensions.hmacCreateSecret, true, "extensions.hmacCreateSecret should be true");
+      is(creationOptions.extensions.minPinLength, true, "extensions.minPinLength should be true");
+    });
+
+    add_task(function test_parseCreationOptionsFromJSON_malformed() {
+      let userIdNotBase64 = {
+        rp: { name: "Example" },
+        user: { id: "/not urlsafe base64+", name: "username", displayName: "display name" },
+        challenge: "XNJTTB3kfqk",
+        pubKeyCredParams: [],
+      };
+      shouldThrow(
+        () => { PublicKeyCredential.parseCreationOptionsFromJSON(userIdNotBase64); },
+        "PublicKeyCredential.parseCreationOptionsFromJSON: could not decode user ID as urlsafe base64",
+        "should get encoding error if user.id is not urlsafe base64"
+      );
+
+      let challengeNotBase64 = {
+        rp: { name: "Example" },
+        user: { id: "-l3qNLTKJng", name: "username", displayName: "display name" },
+        challenge: "this is not urlsafe base64!",
+        pubKeyCredParams: [],
+      };
+      shouldThrow(
+        () => { PublicKeyCredential.parseCreationOptionsFromJSON(challengeNotBase64); },
+        "PublicKeyCredential.parseCreationOptionsFromJSON: could not decode challenge as urlsafe base64",
+        "should get encoding error if challenge is not urlsafe base64"
+      );
+
+      let excludeCredentialsIdNotBase64 = {
+        rp: { name: "Example", id: "example.com" },
+        user: { id: "-l3qNLTKJng", name: "username", displayName: "display name" },
+        challenge: "dR82FeUh5q4",
+        pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+        timeout: 20000,
+        excludeCredentials: [{ type: "public-key", id: "@#$%&^", transports: [ "usb" ]}],
+        authenticatorselection: { authenticatorattachment: "platform", residentkey: "required", requireresidentkey: true, userverification: "discouraged" },
+        hints: ["hybrid"],
+        attestation: "indirect",
+        attestationformats: ["fido-u2f"],
+        extensions: { appid: "https://www.example.com/appid", hmaccreatesecret: true },
+      };
+      shouldThrow(
+        () => { PublicKeyCredential.parseCreationOptionsFromJSON(excludeCredentialsIdNotBase64); },
+        "PublicKeyCredential.parseCreationOptionsFromJSON: could not decode excluded credential ID as urlsafe base64",
+        "should get encoding error if excludeCredentials[0].id is not urlsafe base64"
+      );
+    });
+
+    add_task(function test_parseRequestOptionsFromJSON_minimal() {
+      let requestOptionsJSON = {
+        challenge: "3yW2WHD_jbU",
+      };
+      let requestOptions = PublicKeyCredential.parseRequestOptionsFromJSON(requestOptionsJSON);
+      is(Object.getOwnPropertyNames(requestOptions).length, 4, "request options should have 4 properties");
+      arrayBufferEqualsArray(requestOptions.challenge, [ 223, 37, 182, 88, 112, 255, 141, 181 ], "challenge should be as expected");
+      is(requestOptions.timeout, undefined, "timeout should be undefined");
+      is(requestOptions.rpId, undefined, "rpId should be undefined");
+      isEmptyArray(requestOptions.allowCredentials, "allowCredentials should be an empty array");
+      is(requestOptions.userVerification, "preferred", "userVerification should be preferred");
+      is(Object.getOwnPropertyNames(requestOptions.extensions).length, 0, "extensions should be an empty object");
+    });
+
+    add_task(function test_parseRequestOptionsFromJSON() {
+      let requestOptionsJSON = {
+        challenge: "QAfaZwEQCkQ",
+        timeout: 25000,
+        rpId: "example.com",
+        allowCredentials: [{type: "public-key", id: "BTBXXGuXRTk", transports: ["smart-card"] }],
+        userVerification: "discouraged",
+        hints: ["client-device"],
+        attestation: "enterprise",
+        attestationFormats: ["packed"],
+        extensions: { appid: "https://www.example.com/anotherAppID", hmacCreateSecret: false },
+      };
+      let requestOptions = PublicKeyCredential.parseRequestOptionsFromJSON(requestOptionsJSON);
+      is(Object.getOwnPropertyNames(requestOptions).length, 6, "request options should have 6 properties");
+      arrayBufferEqualsArray(requestOptions.challenge, [ 64, 7, 218, 103, 1, 16, 10, 68 ], "challenge should be as expected");
+      is(requestOptions.timeout, 25000, "timeout should be 25000");
+      is(requestOptions.rpId, "example.com", "rpId should be example.com");
+      is(requestOptions.allowCredentials.length, 1, "allowCredentials should have one element");
+      is(requestOptions.allowCredentials[0].type, "public-key", "allowCredentials[0].type should be public-key");
+      arrayBufferEqualsArray(requestOptions.allowCredentials[0].id, [ 5, 48, 87, 92, 107, 151, 69, 57 ], "allowCredentials[0].id should be as expected");
+      is(requestOptions.allowCredentials[0].transports.length, 1, "allowCredentials[0].transports should have one element");
+      is(requestOptions.allowCredentials[0].transports[0], "smart-card", "allowCredentials[0].transports[0] should be usb");
+      is(requestOptions.userVerification, "discouraged", "userVerification should be discouraged");
+      is(requestOptions.extensions.appid, "https://www.example.com/anotherAppID", "extensions.appid should be https://www.example.com/anotherAppID");
+      is(requestOptions.extensions.hmacCreateSecret, false, "extensions.hmacCreateSecret should be false");
+    });
+
+    add_task(function test_parseRequestOptionsFromJSON_malformed() {
+      let challengeNotBase64 = {
+        challenge: "/not+urlsafe+base64/",
+      };
+      shouldThrow(
+        () => { PublicKeyCredential.parseRequestOptionsFromJSON(challengeNotBase64); },
+        "PublicKeyCredential.parseRequestOptionsFromJSON: could not decode challenge as urlsafe base64",
+        "should get encoding error if challenge is not urlsafe base64"
+      );
+
+      let allowCredentialsIdNotBase64 = {
+        challenge: "QAfaZwEQCkQ",
+        timeout: 25000,
+        rpId: "example.com",
+        allowCredentials: [{type: "public-key", id: "not urlsafe base64", transports: ["smart-card"] }],
+        userVerification: "discouraged",
+        hints: ["client-device"],
+        attestation: "enterprise",
+        attestationFormats: ["packed"],
+        extensions: { appid: "https://www.example.com/anotherAppID", hmacCreateSecret: false },
+      };
+      shouldThrow(
+        () => { PublicKeyCredential.parseRequestOptionsFromJSON(allowCredentialsIdNotBase64); },
+        "PublicKeyCredential.parseRequestOptionsFromJSON: could not decode allowed credential ID as urlsafe base64",
+        "should get encoding error if allowCredentials[0].id is not urlsafe base64"
+      );
+    });
+
+    add_task(async () => {
+      await addVirtualAuthenticator();
+    });
+
+    function isUrlsafeBase64(urlsafeBase64) {
+      try {
+        atob(urlsafeBase64.replace(/_/g, "/").replace(/-/g, "+"));
+        return true;
+      } catch (_) {}
+      return false;
+    }
+
+    add_task(async function test_registrationResponse_toJSON() {
+      let publicKey = {
+        rp: {id: document.domain, name: "none", icon: "none"},
+        user: {id: new Uint8Array(), name: "none", icon: "none", displayName: "none"},
+        challenge: crypto.getRandomValues(new Uint8Array(16)),
+        pubKeyCredParams: [{type: "public-key", alg: cose_alg_ECDSA_w_SHA256}],
+        authenticatorSelection: { residentKey: "discouraged" },
+        extensions: { credProps: true }
+      };
+      let registrationResponse = await navigator.credentials.create({publicKey});
+      let registrationResponseJSON = registrationResponse.toJSON();
+      is(Object.keys(registrationResponseJSON).length, 6, "registrationResponseJSON should have 6 properties");
+      is(registrationResponseJSON.id, registrationResponseJSON.rawId, "registrationResponseJSON.id and rawId should be the same");
+      ok(isUrlsafeBase64(registrationResponseJSON.id), "registrationResponseJSON.id should be urlsafe base64");
+      is(Object.keys(registrationResponseJSON.response).length, 6, "registrationResponseJSON.response should have 6 properties");
+      ok(isUrlsafeBase64(registrationResponseJSON.response.clientDataJSON), "registrationResponseJSON.response.clientDataJSON should be urlsafe base64");
+      ok(isUrlsafeBase64(registrationResponseJSON.response.authenticatorData), "registrationResponseJSON.response.authenticatorData should be urlsafe base64");
+      ok(isUrlsafeBase64(registrationResponseJSON.response.publicKey), "registrationResponseJSON.response.publicKey should be urlsafe base64");
+      ok(isUrlsafeBase64(registrationResponseJSON.response.attestationObject), "registrationResponseJSON.response.attestationObject should be urlsafe base64");
+      is(registrationResponseJSON.response.publicKeyAlgorithm, cose_alg_ECDSA_w_SHA256, "registrationResponseJSON.response.publicKeyAlgorithm should be ECDSA with SHA256 (COSE)");
+      is(registrationResponseJSON.response.transports.length, 1, "registrationResponseJSON.response.transports.length should be 1");
+      is(registrationResponseJSON.response.transports[0], "internal", "registrationResponseJSON.response.transports[0] should be internal");
+      is(registrationResponseJSON.authenticatorAttachment, "platform", "registrationResponseJSON.authenticatorAttachment should be platform");
+      is(registrationResponseJSON.clientExtensionResults?.credProps?.rk, false, "registrationResponseJSON.clientExtensionResults.credProps.rk should be false");
+      is(registrationResponseJSON.type, "public-key", "registrationResponseJSON.type should be public-key");
+    });
+
+    add_task(async function test_assertionResponse_toJSON() {
+      let registrationRequest = {
+        publicKey: {
+          rp: {id: document.domain, name: "none", icon: "none"},
+          user: {id: new Uint8Array(), name: "none", icon: "none", displayName: "none"},
+          challenge: crypto.getRandomValues(new Uint8Array(16)),
+          pubKeyCredParams: [{type: "public-key", alg: cose_alg_ECDSA_w_SHA256}],
+        },
+      };
+      let registrationResponse = await navigator.credentials.create(registrationRequest);
+
+      let assertionRequest = {
+        publicKey: {
+          challenge: crypto.getRandomValues(new Uint8Array(16)),
+          allowCredentials: [{ type: "public-key", id: registrationResponse.rawId }],
+        },
+      };
+      let assertionResponse = await navigator.credentials.get(assertionRequest);
+      let assertionResponseJSON = assertionResponse.toJSON();
+      is(Object.keys(assertionResponseJSON).length, 6, "assertionResponseJSON should have 6 properties");
+      is(assertionResponseJSON.id, assertionResponseJSON.rawId, "assertionResponseJSON.id and rawId should be the same");
+      ok(isUrlsafeBase64(assertionResponseJSON.id), "assertionResponseJSON.id should be urlsafe base64");
+      is(Object.keys(assertionResponseJSON.response).length, 3, "assertionResponseJSON.response should have 3 properties");
+      ok(isUrlsafeBase64(assertionResponseJSON.response.clientDataJSON), "assertionResponseJSON.response.clientDataJSON should be urlsafe base64");
+      ok(isUrlsafeBase64(assertionResponseJSON.response.authenticatorData), "assertionResponseJSON.response.authenticatorData should be urlsafe base64");
+      ok(isUrlsafeBase64(assertionResponseJSON.response.signature), "assertionResponseJSON.response.signature should be urlsafe base64");
+      is(assertionResponseJSON.authenticatorAttachment, "platform", "assertionResponseJSON.authenticatorAttachment should be platform");
+      is(Object.keys(assertionResponseJSON.clientExtensionResults).length, 0, "assertionResponseJSON.clientExtensionResults should be an empty dictionary");
+      is(assertionResponseJSON.type, "public-key", "assertionResponseJSON.type should be public-key");
+    });
+  </script>
+
+</body>
+</html>
diff --git a/dom/webauthn/tests/test_webauthn_store_credential.html b/dom/webauthn/tests/test_webauthn_store_credential.html
new file mode 100644
index 0000000000..92782acc47
--- /dev/null
+++ b/dom/webauthn/tests/test_webauthn_store_credential.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <title>Tests for Store for W3C Web Authentication</title>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="u2futil.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+  <h1>Tests for Store for W3C Web Authentication</h1>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1309284">Mozilla Bug 1309284</a>
+
+  <script class="testbody" type="text/javascript">
+    "use strict";
+
+    add_task(async () => {
+      await addVirtualAuthenticator();
+    });
+
+    isnot(navigator.credentials, undefined, "Credential Management API endpoint must exist");
+    isnot(navigator.credentials.create, undefined, "CredentialManagement create API endpoint must exist");
+    isnot(navigator.credentials.get, undefined, "CredentialManagement get API endpoint must exist");
+    isnot(navigator.credentials.store, undefined, "CredentialManagement store API endpoint must exist");
+
+    function arrivingHereIsBad(aResult) {
+      ok(false, "Bad result! Received a: " + aResult);
+      return Promise.resolve();
+    }
+
+    function expectNotSupportedError(aResult) {
+      ok(aResult.toString().startsWith("NotSupportedError"), "Expecting a NotSupportedError, received: " + aResult);
+      return Promise.resolve();
+    }
+
+    add_task(async function test_store_credential() {
+      let credentialChallenge = new Uint8Array(16);
+      window.crypto.getRandomValues(credentialChallenge);
+
+      let rp = {id: document.domain, name: "none"};
+      let user = {id: new Uint8Array(64), name: "none", displayName: "none"};
+      let params = [ {type: "public-key", alg: "es256"}, {type: "public-key", alg: -7} ]
+
+      let makeCredentialOptions = {
+        rp, user, challenge: credentialChallenge, pubKeyCredParams: params
+      };
+
+      let credential = await navigator.credentials.create({publicKey: makeCredentialOptions})
+        .catch(arrivingHereIsBad);
+
+      await navigator.credentials.store(credential)
+        .then(arrivingHereIsBad)
+        .catch(expectNotSupportedError);
+    });
+  </script>
+
+</body>
+</html>
diff --git a/dom/webauthn/tests/test_webauthn_webdriver_virtual_authenticator.html b/dom/webauthn/tests/test_webauthn_webdriver_virtual_authenticator.html
new file mode 100644
index 0000000000..cb00f80e88
--- /dev/null
+++ b/dom/webauthn/tests/test_webauthn_webdriver_virtual_authenticator.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <title>Tests for WebDriver Virtual Authenticator Extension for W3C Web Authentication</title>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="u2futil.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+  <h1>Tests for WebDriver Virtual Authenticator Extension for W3C Web Authentication</h1>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1460986">Mozilla Bug 1460986</a>
+
+  <script class="testbody" type="text/javascript">
+    "use strict";
+
+    function arrivingHereIsGood(aResult) {
+      ok(true, "Good result! Received a: " + aResult);
+    }
+
+    function arrivingHereIsBad(aResult) {
+      ok(false, "Bad result! Received a: " + aResult);
+    }
+
+    function expectNotAllowedError(aResult) {
+      ok(aResult == "NotAllowedError", "Expecting a NotAllowedError, got " + aResult);
+    }
+
+    function getAssertion(id) {
+        let chall = new Uint8Array(16);
+        crypto.getRandomValues(chall);
+
+        let options = {
+          challenge: chall,
+          allowCredentials: [ { type: "public-key", id } ],
+        };
+
+        return navigator.credentials.get({publicKey: options});
+    }
+
+    add_task(async function test_add_and_remove_credential() {
+      let authenticatorId = await addVirtualAuthenticator();
+      let credIdB64 = await addCredential(authenticatorId, document.domain);
+      let credId = base64ToBytesUrlSafe(credIdB64);
+
+      await getAssertion(credId)
+          .then(arrivingHereIsGood)
+          .catch(arrivingHereIsBad);
+
+      await removeCredential(authenticatorId, credIdB64);
+      await getAssertion(credId)
+          .then(arrivingHereIsBad)
+          .catch(e => expectNotAllowedError(e.name));
+    });
+  </script>
+
+</body>
+</html>
diff --git a/dom/webauthn/tests/u2futil.js b/dom/webauthn/tests/u2futil.js
new file mode 100644
index 0000000000..e8026e8e59
--- /dev/null
+++ b/dom/webauthn/tests/u2futil.js
@@ -0,0 +1,511 @@
+// Used by local_addTest() / local_completeTest()
+var _countCompletions = 0;
+var _expectedCompletions = 0;
+
+const flag_TUP = 0x01;
+const flag_UV = 0x04;
+const flag_AT = 0x40;
+
+const cose_kty = 1;
+const cose_kty_ec2 = 2;
+const cose_alg = 3;
+const cose_alg_ECDSA_w_SHA256 = -7;
+const cose_alg_ECDSA_w_SHA512 = -36;
+const cose_crv = -1;
+const cose_crv_P256 = 1;
+const cose_crv_x = -2;
+const cose_crv_y = -3;
+
+var { AppConstants } = SpecialPowers.ChromeUtils.importESModule(
+  "resource://gre/modules/AppConstants.sys.mjs"
+);
+
+async function addVirtualAuthenticator(
+  protocol = "ctap2_1",
+  transport = "internal",
+  hasResidentKey = true,
+  hasUserVerification = true,
+  isUserConsenting = true,
+  isUserVerified = true
+) {
+  let id = await SpecialPowers.spawnChrome(
+    [
+      protocol,
+      transport,
+      hasResidentKey,
+      hasUserVerification,
+      isUserConsenting,
+      isUserVerified,
+    ],
+    (
+      protocol,
+      transport,
+      hasResidentKey,
+      hasUserVerification,
+      isUserConsenting,
+      isUserVerified
+    ) => {
+      let webauthnService = Cc["@mozilla.org/webauthn/service;1"].getService(
+        Ci.nsIWebAuthnService
+      );
+      let id = webauthnService.addVirtualAuthenticator(
+        protocol,
+        transport,
+        hasResidentKey,
+        hasUserVerification,
+        isUserConsenting,
+        isUserVerified
+      );
+      return id;
+    }
+  );
+
+  SimpleTest.registerCleanupFunction(async () => {
+    await SpecialPowers.spawnChrome([id], id => {
+      let webauthnService = Cc["@mozilla.org/webauthn/service;1"].getService(
+        Ci.nsIWebAuthnService
+      );
+      webauthnService.removeVirtualAuthenticator(id);
+    });
+  });
+
+  return id;
+}
+
+function handleEventMessage(event) {
+  if ("test" in event.data) {
+    let summary = event.data.test + ": " + event.data.msg;
+    log(event.data.status + ": " + summary);
+    ok(event.data.status, summary);
+  } else if ("done" in event.data) {
+    SimpleTest.finish();
+  } else {
+    ok(false, "Unexpected message in the test harness: " + event.data);
+  }
+}
+
+function log(msg) {
+  console.log(msg);
+  let logBox = document.getElementById("log");
+  if (logBox) {
+    logBox.textContent += "\n" + msg;
+  }
+}
+
+function local_is(value, expected, message) {
+  if (value === expected) {
+    local_ok(true, message);
+  } else {
+    local_ok(false, message + " unexpectedly: " + value + " !== " + expected);
+  }
+}
+
+function local_isnot(value, expected, message) {
+  if (value !== expected) {
+    local_ok(true, message);
+  } else {
+    local_ok(false, message + " unexpectedly: " + value + " === " + expected);
+  }
+}
+
+function local_ok(expression, message) {
+  let body = { test: this.location.pathname, status: expression, msg: message };
+  parent.postMessage(body, "http://mochi.test:8888");
+}
+
+function local_doesThrow(fn, name) {
+  let gotException = false;
+  try {
+    fn();
+  } catch (ex) {
+    gotException = true;
+  }
+  local_ok(gotException, name);
+}
+
+function local_expectThisManyTests(count) {
+  if (_expectedCompletions > 0) {
+    local_ok(
+      false,
+      "Error: local_expectThisManyTests should only be called once."
+    );
+  }
+  _expectedCompletions = count;
+}
+
+function local_completeTest() {
+  _countCompletions += 1;
+  if (_countCompletions == _expectedCompletions) {
+    log("All tests completed.");
+    local_finished();
+  }
+  if (_countCompletions > _expectedCompletions) {
+    local_ok(
+      false,
+      "Error: local_completeTest called more than local_addTest."
+    );
+  }
+}
+
+function local_finished() {
+  parent.postMessage({ done: true }, "http://mochi.test:8888");
+}
+
+function string2buffer(str) {
+  return new Uint8Array(str.length).map((x, i) => str.charCodeAt(i));
+}
+
+function buffer2string(buf) {
+  let str = "";
+  if (!(buf.constructor === Uint8Array)) {
+    buf = new Uint8Array(buf);
+  }
+  buf.map(function (x) {
+    return (str += String.fromCharCode(x));
+  });
+  return str;
+}
+
+function bytesToBase64(u8a) {
+  let CHUNK_SZ = 0x8000;
+  let c = [];
+  let array = new Uint8Array(u8a);
+  for (let i = 0; i < array.length; i += CHUNK_SZ) {
+    c.push(String.fromCharCode.apply(null, array.subarray(i, i + CHUNK_SZ)));
+  }
+  return window.btoa(c.join(""));
+}
+
+function base64ToBytes(b64encoded) {
+  return new Uint8Array(
+    window
+      .atob(b64encoded)
+      .split("")
+      .map(function (c) {
+        return c.charCodeAt(0);
+      })
+  );
+}
+
+function bytesToBase64UrlSafe(buf) {
+  return bytesToBase64(buf)
+    .replace(/\+/g, "-")
+    .replace(/\//g, "_")
+    .replace(/=/g, "");
+}
+
+function base64ToBytesUrlSafe(str) {
+  if (str.length % 4 == 1) {
+    throw "Improper b64 string";
+  }
+
+  var b64 = str.replace(/\-/g, "+").replace(/\_/g, "/");
+  while (b64.length % 4 != 0) {
+    b64 += "=";
+  }
+  return base64ToBytes(b64);
+}
+
+function hexEncode(buf) {
+  return Array.from(buf)
+    .map(x => ("0" + x.toString(16)).substr(-2))
+    .join("");
+}
+
+function hexDecode(str) {
+  return new Uint8Array(str.match(/../g).map(x => parseInt(x, 16)));
+}
+
+function hasOnlyKeys(obj, ...keys) {
+  let okeys = new Set(Object.keys(obj));
+  return keys.length == okeys.size && keys.every(k => okeys.has(k));
+}
+
+function webAuthnDecodeCBORAttestation(aCborAttBuf) {
+  let attObj = CBOR.decode(aCborAttBuf);
+  console.log(":: Attestation CBOR Object ::");
+  if (!hasOnlyKeys(attObj, "authData", "fmt", "attStmt")) {
+    return Promise.reject("Invalid CBOR Attestation Object");
+  }
+  if (attObj.fmt == "fido-u2f" && !hasOnlyKeys(attObj.attStmt, "sig", "x5c")) {
+    return Promise.reject("Invalid CBOR Attestation Statement");
+  }
+  if (
+    attObj.fmt == "packed" &&
+    !(
+      hasOnlyKeys(attObj.attStmt, "alg", "sig") ||
+      hasOnlyKeys(attObj.attStmt, "alg", "sig", "x5c")
+    )
+  ) {
+    return Promise.reject("Invalid CBOR Attestation Statement");
+  }
+  if (attObj.fmt == "none" && Object.keys(attObj.attStmt).length) {
+    return Promise.reject("Invalid CBOR Attestation Statement");
+  }
+
+  return webAuthnDecodeAuthDataArray(new Uint8Array(attObj.authData)).then(
+    function (aAuthDataObj) {
+      attObj.authDataObj = aAuthDataObj;
+      return Promise.resolve(attObj);
+    }
+  );
+}
+
+function webAuthnDecodeAuthDataArray(aAuthData) {
+  let rpIdHash = aAuthData.slice(0, 32);
+  let flags = aAuthData.slice(32, 33);
+  let counter = aAuthData.slice(33, 37);
+
+  console.log(":: Authenticator Data ::");
+  console.log("RP ID Hash: " + hexEncode(rpIdHash));
+  console.log("Counter: " + hexEncode(counter) + " Flags: " + flags);
+
+  if ((flags & flag_AT) == 0x00) {
+    // No Attestation Data, so we're done.
+    return Promise.resolve({
+      rpIdHash,
+      flags,
+      counter,
+    });
+  }
+
+  if (aAuthData.length < 38) {
+    return Promise.reject(
+      "Authenticator Data flag was set, but not enough data passed in!"
+    );
+  }
+
+  let attData = {};
+  attData.aaguid = aAuthData.slice(37, 53);
+  attData.credIdLen = (aAuthData[53] << 8) + aAuthData[54];
+  attData.credId = aAuthData.slice(55, 55 + attData.credIdLen);
+
+  console.log(":: Authenticator Data ::");
+  console.log("AAGUID: " + hexEncode(attData.aaguid));
+
+  let cborPubKey = aAuthData.slice(55 + attData.credIdLen);
+  var pubkeyObj = CBOR.decode(cborPubKey.buffer);
+  if (
+    !(
+      cose_kty in pubkeyObj &&
+      cose_alg in pubkeyObj &&
+      cose_crv in pubkeyObj &&
+      cose_crv_x in pubkeyObj &&
+      cose_crv_y in pubkeyObj
+    )
+  ) {
+    throw "Invalid CBOR Public Key Object";
+  }
+  if (pubkeyObj[cose_kty] != cose_kty_ec2) {
+    throw "Unexpected key type";
+  }
+  if (pubkeyObj[cose_alg] != cose_alg_ECDSA_w_SHA256) {
+    throw "Unexpected public key algorithm";
+  }
+  if (pubkeyObj[cose_crv] != cose_crv_P256) {
+    throw "Unexpected curve";
+  }
+
+  let pubKeyBytes = assemblePublicKeyBytesData(
+    pubkeyObj[cose_crv_x],
+    pubkeyObj[cose_crv_y]
+  );
+  console.log(":: CBOR Public Key Object Data ::");
+  console.log("kty: " + pubkeyObj[cose_kty] + " (EC2)");
+  console.log("alg: " + pubkeyObj[cose_alg] + " (ES256)");
+  console.log("crv: " + pubkeyObj[cose_crv] + " (P256)");
+  console.log("X: " + pubkeyObj[cose_crv_x]);
+  console.log("Y: " + pubkeyObj[cose_crv_y]);
+  console.log("Uncompressed (hex): " + hexEncode(pubKeyBytes));
+
+  return importPublicKey(pubKeyBytes).then(function (aKeyHandle) {
+    return Promise.resolve({
+      rpIdHash,
+      flags,
+      counter,
+      attestationAuthData: attData,
+      publicKeyBytes: pubKeyBytes,
+      publicKeyHandle: aKeyHandle,
+    });
+  });
+}
+
+function importPublicKey(keyBytes) {
+  if (keyBytes[0] != 0x04 || keyBytes.byteLength != 65) {
+    throw "Bad public key octet string";
+  }
+  var jwk = {
+    kty: "EC",
+    crv: "P-256",
+    x: bytesToBase64UrlSafe(keyBytes.slice(1, 33)),
+    y: bytesToBase64UrlSafe(keyBytes.slice(33)),
+  };
+  return crypto.subtle.importKey(
+    "jwk",
+    jwk,
+    { name: "ECDSA", namedCurve: "P-256" },
+    true,
+    ["verify"]
+  );
+}
+
+function deriveAppAndChallengeParam(appId, clientData, attestation) {
+  var appIdBuf = string2buffer(appId);
+  return Promise.all([
+    crypto.subtle.digest("SHA-256", appIdBuf),
+    crypto.subtle.digest("SHA-256", clientData),
+  ]).then(function (digests) {
+    return {
+      appParam: new Uint8Array(digests[0]),
+      challengeParam: new Uint8Array(digests[1]),
+      attestation,
+    };
+  });
+}
+
+function assemblePublicKeyBytesData(xCoord, yCoord) {
+  // Produce an uncompressed EC key point. These start with 0x04, and then
+  // two 32-byte numbers denoting X and Y.
+  if (xCoord.length != 32 || yCoord.length != 32) {
+    throw "Coordinates must be 32 bytes long";
+  }
+  let keyBytes = new Uint8Array(65);
+  keyBytes[0] = 0x04;
+  xCoord.map((x, i) => (keyBytes[1 + i] = x));
+  yCoord.map((x, i) => (keyBytes[33 + i] = x));
+  return keyBytes;
+}
+
+function assembleSignedData(appParam, flags, counter, challengeParam) {
+  let signedData = new Uint8Array(32 + 1 + 4 + 32);
+  new Uint8Array(appParam).map((x, i) => (signedData[0 + i] = x));
+  signedData[32] = new Uint8Array(flags)[0];
+  new Uint8Array(counter).map((x, i) => (signedData[33 + i] = x));
+  new Uint8Array(challengeParam).map((x, i) => (signedData[37 + i] = x));
+  return signedData;
+}
+
+function assembleRegistrationSignedData(
+  appParam,
+  challengeParam,
+  keyHandle,
+  pubKey
+) {
+  let signedData = new Uint8Array(1 + 32 + 32 + keyHandle.length + 65);
+  signedData[0] = 0x00;
+  new Uint8Array(appParam).map((x, i) => (signedData[1 + i] = x));
+  new Uint8Array(challengeParam).map((x, i) => (signedData[33 + i] = x));
+  new Uint8Array(keyHandle).map((x, i) => (signedData[65 + i] = x));
+  new Uint8Array(pubKey).map(
+    (x, i) => (signedData[65 + keyHandle.length + i] = x)
+  );
+  return signedData;
+}
+
+function sanitizeSigArray(arr) {
+  // ECDSA signature fields into WebCrypto must be exactly 32 bytes long, so
+  // this method strips leading padding bytes, if added, and also appends
+  // padding zeros, if needed.
+  if (arr.length > 32) {
+    arr = arr.slice(arr.length - 32);
+  }
+  let ret = new Uint8Array(32);
+  ret.set(arr, ret.length - arr.length);
+  return ret;
+}
+
+function verifySignature(key, data, derSig) {
+  if (derSig.byteLength < 68) {
+    return Promise.reject(
+      "Invalid signature (length=" +
+        derSig.byteLength +
+        "): " +
+        hexEncode(new Uint8Array(derSig))
+    );
+  }
+
+  // Copy signature data into the current context.
+  let derSigCopy = new ArrayBuffer(derSig.byteLength);
+  new Uint8Array(derSigCopy).set(new Uint8Array(derSig));
+
+  let sigAsn1 = org.pkijs.fromBER(derSigCopy);
+
+  // pkijs.asn1 seems to erroneously set an error code when calling some
+  // internal function. The test suite doesn't like dangling globals.
+  delete window.error;
+
+  let sigR = new Uint8Array(
+    sigAsn1.result.value_block.value[0].value_block.value_hex
+  );
+  let sigS = new Uint8Array(
+    sigAsn1.result.value_block.value[1].value_block.value_hex
+  );
+
+  // The resulting R and S values from the ASN.1 Sequence must be fit into 32
+  // bytes. Sometimes they have leading zeros, sometimes they're too short, it
+  // all depends on what lib generated the signature.
+  let R = sanitizeSigArray(sigR);
+  let S = sanitizeSigArray(sigS);
+
+  console.log("Verifying these bytes: " + bytesToBase64UrlSafe(data));
+
+  let sigData = new Uint8Array(R.length + S.length);
+  sigData.set(R);
+  sigData.set(S, R.length);
+
+  let alg = { name: "ECDSA", hash: "SHA-256" };
+  return crypto.subtle.verify(alg, key, sigData, data);
+}
+
+async function addCredential(authenticatorId, rpId) {
+  let keyPair = await crypto.subtle.generateKey(
+    {
+      name: "ECDSA",
+      namedCurve: "P-256",
+    },
+    true,
+    ["sign"]
+  );
+
+  let credId = new Uint8Array(32);
+  crypto.getRandomValues(credId);
+  credId = bytesToBase64UrlSafe(credId);
+
+  let privateKey = await crypto.subtle
+    .exportKey("pkcs8", keyPair.privateKey)
+    .then(privateKey => bytesToBase64UrlSafe(privateKey));
+
+  await SpecialPowers.spawnChrome(
+    [authenticatorId, credId, rpId, privateKey],
+    (authenticatorId, credId, rpId, privateKey) => {
+      let webauthnService = Cc["@mozilla.org/webauthn/service;1"].getService(
+        Ci.nsIWebAuthnService
+      );
+
+      webauthnService.addCredential(
+        authenticatorId,
+        credId,
+        true, // resident key
+        rpId,
+        privateKey,
+        "VGVzdCBVc2Vy", // "Test User"
+        0 // sign count
+      );
+    }
+  );
+
+  return credId;
+}
+
+async function removeCredential(authenticatorId, credId) {
+  await SpecialPowers.spawnChrome(
+    [authenticatorId, credId],
+    (authenticatorId, credId) => {
+      let webauthnService = Cc["@mozilla.org/webauthn/service;1"].getService(
+        Ci.nsIWebAuthnService
+      );
+
+      webauthnService.removeCredential(authenticatorId, credId);
+    }
+  );
+}
-- 
cgit v1.2.3